📝 fermata: rewrite docs for public-facing export
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>
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
# CLI Command Reference
|
||||
|
||||
Fermata is a policy gate and secret filtering engine for AI coding agents. It ships two subcommands: `check` for interactive path validation and `hook` for integration with AI harness hook systems.
|
||||
|
||||
---
|
||||
|
||||
## fermata check
|
||||
|
||||
Check whether one or more paths are allowed for a given operation. Fermata locates the nearest project root (a directory containing `.botignore`, `botignore.toml`, or `.git`) and evaluates the policy defined there.
|
||||
|
||||
**Usage**
|
||||
|
||||
```
|
||||
fermata check [OPTIONS] <PATHS>...
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
| Option | Description | Default |
|
||||
|--------|-------------|---------|
|
||||
| `--op <OP>` | Operation to check: `read`, `write`, or `execute` | `read` |
|
||||
| `--json` | Print the full decision as JSON instead of a human message | off |
|
||||
|
||||
**Exit codes**
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| 0 | Allowed (or Ask -- the agent may prompt the user) |
|
||||
| 1 | Denied -- the policy blocks this operation |
|
||||
| 2 | Internal error (could not load policy, bad arguments, etc.) |
|
||||
|
||||
When multiple paths are provided, fermata evaluates each one independently and returns the **worst** result. If any path is denied, the exit code is 1.
|
||||
|
||||
**Examples**
|
||||
|
||||
```bash
|
||||
# Check if the agent can read .env
|
||||
fermata check --op read .env
|
||||
|
||||
# Check if the agent can write to a source file
|
||||
fermata check --op write src/main.rs
|
||||
|
||||
# Check multiple paths at once; exits 1 if any are denied
|
||||
fermata check --op read .env src/main.rs config/secrets.yaml
|
||||
|
||||
# Get a machine-readable JSON decision
|
||||
fermata check --op read .env --json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## fermata hook
|
||||
|
||||
Read a harness hook payload from stdin, evaluate the policy (PreToolUse) or redact secrets from tool output (PostToolUse), and write the response to stdout. This is the command you wire into your AI agent's hook configuration.
|
||||
|
||||
**Usage**
|
||||
|
||||
```
|
||||
fermata hook [OPTIONS]
|
||||
```
|
||||
|
||||
The hook payload is always read from **stdin** as JSON. The response is written to **stdout** as JSON.
|
||||
|
||||
**Options**
|
||||
|
||||
| Option | Description | Default |
|
||||
|--------|-------------|---------|
|
||||
| `--event <EVENT>` | Hook event type. Accepted values: `pre-tool-use`, `PreToolUse`, `post-tool-use`, `PostToolUse` | `pre-tool-use` |
|
||||
| `--harness <HARNESS>` | Harness adapter name. Currently supported: `claude` | `claude` |
|
||||
|
||||
**Exit codes**
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| 0 | Success. The JSON response on stdout tells the harness what to do. |
|
||||
| 2 | Internal error (unknown harness, unknown event type, stdin read failure) |
|
||||
|
||||
Fermata follows a **fail-open** policy for hooks: if the payload cannot be parsed or the policy cannot be loaded, it writes `{}` to stdout and exits 0 so the agent can continue. Errors are reported on stderr.
|
||||
|
||||
**Event types**
|
||||
|
||||
- **pre-tool-use** -- Runs before the tool executes. Fermata checks the requested path or command against `.botignore` / `botignore.toml` and returns an allow, deny, or ask decision to the harness.
|
||||
- **post-tool-use** -- Runs after the tool executes. Fermata loads `.botsecrets`, builds a manifest of known secret values, and redacts any matches from the tool output before it enters the LLM context. A heuristic scanner (regex patterns derived from gitleaks) catches undeclared secrets as a safety net.
|
||||
|
||||
**Examples**
|
||||
|
||||
```bash
|
||||
# PreToolUse: pipe a Claude Code hook payload through fermata
|
||||
echo '{"tool_name":"Read","tool_input":{"file_path":"/project/.env"}}' \
|
||||
| fermata hook --harness claude
|
||||
|
||||
# PostToolUse: redact secrets from tool output
|
||||
echo '{"tool_name":"Bash","tool_input":{"command":"cat .env"},"tool_output":"API_KEY=sk-live-abc123"}' \
|
||||
| fermata hook --harness claude --event post-tool-use
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration Files
|
||||
|
||||
Fermata does not have its own global config file. All configuration lives in your project directory alongside the code:
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `.botignore` | Gitignore-syntax patterns. Blocks both reads and writes to matched paths. |
|
||||
| `botignore.toml` | Per-operation rules with `[read]`, `[write]`, and `[bash]` sections. |
|
||||
| `.botsecrets` | TOML file declaring which files contain secrets, custom key patterns, and heuristic scanning options. |
|
||||
|
||||
Fermata discovers these files by walking up from the target path (or the current working directory for `hook`) until it finds a project root.
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Protect secrets from day one
|
||||
|
||||
Drop two files in your project root:
|
||||
|
||||
```gitignore
|
||||
# .botignore
|
||||
.env
|
||||
.env.*
|
||||
secrets/**
|
||||
credentials/**
|
||||
```
|
||||
|
||||
```toml
|
||||
# .botsecrets
|
||||
[files]
|
||||
patterns = [".env", ".env.*"]
|
||||
|
||||
[heuristic]
|
||||
enabled = true
|
||||
```
|
||||
|
||||
The `.botignore` blocks direct reads. The `.botsecrets` catches secrets that leak through indirect paths (shell output, log files, error messages).
|
||||
|
||||
### Wire fermata into Claude Code
|
||||
|
||||
Add both hook events to `.claude/settings.json`:
|
||||
|
||||
```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" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Block dangerous shell commands
|
||||
|
||||
Use `botignore.toml` to deny specific command patterns:
|
||||
|
||||
```toml
|
||||
[bash]
|
||||
deny = ["rm -rf /", "curl * | sh", "wget * | bash"]
|
||||
```
|
||||
|
||||
### Quick smoke test
|
||||
|
||||
After installing fermata, verify it works against your project:
|
||||
|
||||
```bash
|
||||
# Should exit 0 (allowed)
|
||||
fermata check --op read src/main.rs
|
||||
echo $?
|
||||
|
||||
# Should exit 1 (denied) if .botignore blocks .env
|
||||
fermata check --op read .env
|
||||
echo $?
|
||||
|
||||
# JSON output for scripting
|
||||
fermata check --op read .env --json
|
||||
```
|
||||
Reference in New Issue
Block a user