Developers Katta · Devcon 8 · Day 1: censorship resistance
What survives when the host disappears
Rebuilding a community archive on Swarm, one threat at a time
Alok Nerurkar · Swarm
This deck is being served by my laptop's Swarm node right now: the address bar shows 127.0.0.1:1633/bzz/<upload>/, not a hosting company's domain.
Plan for the next 40 minutes: hosting gets censored, Swarm's mental model, a live demo of this very deck, Swarm as a serverless backend, updates without a server, keeping it alive, a lightning round, then what's possible. Then Q&A.
The afternoon build (starter repo, GUIDE, cheat sheet) is separate, self-serve material — the QR codes for it are on the last slide.
Hook
Where websites live today
your browser
DNS (registrar) CDN edge Origin
VPS, or web / static hosting
each red box is one company that can take the page down
A name needs a registrar; the page needs an origin (a VPS you run, or a managed web/static host); most sites sit behind a CDN in front of that origin.
This is the stack under almost every website, including most "decentralised" dApp frontends: a registrar for the name, a CDN caching and fronting traffic, and an origin that is either a VPS you administer or a managed hosting platform. Four boxes, usually four different companies.
The chain only needs one weak link. Next slide: what each one looks like when it breaks.
Hook
How each one gets censored
Layer Lever Who Users see
VPS terminate account host provider "account suspended"
CDN drop origin at the edge CDN operator TLS error / blocked page
Web / static host take project down the platform 404, "not available"
DNS suspend / seize domain registrar or registry NXDOMAIN
"Service continuity is in the hands of the organisation and is only weakly incentivised by reputation."
Book of Swarm §1.2.2 (lack of service continuity guarantees, single attack target, corruption, censorship)
None of these need a dramatic incident: an abuse complaint, a billing dispute, or a policy change is enough. A "decentralised" dApp's frontend sits on one of the previous slide's four layers, so the lever reaches it the same way it reaches any other site.
The Book of Swarm's failure modes for centrally-held data apply directly: a single operator is a single point of failure, a single attack target, and a target for pressure. Today is about the first two; Day 3 (privacy) picks up the surveillance thread.
Hook
Anatomy of a "decentralised" dApp
Smart contract ✓
Frontend host Domain / DNS RPC provider API / indexer
each red box is one company that can switch it off
The contract can't be stopped. Everything users touch can.
The same four layers, now inside a dApp specifically: the chain part holds; the parts people actually load (the page, its name, the RPC and the API) each have one owner, same as any other website.
Today's goal: make the user-facing parts as hard to remove as the contract.
Where Swarm comes from
The Book of Swarm
2015: the storage of the "world computer" trinity: Ethereum, Whisper, Swarm
Vitalik coined the protocol tag bzz
Mainnet since June 2021 (Bee 1.0)
the book (free PDF)
Book of Swarm, Acknowledgments ("Conception and influences"), §1.3.5
Joke cue: "How does Swarm work? Good news: there's already a book on it." 292 pages, Viktor Trón, 2024 edition, free to read (CC BY-NC-SA 3.0). The QR opens the PDF on ethswarm.org.
History, from the book itself: the concept of Swarm as one pillar of a "holy trinity" for the decentralised web appeared in early 2015, before Ethereum launched; Ethereum computes, Whisper carries messages, Swarm stores. Ethereum's founders (Vitalik Buterin, Gavin Wood, Jeffrey Wilcke) started the bee jokes, and Vitalik coined the protocol tags bzz and shh. The foundations were laid over 2015, and the first implementation was Go Ethereum-based.
Bee 1.0 shipped with the mainnet launch in June 2021. The workshop nodes run Bee 2.8.3. Hub rule: don't name tokens.
Every "§" on the following slides points into this book.
Mental model
Swarm in one picture
your file 4 KB chunks address = nearest
split and hashed hash of the content neighbourhood stores it
paid by a postage batch
no origin server · any node can fetch any chunk · the address checks the content
Book of Swarm §2.2 (chunks and storage), §3.3 (postage), §3.4 (redistribution)
Content is split into 4 KB chunks. Each chunk's address is the hash of its content, and the nodes whose addresses are closest to it store it. There's no "the server": any node can find and fetch any chunk.
Pushback, "who stores it, and why?": nodes in that neighbourhood are paid through the redistribution game (§3.4) for proving they keep the data. Storage is paid up front with a postage batch. Hub rule: don't name tokens.
Mental model
Chunks, addresses, neighbourhoods, postage
A file splits into 4 KB chunks
Content-addressed chunk (CAC): address = hash of the chunk's own bytes
Single-owner chunk (SOC): address = hash of (owner, identifier) — one keypair's slice of the address space; feeds are built from these (next slide)
Neighbourhood: the nodes whose own address shares the most prefix bits with a chunk's address store it
Postage: a stamp on every chunk, paid for by a batch, tells the neighbourhood the write is funded
Book of Swarm §2.2.2 (CAC), §2.2.3 (SOC), §2.1.2-2.1.3 (overlay addressing, Kademlia), §3.3.1 (postage)
Two kinds of address, same storage layer: content decides the address for a CAC, so it can't be swapped without changing the address; an owner's key decides the address space for a SOC, so the same owner keeps writing to predictable slots — that's how feeds work later today.
"Closest" is XOR distance between addresses, not numeric or lexicographic order: more shared prefix bits means closer. Nodes self-organise into Kademlia neighbourhoods around that metric.
Mental model · data structures
Data and metadata are both chunks
Structure What it holds
File 4 KB data chunks; intermediate chunks hold up to 128 child addresses; one root reference for the file
Manifest paths → references + metadata (Content-Type, Filename, index doc); a chunk trie (Mantaray) — a website
Feed (owner, topic) → latest reference; a signed SOC per update, addressed by owner, topic and index
Feed manifest metadata names a feed (owner and topic); /bzz resolves the latest version — the deck's own stable URL
Book of Swarm §4.1.1, §4.1.2, §4.3, §4.3.1
Content, structure, metadata and history are all chunks. A node only stores and serves chunks; the application lives in Swarm.
Ties back to the SOC on the previous slide: a feed is built from SOCs. One storage layer, four shapes: a file is a chunk tree, a manifest is a trie of chunks, a feed is a signed pointer, a feed manifest is a manifest whose metadata names a feed.
Pushback, "so where's the database?": the archive's index is a JSON document stored as bytes, and a feed points at its latest version. Queries run in the client — Swarm is not a query engine (honest limit).
Pushback, "is the metadata public?": yes, chunks are public by default unless encrypted or access-controlled ("Share with some, not all", coming up).
Mental model · this deck
This deck, all chunks
THIS DECK
A RE-UPLOAD
Feed manifest
Latest feed update (a SOC)
Deck manifest
File chunk trees
New deck manifest
New feed update
4 KB chunks: every box above is made of them
owner + topic katta-deck
/ → index.html, /img/…, /qr/… (Content-Type)
feed manifest (katta-deck) stays → URL stays
The reader walks these boxes top to bottom: the feed manifest resolves to the latest feed update, which points at the current deck manifest, and the manifest's paths resolve down into file chunk trees and 4 KB chunks. Nothing in that stack is a server — every arrow is "look up a chunk," never "call an API."
A re-upload (right column) only changes two boxes: a new deck manifest plus one new feed update. The feed manifest itself — the thing the URL names — never moves. That's why this deck's QR stays good across every re-upload this week.
Say: every box here is one or more chunks — the same chunks the live demo serves after my node stops.
Live demo · threat: host takedown
You're looking at Swarm
this deck: uploaded from my laptop node, pinned there
the same deck through a public gateway:
my node stops, and the gateway still serves it
On stage: point at the address bar (127.0.0.1:1633/bzz/<upload>/): this deck is loading from my laptop's node, not a hosting company. That address is the hash of this exact deck, pinned on my node. Open a new tab at the deck's public URL ( ): an ENS name, resolved by Swarm's public gateway to this deck's feed and then to its latest upload. Press Ctrl-C in the node's terminal. Reload the gateway tab: it still serves everything. Press s there to get the notes window back, and carry on from the gateway tab.
Honest note: the gateway may already hold a cached copy of this deck from an earlier visit. The claim holds either way, because push-sync stored the chunks in their neighbourhood before the upload returned ("Stored before the upload returns", two slides on).
The QR on the last slide is that same public URL, which always points at the latest upload. Anyone in the room can scan it and load this right now. If the venue network doesn't cooperate, stay on the local tab: the deck is pinned on my node and keeps working offline.
Live demo · if the web path is blocked
Blocked? Read it yourself
Swarm Desktop: starts in ultra-light mode; any Bee node reads by address
Freedom Browser: its own Swarm and IPFS nodes (bzz:// addresses, .eth names, no gateway)
Nook (independent, beta)
ethswarm.org/access
Freedom Browser
ethswarm.org/access · github.com/solardev-xyz/freedom-browser
If a gateway or a network blocks the web path, the content is still reachable without it: run a client that talks Swarm directly. ethswarm.org/access lists all three official options: Swarm Desktop, Nook and Freedom Browser.
A real example: Swarm's own public gateway serves a website by its bare hash only after the hash is approved (unapproved ones go to an approval page), while sites with an ENS name always open through bzz.link. A gateway is someone else's server with its own rules. Your own node, or Freedom Browser, has no such gate.
Honest note: an unfunded (ultra-light) node reads through a small allowance that refreshes over time — fine for a deck, slow for large files. A funded light node pays its way instead of relying on that allowance.
Serverless backend · push-sync
Stored before the upload returns
With a direct upload , my node pushes every chunk to the network synchronously: each one goes to its neighbourhood, and the upload only finishes when the storers confirm they hold it
As soon as the upload finishes, my node can be switched off: all the content is already in Swarm
That's how this deck was uploaded
Book of Swarm §2.3.2 (push syncing)
Each chunk is forwarded toward its neighbourhood, and a storer there sends back a signed receipt. A direct upload returns only after every chunk has its receipt.
Direct upload is an option you choose when uploading. A plain upload is deferred by default: it returns once the chunks are stored on your own node, which then pushes them in the background. Switching off right after a deferred upload can leave chunks behind.
Trade-off: a direct upload takes a little longer, because it waits for the receipts instead of returning as soon as the chunks are stored locally.
Serverless backend · built-in CDN
Relaying nodes cache what they forward
A node that forwards a retrieval request keeps a copy of the chunk it relayed
On by default: every hop between a reader and the storer is a potential cache
Book of Swarm §2.3.1 ("Opportunistic caching")
A node that relays a chunk back toward a reader keeps a copy, in case someone asks for it again. The book calls this opportunistic caching: "crucial in ensuring that Swarm scales the storage and distribution of popular content automatically".
This is separate from the postage-paid copy in the content's neighbourhood: caching is what makes a popular read fast and spread out; the neighbourhood copy is what makes it durable.
Serverless backend · auto-scaling
Goes viral → more caches → it scales itself
Every reader who pulls content through a relay leaves a copy behind it
A popular file ends up cached on more and more nodes, closer to more and more readers, with nobody provisioning anything
Honest note: a node's cache is bounded (default 1,000,000 chunks, about 4 GB) and evicts the oldest entries. The cache makes it fast; the postage batch paying the neighbourhood is what guarantees it's still there.
Book of Swarm §3.1.2 ("Caching and auto-scaling")
Pushback, "caching isn't storage": correct. Relay caches make popular content fast and spread it out, but they're bounded and evict. The batch pays storers in the content's neighbourhood to keep it — that's "keep it alive", later.
Serverless backend · summary
Swarm is a serverless backend
Storage : postage-paid chunks, held by the content's neighbourhood
Delivery : any node, a relay cache, or a public gateway — all serving the same address
Updates : a feed moves, not a deployment (next)
No server to patch, no CDN contract, no admin login
Pushback, "you still need a gateway": reads work through any node, a public gateway, or your own node, and the reader verifies hashes. Writes go through a node; this afternoon's L4 level shows a browser can stamp its own chunks and post them to any node.
Updates · threat: needing a mutable server
A CMS needs a server, a database and an admin login. A feed needs a key.
A content platform: an always-on server, a database, an admin login — and an operator who can suspend any of them
A Swarm feed: (owner, topic) → latest reference, signed by one key
Only the key holder can move it. Anyone can read it.
Book of Swarm §4.3 (feeds: mutability in an immutable store)
The feed is the mutable pointer; everything it points to is still immutable, content-addressed data. Updating never means redeploying a server — it means moving where a key's pointer points.
This deck is the example: it sits behind a feed (topic katta-deck) and has been re-uploaded many times this week — the URL on the last slide never changed.
Keep it alive · threat: the funder stops paying
Keep it alive
a postage batch pays for storage; anyone can extend it
Pin : another node keeps its own copy
Steward : re-upload with your own batch. Same address.
Erasure coding : survive missing chunks
No permission needed: anyone can top up any batch before it expires.
Book of Swarm §3.3, §5.1, §5.2
This is the answer to "the institution stops paying its bills". On the web, the bill-payer is the only one who can keep the site up. Here, extending storage is permissionless: the postage contract lets anyone top up any batch. Any community member can pay, or re-upload the content under their own batch, and the address doesn't change.
Two caveats. The feed's update chunk stays on the keeper's batch, so keeping the mutable pointer alive still needs a topUp of that batch. And both a top-up and a re-upload only work before the batch expires.
Pushback, "it disappears when the batch expires": persistence is paid for, but paying is permissionless: anyone can top up a batch, and a re-upload works with anyone's batch.
Lightning round · 1/4
Operators can't know what they store
Nodes don't choose what lands on them, so they need plausible deniability
Chunk encryption: an encrypted chunk looks like random data
Book of Swarm §2.2.1 (requirement), §2.2.4 (encryption)
The book makes this a design requirement: "nodes do not have a say in what they store", so they must be able to deny knowing the contents. That's legal protection for the people running the network, which is itself censorship resistance.
Limit: only content uploaded with encryption on (encrypt: true). Our archive is public, so we don't encrypt it.
Lightning round · 2/4
Storers can't tell who asked
Requests are forwarded hop by hop; answers travel back the same way ("backwarding")
The node storing a chunk sees only the peer that forwarded the request, so it can't tell whether that peer asked or was passing it on
Book of Swarm §2.3.1, Fig 2.14
The book: retrieval discloses "no requestor identification in any form", which "enables censorship-resistant access". In Bee, the request carries only the chunk address, and a storer can't tell whether it came from its origin or was forwarded.
Pushback, "retrieval is anonymous, so I'm anonymous": forwarding Kademlia hides you from storers, not from the gateway you connect to. Run your own node for that. This ties into Day 3 (privacy).
Lightning round · 3/4
Share with some, not all
Access control (ACT): grantee lists over encrypted content
For example, recordings a community shares only with its members
Book of Swarm §4.2 · Bee API /grantee
Some archives hold material that belongs to a community and isn't for everyone: ritual songs, family recordings. ACT lets a community grant and revoke access to such content without a central server: Bee uses the publisher node's own key as the ACT publisher key, so grants and revokes are /grantee calls on a node you operate, and grantees read through their own node.
Limit: revoking stops access to versions published after the revocation. A revoked grantee can still decrypt versions published while they were granted (Swarm-Act-Timestamp), and nothing recalls what someone already decrypted.
Lightning round · 4/4
Messages without a server (PSS)
Messages travel as "trojan chunks" through the forwarding network, with mailboxing for a recipient who's offline
No message server to subpoena or switch off
Book of Swarm §4.4 (Pss: direct push messaging with mailboxing) · Bee API /pss
Contributions, comments and chat for an archive, with no message server to block. Real-time, single-topic, many-to-one messaging is GSOC's job, coming up in "what's possible".
Limit: measure latency before promising real-time anything over PSS itself.
What's possible · 1/3
Streaming over Swarm
HLS over feeds: segments are content-addressed chunks; the feed carries the advancing playlist — live and archive are the same object
how it works
Devcon 8 talk
~2-7 s per 1.85 MB segment in-browser · ethswarm.org paper · Devcon 8 talk · Book of Swarm §4.3
This is working code today, not a proposal: Solar Punk's streaming stack uploads HLS segments to Swarm in real time and maintains a feed-based manifest.
Honest throughput note: browser-only playback (no gateway) fetches a ~1.85 MB segment in about 2-7 seconds, ~3 s typical. Low-bitrate streams keep up; high-bitrate ones rebuffer.
Two-second segments put a live viewer a few seconds behind, the same floor HLS has anywhere else; the limit is how many peers the browser node can reach, not decoding.
What's possible · 2/3
Real-time messaging: GSOC
Many writer nodes send to one service node's address — a SOC mined into its own neighbourhood
The service node must be a full node, and listens over a WebSocket. Available in Bee today
docs.ethswarm.org · GSOC
docs.ethswarm.org GSOC page
GSOC (Graffiti Several Owner Chunk) is many-to-one: any writer who knows the service node's mined address and identifier can send it a message; the service node subscribes once over WebSocket and gets each update as it syncs.
What's possible · 3/3
Pub/sub: coming soon
Topic-based publish/subscribe messaging is coming to Swarm
Not released yet. GSOC, on the previous slide, already covers real-time messaging to one service node today.
This afternoon
This afternoon: build
L1 Publish entries + index + feed (checkpoints 1–3)
L2 Unhostable npm run deploy: the app itself on Swarm, 2+ paths
L3 Keep alive batch status, pin elsewhere, adopt another team's archive
L4 Write without us stamp chunks in the browser with your team key, upload to any node
Archive something real: a song, a recipe, a place, a story from your region. Build in your own GitHub repo: GUIDE, checkpoint 0.
16:00 teardown, one line: 2+ read paths, 2+ feed updates, a rejected tamper, no non-Swarm host — npm run teardown checks all four.
starter repo
Pick the level that fits. L4 needs a team card; its values go into the app's L4 panel. One uploader per team, because two uploaders would reuse stamp slots and overwrite each other.
Mentors are at the Swarm table all afternoon; the status page shows node health.
Pushback, "you run all the nodes, so it's centralised": today the workshop write path is ours, and L4 removes that, because pre-stamped chunks go to any node. Reads already come from many operators, and the content's address and the feed's identity (owner and topic) don't depend on our nodes. Today feed updates are written through our node and stamped with our batch, and any node can read them.
Pushback, "open CORS and a shared batch invite abuse": the shared batch is disposable and immutable, and the edge allowlist blocks the wallet, staking and stamp-purchase routes, so the open write path can only add chunks to that one batch. Its utilisation is on the status page, and everything shuts down after the event. The worst case is a full batch, which fails loudly.
Take it with you
Links
this deck (on Swarm)
the book (free PDF)
run a client (access)
Freedom Browser
starter repo (afternoon)
Scan the first QR: this deck, right now, with my laptop node off.
Close the loop: the first QR resolves to this exact deck — the same feed manifest, re-uploaded many times this week, URL unchanged — live right now, with my laptop node off.
Then Q&A. Keep 3 minutes.