Commit Graph
25 Commits
Author SHA1 Message Date
Jannis Braun 53483d6981 polish(server): align sendCallRelay timeout message with codebase convention; use .then on sendTypingRelay fire-and-forget 2026-04-21 13:49:20 +02:00
Jannis Braun 21f220739c feat(server): sendCallRelay auto-peers on demand, typing passes peeringTimeoutMs:0
sendCallRelay now returns CallRelayResult with a typed reason on failure.
When the peer is not already active (or unreachable), runs a racePeering
against CALL_PEERING_TIMEOUT_MS (3s). Background handshake is not aborted
on race loss — next attempt succeeds.

sendTypingRelay passes peeringTimeoutMs:0 so typing never blocks on a
handshake; instead a warm-up ensurePeered runs in the background for any
non-active peer so the NEXT relay (message, call, or typing) benefits.
2026-04-21 13:45:29 +02:00
Jannis Braun 6afad97bd1 fix: revert outgoing peering blocks — autoAcceptPeering only gates incoming
autoAcceptPeering means 'don't accept peering initiated by others', not
'don't initiate peering ourselves'. Two checks were incorrectly blocking
outgoing peering when auto-accept was off:

1. ensurePeered() refused to auto-initiate — reverted. When a local user
   sends a DM, the server should initiate peering. The remote's
   peer/accept decides whether to accept or queue.

2. queueOutboxEvent() refused to create placeholders — reverted. The
   outbox needs placeholders to queue entries. Without them, DM relay
   silently fails.
2026-04-20 18:08:05 +02:00
Jannis Braun 072858cbbb fix: multiple federation peering bugs
1. queueOutboxEvent no longer creates pending peer placeholders when
   autoAcceptPeering is disabled — prevents bypassing the admin's
   peering control

2. Approval endpoint checks for 202 before response.ok — when the
   remote also has autoAcceptPeering off, sets peer to awaiting_approval
   instead of incorrectly activating it

3. awaiting_approval status added to Federation panel UI — status label,
   colors, filter options so these peers are visible and manageable
2026-04-20 17:54:00 +02:00
Jannis Braun 34fe9115b9 fix: compute target origins for 1-on-1 DMs so pending peers are created
getGroupDmTargetOrigins() returned undefined for 1-on-1 DMs, which
queueOutboxEvent() treated as 'broadcast to all existing peers'. When
no peers existed, nothing was queued and no handshake was ever triggered.
Now always computes target origins from DM participants so the pending
placeholder creation path runs, enabling ensurePeered() → peer/accept
→ approval queue flow.
2026-04-20 17:15:22 +02:00
Jannis Braun b434a736a8 fix: address code review findings (C1, I1, I2)
- C1: Include 'unreachable' peers in queueOutboxEvent query to prevent
  UNIQUE constraint violation when creating placeholders
- I1: Add 'rejected' to StatusFilter in FederationPanel so admins can
  see and manage rejected peers with delete/re-initiate actions
- I2: Map ensurePeered 'failed' to 'pending' in peer/ensure response
  to match spec and client expectations
2026-04-09 14:08:54 +02:00
Jannis Braun d5a6d470f9 feat: queueOutboxEvent creates pending peer placeholders, skips rejected/revoked 2026-04-09 13:49:04 +02:00
Jannis Braun 053f5efb4b feat: add queueDmCloseRelay utility for S2S close/reopen relay 2026-04-07 22:23:25 +02:00
Jannis Braun 1ada46baff fix: relay mark_unread to peers, fix docs and timestamp consistency
- Add queueReadStateRelay call in handleMarkUnread (skip '0' sentinel)
- Fix double Date.now() in queueReadStateRelay (use single const)
- Fix federation.md: read state relay uses outbox (not fire-and-forget),
  correct payload schema to match implementation
2026-04-07 20:08:31 +02:00
Jannis Braun 32ccd9c410 feat: outbound S2S read state relay
Queue read_state_update events when users ack DM messages on
channels with a federatedId. Translates local message IDs to
federation coordinates using sourceInstance/sourceMessageId.
2026-04-07 19:49:52 +02:00
Jannis Braun b3011fb3da feat(federation): add fire-and-forget S2S typing relay
sendTypingRelay() mirrors sendCallRelay() — direct POST to peers,
no outbox, no retry. Uses federatedId for cross-instance channel
identification. Wired into handleDmTypingStart() for typing_start
and broadcastDmMessage() for typing_stop.
2026-04-01 12:53:55 +02:00
Jannis Braun 75b84c6ecb feat(federation): add sendCallRelay for direct S2S delivery (FED-009) 2026-03-31 23:22:06 +02:00
Jannis Braun 70678ff198 fix(federation): batch A production readiness — normalization, logging, security
FED-001: normalize homeInstance in processCreateEvent member skip
FED-002: normalize homeInstance in getFriendEventTargets
FED-003: normalize homeInstance in handleSizeRejection
FED-004: add warning log when queueOutboxEvent drops events (zero peer match)
FED-012: remove unused challenge from peer handshake
FED-013: reject non-HTTPS origins in validateOrigin (except localhost)
2026-03-31 03:33:22 +02:00
Jannis Braun 44b6317c16 fix: federation relay for group DMs — origin normalization, missing federatedId, local-only broadcasts
Three stacked bugs prevented federation relay from working for group DMs:

