Agent Hooks
What Are Agent Hooks
AI coding agents like Claude Code interact with your codebase by reading files to understand context, answer questions, and make suggestions. This is a powerful feature, but it introduces a security risk: if those files contain secrets, the agent could inadvertently expose them in conversations, logs, or generated code snippets.
sekretbarilo’s agent hooks solve this problem by integrating directly into the agent’s file-read pipeline. When an AI agent attempts to read a file:
- The agent triggers a sekretbarilo hook before reading the file
- sekretbarilo scans the file for secrets
- If secrets are found, the read is blocked and the agent receives an error
- If the file is clean, the read proceeds normally
This creates a security boundary that prevents sensitive data from ever reaching the agent, protecting your credentials, API keys, and other secrets from accidental exposure.
Claude Code Integration
Claude Code is an official CLI tool from Anthropic that brings Claude AI directly into your development workflow. sekretbarilo integrates with Claude Code through its hooks system:
Hook Configuration:
- Hook type:
PreToolUse(triggered before the Read tool executes) - Tool matcher:
Read(applies to file read operations) - Command:
sekretbarilo check-file --stdin-json - Timeout: 10 seconds
- Status message: “Scanning file for secrets…”
When Claude Code is about to read a file, it automatically calls sekretbarilo, sends the file path as JSON on stdin, and waits for the scan result. A clean file (exit code 0) allows the read to proceed; a blocked file (exit code 2) prevents Claude Code from accessing the content.
Installation
Install agent hooks for Claude Code using the install command. You can install locally (per-project) or globally (for all projects).
Project-Local Installation
Install in the current project’s .claude/settings.json:
sekretbarilo install agent-hook claude
This creates or modifies ./.claude/settings.json in your project root. The hook only applies when Claude Code is run from this project.
Global Installation
Install in your user-level ~/.claude/settings.json:
sekretbarilo install agent-hook claude --global
This applies the hook to all projects where Claude Code runs under your user account.
Install All Hooks at Once
To install both pre-commit hooks and agent hooks in one command:
# install locally (project pre-commit + project agent hook)
sekretbarilo install all
# install globally (global pre-commit + global agent hook)
sekretbarilo install all --global
The install all command is the fastest way to set up complete protection for both git commits and AI agent file access.
How It Works (Pipeline)
When Claude Code reads a file, sekretbarilo executes the following pipeline:
1. Hook Trigger
Claude Code is about to execute the Read tool to read a file. The PreToolUse hook fires, invoking:
sekretbarilo check-file --stdin-json
2. JSON Payload
Claude Code sends a JSON payload on stdin with the file path and optional working directory:
{
"tool_input": { "file_path": "path/to/file" },
"cwd": "/optional/working/directory"
}
3. Path Resolution
sekretbarilo parses the JSON, extracts the file path, and resolves it:
- Absolute paths are converted to relative paths when possible (using
cwdcontext) - Relative paths are resolved against
cwdor the current directory - Path traversal attempts (e.g.,
../../etc/passwd) are rejected
4. Fast-Path Check: Binary Files, Vendor Dirs, Lock Files
Before scanning, sekretbarilo checks if the file is one that cannot contain readable secrets:
Binary extensions (images, executables, archives):
.png, .jpg, .jpeg, .gif, .bmp, .svg, .ico
.pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx
.exe, .dll, .so, .dylib, .o, .bin
.zip, .tar, .gz, .bz2, .7z, .rar
.mp3, .mp4, .avi, .mov, .wav
.woff, .woff2, .ttf, .eot
Vendor directories (dependencies, generated code):
node_modules/, vendor/, .bundle/, bower_components/
__pycache__/, .git/, .svn/, .hg/
Lock files (package manifests, checksums):
package-lock.json, yarn.lock, pnpm-lock.yaml
Cargo.lock, go.sum, Gemfile.lock, poetry.lock
composer.lock, Pipfile.lock
If the file matches any fast-path pattern, sekretbarilo returns exit code 0 immediately without reading the file. This avoids unnecessary scanning overhead for files that pose no secret risk.
5. .env File Blocking
Files matching the .env pattern are always blocked unconditionally, regardless of content:
Blocked:
.env
.env.local
.env.production
.env.development
.env.staging
.env.test
Allowed (safe templates):
.env.example
.env.sample
.env.template
.env files almost always contain secrets (API keys, database passwords, tokens). Rather than scan them, sekretbarilo blocks them outright to prevent any possibility of exposure.
6. Full Scanning
If the file passes fast-path checks and isn’t a .env file, sekretbarilo reads it and runs the full detection engine:
- Aho-corasick keyword pre-filter identifies candidate rules
- Regex matching extracts potential secrets
- Shannon entropy analysis filters low-randomness strings
- Hash detection skips known hash formats (SHA-1, SHA-256, MD5, git commits)
- Stopword filtering removes known-safe values like
example,test,placeholder - Variable reference detection skips patterns like
${VAR},process.env.VAR
7. Exit Code
sekretbarilo returns an exit code to Claude Code:
| Exit Code | Meaning | Claude Code Action |
|---|---|---|
| 0 | Clean (no secrets found, or file skipped via fast-path) | Allow read |
| 2 | Secrets found, or error (file not found, JSON parse error, config failure) | Block read |
Exit code 2 is used for both secrets and errors because failing open would let secrets through. Claude Code treats any non-zero exit as a block.
Stdin JSON Payload
Claude Code sends a JSON payload on stdin when the hook is triggered. sekretbarilo parses this payload to extract the file path and working directory.
Schema
{
"tool_input": { "file_path": "path/to/file" },
"cwd": "/optional/working/directory"
}
Fields
| Field | Required | Description |
|---|---|---|
tool_input.file_path |
Yes | Path to the file Claude Code wants to read (absolute or relative) |
cwd |
No | Working directory context (used to resolve relative paths and vendor dirs) |
Example Payloads
Absolute path with cwd:
{
"tool_input": { "file_path": "/home/user/project/src/config.py" },
"cwd": "/home/user/project"
}
Relative path:
{
"tool_input": { "file_path": "src/config.py" },
"cwd": "/home/user/project"
}
Absolute path without cwd:
{
"tool_input": { "file_path": "/home/user/project/src/config.py" }
}
Extra Fields
sekretbarilo tolerates extra fields in the JSON payload and ignores them. This ensures forward compatibility if Claude Code adds new fields in the future:
{
"session_id": "abc123",
"hook_event_name": "PreToolUse",
"tool_name": "Read",
"tool_input": { "file_path": "src/config.py" },
"cwd": "/home/user/project"
}
Size Limit
stdin input is limited to 1 MB to prevent unbounded memory consumption. This is more than sufficient for JSON payloads containing file paths.
Fast-Path Skipping
Fast-path skipping is an optimization that allows certain files to pass through without scanning. These are files that cannot contain readable secrets or are not secret-relevant.
Why Fast-Path Skipping Matters
- Performance: skipping binary files avoids reading megabytes of image/video data
- Accuracy: scanning binary content produces garbage matches (false positives)
- Usability: AI agents need to read lock files, images, and dependencies without friction
Fast-path decisions are made based on file path patterns only, before the file is read. This keeps the check extremely fast.
Binary Files
Binary files cannot contain readable secrets in a form that matters for leakage:
.png, .jpg, .jpeg, .gif, .bmp, .svg, .ico, .webp
.pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .odt
.exe, .dll, .so, .dylib, .a, .o, .bin, .class
.zip, .tar, .gz, .bz2, .7z, .rar, .tgz, .xz
.mp3, .mp4, .avi, .mov, .wav, .flac, .mkv
.woff, .woff2, .ttf, .eot, .otf
.db, .sqlite, .sqlite3
.min.js, .min.css, .map
Even if a binary file somehow embeds a secret (e.g., an API key in image metadata), it’s not accessible to the AI agent in a way that creates risk.
Vendor Directories
Vendor directories contain third-party dependencies that are not part of your codebase:
node_modules/
vendor/
.bundle/
bower_components/
__pycache__/
.git/
.svn/
.hg/
These directories often contain thousands of files. Scanning them would:
- Slow down the agent significantly
- Produce false positives from test data in dependencies
- Block legitimate reads of library code
sekretbarilo skips these paths entirely.
Lock Files
Lock files are package manifests and checksums, not source code:
package-lock.json
yarn.lock
pnpm-lock.yaml
Cargo.lock
go.sum
Gemfile.lock
poetry.lock
composer.lock
Pipfile.lock
Lock files are often large (thousands of lines) and contain cryptographic hashes that can trigger false positives. They never contain user secrets, so they’re safe to skip.
User-Configured Patterns
In addition to built-in patterns, sekretbarilo respects user-configured allowlists and audit exclude patterns from .sekretbarilo.toml:
[allowlist]
paths = ["test/fixtures/.*", "docs/examples/.*"]
[audit]
exclude_patterns = ["^build/", "^dist/"]
These patterns are evaluated during the fast-path check, so you can customize which files are allowed through without scanning.
.env File Blocking
.env files are a special case: they are always blocked, regardless of content.
Why Block .env Files?
.env files are designed to store secrets. They typically contain:
- Database passwords
- API keys
- Service tokens
- Encryption keys
- OAuth secrets
Allowing an AI agent to read a .env file is almost always a mistake. Even if the file happens to be clean at the moment, it’s likely to contain secrets in the future.
Blocked Patterns
.env
.env.local
.env.production
.env.development
.env.staging
.env.test
.env.ci
Any file whose name matches these patterns is blocked with exit code 2.
Allowed Template Files
Template files are not blocked, because they contain placeholder values:
.env.example
.env.sample
.env.template
These files are safe for AI agents to read because they document the expected structure without exposing real secrets.
Output When Blocked
When sekretbarilo blocks a .env file, it writes a message to stderr:
[AGENT] .env file blocked: /home/user/project/.env
file likely contains environment secrets. reading blocked.
Claude Code will see this message and inform you that the file cannot be read.
Output Format
When check-file detects secrets, it writes diagnostic output to stderr (not stdout) with an [AGENT] prefix. This ensures Claude Code can display the error to you.
Clean File (Exit 0)
No output. The file is allowed through silently.
Secrets Detected (Exit 2)
[AGENT] secret(s) detected in src/config.rs
line: 42
rule: aws-access-key-id
match: AK**************QA
line: 58
rule: generic-api-key
match: sk_live_************************
file contains 2 secret(s). reading blocked to prevent secret exposure.
Output Fields
- line: line number where the secret was found
- rule: which detection rule matched (helps identify the secret type)
- match: partially redacted secret (first 2 and last 2 characters visible)
.env File Blocked
[AGENT] .env file blocked: .env
file likely contains environment secrets. reading blocked.
Error (Exit 2)
Errors (file not found, JSON parse failure, config load failure) also produce stderr output and exit with code 2:
[ERROR] failed to read /path/to/missing.py: No such file or directory
This fail-closed behavior ensures that errors don’t accidentally allow secrets through.
Using check-file Directly
The check-file command can be used standalone, outside of the Claude Code hook context. This is useful for testing, CI pipelines, or integrating with other tools.
Scan a File by Path
sekretbarilo check-file src/config.rs
Exit code 0 = clean, exit code 2 = secrets found.
Read File Path from Stdin JSON (Agent Hook Mode)
Simulate the Claude Code hook payload:
echo '{"tool_input":{"file_path":"src/config.rs"}}' | sekretbarilo check-file --stdin-json
This is the same mode used by the agent hook.
Example: CI Pipeline
Run check-file on all source files in CI:
#!/bin/sh
# scan all python files for secrets
for file in $(find src -name '*.py'); do
sekretbarilo check-file "$file"
if [ $? -eq 2 ]; then
echo "secret detected in $file"
exit 1
fi
done
echo "all files clean"
Example: Pre-Read Script
Use check-file in a script before opening files in an editor:
#!/bin/sh
# check file before opening in vim
sekretbarilo check-file "$1"
if [ $? -eq 2 ]; then
echo "file contains secrets. open anyway? (y/n)"
read -r answer
if [ "$answer" != "y" ]; then
exit 1
fi
fi
vim "$1"
Configuration
The check-file command respects all hierarchical configuration from .sekretbarilo.toml files. This means you can customize detection behavior for agent hooks the same way you customize pre-commit scanning.
Hierarchical Config Discovery
Config files are loaded in priority order (highest priority last):
/etc/sekretbarilo.toml(system-wide)~/.config/sekretbarilo/sekretbarilo.toml(user-level)~/.sekretbarilo.toml(home directory)- Parent directories from
$HOMEdown to project root .sekretbarilo.tomlin project root (highest priority)
Settings are merged across all levels. Scalar values (like entropy_threshold) use the most local value; lists (like allowlist.paths) are concatenated.
Allowlists
Allowlist patterns from config are applied during fast-path checks:
[allowlist]
paths = ["test/fixtures/.*", "docs/examples/.*"]
stopwords = ["my-safe-token"]
Files matching allowlist.paths patterns are allowed through without scanning.
Audit Exclude Patterns
Audit exclude patterns also apply to agent hooks:
[audit]
exclude_patterns = ["^build/", "^vendor/"]
Files matching these patterns are skipped.
Custom Rules
Custom detection rules are loaded and applied:
[[rules]]
id = "internal-api-key"
description = "Internal service API key"
regex = "(MYCO_[A-Z0-9]{32})"
secret_group = 1
keywords = ["myco_"]
Entropy Threshold
Override the entropy threshold globally or per-rule:
[settings]
entropy_threshold = 4.0
Example: Project-Specific Stopwords
Add project-specific stopwords to reduce false positives:
[allowlist]
stopwords = ["project-test-token", "known-safe-key"]
These stopwords apply to both pre-commit scanning and agent hooks.
Idempotent Installation
Running install agent-hook claude multiple times is safe and idempotent. sekretbarilo detects existing hooks and skips installation if already present.
First Install
$ sekretbarilo install agent-hook claude
created claude code hook configuration
Writes the hook to .claude/settings.json.
Second Install (Already Installed)
$ sekretbarilo install agent-hook claude
sekretbarilo already installed in claude code hooks
No changes are made. The existing hook is preserved.
Upgrade (Outdated Command Detected)
If an older version of sekretbarilo installed a hook with a different command format, the new installer updates it in place:
$ sekretbarilo install agent-hook claude
updated claude code hook configuration
The command is replaced with the current format (sekretbarilo check-file --stdin-json), and the timeout and status message are updated.
Preserves Other Hooks
If .claude/settings.json already contains hooks for other tools (e.g., Write, Bash), sekretbarilo preserves them:
Before:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [{"type": "command", "command": "echo write hook"}]
}
]
}
}
After:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [{"type": "command", "command": "echo write hook"}]
},
{
"matcher": "Read",
"hooks": [
{
"type": "command",
"command": "sekretbarilo check-file --stdin-json",
"timeout": 10,
"statusMessage": "Scanning file for secrets..."
}
]
}
]
}
}
The existing Write hook is untouched.
Doctor Diagnostics
The doctor command checks the health of your sekretbarilo installation, including agent hooks. Use it to diagnose issues with hook configuration.
Run Doctor
sekretbarilo doctor
Sample Output
git pre-commit hook:
[OK] local pre-commit hook installed
[NOT INSTALLED] global pre-commit hook not found
claude code agent hook:
[OK] local claude code hook installed (./.claude/settings.json)
[NOT INSTALLED] global claude code hook not found
configuration:
[OK] no custom config files found (using defaults)
[OK] 109 rules loaded successfully
[OK] rules compile successfully
sekretbarilo binary:
[OK] sekretbarilo found in PATH
What Doctor Checks (Agent Hooks)
For both local (./.claude/settings.json) and global (~/.claude/settings.json):
- File exists: settings.json is present
- Valid JSON: file parses correctly
- hooks.PreToolUse array exists: the hooks structure is present
- Read matcher entry exists: there’s a PreToolUse entry for the Read tool
- Command matches: the command is
sekretbarilo check-file --stdin-json - Outdated command detection: warns if an older sekretbarilo command is found
Status Levels
| Status | Meaning |
|---|---|
[OK] |
Check passed |
[WARN] |
Non-critical issue (e.g., outdated command, non-executable hook) |
[ERROR] |
Critical issue (e.g., malformed JSON, config parse failure) |
[NOT INSTALLED] |
Hook not found (informational, not an error) |
Exit Code
- 0: all checks passed (or only NOT INSTALLED status, which is informational)
- 1: one or more WARN or ERROR issues found
Example: Outdated Hook Detected
claude code agent hook:
[WARN] local claude code hook has outdated sekretbarilo command: sekretbarilo scan-file --old-flag
[NOT INSTALLED] global claude code hook not found
Fix by running:
sekretbarilo install agent-hook claude
The installer will update the command in place.
Codex CLI Note
Codex CLI is another AI coding agent tool. However, Codex CLI does not currently support a hooks system like Claude Code’s PreToolUse hooks.
The install agent-hook codex subcommand is reserved for future use. It is recognized by the CLI but not yet implemented:
$ sekretbarilo install agent-hook codex
[ERROR] codex agent hook installation not yet supported
Alternative: MCP Server Approach
An alternative integration approach under consideration is running sekretbarilo as an MCP (Model Context Protocol) tool server. This would allow any agent that supports MCP (including Codex CLI, if it adds MCP support) to call sekretbarilo as a tool.
If you need Codex CLI integration today, consider:
- Running
check-filemanually before Codex reads sensitive files - Using a wrapper script that calls
check-filebefore invoking Codex - Opening an issue or PR to discuss MCP server implementation
Practical Examples
Example 1: Developer Installs Hook, Claude Code Tries to Read .env
Setup:
# install agent hook
sekretbarilo install agent-hook claude
# create .env file
echo 'DB_PASSWORD=supersecret123' > .env
Claude Code session:
User: Read the .env file and show me the database password
Claude Code: [attempts to read .env]
[AGENT] .env file blocked: .env
file likely contains environment secrets. reading blocked.
Claude Code: I was unable to read the .env file because it was blocked by sekretbarilo. This is a security measure to prevent exposing secrets. If you need to share configuration, consider reading .env.example instead, or tell me what specific non-secret information you need.
Result: The .env file is never exposed to Claude Code. The developer is informed that the file was blocked.
Example 2: Claude Code Reads a Python File with AWS Key
Setup:
# install agent hook
sekretbarilo install agent-hook claude
# create file with secret
cat > src/config.py << 'EOF'
import os
aws_access_key = "AKIAIOSFODNN7EXAMPLE"
aws_secret_key = os.environ.get('AWS_SECRET_KEY')
def init_client():
return boto3.client('s3', aws_access_key_id=aws_access_key)
EOF
Claude Code session:
User: Read src/config.py and explain how the AWS client is initialized
Claude Code: [attempts to read src/config.py]
[AGENT] secret(s) detected in src/config.py
line: 3
rule: aws-access-key-id
match: AK**************YZ
file contains 1 secret(s). reading blocked to prevent secret exposure.
Claude Code: I was unable to read src/config.py because it contains an AWS access key. This is a security measure to prevent accidentally exposing the key. If you'd like help with this file, please remove the hardcoded key and use environment variables instead, then I'll be able to read it.
Result: The AWS key is never exposed. Claude Code provides actionable feedback (use environment variables).
Example 3: Claude Code Reads a Normal Source File
Setup:
# install agent hook
sekretbarilo install agent-hook claude
# create clean file
cat > src/utils.py << 'EOF'
import json
def load_config(path):
with open(path, 'r') as f:
return json.load(f)
def save_config(path, data):
with open(path, 'w') as f:
json.dump(data, f, indent=2)
EOF
Claude Code session:
User: Read src/utils.py and explain what it does
Claude Code: [attempts to read src/utils.py]
[sekretbarilo scans silently, exits 0, no output]
Claude Code: [reads file successfully]
This module provides two utility functions for working with JSON configuration files:
1. load_config(path): Reads a JSON file and returns the parsed data
2. save_config(path, data): Writes data to a JSON file with pretty-printing (2-space indent)
The functions use Python's built-in json module and file I/O.
Result: The file passes through transparently. Claude Code reads it normally and provides the requested explanation.
Example 4: Claude Code Reads a Binary/Lock File (Fast-Path Skip)
Setup:
# install agent hook
sekretbarilo install agent-hook claude
# copy a real lock file (thousands of lines)
cp package-lock.json /tmp/test-lock.json
Claude Code session:
User: Read package-lock.json and tell me which version of react is installed
Claude Code: [attempts to read package-lock.json]
[sekretbarilo fast-path detects lock file, exits 0 immediately, no scanning]
Claude Code: [reads file successfully]
The project is using React version 18.2.0. It's listed as a direct dependency with the following details:
- Version: 18.2.0
- Resolved from: https://registry.npmjs.org/react/-/react-18.2.0.tgz
- Integrity: sha512-...
...
Result: The lock file is recognized during fast-path checks and allowed through without scanning. The read completes quickly with no overhead, and Claude Code can answer the question normally.
Troubleshooting
Hook Not Firing
Symptom: Claude Code reads files without triggering sekretbarilo.
Check:
- Verify hook is installed:
sekretbarilo doctor - Check
.claude/settings.jsonexists and contains the hook - Ensure sekretbarilo binary is in PATH:
which sekretbarilo - Test
check-filemanually:sekretbarilo check-file path/to/file
Hook Fails with “command not found”
Symptom: Claude Code shows error: sekretbarilo: command not found.
Fix:
- Add sekretbarilo to PATH:
export PATH="$HOME/.cargo/bin:$PATH" - Or use absolute path in hook: edit
.claude/settings.jsonand change command to/full/path/to/sekretbarilo check-file --stdin-json
False Positives Blocking Clean Files
Symptom: sekretbarilo blocks a file that doesn’t contain real secrets.
Fix:
- Add the false positive value to stopwords in
.sekretbarilo.toml:[allowlist] stopwords = ["known-safe-value"] - Or allowlist the file path:
[allowlist] paths = ["path/to/false-positive-file.py"]
Slow Hook Execution
Symptom: Claude Code shows “Scanning file for secrets…” for several seconds.
Check:
- Verify fast-path patterns are working: binary files, vendor dirs, and lock files should skip instantly
- Large source files (>10k lines) may take longer to scan
- Check if custom rules have expensive regexes
Fix:
- Exclude large generated files in
.sekretbarilo.toml:[audit] exclude_patterns = ["^build/", "^dist/"]
Hook Installed but doctor Shows Outdated
Symptom: sekretbarilo doctor reports outdated command.
Fix: Run the installer again to update the command:
sekretbarilo install agent-hook claude
This will detect the outdated command and replace it with the current format.
Summary
Agent hooks provide a critical security layer for AI-assisted development:
- Prevent secret exposure: Block file reads before secrets reach the agent
- Fast and unobtrusive: Fast-path skipping ensures minimal overhead for binary files, dependencies, and lock files
- Comprehensive coverage: All detection rules, allowlists, and config apply to agent hooks
- Easy installation: One command installs the hook for Claude Code
- Diagnostic support:
doctorcommand validates hook configuration
Install agent hooks today to protect your secrets from accidental exposure through AI coding agents:
# project-local
sekretbarilo install agent-hook claude
# global
sekretbarilo install agent-hook claude --global
# or install everything at once
sekretbarilo install all --global
For more information, see:
- Getting Started - Overview and quick setup
- CLI Reference - Complete command reference
- Configuration - Customizing detection rules and allowlists