Skip to content

Repository files navigation

🔍 AgentDFIR

Open-source digital forensics and incident response for AI agents.

Collect, preserve, reconstruct and investigate activity from Claude Code, Claude Cowork, Codex (CLI and desktop app), Cursor, Gemini CLI, Copilot and other AI agents.

CI License: MIT Go Zero deps

Website · Install · Quick start · Evidence format · Contributing

The AgentDFIR case explorer: the finding “The agent read a password or key, then sent data out”, with what happened, why it matters, the most likely start and the step-by-step story. Demo case from agentdfir simulate.


AI coding agents execute shell commands, edit files, spawn subagents, call MCP servers and push code. When something goes wrong — a prompt injection, a poisoned MCP tool, a rogue subagent, quiet data exfiltration — the transcripts and configs they leave on the endpoint are primary forensic evidence. Almost no tooling exists to acquire and analyze them properly.

AgentDFIR is that tooling. Think KAPE / Velociraptor for the agentic-AI layer. It lets an incident responder answer:

Who instructed which AI agent/subagent to perform what action, through which tool/MCP/identity, against which resource — and what evidence proves it?

⚖️ Evidence vs. claims — the core principle

AI-generated text is never automatically treated as proof of execution. Every action gets an evidence state, and enriching a case with a second witness is what moves it up:

State Meaning
ASKED a human asked for it
CLAIMED the model said it happened — narrative, not proof
RECORDED a tool-call record exists in the transcript
PARTLY CONFIRMED part of it matched a second source
CONFIRMED independent endpoint/network evidence confirms it
DISPROVED endpoint evidence shows it did not occur
UNKNOWN insufficient evidence

An agent claiming "I executed curl example.com" with no matching tool call stays CLAIMED — and AgentDFIR shows you exactly that.

⚡ Quick start — one command

Nothing installed yet? Paste the line for your OS into a terminal. It installs AgentDFIR (SHA256-verified), finds every AI agent on this machine, collects their evidence into one sealed package, analyzes it and opens the results in your browser. Nothing leaves your machine.

macOS / Linux

curl -fsSL https://raw.githubusercontent.com/efij/AgentDFIR/main/install.sh | sh && ~/.local/bin/agentdfir run

Windows (x64 or ARM64) — Command Prompt or PowerShell

powershell -NoProfile -ExecutionPolicy Bypass -Command "[Environment]::SetEnvironmentVariable('AGENTDFIR_RUN','1'); irm https://raw.githubusercontent.com/efij/AgentDFIR/main/install.ps1 | iex"

Already installed: agentdfir run. Homebrew: brew install efij/agentdfir/agentdfir && agentdfir run. Scoop: scoop bucket add agentdfir https://github.com/efij/scoop-agentdfir; scoop install agentdfir. Chocolatey: choco install agentdfir.

Other ways to install — portable binary, Homebrew, Scoop, go install, from source

One static binary, zero runtime dependencies. Full guide with air-gap, checksum and Sigstore verification steps: docs/install.md.

# Portable, air-gap friendly: grab the raw binary for your OS from the releases page
#   https://github.com/efij/AgentDFIR/releases/latest
# macOS: browser downloads are quarantined; unsigned binaries need this once per file
xattr -d com.apple.quarantine agentdfir-v*-darwin-arm64 && chmod +x agentdfir-v*-darwin-arm64

# Online macOS / Linux, no dialogs (verifies SHA256, installs to ~/.local/bin)
curl -fsSL https://raw.githubusercontent.com/efij/AgentDFIR/main/install.sh | sh

# Homebrew
brew install efij/agentdfir/agentdfir

# Scoop (Windows)
scoop bucket add agentdfir https://github.com/efij/scoop-agentdfir; scoop install agentdfir

# Go toolchain
go install github.com/efij/AgentDFIR/v3/cmd/agentdfir@latest

# From source
go build -trimpath -o agentdfir ./cmd/agentdfir

Step by step — the same thing, one command per step

agentdfir detect                                   # 1. what AI agents are on this machine (never runs them)
agentdfir collect --product claude                 # 2. sealed, hash-chained evidence package
agentdfir analyze CASE-2026-042.adfir              # 3. every analysis stage, one command
agentdfir serve   CASE-2026-042.adfir --open       # 4. browse: overview, findings as stories, activity, MCP, search

