-
Notifications
You must be signed in to change notification settings - Fork 5
dart audit
Append-only, tamper-evident JSONL log. Records every MCP call (inputs, outputs, audit_id, timestamp, hash chain) so a human reviewer can replay the agent's reasoning end-to-end.
Forensic findings have a chain-of-custody requirement that ordinary software doesn't. If the agent claims "USB Kingston DataTraveler was inserted at 14:22:18 UTC", a reviewer must be able to verify, after the fact:
- The agent actually saw that artifact (audit recorded the read)
- The artifact has not been edited between the agent's read and the reviewer's verification (hash anchored)
- No log entry has been silently inserted, deleted, or reordered (chain unbroken)
A simple append-only log gives you (1). A SHA-256 chain — where each entry's hash includes the previous entry's hash — gives you (2) and (3) for free. Tampering with any entry breaks the chain at that point and every subsequent point.
from dart_audit import AuditLogger
logger = AuditLogger("audit/CASE-001.jsonl", run_id="run-abc")
logger.log(
tool_name="get_amcache",
inputs={"hive_path": "examples/case-studies/self-evaluation/case-01/evidence_root/disk/Windows/AppCompat/Programs/Amcache.hve"},
output={"items": [...]},
iteration=1,
token_count_in=15,
token_count_out=500,
finding_ids=[], # optional, used when this call directly produced a finding
)Each log() call writes one JSON line to the file. The line contains:
| Field | Type | Purpose |
|---|---|---|
audit_id |
8-char hex (4-byte secrets.token_hex(4)) |
Random per-entry ID. Used by findings for citation. |
prev_hash |
hex-SHA-256 | Hash of the previous entry. First entry uses 0000...0000. |
entry_hash |
hex-SHA-256 | Hash of this entry, including prev_hash. |
iteration |
int | Loop iteration counter. |
tool_name |
str | The MCP function called. |
inputs |
dict | Validated inputs as passed in. |
output_digest |
hex-SHA-256 | Hash of the output (full output not stored — too big). |
finding_ids |
list[str] | Findings this call produced (empty if none). |
ts |
ISO-8601 | Wall-clock timestamp. |
token_count_in, token_count_out
|
int | LLM token accounting (deterministic mode uses synthetic counts). |
Output is referenced by SHA-256 digest only (the output_digest field) — the actual output JSON is not persisted to disk in deterministic mode. This keeps the chain file small enough to read and verify in one pass. To re-derive the output, replay the same tool_name + inputs against the same evidence; the digest will match if the evidence and the function are deterministic. (Live mode preserves outputs on a per-iteration basis under the --out directory; the deterministic demo run does not.)
python3 -m dart_audit verify audit/CASE-001.jsonlOutput (clean chain):
chain verified: 47 entries, tail=4f7a9c1b3e8d2046...8a13c5
Output (tampered):
line 12: entry_hash mismatch (audit_id=8fa06156)
ABORT — chain breaks at line 12
The verifier walks the chain forward, re-hashes each entry from raw fields, and checks entry_hash matches and prev_hash matches the previous entry_hash. Any tamper — payload change, deletion, reorder — breaks the walk.
When the agent emits a finding like F-013, the report cites the audit_id of the supporting MCP call. To trace:
python3 -m dart_audit trace audit/CASE-001.jsonl F-013Walks the chain, finds the entry where F-013 was produced, and prints the chain of MCP calls that led to it (input → call → output digest → previous related call → ...). Up to the original artifact read.
- That the inputs to a tool call were honest. The agent could pass any input.
- That the outputs were not selectively emitted by a buggy or malicious tool implementation.
- That the playbook the agent loaded was the playbook the operator thought they were running.
The audit chain is a transcript integrity tool, not a reasoning correctness tool. See Threat model for the full scope.
dart_audit/src/dart_audit/
├── __init__.py # AuditLogger, AuditEntry classes (~150 lines)
└── __main__.py # CLI: verify, trace
- Architecture deep dive — why chained, why SHA-256
- Threat model
-
tests/test_audit_chain.py— integrity tests
Agentic-DART — autonomous DFIR agent · architecture-first, not prompt-first · MIT license · github.com/Juwon1405/agentic-dart
- The Memex bet ⭐ Why this design
- About the name
- Architecture-first vs prompt-first
- Architecture deep dive
- Threat model
- Glossary
- dart-mcp — typed surface (native + SIFT adapters)
- dart-agent — senior-analyst loop
- dart-corr — cross-artifact correlation
- dart-audit — SHA-256 chained log
- dart-playbook — senior-analyst sequencing rules (v3 default)
- MCP function catalog (native + SIFT adapters)
- Comparison with adjacent tools
- FAQ
- Operator guide — distro-agnostic
- Running on SIFT
- Live mode
- Accuracy report
-
Roadmap ⭐ Phase 1 ~95% complete
- Phase 1 — Agentic DFIR ⭐ dedicated page · SANS submission
-
Phase 2 — Detection engineering
- The self-learning loop ⭐ design note
- Phase 3 — Agentic SOC
- Phase 4 — Broader agentic security