1. Origin format mismatch: users.home_instance stores bare domains
   ("nova.ddns.net") but federation_peers.origin stores full URLs
   ("https://nova.ddns.net"). getGroupDmTargetOrigins() built target
   lists from bare domains, so queueOutboxEvent() never matched any
   peers — events were never queued into the outbox.

2. Missing federatedId in outbox reconstruction: the outbox worker
   rebuilt relay events from stored payloads but never copied the
   federatedId field. Receiving instances check this field and rejected
   all member_add/remove/ownership_transfer events with
   "missing_membership_payload".

3. Duplicate channels from cross-instance broadcasts: dm_channel_created
   was sent to ALL members including remote replicas. Users connected to
   multiple instances received the event twice (once per instance),
   creating duplicate group DMs in their sidebar. Fixed by only
   broadcasting to members whose home instance matches the current
   server — remote members receive the channel via federation bootstrap
   on their home instance.
2026-03-27 16:53:35 +01:00
Jannis Braun 0d6bb9dbd3 fix(federation): auto-create and hydrate user profiles during DM relay
Federated 1-on-1 DMs showed the raw snowflake ID as the display name
and no avatar when the remote user had no pre-existing local record.
processCreateEvent used resolveLocalUser (find-only) instead of
resolveOrCreateReplicatedUser, and relay events carried no profile data
for participants.

- Add profile snapshot (displayName, avatar, avatarColor) to
  FederationRelayParticipant and populate it in getDmParticipants
- Change processCreateEvent to auto-create replicated user stubs and
  hydrate them with profile data from the relay event
- Fix hydrateReplicatedUserProfile URL resolution for homeInstance
  values without protocol prefix
- Fix WelcomeHeader: return null while DM data is loading (eliminates
  "unknown" flash on reload), use displayName for @mention text
2026-03-27 03:43:45 +01:00
Jannis Braun fe3d8b06d8 refactor(federation): generalize outbox function params and add friend relay helpers
Rename appendMutationLog and queueOutboxEvent params from DM-specific names
(dmMessageId/dmChannelId/messageId) to generic (entityId/contextId) with a new
contextType param defaulting to 'dm'. Update all internal schema column references
to match the renamed outbox/mutation-log schema columns. Add buildFriendContextId
and getFriendEventTargets helper functions for friend event relay routing.
2026-03-27 00:43:51 +01:00
Jannis Braun f7ba391678 feat(federation): handle group DM messages via federatedId lookup in relay processor
In processCreateEvent, branch on event.federatedId: group DM messages now
look up the pre-bootstrapped local channel by federatedId instead of
computing a pair hash from two participants. In queueDmRelay, fetch the
channel's federatedId and ownerId and include federatedId in the outgoing
relay payload for group DMs so receiving instances can route correctly.
2026-03-26 20:26:23 +01:00
Jannis Braun 514d46c82e feat(federation): add participant-aware fan-out routing for group DMs 2026-03-26 20:10:51 +01:00
Jannis Braun 0742c37ca1 refactor(federation): rename canonicalDmPairId to computeFederatedId 2026-03-26 20:08:37 +01: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 58e6dbabcd feat(federation): add queueDmRelay() to consolidate relay payload construction
Single function builds complete relay payload with attachments (including
sourceUrl) and participants. Eliminates 4-way duplication that caused
the attachments: [] hardcode bug.
2026-03-26 06:12:56 +01:00
Jannis Braun cf9fcb78ed fix(federation): add participants array to relay events and fix recipient resolution
The relay was failing because processCreateEvent relied on the friends
table to discover the DM recipient, but friendships aren't federated
across instances. Also, resolveLocalUser matched deleted replicated
users before active ones.

- Add participants[] to FederationRelayEvent with homeUserId/homeInstance
  for all DM channel members
- Add getDmParticipants() helper to look up member identities
- Include participants in outbox payloads (create/update) and sync events
- Rewrite processCreateEvent to resolve participants directly, compute
  canonicalDmPairId, and findOrCreateDmChannel — removing the entire
  friends-list fallback (60+ lines)
- Fix resolveLocalUser to filter out deleted users (is_deleted = 0)
  and prefer the replicated user match when multiple candidates exist
2026-03-26 05:36:41 +01:00
Jannis Braun 960ca1c97f fix(federation): set correct homeInstance in relay payload and fix canonical backfill SQL
- buildRelayPayload now uses config.domain for local users instead of
  empty string, so the relay receiver can resolve the user
- Fixed canonical_pair_id backfill: SQLite NULL || ':' || x = NULL,
  so use COALESCE(home_user_id, id) instead of concatenation
2026-03-26 04:19:18 +01:00
Jannis Braun 32a0c2e618 feat(federation): hook outbox and mutation log into DM message and reaction handlers
Wire appendMutationLog + queueOutboxEvent + buildRelayPayload into all
DM mutation paths so federation peers receive relay events:

- REST: POST /api/dm/:id/messages, PATCH /api/dm/messages/:id,
  DELETE /api/dm/messages/:id
- WebSocket: dm_message_create, dm_message_edit, dm_message_delete,
  reaction_add (DM path), reaction_remove (DM path)
- Fix buildRelayPayload parameter types to accept optional replyToId
  and editedAt (matching DmMessageWithUser's optional fields)
2026-03-25 21:14:34 +01:00
Jannis Braun 88ad1c676d feat(federation): add outbox coalescing writer, mutation log, and relay helpers 2026-03-25 21:07:16 +01:00