Hub Daemon
The hub daemon is a background process that manages the multi-session dashboard. It coordinates session registration, serves the dashboard UI, and collects live terminal previews.
Components
Session Registry (registry.ts)
In-memory store of all active sessions. Uses a Map<string, SessionInfo> with EventEmitter for change notifications.
Events emitted:
session-added— new session registeredsession-removed— session unregistered or process diedsession-updated— session renamed or status changed
Health checks: Every 15 seconds, the registry verifies each session's process is still alive using process.kill(pid, 0). Dead sessions are automatically removed. Recent heartbeats (≤20s) are trusted over process checks.
Session Store (session-store.ts)
Persists the session registry to ~/.itwillsync/sessions.json. Saves are debounced (500ms) to avoid excessive disk writes during rapid registration/update cycles.
On hub startup, the store loads persisted sessions and verifies each process is still alive. Dead sessions and sessions older than 24 hours are pruned automatically. This allows sessions to survive hub daemon restarts.
Tool History (tool-history.ts)
Tracks the most recently used agent commands (e.g., claude, aider, bash). Stores up to 20 entries sorted by most-recently-used, persisted to ~/.itwillsync/tool-history.json. Used by the dashboard for quick-access suggestions.
Internal API (internal-api.ts)
HTTP server bound to 127.0.0.1:7963. Only accessible from the local machine. No authentication required (same-machine trust).
| Endpoint | Method | Purpose |
|---|---|---|
/api/health | GET | Hub health check |
/api/sessions | GET | List all sessions |
/api/sessions | POST | Register a session |
/api/sessions/:id | GET | Get session metadata (with memory usage) |
/api/sessions/:id | DELETE | Unregister a session |
/api/sessions/:id/heartbeat | PUT | Update last-seen timestamp |
/api/sessions/:id/stop | POST | Send SIGTERM to session |
/api/sessions/:id/rename | PUT | Rename a session |
/api/tool-history | GET | Recently used agent commands |
/api/recent-dirs | GET | Recently used working directories |
/api/browse | GET | Directory browser (query: ?path=~) |
Dashboard Server (server.ts)
HTTP + WebSocket server bound to 0.0.0.0:7962. Serves the dashboard web UI and provides real-time session updates via WebSocket.
Authentication: Master token required for HTML pages, API endpoints, and WebSocket upgrade. Static assets (JS/CSS) served without token.
Rate limiting: 5 failed auth attempts from the same IP triggers a 60-second lockout.
Preview Collector (preview-collector.ts)
Subscribes to each session's WebSocket as a read-only client. Receives terminal output, strips ANSI escape codes, and buffers the last 5 lines per session.
Updates are throttled to 500ms per session to avoid overwhelming phone connections.
Lifecycle
First session starts
│
├─► Check port 7963 for running hub
│ └─ Not running → spawn hub daemon (detached)
│
├─► Hub starts:
│ ├── Generates master token
│ ├── Writes ~/.itwillsync/hub.json + hub.pid
│ ├── Restores persisted sessions (verifies PIDs)
│ ├── Starts internal API (:7963)
│ ├── Starts dashboard server (:7962)
│ ├── Starts preview collector
│ └── Prints "hub:ready:7963" to stdout
│
├─► Session registers via POST /api/sessions
│
└─► Auto-shutdown timer starts (cancelled when sessions register)
Last session disconnects
│
└─► 30-second timer → hub exits, cleans up filesFiles on Disk
| File | Purpose | Created by | Cleaned up by |
|---|---|---|---|
~/.itwillsync/hub.json | Master token, ports, PID | Hub daemon | Hub shutdown |
~/.itwillsync/hub.pid | Hub process ID | Hub daemon | Hub shutdown |
~/.itwillsync/config.json | Networking preference | Setup wizard | Never (user config) |
~/.itwillsync/sessions.json | Persisted session registry | Session store | Hub shutdown (pruned on startup) |
~/.itwillsync/tool-history.json | Recently used agent commands | Tool history | Never (auto-pruned to 20 entries) |
~/.itwillsync/logs/*.log.gz | Compressed session logs | Session logger (CLI) | After logRetentionDays (default: 30) |