Skip to main content

witbitz-code

Use the Code section of Witbitz Spaces, on any device you're signed in on, to reach OpenCode running on your computer. It's end-to-end encrypted, needs no open port, and doesn't need Tailscale.

This is the Python build of the witbitz-code tool. It speaks the same wire protocol, uses the same pairing file and the same account registry as the single-file Node download (node witbitz-code.mjs), so either one can pair a computer and the other can serve or unpair it.

Install

pipx install witbitz-code          # or: pip install witbitz-code

Python 3.10 or newer. Dependencies: cryptography, websockets, httpx, segno, and tinfoil (Tinfoil's own verifier — attestation, Sigstore and TLS pinning — for the confidential models below).

Use

witbitz-code setup

setup walks five steps and skips whatever is already done: it installs OpenCode if it is missing (it asks first), pairs this computer (scan the QR code in Spaces → Settings → Privacy & security → Add a device), which gives the computer its own keys from your Spaces account, offers to install the tools the agent uses for audio, video, PDFs and scans (ffmpeg, Poppler, Tesseract, … — with winget, Homebrew or one sudo apt-get line; it shows what it will run and asks once), then starts serve in the window. Then open Code in Spaces. Leave it running (Ctrl-C stops it); next time, witbitz-code serve is enough.

Or in your browser: witbitz-code ui (on Windows: Start menu → Witbitz Code) opens a page for this computer — whether it is online, which account it is paired with and this month's spend, Pair (the code is shown large on the page), Unpair, Pair again, Start / Stop, and Start when I log in (Windows and WSL). It serves while its window is open; closing that window stops it, and witbitz-code ui again just opens the page. Each copy has its own page — on one laptop Windows and WSL each get theirs, titled with the computer it controls. The page answers only this computer, and only the browser the link opened (a one-time key traded for a private cookie; no other website can drive it).

No key to paste. Pairing asks your Spaces account for this computer's own TrustedRouter and Tinfoil keys, capped by the providers (today up to $50 of models and $5 of tools a month per computer). They are sent sealed to this computer, kept in ~/.witbitz/code/account-keys.json (0600) and never given to OpenCode: the local proxy puts them on each call. Model calls still go from this computer straight to TrustedRouter and Tinfoil. Coding is by invitation for now — an account that is not invited gets no keys, and the pairing stops with a sentence saying so. A computer paired before 1.3 has no account keys: pair it once more (witbitz-code pair).

Several accounts on one computer: each pays for its own prompts. OpenCode names the conversation on every call it makes; the connector knows which account sent each prompt, and the proxy bills the call to that account. A prompt typed in OpenCode's own window on this computer is refused ("coding runs from the Spaces app"), and while one account's reply is being written another account cannot add to it. This splits the bill fairly between people who share a computer; it is not a wall between them (they already share its files and shell).

To start over: witbitz-code uninstall, then pipx install witbitz-code and witbitz-code setup. (pipx uninstall alone leaves the pairing and its keys behind, and does not stop a witbitz-code that is still running — setup offers to stop one.)

The same thing one step at a time: install OpenCode (curl -fsSL https://opencode.ai/install | bash), then witbitz-code pair and witbitz-code serve.

command what it does
ui [--no-browser] The page above. --no-browser serves without opening it (what a login start runs).
setup [--port <n>] The walk-through above. With --port, a pairing made for another port moves to it (no new scan).
uninstall [--yes] [--remove-keys] [--remove-notes] [--remove-sessions] [--remove-opencode] Remove witbitz-code from this computer: its coding keys go back to each account, each account lets go of it, ~/.witbitz/code and what serve added to OpenCode are deleted, and the pipx install removes itself. Asks first (Enter keeps) before your saved keys, project notes, OpenCode's sessions and OpenCode itself; never deletes sessions an OpenCode still has open. --yes skips the questions and keeps all four unless their flag is given.
pair [--name <name>] [--port <n> | --opencode-url <url>] Show the QR code. The account that scans it gets this computer. --name sets the label your devices show (default: the hostname).
serve [--port <n>] [--no-opencode] Start OpenCode on 127.0.0.1:<n> (default 4096) with your account's keys, then connect the pairings whose OpenCode is on that port. An OpenCode already running there, started some other way, is not used (it cannot have the keys) — close it first. One OpenCode per port, one serve per OpenCode.
status List this computer's pairings — name, account, OpenCode address, relay — and each one's coding keys: live, this month's model spend and the caps. Secrets are never printed.
rotate [--account <email>] Replace the pairing secret(s) without scanning again, then restart serve. Devices pick up the new secret on their next sync.
unpair [--account <email>] Remove this computer from an account. Every device drops it on its next sync.
version, --help

pair, rotate and unpair also accept --dry-run.

Windows and WSL on one computer are two computers to Spaces, and both can run: WSL shares Windows' ports, so the second one setup meets gets its own (it asks — 4097, and 4197 for its model proxy) and serve remembers it. WSL's is named " (WSL)"; one paired before 1.3.8 is renamed by its next setup. In WSL, setup also offers to start witbitz-code when you log in to Windows (WSL runs only once Windows starts it): a shortcut in the Windows Startup folder opens a minimized window running it (ui --no-browser: serving, the page there when you want it) — closing that window stops it, and witbitz-code uninstall removes the shortcut.

Any folder, any drive. New session in Code opens at your home folder; ‹ Up goes past it (on Windows up to "This computer", which lists the drives), and a folder can be typed (D:\projects). witbitz-code lists the folder names itself — nothing else of the computer — and "New folder" makes one where you are. A cloud computer keeps to its workspace.

More than one account. Each account that scans the QR code gets its own pairing, with its own secret and relay channel. OpenCode has no users, so every paired account reaches the same sessions, files and shell. That's fine when all the accounts are yours. If a second account belongs to another person, run a separate OpenCode for them (another port, ideally another OS user) and pair that account with --port.

How it works

 phone / desktop (Code page)                               this computer
 seal ▸ frames ◂ open  ── wss ─▶ code-relay.witbitz.chat ◀─ wss ──  witbitz-code serve ──▶ opencode (127.0.0.1)
  • Both ends dial out to wss://code-relay.witbitz.chat. Nothing listens on your network, and OpenCode never leaves 127.0.0.1.
  • Pairing uses a device link: the QR code holds only an ephemeral public key. Your signed-in device seals the account pointer to that key. The computer then mints a random 32-byte secret for this pairing and does two things with it:
    • stores it in ~/.witbitz/code/pairings.json (mode 0600);
    • publishes it into the account's sealed computers registry, which every signed-in device reads.
  • Keys: the relay channel id and two direction keys (page→computer and computer→page, AES-256-GCM) are derived from the secret with HKDF-SHA256. A frame reflected back at its sender doesn't decrypt.
  • Every frame is sealed. The relay forwards ciphertext it can't read, and the clear header (sender id, sequence number) is authenticated. Receivers drop replays. Large bodies are split into parts below the relay's message size cap.
  • Replays across restarts: each time the connector's socket opens, it announces a fresh random nonce inside its sealed hello. Every request and subscription must carry that nonce, so a request recorded before a restart or reconnect is refused (409) and never reaches OpenCode.
  • The connector only forwards what the Code page itself calls: list and read sessions, send a message, abort, answer a permission prompt, rename and delete. Every other request gets a 403 without touching OpenCode. OpenCode can run shell commands, so a leaked secret must not unlock more than the page can do. The live event stream is forwarded only while a device is watching.
  • The OpenCode password lives in ~/.opencode-server.env (created 0600 on first pair). The connector adds it to local calls, and it never leaves the computer.

Confidential models (the ones labelled "· confidential" in the model menu) are enforced on this computer, by a proxy serve starts on 127.0.0.1:<port + 100> and points OpenCode's TrustedRouter calls at — the same checks as the Node download:

  • the request goes out only after TrustedRouter's gateway proves (Google Confidential Space attestation: Intel TDX, secure boot, debug off, the operator's own image) what it runs, and it carries the hard floor provider.min_privacy = confidential;
  • the answer's words stream as they arrive, but tool calls, the finish and usage wait for TrustedRouter's signed receipt to verify for these exact request and response bytes (an attested key, a TEE-verified upstream under a known policy). A receipt that does not verify ends the step with an error, so no tool from that answer runs;
  • an image or a document a text-only confidential model cannot read is read as text inside Tinfoil's attested enclave with the account's Tinfoil key — or refused, never sent anywhere less;
  • every other model Witbitz offers passes through with the account's key; a model Witbitz does not offer is refused.

Auto mode (Manual · Accept edits · Plan · Auto in the Code composer's mode chip, per session) lets this computer answer OpenCode's permission prompts while you are away:

  • Refused at once: what is never safe, like rm -rf ~, mkfs or dd onto a disk. The agent is told why.
  • Allowed at once: read-only commands that stay inside the project, and edits to ordinary files in it.
  • Everything else goes to the session's own model, in a throw-away session that can use no tools. Only a clear, low-severity allow runs. A refusal, "ask", an unclear answer or a timeout leaves the prompt for you.
  • Never "always": each answer covers that one call.

Each decision is logged to ~/.witbitz/code/auto-log.jsonl as a digest, never the command. The rules are shared with the Node connector and tested against the same cases, so both decide alike.

What is encrypted, and what isn't. Requests, responses and events travel end to end. The relay operator sees a pseudonymous channel id, IP addresses, connection times, and frame sizes and timing. That's the same class of metadata the Spaces room store sees. Anyone holding a pairing secret can drive OpenCode within the allowlist until you run rotate.

Design: docs/opencode-relay.md in the Witbitz repository.

Files and environment

default override
pairings ~/.witbitz/code/pairings.json WITBITZ_CODE_PAIRINGS
OpenCode password ~/.opencode-server.env (OPENCODE_SERVER_PASSWORD=) OPENCODE_ENV_FILE
pairing QR, as SVG ~/.witbitz-rc.link.svg
Auto mode state + decision log ~/.witbitz/code/auto-<computer>.json, auto-log.jsonl WITBITZ_CODE_AUTO_DIR
account API https://api.witbitz.chat/v1/space RC_BASE, RC_ORIGIN
device-link origins https://spaces.witbitz.chat,https://witbitz-spaces.pages.dev RC_LINK_ORIGIN

Development

pip install -e '.[test]'
python -m pytest tests -q

The tests hold this package to the JavaScript reference implementation. They run the modules in spaces/public/ and tools/ under node (22 or newer), and they cover:

  • the pinned HKDF vectors;
  • frames sealed on either side opening on the other;
  • chunking through surrogate pairs;
  • the device-link reply and the backup keys;
  • the registry merge rules, compared byte for byte;
  • the pairing file in both directions;
  • the gzip wrap of account docs;
  • a connector of each language driven by a client of the other, through a local fake relay and a fake OpenCode.

Without the repository checkout (WITBITZ_REPO) or node, the cross-implementation tests are skipped.

Metadata

Release files for witbitz-code 1.7.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for witbitz-code 1.7.2
File Size Uploaded
witbitz_code-1.7.2.tar.gz 406.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for witbitz-code 1.7.2
File Interpreter ABI Platform
witbitz_code-1.7.2-py3-none-any.whl Python 3 none any Details

Total release size: 728.3 kB

Release files / witbitz_code-1.7.2.tar.gz

Download URL witbitz_code-1.7.2.tar.gz
Size 406.9 kB
Tags Source
SHA-256 checksum
How to use checksums
a04ecd6743ce0b73ac670d67e41b875c2ada24e3641b07348b3d0bf50785c00a
BLAKE2b-256 checksum
How to use checksums
d13a1f78f8c6bee859730f89a2b33b51e7cb438f89928b6c7870425f31fedc95
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.5

Release files / witbitz_code-1.7.2-py3-none-any.whl

Download URL witbitz_code-1.7.2-py3-none-any.whl
Size 321.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7791621ed67341736a0f7dc30e5f1cff51fd6e4712741e3e0882aea2f89e0fcb
BLAKE2b-256 checksum
How to use checksums
3901ef9ab6769e5556c4cdf9e52bd1e7e2964b10e98fdd34f433df78357c3fb4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.5

Release history Release notifications | RSS feed

1.7.4

2 release files

1.7.3

2 release files

This release

1.7.2 This release

2 release files

1.7.1

2 release files

1.7.0

2 release files

1.6.10

2 release files

1.6.9

2 release files

1.6.8

2 release files

1.6.7

2 release files

1.6.6

2 release files

1.6.5

2 release files

1.6.4

2 release files

1.6.3

2 release files

1.6.2

2 release files

1.6.1

2 release files

1.6.0

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.3.9

2 release files

1.3.8

2 release files

1.3.7

2 release files

1.3.6

2 release files

1.3.5

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.1

2 release files

1.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page