{"id":51620878,"url":"https://github.com/hoophq/fence","last_synced_at":"2026-07-12T19:30:23.516Z","repository":{"id":368865409,"uuid":"1285299512","full_name":"hoophq/fence","owner":"hoophq","description":"Guardrails for AI coding agents — block catastrophic tool calls before they run. Semantic, agent-neutral, near-zero false positives.","archived":false,"fork":false,"pushed_at":"2026-07-06T19:40:14.000Z","size":1252,"stargazers_count":14,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-06T21:13:46.657Z","etag":null,"topics":["ai-agents","ai-safety","claude-code","cli","developer-tools","go","guardrails","security"],"latest_commit_sha":null,"homepage":"https://hoop.dev/?utm_source=fence\u0026utm_medium=github\u0026utm_campaign=att-launch-072026","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hoophq.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-30T17:04:19.000Z","updated_at":"2026-07-06T19:40:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/hoophq/fence","commit_stats":null,"previous_names":["hoophq/leash","hoophq/fence"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/hoophq/fence","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoophq%2Ffence","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoophq%2Ffence/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoophq%2Ffence/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoophq%2Ffence/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hoophq","download_url":"https://codeload.github.com/hoophq/fence/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoophq%2Ffence/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35401732,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-12T02:00:06.386Z","response_time":87,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agents","ai-safety","claude-code","cli","developer-tools","go","guardrails","security"],"created_at":"2026-07-12T19:30:22.871Z","updated_at":"2026-07-12T19:30:23.507Z","avatar_url":"https://github.com/hoophq.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# 🚧 Fence\n\n### Guardrails for AI coding agents.\n\nFence blocks the catastrophic command **before** your agent runs it —\nand stays silent for everything else.\n\n[![CI](https://github.com/hoophq/fence/actions/workflows/ci.yml/badge.svg)](https://github.com/hoophq/fence/actions/workflows/ci.yml)\n\u0026nbsp;·\u0026nbsp; ![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n\u0026nbsp;·\u0026nbsp; [CLI](docs/cli.md) \u0026nbsp;·\u0026nbsp; [Rules](docs/rules.md) \u0026nbsp;·\u0026nbsp; [Architecture](docs/architecture.md)\n\n\u003cimg src=\"docs/assets/deny.gif\" alt=\"An agent is asked to exfiltrate AWS credentials; Fence blocks the tool call before it runs\" width=\"860\"\u003e\n\n\u003c/div\u003e\n\nAn agent is asked to send your AWS credentials to a URL. It reaches for the tool\ncall — `cat ~/.aws/credentials | curl …` — and **Fence blocks it before it runs**,\nso the agent backs off. No denylist to evade: Fence parses the command and judges\nwhat it actually _does_.\n\n---\n\n## Why Fence\n\nAI agents run with **your** permissions. A confused — or prompt-injected — agent\ncan delete your files, leak your keys, or wire up persistence, with nothing\nstanding between it and your machine. The denylist \"guardrails\" floating around\nare substring matchers: trivially dodged (`rm -fr`, a script written then run),\nand so noisy you turn them off.\n\nFence is built the other way:\n\n- 🧠 **Semantic, not substring.** `rm -rf ~`, `rm -fr ~`, `rm -r -f ~`,\n  `sudo rm -rf $HOME` — one dangerous intent, all caught.\n- 🎯 **Near-zero false positives.** `rm -rf node_modules`,\n  `git push --force-with-lease`, `npm install` — never touched. This *is* the\n  product.\n- 🚦 **Block, ask, or allow.** Unambiguous catastrophe is blocked; the\n  plausibly-legit gets a confirm prompt; the everyday passes in silence.\n- 🔒 **Permissive modes don't weaken it.** Agent hooks run *before* the\n  permission system, so a fence `deny` blocks even in auto-accept or\n  `--dangerously-skip-permissions` sessions — where it's the only guardrail\n  left standing.\n- 🪶 **Fails open.** If Fence can't parse something, the command runs. A\n  guardrail must never brick the agent it protects.\n- 🧩 **Agent-neutral.** One portable rulepack. Claude Code, Codex, and OpenCode\n  today; Cursor and Gemini next.\n\n---\n\n## See it in the loop\n\n**It stops prompt injection, not just clumsy commands.** A hidden instruction in\na file steers the agent into `rm -rf ~` — Fence blocks the tool call and tells you\nwhere it came from.\n\n\u003cimg src=\"docs/assets/inject.gif\" alt=\"A prompt injection hidden in a Makefile tries to make the agent delete your home directory; Fence blocks it\" width=\"860\"\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eMore scenarios\u003c/b\u003e — asks when unsure · stays quiet on routine work · keeps secrets out of the model\u003c/summary\u003e\n\n\u003cbr\u003e\n\n**Asks before an irreversible action** — force-push, history rewrite:\n\n\u003cimg src=\"docs/assets/ask.gif\" alt=\"Fence pauses a force-push and asks the human to confirm\" width=\"820\"\u003e\n\n**Stays out of the way on everyday commands** — near-zero false positives:\n\n\u003cimg src=\"docs/assets/safe.gif\" alt=\"Fence lets rm -rf node_modules and npm install run without a prompt\" width=\"820\"\u003e\n\n**Keeps secrets out of the model's context** — even a `cat` with no network:\n\n\u003cimg src=\"docs/assets/secret-read.gif\" alt=\"Fence asks before an agent reads cloud credentials into its context\" width=\"820\"\u003e\n\n\u003c/details\u003e\n\n---\n\n## Install\n\n```bash\n# Homebrew — macOS\nbrew install hoophq/tap/fence\n```\n\n```bash\n# npm — macOS / Linux\nnpm install -g @hoophq/fence\n```\n\n**Windows** isn't supported natively yet — the hook path hasn't been verified\nthere, and a silently broken hook is worse than an honest no. **WSL works\ntoday** (Fence behaves exactly as on Linux inside it); native support is\ntracked in [#26](https://github.com/hoophq/fence/issues/26).\n\n## Quickstart — Claude Code\n\n```bash\nfence init --global   # add the Fence hooks to .claude/settings.json, for every project\n```\n\nStart a Claude Code session and Fence is live — a banner in the chat confirms\nit (`🚧 Fence is guarding this session…`). Ask the agent for something\nreckless — it gets stopped, or asked to confirm, with a `🚧` notice in the chat\nsaying which rule fired. Allowed calls get a notice too, so you can see Fence\nwatching; `fence init --quiet` turns those off.\n\n```bash\nclaude\n```\n\nAnd here's how you leave: `fence uninstall` removes exactly the hooks `init`\nadded and touches nothing else. Dev-owned means you can walk away cleanly.\n\n## Quickstart — Codex\n\nSame guardrails, same rulepacks, one command:\n\n```bash\nfence init codex   # writes ./.codex/hooks.json (--global for ~/.codex)\n```\n\nThen run `/hooks` inside Codex once to trust the Fence entries (Codex only\nruns hooks you've approved). Shell commands and `apply_patch` file edits are\nscreened by the same engine — one rulepack, every agent.\n\n## Quickstart — OpenCode\n\n```bash\nfence init opencode   # writes ./.opencode/plugins/fence.js (--global for ~/.config/opencode)\n```\n\nOpenCode has no hook protocol, so `init` generates a tiny plugin that pipes\nevery shell command and file edit to the same engine. One honest caveat:\nOpenCode plugins can block a call but can't show an approval prompt, so `ask`\nrules stop the call and route the agent to you for confirmation instead.\n\n**→ [All CLI commands](docs/cli.md)** — `init`/`uninstall`, `check` (test a\nverdict without an agent), `add`/`search` (rulepacks), `hook`, and `version`.\n\n---\n\n## What it stops\n\nThe **recommended** pack is embedded in the binary and always on:\n\n| It stops an agent from… | like | |\n|---|---|:--|\n| wiping your home or root | `rm -rf ~` · `sudo rm -rf /` | 🛑 `deny` |\n| wiping a disk | `dd of=/dev/sda` · `mkfs.ext4 /dev/sdb1` | 🛑 `deny` |\n| detonating a fork bomb | `:(){ :\\|:\u0026 };:` | 🛑 `deny` |\n| exfiltrating a secret | `cat ~/.ssh/id_rsa \\| curl …` | 🛑 `deny` |\n| opening up a system path | `chmod -R 777 /` | 🛑 `deny` |\n| deleting outside your workspace | `rm -rf ~/.config/x` | ⚠️ `ask` |\n| reading a key into its context | `cat ~/.aws/credentials` | ⚠️ `ask` |\n| piping the web into a shell | `curl … \\| sh` | ⚠️ `ask` |\n| rewriting git history | `git push --force` · `git reset --hard` | ⚠️ `ask` |\n| installing off-registry | `npm i git+https://…` · `pip install git+…` | ⚠️ `ask` |\n| injecting an install hook | a `postinstall` added to `package.json` | ⚠️ `ask` |\n| setting up persistence | `crontab -` · a LaunchAgent · `systemctl enable` | ⚠️ `ask` |\n\n…and it is **not** fooled by flag reordering, `sudo`, `$HOME` vs `~`, or a\nrenamed fork bomb. Every detector ships with tests that pin both the catch *and*\nthe safe cases.\n\n**→ [Write your own rules \u0026 the full match reference](docs/rules.md)**\n\n---\n\n## Make it yours\n\nLayer your own rules with a `./.fence.yaml` (auto-discovered) or `--rules \u003cfile\u003e`:\n\n```yaml\nrules:\n  - id: no-terraform-destroy\n    effect: deny\n    match:\n      shell: { command_in: [terraform] }\n      regex: '\\bterraform\\b.*\\bdestroy\\b'\n```\n\nOr retune a built-in rule's effect in a single line — no need to redefine it:\n\n```yaml\noverrides:\n  git-force-push: deny                # ask  -\u003e deny\n  pipe-to-shell-from-network: allow   # silence it\n```\n\n**→ [Rules \u0026 overrides, in depth](docs/rules.md)**\n\n---\n\n## Grab a rulepack\n\nGuardrails others already wrote — Terraform, Kubernetes, production databases —\ninstall with one command and are active on the next tool call, in every project:\n\n```bash\nfence search                    # see what's published\nfence add terraform-safety      # checksum-verified, then live everywhere\n```\n\nCompose them in a committed `.fence.yaml` (`extends: [terraform-safety]`) to\npin a baseline for your whole team — and publishing your own pack is just a PR.\n\n**→ [The registry: install, author, publish](docs/registry.md)**\n\n---\n\n## How it works\n\n```\nagent tool call  →  adapter  →  engine (shell-AST facts)  →  rulepack  →  allow · ask · deny\n```\n\nAn **adapter** normalizes each agent's tool call into a neutral action; the\n**engine** parses shell commands into semantic facts; **rules** match those facts\nand the most severe effect wins. The engine and rulepacks know nothing about any\nspecific agent — which is what makes one rulepack portable across all of them.\n\n**→ [Architecture \u0026 extension points](docs/architecture.md)**\n\n---\n\n## What Fence is — and isn't\n\nFence is **local self-protection**: it lives in your config, and you can edit or\nremove it. That's exactly right for protecting *yourself* from an agent's\nmistakes. It is honestly **not** a compliance control — a determined user (or an\nagent running as you) can disable anything on a machine they fully control.\nThe **[threat model](docs/threat-model.md)** spells this out, including the\nknown evasion paths and why they're accepted.\n\nNeed guardrails your developers **can't** turn off — centrally managed, enforced\nfleet-wide, with approval workflows and audit? That's a different trust model,\nand it's what **[hoop.dev](https://hoop.dev/start?utm_source=fence\u0026utm_medium=github\u0026utm_campaign=att-launch-072026)** does. Same idea, enforced where the\ndeveloper can't override it.\n\n---\n\n## Roadmap\n\n- [x] Semantic detectors — deletes, disk wipes, fork bombs, exfiltration,\n      world-writable, off-registry installs, manifest hooks, persistence\n- [x] One-line installers — Homebrew, npm\n- [x] A shareable rulepack registry — `fence search` · `fence add \u003cpack\u003e` ·\n      [publish your own](docs/registry.md)\n- [x] A second agent — Codex (same rulepacks, zero engine changes)\n- [x] A third — OpenCode (via its plugin surface, engine still untouched)\n- [ ] More agents — Cursor, Gemini CLI\n\n## License\n\nMIT © [hoop.dev](https://hoop.dev/?utm_source=fence\u0026utm_medium=github\u0026utm_campaign=att-launch-072026) — built by the team behind hoop.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoophq%2Ffence","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhoophq%2Ffence","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoophq%2Ffence/lists"}