Middleware

Risk gate

Asks for approval only when a call's arguments actually look dangerous.

Slot: policy · Ships: off · Enable per agent in agent.toml

When you want it

Agents where blanket per-tool approval interrupts too often. Autonomy decides which tools need approval; this decides which calls do.

Turning on require_approval for a file-writing tool asks about every write, including the fifty harmless ones into a scratch directory. Point risk_gate at the paths you actually care about and leave the tool on full autonomy: routine writes go straight through, and the one that touches /etc stops for you.

Turn it on

[middleware.risk_gate]
enabled = true
protected_paths = ["/etc", "/var", ".ssh"]
destructive_patterns = ["rm -rf", "DROP TABLE", "git push --force"]

Both lists are empty by default, so enabled = true on its own gates nothing. The middleware is only as good as the lists you give it.

Settings

KeyDefaultWhat it does
protected_paths[]Path prefixes whose modification always requires approval.
destructive_patterns[]Substrings that require approval when they appear in an argument.

A typo in any of these keys fails the boot rather than being silently ignored.

How a call is judged

Every string argument of the call is inspected, however the tool names its arguments - path, file, command, an array of argv words, or a string nested inside an object. If any one of them trips a rule, the call needs approval; otherwise it proceeds untouched.

protected_paths applies to tools that write. A file:// URL is matched as the path it names. Every word in an argument is treated as a possible path - a / is not required, because the file tools take a bare relative path and .env is the natural call shape - and each is resolved lexically first, so . and .. cannot smuggle a write past the list: /srv/app/../../etc/hosts is judged as /etc/hosts. URLs are skipped - https://example.com/etc/passwd is a remote address, not a local file.

An entry starting with / anchors at the filesystem root and matches that directory and everything under it. An entry that does not, like .ssh, matches a directory of that name anywhere - both ~/.ssh/id_rsa and /home/dana/.ssh/config. Matching is by whole path component, so .ssh does not match .sshrc.

A plain relative argument such as etc/hosts sits under a working directory this middleware cannot see, so it is judged against your relative entries only. The exception is an argument whose .. climbs out of that directory: where it lands is unknown, so it is compared against the absolute entries as well, and ../../etc/hosts still asks.

destructive_patterns applies to every call, reading or writing, since a command string is dangerous regardless of how the tool declares itself. It is a literal substring match on lowercased text with whitespace runs collapsed, so rm -rf also catches RM -rf. An array's string elements are additionally matched as one space-joined command, so ["rm", "-rf", "/srv/build"] trips rm -rf - and a stray number among them, as in ["rm", "-rf", "/data", 0], does not let the command past.

It catches the spelling you configure and not every equivalent command: rm -rf does not match rm -fr, rm -r -f, or rm --recursive --force. Configure the spellings you expect.

What it does not change

Only the approval decision. The tool result the user sees, the transcript the model reads, and the audit line are all exactly what they would be without it - an approved call runs and reports normally, and the audit entry simply carries approval_status.

Raising the gate is one-way: a call that autonomy already gated stays gated no matter what this middleware decides, and this middleware can never approve anything on the user's behalf.

Caveats

  • Paths only gate writes. A protected path stops modification, not reading. If you also want reads of a directory to ask first, set that tool's autonomy to require_approval instead.
  • A tool that misreports its own operation type slips through the path rules. The write check keys off the tool's declared operation type, so a tool that declares itself read-only but modifies files is judged by destructive_patterns alone.
  • ~ is not expanded. The agent's home directory is not known here, so protect home-relative locations with a relative entry (.ssh, .aws) rather than /home/....
  • Patterns match anywhere in any string. Keep them specific: rm -rf rather than rm, or an agent that merely quotes a command in a search query will interrupt the user.
  • This is a heuristic, not a sandbox. A determined prompt can phrase a destructive command in a way no substring list covers. Use it to cut interruptions on an agent you already trust, not as the thing that makes an untrusted agent safe - that is what autonomy levels and the sandbox are for.

Check it is loaded

axl weave print --config axl-config/agents/<your-agent>/agent.toml

risk_gate appears in the policy band of the printed stack.

Next

On this page