Proof of fair draws without a blockchain
The draw stays local and deterministic. Rekor only proves that a public commitment existed at a specific time.
- Engineering
- Rust
- Security
- Distributed Systems
Weighted draws eventually raise a reasonable question: can somebody outside CrowdRelay verify that the result wasn't quietly changed afterwards?
My first implementation path pointed toward a smart contract. It would have produced an impressive diagram and several dependencies that had nothing to do with selecting a fair winner: wallets, gas, RPC providers, chain configuration, contract upgrades and operational keys.
What the draw needed
The draw didn't need a blockchain. It needed a durable local proof and an independent public timestamp.
Two layers of proof
CrowdRelay now separates those jobs.
PostgreSQL stores the complete deterministic proof: the eligible candidate snapshot, winner snapshot, revealed seed, algorithm version, receipt hash and Merkle inclusion data. That's the source of truth. The result can be reproduced and checked offline without contacting a public service.
A separate worker builds a metadata-only commitment for a proof batch, signs it and publishes it to the Sigstore Rekor transparency log. Rekor adds an append-only public record, integrated time, log index, Signed Entry Timestamp and Merkle inclusion proof.
What gets published
The public payload is intentionally boring:
proof batch UUIDproof kind and schema versionSHA-256 rootleaf counthash and tree algorithmsFan records, winners, email addresses, ticket data, consent and event payloads stay inside CrowdRelay. Publishing the evidence shouldn't mean publishing the people it's about.
Keeping Rekor off the critical path
Rekor is also outside the critical path. A draw completes when the local transaction and deterministic proof complete. If the transparency log is unavailable, the draw doesn't wait, roll back or pretend that no winner exists. The external proof batch stays pending and a bounded worker retries it later.
Handling lost responses
That worker needed careful failure semantics. A successful upload followed by a lost response must not create a second public entry on the next attempt. The submission is deterministic, the worker keeps a crash-safe pending-confirmation journal, and a Rekor conflict can be resolved through the existing entry location instead of logging another copy.
Running it in production
Running the anchor as a production component exposed details the architecture diagram had left out. The proof uploader now lives in an isolated container. The canonical receipt hash is treated as a contract between the main system and the anchor. Pending journal entries are reconciled after restarts, and proof claims are authenticated instead of trusting a convenient local call. The isolation is intentional: proof anchoring can become unavailable without taking ticketing, mail delivery or the draw itself down with it.
What each layer proves
The two layers answer different questions:
local proof: explains and reproduces the draw
public proof: shows that one exact commitment existed by a public timeRekor doesn't decide the winner. It doesn't know the candidate list, and it can't prove that an application rule was ethically perfect. It proves that the signed commitment returned during verification is bound to an append-only public log entry.
Verification
Verification follows the same split. A local receipt can be checked offline. The Rekor binding, signature and Merkle path can also be checked from the stored public proof, and an optional online check confirms that the same body is currently present in the public log.
Why not on-chain
Putting every draw on-chain would look more impressive. This setup is cheaper, easier to operate and closer to the actual threat model. PostgreSQL stays authoritative, private data stays in CrowdRelay, and the external system adds one property we can't create alone: independent public evidence that a commitment existed.
No wallet appears in the fan journey. Ticketing, admission, mail delivery and consent don't depend on Rekor. The feature can be disabled without changing any business flow.
Fairness still comes from explicit rules, frozen inputs and deterministic receipts. The transparency log makes it harder to rewrite a result later without anyone noticing, and that was the one thing I wanted from a public ledger.