Skip to content

hoophq/fence

Repository files navigation

🚧 Fence

Guardrails for AI coding agents.

Fence blocks the catastrophic command before your agent runs it — and stays silent for everything else.

CI  ·  License: MIT  ·  CLI  ·  Rules  ·  Architecture

An agent is asked to exfiltrate AWS credentials; Fence blocks the tool call before it runs

An agent is asked to send your AWS credentials to a URL. It reaches for the tool call — cat ~/.aws/credentials | curl … — and Fence blocks it before it runs, so the agent backs off. No denylist to evade: Fence parses the command and judges what it actually does.


Why Fence

AI agents run with your permissions. A confused — or prompt-injected — agent can delete your files, leak your keys, or wire up persistence, with nothing standing between it and your machine. The denylist "guardrails" floating around are substring matchers: trivially dodged (rm -fr, a script written then run), and so noisy you turn them off.

Fence is built the other way:

  • 🧠 Semantic, not substring. rm -rf ~, rm -fr ~, rm -r -f ~, sudo rm -rf $HOME — one dangerous intent, all caught.
  • 🎯 Near-zero false positives. rm -rf node_modules, git push --force-with-lease, npm install — never touched. This is the product.
  • 🚦 Block, ask, or allow. Unambiguous catastrophe is blocked; the plausibly-legit gets a confirm prompt; the everyday passes in silence.
  • 🔒 Permissive modes don't weaken it. Agent hooks run before the permission system, so a fence deny blocks even in auto-accept or --dangerously-skip-permissions sessions — where it's the only guardrail left standing.
  • 🪶 Fails open. If Fence can't parse something, the command runs. A guardrail must never brick the agent it protects.
  • 🧩 Agent-neutral. One portable rulepack. Claude Code, Codex, and OpenCode today; Cursor and Gemini next.

See it in the loop

It stops prompt injection, not just clumsy commands. A hidden instruction in a file steers the agent into rm -rf ~ — Fence blocks the tool call and tells you where it came from.

A prompt injection hidden in a Makefile tries to make the agent delete your home directory; Fence blocks it

More scenarios — asks when unsure · stays quiet on routine work · keeps secrets out of the model

Asks before an irreversible action — force-push, history rewrite:

Fence pauses a force-push and asks the human to confirm

Stays out of the way on everyday commands — near-zero false positives:

Fence lets rm -rf node_modules and npm install run without a prompt

Keeps secrets out of the model's context — even a cat with no network:

Fence asks before an agent reads cloud credentials into its context

Install

# Homebrew — macOS
brew install hoophq/tap/fence
# npm — macOS / Linux
npm install -g @hoophq/fence

Windows isn't supported natively yet — the hook path hasn't been verified there, and a silently broken hook is worse than an honest no. WSL works today (Fence behaves exactly as on Linux inside it); native support is tracked in #26.

Quickstart — Claude Code

fence init --global   # add the Fence hooks to .claude/settings.json, for every project

Start a Claude Code session and Fence is live — a status line at the bottom of the UI confirms it, for the whole session (🚧 Fence v1.2.0 · 1 pack · 19 rules). Already have a status line? Fence won't touch it — it announces itself with a chat banner instead. Ask the agent for something reckless — it gets stopped, or asked to confirm, with a 🚧 notice in the chat saying which rule fired. Allowed calls get a notice too, so you can see Fence watching; fence init --quiet turns those off.

claude

And here's how you leave: fence uninstall removes exactly the hooks init added and touches nothing else. Dev-owned means you can walk away cleanly.

Quickstart — Codex

Same guardrails, same rulepacks, one command:

fence init codex   # writes ./.codex/hooks.json (--global for ~/.codex)

