# Code — run OpenCode on your computer from the Spaces app > **Status: Beta.** The Code section ships in the Spaces app; your computer joins through a single-file download > (`witbitz-code.mjs`, Node ≥ 22). **Open source:** the connector, pairing, frame codec and relay are on GitHub at > **[github.com/witbitzchat/witbitz-code](https://github.com/witbitzchat/witbitz-code)**. A Python implementation is on > PyPI: **[`pipx install witbitz-code`](https://pypi.org/project/witbitz-code/)**. The **Code** section (Spaces → ☰ → **Code**) is a chat for [OpenCode](https://opencode.ai), the open-source coding agent — running on **your own computer**, against **your own code**, reached from any device you are signed in on: your phone on cellular, a tablet, another laptop. There is no server address to type, no port to open, no VPN to run. **No computer of your own? Use a cloud computer.** Code → ☰ → Computers → **New cloud computer** makes one for the agent: a protected virtual machine (an AWS Nitro Enclave) only you can open — Witbitz cannot read its files, which are saved encrypted after every turn. It sleeps after 30 minutes unused and wakes when you open it, and the agent's keys stay outside it, added to each call by a separate protected machine. You choose what it may reach — nothing (the default), package registries and GitHub, or hosts you list — and its agent has a browser you can watch and take over from any device. With **Private browsing** on, that browser forgets cookies, logins and history every time the computer opens or closes. Cloud computers are part of the **Plus** plan; during early access they are open to invited accounts. The steps are in the Spaces guide, section "Cloud computers" (also in the app: Help). ## How it works ``` your phone (Spaces app → Code) your computer ┌─────────────────────────┐ ┌────────────────────────────────┐ │ the Code page │ │ opencode serve 127.0.0.1:4096│ │ seals every request │ │ ▲ local HTTP only │ └───────────┬─────────────┘ │ witbitz-code (the connector) │ │ wss — dials out │ opens and answers each request │ ▼ └───────────┬────────────────────┘ ┌──────────────────────────────────────┐ wss — dials out │ │ wss://code-relay.witbitz.chat │◂──────────────────────────────────┘ │ forwards sealed frames it cannot read│ └──────────────────────────────────────┘ ``` 1. **OpenCode stays on your computer**, listening on `127.0.0.1` only. Nothing on the network can reach it. 2. **The connector** (`witbitz-code serve`) runs beside it and makes one **outbound** WebSocket to the relay. It does for the Code page what a browser on the same machine would do — calls OpenCode's API and streams its events back. 3. **The Code page** opens its own outbound WebSocket to the same relay channel. Every frame in both directions is **encrypted end to end** (AES-256-GCM) with keys only your devices and your computer hold. 4. **The relay** — a Witbitz service — pairs the two sockets and forwards bytes it cannot read. ### The keys Pairing creates one random **32-byte secret** for this computer and your account. From it, both ends derive (HKDF-SHA-256) the relay channel name and two encryption keys, one per direction. The secret is stored in two places only: - on your computer, in `~/.witbitz/code/pairings.json` (readable only by you), and - in your account's **encrypted** index, which is how every signed-in device learns about the computer — the same end-to-end encrypted sync that carries your Spaces list. Witbitz stores it sealed and cannot read it. The OpenCode password (`OPENCODE_SERVER_PASSWORD`) never leaves the computer; the connector adds it locally. ### What Witbitz can and cannot see | | sees | cannot | |---|---|---| | **The relay** | a random channel name, IP addresses, when sockets connect, frame sizes and timing | read or alter any request, response or event; replay a frame | | **The account store** | that your encrypted index changed | read your computers, their names or secrets | | **Anyone who guesses nothing** | nothing | join a channel usefully — the name is 256 bits derived from the secret | Two honest limits: - **Your prompts and code go to the model you configure in OpenCode**, from your computer. That is OpenCode's own traffic, outside Witbitz. Pick a **confidential** model (below) and that request is provably handled inside attested hardware; any other model is an ordinary API call to its provider. - The relay sees **metadata** (who is online, when, how much). It is the same class of metadata the rest of the platform sees; the content is the part that is sealed. ### What the connector will do Only what the Code page itself does: list, open, create, rename, archive and delete sessions; send a turn, stop it, undo or redo it, and compact the conversation; answer a permission prompt or a question the agent asks; read the model, agent, command and skill catalogs, the agent's todo list, and which sessions are running; list folder names under your home folder for a new session; save the files a turn carries and hand them back to your devices (below); show the files a reply produced inside that session's folder; say which suggested tools are installed; keep the record of which sessions you have seen; run Auto mode's checks; and follow the event stream. Any other OpenCode API call is refused **on your computer**, before it reaches OpenCode — OpenCode can run shell commands, so a leaked secret must not unlock more than the page can. That includes OpenCode's own route for running a `/command`, which executes shell snippets from the typed arguments without asking: the Code page fills a command's template in itself and sends it as an ordinary message instead. You don't have to take this on trust. The allowlist is `ALLOW` in `spaces/public/codeRelay.js` in the [public repository](https://github.com/witbitzchat/witbitz-code), and the connector that enforces it is `tools/opencode-connector.mjs` in the same place. The page's [egress lock](https://witbitz.chat/docs/verify) lists `wss://code-relay.witbitz.chat` — a Witbitz origin. Your computer's address never appears in the app, and no third-party host is added to the allowlist. With **Hide my IP** on (Settings), the app's own traffic goes through OHTTP, but the relay connection is a plain WebSocket that would see your device's IP address. The Code section therefore asks before it connects, and remembers a yes on that device while the setting stays on. ## Set it up You need a computer that stays on while you work (macOS, Linux, or Windows), and the Spaces app on your phone. **No keys to bring.** Pairing the computer with your Spaces account gives that computer its own model keys — [TrustedRouter](https://trustedrouter.com) for the AI models (the **· confidential** ones included) and [Tinfoil](https://tinfoil.sh) for reading images and documents confidentially — capped by the providers (today up to $50 of models and $5 of tools a month per computer, at most three computers per account). They are sent sealed to the computer and never given to OpenCode: `witbitz-code` puts them on each call. Model calls go from your computer straight to TrustedRouter and Tinfoil; Witbitz is not in the middle. **Free to try:** an account that is not invited gets the free allowance — one computer, $1 of models and $0.25 of tools a month; invited accounts get the full caps above. At the cap the provider stops, and Code says *"This computer's Code allowance for the month is used up — it comes back on …"* with a link to your plan; an invitation later raises the caps in place, with no new pairing. ### On Windows Open **PowerShell** — Start menu → type *PowerShell* → Enter. Use a **normal** window, not *Run as administrator*: nothing here needs administrator rights, and the installer stops if it is run as one. Paste this line and press Enter: ```powershell irm https://app.witbitz.chat/code-vaulted.ps1 | iex ``` That is the line for the Spaces app at app.witbitz.chat. Each Spaces app has its own installer and its own Witbitz Code — a computer installed for one app cannot be paired with another — and the app shows its own line in Code → Computers → Add a computer (the regular app's is `irm https://witbitz-spaces.pages.dev/code.ps1 | iex`). It asks nothing. It puts everything in one folder of yours, `%LOCALAPPDATA%\Witbitz Vaulted` (`Witbitz` for the regular app): [uv](https://docs.astral.sh/uv/) (which brings its own Python), OpenCode for Windows, `witbitz-code` from [PyPI](https://pypi.org/project/witbitz-code/), Git Bash (Git for Windows' portable build, only if Git is not installed) and a Python for the agent's own scripts — each pinned to a version, and every download checked against the SHA-256 written in the script, so a file that does not match is never used. The script is short and readable at [app.witbitz.chat/code-vaulted.ps1](https://app.witbitz.chat/code-vaulted.ps1). On Windows the agent works in Git Bash, with UTF-8 Python and a note telling it how Windows paths work: measured on teacher-style tasks with Hebrew files, that is what took Windows from 45 to 48 correct results out of 48. When it is done, **Witbitz Code starts in the background — no window to keep open — and its page opens in your browser.** You can close PowerShell. Finish there (see [The Witbitz Code page](#the-witbitz-code-page)): 1. **Pair this computer** — the page shows a QR code. On your phone, open **Spaces → Settings → Privacy & security → Add a device** and scan it. 2. **Tools** — if the agent's tools for audio, video, PDFs and scans are missing (Poppler, Tesseract, Pandoc…), the page lists them with what it would run; **Install them** does it. Windows may ask once to allow an installer. 3. **Start when I log in** — tick it to have Witbitz Code start in the background each time you sign in to Windows. **Start menu → Witbitz Code** opens the page again. **Quit** at the bottom of the page stops Witbitz Code. **To update**, paste the same line again: it stops the running copy, updates it and opens the page. **To remove it**, see [Removing it on Windows](#removing-it-on-windows). ### In WSL (Linux on Windows) In your WSL terminal: ```bash pipx install witbitz-code # or: uv tool install witbitz-code witbitz-code setup ``` Setup checks that OpenCode is installed (and offers to install it without `sudo`), then **opens the same page in your Windows browser** and runs in the background — Windows holds WSL open with a hidden process, so you can close the terminal. Pair, install the tools and tick *Start when I log in* on the page, as on Windows. The Start menu gets its own entry, **Witbitz Code (WSL )**, and the browser tab is titled the same way, so it is never mixed up with the Windows one. `witbitz-code setup --here` does every step in the terminal instead. **Windows and WSL on one laptop** can both run: they are two computers in your account — *Laptop* and *Laptop (WSL)* — on their own ports (the second one takes 4097 when 4096 is in use). **To update:** `pipx upgrade witbitz-code` (or `uv tool upgrade witbitz-code`), then `witbitz-code setup` once. **To remove it:** `witbitz-code uninstall` in WSL. ### On a Mac or Linux #### 1. Install Node.js Download the **LTS** installer from **[nodejs.org](https://nodejs.org/en/download)** and run it (on a Mac you can also use `brew install node`). `witbitz-code` needs Node.js 22 or newer. #### 2. Paste one line in Terminal Open **Terminal** on that computer (on a Mac: press ⌘-Space, type *Terminal*, press Enter). Paste this line and press Enter: ```bash curl -fsSL https://app.witbitz.chat/code-vaulted.sh | bash ``` (The regular app's line is `curl -fsSL https://witbitz-spaces.pages.dev/code.sh | bash`.) It checks that Node.js is there, downloads `witbitz-code.mjs` into your home folder, checks the file against the app's asset manifest — the list of file hashes the [signed build certificate](https://witbitz.chat/docs/verify) commits to — and starts the setup. A file that does not match is never used. The script is short and readable at [app.witbitz.chat/code-vaulted.sh](https://app.witbitz.chat/code-vaulted.sh). Paste the same line again later to update. **Pairing a computer from its own screen.** A laptop's camera cannot scan a QR code on its own screen. In **Add a device**, choose **a picture of the code** (a screenshot, or the image the setup saves) or **paste the code** — the setup prints it after `raw:`. Only ever use a code from a computer in front of you: a code someone sends you would give them your account. The setup walks through four steps and skips whatever is already done, so it is safe to run again: 1. **OpenCode** — if it is not installed, it offers to install it without `sudo`: with npm where npm can install for you, otherwise with OpenCode's own installer (`curl -fsSL https://opencode.ai/install | bash`, into `~/.opencode`). 2. **Pairing** — a QR code appears. On your phone, open **Spaces → Settings → Privacy & security → Add a device** and scan it. The QR carries no secret — your phone seals your account to a one-time key the computer just made. 3. **Coding keys** — the computer asks your account for its own keys, with the sign-in your phone just handed over. They arrive sealed to the computer and are kept in `~/.witbitz/code/account-keys.json` (readable only by you). Nothing to paste. A computer paired before this asks you to scan once more. 4. **Keep it running** — it offers to start `witbitz-code` now and whenever you log in: a systemd user service on Linux, a launch agent on macOS. Where that is not available (WSL without systemd), or if you say no, it runs in the terminal window — keep it open. If an OpenCode you started yourself is already running, setup asks you to close it first: only an OpenCode that `witbitz-code` starts can use your account's keys. **Several accounts on one computer** pay for their own prompts: OpenCode names the conversation on every model call, the computer knows which account sent each prompt, and each call is billed to that account. A prompt typed straight into OpenCode on the computer is refused ("coding runs from the Spaces app"), and while one account's reply is being written another account cannot add to it. Accounts on one computer share its files and shell, so this splits the bill fairly; it is not a wall between them. ### Open Code On your phone: **Spaces → ☰ → Code**. Your sessions appear; open one, or start a new one, and type. A new computer starts on **DeepSeek V4.1 Flash · confidential**; the model chip changes it, and the choice is remembered per computer. ### The Witbitz Code page On Windows and in WSL, `witbitz-code` is managed from a page in your browser, served by `witbitz-code` itself on `127.0.0.1` and opened only through the Start-menu entry or `witbitz-code ui` (it carries a one-time key; other sites and other users of the computer cannot use it). It shows: - **whether the computer is reachable** from Code — *Online*, *Connecting…*, *Stopped* — with **Start** and **Stop**; - **Pair this computer**, **Pair again** and **Unpair**, with the QR code on the page; - **Tools for audio, video, PDFs and scans** — what is missing and an **Install them** button (a step that needs your password, such as Linux's `apt`, is shown for you to run in a terminal, never run from the page); - **Start when I log in**; - **Details** — the last lines `witbitz-code` printed, the first place to look when something is wrong; - **Quit**, which stops Witbitz Code (it has no window to close). If `witbitz-code` is already serving in another window (an earlier `setup`, say), the page says **Running in another window** and names the process; **Run it here** stops that copy and starts one under the page. On a Mac or Linux, `witbitz-code ui` (Python package) opens the same page, but it runs only while its terminal stays open. ### Other ways to get `witbitz-code` **Download it yourself and check the hash.** The file ships inside the Spaces app build, so its hash is in the app's asset manifest: ```bash curl -fsSLo witbitz-code.mjs https://app.witbitz.chat/downloads/witbitz-code.mjs curl -s https://app.witbitz.chat/assets-manifest.json | python3 -c 'import json,sys; print(json.load(sys.stdin)["files"]["downloads/witbitz-code.mjs"])' shasum -a 256 witbitz-code.mjs # (Linux: sha256sum) — the two must match node witbitz-code.mjs setup ``` **Build it from source.** The file is built with a pinned esbuild from [github.com/witbitzchat/witbitz-code](https://github.com/witbitzchat/witbitz-code): ```bash git clone https://github.com/witbitzchat/witbitz-code && cd witbitz-code npm ci && npm run build # → spaces/public/downloads/witbitz-code.mjs shasum -a 256 spaces/public/downloads/witbitz-code.mjs # (Linux: sha256sum) — compare with the manifest line above ``` Its hash matches the manifest when the repository and the live app are the same release — the repository's latest commit message names the release and its hash. **Python instead of Node?** A second, independent implementation of the same protocol is on [PyPI](https://pypi.org/project/witbitz-code/) — it is what Windows and WSL run: `pipx install witbitz-code` (Python ≥ 3.10), then `witbitz-code setup`. It pairs with the same coding keys and enforces confidential models the same way. On Windows and in WSL it runs in the background and is managed from [the page](#the-witbitz-code-page); on a Mac or Linux it runs in its terminal window, and it does not make text copies of attached documents yet. ### Step by step, without `setup` The same thing one command at a time, in the folder that holds `witbitz-code.mjs` (your home folder, if the line above downloaded it): ```bash npm install -g opencode-ai # or: curl -fsSL https://opencode.ai/install | bash node witbitz-code.mjs pair --name "my laptop" # scan the QR code with the Spaces app — this brings the computer's keys node witbitz-code.mjs serve # starts OpenCode on 127.0.0.1:4096, then the connector — leave it running ``` To start it with the computer afterwards: `node witbitz-code.mjs service install`. ## Everyday use On **Windows and in WSL**, the [page](#the-witbitz-code-page) does all of this (Start menu → Witbitz Code). The commands below are for the Node file on a Mac or Linux; the Python package takes the same commands as `witbitz-code `. | | | |---|---| | **Commands** | run them in your home folder, where the one-line install saved `witbitz-code.mjs` (a new Terminal window starts there) | | **See what is paired** | `node witbitz-code.mjs status` | | **Computer offline** | the Code page says *"my laptop is offline — start it"* and reconnects by itself when it returns | | **Several computers** | pair each one; a picker at the top of the Code list chooses which computer you are looking at | | **Remove a computer** | from the phone: Code's menu (☰) → Computers → the computer → **Remove**; or on the computer: `node witbitz-code.mjs unpair` | | **New secret** | `node witbitz-code.mjs rotate`, then restart `serve` — devices pick up the new secret on their next sync | | **Background service** | `node witbitz-code.mjs service status` (it prints where the logs are); `service uninstall` stops it starting with the computer | | **Update** | paste the install line again — it downloads the new file and runs `setup`, and the background service switches to it | | **Another port** | `node witbitz-code.mjs setup --port 4097` — for a second OpenCode, or when 4096 is someone else's; an existing pairing moves with one question, no new scan | | **Uninstall** | `node witbitz-code.mjs uninstall` — see below | ### Removing it on Windows **Settings → Apps → Installed apps → Witbitz Code → ⋯ → Uninstall.** A window lists what it will remove and asks about the rest — answer **y** to each to remove everything: it takes the computer out of your account (its coding keys are cancelled and your devices stop showing it), stops Witbitz Code, deletes `.witbitz\code`, and asks whether to also delete your project notes, **all OpenCode sessions** on the computer and OpenCode itself. Then it removes the Start-menu and log-in entries, the Settings entry, the PATH line and the `%LOCALAPPDATA%\Witbitz` folder. OpenCode's own settings in `.config\opencode` stay; delete that folder too to remove everything. The same from PowerShell, with no questions: ```powershell powershell -ExecutionPolicy Bypass -File "$env:LOCALAPPDATA\Witbitz\uninstall.ps1" --yes --remove-keys --remove-notes --remove-sessions --remove-opencode ``` A computer installed from an *Administrator* PowerShell by a version before 1.6.6 may answer *"Access is denied … pairings.json"* — run that line from a **Run as administrator** PowerShell instead. ### Removing witbitz-code on a Mac or Linux ```bash node witbitz-code.mjs uninstall ``` No `witbitz-code.mjs` any more? The install line can fetch it and run uninstall instead of setup: `curl -fsSL https://app.witbitz.chat/code-vaulted.sh | bash -s uninstall` (the regular app's: its own address). It lists what it will remove and asks first: the background service, this computer from every account it is paired with (its coding keys go back to the account first, and your devices stop showing it), `~/.witbitz/code` (the pairing secrets, the files you attached in Code, the Auto-mode log), the project-notes plugin if it is installed, and the downloaded `witbitz-code.mjs` itself. Then it asks — Enter keeps each — whether to also remove: - **keys you saved yourself** before coding keys came from the account: a Tinfoil key, and a TrustedRouter key in OpenCode's credentials; - your **project notes** in `~/.local/share/witbitz-notes` (they exist only where the notes plugin was installed); - **all OpenCode sessions** on this computer — every conversation, from Code *and* from the OpenCode app, since they share one store (`~/.local/share/opencode`). Your provider logins stay. If an OpenCode on this computer still has them open (a terminal running it, or the OpenCode app), it names it and offers to stop it first. - **OpenCode itself.** It says how OpenCode was installed and removes it that way: OpenCode's installer (`~/.opencode`, plus the `# opencode` PATH line it added to your shell's startup file — the file is kept as `.before-witbitz-uninstall`), npm (`npm uninstall -g opencode-ai`; if that needs `sudo`, it tells you the command instead of running it), or Homebrew. Open a new terminal window afterwards. Your projects always stay. If the computer is offline, uninstall still removes everything here and tells you to remove the computer from your phone (Code's menu → Computers → the computer → **Remove**) — that also cancels its coding keys, which is how you cut off a lost laptop. For scripts: `uninstall --yes`, plus `--remove-keys`, `--remove-notes`, `--remove-sessions`, `--remove-opencode` for each of those. ### In the Code section - Type **/** in the message box for your OpenCode commands and skills, plus **/undo**, **/redo** and **/compact**. Undo takes back your last turn — and, in a git project, the file changes it made; Redo brings them back until you send a new message. - When the agent **asks you a question**, or **asks permission** for a step, the card appears above the composer. A card you missed — asked while the app was closed, or on another device — comes back when you return. - The agent's **todo list** shows as one line above the conversation; tap it to see every item. - The bar shows the conversation's title and, under it, **the folder it works in** (`~` for your home folder). - A **spark** next to a session in the list means it is running a turn right now, wherever it was started. A **green dot** means activity you have not seen yet (on ☰ too, while the list is closed). What you have seen is kept on your computer, so a conversation you read on your phone is not news on your laptop. - **Long conversations** open on their newest messages; scroll up, or tap **Load earlier messages**, for the rest. Switching back to a conversation you opened before is instant. - Write in **Hebrew** and the agent narrates and answers in Hebrew. - **Steer a running turn.** While the agent works, the send button is a stop square; type (or dictate) and it turns back into the arrow, and what you send goes into the turn — the agent reads it at its next step, without stopping. - The model menu has an **Effort** row, as in a room: *Default* or a level, for models that offer them. The chip shows it. ### What the agent is doing A reply's work — its commands, file reads and edits, and what it wrote between them — collapses into **one line**. While it runs, the line says what it is doing now and for how long (*"✶ Running a command · 1m 12s"*), or *"Waiting for your approval"* when a card is up; afterwards it sums up (*"Ran 3 commands, created 5 files · 2m 35s"*). Tap it for an outline: a line per group of steps, with the agent's note before each one; tap a step for its command, output or diff. The answer itself stays outside, in full, once the reply is done. The agent works quietly. When something comes up that you should know before it finishes — a significant discovery, a change of direction, a limit it ran into, a decision where your view helps — it tells you in a short **update** that stands in the conversation, and its work carries on in a new line under it. A short task has none; a long one a few. When the agent keeps a **plan** (its todo list), the plan stands above its work as a card — open while the turn runs, with the current item in bold and finished ones ticked, and folded to *"Plan · 5 of 5"* afterwards. The agent's own first sentence about how it will go about the task stands above the work line too, and while it thinks, the line shows the last thing it thought. When the agent starts **agents** of its own (to explore a codebase in parallel, say), each one gets a card in the conversation, and the agents glyph at the top right lists them with a count. Tap one to read its own conversation, live; ‹ takes you back. ### Modes: Manual · Accept edits · Plan · Auto The chip beside the model sets how much the agent may do without asking (Shift+Tab in the message box steps through it): - **Manual** — every permission the agent asks for is a card. - **Accept edits** — file edits in the project go ahead; protected files (`.env`, keys, `.git/`) and everything else still ask. - **Plan** — the agent plans without changing anything (shown when OpenCode has a plan agent). - **Auto** — a check **on your computer** answers each ask first. Plainly harmless steps (reading files, `git status`, edits inside the project) pass at once; the rest is judged by the conversation's own model, which allows routine work, refuses what must not run — telling the agent why, so it tries something safer — and leaves anything unclear to you as a card. Nothing is ever allowed "always". Each decision is a 🛡 line under the step that asked (a refusal in red), and all of them are logged in `~/.witbitz/code/auto-log.jsonl`. It keeps working with your phone closed. ### Tools for the agent The first time a computer connects, a card at the top of the session list names the tools it is missing that coding and document work often needs — Git, ripgrep, jq, Python 3, uv, ffmpeg, Whisper, Poppler, qpdf, Tesseract, Pandoc and ImageMagick — each with a line on why. Untick what you do not want, then **Install with the agent**: it opens a *Set up this computer* conversation in your home folder in Manual mode, so every command asks. It never runs `sudo`: on Linux, the packages that need admin rights are one line on the card for you to copy and run. **Not now** is remembered for that computer; **Settings → Tools** checks again. The setup conversation tidies itself away once everything it was asked for is installed. `witbitz-code serve` also gives the agent tools of its own, on your computer: - **Media tools** (`witbitz-media`) — look at an image or a PDF page and zoom into part of it, measure, read text (OCR), compare two images, read QR codes and barcodes, read a file's metadata, find one image inside another, pull frames and scenes from a video or follow something through it, and **transcribe** audio to timestamped text with whisper.cpp — the audio never leaves the computer. A picture a tool makes reaches the model as a picture. Engines it lacks are fetched once, pinned by hash. - **Web search** (`web_search`) — the same confidential search the Spaces assistant uses, answered inside a Tinfoil enclave, with numbered sources. It needs a Tinfoil key in the media tools' environment — a cloud computer's has one; on a laptop it is not wired to the account's key yet and says so. - **A browser** (`browser_open`, `browser_navigate`, `browser_snapshot`, …) — a Chromium with its own profile (never your cookies or logins), opened only when the agent needs one. From the Code section on any paired device, **Watch** it, or **Take over** to drive it yourself while the agent waits. It needs Node.js 22 or newer. Turn it off with `witbitz-code serve --no-browser`. On your own computer the agent runs as you, so Take over pauses the agent's browser tools but is not a security boundary the way it is on a cloud computer. **Private browsing** wipes the browser's cookies, logins and history every time it opens or closes: on a cloud computer chosen when you create it (or on its card), on your own computer with the switch on its card in Code or `witbitz-code serve --private-browser` (witbitz-code 1.11 or later; 1.13 from the Windows installer or pipx). Without it, what you log into there stays, and the agent can use it afterwards — never type a password you use anywhere else. - **ast-grep** for search and rewrite by code structure rather than text, and OpenCode's **lsp** tool where a language server is set up — and the agent is asked to run the project's own checks before it calls the work done. ### Files you attach Tap **+** to attach a file (up to 5 MB each): a PDF, a Word or Excel file, an image, text. The way it reaches the agent is the way a coding agent on your own machine works — **the file is saved on your computer and the agent reads it there**: 1. `witbitz-code` saves it under `~/.witbitz/code/attachments//` (readable only by you) and puts a note in your message saying where it is. Apart from an image (which a model that can see still gets directly), OpenCode never receives the raw file — it cannot take Word or Excel files at all. 2. For a PDF, Word or Excel file it also writes a **text copy** beside it, made by [Tinfoil](https://tinfoil.sh)'s attested document reader with the **Tinfoil key your account gave this computer**. The note tells the agent to read that copy. Without a key there is no copy; the agent reads what it can and asks before converting anything. 3. The agent may read that session's folder without asking; any other folder outside your project still asks. 4. Your message shows the file as a chip — a preview for a PDF or an image. Tap it: the download glyph saves it (on a phone, the share sheet — Save to Files, AirDrop…; on a computer, a plain download), and **View text** shows the copy the agent read. Saved files are deleted with their session, or after 30 days unused. Limits: 25 MB per file, 200 MB per session, 2 GB in all. (The Python package saves files but makes no text copies yet.) ### Files a reply produces When a reply creates or changes a file — a PDF, a picture, a spreadsheet — it shows **under the answer**, without asking: a PDF's first page, the picture, the first lines of a table or a text; a Word, Excel or PowerPoint file, an archive or a recording as a chip. A **screenshot the agent takes of its browser** (ask it: "show me the page") shows the same way — it is saved in Witbitz's own folder for that session (`~/.witbitz/screens/`), never in your project. Code files show nothing here (their changes are in the steps). Tap one to see it large, with the download glyph and ✕ at the top. - A card is a file the reply **wrote** with its file tools, or one its **answer names** — not files it only read, not scratch files it mentioned while working, and never a path from your own message. At most six per reply. - Only files **inside the conversation's folder**, and never a name that looks like a secret (`.env`, keys, `.git/`). Your computer refuses anything else before it touches the disk, sends at most 20 MB, and logs each file it hands out in `~/.witbitz/code/output-log.jsonl` — as a hash of the path, not the path. - A card says *"changed since this reply"* when the file was modified later, and *"not on the computer any more"* when it is gone. ### Confidential models The model menu includes models marked **· confidential** (DeepSeek V4.1 Flash — the default — GLM 5.3 Flash, GLM 5.3, GPT-OSS 120B, Gemma 4 31B, Llama 3.3 70B). The mark is enforced on your computer, not asserted: OpenCode's TrustedRouter calls go through `witbitz-code` on `127.0.0.1`, and for these models it 1. sends the request only after **TrustedRouter's gateway proves** (Google Confidential Space, Intel TDX) that it runs its published code, and with TrustedRouter's hard floor `min_privacy: confidential` — never routed to an ordinary endpoint; 2. checks TrustedRouter's **signed receipt** for that exact request and answer: which enclave served it and that the gateway verified that enclave. A receipt that does not verify ends the step with *"not confidential: …"* — no tool runs on it; 3. reads **images and documents** a confidential model cannot see through **[Tinfoil](https://tinfoil.sh)'s attested enclave**: `witbitz-code` verifies Tinfoil's AMD SEV-SNP evidence and pins the connection to the attested key before a byte is sent, then gives the model the text. This uses the **Tinfoil key your account gave this computer** (the account of the prompt it reads for). Without a key, an image for a confidential model is **not sent anywhere**; the turn stops with a message saying so. 4. **asks once more when a proof lapses mid-answer.** TrustedRouter renews its proof of each model's enclave every 15 minutes; an answer still being written when that proof expires gets a receipt that fails the time check. Only that failure is retried — a fresh request, checked from the start — and only when every other part of the receipt holds. Words already on your screen stay; from the new answer only its actions are used, so nothing appears twice. An answer that starts close to the end of a proof is held back until its receipt verifies, so a lapse there is retried unseen. A second lapse, or any other fault, still stops the step. While a confidential model works, the working line says what it is doing even when nothing has reached the conversation yet: *"Kimi K3 is reading the conversation"* (a long conversation can take minutes before the first word), *"Writing report.pdf · 12 KB"* (a file being written is held back until its receipt verifies), *"Checking TrustedRouter's receipt"*, or *"TrustedRouter renewed its proof — asking again"*. Each call is paid with the coding keys your account gave this computer, straight to TrustedRouter and Tinfoil; Witbitz is not on this path. Other models pass through unchanged. Both versions of `witbitz-code` — the Node file and the Python package that Windows and WSL run — enforce all of this the same way. ### More than one account on one computer Each account pairs separately (run `pair` again and scan with the other account). **OpenCode has no users**: every account paired to the same OpenCode sees the same sessions, files and shell. That is fine for your own work and personal accounts. For **another person**, run a separate OpenCode — another port, ideally another operating-system user — and pair it on its own: `node witbitz-code.mjs pair --port 4097`, then `node witbitz-code.mjs serve --port 4097`. ## Troubleshooting | you see | means | do | |---|---|---| | *"… is offline — start it"* | the connector is not running (or the computer is asleep) | run `node witbitz-code.mjs setup` on it (it starts it, and offers to keep it running) | | *"OpenCode is not answering at http://127.0.0.1:4096"* | the connector runs, OpenCode does not | start OpenCode, or let `serve` start it | | *"pair your computer"* | this account has no paired computer | install on the computer — [Set it up](#set-it-up) | | the QR expires | the scan did not complete within 5 minutes | run `pair` again — nothing changed in the meantime | | the QR in the terminal looks broken or will not scan | the terminal's font draws the half-block characters badly | `WITBITZ_QR=large node witbitz-code.mjs setup` draws it the old, larger way — or open the `.svg` file it names and scan that | | *"OpenCode is not installed"* | `opencode` is not on `PATH` | run `setup` — it offers to install it | | *"… (confidential) cannot read images or files, and this computer has no Tinfoil key"* | a confidential model was sent a picture before the account's Tinfoil key arrived | wait a minute (`serve` asks for it again) or pick a model that can see | | *"On this computer, coding runs from the Spaces app"* | a prompt was typed straight into OpenCode, or the conversation's account unpaired | send it from the Code section | | *"This computer must be paired again to use your Spaces account"* | its coding keys were cancelled (Remove on a phone), or it was paired before account keys | run `pair` again | | *"… is not offered on Witbitz"* | a model outside Witbitz's list was picked in OpenCode | pick one of the models the Code section lists | | *"another account on this computer is running this conversation"* | a second account on the same computer tried to add to a reply the first one started | wait for that reply to finish | | *"This computer's Code allowance for the month is used up — it comes back on …"* | the free allowance ($1 of models a month) is spent | wait for the 1st, or see your plan at getspaces.witbitz.chat/account | | pairing says *"the free Code allowance is for one computer …"* | the free allowance covers one computer, and this account already has one | remove the other computer in Code first | | *"not confidential: TrustedRouter's receipt … did not verify"* | the proof for that answer failed; it was not used, and no step it asked for ran | send again, and tell us if it repeats — a lapsed proof is retried by itself, so this is another fault | | *"… TrustedRouter renewed its proof of the model's enclave while this answer was on its way …"* | a long answer outlived its proof twice, or its receipt had another fault too | send your message again — a fresh proof lasts 15 minutes | | *"… hasn't started answering after 60 s"* | the model is queued or unavailable (the working line says so instead when a confidential model is just reading a long conversation) | wait, stop it, or pick another model | | *" runs an older witbitz-code"* | the computer's connector predates a feature the page uses (tools, previews, the seen record) | update `witbitz-code` on that computer — see [Set it up](#set-it-up) for its system | | a reply made a file but shows no card under it | the file is outside the conversation's folder, or the computer runs an older `witbitz-code` | move it into the folder, or update `witbitz-code` | | *"Code asks before connecting"* with Hide my IP on | the relay would see this device's IP address | connect anyway on this device, or turn Hide my IP off | | *"That file is no longer on the computer"* | its session was deleted, or it was unused for 30 days | attach it again | | *"To open saved files from here, update witbitz-code"* | the computer runs an older `witbitz-code` | update `witbitz-code` — see [Set it up](#set-it-up) | | no **· confidential** models in the menu | OpenCode was already running when `serve` started, or TrustedRouter is not connected | close that OpenCode and run `node witbitz-code.mjs setup` — it connects TrustedRouter and starts OpenCode the right way | | *"Port 4096 is taken by a program that is not yours"* during setup | something else listens there — on WSL, another Linux distro's OpenCode (distros share ports) | say yes to the free port it offers; the pairing moves with it, no new scan | | *"Already running on 127.0.0.1:4096: opencode …"* during setup | an OpenCode you started yourself | let setup stop it — witbitz-code must start OpenCode itself for the confidential-model checks | | *"This computer is paired, but for OpenCode on another port"* | the pairing points at a different OpenCode port than setup used | run `setup` with that port, or `setup --port ` and say yes to moving it | | the page says **Running in another window** | another `witbitz-code` serves this computer (an earlier `setup` window, a log-in start) | **Run it here** takes it over, **Stop** stops it | | *"Witbitz Code installs for your own account - it does not need administrator rights"* | the Windows install line ran in an *Administrator* PowerShell | paste it in a normal PowerShell window | | the **Auto** chip stays grey on Windows | that computer runs a `witbitz-code` older than 1.6.5 (Auto did not accept Windows or network-share folders) | paste the Windows install line again | | **Witbitz Code (WSL …)** in the Start menu opens nothing | `witbitz-code` in WSL is older than 1.6.3 | `pipx upgrade witbitz-code`, then `witbitz-code setup` | | *"Background start is not available"* during setup | WSL without systemd, or another system | in WSL, use the Python `witbitz-code` (it starts in the background there); otherwise keep the window open, or turn systemd on in WSL (`/etc/wsl.conf`: `[boot]` `systemd=true`, then `wsl --shutdown`) and run `setup` again | ## Reference - Source: **[github.com/witbitzchat/witbitz-code](https://github.com/witbitzchat/witbitz-code)** — the connector and pairing (JavaScript and Python), the frame codec, and the relay itself (`relay/relay.mjs`). Build the download from it and its hash matches the one in the app's asset manifest for the same release (see *Other ways to get witbitz-code*). Issues and questions go there too. - Wire format, key derivation, replay protection and the relay's limits: `docs/opencode-relay.md` in the same repository.