Commit Graph
930 Commits
Author SHA1 Message Date
Jannis Braun cb9a70d7a6 fix(federation): use canonical_pair_id for sync scope instead of home_instance matching
The old query missed channels where both members appear as 'local'
(e.g., native Nova + native youruser on orbit). Using
canonical_pair_id is simpler and correct — any channel with a pair
ID is a federated DM that should be synced.
2026-03-26 04:41:00 +01:00
Jannis Braun 49f88c2552 fix(federation): actually remove source_instance IS NULL filter from sync SQL queries 2026-03-26 04:37:47 +01:00
Jannis Braun 277ff0e50c fix(federation): sync ALL locally-created messages, not just local-home-user messages
Messages by replicated users (e.g., Jannis browsing orbit) have
source_instance IS NULL because they were created on that instance.
The sync endpoint was incorrectly filtering these out, causing most
DM history to be missing from the sync response.
2026-03-26 04:34:13 +01:00
Jannis Braun ae004b72e1 feat(federation): trigger checkpoint sync on startup for peers that have never been synced 2026-03-26 04:30:54 +01:00
Jannis Braun a3d41fd568 fix(federation): handle existing canonical_pair_id during backfill by merging duplicate first 2026-03-26 04:25:03 +01:00
Jannis Braun 306e26a525 fix(federation): remove require('crypto') — already imported as ESM at top of file 2026-03-26 04:22:08 +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 4a109f71da fix(federation): backfill canonical_pair_id and merge duplicate DM channels
Existing 1-on-1 DM channels created before federation had canonical_pair_id = NULL,
causing the relay to create duplicate channels on receipt of a message. On server
startup, migrations now: (1) compute and set canonical_pair_id for all existing
1-on-1 channels using home_user_id (falling back to regular id for local users),
and (2) merge any duplicate channels that share the same canonical_pair_id by
moving all messages, read states, and members into the oldest channel and deleting
the stale duplicate. Both steps are non-fatal and idempotent.
2026-03-26 04:15:12 +01:00
Jannis Braun 73a4b69fe6 fix(federation): auto-peering on reconnect and DM deduplication across origins 2026-03-26 03:59:59 +01:00
Jannis Braun b34732e064 fix(federation): enable relay by default — it fixes a bug, not an optional feature
Federation relay was incorrectly defaulting to disabled, requiring
admins to discover a buried settings toggle. Since federation itself
is opt-in (you connect instances manually), relay should be on by
default. Also migrates existing instances from 0 → 1.
2026-03-26 03:54:20 +01:00
Jannis Braun 2b2f64e7f9 fix: prune stale Docker images and build cache after each deploy
Adds a post-deploy step that removes Docker images and build cache
older than 24 hours. Prevents the gradual disk bloat from repeated
docker compose --build invocations (was 71GB on the Pi).
2026-03-26 03:31:52 +01:00
Jannis Braun c5188c1702 fix(federation): fix TypeScript strict mode error in parseFederationHeaders 2026-03-25 21:51:35 +01:00
Jannis Braun 46e6f8a7e2 feat(federation): add cleanup functions for outbox, mutation log, and file queue TTL 2026-03-25 21:48:46 +01:00
Jannis Braun 44de9b6f41 feat(federation): add client peering integration, transparency indicator, and admin federation settings
Task 13: Hook server-to-server peering into connectToRemote (non-fatal)
and add federation API namespace to the client (initiate, peers, revoke).

Task 14: Show a transparency notice in the DM welcome header when the
other user is on a remote instance, informing that messages are stored
on both home instances and are not end-to-end encrypted.

