CLI Reference¶
The vhir CLI handles all human-only operations: case management, approval, reporting, evidence handling, and configuration. It is not callable by the AI.
Global Options¶
| Option | Description |
|---|---|
--version |
Show version and exit |
--case PATH |
Override active case directory (most commands) |
Case Management¶
vhir case init¶
Initialize a new case. Run with no arguments for interactive mode.
vhir case init # Interactive — prompts for name, ID, directory
vhir case init "Ransomware Investigation" # Create with auto-generated ID
vhir case init "Investigation" --case-id INC-2026-001 # Create with custom case ID
vhir case init "Phishing Campaign" --description "CEO spearphish, Feb 2026"
| Argument/Option | Description |
|---|---|
name |
Case name (optional — interactive prompts if omitted on TTY) |
--case-id |
Override auto-generated case ID |
--description |
Case description |
vhir case activate¶
Set the active case for the session.
vhir case list¶
List all available cases.
vhir case status¶
Show active case summary.
vhir case close¶
Close a case.
vhir case reopen¶
Reopen a closed case.
vhir case migrate¶
Migrate a case from the legacy examiners/ directory structure to the current flat layout.
vhir case new-identity¶
Give a copied case (a cp -r of another case) its own identity.
| Argument/Option | Description |
|---|---|
case_id |
Case ID of the copy |
--cases-dir |
Cases root directory (default: $VHIR_CASES_DIR or ~/cases) |
vhir case prune-ingest-manifests¶
Remove ingest manifests from the case's evidence registry. Older ingests registered each ingest manifest as evidence; they belong in the case's audit folder. Running it again on a clean case changes nothing.
Examiner Portal¶
vhir portal¶
Open the Examiner Portal in the default browser.
The portal is the primary review interface — examiners can review, edit, approve, reject, and commit findings entirely in the browser. Use the Commit button (Shift+C) to apply decisions with challenge-response authentication. Alternatively, vhir approve --review applies pending edits from the CLI.
vhir dashboard¶
Retired: opens the Examiner Portal. Use vhir portal.
Review¶
vhir review¶
Display case information, findings, timeline, evidence, and audit logs.
vhir review # Case summary
vhir review --findings # Findings table
vhir review --findings --detail # Full finding details
vhir review --findings --status DRAFT # Filter by status
vhir review --timeline # Timeline events
vhir review --timeline --type lateral # Filter by event type
vhir review --timeline --start 2026-02-20T00:00 --end 2026-02-22T23:59
vhir review --todos # All TODOs
vhir review --todos --open # Open TODOs only
vhir review --audit # Audit trail
vhir review --evidence # Evidence integrity
vhir review --iocs # IOCs from findings
vhir review --findings --verify # Verify every signed decision (no passphrase)
vhir review --findings --verify --expect-key alice=SHA256:… --expect-head 41:… # Anchored
vhir review --findings --verify --mine # Show only your items
| Option | Description |
|---|---|
--findings |
Show findings summary table |
--detail |
Show full detail (with --findings or --timeline) |
--timeline |
Show timeline events |
--todos |
Show TODO items |
--open |
Show only open TODOs (with --todos) |
--audit |
Show audit log |
--evidence |
Show evidence integrity |
--iocs |
Extract IOCs from findings grouped by status |
--verify |
Verify every signed decision against the case (with --findings). No passphrase or terminal needed. Exit 0: every approved item verifies (or was approved before 0.6.4), with no failure; 2: a failure; 5: a usage error. Prints every signing key with its full fingerprint and each chain's head, and "anchored" or "Not anchored" |
--expect-key |
NAME=SHA256:<fingerprint>[:FROM-TO]: a fingerprint you kept off this machine (repeatable) |
--expect-head |
[CHAIN@]SEQ:HASH: a head you kept off this machine (repeatable) |
--ledger |
Verify against this chain copy instead of the box's ledger and the case's copies (repeatable) |
--mine |
With --verify, show only your items (the exit code still covers the case) |
--status |
Filter by status: DRAFT, APPROVED, REJECTED |
--start |
Start date filter (ISO format) |
--end |
End date filter (ISO format) |
--type |
Filter by event type (with --timeline) |
--limit N |
Limit entries shown (default: 50) |
Approval¶
vhir approve¶
Approve staged findings and/or timeline events. Every item, and each item decided with it ("cascade from F-…"), is shown first; then your passphrase signs exactly those decisions. A 0.6 passphrase is upgraded to a signing key at the first signed decision.
vhir approve # Interactive review
vhir approve F-alice-001 F-alice-002 # Approve specific findings
vhir approve F-alice-003 --note "Confirmed" # With examiner note
vhir approve F-alice-004 --edit # Edit in $EDITOR first
vhir approve --findings-only # Review only findings
vhir approve --timeline-only # Review only timeline
vhir approve --by bob # Review items by examiner
vhir approve --review # Sign reviews staged in the portal
vhir approve --reattest F-alice-005 # Sign a fresh approval of an item approved before 0.6.4
vhir approve F-alice-006 --show-signed # Also print the full signed view
| Option | Description |
|---|---|
ids |
Finding/event IDs to approve (omit for interactive) |
--note |
Add examiner note |
--edit |
Open in $EDITOR before approving |
--interpretation |
Override interpretation field |
--by |
Filter items by creator examiner |
--findings-only |
Review only findings |
--timeline-only |
Review only timeline events |
--review |
Sign the reviews staged in the Examiner Portal (pending-reviews.json). Your own entries are selected; another examiner's are shown and included only if you say so |
--reattest |
Sign a fresh approval of APPROVED items with no signed line (approved before 0.6.4), dated now; the original record is unchanged |
--show-signed |
Also print each item's full signed view |
vhir reject¶
Reject findings or timeline events (an APPROVED one too). A rejection is signed like an approval.
vhir reject F-alice-004 --reason "Insufficient evidence"
vhir reject T-alice-007 --reason "Timestamp unreliable"
| Option | Description |
|---|---|
ids |
Finding/event IDs to reject (required) |
--reason |
Reason for rejection |
--review |
Walk through DRAFT items interactively |
Evidence¶
vhir evidence register¶
Register an evidence file (computes and records SHA-256 hash).
vhir evidence list¶
List registered evidence files with hashes.
vhir evidence verify¶
Re-hash registered evidence files and report any modifications.
Exits 0 only when every registered file verifies: 2 when any file was modified, 1 when any is missing or can't be read.
vhir evidence log¶
Show evidence access log.
vhir evidence lock / vhir evidence unlock¶
Set evidence directory to read-only (bind mount) or restore write access.
Legacy aliases: vhir lock-evidence, vhir unlock-evidence, vhir register-evidence.
Evidence Indexing (OpenSearch)¶
These commands come with opensearch-mcp and appear only when it is installed. Every ingest waits until its hosts are confirmed in the Examiner Portal's Hosts tab (vhir portal); until then the CLI prints "Waiting for host confirmation (N host name(s)): …" and the host names it waits on.
vhir ingest¶
Ingest evidence into OpenSearch: an evidence directory or archive.
vhir ingest /cases/evidence/triage.zip
vhir ingest /cases/evidence/C --hostname WKSTN01 # A flat directory needs --hostname
| Argument/Option | Description |
|---|---|
path |
Path to evidence directory or archive |
--hostname |
Override hostname (required for flat directories) |
--case |
Case ID (reads ~/.vhir/active_case if omitted) |
--password |
Archive password |
--from / --to |
Start / end date (ISO) |
--all-logs |
Parse all evtx files |
--reduced-ids |
Filter to high-value Event IDs |
--source-timezone |
Evidence system's local timezone |
--include / --exclude |
Artifact types (comma-separated) |
--full |
Include all tiers |
--config |
YAML config file |
--vss |
Include volume shadow copies |
--yes |
Skip confirmation |
--skip-triage |
Skip post-ingest triage baseline enrichment |
--no-hayabusa |
Skip Hayabusa detection after evtx ingest |
vhir ingest-memory¶
Parse a memory image with Volatility 3 and index the results.
| Argument/Option | Description |
|---|---|
path |
Path to memory image |
--hostname |
Source hostname, if known (else the examiner names it in the Hosts tab) |
--case |
Case ID |
--tier |
Analysis depth: 1 (fast, the default), 2 or 3 (deep) |
--plugins |
Specific plugins (comma-separated) |
--timeout |
Per-plugin timeout in seconds |
--yes |
Skip confirmation |
vhir ingest-json¶
Index a JSON or JSONL file, or a directory of them.
Options: path (JSON/JSONL file or directory), --hostname (required), --index-suffix, --time-field, --case, --from, --to, --batch-size, --dry-run.
vhir ingest-delimited¶
Index a CSV, TSV, Zeek or bodyfile file, or a directory of them.
vhir ingest-delimited /cases/evidence/conn.log --hostname SENSOR01 --format zeek
vhir ingest-delimited /cases/evidence/hosts/ --recursive # Each subdirectory is a host
Options: path (delimited file or directory), --hostname, --recursive (treat subdirectories as hosts), --index-suffix, --time-field, --delimiter, --format {csv,tsv,zeek,bodyfile}, --case, --from, --to, --batch-size, --dry-run.
vhir ingest-accesslog¶
Index Apache or Nginx access logs: a file or a directory of them.
Options: path (access log file or directory), --hostname (required), --index-suffix, --case, --from, --to, --dry-run.
vhir enrich-intel¶
Enrich the case's indexed data with OpenCTI threat intel.
| Option | Description |
|---|---|
--case |
Case ID (default: active case) |
--dry-run |
Extract IOCs and show counts without lookup |
--force |
Re-enrich even if already enriched |
Reporting¶
vhir report¶
Generate case reports from approved data.
vhir report --full --save full-report.json
vhir report --executive-summary
vhir report --timeline --from 2026-02-20 --to 2026-02-22
vhir report --ioc
vhir report --status-brief
vhir report --findings F-alice-001,F-alice-002
| Option | Description |
|---|---|
--full |
Full case report (JSON) |
--executive-summary |
Executive summary |
--timeline |
Timeline report |
--ioc |
IOC report from approved findings |
--findings IDS |
Specific finding IDs (comma-separated) |
--status-brief |
Quick status counts |
--from |
Start date filter (ISO) |
--to |
End date filter (ISO) |
--save FILE |
Save output to file (relative paths use case_dir/reports/) |
TODOs¶
vhir todo add¶
Add a TODO item.
vhir todo add "Analyze USB device history" --priority high --finding F-alice-002
vhir todo add "Cross-reference DNS logs" --assignee bob
vhir todo complete¶
Mark a TODO as completed.
vhir todo update¶
Update a TODO.
vhir todo update TODO-alice-001 --note "Partial analysis done, needs USB timeline"
vhir todo update TODO-alice-001 --priority high
vhir todo update TODO-alice-001 --assignee carol
Audit¶
vhir audit log¶
Show audit trail entries.
vhir audit log
vhir audit log --limit 20
vhir audit log --mcp forensic-mcp
vhir audit log --tool run_command
vhir audit summary¶
Show audit summary with counts per MCP and tool.
Collaboration¶
vhir export¶
Export findings and timeline as JSON for sharing.
vhir merge¶
Merge incoming JSON into local findings and timeline.
Backup¶
vhir backup¶
Back up case data for archival, legal preservation, or disaster recovery.
vhir backup /path/to/destination # Case data only (interactive)
vhir backup /path/to/destination --include-evidence # Include evidence files
vhir backup /path/to/destination --include-extractions # Include extractions
vhir backup /path/to/destination --include-opensearch # Include OpenSearch indices
vhir backup /path/to/destination --all # Everything (evidence + extractions + OpenSearch)
vhir backup --verify /path/to/backup/INC-2026-0225/ # Verify backup integrity
| Argument/Option | Description |
|---|---|
destination |
Backup destination directory |
--include-evidence |
Include evidence files |
--include-extractions |
Include extraction files |
--include-opensearch |
Include OpenSearch index snapshot (local Docker only) |
--all |
Include evidence + extractions + OpenSearch (if available) |
--verify BACKUP_PATH |
Verify an existing backup's integrity |
Creates a timestamped directory with all case metadata, findings, timeline, audit trails, and a backup-manifest.json with SHA-256 hashes. The case's copies of its signed history (ledger/: its chains, its 0.6 records and the public signing keys) are written first and carried; no passphrase verifier or signing-key file is. The printout lists every signing key with its full fingerprint and each chain's head. The --verify option re-hashes every file and reports mismatches or missing files.
vhir restore¶
Restore a case from a backup directory, into a folder you confirm.
vhir restore /path/to/backup/INC-2026-0411-2026-04-13 # Full restore
vhir restore /path/to/backup/... --skip-opensearch # Skip OpenSearch index restore
vhir restore /path/to/backup/... --skip-ledger # Skip restoring the 0.6 approval records
vhir restore /path/to/backup/... --target /cases/INC-1 # Restore here, without asking
| Argument/Option | Description |
|---|---|
backup_path |
Path to backup directory (contains backup-manifest.json) |
--skip-opensearch |
Skip OpenSearch index restore |
--skip-ledger |
Skip restoring the case's 0.6 approval records to this box |
--target DIR |
Restore into DIR instead of being asked. Required without a terminal |
Restore asks Restore case <id> to [<folder it was backed up from>]:; Enter accepts that folder, or type another path. Without a terminal (scripts, agents), --target is required and restore refuses without it. If the folder holds an interrupted restore (.restore-in-progress), restore names it and asks before removing it, defaulting to no; without a terminal it refuses, and you remove the folder yourself. An existing folder without that marker is never overwritten. If the folder can't be created (permission denied), prints exact mkdir/chown commands to fix it. Never auto-activates the case — prints activation instructions after restore. No credential is restored: each signing key the case's chains hold that this box doesn't is added as a retired public key ("KEY ADDED"), so the case verifies here; a chain of another case is refused. Examiners enrol on this box with vhir config --setup-password.
Execution¶
vhir exec¶
Execute a forensic command with audit trail logging. Requires TTY confirmation.
Setup¶
vhir setup¶
Routes to setup subcommands. Run vhir setup client to configure your LLM client.
vhir setup client¶
Configure LLM client for Valhuntir endpoints.
vhir setup client # Interactive wizard
vhir setup client --client=claude-code -y # Solo, Claude Code
vhir setup client --sift=http://10.0.0.5:4508 --windows=10.0.0.10:4624
vhir setup client --remote --token=vhir_gw_... # Remote with auth
| Option | Description |
|---|---|
--client |
Target client: claude-code, codex, librechat, other (claude-desktop is refused) |
--sift |
SIFT gateway URL |
--windows |
Windows wintools-mcp endpoint |
--remnux |
REMnux endpoint |
--examiner |
Examiner identity |
--no-mslearn |
Exclude Microsoft Learn MCP |
-y / --yes |
Accept defaults |
--remote |
Remote setup (gateway on another host) |
--token |
Bearer token for gateway auth |
vhir setup test¶
Test connectivity to all detected MCP servers.
vhir setup join-code¶
Generate a join code for remote machines.
vhir setup join-code --expires 2
vhir setup join-code --name lab2 # A code that joins as lab2 only, ending lab2's earlier gateway tokens
A code bound with --name lets a LAN examiner re-join under their own name; their passphrase, signing key and ledger lines are unchanged. It can't take the box owner's own name.
vhir setup hayabusa¶
Install the current Hayabusa release for this user, into ~/.local (no sudo). SANS's installer (sudo cast install teamdfir/sift) installs Hayabusa 3.8.1 with the rest of SIFT.
The release is checked against the sha256 digest GitHub's release API gives, unpacked with its rules into ~/.local/share/hayabusa/<version>/ and linked as ~/.local/bin/hayabusa.
Service Management¶
vhir service status¶
Show status of all backend services.
vhir service start / stop / restart¶
Manage backend services through the gateway API.
vhir service start forensic-mcp
vhir service stop threatintel-mcp
vhir service restart # All backends
Updating¶
vhir update¶
Pull the latest code, reinstall the packages, redeploy the client controls and restart the gateway. Run it on the SIFT workstation. See Updating for the Windows workstation, remote examiner machines and the steps after an update.
vhir update --check # Check for updates without applying
vhir update # Pull latest, reinstall, redeploy, restart
vhir update --no-restart # Skip gateway restart after update
vhir update --cpu # Install CPU PyTorch for knowledge search
| Option | Description |
|---|---|
--check |
Check for updates without applying |
--no-restart |
Skip gateway restart |
--cpu |
Install CPU PyTorch for knowledge search (~0.7 GB) |
--gpu |
Install GPU (CUDA) PyTorch for knowledge search (~5.4 GB) |
Configuration¶
vhir config¶
Manage Valhuntir settings.
vhir config --show # Show current config
vhir config --examiner alice # Set examiner identity
vhir config --setup-password # Passphrase and signing key (at least 8 characters; 15+ recommended)
vhir config --reset-password # Change the passphrase (same key, requires the current one)
vhir config --forgot-password # New key under a new passphrase; the old key is kept as retired
vhir config --upgrade-key # Signing key for a 0.6 passphrase (prints the fingerprint if you have one)
Join (Remote Setup)¶
vhir join¶
Join a SIFT gateway from a remote machine using a join code.
vhir join --sift 10.0.0.5 --code ABC123
vhir join --sift 10.0.0.5:4508 --code ABC123 --ca-cert ca-cert.pem
vhir join --sift 10.0.0.5 --code ABC123 --name lab2 # Join under a chosen examiner name
The gateway names the joining examiner after this machine's host name (lower case, the part before the first dot, at most 20 characters), or --name. A name another token or an enrolled examiner already holds is refused. The join prints the name it got and the command to enrol it for approvals, which the person named runs once at the SIFT terminal:
vhir enrol --name lab2 # Passphrase and signing key for lab2 (at least 8 characters; 15+ recommended)
vhir enrol --name lab2 --reset # Change lab2's passphrase
vhir enrol --name lab2 --forgot # New key for lab2; the old one is kept as retired
vhir enrol --name lab2 --upgrade # Signing key for lab2's 0.6 passphrase