Releases: ancsemi/Haven
Release list
v3.29.0
Added
- Spoiler images and a per-viewer Hide Image option. When sending an image you can now mark it as a spoiler — an eye toggle on each queued image — and it renders blurred behind a "Spoiler" label for everyone, revealing on click. Works for normal uploads, encrypted DM images, and persona/bundled sends. Separately, you can right-click any image already in chat and choose "Hide Image" to collapse it to a placeholder in your own view only; it's stored per-device, and clicking the placeholder restores the image.
- Invite link management. Admins can now create and manage multiple invite links from a dedicated menu, each with its own per-link channel grants, optional expiry, and use limits. Copying a link is reliable with a clear success toast and a dedicated popout, and the landing site can auto-join the community from an
?invite=link instead of manual code entry.
Fixed
- Guests couldn't reach sub-channels or voice rooms in their allowed area (#5401). The guest access picker only listed top-level channels and auto-joined just the public sub-channels under a whitelisted parent, so private sub-channels were unreachable and voice rooms a guest wasn't a member of failed with "not a member of this channel" (the server already allowed guests to use voice once they were in the room). The picker now lists every channel individually — parents, sub-channels, and voice rooms, each tagged so you can see what's private or voice — and guests are joined to exactly the rooms you select (ticking a sub-channel includes its parent so it appears in the sidebar). The server still enforces membership and per-channel settings, so nothing is widened beyond what you grant.
- Back-to-back messages from one person showed as separate blocks in pop-out DMs and threads. Consecutive messages from the same sender within a few minutes now group together — no repeated name/avatar header — the way the main channel already does, in both the DM pop-out and in threads. In threads, deleting the first message of a group now promotes the next reply so it keeps its author header.
Changed
- Haven Desktop download links bumped to v1.4.25 on the landing pages.
Haven v3.28.0
Added
- Admin-only "Hide Channel" to declutter the sidebar (#5409). Admins can't leave channels (they need access to all of them), so their channel list just keeps growing. Right-click a channel (admin only) to hide it from your own sidebar without touching membership or affecting anyone else. Hidden channels stay fully accessible via an "N hidden channels" bar that opens a modal to unhide them individually or all at once. Stored per-device, like mute.
Fixed
- Screen-share audio drifted out of sync over TURN relays (#5426). Audio was routed through the Web Audio mixer, whose fixed clock drifts against WebRTC's jitter buffer, so after a minute or two on a relayed connection it stuttered and desynced (LAN was fine). Native playout is now the default; the Web Audio mixer is an opt-in toggle for the >100% per-stream volume boost.
- Custom sounds, emojis, and stickers didn't update live (#5426). Other clients only saw changes after a restart. Uploads, deletes, and renames now refresh the affected library for everyone immediately.
- Voice could go silent for some peers after a brief disconnect (#5427). After a reconnect while still in voice, Haven now ICE-restarts every peer (staggered) so a dead relayed path is rebuilt both ways instead of leaving you audible to some people and silent to others.
- Reopening a screen share could stick on "Requesting stream…" (#5426). The watch path now uses the late-joiner retry watchdog, re-requesting until a live video track arrives.
- Sub-channel access via a crafted code or deep link (#5408). Joining a sub-channel by code now requires membership of the parent; private subs are never granted by code. Rejections reuse the generic "invalid code" error.
- Guests couldn't see the Join Voice button in allowed channels (#5401). The button-visibility gates now exempt guests too, matching the server, which already allowed it.
- Server address in the status bar didn't copy in the desktop app. Added the Electron clipboard fallback the other copy buttons use (#182).
Changed
v3.27.0
Haven v3.27.0
Added
- Admin-configurable STUN/TURN voice servers (#5399). A new Settings → Voice & Connectivity section lets server admins point voice and screen share at their own STUN servers (one
stun:URI per line, or comma-separated) and an optional TURN server with static credentials — without setting environment variables or redeploying./api/ice-serversnow resolves in the order admin setting →STUN_URLS/TURN_URLenv → built-in STUN pool, so a server's connectivity can be fixed live from the admin panel. The TURN password is stored as an admin-only sensitive setting and every value is scheme/length validated before saving. Clients that can't reach any STUN server now show a one-time warning toast (calls may only work on the local network until an admin sets STUN/TURN) instead of silently hanging on "ICE: Connecting…".
Fixed
- The stream viewer's "close" (✕) button broke screen sharing. The viewer header has two different ✕ buttons: the per-tile ✕ safely hides a single stream (restorable), but the header ✕ stopped your own broadcast and fully tore down every stream tile. Destroying a tile dropped the only reference to a sharer's still-live video, so the stream couldn't be reopened and even a fresh reshare wouldn't reattach without a hard-refresh — it also closed streams you were only watching and removed the restore button. The header ✕ now closes streams the same restorable way the per-tile ✕ does (hide + mute, recoverable from the hidden-streams bar or the LIVE badge) and never stops your own share.
Full changelog: https://github.com/ancsemi/Haven/blob/main/CHANGELOG.md
Haven v3.26.0
Per-channel soundboard control plus a batch of moderation, upload, and screen-share fixes. Covers everything on main since v3.25.2.
Added
- Per-channel Soundboard toggle. The Channel Functions menu (⚙️, admins /
create_channel) now has a Soundboard ON/OFF switch next to Voice, Text, Streams, Music, and Media. When it's off for a channel, nobody can play soundboard sounds into that channel's voice chat. Newsoundboard_enabledcolumn on channels (defaults to on), wired through the existing channel-toggle path.
Fixed
- Auto-cleanup was deleting persona avatars and other non-post files (#5423). Cleanup used to wipe anything in
uploads/that wasn't on a hand-maintained allow-list, so any file type nobody remembered to protect got swept up (persona avatars now, server stickers/emojis before). It now follows only the messages it deletes — relocating their attachments and purging those by age — and never scans the uploads folder, so avatars, personas, emojis, sounds, stickers, and the server icon are safe by default, including future file types. - Sub-channel Rename and Organize ignored the permissions named for them (#5424). Rename was shown only to moderators by level, and Organize required the server-wide
create_channelpermission, so grantingrename_sub_channel/manage_sub_channelsdid nothing. They now follow the matching permissions; top-level/server-structure changes still requirecreate_channel. - Non-image attachments queued on their own never sent (#5425). A queued PDF/audio/video with no image alongside it would stay stuck in the queue (text sent without it, or nothing sent at all). File-only messages now send, and the image and file queues flush independently.
- Late joiners and tile-less viewers couldn't reopen an active screen share. Joining a voice channel after a share started, or dismissing the tile, left no way back in. You can now reopen any active screen share in the channel.
- Screen-share audio could play twice (#5426). Stream audio was routed through both a Web Audio gain node and the media element at once; it now plays through a single path.
Changed
- Haven Desktop download links bumped to v1.4.24 on the landing pages (#5422 hotfix).
- Refreshed donor and sponsor credits.
Update: download the source zip below (or git pull), then restart your Haven server. Browser clients hard-refresh (Ctrl+Shift+R) to pick up the front-end changes.
v3.25.2 — Hotfix: blank app
Hotfix: app loaded completely blank
If your Haven app came up empty — no servers, no channels, no users, no DMs, and nothing responding to clicks — this release fixes it.
What was wrong
Three of the front-end JavaScript modules (app-messages.js, app-media.js, and app-ui.js) had been silently truncated mid-file in an earlier commit, cutting off the end of each one. The app bundles these modules together when it starts up, so a single unterminated file is a fatal parse error that halts the entire interface. The page would connect to the server but render nothing and ignore every click, including the log out button.
The fix
All three modules were restored to their complete form and every front-end module was re-validated to confirm it parses cleanly.
If you were stuck on a blank screen, update to v3.25.2 and hard-refresh (Ctrl+Shift+R) and Haven will load normally again.
Commits since v3.25.1
- Restore truncated
app-messages.js - Restore truncated
app-media.jsandapp-ui.js - Bump Desktop download links to v1.4.23
- v3.25.2 release (version, changelog, site)
v3.25.1
Fixed
- Haven Desktop stuck on "Loading Haven…" / CSP-blocked i18n init.
app.htmlhad a single inline<script>i18n.init();</script>tag that the page CSP (no'unsafe-inline'inscript-src) was refusing to execute on strict clients, leaving the locale layer uninitialized and the desktop preload hanging on the splash. Moved the bootstrap call intoi18n.jsitself (I18n.init()at the bottom of the module) and removed the inline tag fromapp.html.init()is idempotent so theauth.jsawait window.i18n.init()call still resolves against the same shared promise.
Haven v3.25.0
Highlights
- Soundboard sidebar mode. Pin the soundboard as a side panel alongside chat instead of opening a popup. Includes search, collapsible category groups, resize, and per-sound hide/show + custom ordering. Closes automatically when you leave voice.
- Non-image file upload queue (#5417). PDFs, audio, video, archives, etc. now queue as a removable chip before sending instead of uploading the moment you pick them. Works for the upload button, paste, and drag-drop in the main composer.
- Bluesky link previews (#5412) and a new Off / Normal / Large link preview size picker in Layout settings.
- Big sweep of fixes for the botched post-3.24.0 soundboard rework: panel position, slide animation, auto-close on voice leave, popout no longer blocks Assign/Manage tabs (#5419), and a custom Sound Manager dropdown that stays inside the modal (#5418).
- UTF-8 mojibake repair. Restored about 15 theme logos and UI icons that survived the v3.17.0-era cp1252 round-trip damage (eldenring 💍, zelda 🗡️, halo ⌁, nord ❄, minecraft ⛏️, ffx ⚔️, fallout ☢️, scripture ✝️, gospel 🕊️, cloudy ☁, plus the 🎙️ Sound Manager group label, 🛡️ Moderation label, ❌ avatar-error indicator, and TOS-page em-dashes / ⚖️ balance scale).
Added
- Soundboard sidebar mode. Settings → Sounds → Sidebar Mode, or the popup checkbox. Resizable handle, collapsible Custom / Built-in groups, search input, fixed-position arrow toggle that slides with the panel. Auto-closes when you leave voice.
- Custom sound management (hide individual sounds from the visible grid, drag to reorder).
- Bluesky link previews (#5412) in
/api/link-preview. - Link preview size picker (Off / Normal / Large) in Settings → Layout.
- File upload queue for non-image attachments (#5417).
Changed
- Soundboard popout no longer blocks Assign / Manage (#5419). Previously the modal-opener early-returned for any tab when the PiP was active; now only the Soundboard tab refocuses the PiP.
- Sound Manager dropdown is now a custom component (#5418). Native
<select>popups could overflow the Haven window on long sound lists. - better-sqlite3 upgraded to v12.10.0 (Node 22–26 prebuilts).
start.shno longer kills processes on the configured port (#5415). Contributed by @MutantRabbit767. The script now fails fast with EADDRINUSE if the port is busy.
Fixed
- Soundboard sidebar rendered at the far LEFT of the window instead of next to voice/users (missing flex
order). - Soundboard collapse arrows snapped instead of sliding when toggling sidebars (missing
right/topin CSS transition). - Sidebar toggle arrow visually crowded the first sound row (the closed-state stagger was being used when the panel was open).
- Soundboard surfaces stayed open after leaving voice (sidebar, popout PiP, and modal now all close on voice leave).
- Sound Manager dropdown overflowed the Haven window (see #5418 above).
- Residual UTF-8 mojibake (#5418). ~15 spots across theme logos, sound manager labels, the Moderation label, the avatar-save error indicator, and the TOS page.
- Shared stickers no longer moved to
deleted-attachments/on message/DM delete (#5413). Contributed by @Amnibro. - Slash subcommand picker follow-up for #5403.
- Chat scroll-jumping when images/media load (debounced + min-dimensions).
- Auto-heal zero-membership users on channel bootstrap. Legacy accounts that ended up with no
channel_membersrows now auto-join the configured default channels (or every public non-DM channel if no defaults are set). - Sound migration parsing edge case.
- Missing sound pref functions + broken template literal in
app-media.js.
Upgrade notes
No schema changes that require a manual step. The auto-heal on bootstrap will silently fix any legacy zero-membership accounts the first time they reconnect. The new .gitattributes * text=auto eol=lf rule may cause git add --renormalize . to want to convert your working tree to LF on first pull — that's intentional and a one-shot cleanup if you want a clean status; harmless to skip.
Full Changelog: v3.24.0...v3.25.0
Haven 3.24.0
Haven 3.24.0
Added
- Bulk sticker upload in the Sticker Manager (#5406). Admins can now select multiple sticker images at once, auto-generate names from filenames, and optionally apply one shared pack name to the whole batch.
- A new
Mute / PTT Cuestoggle underSettings -> Sounds(Haven-Desktop #37). Desktop users can now silence the short mute, unmute, deafen, and push-to-talk cue beeps without muting all other Haven sounds.
Fixed
Haven v3.23.0
Haven v3.23.0
This release includes all changes since the previous tag v3.21.0, including both the v3.22.0 and v3.23.0 changelog sections.
Highlights from v3.23.0
- Private webhook bot replies (#5404):
POST /api/webhooks/:tokennow supportsephemeral: truewithrecipient_idfor recipient-only bot messages. - Recipient-only webhook messages are validated against channel membership, delivered only to the target user, and not persisted to channel history.
- Clients now show an "Only visible to you" marker for recipient-only bot messages.
Highlights from v3.22.0
- Added "Never expire" login session option and a cleaner first-time popup queue.
- Added server-side per-channel mute sync so muted channels stop push notifications reliably.
- Fixed guest mode sub-channel auto-join and guest voice behavior in allowed channels.
- Fixed blank login / blank app update path from v3.18.0 due to latent syntax errors.
- Added slash-command subcommand discoverability for bots (#5403).
- Refreshed
/api/ice-serversfallback defaults and fixed image-copy reliability.
For full details, see CHANGELOG.md in this release source.
v3.21.0
Added
- Server-side per-channel mute (#5399 followup). Channel mute state has only ever lived in browser localStorage, which meant the server's push helper had no idea who'd muted what and pushed every message to every member regardless. Mobile users in particular reported getting FCM pings for channels they'd muted in the web client weeks earlier; on Android there was no way to silence a noisy channel short of disabling Haven notifications system-wide. A new
user_channel_prefstable now mirrors the mute set on the server, withGET /api/user/channel-prefs,POST /api/user/channel-prefs/mute(single-toggle), andPUT /api/user/channel-prefs/muted(transactional bulk replace, capped at 500 entries) backing it.sendPushNotificationsfilters muted recipients out of both the web-push subscription loop and the FCM inactive-members list. Existing clients converge automatically on first connect — the renderer unions localStorage with the server set and pushes the merged list back up, so nobody loses their existing mutes.
Fixed
- Blank login page / blank app shell after updating directly from v3.18.0 (#5399). Two SyntaxErrors had been quietly sitting in
mainsince the v3.19.0 Guest mode merge (b6b95bd): a duplicateconst loginFormredeclaration inauth.js, and an orphan_setupServerBar() {opener with no body inapp-ui.js(a half-merged method definition that was never closed). Anyone already running v3.19.x kept working because their cached/loaded modules survived the crash on initial parse, but users updating directly from v3.18.0 — the prior version many self-hosters were sitting on — hit both errors on first load and got a blank login page followed by a blank app. Mistakenly attributed to the v3.20.1 STUN refresh at first; that work is unaffected. /api/ice-serverswas still returning dead STUN URLs (#5399 followup). The server-side default in/api/ice-serverswas still handing outstun.stunprotocol.org+stun.nextcloud.com— the same pair the v3.20.1 client fix had to route around. Any Haven server using the server-side STUN defaults was giving its clients dead endpoints and only working at all becausevoice.jshad been updated to ignore them. Mirrored the same Cloudflare/Metered/Twilio/Google fallback list on the server so the two sides stay in sync.- Right-click → Copy image silently failed for almost everyone, on both static images and GIFs. Two compounding causes: the previous implementation awaited an
Image()load +canvas.toBlobbefore callingnavigator.clipboard.write, by which point the user-gesture token had been dropped and Chromium silently rejected the write withNotAllowedError; on top of that, Electron's renderer added enough latency around fetch+decode that even a corrected promise-based path was unreliable. Rewrote with three strategies tried in order: under Haven Desktop, hand the PNG bytes to the main process via a newclipboard:write-imageIPC (Electron'sclipboard.writeImagehas no gesture restrictions); otherwise callnavigator.clipboard.writewith a promise-basedClipboardItemso the gesture token is preserved; last-ditch, copy the image URL as text so the user has something to paste. Failure toasts now include the underlying error message so diagnosing future regressions doesn't require devtools. (Requires Haven Desktop 1.4.22 for the IPC path.) - Image and member context menus appeared offscreen on top of the image lightbox or PiP DM. Both
.image-context-menuand.user-context-menusat atz-index: 10001, which left them buried under the image lightbox (100010) and the PiP DM panel (99999). Right-clicking an enlarged image or a member while a PiP DM was open made the menu look like it vanished. Bumped both to100020. - Password eye-icon toggle sat on top of the typed characters. The
.haven-pw-wrapwrapper wasinline-blockwith no width hint, so the inner input collapsed to its intrinsic width and the absolutely-positioned eye sat directly over the right edge of the password text instead of in its own gutter. The wrap is now a full-width block and the input fills it with reserved right-padding for the toggle.