CLI & API reference¶
Command line¶
revenant scenarios list built-in synthetic scenarios
revenant demo [--scenario NAME] [--top N] run a scenario end-to-end, print report
revenant analyze PATH [PATH ...] analyse artefacts (OTRF JSON, .evtx, plaso
json_line/l2tcsv, Volatility dir, auth.log,
auditd, ATLAS preprocessed logs)
--kind otrf|evtx|plaso|volatility|authlog|auditd|atlas --host H
--format md|html|json|cypher --out FILE --pdf FILE
--ledger case.sqlite --top N --include-noisy
revenant verify FILE|LEDGER.sqlite custody-ledger integrity check (read-only)
--expect-head HASH --expect-count N external anchors (detect truncation/rewrite)
revenant --version
revenant serve [--host H] [--port P] [--allow-remote]
FastAPI + timeline/graph UI; a non-loopback
--host is refused without --allow-remote
--ledger persists the custody ledger to a new SQLite store; a store that already holds
another case's ledger is refused (exit 2) before any report is written.
```
HTTP API¶
When run with revenant serve (needs the api extra), the service exposes:
| Method & path | Purpose |
|---|---|
GET / |
single-page timeline + causal-graph UI |
GET /api/health |
liveness |
GET /api/scenarios |
built-in synthetic scenarios |
POST /api/cases/scenario/{name} |
analyse a synthetic scenario |
POST /api/cases/path |
analyse artefacts under the evidence root |
GET /api/cases |
list cases |
GET /api/cases/{id} |
stories, events, edges, indicators (JSON) |
GET /api/cases/{id}/report.md / .html |
court-style report |
GET /api/cases/{id}/cypher |
Neo4j export |
GET /api/cases/{id}/custody |
custody ledger + verification |
The service only reads evidence, only below REVENANT_EVIDENCE_ROOT, and binds to
localhost by default.
Python API¶
revenant.pipeline
¶
End-to-end orchestration.
Two entry points share one engine:
run-- legacy v0.1 API over raw(kind, record)tuples (synthetic scenarios, hand-written fixtures).analyze_paths/analyze_events-- real artefacts (OTRF JSON,.evtx, plaso, Volatility, auth.log) loaded byrevenant.parsers.
Stages: ingest + integrity hashing -> custody ledger -> provenance graph -> cross-artefact fusion (corroboration) -> causal rules -> anti-forensics scan -> incident stories (ATT&CK-tagged, suspicion + confidence) and, for small graphs, v0.1 path chains. The custody ledger records every stage so every derived claim is traceable to the ingest records it depends on.
file_sha256(path)
¶
Return the SHA-256 hex digest of a file, read in 1 MiB chunks.
analyze_events(events, *, backend='auto', use_guids=True, story_config=None, ledger=None, chains=None)
¶
Run the full engine over already-normalized, hashed events.
analyze_paths(paths, *, kind=None, include_noisy=False, **kwargs)
¶
Load artefacts from disk (auto-detecting their kind) and analyse them.
Loader options (year and utc_offset_hours for auth.log, host
for Volatility output) are routed to the parsers; everything else goes to
analyze_events.
run(records, *, backend='auto')
¶
v0.1 API: normalize raw (kind, record) pairs and analyse them.
revenant.models
¶
Typed contracts for REVENANT.
Every forensic fact flows through these models. The schema is deliberately
small and frozen-ish: an Event is an (actor, action, object, time) tuple
tied back to the raw artifact that produced it, plus an integrity hash. All
downstream reasoning (edges, chains, narratives) references events by id, so a
claim can always be traced to evidence.
SourceReliability
¶
Bases: str, Enum
Admiralty-code style source reliability (A=most reliable ... F=unknown).
weight
property
¶
Return the reliability weight of this evidence source class.
EventType
¶
Bases: str, Enum
Normalized event categories used by the causal rule engine.
KillChainStage
¶
Bases: str, Enum
Lockheed-Martin cyber kill chain stages used for chain classification.
ConfidenceGrade
¶
Bases: str, Enum
Human-facing confidence bands for a reconstructed chain.
from_score(score)
classmethod
¶
Map a numeric confidence score to a grade using the fixed cut-offs.
Event
¶
Bases: BaseModel
A single normalized forensic event.
event_id is stable and derived from content by the integrity layer.
integrity_hash binds the event to its raw source bytes.
canonical()
¶
Deterministic string used for hashing / stable ids.
CausalEdge
¶
Bases: BaseModel
A typed causal link inferred between two events.
ProvenanceChain
¶
Bases: BaseModel
An ordered candidate incident narrative (sequence of events).
CustodyRecord
¶
Bases: BaseModel
Append-only chain-of-custody ledger entry.
TamperingIndicator
¶
Bases: BaseModel
A cross-artifact inconsistency suggesting anti-forensics.
IncidentStory
¶
Bases: BaseModel
A reconstructed incident narrative: a causal subtree, not a single path.
Real process trees fan out (one payload spawns many children); a story keeps the whole subtree below a story root so the narrative reads like an analyst's write-up, with confidence and suspicion scored over it.
revenant.confidence
¶
Confidence scorer (Admiralty-inspired source reliability).
A chain's confidence is a weighted sum of four signals, each in [0,1]:
- reliability - mean source-reliability weight of its events
- corroboration - how many distinct source artifacts back the chain, counting cross-artefact corroborations found by fusion
- temporal_fit - mean temporal tightness of its causal edges
- edge_strength - mean rule confidence of its edges
Tampering indicators apply a multiplicative penalty. The result maps to a
human-facing grade (LOW/MEDIUM/HIGH/CONFIRMED). Weights and grade cut-offs
are explicit, hand-set and documented so the score is defensible, not a black
box. Only the per-rule edge confidences feeding edge_strength are
calibrated against ground truth; the story score itself is not a calibrated
probability.
ConfidenceBreakdown
dataclass
¶
Every term of a story/chain confidence score, for the report and the UI.
Attributes:
| Name | Type | Description |
|---|---|---|
reliability, corroboration, temporal_fit, edge_strength |
The four weighted terms, each in [0, 1]. |
|
penalty |
float
|
Multiplicative tampering penalty (1.0 = none, floored at 0.5). |
score |
float
|
|
grade |
ConfidenceGrade
|
|
as_dict()
¶
Return the fields as a JSON-serialisable dict.
score_chain(chain, graph)
¶
Score a chain or story against the provenance graph it came from.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chain
|
ProvenanceChain | IncidentStory
|
A path-view chain or an incident story (its |
required |
graph
|
ProvenanceGraph
|
The graph holding the events, used for reliability and corroborating sources. |
required |
Returns:
| Type | Description |
|---|---|
ConfidenceBreakdown
|
All terms plus the final score and grade. |
apply_score(chain, graph)
¶
Compute score_chain and store the score and grade on chain (returned).
revenant.integrity
¶
Integrity + chain-of-custody primitives.
Two guarantees:
- Every event gets a content-derived
integrity_hashand a stableevent_id(never trust an id supplied by an artifact). - An append-only, tamper-evident custody ledger: each record commits to the previous record's hash (a hash chain), so any silent edit or deletion breaks verification. The tool never mutates source artifacts.
CustodyLedger
¶
Append-only tamper-evident ledger of custody records.
records
property
¶
Return a copy of the custody records in ledger order.
append(action, digest, *, event_id=None, artifact=None, when=None)
¶
Append a hash-chained custody record.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
action
|
str
|
What happened (for example |
required |
digest
|
str
|
SHA-256 of the subject. |
required |
event_id
|
str
|
What the record refers to. |
None
|
artifact
|
str
|
What the record refers to. |
None
|
when
|
datetime
|
Record time; defaults to now (UTC). |
None
|
Returns:
| Type | Description |
|---|---|
CustodyRecord
|
The new record, chained to the previous one. |
record_ingest(event)
¶
Append an ingest record for a finalised event.
verify()
¶
Recompute the hash chain; return False if any link is broken.
hash_event(event)
¶
Deterministic SHA-256 over the event's canonical form.
stable_event_id(event)
¶
A short, stable id derived from content (first 16 hex of the hash).
finalize_event(event)
¶
Return a copy of event with integrity_hash and event_id populated.
verify_event(event)
¶
True if the event's stored integrity_hash matches its content.
revenant.custody_store
¶
Persistent, append-only custody ledger (SQLite).
The spec calls for an append-only store (PostgreSQL). SQLite gives the same guarantees for a single examiner workstation with zero infrastructure:
BEFORE UPDATE/BEFORE DELETEtriggers abort any attempt to rewrite history through SQL;- every row still carries the hash chain from
CustodyLedger, so an edit made around SQLite (hex-editing the file) is caught byverify_store.
The same schema ports to PostgreSQL unchanged apart from the trigger syntax (see docs/adr/0005-custody-ledger.md).
append_ledger(ledger, path)
¶
Append ledger records not yet in the store. Returns rows written.
The store must be a prefix of ledger (same hashes); anything else
means the two histories diverged and is refused.
missing_triggers(path)
¶
Append-only triggers absent from the store (a dropped trigger is a tamper sign).
load_ledger(path)
¶
Load a stored ledger read-only. Raises FileNotFoundError for a missing path.
verify_store(path, *, expect_head=None, expect_count=None)
¶
Recompute the hash chain of a stored ledger.
The internal chain alone cannot reveal a truncated tail or a full rewrite; pass the head hash / record count printed in the report (an external anchor) to detect those too. Missing append-only triggers fail verification.
revenant.export
¶
Exports: JSON (API/UI), Cypher (Neo4j) and an optional live Neo4j push.
The engine stays in-memory (networkx); Neo4j is a sink for analysts who want to explore the provenance graph in Neo4j Browser/Bloom. Export only the part of the graph that carries meaning -- events inside stories plus their edges -- so a 100k-event capture does not become a 100k-node dump.
story_event_ids(analysis, top=None)
¶
Return the unique event ids of the top stories, in first-seen order.
to_dict(analysis, *, top=20, include_all_events=False, max_events=5000)
¶
JSON-able view of an analysis: stories with their events and edges.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
analysis
|
Analysis
|
Result of |
required |
top
|
int
|
Number of ranked stories to include. |
20
|
include_all_events
|
bool
|
Also include events outside any story (up to |
False
|
to_json(analysis, *, top=20)
¶
Serialise an analysis to indented JSON, limited to the top stories.
to_cypher(analysis, *, top=20)
¶
A self-contained Cypher script (cypher-shell < out.cypher).
push_neo4j(analysis, uri, user, password, *, top=20)
¶
Execute the Cypher export against a live Neo4j (optional neo4j driver).