* Remove location data from every uploaded photo and video
Photos and videos can carry the GPS coordinates of where they were
taken, and Haven kept whatever arrived: anyone who could see such a
file could find where it was taken. Not every upload has one. Android
zeroes the location for apps without its media location permission,
and a photo taken before the phone knows where it is has none. But
files uploaded from a computer, or through an app that has that
permission, keep their coordinates, and so may photos from an iPhone
(not tested). Every photo and video now has its location removed
before anyone else can see it, and the ones already on the server are
cleaned once. There is no setting to turn this off.
New dependencies: exiftool and ffmpeg
Both are now required. They are tried and true, kept up to date as
phones and cameras change their formats, and thorough, which is the
whole job here. exiftool knows every place a device hides a location
(EXIF, XMP, IPTC, maker notes, Apple and Android video tags, the
location tracks dashcams and action cameras record) and can read and
write all of them. ffmpeg rewrites a video keeping only its picture
and sound.
Haven looks for each in HAVEN_EXIFTOOL_PATH / HAVEN_FFMPEG_PATH, then
the data folder's tools/, then the PATH. When one is missing it
downloads a pinned version from GitHub into tools/ and checks it
against a fixed SHA-256 before using it: ExifTool 13.59 from its
author's repository (byte for byte the exiftool.org release; it needs
Perl, which Linux and macOS ship), ffmpeg from the ffmpeg-static
b6.1.1 release. ExifTool's Windows build is not on GitHub, so Windows
installs it by hand. HAVEN_NO_TOOL_DOWNLOAD=1 turns downloading off.
The Docker image installs both from Debian.
What happens to a file
1. exiftool reads it. The type comes from the contents, not the name.
Anything that is not a photo or video is left alone (PDFs, audio,
voice messages, encrypted DM files).
2. Nothing to remove (no location, XMP or IPTC) means no rewrite.
3. Photos: exiftool deletes the GPS tags, every location tag, all XMP
and all IPTC, without re-encoding, so pixels and rotation are
untouched. If anything survives, a second pass keeps only rotation
and the colour profile.
Videos: ffmpeg copies only the video and audio streams into a new
file with no metadata, without re-encoding. Dropping every other
stream is what removes the separate location tracks iPhones and
action cameras add.
4. exiftool reads the result again, embedded tracks included. Any
location left means the file is refused, never let through.
Files are rewritten under a temporary name and swapped in with a
rename, so a crash leaves the old file or the clean one, never both.
A rewrite keeps the file's original date, so retention and cleanup
still count from when it was uploaded.
New uploads
Uploads land in data/unprocessed/, which is not served, and only move
into uploads/ once clean, so a dirty file is never reachable, not
even for a moment. This covers every upload route: chat files and
pictures, avatars and borders, server icons and banners, role and
badge icons, emoji, stickers, and avatars fetched from a sign-in
provider.
- A photo or video whose extension lies (image.bin) gets its real
one, on disk and in the name shown in chat, so it displays inline.
- The progress bar fills once for the upload, then again as
"Removing location data..." following ffmpeg's progress on videos.
Photos take a moment, so they only change the label. Progress
reaches the uploader over the socket, so the upload endpoint and
the clients that use it are unchanged.
- Cancelling during that step stops the tool and discards the file.
- New uploads always go first: the sweep waits while one is cleaned.
At most two videos are cleaned at once, and the tools run at the
lowest CPU priority so chat and voice come first.
- A file that cannot be cleaned is refused, deleted, logged to the
console, and the uploader is told why:
"Your file contains GPS data that Haven failed to remove. Please
try again or remove the GPS data manually" (location still there)
"Haven couldn't process this file. Please try again or upload a
different file." (the tool itself failed)
- Without the tools, photo and video uploads are refused with "Photo/
Videos uploads are disabled due to a misconfiguration of the server";
chat uploads check this before sending anything. Other files still
upload. Startup logs the problem, and installing the tools reopens
uploads within five minutes, without a restart.
Files already on the server
A one-time sweep at startup cleans every photo and video in uploads/,
deleted-attachments included (those can be restored), where it is.
Old files keep being shown as normal while it runs, and it resumes
where it left off after a restart. A file it cannot clean is moved to
unprocessed/held/, out of reach, and retried after the sweep, up to
three more times, one file at a time. After that it is listed in
unprocessed/held/failed.json with the tool's error and the messages
that use it. Held files are never deleted automatically: a retry that
works puts the file back, otherwise an admin decides.
In chat, a held file is taken out of its message and the message gets
one note under whatever still shows, starting with a lock:
"This file is still being processed." while retries remain,
"This file couldn't be processed." after,
and counts when more than one file is held ("1 file is still being
processed. 2 files couldn't be processed."). The message text stays.
A picture group re-lays itself out around the files that remain; a
single picture left shows like a lone picture, a single video keeps
its tile so it still plays.
What this cannot reach
- Copies people already downloaded, copies Ferry already sent to
Discord, and the server's backups.
- Encrypted DMs: their files reach the server already encrypted. Each
DM now shows a dismissable notice saying so and asking people to
turn off location on their camera and check what they send.
Testing
Check a photo really has a location before uploading it:
exiftool -a -G1 -gps:all -c "%.6f" photo.jpg
Real coordinates show as [GPS] GPS Latitude / GPS Longitude lines.
Without -a, exiftool hides them: it keeps only its own calculated
copy of each, which -gps:all then filters out, so a photo with a
location looks like it has none.
Where uploads come from matters on Android. Since Android 10, an app
reads photos with the location zeroed out unless it declares
ACCESS_MEDIA_LOCATION, shown in its permission list as "read
locations from your media collection".
- Haven Mobile asks for photo and video access but not for that
permission, so on Android 10 and later every photo it sends
arrives with its GPS already zeroed by Android. Videos were not
tested but are assumed to be the same case.
- Firefox for Android does ask for it, and a photo uploaded through
Firefox was confirmed to reach Haven with its GPS intact. Test
from a browser like that, or from a computer.
- iPhone was not tested.
Then upload it, open the stored file in data/uploads/ (or download
it from chat), and run the same command: no location should come
back.
To see a held file, cut a phone photo that has GPS short, inside its
metadata (head -c 2000 usually is), and drop it in uploads/ before a
first start: exiftool can still read the location but cannot rewrite
the broken file, so it is held, retried and listed in failed.json.
test/privacyPass.test.js covers detection, renaming, and cleaning a
photo and a video. The cleaning tests and the image upload tests need
exiftool and ffmpeg installed; without them they skip or expect the
refusal, and test servers never download the tools.
Translations added for German, Persian, Japanese, Korean, Portuguese
and Chinese; Spanish, French, Polish and Russian get the progress label
(their other strings in these sections are still English).
* Leave Persian out of the location data strings
The translations validator fails any change to fa.json, which has no
_meta.flag, and choosing a flag for Persian is not this change's call.
Persian shows the new privacy pass text in English until that is
settled.
* Bump cache tags above main
---------
Co-authored-by: bo0sted <mc526+github@proton.me>
Co-authored-by: ancsemi <ancsemi@gmail.com>
⬡ HAVEN
A private Discord alternative that runs on your own machine. No cloud, no company account, no telemetry, no paid tier. Free forever.
One person runs the server. Everyone else joins with a code. Messages, files and voice never touch anybody else's infrastructure.
Get started in about a minute
Docker
docker run -d -p 3000:3000 -v haven_data:/data ghcr.io/ancsemi/haven:latest
Windows, no Docker: download the zip, unzip it, double-click Start Haven.bat.
It installs Node.js for you if you do not have it.
Linux / macOS, no Docker: chmod +x start.sh && ./start.sh
Then open https://localhost:3000, register with the username admin, make a
channel, share the code. The certificate warning on first load is expected, click
Advanced then Proceed.
One-click cloud deploy on Zeabur · on RepoCloud · Full setup guide
Three ways in
| 🖥️ Haven Desktop | Windows and Linux. Per-app audio sharing (send one app’s sound, nothing else), device switching mid-call, native notifications, tray. It can also run the server itself. |
| 📱 Haven for Android (Google Play) 💠 Android (via Obtainium) |
On Google Play, or as a direct download through Obtainium. Native, not a web wrapper. Full chat, voice and push notifications. Built by Amnibro. |
| 🍎 Haven for iOS | In testing through TestFlight. Native, by Amnibro as well. Until it is on the App Store, the browser works too: open your server in Safari and Add to Home Screen. |
| 🌐 Any browser | Nothing to install. Send a link, they are in. Works on phones. |
Apps and browser talk to the same server and see the same thing, so nobody has to install anything to be included.
Want to look around first? The community server is open, no code needed, just sign up. Volunteer-hosted, thanks MutantRabbit.
Why bother
| Discord | Haven | |
|---|---|---|
| Hosting | Their cloud | Your machine |
| Account | Email and phone | No email, no verification |
| Your messages | Stored by Discord Inc. | Never leave your server |
| Cost | Nitro, boosts | Free, no paid tier |
| Telemetry | Analytics and tracking | None |
| Source | Closed | Open, AGPL-3.0 |
What you get
Coming from Discord
- Import your whole server history from inside Haven. Every channel, thread, forum post, reaction, pin and attachment. Paste a token, pick a server, go.
- Ferry, a two-way Discord bridge. Messages pass both ways between a Haven channel and a Discord channel, under each person's real name. Friends who are not ready to leave Discord can still talk to the ones who did.
Talking
- Voice chat, peer to peer by default so audio doesn't pass through the server (an optional voice relay is there for big servers). Per-user volume, mute, deafen, noise suppression, talking indicators.
- Screen sharing, several people at once in a tiled grid. Watch parties work.
- Listen together with synced playback from Spotify, YouTube and SoundCloud, or a music player right in the voice channel.
- Threads, replies, reactions, pins, editing, mentions, polls, search, link previews, code blocks, spoilers.
- Direct messages, end-to-end encrypted. Private keys never leave the browser, so not even the person hosting can read them.
- Rich presence for what people are playing and listening to, via Steam, Last.fm or Spotify. Off until you turn it on, never written to the database.
Making it yours
- 26 built-in themes, a colour picker, an RGB cycle, and stackable visual effects like CRT, Matrix rain and snowfall.
- A plugin and theme system, so you can drop in your own without forking anything.
- Custom stickers, custom emoji, personas (send as an alternate character), soundboard, avatar shapes and borders.
- Server branding, sidebar layout options, font size and density controls.
- 11 languages out of the box: English, Français, Deutsch, Español, Polski, Русский, 中文, Português, فارسی, 日本語 and 한국어.
Running it
- One-click updates from inside the app. Haven takes a pre-update backup and restarts itself.
- Built-in backups, scheduled or on demand, with restore.
- Single sign-on via OpenID Connect, so Authentik, Keycloak, Authelia and Auth0 all work as your login.
- Bots and webhooks with a REST API. Send messages, play soundboard sounds, register slash commands, join voice, receive signed callbacks.
- Roles with per-channel permissions. Kick, timed mute, ban, IP bans, slow mode, automod link filtering, an audit log, and a moderation API.
- Guest accounts, invite links, per-channel join codes with automatic rotation.
- Push notifications, including UnifiedPush if you would rather leave Google out.
- Built-in Cloudflare and LocalTunnel support, so friends can reach you without port forwarding.
- Bcrypt passwords, JWT auth, HTTPS, MFA, rate-limited logins, and a disk reserve so a full drive never wedges the server.
Documentation
Everything in detail lives in GUIDE.md: setup, Docker, remote access (port forwarding, Tailscale, Cloudflare Tunnel, reverse proxies), themes, voice and TURN, Discord import, Ferry, encryption, backups, configuration, translations, the bot API, and troubleshooting.
Release notes are in CHANGELOG.md.
Contributing
Issues and PRs are welcome, including translations and themes. If you are reporting
a bug, the version number from https://your-server:3000/api/version helps.
AI usage
Haven is built with AI. I'm not a professional programmer. I came up with Haven, I decide what goes in, and I test it and run it with my own community. Most of the code, the server logic included, was written by AI coding tools (mainly Claude Code) working from my direction, and most code review is AI-assisted too. Commits don't carry a per-commit AI label, so assume any part of the code may have been written by AI. Contributors sometimes use AI as well, and pull requests are welcome either way.
What keeps it in check:
- An automated test suite (over 650 tests) runs before every push.
- Every pull request is read in full and checked for security problems before it's merged.
- Logins, permissions, encryption and uploads have had dedicated security reviews, and the fixes ship in public releases.
- Bugs are reported and fixed in the open, here on GitHub.
License
AGPL-3.0. Free to use, modify and share. If you deploy a modified version as a network service, you have to publish its source. See LICENSE.
Gallery
⬡ Haven