Add a second witness and the same commands upgrade every finding from the agent says to the OS confirms:

agentdfir analyze CASE-2026-042.adfir --endpoint /var/log/audit/audit.log   # auditd / Sysmon XML / EDR exports
agentdfir analyze CASE-2026-042.adfir --endpoint cloudtrail.json           # CloudTrail / Azure Activity Log / GCP audit exports
agentdfir analyze CASE-2026-042.adfir --gateway-log mcp-gateway.jsonl        # your MCP gateway's own log

Other ways in: collect --path <copied home>, --import <KAPE/Velociraptor tree>, --docker <container>, --archive <zip|tar>. Other ways out: report --format pdf|html|ocsf|sarif|timesketch|…, rules list --packs rules (every detection → MITRE ATLAS / ATT&CK), rules export --sigma. Before an incident: monitor --detect --alert <url>, mcp audit. Every command is listed by workflow step in agentdfir help.

Example finding:

HIGH — Unexpected Agent Activity [ORPHAN_AGENT]
  Session: 9b2d7e3a-…
  Agent:   adad4e2c    Parent: UNKNOWN
  Finding: Agent appeared without a verified parent invocation.
  Related: SendMessage/resume interaction with agent a7c3f19b
  Evidence: .claude/projects/…/agent-adad4e2c.jsonl:1 (artifact 6443bed58e63)
  Evidence: RECORDED    Second witness: UNKNOWN

No auto-escalation to "compromise" or "exfiltration" — findings state exactly what the evidence shows, with clickable references to the raw artifact behind every claim.

🧭 The last twelve months of agent incidents, as commands (v3.0)

Every capability below comes from a real 2025–2026 incident and is tested against a reproduction of it (agentdfir simulate --scenario list).

agentdfir hunt --path ~/src          # was I hit? s1ngularity, Shai-Hulud 1/2, keyv wave, SANDWORM_MODE, postmark-mcp, codexui, Amazon Q, Storm-3168/JADEPUFFER
agentdfir scan-repo ~/src/untrusted  # before an agent opens it: committed SessionStart hooks, folderOpen tasks, repo MCP servers, injected AGENTS.md
agentdfir decode payload.txt         # nested base64/gzip/hex/UTF-16LE payloads, offline — no model refuses to help
agentdfir monitor --journal          # hash-chain every transcript append; a later edit becomes TRANSCRIPT_REWRITTEN
  • hunt — verified indicators from each incident's primary write-up, plus STIX 2.1 / MISP feeds. Every hit says where it was seen: a command the agent ran is OBSERVED, a shell's output is SEEN_IN_OUTPUT, a question about the incident is only MENTIONED. A case whose evidence starts after the incident says INCONCLUSIVE instead of a false all-clear. Lockfiles, npm's hidden lockfile, shell rc files and payload hashes on disk with --path.
  • scan-repo — the files dependency scanners never read: Claude/Cursor/Gemini hooks, statusLine and credential helpers, committed env overrides, VS Code folderOpen tasks, devcontainer host commands, project MCP configs (typosquats, remote fetch, poisoning), instruction files, install scripts that launch AI CLIs headless, git config and ref-name injection. SARIF for GitHub code scanning; a GitHub Action and a pre-commit hook.
  • Decoder — every command rule also sees what an executed payload decodes to (ENCODED_PAYLOAD_EXECUTED).
  • Journal — tamper evidence for transcripts, with the chain head anchored off the file.
  • New detections — AI CLIs run headless with approvals off (AI_CLI_HEADLESS_BYPASS, s1ngularity), a session opened with a secret-hunting prompt, exfiltration through trusted services (link shorteners, screenshot services, workers.dev, blockchain RPC, tunnels), GitHub repo creation as an exfil path, data encoded into DNS labels, cloud/database destruction (Replit, PocketOS), cloud resource deletion and removal of resource locks, backups and deletion protection (Storm-3168), MCP tool-definition rug-pulls and typosquatted MCP packages.

🛡️ Stop it happening again — agentdfir mitigate (v2.7)

Findings become guardrails in the agents' own settings: permissions.deny/ask for Claude Code, a rules file for Codex, permissions.deny for Cursor, pinned MCP packages and cleared auto-approve lists. Two packs are on by default and never get in the way of legitimate work — keep the agents' own logs (a PreToolUse guard that refuses transcript deletion) and keep credential files away from the agent. Seven more are opt-in, each with its friction stated, including ask before deleting cloud resources or their backups.

