Skip to main content

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.
The control plane is not the firewall — it is the supervisor that, among other things, owns the firewall lifecycle. See Control Plane for the user-facing CP guide and Firewall for the firewall guide.

CLI Command Structure

Commands are organized under internal/cmd/ with subpackages per subcommand: Top-level shortcuts: initproject init, buildimage build, run/startcontainer 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 via goda.

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) — Generic Store[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 composing Store[Project] + Store[Settings]. Exposes the Config interface with schema types, path helpers, and ~40 accessor methods.
  • project — Project domain layer. Owns the registry.yaml project registry via its own Store[ProjectRegistry]. Handles registration CRUD, path resolution, worktree lifecycle.
Commands access config through the Config interface and ProjectManager interface — never storage directly.

Dependency Injection: The Factory Pattern

Follows the GitHub CLI’s three-layer pattern:
  1. Wiring (internal/cmd/factory/) — Creates *cmdutil.Factory with all deps as sync.Once closures. Called once at entry point. Tests never import this.
  2. Contract (internal/cmdutil/) — Factory is a pure struct with closure fields. Eager: Version, IOStreams, TUI. Lazy: Config, Client, ProjectManager, GitManager, HostProxy, SocketBridge, AdminClient, ControlPlane, HttpClient, Logger, Prompter.
  3. Consumers (internal/cmd/*/) — Cherry-pick Factory closures into per-command Options structs. Run functions accept *Options only.

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 iostreams imports lipgloss
  • Only tui imports bubbletea/bubbles
  • Only term imports golang.org/x/term