feat(federation): reset-cleanup panel — informational detach copy, real server-side Dismiss, Keep removed (detach spec §4.6)

This commit is contained in:
Jannis Braun
2026-07-02 19:06:21 +02:00
parent 42ad5e141d
commit 5ad8aefaff
5 changed files with 162 additions and 67 deletions
+1 -1
View File
@@ -370,7 +370,7 @@ Detection (`markPeerReset`) only snapshots + journals + notifies; it destroys no
**Login self-heal epoch guard (design §6.3a).** The federated password self-heal (`auth.ts` §4) now gates re-hashing on the home instance's current epoch, read via the authenticated `fetchPeerEpoch(peer)` (HMAC-signed both ways): no baseline on record → allow (legacy); baseline differs from the fetched epoch → refuse; epoch can't be determined (`fetchPeerEpoch` null — 404/unreachable/bad-sig/desynced secret) → **fail closed/refuse**; match → allow. Closes the *pre*-re-peer hijack (a reset home accepting a new same-name user's password); the universal quarantine freeze closes the post-re-peer window. Full three-way in `auth.md` §4.
**Reset-events admin surface (`GET /api/federation/reset-events`).** Admin-only, read-only. Returns the durable `federation_reset_events` journal joined with each origin's current orphaned real accounts (`federation_home_orphaned = 1`, `homeInstanceMatch`), each with `ownedSpaces`, `spaceMemberCount`, and authored-`messageCount` for disposition. Response type `FederationResetEventsResponse` (`{ events: FederationResetEvent[] }`, each event carrying `orphanedAccounts: FederationOrphanedAccount[]`). Disposition actions reuse existing endpoints — one-click Re-peer (`/peers/:id/reset``/peer/initiate`) and full-purge Remove (`DELETE /api/admin/users/:id`, owns-spaces → transfer first). See `admin.md` "FederationPanel" and `client-federation.md` §8.
**Reset-events admin surface (`GET /api/federation/reset-events`).** Admin-only. Returns the durable `federation_reset_events` journal (each event carrying a nullable `acknowledgedAt`) joined with each origin's current detached real accounts (`federation_home_orphaned = 1`, `homeInstanceMatch`), each with `ownedSpaces`, `spaceMemberCount`, and authored-`messageCount` for disposition. Response type `FederationResetEventsResponse` (`{ events: FederationResetEvent[] }`, each event carrying `orphanedAccounts: FederationOrphanedAccount[]`). The endpoint returns **all** events (including acknowledged ones, for audit); the client filters to `acknowledgedAt === null`. Disposition actions — one-click Re-peer (`/peers/:id/reset``/peer/initiate`), full-purge Remove (`DELETE /api/admin/users/:id`, owns-spaces → transfer first) for genuinely-abandoned detached accounts, and a non-destructive **Dismiss** (`POST /api/federation/reset-events/acknowledge` with `{ origin }`, idempotent — sets `acknowledged_at`) that hides the card without touching the accounts (detached accounts keep working locally). See `admin.md` "FederationPanel" and `client-federation.md` §8.
**`needsAttentionReason` on the peer API.** `GET /api/federation/peers` returns `needsAttentionReason: 'auth_failures' | 'peer_reset_detected' | 'repeer_incomplete' | null` per peer, so the admin UI distinguishes a reset-detected peer (persistent Reset-cleanup banner + one-click Re-peer) from a generic auth-failure peer (plain "Reset Peering") and from a peer whose Re-peer could not be cryptographically verified (`repeer_incomplete` — see "Trust re-establishment contract"). `repeer_incomplete` is a nullable-TEXT value only; no schema migration.