Haven runs on Linux in two capacities, sharing the same Rust core (haven-p2p +
haven-net) as the iOS, macOS, and Android apps:
- Desktop GUI (
desktop/, Tauri 2) — a real iroh peer at feature parity with the iOS/macOS app: identity, circles, feed, stories, DMs, reactions/comments, in-app camera, media, WebRTC audio/video/group calls + screen share, music, system tray, native notifications, and BYO S3/R2 storage. The same binary also runs headless as a relay (--headless). - Headless relay daemon (
core/haven-relay) — a tiny, dependency-free static binary (no WebKit, no GUI) that links one of your circles and serves it as an always-on connection relay + sealed-media mailbox. This is the right thing to run on a Raspberry Pi or a server. It only ever moves ciphertext.
Which one do I want? A laptop/desktop/Steam Deck you use → the GUI. A Pi or headless box left running to keep your circle reachable → the relay daemon.
| Distro | GUI | Relay daemon | Recommended install |
|---|---|---|---|
| Ubuntu | ✅ x86_64 | ✅ | GUI: .deb / AppImage · Relay: haven-relay .deb or install.sh |
| Debian | ✅ x86_64 | ✅ | same as Ubuntu |
| Raspberry Pi OS / Raspbian | ✅ primary role | Relay: install.sh (arm64 / armv7 / armv6) or .deb |
|
| Arch | ✅ x86_64 | ✅ | GUI: build from the in-repo PKGBUILD (not on the AUR yet — why) · Relay: binary + systemd |
| SteamOS / Steam Deck | ✅ x86_64 | ✅ | GUI: Flatpak — sideload haven.flatpak from the release (not on Flathub) · Relay: binary + systemd user service |
The GUI needs WebKitGTK + a glibc userland, so it targets desktop distros. The relay is a musl static binary with no dependencies — it runs anywhere, including 32-bit Pis.
sudo apt install ./Haven_1.0.5_amd64.deb # or arm64 on a 64-bit Pi
haven-desktop # launch (also in your app menu)The .deb declares its runtime deps (libwebkit2gtk-4.1-0, libgtk-3-0,
libayatana-appindicator3-1) and recommends pipewire + xdg-desktop-portal for camera
and screen share. CI also produces an AppImage (no install — chmod +x and run) and an
.rpm.
Not published to the AUR yet.
haven-desktopdoes not exist onaur.archlinux.org—git clone https://aur.archlinux.org/haven-desktop.gitwill fail today. The PKGBUILD is maintained in this repo. CI can publish it (release.ymlhas anaurjob that pushes on everyv*tag), but that job is gated on anAUR_SSH_PRIVATE_KEYsecret which is not set yet — publishing requires a personal AUR account, so it can't be automated away. Until then, build from the in-repo recipe:
git clone https://github.com/blaineam/haven.git
cd haven/packaging/aur/haven-desktop && makepkg -siThe web UI is static and embedded into the binary at compile time, so the build needs no Node/npm — just Rust + system WebKitGTK.
Prefer not to compile? Use the .deb, .rpm, or AppImage from the release — the AppImage runs
on Arch as-is. Note CI builds the desktop GUI on ubuntu-latest only, so the prebuilt Linux
GUI artifacts are x86_64 only; aarch64 Arch/Linux means building from source.
SteamOS has an immutable root filesystem, so a .deb/AppImage won't persist across
updates — Flatpak is the supported path. From Desktop Mode, easiest is the prebuilt
bundle attached to each release:
# Grab haven.flatpak from the GitHub release, then:
flatpak install --user haven.flatpak
flatpak run com.blaineam.havenOr build it yourself from the version-pinned manifest the release publishes (its .deb
sha256 is already filled in):
flatpak install -y flathub org.gnome.Platform//47 org.gnome.Sdk//47
# com.blaineam.haven.yml is a release asset (the in-repo copy uses a placeholder sha256)
flatpak-builder --user --install --force-clean build-dir com.blaineam.haven.yml
flatpak run com.blaineam.havenThen add it to Game Mode via "Add a Non-Steam Game." The Flatpak grants the Camera and
ScreenCast portals (camera + screen share over PipeWire), audio, Wayland/X11, the Secret
Service for the identity seed, and a tray. See desktop/flatpak/README.md.
# build deps (Debian/Ubuntu):
sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libsoup-3.0-dev \
libayatana-appindicator3-dev librsvg2-dev patchelf file
cargo install tauri-cli --version '^2'
cd desktop/src-tauri
cargo tauri dev # run the GUI
cargo tauri build # → target/release/bundle/{deb,rpm,appimage}/
cargo run -- --headless # run ONLY the relaycurl -fsSL https://wemiller.com/apps/haven/relay/install.sh | shinstall.sh auto-detects the arch and downloads the matching prebuilt static binary:
uname -m |
Target |
|---|---|
x86_64 |
x86_64-unknown-linux-musl |
aarch64 / arm64 (64-bit Pi OS, Arm servers) |
aarch64-unknown-linux-musl |
armv7l (32-bit Raspbian, Pi 2/3/4) |
armv7-unknown-linux-musleabihf |
armv6l (Pi Zero / Pi 1) |
arm-unknown-linux-musleabihf |
Then attach it to a circle (the app shows the link under You → Advanced → Relay → Add a relay):
haven-relay run --link "haven-relay://circle#...." # first run; saves the link
haven-relay run # restart later; reuses itsudo apt install ./haven-relay_0.0.1_amd64.deb # also arm64 / armhf
# attach to a circle once, then enable the hardened system service:
sudo -u haven-relay HOME=/var/lib/haven-relay haven-relay run --link "<code>" # Ctrl-C after "saved"
sudo systemctl enable --now haven-relay
journalctl -u haven-relay # the relay node id is in the logThe .deb ships a locked-down systemd system service that runs as a dedicated
haven-relay user with ProtectSystem=strict, PrivateDevices, NoNewPrivileges, etc.
(relay/debian/haven-relay.service).
Not on the AUR yet —
aur.archlinux.org/haven-relay.gitdoes not exist. Same story as the GUI: CI is wired to publish it on a tag, but theAUR_SSH_PRIVATE_KEYsecret isn't set. Build from the in-repo recipe (or just use the static musl binary +install.shabove, which needs no compiler):
git clone https://github.com/blaineam/haven.git
cd haven/packaging/aur/haven-relay && makepkg -si
sudo systemctl enable --now haven-relay- System (boot, no login) — shipped in the
.deb/ Arch pkg; source atrelay/debian/haven-relay.service. - Per-user (no root, needs
loginctl enable-linger) —relay/haven-relay.service:mkdir -p ~/.config/systemd/user && cp relay/haven-relay.service ~/.config/systemd/user/ loginctl enable-linger "$USER" systemctl --user enable --now haven-relay
The GUI is at parity with iOS/macOS; here is how each Apple-specific capability is realized
(see also ANDROID-PARITY.md — the same portable approach):
| iOS/macOS feature | Linux (Tauri / WebKitGTK) |
|---|---|
| Crypto / identity / circles / feed / DMs / stories | Same haven_ffi crate, linked directly (no FFI hop) — identical engine |
| iroh P2P transport + mesh relay | Same haven-net crate; native iroh peer in-process |
| Keychain (seed storage) | OS Secret Service via keyring (keys never leave the device) |
| In-app camera + 6 filters | getUserMedia (V4L2) live preview + filter strip; the selected filter is baked into both photos (canvas → JPEG) and video (recording a filtered canvas.captureStream() + mic audio); sealed in Rust before send |
| Photo/video picker | Tauri dialog + XDG portal |
| Voice messages | MediaRecorder (Opus/WebM) → sealed a: media ref; <audio> playback in feed + DMs (container MIME-sniffed server-side) |
| WebRTC audio/video/group calls | RTCPeerConnection full-mesh in the WebView; SDP/ICE signaled over the sealed iroh channel — no call server |
| Screen share | getDisplayMedia → on Wayland/SteamOS routes through xdg-desktop-portal ScreenCast (PipeWire); replaces the outgoing video track |
| Secret / screenshot-protected messages | Same \u{2}-prefixed wire encoding as iOS (interops byte-for-byte); conceal-until-tap + auto-hide after 5s. Best-effort only — webviews can't truly block screenshots like iOS/Android |
| Scheduled messages | Serverless "send later": queued to scheduled.json, fired by an in-process timer (and once on launch for anything overdue). Fires while the GUI is open or while haven-desktop --headless runs on an always-on machine (the relay box doubles as the scheduler) |
| Multi-identity switcher | Roster of identities, each with its own seed (secure store), profile, circles + data dir; switching relaunches the app on the new identity |
| Apple Music on posts | Portable music ref: paste a streaming link (deep-links out); local audio = a voice/audio attachment. No Apple Music catalog API on Linux |
| Notifications | tauri-plugin-notification (libnotify / XDG) + system tray; no push server (honors the zero-recurring-cost mandate) |
| CloudKit favorites/resume | Mailbox-based prefs blob (circle-sealed), same as Android |
| BYO storage | Shared core/haven-s3 SigV4 client |
- Screenshot-protected secret messages: Linux/webviews have no reliable cross-compositor
screenshot block, so secret messages are conceal-on-idle only (unlike iOS's secure field /
Android's
FLAG_SECURE). The wire encoding still interops with iOS byte-for-byte. - Scheduled messages fire while the GUI is open, or while
haven-desktop --headlessruns on an always-on machine (serverless — no remote server holds the queue). A relay-backed timed-release path that works even when all your machines are off is designed inSCHEDULED-MESSAGES.md(Option B, cross-platform follow-up). - GUI on Raspberry Pi: builds for arm64 but camera/calls/perf parity on Pi hardware is a stretch — a Pi's real role here is the relay daemon, which runs great on all Pis.
Cut a release by pushing a tag — everything is built and published automatically:
git tag v1.0.5 && git push origin v1.0.5The tag must be a plain vX.Y.Z matching MARKETING_VERSION in apple/project.yml —
Haven ships one version across every store. release.yml fails the build if it doesn't.
See RELEASING.md for the scheme and the full checklist.
.github/workflows/release.yml then builds and attaches
to one GitHub Release. By policy the GitHub Release carries only the platforms that have no
app store — the Linux desktop app and the relay CLI. iOS/macOS (App Store), Android (Google
Play) and Windows (Microsoft Store) are store apps (free, like every Haven build) that ship
through their stores and do not accumulate a Release history. (Windows/Android are still built by CI but are no longer attached
to the Release — both PUBLISH_WINDOWS_TO_GH and PUBLISH_ANDROID_TO_GH are now set to false;
the interim builds are the short-retention desktop-windows / haven-android-apk CI artifacts.
See RELEASING.md.) On the Release you get:
- Relay
haven-relay-<target>for x86_64 / aarch64 / armv7 / armv6 (musl, viacargo-zigbuild) + macOS + Windows — the exact assetsrelay/install.shdownloads — plus.debs. (The Windows relay.exeis a self-host CLI, not the Store app, so it stays here.) - Linux desktop installers:
.deb/.rpm/AppImage. - SteamOS
haven.flatpakplus a version-pinnedcom.blaineam.haven.yml(the real.debsha256is computed and injected at release time, so building the Flatpak from the release manifest needs no manual pin). This is a sideload bundle — Haven is not on Flathub, and that manifest is not Flathub-submittable as-is (it unpacks a prebuilt.deb; Flathub requires building from source). SeeRELEASING.md. - Arch: the
aurjob pushespackaging/aur/*toaur.archlinux.org— only if theAUR_SSH_PRIVATE_KEYsecret is set. It skips cleanly otherwise.
The tag drives the version (stamped into tauri.conf.json + the relay crate before build).
Supporting workflows:
.github/workflows/desktop.yml— PR/push CI: tests the core + desktop and builds the installers + Flatpak as artifacts (no publish)..github/workflows/relay-release.yml— a relay-only path: tagrelay-v*to ship just a newhaven-relaywithout rebuilding the desktop app.