OpenClaw Safe Bins: Fast Stream Filters Without File Access
Read from search, close with the playbook
If this post helped, here is the fastest path into the full operator setup.
Search posts do the first job. The preview, homepage, and full playbook show how the pieces fit together when you want the whole operating system.
Most agent command policies fail in one of two boring ways. They either block every shell command until the agent becomes slow and dependent on the operator, or they approve too much and quietly turn a helper into a host-level process runner.
OpenClaw safe bins are the middle lane for a narrow class of commands: small stream filters that read stdin, transform text, and return output. They are not a replacement for exec approvals. They are not a generic allowlist. They are a fast path for operations that should not need a prompt every time, provided the command cannot pivot into file reads, interpreter execution, shell expansion, or broad filesystem traversal.
The buyer problem is practical. An operator wants an agent to summarize logs, trim command output, count lines, pick columns, normalize whitespace, and inspect the first few rows of a stream. That work should be quick. But the same operator does not want to accidentally authorize python -c, node -e, recursive grep, jq environment dumps, or a package-manager binary shadowed through a writable PATH.
Safe bins exist for that exact gap.
What safe bins actually are
The OpenClaw exec docs define tools.exec.safeBins as stdin-only binaries that can run in allowlist mode without explicit allowlist entries. The default safe bins are cut, uniq, head, tail, tr, and wc. That list is intentionally small because the trust model is intentionally small.
A safe bin should act on an incoming stream. It should not need a filename. It should not evaluate code. It should not open scripts, plugins, modules, or recursive directory trees. It should not write output files as a side effect. If a command needs those powers, use an explicit allowlist entry and keep the approval prompt path available.
A minimal safe-bin posture looks like this:
{
tools: {
exec: {
safeBins: ["cut", "uniq", "head", "tail", "tr", "wc"],
safeBinTrustedDirs: ["/bin", "/usr/bin"],
strictInlineEval: true
}
}
} The key point is not the syntax. The key point is the boundary: safe bins are command-specific stdin transforms, while the normal allowlist is explicit trust for executable paths. Those are different jobs.
Why stdin-only matters
File arguments are where a harmless-looking filter becomes a filesystem read surface. wc on a stream is useful. wc /private/file is different. head on piped build output is useful. head ~/.ssh/config is different. Safe-bin mode rejects positional file arguments and path-like tokens so the command can operate only on data already flowing through the pipeline.
That lets an agent run simple transforms without asking the operator to approve each one:
printf '%s\n' "$ROWS" | cut -d, -f1 | sort | uniq | head -20 That pipeline is not a promise that every command in it is safe. It is a policy question. The docs say shell chaining and pipelines are allowed in allowlist mode only when every top-level segment satisfies the allowlist, safe-bin policy, or skill auto-allow path. Redirections remain unsupported in allowlist mode. That is the right shape: each segment has to earn trust independently.
There is another subtle protection here. Safe bins validate from argv shape, not host filesystem existence checks. That avoids a file-existence oracle where allow or deny behavior leaks whether a path exists on the host. The command is judged by what the argument structure is allowed to mean, not by probing the machine for secret paths.
The unsafe commands are usually obvious
When I review agent exec policy, I look for commands that can escape the stream-filter mental model. These are the patterns I do not want in safeBins:
python3 -c 'import os; print(os.environ)'
node -e 'require("fs").readFileSync("/etc/passwd", "utf8")'
jq -n env
grep -R password .
sort -o output.txt Interpreters and runtimes are the clearest examples. python3, node, ruby, bash, sh, and zsh can evaluate code, execute scripts, load modules, spawn subcommands, and read files by design. The exec approval docs explicitly warn against putting interpreter or runtime binaries in safeBins. If you need them for real work, approve them as broader tools, enable tools.exec.strictInlineEval, and let risky inline forms take the approval path.
Broad Unix tools deserve the same skepticism. grep is excellent, but recursive flags turn it into a directory crawler. sort is useful, but output and temporary-file flags can change the side-effect profile. jq is powerful, but the docs call out broad program semantics and the env builtin. OpenClaw now warns when broad-behavior tools are added to safeBins, because that weakens the low-risk stream-filter model.
Want the operator version of this policy, not just the config keys?
ClawKit turns these boundaries into reusable agent operating patterns for shell access, memory, channel safety, and production recovery. Get ClawKit for $9.99.
Default denials are doing real work
The safe-bin docs list denied flags for default and common opt-in profiles. Examples include sort -o, sort --output, sort --files0-from, sort --compress-program, sort --random-source, sort --temporary-directory, wc --files0-from, jq -f, jq --from-file, grep -f, and recursive grep flags such as -r and -R.
Those flags are not random. They are the point of the feature. Safe-bin mode has to block the options that turn a stream filter into a file reader, file writer, program loader, temp-file workflow, or recursive scanner. It also validates long options fail-closed, so unknown flags and ambiguous abbreviations are rejected instead of guessed.
For grep, the docs add a useful detail: if you opt grep into safe-bin mode, provide the pattern with -e or --regexp. Positional pattern form is rejected because a positional token can become ambiguous with a file operand. That is a small inconvenience, but it keeps the policy deterministic.
Custom profiles are a contract, not decoration
Custom safe bins must define an explicit profile under tools.exec.safeBinProfiles.<bin>. OpenClaw can scaffold missing custom profiles with openclaw doctor --fix, but the docs are clear that an empty scaffold is something to review and tighten afterward. It is not a security design by itself.
If you intentionally opt in a tool such as grep, the profile should describe the command you are willing to run, not the command's full feature set:
{
tools: {
exec: {
safeBins: ["grep"],
safeBinProfiles: {
grep: {
minPositional: 0,
maxPositional: 0,
allowedValueFlags: ["-e", "--regexp"],
deniedFlags: ["-r", "-R", "-f", "--file", "--recursive"]
}
}
}
}
} That example keeps the positional count at zero and allows explicit regex value flags. It also denies file and recursive forms. The exact profile you use should match your operations, but the principle holds: allow the transform you need, not the binary's entire personality.
Trusted directories close the PATH hole
Safe bins must resolve from trusted binary directories. The default trusted directories are /bin and /usr/bin. The docs explicitly say PATH entries are never auto-trusted. If a safe-bin executable lives in a package-manager or user path such as /opt/homebrew/bin, /usr/local/bin, /opt/local/bin, or /snap/bin, add that directory to tools.exec.safeBinTrustedDirs intentionally.
This matters because command names are easy to spoof. If policy allowed whatever head appears first in a mutable PATH, an attacker or broken setup could replace a trusted stream filter with a different executable. Resolving safe bins only from trusted directories keeps the fast path tied to known binaries.
There is still operator judgment here. Adding a writable project directory to safeBinTrustedDirs defeats the point. If a directory is group-writable, world-writable, or controlled by the same workflow the agent can edit, it is a poor trust root.
How I would run this in production
For a production agent, I would start with safe bins enabled only for the defaults. Then I would make each broader command prove why it belongs somewhere else:
- Keep
cut,uniq,head,tail,tr, andwcas stdin-only helpers. - Use explicit executable-path allowlist entries for tools that read files, inspect repos, run builds, or call APIs.
- Keep
ask="on-miss"or stricter for host exec so unexpected commands still reach a review path. - Set
askFallback="deny"so missing UI does not become silent permission. - Enable
strictInlineEvalbefore allowing interpreters or runtimes. - Run
openclaw security auditand treat safe-bin warnings as real policy drift.
This setup keeps routine stream work fast without pretending that every CLI is a harmless text filter. It also gives you a clean explanation when something is denied: the command tried to cross from stdin transform into file access, interpreter behavior, untrusted binary resolution, or an option the profile does not allow.
The operator payoff
The best agent systems do not ask the human to review trivia. They also do not turn convenience into ambient root-like trust. Safe bins are useful because they remove approval friction from the boring text transforms while preserving the bigger approval model for commands that deserve scrutiny.
That is the line I want in an operator workflow. Let the agent trim output, count rows, and reshape streams quickly. Make it ask before it reads files, runs code, loads scripts, writes output, walks directories, or trusts a binary from a mutable path.
Fast agents are good. Explainably fast agents are better.
Want the complete guide? Get ClawKit — $9.99