Architecture Overview

OpenCode v2 is a monorepo built around a client-server architecture. A managed background service owns sessions, configuration, plugins, permissions, and tool execution. Client interfaces (TUI, desktop, web) connect to this service over HTTP.

System Topology

The lifecycle shell binds before the application boots. This lets clients distinguish a slow winner from an absent server during startup.

Package Structure

V2 separates concerns across packages. V1 lives in packages/opencode for reference only.

PackageRole
packages/coreApplication behavior: sessions, agents, tools, permissions, plugins, config
packages/serverHTTP transport, routes, handlers, auth, middleware
packages/protocolAPI contracts, groups, middleware, error types
packages/schemaSchema definitions for sessions, permissions, events, agents
packages/cliCLI entry point, service config, daemon management
packages/tuiTerminal user interface (Solid)
packages/clientGenerated TypeScript HTTP client
packages/sdk-nextIn-process Effect-native SDK (no HTTP listener)
packages/pluginPlugin API for extending OpenCode
packages/aiProvider-neutral LLM abstraction (@opencode-ai/ai)
packages/appWeb app
packages/desktopDesktop application

Gotcha: The default branch is v2, not dev or main. Base all new branches on v2 or origin/v2.

Runtime Dependency Direction

Dependencies flow in one direction: Schema to Core and Protocol, then Core and Protocol to Server. Client runtime code depends on Schema and Protocol but never Core or Server. The sdk-next package composes Client, Core, and Server.

Service Election

The managed V2 service uses a process-held OS lock to elect exactly one server owner for its lifetime. This replaced the earlier heartbeat lease approach after incident #36688.

ConceptAuthority
OwnershipProcess-held OS lock (flock on POSIX, named pipe on Windows)
DiscoveryRegistration file with endpoint URL
LifecycleMinimal HTTP shell bound before application boot

Key invariants:

  1. Only one process may hold the service lock per installation channel
  2. A contender performs no expensive initialization before acquiring the lock
  3. The lock is held for the entire process lifetime
  4. Transport loss is retryable - never a terminal error for TUIs
  5. Only a fresh TUI launch enforces package version (existing TUIs reconnect)

Service Status

The server reports a status discriminant that clients use for reconnection logic:

StatusMeaning
startingOwner acquired lock, application boot in progress
readyApplication initialized, serving normally
stoppingGraceful replacement in progress
failedBoot failed; owner stays bound with diagnostic message

During starting or stopping, application requests get an immediate 503 with Retry-After: 1.