WarpCore / Documentation
Audit Replay
SOURCE · docs/specs/audit-replay-v1.md · synced from the engineering repo
Deterministic Audit Replay — Specification v1
Status: Draft for regulator review
Tool: warpcore-replay
Document version: 2026-04-11
Reference implementation: crates/warpcore-replay
1. Abstract
WarpCore’s audit log is a hash-chained append-only record of every event that affects payment state. Today a regulator who wants to verify the integrity of a day’s activity has to trust WarpCore’s own reporting tools. That’s not good enough. This specification defines a deterministic audit replay protocol: given any archival bundle of audit events, an independent party can, using only open-source software, reproduce the final per-payment state set and compute a single 32-byte state root that must exactly match WarpCore’s own state root at that point in time.
Divergence between two independent computations of the state root means exactly one thing: someone has tampered with the bundle.
2. Problem
Regulators asking “was this payment settled correctly on April 4th at 14:32 UTC?” currently face three bad options:
- Trust the operator’s dashboard. Unacceptable for adverse proceedings.
- Replay SWIFT gpi logs. SWIFT’s logs are proprietary, opaque, and only show the happy path.
- Subpoena the database. Weeks to months, plus arguments over admissibility.
A deterministic replay tool gives the regulator a fourth option: receive a JSON bundle signed by WarpCore, run one command, get back a state root, compare it to WarpCore’s published state root for that window. Five seconds, no trust, no theatre.
3. Non-goals
- Replay does not recover payment PII. The audit log’s
datafield may contain limited contextual information; replay does not interpret it beyond hashing. - Replay does not verify the correctness of compliance decisions. It verifies that the sequence of decisions was not tampered with.
- Replay does not depend on Kaspa block history. The audit chain is self-contained; Kaspa anchoring is a parallel proof (documented separately).
4. Audit bundle format
A bundle is a JSON document conforming to one of two layouts:
// Layout 1 — bare array
[ <AuditEvent>, <AuditEvent>, ... ]
// Layout 2 — wrapper object (matches /v1/audit/log response)
{ "events": [ <AuditEvent>, ... ] }
An AuditEvent has the following fields (see
warpcore_models::audit::AuditEvent):
| Field | Type | Description |
|---|---|---|
id | i64 | Strictly increasing sequence number |
event_type | enum | e.g. PAYMENT_RECEIVED, PAYMENT_SETTLED |
payment_id | UUID or null | Associated payment, if any |
actor_id | string | Who caused the event |
actor_role | enum | admin, operator, compliance, auditor, readonly |
data | JSON | Event-specific payload |
timestamp | RFC3339 | When the event was recorded |
prev_hash | [u8; 32] | SHA-256 of the previous event’s row_hash |
row_hash | [u8; 32] | SHA-256 chain link for this event |
5. Hash chain definition
For each event, the row hash is:
row_hash = SHA-256(
prev_hash ||
event_type.to_string().as_bytes() ||
payment_id.as_bytes() (if Some) ||
actor_id.as_bytes() ||
serde_json::to_vec(data) ||
timestamp.to_rfc3339().as_bytes()
)
The replayer recomputes row_hash for each event independently and
compares it to the stored value. Any mismatch aborts with exit code 1.
Additionally, for every event after the first, event[i].prev_hash MUST
equal event[i-1].row_hash. Violations abort with exit code 1.
6. State reduction
Events that affect payment lifecycle are mapped to a
ReplayedState as follows:
| Event type | Mapped state |
|---|---|
PaymentReceived | Received |
PaymentValidated | Validated |
PaymentComplianceChecked | ComplianceChecked |
PaymentSubmitted | Submitted |
PaymentConfirming | Confirming |
PaymentSettled | Settled |
PaymentRejected | Rejected |
PaymentCancelled | Cancelled |
PaymentFailed | Failed |
| (all others) | (skipped) |
The reducer walks events in id order and sets final_states[payment_id]
to the most recent mapped state. Non-payment events (sanctions screening
records, auth events, admin config changes) are skipped — they verify
the chain integrity but do not contribute to the state root.
7. State root
The state root is computed as:
state_root = SHA-256(
"warpcore-replay/state-root/v1" ||
be_u64(count) ||
for each (payment_id, state) in final_states sorted by uuid:
payment_id.as_bytes() // 16 bytes
state.ordinal() // 1 byte
)
Key properties:
- Domain-separated. The constant prefix prevents a state root from being mistaken for an event hash.
- Length-prefixed. The count field prevents length-extension ambiguity between bundles of different sizes.
- Fixed-width entries. Each payment contributes exactly 17 bytes regardless of content, so the hash is stable against encoding drift.
- Canonical ordering. Entries are sorted by UUID byte order (a
BTreeMap<Uuid, _>provides this natively).
The state root is a 32-byte number, typically rendered as a 64-character lowercase hex string.
Two independent implementations following this spec MUST produce bit-identical state roots on bit-identical bundles.
8. State ordinal table
The ordinal value of each state is fixed by this specification. Changing it is a breaking change and requires a spec version bump.
| State | Ordinal |
|---|---|
Unknown | 0 |
Received | 1 |
Validated | 2 |
ComplianceChecked | 3 |
Submitted | 4 |
Confirming | 5 |
Settled | 6 |
Rejected | 7 |
Cancelled | 8 |
Failed | 9 |
9. CLI contract
warpcore-replay <bundle.json> [OPTIONS]
OPTIONS
--format <human|json> Output format (default: human)
--expect-root <hex> Fail if the computed state root does not
match this value.
EXIT CODES
0 chain valid, replay succeeded, expect-root passed
1 chain broken OR replay failed OR expect-root mismatched
2 invocation error (missing file, unknown flag)
Human-readable output includes:
- Event count and ID range
- Time range (earliest/latest timestamps)
- Chain integrity status
- Unique payment count
- Final state distribution
- Hex state root
JSON output is a ReplayReport object with identical semantic content.
10. Reproducibility contract
A conforming implementation MUST:
- Parse both array and wrapper bundle layouts.
- Use SHA-256 exclusively. No substitution.
- Walk events in strict
idorder; reject on gaps or duplicates. - Recompute
row_hashusing the formula in §5. - Reduce to final states per §6.
- Compute the state root per §7 and §8.
It MAY:
- Parallelise the chain verification pass using a prefix-sum over hashes.
- Skip the state reduction pass if the caller only needs chain integrity.
11. Security properties
- Tamper evidence. Mutation of any field in any event flips the event’s row hash and breaks verification.
- Insertion evidence. Inserting a new event breaks chain linkage at the next event (prev_hash mismatch).
- Deletion evidence. Removing an event breaks chain linkage at the following event.
- Reordering evidence. The
idordering check plus hash chain prevents any reordering. - No bundle forgery without the preceding events. A bundle cannot be “trimmed” to hide activity before a chosen start point, because the regulator can demand the predecessor’s row_hash and verify.
12. Regulator workflow
- Regulator requests audit bundle for a specific time window.
- WarpCore exports the bundle as JSON, signs it, publishes it.
- Regulator downloads
warpcore-replayfrom the public source repository, compiles from source, runscargo test. - Regulator executes
warpcore-replay bundle.json --expect-root <hex>where<hex>is the state root WarpCore attested. - Exit 0 → bundle verifies. Any other code → evidence preserved for adverse proceedings.
The entire workflow has a trust assumption list of exactly two items:
- The Rust compiler is not compromised.
- SHA-256 has not been broken.
WarpCore is not in that list.
13. Reference implementation
- Crate:
warpcore-replay - Key files:
src/lib.rs—Replayer,ReplayReport,ReplayedState, bundle loadersrc/bin.rs—warpcore-replayCLItests/replay_integration.rs— 7 passing tests
- Build:
cargo build -p warpcore-replay --release - Test:
cargo test -p warpcore-replay
14. Test vectors
Regenerate from the reference implementation:
cargo test -p warpcore-replay -- --ignored dump_bundle_for_cli_demo
cargo run -q -p warpcore-replay -- /tmp/valid_bundle.json
Expected state root for the 6-event synthetic chain generated by
make_chain(6) (with IDs 1..=6, timestamps starting at Unix epoch
1_700_000_000, payment_ids 0x01..=0x06, and the canonical
Received→Validated→Settled cycle):
dcbc452671e463bdf9e8ab39ea6d14a4213d6934091d856761114f5948a1ecc1
15. Roadmap
| Version | Planned |
|---|---|
| v1 (this doc) | SHA-256 chain, synchronous replay, BTreeMap state |
| v1.1 | Parallelised chain verification (Rayon) |
| v1.2 | Incremental replay against an existing state root for streaming audits |
| v2 | Kaspa anchoring cross-check — state root is also anchored in a Kaspa transaction so the root itself is tamper-evident without trusting the bundle distributor |
Authors: WarpCore engineering