How it works
afterwrd is a social network with no company in the middle. Each person's site runs in their own Cloudflare account, holds only their own data, and talks to nothing but Cloudflare, the place it gets updates from, and the friends they've chosen. This page explains how, and how to check it for yourself.
The code is public
Everything afterwrd runs is in one public repository on GitHub: https://github.com/stemill/afterwrd. That's the code that runs on your site, the code that installs and updates it, this install page, and the end-to-end tests. Anyone can read it, and the people who wrote it would rather you did than took their word for it.
Two ways to check what the code talks to, without reading all of it:
- Search it for
fetch(. That's the only way a Cloudflare Worker or a web page can call out to the internet. Every call in afterwrd goes to one of three places: Cloudflare (its sign-in and its API, for installing and updating), GitHub (downloading releases and checking for new ones), or a site you've connected with. There are no analytics, no error-reporting services, no embedded scripts from anyone else, and no fonts fetched from elsewhere. - Compare the installer with the release. The installer this page offers is the file
installer.jsfrom the published release 0.26.0, which GitHub builds from a tagged commit. The installer carries a fingerprint (a SHA-256 hash) of that release's manifest, and refuses any file that doesn't match it. - Rebuild it yourself. Releases are reproducible: building a release's tag on your own computer gives exactly the same files, so the same fingerprint. GitHub also signs a record of each build (who built it, from which commit), kept in Sigstore's public log where it can't be quietly changed. Check it yourself has the commands.
Once installed, the code running on your site is visible in your own Cloudflare dashboard (Workers & Pages → your Worker → Edit code). What you see there is what runs; there's no other server involved.
Where your site lives
An afterwrd site is a Cloudflare Worker plus two kinds of storage, all inside your Cloudflare account and all on the free plan:
Small JavaScript that answers every request to your address: it checks who's asking, reads the database, and builds the pages. It runs in Cloudflare's data centres, near whoever is looking.
An SQLite database holding your posts, comments, albums, the people named in your Facebook download, your connections, and the site's own keys.
Photos and videos, stored by the fingerprint of their contents in Cloudflare's SQLite-backed objects, which the free plan includes. They're only ever served through the Worker.
The stylesheet, fonts and the browser-side scripts (import, posting, download), published with each release and checked against its fingerprint.
There is no afterwrd server. The address afterwrd.app hosts only this install page, as plain files on
GitHub Pages; it never hears from your site. Nobody who works on afterwrd can see your site, reset your password (there
isn't one), or sign in as you. The flip side is that nobody can recover your site for you either: your Cloudflare
account is the only key, which is why the install page asks you to turn on two-factor authentication there.
What it talks to
| Your site calls | When | What's sent |
|---|---|---|
| Cloudflare (its sign-in, then its API) | Installing, updating, and making or deleting a group | The release's files, and the hour's token Cloudflare gave when you pressed Allow (over HTTPS, to Cloudflare only) |
| GitHub (afterwrd's releases) | Installing, updating, and a check for a newer version at most every six hours | Nothing about you: it's a download |
| A friend's afterwrd site | Only once you've both connected: when you open Home or their page, when you comment, and when you post or reply | The key for that connection, your comments on their posts, and a short note when there's something new (the start of a new post, or of a comment on something they've commented on too) |
What it never talks to
- Facebook. afterwrd never signs in to Facebook, never fetches anything from it, and never sends anything to it. It only reads the download file Facebook gives you, on your own computer.
- afterwrd.app, or anyone who makes afterwrd. There's no registration, no licence check, no usage report.
- Analytics, advertising or error-reporting services. None are included.
Browsers help you check this: open your site, open the browser's developer tools (Network), and watch. Every request goes to your own address, except photos from friends' sites, which come from their addresses.
Signing in
Only one person signs in to an afterwrd site: its owner. There's no password and no email address to steal, reuse or reset. Instead, each phone or computer you use has a passkey: a key pair made by the device itself, unlocked by its own face, fingerprint or PIN. Passkeys are the WebAuthn standard, the same thing banks and Apple, Google and Microsoft accounts use.
- The private half of a passkey never leaves your device; your site stores only the public half, which can verify a signature but can't make one.
- A passkey is tied to your site's exact address. A look-alike site can't use it, so there's nothing to phish.
- Signing in signs a one-off challenge from your site, so a sign-in can't be recorded and replayed.
- Your first passkey is made during setup, from a link the installer gives you that holds a random 192-bit code known only to your site. Adding another device makes a one-time link that works for an hour; Your devices can cancel links, remove a passkey (which signs that device out) or sign out everywhere.
- A signed-in session is a random token in a cookie the browser won't share with scripts or other sites
(
HttpOnly,Secure,SameSite,__Host-), valid for 30 days.
Friends don't sign in to your site at all. They see your posts from their own site, through a connection you approved (below).
How it's locked down
The Worker checks every request before it does anything else. Without the owner's session, the only things it will serve are the sign-in and setup pages, the fonts, and photo links that carry a valid signature. Everything else redirects to sign-in or is refused.
- Photos and videos are never public files. Each is served by the Worker, which checks the session first. The only exception is a signed link: a link your site makes for a friend's browser, signed with a key unique to your site, that expires after an hour or two. Without a valid signature, a photo address is useless.
- Every change needs to come from your own site. Each form and request that changes anything is checked
against the browser's
Originheader, so another website open in the same browser can't post, delete or connect on your behalf. - Pages can't be framed or embedded elsewhere, and your site tells browsers not to pass its address on as a referrer, not to index it, and not to guess at file types.
- Everything shown is escaped. Names and comments, including ones from friends' sites, are treated as text, never as HTML or script.
- Keys are made on your site and stay there. The signing key for photo links, the key for sign-in challenges, and the key for each connection are random bytes generated by your site on first use. They're never in a download, never shown on a page, and never sent anywhere.
- Limits on the free plan are limits on abuse. Comments and other writes are rate-limited in the same database statement that stores them, so a runaway script can't slip past by sending everything at once.
The code has had independent read-only security reviews before each major release, and the fixes are in the public history. The end-to-end tests in the repository drive real copies of the site with a browser and a virtual passkey, and include tests that try to break in: forged sign-ins, cross-site posts, impersonating a friend's site, and hostile data from a connected site.
Friends between sites
Two afterwrd sites connect directly to each other. There's no directory, no central account, and no third site involved.
- You make a one-time Connect link on your site and send it to a friend however you like.
- Your friend opens it, says where their site is, and confirms on their own site. Their site makes a random key for yours and hands it over; your site checks, by calling their site back, that the key is really theirs and meant for you, then gives them a key in return.
- Nothing is shared yet. The request appears on your Friends page with their site's address; you approve or decline. A link that was passed on to a stranger gets them no further than this.
- Once approved, each site can ask the other, using its key, for posts and photos written on afterwrd, and can leave comments. Each key works for one connection only, and disconnecting from either side ends it at once.
- Only what you wrote on afterwrd is shared: your posts, albums and photos made here, and the comments on them. Your imported Facebook archive is never shared with anyone.
- Your site decides what to believe. Data from a friend's site is treated as untrusted: names are cut to size and escaped, photo links must be exactly the signed form from that site, and only your own copy of a comment is used to decide which comments are yours.
- Photos load straight from your friend's site into your browser, by those short-lived signed links. They don't pass through yours.
- Something new is passed on as a short note, so friends' pages can say so: a new post goes to your friends, and a comment goes only to friends who commented on the same post or photo. A friend's site keeps a note only if it's about your own post or photo, cuts it to size, and limits how many it takes. Notes are sent in batches, so a post reaches any number of friends, and tried again for a few hours if a friend's site is down. While a page is open, it checks your own site for news every 30 seconds and offers to show it; nothing is fetched while the tab is hidden, and nothing is pushed to your phone.
- Posts and comments travel between sites as ActivityStreams, the open format the fediverse uses, with a permanent address for every post, photo and comment.
The Facebook import
Facebook will give you a copy of everything you've put on it (Download your information). afterwrd reads that file, and nothing else, and it does the reading in your browser, on your computer: the zip file is never uploaded as a whole.
- You pick the zip file(s) on the Import page. The browser opens them locally and works out your posts, photos, comments, likes and friends from the JSON inside.
- You tick which sections to publish. The default is posts and photos; likes, the friends list and the rest stay on your computer unless you tick them. Only what the ticked sections need is sent to your site.
- Each photo and video is fingerprinted and a thumbnail made, in the browser. Only files your site doesn't already have are uploaded, so importing a newer download sends almost nothing.
- The new import replaces the old one in a single step, so your site never shows a half-finished archive.
- Photo locations are left out by default. Facebook's download includes where some photos were taken; afterwrd only keeps that if you tick Photo locations.
- Photos you post on afterwrd itself are resized in your browser before upload, which also strips the hidden details phones put in them (location, device, time).
- Nothing in this process contacts Facebook or any third party. The only place the data goes is your own site.
Installing and updating
The installer is a small Worker you deploy first. To create your site's database and upload afterwrd into your account it needs Cloudflare's permission, which it asks for through Cloudflare's own sign-in for apps: you press Connect Cloudflare, Cloudflare shows what afterwrd is asking for (making, changing and deleting Workers, which Cloudflare calls Workers Admin; making, changing and deleting databases; reading the account's workers.dev name) and you press Allow. Cloudflare then gives your installer a token that lasts an hour, kept only in a cookie on that page, used only against Cloudflare's API over HTTPS, and never shown, logged or sent anywhere else. afterwrd never asks for permission to make longer-lived tokens. The installer downloads the release from GitHub and checks every file against the fingerprint built into it, so what's installed is exactly the published release and can't be swapped on the way. Then it removes itself.
On the way, Cloudflare sends your browser through afterwrd.app/connect/, the one address
registered for afterwrd. That's a static page with no server behind it (this whole site is files on GitHub Pages):
it shows you which site is being connected and asks you to continue, sends you to Cloudflare, and on the way back
hands the one-time code to that site, in your browser, keeping nothing. It only ever hands codes to sites at
workers.dev addresses, where afterwrd lives. Only your own site holds the secret that turns the code
into a token (the PKCE standard), so the code is useless to anything else. One thing to check for yourself:
Cloudflare's page says what afterwrd is asking for, but not which site it's for. The afterwrd page before it does,
so make sure the address it shows is your own site's. The page's code is in the repository with everything else.
Updates work the same way, from your site's Update page: connect, press Allow, and your site fetches the newer release, checks it, and installs it over itself, keeping your passkeys, connections, photos and database. Nothing is remembered between updates. Updates only ever go forward: an older release can't be installed over a newer one.
To see for yourself what the installer did, Check your install lists what should be in your account afterwards and the trail it leaves in Cloudflare's audit log.
What Allow covers, plainly: for that hour, every Worker and database in the Cloudflare account, not just afterwrd's, and the account's basic settings. Not your domains, other Cloudflare services, billing or login. That's why, if you use Cloudflare for anything else, it's best to make an account just for afterwrd. What you've allowed is listed, and can be revoked, under My Profile → Connected Applications.
When an update fixes a security problem, your site says so on Home. To hear about every release, follow the releases feed or Watch the repository on GitHub (Custom → Releases).
Your data stays yours
- Download everything gives you the whole site as one zip, built in your browser: posts, photos and videos, albums, comments (yours and friends'), likes, friends and connections, as ActivityStreams JSON with every photo. The README inside explains the files. Keys and sign-in details are never included.
- Leaving is deleting the Worker and its database in your Cloudflare dashboard. There's no account with afterwrd to close, because there isn't one.
- Moving is installing afterwrd elsewhere and importing your download. The permanent addresses in it
(
https://<your site>/posts/<key>and so on) are how two friends' downloads agree on which comment replied to which post.
What it can't protect against
Being honest about the edges:
- Your Cloudflare account. Anyone who gets into it can read your database and change your Worker. Use a strong password and two-factor authentication there, and ideally an account used for nothing else.
- Your devices. A passkey is as safe as the phone or computer it's on. If one is lost, remove its passkey from Your devices, which signs it out.
- Your friends. Anything you share with a connected site is on that site, under its owner's control, the same as telling a friend something. Choose who you approve.
- Cloudflare itself. Your site runs on Cloudflare's computers, under Cloudflare's terms. afterwrd doesn't encrypt the database against Cloudflare; it keeps your data away from everyone else.