Architecture

The in-front design. kaged is the long-lived parent. Everything else is a capability it supervises.

In front, not inside.

kaged sits in front of adjacent operator tooling — not inside it, not beside it. Adjacent tools are things kaged can call, install, or expose. Not bases. Not parents. Not peers.

                       ┌─────────────────────────────┐
  browser / mobile (web ui) 
                       └──────────────┬──────────────┘

                               [https/wss]

                       ┌──────────────┴──────────────┐
     cloudflare tunnel       
                       └──────────────┬──────────────┘

                       ┌──────────────┴──────────────┐
   oauth2 sidecar (workspace)
                       └──────────────┬──────────────┘

   ╔══════════════════════════════════╧═══════════════════════════════════╗
                             kaged daemon                                
    ┌──────────────────────────────────────────────────────────────────┐ 
      http/ws api · session mgr · pty broker · dsl runtime             
      primary agent orchestrator · subagent supervisor · plugins       
    └──────────────────────────────────────────────────────────────────┘ 
   ╚════╤══════════╤══════════╤══════════╤══════════╤══════════╤══════════╝
        │          │          │          │          │          │
     [llm api]  [pty]   [sandbox]   [plugin]   [storage]   [tunnel]
     claude/    bash/   bwrap/      oh-my-pi   sqlite      cloudflared
     openai/    tmux    firejail    ollama     (postgres)  api
     local               podman     custom

↳ Read top-to-bottom: operator → tunnel → auth → kaged → things kaged supervises.

Six moving parts.

Each component has a spec. Each spec was written before the code. The boundaries are intentional.

Daemon
Single binary. Long-lived. Owns the HTTP/WS API, session manager, PTY broker, DSL runtime, primary agent orchestrator, subagent supervisor, and plugin host.
→ specs/daemon.md
Web UI
React 19 + TanStack Router/Query + Zustand. Mobile-first. The terminal is xterm.js over the daemon's PTY WebSocket. Brand v0.2 applied throughout.
→ specs/ui/
Project DSL
YAML + Zod. Declares subagents, cages, interconnects, tools, and tasks. Federated config with URI-prefixed paths and local overlays.
→ specs/project-dsl.md
Sandbox
The [CAGED] mechanism. bwrap on Linux, firejail as alt. The DSL's cage: block compiles to sandbox config; the supervisor enforces it before spawning.
→ specs/sandbox.md
Plugin Host
External tools plug in via JSON-RPC line protocol over stdio. Subprocess by default. Language-agnostic. In-process discouraged for trust-boundary reasons.
→ specs/plugin-host.md
Storage
SQLite by default — file-backed, runs on any small Linux host. Schema covers projects, sessions, agent runs, checkpoints, audit log, and prompt history.
→ adr/0005

A real session, end to end.

One task, traced from the operator's phone through the daemon and back.

  1. OPERATOR (PHONE) "scrape the new bandcamp releases and prep a deploy" — POST /api/projects/music-site/messages
  2. DAEMON Routes to session "music-site/active". Persists message. Returns 202.
  3. SESSION MANAGER Primary agent loaded with project DSL context. Model + system prompt + subagent topology resolved.
  4. PRIMARY AGENT Decides: dispatch to scraper subagent. Calls subagent.invoke(name="scraper").
  5. SUPERVISOR Loads cage config from DSL. Spawns scraper in bwrap with allowlist. Streams stdout/stderr to daemon.
  6. SCRAPER SUBAGENT Hits bandcamp via cage's net allowlist. Writes results. Emits event "found_release".
  7. DSL INTERCONNECT scraper→deployer on found_release. Supervisor enqueues deployer invocation.
  8. DEPLOYER SUBAGENT Spawned in its own cage. Network scoped to k3s.local:6443. Begins deploy prep.
  9. OPERATOR (LAPTOP) Reopens browser on laptop. Reattaches to session — full transcript replayed via WebSocket. Hits ⏸ to inspect. Edits the plan. Hits ▶ resume.

↳ The session survived the operator switching devices.
↳ The cage prevented scraper from touching anything outside its allowlist.
↳ The debug pause was operator-initiated, not model-initiated.

Concentric rings.

Each ring is a boundary. Crossing a ring requires explicit policy. Subagents do not get host-level capabilities.

INTERNET
TUNNEL
AUTH SIDECAR
KAGED DAEMON
PRIMARY AGENT
SUBAGENT
[CAGED]

The daemon does not directly expose itself to the internet — the tunnel and the OAuth sidecar mediate. Everything below kaged is a capability. Everything above is access.

Intentionally not in v0.

These are not oversights. They're explicitly deferred with significant access-control implications.

Multi-operator collaboration
Single-operator is the v0 assumption. Multi-op is a v2 concern with significant access-control implications.
→ DEFERRED TO V2
Cross-daemon mesh
kaged-to-kaged interconnect. The DSL's interconnect block is designed not to preclude it.
→ DEFERRED TO V2
Distributed sandboxes
Sandboxes run on the same host as the daemon. Distributing them across hosts is out of scope.
→ DEFERRED TO V2