Then run /hooks inside Codex once to trust the Fence entries (Codex only runs hooks you've approved). Shell commands and apply_patch file edits are screened by the same engine — one rulepack, every agent.

Quickstart — OpenCode

fence init opencode   # writes ./.opencode/plugins/fence.js (--global for ~/.config/opencode)

OpenCode has no hook protocol, so init generates a tiny plugin that pipes every shell command and file edit to the same engine. One honest caveat: OpenCode plugins can block a call but can't show an approval prompt, so ask rules stop the call and route the agent to you for confirmation instead.

All CLI commandsinit/uninstall, check (test a verdict without an agent), add/search (rulepacks), hook, and version.


What it stops

The recommended pack is embedded in the binary and always on:

It stops an agent from… like
wiping your home or root rm -rf ~ · sudo rm -rf / 🛑 deny
wiping a disk dd of=/dev/sda · mkfs.ext4 /dev/sdb1 🛑 deny
detonating a fork bomb :(){ :|:& };: 🛑 deny
exfiltrating a secret cat ~/.ssh/id_rsa | curl … 🛑 deny
opening up a system path chmod -R 777 / 🛑 deny
deleting outside your workspace rm -rf ~/.config/x ⚠️ ask
reading a key into its context cat ~/.aws/credentials ⚠️ ask
piping the web into a shell curl … | sh ⚠️ ask
rewriting git history git push --force · git reset --hard ⚠️ ask
installing off-registry npm i git+https://… · pip install git+… ⚠️ ask
injecting an install hook a postinstall added to package.json ⚠️ ask
setting up persistence crontab - · a LaunchAgent · systemctl enable ⚠️ ask

…and it is not fooled by flag reordering, sudo, $HOME vs ~, or a renamed fork bomb. Every detector ships with tests that pin both the catch and the safe cases.

Write your own rules & the full match reference


Make it yours

Layer your own rules with a ./.fence.yaml (auto-discovered) or --rules <file>:

rules:
  - id: no-terraform-destroy
    effect: deny
    match:
      shell: { command_in: [terraform] }
      regex: '\bterraform\b.*\bdestroy\b'

Or retune a built-in rule's effect in a single line — no need to redefine it:

overrides:
  git-force-push: deny                # ask  -> deny
  pipe-to-shell-from-network: allow   # silence it

Rules & overrides, in depth


Grab a rulepack

Guardrails others already wrote — Terraform, Kubernetes, production databases — install with one command and are active on the next tool call, in every project:

fence search                    # see what's published
fence add terraform-safety      # checksum-verified, then live everywhere

Compose them in a committed .fence.yaml (extends: [terraform-safety]) to pin a baseline for your whole team — and publishing your own pack is just a PR.

The registry: install, author, publish


How it works

agent tool call  →  adapter  →  engine (shell-AST facts)  →  rulepack  →  allow · ask · deny

An adapter normalizes each agent's tool call into a neutral action; the engine parses shell commands into semantic facts; rules match those facts and the most severe effect wins. The engine and rulepacks know nothing about any specific agent — which is what makes one rulepack portable across all of them.

Architecture & extension points


What Fence is — and isn't

Fence is local self-protection: it lives in your config, and you can edit or remove it. That's exactly right for protecting yourself from an agent's mistakes. It is honestly not a compliance control — a determined user (or an agent running as you) can disable anything on a machine they fully control. The threat model spells this out, including the known evasion paths and why they're accepted.

Need guardrails your developers can't turn off — centrally managed, enforced fleet-wide, with approval workflows and audit? That's a different trust model, and it's what hoop.dev does. Same idea, enforced where the developer can't override it.


Roadmap

  • Semantic detectors — deletes, disk wipes, fork bombs, exfiltration, world-writable, off-registry installs, manifest hooks, persistence
  • One-line installers — Homebrew, npm
  • A shareable rulepack registry — fence search · fence add <pack> · publish your own
  • A second agent — Codex (same rulepacks, zero engine changes)
  • A third — OpenCode (via its plugin surface, engine still untouched)
  • More agents — Cursor, Gemini CLI

License

MIT © hoop.dev — built by the team behind hoop.

About

Guardrails for AI coding agents — block catastrophic tool calls before they run. Semantic, agent-neutral, near-zero false positives.

Topics

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages