docs(federation/auth/api/db/client): detached-account re-attach flow; reset-cleanup panel mentions re-attach
This commit is contained in:
@@ -302,6 +302,8 @@ Trade-off: the separate authenticated call can fail independently of the login P
|
||||
|
||||
Detach is sovereign but not permanent: the legitimate owner who re-created their account on the reset home can re-bind the detached account to the new home identity via `POST /api/users/@me/reattach` (registered in `routes/federation.ts`; see `federation.md` "Peer-Side Re-Attach"). It re-binds **only** on possession of BOTH identities — the session IS the detached account (local password authority, via `authenticate`) AND a one-time proof token minted on the home via `POST /api/auth/attach-proof` verifies with the home peer over signed S2S. Identity is never username-matched (that is the tier-2 hijack). On success the endpoint merges any pre-existing replicated stub for the new identity into the detached row, sets `home_user_id = <new homeUserId>`, **clears `federation_home_orphaned` (0)**, nulls `profile_updated_at`, and applies the current home profile. Clearing the flag automatically **re-enables** normal federated-account semantics: login self-heal resumes (the epoch guard above runs again) and the S2S binding guards stop excluding the account — live profile/presence sync from the home is restored. The local password hash is kept; a detached tombstone (`is_deleted = 1`) is never re-attachable.
|
||||
|
||||
**Proof-mint endpoint — `POST /api/auth/attach-proof`** (`routes/auth.ts`). The home-side half of the exchange, run against the owner's re-created **native** account on the reset home. JWT-authenticated, rate-limited 5/15min. Body `{ targetDomain }` names the peer the caller intends to re-attach on. Guards: the session user must be **native** (`homeInstance` null) — a federated/replicated session cannot mint a proof for itself. Mints a random 256-bit token (`randomBytes(32).toString('hex')`), inserts a `federation_attach_proofs` row `{ homeUserId, targetDomain, createdAt, expiresAt = now + 60s, usedAt: null }`, and returns `{ token }`. The token is **single-use, 60s TTL, and bound to `targetDomain`** — only the named peer can redeem it (verified server-side against the authenticated peer domain, not the request body, at `POST /api/federation/verify-attach-proof`). Each mint opportunistically janitors expired rows (`DELETE ... WHERE expires_at < now`); since the TTL is 60s, any spent (`used_at` set) token is swept on the next mint after it expires, so the table stays bounded without a background worker.
|
||||
|
||||
---
|
||||
|
||||
## 5. Password Change
|
||||
|
||||
@@ -428,6 +428,18 @@ Instance-epoch self-healing ledger. One row per origin recording a detected fede
|
||||
| orphanedAccountCount | integer NOT NULL | 0 | Count of accounts that could not be re-linked to the new epoch |
|
||||
| acknowledgedAt | integer | | Epoch ms the admin dismissed this reset event from the banner (`POST /api/federation/reset-events/acknowledge`, idempotent); `NULL` while unacknowledged. Purely informational — detach spec §4.6 |
|
||||
|
||||
### federation_attach_proofs
|
||||
One-time proof tokens for **detached-account re-attach** (re-attach spec §3.1). Minted on the owner's re-created **native** account on the reset home instance via `POST /api/auth/attach-proof` (bound to the peer domain the account is detached on), then redeemed exactly once by that peer over signed S2S via `POST /api/federation/verify-attach-proof` to re-bind the detached row to the new home identity. Tokens are single-use (`used_at` set atomically on redemption), short-lived (60s TTL), and peer-domain-bound (verified against the authenticated peer's domain, not the token bearer). Expired rows are janitored opportunistically on each mint, so the table needs no background worker.
|
||||
|
||||
| Column | Type | Default | Notes |
|
||||
|--------|------|---------|-------|
|
||||
| token | text PK | | Random 256-bit token (`randomBytes(32).toString('hex')`, 64 hex chars) handed to the target peer |
|
||||
| homeUserId | text NOT NULL | | The native home account's `users.id` this proof asserts control of |
|
||||
| targetDomain | text NOT NULL | | Normalized peer domain (lowercase, no scheme/trailing slash) allowed to redeem — checked against the authenticated caller peer, not the request body |
|
||||
| createdAt | integer NOT NULL | | Epoch ms the token was minted |
|
||||
| expiresAt | integer NOT NULL | | Epoch ms; `createdAt + 60_000`. Redemption requires `expires_at > now` |
|
||||
| usedAt | integer | | Epoch ms the token was redeemed; `NULL` while unused. Set atomically via `UPDATE ... WHERE used_at IS NULL ... RETURNING` so a token can never be redeemed twice, even under concurrent verification |
|
||||
|
||||
### peer_approval_requests
|
||||
Queue of peering requests pending admin review when `autoAcceptPeering` is `false`. Holds **both directions**: inbound rows (remote asked to peer with us) and outbound rows (a local user-initiated `ensurePeered` call gated on this side; see [federation.md → Outbound Peering Gate](federation.md#outbound-peering-gate)). UNIQUE on `(origin, direction)` so the same origin may have at most one row per direction simultaneously. Rows expire after 30 days via janitor cleanup.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user