This development version extends the original manuscript prototype. It adds actual local file operations, a durable hash-chained journal, detached verification receipts, derived investigation findings, an offline interactive dashboard, and repeatable tests. The original paper demo below remains available unchanged.
Scope: an instrumented local tool adapter and investigation tool, not a
production agent sandbox or an automatic prompt-injection detector. The included
agent decisions are scripted. There is no live LLM or external email transmission.
An outbox/ path is an ordinary local directory used to demonstrate a denied
destination. Both read and write effects are real local filesystem operations.
Clone this repository into a folder named afra (or extract the release ZIP)
and open a terminal in the directory containing afra/.
Python 3.10+ is required. The new workflow needs no third-party dependencies.
An example report and recorded results are included under examples/.
Download or clone the repository and open examples/investigation.html in a
browser to explore the ten cases without running Python. GitHub's file viewer
shows the HTML source, so open a downloaded copy for the interactive dashboard.
python -m afra.lab run --output runs/first-investigation
python -m unittest discover -s afra/tests -vOpen runs/first-investigation/investigation.html in a browser. Select a case,
inspect findings and outcomes, filter the evidence timeline, and expand events to
see their IDs, source, dependency parents, timestamp, and hash. The report works
offline, with no CDNs or telemetry. Existing run directories are never overwritten;
choose a new output directory for each run.
python -m afra.lab verify runs/first-investigation/clean/evidence.jsonl --receipt runs/first-investigation/clean/receipt.json
python -m afra.lab inspect runs/first-investigation/audit_conflict/evidence.jsonl --receipt runs/first-investigation/audit_conflict/receipt.jsonThe verify command exits 0 for a receipt match, 2 for a failed check, and 3 when
the chain is internally consistent but no receipt was supplied. inspect prints
an investigation as JSON; it does not modify the evidence. --taint EVENT_ID marks
an investigator-selected source and propagates its dependency lineage. This is
not automatic identification of malicious instructions or proof of causation.
| Capability | Original demo | New 0.2 workflow |
|---|---|---|
| Action evidence | Scripted artifact descriptions | Captures file reads, writes, policy decisions, errors, and missing completion observations |
| Evidence custody | Mutable in-memory artifacts, JSON snapshot | Snapshot JSONL events, SHA-256 predecessor chain, fsync, detached receipt |
| Analysis | Trust tier / five-question worked scenario | Per-action outcomes, denied-but-observed effects, contradictory authorization claims, source lineage |
| Tamper checks | None | Edits, deleted/reordered events, truncation and rewritten chains checked against a retained receipt |
| Demonstration | One scripted scenario | Ten controlled cases and measured local recording cost |
| Interface | Flask paper demo | Searchable, offline investigation report; no server required |
The new ledger does not replace the paper's artifact model or recalibrate its tiers. It is a separate runtime evidence layer. Paper claims, this version's features, and local experimental results must be described separately.
LocalRuntime.read_document() and LocalRuntime.save_report() can be exposed as
tools to an agent framework. They return a status, call ID, event ID, and result.
Pass the source event IDs into subsequent tool calls to preserve declared lineage.
The adapter is framework-independent; no live framework integration has been
validated in this release.
from pathlib import Path
from afra.journal import Journal
from afra.runtime import LocalRuntime
from afra.investigation import investigate
journal = Journal("my-run/evidence.jsonl")
tools = LocalRuntime("my-run/sandbox", journal, mode="enforce")
Path("my-run/sandbox/documents/brief.txt").write_text("Sales: 42 units.")
source = tools.read_document("documents/brief.txt")
result = tools.save_report("reports/summary.txt", source["value"],
sources=[source["event_id"]])
journal.save_receipt("my-run/receipt.json")
report = investigate(journal.path, journal.checkpoint())Seven behavior cases cover clean use, blocked writes, audit-mode policy conflicts, false authorization claims, a missing completion observation after a real write, an existing-file error, and investigator-marked source lineage. Three further cases alter, truncate, or rehash captured evidence. Integrity failures suppress action conclusions. These are controlled assertions, not a security detection rate or a generalization benchmark. The source includes 17 unit/integration tests, including clean negative controls, failure injection and the case where an attacker controls both the journal and receipt (which cannot be detected).
The benchmark alternates paired bare and witnessed file writes, read-back and SHA-256, excludes three warm-up pairs, and reports median/p95 latency over 25 pairs by default. Witnessed calls include four durable event appends. Timing is machine-specific and excludes any model/network latency. Results and raw evidence are written to the run directory, without claiming paper-reviewed performance.
- The witness process and tool adapter must be trusted. They run in-process; an agent with unrestricted Python, shell, filesystem, or network access can bypass them. This is not complete mediation or OS-level isolation.
- A receipt must be retained independently of the writer to detect a malicious full-chain rewrite. The local demo stores it beside the journal for convenience. A user who can replace both can forge a self-consistent history. No authenticated signature, remote witness, or external timestamp authority is implemented.
- Hash integrity authenticates neither the generator nor the truth of its payload. Wall-clock timestamps can be affected by clock changes. Sequence numbers give local admission order only.
- One
Journalinstance is a single writer. In-process calls are serialized; multiple processes, adversarial concurrent path changes, crash recovery and multi-host ordering are not supported. A crash can leave an incomplete final record, which fails verification rather than being silently repaired. - Missing completion means unknown outcome, even when a tool really wrote a file. A tool error may also follow a partial effect. The journal is not a transactional filesystem or proof of current disk state after a later modification.
- Body contents are excluded from runtime events; paths, hashes and metadata remain. Dependency parents are caller-supplied. Missing or omitted lineage limits analysis.
auditrecords allowed and denied operations within the adapter's local folders;enforceblocks denied outbox writes. Both modes reject path traversal and symlinks, and neither allows overwriting existing report files.
Next meaningful experiments: an isolated external witness, a real agent-framework adapter, independent retention of authenticated checkpoints, and a varied evaluation set with predeclared ground truth. None is claimed as implemented here.
A research prototype for forensic evidence capture and trust calibration in autonomous AI-agent incidents.
AFRA explores a central forensic problem: preserving an agent's record does not establish that the agent's explanation is true. This Python prototype separates evidence by its generator, tracks dependencies, propagates contamination, and returns investigative verdicts bounded by the available evidence.
Status: companion implementation for an unpublished research manuscript by Aswin T. J.; paper preparation/submission work is in progress. The demonstration is scripted, with no live language model or real email transmission.
| Module | Responsibility |
|---|---|
models.py |
Artifact records, generator types, trust tiers, and verdicts |
witness.py |
TypingGate, IntentBinder, EffectObserver, and EtiologyRecorder |
correlation.py |
Dependency graph and divergence analysis |
calibration.py |
Tier assignment, contamination propagation, and compromise-window estimation |
adjudication.py |
Answers to five investigative questions |
storage.py |
In-memory evidence collection and JSON persistence |
demo.py |
Command-line worked scenario |
server.py, static/ |
Flask API and interactive dashboard |
Requires Python 3.10+. The command-line demo uses the standard library. The web dashboard additionally requires Flask; the existing requirements file does not install it.
Clone into a folder named afra, and run the package from its parent directory:
git clone https://github.com/Aswinjagadeesh1/afra.git afra
python -m afra.demoTo use the dashboard, install Flask in your Python environment and run from the same parent directory:
python -m pip install flask
python -m afra.serverOpen http://127.0.0.1:5000. This is a local development dashboard, not a hardened multi-user service.
The demo records a poisoned document, a dependent memory write, a pre-action commitment, an authority decision, a simulated action, and a later agent claim. It then calibrates the evidence and answers:
| Investigative question | Expected verdict |
|---|---|
| What happened, to whom, and when? | SOUND |
| Under what authority? | SOUND_BUT_CLAIM_CONTRADICTED |
| When did influence enter? | SOUND |
| What did the agent intend? | COMMITMENT_ONLY |
| Why did the agent act? | UNAVAILABLE |
| Tier | Meaning within the prototype's assumptions |
|---|---|
| T1 | Evidence treated as independent of the reasoner |
| T2 | Dependent evidence corroborated by independent evidence |
| T3 | Uncorroborated dependent evidence |
| T4 | Structurally contradicted evidence |
| T5 | Evidence included in contamination closure |
These labels describe evidentiary standing, not calibrated probabilities of truth. Independence of a generator does not establish the truth of every payload it records.
- Witness-controlled timestamps record admission order; dependency links support contamination tracing.
- The store exposes append operations and rejects duplicate IDs, but artifacts remain mutable Python objects. It is not a cryptographically protected, tamper-proof ledger.
- Calibration updates artifact tiers in this implementation. The manuscript's architecture and the prototype should be distinguished when discussing immutable evidence storage.
- The scenario demonstrates component behaviour, not detection accuracy, production overhead, complete mediation, or investigative effectiveness against live agents.
- The prototype does not prevent the simulated action or recover an agent's true motivation.
Companion to AFRA: Forensic Readiness by Design, An Evidence-Preserving Reference Architecture for Autonomous AI Agents. No publication acceptance or peer-reviewed performance claim is implied.