@agentbee/guards
v0.1.2
Published
Pure, tested risk classifiers that decide which AI-agent actions need a human approval on an AgentBee hardware key.
Maintainers
Readme
AgentBee Guards 🐝
A guard is the smallest possible AgentBee contribution: one function that looks at a tool call and decides whether a human should approve it on the bee, and how hard (tap vs hold). Guards are pure, testable, and framework-agnostic - they plug into the Claude skill, the OpenClaw plugin, the MCP server, git hooks, or your own code.
classify(toolName, args, ctx) -> { tier, label } | null
null = not gated, let it run
L0-L2 = a quick TAP approves
L3-L4 = a deliberate HOLD approvesWhen a guard returns a tier, the integration calls the gate, the bee shows label,
a human decides, and an approved call gets a signed, ledger-logged receipt.
Available guards
| Guard | What it gates |
|---|---|
| prompt-injection-guard.js | Side-effecting actions triggered by external content (web/email/file/RAG) rather than the user. The prompt-injection backstop. |
| payments-guard.js | Money movement (pay/transfer/trade/checkout/refund), tier graduated by amount. |
| send-as-me-guard.js | Sending/posting/signing as you (email, social, DocuSign, reply-all, mass send). |
| exfil-egress-guard.js | Data leaving or being exposed (secret reads, PII export, make-public, external upload). |
| iam-access-guard.js | Identity/access changes (grant admin, create key, disable MFA, open firewall, approve JIT). |
| supply-chain-guard.js | Publishing artifacts the world pulls (npm publish, PyPI, container push, releases). |
| agent-governance-guard.js | Agent meta-actions (spawn sub-agent, self-escalate, agent-to-agent pay, exceed budget). |
| smart-home-guard.js | Physical/IoT actions (unlock door, disarm alarm, open garage, start vehicle, move robot, disable camera). |
| host-access-guard.js | Local-system control (sudo, rm -rf, pipe-to-shell, read SSH/cloud creds, disable security, install daemons, AppleScript, modify system files). |
Run the tests: npm test (70/70, CI-checked on every push). Each guard ships with
benign cases proving it does not gate safe actions.
Use a guard in your agent (3 ways)
Import the runner once; it composes every guard (first match wins):
const { decide } = require("./index.js");
const d = decide(toolName, args, ctx); // -> { tier, label, guard } | null1. Deterministic (recommended) — a tool-call hook. The guard decides, not the
model, so it cannot be skipped. See example-hook.js:
const d = decide(name, args, ctx);
if (d) execFileSync("python3", [GATE, d.label, scope, d.tier]); // bee must be pressed; fail-closedWire that into OpenClaw before_tool_call, Claude PreToolUse, an OpenAI Agents
tool wrapper, or a git/CI step. Choose guards with
decide(name, args, ctx, { use: ["iam-access", "payments"] }).
2. SDK call in your own code. Before the risky step:
if (decide(name, args)) await bee.approve({...}).
3. Cooperative (plain English). With the MCP request_approval tool added, just
tell the agent which classes to gate: "Gate IAM changes, payments, and anything
triggered by external content with AgentBee." This is softer — the model chooses to
call it — so use mode 1 when you need it actually enforced.
The prompt-injection guard needs
ctx.provenance("web"/"email"/"user"/…). If you do not supply it, that guard abstains and the others still gate dangerous actions on their own.
Write your own guard (the template)
Copy this, change the matcher, add tests, open a PR.
"use strict";
// AgentBee Guard: <what it protects>.
const RISKY = /\b(your|keywords|here)\b/;
function classify(toolName, args, ctx = {}) {
const s = (String(toolName) + " " + JSON.stringify(args || {}))
.toLowerCase().replace(/[_\-.]/g, " "); // normalise snake_case / kebab / dotted
if (!RISKY.test(s)) return null; // not my concern -> let other guards decide
return { tier: "L4", label: "Short human-readable action" };
}
module.exports = { classify };Rules for a good guard
- Fail safe. If you are unsure of the risk, return
L4(hold), nevernull. - Be specific. A guard should own one class of danger, not everything.
- Label for a human.
labelis what shows on the 1.47" screen and gets signed into the receipt - keep it short, plain, and truthful (it must match what runs). - Normalise names. Tool names come as
db_drop_table,db.drop,db-drop- replace[_\-.]with spaces before matching. - Pure + tested. No I/O in
classify. Add cases totest.js. PRs need green tests. - Never downgrade a destructive action below L3 without an explicit opt-in.
Then wire it in
Any integration can chain guards - first non-null wins:
const guards = [require("./prompt-injection-guard").classify,
require("./payments-guard").classify,
require("./your-guard").classify];
function decide(name, args, ctx) {
for (const g of guards) { const r = g(name, args, ctx); if (r) return r; }
return null;
}License & signature
Apache-2.0 (see LICENSE). Every release is signed with the AgentBee release
key (HSM-held); verify it with SIGNING.md. Built by CyberSecAI Ltd.
Contributions welcome under the same license.
