RUNTIME GOVERNANCE
What actually stops an agent mid-task
Four ways to sit between an agent and the machine it is working on. They are not four versions of the same product: each one stands in a different place, and where a tool stands decides what it can possibly see. Read the stances first, because they explain every row that follows.
Where each one stands
The table
Commands the agent runs
The difference between refusing a string and understanding a command shows up entirely in this group.
| What is being asked | node9 | agentsh | pipelock | No tool (built-in approval) |
|---|---|---|---|---|
| Stops a destructive command before it executesdocker system prune -af --volumes, on a machine with local databases | ● | ● | ● | ◐asks, and a tired human clicks yes |
| Parses the command, so a policy can allow one and deny anotherallow git push, deny git push --force, without listing every spelling | ●AST | ● | ○seven regexes, no shell parser | ○ |
| Sees what a script does when the agent runs the scriptbash ./setup.sh, where the secret read is on line 4 of the file | ○ | ●syscall level | ○ | ○ |
| Governs the subprocesses a command spawnsnpm install runs a postinstall script that opens a socket | ○ | ●process tree | ◐containment, not policy | ○ |
| Gives the agent a way forward when an action is refusedan hour into an unattended task, a bare no ends the task and a retry loop burns the budget | ●returns the reason, the agent tries another way | ●substitutes a safer command | ○ | ○ |
Files and credentials
| What is being asked | node9 | agentsh | pipelock | No tool (built-in approval) |
|---|---|---|---|---|
| Blocks reads of credential files with nothing configuredcat ~/.ssh/id_rsa on a fresh install | ◐always on, but scoped to reading verbs | ●once a rule is written | ● | ○ |
| Still catches it when the path is built at runtimethe same read, through a variable or a command substitution | ○ | ●the value is real by then | ●matches the text | ○ |
| Keeps writes inside the workspacean edit that lands in ~/.bashrc instead of the project | ● | ●and can redirect them back | ● | ◐ |
| Makes a delete recoverablerm -rf on a directory that was not backed up | ○ | ●quarantine with restore | ○ | ○ |
Where the agent goes
| What is being asked | node9 | agentsh | pipelock | No tool (built-in approval) |
|---|---|---|---|---|
| Blocks cloud metadata and link-local addressesa fetch of 169.254.169.254 to lift the instance role | ◐shell and WebFetch, not MCP fetch | ● | ●immutable floor | ○ |
| Restricts outbound traffic to an allowlistthe agent posts a file to a host nobody approved | ◐off by default, and it recognises named clients rather than every form | ● | ● | ○ |
| Records DNS lookupsa lookup of a domain that resolves somewhere it should not | ○ | ● | ● | ○ |
| Reads inside encrypted traffic to scan the bodya secret in the body of an HTTPS POST | ○ | ◐its own LLM proxy only | ●opt-in TLS interception | ○ |
MCP servers and tools
| What is being asked | node9 | agentsh | pipelock | No tool (built-in approval) |
|---|---|---|---|---|
| Governs MCP tool calls, not just shella database tool that runs a delete without a shell in sight | ● | ● | ● | ◐ |
| Different permissions per MCP serverthe GitHub server may write, the scratch server may not | ● | ● | ● | ○ |
| Scans a server's response before the agent reads itan issue body carrying an instruction aimed at the model | ○records it, does not block | ○ | ●33 patterns | ○ |
| Notices a tool description that changed after you installed ita server that behaves for a week and then rewrites its own tools | ● | ● | ● | ○ |
What leaves the machine
| What is being asked | node9 | agentsh | pipelock | No tool (built-in approval) |
|---|---|---|---|---|
| Scans arguments for secrets before the call goes outan API key pasted into a tool argument | ●58 named provider credentials | ○its DLP only sees LLM traffic | ●65 patterns | ○ |
| Scans tool output on the way backa command whose output contains the contents of an env file | ● | ○scrubs LLM responses, not tool output | ● | ○ |
| Blocks a prompt injection rather than logging ita README that tells the agent to push its keys somewhere | ○detector exists, not on the live path | ○ | ● | ○ |
| Strips a secret before it reaches the model providera key in the conversation body on its way out to Anthropic or OpenAI | ○finds it afterwards, does not stop it | ●email, phone, card, SSN | ●parsers for three chat APIs | ○ |
| Hands the agent fake credentials and swaps them at the edgethe real token never sits in the environment the agent reads | ○specified, not built | ●credential substitution | ○ | ○ |
The team around the agent
Everything above is about one machine. This group is about the fact that an engineering team has many.
| What is being asked | node9 | agentsh | pipelock | No tool (built-in approval) |
|---|---|---|---|---|
| Agents wired with no integration workClaude Code, Codex, Cursor, Gemini, Copilot, OpenCode, Antigravity, Hermes, Pi | ●nine | ◐two | ◐two by hook, more via proxy | — |
| Tracks what the agent spends, per providerwhat the overnight run actually cost, broken down by model | ●per provider | ◐usage counters | ◐budget pressure, not spend | ◐ |
| Says what the agent spent in money, not in tokensthe number you can put in front of a finance team | ●priced per provider | ○token counts, no pricing | ○no pricing outside tests | ○ |
| Cuts off an agent that is burning moneya loop that retries the same failing call until the month's budget is gone | ◐cuts off a repeating call on the fifth, not a rising bill | ○ | ●ceilings on calls, retries and wall clock | ○ |
| One dashboard across everyone's machines, includedwho ran what, on which laptop, and what it was allowed to touch | ●included | ○ | ◐paid tier | ○ |
| Tamper-evident evidence, not just a logproving to an auditor that the record was not edited afterwards | ◐hashed for privacy, not chained | ◐ | ●signed receipts | ○ |
How to read it
Depth and reach are different products
agentsh is the deepest thing here. It sits under the process, so a script's fourth line is as visible to it as the command that started it, and node9 is honestly blind there. What it asks in return is that your work runs inside its session, and it wires two agents. pipelock owns the wire: response scanning, DNS, TLS interception, signed receipts. node9 sits at the tool call, which is the only place that knows which agent, which person, and how much. Those are three products, not three scores.
A refusal that explains itself is a design, not a fallback
node9 answers a refused call with the reason, and the agent reads it and tries another way. agentsh answers by substituting a command it considers safer. The substitution is coherent for agentsh because it sits under the process and can keep the whole world consistent around the swap; at the tool boundary the same trick would half-change reality, and a build that writes somewhere it will later read from would break with no explanation. It is also a guess about intent: adding --ignore-scripts to an install closes the postinstall path and breaks every package that legitimately needs it. What our approach costs is that it assumes a cooperative agent. A substitution still holds when the agent is confused or hostile; a reason only works on one that reads it.
The rows node9 loses are one shape
Scripts, subprocesses, and a path built at runtime are the same miss three times: a rule that reads the call the agent made, and not the world that call creates. It is the honest cost of standing at the tool boundary instead of the kernel, and it is the work we have named next rather than the work we hide.
The baseline column is the one that matters commercially
Most teams are not choosing between these three. They are running an agent with its built-in prompt, approving things at speed, and calling that a control. Every column beats that column. The argument worth having is which of the three you put in front of a team, not whether to have one.
Measured 9 September 2026 against agentsh main, pipelock v3.5.0, and node9 2.12.0, from their source and their own documentation.
The other question is on its own page: What each scanner actually looks at.