Rate Limits Without Identity

A good credential system lets a service say no without first learning who you are.

The web has a simple abuse problem and a complicated memory problem.

The simple problem is scarcity. A service has finite attention, finite compute, finite trust, and finite patience for adversaries. It needs to distinguish one honest request from a flood. It needs to say “not again” to the same actor without forcing everyone else to carry a passport through the network.

The memory problem is that the obvious implementation of scarcity is identity. Create an account. Bind requests to it. Store the history. Build a reputation system. Add device fingerprints when the account is not enough. Add phone numbers when the fingerprints are not enough. Soon the abuse system has become a surveillance system with an abuse dashboard attached.

Anonymous tokens are an attempt to keep the scarcity and remove the biography.

The flood

A service has finite attention, finite compute, and finite patience for adversaries. From where it stands, an honest request and a hostile one look the same. It needs a way to say not again to one actor without stopping everyone else at the door.

The tempting fix

The obvious implementation of scarcity is identity. Create an account, bind every request to it, and store the history. It works, and it keeps working until the abuse system has become a surveillance system with an abuse dashboard attached.

Blind issuance

Anonymous tokens split the job in two. The client proves eligibility to an issuer, and the issuer signs blinded tokens it will never recognize again. The account stays behind, and only sealed tickets travel forward.

Spend once

Each token is single-use. Redemption reveals a spent nonce, so the verifier can reject a replayed token on sight. It learns that some issuer authorized some token, and nothing about which issuance event produced it.

The budget

Rate limiting becomes a budget rather than a name. The service still enforces its quota and still observes aggregate load. What it gives up is the default join between issuance and redemption, and that missing arrow is the product.

Anonymous token lifecycle visualizationscarcity without biographyservicefinite patienceid:aliceid:bobid:carolaccounteligible: yesstays behindissuerblind signverifierspent: 8f3aTODAY'S BUDGETissuanceredemption

The basic shape

An anonymous token protocol has three acts.

First, the client proves eligibility to an issuer. The issuer might know the client at this moment: an account is in good standing, a device passed a check, or a previous interaction created a limited budget.

Second, the issuer signs a blinded token. In the simplest mental model, the client chooses a random message mm, blinds it into B(m)B(m), and asks the issuer to sign the blind value. The issuer returns a signature that the client can unblind into σ\sigma.

Third, the client redeems (m,σ)(m, \sigma) with a verifier. The verifier checks:

Verify(pk,m,σ)=1.\mathsf{Verify}(pk, m, \sigma) = 1.

The verifier learns that some issuer authorized some token. It does not learn which issuance event produced this token, assuming the protocol and deployment are doing their jobs.

That “some” is the whole point.

Scarcity as a budget

Rate limiting can be expressed as a budget rather than an identity.

Instead of saying “Alice may send 100 messages today,” the system can say “this eligible client may receive 100 unlinkable message tokens today.” Each token is single-use. Redemption reveals a spent nonce, so the verifier can reject replays:

nonce(m)Spentreject.\mathsf{nonce}(m) \in \mathsf{Spent} \Rightarrow \mathsf{reject}.

This gives the service a hard edge. It can enforce a quota. It can prevent the same token from being used twice. It can observe aggregate load. What it should not get for free is a stable trail from every redemption back to the person.

The difference is subtle in code and enormous in social meaning.

Metadata is the real interface

Most failures in privacy systems are not failures of the primitive. They are failures of the envelope around the primitive.

The signature might be unlinkable, while the HTTP headers are not. The token might hide the account, while the redemption endpoint receives a stable device identifier. The issuer and verifier might be separate in the diagram, while their logs meet later in a warehouse.

So the central design question becomes: what metadata is carried, and who can join it?

Useful metadata is often coarse:

  • a public key identifying the issuer;
  • a token type identifying the budget;
  • an epoch such as “2026-04-27”;
  • a domain separator binding the token to one use case;
  • a spent nonce used only to stop replay.

Dangerous metadata is often stable, precise, and cross-context:

  • account identifiers;
  • device identifiers;
  • exact issuance timestamps;
  • network addresses retained with redemption events;
  • token types so narrow that they identify a small group.

The primitive gives you unlinkability in a mathematical model. The deployment must avoid reintroducing linkability through the side door.

Hidden metadata

Sometimes a verifier needs to know a small fact about a token without learning the whole story. For example, a service might need to enforce different budgets for different risk classes, regions, or products. The naive design places that metadata in the clear. The better design asks whether the metadata can remain hidden from some parties while still being bound to the signature.

This is where anonymous tokens with hidden metadata become interesting. The issuer can embed structured facts into the credential, and the protocol can control which facts are revealed at redemption. The goal is not to make all information disappear. The goal is to make each fact travel only as far as it must.

Good cryptographic engineering is often a theory of distance.

What the service still gets

Privacy-preserving does not mean helpless.

A verifier can still reject invalid signatures. It can still reject replays. It can still count redemptions by token type and epoch. It can still rotate keys, expire budgets, and change issuance policy. An issuer can still decide who is eligible. The system can still have abuse response.

What changes is the default join:

issuance  ↮  redemption.\mathsf{issuance} \;\not\leftrightarrow\; \mathsf{redemption}.

That missing arrow is not an inconvenience. It is the product.

The standard worth wanting

The web needs primitives that let ordinary services do the responsible thing without becoming identity brokers. That means protocols with boring deployment stories: clear token formats, crisp validation rules, predictable key rotation, and failure modes that do not quietly degrade into tracking.

The best version of this technology will feel almost invisible. A request will arrive with evidence that it is allowed. The service will answer. The system will remember the budget it had to remember, and nothing more.

That is what dignity looks like when it becomes infrastructure.