Ozzy Dev — Product Architecture

Version: 0.7.0 Status: Canonical current-state architecture. Forward-facing plans live in the console at /roadmap; superseded revisions live in `DECISIONS/`. Last updated: 2026-08-03 Owner: nexartis-ozzydev

Ozzy Dev is the Nexartis reference implementation of the on-machine agentic client. It is one product, and simultaneously the marquee case study for the whole Nexartis agentic stack: KnowYourModel, NANDA Node, Cubicle SDK, ABI Chat SDK, and the Nexartis fork of OpenCode.

1. Reason to exist

Ozzy Dev turns a developer's machine into a trusted, discoverable, voice-driven agentic workstation. A single git clone plus one install script gives a new engineer:

  • A local runtime host (local-mac first; local-windows and local-linux follow) that runs harnessed agents pulled from KnowYourModel.
  • A curated MCP tool catalog that gives those agents real capability — accessibility-tree desktop drive, web drive, memory, code context, KYM discovery, voice.
  • A local console (SvelteKit) built on the ABI Chat SDK Svelte components, showing the currently active agent, its Cubicle spec, live turns, receipts, and the tool catalog.
  • A first-party agent, "Ozzy", published in KYM, that ships as the default primary agent — with the option to fork, modify, and publish your own variant back to KYM.

Everything else — the agent harness, the security boundary, the agent registry, the discovery graph, the payment rails — is provided by an existing Nexartis SDK or service. Ozzy Dev composes them.

2. Product boundaries

Ozzy Dev is:

  • The clone-and-install harness for the local-mac, local-windows, and local-linux Cubicle runtime hosts.
  • The on-machine orchestrator: it discovers the machine, picks a runtime, picks an agent, compiles a Cubicle spec, launches a harness, and writes receipts back to KYM every turn.
  • The MCP tool catalog manager on the machine.
  • The identity anchor on the machine (Ed25519 keys, DID, keychain refs).

Ozzy Dev is not:

  • Not a coding assistant for third-party editors. Cursor, Kilo, and Claude Code are unrelated products.
  • Not Remote Control. Remote Control is a separate, opt-in bridge that lets an off-machine driver instruct Ozzy Dev. Kept decoupled because opening a socket to a relay is the moment a machine's security posture changes; that should be a discrete, revocable step.
  • Not a wrapper around OpenCode's UX. Ozzy Dev uses OpenCode as its agent harness; Ozzy Dev is the product identity users see.
  • Not a general MCP installer for the world. It curates the Nexartis blessed catalog for a Nexartis-ready workstation.

3. Mac Local Cubicle Architecture & Per-Workspace Governance

