Deterministic IPFS as a Browser-Compatible Discovery Layer
No hardcoded endpoints. No DNS. Just math and public gateways.
- The Hidden Centralization in Web3: DNS as a Single Point of Failure
- The Browser Is the Bottleneck: Why dApps Can’t Escape HTTP
- Deterministic IPFS as a Browser-Compatible Discovery Layer — you are here
In part one I argued that DNS is Web3's real single point of failure — that seizing a domain or dropping a CDN can kill a "decentralized" app even while the chain hums along. In part two I showed why the browser makes this hard to fix: browser tabs can only speak HTTP and WebSocket, libp2p doesn't escape that constraint, and the industry's answer — hardcoded RPC endpoints through Infura or Alchemy — just moves the fragility from one company to another.
This post is the solution. Not a theoretical one. This is what VoiceBan actually does.
The core problem, restated
A browser-based dApp needs to connect to a backend. That backend has a network address. Today, that address is either:
- Hardcoded as a URL (
wss://rpc.voiceban.com) — fragile, censorable, DNS-dependent. - Looked up via a naming system like ENS — which still resolves through a DNS-dependent gateway.
Both approaches store the address somewhere static. Static means enumerable. Enumerable means blockable.
What if there was no static address? What if both the frontend and the backend could independently arrive at the same answer to "where should we meet right now?" without ever asking a centralized directory?
That's the idea. The address is computed, not looked up.
IPFS and IPNS: a quick primer
If you already know how IPFS content addressing works, skip ahead. If not, here's the short version.
IPFS (InterPlanetary File System) is a content-addressed network. When you add a file to IPFS, it gets a hash — a CID (Content Identifier) — derived from the file's contents. Anyone who knows the CID can retrieve the file from any IPFS node that has it. The CID is the address, and it's determined by the content, not by who hosts it or where.
You can access IPFS content from a regular browser through public gateways — HTTP endpoints that fetch IPFS content on your behalf. https://ipfs.io/ipfs/<CID>, https://dweb.link/ipfs/<CID>, https://cloudflare-ipfs.com/ipfs/<CID>. These are normal HTTPS URLs. The browser can fetch them with a standard fetch() call. No extensions, no special protocols.
IPNS (InterPlanetary Name System) adds a mutable pointer on top of IPFS. An IPNS name is tied to a cryptographic keypair. The owner of the private key can update which CID the name points to. Anyone with the public key (the IPNS name) can resolve it to the current CID.
Think of IPNS like DNS but without registrars, without ICANN, and without any central authority. The "domain" is a public key. Ownership is proven by signing, not by a database entry at a registrar. And it resolves through the same public gateways that serve IPFS content.
The trick: deterministic keypairs
Here's where it gets interesting.
A normal IPNS setup works like this: you generate a keypair, publish your content under the IPNS name, and share the name with whoever needs it. The name is static — it doesn't change unless you generate a new keypair.
VoiceBan does something different. The keypairs aren't random. They're derived deterministically from a seed that includes the current time.
Both the frontend and the backend know the derivation function. Both know the seed material. So at any given moment, both sides can independently compute the same keypair — and therefore the same IPNS name — without communicating.
The backend publishes its connection information under this time-derived IPNS name. The frontend computes the same name and resolves it through a public IPFS gateway. The connection information comes back. The frontend connects.
No hardcoded endpoint. No DNS lookup. No directory service. Just a shared algorithm and the clock.
How the rotation works
The keypair rotates on a fixed interval. Every rotation window, a new keypair is derived, and the backend publishes fresh connection metadata under the new IPNS name.
From the frontend's perspective:
- Compute the current IPNS name from the derivation function and the current time window.
- Hit multiple IPFS gateways in parallel:
https://ipfs.io/ipns/<name>,https://dweb.link/ipns/<name>,https://cloudflare-ipfs.com/ipns/<name>, and so on. - Whichever gateway responds first, use that response.
- Parse the connection metadata from the IPNS record.
- Connect to the backend over WebSocket.
If one gateway is down, the others still respond. If a gateway is blocked in a particular country, the frontend tries the rest. The frontend doesn't care which gateway works — it just needs one.
And because the IPNS name changes with each rotation window, there's no stable identifier for a censor to target. By the time someone identifies and blocks a particular IPNS name, the system has already moved to the next one.
Why this works within browser constraints
This is the part that ties back to article two.
The browser can only make HTTP requests. Fine. IPFS gateways speak HTTP. The frontend makes standard fetch() calls to public gateways — no raw TCP, no custom protocols, no browser extensions. It works in Chrome, Firefox, Safari, on desktop and mobile. No user configuration.
The browser can't run a libp2p node. Fine. It doesn't need to. The gateways do the IPFS resolution on the backend side. The browser just sees an HTTPS response with the data it needs.
The browser needs a server address to connect to. Fine. The IPNS resolution gives it one — but the address wasn't looked up from a central directory. It was computed by both sides independently.
The browser's constraints aren't violated. They're satisfied in a way that doesn't create a new central dependency.
What the attacker sees
Walk through the threat model from a censor's perspective.
Can they block the domain? There is no domain. voiceban.com is a convenience entry point, but the app is pinned to IPFS by content hash and works as a PWA after the first load. Seizing the domain doesn't stop existing users and doesn't stop new users who find the CID through other channels.
Can they block the IPFS gateways? They can try. There are dozens of public gateways, and more can be added. The frontend tries several in parallel. Blocking all of them means blocking IPFS itself — which is a much larger target than one dApp, and affects every project on the network.
Can they block the IPNS name? They'd have to figure out the derivation function to predict the next name, and even then they'd need to update their block list on the same rotation schedule. Reactive blocking doesn't work when the name has already changed by the time the block is deployed.
Can they block the backend WebSocket endpoint? They can block a specific IP, but the frontend does not depend on any one of them: it rediscovers whatever backends are reachable at the next IPNS resolution, so blocking one address moves traffic rather than stopping it.
There's no single target. No domain to seize, no API key to revoke, no company to serve a court order to. The censor has to play whack-a-mole against math and a clock.
Why this matters beyond VoiceBan
The pattern — deterministic naming on a content-addressed network, resolved through commodity HTTP gateways — isn't specific to us. Any dApp that wants to decouple its frontend from DNS and its backend from hardcoded RPC providers can use the same approach.
The ingredients are all public:
- IPFS gateways are commodity infrastructure. Dozens exist. Anyone can run one.
- IPNS is a standard protocol. The keypair derivation is just math.
- The frontend is a static asset that can be pinned anywhere.
- WebSocket is a browser primitive.
We didn't invent new infrastructure. We composed existing pieces in a way that removes the single points of failure I described in the first article. The chain does what the chain does. The browser does what the browser does. And the path between them no longer runs through one company's data center.
That's the last mile, covered.
What's next
I mentioned hitting multiple gateways in parallel, but I glossed over why that matters and how it actually works. Picking the wrong strategy here — sequential fallback vs parallel racing, how many gateways, which ones — is the difference between a 200ms discovery and a 20-second spinner. The next post digs into the resilience engineering behind multi-gateway resolution.
References
- IPFS content addressing — files are identified by their cryptographic hash (CID), not by location; any node hosting the content can serve it: IPFS documentation
- IPNS mutable names — keypair-based mutable pointers that resolve through the same DHT and gateway infrastructure as IPFS content: IPNS documentation
- Public IPFS gateways — HTTP endpoints that resolve IPFS/IPNS content for clients that can't run a full node; dozens are operated independently: Public gateway checker
- Browser network sandbox — the W3C/WHATWG security model restricts webpage network access to HTTP, WebSocket, and WebRTC: Fetch Living Standard · WebSocket API — MDN
- IPNS over HTTP gateways — IPNS names can be resolved through any public gateway via
https://<gateway>/ipns/<name>, requiring only a standard HTTPS request: Gateway specification