# โฌก Haven โ€” User Guide Welcome to **Haven**, your private chat server. This guide covers everything you need to get Haven running and invite your friends. --- ## ๐Ÿ“‹ What You Need - **Windows 10 or 11** (macOS / Linux can run it manually) - **Node.js** version 18 or newer โ†’ [Download here](https://nodejs.org/) - About **50 MB** of disk space - **OR** just [Docker](https://docs.docker.com/get-docker/) โ€” no Node.js needed --- ## ๐Ÿณ Docker Setup (Alternative) If you'd rather run Haven in a container (great for NAS boxes, servers, or if you just like Docker): ### Quick Start **Option A โ€” Pre-built image** (fastest): ```bash docker pull ghcr.io/ancsemi/haven:latest docker run -d -p 3000:3000 -v haven_data:/data ghcr.io/ancsemi/haven:latest ``` **Option B โ€” Build from source**: ```bash git clone https://github.com/ancsemi/Haven.git cd Haven docker compose up -d ``` That's it. Haven will be running at `https://localhost:3000`. ### What Happens Automatically - Self-signed SSL certs are generated on first launch (needed for voice chat) - Database, config, and uploads are stored in a Docker volume (`haven_data`) - The container runs as a non-root user for security - Restarts automatically if it crashes ### Customizing Edit `docker-compose.yml` to change the port, server name, or other settings. The environment variables are commented out with examples โ€” just uncomment what you need. ### Using a Local Folder Instead of a Volume If you want your data in a specific folder (common on Synology / NAS): ```yaml volumes: - /path/to/your/haven-data:/data ``` Replace the `haven_data:/data` line in `docker-compose.yml`. ### Updating **Option A โ€” Pre-built image** (default, recommended): ```bash docker compose pull docker compose up -d --force-recreate ``` **Option B โ€” Built from source** (only if you uncommented `build: .`): ```bash git pull docker compose build --no-cache docker compose up -d ``` Your data is safe โ€” it lives in the volume, not the container. ### Checking Your Version Open this URL in your browser (replace with your domain/IP if needed): ``` https://localhost:3000/api/version ``` Or from inside the container: ```bash docker compose exec haven cat /app/package.json | grep '"version"' ``` ### Linux Prerequisites If you're on Linux (Ubuntu, Mint, Debian, etc.), make sure you have Docker's official packages installed โ€” the default `docker.io` package from some distros may be missing Compose V2. **1. Install Docker Engine + Compose plugin:** ```bash sudo apt update sudo apt install ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$UBUNTU_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin ``` **2. Add your user to the `docker` group** (so you don't need `sudo` for every command): ```bash sudo usermod -aG docker $USER newgrp docker ``` After that, `docker compose up -d` should work without errors. --- ## ๐Ÿš€ Getting Started ### Step 1 โ€” First Launch Double-click **`Start Haven.bat`** That's it. The batch file will: 1. Check that Node.js is installed 2. Install dependencies (first time only) 3. Generate SSL certificates (first time only) 4. Start the server 5. Open your browser to the login page ### Step 2 โ€” Create Your Admin Account 1. On the login page, click **Register** 2. Create an account with the admin username (default: `admin` โ€” check your data directory's `.env` file) 3. This account can create and delete channels ### Step 3 โ€” Create a Channel 1. In the sidebar, use the **Create Channel** box (admin only) 2. Give it a name like "General" or "Gaming" 3. Haven generates a unique **channel code** (8 characters) 4. Share this code with your friends โ€” it's the only way in ### Step 4 โ€” Invite Friends Send your friends: 1. Your server address: `https://YOUR_IP:3000` 2. The channel code They'll register their own account, then enter the code to join your channel. --- ## ๐Ÿ“‚ Channels & Sub-Channels ### How Channels Work Every conversation in Haven happens inside a **channel**. Channels are like rooms โ€” each has a unique 8-character code (e.g. `a3f8b2c1`). To get into a channel, you either create it or enter its code. ### Creating Sub-Channels Right-click (or click โ‹ฏ) on any channel to create a **sub-channel** beneath it. Sub-channels appear indented under their parent with a `โ†ณ` icon. They have their own code and their own message history. **When you create a sub-channel:** - All current parent channel members are **automatically added** to it - The sub-channel gets its own unique invite code - Max one level deep (no sub-sub-channels) **When someone joins a parent channel later:** - They're **automatically added** to all non-private sub-channels of that parent - They do NOT get access to private sub-channels (see below) ### Private Sub-Channels ๐Ÿ”’ When creating a sub-channel, check the **๐Ÿ”’ Private** checkbox. Private sub-channels: - Only add the **creator** as initial member (not all parent members) - Show a **๐Ÿ”’** icon instead of `โ†ณ` in the sidebar - Appear in *italic* text with reduced opacity - Can only be joined by entering the sub-channel's code directly - Are invisible to non-members (they won't see it in their channel list) Use private sub-channels for admin-only discussions, sensitive topics, or small breakout groups within a larger channel. --- ## ๐Ÿ“ฅ Importing from Discord Haven can import your entire Discord server's message history โ€” directly from the app. No external tools required. ### Method 1: Direct Connect (Recommended) 1. Open **Settings** (โš™๏ธ in the sidebar) โ†’ scroll to **Import Discord History** 2. Click the **๐Ÿ”— Connect to Discord** tab 3. Get your Discord token: - Open Discord in your browser (or desktop app with dev tools enabled) - Press **F12** โ†’ go to the **Application** tab - In the left sidebar: **Local Storage** โ†’ **https://discord.com** - Find the key called **`token`** and copy its value (without quotes) 4. Paste the token and click **Connect** 5. Pick a server from the grid, then select which channels and threads to import 6. Click **Fetch Messages** โ€” Haven downloads everything 7. In the preview, rename channels if you want, then click **Import** **What gets imported:** messages, replies, embeds, attachments, reactions, pins, forum tags, and original Discord avatars. **Channel types supported:** text, announcement, forum, media, plus active and archived threads. ### Method 2: File Upload If you prefer, export your Discord data with [DiscordChatExporter](https://github.com/Tyrrrz/DiscordChatExporter) (JSON format), then: 1. Open **Settings** โ†’ **Import Discord History** 2. Click the **๐Ÿ“ Upload File** tab 3. Drag/drop or browse for the `.json` or `.zip` file 4. Preview, rename channels, and import ### Important Notes - Imported messages appear as the original Discord usernames, but they're all stored under the admin account. They're clearly marked as imported from Discord. - The import is **history only** โ€” Discord roles, permissions, bots, and webhooks are not imported. - Your Discord token is never stored by Haven. It's used only during the import session and discarded. --- ## ๐Ÿ”‘ Join Code Settings (Admin) Each channel's invite code can be configured by admins. Click the **โš™๏ธ gear icon** next to the channel code in the header. ### Code Visibility | Setting | Behavior | |---------|----------| | **Public** | All members can see the channel code | | **Private** | Only admins see the code; others see `โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข` | ### Code Mode | Setting | Behavior | |---------|----------| | **Static** | Code never changes | | **Dynamic** | Code automatically rotates based on a trigger | ### Rotation Triggers (Dynamic mode only) | Trigger | Behavior | |---------|----------| | **Time-based** | Code rotates every X minutes | | **Join-based** | Code rotates after X new members join | You can also click **Rotate Now** to manually change the code immediately. > ๐Ÿ’ก Dynamic codes are great for public communities where you want to limit code sharing. Old codes stop working after rotation. --- ## ๐Ÿ–ผ๏ธ Avatars ### Uploading a Profile Picture 1. Click the **โš™๏ธ Settings** button in the sidebar 2. In the **Avatar** section, click **Upload** 3. Choose an image (max 2 MB; JPEG, PNG, GIF, or WebP) 4. Pick a shape: โšช Circle, โฌœ Square, โฌก Hexagon, or โ—‡ Diamond 5. Click **Save** Your avatar and shape are visible to everyone in messages and the member list. Each user's shape is stored independently. ### Removing Your Avatar Click **Clear** to remove your avatar and revert to the default initial-letter avatar. --- ## ๐ŸŽจ Themes & Effects ### Themes Haven includes 20+ visual themes. Click the **๐ŸŽจ** button at the bottom of the sidebar to open the theme picker. Themes change colors, fonts, and overall aesthetic. Your choice is saved per browser. ### Effect Overlays Effects are stackable visual layers on top of any theme. Choose from the effect selector in the theme popup: | Effect | Description | |--------|-------------| | **โŸณ Auto** | Matches your current theme's default effect | | **๐Ÿšซ None** | No overlays | | **๐Ÿ“บ CRT** | Retro scanlines + vignette + flicker | | **โ…ฏ Matrix** | Green digital rain cascade | | **โ„ Snowfall** | Falling snowflakes | | **๐Ÿ”ฅ Campfire** | Ember particles + warm glow | | **๐Ÿ’ Golden Grace** | Elden Ring-style golden particles | | **๐Ÿฉธ Blood Vignette** | Dark pulsing edges | | **โ˜ข๏ธ Phosphor** | Fallout-style green vignette | | **โš”๏ธ Water Flow** | Gentle blue sidebar animation | | **๐ŸงŠ Frost** | Ice shimmer + icicle borders | | **โšก Glitch** | Cyberpunk text scramble (see below) | | **โšœ Candlelight** | Warm sidebar glow | | **๐ŸŒŠ Ocean Depth** | Deep blue vignette | | **โœ๏ธ / โ›ช / ๐Ÿ•Š๏ธ** | Sacred themed overlays | ### Cyberpunk Text Scramble โšก When the Glitch effect is active, text around the UI randomly "scrambles" โ€” cycling through random characters before resolving back to the original text. This affects: - The **HAVEN** logo - Channel names in the sidebar - Section labels - Your username - The channel header - User names in the member list A **Glitch Frequency** slider appears in the theme popup when this effect is active. Slide left for rare, subtle glitches โ€” or right for constant chaos. --- ## ๐ŸŒ Setting Up Remote Access (Friends Over the Internet) If your friends are **not** on your local WiFi, you need to set up port forwarding so they can reach your PC from the internet. ### Find Your Public IP Visit [whatismyip.com](https://whatismyip.com) โ€” the number shown (like `203.0.113.50`) is what your friends will use. ### Port Forwarding on Your Router Every router is different, but the general steps are: 1. **Log into your router** โ€” usually `http://192.168.1.1` or `http://10.0.0.1` in your browser 2. Find **Port Forwarding** (sometimes called NAT, Virtual Servers, or Applications) 3. Create a new rule: | Field | Value | |-------|-------| | Port | `3000` | | Protocol | TCP | | Internal IP | Your PC's local IP (e.g. `10.0.0.60`) | 4. Save and apply > **How to find your local IP:** Open Command Prompt and type `ipconfig`. Look for the "IPv4 Address" under your Ethernet or WiFi adapter. ### Windows Firewall The server needs permission to accept incoming connections: 1. Open **Start Menu** โ†’ search **"Windows Defender Firewall"** 2. Click **"Advanced settings"** on the left 3. Click **"Inbound Rules"** โ†’ **"New Rule..."** 4. Select **Port** โ†’ **TCP** โ†’ enter `3000` 5. Allow the connection โ†’ apply to all profiles 6. Name it something like "Haven Chat" Or run this in PowerShell (as Administrator): ```powershell New-NetFirewallRule -DisplayName "Haven_Chat" -Direction Inbound -LocalPort 3000 -Protocol TCP -Action Allow ``` ### Tell Your Friends Send them this URL: ``` https://YOUR_PUBLIC_IP:3000 ``` > โš ๏ธ **Certificate Warning:** Your friends' browsers will show a security warning because Haven uses a self-signed certificate. This is normal and expected. Tell them to click **"Advanced"** โ†’ **"Proceed to site"**. The connection is still encrypted. --- ## โ˜๏ธ Cloudflare Tunnel (No Port Forwarding) If you don't want to mess with port forwarding or expose your home IP, you can use a **Cloudflare Tunnel** to securely share your Haven server over the internet. Cloudflare gives your server a public URL and handles all the networking โ€” no router config needed. ### Step 1 โ€” Install Cloudflared **Windows (via winget):** ```powershell winget install cloudflare.cloudflared ``` **macOS (via Homebrew):** ```bash brew install cloudflared ``` **Linux:** ```bash curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared chmod +x /usr/local/bin/cloudflared ``` Verify it installed: ```bash cloudflared --version ``` ### Step 2 โ€” Enable the Tunnel in Haven 1. Start Haven normally (`Start Haven.bat`) 2. Log in as admin 3. Open **โš™๏ธ Settings** โ†’ scroll to the **Tunnel** section 4. Select **Cloudflare** as the tunnel provider 5. Flip the toggle **on** 6. Haven will start cloudflared and display your public URL (e.g. `https://abc-def-123.trycloudflare.com`) ### Step 3 โ€” Share the URL Copy the tunnel URL and send it to your friends. That's it โ€” no port forwarding, no firewall rules, no IP address sharing. The URL changes each time you restart the tunnel, so you'll need to re-share it. ### How It Works - Haven runs **cloudflared** as a child process that creates an encrypted tunnel to Cloudflare's network - Cloudflare assigns a random public URL and proxies traffic through the tunnel to your local server - Your home IP is **never exposed** to visitors โ€” they only see Cloudflare's IP - Since Haven runs HTTPS with a self-signed cert, the tunnel connects to `https://localhost:3000` with TLS verification disabled (the Cloudflareโ†’You leg is already encrypted by the tunnel itself) ### Tunnel vs. Port Forwarding | | Port Forwarding | Cloudflare Tunnel | |---|---|---| | **Router config** | Required | None | | **Exposes home IP** | Yes | No | | **Firewall rules** | Required | None | | **Stable URL** | Your IP (may change) | Random URL (changes on restart) | | **Push notifications** | โœ… (if HTTPS) | โœ… | | **Voice chat** | โœ… | โœ… | > ๐Ÿ’ก **Tip:** For a permanent URL, you can set up a free Cloudflare account and use a named tunnel with your own domain. See [Cloudflare's tunnel docs](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) for details. ### Troubleshooting Tunnels | Problem | Solution | |---------|----------| | "cloudflared not found" | Restart your terminal after installing, or add it to your PATH manually | | Tunnel shows "502 Bad Gateway" | Make sure Haven is running before enabling the tunnel | | URL changes every restart | Normal for quick tunnels. Use a named tunnel + custom domain for permanence | | "Connection refused" in tunnel logs | Haven isn't running on port 3000, or it's running HTTP instead of HTTPS | --- ## ๐Ÿ” Reverse Proxy (Caddy, nginx, Traefik) If you already have a domain and want Haven to live behind a proper reverse proxy (so you get a real Let's Encrypt cert, no browser warnings, and the same `https://chat.example.com` URL every time), set `FORCE_HTTP=true` and let the proxy terminate TLS for you. ### Quick Recipe (Caddy) 1. **Stop Haven** if it's running. 2. Add the following line to your `.env` file (create one next to `package.json` if it doesn't exist): ```env FORCE_HTTP=true ``` This tells Haven to skip its built-in self-signed cert generation and listen on plain HTTP on port 3000. Caddy will handle the HTTPS leg. 3. **Install Caddy** ([caddyserver.com/download](https://caddyserver.com/download)) and create a `Caddyfile`: ```caddy chat.example.com { reverse_proxy localhost:3000 } ``` Replace `chat.example.com` with your real domain. Caddy will auto-fetch a Let's Encrypt cert on first run. Make sure ports **80 and 443** are open / forwarded to the Caddy host. 4. **Start Caddy**, then **start Haven** (`Start Haven.bat` or `npm start`). 5. Open `https://chat.example.com` in a browser. You should see Haven with a clean padlock and no cert warnings. ### Using a Tunnel + Caddy If you don't want to port-forward 80/443, point a tunnel (Cloudflare Tunnel, Tailscale Funnel, ngrok, etc.) at the Caddy host. The flow becomes: ``` Browser โ†’ Tunnel (HTTPS) โ†’ Caddy (HTTPS) โ†’ Haven (HTTP, FORCE_HTTP=true) ``` Caddy still terminates TLS for the LAN leg, and the tunnel terminates a second TLS layer for the public leg. That's the setup minecraft_bread used successfully (see the support thread for the full step-by-step). ### nginx Snippet ```nginx server { listen 443 ssl http2; server_name chat.example.com; ssl_certificate /etc/letsencrypt/live/chat.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem; location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 86400; } } ``` The `Upgrade` / `Connection` headers are required for Socket.io WebSocket traffic; without them voice chat and live messages will silently break. ### Common Gotchas | Problem | Fix | |---------|-----| | Browser shows Haven's self-signed cert warning instead of the Let's Encrypt one | You forgot `FORCE_HTTP=true`. Haven is still serving its own HTTPS on 3000 and Caddy is just proxying that. Add the line, restart Haven. | | Voice chat / live updates don't work behind nginx | Add the `Upgrade` and `Connection "upgrade"` headers shown above. Caddy handles WebSockets automatically. | | Mixed-content errors in browser console | Make sure the proxy forwards `X-Forwarded-Proto $scheme` so Haven knows it's serving over HTTPS. | | "502 Bad Gateway" from Caddy | Haven isn't running, or it's still bound to HTTPS on 3000. Double-check `FORCE_HTTP=true` is in `.env` and you restarted Haven after adding it. | > ๐Ÿ’ก A full Docker Compose example with Traefik + coturn lives in [`docs/examples/haven-traefik-coturn/`](docs/examples/haven-traefik-coturn/) if you'd rather run the whole stack containerised. --- ## ๐Ÿ”ง Router-Specific Tips ### Xfinity / Comcast (XB7 Gateway) 1. Open the **Xfinity app** on your phone 2. Go to **WiFi** โ†’ scroll down โ†’ **Advanced settings** โ†’ **Port forwarding** 3. Select your PC from the device list 4. Add port `3000` (TCP/UDP) and apply 5. **Important:** Go to **Home** โ†’ disable **xFi Advanced Security** โ€” it silently blocks all inbound connections 6. Verify the **reserved IP** in port forwarding matches your PC's actual IP (`ipconfig` to check) ### Common Issues | Problem | Solution | |---------|----------| | **"SSL_ERROR_RX_RECORD_TOO_LONG"** | Browser is using `https://` but server is running HTTP. Change URL to `http://localhost:3000`, or install OpenSSL and restart (see Troubleshooting below) | | Friends get "took too long to respond" | Port forwarding not set up, or firewall blocking | | Friends get "connection refused" | Server isn't running โ€” launch `Start Haven.bat` | | Can't connect with `https://` | Make sure you're using port 3000, not 443 | | Voice chat doesn't work | Must use `https://` โ€” voice requires a secure connection | | "Certificate error" in browser | Normal โ€” click Advanced โ†’ Proceed | --- ## ๐ŸŽจ Themes Haven comes with 6 themes. Switch between them using the theme buttons at the bottom of the left sidebar: | Button | Theme | Style | |--------|-------|-------| | โฌก | **Haven** | Deep blue/purple (default) | | ๐ŸŽฎ | **Discord** | Dark gray with blue accents | | โ…ฏ | **Matrix** | Black and green, scanline overlay | | โ—ˆ | **Tron** | Black with neon cyan glow | | โŒ | **HALO** | Military green with Mjolnir vibes | | โšœ | **LoTR** | Parchment gold and deep brown | | ๐ŸŒ† | **Cyberpunk** | Neon pink and electric yellow | | โ„ | **Nord** | Arctic blue and frost | | ๐Ÿง› | **Dracula** | Deep purple and blood red | | โš” | **Bloodborne** | Gothic crimson and ash | | โฌš | **Ice** | Pale blue and white | | ๐ŸŒŠ | **Abyss** | Deep ocean darkness | Your theme choice is saved per browser. --- ## ๐ŸŽค Voice Chat 1. Join a text channel first 2. Click **๐ŸŽค Join Voice** in the channel header 3. Allow microphone access when your browser asks 4. Click **๐Ÿ”‡ Mute** to toggle your mic 5. Click **๐Ÿ“ž Leave** to disconnect from voice Voice chat is **peer-to-peer** โ€” audio goes directly between you and other users, not through the server. > Voice requires HTTPS. If you're running locally, use `https://localhost:3000`. For remote connections, use `https://YOUR_IP:3000`. ### TURN Server (Voice Over the Internet) By default, voice/screen sharing uses STUN servers, which work when both users are on the same network or behind simple NATs. For connections across different networks (especially mobile data / 5G), you need a **TURN server** to relay traffic. **Quick setup with coturn (free, open-source):** ```bash # Ubuntu/Debian sudo apt install coturn # /etc/turnserver.conf: listening-port=3478 tls-listening-port=5349 realm=your-domain.com use-auth-secret static-auth-secret=YOUR_RANDOM_SECRET_HERE ``` Then add to your Haven `.env`: ```env TURN_URL=turn:your-server.com:3478 TURN_SECRET=YOUR_RANDOM_SECRET_HERE ``` Restart Haven, and voice/screen sharing will work across any network. > **Docker users:** Add `TURN_URL` and `TURN_SECRET` as environment variables in your `docker-compose.yml`. See the commented example in the default compose file. > **Oracle Cloud / cloud VMs:** Make sure ports 3478 (UDP+TCP) and 49152โ€“65535 (UDP) are open in your security group / firewall rules. These are needed for TURN relay traffic. --- ## ๐ŸŽฎ Rich Presence (What You're Playing & Listening To) Haven can show your current game or track next to your name in the member list, and on your profile card. Games take priority in the member list so the sidebar stays readable; your profile card shows both if you're doing both. **This is off until you turn it on.** Nothing is shared until you link an account. ### Linking your accounts 1. Click **โš™๏ธ Settings** โ†’ **Connections** 2. Pick a source and follow the prompt: | Source | What you do | What it covers | |--------|-------------|----------------| | **Haven's music player** | Nothing, it just works | Anything playing in a Haven voice channel | | **Last.fm** โญ | Enter your username | Spotify, Apple Music, YouTube Music, Navidrome, Plex | | **Steam** | Click Link and sign in | Games | | **Spotify** | Click Link and sign in | Spotify only | **Last.fm is the one to pick for music.** It's just a username, there's no sign-in redirect and nothing gets stored, and because most music apps scrobble to Last.fm, that single connection covers whatever you actually listen with. > Scrobbling has to be turned on in Last.fm's own settings first. Haven's setup > panel walks you through it for each app. ### Controlling what people see - **Master switch** โ€” turn presence off entirely - **Per-category** โ€” show games but not music, or the other way round - **Invisible** โ€” while your status is Invisible, nothing is shared, regardless of the settings above None of it is written to the database. Presence lives in memory and disappears when the server restarts. ### If nothing is showing up - Give it a minute. Haven checks for updates on a timer rather than instantly. - For music, confirm the track actually appears on your Last.fm profile. If it isn't scrobbling there, Haven has nothing to read. - For Steam, your Steam profile's game details must be set to **Public**. Steam's API returns nothing for private profiles. - Check that your status isn't set to Invisible. **Admins:** each provider needs a free API key before anyone can link to it. Settings โ†’ Connections has a **Set up** button per provider with a link to where that key comes from, and keys are saved without a restart. To swap a key later, use **Change key** on that row. --- ## ๐Ÿ”” Push Notifications Push notifications let you receive alerts when someone messages a channel you're in, even when the Haven tab is in the background or closed. ### Requirements - **HTTPS is required.** Push notifications use Service Workers, which only work over `https://` or `localhost`. If you're accessing Haven via a LAN IP like `http://192.168.1.x:3000`, push will **not** work. - A modern browser (Chrome, Edge, Firefox, or Safari 16+) - Haven must be running with SSL certificates (the default if OpenSSL is installed) ### How to Enable 1. Open Haven in your browser via `https://` (e.g., `https://localhost:3000` or `https://your-domain:3000`) 2. Click the **โš™๏ธ Settings** button (bottom of the right sidebar) 3. Scroll to **Push Notifications** and flip the toggle **on** 4. Your browser will ask for notification permission โ€” click **Allow** 5. The status should change to **Enabled** ### Setting Up on Your Devices **Desktop (Windows / macOS / Linux):** - Works in Chrome, Edge, and Firefox out of the box - Make sure you access Haven via `https://` (not `http://`) - If you see "Service worker failed" or "Requires HTTPS", you're on an insecure connection **Mobile (Android):** - Open Haven in **Chrome** or **Edge** via `https://` - Enable push in Settings (same steps as above) - Notifications appear even when Chrome is closed **Mobile (iOS / iPadOS):** - Requires **Safari 16.4+** (iOS 16.4 or later) - First, **Add to Home Screen**: tap Share โ†’ "Add to Home Screen" - Open Haven from the home screen icon (it runs as a web app) - Enable push in Settings โ€” Safari will ask for permission ### Troubleshooting Push | Problem | Solution | |---------|----------| | "Service worker failed" | You're not on HTTPS. Use `https://localhost:3000` or set up SSL certs (see Troubleshooting below) | | "Requires HTTPS" | Access Haven via `https://` instead of `http://` | | "Permission denied" | You blocked notifications. Reset in browser settings: Settings โ†’ Site Settings โ†’ Notifications โ†’ find Haven โ†’ Allow | | Toggle is grayed out | Your browser doesn't support push, or you're in incognito/private mode | | Notifications not appearing | Check your OS notification settings โ€” Haven notifications may be muted at the system level | | Only works on localhost | For LAN/remote access, you need valid SSL. Haven auto-generates self-signed certs if OpenSSL is installed | --- ## โš™๏ธ Configuration All settings are in the `.env` file in your **data directory**: | OS | Data Directory | |----|---------------| | Windows | `%APPDATA%\Haven\` | | Linux / macOS | `~/.haven/` | | Setting | What it does | |---------|-------------| | `PORT` | Server port (default: 3000) | | `ADMIN_USERNAME` | Which username gets admin powers | | `JWT_SECRET` | Auto-generated security key โ€” don't share this | | `HAVEN_DATA_DIR` | Override where data is stored | > `.env` is created automatically on first launch. If you change it, restart the server. --- ## ๐Ÿ’ก Tips - **Bookmark the URL** โ€” so you don't have to type the IP every time - **Keep the bat window open** โ€” closing it stops the server - **Your data is stored separately** โ€” all messages, config, and uploads are in your data directory (`%APPDATA%\Haven` on Windows, `~/.haven` on Linux/macOS), not in the Haven code folder - **Back up your data directory** โ€” copy it somewhere safe to preserve your chat history - **Channel codes are secrets** โ€” treat them like passwords. Anyone with the code can join. --- ## ๐Ÿ” End-to-End Encryption (E2E) All direct messages in Haven are **end-to-end encrypted**. The server never has access to the plaintext of your DMs or the keys needed to decrypt them. ### How It Works - When you first log in, your browser generates an **ECDH P-256 key pair**. - The private key is encrypted (wrapped) with a key **derived from your password** using PBKDF2, and the encrypted blob is stored on the server for cross-device sync. - The server **never sees** your password-derived wrapping key โ€” it's computed in your browser and never transmitted. - When you message someone, both users' public keys are combined via ECDH + HKDF to produce a shared AES-256-GCM encryption key. Messages are encrypted before leaving your browser. ### When Keys Are Preserved (Old Messages Readable) | Scenario | Why it works | |---|---| | Close the tab and reopen it | IndexedDB still has your keys โ€” no password needed | | Refresh the page | Same โ€” IndexedDB survives refreshes | | JWT auto-login (return visit) | IndexedDB has the keys cached | | Log in on a new device/browser | You type your password โ†’ wrapping key is derived โ†’ server backup is downloaded and unwrapped | | Clear cookies (but NOT site data) | IndexedDB is site data, not cookies โ€” keys survive | | Change your password | Private key is re-wrapped with the new password and re-uploaded โ€” the ECDH key pair itself doesn't change | ### When Keys Are Lost (Old Messages Permanently Unreadable) | Scenario | Why keys are lost | |---|---| | Clear all browser/site data when that's your only device | IndexedDB is wiped โ€” on re-login the server backup may still unwrap if password hasn't changed | | Clear browser data **after** changing your password | Server backup was wrapped with the old password โ€” new password can't unwrap it โ†’ new keys generated | | Manually reset encryption keys (๐Ÿ”„ button in DM header) | Intentional wipe โ€” new key pair, old messages unreadable | | Admin deletes your account or resets the database | Server backup gone โ€” if IndexedDB is also empty, fresh keys are generated | **Short version:** Same password + at least one of (IndexedDB **or** server backup) = keys survive. Lost both = old messages gone forever. ### Can Anyone Intercept Messages? | Attack vector | Can they read messages? | Why | |---|---|---| | Server admin reading the database | **No** | Encrypted private key is wrapped with a key derived from YOUR password โ€” admin has the blob but not the key | | Someone with physical server access | **No** | Same reason โ€” the blob is useless without your password | | Man-in-the-middle on the network | **No** | Messages are encrypted client-side before transmission | | Stolen JWT token | **No** | JWT authenticates you, but E2E keys live in your browser's IndexedDB โ€” attacker can't unwrap the server backup without your password | | Someone who knows your password + has your JWT | **Yes** | Equivalent to using your login โ€” they can derive the wrapping key and decrypt everything | | Modified server JavaScript | **Yes** | If the admin pushes tampered JS that exfiltrates keys, all bets are off โ€” this is true of every web-based E2E system | ### Resetting Encryption Keys In any DM conversation, click the **๐Ÿ”„** button in the channel header to reset your encryption keys. This: - Generates a brand new key pair - Makes **all** previous encrypted messages **permanently unreadable** for both parties - Posts a timestamped notice in the chat so both users know when/why old messages became unreadable - Requires you to type **RESET** to confirm (there is no undo) ### Verifying Encryption Click the **๐Ÿ”** button in the DM header to view your **safety number** โ€” a 60-digit code derived from both users' public keys. Compare it with your conversation partner through a separate channel (phone, in person, etc.). If they match, no one is intercepting your conversation. --- ## ๐Ÿค– Bot & Webhook Developer Guide Haven has a built-in bot API powered by webhooks. Bots can send messages, delete messages, play soundboard sounds, and register custom slash commands. > **Looking for ready-made bots and webhooks?** The community library at [**ancsemi/haven-community**](https://github.com/ancsemi/haven-community) collects user-contributed integrations you can deploy as-is โ€” a GitHub releases poster, etc. PRs welcome there if you've built one of your own. See its [`CONTRIBUTING.md`](https://github.com/ancsemi/haven-community/blob/main/CONTRIBUTING.md) for how to submit. ### Creating a Bot 1. Go to **Settings โ†’ Server Admin Settings โ†’ Bots** (or open a channel's settings and look for the webhook/bot option) 2. Create a new webhook โ€” give it a name, optionally set an avatar URL and a callback URL 3. Copy the **Webhook Token** (64-character hex string) โ€” this is your bot's API key ### Sending Messages ``` POST https://your-server.com/api/webhooks/ Content-Type: application/json { "content": "Hello from my bot!", "username": "MyBot", "avatar_url": "https://example.com/avatar.png" } ``` - `content` (required) โ€” message text, max 4000 characters - `username` (optional) โ€” override the bot's display name for this message - `avatar_url` (optional) โ€” override the bot's avatar for this message - `ephemeral` (optional) โ€” when `true`, deliver only to `recipient_id` and do not store in history - `recipient_id` (required when `ephemeral` is `true`) โ€” user id that should receive the private bot message Ephemeral example: ``` POST https://your-server.com/api/webhooks/ Content-Type: application/json { "content": "Your dashboard token: abc123", "ephemeral": true, "recipient_id": 42 } ``` Response (regular): `{ "success": true, "message_id": 123 }` Response (ephemeral): `{ "success": true, "ephemeral": true, "recipient_id": 42, "delivered": true }` ### Deleting Messages ``` DELETE https://your-server.com/api/webhooks//messages/ ``` Bots can delete any message in their assigned channel. Returns `{ "success": true }`. ### Playing Soundboard Sounds ``` POST https://your-server.com/api/webhooks//sounds Content-Type: application/json { "sound": "AOL - You've Got Mail" } ``` Plays the named sound for all users currently viewing the bot's channel. Use `GET /api/sounds` (with a Bearer token) to list available sound names. ### Registering Slash Commands Bots with a `callback_url` can register custom slash commands that users can invoke from chat: **Register:** ``` POST https://your-server.com/api/webhooks//commands Content-Type: application/json { "command": "leaderboard", "description": "Show the current leaderboard" } ``` Optional subcommands can be included to improve autocomplete discoverability: ``` POST https://your-server.com/api/webhooks//commands Content-Type: application/json { "command": "rss", "description": "Manage RSS feeds", "subcommands": [ { "name": "add", "description": "Add an RSS feed" }, { "name": "remove", "description": "Remove an RSS feed" }, { "name": "list", "description": "List active feeds" } ] } ``` The callback payload format is unchanged. Haven still sends `command` as the base command (`rss`) and the full remaining text in `args` (for example `"add https://example.com/feed.xml"`). **List:** ``` GET https://your-server.com/api/webhooks//commands ``` **Unregister:** ``` DELETE https://your-server.com/api/webhooks//commands/leaderboard ``` When a user types `/leaderboard`, Haven sends a POST to your bot's callback URL with the command details, signed with HMAC so you can verify authenticity. ### Rate Limits All webhook endpoints are rate-limited to **30 requests per minute** per IP. ### Callback Payloads If your webhook has a `callback_url` and `callback_secret` configured, Haven will POST command invocations to your URL. The payload includes an HMAC signature in the `X-Haven-Signature` header that you should verify using your callback secret. --- ## ๐Ÿ†˜ Troubleshooting **"SSL_ERROR_RX_RECORD_TOO_LONG" or "ERR_SSL_PROTOCOL_ERROR" in browser** โ†’ Your browser is trying to connect via `https://` but the server is actually running in HTTP mode. This happens when SSL certificates weren't generated (usually because OpenSSL isn't installed). **Quick fix:** Change the URL in your browser from `https://localhost:3000` to `http://localhost:3000`. **Permanent fix:** Install OpenSSL so Haven can generate certificates: 1. Download from [slproweb.com/products/Win32OpenSSL.html](https://slproweb.com/products/Win32OpenSSL.html) (the "Light" version is fine) 2. During install, choose **"Copy OpenSSL DLLs to the Windows system directory"** 3. **Restart your PC** (so OpenSSL is added to PATH) 4. Delete the `certs` folder in your data directory (`%APPDATA%\Haven\certs`) 5. Re-launch `Start Haven.bat` โ€” it will regenerate certificates and start in HTTPS mode **How to tell if you're running HTTP or HTTPS:** Check the server's startup banner in the terminal. If it says `http://localhost:3000` โ€” you're on HTTP. If it says `https://localhost:3000` โ€” you're on HTTPS. The protocol in the URL you use must match. **"Node.js is not installed"** โ†’ Download and install from [nodejs.org](https://nodejs.org/). Restart your PC after installing. **Server starts but browser shows blank page** โ†’ Try clearing your browser cache, or open in an incognito/private window. **Friends can connect locally but not remotely** โ†’ Port forwarding isn't configured correctly. Double-check the port, protocol, and internal IP. **"Error: EADDRINUSE"** โ†’ Another program is using port 3000. Close it, or change the port in `.env`. **Voice chat echoes** โ†’ Use headphones to prevent your speakers from feeding into your microphone. ---

โฌก Haven โ€” Your server. Your rules.