Motivation
Today a Ghost Key is anonymous at issuance (blind signing) but pseudonymous in use: reusing one key across messages or apps makes those uses cryptographically linkable to the same holder. See freenet/web#27 for the disclosure side.
One-time-use keys — a fresh Ghost Key per signed action — are the cleanest mitigation available short of ZK, and they avoid the substantial redesign a ZK-over-certificate approach would need.
Problem with naive per-transaction issuance
If each action required its own donation, the UX would be terrible and the project would look like a money grab even though per-action keys are genuinely the right privacy unit for many use cases.
Proposal
Decouple donation size from key count. A single donation issues a bundle of N blinded Ghost Keys in one pass. The user pays once for a supply and spends them locally, one per action, at their own pace. The donation server still never sees any key (blind signing is per-key), and bundle size is purely a policy / UX knob — it does not change the anonymity property at issuance.
Rough shape:
- `ghost-key request --bundle 100` blinds 100 candidate keys, submits to the donation server, unblinds 100 signed certificates.
- Local storage holds the bundle; the delegate / CLI draws one when the user takes a signable action.
- The user refills when the bundle runs low. Replenishment is just another donation, so the economic model is unchanged — the project gets paid for a pack, not per stamp.
When not to use one-time-use keys
Per-action unlinkability is correct for votes, donations, ephemeral posts, anonymous feedback. It is wrong for apps where continuity is the feature — River room membership, long-lived reputation, anything where "this is the same person as yesterday" is load-bearing. For those, per-persona (one stable key, scoped to one context) is the right privacy unit.
The guidance, and what the CLI/delegate should support, is: match key lifetime to the privacy unit you actually want. Both modes need to be first-class.
Open questions
- What's a reasonable default bundle size? Tradeoff between UX friction (too few → constant refills) and donation-server workload / storage (too many per donation).
- Should bundles be tagged with a soft expiration to prevent long-term key hoarding breaking future format migrations?
- How does the delegate pick which key to spend — random, oldest-first, tagged-by-context? Random is safest against any ordering side-channel.
- How does a holder back up a bundle without creating a correlation oracle (e.g. cloud backup that sees all their keys)?
- Does the CLI need a "give me another" primitive that blinds + submits + unblinds in one shot, so apps can top up transparently?
Non-goals
- ZK proof of certificate validity (separate, larger work item; tracked as future direction).
- Changing the blind-signing protocol itself.
- Making per-action the default for apps where continuity is correct.
Next step
Sketch the bundle-issuance CLI flow and delegate storage schema; confirm the donation server can sign N keys in one request without breaking its existing rate/abuse controls. Not urgent; driven by freenet/web#27 and follow-up demand from privacy-sensitive app authors.
[AI-assisted - Claude]
Motivation
Today a Ghost Key is anonymous at issuance (blind signing) but pseudonymous in use: reusing one key across messages or apps makes those uses cryptographically linkable to the same holder. See freenet/web#27 for the disclosure side.
One-time-use keys — a fresh Ghost Key per signed action — are the cleanest mitigation available short of ZK, and they avoid the substantial redesign a ZK-over-certificate approach would need.
Problem with naive per-transaction issuance
If each action required its own donation, the UX would be terrible and the project would look like a money grab even though per-action keys are genuinely the right privacy unit for many use cases.
Proposal
Decouple donation size from key count. A single donation issues a bundle of N blinded Ghost Keys in one pass. The user pays once for a supply and spends them locally, one per action, at their own pace. The donation server still never sees any key (blind signing is per-key), and bundle size is purely a policy / UX knob — it does not change the anonymity property at issuance.
Rough shape:
When not to use one-time-use keys
Per-action unlinkability is correct for votes, donations, ephemeral posts, anonymous feedback. It is wrong for apps where continuity is the feature — River room membership, long-lived reputation, anything where "this is the same person as yesterday" is load-bearing. For those, per-persona (one stable key, scoped to one context) is the right privacy unit.
The guidance, and what the CLI/delegate should support, is: match key lifetime to the privacy unit you actually want. Both modes need to be first-class.
Open questions
Non-goals
Next step
Sketch the bundle-issuance CLI flow and delegate storage schema; confirm the donation server can sign N keys in one request without breaking its existing rate/abuse controls. Not urgent; driven by freenet/web#27 and follow-up demand from privacy-sensitive app authors.
[AI-assisted - Claude]