agentdfir mitigate                 # the plan — nothing is written
agentdfir mitigate --apply         # asks per file; backed up, ledgered, reversible
agentdfir mitigate --status        # verified against the files now: in place / drifted
agentdfir mitigate --revert-all    # every file back, byte-exact

This is the only thing that changes files outside a case, and run never does it on its own. The explorer's Protect tab (v3.1) previews every file and diff, applies the changes when you press Apply, and lists each one with Undo — on the machine the case came from; for a case from another computer it builds the command to run there. Full rules: docs/mitigate.md.

📤 Look at another computer's case

agentdfir export                   # on the suspect machine: one file, IR.adfir.tgz, + SHA-256
agentdfir open IR.adfir.tgz        # on yours: unpacked, seal verified, analyzed, open in the browser

The file carries the sealed evidence and the case notes; the analysis is rebuilt by the receiving machine's binary, so nobody has to trust someone else's conclusions. open also takes a package directory or an encrypted .enc file.

🔎 From findings to a story — the investigation layer (v1.0)

A list of 700 findings is not an answer. The explorer (agentdfir serve) turns it into one:

  • The answer first, in plain words. "4 alerts need action now", then the few kinds of issue to start with. Findings are grouped by kind (1,237 → ~60 on a real machine), and each reads what happened · why it matters · ask yourself · what to do, with one verdict line ("Act now if real · Needs your check") and the account it ran under — for people who are not security analysts.
  • Attack chains (toxic combinations). Individually unremarkable steps that together are an attack: injection in a tool result → agent rewrites its own instructions → shell runs, secret read → upload, orphan agent → config change → tool use, poisoned MCP result → destructive command, injection → commit → push, action → log deletion… Eight ship built in, matched inside one session or one agent's lineage within a time window, mapped to MITRE ATLAS / ATT&CK. Add your own as *.chains.json (docs).
  • How it happened, as a story. Every finding opens to a swimlane diagram — You · The AI agent · Tools & outside world — one card per evidence line in time order, the flagged steps in red, the outside addresses they reach, and the most likely start (your request, an automatic skill message, injected text the agent read, or a helper agent) marked on its card. Every card opens the exact sealed log line.
  • Plugin (MCP) activity. Every MCP call in time order with the account, plugin, what the agent sent and what came back.
  • Command palette. Cmd+K / Ctrl+K (or /) opens one box for everything: jump to any view, finding or conversation by fuzzy match, run actions, and see matching actions from the evidence as you type. Cmd/Ctrl+Enter runs the full search.
  • Search everything. Every event field, every finding, and the raw bytes of every sealed artifact, live, in seconds, regex or literal.
  • Case file. Mark findings true / false positive / needs review, pin key evidence, tag sessions, write notes. Saved as a hash-chained log outside the sealed evidence, attributed to you, and rendered as an Analyst Investigation section in the PDF and HTML reports.

Try it on a synthetic incident: agentdfir simulate --scenario toxic-chain --out ./sim && HOME=./sim agentdfir run.

📦 The .adfir evidence package

Every acquisition produces a sealed, self-describing, independently parseable package:

case.adfir/
├── raw/<sha256>[.gz]       content-addressed evidence bytes (deduped, compressed when it pays)
├── manifest.jsonl          append-only per-artifact metadata + logical paths
├── collection.jsonl        hash-chained collection log
├── chain-of-custody.jsonl  hash-chained custody log
├── case.json               case, operator, timezone/clock metadata, per-round summaries
├── seals/SHA256SUMS.<n>    the seal each earlier round was closed with
├── SHA256SUMS              covers the sealed zone exactly, as of the latest round
├── normalized/             events / entities / relationships (regenerable)
└── detections/             findings.json

