Security and privacy
What the cryptography guarantees
- Unlinkability. A spend reveals the nullifier, the public amounts, and a proof whose distribution is independent of which token produced it. Under each scheme's assumptions, the issuer cannot link a spend to its issuance or to any other spend, even with its private key and a full transcript of everything it ever signed.
- Balance soundness. A client cannot spend more than it was issued. Each token spends once (the nullifier set enforces it), the hidden balance is bound by the issuer's signature, and a range proof stops negative balances from wrapping into large ones.
What it cannot guarantee
Token-layer unlinkability does not hide the network layer. This server sees the IP address and timing of every request, like any web server. A client that mines, issues, and spends in one burst from one address has told a story the cryptography never gets a chance to protect.
What this deployment does about it: application logging on token endpoints records method and path only, deliberately omitting bodies and any correlation of request content with client addresses; that linkage is exactly what the scheme exists to prevent. What it cannot do: make the platform's infrastructure logs not exist (see privacy), or stop a hostile operator from logging more. Readers who need network privacy should reach the API over their own choice of VPN, Tor, or mixnet, and put time between issuance and spending.
Two further honest limits. Spend amounts are public, so in a thin crowd a distinctive pattern of amounts can be a fingerprint; the anonymity set is everyone using the same scheme in the same epoch. And on the PQ side, choosing the deferred-return mode is visible from the proof itself and partitions that set further.
Experimental status
Both schemes are experimental and unaudited. The classical scheme implements the Katz-Schlesinger design but has had no independent security review; the post-quantum scheme additionally rests on a novel assumption. The service states its own status in machine-readable form: experimental and unaudited; credits have no monetary value and may be erased at any time.
Epochs and key rotation
Issuer keys can rotate by retiring the epoch (currently e1). A new epoch means a new domain separator, fresh keys, and an empty nullifier set, and it invalidates every outstanding token from the old epoch, permanently. Rotation is the recovery lever for key compromise and the mandatory response to any nullifier-store rollback; because credits here are worthless by design, it can be pulled freely.
Within an epoch, nullifier retention is permanent. A nullifier row must outlive every token that could ever collide with it, which is the lifetime of the epoch's keys. The rows are 32 opaque bytes plus the stored retry record; they contain no identity.
The proof of work
Issuance is metered by CPU time. The design goal is that work is useless before a challenge exists, useless for any other request, and useless twice; an earlier prototype of this service hashed a constant preimage and learned the offline-bulk-grinding lesson so this one does not have to.
- Challenge-bound. The server mints a single-use challenge (16-byte id, 16-byte salt) with a 15-minute TTL. The work hash covers the scheme, the challenge id and salt, a digest of the exact issuance request being paid for, and the miner's nonce, under a versioned BLAKE3 domain key.
- Single-use. The challenge is consumed in the same atomic step that stores the issuance response, so a solution can pay for one issuance exactly once, and an identical retry replays rather than re-mints.
- Difficulty schedule. 18 leading zero bits for a 1-credit grant, plus one bit per doubling of the grant: expected work scales linearly with credits. Issuance is capped at 1024 credits, so the schedule tops out at 28 bits, around 2^28 hashes, seconds on a multicore machine; the 2^18 base is roughly a tenth of a second of one modern core.
The exact preimage layout an independent miner needs is on the API reference.