Architecture
Components, the request loop, durable state, and the rules the runtime keeps. For contributors and technical readers.
This is the technical companion to How Oktapod works.
Components
Oktapod is a TypeScript monorepo. The runtime is two long-running processes, core and gateway, plus a CLI.
| Path | Role |
|---|---|
apps/core | The runtime daemon: intake, planning, policy, approvals, execution, memory, scheduler, replay, audit. |
apps/gateway | The authenticated edge: serves the Control UI, checks access keys and Tailscale identity, relays push. |
apps/ctl | The oktapod CLI. |
apps/ui | The Control UI (web app), built with Lit and served by the gateway. |
apps/mcp | The MCP connector broker. |
apps/local-runtime-connectors | Supervisor for connectors that run next to the runtime. |
apps/observer | The lightweight observer for other machines. |
apps/desktop-installer | The Windows and Linux setup app. |
apps/landing | The public website. |
apps/shared | Shared Swift package for the Apple clients. |
connectors/* | Channel adapters: Discord, Slack, Telegram, web chat, Microsoft Teams, Google Chat, Matrix, IRC, voice. |
native/ios, native/android, native/macos | Native clients. |
packages/contracts | Canonical schemas, envelopes, and validators. |
packages/shared | Shared utilities. |
packages/coder | The first-party coding runtime that drives Codex, Claude, and Gemini. |
packages/connector-runtime-shared | Helpers between connectors and the runtime. |
Only core decides. The gateway, connectors, native apps, plugins, and coding engines are input and output boundaries: they translate, authenticate, and deliver, but do not own planning, policy, or memory.
The request loop
- Intake. A message arrives in a canonical envelope that carries
trace_id,idempotency_key,session_id,tenant_id, andrequest_id. - Policy. Each proposed action is evaluated by its effect: target, reach,
reversibility, blast radius, and risk tier. The result is a lane (
silent,report,hold, orask) and a recorded reason. - Planning. A typed plan of steps with dependencies. A deterministic plan critic rejects structurally broken plans (duplicate step IDs, missing action types, invalid timeouts) before anything runs.
- Execution. Steps run as a dependency graph within one run. Every mutation carries an idempotency key, so retrying a completed action returns its stored outcome instead of repeating the effect. Transient failures are not stored as final answers; they can be retried.
- Approvals. Gated steps park durably until a person resolves them; resolution dispatches the approved run.
- Proactive scheduling. A single-flight scheduler tick drives schedules, queued work, media processing, embeddings, retention, and health checks.
- Replay and audit. Every side effect is recorded as input, decision, action, and outcome.
Every stage that uses a model resolves it through one model picker. A stage may choose no model, but the choice is recorded.
Action types
core.*actions are canonical runtime actions with first-party policy, state, replay, and audit.tool.*actions are tool or integration entry points. A tool that routes into a core action declares itscanonical_action_typein the tool registry.- Action identity is defined once in
apps/core/src/action-taxonomy.ts.
Durable state
- Canonical state is SQLite, with
sqlite-vecfor vector search. Markdown files are projections, never the source of truth. - Memory writes carry provenance, confidence, retention class, and policy reason. Retrieval scope is enforced while candidates are selected.
- Growth is bounded. A daily retention pass soft-deletes expired records and hard-deletes old ones, and high-frequency writers are rate-limited at the source.
- Media (uploads, downloads, generated files) is tracked as durable items with hashes, provenance, retention, and scheduler-owned processing and generation jobs.
Hosted edge services
Two optional hosted services exist for Google and Microsoft sign-in. The account issuer approves one installation through a short email link. The OAuth broker exchanges authorization codes and refreshes tokens with its app credentials, encrypts results to the requesting installation, and holds only ciphertext briefly. Core still owns PKCE state, connected accounts, token custody, policy, and audit. Bring-your-own OAuth clients keep the exchange and refresh local.
Rules that do not change
- The core loop is first-party. External projects do not orchestrate it.
- The runtime is a TypeScript daemon.
- Canonical state lives in SQLite plus
sqlite-vec. - Adapters and connectors stay thin input and output boundaries.
- Every side effect is auditable as input, decision, action, and outcome.
- Every mutation is idempotent and replay-safe.
- High-risk proactive actions require approval by default.
- Coding engines run through the first-party direct runtime; unsupported engines need an explicit command.