orcha docs

orcha runs on the machine where your coding agents live. It lets you interact directly with your tmux panes.

You need two things on that machine: a tmux server and a brain CLI (e.g., Claude Code).

Install

Linux or macOS with tmux.

curl -fsSL https://orcha.cc | sh

The script downloads the release binary, verifies its checksum, and installs to ~/.local/bin.

To inspect it first:

curl -fsSL https://orcha.cc/install.sh | less

Or build from source:

go install github.com/orcha-cc/orcha/cmd/orcha@latest

To update, re-run the install command. To remove, delete ~/.local/bin/orcha, ~/.config/orcha, and ~/.local/state/orcha.

Run it

orcha up

orcha up opens a 10-minute pairing window and prints a QR code and a link. Scan the QR from your phone, then approve the device in the terminal. Paired devices are remembered; pair more the same way.

Voice needs HTTPS. When a Tailscale HTTPS cert is available, orcha up also prints an https:// link; open that one for voice.

Configuration

orcha reads ~/.config/orcha/config.toml, created with commented defaults on first run:

[brain]
backend = "agent-cli"   # agent-cli | codex-cli | gemini-cli | antigravity | openai-compat
command = "claude"      # the CLI to drive: claude / codex / gemini / agy
model   = "haiku"       # blank uses the backend's own default
# openai-compat (ollama or any OpenAI-compatible API):
base_url = "http://127.0.0.1:11434/v1"
# api_key = ""          # or set ORCHA_API_KEY
# system_prompt = """   # override the built-in orchestrator persona (multi-line)
# You are a helpful fleet manager...
# """

[safety]
default_policy = "confirm-destructive"   # read-only | confirm-destructive | confirm-all | full
# protected_sessions = ["prod-experiment"]

[tts]
command = ""            # server-side voice: reads reply text on stdin, writes WAV to stdout
# voice  = "af_heart"   # default voice preset (passed as ORCHA_VOICE)
# voices = ["af_heart", "af_bella", "am_michael", "bf_emma"]  # shown in settings UI

[remote]
relay = "wss://relay.orcha.cc"   # the default hosted rendezvous relay (blind to your data)
# relay = ""            # disable it and pair directly with `orcha up --local`

Brains

The brain is the model that answers your questions and decides what to type:

  • agent-cli (default): your installed claude CLI (Claude Code). Fastest, and the recommended default. Blank model means haiku.
  • codex-cli: your installed codex CLI (OpenAI Codex CLI).
  • gemini-cli / antigravity: Gemini via Google's CLI (command = "gemini" or "agy"). Antigravity always runs with --sandbox.
  • openai-compat: a local model via ollama, or any OpenAI-compatible endpoint. Set base_url and model; both must support OpenAI-style tool calls.

Connect from anywhere

Device-to-host traffic is end-to-end encrypted (see Security); the relay just passes it through. In order of least trust:

  • Direct, over your tailnet (strongest). orcha up --local serves the web app and the relay from your own machine; your devices reach it directly over your tailnet. No third party serves the code or carries your traffic. (Set relay = "" to also drop the standing fallback link to the hosted relay.)
  • A relay you host elsewhere. Run the orcha-relay binary on any host with a public address, point remote.relay at it, and run orcha serve. The relay never holds keys or plaintext; serve it over HTTPS/WSS.
  • The hosted relay (the default). Out of the box remote.relay is wss://relay.orcha.cc, so plain orcha up pairs your phone through the hosted relay. It is blind to your data, but it serves the app code (see Security).

Safety

Every byte typed into a pane passes a destructive-input classifier, the pane's policy, and an append-only audit log that fails closed: if the action cannot be logged, it is not sent. The log lives at ~/.local/state/orcha/audit.jsonl.

The policy below governs input from your devices. The brain's own typing always stops for your confirmation first (except under read-only, which denies it). orcha never lets the model act unattended.

  • read-only: observe only; never type.
  • confirm-destructive (default): your input goes through, but anything the classifier flags as destructive or irreversible (rm -rf, git push, publishes, docker push, remote rsync) stops to ask.
  • confirm-all: ask before every input.
  • full: your input goes through without a prompt (still audited).

protected_sessions are tmux session names orcha must never type into: a hard deny, not a prompt. Observation still works. Use it for a mid-experiment or production session you want visible but untouchable.

