You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Today CCAM only monitors Claude Code sessions on the same machine the dashboard runs on. Many users run Claude Code on other machines — most commonly over SSH (dev boxes, cloud VMs, remote workstations) — while keeping the CCAM app on their local machine.
Request: let users collect Claude Code usage from additional places (SSH machines, and potentially other transports) and see that data live, in real time, in their local CCAM dashboard — alongside or instead of local data, user's choice.
Motivating use case
I use Claude Code on an SSH machine, but run the CCAM app on my laptop. I want to see the SSH machine's usage live in my local CCAM.
Proposed UX (new Settings section)
A new "Remote Data Sources" section on the Settings page where the user picks where their data comes from:
Local only (current behavior, default)
Specific remote source(s) — one or more SSH (or other) sources
All / both — local + every configured remote, merged
Changing the selection updates the entire dashboard immediately to reflect the chosen scope.
Investigation — is this feasible?
Yes. Real-time is achievable and SSH is the natural first transport. Below is an architecture-grounded analysis (file:line refs against current master-line code).
Current pipeline (single-machine by design)
Claude Code event
-> scripts/hook-handler.js POST http://127.0.0.1:<port>/api/hooks/event (target host HARDCODED)
-> server/routes/hooks.js processEvent() transaction
-> SQLite (~/.claude/agent-dashboard/dashboard.db, single shared file)
-> server/websocket.js broadcast (every client gets every message)
-> React UI talks same-origin /api + /ws only
Blockers to remote collection
#
Blocker
Where
Fix size
1
Hook POST target host hardcoded 127.0.0.1
scripts/hook-handler.js:76 (path /api/hooks/event also hardcoded :78)
small
2
Server binds 127.0.0.1 by default (opt-in wider via DASHBOARD_HOST)
server/lib/security.js:28, server/index.js:176
env only
3
Ingestion is unauthenticated even when DASHBOARD_TOKEN is set — /hooks is in TOKEN_EXEMPT_PREFIXES
server/lib/security.js:125
security must-fix before any network exposure
4
No machine / host / origin column in the schema — cannot tell which machine a session/event/agent came from
server/db.js:136-407
additive, migration-safe
5
Client hardwired to same-origin /api + /ws; no "point elsewhere" setting
"Household hooks": the ingest route already accommodates hooks forwarded from other machines by an external forwarder — when a POST carries data.remote_custom_title / data.remote_ai_title, it is applied directly because the remote transcript isn't readable locally. server/routes/hooks.js:317-330. Proves inbound remote push is a designed-for case.
Remote-aware liveness reaper: forwarded sessions report a non-POSIX cwd (e.g. D:\Git\...) and are deliberately skipped by the reaper so they aren't falsely killed. server/routes/hooks.js:1424-1442.
Consolidation via export/import:server/lib/data-transfer.js — UUID-keyed, idempotent, session-atomic merge of another machine's DB. Offline (manual file transfer), not live. (Related work: Feature: Local-first backup, restore, and sync workflow #149.)
Session IDs are globally-unique UUIDs (data.session_id verbatim, server/routes/hooks.js:312), so merging data from multiple machines into the one shared DB cannot cause PK collisions — only the inability to distinguish origin (blocker Feature: Custom Dashboard Builder (Configurable Widgets & Layouts) #4).
Notable: the core SSH use case already works today with zero code (reverse tunnel)
Remote hooks POST to 127.0.0.1:4820, tunneled to the local dashboard, ingested and pushed live over the existing WebSocket. The only thing missing to make it first-class is a machine-origin tag (blocker #4) so remote and local sessions can be labeled, filtered, and toggled — which is the heart of this feature.
Approaches (design options — pick during implementation)
A — SSH tunnel + origin label (MVP). Document the reverse tunnel above; add a machine-origin tag + a source facet/filter in the UI. Transport is SSH (secure, no open port). Needs only #4 + UI. Smallest real feature.
B — Direct network push. Remote hooks POST straight to the local dashboard's LAN IP. Needs #1 + #2 + #3 (add ingest auth) + #4. Opens a network port — largest security surface.
C — Dashboard pulls over SSH (closest to the proposed UX). Server connects out via SSH to each configured remote, tails ~/.claude/projects/*.jsonl transcripts (tail -f stream or poll), and ingests. Settings → "add SSH source", toggle which are included. No changes required on the remote at all. Biggest build (SSH client / key management / streaming), cleanest UX. Needs #4.
D — ccam forward shipped remote agent. Generalize the "household hooks" forwarder into a first-class command the remote runs; forwards over HTTPS/WS with a token. Real-time, authenticated, origin-tagged. Medium build; requires a remote install step.
Real-time in all cases
A / B / D are push → instant via the existing WebSocket broadcast.
C is pull → near-real-time (tail-stream or short poll).
The WebSocket is broadcast-only, so no WS changes are needed — the client just filters incoming messages by the new source field.
Recommended shape (if built)
Schema: add a nullable source column to sessions / agents / events, default "local" (additive, migration-safe per server/db.js conventions).
Transport: ship A (documented SSH tunnel + label) as the MVP; consider C (SSH pull) as the full "add a source in Settings" experience.
Security: any non-loopback path must authenticate ingestion — remove the /hooks token exemption for remote requests while keeping loopback exempt (server/lib/security.js:125).
UI: new Settings "Remote Data Sources" section (slots alongside claude-home / import; the TOC auto-registers it — client/src/pages/Settings.tsx:67-82) + a cross-app source facet/filter mirroring the existing cwd facet pattern (client/src/pages/Sessions.tsx, api.sessions.facets). Regenerate screen snapshots (client/src/pages/__tests__/screens.snapshot.test.tsx).
Acceptance criteria (high-level)
User can configure one or more remote data sources (SSH first) from Settings.
User can choose scope: local only / specific source(s) / all — dashboard updates immediately.
Remote session/event/agent data is tagged with its origin machine and is filterable everywhere it's listed.
Remote data streams in live (real-time), consistent with local behavior.
Any network-exposed ingestion path is authenticated (no unauthenticated remote writes).
No regression to the local-only default (loopback, zero-config).
Docs updated (README + wiki + server/README.md) per the repo doc-sync policy.
Investigation performed against the feat/data-export-import-roundtrip working tree; file:line refs reflect current code. No code changes were made as part of filing this issue.
Summary
Today CCAM only monitors Claude Code sessions on the same machine the dashboard runs on. Many users run Claude Code on other machines — most commonly over SSH (dev boxes, cloud VMs, remote workstations) — while keeping the CCAM app on their local machine.
Request: let users collect Claude Code usage from additional places (SSH machines, and potentially other transports) and see that data live, in real time, in their local CCAM dashboard — alongside or instead of local data, user's choice.
Motivating use case
Proposed UX (new Settings section)
A new "Remote Data Sources" section on the Settings page where the user picks where their data comes from:
Changing the selection updates the entire dashboard immediately to reflect the chosen scope.
Investigation — is this feasible?
Yes. Real-time is achievable and SSH is the natural first transport. Below is an architecture-grounded analysis (file:line refs against current
master-line code).Current pipeline (single-machine by design)
Blockers to remote collection
127.0.0.1scripts/hook-handler.js:76(path/api/hooks/eventalso hardcoded:78)127.0.0.1by default (opt-in wider viaDASHBOARD_HOST)server/lib/security.js:28,server/index.js:176DASHBOARD_TOKENis set —/hooksis inTOKEN_EXEMPT_PREFIXESserver/lib/security.js:125server/db.js:136-407/api+/ws; no "point elsewhere" settingclient/src/lib/api.ts:100,client/src/hooks/useWebSocket.ts:55Foundations that already exist (partial)
data.remote_custom_title/data.remote_ai_title, it is applied directly because the remote transcript isn't readable locally.server/routes/hooks.js:317-330. Proves inbound remote push is a designed-for case.cwd(e.g.D:\Git\...) and are deliberately skipped by the reaper so they aren't falsely killed.server/routes/hooks.js:1424-1442.server/lib/data-transfer.js— UUID-keyed, idempotent, session-atomic merge of another machine's DB. Offline (manual file transfer), not live. (Related work: Feature: Local-first backup, restore, and sync workflow #149.)data.session_idverbatim,server/routes/hooks.js:312), so merging data from multiple machines into the one shared DB cannot cause PK collisions — only the inability to distinguish origin (blocker Feature: Custom Dashboard Builder (Configurable Widgets & Layouts) #4).Notable: the core SSH use case already works today with zero code (reverse tunnel)
On the SSH machine:
Remote hooks POST to
127.0.0.1:4820, tunneled to the local dashboard, ingested and pushed live over the existing WebSocket. The only thing missing to make it first-class is a machine-origin tag (blocker #4) so remote and local sessions can be labeled, filtered, and toggled — which is the heart of this feature.Approaches (design options — pick during implementation)
A — SSH tunnel + origin label (MVP). Document the reverse tunnel above; add a machine-origin tag + a source facet/filter in the UI. Transport is SSH (secure, no open port). Needs only #4 + UI. Smallest real feature.
B — Direct network push. Remote hooks POST straight to the local dashboard's LAN IP. Needs #1 + #2 + #3 (add ingest auth) + #4. Opens a network port — largest security surface.
C — Dashboard pulls over SSH (closest to the proposed UX). Server connects out via SSH to each configured remote, tails
~/.claude/projects/*.jsonltranscripts (tail -fstream or poll), and ingests. Settings → "add SSH source", toggle which are included. No changes required on the remote at all. Biggest build (SSH client / key management / streaming), cleanest UX. Needs #4.D —
ccam forwardshipped remote agent. Generalize the "household hooks" forwarder into a first-class command the remote runs; forwards over HTTPS/WS with a token. Real-time, authenticated, origin-tagged. Medium build; requires a remote install step.Real-time in all cases
sourcefield.Recommended shape (if built)
sourcecolumn tosessions/agents/events, default"local"(additive, migration-safe perserver/db.jsconventions)./hookstoken exemption for remote requests while keeping loopback exempt (server/lib/security.js:125).claude-home/import; the TOC auto-registers it —client/src/pages/Settings.tsx:67-82) + a cross-app source facet/filter mirroring the existingcwdfacet pattern (client/src/pages/Sessions.tsx,api.sessions.facets). Regenerate screen snapshots (client/src/pages/__tests__/screens.snapshot.test.tsx).Acceptance criteria (high-level)
server/README.md) per the repo doc-sync policy.Related
Investigation performed against the
feat/data-export-import-roundtripworking tree; file:line refs reflect current code. No code changes were made as part of filing this issue.