feat(federation): queue S2S presence_update on auth/disconnect/status/activity changes

New FederationPresenceUpdatePayload + queuePresenceRelay() helper. Five WS
sites now project the native user's status (and optional activities) to all
active peers via the outbox: WS auth-success, finalizeDisconnect,
manual presence_update, activity_update, showActivity-toggle clear.

Outbox-only (no mutation-log entry) — presence is ephemeral; the upcoming
peer-activation hook re-emits a fresh snapshot so peers recovering from
unreachable converge without history replay. No-op for replicated users.
This commit is contained in:
Jannis Braun
2026-05-05 16:01:28 +02:00
parent bdbc90ebd2
commit 613424e1c7
6 changed files with 230 additions and 1 deletions
+16 -1
View File
@@ -882,7 +882,7 @@ export interface FederationRelayEvent {
| 'friend_add' | 'friend_remove' | 'file_rejected'
| 'dm_call_start' | 'dm_call_accept' | 'dm_call_reject' | 'dm_call_end'
| 'dm_typing_start' | 'dm_typing_stop'
| 'profile_update'
| 'profile_update' | 'presence_update'
| 'read_state_update'
| 'dm_close' | 'dm_reopen';
contextType?: 'dm' | 'friend' | 'profile';
@@ -922,6 +922,7 @@ export interface FederationRelayEvent {
username: string;
};
profileUpdate?: FederationProfileUpdatePayload;
presenceUpdate?: FederationPresenceUpdatePayload;
readState?: {
user: { homeUserId: string; homeInstance: string };
messageRef: { sourceInstance: string; sourceMessageId: string };
@@ -986,6 +987,20 @@ export interface FederationProfileUpdatePayload {
bio: string | null;
}
/**
* Presence projection from a home instance to peers. Carries the user's current
* online status and (optionally) rich activities. Outbox-only on the wire — never
* written to federation_mutation_log; presence is ephemeral and stale replays on
* peer activation are wrong (the activation hook re-emits a fresh snapshot).
*/
export interface FederationPresenceUpdatePayload {
homeUserId: string;
homeInstance: string;
status: 'online' | 'idle' | 'dnd' | 'offline';
activities?: Activity[];
ts: number; // emitter clock; receiver may use for last-write-wins
}
export interface FederationFriendshipPayload {
from: FederationRelayParticipant;
to: FederationRelayParticipant;