tombstone gate all user paths; cleanup deletes late follower rows; spa self-delete to /; adr 0016 + purge comment

This commit is contained in:
2026-10-26 03:22:13 +01:00
parent caab63596a
commit b9cdb54340
7 changed files with 63 additions and 10 deletions

View File

@@ -29,11 +29,20 @@ exists), so two admins deleting each other concurrently cannot both succeed; the
`count_active_admins` check is only the early, friendly refusal (409).
**Immediate effects, before the purge runs.** Login is refused and tokens are rejected
(`ensure_active` in the extractors; the user lookups filter `deleted_at IS NULL`). The profile
(`ensure_active` in the extractors; the user lookups filter `deleted_at IS NULL`). The user's
webhook tokens stop ingesting at once (`find_by_token_hash` ignores tokens of tombstoned users,
so the endpoint answers 401). The profile
and `?embed=true` pages 404. The ActivityPub actor document, inbox, outbox and featured answer
410 (`tombstone_gate` for the collection endpoints, `actor_is_gone` for the actor). WebFinger
410 (`tombstone_gate` for every `/users/{id}/...` path, content objects included, and
`actor_is_gone` for the actor). WebFinger
answers 404 (owned by k-ap).
**The service actor is not deletable.** The instance's service actor (`UserId::service_actor()`,
seeded by migration 0022, used for signed fetches) is refused by `request_account_deletion`
(403 `Forbidden`), and both UIs hide the action (`is_service_account` on the profile response;
the classic template checks the same). Deleting it would retire its name and, after the
retention period, its keypair, breaking signed fetches for good.
**Purge.** Order matters and every step is idempotent:
1. Notify the fediverse through `FederationDeletionNotifier`, implemented over k-ap's
@@ -47,6 +56,11 @@ answers 404 (owned by k-ap).
`watch_events`, `wrap_up_records`, `import_*`, `blocked_actors`). `PURGED_USER_TABLES`,
`URL_KEYED_USER_COLUMNS` and `PAYLOAD_KEYED_USER_COLUMNS` are checked against the schema by a
guard test, so a new user-owned table cannot be forgotten.
Global wrap-up reports (`wrap_up_records` with no user) embed `most_active_user`; the purge
sets it to null in any report naming the user and keeps the rest (`wrap_up_records.report_json`
is in `PAYLOAD_KEYED_USER_COLUMNS`).
A failed purge is redelivered and queues the `Delete` again; a repeated `Delete` is
idempotent on remote servers.
3. Image objects (avatar, banner) are deleted after the commit, then the references are dropped
(`forget_images`), so a crash in between is repaired by redelivery.
@@ -72,7 +86,9 @@ registration path refuses a retired username, so a deleted name cannot be impers
- Events already relayed to NATS JetStream are not erased; they live as long as the stream's
retention allows.
- A Follow arriving on the shared inbox can create a `Pending` `ap_followers` row for the
tombstoned user; it is removed by the cleanup.
tombstoned user; `purge_expired_tombstones` deletes the user's `ap_followers` and
`ap_following` rows explicitly before the `users` row (a test covers it), not by relying on
the foreign-key cascade.
- Remote servers that stay down through the delivery retries, or that ignore `Delete`, may keep
stale copies.
- A handler that defers a tombstone-naming event forever delays the tombstone (and keypair)