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
claudeCLI (Claude Code). Fastest, and the recommended default. Blankmodelmeanshaiku. - codex-cli: your installed
codexCLI (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_urlandmodel; 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 --localserves 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. (Setrelay = ""to also drop the standing fallback link to the hosted relay.) - A relay you host elsewhere. Run the
orcha-relaybinary on any host with a public address, pointremote.relayat it, and runorcha serve. The relay never holds keys or plaintext; serve it over HTTPS/WSS. - The hosted relay (the default). Out of the box
remote.relayiswss://relay.orcha.cc, so plainorcha uppairs 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, remotersync) 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 inremote.relayand 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 withorcha servealready 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(akastop): 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 withorcha 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_keyspath. - Content Security Policy. The web client is served with
script-src 'self'; relay-hosted clients useframe-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 serveonly dials a relay. The all-in-oneorcha updeliberately embeds a relay, listening on the LAN only without TLS and binding plaintext to localhost when Tailscale HTTPS is active. - Revocable devices.
orcha devices revokeupdates 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 upbinds plaintext to localhost-only when TLS is available. - Pairing is explicitly opened. Headless and systemd startup
do not create a pairing window. Run
orcha pairfrom 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 withorcha 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.