Files
fermata/docs/commands.md
T
g4borg 168aefd415 🏗️ 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>
2026-05-26 01:10:07 +02:00

6.0 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 .botsecrets, .botignore, 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 .botsecrets [policy] / .botignore 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
.botsecrets TOML file for secret redaction ([files], [heuristic], [keys]) and access-control policy ([policy] section with [policy.read], [policy.write], [policy.bash]). Unified config for most projects. fermata.toml is accepted as an alias (same format, .botsecrets takes priority).
.botignore Gitignore-syntax patterns. Blocks both reads and writes to matched paths.

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).

Single-file setup with .botsecrets

You can consolidate both secret redaction and access policy into .botsecrets alone, replacing the need for a separate .botignore:

# .botsecrets
[files]
patterns = [".env", ".env.*"]

[heuristic]
enabled = true

[policy.read]
deny = [".env", ".env.*", "secrets/**", "credentials/**"]

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

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 .botsecrets [policy.bash] to deny specific command patterns:

[policy.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