fix(federation): close detached-account gaps from final review — presence/hydrate guards, ack re-detect clear, self-delete password (detach spec §4.3/§4.4/§4.6)

This commit is contained in:
Jannis Braun
2026-07-02 19:34:32 +02:00
parent 172398171a
commit 13d050c1bb
10 changed files with 190 additions and 8 deletions
+16
View File
@@ -4962,6 +4962,12 @@ export async function hydrateReplicatedUserProfile(
): Promise<typeof schema.users.$inferSelect> {
if (!profile) return user;
if (!user.homeInstance) return user; // Don't update native users
// Detached accounts are sovereign local accounts: the home domain now belongs
// to a different incarnation, so a relayed snapshot resolved via an old
// homeUserId (tier-1 historical hit) must never fill this row's fields. No-op
// return, mirroring the profile_update / presence_update / identity-delete
// guards (detach spec §4.3).
if (user.federationHomeOrphaned === 1) return user;
const baseUrl = user.homeInstance.startsWith('http') ? user.homeInstance : `https://${user.homeInstance}`;
const buildAbsoluteUrl = (value: string): string => {
@@ -6572,6 +6578,16 @@ export function processPresenceUpdateEvent(
return;
}
// Detached accounts are sovereign: the domain now belongs to a different
// incarnation, which must never flip the established account's presence by
// replaying its old homeUserId. Ack (not reject) — the sender considers this
// identity theirs to update; from our side the update simply no-ops.
if (localUser.federationHomeOrphaned === 1) {
console.log(`[federation] Skipping presence_update for detached account ${localUser.id} (home-orphaned)`);
accepted.push(event.messageId);
return;
}
db.update(schema.users)
.set({ status: payload.status })
.where(eq(schema.users.id, localUser.id))