Acquisition guarantees:

  • 🔒 Lossless — nothing redacted or rewritten at collection time
  • #️⃣ Hash-while-copy — artifact_id is the SHA-256 of the plaintext, whatever the bytes on disk look like; stored_sha256 covers the stored bytes, so compression can never mask tampering. Torn-read detection for files a live agent is still writing
  • 🔗 Symlinks never followed — a planted symlink can't pull ~/.ssh into evidence, and sources are opened O_NOFOLLOW with a post-open identity check so the window between classifying a path and reading it isn't exploitable
  • 📝 Every failure recorded — access denied, size bounds, irregular files, policy exclusions
  • 🧾 Tamper-evident — hash-chained logs detect edits, deletions and forged appends; verify catches a single flipped byte
  • 🧊 Compressed and deduped — identical bytes are stored once, and text evidence is gzipped (any analyst can gunzip one blob without this tool). A real macOS profile: 3.9 GB of sources → 525 MB sealed

Repeat collections add a round, they don't copy everything again

agentdfir run writes to one case per host/user under $AGENTDFIR_HOME (default ~/.agentdfir), so running it from a different directory extends that case instead of producing another multi-gigabyte copy. A second round:

  • carries forward files that are unchanged by size, inode and ctime — never mtime alone, which any writer can set — recorded as carried_forward with the round that actually read them, so carried evidence is never presented as a fresh acquisition
  • stores only the new tail of a transcript that grew, after proving the earlier bytes still hash to what was preserved
  • continues both hash chains from their previous last line (a chain that is already broken is refused, not extended) and archives the seal that closed the previous round
  • proves the earlier rounds first: every round is signed (a per-machine key, or --sign <key>), its digest is recorded in an anchor log outside the case and printed, and the next run checks the signature, the anchor and the sealed files before adding anything. A failure is recorded in the new round for good and the run exits 4
  • re-analyzes only what changed: only new or grown transcripts are parsed, and when neither the evidence nor the parsing and rule code changed, the stored results are reused outright. A new release that doesn't touch parsers or rules doesn't trigger a re-analysis
  • stays out of the way: lowered priority, half the CPUs, a soft memory cap, paced reads, a pause while the machine is busy, and a free-disk floor it won't cross (--priority normal for full speed). One progress bar covers the whole run, with elapsed time and a time remaining learned from this machine's earlier runs

On the same machine as above, a second run re-read 10 files, carried 8,269 forward, and added 157 KB to disk.

On Windows, carry-forward is off by design — there is no change time an unprivileged writer cannot set — so rounds re-read their sources there. That costs read time, not storage: unchanged content dedupes against what the package already holds, and grown transcripts still store only the tail.

Identical blobs are shared between cases by hardlink through agentdfir store status / store gc; each case directory still holds real files, so cp -a, tar and export still produce a self-contained package. --no-share turns it off.

normalized/ and detections/ are the analysis overlay: derived from the sealed evidence, excluded from SHA256SUMS, and safe to delete at any time — agentdfir analyze <pkg> rebuilds them. On a large case they can be a meaningful share of the directory, so removing them is the quickest way to reclaim space without touching evidence.

🛡️ Built for hostile evidence

AI incident evidence may intentionally contain prompt injection and anti-analysis payloads. Therefore:

  • Suspect binaries are never executed — not even for --version
  • Transcript parsers are size-bounded; malformed regions become TRACE_GAP findings, never silent skips
  • ANSI escapes and invisible Unicode (bidi overrides, zero-width, tag smuggling) are neutralized in all evidence-derived output — your terminal is part of the attack surface
  • Model text is data, never instructions

🚀 Deploy with your existing stack

Ships with wrappers for tools IR teams already run:

  • KAPE — deploy/kape/: Target (raw files) + Module (sealed .adfir package)
  • Velociraptor — deploy/velociraptor/: client artifact invoking agentdfir collect
  • Containers, CI, exports — collect --docker <container|export.tar> and collect --archive <zip|tar|tgz> (docs)
  • Triage-tree import — collect --import <tree> turns any KAPE/Velociraptor/CyLR output or mounted image into one sealed package (all products, all users)
  • Timesketch / Plaso — report --format timesketch|l2tcsv puts the agent timeline next to your host timeline (docs)

🗺️ Roadmap

