Why chat history is usually hostage
Ask a hosted chat platform for your data and what you usually get is a records file: a zip of JSON and attachments, fine for compliance and grep, useless for continuing the conversation anywhere else. The live community, the thing you actually built, does not come out. "Export your data" and "leave with your community" are different promises, and most of the industry only makes the first one.
Buzz makes the gap concrete rather than commercial. Every message is signed by its author, and the signature covers the timestamp. A relay accepts an event only from the key that signed it, and only while the timestamp is fresh, about fifteen minutes in the stock relay. So nobody can re-post last year's history into a fresh relay over the wire: not us, not you, not the people who wrote the messages. This is not our policy to relax; it is how the protocol defends itself against forged and replayed history, and it binds everyone running Buzz anywhere.
We learned that the honest way. Our first export design assumed signed events could be republished to any relay; a drill disproved it, and we corrected our own copy. Since then the export has been a record and has said so plainly. This article is about what we built past that: a migration path that moves the live community in both directions, without pretending the wire can do what it cannot.
Identity lives on the hostname
The second fact everything rests on: a Buzz community's identity is welded to the relay hostname. When a client connects, the relay resolves which community it is serving from the host the connection arrived on, and every message, membership and file hangs off that community's internal id. The hostname is not where the community lives so much as how the community is named, and the binding exists in exactly one place: one row in the relay's database that names the community's host.
That row is the hinge the whole migration swings on, and it also produces the advice we now give everyone before anything else: choose your hostname carefully, in both directions, before people move in. Fifty members later it is the name your community answers to, and changing it is a migration, not a settings toggle.
Restore, rebind, boot
Because the wire is closed to the past, real migration runs underneath it, at the database. Three steps, and the order is the entire trick: restore, rebind, boot.
Restore first, with the relay not yet running: load the database dump into an empty database, and mirror the media into the new media store. Media in Buzz is content-addressed, named by the hash of its own bytes, so it does not care which hostname serves it. Then rebind: update that one host row to the new hostname. Then, and only then, boot the relay. On startup it looks for a community at its configured host, finds the row you rebound, and adopts it: same community, same internal id, full history, original timestamps.
Boot first and you meet the failure that makes all this look impossible: the relay finds no community at its host, helpfully seeds a fresh empty one, and your restored history sits in the same database, orphaned beside it. Everything appears healthy and every room is empty. Rebind before first boot and the relay never gets the chance to be helpful. In our drills the restored history was readable over an authenticated connection the moment the relay came up, and the same owner key that ran the old instance posted new messages on the new one.
Cały mechanizm w skrócie. Sztuką jest kolejność: relay uruchomiony przed przepięciem sieje pustą społeczność pod swoim hostname, a przywrócona historia leży osierocona obok.
In: when you are tired of ops
Direction one: in. You have been self-hosting Buzz, the novelty of 2 a.m. disk alerts has worn off, and the reason you have not moved is that moving usually means starting over: new accounts, empty rooms, a "the history is in the old thing" link nobody ever follows. That is the move we drilled end to end this week, with the same import tooling we will run against your community, and none of those costs survived the drill.
What carried, verified rather than hoped: the full history, with its original timestamps, because a restore is a database operation and the wire's fifteen-minute window never gets a vote. Every member identity, with zero re-enrollment, because a Buzz identity is a Nostr keypair the member holds rather than an account we issue: the same keys that worked against your hostname keep working against the new one, and nobody is asked to sign up again. And the media, byte for byte, verified against its content addresses.
What we ask from you is deliberately small: a dump of your database and a way to read your media store. We do the rest hands on, with a backup taken before the import touches anything, and the drill's checklist runs before we call it done. There is no self-serve import button today, and we are in no hurry to add one: a migration is the one day your community deserves an operator paying full attention.
Out: when you want the keys
Direction two: out. Every hosted workspace has always been able to download an export: the signed events, the media, a README. That is the record layer, and it is honest about being a record. What the bundle is gaining now is a sovereign layer next to it: the full database dump, the exact container image reference the workspace was running, a compose file, and a restore script that enforces restore, rebind, boot in that order.
The test we hold it to is the only one that counts: no help from us. In the drill, a clean machine that had never touched our systems followed the bundle's own README and came back up self-hosted on a new hostname. The same owner key signed in. The full history was there. Media served at its content addresses. And the instance accepted new messages, which is the difference between a museum exhibit and a community that moved.
An export you can only restore with the vendor on a support call is not a door, it is a leash with extra steps. So the README is the standard we drill against: leaving should be a boring afternoon of following instructions that work. We would rather earn the ops than own the exit.
The edges, plainly
A migration story without its edges is an advertisement, so here they are. First: old messages that embedded absolute URLs, thumbnails inside older uploads, upload responses that quoted a full address, keep pointing at the old hostname forever. Those URLs live inside signed content, and rewriting signed content would be forgery, so no honest migration can touch them. The media files themselves are content-addressed and serve correctly from the new host; it is only the literal URLs frozen inside old signed messages that still name the place you left.
Second, on the way out: the relay's own signing key does not come with you. Your restored instance generates a fresh relay identity, so the system notices the old relay signed stay attributed to the identity that actually signed them, which is exactly what signatures are for. Member access is unaffected: your people are their own keys, and those travel because they were never ours to hold.
Third, in both directions: pick the destination hostname like it is permanent, because it effectively is. The same weld that makes the one-row rebind sufficient makes the hostname the one thing you cannot casually change later.
Control, in both directions
Put it together and sovereignty stops being a slogan and becomes a checklist. Your people hold their own keys, so no operator, including us, sits between them and their identity. Your history restores under a hostname you control, in whichever direction you are moving. The wire refuses replayed history for everyone equally, and the database path underneath works for everyone equally. Control is not a feeling in a pricing table; it is whether the door out has been drilled until it opens. Ours has, this week, from the README alone.
Where this leaves you
The hosted side of the trade lives at hachiflow.com: $50 a month flat, your own relay at name.hachiflow.chat, and the migration described here done with you, hands on, in whichever direction you are heading.
And if you never send us a dollar, take the mechanism: restore, rebind, boot, in that order, with the rebind before the relay's first start. That one ordering is most of what a Buzz migration is. We proved it on a clean machine this week, and it is yours to keep.