Skip to main content
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