Skip to main content
A harness definition tells clawker how to build and run a coding-agent CLI. Authoring one means writing a manifest that declares the agent’s runtime needs plus a Dockerfile fragment that installs it. This page covers the harness directory; see Harnesses for selecting and running harnesses, and Authoring bundles for packaging one for distribution.

Directory layout

A harness is a directory whose name is the harness’s component name:
Place it in a loose convention directory to use it immediately, or inside a bundle’s 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 srcdest 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 egress floor is a security boundary. A container refuses to start if its harness label names a harness no longer available, precisely so it never runs with a weaker floor than it was built for. Declare exactly the domains the agent needs — no more.

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:
Validation loads each harness the same way clawker build does, so a harness.yaml that would fail at build time fails here instead.