{"id":50965072,"url":"https://github.com/ch4nn0n/fencepost","last_synced_at":"2026-06-18T19:01:44.133Z","repository":{"id":363591810,"uuid":"1210487906","full_name":"ch4nn0n/fencepost","owner":"ch4nn0n","description":"A configurable permission gate for every Claude Code tool call","archived":false,"fork":false,"pushed_at":"2026-06-17T02:22:57.000Z","size":1332,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-17T04:16:50.517Z","etag":null,"topics":["claude","claude-code","guardrail","hook","permissions","plugin","security"],"latest_commit_sha":null,"homepage":"https://ch4nn0n.github.io/fencepost/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ch4nn0n.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":".github/CODEOWNERS","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-04-14T13:16:52.000Z","updated_at":"2026-06-17T02:22:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ch4nn0n/fencepost","commit_stats":null,"previous_names":["ch4nn0n/fencepost"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ch4nn0n/fencepost","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ch4nn0n%2Ffencepost","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ch4nn0n%2Ffencepost/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ch4nn0n%2Ffencepost/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ch4nn0n%2Ffencepost/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ch4nn0n","download_url":"https://codeload.github.com/ch4nn0n/fencepost/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ch4nn0n%2Ffencepost/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34503511,"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-06-18T02:00:06.871Z","response_time":128,"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":["claude","claude-code","guardrail","hook","permissions","plugin","security"],"created_at":"2026-06-18T19:01:42.235Z","updated_at":"2026-06-18T19:01:44.127Z","avatar_url":"https://github.com/ch4nn0n.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# fencepost\n\n**A configurable permission gate for every Claude Code tool call.**\n\n[![CI](https://github.com/ch4nn0n/fencepost/actions/workflows/ci.yml/badge.svg)](https://github.com/ch4nn0n/fencepost/actions/workflows/ci.yml)\n[![Docs](https://github.com/ch4nn0n/fencepost/actions/workflows/docs.yml/badge.svg)](https://ch4nn0n.github.io/fencepost/)\n[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE)\n\nfencepost installs as a [Claude Code](https://docs.claude.com/en/docs/claude-code) plugin and runs on the `PreToolUse` hook (plus `PostToolUse` for secrets redaction), so it sees **every tool call before it happens** and evaluates it against a YAML rule set you control. Each call resolves to one of three decisions:\n\n| Decision | What happens | Reason shown to |\n|----------|--------------|-----------------|\n| **allow** | The tool runs, silently (fast path). | — |\n| **ask** | Claude Code prompts you to approve. | You |\n| **deny** | The tool is blocked, with an actionable alternative. | Claude |\n\nA denial isn't a dead end: fencepost steers Claude toward the suggested alternative instead of letting it retry the same wall.\n\nfencepost is meant to **be** your permission layer. Run Claude Code in [`bypassPermissions` mode](https://ch4nn0n.github.io/fencepost/docs/concepts/permission-modes) and the YAML rule set becomes the single gate — Claude's native prompts step aside while fencepost's `allow`/`ask`/`deny` still decide every call.\n\n\u003e 📖 **Full documentation: [ch4nn0n.github.io/fencepost](https://ch4nn0n.github.io/fencepost/)**\n\n## Why\n\n- **Curated, shareable rules.** Import battle-tested rule sets for `git`, `kubernetes`, `helm`, `ansible`, `filesystem` and more with one line.\n- **Real bash understanding.** Commands are parsed with tree-sitter, not pattern-matched — fencepost reasons about redirections, every argument, compound commands, and even inline `python -c` / `node -e` snippets.\n- **A scratch sandbox.** Funnel temp files into `/tmp/claude` and scope destructive permissions to it; the rest of the filesystem stays gated.\n- **Secrets protection.** Using whichever of gitleaks (recommended) / betterleaks / trufflehog / detect-secrets you have installed, fencepost denies tool inputs that embed credentials and redacts secrets from tool output before they reach the model.\n- **Fail-closed safety.** A broken security config denies everything until a human fixes it, rather than silently degrading.\n- **An audit trail.** Every decision is logged; the `/audit` skill turns real usage into concrete config suggestions.\n\nfencepost ships as a small committed JS bundle (~280 KB plus the tree-sitter grammars), so there's no build step for users and no large binary to download. It runs on **Node** (which you already have if you run Claude Code via npm) or **Bun**.\n\n## Install\n\nThe repository doubles as a single-plugin marketplace:\n\n```text\n/plugin marketplace add ch4nn0n/fencepost\n/plugin install fencepost@fencepost\n```\n\nThe hooks register and the gate is live on the next tool call. Run Claude with fencepost as the sole gate:\n\n```bash\nclaude --permission-mode bypassPermissions\n```\n\nThis drops Claude's own prompts and lets your config decide. It also disables Claude's other safety nets, so use it in a container or VM and keep `default`/`onError` at `ask` or `deny` — see [permission modes](https://ch4nn0n.github.io/fencepost/docs/concepts/permission-modes). To try it against a clone without installing:\n\n```bash\ngit clone https://github.com/ch4nn0n/fencepost.git\nclaude --permission-mode bypassPermissions --plugin-dir ./fencepost\n```\n\nSee the [installation guide](https://ch4nn0n.github.io/fencepost/docs/getting-started/installation) for details.\n\n## Quick start\n\nCreate `.claude/fencepost.yaml` in your project:\n\n```yaml\nimport:\n  - claude        # allow Claude's built-in tools + enable the /tmp sandbox\n  - git           # sensible git porcelain rules\n\ndefault: ask      # what to do when nothing matches\nonError: ask      # what to do when a command can't be checked\n```\n\nThat's enough to be useful:\n\n```bash\ngit status            # → allow (silent)\ngit push origin main  # → ask   (you approve)\ngit push --force      # → deny  (steered to --force-with-lease)\n```\n\nLayer your own rules on top — imports are the base, your rules always win. See the [configuration guide](https://ch4nn0n.github.io/fencepost/docs/configuration/config-files).\n\n## Bundled presets\n\n`claude` · `claude-web` · `git` · `gh` · `kubernetes` · `helm` · `helmfile` · `ansible` · `context7` · `playwright` · `filesystem` · `python-safety` · `secrets`\n\nSee the [preset reference](https://ch4nn0n.github.io/fencepost/docs/presets).\n\n## Development\n\n```bash\nbun install\nbun test             # run the test suite\nbun run typecheck    # tsc --noEmit\nbun run build        # bundle src/ → dist/index.js (+ wasm grammars)\nbun run dev          # run the entry point directly\n```\n\n`dist/` is committed on purpose — it's the artifact the plugin ships — so rebuild and commit it whenever you change `src/`.\n\nThe documentation site lives in [`docs/`](./docs) (Docusaurus):\n\n```bash\ncd docs\nbun install\nbun start            # dev server with HMR\nbun run build        # static site → docs/build/\n```\n\n## Releases\n\nReleases are automated with [release-please](https://github.com/googleapis/release-please). Write [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, …); release-please maintains a **release PR** that bumps the version (in both `package.json` and `.claude-plugin/plugin.json`) and updates `CHANGELOG.md`. Merging that PR tags the release and publishes a ready-to-install plugin archive.\n\n## License\n\n[GNU General Public License v3.0](./LICENSE) or later.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fch4nn0n%2Ffencepost","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fch4nn0n%2Ffencepost","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fch4nn0n%2Ffencepost/lists"}