docs(systems): document needs_attention state and Reset peering action

This commit is contained in:
Jannis Braun
2026-04-21 21:11:17 +02:00
parent 64d231d782
commit e236d0730e
3 changed files with 49 additions and 21 deletions
+12 -4
View File
@@ -9,6 +9,7 @@ Source files:
- `packages/server/src/utils/storageJanitor.ts` -- Storage stats, orphan detection, cleanup
- `packages/web/src/stores/settingsStore.ts` -- Zustand store for instance/streaming settings
- `packages/web/src/components/modals/instanceSettingsPanels/GeneralPanel.tsx` -- General settings UI
- `packages/web/src/components/modals/instanceSettingsPanels/FederationPanel.tsx` -- Federation peers panel (peering, approval queue, peer status, rotation, reset)
- `packages/web/src/components/modals/instanceSettingsPanels/StoragePanel.tsx` -- Storage management UI
- `packages/web/src/components/modals/instanceSettingsPanels/StreamingPanel.tsx` -- Streaming config UI
- `packages/web/src/components/modals/instanceSettingsPanels/UsersPanel.tsx` -- User management UI
@@ -422,14 +423,21 @@ All panels live under `packages/web/src/components/modals/instanceSettingsPanels
#### GeneralPanel
Manages: instance name, registration toggle, discovery toggle, GIF API key, federation relay toggle/TTL, pending approval requests, peered instances list.
Manages: instance name, registration toggle, discovery toggle, GIF API key, federation relay toggle/TTL.
- Instance name input: max 32 chars, enforced client-side via `slice(0, 32)`
- GIF key: password input, separate dirty tracking (`gifKeyDirty`). Only sent on save if modified. "Clear key" button sets empty string.
- Federation relay toggle and TTL input: drive `federationRelayEnabled` and `federationRelayTtlDays` instance settings.
#### FederationPanel
Manages: federation peers list, pending approval requests, manual peering initiation, secret rotation, peer reset.
- **Pending Approvals section:** Visible only when `pendingApprovalCount > 0` (from ready payload). Positioned above the peer list. Each row shows the requesting instance name and origin with Approve and Deny buttons. Approve calls `api.federation.approveApprovalRequest(id)` and Deny calls `api.federation.denyApprovalRequest(id)`; both remove the row from the local list on success.
- Federation peers: fetched via `api.federation.peers()`, displayed as a list with status badges (active/pending/unreachable/awaiting_approval), last-seen/synced times, revoke button
- Peers with status `'revoked'` are filtered out of the visible list
- Revoke calls `api.federation.revokePeer(peerId)` and removes from local list
- Federation peers: fetched via `api.federation.peers()`, displayed as a list with status badges (active/pending/unreachable/awaiting_approval/rejected/needs_attention), last-seen/synced times, and per-peer actions.
- Peers with status `'revoked'` are filtered out of the visible list.
- Revoke calls `api.federation.revokePeer(peerId)` and removes from local list.
- Peers in `needs_attention` status render with a rose "Needs Attention" pill and a single "Reset Peering" action. The action opens a danger-variant ConfirmDialog explaining that reset deletes the local peer record (cascade-removes outbox entries) and requires out-of-band re-peering with the remote admin.
#### StoragePanel
+3 -2
View File
@@ -357,10 +357,11 @@ Migration flags (internal): `voice_bit_migrated`, `profile_attachments_cleaned`,
| origin | text NOT NULL UNIQUE | | `https://domain.tld` |
| instanceName | text | | |
| hmacSecret | text NOT NULL | | 256-bit hex |
| status | text NOT NULL | `'active'` | active/pending/awaiting_approval/unreachable/revoked/rejected |
| status | text NOT NULL | `'active'` | active/pending/awaiting_approval/unreachable/revoked/rejected/needs_attention |
| lastSeenAt | integer | | |
| lastFailureAt | integer | | |
| consecutiveFailures | integer | 0 | >=10 → unreachable |
| consecutiveFailures | integer | 0 | >=10 → unreachable (network/5xx failures) |
| consecutiveAuthFailures | integer NOT NULL | 0 | >=5 → needs_attention. Tracked separately from `consecutiveFailures` (network) because auth (401/403) and network failures have different resolution paths. |
| lastSyncedAt | integer | 0 | |
| remoteMaxUploadSize | integer | | Bytes, from peer |
| createdAt | integer NOT NULL | | |
+34 -15
View File
@@ -73,22 +73,26 @@ Both instances store the **same** HMAC secret. The initiating instance generates
```
ensurePeered
(none) ──────────► pending ──────────► active
▲ │ │
│ │ remote 202 │ delivery failures
│ ▼ ▼
│ awaiting_approval unreachable
│ │ │
│ ┌───────────┼──────────┐ │ health check OK
│ │ │ │
│ accept denied expired active
│ (fresh) (admin) (janitor) │
│ │ │ │ │ admin revoke
│ ▼ ▼ ▼
│ active rejected rejected revoked
│ auto-peer rejected (403 PEERING_REQUIRES_APPROVAL)
(none) ──────────► pending ──────────► active ──────► needs_attention
▲ │ │ ▲ │
│ │ remote 202 │ │ │ admin Reset
│ ▼ │ │
│ awaiting_approval │ │ (deleted)
│ │ │
│ ┌───────────┼──────────┐ │ │ N consecutive
│ │ │ │ │ │ auth failures (401/403)
│ accept denied expired │ │
│ (fresh) (admin) (janitor) │ │ delivery failures
│ │ │ │
│ ▼ ▼ ▼ unreachable
│ active rejected rejected
│ health check OK
│ auto-peer rejected
│ (403 PEERING_REQUIRES_APPROVAL) active
└──────────────────────────── rejected
│ admin revoke (active)
revoked
```
| Status | Outbox delivery | Health check | Relay accepts | Re-initiation | Admin clear |
@@ -97,6 +101,7 @@ Both instances store the **same** HMAC secret. The initiating instance generates
| `pending` | No | No | No | No (returns 409) | N/A |
| `awaiting_approval` | No | No | No | Returns pending; no re-handshake | Yes (admin deletes) |
| `unreachable` | No (entries wait) | Yes (1h interval) | Yes (resets to active) | No | N/A |
| `needs_attention` | No (entries bounded by TTL) | No | Yes (200 no-update, same as active) | No (admin must Reset first) | Yes (admin Reset deletes record) |
| `revoked` | No (entries purged) | No | No (returns 403) | Yes (old record deleted) | N/A |
| `rejected` | No | No | No | Yes (admin deletes record, then re-initiates) | N/A |
@@ -151,6 +156,7 @@ When `autoAcceptPeering` is `false` and an instance calls `POST /api/federation/
| `/api/federation/peers/:id` | PATCH | JWT + admin | Update peer settings (auto-rotation interval) |
| `/api/federation/peers/:id` | DELETE | JWT + admin | Revoke peer, purge outbox |
| `/api/federation/peers/:id/permanent` | DELETE | JWT + admin | Hard-delete revoked peer record |
| `/api/federation/peers/:id/reset` | POST | JWT + admin | Delete peer record (cascade-deletes outbox). Only admissible in `needs_attention` state. |
| `/api/federation/peers/:id/rotate` | POST | JWT + admin | Trigger immediate secret rotation |
| `/api/federation/approval-requests` | GET | JWT + admin | List pending peering approval requests |
| `/api/federation/approval-requests/:id/approve` | POST | JWT + admin | Approve request, initiate handshake |
@@ -480,6 +486,19 @@ Trigger (API/WS handler)
| 6 | 6 hours |
| 7+ | 24 hours (cap) |
### Authentication-failure handling (401 / 403)
When a relay response is 401 (HMAC rejected) or 403 (remote's peer row is non-active or missing), the worker increments `consecutive_auth_failures` on the peer row, applies backoff to the queued outbox entries via the existing `BACKOFF_SCHEDULE_MS`, and preserves `hmac_secret`. After `AUTH_FAILURE_THRESHOLD = 5` consecutive auth failures (~21.5 min with the existing schedule), the peer transitions to `needs_attention`:
- Outbox delivery halts (the existing `status = 'active'` filter on the delivery query excludes `needs_attention`).
- `hmac_secret` is preserved (admin can inspect; no silent rotation).
- Affected local users receive `federation_peer_rejected` WS events with reason "Federation trust broken — admin must reset peering".
- Admins receive `federation_peers_changed`.
The worker NEVER re-handshakes via unauthenticated `/peer/accept` in response to a 401/403. The safeguard at `/peer/accept` (idempotent-200-no-update on `active` OR `needs_attention` peers) is what prevents silent HMAC rotation; the worker's job is to respect that signal and surface it to admins rather than loop. Recovery is via the admin "Reset peering" action, which deletes the local peer row and requires out-of-band re-peering.
Network failures (timeouts, non-401/403 non-2xx responses) are tracked separately via `consecutive_failures` and lead to `unreachable` at `PEER_UNREACHABLE_THRESHOLD = 10`. A successful delivery resets both counters.
### Relay Request/Response Format
**Request:**