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:

  1. Trust the operator’s dashboard. Unacceptable for adverse proceedings.
  2. Replay SWIFT gpi logs. SWIFT’s logs are proprietary, opaque, and only show the happy path.
  3. 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 data field 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):

FieldTypeDescription
idi64Strictly increasing sequence number
event_typeenume.g. PAYMENT_RECEIVED, PAYMENT_SETTLED
payment_idUUID or nullAssociated payment, if any
actor_idstringWho caused the event
actor_roleenumadmin, operator, compliance, auditor, readonly
dataJSONEvent-specific payload
timestampRFC3339When 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 typeMapped state
PaymentReceivedReceived
PaymentValidatedValidated
PaymentComplianceCheckedComplianceChecked
PaymentSubmittedSubmitted
PaymentConfirmingConfirming
PaymentSettledSettled
PaymentRejectedRejected
PaymentCancelledCancelled
PaymentFailedFailed
(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:

  1. Domain-separated. The constant prefix prevents a state root from being mistaken for an event hash.
  2. Length-prefixed. The count field prevents length-extension ambiguity between bundles of different sizes.
  3. Fixed-width entries. Each payment contributes exactly 17 bytes regardless of content, so the hash is stable against encoding drift.
  4. 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.

StateOrdinal
Unknown0
Received1
Validated2
ComplianceChecked3
Submitted4
Confirming5
Settled6
Rejected7
Cancelled8
Failed9

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:

  1. Parse both array and wrapper bundle layouts.
  2. Use SHA-256 exclusively. No substitution.
  3. Walk events in strict id order; reject on gaps or duplicates.
  4. Recompute row_hash using the formula in §5.
  5. Reduce to final states per §6.
  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 id ordering 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

  1. Regulator requests audit bundle for a specific time window.
  2. WarpCore exports the bundle as JSON, signs it, publishes it.
  3. Regulator downloads warpcore-replay from the public source repository, compiles from source, runs cargo test.
  4. Regulator executes warpcore-replay bundle.json --expect-root <hex> where <hex> is the state root WarpCore attested.
  5. 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.rsReplayer, ReplayReport, ReplayedState, bundle loader
    • src/bin.rswarpcore-replay CLI
    • tests/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

VersionPlanned
v1 (this doc)SHA-256 chain, synchronous replay, BTreeMap state
v1.1Parallelised chain verification (Rayon)
v1.2Incremental replay against an existing state root for streaming audits
v2Kaspa 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