# opencode Admin & Architecture Guide

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](https://effect.website) — 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

```bash
OPENCODE_SERVER_PASSWORD='strong-secret' \
opencode serve --hostname 127.0.0.1 --port 4096
```

```text
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:

```bash
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:

```bash
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

```bash
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:

```ts
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 `Layer`s, 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 `Schema`s, 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

```text
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

```text
--help/-h   --version/-v   --print-logs   --log-level <DEBUG|INFO|WARN|ERROR>   --pure
```

### Key files & locations

```text
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)

```text
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

```text
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_*
```

---

_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. Unofficial; not affiliated with or reviewed by the project._
