Anonymity Is an Architecture Problem
TL;DR: Every platform claims to respect your privacy. Almost none are built so that respecting it is the only thing they can do. That difference is structural, not editorial — and it shows up in the boring decisions, like how you implement “which friends are online.”
A privacy policy is a promise about what a company will choose not to do with data it holds. An architecture is a statement about what data it never holds in the first place. The first can be revised by a new owner, a subpoena, or a growth target. The second can’t be revised without rewriting the system.
I’ve been working through the architecture of a Nostr relay suite built around that second idea: a community platform where someone arriving over Tor can read, post notes, publish long-form journalism, list something for sale, message a friend, and eventually pay someone — without the platform ever learning who they are, and without the platform being able to learn it later.
Here’s what that actually requires.
Identity is a keypair, and the server never holds it
On Nostr, your identity is a public key. There is no email, no phone number, no ID upload, no recovery question. Authentication proves you control a key — that’s it. It does not prove you’re a human, a citizen, or the person you were last week.
That sounds like a small thing until you notice how much conventional infrastructure exists purely to service the account: password resets, session stores, device fingerprints, “we noticed a login from a new location.” All of it is surveillance apparatus wearing a helpful expression. Delete the account, and the apparatus has nothing to do.
The interesting engineering move is making that a property of the build rather than a promise. In this suite, a test walks the source tree and fails if any shipped binary so much as names key custody or imports the signing curve outside two audited packages. The browser signs; the server receives a finished, signed event and forwards it. Nothing in the system can sign as you, because nothing in the system can hold a key that would let it — and a test breaks if someone changes that.
You cannot leak what you never held. Encoding that as a build failure is how you keep it true after the people who decided it have moved on.
Two front doors that never learn about each other
Tor ingress and clearnet ingress are both first-class, and the rule is that they are never correlated. Not “we don’t join those tables today” — the metadata that would let you join them isn’t stored.
The presence system is the clearest case. Showing which friends are online is the one feature on the whole platform whose entire purpose is to disclose real-time activity, so it’s the honest test of whether the privacy posture survives contact with a product requirement.
The obvious implementation is to derive presence from open WebSocket connections. Free, always accurate, zero storage. It was rejected — because answering “is Alice online?” that way means knowing which connection is Alice’s, and that is exactly the Tor-to-clearnet correlation the platform refuses to perform.
What replaced it is a lease. A session holds a state and an expiry, and expiry is evaluated on read, so a lapsed lease stops being disclosed the instant it lapses, whether or not a cleanup job has run. Renewal overwrites in place, so the store cannot quietly become a login history. No IP address, no Tor circuit, no user agent, no device fingerprint. Your friend learns “Alice is away” and never learns how many devices you have, which one changed, or how it reached the network. “Invisible” is indistinguishable from “offline” in every response, including the errors and the timing — because a state that only appears when a hidden session exists discloses that it exists.
The relay carries envelopes it cannot open
Private messages use gift wrapping: the relay stores and delivers a wrapper whose sender, recipient, and contents it cannot read. Only the envelope is visible to the infrastructure.
This started as a bug fix, which is instructive. The legacy direct-message kind names its recipient in plaintext and was being served to any reader — so the social graph of every conversation the relay held was publicly queryable. Content was encrypted; the relationships were not. Metadata was the leak, as it usually is. Even aggregate counts had to be restricted, because a count can’t withhold rows: let someone total messages tagged with a stranger’s key and you’ve built a conversation-volume oracle.
Paying for things without building a dossier
Anonymous payment is where most privacy-first platforms quietly give up, because billing is where the identity requirement usually enters through the back door.
Three commitments keep it out:
- Non-custodial by default. The platform observes settlement rather than holding funds, and a Nostr event claiming “paid” is never treated as proof — only objective rail state is.
- Financial keys stay separate from social keys. Static receiving addresses are never published by default, because a reused address correlates a pseudonymous key with a permanent public financial graph.
- Credentials that don’t accumulate. Access assertions are scoped to one resource, bound to one audience, valid for minutes, and never written to the event log or any derived view — so paying for premium content doesn’t build a durable record of what you read.
A “payment-graph analyst” is a named adversary in the threat model, sitting alongside scrapers and Sybil farms.
Abuse control without identity
The hard objection to anonymity isn’t philosophical, it’s operational: if identity is free, what stops the spam?
Not identity verification. The term used here is deliberately write trust. Pubkeys are assumed to be cheap, so admission is earned through elapsed participation, bounded proof-of-work, behavioral history, community sponsorship, and optional economic friction:
NEW ──▶ READ_ONLY_PROBATION ──▶ VERIFIED_WRITE_TRUST
└─▶ THROTTLEDA new key gets read access and a probationary write budget. Trust accrues to the key, from what it did — never from who holds it. Paying for a subscription buys no write trust, and being trusted to write buys no premium content. Those systems are kept separate on purpose, because conflating them is how a platform ends up selling reach.
The honest part
Much of this is architecture and not yet code. The relays, projections, and abuse controls are real; federation is partly built; payments, subscriptions, and translation are decided and unwritten. The repository is scrupulous about labeling which is which, and the threat model refuses to call itself implemented while the controls for half its threats don’t exist.
That discipline is part of the argument. Decentralized, anonymous social infrastructure doesn’t fail because the ideas are wrong. It fails because someone shipped the friends-online feature the easy way, and the easy way needed to know which connection was Alice’s.
Privacy that depends on nobody taking a shortcut isn’t privacy. It’s optimism. The alternative is to build systems where the shortcut doesn’t exist — and then write the test that fails when someone adds one.
Comments // nostr
no comments yet.
Reply from your Nostr client Reply on the web No zap needed. Replies appear here a few minutes after they reach the relay.