Architecture
System Overview
Clawker is a CLI tool that manages isolated Docker containers for AI coding agents. It has three main subsystems that work together: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, up, down, enable, disable, bypass, rotate-ca | Egress firewall control |
monitor | init, up, down, status | Observability stack (Grafana, Prometheus) |
loop | iterate, status, tasks, reset | Autonomous agent loops |
settings | edit | User settings TUI editor |
init → project init, build → image build, run/start → container run/start, generate, version
Shared domain logic (container creation, loop orchestration) 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 |
firewall | config, logger, storage | Envoy+CoreDNS firewall: daemon, config gen, PKI, rules |
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 (Grafana, 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. Ownsprojects.yamlregistry 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,Firewall,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 |
firewall.FirewallManager | internal/firewall | Envoy+CoreDNS firewall lifecycle, rules, bypass |
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