Jesse A. Green / notebook

opencode Admin & Architecture Guide

Generated June 2026 from anomalyco/opencode at 3cf1cef

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

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 opencode CLI/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:

  1. 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.
  2. A local HTTP server that exposes the runtime as an API. Every front end — the TUI, the run command, 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.
  3. 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 opencode TUI boots an in-process server and talks to it over the SDK. This is why serve, 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 Schema definitions; 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:

  1. A client (TUI, run, SDK) calls session.prompt (or session.command) on the server via the SDK.
  2. 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.
  3. 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.
  4. 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).
  5. 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.
  6. 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 as client.subscribe("session.updated", …)).
  • Sync events — a lower-level, event-sourced layer for session replayability. Sync events carry type, id, seq, aggregateID, and data, 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 Bus consumers and SDK clients continue to work unchanged. The design rationale lives in packages/opencode/src/sync/README.md and 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.toml enforces exact = true and minimumReleaseAge = 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:

  1. opencode.json / opencode.jsonc — the main agent/server config ($schema: https://opencode.ai/config.json).
  2. tui.json — terminal-UI-only settings ($schema: https://opencode.ai/tui.json).
  3. 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:

  1. Remote well-known config (for wellknown auth providers)
  2. Global config dir
  3. OPENCODE_CONFIG file
  4. Project config files (root → closest)
  5. .opencode directory configs
  6. OPENCODE_CONFIG_CONTENT
  7. Active organization (Zen) config
  8. System managed config dir
  9. 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.json with an InvalidError that 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/tui keys out of opencode.json into tui.json on first run, writing a .tui-migration.bak backup. The tui key (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 allowing edit/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-only plan agent) 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.0 or 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" / --share enable it; otherwise it is off.
  • OPENCODE_DISABLE_SHARE disables it entirely (org-enforceable via managed config).
  • opencode export --sanitize produces 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 with OPENCODE_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, and random as 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|open manages 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.ts resource 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 with Layers, and errors are typed (Schema.TaggedErrorClass). Reading the code requires comfort with Effect.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.json errors fail loudly (strict, with precise locations); tui.json and plugin/agent load errors are logged and skipped so the app still starts.
  • Comment-preserving config edits. jsonc-parser modify/applyEdits is 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.xml for CI.
  • HTTP record/replay. packages/http-recorder and 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.json deliberately routes tests through package-scoped tasks and forbids running tests from the repo root (bunfig.toml sets 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_*