9.2 KiB
Collab: Live Session Sharing
/collab shares your running session with other omp instances in real time. Guests render the same session natively in their own TUI — streaming assistant text, tool-call cards, footer state (cwd, model, context %, cost), ctrl+o expansion, /dump — no terminal mirroring. Guests can prompt and interrupt the agent; the host machine runs the agent and all tools.
Quick start
Host:
/collab
prints
Collab session started!
• Join from another terminal: omp join "mgAYTZwEnpRQtca0CTgn-Q.gdJUbTovD94ofDaa8YvhY0-ty16w4fn8PgB6PLnoA30"
• or any web browser: my.omp.sh/#mgAYTZwEnpRQtca0CTgn-Q.gdJUbTovD94ofDaa8YvhY0-ty16w4fn8PgB6PLnoA30
The browser line is click-to-join (an OSC 8 hyperlink to the full https:// deep link): the relay serves the web guest client at /, and the room id + key ride in the URL fragment. From another omp (any directory, any machine), either form works:
Running /collab or /collab view starts or displays the active hosting session, rendering both the terminal/browser join links and their corresponding QR codes.
/join my.omp.sh/#mgAYTZwEnpRQtca0CTgn-Q.gdJU…
The guest's previous session is restored on /leave (or when the host stops).
Commands
| Command | Effect |
|---|---|
/collab |
Start sharing full-control (or re-print the link/QR when already hosting) |
/collab <relay> |
Start sharing through a specific relay (relay.example.com, ws://localhost:7475) |
/collab view |
Start sharing read-only (or re-print the link/QR when already hosting) |
/collab status |
Show link + participants |
/collab stop |
Stop sharing |
/join <link> |
Join a shared session as a guest |
/leave |
Leave (guest) or stop sharing (host) |
Link format
Accepted by /join <link> and omp join "<link>":
<roomId>.<key> → default relay (wss://my.omp.sh)
<roomId>#<key> → legacy bare form
host[:port]/r/<roomId>.<key> → custom relay, wss:// inferred
host[:port]/r/<roomId>#<key> → legacy direct relay form
https://host[:port]/r/<roomId>.<key> → direct relay URL, normalized to wss://
wss://host[:port]/r/<roomId>.<key> → direct websocket relay URL
ws://localhost:7475/r/<roomId>.<key> → direct plain ws, localhost only
https://host[:port]/#<link> → browser deep link when web UI and relay share a host
https://web-host[:port][/<path>]/#<relay-link> → browser UI wrapper with relay link in the fragment
https://web.example/collab/#relay.example.com/r/<roomId>.<key> → web UI and relay on different hosts
<link> / <relay-link> are parsed recursively as any accepted link above. For http(s) browser wrappers with a parseable fragment, the fragment wins before the HTTP host/path are treated as a relay. This lets https://web.example/collab/#relay.example.com/r/<roomId>.<key> open the web UI at web.example while joining wss://relay.example.com/r/<roomId>. If the fragment is not a complete collab link, parsing falls back to the legacy direct relay form, so https://relay.example.com/r/<roomId>#<key> still means relay relay.example.com.
The trailing .<key> or #<key> part is the room secret, base64url-encoded, in one of two strengths:
- Full link — 48 bytes: the 32-byte AES-256-GCM room key followed by a 16-byte write token. Grants prompting, interrupting, and subagent control.
- View-only link — the bare 32-byte key, no write token. Grants live read access only. Pre-token links parse as view-only.
The room secret is dot-joined in newly generated links because RFC 3986 forbids a raw # inside a URL fragment; parsers still accept legacy # forms and %23-mangled legacy deep links.
End-to-end encryption
Every session payload (entries, events, state, prompts) is sealed with AES-256-GCM before it touches the socket. The relay sees only:
- room ids and connection counts,
- opaque ciphertext frames and their sizes,
- a 4-byte routing prefix (which guest a frame targets).
Possession of the link is the trust boundary: a full link reads and steers the session, a view-only link reads it. Share both like secrets.
Guest permission model
Two trust levels, enforced by the link itself — the host verifies the 16-byte write token at join and rejects writes from peers without it (they appear as read-only in the participants list, and the join notice says so).
Guests with a full link can:
- read the entire session (including the back-transcript at join time),
- prompt the agent (rendered with their name badge on every participant's transcript; the LLM sees the prompt text verbatim — names are display-only),
- interrupt the agent (Esc),
- use the Agent Hub against the host's subagents: live table and progress, chat (steers the host's subagent), kill, revive, and transcript viewing (fetched from the host on demand).
Guests with a view-only link can read everything live — back-transcript, streaming text, tool cards, subagent transcripts — but the host rejects prompting, interrupting, and agent control from them.
Everything that mutates the host session or machine is host-only: /model, /compact, /resume, /branch, bash (!), python ($), skills, etc. Guests keep a small local allowlist (/dump, /export, /copy, /help, /hotkeys, /theme, /settings, /leave, /collab, /exit, /quit).
Known v1 limit for guests: a turn already streaming when you join becomes visible from its next message boundary.
Web client
packages/collab-web is a standalone browser client for the same links — no omp install needed on the guest side. The relay serves it at /, which is what makes the /collab deep link click-to-join: https://<relay>/#<link> loads the client and auto-connects from the fragment. It renders the live transcript (streaming text, thinking, tool cards), a subagent panel with on-demand transcripts, and a composer with the same guest powers (prompt, interrupt, hub actions). Run bun run dev in the package for a local instance, bun run mock-host for an offline scripted host to develop against, and bun run build to emit a static dist/ deployable anywhere (HTTPS required for WebCrypto). The client never talks to anything but the relay, and the key stays in the URL fragment.
Set collab.webUrl when the browser UI is hosted separately from the websocket relay. When empty, /collab derives http(s)://host[:port] from collab.relayUrl; explicit web UI URLs must use https:// except for http://localhost development origins. The generated browser URL still carries the relay-specific collab link in the fragment.
Settings
| Setting | Default | Meaning |
|---|---|---|
collab.relayUrl |
wss://my.omp.sh |
Relay used by /collab when no relay is passed inline |
collab.webUrl |
empty | Browser UI URL for /collab links; empty derives from relay; explicit http:// is allowed only for localhost |
collab.displayName |
OS username | Name shown to other participants |
share.serverUrl |
https://my.omp.sh/s |
Share viewer/upload base used by /share (links are <base>/<id>#<key>) |
share.redactSecrets |
true |
Run the secret obfuscator over /share snapshots before upload |
Self-hosting the relay
The relay is a small content-blind Go service. It keeps no state beyond live connections and exposes:
GET /— the static collab-web guest client (target of the/collabdeep link),GET /r/<roomId>?role=host|guest— WebSocket upgrade,POST /s/GET /s/<id>/GET /s/<id>/raw—/shareblob upload, viewer page, and blob fetch,GET /healthz— liveness.
Architecture notes
Hub topology — the host is authoritative, guests never peer:
entryframes — durable session entries, broadcast pre-blob-externalization so images stay inline (guests cannot resolve host blob refs). Guests append them verbatim (ids preserved) to a replica session file under~/.omp/collab/<roomId>.jsonland into the agent's message array, which is why/dumpand context estimates work.eventframes — live agent events, fed straight into the guest's normal event controller; rendering is events-only to prevent double-render.stateframes — debounced footer snapshots: streaming flag, the host's full model object and thinking level (applied to the guest's replica agent state, so model display and context-window math are native), host context numbers, and participants.busframes — mirrored task-subagent lifecycle/progress EventBus traffic, republished on the guest's local bus so the subagent HUD and status-line count work natively.agentsframes — agent-registry snapshots feeding a guest-local registry, so the Agent Hub table renders host subagents.
Guest→host: hello, prompt, abort, agent-cmd (hub chat/kill/revive), and fetch-transcript (incremental subagent-transcript reads answered by targeted transcript frames). The replica loads through the regular /resume machinery, so theming, ctrl+o, and transcript behavior are native by construction; the guest process never chdirs to host paths.