Files
fermata/docs/commands.md
T
g4borg 77520819f6 📝 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>
2026-05-25 18:27:51 +02:00

5.5 KiB

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

# 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

# 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:

# .botignore
.env
.env.*
secrets/**
credentials/**
# .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:

{
  "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:

[bash]
deny = ["rm -rf /", "curl * | sh", "wget * | bash"]

Quick smoke test

After installing fermata, verify it works against your project:

# 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