17 Commits
Author SHA1 Message Date
Jannis Braun 493deefc64 docs(federation): document instance-epoch self-healing (Phase 2) 2026-07-02 02:09:19 +02:00
Jannis Braun 45e1c88bdc feat(federation): reset detection (markPeerReset) via handshake + probe 2026-07-01 22:09:43 +02:00
Jannis Braun e84daf57aa fix(voice): push voice presence to user on mid-session space join
Voice presence (voiceStates/voiceUserStates/spaceVoiceStates) was only ever
delivered in the WS `ready` payload — i.e. at connect/reload. A user joining a
space mid-session got `member_joined` (no voice data) and a bare space object;
`GET /api/spaces/:id` (the channel-sidebar hydrator) carries no voice state
either. So members already sitting in a voice channel stayed invisible in the
new member's sidebar until a full page reload.

Fix at the systemic root: ConnectionManager.addUserSpace — the single chokepoint
every join path funnels through (invite, public join, join-request approval),
and which is NOT used on reconnect (that path uses setUserSpaces) — now pushes a
scoped `space_voice_state` snapshot to the joining user. The snapshot is built by
a new buildSpaceVoiceState(spaceId, userId) helper that is also the single source
of truth feeding buildReadyPayload (refactored to use it), so the connect-time
and join-time paths can never drift.

Robustness:
- Delivered over the same ordered WebSocket as voice_state_update deltas — no
  REST snapshot-vs-event-stream race.
- VIEW_CHANNEL-filtered via computePermissions exactly like `ready`: a joiner is
  never told who occupies a voice channel they cannot see.
- Client applies it scoped to the space (utils/voiceStateSync.applySpaceVoiceState):
  merges occupants/statuses and rebuilds only that space's restriction keys,
  never disturbing voice state in other spaces.
- Skipped when the space has no active voice and no restrictions (e.g. space
  creation).

Tests: server helper behavior, the join push, and private-channel exclusion;
client scoped-apply. Specs updated (websocket.md, voice.md, spaces.md).
2026-06-30 17:00:37 +02:00
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 ba0f8637f5 docs(federation): document username on profile_update + new presence_update relay + stub backfill
- federation.md §10: extend FederationProfileUpdatePayload with username, document
  receiver fallback. Add Presence Sync sub-section: event shape, sender call sites,
  outbox-only (no mutation log) policy, peer-lifecycle hooks, flap recovery
  semantics. Add Stub Username Backfill sub-section + new
  /api/federation/users/by-home-id endpoint.

- activity-presence.md: resolve drift — line 147 previously claimed S2S
  presence_update relays existed but the code didn't ship them. Now points to
  federation.md §10 which describes the actually-implemented mechanism. Connect/
  Disconnect Flow updated to reflect collectProfileBroadcastTargetIds recipient
  set + S2S queueing.

- social.md / websocket.md: presence_update recipient column now reflects
  friends + DM + space co-members (matches user_updated), plus federated stub
  presence sourced via S2S.
2026-05-05 16:19:03 +02:00
Jannis Braun e35b44c05f docs(systems): outbound peering gate documentation across federation, db, api, ws, admin, client-federation
- federation.md: Outbound Peering Gate subsection (gate, intent contract,
  gate-but-don't-queue split, lifecycle invariant on onPeerActivated)
- database.md: peer_approval_requests direction + nullable hmac_secret +
  UNIQUE relaxation + CHECK; new peer_approval_subscribers + peer_approval_
  notifications tables
- api.md: /approval-requests direction-branched approve/deny + extended GET
  response; new peering-subscriptions and peering-notifications endpoints
- websocket.md: peering_subscription_changed, peering_notification_received
  events; federation_approval_request_received fires for outbound too
- admin.md: outbound row rendering with subscriber list
- client-federation.md: 'admin_required' status, peer_pending_local_admin
  error code, new Connections surfaces, federationStore slice

Spec §11 closes the Approve-button investigation finding.
2026-04-26 22:59:27 +02:00
Jannis Braun 34622a4290 docs(systems): #18 review followups — bullet formatting + host_unreachable phase
dm-system.md: fold the voice.md cross-reference into the paragraph so it
renders as part of the Cross-instance access explanation instead of an
orphaned bullet.

websocket.md: add 'host_unreachable' to the dm_call_undeliverable phase
union — stale since #32 was merged (docs drift noted in Task 8 review).
2026-04-24 21:22:21 +02:00
Jannis Braun 6edf02cb33 docs(systems): document three-way ack classification + no_recipient (#18)
federation.md — new undeliverable bucket subsection with three-way
classification table, Path A/B semantics, and wire backward-compat note.
voice.md — no_recipient row in failure-surface table.
websocket.md — DmCallUndeliverableReason union updated to include no_recipient.
dm-system.md — cross-reference to voice.md for no_recipient reason.
2026-04-24 21:19:34 +02:00
Jannis Braun 1719e6d580 docs(systems): document federation call state machine hardening 2026-04-23 23:23:06 +02:00
Jannis Braun 740dae298d docs: document call-relay auto-peering and dm_call_undeliverable surface 2026-04-21 14:05:59 +02:00
Jannis Braun ea8f786c2b docs: document pending peering approval queue, new endpoints, and awaiting_approval status 2026-04-20 15:16:07 +02:00
Jannis Braun f40ea03cfb docs: update system specs for federated DM calls v2 2026-04-08 03:28:54 +02:00
Jannis Braun 626a8ded50 docs: update subsystem specs for cross-instance DM access
Document read_state_update relay event, lifted DM gates,
federatedId dedup, and relaxed authority check.
2026-04-07 20:01:43 +02:00
Jannis Braun c9d2423c38 docs: add federation filtering note to ready payload spec 2026-04-02 11:38:25 +02:00
Jannis Braun 39dce67a7f docs: update specs for S2S DM unification and typing relay 2026-04-01 12:59:45 +02:00
Jannis Braun f97ac742cd docs: update websocket.md for FED-009 — dm_call_incoming federation fields, ActiveCallInfo extension 2026-04-01 00:02:56 +02:00
Jannis Braun a3a7527c9e chore: add system docs, specs, and misc updates from other sessions
- Add complete docs/systems/ reference (18 system docs)
- Add federation relay status doc and prior spec/plan docs
- Remove superseded docs/federation-dm-s2s.md (replaced by docs/systems/federation.md)
- CLAUDE.md updates
- Minor fixes in social.ts, types.ts, AddDmMemberModal, NewDmModal, UserSettings
2026-03-31 03:40:34 +02:00