-
Notifications
You must be signed in to change notification settings - Fork 0
Agent Integration
RedLog (Red Team Operation Log) is designed to work as a passive recorder for AI-driven penetration testing. This document covers all integration methods, from zero-config terminal hooks to full MCP control.
┌─────────────────────────────────────────────────────────┐
│ AI Agent │
│ (Claude Code / Codex / GPT / Cursor / Aider / custom) │
└──────┬────────────────┬────────────────┬────────────────┘
│ │ │
┌────▼────┐ ┌──────▼──────┐ ┌─────▼─────┐
│ Terminal │ │ MCP Server │ │ HTTP API │
│ Hooks │ │ (18 tools) │ │ (REST) │
│ (log) │ │ (operate) │ │(universal)│
└────┬────┘ └──────┬──────┘ └─────┬─────┘
│ │ │
└────────────────┼────────────────┘
│
┌─────────▼─────────┐
│ RedLog Engine │
│ (SQLite + Event │
│ Bus + Timeline) │
└───────────────────┘
Passive hooks capture everything without the agent knowing. MCP/API lets the agent actively query scope, create markers, and search history.
The cleanest way to think about it:
- Hooks are the data plane — they record. Passive, automatic, fire on every command. This is where logging happens.
- MCP is the control plane — it operates and configures the app. Create markers, check scope, anchor the chain, read the timeline. These are actions on RedLog, not a substitute for capturing what the agent did.
For logging, always prefer hooks; use MCP only for what a hook cannot do. The reason is completeness, not taste:
- A hook fires whether or not the agent is thinking about it — a command cannot run without being recorded. For an audit log, that guarantee is the whole point.
- MCP only records what the agent decides to call. An agent that forgets, is interrupted, or wasn't prompted will leave gaps, and a gap in an audit log is indistinguishable from "nothing happened." Never make capture depend on the agent's memory.
So the division of labour is:
| Use hooks for (logging) | Use MCP for (operating the app) |
|---|---|
| Every shell / Bash command and its output (automatic) | Findings and phase markers (redlog_mark) — a judgement a hook can't infer |
| mitmproxy HTTP traffic (automatic) | Scope checks before acting (redlog_scope) |
| Screenshots, clipboard, file transfers (automatic) | Loot scanning of pasted output (redlog_loot_scan) |
Confirming identity (redlog_whoami), anchoring the chain (redlog_chain_anchor_now), reading history (redlog_search/redlog_events) |
|
Structured events for actions no shell ran — GUI clicks, manual observations (redlog_log_event) |
If activity can be captured by a hook, let the hook capture it and do not also log it over MCP — that just produces duplicates. MCP is for the control-plane actions and the observations that never touched a shell.
So: install all applicable hooks first for logging, then connect MCP for operating the app.
Terminal hooks intercept commands at the shell level. The agent doesn't need to be RedLog-aware — every command it runs is automatically logged with timestamps, exit codes, and duration.
Claude Code's hook system fires a script after every tool call. Our hook captures Bash tool calls specifically.
Setup:
Add to ~/.claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"command": "/path/to/redlog/hooks/claude-code-hook.sh"
}
]
}
]
}
}Or per-project in .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"command": "$PROJECT_DIR/hooks/claude-code-hook.sh"
}
]
}
]
}
}What it captures:
| Field | Source | Example |
|---|---|---|
| command | CLAUDE_TOOL_INPUT.command |
nmap -sV target.com |
| output_preview |
CLAUDE_TOOL_OUTPUT (first 500 chars) |
Starting Nmap 7.94... |
| session_id | CLAUDE_SESSION_ID |
abc123-def456 |
| subtype | hardcoded | claude_code_bash |
Environment variables provided by Claude Code:
-
CLAUDE_TOOL_NAME— tool name (we filter for"Bash") -
CLAUDE_TOOL_INPUT— JSON string of tool input parameters -
CLAUDE_TOOL_OUTPUT— JSON string of tool output (PostToolUse only) -
CLAUDE_SESSION_ID— current Claude Code session identifier
Timeline appearance: Events appear as agent type with subtype claude_code_bash in the Timeline swim-lane view.
Works with ANY agent that spawns a shell process. Hooks into zsh preexec/precmd or bash DEBUG trap.
Setup:
# Add to ~/.zshrc
source /path/to/redlog/hooks/shell-preexec-hook.sh
# Or add to ~/.bashrc
source /path/to/redlog/hooks/shell-preexec-hook.shWhat it captures:
| Event | Timing | Fields |
|---|---|---|
command_start |
Before command runs | command, shell, pid |
command_end |
After command completes | command, exit_code, duration_sec |
How it works (zsh):
-
preexechook fires before each command — records the command text and start time -
precmdhook fires after each command — records exit code and calculates duration - Events are sent to RedLog via HTTP API in the background (
curl ... &)
How it works (bash):
-
DEBUGtrap fires before each command -
PROMPT_COMMANDfires after each command - Same event emission as zsh
Performance: Negligible. The curl call runs in background and has a 1-second timeout. If RedLog isn't running, the hook silently does nothing.
For agents where you can't modify their hook system but can control the shell they launch.
Usage:
# Option 1: Set as the agent's shell
SHELL=/path/to/redlog/hooks/codex-wrapper.sh codex run "scan the target"
# Option 2: Wrap a specific command
./hooks/codex-wrapper.sh nmap -sV target.com
# Option 3: Start a wrapped interactive shell
./hooks/codex-wrapper.sh
# (loads shell-preexec-hook.sh automatically)How it works:
- If called with arguments: wraps that single command with start/end events
- If called without arguments: starts an interactive shell with preexec hooks loaded
- Sets
REDLOG_SHELL_WRAPPED=1env var so tools can detect the wrapper
The MCP (Model Context Protocol) server lets compatible agents operate RedLog — check scope before scanning, create markers for findings, anchor the chain, search history. Two transports; HTTP is recommended.
RedLog hosts its own MCP server over HTTP from the process already running the API, so MCP is live the moment the app is open — no subprocess to spawn, no node on PATH, nothing to unpack from the app bundle.
- In RedLog: Settings ▸ Team & Integrations ▸ MCP Server ▸ Set up MCP access. This mints a dedicated, non-rotating operator token (so the registration survives restarts and MCP activity is attributed to its own identity) and shows a ready-to-paste command.
- Run it once:
claude mcp add --transport http redlog http://127.0.0.1:6660/mcp \
--header "Authorization: Bearer <mcp-operator-token>"claude mcp list should show redlog … (HTTP) - ✔ Connected. From then on, every time RedLog is open the endpoint is live.
For clients without HTTP-MCP support, the standalone bridge still works. It's a subprocess the client spawns, and in a packaged app the script lives inside the bundle's resources:
# Claude Code (dev checkout)
claude mcp add redlog -- node /path/to/redlog/mcp/redlog-mcp-server.js
# Cursor — .cursor/mcp.json
{ "mcpServers": { "redlog": { "command": "node", "args": ["/path/to/redlog/mcp/redlog-mcp-server.js"] } } }The stdio bridge discovers the running app via ~/.redlog/api-port + api-token and proxies to the same tools. HTTP avoids the subprocess and the node-on-PATH requirement entirely.
The MCP server automatically discovers a running RedLog instance by reading:
-
~/.redlog/api-port— the HTTP API port (default 6660) -
~/.redlog/api-token— the Bearer auth token
These files are created when RedLog starts. If they don't exist, the MCP server returns an error message suggesting to launch RedLog first.
Returns current IP addresses (external/internal), IP safety flag, total event count, and scope violation count.
Parameters: none
Returns:
{
"ip": {
"externalIP": "203.0.113.42",
"internalIP": "10.0.1.5",
"ipSafety": "safe",
"lastCheck": 1706400000000,
"error": null
},
"eventCount": 1247,
"scopeViolations": 3
}Return the operator identity that this token resolves to. Every event logged under this token is attributed to this operator. Call once at session start to catch loaded-wrong-token mistakes.
Parameters: none
Returns:
{
"operator": {
"id": "codex-agent-a1b2c3",
"name": "Codex agent",
"isPrimary": false,
"createdAt": 1706300000000,
"revokedAt": null
},
"engagementId": "client-pentest-q3"
}List every operator token registered on this RedLog instance.
Returns: { "operators": [ ... ] } — each entry mirrors the operator object in redlog_whoami.
Length of the SHA-256 hash chain and the latest OpenTimestamps anchor.
Returns:
{
"length": 1247,
"lastAnchor": {
"id": "…",
"headHash": "9f2c…",
"eventCount": 1200,
"status": "complete",
"createdAt": 1706398800000,
"calendarReceipts": [
{ "calendar": "https://a.pool.opentimestamps.org", "ok": true, "receiptB64": "…" }
]
}
}Submit the current chain head to public OpenTimestamps calendars immediately. Use before wrapping up a session or right after a critical finding.
Fast check: does the latest anchor still describe a prefix of the current chain? Compares event counts (not a full re-walk).
Returns: { "ok": true, "anchor": { … }, "currentHead": "9f2c…" }
Creates a timestamped marker in the timeline — used for findings, phase changes, and notes.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
| title | string | yes | Marker title |
| notes | string | no | Detailed notes |
| severity | string | no |
info / low / medium / high / critical
|
| target_id | string | no | Associated target |
Example:
{
"title": "SQL Injection in /api/users",
"notes": "Parameter 'id' is injectable — time-based blind, PostgreSQL",
"severity": "high",
"target_id": "api.example.com"
}Logs a raw event with custom agent type and data payload.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
| agent_type | string | yes | Event category (agent, scanner, recon, exploit) |
| data | object | yes | Event payload (freeform JSON) |
| target_id | string | no | Associated target |
Full-text search across all events.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
| query | string | yes | Search query |
| limit | number | no | Max results (default 20) |
Query recent events with filters.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
| agent_type | string | no | Filter by type |
| target_id | string | no | Filter by target |
| limit | number | no | Max results (default 50) |
Returns scope configuration, target list, violations, and violation count.
Returns:
{
"targets": ["*.example.com", "10.0.0.0/8"],
"excludeTargets": ["10.0.0.1"],
"enforcement": "warn",
"violations": [...],
"violationCount": 3
}Returns the full project configuration (engagement, operator, network, scope settings).
Creates a bookmark for an interesting URL, endpoint, or finding.
Parameters:
| Param | Type | Required |
|---|---|---|
| title | string | yes |
| url | string | no |
| note | string | no |
Lists all bookmarks in the current project.
Scans arbitrary text for credentials, secrets, and sensitive data.
Parameters:
| Param | Type | Required |
|---|---|---|
| text | string | yes |
Detected patterns: AWS keys, API tokens, JWTs, password hashes, private keys, database connection strings, CTF flags.
Captures a screenshot and saves it to the project's screenshot directory.
Controls recording state.
Parameters:
| Param | Type | Required | Description |
|---|---|---|---|
| action | string | yes |
pause / resume / toggle
|
Ship-ready skill file at docs/skills/redlog-pentest.md. Install with:
cp docs/skills/redlog-pentest.md ~/.claude/skills/redlog-pentest.md
# or per-project
cp docs/skills/redlog-pentest.md .claude/skills/redlog-pentest.mdThe skill covers the full loop: session start (whoami + status + scope), real-time findings via redlog_mark, loot scanning, and end-of-session chain_anchor_now. Read the skill directly for the exact flow.
Direct REST API for scripts, custom agents, and non-MCP tools. Runs on 127.0.0.1:6660 (configurable).
TOKEN=$(cat ~/.redlog/api-token)
PORT=$(cat ~/.redlog/api-port)
# GET request
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:$PORT/api/status
# POST request
curl -X POST http://127.0.0.1:$PORT/api/marker \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"title":"Finding","severity":"high"}'| Method | Route | Auth | Description |
|---|---|---|---|
| GET | /api/health |
no | Health check |
| GET | /api/whoami |
yes | Resolve token → operator |
| GET | /api/status |
yes | System status (IP, events, violations) |
| GET | /api/config |
yes | Project configuration |
| GET | /api/scope |
yes | Scope targets + violations |
| GET | /api/recording |
yes | Recording state |
| POST | /api/recording |
yes | Control recording ({"action":"pause|resume|toggle"}) |
| POST | /api/events |
yes | Insert event (operator_id set from token) |
| GET | /api/events |
yes | Query events (?agent_type=&limit=&target_id=&since=) |
| GET | /api/events/search |
yes | Search events (?q=&limit=) |
| GET | /api/events/count |
yes | Event count |
| POST | /api/marker |
yes | Create marker |
| GET | /api/quickmarks |
yes | List bookmarks |
| POST | /api/quickmarks |
yes | Create bookmark |
| POST | /api/loot/scan |
yes | Scan for secrets |
| POST | /api/screenshot |
yes | Trigger manual capture |
| GET | /api/operators |
yes | List operators (any valid token) |
| POST | /api/operators |
primary | Create operator, returns token once |
| PATCH | /api/operators/:id |
primary | Rename |
| POST | /api/operators/:id/rotate |
self or primary | Rotate token |
| POST | /api/operators/:id/revoke |
primary | Revoke token |
| DELETE | /api/operators/:id |
primary | Delete operator |
| GET | /api/chain |
yes | Chain length + latest anchor |
| GET | /api/anchors |
yes | List OTS anchors (?limit=) |
| POST | /api/anchors |
yes | Anchor current chain head now |
| GET | /api/anchors/verify |
yes | Fast prefix check (add ?full=1 for full re-walk) |
| GET | /api/anchors/:id/ots |
yes | Download standard .ots bundle (?calendar=<url> optional) |
| GET | /api/clock |
yes | NTP offset + last query time + host wall clock |
| POST | /api/export/bundle |
yes | Produce signed evidence bundle in project's exports/ dir |
| GET | /api/deconfliction |
yes | Current deconfliction config (secret redacted) |
| POST | /api/deconfliction/test |
yes | Send a test payload to the configured webhook |
{
"agent_type": "shell|scanner|agent|recon|exploit",
"data": {
"subtype": "command_start|command_end|scan_complete",
"command": "nmap -sV target.com",
"output": "..."
},
"target_id": "target.com"
}{
"title": "Found SQLi in /api/users",
"notes": "Parameter 'id' is injectable, time-based blind",
"severity": "high",
"target_id": "api.example.com"
}Source the helper script for quick access from any terminal:
source /path/to/redlog/shell/redlog-agent.sh| Function | Description | Example |
|---|---|---|
redlog_status |
Check if RedLog is running | redlog_status |
redlog_mark |
Create a marker | redlog_mark "Finding" "Details" "high" |
redlog_event |
Log raw event | redlog_event "agent" '{"subtype":"done"}' |
redlog_note |
Quick note marker | redlog_note "Switching to auth bypass" |
redlog_search |
Search events | redlog_search "password" 20 |
redlog_loot |
Scan for creds | redlog_loot "root:x:0:0:..." |
redlog_scope |
Get scope info | redlog_scope |
redlog_config |
Get project config | redlog_config |
redlog_quickmark |
Bookmark a URL | redlog_quickmark "Endpoint" "https://..." |
redlog_screenshot |
Manual capture | redlog_screenshot |
See codex-tools.json for OpenAI-compatible function definitions. These work with Codex, GPT, or any OpenAI-API-compatible model.
8 tool definitions included:
-
redlog_status— get recording status -
redlog_mark— create finding/phase marker -
redlog_log_event— log arbitrary event -
redlog_search— full-text search -
redlog_scope— check scope config -
redlog_loot_scan— scan for credentials -
redlog_screenshot— capture screenshot -
redlog_recording— control recording state
Export your project config for team sync:
# In RedLog UI: Settings → Team Profile Sync → Export Profile
# Produces a YAML file like:
# redlog-profile-my-pentest.yaml
version: 1
engagement:
id: client-pentest-q3
name: "Client Pentest Q3"
scope:
enforcement: warn
targets:
- "192.168.1.0/24"
- "*.example.com"
network:
vpnIPs:
- "10.8.0.0/24"Team members import this when creating a new project:
- Project Picker → Advanced Setup → Import Profile
- Settings → Team Profile Sync → Import Profile
The title-bar Launch Browser button (also in Settings ▸ Data) starts a Chromium-based browser wired up for capture in one click:
| Flag | Why |
|---|---|
--proxy-server=<your proxy> |
mitmproxy sees the traffic, so it lands in the timeline as scanner events |
--proxy-bypass-list=<-loopback> |
Chrome bypasses the proxy for localhost by default — this re-enables capture for local targets |
--remote-debugging-port=<port> |
QuickMarks can read the active tab's URL and title |
--user-data-dir=<project>/browser-profile |
Project-local profile, so none of this touches your daily browsing |
--ignore-certificate-errors |
Required for mitmproxy's interception cert |
Launching logs a system event with subtype: browser_launched recording the binary, proxy, CDP port, and pid — so the report shows exactly which browser instance produced the captured traffic. The browser is terminated when RedLog quits.
Configure under Settings ▸ Data ▸ Proxied Browser, or in config.yaml:
browser:
binary: "" # blank = auto-detect Chrome/Chromium/Brave/Edge
proxy: "http://127.0.0.1:8080" # blank = no proxy flag
cdpPort: 9222
isolateProfile: true
ignoreCertErrors: true
startUrl: ""
extraArgs: []Each API token maps to one operator identity. The audit log tags every event with the operator resolved from the token — so several people (or several agents) can share one RedLog instance and stay distinguishable.
Full lifecycle in docs/operators.md. TL;DR:
-
You (primary): use RedLog's own generated token in
~/.redlog/api-token. Rotated every app launch. Do nothing. -
Teammate: Settings ▸ General ▸ Operator Tokens ▸ Add operator ▸ copy token once ▸ hand over securely. On their machine:
echo -n '<token>' > ~/.redlog/api-token && chmod 600 ~/.redlog/api-token. -
Distinct agent context on the same machine (e.g. Codex vs Claude): create a secondary token and have that agent use
REDLOG_TOKEN=<token>instead of the file — patch the one hook that agent uses.
The events table is a SHA-256 hash chain (every row's hash covers the previous row's). Once an hour RedLog submits the chain head to three public OpenTimestamps calendars, producing receipts stored in chain_anchors. Anyone with a receipt can later prove the chain existed at that time — without touching your machine.
Agents should call redlog_chain_anchor_now right before ending a session so anything done in-session is timestamped in-session (don't wait for the hourly loop).
Details, threat model, and verification workflow: docs/audit-trail.md.
For maximum coverage with minimal friction — hooks first, MCP only for the gaps (see Capture priority):
-
Install the shell preexec hook in
~/.zshrc— passive, captures every command from every agent. This is the backbone; do it first. - Add the Claude Code PostToolUse hook — structured Bash tool-call capture.
- Add the mitmproxy addon if you're proxying traffic — passive HTTP capture.
- Only then add the MCP server — for the agent-initiated actions hooks can't do (markers, scope checks, anchoring), never as a substitute for capture the hooks already handle.
- Install the redlog-pentest skill — guides the agent to use MCP only for those gaps.
- For each teammate: add a secondary operator via Settings ▸ Operator Tokens so the audit log stays distinguishable.
This gives you:
- Automatic passive capture of every command (hooks)
- Agent-initiated markers and scope checks (MCP)
- Team-shared scope configuration (profiles)
- Per-operator attribution + Bitcoin-backed integrity anchoring
Integration
Evidence
SSH / VPS
Extending
Privacy
Agent skill
Ops