Commit Graph
9 Commits
Author SHA1 Message Date
Jannis Braun 3c7bb02901 fix(dm): ownership transfer divergence after back-and-forth — canonicalize ownerHomeInstance + normalize authority checks
Manual ownership transfers between two federated instances diverged because
`dm_channels.ownerHomeInstance` was stored as a BARE host (`orbit.ddns.net`)
for federated owners — via `transferGroupDmOwnership` copying `users.homeInstance`
verbatim — while `sourceInstance` always arrives as a full URL on the wire.
`processOwnershipTransferEvent` and `processMemberRemoveEvent` then compared the
two with strict equality and rejected legitimate inbound events as
`unauthorized_source`, keeping ownership permanently divergent across peers.
Live DB inspection on the two test instances confirmed both rows (nova + orbit)
had a BARE `owner_home_instance`, matching the bug report exactly.

Three compounding fixes:

1. Receiver authority checks now compare via `normalizeOriginForCompare` so
   legacy bare-vs-full rows accept legitimate transfers (and kicks).
2. New `canonicalizeHomeInstance` helper in `federationAuth.ts`; every write
   site that persists `ownerHomeInstance` (`transferGroupDmOwnership`, group DM
   creation, lazy federation in member-add, `processMemberAddEvent` bootstrap,
   `processOwnershipTransferEvent` receiver storage) routes through it. Full URL
   is the canonical storage form, matching how `sourceInstance` arrives.
3. `dm_owner_updated` WS event extended with optional `newOwnerHomeUserId` and
   `newOwnerHomeInstance` fields. Client `updateDmOwner` writes them when
   present and leaves existing values untouched otherwise (legacy-server safe).
   Without this, `getOwnerInstanceForDm` returned the previous owner's home
   after a successful WS broadcast, routing the next owner-only op to the wrong
   instance.

Coverage: new `federation.ownershipTransfer.test.ts` (7 receiver tests including
the headline bare-vs-full regression and the dedup replay guard); new bare-vs-full
case in `federation.kick.test.ts`; two new client-side cases in
`groupDm.ownerRouting.test.ts` covering both the extended-payload write path and
the legacy-server passthrough. Tests: 1053 server + 364 web, all green.

Specs updated: `dm-system.md` historical bugs + frontend handler table + WS
state-change events table; `federation.md` `ownership_transfer` receiver flow;
`websocket.md` event-fields table.
2026-05-10 22:38:03 +02:00
Jannis Braun d55e85d2c5 feat(federation): PUBLIC_ORIGIN env override for getOurOrigin
Adds an explicit override for the federation transport URL returned by
getOurOrigin(). When unset, behaviour is unchanged (https://${DOMAIN} ->
http://localhost:${PORT} dev fallback). Intended for reverse-proxy /
dev-without-TLS deployments where the public origin must be advertised
explicitly (typically http://...) and differs from the bare DOMAIN
value used for federated identity.

Wired via config.publicOrigin (envOptional('PUBLIC_ORIGIN')) so the
override flows through the existing config layer rather than scattering
process.env reads. Trailing slash is stripped for symmetry with
peer.origin storage.

docs/systems/federation.md gets a "Public Origin Override" subsection
under §14 Background Workers documenting the resolution order.
2026-05-04 00:02:14 +02:00
Jannis Braun 1b63ca538e feat(federation): normalizeOriginForCompare helper 2026-04-25 21:14:58 +02:00
Jannis Braun dcf0bc1857 feat(federation): add verifyPeerSignature with dual-secret grace period (FED-011) 2026-03-31 20:40:38 +02:00
Jannis Braun d0ed43cf58 fix(federation): add nonce length validation and fix verification flow docs (FED-008) 2026-03-31 18:18:50 +02:00
Jannis Braun d7c30f2c50 feat(federation): add nonce to HMAC sign/verify and header build/parse (FED-008)
- signRequest now accepts optional nonce; payload becomes `${timestamp}.${nonce}.${body}` when present, falling back to `${timestamp}.${body}` for legacy peers
- verifySignature accepts matching nonce parameter and passes it through
- buildFederationHeaders generates a UUID nonce per request and includes X-Federation-Nonce header
- parseFederationHeaders extracts X-Federation-Nonce into nonce field (null when absent)
2026-03-31 18:07:19 +02:00
Jannis Braun 22664a6a14 refactor(federation): extract getOurOrigin() into shared federationAuth export
Consolidates 4 inline constructions of the instance origin URL into a
single shared function. Removes the private copy in federationWorker
and two ad-hoc domainOrigin variables in federationOutbox.
2026-03-26 18:39:42 +01:00
Jannis Braun c5188c1702 fix(federation): fix TypeScript strict mode error in parseFederationHeaders 2026-03-25 21:51:35 +01:00
Jannis Braun 2ff53a3f9b feat(federation): add HMAC signing and verification utilities
Implements federationAuth.ts with generateHmacSecret, signRequest,
verifySignature (constant-time, 15-minute replay window), buildFederationHeaders,
and parseFederationHeaders for server-to-server federation authentication.
2026-03-25 21:03:03 +01:00