Task 15: Add Federation section to the instance settings General panel
with DM relay toggle, TTL config, and a live peer list with revoke
buttons. Also extends InstanceAdminSettings type and the server settings
route to support federationRelayEnabled / federationRelayTtlDays.
2026-03-25 21:47:10 +01:00
Jannis Braun 3898e4940a feat(federation): add outbox delivery, file queue download, and health check workers 2026-03-25 21:39:32 +01:00
Jannis Braun 7f20a4517c feat(federation): implement checkpoint sync endpoint using mutation log
Add POST /api/federation/sync for peer instances to catch up on missed
DM mutations after downtime. Uses HMAC-SHA256 authentication (same as
relay), determines shared channels between peers via raw SQL join on
dm_members/users, queries the federation_mutation_log with timestamp
pagination, and returns FederationRelayEvent payloads for each mutation
type (create, update, delete, reaction_add, reaction_remove).
2026-03-25 21:33:47 +01:00
Jannis Braun 5b9272e22b feat(federation): implement relay receive endpoint with dedup, channel mapping, SSRF validation 2026-03-25 21:27:28 +01:00
Jannis Braun fbdec82172 feat(federation): add peering handshake and admin peer management endpoints
Add federation routes module with four endpoints:
- POST /api/federation/peer/initiate — admin-only, starts peering handshake
  with a remote instance (validates origin, generates HMAC secret, sends
  server-to-server accept request with 10s timeout)
- POST /api/federation/peer/accept — server-to-server first contact,
  rate-limited to 10 req/min per IP, creates/activates peer record
- GET /api/federation/peers — admin-only, lists all peers (hmacSecret excluded)
- DELETE /api/federation/peers/:id — admin-only, revokes peer and cleans outbox
2026-03-25 21:19:47 +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
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
Jannis Braun 6afddab54e feat(federation): add shared types for relay events, sync, and peer management 2026-03-25 21:01:26 +01:00
Jannis Braun 4726eeb097 feat(federation): add migrations for federation tables, indexes, and mutation log backfill
Adds column migrations (dm_channels.canonical_pair_id, dm_messages.source_*,
attachments.source_url, instance_settings.federation_relay_*), CREATE TABLE
for federation_peers/outbox/file_queue/mutation_log, partial/filtered indexes,
and an idempotent backfill that seeds the mutation log from existing DM messages.

