CodeNomad/dev-docs/architecture.md
Pascal André 4359b4bfe9
fix(v2): align with current OpenCode client contract
Pin the server and UI client to the installed beta runtime and reject mismatched CLI versions before shared-service startup. Delegate proven shared-service shutdown to Service.stop while retaining CodeNomad lease, peer, registration, endpoint, and process-identity checks.

Adopt native Forms and the client/solid data reducer for live messages, tools, permissions, and input forms. Propagate internal stream generations into authoritative reconnect reconciliation and replace directory-wide session scans with native project cursor pagination.

Fail worktree deletion when session evacuation fails and deduplicate canonical workspace folders instead of exposing non-isolated duplicate instances. Update migration notes and CI coverage for the reviewed contract.

Validated with server/UI/Electron typechecks, 244 UI partition tests, 122 Electron native tests, 248 server tests plus 3 platform skips (the sole Windows cleanup race passed in isolation), UI/server/Electron builds, Tauri cargo check --locked, and git diff --check.
2026-08-19 01:50:39 +02:00

5.7 KiB

CodeNomad Architecture

Overview

CodeNomad is a SolidJS UI and Fastify server hosted by Electron or Tauri. It integrates with the experimental @opencode-ai/client protocol, with server and UI kept on the same latest reviewed next release. This is not the current public @opencode-ai/sdk contract.

Desktop host -> CodeNomad server -> one shared OpenCode service
                    ^    |
                    |    +-> CodeNomad /api/* and /api/events
                    +------ UI clients through /workspaces/:id/instance/api/*

There is no @opencode-ai/sdk integration and no packages/opencode-plugin package.

Shared Service And Locations

packages/server/src/workspaces/opencode-service.ts uses native discovery and headers while retaining a custom launcher that serializes lifecycle changes with cross-process leases, records the registration and authenticated endpoint, proves daemon and CodeNomad PIDs with process-start identity in the host or WSL namespace, and binds that proof to a launch command/environment hash. Live peer leases can inherit that proof; only the final verified CodeNomad process may call Service.stop. WSL daemons use the same authenticated graceful-stop request instead because the published fallback signals PIDs in the caller's namespace.

The V2 service always uses ~/.local/share/opencode2/opencode.db. V1 and V2 must use separate databases because their schemas are incompatible.

packages/server/src/workspaces/manager.ts treats selected folders as native OpenCode locations:

  1. Validate the directory with client.location.get.
  2. Store the returned LocationRef and publish the logical workspace.
  3. Reuse the shared service for every additional directory.
  4. Queue eviction after the final logical owner is deleted.
  5. Flush queued evictions only during proven final shared-service shutdown, then stop only the exact daemon covered by transferable CodeNomad process proof.

Workspaces are not OpenCode processes and do not own ports or PIDs.

API Boundaries

CodeNomad control APIs live under /api/*. Important routes include:

  • /api/workspaces and /api/workspaces/:id/worktrees/*
  • /api/workspaces/:id/worktrees/:slug/git-status|git-diff|git-stage|git-unstage|git-commit
  • /api/events and /api/client-connections/pong
  • /api/storage, /api/settings, /api/filesystem, /api/speech

Native OpenCode requests use /workspaces/:id/instance/api/*. The Fastify proxy exposes an explicit method/path allowlist, adds shared-service authorization, and rejects locations/directories outside the selected workspace or its worktrees. Session routes also verify session.location.directory. Upstream additions require an explicit proxy review and are not available automatically.

Yolo state endpoints currently live at /workspaces/:id/yolo/sessions/:sessionId; Yolo notifications use /api/events.

Client And Events

packages/ui/src/lib/sdk-manager.ts uses OpenCode.make() and caches generated Promise clients by instance proxy path. packages/ui/src/stores/opencode-client.ts is the root-client authority; native directory/location fields replace old per-worktree SDK clients.

The server holds one client.event.subscribe() stream. InstanceEventBridge maps native location events to CodeNomad instance.event records, and /api/events multiplexes them with workspace and Yolo events for the browser. The stream is volatile and has no replay guarantee: reconnect must refetch authoritative state.

Current native events include session lifecycle/output events (session.created, session.renamed, session.moved, session.status, session.idle, session.execution.*, session.compaction.*, session.text.*, session.reasoning.*, session.tool.*), file invalidation via filesystem.changed, and configuration invalidation via config.updated.

Feature Ownership

Feature Owner
Sessions, messages, permission/question APIs OpenCode V2
Shell mode client.session.shell
Conversation instructions client.session.instructions.entry
PTY management Location-scoped OpenCode V2 API through the ownership-checking proxy; Status panel UI
PTY output and distinct stop Unavailable in the current installed declarations; removal is the native stop action for a running PTY
Workspace lifecycle and directory authorization CodeNomad
Git status/diff/stage/unstage/commit CodeNomad server
Yolo state, persistence and auto-accept CodeNomad server
Browser SSE multiplexing CodeNomad server

Native Shell remains separate from PTY management. The Status panel lists location-scoped native PTYs, refreshes on PTY events/reconnect, displays native metadata, and allows title updates and ownership-checked removal. Current installed declarations expose no PTY output/read/stream API or separate stop endpoint, so output display and a distinct stop action are unavailable. packages/opencode-plugin and the server plugin/background-process paths remain deleted and must not be restored.

Persistence

CodeNomad configuration resolves through packages/server/src/config/location.ts: config.yaml, state.yaml, and instances/ under ~/.config/codenomad/. config.json is migration input only.

Key Files

  • packages/server/src/index.ts
  • packages/server/src/server/http-server.ts
  • packages/server/src/workspaces/opencode-service.ts
  • packages/server/src/workspaces/manager.ts
  • packages/server/src/workspaces/instance-events.ts
  • packages/server/src/workspaces/git-mutations.ts
  • packages/server/src/permissions/auto-accept-manager.ts
  • packages/ui/src/lib/sdk-manager.ts
  • packages/ui/src/lib/api-client.ts
  • packages/ui/src/stores/session-api.ts
  • packages/ui/src/stores/session-actions.ts