{"slug": "govern-your-coding-agent-in-one-command", "title": "Govern Your Coding Agent in One Command", "summary": "Ai2rules-harness, a new open-source tool, lets developers govern coding agents with a single command by installing a gate that denies tool calls based on destination path and taint state, not command shape. The tool, which runs locally without telemetry, blocks writes outside the project root and prevents prompt-injection attacks by denying externally-effectful actions after reading untrusted content. Its default additive mode only adds denials, ensuring users cannot be locked out.", "body_md": "# Govern Your Coding Agent in One Command\n\nStart with the part you can check yourself, in a directory you do not care about:\n\n```\nnpm install -g ai2rules-harness\nharness init\n```\n\nThen, without starting an agent session at all, ask the gate what it thinks of a\nwrite to `/etc/passwd`\n\n:\n\n```\necho '{\"tool_name\":\"Write\",\"tool_input\":{\"file_path\":\"/etc/passwd\"}}' \\\n  | CLAUDE_PROJECT_DIR=$PWD bash .claude/hooks/world-gate.sh\n{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"deny\",\n \"permissionDecisionReason\":\"the target path is read-only under the roots policy (path_scope_readonly)\"}}\n```\n\nNow change the path to something inside the project and run it again. Nothing comes back — silence means it passed. That difference is the entire product, and you just reproduced it in about five seconds.\n\n## What the install actually did\n\nFour files, all in your project, all readable:\n\n```\n.claude/cc-world.yaml         a manifest: what this project's agent may do\n.claude/hooks/world-gate.sh   a shim your host calls before every tool call\n.claude/settings.json         the hook entry, merged into whatever was there\n.gitignore                    ignores the runtime state and the kill-switch\n```\n\nNothing else. No daemon, no account, no telemetry, no network calls at runtime. The shim is nine lines of shell that hand the proposed call to a Rust binary and print its verdict.\n\nIt is safe to run twice. If you have already tuned the manifest, `init`\n\nkeeps it\nand says so; your existing hooks and settings survive untouched.\n\n## Why the deny is more interesting than it looks\n\nMost agent permission systems are lists of command strings: allow `git status`\n\n,\nask about `rm -rf`\n\n, deny `curl`\n\n. That model breaks in a specific and boring way —\nit can only recognise things it has seen written down before, and there are\ninfinitely many ways to write “delete that file”.\n\nThe deny above is not about the command’s shape. It is about **where the path\npoints**. The manifest declares the project directory as the writable root, and\nanything resolving outside it is refused whatever it looks like. `/etc/passwd`\n\n,\n`../../secrets`\n\n, a symlink, an absolute path assembled at runtime — same answer,\nbecause the question being asked is about the destination, not the spelling.\n\n## The one an allowlist cannot express at all\n\nHere is the sequence worth running, because no list of permitted commands can represent it. Fetch a web page:\n\n```\necho '{\"tool_name\":\"WebFetch\",\"tool_input\":{\"url\":\"https://example.com\"}}' \\\n  | CLAUDE_PROJECT_DIR=$PWD bash .claude/hooks/world-gate.sh\n# (silence — allowed)\n```\n\nNow fetch a *different* page, or run `curl`\n\n:\n\n```\n{\"permissionDecision\":\"deny\",\n \"permissionDecisionReason\":\"tainted context cannot reach an externally-effectful action (taint_invariant)\"}\n```\n\nThe identical call, allowed and then denied. Nothing about the command changed. What changed is that the session read something from the outside world in between, and the gate now treats everything downstream as carrying that influence.\n\nThis is the shape of a real prompt-injection attack: the model reads a web page\nor an issue body, that text says something persuasive, and the model then does\nsomething outward-facing with your credentials. A permission list sees two\nidentical `curl`\n\ncalls and has no vocabulary for “but one of them came after\nreading a stranger’s text”. This gate does, and it is the reason the project\nexists.\n\nYou can see the state it is tracking:\n\n```\ncat .claude/state/taint-default\n# tainted by WebFetch (WebFetch)\n```\n\nDelete that file and the session is clean again. It is a text file. That is deliberate — you should be able to read everything this thing believes.\n\n## Two modes, and the difference matters\n\n```\nharness init            # additive\nharness init --grant    # replace\n```\n\n**Additive** is the default and cannot lock you out. It only ever *adds* denials\nand prompts on top of your host’s existing permissions. If the gate vanishes,\nyou are back to normal.\n\n**Replace** (`--grant`\n\n) means the manifest becomes the allowlist: it returns an\nexplicit “allowed” and your host stops asking. That is genuinely better once you\ntrust your manifest — fewer prompts, one file deciding — and genuinely worse if\nyou have not read it. Start additive.\n\n## Turning it off is one file\n\n```\ntouch .claude/gate-off      # this project\ntouch ~/.claude/gate-off    # everywhere, right now\n```\n\nEffective on the very next tool call, no restart. Delete the file to re-enable.\n\nI put this section here rather than at the end because it is the reason it is reasonable to try any of this on a real project. A governance tool you cannot switch off in one second is a governance tool you should not install.\n\n## What this does not do\n\n**Governed is not sandboxed.** This decides what the agent may*do*; it does not contain*where it runs*. If you want both — and you probably do — this sits alongside a sandbox, not instead of one.**Depth depends on your host.** Deep where the host exposes a pre-execution hook, MCP-seam-only where it does not. What each tool actually permits is measured, including the cells where the answer is “we don’t know”, in[the Agent Governability Index](https://github.com/sv-pro/ai2rules/blob/main/docs/GOVERNABILITY-INDEX.md).**The starter manifest is a starting point.** It is a generic default that confines file writes to the project and treats network reads as tainting. It knows nothing about your project. Read it; it is about ninety lines of YAML.**Prefer the global install over**`npx`\n\nfor real use.`npx ai2rules-harness init`\n\nworks and is the fastest way to see a verdict, but the shim records the path of the binary that ran it, and under`npx`\n\nthat path lives in a temporary cache. When the cache is cleared the binary is gone — and the shim then**fails open**, which is the right behaviour for a missing kernel and a surprising place to discover it. After installing globally, re-run`harness init`\n\n.\n\n## If you want to look further\n\nThe gate is a Rust kernel: a verdict is a pure function of the proposed call, the session’s accumulated context, and the compiled manifest. No model is consulted at decision time, which is what makes the same inputs always produce the same verdict and makes every decision replayable afterwards.\n\n- Package:\n— no dependencies; the install fetches a checksum-verified prebuilt binary.`ai2rules-harness`\n\n- Source, and the argument in full:\n[github.com/sv-pro/ai2rules](https://github.com/sv-pro/ai2rules). - A longer offline tour, nine stops, no credentials:\n.`docs/TUTORIAL.md`\n\nIf you run it and something behaves differently from what is written here, that is worth an issue — the whole point of a deterministic gate is that its behaviour is supposed to be a fact you can check, not a claim you have to accept.", "url": "https://wpnews.pro/news/govern-your-coding-agent-in-one-command", "canonical_source": "https://ai2rules.dev/blog/govern-your-coding-agent-in-one-command/", "published_at": "2026-08-10 00:00:00+00:00", "updated_at": "2026-08-15 07:42:42.551963+00:00", "lang": "en", "topics": ["ai-tools", "ai-safety", "developer-tools"], "entities": ["ai2rules-harness"], "alternates": {"html": "https://wpnews.pro/news/govern-your-coding-agent-in-one-command", "markdown": "https://wpnews.pro/news/govern-your-coding-agent-in-one-command.md", "text": "https://wpnews.pro/news/govern-your-coding-agent-in-one-command.txt", "jsonld": "https://wpnews.pro/news/govern-your-coding-agent-in-one-command.jsonld"}}