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.
Components
Section titled “Components”Daemon
Section titled “Daemon”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
Holder Processes
Section titled “Holder Processes”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
tmux Backend
Section titled “tmux Backend”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
Local Web UI
Section titled “Local Web UI”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
Clients
Section titled “Clients”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
Remote Access
Section titled “Remote Access”Signaling Service
Section titled “Signaling Service”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
Relay Node
Section titled “Relay Node”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
Connection Flow
Section titled “Connection Flow”- Remote client connects to signaling, authenticates, and sees online devices and their encrypted session lists
- Remote client decrypts the session list locally and selects a session
- Signaling orchestrates pairing: generates a JWT token, picks a relay node, and notifies both sides
- Both the daemon and remote client connect to the relay and present the token
- Ephemeral X25519 key exchange through the relay derives a shared session key
- E2E encrypted IPC frames flow through the relay - the relay sees only opaque bytes
- On disconnect, ephemeral keys are discarded (forward secrecy)
Encryption
Section titled “Encryption”Device Identity Keys
Section titled “Device Identity Keys”- 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
Ephemeral Connection Keys
Section titled “Ephemeral Connection Keys”- 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
Sender-Key Model
Section titled “Sender-Key Model”- 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
What Each Component Can See
Section titled “What Each Component Can See”| Component | Can see |
|---|---|
| Local clients | Everything (localhost trust) |
| Daemon | Everything (owns sessions) |
| Signaling service | Device presence, encrypted blobs only |
| Relay node | Opaque encrypted bytes only |
| Remote client | Decrypted terminal I/O (has session key) |