Session Forensics
Session Forensics is the mechanism by which Govrix Scout creates a cryptographically-linked, tamper-evident audit trail for every conversation an agent has with an LLM. It is designed to satisfy compliance requirements where you need to prove — not just claim — what an agent said and when.
The compliance invariant
Every event emitted by the proxy must carry the following four fields. This is enforced at compile time: AgentEvent::new() will not compile without all four.
| Field | Type | Description |
|---|---|---|
session_id | UUID v4 | Groups all events belonging to one logical conversation |
timestamp | ISO-8601 UTC | Nanosecond-precision wall-clock time of the event |
lineage_hash | SHA-256 hex | Merkle chain node linking this event to its predecessor |
compliance_tag | String | Regulatory classification applied at policy evaluation time (e.g. pci-dss, hipaa, internal) |
Removing any of these four fields from AgentEvent is a compile error, not a runtime warning. This is intentional — compliance invariants are structural, not optional.
How the Merkle chain works
Each event’s lineage_hash is computed as:
SHA-256( previous_lineage_hash || session_id || timestamp || payload_hash )The first event in a session uses the session ID itself as the seed (there is no predecessor hash). This produces a chain where any retroactive modification to an event — including deleting it — produces a detectable break in the hash sequence when you walk the chain.
This is not a blockchain. It is a single-author, append-only Merkle chain. Its purpose is tamper detection, not consensus.
Implementation
| File | Role |
|---|---|
crates/govrix-scout-proxy/src/policy/session.rs | SessionRecorder — appends events to the session trace, computes lineage hashes |
crates/govrix-scout-common/src/models/event.rs | AgentEvent struct — enforces the four required fields at the type level |
crates/govrix-scout-store/src/sessions.rs | Database reads for the session query API |
The SessionRecorder is called from log_response_event via PolicyHook::record_session_event — it is on the event-write path, not the hot-path request handler.
API
List sessions
curl -X GET http://localhost:4001/api/v1/sessions \
-H "Authorization: Bearer $GOVRIX_API_KEY"Response:
{
"sessions": [
{
"session_id": "018e4b7a-1234-7abc-8def-000000000001",
"agent_id": "billing-agent",
"started_at": "2026-03-05T08:00:00.000Z",
"last_event_at": "2026-03-05T08:04:22.317Z",
"event_count": 12,
"compliance_tag": "pci-dss"
}
]
}Event writes are fire-and-forget: the proxy never awaits the database write. Events are buffered in a bounded in-process channel (capacity 10,000) and flushed by a background batch writer every 100ms or every 100 events, whichever comes first. This means a session’s event list may lag real-time by up to 100ms.
Verifying chain integrity
To verify that no events have been tampered with or deleted, walk the events for a session in timestamp order and recompute each lineage_hash. A break in the chain at position N means event N (or an event between N-1 and N) was altered or removed.
Govrix does not currently expose a built-in chain verification endpoint. You can verify offline by querying the raw events table directly:
SELECT event_id, timestamp, lineage_hash
FROM agent_events
WHERE session_id = '018e4b7a-1234-7abc-8def-000000000001'
ORDER BY timestamp ASC;