Skip to content

Releases: ancsemi/Haven

v3.17.1 - critical hotfix for Docker / Node 24+ crash

Choose a tag to compare

@ancsemi ancsemi released this 23 May 18:05

Critical hotfix for v3.17.0

If you're on 3.17.0, update now. The shipped package.json contained an invalid UTF-8 byte that caused servers running newer Node versions and the official Docker image to crash on startup with ERR_INVALID_PACKAGE_CONFIG (#5374). The container would restart-loop forever and you'd see your server go offline. This release replaces the bad byte with plain ASCII so the file parses cleanly everywhere.

Fixed

  • Server fails to start with ERR_INVALID_PACKAGE_CONFIG on newer Node / Docker (#5374).
  • Expired or invalid auth tokens now redirect to the login screen on the first socket-auth rejection instead of leaving you stuck on an empty channel list with a "Server Error" red dot (#5375). The previous 3-strike retry counter could be reset by transport errors and never trip, stranding users after long absences.

Added

  • Show/hide password toggle (eye button) on every password input: login, register, SSO, account recovery, settings, transfer admin, delete account, E2E unlock. Catch typos before submitting.

What was in v3.17.0 (for anyone updating from earlier)

Added

  • Pinned messages PiP floating panel (#5370): pop-out button on the pinned messages panel opens a draggable, resizable PiP overlay so you can browse and manage pins without leaving the message feed. Includes fullscreen toggle.

Fixed

  • Muted channels: bot/webhook messages now respect mute state (no notifications, no sound, no unread bump).
  • Muted channels: server-emitted unread badges on reconnect no longer re-seed unread state for muted channels (badges stay clean across reloads).
  • DM status dot: offline DM recipients no longer show as online (#5372).
  • PiP panels: z-index now sits below the message input area so the composer is never covered (#5373).
  • Pinned messages panel: header buttons now align right consistently across themes.

v3.17.0

Choose a tag to compare

@ancsemi ancsemi released this 23 May 15:43

What's new in v3.17.0

Added

  • Pinned messages PiP floating panel (#5370). A pop-out button (⧉) in the pinned-panel header opens a draggable, resizable picture-in-picture overlay so you can browse and manage pins without leaving the message feed. Supports jump-to-message, unpin (with confirm), and live updates when pins change. Closes automatically on channel switch so stale pins never linger.
  • Fullscreen button for pins PiP. A maximize button in the PiP header expands the panel to fill the viewport, matching the DM PiP behavior.

Fixed

  • Muted channels now block bot/webhook notifications and badge re-seeding. Bot/webhook messages (sent with user_id = null) could bypass the per-channel mute check in the notification path. Also, on reconnect the server's stale unread count was being re-imported for muted channels, causing badges to reappear every session even for channels you'd muted.
  • DM status dot showing offline users as online (#5372).
  • PiP panel rendering below the message-input area (#5373). Z-index layering fix so PiP overlays always appear above the composer toolbar.
  • Pinned panel action buttons now right-aligned. Layout fix so the unpin and close buttons sit flush to the right edge of the panel header.

v3.16.15

Choose a tag to compare

@ancsemi ancsemi released this 20 May 15:38

v3.16.15

Fixed

  • Version display regression from v3.16.14 (#5369). The v3.16.14 release tag was created without bumping package.json, so servers running v3.16.14 reported version v3.16.13 via GET /api/version and the session-info socket event. Corrected in this release.
  • Voice chat: ACTUAL ROOT CAUSE of the recurring self-vanish bug (#5347) — channel code rotation desync. Every time the bug hit, the server's auto-rotation timer had just rotated the channel code, but voice clients kept emitting voice-rejoin / request-voice-users / voice-mute-state with the old code. The server couldn't match the old code (it was just updated), and the existing channel-code-rotated handler only migrated this.currentChannel — never this.voice.currentChannel. Every voice client was stuck with a dead reference, the watchdog loop ran forever, peers couldn't connect, and manual leave+rejoin was the only way out.
    • Client now migrates voice-side state on rotation (this.voice.currentChannel and this.voice._softLeftChannel), not just the text-channel state.
    • Server now migrates the voice socket-room AND broadcasts the rotation event to the voice room too, so users in voice but not viewing the text channel still receive the update.
    • pendingVoiceLeave grace-period keys are rekeyed to the new code so an in-flight disconnect mid-rotation can still be cancelled correctly.
  • Voice chat: null audioCtx.currentTime crash on leave (voice.js:1829). The noise-gate setInterval and its hold-timeout could fire once after leave() nulled this.audioCtx, throwing Cannot read properties of null. Added guards so both paths bail safely if the audio context is gone.

v3.16.14

Choose a tag to compare

@ancsemi ancsemi released this 19 May 17:46

Fixed

Voice chat: recurring self-vanish glitch (#5347, again). Previous patches deferred the soft-leave and added a passive self-inject, but neither stopped the root cause — the server's \disconnect\ handler was eagerly evicting the user from \�oiceUsers\ on every blip and broadcasting \�oice-user-left\ to peers. This release stops that at the source:

  • Server-side 4 s grace period before evicting on disconnect. If the user's voice slot is reclaimed by a \�oice-join\ or \�oice-rejoin\ from a new socket before the timer fires, the eviction is cancelled, \�oiceUsers\ is rebound to the new socketId, and peers are never told \�oice-user-left\ — meaning their RTCPeerConnections stay live and audio is uninterrupted across the blip.
  • Fast-path rejoin that skips renegotiation. When the grace-period fast-path triggers, the server sends \�oice-existing-users\ with a new \skipRenegotiate\ flag so the rejoining client does not rebuild fresh RTCPeerConnections on top of working ones.
  • Client-side voice roster watchdog. Every 10 seconds, if we're in voice and the socket is connected, the client polls the server's authoritative roster. If the server confirms we're missing despite claiming to be in voice, \�oice-rejoin\ fires automatically — so any remaining edge case self-corrects within 10 s instead of sticking forever.
  • Verbose [VoiceDiag]\ / [VoiceWatchdog]\ / [VoiceSelfHeal]\ server + client logs at every critical transition.

v3.16.13

Choose a tag to compare

@ancsemi ancsemi released this 18 May 14:22

This release rolls up everything since v3.16.10 (v3.16.11 and v3.16.12 were committed but never tagged, so their changes are included here too).

v3.16.13 — added

  • Channel and sub-channel composers are now drag-resizable (#5327). Grab the handle at the top of the message-input area and drag up to expand the textarea up to 60% of the viewport. The dragged height is sticky across keystrokes and sends until you drag it back down.
  • Sub-channels inherit parent channel role access on creation (#5328). New sub-channels automatically copy the parent's role_channel_access rows and grant membership to current holders of any role marked grant_on_promote. Existing sub-channels are untouched, and per-sub access can still be customised afterwards.

v3.16.13 — fixed

  • Voice chat: "I lose myself in the right panel after a while and have to manually leave + rejoin, which kicks everyone." Two converging issues addressed:
    • Transient socket blips no longer instantly tear down the voice session. The disconnect handler used to call _softLeave() on every dropped frame, killing the mic and every RTCPeerConnection. Socket.io reconnects within a few hundred ms on most blips (Electron renderer suspending, brief Wi-Fi hiccup), so the teardown is now deferred by 2 s and cancelled if the socket reconnects in time. voice-rejoin rebinds our voice slot to the new socketId without rebuilding mic or peers.
    • When the panel still loses us, we no longer just patch the UI silently. The voice-users-update handler now also emits voice-rejoin (throttled to once per 3 s) whenever it has to inject self into the roster, so the server cleans up any stale entry of us and re-broadcasts the fresh roster to peers automatically — no manual leave+rejoin needed.

v3.16.12 — fixed

  • Voice chat: missing-self in the right panel after a server restart / reconnect race. When the server briefly didn't have us in the voice roster at the moment a voice-users-update was rendered, the right panel could show every other participant but omit ourselves. _renderVoiceUsers() now belt-and-suspenders injects the local user when we're in voice on the channel being rendered, tracked via a new _lastVoiceUsersChannel so the injection is correctly scoped. The voice-count-update handler does the same for the sidebar badge.
  • Voice chat: stale roster after a server reboot. request-voice-users server-side now prunes stale voice entries before responding and re-broadcasts a fresh roster if any ghosts were removed.
  • "Start Voice did nothing, I clicked it 15 times and got 15 toasts at once." _fetchIceServers() now aborts after 4 s and falls back to default STUN-only config; _joinVoice() ignores re-entrant presses while a join is in-flight; and voice.join() no longer buffers a voice-join emit while the socket is disconnected.

v3.16.11 — fixed

  • Removed servers no longer reappear after a restart. ServerManager.add() now honors the local "removed" set on bootstrap and sync paths — only an explicit click on Add Server in the modal can resurrect a previously-removed entry.
  • Transient server restarts no longer log users out. The socket connect_error handler now requires three consecutive auth errors before clearing the token and bouncing to the login page, instead of nuking the session on the first error.

Other

  • UI polish on the create-channel modal (checkbox hints moved to tooltips, shorter labels).

v3.16.10

Choose a tag to compare

@ancsemi ancsemi released this 16 May 03:29

Fixed

  • Displayed server version was stuck at v3.16.5 even after updating (#5364). The v3.16.6 commit bumped package.json off-by-one to 3.16.5, and none of the subsequent v3.16.7 / v3.16.8 / v3.16.9 commits actually bumped the version field. Because both /api/version and the session-info socket emit read directly from package.json, every install since v3.16.6 has reported itself as v3.16.5 in the status bar regardless of which release was actually deployed — which also kept the in-app update banner flagging an update users had already installed. Fixed by bumping package.json to 3.16.10 and bumping all ?v= cache-bust query strings in app.html so the client also picks up the freshly versioned assets.
  • Belatedly shipping the originally-intended v3.16.5 fixes. The [3.16.5] changelog entry described an edit-message DM-PiP fix and a more detailed encrypted-upload error toast, but the corresponding code changes (app-admin.js, app-utilities.js, socketHandlers/messages.js) were never committed and only existed in the working tree. They are now committed as part of this release. Editing a message from a DM PiP while focused on a different server channel now resolves the correct channel server-side, and encrypted DM upload failures now show the underlying error message in the toast.

v3.16.8

Choose a tag to compare

@ancsemi ancsemi released this 16 May 00:30

Fixes a phantom unread badge that would appear on the currently-viewed channel after returning from the background.

Fixed

  • Phantom unread badge on current channel after returning from background. When the app was backgrounded (alt-tabbed, minimised, or the BrowserView was hidden in Desktop's multi-server switcher) while the user was already at the bottom of a channel, incoming messages bumped the sidebar unread badge even though the user was actively reading that channel. The badge-clearing code only fires when a new message arrives while the page is visible, so if no further messages arrived the stale badge was stuck permanently. The fix clears the badge (and syncs \mark-read\ to the server) immediately when the window/tab regains visibility and the user is still coupled to the bottom of the current channel.

v3.16.6

Choose a tag to compare

@ancsemi ancsemi released this 15 May 17:23

Added

  • Unpin from the pinned-message panel. Users with the pin_message permission (or admin) now see an Unpin button on every entry in the pinned-messages panel. Clicking it shows a confirmation before removing the pin — no more having to scroll back to the original message to unpin it, which was especially painful in encrypted DMs.

Fixed

  • Webhook management ignored the manage_webhooks permission. The create-webhook, get-webhooks, delete-webhook, and toggle-webhook handlers all rejected non-admin users even when they had manage_webhooks granted through a role. The per-channel Webhooks option in the channel context menu was also hidden for them. Both are now fixed. The bot-manager panel in Admin Settings remains admin-only.
  • Editing messages in a DM PiP did nothing. The edit handler always resolved the channel from socket.currentChannel. If you were viewing a server channel while a DM was open in the floating PiP, the edit silently failed and rolled back. The handler now accepts an optional channelCode from the client (same pattern as delete), and the client sends it when editing from the PiP.
  • Encrypted image upload failure toast always said the same generic message. The error toast for encrypted image upload failures now includes the underlying error (e.g. "Upload failed (403)") instead of a fixed string.

v3.16.4

Choose a tag to compare

@ancsemi ancsemi released this 14 May 20:49

Two bug fixes.

Fixed

  • Video and audio files mis-categorized in media gallery (#5361). File attachments are stored as [file:name](url|size). The |size suffix was being included in the URL string when the gallery parser ran, so the extension check (e.g. .mp4(?:$|[?#])) never matched and everything fell into the Files tab instead of Videos or Audio. The parser now strips the size suffix before checking the extension.
  • Unicode letters blocked in channel names (#5362). The channel-name validation regex used \w which is ASCII-only in JavaScript even with the unicode flag, so umlauts, accented letters, and other non-ASCII characters were rejected with "invalid characters." Added \p{L}\p{M} (Unicode letter and combining-mark categories) to the regex so any script is accepted.

v3.16.0

Choose a tag to compare

@ancsemi ancsemi released this 12 May 16:29

Discord history import is now fully idempotent. Previously, importing overlapping exports (e.g. two people exporting the same server at different times) would produce duplicate messages and duplicate channels. Now the importer handles all of this automatically.

Added

  • Idempotent Discord import. Each imported message now stores its original Discord snowflake ID. Re-importing the same export, or importing a second overlapping export, skips messages that already exist rather than inserting them again. Native Haven messages are completely unaffected — the deduplication only touches imported messages.
  • Channel reuse on re-import. Imported channels now store their Discord channel ID. If you import a second export that contains the same Discord channel, the new messages are appended to the existing Haven channel instead of creating a second duplicate channel.
  • Import result details. The success message after import now reports how many duplicate messages were skipped and how many existing channels were updated, in addition to the usual channel/message counts.