77520819f6
New user-friendly README modeled after sandcage's layout (Why / Quick Start / How It Works), plus four focused docs under docs/: - commands.md — full CLI reference with options, exit codes, examples - configuration.md — .botignore, botignore.toml, .botsecrets reference - security-model.md — the Reveal Triangle and defense-in-depth layers - threat-model.md — L0-L6 coverage, honest limitations, pairing guidance All Dirigent/monorepo internals stripped — ready for standalone export. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
466 lines
13 KiB
Markdown
466 lines
13 KiB
Markdown
# 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.
|
|
|
|
| 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 |
|
|
|
|
All three files are optional. Without any configuration Fermata allows all
|
|
operations and performs no redaction. Add only the files you need.
|
|
|
|
---
|
|
|
|
## `.botignore` -- Path-Based Access Control
|
|
|
|
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.).
|
|
|
|
### Layered Configuration
|
|
|
|
`.botsecrets` configuration is layered, with later sources overriding earlier
|
|
ones:
|
|
|
|
1. **Built-in defaults** -- sensible patterns that cover common secret files
|
|
and key names.
|
|
2. **User-global** -- `~/.config/fermata/.botsecrets` (Linux/macOS) or
|
|
`%APPDATA%\fermata\.botsecrets` (Windows). Applies to all projects.
|
|
3. **Project** -- `<project-root>/.botsecrets`. Checked into version control.
|
|
4. **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.
|
|
|
|
```toml
|
|
[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.
|
|
|
|
```toml
|
|
[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. |
|
|
|
|
```toml
|
|
[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. |
|
|
|
|
```toml
|
|
[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. |
|
|
|
|
```toml
|
|
[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). |
|
|
|
|
```toml
|
|
[[file]]
|
|
path = "config/database.yml"
|
|
format = "yaml"
|
|
keys = ["password", "secret_key_base"]
|
|
|
|
[[file]]
|
|
path = ".env.production"
|
|
format = "env"
|
|
```
|
|
|
|
---
|
|
|
|
## Examples
|
|
|
|
### Minimal: Just Block Sensitive Files
|
|
|
|
If you only need to prevent the agent from reading certain files, a single
|
|
`.botignore` is enough:
|
|
|
|
```gitignore
|
|
# .botignore
|
|
.env
|
|
.env.*
|
|
*.pem
|
|
*.key
|
|
```
|
|
|
|
### 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
|
|
```
|
|
|
|
```toml
|
|
# .botsecrets
|
|
[files]
|
|
patterns = [".env", ".env.*", "secrets/*.yaml"]
|
|
|
|
[keys]
|
|
include = ["STRIPE_*"]
|
|
|
|
[redaction]
|
|
style = "masked"
|
|
```
|
|
|
|
### Fine-Grained: Separate Read, Write, and Bash Rules
|
|
|
|
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:
|
|
|
|
```gitignore
|
|
# .botignore
|
|
.env
|
|
.env.*
|
|
*.pem
|
|
*.key
|
|
id_rsa
|
|
id_ed25519
|
|
```
|
|
|
|
```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"]
|
|
```
|
|
|
|
```toml
|
|
# .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"
|
|
|
|
[[file]]
|
|
path = "config/credentials.yaml"
|
|
format = "yaml"
|
|
keys = ["api_key", "webhook_secret"]
|
|
```
|
|
|
|
```toml
|
|
# .botsecrets.local (git-ignored, developer-specific)
|
|
[redaction]
|
|
style = "masked"
|
|
|
|
[enforcement]
|
|
mode = "permissive"
|
|
```
|