Skip to main content
Ops work is where Β« the agent ran a command Β» stops being acceptable and Β« the file says exactly which commands, and the runtime enforces it Β» starts to matter. This guide builds a Docker health report the audited way: two pinned docker reads, one local model, one kept artifact.

The whole workflow

Checked clean against nika 0.107.0 (nika check exit 0 Β· permits declared Β· 0 hints):

Why the exec tasks are arrays

Write shell: "docker ps | grep Up" and nika check refuses to bless the boundary:
That refusal is the feature. A shell string can smuggle any program through a pipe; the argv array names exactly one program with exactly those arguments, so permits.exec: ["docker"] is a boundary the checker can actually prove. Transforms that would have lived in | grep | awk belong in extract: bindings and nika:jq instead.

What the audit says before a token is spent

Two things worth noticing:
  • The two docker reads run in parallel (wave 1) β€” the scheduler proved it from the with: bindings, nobody ordered it.
  • The cost line practices the honesty rule: a local model is unpriced compute, not Β« free Β» β€” the report says FLOOR and names why, it never rounds unknown to $0.

Run it

Captured verbatim (Apple-silicon laptop Β· Docker Desktop Β· a small local thinking model β€” the two-minute infer is the model, not the engine):
docker-health.md now holds the model’s prose, and the run left a hash-chained journal you can prove later:

When the daemon is down

The failure is part of the design. With Docker stopped, the same run fails honestly and early β€” the model is never called, nothing is spent, and the card hands over:

Take it further

  • Gate an action on the report β€” add an agent: task allowed only nika:done, or a when: gate on a structured diagnose output, and the workflow can act on unhealthy containers, still inside the declared boundary. See patterns.
  • Schedule it β€” the file is the artifact: cron or CI runs nika run and every execution leaves a verifiable trace.
  • Structured instead of prose β€” give diagnose a schema: and the report becomes typed data downstream tasks can branch on. See testing for pinning it with a golden.
The two exec tasks carry the repo’s exec ledger (the header comment): every surviving exec: names why no builtin or MCP tool covers it yet, and what would remove it. When a Docker MCP server enters your stack, invoke: replaces both reads and the ledger empties β€” that is the native-first law working as intended.