Skip to content

Architecture

carryOn is a daemon-based terminal session manager. A background daemon process manages all sessions on a machine. Local clients - the CLI, VS Code, JetBrains, and the browser - connect to the daemon over IPC or WebSocket. Sessions persist via independent holder processes that own the underlying PTY. For remote access from other devices, carryOn uses end-to-end encrypted relay connections through a lightweight cloud signaling service, ensuring that no server ever sees your terminal data in plaintext.

LOCAL MACHINE CLOUD (carryon.dev) CLI VS Code JetBrains Browser IPC IPC IPC WebSocket Daemon Session manager - IPC server - Web server Local Web UI localhost:8384 Unix socket CLI Holder Process Owns PTY - 256KB scrollback tmux (optional) PTY Shell Signaling Service Cloudflare Workers + Durable Objects Auth - Presence - Session cache Sees only encrypted blobs D1 Database Relay Node Stateless WebSocket proxy Cannot decrypt - forwards opaque bytes Remote Client CLI, Browser, or Mobile WebSocket (TLS) E2E encrypted E2E encrypted Local connection E2E encrypted

The daemon is a background process and the central orchestrator of carryOn. It manages session lifecycle and multiplexes I/O between clients and backends.

  • Listens on ~/.carryon/daemon.sock (Unix) or a named pipe (Windows)
  • Starts automatically on first command - you never need to launch it manually
  • Stopping the daemon does not kill sessions - holder processes keep running independently
  • Optionally connects to the signaling service for remote access

Each native session runs in its own holder process, the default backend.

  • The holder owns the PTY file descriptor and runs independently from the daemon
  • Maintains a 256KB scrollback ring buffer so new clients see recent output
  • If the daemon crashes or restarts, holders keep running and the daemon reconnects on startup
  • This is how sessions survive everything - the holder is the unit of persistence

An optional alternative backend that wraps an existing tmux installation.

  • Persistence is handled by the tmux server itself
  • Useful for users already comfortable with tmux who want to keep their workflow
  • carryOn provides the multi-client, multi-device layer on top

A built-in HTTP and WebSocket server at localhost:8384.

  • xterm.js terminal in the browser with session list, tabs, and mobile support
  • Auto-reconnects on network interruption or phone wake
  • No authentication required - uses a localhost trust model
  • Enable with carryon config set local.enabled true

All clients use the same binary framing protocol over their respective transports.

  • CLI - Go binary, connects via IPC, primary interface
  • VS Code - Extension with session browser sidebar and project auto-detection
  • JetBrains - Plugin (coming soon), same model as VS Code
  • Browser - Connects via localhost web UI or remote relay

The signaling service runs on Cloudflare Workers with Durable Objects (one DO per account).

  • Handles auth via OAuth and device registration
  • Tracks device presence (online/offline)
  • Caches encrypted session lists - the server sees only encrypted blobs, never session names or metadata
  • Orchestrates connection pairing between devices
  • Issues short-lived JWT pairing tokens for relay connections

A stateless Go binary that acts as a WebSocket proxy.

  • Pairs two WebSocket connections by signed JWT token
  • Forwards opaque bytes bidirectionally - cannot decrypt, cannot inspect content
  • Horizontally scalable with no persistence
  • If a connection drops, both sides fall back to signaling for re-pairing
  1. Remote client connects to signaling, authenticates, and sees online devices and their encrypted session lists
  2. Remote client decrypts the session list locally and selects a session
  3. Signaling orchestrates pairing: generates a JWT token, picks a relay node, and notifies both sides
  4. Both the daemon and remote client connect to the relay and present the token
  5. Ephemeral X25519 key exchange through the relay derives a shared session key
  6. E2E encrypted IPC frames flow through the relay - the relay sees only opaque bytes
  7. On disconnect, ephemeral keys are discarded (forward secrecy)
  • X25519 keypair generated on carryon remote login
  • Public key registered in the cloud, private key stored locally at ~/.carryon/remote/device.key
  • Long-lived, persists across daemon restarts
  • Fresh X25519 keypair generated per relay connection (both sides)
  • Symmetric session key derived via HKDF
  • Frames encrypted with ChaCha20-Poly1305
  • Keys discarded when the connection ends - forward secrecy
  • Session lists cached in signaling are encrypted with a random AES-256-GCM data key
  • The data key is wrapped individually for each device’s public key
  • Any device decrypts by unwrapping with their private key
  • The signaling server stores only encrypted blobs - it cannot read session names or metadata
ComponentCan see
Local clientsEverything (localhost trust)
DaemonEverything (owns sessions)
Signaling serviceDevice presence, encrypted blobs only
Relay nodeOpaque encrypted bytes only
Remote clientDecrypted terminal I/O (has session key)
Client IPC / WebSocket Daemon Unix socket Holder Process PTY Shell
Remote Client E2E encrypted Relay Node opaque passthrough E2E encrypted Daemon decrypts here Unix socket Holder Process PTY Shell