Directory layout
A harness is a directory whose name is the harness’s component name:harnesses/ directory to distribute it:
The manifest
harness.yaml declares everything clawker’s engine needs outside template
rendering — how to resolve the agent’s version, which stacks it needs, the
volumes it keeps state in, what to seed and stage into those volumes, where the
agent reads managed context from, and the egress the firewall must allow.
version
Declares how the agent’s version is resolved and rendered into the build
fragment.
stacks
The stack dependencies this harness’s fragment needs, resolved by the same
algorithm as every selection surface: a bare name resolves user loose → project
loose → built-in floor; a qualified namespace.bundle.component address
resolves from an installed bundle. A bundled harness that depends on a stack it
ships alongside references it by its own qualified address.
managed_prompt
Where the agent reads managed context from. Clawker ships a managed
agent-context briefing — a harness-agnostic markdown file describing the
container environment, the firewall, and how to ask for help when a domain is
blocked. Declaring managed_prompt tells the build to bake that file into the
image at the location your agent loads managed instructions from. The content
is clawker’s, not the harness’s; the manifest only names the destination.
Omit the block entirely if the agent has no managed-context location — nothing
is copied.
The copy happens at build time, never at runtime, so the destination must
not sit under a declared volume (a volume mount would shadow it).
volumes
Each entry is a persisted directory that becomes a named volume mounted under
the container home. name is the volume-name suffix; path is
container-home-relative. Every seeds/staging destination must fall under a
declared volume.
seeds
First-boot content applied to a volume by clawker’s init step. file is a
bundle-relative source under assets/; dest is container-home-relative; and
apply is one of copy-if-missing, copy-if-missing-or-empty, or json-merge.
staging
The create-time host→container copy of agent state that lives outside the
workspace (the workspace itself arrives via mount and is never staged). Every
entry is an explicit src→dest directive; nothing is copied by convention.
src expands ~, $VAR/${VAR}, and ${VAR:-fallback}; dest lands in a
declared volume. copy entries can narrow what is copied (for example, an
allowlist of JSON keys) so host secrets never travel into the container.
egress
The egress floor — the domains the firewall must allow for the agent to
function, composed with the project’s security.firewall rules. Entries use the
same rule vocabulary as project firewall rules (dst, optional proto/port,
optional path_rules), so a harness can allow a host broadly or scope it to
specific paths. See Firewall for the rule grammar.
The Dockerfile fragment
Dockerfile.harness.tmpl is the install fragment for this harness’s image — it
installs the agent CLI and sets its CMD, and renders on top of the shared base
image. The resolved version and the harness’s stacks are available to it.
The shipped claude and codex harnesses are the best working reference for
the fragment shape, the seed/staging manifests, and the egress floor.
The fragment is a set of {{define "<block>"}} bodies filling slots the master
template declares. Each slot is named for the permission scope it runs in and
the template event around it — the name tells you where you are in the build
timeline, not what to put there. A fragment may define any subset, and any
instructions may go in any block:
Defining any other template name is a validation error, and the project
inject-point names (
user_commands, before_entrypoint, …) are
reserved — a fragment can never override them.
Validating
Bundle a harness and validate it before publishing:clawker build does, so a
harness.yaml that would fail at build time fails here instead.