Realign fermata around redaction (PostToolUse) as the primary security layer, with access control (PreToolUse) as supplementary write/bash protection. Remove botignore.toml — policy rules now live in .botsecrets [policy] section. Add fermata.toml as an alias for .botsecrets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
12 KiB
Configuration Reference
Fermata uses up to two configuration files to control what an AI coding agent
can access and what secret values it can see. .botsecrets is the primary
configuration file. Most projects need only this file.
| File | Purpose | Syntax |
|---|---|---|
.botsecrets |
Declare secrets, control redaction, and set policy rules | TOML |
.botignore |
Block agent access to matching paths (optional, gitignore syntax) | gitignore |
fermata.toml is accepted as an alias for .botsecrets (same format, .botsecrets takes priority when both exist).
Configuration is layered, with later (more specific) sources overriding earlier ones. All files are optional. Without any configuration Fermata allows all operations and performs no redaction.
.botsecrets -- Secret Redaction and Policy
.botsecrets declares which files contain secrets, how Fermata should
redact them from tool output, and (optionally) access control policy rules.
This prevents secret values from leaking into the LLM context window even
when the agent reads files indirectly (via shell output, log files, error
messages, etc.).
Layered Configuration
.botsecrets configuration is layered, with later sources overriding earlier
ones:
- Built-in defaults -- sensible patterns that cover common secret files and key names.
- User-global --
~/.config/fermata/.botsecrets(Linux/macOS) or%APPDATA%\fermata\.botsecrets(Windows). Applies to all projects. - Project --
<project-root>/.botsecrets. Checked into version control. - Local overrides --
<project-root>/.botsecrets.local. Git-ignored, for machine-specific or developer-specific settings.
Merge rules:
- Vec fields (
files.patterns,heuristic.patterns,file_overrides): replaced by the more specific layer when present. - Key lists (
keys.include,keys.exclude): accumulated across layers (appended, not replaced). - Scalar fields (
redaction.style,heuristic.enabled,enforcement.mode, etc.): the most specific value wins.
[files] -- Secret File Patterns
Declares which files contain secrets. Fermata parses these files, extracts key-value pairs, and uses the values for exact-match redaction.
| Field | Type | Default | Description |
|---|---|---|---|
patterns |
string[] |
See below | Glob patterns matching files that contain secrets. |
Built-in default patterns (active when [files] is not specified):
.env, .env.*, *.env, secrets.*, credentials.*, *.key, *.pem, *.p12, *.pfx,
id_rsa, id_ed25519, id_ecdsa, Secrets.toml, Secrets.*.toml,
terraform.tfvars, *.auto.tfvars, terraform.tfstate, *.tfstate,
.docker/config.json, config/master.key, config/credentials/*.key,
.aws/credentials, .netrc, .htpasswd, service-account.json,
service-account-key.json
Setting files.patterns in a layer replaces the defaults entirely.
[files]
patterns = [".env", ".env.*", "config/secrets.yaml"]
[keys] -- Secret Key Name Patterns
Controls which key names within secret files are treated as sensitive. Patterns use glob syntax and are matched case-insensitively.
| Field | Type | Default | Description |
|---|---|---|---|
include |
string[] |
[] |
Additional key name patterns to treat as secret. Accumulated across layers. |
exclude |
string[] |
[] |
Key name patterns to remove from the effective set. Exact string match against the pattern text. |
Fermata ships with ~30 built-in key patterns that are always active:
*PASSWORD*, *SECRET*, *API_KEY*, *APIKEY*, *TOKEN*, *ACCESS_KEY*,
*PRIVATE_KEY*, *AUTH*, *CREDENTIAL*, *CONNECTION_STRING*, DATABASE_URL,
AWS_SECRET_ACCESS_KEY, GITHUB_TOKEN, OPENAI_API_KEY, ANTHROPIC_API_KEY,
JWT_SECRET, ENCRYPTION_KEY, MASTER_KEY, SECRET_KEY_BASE, ...
Use keys.include to add project-specific patterns. Use keys.exclude to
suppress a built-in pattern that causes false positives.
[keys]
include = ["STRIPE_*", "MY_APP_SIGNING_*"]
exclude = ["*AUTH*"] # too broad for this project
[redaction] -- Redaction Style
Controls how redacted values appear in tool output.
| Field | Type | Default | Description |
|---|---|---|---|
style |
string |
"masked" |
How to replace secret values. |
Available styles:
| Style | Output | Description |
|---|---|---|
masked |
***** |
Replaces the value with asterisks. Default. |
typed |
<secret:string> |
Shows the value type but not the content. |
named |
<secret:DB_PASSWORD> |
Shows the key name but not the value. |
absent |
(empty string) | Removes the value entirely. |
[redaction]
style = "named"
[heuristic] -- Heuristic Secret Scanning
In addition to known-value redaction, Fermata can scan all tool output for
patterns that look like secrets (AWS keys, JWTs, GitHub PATs, high-entropy
strings, database connection URLs). This catches secrets not covered by the
.botsecrets manifest.
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool |
true |
Enable or disable heuristic scanning. |
mode |
string |
"enforce" |
What to do when a heuristic match is found. |
patterns |
string[] |
[] |
Additional regex patterns to scan for. Replaces the layer's custom patterns when set. |
Available modes:
| Mode | Behavior |
|---|---|
enforce |
Redact heuristic matches from tool output. Default. |
report |
Log findings but do not redact. |
disabled |
Do not run heuristic scanning at all. |
[heuristic]
enabled = true
mode = "enforce"
patterns = ["MYAPP-[A-Za-z0-9]{32}"]
[enforcement] -- Enforcement Behavior
Controls how strictly Fermata enforces redaction, especially in edge cases.
| Field | Type | Default | Description |
|---|---|---|---|
mode |
string |
"permissive" |
Global enforcement strictness. |
on_parse_error |
string |
"mask-entire-file" |
What to do when a secret file cannot be parsed. |
Enforcement modes:
| Mode | Behavior |
|---|---|
strict |
Any error or ambiguity results in denial. |
permissive |
Best-effort redaction; non-fatal errors are tolerated. Default. |
audit |
Log all decisions but do not block or redact. |
Parse error actions:
| Action | Behavior |
|---|---|
mask-entire-file |
Treat the entire file content as a secret. Default and safest. |
allow |
Skip the unparseable file (secrets may leak). |
deny |
Block all access to the file. |
[enforcement]
mode = "strict"
on_parse_error = "deny"
[[file]] -- Per-File Overrides
Override parsing behavior for specific secret files. Useful when a file uses a non-standard format or you only want to redact specific keys.
| Field | Type | Required | Description |
|---|---|---|---|
path |
string |
Yes | Path to the file (relative to project root). |
format |
string |
No | Force a specific parser: "env", "toml", "yaml", "json". Auto-detected if omitted. |
keys |
string[] |
No | Only redact these specific keys from this file (instead of applying global key patterns). |
[[file]]
path = "config/database.yml"
format = "yaml"
keys = ["password", "secret_key_base"]
[[file]]
path = ".env.production"
format = "env"
[policy] -- Access Control Rules
Optional access control rules embedded directly in .botsecrets.
[policy.write] -- Write Protection
Blocks the agent from writing to matching paths. This is the primary use case for access control -- protecting vendored code, lock files, and policy files from modification.
| Field | Type | Description |
|---|---|---|
patterns |
string[] |
Glob patterns for paths the agent cannot write to. |
[policy.write]
patterns = [".claude/**", "vendor/**", "*.lock", "dist/**"]
[policy.bash] -- Command Execution Rules
Controls which shell commands the agent can run.
| Field | Type | Description |
|---|---|---|
deny |
string[] |
Patterns that block commands outright. |
allow_prefixes |
string[] |
Command prefixes always allowed. |
ask |
string[] |
Patterns requiring user confirmation. |
[policy.bash]
deny = ["rm -rf /", "curl * | sh"]
allow_prefixes = ["cargo", "npm", "just"]
ask = ["docker", "kubectl"]
[policy.read] -- Read Restrictions (Rarely Needed)
Blocks reads of matching paths. Rarely needed -- secret values are redacted by the PostToolUse layer regardless of whether the read is allowed. Use this only for files the agent cannot usefully read (e.g., binary blobs, large data files).
[policy.read]
patterns = ["*.sqlite", "*.dat"]
.botignore -- Path-Based Access Control
For most projects, .botsecrets with [policy] is sufficient. .botignore
remains useful for monorepo subtree exclusion or teams that prefer gitignore
syntax for simple path blocking.
A .botignore file uses gitignore syntax to block agent access to matching
paths. When a path matches, both reads and writes are denied. There is no
distinction between operations -- if you need per-operation control, use
the [policy] section in .botsecrets instead.
Placement and Scoping
.botignore files can be placed at any level of your project directory tree.
Fermata walks the project root recursively and loads every .botignore it
finds. Each file is scoped to its own directory:
myproject/
.botignore # applies to the entire project
infra/
.botignore # applies only under infra/
src/
.botignore # applies only under src/
When multiple .botignore files match the same path, the deepest
(most specific) file wins. A negation pattern (!) at any depth overrides
an ignore from a shallower .botignore.
Syntax
Standard gitignore rules apply:
- Blank lines and lines starting with
#are ignored. *matches anything except/.**matches zero or more directories.- A trailing
/matches directories only. - A leading
/anchors the pattern to the.botignorefile's directory. - Prefix a pattern with
!to negate (whitelist) a previously ignored path.
Example
# Block entire subtrees in a monorepo
packages/legacy-app/
vendor/
# Block private keys
*.pem
*.key
id_rsa
id_ed25519
# But allow the public key
!id_ed25519.pub
Examples
Minimal: Just Redact Secrets (most projects)
# .botsecrets
[files]
patterns = [".env", ".env.*"]
Standard: Redact Secrets + Write Protection
# .botsecrets
[files]
patterns = [".env", ".env.*", "secrets/*.yaml"]
[keys]
include = ["STRIPE_*"]
[policy.write]
patterns = [".claude/**", "vendor/**", "*.lock"]
[policy.bash]
deny = ["rm -rf /", "curl * | sh"]
With .botignore: Simple Path Blocking + Redaction
For teams that prefer gitignore syntax:
# .botignore (optional)
node_modules/
build/
dist/
# .botsecrets
[files]
patterns = [".env", ".env.*"]
Full-Featured: Complete Configuration
A production setup using .botsecrets for both redaction and policy:
# .botsecrets
[files]
patterns = [".env", ".env.*", "config/credentials.yaml"]
[keys]
include = ["STRIPE_*", "PLAID_*"]
exclude = ["*AUTH*"]
[redaction]
style = "named"
[heuristic]
enabled = true
mode = "enforce"
[enforcement]
mode = "strict"
on_parse_error = "mask-entire-file"
[policy.write]
patterns = [".claude/**", "vendor/**", "*.lock", "dist/**", "migrations/**"]
[policy.bash]
deny = ["rm -rf /", "curl * | sh"]
allow_prefixes = ["cargo", "npm", "just", "git"]
ask = ["docker", "terraform"]
[policy.read]
patterns = ["*.sqlite", "*.dat"]
[[file]]
path = "config/credentials.yaml"
format = "yaml"
keys = ["api_key", "webhook_secret"]
# .botsecrets.local (git-ignored, developer-specific)
[redaction]
style = "masked"
[enforcement]
mode = "permissive"