🏗️ fermata: redaction-first security model, unified .botsecrets config

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>
This commit is contained in:
2026-05-26 01:10:07 +02:00
parent 77520819f6
commit 168aefd415
17 changed files with 571 additions and 423 deletions
+158 -214
View File
@@ -1,165 +1,29 @@
# Configuration Reference
Fermata uses three configuration files to control what an AI coding agent can
access and what secret values it can see. Configuration is layered, with later
(more specific) sources overriding earlier ones.
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 |
|------|---------|--------|
| `.botignore` | Block agent access to matching paths (reads and writes) | gitignore |
| `botignore.toml` | Fine-grained per-operation rules (read, write, bash) | TOML |
| `.botsecrets` | Declare secret-containing files and control redaction | TOML |
| `.botsecrets` | Declare secrets, control redaction, and set policy rules | TOML |
| `.botignore` | Block agent access to matching paths (optional, gitignore syntax) | gitignore |
All three files are optional. Without any configuration Fermata allows all
operations and performs no redaction. Add only the files you need.
`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.
---
## `.botignore` -- Path-Based Access Control
## `.botsecrets` -- Secret Redaction and Policy
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
`botignore.toml` 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 `.botignore` file's directory.
- Prefix a pattern with `!` to negate (whitelist) a previously ignored path.
### Example
```gitignore
# Block all environment files
.env
.env.*
# Block the entire secrets directory
secrets/**
# Block private keys
*.pem
*.key
id_rsa
id_ed25519
# But allow the public key
!id_ed25519.pub
```
---
## `botignore.toml` -- Per-Operation Rules
`botignore.toml` provides fine-grained control by separating rules into three
namespaces: `[read]`, `[write]`, and `[bash]`. Place this file at the project
root alongside `.botignore`.
When both `.botignore` and `botignore.toml` are present, they are evaluated
together. `.botignore` is checked first (blocking both reads and writes), then
`botignore.toml` namespace-specific rules are applied. A path blocked by either
source is denied.
### `[read]` -- Read Access Rules
Blocks the agent from reading matching paths. Patterns use glob syntax.
| Field | Type | Description |
|-------|------|-------------|
| `patterns` | `string[]` | Glob patterns for paths the agent cannot read. |
```toml
[read]
patterns = [".env*", "secrets/**", "*.pem"]
```
### `[write]` -- Write Access Rules
Blocks the agent from writing to matching paths. Patterns use glob syntax.
| Field | Type | Description |
|-------|------|-------------|
| `patterns` | `string[]` | Glob patterns for paths the agent cannot write to. |
```toml
[write]
patterns = ["vendor/**", "*.lock", "migrations/**"]
```
### `[bash]` -- Command Execution Rules
Controls which shell commands the agent can run. Commands are evaluated in
priority order: deny, then allow_prefixes, then ask. If none match, the
command is allowed.
| Field | Type | Description |
|-------|------|-------------|
| `deny` | `string[]` | Patterns that block commands outright. Substring match by default; glob metacharacters (`*`, `?`, `[`) enable glob matching. |
| `allow_prefixes` | `string[]` | Command prefixes that are always allowed. If a command starts with a prefix, it passes immediately (before `ask` is checked). Trailing `:*` is stripped before matching. |
| `ask` | `string[]` | Patterns that require user confirmation before executing. Same matching rules as `deny`. |
Evaluation order:
1. If any `deny` pattern matches, the command is **blocked**.
2. If any `allow_prefixes` entry matches, the command is **allowed**.
3. If any `ask` pattern matches, the command **requires confirmation**.
4. Otherwise the command is **allowed**.
```toml
[bash]
deny = ["rm -rf /", "curl * | sh", ":(){ :|:& };:"]
allow_prefixes = ["cargo", "npm", "git status"]
ask = ["docker", "kubectl"]
```
### Full `botignore.toml` Example
```toml
[read]
patterns = [".env*", "secrets/**"]
[write]
patterns = ["vendor/**", "*.lock", "dist/**"]
[bash]
deny = ["rm -rf /", "curl * | sh"]
allow_prefixes = ["cargo", "npm", "just"]
ask = ["docker", "kubectl", "terraform"]
```
---
## `.botsecrets` -- Secret Redaction
`.botsecrets` declares which files contain secrets and how Fermata should
redact them from tool output. 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.).
`.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
@@ -341,35 +205,126 @@ 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. |
```toml
[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. |
```toml
[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).
```toml
[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 `.botignore` file's directory.
- Prefix a pattern with `!` to negate (whitelist) a previously ignored path.
### Example
```gitignore
# 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 Block Sensitive Files
### Minimal: Just Redact Secrets (most projects)
If you only need to prevent the agent from reading certain files, a single
`.botignore` is enough:
```gitignore
# .botignore
.env
.env.*
*.pem
*.key
```toml
# .botsecrets
[files]
patterns = [".env", ".env.*"]
```
### Standard: Block Access and Redact Secrets
The most common setup. Block direct access with `.botignore` and redact leaked
values with `.botsecrets`:
```gitignore
# .botignore
.env
.env.*
secrets/**
*.pem
```
### Standard: Redact Secrets + Write Protection
```toml
# .botsecrets
@@ -379,56 +334,34 @@ patterns = [".env", ".env.*", "secrets/*.yaml"]
[keys]
include = ["STRIPE_*"]
[redaction]
style = "masked"
[policy.write]
patterns = [".claude/**", "vendor/**", "*.lock"]
[policy.bash]
deny = ["rm -rf /", "curl * | sh"]
```
### Fine-Grained: Separate Read, Write, and Bash Rules
### With .botignore: Simple Path Blocking + Redaction
Use `botignore.toml` when you need different rules for different operations:
```toml
# botignore.toml
[read]
patterns = [".env*", "secrets/**"]
[write]
patterns = ["vendor/**", "*.lock", "Cargo.toml"]
[bash]
deny = ["rm -rf *"]
allow_prefixes = ["cargo", "npm", "git"]
ask = ["docker", "kubectl"]
```
### Full-Featured: All Three Files
A production setup using all configuration files together:
For teams that prefer gitignore syntax:
```gitignore
# .botignore
.env
.env.*
*.pem
*.key
id_rsa
id_ed25519
# .botignore (optional)
node_modules/
build/
dist/
```
```toml
# botignore.toml
[read]
patterns = ["secrets/**", ".aws/**"]
[write]
patterns = ["vendor/**", "*.lock", "dist/**", "migrations/**"]
[bash]
deny = ["rm -rf /", "curl * | sh"]
allow_prefixes = ["cargo", "npm", "just", "git"]
ask = ["docker", "terraform"]
# .botsecrets
[files]
patterns = [".env", ".env.*"]
```
### Full-Featured: Complete Configuration
A production setup using `.botsecrets` for both redaction and policy:
```toml
# .botsecrets
[files]
@@ -449,6 +382,17 @@ mode = "enforce"
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"