A relayer pays gas for every record, so it needs to limit how much each person writes. If it
limited by wallet, it would learn which wallet wrote which record. Instead, relayers can require a
blind token per write: the issuer sees your wallet when it hands out tokens, the relayer sees a
token when you spend it, and neither can connect the two. See blind tokens
for how.
In an app: nothing to do
When a relayer requires tokens, createPrivateClient fetches the day’s tokens with the user’s
wallet as soon as the client is created, and spends one per write:
What the user notices:
- One extra signature at connect, asking for tokens (on top of the two key signatures).
- A daily limit. The default is 5 writes per wallet per app per day; operators can raise it.
- The first write right after connecting can wait up to 60 seconds. A token is never spent
until it is 60 seconds old, so fetching and spending never sit next to each other in time.
Keep tokens across sessions
By default tokens live in memory. In a browser, keep them in localStorage so a session can spend
tokens fetched in an earlier one (better privacy, and no wait):
Attestation codes
A per-wallet quota limits wallets, not people, and wallets are free. An issuer can instead require
a one-time attestation code (for example, printed on a receipt or handed out at a counter).
Each code grants one day’s quota:
The issuer learns which wallet used which code; it still never learns which records the tokens paid
for. Operators: see ISSUER_CODES_FILE in the configuration reference.
Errors
Last modified on October 12, 2026