docs: update federation spec for homeward relay and DM federatedId

This commit is contained in:
Jannis Braun
2026-04-01 03:59:07 +02:00
parent e40a63ba9b
commit 91362493ea
+9 -2
View File
@@ -233,8 +233,11 @@ Locations where normalization is applied:
**Attribution verification (`verifyAttribution`):** **Attribution verification (`verifyAttribution`):**
- `verifyAttribution(actingUserHomeInstance, sourceInstance)` normalizes both via `extractDomain` and compares - `verifyAttribution(actingUserHomeInstance, sourceInstance)` normalizes both via `extractDomain` and compares
- Applied as the FIRST check in every relay event processor (13 handlers) — before user resolution or DB writes - Two valid cases:
- Prevents malicious peers from forging events attributed to users on other instances 1. **Direct**: `authorDomain === sourceDomain` — standard S2S, peer sends events for its own users
2. **Homeward relay**: `authorDomain === extractDomain(getOurOrigin())` — a client-federation user sent a message on a remote server, and the S2S relay forwards it back to the author's home instance
- Applied as the FIRST check in every relay event processor (13+ handlers) — before user resolution or DB writes
- Prevents malicious peers from forging events attributed to users on *unrelated* instances (neither source nor receiver)
All origin comparisons use `extractDomain()` or `getOurOrigin()` with normalization, handling both bare domains and full URLs consistently. All origin comparisons use `extractDomain()` or `getOurOrigin()` with normalization, handling both bare domains and full URLs consistently.
- `federation.ts:2447` -- same pattern in `processFriendRemoveEvent` - `federation.ts:2447` -- same pattern in `processFriendRemoveEvent`
@@ -253,6 +256,10 @@ All origin comparisons use `extractDomain()` or `getOurOrigin()` with normalizat
5. `getGroupDmTargetOrigins(channelId)` returns `undefined` (no owner -> broadcast to all) 5. `getGroupDmTargetOrigins(channelId)` returns `undefined` (no owner -> broadcast to all)
6. `queueOutboxEvent(messageId, channelId, 'create', payload, undefined)` -> queued to ALL active peers 6. `queueOutboxEvent(messageId, channelId, 'create', payload, undefined)` -> queued to ALL active peers
**Channel creation (1-on-1 with federated user):**
- When `POST /api/dm` creates a channel where either participant has `homeInstance` set, the deterministic `federatedId = SHA256(sorted([homeUserIdA, homeUserIdB])).slice(0, 32)` is computed and stored immediately
- This ensures `findOrCreateDmChannel` on the receiving instance finds the existing channel when the S2S reply arrives, preventing duplicate channels
**Inbound (receiving instance -- `processCreateEvent`):** **Inbound (receiving instance -- `processCreateEvent`):**
1. Validate: `event.message` and `event.participants` (>= 2) required 1. Validate: `event.message` and `event.participants` (>= 2) required
2. Dedup: check `(sourceInstance, sourceMessageId)` -- reject if exists 2. Dedup: check `(sourceInstance, sourceMessageId)` -- reject if exists