The Mac Local Cubicle completes the local macOS developer instance of the Nexartis agentic stack, pairing with the edge nexartis-my-cubicle-cube implementation:

  1. Edge vs Local Parity: While my-cubicle-cube executes inside Cloudflare Dynamic Workers / Durable Objects with Pegasus HB3 container integration, nexartis-ozzydev executes on macOS via supervised Node.js console services and stdio/HTTP native MCP engines (ozzydev-search, ozzydev-write, ozzydev-harness, ozzydev-voice).
  2. Per-Workspace Scope: A single developer machine can host multiple independent workspaces (e.g. 20260802-ozzydev, 20260730-my-cubicle-cube). Settings, privacy policies, autonomy constraints, and capability allowlists apply strictly on a per-workspace basis stored in D1 (workspace_cubicle_settings table).
  3. Privacy & Autonomy Levels:
    • privacy_level: 'strict' (zero telemetry / local vector storage only), 'standard' (PII pseudonymized in-flight), 'permissive' (full context).
    • autonomy_level: 'full_autonomous' (direct execution), 'semi_autonomous' (approval gates on deploy/publish), 'manual_approval' (approval on every file write).
    • local_vector_storage_only: Embeddings kept local in codesearch / chunkhound without Zilliz sync.
    • code_snippet_redaction: Sensitive tokens and code snippets redacted in turn receipts.
  4. KnowYourModel (KYM) Orchestrator Card Publishing:
    • Integrates with KnowYourModel (https://knowyourmodel.ai).
    • Exposes POST /api/kym/publish-card and Svelte 5 <KymOrchestratorPublisher /> component on /harness to design, configure, and publish custom Orchestrator and Subagent Cards (nexartis:harness/orchestrator) directly to KYM.
    • Resolves role cards (implementer, verifier, validator, researcher, orchestrator) dynamically at dispatch time via kym-resolver.ts.

4. Host vs harness & Multi-Target Dispatch

@nexartis/cubicle-core already formalises the distinction, and Ozzy Dev adopts it verbatim. See `DECISIONS/ADR-001-host-and-harness.md` for the decision record.

  • Host = the deployment platform with its own security boundary and platform APIs (hb3, cloudflare-dynamic-worker, browser, device, home-hub, custom). Ozzy Dev adds local-mac, local-windows, local-linux.
  • Harness = the agent engine that runs inside a host (claude-code, opencode). One host can support multiple harnesses.

The host is deliberately harness-agnostic. local-mac is not local-mac-opencode; the same host will run OpenCode today, Claude Code tomorrow, and any future Nexartis harness after that.

Multi-Target Dispatch Vision & Native Engine Stack

Ozzy Dev operates as the on-machine orchestrator. It can spin up and dispatch agent dispatches across three distinct target runtimes from the same local console:

  1. Local Mac Harness: Ephemeral git worktree containment running local subagents with Thompson Sampling bandit engine selection (task_class='auto') and diff-content reward signal weighting.
  2. Cloudflare Dynamic Worker Runtime: Serverless edge execution for fast stateless jobs.
  3. HB3 Cloud Coordinator: High-scale cloud dispatch for multi-agent waves.

Native MCP engines deliver low latency and high reliability:

  • ozzydev-search (v1.6.0): MoE search router with persistent file-backed hint caching under $WORKSPACE/.ozzydev/hints-cache.json (1ms hint fetch).
  • ozzydev-write (v0.0.10): Rubric-routed write engine with direct-mode priority queue lane (directQueue vs multiplexQueue) and automatic Prettier post-apply formatting.
  • ozzydev-voice (v0.0.1): Native ElevenLabs TTS voice assistant with CLI speak subcommand.

4. Runtime-first discovery

Ozzy Dev discovers agents by asking, in order:

  1. What runtimes does this machine offer? Host, installed harnesses, and MCP tool catalog from mcp/registry.json.
  2. Which agents in KYM support at least one of my host + harness combinations? Query KYM with the runtime filter, then narrow by capability, reputation, and verification type.
  3. Compile the chosen agent's Cubicle spec for the selected host + harness combination using the matching Cubicle adapter.
  4. Launch the harness with the compiled plan; stream turns through the ABI Chat SDK; write signed receipts to KYM every turn.

This is the opposite of the "find an agent, hope it works" flow. Runtime capabilities come first; the agent list follows.

5. Harness declaration on the KYM Agent Card

A KYM Agent Card carries the harness declaration under the x-nexartis-runtime extension:

{
	"name": "Ozzy",
	"version": "1.0.0",
	"description": "Nexartis voice-first on-machine agent for developers.",
	"protocolVersions": ["1.0"],
	"x-nexartis-runtime": {
		"harnesses": ["opencode"],
		"supportedHosts": ["local-mac", "local-windows", "local-linux"],
		"supportedCubicles": ["ozzy-dev-buyer/v1"],
		"model": "anthropic/claude-sonnet-4",
		"temperature": 0.2,
		"tools": { "*": true },
		"permission": { "edit": "ask", "bash": "ask" }
	}
}

The runtime-first discovery flow queries this extension. Ozzy Dev extends the existing assertAgentCardCubicleCompatible helper in the Cubicle SDK to recognise the local-* hosts.

5. Workspace, Developer & Multi-PR Collaboration Model

Ozzy Dev is designed for multi-workspace, multi-agent collaboration with a zero-friction developer experience:

  1. Shared Local D1 Database: A single developer machine can host multiple independent workspaces. All workspaces share one local D1 database (workspaces, repos, workspace_repos, code_maps). This provides instant cross-workspace inventory (GET /api/repos/inventory/cross), tracking which workspace holds which repo, open PR number, branch, and indexed HEAD.
  2. Parallel Waves & Single PR to dev: In any workspace, parallel agent waves execute across repositories. Per Nexartis conventions, every repository maintains exactly one open PR to dev, stacking all concurrent features safely onto that branch.
  3. Rebase Discipline & Content-Aware Freshness: When a PR merges into dev, parallel workspaces holding checkouts of that repo trigger background content-aware freshness checks (indexed_head..HEAD). Unchanged indexable code automatically re-stamps sidecars without intrusive re-indexing, ensuring search query latency stays under 10ms without impeding the operator.
  4. Zero-Step Bootstrap & Automated Key Distribution: New developers or secondary workspaces run ./bin/bootstrap.sh --use-shared-console. The installer connects to the active console, provisions supervisor LaunchAgents, applies D1 keys automatically, and registers all workspace repositories with zero manual configuration steps.

6. Component architecture

flowchart TB Dev["Developer<br/>voice · console · CLI"] subgraph Ozzy["Ozzy Dev — on-machine agentic client"] direction TB subgraph UI["UI layer"] Console["SvelteKit console<br/>127.0.0.1 + PIN"] ChatSv["@nexartis/abi-chat-sdk-svelte<br/>shell · chat · command · voice"] end subgraph Session["Session runtime"] ChatCore["@nexartis/abi-chat-sdk-core<br/>providers · SSE · sessions · tools · payments"] end subgraph Harness["Agent harness"] OpenCode["vendor/opencode<br/>(Nexartis fork of sst/opencode)"] Plugins["Ozzy Dev plugins<br/>kym-loader · cubicle-runner · kym-receipts · voice · discovery"] end subgraph Sec["Security / permission"] CubCore["@nexartis/cubicle-core"] CubLocal["@nexartis/cubicle-adapter-local-mac"] end subgraph MCP["MCP tool catalog (mcp/registry.json)"] Touch["Touchpoint<br/>desktop drive (a11y tree)"] Play["@playwright/mcp<br/>web drive"] Mem["basic-memory<br/>workspace memory"] Serena["Serena<br/>code context (LSP)"] KymMCP["KYM MCP<br/>agent discovery"] end end KYM["KnowYourModel<br/>knowyourmodel.ai<br/>agents · cards · receipts · votes"] NANDA["NANDA Node<br/>discovery · x402-NP · orchestration"] Base["Base L2<br/>USDC escrow"] Dev --> Console Console --> ChatSv ChatSv --> ChatCore ChatCore --> OpenCode OpenCode --> Plugins Plugins --> CubCore CubCore --> CubLocal OpenCode --> Touch OpenCode --> Play OpenCode --> Mem OpenCode --> Serena OpenCode --> KymMCP Plugins -.pull agent + Cubicle.-> KYM Plugins -.signed receipt per turn.-> KYM Plugins -.discover · buy.-> NANDA NANDA -.settle.-> Base classDef ozzy fill:#0b1220,stroke:#58a6ff,color:#e6edf3 classDef ext fill:#161b22,stroke:#30363d,color:#e6edf3 class Ozzy,UI,Session,Harness,Sec,MCP ozzy class KYM,NANDA,Base ext

The Cubicle SDK is the security boundary. The ABI Chat SDK is both the session runtime and the UI vocabulary. KYM is the source of truth for agents; NANDA is the discovery + payments layer; Base is the settlement rail. Ozzy Dev is the composition.

7. Install-time flow

The one-time install a developer runs on a fresh laptop.

Status — target vs current. This sequence is the target install-time flow. The current entrypoint is pnpm run bootstrap (bin/bootstrap.sh): it installs prereqs, builds the vendored SDKs and the native MCP servers, generates the Ed25519 receipt-signer key into the macOS Keychain, applies the D1 migrations, provisions the memory systems via mcp/install.sh, and starts the console. It does not yet request TCC permissions, register a host DID or pull the Ozzy agent card + Cubicle spec from KnowYourModel, compile a Cubicle spec, or gate the console behind a PIN — those steps are planned (host/harness Wave).

sequenceDiagram autonumber actor Dev as Developer participant Repo as git · nexartis-ozzydev participant Install as install.sh (target: bin/bootstrap.sh) participant OS as macOS<br/>TCC · Keychain participant Vendor as vendor/opencode participant CubL as cubicle-adapter-local-mac participant KYM as KnowYourModel participant Console as SvelteKit console Dev->>Repo: git clone Dev->>Install: ./install.sh Install->>Install: install node/pnpm/git/gh prereqs Install->>Vendor: build vendored OpenCode Install->>OS: request Accessibility · Screen Recording · Mic Install->>OS: generate Ed25519 key → Keychain Install->>KYM: register host DID (orchestrator entity) Install->>KYM: pull Ozzy agent card + Cubicle spec KYM-->>Install: signed { agentCard, cubicleSpec } Install->>CubL: compile(cubicleSpec) CubL-->>Install: LocalMacHostPlan (opaque refs · TCC reqs) Install->>Install: install MCP tools from mcp/registry.json Install->>Console: start on 127.0.0.1 · PIN Console-->>Dev: browser opens · voice welcome

8. Per-turn flow

The loop that runs every time the developer speaks or types.

sequenceDiagram autonumber actor Dev as Developer participant UI as ABI Chat<br/>Svelte participant Core as ABI Chat<br/>Core participant Bridge as OpenCode<br/>bridge provider participant Ozzy as opencode<br/>+ Ozzy plugins participant Tool as MCP tool<br/>(Touchpoint / Playwright / …) participant Signer as Ozzy identity<br/>Ed25519 · Keychain participant KYM as KnowYourModel Dev->>UI: voice or text UI->>Core: ChatRequest Core->>Bridge: dispatch via CustomProvider Bridge->>Ozzy: prompt + tool set Ozzy->>Ozzy: plan · reflect Ozzy->>Tool: call tool (MCP) Tool-->>Ozzy: result Ozzy-->>Bridge: SSE events Bridge-->>Core: SSE events Core-->>UI: stream chunks UI-->>Dev: rendered · spoken Ozzy->>Signer: sign turn envelope Signer-->>Ozzy: signature Ozzy->>KYM: POST /api/receipts KYM-->>Ozzy: 202 accepted

Every completed turn writes a signed receipt back to KYM, which is what makes Ozzy Dev's on-machine execution independently auditable and KYM's reputation graph richer.

9. Agent purchase flow

Discovering, buying, and installing a second agent from KYM through NANDA payments.

sequenceDiagram autonumber actor Dev as Developer participant Console as Console<br/>/agents participant KYM as KnowYourModel participant NANDA as NANDA Node participant Pay as opencode-nanda-payments participant Base as Base L2 (USDC) participant Loader as opencode-kym-loader participant CubL as cubicle-adapter-local-mac participant Ozzy as opencode Dev->>Console: browse KYM Console->>KYM: search · filter by host+harness KYM-->>Console: candidate agents Dev->>Console: select agent · buy Console->>NANDA: request purchase NANDA-->>Pay: payment challenge (x402-NP or USDC) Pay->>Base: settle Base-->>Pay: settlement receipt Pay-->>NANDA: proof of payment NANDA-->>Loader: signed delivery envelope<br/>{ agentCard, cubicleSpec, provenance, license, expiry } Loader->>Loader: verify KYM + NANDA signatures Loader->>CubL: compile(cubicleSpec) CubL-->>Loader: LocalMacHostPlan Loader->>Ozzy: register new agent Ozzy-->>Dev: agent available in Tab cycle

10. Component provenance

Component Repo / Package Role in Ozzy Dev
UI shell + primitives @nexartis/abi-chat-sdk-svelte Shell, chat, command, context, console, form-builder, voice input
Session runtime @nexartis/abi-chat-sdk-core Provider routing, SSE bridge, session persistence, tool registry, cubicle bridge
Security layer @nexartis/cubicle-core CubicleSpec, validation, opaque refs, host-adapter contract
Local runtime adapter @nexartis/cubicle-adapter-local-mac Compiles a CubicleSpec to a local-Mac runtime plan
Agent harness vendor/opencode (Nexartis fork) Primary + subagents, plugins, MCP client, tools, permissions
Ozzy Dev plugins plugins/* (this repo) KYM loader, Cubicle runner, KYM receipts, voice, discovery
MCP catalog mcp/registry.json (this repo) Curated tool inventory installed on the machine
Agent registry KnowYourModel (external service) Where agents live; receipts flow back
KYM SDK @nexartis/knowyourmodel-sdk Typed KYM API + Ed25519 signing
Discovery + payments NANDA Node (external service) A2A discovery, x402-NP micropayments, USDC settlement
Homeport SDK @nexartis/homeport-sdk Typed Homeport (NANDA node) API

11. Non-goals

  • Multi-user session sharing. Ozzy Dev is single-user local.
  • Cloud-hosted Ozzy. local-mac is the point; a cloud Ozzy is a different product.
  • Auto-installing MCP into third-party coding agents (Cursor, Kilo, etc.). Ozzy Dev curates its own catalog for its own harness.
  • Off-machine remote driving in the initial waves. Remote Control is a separate, opt-in bridge scheduled for a later wave.

12. Persistence baseline

The console runs on Cloudflare D1. One SvelteKit codebase, one schema, three D1 databases — one per environment (local, dev, prod). @sveltejs/adapter-cloudflare binds platform.env.DB in every runtime; drizzle-orm (drizzle-orm/d1 dialect) is the ORM; migrations are generated by drizzle-kit and applied via wrangler d1 migrations apply <db> --remote. vite dev reaches the real local/shared D1 through wrangler's platform proxy with remote: true on the binding (adapter-cloudflare platformProxy option, GA since Wrangler 4.37).

  • Databases (schema-identical, forward-only migrations):
    • localnexartis-ozzydev-local-console-shared-database (a2353127-a862-4941-93e1-604c98788768, region WNAM). Backs vite dev on operator Macs and every local console in the Nexartis org — the "shared vault" per operator identity.
    • dev — workers-dev D1 (690460fe-5cfe-42ad-a2ae-34f78512ef53).
    • prod — workers-prod D1 (400484e4-f299-499d-82a8-2a8da676df44).
  • Auth is wrangler OAuth (wrangler login). That is the entire local credential story — no API tokens on disk, no macOS Keychain master key for the datastore. CI (when needed) uses CLOUDFLARE_API_TOKEN scoped Account → D1 → Edit.
  • Backups: D1 Time Travel — 30-day point-in-time recovery is the primary durability control. Operator runbook: `RUNBOOKS/d1-time-travel-recovery.md`.

12.a Schema (initial — Phase 1)

Seven tables ship today (v0.4 schema, 0001_machine_scoping migration); MCP-controller tables land with the D5 controller sprint. Column-level definitions live in `SCHEMA-DESIGN-console-d1.md` §3.1.

Table Role
machines Per-host row (hostname natural key, auto-registered on first console request). Every workspace + console_state row scopes to a machine.
workspaces Workspace registry — one row per (machine, absolute path); UNIQUE(machine_id, path). Machine-scoped visibility is D1-enforced.
console_state Per-machine singleton (PRIMARY KEY (machine_id)) — carries active_workspace_id for that machine.
secrets Provider API keys — AES-256-GCM envelope in ciphertext, materialised hint, envelope_version (see §12.b, ADR-006 amendment 2).
applied_secret_targets Which workspace files each secret was applied into, for revoke-and-remove flows.
audit_log Append-only trail with request_id, sensitive-field allowlist enforced at write time; secret.rotated is a first-class action.
settings Global / machine / workspace KV — all client-state (UI tabs, filters, per-workspace preferences) lives here; zero localStorage.

MCP controller tables (mcp_servers, mcp_server_config_history, mcp_health_probes, mcp_incidents) are deferred to the D5 sprint (schema doc §3.1.5 amendment A2) — Phase 1 stays on the live-probe /api/doctor endpoint with no persistence.

KYM still owns agent cards and receipts; the memory systems still own their local indexes under the workspace.

Workspace-tooling home (shipped in WS-A, 2026-07-19). Per ADR-012 §D-F, .ozzydev/ at each workspace root is the single home for workspace-tooling state: .ozzydev/chunkhound.json + .ozzydev/chunkhound/db (chunkhound --config), .ozzydev/codesearch/repos.json (via CODESEARCH_REPOS_CONFIG), the WS-A branch-freshness sidecars at .ozzydev/{codesearch,chunkhound}/index-meta.json, and (WS-B) per-workspace daemon state under .ozzydev/daemons/. .serena/ stays at the workspace root (upstream directory-name convention; no override flag today) and kilo.json stays at the root (harness-defined). mcp/install.sh runs an idempotent migrate at every invocation — .chunkhound.json.ozzydev/chunkhound.json, .chunkhound/.ozzydev/chunkhound/, .codesearch/.ozzydev/codesearch/; if BOTH old and new exist the installer refuses and asks the operator to resolve (never leave both). Current-state layout on a bootstrapped workspace is therefore .ozzydev/{chunkhound,codesearch}/ + .serena/ at the workspace root. Per-workspace codesearch serve daemons SHIPPED 2026-07-31 (ADR-012 §D-A A1-lazy): mcp/install.sh scaffolds ~/Library/LaunchAgents/com.nexartis.codesearch-serve.<slug>.plist (slug = kebab workspace basename) on a deterministic port 40000 + sha256(realpath)[0:4] % 2000, with the registry $WORKSPACE/.ozzydev/codesearch/repos.json baked into the plist env and the resolved URL written to $WORKSPACE/.ozzydev/codesearch/serve_url (+ D1 workspaces.metadata.codesearch_url). Serena remains a shared machine-global streamable-HTTP daemon on :24283 (LaunchAgent com.nexartis.serena-serve); the machine-global ~/.codesearch/ registry + :39725 codesearch daemon are FROZEN LEGACY for workspaces bootstrapped from older ozzydev clones — never delete, never merge into them (see docs/RUNBOOKS/reset-codesearch-registry.md for the housekeeping runbook).

12.a2 Multi-workspace, multi-machine model

  • One console instance per machine. The console binds to port 5173; bin/bootstrap.sh fails loudly if that port is already in use (vite would otherwise fall forward to 5174 and every probe would silently target the wrong instance). Stop the running console or use it — never run two side-by-side.
  • Machines auto-register by hostname. On the first request from a new host, the hook layer upserts a machines row keyed by os.hostname(). No manual "register this machine" ceremony; last_seen_at bumps on every request. Workers runtimes get a stub machine row (demo surface only).
  • N workspaces per machine. Each workspace is a (machine_id, path) pair with UNIQUE(machine_id, path). Visibility is D1-enforced: the workspaces list query filters by machine_id = <this machine>. Two Macs may share the shared local D1 vault and yet each console lists only its own machine's workspaces.
  • Per-workspace tool configs. Every workspace has its own kilo.json / .ozzydev/chunkhound.json / .codesearchignore (plus the per-repo tracked .contextignore, the claude-context exclusion file sanctioned by the #372 allowlist our embedded core is patched to), scaffolded by mcp/install.sh on install and rewritten (mode 0600) at point-of-use by the secrets apply pipeline. Cursor users mirror the MCP entries via mcp/scaffold/cursor-mcp.json.template.cursor/mcp.json.
  • Secrets vault is global, applied-targets are per-workspace. One secrets row per integration serves every workspace on every machine the operator signs in on; applied_secret_targets records which workspace files each key has been written into for the revoke/scrub flow.
  • All client state lives in D1 via the settings table. Global / machine / workspace scopes (see schema doc §3.1.6). The UI wave removes every localStorage call in the same PR wave; zero browser-local console state remains.

Local search daemons: per-workspace codesearch (SHIPPED 2026-07-31), shared serena (current). Per ADR-012 §D-A, codesearch-serve runs as a per-workspace LaunchAgent (com.nexartis.codesearch-serve.<slug>) with a deterministic port 40000 + sha256(realpath)[0:4] % 2000, KeepAlive + ThrottleInterval 10 + an ERR-trap that re-bootstraps on mid-script failure; the resolved URL is recorded in $WORKSPACE/.ozzydev/codesearch/serve_url and in D1 workspaces.metadata.codesearch_url. The search MCP resolves the codesearch URL from that field at config load (search-router ≥ 1.6.0, ADR-007 §2 config_version bump). Serena remains a shared machine-global streamable-HTTP daemon on :24283 (LaunchAgent com.nexartis.serena-serve); per-workspace Serena is the next slice under this ADR. The machine-global codesearch daemon (:39725) is frozen legacy for older-workspace ozzydev clones.

Branch-aware freshness (target — decided 2026-07-19; WS-A defines the sidecar schema, WS-C mirrors to D1). Every indexer persists {indexed_head, indexed_at} per repo in shared sidecars at $WS/.ozzydev/{codesearch,chunkhound}/index-meta.json (schema v1; see ADR-012 §D-B). Experts compare indexed_head vs git rev-parse HEAD and fail loud with classified expert_index_stale; the already-designed ExpertHit.branch_match and freshness_index_age_s fields are wired (previously hard-coded null). Auto incremental reindex runs on workspace activation for the active workspace only; ChunkHound reindex stays explicit + capped per upstream #365. Amended 2026-08-01 (ADR-014 D-A): staleness is content-aware — on a HEAD mismatch with a MATCHING branch the codesearch expert diffs indexed_head..HEAD and downgrades to fresh when the delta has zero indexable content, re-stamping the sidecar and marking the receipt (content_fresh_verified); branch mismatch stays strict.

12.a3 Team fleet model (SHIPPED 2026-08-01, ADR-014 D-B — migration 0006)

Per ADR-012 §D-D as built by ADR-014 D-B, the shared D1 gains:

  • workspace_repos — per-(workspace_id, repo_id) checkout record (FK to repos, whose natural key is remote_url) with checkout_path, alias, open_pr_number/branch, indexed_head, indexed_branch, indexed_at, and semantic_indexed (per-plane flags beyond semantic remain sidecar/doctor-derived today). Upserted by refresh-index-meta.sh's POST hook after every sidecar refresh; GET /api/repos/inventory/cross returns every workspace's checkout of one repo side by side — the one-PR-per-repo visibility primitive, verified live with two workspaces on one branch (2026-08-01). The Repos tab D1 read-path and the console /fleet view (machines × workspaces × repos × branches × freshness × last-seen) consume this table next.
  • machine_uuid (IOPlatformUUID) added alongside hostname on machines, so two Tonys-MacBook-Pro machines cannot collide.
  • No users table yet. Machine-scoped attribution is adequate until there is a real multi-seat auth story; KYM DIDs are the future identity per the target architecture.

Semantic corpus re-key (target — decided 2026-07-19, ships in WS-SEM). Per ADR-012 §D-C, Zilliz semantic collections re-key from md5(abs-path)[0:8] to md5(remoteUrl + branch) so team members share the dev-branch corpus (indexed once by a designated indexer or CI) while per- workspace ChunkHound covers local deltas. The migration wave is gated on an override seam around the embedded Context.semanticSearch path-hashing call — patched in our embedded @zilliz/claude-context-core or a collection-name override honored end-to-end (search + insert + doctor probe). Interim: existing path-keyed orphan detection stays in GET /api/doctor/repos.

12.b Secret storage — envelope encryption (ADR-006 amendment 2)

The vault stores provider API keys as AES-256-GCM envelopes in secrets.ciphertext (v1.<iv>.<ciphertext>.<tag> base64), alongside a materialised hint (masked preview, computed at write time) and an envelope_version gate for future format migrations. The 32-byte machine master key lives in the macOS Keychain on the operator's Mac (service com.nexartis.ozzydev, account secrets-master-key, created on first use). The DB never sees key material; the Keychain never sees secret values.

Per the 2026-07-12 operator directive ("all data lifecycle in D1 AND api keys encrypted per best practice with rotation"), this supersedes the same-day plaintext-at-row-level amendment. See `DECISIONS/ADR-006-encrypted-secrets-vault.md`Amendment 2 — 2026-07-12 · Envelope encryption reinstated.

Runtime split:

  • local runtime holds the master key and is the only runtime that decrypts. File-apply of the plaintext to .ozzydev/chunkhound.json / kilo.json is local-runtime-only.
  • workers-dev / workers-prod runtimes can list vault metadata (id, integrationId, hint, timestamps, applied targets) via the write-only contract but throw requires_local_runtime on any decrypt or apply — loud, never silent.
  • MasterKeyError contract retained. Keychain lookup failure surfaces as master_key_unreachable (HTTP 500). No silent regeneration, no fresh-start fallback that would invalidate stored ciphertext.
  • Rotation = re-save. Verify-live-on-save → encrypt under the current master → upsert → re-apply to every applied_secret_targets row. Audited as secret.rotated.

The compensating-controls stack from amendment 1 REMAINS as defense layers beneath envelope encryption (they no longer stand alone):

  1. D1 at-rest encryption — Cloudflare-managed keys cover the storage tier under the envelope.
  2. Wrangler OAuth-scoped access — the only human path into a row is wrangler d1 execute / D1 dashboard, gated on the Cloudflare account and access-logged.
  3. Audit-log fixed-key allowlistaudit() refuses to write plaintext or ciphertext into audit_log.detail; secret.rotated joins the allowlist; unknown keys fail loud with audit_unknown_field.
  4. Verify-live-on-save — provider-verify gates acceptance before encryption. Possession of a rotated ciphertext without a live verify does not re-apply anywhere.
  5. Apply-time 0600 files — applied configuration files (.ozzydev/chunkhound.json embedding block, kilo.json context7 header
    • morph env) are machine-local, mode 0600, untracked.

12.c Non-negotiables (workspace §2 alignment)

  • No silent fallback between D1 and local SQLite. If the binding is unavailable (offline, not logged in), the console fails loudly with a classified datastore_unreachable error and a fix-it hint (wrangler login / pnpm run deploy:local). bin/bootstrap.sh probes /api/secrets after readiness and hard-fails on 503 rather than continuing into secret re-apply against a broken binding. One datastore, one code path.
  • Loud failure surfaces in the UI — a dedicated "datastore unreachable" state on every data-backed page.
  • Forward-only migrations — every schema change is a generated, committed migration; no drizzle-kit push against the shared DB. Time Travel is for data recovery, not schema rollback.
  • request_id on every mutation. hooks.server.ts assigns a ULID, threads it into every audit_log row and every classified error envelope, so a support session traces a UI action to its DB effects and its log lines.
  • Machine-scoped workspace visibility (D1-enforced). The workspaces list query filters by machine_id; a workspace row from another machine on the shared D1 is invisible to this console. Applied targets follow the workspace scope; secrets are global per operator identity.

12.d Migration story (fresh start)

Legacy local vault data (~/.ozzydev/console.db, ~/.ozzydev/workspaces.json, and any prior secret-crypto.ts envelope contents in those files) are abandoned in W2 — no automatic migration, no pre-seeding from any legacy file (silent behavior we explicitly avoid, workspace §2). The node:sqlite adapter is the only piece actually retired: production storage moved to Cloudflare D1. secret-crypto.ts and the macOS Keychain master key entry (com.nexartis.ozzydev / secrets-master-key) are current, not abandoned — ADR-006 Amendment 2 reinstated envelope encryption at the row level (AES-256-GCM v1.<iv>.<ct>.<tag> stored in secrets.ciphertext, master key held only on the local runtime). Operators re-enter each key once through /system → API Keys; verify-live-on-save is the acceptance contract, and applied_secret_targets rows are rebuilt as operators re-apply. One-time cost, permanent win.

12.e Endstate file map

console/
  wrangler.jsonc              # DB binding → shared/dev/prod D1, remote: true
  drizzle.config.ts           # dialect sqlite, driver d1-http (for generate only)
  migrations/                 # drizzle-kit output, applied via wrangler
  src/lib/server/db/          # drizzle client factory + schema (source of truth)
  src/lib/server/secrets.ts   # typed drizzle queries; no crypto
  src/lib/server/runtime.ts   # capability module — local vs workers-dev/prod
  src/lib/server/errors.ts    # ConsoleError + handleError (request-id envelopes)

12.f Native MCP servers (search + write + harness + voice)

Four first-party MCP servers ship under mcp/servers/ and are seeded into mcp/registry.json alongside the third-party catalog:

  • mcp/servers/ozzydev-native-search-mcp — the MoE search router (ADR-007, the fifth memory system alongside chunkhound / codesearch / serena / context7; the chunkhound MCP is re-enabled read-only per D4, while the router semantic slot is claude-context + Zilliz Cloud pending the bakeoff, with our embedded core carrying the #372 ignore-allowlist patch, ADR-007 §2.1). Per-repo coverage of the memory systems — codesearch/group/serena/semantic + ignore hazards
    • orphaned-collection detection — is live at the console /memory Repos tab (GET /api/doctor/repos). Classifies a query into an intent class, fans out to per-class experts, and fuses via RRF. Ships enabled: true in the workspace kilo.json. Baseline metrics and the soak / chaos / adversarial harness live in `DECISIONS/BENCHMARK-PLAN-ozzydev-native-search-mcp.md`.
  • mcp/servers/ozzydev-native-write-mcp — the rubric-routed write + verify server (ADR-008). Routes each edit to the right write tool (native / morph / relace / weave / ast-grep / serena) by the AGENTS.md §1 write rubric, then verifies the on-disk result. enabled: true since 2026-07-16 (wave-1-4 milestone): the r2–r5 bakeoff program completed the pre-enable gate — a deterministic splice-first preflight resolves ~74% of real lazy edits at $0/0ms and hands classified failures to the vendor arm (fast_apply_engine = morph, chosen on reconciled artifact evidence; Relace stays as the one-flip alternate). On a fresh workspace the entry scaffolds enabled: false; saving a verified Morph key in the Secrets panel fans MORPH_API_KEY into the ozzydev-write env and flips it on (applyMorph, 2026-07-16 — splice preflight itself is keyless).
  • mcp/servers/ozzydev-native-harness-mcp — the harness engine router (ADR-013). Routes a unit of work to a harness EngineAdapter (generic seam — vendors like acp/cli engines are config, never type identifiers), runs the verify ladder, and emits a receipt. Scaffolds enabled: false (bench-harness driven).
  • mcp/servers/ozzydev-native-voice-mcp — native voice / TTS speak_update, extracted from nexartis-remote-control and dependency-free: a bundled Python-stdlib ElevenLabs runtime (voice-runtime/) + a node-builtins spawn wrapper + a @modelcontextprotocol/sdk stdio server. No console/receipt coupling (voice is an output modality, not a code-write); key via ELEVENLABS_API_KEY; scaffolds enabled: false until the key is provisioned. Registry catalog category voice.

The receipt-coupled servers (search / write / harness) emit ADR-009 Ed25519-signed receipts for every tool call into the console D1 (tool_benchmark_events + experiments tables), so their behavior is independently auditable on the same rail as KYM turn receipts (§8).

12.1 Runtime targeting & environment model (D1 refactor)

The D1 refactor lands with an explicit three-environment runtime model. The console is one SvelteKit codebase deployed against three runtime targets; environment is declared, never sniffed. Selection is via the OZZYDEV_RUNTIME variable set per wrangler env (local, dev, prod).

  • local — primary sidecar runtime. vite dev on the operator's Mac. Full node:fs, node:child_process, and shell access are available; platform.env.DB is provided by wrangler's platform proxy with remote:true against the local/shared D1 (a2353127-a862-4941-93e1-604c98788768). This is the primary runtime — Ozzy Dev is the on-machine agentic client (§3), and every local-only capability (file-apply of secrets, subprocess health probes, MCP supervisor introspection) targets this runtime.
  • dev — workers-dev. wrangler deploy --env dev targets the dev D1 (690460fe-5cfe-42ad-a2ae-34f78512ef53) and is used for demos and the future cube-plugin surface. node:fs is a per-request ephemeral sandbox; node:child_process is a stub. Local-only features fail loud with a classified requires_local_runtime error — never silently degrade.
  • prod — workers-prod. wrangler deploy --env prod targets the prod D1 (400484e4-f299-499d-82a8-2a8da676df44). Same sandboxing and same requires_local_runtime contract as dev.

Three D1 databases, one per environment, identical schema (schema doc §5 Q2). Deploy convention: deploy:local runs the local start command; deploy:dev and deploy:prod run the matching wrangler deploy --env invocations. The workers deploys are for demos and the future cube-plugin surface today; they are not the Ozzy Dev product.

The canonical runtime contract lives in the Cubicle SDK — see `_NEXARTIS-SDKs/nexartis-cubicle-sdk/docs/sdk-architecture.md`. The relevant machinery: the HB3 container adapter vs the cloudflare-dynamic-worker adapter, and the x-nexartis-runtime.supportedCubicles field on the KYM Agent Card that declares which runtime an agent supports. This document does not duplicate those definitions; it cross-references them.

Product direction. Ozzy Dev's local runtime is the harness we use to determine the default Cubicle tool loadout and the runtime-capability categorization of every agent and skill. Preferred loadout is the cloudflare-dynamic-worker adapter (fast cold starts, cheap, no supervisor); containers via the HB3 adapter are reserved for agents that genuinely require them. The HB3 Cloudflare container runtime itself ships after MVP — local Mac + dynamic-workers is the MVP surface.

Cloud exposure + domains. Per fleet convention (mirroring helix-command-center/wrangler.jsonc), each workers env carries a custom domain: dev.ozzydev.com (dev) and ozzydev.com (prod); the corresponding nexartis-ozzydev-console-{dev,prod}.workers.dev names remain as Cloudflare-managed fallbacks. Both workers runtimes are gated by @nexartis/sentinel-sdk (fleet pattern): hooks.server.ts runs the Sentinel handle, then a Sentinel admin authorization wrap. Public on workers: GET / (landing), the entire /auth surface (login, magic-link processing, verify/verified, magic-success, unauthorized), the /api/auth/* catch-all handler, and framework/static assets. Registration is not self-serve on this console — every mutating request to /api/auth/register is short-circuited with a loud classified 403 forbidden before Sentinel's handler runs, since accounts are provisioned inside the Sentinel system directly. Every other page and every other /api/* requires locals.user.roles containing admin — unauthenticated pages 302 to /auth?redirect=…, unauthenticated APIs return the classified admin_auth_required (401), authenticated non-admin pages 302 to /auth/unauthorized, and authenticated non-admin APIs return forbidden (403). The local runtime is completely unaffected — it is the machine trust boundary, and agent tooling (telemetry-emit, doctor, memory admin) depends on ungated 127.0.0.1:5173.

13. Related documents

  • Roadmap — waves, exit criteria, current sprint, tech-debt log — lives in the console at /roadmap (console/src/routes/roadmap/+page.svelte).
  • Research — long-lived technical white papers, in the console at /research.
  • `OPENCODE_VENDORING.md` — how the vendored fork is kept in sync.
  • `DECISIONS/` — architectural decision records.
  • `../AGENTS.md` — repo-specific agent behavior and the Nexartis release convention.