Status Capability
✅ Sealed .adfir packages, hash-chained custody, verify
✅ Claude Code: detect, collect, normalize, timeline, triage
✅ 172 deterministic detections (91 built-in incl. 8 attack chains + 81 pack rules; 117 HIGH/CRITICAL, all but two mapped to MITRE ATLAS 5.6 / ATT&CK — 28 ATLAS and 69 ATT&CK techniques): rogue/orphan agents, exfiltration via tool invocation, context/memory/tool/MCP poisoning, agent credential-store theft, agent config modification, jailbreak & system-prompt extraction, secret & sensitive-file access, persistence (rc files, services, run keys, git hooks), credential dumping, bulk encryption, self-modification, log deletion, timestomping, session tampering… agentdfir rules list prints the matrix
✅ simulate — synthetic incident generation (adversary emulation for AI agents): orphan-agent, toxic-chain, and reproductions of real incidents — keyv-hook, sandworm-mcp, s1ngularity, mcpoison-rugpull, pocketos-wipe, swarm-antiforensics (v3.0)
✅ Attack chains, session cards, investigation tree, whole-case search and the hash-chained analyst case file in the explorer (v1.0)
✅ Full parsers for 13 products: Claude Code, Claude Cowork (desktop-app agent mode: HMAC audit log, in-VM transcripts, shared folders and egress allowlist per session), Codex CLI + Codex desktop app (rollout JSONL and the SQLite thread store, read with a stdlib-only reader that applies the write-ahead log), Gemini CLI, Cursor, Copilot CLI, Copilot Chat (VS Code), Cline, Roo, OpenClaw, OpenCode, Aider, Warp — plus Kiro (steering, specs, MCP, powers, skills and extension state; no transcript store to parse)
✅ Enrich with a second witness — auditd, Sysmon XML, Velociraptor/osquery/eslogger/EDR exports, and AWS CloudTrail / Azure Activity Log / GCP Cloud Audit Log exports for the agent's az / aws / gcloud commands: tool calls → CONFIRMED / DISPROVED, unlogged agent processes and connections surfaced
✅ Reports: network-silent HTML, self-contained PDF (stdlib writer, no renderer deps), JSON, CSV, STIX 2.1, OTel · OCSF 1.3, SARIF 2.1, Sigma export for SIEM/SOC pipelines
✅ serve — local browser case explorer: agent tree, density-scrubber timeline, raw evidence pane, findings, topology; loopback-only, zero external resources
✅ monitor live watch · --detect --alert real-time sensor (webhook / syslog / file) · replay session step-through · investigate explorer
✅ Declarative rule packs (agentdfir-rules) + signed knowledge packs
✅ Package signing (ed25519), full-package encryption (AES-256-GCM)
✅ Injection-surface detections: prompt-injection indicators, invisible-Unicode smuggling, honeytokens
✅ Instruction provenance — per-line attribution of CLAUDE.md / AGENTS.md / rules / settings to the session, agent, tool and trigger (human prompt vs tool output) that wrote it
✅ MCP supply-chain audit — inventory of every MCP server across 9 hosts (JSON/JSONC/TOML), unpinned packages, plaintext transports, auto-approve, tool-description poisoning, baseline drift, gateway-log enrichment
✅ Product packs — add any new AI agent with one signed JSON file (detect + collect + parse), no Go
✅ mitigate — findings become guardrails in the agents' own settings (Claude Code, Codex, Cursor CLI), MCP pins and auto-approve fixes; plan first, backed up, hash-chained ledger, drift check, byte-exact revert; Protect tab in the explorer (v2.7)
✅ export / open — one file moves a case to another computer; the seal is verified and the analysis rebuilt on arrival (v2.7)
✅ hunt — known-incident IOC packs + STIX 2.1 / MISP import, lockfile and on-disk checks, evidence-window verdicts (v3.0)
✅ scan-repo — pre-open repository check, SARIF, GitHub Action, pre-commit hook (v3.0)
✅ Offline decoder, MCP rug-pull fingerprints and typosquat detection, monitor --journal tamper-evident transcripts (v3.0)
🔜 Raw-NTFS/VSS locked-file fallback, EDR/DNS adapters, fleet integrations, npm cache/log parsing for hunt

🤝 Contributing

Adding a new AI agent product = one product pack (JSON: detection + collector manifest + parser binding) plus a synthetic fixture. No core changes needed. See CONTRIBUTING.md.

Zero third-party runtime dependencies in the collector core, by policy — a forensic tool should be auditable in an afternoon.

📄 License

MIT — free forever. Use it, embed it, build on it.

About

Open-source digital forensics and incident response for AI agents (Claude Code, Codex, Cursor, Gemini, Copilot…). One command: detect → collect → analyze → browse.

Topics

Resources

Contributing

Security policy

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages