Skip to main content
Every event in ProvenLog is cryptographically linked to the one before it, forming a hash chain. This makes tampering detectable — modifying any event breaks the chain from that point forward.

How it works

Each event’s hash is computed as:
hash = SHA-256(canonical_json(content_fields) || prev_hash)
  • Canonical JSON — keys sorted alphabetically, deterministic serialization
  • Content fields — all event data that matters for integrity
  • prev_hash — the hash of the previous event in the chain

Content fields (included in hash)

These fields are included in the hash computation: id, schema_version, agent_id, session_id, source, action_type, action_name, action_input, action_output, action_status, error_message, timestamp, duration_ms, labels, metadata, capture_method

Excluded from hash

These fields are not included in the hash:
  • sequence — server-assigned ordering
  • hash — the computed value itself
  • prev_hash — linked separately
  • validation_warnings — quality metadata added by the pipeline

Genesis hash

The first event in each chain uses a well-known zero hash as its prev_hash:
0000000000000000000000000000000000000000000000000000000000000000

Per-agent chains

Each agent_id maintains an independent hash chain. This design avoids a single-threaded write bottleneck — agents don’t block each other when writing events.
Agent A:  [E1] -> [E2] -> [E3] -> [E4]
Agent B:  [E1] -> [E2] -> [E3]
Agent C:  [E1] -> [E2] -> [E3] -> [E4] -> [E5]

Server-authoritative sequencing

The SDK sends events with client-side timestamps. The server assigns:
  • sequence — monotonically increasing within each agent’s chain
  • hash / prev_hash — computed server-side to maintain chain integrity
This ensures a single source of truth for ordering, even when events arrive out of order or from multiple SDK instances.

Verification

Verification walks the chain from the genesis event and recomputes each hash:
plog verify --agent-id my-agent
result = client.verify("my-agent")
# {"valid": True}
Verification is O(n) and detects both modified events (hash mismatch) and missing events (sequence gap).

Timing-safe comparison

Hash comparisons use crypto/subtle.ConstantTimeCompare (Go) to prevent timing side-channel attacks.