Prose typed into agent panes carries a [user via orcha] or [<model> via orcha] prefix, so anyone reading the pane can tell what came from orcha.

Voice

Voice input uses the browser's speech recognition and needs a secure context (HTTPS), which orcha up provides on a tailnet (see Run it). Spoken transcripts are shown before any gated action runs.

Spoken replies default to your phone or browser's built-in speech. For far more natural replies, run orcha setup-tts, or set [tts].command to any wrapper that reads text on stdin and writes WAV to stdout.

Commands

  • orcha up: embed a relay, serve, and open a pairing window. The quickstart path.
  • orcha serve: connect out to the relay in remote.relay and serve paired devices (for a relay you host).
  • orcha pair: open a 10-minute pairing window for one device and print its QR and link. Run it with orcha serve already running, or to add a device.
  • orcha chat: talk to the brain from this terminal, no device needed (handy for testing your config).
  • orcha status: print the current fleet (sessions, panes, activity).
  • orcha down (aka stop): stop the running orcha daemon.
  • orcha restart: stop then start; picks up a new build or config change.
  • orcha devices: list paired devices; revoke one immediately with orcha devices revoke <name-or-key-prefix>.
  • orcha setup-tts: install the natural-voice TTS (~350 MB; also offered on first run).
  • orcha version: print the version.

Flags: orcha up --local pairs directly over your tailnet/LAN (no hosted relay); --port N overrides the default listen port.

Always-on (systemd)

To keep orcha running across logouts on Linux, install the user service:

mkdir -p ~/.config/systemd/user
curl -fsSL https://raw.githubusercontent.com/orcha-cc/orcha/main/packaging/systemd/orcha.service \
  -o ~/.config/systemd/user/orcha.service
systemctl --user daemon-reload
systemctl --user enable --now orcha
loginctl enable-linger "$USER"      # keep running after logout
orcha pair                           # explicitly open the pairing window

Security

orcha types into your terminals on command from a phone. That is remote code execution as a feature, so the design is explicit about what is protected and what is trusted.

Guarantees

  • End-to-end encryption. Device-to-host traffic is NaCl box (X25519 + XSalsa20-Poly1305), fresh random nonce per frame. The relay forwards ciphertext only; it never holds keys or plaintext.
  • Pairing secrets never touch the server. They travel in the URL fragment (browsers do not send fragments to the server), are one-time use, and expire in 10 minutes.
  • Verification code. During pairing, both the terminal and the phone display a 6-digit code derived from both public keys. Verify they match before approving; this prevents an attacker who photographs the QR from silently substituting their own device.
  • Gated input. Every keystroke into any pane passes a destructive-input classifier, per-pane policies, and an append-only audit log that fails closed: if the audit entry cannot be written, the action does not execute. Raw control characters are rejected in text input to prevent classifier bypass; deliberate terminal control goes through the separately classified send_keys path.
  • Content Security Policy. The web client is served with script-src 'self'; relay-hosted clients use frame-ancestors 'none', while the marketing origin permits only its same-origin demo frame. Vendored crypto (nacl.min.js) carries a subresource integrity hash.
  • Outbound-only host mode. orcha serve only dials a relay. The all-in-one orcha up deliberately embeds a relay, listening on the LAN only without TLS and binding plaintext to localhost when Tailscale HTTPS is active.
  • Revocable devices. orcha devices revoke updates the identity atomically and makes a running daemon reload authorization immediately.

Known limitations

  • No forward secrecy. Static NaCl keys mean a future private-key compromise decrypts recorded relay traffic. A Noise handshake with ephemeral session keys is planned.
  • LAN without TLS is trust-the-network. Without Tailscale HTTPS certs, the web client is served over plaintext. An active attacker on the same WiFi could swap the client code. With TLS this is eliminated; orcha up binds plaintext to localhost-only when TLS is available.
  • Pairing is explicitly opened. Headless and systemd startup do not create a pairing window. Run orcha pair from a terminal; both sides show the verification code and the terminal prompts y/N.
  • Whoever serves the app is trusted for code delivery. The web client is served by whatever you point your phone at, by default the hosted relay.orcha.cc, which is blind to your data but delivers the key-holding JavaScript (same model as all web-delivered E2E apps, e.g. Proton Mail). Mitigation: serve it yourself with orcha up --local (or a relay you self-host), so the code that holds your keys comes from you, not a third party.

The full threat model is in SECURITY.md in the repository.