Hero Sample — TamperProof
Derived page. The behaviour described here is specified by the
tamper-evident-streamscapability underopenspec/specs/. That specification is the source; this page explains and illustrates it. Where the two disagree, the specification is right and this page is a bug.
Concept: Hash-chained event streams catch direct-DB tampering at the next verification pass. The full why lives in the Tamper-Evident Streams concept page; this is the runnable proof.
- Code:
samples/Stratara.Sample.TamperProof - Lines: ~320
- Read time: 5–10 min
- Dependencies: none — pure in-memory, no database, no DI container.
What you'll see
HashChainedEventStore— everyAppendcomputesSHA-256(previousHash || canonical-json(payload))and stores both the entry's own hash and a pointer to the previous entry's hash.- Three events appended —
AccountOpened,AmountDeposited,AmountWithdrawn. The console prints each entry with a six-byte hash preview so the chain is visually inspectable. ChainVerifier.Verify— the clean run passes. Every stored hash matches a fresh re-hash; every previous-hash pointer matches the prior entry's hash.TamperWithPayloadForDemo— simulates an attacker editing entry #2 directly in the database, rewriting$50to$5000. The stored hash is untouched.ChainVerifier.Verifyagain — raisesEventStreamCorruptedExceptionat sequence #2 with a precise reason: "stored hash does not match a fresh re-hash of the payload (payload was modified after commit)".
Running
dotnet run --project samples/Stratara.Sample.TamperProof
Expected output (abridged):
=== Stratara TamperProof ===
--- Append three events ---
#1 AccountOpened hash=9d5ad374cb03…
#2 AmountDeposited hash=d97eaadead5a…
#3 AmountWithdrawn hash=0ec278df5cf4…
--- Verify the chain (clean) ---
OK — every entry's stored hash matches a fresh re-hash of its payload.
OK — every entry's previous-hash pointer matches the prior entry's hash.
--- Tamper: rewrite entry #2's deposit from $50 to $5000 ---
--- Verify the chain (tampered, stale hash) ---
CAUGHT: Event stream tampering detected at sequence #2: stored hash does not match a fresh re-hash of the payload (payload was modified after commit)
The hashes differ on every run. The run goes on past this point: the clean chain is anchored to an external source of truth first, then a determined insider recomputes every hash after the tamper so the chain verifies clean again, and only the check against the external anchor catches it.
How this maps to the real Stratara
| Sample | Real Stratara |
|---|---|
HashChainedEventStore (in-memory list) |
Hash columns on event_stream_entry populated by EF Core |
HashChainedEventStore.Append computing the hash inline |
The EventStreamHashing worker (AddEventStreamHashWorkerServices()) — a background service that hashes each newly-committed event a beat behind the write, in batches, so appends stay cheap |
ChainVerifier.Verify (on-demand) |
No framework equivalent — this stays your job. Stratara writes the chain and the anchors; recomputing them to look for a break is a deliberate pass you schedule (an audit job, or the external-anchor check). Nothing in the framework scans for tampering on its own |
TamperWithPayloadForDemo |
No equivalent — production code has no public mutation path on persisted entries |
Sister hero sample
- Hero Sample — Encryption — the confidentiality counterpart. Same goal of integrity-by-construction, applied at the field level instead of the stream level.