GRAIL Proofs
Post-quantum signed platform records: what is signed, how to verify, and what it doesn't prove.
What GRAIL Proofs are
GRAIL signs these records so you can verify who issued them and whether their contents have changed. They are GRAIL platform attestations: naming a token creator inside a record does not mean the creator signed it.
Uses the NIST-standard SLH-DSA signature algorithm. Platform records are signed by GRAIL; wallets and asset custody are unchanged.
What is signed, and when
- Launch: after the mint and the finalized creator-fee destination are confirmed. An initial buy is a separate record.
- Acquisition: after a confirmed purchase and ownership check. Ownership is an observation at that chain state.
- Event rules: the published rules snapshot. Event entries: the frozen entry-list commitment and count.
- Award: after the winner and asset are committed. An award is not a delivery.
- Delivery: after the transfer is confirmed. This is NFT delivery, not physical shipping.
Signing happens in the background after the event is recorded. It never delays a launch, a game, a draw or a payout, and a record may show “Record pending” until it is signed.
Format and canonicalization
The payload follows the strict grail-proof/1 schema: amounts and other large integers are decimal strings, addresses keep exact case, timestamps are UTC with milliseconds. Parsers reject duplicate keys, invalid Unicode, unknown fields and nonfinite numbers.
The payload is canonicalized with RFC 8785 (JCS). The signed message is UTF8("GRAIL-PROOF/v1\n") followed by the canonical bytes, signed with SLH-DSA-SHA2-128f in pure mode with an empty context. The envelope adds a base64url signature and a SHA-512 digest of the canonical bytes; the digest is an identifier, not a substitute for the signature.
Algorithm and library
Signing uses @noble/post-quantum 0.5.2 (MIT). Its maintainers state it has not been independently audited and that JavaScript cannot guarantee constant-time execution. GRAIL's integration is not audited, certified or FIPS-validated.
Measured cost on the signing server: signing about 250 ms per record, verification about 25 ms. Signatures are 17,088 bytes.
Verifying a record
Opening a record page checks it in your browser: signature, issuer key, record context (production vs test) and digest are shown separately. Evidence links are shown as links; the signature doesn't prove what's behind them.
Offline: download the proof and the public key, then run tools/grail-proof-verify with --trusted-key. It reads only your files and can't know about later key retirement without fetching the key list again.
Keys, rotation and compromise
GRAIL has one production key and a separate test key. Keys are published at /proofs/spec with SHA-256 fingerprints. Private keys are derived on the server from protected backend secrets and never appear in the site, the database, downloads or logs.
Rotation adds a new key and marks the old one retired; old records still verify. A compromised key is marked compromised and its records show that status.
Historical records
Older records are signed only when persisted evidence supports them, with the real signing time and a historical flag. A record signed after a draw does not prove it existed before it. GRAIL's timestamps are its own assertion, and there is no onchain anchor in this release.
Example (test key)
The admin setup can publish one example record signed with the test key. It is labelled TEST EXAMPLE, describes no launch, purchase, award or delivery, and verifies as “test, not production”.