diff --git a/docs/systems/auth.md b/docs/systems/auth.md index b98d035d..a2cf3e73 100644 --- a/docs/systems/auth.md +++ b/docs/systems/auth.md @@ -364,6 +364,38 @@ isAdmin: 0 - Delete files from disk via `deleteUploadFile()` - `connectionManager.forceDisconnectUser()` -- closes all WS connections, leaves voice rooms, broadcasts presence +### `tombstoneUser()` Options + +`tombstoneUser` accepts an optional second argument: + +```typescript +interface TombstoneOptions { purgeContent?: boolean } +function tombstoneUser(uid: string, options?: TombstoneOptions): string[] +``` + +- **`purgeContent: true`** (default / omitted): full tombstone — existing behavior including reaction deletion and orphaned DM cleanup. +- **`purgeContent: false`**: soft tombstone — skips `reactions`, `dmReactions` deletion and orphaned DM channel purge. Used by the federation identity soft-delete endpoint so remote message history is retained. + +### `resolveOrCreateReplicatedUser` and Deleted Users + +`resolveOrCreateReplicatedUser` checks whether a user matching `homeUserId + homeInstance` already exists and has `isDeleted = 1`. If so, it returns `null` rather than returning or re-creating the deleted stub. This prevents zombie identities from reappearing after a federation identity deletion. + +### Federation Identity Deletion (Home-Side Trigger) + +**Endpoint:** `POST /api/users/@me/federation-identity/delete` +**Rate limit:** 5 requests / 15 minutes +**Auth:** JWT (`authenticate` preHandler) + +**Request body:** `{ origins: string[], mode: 'soft' | 'full' }` + +Fans out HMAC-signed `DELETE /api/federation/identity` requests to each listed remote in parallel. Returns a per-origin results map: + +```json +{ "results": { "": { "success": true } } } +``` + +On failure for a given origin the entry contains `{ "success": false, "error": "", "ownedSpaces"?: [...] }`. A `409` from a remote means the user owns spaces there that must be resolved before deletion can proceed. + ### `sanitizeUser()` for Deleted Users When `isDeleted === 1`, returns an anonymized profile: diff --git a/docs/systems/client-federation.md b/docs/systems/client-federation.md index 78df8e6d..a3a098f9 100644 --- a/docs/systems/client-federation.md +++ b/docs/systems/client-federation.md @@ -245,6 +245,20 @@ The **Connections** panel (in user settings) allows managing remote instance con - **Remote Instances** — each shows status (connected/disconnected/error), hostname, username. Actions: Reconnect, Re-authenticate, Sync Password, Disconnect. - **Add Instance** — multi-step form: enter hostname → verify password → register/login → connected. +### Identity Deletion + +Each remote instance row exposes an identity deletion flow with three modes: + +| Mode | Label | Behavior | +|------|-------|----------| +| `leave` | Leave quietly | Client-only disconnect; no server call. Registry entry removed locally. | +| `soft` | Delete User | S2S soft delete — anonymizes the remote account and removes memberships; message history is retained. | +| `full` | Nuke everything | S2S full tombstone — soft delete plus purge of DM data and reactions. | + +A scope selector controls which remotes are targeted: **This instance** (single remote) or **All remote instances** (fans out to every connected remote). A "Select instances" option is planned for future multi-select. + +Deletion is triggered via `POST /api/users/@me/federation-identity/delete` on the home instance (rate-limited 5/15 min). The home instance fans out HMAC-signed `DELETE /api/federation/identity` requests to each target remote in parallel and returns a per-origin results map `{ [origin]: { success, error?, ownedSpaces? } }`. If a remote reports owned spaces (`409`), the UI surfaces the space list so the user can resolve ownership before retrying. + --- ## 7. Federation Registry diff --git a/docs/systems/federation.md b/docs/systems/federation.md index 82d2072e..d85ab68e 100644 --- a/docs/systems/federation.md +++ b/docs/systems/federation.md @@ -116,6 +116,25 @@ Defined in `federationWorker.ts:45` as `10`. After 10 consecutive delivery failu | Endpoint | Method | Auth | Purpose | |----------|--------|------|---------| | `/api/federation/peer/rotate` | POST | HMAC | Accept secret rotation from peer | +| `/api/federation/identity` | DELETE | HMAC | Delete federated user identity (soft/full mode) | + +### S2S Identity Deletion (`DELETE /api/federation/identity`) + +Allows a home instance to remove a user's replicated identity from a remote instance. + +**Request body:** +```json +{ "homeUserId": "", "homeInstance": "", "mode": "soft" | "full" } +``` + +**Behavior:** + +- **Attribution guard:** Rejects with `403` if the user's `homeInstance` doesn't match the `X-Federation-Origin` of the signing peer. Prevents one instance from deleting another instance's users. +- **Idempotent:** Returns `{ success: true }` for already-deleted or nonexistent users (no error). +- **Owned spaces check:** Returns `409` with `{ ownedSpaces: string[] }` if the user owns any spaces on the remote. The user must transfer or delete those spaces before identity removal proceeds. +- **Mode `"soft"`:** Calls `tombstoneUser(uid, { purgeContent: false })` — anonymizes the user row and removes memberships, but skips reaction deletion and orphaned DM purge. +- **Mode `"full"`:** Calls `tombstoneUser(uid, { purgeContent: true })` — full tombstone including reactions and orphaned DM cleanup. +- **Post-deletion:** Broadcasts `member_left` WS events for all spaces the user belonged to before removal. ---