Also makes migrateDmMessagesReplyToFk and migrateAttachmentsDmMessageFk
column-aware so table recreation preserves any columns added by earlier
migrations (prevents dropping federation columns on upgrade).
2026-03-25 21:00:10 +01:00
Jannis Braun c80d565f7f feat(federation): add federation tables and columns to schema 2026-03-25 20:53:43 +01:00
Jannis Braun a1363a1ab3 fix: match GIF-only message size to standard media constraints
GIFs from Klipy were capped at 250×250px while all other visual media
(attachments, image embeds) used 400×300px. Unified to 400×300px.
2026-03-25 14:42:27 +01:00
Jannis Braun f7fa7371d6 fix: hide Open Original when sourceUrl is present to avoid redundancy 2026-03-25 05:13:14 +01:00
Jannis Braun e0d45da97c refactor: skip imageEmbedSourceUrl computation when isGifOnly is true 2026-03-25 05:05:49 +01:00
Jannis Braun 4fd72cd29b fix: remove redundant !isEditing guard in image-embed-only branch 2026-03-25 05:02:38 +01:00
Jannis Braun 001d45da2b feat: suppress URL text for image-only messages, add sourceUrl to context menu 2026-03-25 04:59:53 +01:00
Jannis Braun 5c45a55ef7 fix: add clipboard error handling for Copy Link context menu item 2026-03-25 04:58:27 +01:00
Jannis Braun d1fad2b262 feat: add sourceUrl param to context menu with Copy Link and Open Link items 2026-03-25 04:55:44 +01:00
Jannis Braun f433093777 fix: remove 150px threshold from scroll observers
The dist < 150 check caused the ResizeObserver and media load
listener to miss large layout shifts (e.g., GIF loading adding
400+ px of height). The isAtBottomRef gate already determines
whether to auto-scroll — the pixel threshold is redundant.
2026-03-25 04:50:24 +01:00
Jannis Braun 6817f65e56 docs: update instance_settings schema in CLAUDE.md (add missing columns + allow_custom_bitrate) 2026-03-25 04:09:54 +01:00
Jannis Braun f0104d19b7 feat: conditionally show bitrate slider based on instance allowCustomBitrate 2026-03-25 04:09:18 +01:00
Jannis Braun 3627974ba1 feat: add Allow Custom Bitrate toggle to admin streaming panel 2026-03-25 04:08:10 +01:00
Jannis Braun de6db687c4 feat: enforce allowCustomBitrate flag in stream option builders 2026-03-25 04:07:15 +01:00
Jannis Braun 64d919c765 feat: add allowCustomBitrate to DEFAULT_LIMITS in settingsStore 2026-03-25 04:05:50 +01:00
Jannis Braun b4582a53f1 feat: wire allowCustomBitrate through rowToLimits and PATCH handler in settings route 2026-03-25 04:05:48 +01:00
Jannis Braun f02cb4e52a feat: add allow_custom_bitrate column to instance_settings schema and migration 2026-03-25 04:05:43 +01:00
Jannis Braun 35573c9f37 feat: add allowCustomBitrate to InstanceStreamingLimits shared type 2026-03-25 04:05:40 +01:00
Jannis Braun c60fb5af6d fix: resolve chat scroll race condition on channel open
Remove isAtBottom state from scroll effect deps to break the
re-triggering feedback loop. Use isAtBottomRef (ref) instead.
Gate ResizeObserver and media load effects behind the ref so
they only auto-scroll after the initial snap completes.
2026-03-25 04:03:14 +01:00
Jannis Braun dae6f2d518 fix: remove forced 4/3 aspect ratio and dark background from image containers
Images now size naturally within max constraints instead of being
letterboxed into a 4/3 bg-surface-input container. Fixes dark bars
on GIFs and images whose aspect ratio doesn't match 4/3.
2026-03-25 03:37:18 +01:00
Jannis Braun c255b0066a fix: copy GIF URLs as text to preserve animation
PNG conversion strips GIF animation. GIFs are now detected by URL
pattern (.gif extension or Tenor/Klipy CDN) and copied as URL text
instead, so pasting back into chat re-renders the animated GIF.
2026-03-25 03:28:00 +01:00
Jannis Braun aeb4ea6e1d fix: exclude embed thumbnails from image context menu actions
Add data-embed-thumbnail marker to VideoEmbed, GenericEmbed, and
RichEmbed thumbnail containers. Update detection logic to skip
these alongside avatars. Also remove redundant filename derivation
in Save Image menu item.
2026-03-25 03:23:23 +01:00
Jannis Braun 4ea319646a feat: add save/copy buttons to image preview modal 2026-03-25 03:19:17 +01:00
Jannis Braun 91ffbb5d0a fix: add noopener to window.open fallback in saveImage 2026-03-25 03:15:54 +01:00
Jannis Braun 97f0ebc915 feat: add image actions to message context menu
Detect right-clicked images in handleContextMenu and prepend
Save Image, Copy Image, and Open Original to the custom menu.
Avatars are excluded via the data-avatar semantic marker.
2026-03-25 03:13:58 +01:00
Jannis Braun 74939f045f feat: add data-avatar attribute for image context menu exclusion 2026-03-25 03:10:37 +01:00
Jannis Braun 6bd2100e04 feat: add saveImage and copyImageToClipboard utilities
Implements image save/download (blob fetch + anchor trigger, fallback to window.open)
and clipboard copy (PNG write via ClipboardItem, fallback to URL text copy) with
toast feedback. Also adds ClipboardItem polyfill and Response.blob() content-type
fix to the jsdom test setup so the clipboard test suite runs correctly.
2026-03-25 03:09:51 +01:00
Jannis Braun 78555a43f8 fix(voice): stabilize grid layout across focus-mode and fullscreen transitions
Refactor useGridLayout from RefObject to callback-ref API so the
ResizeObserver reattaches when the grid container remounts after
focus-mode toggle. Fixes tiles rendering at stale fullscreen dimensions
on 4:3 monitors.

Also clamp focus-mode participant strip height (max-h-[20vh] min-h-[80px])
to prevent it from crowding the focused stream on short viewports.
2026-03-25 03:08:45 +01:00