Implement Goals 1–3 and 5 from the reveal-layer security brain goal.
fermata now detects, redacts, and scans for secrets in AI agent tool
output, filling the ecosystem gap where no coding agent filters secrets
post-read.
New core/secrets/ module:
- config.rs: .botsecrets TOML format with hierarchical merge and ~40
built-in key patterns
- parser.rs: multi-format secret file parser (.env, TOML, YAML, JSON,
Python assignments, Java properties)
- manifest.rs: file discovery + parsing → known-secrets set
- redactor.rs: Aho-Corasick multi-pattern replacement with 4 styles
- scanner.rs: RegexSet heuristic detection with 35 gitleaks-derived
patterns (MIT) and Shannon entropy filtering
- patterns.rs: curated rules for AWS, GitHub, Stripe, Slack, JWT, etc.
Hook integration:
- fermata hook --event post-tool-use reads tool output, runs redactor +
scanner, returns updatedToolOutput for Claude Code
- Backward compatible: --event pre-tool-use (default) unchanged
- Fail-open: errors produce {} and exit 0
Library API:
- Redactor::new(manifest, style).redact(text) → RedactedText
- Scanner::new(config).scan(text) → Vec<Finding>
- Compiles without CLI feature for embedding in other crates
195 tests (130 new), all passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5.9 KiB
dirigent_fermata
A fast, harness-agnostic policy gate and secret filtering engine for AI coding agents.
Drop a .botignore to control what your agent can touch. Drop a .botsecrets to control what secret values your agent can see. Fermata enforces both -- before and after tool calls happen.
Why Fermata
AI coding agents don't have an innate sense of "don't touch .env" -- and even if you block the file, they can still see its contents through shell output, log files, and indirect reads. Fermata solves both problems:
- Policy gate --
.botignoreblocks reads, writes, and dangerous commands before they execute (PreToolUse). - Secret filtering --
.botsecretsredacts secret values from tool output before they enter the LLM context (PostToolUse). - Fast -- Rust, Aho-Corasick automaton for redaction, ~1-5ms per call.
- Familiar syntax --
.botignoreuses gitignore rules;.botsecretsuses TOML with glob patterns. - Harness-agnostic -- hook adapters for Claude Code (shipped), Codex and Gemini (planned), MCP proxy (planned).
Status: v0.2
| Component | Status |
|---|---|
Library (Policy::check, Policy::check_command) |
Done |
.botignore walker (gitignore semantics) |
Done |
botignore.toml parser (read / write / bash namespaces) |
Done |
CLI: fermata check / fermata hook |
Done |
| Claude Code PreToolUse adapter | Done |
| Claude Code PostToolUse adapter (output redaction) | Done |
.botsecrets config parser |
Done |
| Secret manifest discovery and loading | Done |
| Multi-format secret file parser (.env, TOML, YAML, JSON) | Done |
Redactor (known-value Aho-Corasick replacement) |
Done |
Scanner (heuristic regex + gitleaks patterns) |
Done |
Out of scope for v0.2: Codex / Gemini hook adapters, MCP proxy mode, audit log, filesystem watcher.
Install
From source (this monorepo):
cargo install --path crates/dirigent_fermata --features cli
Secret Filtering
Fermata's secret filtering operates in three layers:
- Policy gate (PreToolUse) --
.botignoreblocks direct access to sensitive files. Catches ~90% of accidental reads. - Known-value redaction (PostToolUse) --
.botsecretsdeclares which files contain secrets. Fermata parses them, extracts values, and replaces them in all tool output using an Aho-Corasick automaton. Zero false negatives for declared secrets. - Heuristic scanning (PostToolUse) -- regex patterns derived from gitleaks detect undeclared secrets (AWS keys, JWTs, GitHub PATs, database URLs). Safety net for secrets not covered by the manifest.
.botsecrets format
Create a .botsecrets file at your project root:
# Files that contain secrets -- fermata parses these and redacts values
[files]
patterns = [".env", ".env.*", "secrets.*"]
# Additional secret key names (built-in defaults cover *_KEY, *_SECRET, etc.)
[keys]
include = ["STRIPE_*", "MY_APP_SIGNING_*"]
# Heuristic scanning on all tool output
[heuristic]
enabled = true
That's the typical case. Built-in key patterns (*_KEY, *_SECRET, *_PASSWORD, *_TOKEN, DATABASE_URL, etc.) handle most projects without custom configuration.
Usage
Claude Code hook configuration
Add both PreToolUse and PostToolUse hooks in .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|Read|Edit|Write",
"hooks": [
{ "type": "command", "command": "fermata hook --harness claude" }
]
}
],
"PostToolUse": [
{
"matcher": "Bash|Read|Edit|Write",
"hooks": [
{ "type": "command", "command": "fermata hook --harness claude --event post-tool-use" }
]
}
]
}
}
PreToolUse blocks forbidden operations. PostToolUse redacts secret values from tool output before they reach the LLM.
Checking a path
fermata check --op read /path/to/.env
# exit 1 -- blocked
fermata check --op write /path/to/src/main.rs
# exit 0 -- allowed
Library API
use dirigent_fermata::core::secrets::{Manifest, Redactor, Scanner, SecretsConfig};
// Load .botsecrets config and build the manifest
let config = SecretsConfig::load("/path/to/project")?;
let manifest = Manifest::discover(&config)?;
// Known-value redaction (Aho-Corasick, sub-millisecond)
let redactor = Redactor::from_manifest(&manifest);
let clean = redactor.redact("DB_PASSWORD=hunter2\nAPI_KEY=sk-abc123");
// -> "DB_PASSWORD=*****\nAPI_KEY=*****"
// Heuristic scanning (regex patterns)
let scanner = Scanner::new(&config);
let findings = scanner.scan("Found key: AKIA1234567890ABCDEF");
// -> [Finding { pattern: "AWS Access Key", confidence: High, .. }]
Configuration
.botignore -- access control
Gitignore syntax. Blocks both reads and writes.
.env
.env.*
secrets/**
botignore.toml -- per-operation rules
[read]
patterns = [".env*", "secrets/**"]
[write]
patterns = ["vendor/**", "*.lock"]
[bash]
deny = ["rm -rf /", "curl * | sh"]
.botsecrets -- secret value redaction
See the Secret Filtering section above.
Architecture
Three concentric layers; nothing inner imports from anything outer:
core/-- harness-unaware, sync. Policy types,.botignorewalker,botignore.tomlparser,Policy::check.core/secrets/--.botsecretsconfig, manifest discovery, multi-format parser, Aho-Corasick redactor, heuristic scanner.
harness/--HarnessAdaptertrait for PreToolUse (policy gate) and PostToolUse (output redaction). Each adapter is feature-gated.bin/fermata.rs--clap, stdio, and exit codes.
See also
docs/tools/fermata.md-- Dirigent integration plandocs/architecture/fermata-security-philosophy.md-- security philosophy and the reveal triangledocs/workpad/brainstorm/fermata.md-- full product spec and field notesdocs/architecture/crates.md-- crate dependency map