This guide is the deep reference for the people who operate, deploy, extend, and reason about opencode: ops engineers running it as a server or in CI, architects evaluating it, and developers building on its SDK, plugins, and APIs. It covers the system architecture, the monorepo layout, configuration in full, the security model, operational procedures, and the internals needed to understand — or reproduce — how opencode works.
For day-to-day usage (installing, the TUI, commands, providers), see the companion User Guide.
Note
opencode is a large monorepo. The shipped end-user product is the
opencodeCLI/TUI agent, but the repository also contains the hosted console (opencode Zen), a desktop app, an editor extension, an SDK, a plugin framework, and supporting infrastructure. This guide describes the whole system, with emphasis on the parts an operator or integrator must understand.
1. Executive Summary
opencode is a provider-agnostic AI coding agent. Architecturally it separates concerns into three layers:
- A core domain and runtime (
@opencode-ai/core) that owns sessions, messages, configuration, providers, the database, permissions, the event/sync system, and the agent execution loop. - A local HTTP server that exposes the runtime as an API. Every front end — the TUI, the
runcommand, the desktop app, the editor extension, the web UI, and ACP clients — is a client of this server, even when the server runs in-process. - Clients and surfaces: the terminal UI, the CLI commands, the SDK, the GitHub Action, and the hosted console.
Key architectural properties an operator should internalize:
- Server-centric. Even the bare
opencodeTUI boots an in-process server and talks to it over the SDK. This is whyserve,web,--attach, and ACP all share one code path. - Local-first storage. Sessions, messages, and parts live in a local SQLite database (via Drizzle ORM). Auth and config are local files. Nothing leaves the machine except calls to the chosen model provider and (opt-in) session sharing.
- Effect-TS throughout. The codebase is built on Effect — services, layers, typed errors, and structured concurrency. Understanding this is essential for reading or extending the code.
- Schema-driven config and events. Configuration and event payloads are validated against Effect
Schemadefinitions; invalid config degrades gracefully rather than crashing. - Event-sourced sessions. Session mutations flow through a sync-event system that records and replays events, enabling multi-device session sync with a single writer.
| Attribute | Value |
|---|---|
| Current version | 1.16.0 |
| Primary language | TypeScript |
| Runtime | Bun |
| Monorepo tooling | Turborepo, Bun workspaces |
| Local storage | SQLite (Drizzle ORM) |
| Core framework | Effect-TS |
| CLI framework | yargs |
| License | MIT (most packages) |
| Hosted backend | Cloudflare Workers, deployed via SST |
2. System Architecture
2.1 High-level component diagram
┌──────────────────────────────────────────┐
You ── terminal ──► │ CLI (yargs) ──► TUI (OpenTUI/Solid) │
│ │ │ │
│ ▼ ▼ │
│ @opencode-ai/sdk ──► HTTP server │
└──────────────────────────┼───────────────┘
│ (in-process or remote)
▼
┌─────────────────────── @opencode-ai/core ───────────────────────┐
│ Config Sessions Messages Permissions Providers Plugins │
│ Agents Commands Skills MCP Tools Sync/Bus │
│ │ │ │
│ ▼ ▼ │
│ SQLite (Drizzle) model providers (AI SDK)│
└───────────────────────────────────────────────────────────────────┘
Other surfaces sharing the same server/core:
• opencode run (non-interactive client)
• opencode serve / web (headless / browser)
• opencode acp (Agent Client Protocol over stdio)
• desktop app (Tauri/Electron shell around the server)
• editor extension (VS Code), SDK consumers
• GitHub Action (runs the agent in CI)
2.2 The request/agent flow
A single prompt flows through the system like this:
- A client (TUI,
run, SDK) callssession.prompt(orsession.command) on the server via the SDK. - The server resolves the instance context (project root, directory, config, auth) for the request — keyed off the working directory, so one server can serve many projects.
- The session runner builds the model request: system prompt (from the agent + project instructions), conversation history, available tools (built-ins + MCP + skills), and the resolved model/provider/variant.
- The model streams back text, reasoning, and tool calls. Tool calls run against the permission system; anything not allowed raises a permission request (answered by the client, or auto-rejected in non-interactive mode).
- Each step emits events (
message.updated,message.part.updated,permission.asked,session.status, …) over the bus; clients subscribe and render them. Mutating events also flow through the sync system and persist to SQLite. - When the session goes idle, the run completes.
2.3 Instance context and multi-project serving
opencode does not assume a single global project. Each request carries (or resolves) an instance context containing the directory, the project's worktree root, and that project's merged configuration. The headless server reads the target directory from an x-opencode-directory header, so a single opencode serve process can handle requests for many different projects/directories concurrently — each with its own config, agents, and plugins.
2.4 Events, the bus, and sync
opencode has two related eventing concepts:
- Bus events — the general pub/sub used across the runtime and exposed to clients (
Bus.publish/Bus.subscribe, surfaced to SDK consumers asclient.subscribe("session.updated", …)). - Sync events — a lower-level, event-sourced layer for session replayability. Sync events carry
type,id,seq,aggregateID, anddata, are totally ordered by a monotonic sequence id (single-writer model), and automatically re-publish as bus events for backward compatibility. Projectors apply sync events to perform the actual DB mutations.
This design lets one device write to a session while other devices replay the event log to stay in sync, without distributed clocks or causal ordering.
Note
The sync system was introduced to be fully backward compatible — existing
Busconsumers and SDK clients continue to work unchanged. The design rationale lives inpackages/opencode/src/sync/README.mdand is worth reading before touching session persistence.
2.5 Providers and models
Models are resolved through the AI SDK (ai, @ai-sdk/*). The catalog of providers and models comes from models.dev (cached locally; refreshable). Provider credentials are resolved from stored auth (auth.json), environment variables, plugin-provided auth methods, and org-managed config (for Zen organizations). The provider layer also normalizes errors across providers — notably detecting context-overflow errors from a wide range of providers (Anthropic, OpenAI, Google, Bedrock, Groq, OpenRouter, DeepSeek, Ollama, Mistral, and more) by pattern-matching their distinct error messages, and translating gateway 401/403 HTML responses into actionable re-auth guidance.
3. Repository Structure
opencode is a Bun + Turborepo monorepo. The packages/ directory holds ~30 workspaces; the most important for operators and integrators:
| Package | Role |
|---|---|
packages/opencode |
The end-user CLI/TUI app. Commands, TUI, server wiring, MCP/plugin/provider glue. |
packages/core (@opencode-ai/core) |
The domain and runtime: sessions, config, database, providers, events, permissions, skills, installation, git. The largest and most important package. |
packages/cli |
A general CLI framework used by parts of the toolchain. |
packages/llm |
LLM routing, provider protocols, schema, and transport. |
packages/sdk (@opencode-ai/sdk) |
The generated TypeScript SDK and client (v1 and v2) for the HTTP API. |
packages/server |
Server handlers/middleware building blocks. |
packages/plugin (@opencode-ai/plugin) |
The plugin framework and public hook types. |
packages/app |
The front-end app (SolidJS) used by desktop/web surfaces. |
packages/desktop |
Desktop shell (Tauri + Electron, multi-channel: dev/beta/prod). |
packages/ui |
Shared UI components, themes, icons. |
packages/web |
The marketing/docs site (Astro). |
packages/console |
The hosted console (opencode Zen): SolidStart app, core, functions, mail, deployed to Cloudflare. |
packages/enterprise |
Enterprise features (e.g. self-hosted share storage). |
packages/stats |
Usage/stats site and backend. |
packages/slack |
Slack integration. |
packages/identity |
Identity helpers. |
packages/effect-drizzle-sqlite, packages/effect-sqlite-node |
Effect-flavored SQLite/Drizzle integration libraries. |
packages/http-recorder |
HTTP record/replay used for deterministic tests. |
sdks/vscode |
The VS Code extension. |
github/ |
The GitHub Action entry point. |
infra/ |
SST infrastructure-as-code (infra/app.ts, console.ts, etc.). |
Repository-root tooling: turbo.json (task graph), bunfig.toml (Bun install policy, including a 3-day minimum release age for new dependencies), .oxlintrc.json (oxlint config tuned for Effect + SolidJS), and sst.config.ts (cloud deployment).
Note
bunfig.tomlenforcesexact = trueandminimumReleaseAge = 259200(3 days) for newly resolved dependencies — a supply-chain hardening measure that delays adoption of freshly published versions. A short excludes list covers fast-moving first-party/UI packages.
4. Technology Stack
| Layer | Technology |
|---|---|
| Runtime | Bun (standalone binary for curl/brew/scoop installs) |
| Language | TypeScript |
| Core framework | Effect-TS (services, layers, typed errors, Schema, structured concurrency) |
| CLI | yargs, @clack/prompts (interactive prompts) |
| TUI | OpenTUI (@opentui/core, @opentui/keymap, @opentui/solid) + SolidJS |
| Local DB | SQLite via Drizzle ORM (effect-drizzle-sqlite) |
| Model access | Vercel AI SDK (ai, @ai-sdk/anthropic, @ai-sdk/openai, @ai-sdk/google, @ai-sdk/amazon-bedrock, @ai-sdk/github-copilot, gateway, …) |
| Model catalog | models.dev |
| MCP | @modelcontextprotocol/sdk (local + remote/streamable-HTTP, OAuth) |
| GitHub | @octokit/rest, @octokit/graphql, @actions/* |
| Config parsing | jsonc-parser (comment-preserving edits), gray-matter (frontmatter) |
| Desktop | Tauri + Electron + Vite |
| Web/console | Astro (web), SolidStart (console), Cloudflare Workers |
| IaC / deploy | SST on Cloudflare (home), AWS (lake/SES/Bedrock), PlanetScale, Honeycomb, Stripe |
| Lint | oxlint (type-aware) |
5. Requirements
5.1 Client (developer machine)
| Item | Requirement |
|---|---|
| OS | macOS, Linux, Windows |
| Terminal | 256-color, mouse-capable |
| Disk | Space for sessions DB, snapshots, cache (grows with usage) |
| Network | Outbound HTTPS to the chosen model provider(s) and models.dev |
| Git | Recommended; required for project config, the GitHub agent, and opencode pr |
sqlite3 |
Optional; only for the interactive opencode db shell |
5.2 Server / headless
To run opencode serve or opencode web for others to use:
| Concern | Requirement |
|---|---|
| Auth | Set OPENCODE_SERVER_PASSWORD (and OPENCODE_SERVER_USERNAME) before exposing beyond localhost |
| Network | Bind to 0.0.0.0 only behind a trusted network or reverse proxy with TLS |
| Provider creds | The server process needs provider credentials (env vars or auth.json) |
5.3 GitHub Action
| Item | Requirement |
|---|---|
| GitHub App | opencode-agent installed on the repo/org |
| Workflow permissions | id-token: write, contents: read, pull-requests: read, issues: read |
| Secrets | The provider API key(s) referenced by the workflow (or AWS OIDC for Bedrock) |
| Runner | ubuntu-latest (as generated) |
6. Configuration Mastery
opencode has three configuration surfaces, each with its own file, schema, and precedence rules:
opencode.json/opencode.jsonc— the main agent/server config ($schema: https://opencode.ai/config.json).tui.json— terminal-UI-only settings ($schema: https://opencode.ai/tui.json).- Managed config — system/MDM-deployed config for centrally managed installs.
6.1 Config directories and discovery
opencode resolves config directories by walking from the working directory up to the project root, plus the global config dir and any OPENCODE_CONFIG_DIR:
| Location | Purpose |
|---|---|
| Global config dir | User-wide defaults (opencode.json, tui.json, auth.json) |
<repo>/opencode.json (walked up to root) |
Project config |
<dir>/.opencode/opencode.json (walked up) |
Project config in a dedicated folder; also holds agent/, command/, plugin/ |
OPENCODE_CONFIG |
Explicit config file override |
OPENCODE_CONFIG_DIR |
Additional config directory |
OPENCODE_CONFIG_CONTENT |
Inline JSON config |
The global config directory follows platform conventions (XDG on Linux). opencode also resolves data, cache, and state directories separately (used by uninstall).
6.2 Merge precedence (opencode.json)
Sources are merged in this order; later wins, except arrays like instructions which are concatenated and de-duplicated:
- Remote well-known config (for
wellknownauth providers) - Global config dir
OPENCODE_CONFIGfile- Project config files (root → closest)
.opencodedirectory configsOPENCODE_CONFIG_CONTENT- Active organization (Zen) config
- System managed config dir
- macOS MDM managed preferences (override everything)
6.3 opencode.json key reference
| Key | Type | Description |
|---|---|---|
$schema |
string | https://opencode.ai/config.json (auto-seeded) |
model |
string | Default model provider/model |
provider |
object | Per-provider overrides: custom name, options, endpoints, npm package |
enabled_providers / disabled_providers |
string[] | Allow/deny providers |
agent |
object | Inline agent definitions (merged with agent/ files) |
mode |
object | Legacy "mode" agents (treated as primary agents) |
command |
object | Inline command definitions (merged with command/ files) |
mcp |
object | MCP servers (local command or remote url + optional oauth) |
permission |
object | Per-capability actions (allow/deny/ask) |
tools |
object | Tool enable/disable booleans (mapped onto permission; write/edit/patch → edit) |
instructions |
string[] | Extra instruction files/snippets (concatenated across sources) |
plugin |
array | Plugin specs (npm names or paths; [spec, options] tuples allowed) |
share |
string | auto to auto-share sessions |
autoshare |
boolean | Legacy; normalized to share: "auto" |
username |
string | Transcript display name (defaults to system username) |
compaction |
object | { auto, prune } automatic context management |
Important
opencode rejects unrecognized top-level keys in
opencode.jsonwith anInvalidErrorthat names the offending key(s). It also validates against the schema and produces precise JSONC error locations (line/column) on parse failure. This strictness is intentional — typos fail loudly rather than being silently ignored.
6.4 tui.json key reference
tui.json is loaded and merged separately (global → OPENCODE_TUI_CONFIG → project tui.json root-first → .opencode dirs). Unlike opencode.json, invalid tui.json degrades gracefully (logged and skipped) so a bad TUI config never blocks startup.
| Key | Type | Default | Description |
|---|---|---|---|
theme |
string | opencode |
Theme name |
keybinds |
object | — | Per-action overrides; unknown keys logged and dropped |
leader_timeout |
int (ms) | 2000 |
Leader-sequence timeout |
mouse |
boolean | true |
Mouse capture |
scroll_speed |
number | — | ≥ 0.001 |
scroll_acceleration |
object | — | { enabled } |
diff_style |
auto|stacked |
auto |
Diff rendering style |
prompt.max_height / prompt.max_width |
int / int|auto |
— | Prompt sizing |
attention.enabled |
boolean | false |
Master switch for attention notifications |
attention.notifications |
boolean | true |
Desktop notifications |
attention.sound |
boolean | true |
Sound on events |
attention.volume |
number 0–1 | 0.4 |
Volume |
attention.sound_pack |
string | opencode.default |
Sound pack |
attention.sounds |
object | — | Per-event sound files: default, question, permission, error, done, subagent_done |
plugin / plugin_enabled |
array / object | — | TUI plugins and enable flags |
Note
opencode auto-migrates legacy
theme/keybinds/tuikeys out ofopencode.jsonintotui.jsonon first run, writing a.tui-migration.bakbackup. Thetuikey (deprecated) is still flattened if present.
6.5 Managed / centrally controlled config
For fleet deployments, opencode reads a managed config directory that overrides user config:
| Platform | Managed config path |
|---|---|
| macOS | /Library/Application Support/opencode |
| Windows | %ProgramData%\opencode |
| Linux | /etc/opencode |
It places opencode.json/opencode.jsonc there and they merge in with global scope. On macOS, opencode also reads MDM-deployed managed preferences (.mobileconfig, domain ai.opencode.managed) via plutil, and those override everything else. Use this to enforce provider allow-lists, disable sharing, or pin models across an organization's managed devices.
6.6 Variable substitution
Both config surfaces support:
{env:VAR}— environment variable interpolation{file:path}— inline a file's contents (relative to the config file,~/for home; commented lines are skipped)
6.7 Environment variable reference
| Variable | Effect |
|---|---|
OPENCODE_CONFIG |
Load this config file |
OPENCODE_CONFIG_DIR |
Extra config directory |
OPENCODE_CONFIG_CONTENT |
Inline JSON config |
OPENCODE_DISABLE_PROJECT_CONFIG |
Ignore project config |
OPENCODE_PERMISSION |
Inline JSON permission overrides |
OPENCODE_TUI_CONFIG |
Explicit tui.json path |
OPENCODE_PURE |
Run without external plugins |
OPENCODE_SERVER_PASSWORD / OPENCODE_SERVER_USERNAME |
Server/attach auth |
OPENCODE_DB |
Override the SQLite database path |
OPENCODE_DISABLE_AUTOUPDATE / OPENCODE_ALWAYS_NOTIFY_UPDATE |
Update behavior |
OPENCODE_DISABLE_AUTOCOMPACT / OPENCODE_DISABLE_PRUNE |
Context management |
OPENCODE_DISABLE_SHARE |
Disable session sharing |
OPENCODE_DISABLE_MODELS_FETCH |
Don't fetch the models catalog |
OPENCODE_DISABLE_MOUSE / OPENCODE_DISABLE_TERMINAL_TITLE |
TUI behavior |
OPENCODE_GIT_BASH_PATH |
Git Bash path (Windows) |
OPENCODE_MODELS_URL / OPENCODE_MODELS_PATH |
Override model catalog source |
OPENCODE_WORKSPACE_ID |
Workspace targeting (experimental) |
OPENCODE_FAKE_VCS |
Test/CI VCS stubbing |
OTEL_EXPORTER_OTLP_ENDPOINT / OTEL_EXPORTER_OTLP_HEADERS |
OpenTelemetry export |
OPENCODE_EXPERIMENTAL + OPENCODE_EXPERIMENTAL_* |
Opt into experimental features |
Experimental flags include OPENCODE_EXPERIMENTAL_FILEWATCHER, OPENCODE_EXPERIMENTAL_WORKSPACES, OPENCODE_EXPERIMENTAL_SESSION_SWITCHER, OPENCODE_EXPERIMENTAL_REFERENCES, and OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT (default on for Windows).
7. Security
opencode's security model spans local execution, server exposure, provider/MCP credentials, and content handling.
7.1 The permission system
The agent's ability to act is gated by permissions, keyed by capability:
bash, read, edit (write/edit/patch), glob, grep, webfetch, task, todowrite, websearch, lsp, skill
Plus interaction permissions like question, plan_enter, plan_exit.
Each permission resolves to allow, deny, or ask. Sources, in increasing authority: agent frontmatter → tools shorthand → permission config → OPENCODE_PERMISSION env → managed config. In interactive mode, gated actions raise a permission.asked event for the user to approve. In non-interactive run, question/plan_enter/plan_exit are denied (so the run never blocks) and other un-allowed permissions are auto-rejected and logged — unless --dangerously-skip-permissions is passed, which auto-approves.
Caution
--dangerously-skip-permissions(and broadly allowingedit/bash) lets the agent modify files and execute shell commands without confirmation. Restrict these to disposable, sandboxed environments. Per-agent permission maps (e.g. a read-onlyplanagent) are the safe way to constrain capability.
Warning
Community reports (e.g. issues around read-before-edit) note that some tool-flow invariants are enforced at the prompt level rather than the code level. Treat the agent as a privileged actor: scope its filesystem and shell access through permissions and run untrusted automation in a container or VM.
7.2 Server authentication
serve, web, and acp start the HTTP server. If OPENCODE_SERVER_PASSWORD is unset, the server is unsecured and opencode prints a warning. Basic auth uses OPENCODE_SERVER_PASSWORD and OPENCODE_SERVER_USERNAME (default username opencode). Clients attach with --password/--username or the matching env vars.
Caution
Never bind the server to
0.0.0.0or expose it to a network without a password and TLS. The server can read your filesystem and run commands. Put it behind a reverse proxy that terminates TLS and enforces auth, or restrict it to localhost / a private network (e.g. a VPN).
7.3 Credential storage
| Credential | Storage |
|---|---|
| Provider API keys / OAuth tokens | auth.json in the data directory |
| MCP OAuth tokens / client registrations | Stored per-server; inspectable via opencode mcp debug |
| Zen account/org tokens | Pulled at runtime; console token set to OPENCODE_CONSOLE_TOKEN |
Provider env vars (ANTHROPIC_API_KEY, etc.) are read directly and never written to disk by opencode.
7.4 MCP server trust
MCP servers extend the agent with tools and run with the agent's trust. Remote servers default to OAuth-capable; opencode mcp auth runs the flow, and opencode mcp debug inspects connectivity and token state (including WWW-Authenticate discovery). Only add MCP servers you trust — a malicious server can exfiltrate context or take actions through its tools.
7.5 Session sharing
Sharing publishes a session transcript (including file snippets and tool output) to a public URL (opencode.ai/s/<id>). Controls:
share: "auto"/--shareenable it; otherwise it is off.OPENCODE_DISABLE_SHAREdisables it entirely (org-enforceable via managed config).opencode export --sanitizeproduces a redacted JSON copy (file text, paths, tool I/O, titles, directories all redacted) for safe handoff.
Caution
Review sessions before sharing. Transcripts can contain secrets, proprietary code, and internal paths. For untrusted recipients, prefer a sanitized export over a live share. Enterprises can self-host share storage (see
packages/enterprise, configured withOPENCODE_STORAGE_*variables) instead of using the public service.
7.6 GitHub agent security
The Action checks that the triggering user has write/admin permission before acting, runs with least-privilege workflow permissions, exchanges a short-lived OIDC token for a scoped app token, and revokes the app token when done. It checks out PR branches (including forks) into isolated branches and never persists checkout credentials (persist-credentials: false). Trigger phrases (/oc, /opencode) are configurable via MENTIONS.
7.7 Enterprise storage adapters
For self-hosted deployments, the enterprise package supports pluggable share/storage backends configured through environment variables: OPENCODE_STORAGE_ADAPTER, OPENCODE_STORAGE_BUCKET, OPENCODE_STORAGE_REGION, OPENCODE_STORAGE_ACCESS_KEY_ID, OPENCODE_STORAGE_SECRET_ACCESS_KEY, OPENCODE_STORAGE_ACCOUNT_ID. This lets organizations keep shared session data inside their own S3-compatible storage.
8. Operations
8.1 Running a headless server
OPENCODE_SERVER_PASSWORD='strong-secret' \
opencode serve --hostname 127.0.0.1 --port 4096
opencode server listening on http://127.0.0.1:4096
Front it with a reverse proxy (nginx/Caddy/Traefik) for TLS. Clients connect with opencode run --attach https://your-host --password ... --dir /path/on/server.
8.2 Logs
opencode writes structured logs to a log file; print to stderr and raise verbosity with global flags:
opencode --print-logs --log-level DEBUG run "..."
Levels: DEBUG, INFO, WARN, ERROR. Local/dev installs default to DEBUG; released builds to INFO. On a fatal error, opencode prints the log file path. OpenTelemetry export is available via OTEL_EXPORTER_OTLP_ENDPOINT/OTEL_EXPORTER_OTLP_HEADERS.
8.3 The database
Sessions, messages, and parts persist in SQLite (Drizzle ORM), with migrations under packages/core/migration. Inspect it:
opencode db path
opencode db "SELECT id, title, time_updated FROM session ORDER BY time_updated DESC LIMIT 10"
opencode db "SELECT count(*) FROM message" --format json
Override the path with OPENCODE_DB. The schema evolves via timestamped migration directories; treat the DB as opencode-owned and prefer read-only access.
8.4 Backups and data locations
opencode separates data, cache, config, and state directories (visible in the uninstall summary). Back up:
- The data directory (sessions DB, snapshots,
auth.json). - The config directory (
opencode.json,tui.json, agents, commands, plugins).
opencode uninstall --keep-config --keep-data --dry-run is a quick way to enumerate these locations and their sizes per platform.
8.5 Upgrades and rollback
opencode upgrade # latest, via the detected method
opencode upgrade 1.16.0 # pin a version (rollback by installing an older one)
opencode upgrade --method npm # force a method
Disable auto-update checks with OPENCODE_DISABLE_AUTOUPDATE=true. For fleets, pin versions through your package manager and managed config.
8.6 Stats and cost monitoring
opencode stats aggregates per-user token usage and cost (with --days, --project, --models, --tools). For org-level reporting, the hosted console/stats backend aggregates usage server-side. Note that stats reads every session and warns when a dataset exceeds 1,000 sessions, as aggregation may take a while.
9. Provider Integration (Deep Dive)
9.1 Resolution order
For a given provider, credentials resolve from: stored auth.json → provider env vars → plugin-supplied auth → Zen org-managed config. Models and provider metadata come from the models.dev catalog (cached; --refresh or OPENCODE_DISABLE_MODELS_FETCH/OPENCODE_MODELS_URL/OPENCODE_MODELS_PATH to control).
9.2 Custom and self-hosted providers
Add a provider opencode doesn't know via opencode auth login → Other, then configure it under provider in opencode.json (npm package, base URL, options, headers). OpenAI-compatible endpoints (local servers like vLLM, LM Studio, Ollama, llama.cpp; gateways like Cloudflare AI Gateway, Vercel AI Gateway) are supported this way. The provider error layer already recognizes overflow/error shapes from these engines.
9.3 Provider-specific notes
| Provider | Notes |
|---|---|
| Amazon Bedrock | Bearer token (AWS_BEARER_TOKEN_BEDROCK) or AWS credential chain (AWS_PROFILE, AWS_REGION, IAM/IRSA). Configure profile/region/endpoint in config. GitHub agent uses OIDC. |
| GitHub Copilot | OAuth; uses a dedicated compatibility path (packages/core/src/github-copilot). |
| OpenAI | OAuth (ChatGPT Plus/Pro) or API key. Optional Responses-API WebSocket transport (enabled on dev channels; OPENCODE_EXPERIMENTAL_WEBSOCKETS=true on stable). |
| Cloudflare AI Gateway | CLOUDFLARE_GATEWAY_ID, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN. |
| Snowflake Cortex | Account identifier + programmatic access token (PAT). |
| opencode (Zen) | Single key for many models; org-managed providers/config when authenticated to an org. |
9.4 Model variants and output limits
Models expose variants (provider-specific reasoning effort, e.g. minimal/high/max) selectable with --variant or the TUI's variant cycle. The provider transform layer caps output tokens (a 32k default in the transform path) and handles multi-modal inputs (image/audio/video/pdf) based on each model's declared modalities.
10. Extending opencode
10.1 Agents
Agents are Markdown-with-frontmatter files in agent//agents/ (global or .opencode/). Frontmatter: description (for delegation), mode (primary/subagent/all), permission map, optional model. The body is the system prompt. opencode agent create can scaffold one (optionally fully non-interactive). Use subagents for delegated, capability-restricted tasks (e.g. a read-only reviewer).
10.2 Commands
Commands are Markdown templates in command//commands/. Templates support @file inlining and !`shell` substitution. Run via the command palette, --command, or session.command over the API.
10.3 Skills
opencode supports skills (a skill permission/tool exists, with skill fixtures in tests). Skills package reusable capabilities the agent can invoke; they are discovered alongside other project config.
10.4 Plugins
Plugins are npm packages or local .opencode/plugin/*.{ts,js} files. They expose hooks via @opencode-ai/plugin (e.g. auth to add a provider's login methods, shell.env to inject environment for spawned shells, and TUI hooks/themes). opencode plugin <module> installs an npm plugin and patches the right config file based on the plugin's declared entrypoints (./tui, ./server, oc-themes). opencode auto-installs the @opencode-ai/plugin dependency into config directories that declare plugins.
10.5 The SDK and HTTP API
@opencode-ai/sdk (v1 and v2) is a generated client over the server's OpenAPI. Create a client and drive sessions:
import { createOpencodeClient } from "@opencode-ai/sdk/v2"
const client = createOpencodeClient({ baseUrl: "http://localhost:4096" })
const session = await client.session.create({ title: "automation" })
await client.session.prompt({
sessionID: session.data!.id,
parts: [{ type: "text", text: "Summarize the repo" }],
})
client.subscribe("message.part.updated", (e) => { /* stream */ })
The OpenAPI spec (with code samples) is generated by opencode generate. Subscribe to the event stream (client.event.subscribe() / client.subscribe(...)) for live updates. The SDK targets the directory via the x-opencode-directory header so one server can serve many projects.
10.6 ACP
opencode acp speaks the Agent Client Protocol over stdio, letting ACP-aware editors use opencode as their agent backend. Internally it boots the same server and bridges it to the ACP connection.
11. The Hosted Console (opencode Zen) — Operator Notes
The packages/console workspace and infra/ deploy opencode's hosted backend. While most operators only consume Zen, the architecture is relevant when self-hosting or evaluating data flows.
- Deployment: SST (
sst.config.ts) with Cloudflare as the home provider; AWS, Stripe, PlanetScale, Honeycomb, andrandomas additional providers. Console apps are SolidStart on Cloudflare; the web/marketing site is Astro. - Capabilities: accounts and organizations, API keys, billing (Stripe — subscriptions like "Go"/"Black"/"Lite", reloads, coupons, referrals), the Zen model gateway (
ZEN_MODELS*,ZEN_LIMITS,ZEN_*_PRICE), usage stats, and support tooling (Discord/Feishu/Salesforce integrations). - Storage: Cloudflare R2 buckets (auth/zen/enterprise data), KV (auth storage), a relational database (PlanetScale), and Honeycomb for observability.
- CLI surface:
opencode console login|logout|switch|orgs|openmanages account/org from the terminal; the active org's config and provider access merge into the local config at runtime.
Note
Zen secrets and pricing are managed entirely server-side via SST secrets (see the generated
sst-env.d.tsresource list). End users only ever hold a Zen API key. Organizations can centrally manage provider access and config that flows down to members' CLIs.
12. Code Quality & Conventions
- Effect-TS everywhere. Services are defined with
Context.Service, wired withLayers, and errors are typed (Schema.TaggedErrorClass). Reading the code requires comfort withEffect.gen,yield*, and layer composition. - Schema-first. Config, events, provider auth methods, and API payloads are Effect
Schemas, giving validation, typed decode, and generated docs/SDK types from one source of truth. - Graceful degradation.
opencode.jsonerrors fail loudly (strict, with precise locations);tui.jsonand plugin/agent load errors are logged and skipped so the app still starts. - Comment-preserving config edits.
jsonc-parsermodify/applyEditsis used so programmatic config changes (MCP add, plugin install, schema seeding) keep user comments and formatting. - Instance-scoped state. Per-project state (config, providers) is built per instance context, enabling multi-project serving.
- Linting. oxlint with type-aware rules, tuned for Effect's
function*/generator patterns and SolidJS reactivity. - Dependency hygiene. Bun
minimumReleaseAge(3 days) plus exact pinning to reduce supply-chain risk.
13. Testing Strategy
- Unit/integration tests live beside packages (
packages/*/test), run through Turborepo (opencode#test,@opencode-ai/app#test,@opencode-ai/ui#test), emitting JUnit XML to.artifacts/unit/junit.xmlfor CI. - HTTP record/replay.
packages/http-recorderand fixture "recordings" (per provider: anthropic, openai, gemini, bedrock, cloudflare, openai-compatible, …) make provider interactions deterministic in tests. - CLI process tests exercise the binary end-to-end (
packages/opencode/test/cli), including help-output snapshots and smoke tests. - Patch/diff correctness. The patch engine (
packages/core/src/patch.ts) has extensive validation with specific error messages for malformed patches — a high-risk area given the agent edits files. - Config tests cover merge precedence, schema validation, and migration.
Note
The root
turbo.jsondeliberately routes tests through package-scoped tasks and forbids running tests from the repo root (bunfig.tomlsets the test root to a non-existent./do-not-run-tests-from-root). Run tests per package or via the Turbo tasks.
14. Known Issues & Improvement Areas
Drawn from the community issue tracker at the documented commit; useful for operators assessing risk.
| Area | Issue | Operator guidance |
|---|---|---|
| Read-before-edit enforcement | Some invariants are enforced via prompts, not code (write/bash/MCP paths, the question tool). |
Constrain capability via per-agent permissions; don't rely on the agent never editing unread files. |
| Windows binary | Reports of a broken x64 wrapper binary / server error on startup in some builds. | Verify opencode --version after install; prefer a package-manager install if the curl binary misbehaves. |
| Auto-compaction loop | Sessions can enter a compaction loop and stop responding. | Start a new session; optionally OPENCODE_DISABLE_AUTOCOMPACT=true (watch context limits). |
node_modules scan hang |
.opencode/node_modules/ can stall startup despite .gitignore. |
Keep heavy node_modules out of .opencode; opencode writes a .gitignore for installed plugin deps. |
Empty oldString in edit |
An empty oldString could overwrite a file; permission prompt may lack a diff preview. |
Keep edit on ask; review diffs before approving. |
| Desktop MCP display | Desktop may show 0 MCP services after certain upgrades. | Verify via opencode mcp list on the CLI; re-auth if needed. |
Ctrl+Z on Linux |
Suspends opencode instead of editing text. | Remap terminal_suspend/input_undo in tui.json (handled automatically on Windows). |
Improvement directions evident in the codebase: continued migration to the event-sourced sync system, the v2 API/SDK surface, experimental workspaces and references, and the OpenAI Responses WebSocket transport graduating from experimental.
15. Quick Reference
Commands
opencode Launch the interactive TUI
opencode run [message..] Non-interactive run (--continue, --session, --fork, --model,
--agent, --variant, --format json, --file, --attach, --interactive)
opencode serve Headless HTTP server (--hostname, --port)
opencode web Server + browser UI
opencode acp ACP server over stdio (--cwd)
opencode auth login|logout|list Manage provider credentials (alias of `providers`)
opencode models [provider] List models (--verbose, --refresh)
opencode agent create|list Manage agents
opencode mcp add|list|auth|logout|debug Manage MCP servers
opencode plugin <module> Install a plugin (alias `plug`; -g, -f)
opencode session list|delete Manage sessions
opencode stats Token/cost statistics (--days, --project, --models, --tools)
opencode export [sessionID] Export session JSON (--sanitize)
opencode import <file|url> Import a session
opencode db [query] SQLite tools (`db path`, `db <sql>`, interactive shell)
opencode github install|run GitHub agent
opencode console login|... opencode Zen account/org
opencode pr <number> Check out a PR and run opencode
opencode upgrade [target] Upgrade (--method)
opencode uninstall Remove opencode (--keep-config, --keep-data, --dry-run, --force)
opencode completion Shell completion script
Global flags
--help/-h --version/-v --print-logs --log-level <DEBUG|INFO|WARN|ERROR> --pure
Key files & locations
opencode.json / opencode.jsonc Agent/server config ($schema https://opencode.ai/config.json)
tui.json TUI config ($schema https://opencode.ai/tui.json)
.opencode/ Project config dir (agent/, command/, plugin/, configs)
auth.json Provider credentials (data dir)
<data dir>/... SQLite sessions DB (find via `opencode db path`)
/Library/Application Support/opencode • %ProgramData%\opencode • /etc/opencode Managed config
Capabilities (permissions)
bash read edit(write/edit/patch) glob grep webfetch task todowrite websearch lsp skill
question plan_enter plan_exit actions: allow | deny | ask
Critical env vars
OPENCODE_SERVER_PASSWORD / OPENCODE_SERVER_USERNAME Server auth
OPENCODE_CONFIG / OPENCODE_CONFIG_DIR / OPENCODE_CONFIG_CONTENT Config sources
OPENCODE_PERMISSION OPENCODE_DISABLE_SHARE OPENCODE_DISABLE_AUTOUPDATE
OPENCODE_DB OPENCODE_TUI_CONFIG OPENCODE_PURE OPENCODE_GIT_BASH_PATH
OPENCODE_STORAGE_* (enterprise share storage) OTEL_EXPORTER_OTLP_*