Architecture
System Overview
Clawker is a CLI tool plus two long-lived daemons —clawkercp (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:
Top-level shortcuts:
init → project init, build → image build, run/start → container run/start, 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.Foundation Packages (import leaves only)
Universally imported infrastructure. Their imports are leaf-only.Domain Packages (import leaves + foundation)
Core business logic. Import leaves and foundation packages only.Composite Packages (import domain packages)
Higher-level subsystems that compose domain packages.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
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 (reserved purposes: workspace, history, clawker lifecycle volume; plus the volumes the harness bundle declares, e.g. config)
Labels (all under dev.clawker.*):
Labels are authoritative for resource ownership. Names are for human readability. Clawker refuses to operate on resources without
dev.clawker.managed=true.
Presentation Layer
Commands follow a 4-scenario output model:
Import boundaries (enforced):
- Only
iostreamsimportslipgloss - Only
tuiimportsbubbletea/bubbles - Only
termimportsgolang.org/x/term