I want to be clear about something before I start: this was my fault. Not Tranquil's, not the AT Protocol's, not anyone else's. I took early software and pointed it at the one account I couldn't afford to lose, and then I was surprised when it went sideways. That's on me.
But I want to be clear about something else, too: I didn't do it blindly. I had reasons. They were good reasons. They just weren't good enough.
Here's what happened.
Standing It Up
Today I set up a Tranquil PDS instance on pds.croft.click. Tranquil is a Rust-based alternative to the reference PDS, built by the team at tranquil.farm. It's early software. I knew it was early software. I set it up anyway because I wanted to move my main account off eurosky.social and onto something I controlled entirely, and the reference PDS felt like a thing I should be able to improve on.
The setup itself was fine. Cloudflare Tunnel for ingress, bind-mounted storage on /Volumes/Storage/Server/PDS/, the whole stack running under OrbStack. Tranquil ran. It served requests. The problem wasn't standing it up — it was moving to it.
Why I Thought It Would Be Fine
Here's the thing: I'd done migrations before. Several of them, between reference PDS instances, and they'd all gone smoothly. Account migration on the AT Protocol isn't new to me — I know how it works, I know what it's supposed to do, and I've watched it do it correctly more than once.
On top of that, a friend was running acey.sh on their own Tranquil PDS — a fresh account, not a migration, but it was live and serving and working. The PDS was up, it was handling requests, it was signing things. From the outside, the difference between "works for a new account" and "works for a migration" looked like a gap I could safely step over.
It wasn't. Those are different code paths, and the migration path is the one that touches your DID document — the one record the entire network uses to decide whether you exist. "New accounts work" tells you nothing about whether migrations work, and I knew that, and I did it anyway, because curiosity is a hell of a thing.
And here's the part I can't explain: I've burned accounts before. I've pointed experimental software at accounts I didn't care about, watched them break, and moved on. That's the sane version of what I do. This time I used my main account — the one I've had for over two years, the one with my blog, my identity, my history on this network — and I don't have a good reason for why. I have reasons, plural, and none of them survive the sentence "and so I pointed it at my main." I did it because I was curious, because migrations had worked before, because a friend's account was fine, and because at no point did I stop and ask the one question that would have stopped me: "what if it doesn't work?"
It was midnight and I guess I was tired.
Pointing It at the One Thing I Couldn't Lose
Account migration on the AT Protocol works by updating your DID document — the record that tells the rest of the network where your data lives and who's allowed to sign things on your behalf. You point your DID at the new PDS, the new PDS takes over signing, and the network follows.
In theory.
In practice, something in Tranquil's migration path corrupted my DID document. I don't know exactly what — I'm leaving that to upstream to diagnose — but the result was that my DID doc was wrong, and the AppView couldn't resolve my account against it. My handle still resolved — that's DNS, separate from the DID doc — but my posts didn't load, my profile didn't render, and I couldn't post or interact. The account data itself was fine — every post, every record, every blob, all intact on the PDS. The problem was purely the DID doc. But that's the one record the entire network uses to decide where your data lives, and when it's wrong, it doesn't matter that your data is safe. Nobody can find it. From the perspective of anyone using Bluesky, I was still technically there but functionally gone — hollowed out at the AppView layer, not because anything was lost, but because the map to it was broken.
The Network Forgot Who I Was
The first hour was denial. The second was checking whether anyone else could see me — they couldn't, not properly. The third through sixth were a slow, grinding debug that I'm not going to pretend was dignified.
I spent six hours chasing my own tail. Trying things, checking whether they'd worked, trying something else. Reading docs, re-reading docs, convincing myself I'd understood something I hadn't. The panic was physical — my stomach dropped and didn't come back up — and it didn't stop when the fix started working, because the entire time I was thinking about what happens if the backup key doesn't work either. I've had this account for over two years. It's not a burner or a test handle — it's the account, the one with my history, my posts, my blog, my identity on this network. And watching it hollow out in real time — still technically there, but functionally gone — I genuinely felt like I was going to be sick.
Eventually I went to bed. Not because I'd fixed anything, but because I'd run out of things to try and it was light outside and I hadn't slept.
The next day I spent about half an hour on Signal with Bailey — @pds.dad — who built pdsmoover.com. Bailey was patient, generous with their time, and had clearly seen worse. We worked through what was actually wrong with the DID doc and how to fix it. I had a backup PLC rotation key, which is the thing that ultimately saved me: it let me re-sign my PLC records and point the DID back at a working PDS. Without it, the DID — and thus the account — would have effectively become a ghost on the network. Still technically there, but permanently untouchable. No way to update it, no way to move it, no way to prove it's yours. War flashbacks to the fifteen ActivityPub accounts I used from November 2022 to April 2024, all of them ghosted for similar reasons. I know what a ghost account looks like. I didn't need a sixteenth.
The actual fix was done with goat — the Go AT Protocol CLI tool from bluesky-social. It's a general-purpose CLI for the AT Protocol, sort of like curl for at:// URIs, and it has manual migration commands that let you work directly with DID documents and PLC records. Without that backup key, I'd have been at the mercy of whatever state the migration left things in. With it, goat could override the broken state and restore. The fix was not elegant. It was the digital equivalent of a spare tyre and a can of tyre weld.
Three Things, In Order of Cost
The first thing: don't migrate your main account to early software. I knew this. I did it anyway. I'm not going to do it again. The reference PDS exists for a reason, and the reason is that it handles the boring, critical, unglamorous work of not destroying your identity. My main account stays on eurosky.social until Tranquil's migration path is proven, and probably after.
The second thing: keep a backup rotation key. I had one, and it's the only reason this story has a happy ending. If you're on the AT Protocol and you don't have a backup PLC rotation key stored somewhere safe, stop reading this and go sort that out. I'll wait.
The third thing: the AT Protocol is very good at making you feel like you don't exist. When your DID doc is broken, you're not just offline — you're gone. There's no "account temporarily unavailable" page. There's no fallback. The network just doesn't know who you are. That's by design — it's what makes the protocol portable — but it means a corrupted DID doc is not an inconvenience. It's an existential event.
Where Things Stand
I'm back on eurosky.social. The Tranquil instance on pds.croft.click is gone — I tore it down. Not out of anger at the software, just out of a clear-headed assessment that I don't currently have a reason to run a PDS I can't safely migrate to, and keeping it up was just inviting myself to try again before it's ready.
The only thing left from that server attempt is git.croft.click — a Forgejo instance I use as a mirror of my GitHub. It's separate from all the ATProto stuff and it's staying, because it works and it was never the thing that broke.
Tranquil is good software being built by good people. It just isn't done yet, and migration is the part where "not done yet" costs you the most. That's not a criticism — it's a calendar. I'll check back when it's ready.
In the meantime: don't be an idiot like me.