An account Jetstream says was deleted goes only when an answer says so
ClosedThis makes a Jetstream "account deleted" event purge only when an answer says the account is gone. ask returns Here, Gone or Unsure. settle re-asks with doubling waits and keeps the account if it is still Unsure after six asks. deletions runs one task per account. I read the new logic in accounts.rs and the identity::document it relies on, and it matches the description. The tests are reported as run on the runner. I did not read them or the checks, and did not read the diff for tests/gone.rs or tests/deletes.rs.
I found nothing that has to change. Two points to be aware of, both minor:
did:web404: adid:webhost that answers404counts asGoneand purges at once. A host that misconfigures its/.well-known/did.jsonfor a moment would purge an account. That is what the description says it wants, and PLC404and410are unambiguous. If you want to be stricter, make adid:web404Unsure.- Repeat events: two deletion events for one DID spawn two independent
settletasks. They are idempotent, so this is harmless, but each waits out the full schedule.
Closed: the coordinator moved this fix onto batch/two (The web proxy, deletes and the cross-post test land together, checked once), at 48472f4, as its reviewer asked.
What changes
Three answers, not two. When Jetstream says an account was deleted,
accounts::asknow asks where its identity points and answers one of three things (crates/grain-serve/src/accounts.rs):Here: the host'scom.atproto.sync.getRepoStatussaysactive: true. The account moved there, so it stays.Gone: the host saysactive: false, or refuses withRepoNotFound, or PLC (or thedid:webhost) answers410(a tombstone) or404(nothing there). Only these purge.Unsure: anything else. The DID document can't be read (transport error, a 5xx), it names no PDS, the PDS can't be reached or answers with an error other thanRepoNotFound, or it answers with noactive.Asked again, then kept.
accounts::settleasks until an answer settles it, doubling the wait each time.Patience::default()is six asks, starting at 30 s and capped at 10 min a wait, about half an hour in all. If it is stillUnsureafter that, it logs an error and keeps everything.deletionsgives each account its own task (aJoinSet), so one account waiting holds none of the others up, and it ends once the stream's channel closes and every task has settled.jetstream::followpasses the default patience.Why
A passing PLC or network failure met while handling a Jetstream deletion of an account that had moved used to count as gone. That purged state nothing rebuilds: preferences, push tokens, mutes, story views and invites. Purging is the one answer that can't be undone, so it now waits for a host or directory to actually say so.
knotless does not do this. Its
knotless-serve/src/accounts.rsdeleted()takes an identity that fails to resolve, or a status call that errors, as deleted (active != Some(true)), which is the shape grain had. So this is grain's own, and the right change is to make it in knotless too. What knotless loses to a false purge is less (it keeps less per account), which is likely why it hasn't mattered there.How it was checked
crates/grain-serve/tests/gone.rsruns a stand-in PLC directory on loopback that is also each account's host.askanswersHerefor an active repo.Goneforactive: false,RepoNotFound, a410tombstone and a404.Unsurefor PLC answering 503, the host answering 502, a host nobody listens on, a status with noactive, and a document that names no PDS.settlekeeps an active account, purges one whose PLC fails once and then says it is inactive, and gives up on one PLC never answers. Its preferences and host status are left as they were.deletionspurges a tombstone and keeps an account it couldn't ask, both sent through the channel at once.tests/deletes.rs's guard test now checksaskagainst the reference PDS: an account there isHere, and a DID its PLC never heard of isGone. Those tests ran here; the rest is the runner's.Shortcomings
active: falsecounts as gone, as asked for, even when itsstatussaysdeactivatedat the new host. Deactivated accounts already sit out of reads until they're back, so this is the stricter of the two readings.deletedevent, or an admin, has to do it.🤖 Generated with Claude Code