CodeDeck+ runs sessions on any agent its agent host has a driver for, and
OpenCode is the default one: a bridge with no OpenCode
installs it when it first starts (see "Agent binaries" in
BRIDGE.md). A phone picks the agent per session from the "New
session" sheet, which lists the agents the bridge advertises in its
heartbeat; OpenCode can be removed from the machine's page like any agent
CodeDeck installed, and is then not installed again until someone asks.
The bridge never manages OpenCode's model access itself — the OpenCode driver
(packages/agent-host/src/drivers/opencode/) talks to a running
opencode serve HTTP server through @opencode/client, OpenCode 2.x's own
client. There are two ways to give it that server:
Run opencode serve yourself (on the same machine, another machine, or
wherever), and tell the bridge its URL:
codedeck-bridge run --opencode-server-url http://127.0.0.1:4096
# or: CODEDECK_OPENCODE_SERVER_URL=http://127.0.0.1:4096 codedeck-bridge run
# or in config.json: { "openCodeServerUrl": "http://127.0.0.1:4096" }This is the right choice when you already run OpenCode for other purposes
(e.g. driving its own TUI), or when the bridge and the OpenCode server live on
different machines. Precedence is the same as every other bridge setting:
--opencode-server-url flag > CODEDECK_OPENCODE_SERVER_URL env >
openCodeServerUrl in config.json.
Without a server URL, the bridge spawns opencode serve itself once
OpenCode is installed, and shuts it down cleanly when the bridge stops.
Nothing needs setting: this is the simplest option when the bridge runs in a
container (the same container runs OpenCode too, with nothing else to
deploy) or as a plain local process on a machine you control.
Settings for this mode:
--opencode-path <path>/CODEDECK_OPENCODE_PATH/"openCodePath"— explicit path to theopencodebinary. Otherwise the agent host looks for it onPATHand in a few well-known global-install locations (mirroring how it resolvesclaude), and when there is none it installs it: the@opencode/cli-<os>-<arch>build pinned inpnpm-lock.yaml(thebaselinevariant on x64, which needs no AVX2), downloaded in the background into<home>/agents/. The phone shows it as installing meanwhile, and new sessions start on it once it is ready.CODEDECK_OPENCODE_PORT/"openCodePort"(env/config-file only, no flag — a rarely hand-typed knob) — fixes the port instead of leaving the choice to OpenCode (which takes 4096 when it is free). Useful if you also want to point OpenCode's own TUI at the same running instance for debugging.
The embedded server always binds to 127.0.0.1 only — it is spawned
exclusively for the bridge's own use and is never configurable to listen on
any wider interface — and is guarded by a password made for each start
(OPENCODE_PASSWORD), which OpenCode 2 requires before it will start at all.
It runs with self-updating off (its version is the one pnpm-lock.yaml
pins). Web search is offered to every model rather than only OpenCode's own
providers': 2.x gates it behind a connected search provider with no keyless
default of its own, so the driver names the one provider its list serves
without a key.
The driver speaks OpenCode 2's API and nothing older. A binary from the 1.x
line will not work with it — set CODEDECK_OPENCODE_PATH to a 2.x install if
the machine has an older one on PATH.
If an installed opencode fails to come up, the agent host logs one
actionable line and reports OpenCode as unavailable (a session on it fails
with that reason). If the install fails, the phone shows OpenCode as failed
with the reason and offers Retry. Either way, OpenCode never blocks the other agents'
sessions.
A server URL wins over everything in this mode: with one set, nothing is installed or started.
OpenCode knows about 230 providers. The phone connects them on the machine's page: OpenCode → AI providers → Connect a provider. Pick one, then either type its API key or sign in to a subscription. This goes through OpenCode's own API, so it takes effect on the running server at once — the provider's models join the list, with no restart and no session cut.
- A key is kept in OpenCode's database on the machine; the phone does not keep it, and nothing sends it back. A provider can hold several keys; the page marks the one in use and switches between them.
- A device sign-in (GitHub Copilot, OpenCode Console for Zen/Go, xAI) shows a code: open the sign-in page on any device and enter it. OpenCode watches the provider itself; the page says when it is done.
- A sign-in that returns to the machine (Sign in with ChatGPT, GitLab) sends the browser back to a listener on the machine's loopback, which a phone cannot open. After signing in, copy the address the browser fails to load and paste it on the page: the machine opens it itself. Only that sign-in's own address is accepted, so nothing else on the machine can be reached this way.
The environment still counts: whatever reaches the opencode process (the
bridge's own environment, for the server it starts; or however you started
the external server) is what OpenCode sees, including provider variables like
ANTHROPIC_API_KEY. Such a provider shows as connected "from the machine's
environment", and only the machine can change it.
Under Docker the image redirects OpenCode's config and auth storage under
/data, the volume the bridge's identity lives in, so keys and sign-ins
survive container recreation. opencode auth login on the machine
(docker compose exec codedeck-bridge opencode auth login) still works and
shows on the phone the same way.
A provider profile made on the phone for OpenCode (any OpenAI-compatible endpoint or gateway) adds the endpoint's models to OpenCode's model list, beside every provider it already has; a session picks one like any other model. OpenCode's own catalog places each profile: an endpoint it knows (DeepSeek's, OpenRouter's) signs in to that provider, and a gateway's models get the catalog's limits, tool calls, image input and reasoning levels. A profile whose name one of OpenCode's providers already has is refused, with the reason.
OpenCode reads its providers only when it starts, so a changed list restarts the bridge's server: its sessions end and the bridge resumes each on the new server, where OpenCode still has the conversation (the transcript shows a restart notice). An external server gets no profiles.
OpenCode retries a provider that fails, waiting longer each time (about a minute in all). Each retry shows in the transcript with the provider's reason, so a model that is down can be switched rather than waited out.
A permission card offers "Always allow "" in this project" when
OpenCode offers to save a rule for the call: the pattern is OpenCode's own
(git push * for git push origin main), usually wider than the call.
Choosing it answers OpenCode's always, and OpenCode saves the rule for the
project in its database. Every later session in that project — whatever its
mode, Ask included — runs matching calls without asking, and the rule
survives a restart of the server. The machine's page on the phone lists them
under OpenCode → Saved permissions, by project, and removes them (OpenCode then
asks again).
- No connection method that reads the machine's own setup (Amazon Bedrock's and Google Vertex's "external" credentials, AWS profiles and the like): those are configured on the machine. Their key methods are offered.
- No support for multiple simultaneous OpenCode servers.
- No change to the wire — this only controls how the OpenCode driver finds its server.