使用说明
SIFT MCP
Monorepo for all SIFT-side Valhuntir components. 11 packages: forensic-mcp, case-mcp, report-mcp, sift-mcp, sift-gateway, forensic-knowledge, forensic-rag, windows-triage, opencti (the threatintel-mcp backend), sift-common, and case-dashboard. With optional opensearch-mcp for evidence indexing and querying at scale. Part of the Valhuntir platform.
Documentation · Getting Started · CLI Reference · MCP Reference
Important Note — While extensively tested, this is a new platform. ALWAYS verify results and guide the investigative process. If you just tell Valhuntir to "Find Evil" it will more than likely hallucinate rather than provide meaningful results. The AI can accelerate, but the human must guide it and review all decisions.
Valhuntir — AI-Assisted Forensic Investigation
Valhuntir is a forensic investigation platform that connects AI to structured MCP tools, enforces human-in-the-loop review, and maintains a complete audit trail from evidence to finding to report.
The platform is LLM client agnostic — connect any locally installed MCP-compatible client through the gateway. Supported clients include Claude Code and Codex (agents with a shell, set up with Valhuntir's controls), self-hosted LibreChat and Open WebUI (MCP-only, with a locked configuration), and any client that supports Streamable HTTP transport with Bearer token authentication. The client must run on your machine or local network — cloud-hosted services cannot reach internal gateway addresses. Forensic discipline is provided structurally at the gateway and MCP layer, not through client-specific prompt engineering, so the same rigor applies regardless of which AI model or client drives the investigation.
With opensearch-mcp, evidence is parsed programmatically and indexed into OpenSearch, giving the LLM purpose-built query tools instead of consuming billions of tokens reading raw artifacts. A 30-host triage collection with 50 million records becomes instantly searchable. Triage baseline and threat intelligence enrichment run programmatically — zero LLM tokens consumed. OpenSearch integration is optional but recommended for investigations at scale.
Looking for a simpler setup without the gateway or OpenSearch? See Valhuntir Lite.
What You Get
- Gateway with auth + lifecycle management (every SIFT backend behind one endpoint; MCP Tools lists sift-mcp's tools)
- Evidence indexing — 15 parsers (evtx, EZ tools, Volatility, JSON, CSV, W3C, and more) with deterministic dedup and full provenance (via opensearch-mcp)
- Structured querying — case summary, search, aggregation, timeline, field enumeration, detection listing (via opensearch-mcp)
- Programmatic enrichment — triage baseline validation and threat intelligence stamping at index scale, zero LLM tokens (via opensearch-mcp)
- Examiner Portal — 8-tab browser UI for review, approval, and commit (findings, timeline, hosts, accounts, evidence, IOCs, TODOs, overview) with keyboard shortcuts, search, provenance chain display, and signing in the browser (the passphrase never leaves it)
- IOC auto-extraction from findings with approval cascade
- Evidence provenance chain linking findings back to registered evidence through audited tool executions
- RAG search — 22K+ forensic records (Sigma, MITRE ATT&CK, LOLBAS, Atomic Red Team, and more)
- Windows baseline validation — offline file/process/service validation against 2.6M known-good records
- Case management — init, activate, close, backup with SHA-256 manifest and verification
- Structured JSON case files with integrity verification
- Formal report generation (6 profiles) with Zeltser IR Writing guidance
- Audit trail — JSONL logs with SHA-256 hashes for every MCP tool call and Bash command
- Optional add-ons — OpenCTI or MISP threat intelligence, REMnux malware analysis, Microsoft Learn, Zeltser IR Writing
When Claude Code is the client, additional controls are deployed:
- Bubblewrap sandbox — kernel-level filesystem isolation, Bash restricted to project directory
- Permission deny rules — Edit/Write blocked on case data files (findings.json, timeline.json, approvals.jsonl, etc.), and Read blocked on
/var/lib/vhir(the ledgers, signing keys and passphrase files) - PreToolUse case-data guard — refuses Bash commands that would delete or overwrite case records, audit logs or evidence files (a best-effort check of listed commands, not a security boundary)
- Signed decisions — each examiner's own Ed25519 key, unlocked by their passphrase, signs every approval and rejection into the case's ledger, which
vhir review --verifychecks - Provenance enforcement — rejects findings that lack an evidence trail in the audit log
- PostToolUse audit hook — while a case is active, each Bash command logged to the case's JSONL audit trail with SHA-256 hashes (with no active case, nothing is logged)
- Prompt hook — forensic discipline reminders injected on every prompt
Examiners review findings in the Examiner Portal — validating artifacts, observations, and interpretations, with the full command audit trail from original evidence to final result.
The timeline view places findings and other observables in chronological context across the investigation.
Investigation Workflow
The recommended workflow uses OpenSearch for evidence indexing, enabling structured queries across millions of records. Without OpenSearch, the same investigation tools are available through direct file-based analysis via run_command — OpenSearch adds scale, not capability.
1. case_init("Ransomware Investigation") → Create case, set examiner
2. evidence_register(path, description) → SHA-256 hash, chain of custody
3. idx_ingest(case_dir, hostname) → Parse + index into OpenSearch
4. idx_case_summary(case_id) → Hosts, artifacts, fields, time range
5. idx_search / idx_aggregate / idx_timeline → Structured queries (~500 tokens each)
6. idx_enrich_triage + idx_enrich_intel → Programmatic enrichment (zero tokens)
7. record_finding / record_timeline_event → Stage as DRAFT with provenance
8. Examiner Portal or vhir approve → Human review → APPROVED/REJECTED
9. generate_report(profile="full") → IR report from approved findings
Without OpenSearch, steps 3-6 are replaced by direct tool execution (run_command) and manual analysis. The investigation workflow, findings, timeline, and reporting are identical either way.
Architecture
Each MCP backend runs as a stdio subprocess of the sift-gateway, aggregated behind a single HTTP endpoint. opensearch-mcp connects to a local or remote OpenSearch instance for evidence indexing and querying. The Examiner Portal is served by the gateway for browser-based review and approval. See the Valhuntir README for the full deployment topology including REMnux and Windows VMs.
graph LR
GW["sift-gateway :4508"]
FM["forensic-mcp<br/>findings, timeline,<br/>evidence, discipline"]
CM["case-mcp<br/>case management,<br/>audit queries, backup"]
RM["report-mcp<br/>report generation,<br/>IOC aggregation"]
SM["sift-mcp<br/>Linux forensic<br/>tool execution"]
RAG["forensic-rag<br/>semantic search<br/>22K records"]
WT["windows-triage<br/>offline baseline<br/>validation"]
OC["threatintel-mcp<br/>threat<br/>intelligence"]
OS["opensearch-mcp<br/>evidence indexing,<br/>query, enrichment"]
CD["Examiner Portal<br/>browser review + commit"]
FK["forensic-knowledge<br/>shared YAML data"]
CASE["Case Directory"]
OSD["OpenSearch<br/>Docker :9200"]
GW -->|stdio| FM
GW -->|stdio| CM
GW -->|stdio| RM
GW -->|stdio| SM
GW -->|stdio| RAG
GW -->|stdio| WT
GW -->|stdio| OC
GW -->|stdio| OS
GW --> CD
FM --> FK
SM --> FK
FM --> CASE
CM --> CASE
RM --> CASE
CD --> CASE
OS --> OSD
The gateway exposes each backend as a separate MCP endpoint. Clients can connect to the aggregate endpoint or to individual backends:
http://localhost:4508/mcp # Aggregate (all tools)
http://localhost:4508/mcp/forensic-mcp
http://localhost:4508/mcp/case-mcp
http://localhost:4508/mcp/report-mcp
http://localhost:4508/mcp/sift-mcp
http://localhost:4508/mcp/windows-triage-mcp
http://localhost:4508/mcp/forensic-rag-mcp
http://localhost:4508/mcp/threatintel-mcp (also /mcp/opencti-mcp)
http://localhost:4508/mcp/opensearch-mcp
When the LLM client runs on a different machine, install with --remote to generate TLS certificates and a bearer token. The gateway binds to all interfaces and requires Authorization: Bearer <token> on every request.
Deployment Configurations
All configurations run on a SIFT Workstation (Ubuntu-based) with Python 3.10+.
| Configuration | What runs on SIFT | RAM (min) | RAM (recommended) | Best for |
|---|---|---|---|---|
| Valhuntir | Gateway + backends + OpenSearch (Docker) | 24 GB | 32 GB | Solo analyst, lab environments |
| Valhuntir (remote OpenSearch) | Gateway + backends; OpenSearch on separate host | 16 GB SIFT, 8 GB OS host | 16 GB SIFT, 16 GB OS host | Larger cases, persistent clusters |
| Valhuntir + Windows | Above + wintools-mcp on Windows VM | +8 GB Windows | +8 GB Windows | Full artifact coverage |
| Valhuntir + REMnux | Above + remnux-mcp on REMnux VM | +4 GB REMnux | +8 GB REMnux | Malware analysis |
| Valhuntir Lite | No gateway, no OpenSearch — stdio MCPs only | 8 GB | 16 GB | Quick setup, smaller investigations |
Where the RAM goes (all-in-one Valhuntir):
- OpenSearch Docker: 4-12 GB heap (default 4 GB, increase for larger cases)
- Gateway + MCP backends: ~2-3 GB (Python processes)
- RAG embedding model: ~2 GB (when forensic-rag is loaded)
- Evidence parsing during ingest: 1-4 GB (spikes during large ingests)
- OS + Docker overhead: ~2 GB
Disk space: ~14 GB for RAG + triage databases, plus evidence and OpenSearch indices.
Valhuntir Installation
Requires Python 3.10+ and sudo access. The installer handles everything: MCP servers, gateway, vhir CLI, the ledger and signing-key folders, examiner identity, and LLM client configuration. When you select Claude Code, the forensic controls listed above are deployed automatically.
Quick — Core platform only, no databases (~70 MB):
curl -fsSL https://raw.githubusercontent.com/AppliedIR/sift-mcp/main/quickstart.sh -o /tmp/vhir-quickstart.sh && bash /tmp/vhir-quickstart.sh
Recommended — Adds the RAG knowledge base (22,000+ records from 23 authoritative sources) and Windows triage databases (2.6M baseline records). Requires ~14 GB disk space:
- ~7 GB — ML dependencies (PyTorch, CUDA) required by the RAG embedding model
- ~6 GB — Windows triage baseline databases (2.6M rows, decompressed)
- ~1 GB — RAG index, source code, and everything else
curl -fsSL https://raw.githubusercontent.com/AppliedIR/sift-mcp/main/quickstart.sh -o /tmp/vhir-quickstart.sh && bash /tmp/vhir-quickstart.sh --recommended
Custom — Individual package selection, OpenSearch integration, OpenCTI, or remote access with TLS:
git clone https://github.com/AppliedIR/sift-mcp.git && cd sift-mcp
./setup-sift.sh
Adding OpenSearch — Add --opensearch to any install command to include evidence indexing. The installer clones the repo, installs the package, and sets up the OpenSearch Docker container automatically. Requires Docker.
bash /tmp/vhir-quickstart.sh --recommended --opensearch
If opensearch-mcp is already cloned alongside sift-mcp, the installer detects and installs it automatically — no flag needed.
Dependency versions — The installers and vhir update install every third-party package at the version and hash listed in deps/vhir.lock, and require uv 0.6.0 or newer. An install from an earlier release may still have OpenCTI's old client, pycti, which nothing uses any more: the installers and vhir update leave it in place and print the command that removes it.
Behind a package mirror (PIP_INDEX_URL or UV_INDEX_URL) that serves files different from PyPI's, the lock's hashes don't match and the install stops with a hash error. Use a mirror that serves PyPI's original files, or regenerate the lock against your mirror with deps/regen-lock.sh.
Valhuntir Lite
In its simplest form, Valhuntir Lite provides Claude Code with forensic knowledge and instructions on how to enforce forensic rigor, present findings for human review, and audit actions taken. MCP servers enhance accuracy by providing authoritative information — a forensic knowledge RAG and a Windows triage database — plus optional OpenCTI threat intelligence and REMnux malware analysis.
Quick — Forensic discipline, MCP packages, and config. No databases (<70 MB):
git clone https://github.com/AppliedIR/sift-mcp.git
cd sift-mcp
./quickstart-lite.sh --quick
Recommended — Adds the RAG knowledge base (22,000+ records from 23 authoritative sources) and Windows triage databases (2.6M baseline records). Requires ~14 GB disk space:
- ~7 GB — ML dependencies (PyTorch, CUDA) required by the RAG embedding model
- ~6 GB — Windows triage baseline databases (2.6M rows, decompressed)
- ~1 GB — RAG index, source code, and everything else
git clone https://github.com/AppliedIR/sift-mcp.git
cd sift-mcp
./quickstart-lite.sh
This one-time setup takes approximately 15-30 minutes depending on internet speed and CPU. Subsequent runs reuse existing databases and index.
claude
/welcome
What You Get
- Forensic discipline — CLAUDE.md + FORENSIC_DISCIPLINE.md + reference docs
- Prompt reinforcement — forensic rules injected on every prompt
- Audit trail — JSONL logs with SHA-256 hashes for every MCP query, and for each Bash command run while a case is active
- RAG search — 22K+ forensic records (Sigma, MITRE ATT&CK, LOLBAS, Atomic Red Team, and more)
- Windows baseline validation — offline file/process validation against known_good.db
- Case management —
/case init,/case open,/case status,/case list,/case close - Post-install verification —
/welcomevalidates setup and orients you - Optional add-ons — OpenCTI, REMnux, Microsoft Learn, Zeltser IR Writing
No gateway, no sandbox, no deny rules. Claude runs forensic tools directly via Bash. Forensic discipline is suggested and reinforced via prompt hooks and reference documents, but Claude Code can choose to ignore them.
Optional Add-ons
./quickstart-lite.sh --opencti # Live threat intelligence
./quickstart-lite.sh --remnux=HOST:PORT # Automated malware analysis
./quickstart-lite.sh --mslearn # Microsoft documentation search
./quickstart-lite.sh --zeltser # IR writing guidelines
Upgrading from Lite to Valhuntir
Both modes share the same knowledge base, MCPs, and audit format. Upgrading adds the gateway, sandbox, enforcement layer, structured case management, and optionally OpenSearch for evidence indexing at scale. Note: Lite case data (markdown files) does not auto-migrate to Valhuntir case data (structured JSON). Start fresh or transfer findings manually.
Execution Pipeline
Every tool call follows the same pipeline: denylist check, safe execution, output parsing, knowledge enrichment (for cataloged tools), audit logging.
graph LR
REQ["MCP tool call"] --> DENY{"Denylist<br/>Check"}
DENY -->|"denied"| REJECT["Rejected"]
DENY -->|"allowed"| EXEC["subprocess.run()<br/>shell=False"]
EXEC --> PARSE["Parse Output"]
PARSE --> CAT{"In Catalog?"}
CAT -->|"yes"| ENRICH["FK Enrichment"]
CAT -->|"no"| BASIC["Basic Envelope"]
ENRICH --> RESP["Response Envelope"]
BASIC --> RESP
RESP --> AUDIT["Audit Entry"]
MCP Tools
sift-mcp's core tools: discovery tools plus one generic execution tool.
Discovery
| Tool | Description |
|---|---|
list_available_tools |
List cataloged tools (enriched) with availability status — uncataloged tools can also execute |
get_tool_help |
Usage info, flags, caveats, and FK knowledge for a tool |
check_tools |
Check which tools are installed and available |
suggest_tools |
Given an artifact type, suggest relevant tools with corroboration guidance |
Generic Execution
| Tool | Description |
|---|---|
run_command |
Execute any forensic tool (denied binaries are blocked) |
All 30+ per-tool wrappers (Zimmerman suite, Sleuth Kit, Volatility, etc.) are consolidated into run_command. A small denylist blocks system-destructive binaries. Tools listed in the catalog get enriched responses with forensic-knowledge data. Uncataloged tools execute with basic response envelopes.
What Can You Ask?
"Ingest all evidence from /cases/evidence/ into OpenSearch and give me a summary of the artifacts ingested"
"Show me all 4688 events where cmd.exe spawned from an unusual parent process"
"Aggregate the top 20 source IPs across all hosts and check them against threat intel"
"Run triage enrichment and show me anything flagged as suspicious"
"Parse the Amcache hive from workstation3"
"See if this registry value exists on any of the other hosts in OpenSearch"
"What tools should I use to investigate lateral movement artifacts?"
"Run hayabusa against the evtx logs and show critical/high alerts"
"Extract the $MFT and build a filesystem timeline"
"Analyze this memory dump with Volatility -- list processes and network connections"
"Check if svchost.exe with parent wsmprovhost.exe is normal"
"Look up this hash in threat intel"
"Upload this binary to REMnux and analyze it"
Response Envelope
Every tool response is wrapped in a structured envelope enriched by forensic-knowledge (in packages/forensic-knowledge/). This ensures the LLM always receives artifact caveats, corroboration suggestions, and discipline reminders alongside tool output.
{
"success": true,
"tool": "run_command",
"data": {"output": {"rows": ["..."], "total_rows": 42}},
"data_provenance": "tool_output_may_contain_untrusted_evidence",
"audit_id": "sift-steve-20260220-01khxqe4jr7wn9rmegfd2ed8e8",
"examiner": "steve",
"caveats": [
"Amcache entries indicate file presence, not execution"
],
"advisories": [
"This artifact does NOT prove: Program was executed by the user",
"Amcache proves installation -- Prefetch is needed to confirm execution"
],
"corroboration": {
"for_execution": ["Prefetch", "UserAssist"],
"for_timeline": ["$MFT timestamps", "USN Journal"]
},
"discipline_reminder": "Evidence is sovereign -- if results conflict with your hypothesis, revise the hypothesis, never reinterpret evidence to fit"
}
| Field | Description |
|---|---|
audit_id |
Unique ID for referencing in findings (sift-{examiner}-YYYYMMDD-NNN) |
caveats |
Tool-specific limitations from FK |
advisories |
What the artifact does NOT prove, common misinterpretations |
corroboration |
Suggested cross-references grouped by purpose |
field_notes |
Timestamp field meanings and interpretation guidance |
discipline_reminder |
Rotating forensic methodology reminder |
Execution Security
A denylist blocks destructive system commands (mkfs, dd, fdisk, shutdown, etc.). When Claude Code is the LLM client, additional deny rules block Edit/Write to case data files (findings.json, timeline.json, approvals.jsonl, etc.), a PreToolUse hook refuses Bash commands that would delete or overwrite case records, audit/ or evidence files (rm, mv, cp, truncate, shred, redirections and find -delete, through sudo/env/chains/cd; not bash -c, xargs, scripts, tee, in-place editors (sed -i, perl -i), >&, variables, backquotes, a quoted "$(...)", an operand after a $(...) inside an argument, anything after a # comment (on later lines too), timeout -s/env -u options, an option value placed after the operands of cp, install or ln (--suffix .bak, -m 0644), a single-operand ln (the link is created in the current folder), or a cd in a subshell or one that fails, and a quoted '>' is read as a redirection and a quoted 'x$' before \( can be misread: a best-effort check, not a security boundary), and findings.json and timeline.json are set to chmod 444 after every write. All other binaries can execute. This follows the REMnux MCP philosophy: VM/container isolation is the security boundary, not in-band command filtering.
Additional protections:
subprocess.run(shell=False)— no shell, no arbitrary command chains- Argument sanitization — shell metacharacters blocked
- Path validation — input paths under /proc, /sys, /dev, /etc, /boot, ~/.vhir, ~/.ssh, ~/.claude, ~/.claude.json (and its
.vhir-backup-*copies), ~/.agents, ~/.codex, ~/.config/opencti-mcp (the threat-intel token) and /var/lib/vhir are refused, including bare names in the working folder (so a search pattern that names one, such asgrep .ssh log.txtrun from the home folder, is refused too). Defence in depth only: text insidepython -corbash -c, recursive reads of a parent folder (tar ~), and a symlinked home folder or~/.claudearen't checked rmprotection — case directories protected from deletion- Case-data protection — commands that would delete or overwrite case records,
audit/or evidence files are refused (rm, mv, cp, install, ln, tee, truncate, shred, unlink, also under sudo, nice, nohup, time, timeout and stdbuf);bash -c,python -c,dd, a tool's own output options, a wrapper option that takes a value (timeout -s KILL,stdbuf -o 0,sudo --user), an attached or combined-t(-t<dir>,-ft <dir>), an option value placed after the operands of cp, install or ln (--suffix .bak,-m 0644), a single-operandln(the link is created in the current folder) and an evidence file that is a symlink to a file outside the cases dir are not checked - Output truncation — large output capped
- Audit trail — every execution logged with audit ID
Forensic Catalog (Enrichment)
Tools listed in YAML catalog files get enriched responses with forensic-knowledge data (caveats, corroboration suggestions, field meanings, discipline reminders). Uncataloged tools execute with basic response envelopes (audit_id, audit, discipline reminder).
| File | Tools |
|---|---|
zimmerman.yaml |
AmcacheParser, PECmd, AppCompatCacheParser, RECmd, MFTECmd, EvtxECmd, JLECmd, LECmd, SBECmd, RBCmd, SrumECmd, SQLECmd, bstrings |
volatility.yaml |
vol3 |
timeline.yaml |
hayabusa, log2timeline, mactime, psort |
sleuthkit.yaml |
fls, icat, mmls, blkls |
malware.yaml |
yara, strings, ssdeep, binwalk |
analysis.yaml |
grep, awk, sed, cut, sort, uniq, wc, head, tail, tr, diff, jq, zcat, zgrep, tar, unzip, file, stat, find, ls, md5sum, sha1sum, sha256sum, xxd, hexdump, readelf, objdump |
network.yaml |
tshark, zeek |
file_analysis.yaml |
bulk_extractor |
misc.yaml |
exiftool, regripper, hashdeep, 7z, dc3dd, ewfacquire, ewfmount, vshadowinfo, vshadowmount |
Some analysis tools have flag restrictions enforced by security.py: find blocks -exec/-execdir/-delete; sed blocks -i/--in-place; tar blocks extraction/creation and code execution flags (listing only); unzip blocks overwrite modes; awk program text is scanned for system(), getline, pipe operators, and output redirection.
Prerequisites
- SIFT Workstation (Ubuntu-based) — for Valhuntir
- Any Linux/macOS machine — for Valhuntir Lite
- Python 3.10+
- Docker — for OpenSearch (Valhuntir with evidence indexing)
- sudo access (required for Valhuntir's ledger and signing-key folders under
/var/lib/vhir/) - Forensic tools installed via SIFT package or manually
External Dependencies
- opensearch-mcp (https://github.com/AppliedIR/opensearch-mcp) — Evidence indexing and querying. Optional but recommended. Detected automatically by
setup-sift.shwhen cloned alongside sift-mcp. - Zeltser IR Writing MCP (https://website-mcp.zeltser.com/mcp) — Required for report generation (Valhuntir). The
vhir setup clientwizard configures this automatically. HTTPS, no authentication required. - MS Learn MCP (https://learn.microsoft.com/api/mcp) — Optional. Provides Microsoft documentation search.
Configuration
| Variable | Default | Description |
|---|---|---|
SIFT_TIMEOUT |
600 |
Default command timeout in seconds |
SIFT_TOOL_PATHS |
(none) | Extra binary search paths (colon-separated) |
SIFT_HAYABUSA_DIR |
/opt/hayabusa |
Hayabusa install location |
VHIR_CASE_DIR |
(none) | Active case directory — enables audit trail. Falls back to ~/.vhir/active_case if unset. |
VHIR_CASES_DIR |
(none) | Root directory containing all cases |
VHIR_EXAMINER |
(none) | Examiner identity for evidence IDs and audit |
OPENSEARCH_CONFIG |
(none) | Path to OpenSearch connection config (e.g. ~/.vhir/opensearch.yaml) |
Remote Access (TLS + Auth)
When installed with --remote, setup-sift.sh generates a local CA and gateway certificate at ~/.vhir/tls/. The gateway binds to 0.0.0.0:4508 with TLS enabled. A bearer token (vhir_gw_ prefix) is generated and written to gateway.yaml.
Remote clients join via platform-specific setup scripts. The installer prints per-OS commands with a join code. See the Deployment Guide for details.
Without --remote, the gateway listens on 127.0.0.1 only. Auth tokens are still generated but optional for localhost.
Security Considerations
All Valhuntir components are assumed to run on a private forensic network, protected by firewalls, and not exposed to incoming connections from the Internet or potentially hostile systems. The design assumes dedicated, isolated systems are used throughout.
Any data loaded into the system or its component VMs, computers, or instances runs the risk of being exposed to the underlying AI. Only place data on these systems that you are willing to send to your AI provider.
Outgoing Internet connections are required for report generation (Zeltser IR Writing MCP) and optionally used for threat intelligence (OpenCTI, MISP) and documentation (MS Learn MCP). No incoming connections from external systems should be allowed.
Valhuntir is designed so that AI interactions flow through MCP tools, enabling security controls and audit trails. Clients with direct shell access (like Claude Code) can also operate outside MCP, but vhir setup client deploys forensic controls for Claude Code: a kernel-level sandbox restricts Bash writes, deny rules block Edit/Write to case data files, a PreToolUse hook refuses Bash commands that would delete or overwrite case records or evidence (a best-effort check of listed commands), a PostToolUse hook records each Bash command to the audit trail while a case is active, provenance enforcement ensures findings are traceable to evidence, and every examiner decision is signed into the case's ledger, which vhir review --verify checks (what that does and doesn't show is in the security model's stated limits). Claude Code's deny rules and sandbox limit that client's own tools, not other programs running as the examiner's user. Valhuntir is not designed to defend against a malicious AI or to constrain the AI client that you deploy.
Audit Trail and Provenance
Every MCP tool call is logged to a per-backend JSONL file in the case audit/ directory with a unique evidence ID ({backend}-{examiner}-{date}-{ulid}). When Claude Code is the client, a PostToolUse hook additionally records each Bash command run while a case is active to audit/claude-code.jsonl.
Reference lookups that have no audit entry (forensic-mcp's discipline and playbook lookups, get_procedure, sift-mcp's list_available_tools and check_tools, and, under Claude Code, each skill load) are logged one line each to ~/.vhir/logs/lookups.jsonl: the time, the server, the tool, the active case's name and the tool's short arguments. It never holds tool output or evidence. It's outside every case, so it's never part of an export, a bundle or a report. A skill loaded by Codex isn't logged there, because Codex has no skill hook.
Evidence Artifacts
Findings should include an artifacts list showing the actual evidence — source file, tool command, and raw output. The audit_id from the tool response ties each artifact to a specific audit trail entry, and the source file must be registered in the evidence registry. Findings without artifacts (analytical conclusions, exclusions) can use supporting_commands instead.
Provenance
When a finding is staged, record_finding() classifies its provenance by scanning the audit trail for each referenced audit_id:
| Tier | Where the audit_id was found | Trust Level |
|---|---|---|
| MCP | MCP backend audit log | System-witnessed (highest) |
| HOOK | Claude Code hook log (claude-code.jsonl) |
Framework-witnessed |
| SHELL | Not in audit trail — provided via supporting_commands |
Self-reported |
| NONE | Not found anywhere | Rejected by hard gate |
The tier is a gate, not stored on the finding: findings with NONE provenance and no supporting commands are rejected. Each evidence artifact is also traced to registered evidence through the links the case recorded (a registered path or folder manifest, a run's recorded inputs and outputs by path and hash, a recorded mount) and graded FULL, DECLARED (the AI said so), AMBIGUOUS (more than one recorded origin, each named), BROKEN (with where and why) or CITED-UNVERIFIED (the quote couldn't be verified against the cited operation's recorded output); the finding's grade is its worst artifact's, and an item merged from another case is IMPORTED. The Evidence Provenance Chain in the Examiner Portal, vhir review and the report show each chain from finding back to registered evidence.
Content Integrity
Content hashes (SHA-256) are computed at staging and checked at review, so a decision staged in the portal isn't signed over content that changed since. After approval, vhir review --findings --verify checks each approved item against its signed line; approvals.jsonl is an unsigned activity log, not verification.
Report Generation
Report generation uses the report-mcp package with data-driven profiles:
| Profile | Purpose |
|---|---|
full |
Comprehensive IR report with all approved data |
executive |
Management briefing (1-2 pages, non-technical) |
timeline |
Chronological event narrative |
ioc |
Structured IOC export with MITRE mapping |
findings |
Detailed approved findings |
status |
Quick status for standups |
generate_report() produces structured JSON with case data, IOC aggregation, MITRE ATT&CK mapping, and Zeltser IR Writing guidance. The LLM renders narrative sections using Zeltser's IR templates. Reports only include APPROVED findings — provenance, confidence, and other internal working notes are stripped.
Evidence Handling
Never place original evidence on any Valhuntir system. Only use working copies for which verified originals or backups exist. Valhuntir workstations process evidence through AI-connected tools, and any data loaded into these systems may be transmitted to the configured AI provider. Treat all Valhuntir systems as analysis environments, not evidence storage.
Evidence integrity is verified by SHA-256 hashes recorded at registration. Examiners can optionally lock evidence to read-only via vhir evidence lock. Proper evidence integrity depends on verified hashes, write blockers, and chain-of-custody procedures that exist outside this platform.
Case directories can reside on external or removable media. ext4 is preferred for full permission support. NTFS and exFAT are acceptable but file permission controls (read-only protection) will be silently ineffective. FAT32 is discouraged due to the 4 GB file size limit.
Responsible Use and Legal
While steps have been taken to enforce human-in-the-loop controls, it is ultimately the responsibility of each examiner to ensure that their findings are accurate and complete. The AI, like a hex editor, is a tool to be used by properly trained incident response professionals. Users are responsible for ensuring their use complies with applicable laws, regulations, and organizational policies. Use only on systems and data you are authorized to analyze.
This software is provided "as is" without warranty of any kind. See LICENSE for full terms.
MITRE ATT&CK is a registered trademark of The MITRE Corporation. SIFT Workstation is a product of the SANS Institute.
Acknowledgments
Architecture and direction by Steve Anson. Implementation by Claude Code (Anthropic). Code review by Codex (OpenAI). Design inspiration drawn from Lenny Zeltser's REMnux MCP.
Clear Disclosure
I do DFIR. I am not a developer. This project would not exist without Claude Code handling the implementation. While an immense amount of effort has gone into design, testing, and review, I fully acknowledge that I may have been working hard and not smart in places. My intent is to jumpstart discussion around ways this technology can be leveraged for efficiency in incident response while ensuring that the ultimate responsibility for accuracy remains with the human examiner.
License
MIT License - see LICENSE