Mac: a menu bar app that runs an iroh node and accepts connections from devices paired by QR code. Each session maps to a long-running claude -p --input-format stream-json process whose output is streamed to clients as events.
iOS: the app embeds an iroh node (no account, no system VPN slot), pairs with Macs by QR code, manages multiple Macs, projects and git worktrees, and shows runs live.
Pairing: the hello message carries the one-time pairing code from the QR code; the Mac checks it and records the peer’s iroh node ID. From then on, only the node ID (authenticated by iroh) is needed.
Timeline: the session’s jsonl is the source of truth (history + watchSession); live run events provide streaming text. Both sources share message uuids and are de-duplicated by id. Sessions started directly in the Mac’s terminal can be watched live too.
The iOS app writes each prompt to a persistent outbox (outbox.json) before sending, and removes it only after the server acknowledges. After a network drop, request timeout or the app being killed, pending prompts are resent in order on reconnect
De-duplication
Every message carries a client-generated clientMessageId. The server handles it idempotently and persists it to runs.json, so a message runs only once no matter how often it’s resent
Session ownership
The client pre-generates the sessionId for new sessions; the server creates them with claude --session-id and continues existing ones with --resume. Resent messages always land in the right session
Receiving
Run events carry an increasing seq; after reconnecting the client resumes with subscribe(runId, afterSeq). Once a complete text or thinking item arrives, the streaming deltas before it are compacted out of the buffer, so buffer size is largely independent of output length. If the buffer ever overflows, the server returns complete=false and the client reloads from jsonl
Disconnect detection
10s heartbeat; 30s without data counts as disconnected. Every request has a timeout that closes the connection, so half-open connections don’t hang. iOS watches network changes, reconnects immediately when the network returns and probes the connection when switching between Wi-Fi and cellular. Reconnects use exponential backoff with jitter
Mac restart
Runs that were in progress are marked interrupted after a restart and are not re-run automatically, so side-effecting operations never execute twice. The client asks you to confirm before resending