Architecture
System Overview
Clawker is a CLI tool plus two long-lived daemons —clawker-cp (the control plane, one per host) and clawkerd (one per agent container) — that together manage isolated Docker containers for AI coding agents. The CLI is the root of trust; the daemons hold the security boundary at runtime.
CLI Command Structure
Commands are organized underinternal/cmd/ with subpackages per subcommand:
| Command Group | Subcommands | Purpose |
|---|---|---|
container | list, run, start, stop, kill, exec, attach, logs, inspect, cp, pause, unpause, restart, rename, remove, stats, top, update, wait, create | Docker container management |
image | list, build, inspect, remove, prune | Image lifecycle |
volume | list, create, inspect, remove, prune | Volume management |
network | list, create, inspect, remove, prune | Network management |
project | init, register, list, info, edit, remove | Project registration and config |
worktree | add, list, prune, remove | Git worktree management |
firewall | status, list, add, remove, reload, refresh, up, down, enable, disable, bypass, rotate-ca | Egress firewall control (all RPCs route through f.AdminClient to the CP daemon) |
controlplane | up, down, status, agents | Break-glass control plane lifecycle + registered agent listing |
auth | rotate | CLI-side auth material (CA, server certs, OAuth2 signing key) |
monitor | init, up, down, status | Observability stack (OTel Collector, OpenSearch, OpenSearch Dashboards, Prometheus) |
settings | edit | User settings TUI editor |
skill | install, show, remove | Manage the clawker-support Claude Code plugin |
init → project init, build → image build, run/start → container run/start, generate, version
Shared domain logic (container creation) lives in shared/ subpackages within each command group — not in library packages.
Package Dependency Graph
Packages follow a strict DAG with no cycles. Verified viagoda.
Leaf Packages (zero internal imports)
Importable by anyone. Depend only on stdlib or external libraries.| Package | Purpose |
|---|---|
storage | Generic layered YAML store engine (Store[T]): discovery, merge, provenance, atomic write |
git | Git operations, worktree management (go-git) |
logger | Struct-based zerolog (file rotation + optional OTEL bridge) |
text | Pure ANSI-aware text utilities (Truncate, PadRight, StripANSI) |
term | Terminal capabilities + raw mode (sole x/term gateway) |
signals | OS signal utilities (SetupSignalContext, ResizeHandler) |
build | Build-time metadata (version, date via ldflags) |
update | Background GitHub release checker (24h cached) |
keyring | Credential storage service |
pkg/whail | Reusable Docker engine library with label-based isolation |
Foundation Packages (import leaves only)
Universally imported infrastructure. Their imports are leaf-only.| Package | Imports | Purpose |
|---|---|---|
config | storage | Config loading/validation, schema types, path resolution. Composes Store[Project] + Store[Settings] |
iostreams | term, text | I/O streams, TTY detection, colors, styles, spinners, progress |
Domain Packages (import leaves + foundation)
Core business logic. Import leaves and foundation packages only.| Package | Imports | Purpose |
|---|---|---|
project | config, git, logger, storage, text | Project registration, worktree lifecycle, registry CRUD |
bundler | config + own subpkgs | Dockerfile generation, content hashing, semver, npm registry |
tui | iostreams, text | BubbleTea models, viewports, panels, progress display |
prompter | iostreams | Interactive prompts with TTY/CI awareness |
storeui | iostreams, storage, tui | Generic TUI editor for Store[T] instances |
controlplane | config, docker, logger, ory subprocess managers | CP daemon core: AdminService composition, Ory auth stack, startup orchestrator, agent watcher |
controlplane/agent | auth, consts, dockerevents, overseer, logger | CP-side agent surface: Dialer, sqlite Registry, Register handler, IdentityInterceptor, session/agent events |
controlplane/overseer | logger | Typed event bus + in-memory State worldview (containers + agents) |
controlplane/dockerevents | overseer, logger | Docker events feeder (reconnecting stream → typed events on overseer bus) |
controlplane/cpboot | config, docker, logger (+ embedded clawker-cp + ebpf-manager binaries) | Host-side CP lifecycle: EnsureRunning/Stop/CPRunning |
controlplane/adminclient | auth, consts | CLI-side AdminService gRPC dial (mTLS + auto-refreshing OAuth2 bearer) |
controlplane/firewall | config, docker, logger, storage, controlplane/firewall/ebpf | Firewall Handler (13 RPCs), Stack (Envoy+CoreDNS lifecycle), ActionQueue, config generators, certificate PKI, rules store |
controlplane/firewall/ebpf | logger | eBPF program loader/manager, SyncRoutes replaces global route_map atomically; break-glass ebpf-manager CLI under cmd/ |
controlplane/firewall/ebpf/netlogger | controlplane/firewall/ebpf, controlplane/overseer, controlplane/dockerevents, docker, logger, OTel SDK | Per-decision-point egress event emitter — drains the BPF events_ringbuf, enriches by cgroup ID with container/agent/project attribution via overseer enrollment events, emits OTLP log records via a *sdklog.LoggerProvider (service.name=ebpf-egress) |
dnsbpf | controlplane/firewall/ebpf | CoreDNS plugin that writes DNS resolutions to the BPF dns_cache map in real time (embedded in cmd/coredns-clawker) |
clawkerd | (embed only) | //go:embed assets/clawkerd — exports the per-container daemon binary; bundler drops it into every per-project image at /usr/local/bin/clawkerd |
hostproxy | config, logger | Host proxy for container-to-host communication |
socketbridge | config, logger | SSH/GPG agent forwarding via muxrpc |
containerfs | config, keyring, logger | Host config preparation for container init |
monitor | config | Observability stack templates (OTel Collector, OpenSearch, OpenSearch Dashboards, Prometheus) |
docs | config, storage | CLI documentation generation |
Composite Packages (import domain packages)
Higher-level subsystems that compose domain packages.| Package | Key Imports | Purpose |
|---|---|---|
docker | bundler, config, pkg/whail, pkg/whail/buildkit | Clawker middleware wrapping whail with labels/naming |
workspace | config, docker, logger | Bind vs Snapshot strategies |
cmdutil | config, docker, iostreams, tui, + type imports for Factory | Factory struct (DI container), error types, arg validators |
Import Rules
Configuration and Storage
Three packages form the configuration subsystem:storage(leaf) — GenericStore[T]engine. Handles file discovery (static paths + walk-up), YAML loading with migrations, N-way merge with provenance tracking, and atomic writes. Zero domain knowledge.config— Thin domain wrapper composingStore[Project]+Store[Settings]. Exposes theConfiginterface with schema types, path helpers, and ~40 accessor methods.project— Project domain layer. Owns theregistry.yamlproject registry via its ownStore[ProjectRegistry]. Handles registration CRUD, path resolution, worktree lifecycle.
Config interface and ProjectManager interface — never storage directly.
Dependency Injection: The Factory Pattern
Follows the GitHub CLI’s three-layer pattern:-
Wiring (
internal/cmd/factory/) — Creates*cmdutil.Factorywith all deps assync.Onceclosures. Called once at entry point. Tests never import this. -
Contract (
internal/cmdutil/) —Factoryis a pure struct with closure fields. Eager:Version,IOStreams,TUI. Lazy:Config,Client,ProjectManager,GitManager,HostProxy,SocketBridge,AdminClient,ControlPlane,HttpClient,Logger,Prompter. -
Consumers (
internal/cmd/*/) — Cherry-pick Factory closures into per-commandOptionsstructs. Run functions accept*Optionsonly.
Key Abstractions
| Abstraction | Package | Purpose |
|---|---|---|
storage.Store[T] | internal/storage | Generic layered YAML store with merge, provenance, and atomic write |
config.Config | internal/config | Typed config interface composing Store[Project] + Store[Settings] |
project.ProjectManager | internal/project | Project identity, registration CRUD, worktree orchestration |
whail.Engine | pkg/whail | Reusable Docker engine with label-based resource isolation |
docker.Client | internal/docker | Clawker middleware (labels, naming) wrapping whail |
cmdutil.Factory | internal/cmdutil | DI struct (closure fields); constructor in cmd/factory |
CreateContainer() | internal/cmd/container/shared | Single entry point for container creation |
adminclient.Dial | internal/controlplane/adminclient | CLI-side AdminService gRPC dial (mTLS + OAuth2) |
firewall.Handler | internal/controlplane/firewall | 13-method AdminService firewall surface inside the CP daemon |
firewall.Stack | internal/controlplane/firewall | Envoy + CoreDNS container lifecycle |
dnsbpf.Handler | internal/dnsbpf | CoreDNS plugin populating the BPF dns_cache map in real time |
ebpf.Manager | internal/controlplane/firewall/ebpf | BPF program loader; SyncRoutes replaces the global route_map atomically |
netlogger.Service | internal/controlplane/firewall/ebpf/netlogger | Drains BPF events_ringbuf, enriches by cgroup_id, emits OTLP log records (service.name=ebpf-egress) on the trusted infra lane |
controlplane.NewOtelLoggerProvider | internal/controlplane | Generic *sdklog.LoggerProvider factory (mTLS + infra cert, BatchProcessor, optional ExporterWrap) reused by netlogger and any future CP-side OTLP log emitter |
cpboot.Manager | internal/controlplane/cpboot | Host-side CP container lifecycle (EnsureRunning/Stop/CPRunning) |
agent.Registry | internal/controlplane/agent | sqlite-backed agent identity (CP is sole writer) |
overseer.Overseer | internal/controlplane/overseer | Typed event bus + worldview state inside CP |
cmd/clawker-cp | cmd/clawker-cp | CP daemon entry point — PID 1 of the clawker-controlplane container |
cmd/clawkerd | cmd/clawkerd | Per-container agent daemon — PID 1 of every agent container |
cmd/coredns-clawker | cmd/coredns-clawker | Custom CoreDNS entrypoint embedding dnsbpf; built into clawker-coredns:latest on demand |
tui.TUI | internal/tui | Factory noun for presentation layer |
Container Naming and Labels
Container names:clawker.project.agent (3-segment) or clawker.agent (2-segment when project is empty)
Volume names: clawker.project.agent-purpose (purposes: workspace, config, history)
Labels (all under dev.clawker.*):
| Label | Purpose |
|---|---|
managed | true — authoritative ownership marker |
project | Project name (omitted when empty) |
agent | Agent name |
version | Clawker version |
image | Source image reference |
dev.clawker.managed=true.
Presentation Layer
Commands follow a 4-scenario output model:| Scenario | Description | Packages |
|---|---|---|
| Static | Print and done | iostreams + fmt |
| Static-interactive | Output with y/n prompts | iostreams + prompter |
| Live-display | Continuous rendering, no input | iostreams + tui |
| Live-interactive | Full keyboard input, navigation | iostreams + tui |
- Only
iostreamsimportslipgloss - Only
tuiimportsbubbletea/bubbles - Only
termimportsgolang.org/x/term