Files
Haven/docs/index.html
T
ancsemi bb7fbb28af Homepage: logo hero, arrow-key gallery, full-size screenshots, phone beside Security
The logo and name lead the page with the tagline as a small bold title above the lead. Screenshots are stored at full resolution, the gallery has a nav link and steps through every screenshot with the arrow keys or on-screen arrows, the phone shot moves beside the Security and privacy list, a community channel shot sits under the feature list, features get real bullets, and the wording moves backups to Extras and mentions theme effects.
2026-09-09 20:16:27 -04:00

678 lines
39 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="og:title" content="Haven, a chat server you run yourself">
<meta property="og:description" content="Haven is a free, open source alternative to Discord that you host on your own computer. Channels, voice, screen sharing, threads and encrypted DMs. Friends join with a link, the desktop app or the Android app.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://ancsemi.github.io/Haven/">
<meta property="og:image" content="https://ancsemi.github.io/Haven/img/haven-4-6-default.jpg">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Haven, a chat server you run yourself">
<meta name="twitter:description" content="Haven is a free, open source alternative to Discord that you host on your own computer. Channels, voice, screen sharing, threads and encrypted DMs. Friends join with a link, the desktop app or the Android app.">
<meta name="twitter:image" content="https://ancsemi.github.io/Haven/img/haven-4-6-default.jpg">
<meta name="twitter:site" content="@ancsemi">
<link rel="canonical" href="https://ancsemi.github.io/Haven/">
<meta name="theme-color" content="#F6F5F9">
<title>Haven, a chat server you run yourself</title>
<meta name="description" content="Haven is a free, open source alternative to Discord that you host on your own computer. Channels, voice, screen sharing, threads and encrypted DMs. Friends join with a link, the desktop app or the Android app.">
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cpolygon points='50,3 93,28 93,72 50,97 7,72 7,28' fill='none' stroke='%236b4fdb' stroke-width='8' stroke-linejoin='round'/%3E%3Ctext x='50' y='63' text-anchor='middle' font-family='Arial,Helvetica,sans-serif' font-weight='bold' font-size='38' fill='%236b4fdb' opacity='0.95'%3EH%3C/text%3E%3C/svg%3E">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400&family=IBM+Plex+Mono:wght@400;500&display=swap">
<style>
:root {
--paper: #F6F5F9;
--surface: #FFFFFF;
--ink: #1A1726;
--muted: #5E5A70;
--rule: #D9D5E3;
--rule-strong: #B9B3CB;
--accent: #5537C4;
--accent-ink: #FFFFFF;
--accent-soft: #ECE8FA;
--term-bg: #1B1826;
--term-ink: #E6E2F0;
--term-dim: #8F89A6;
--term-accent: #B7A9F8;
--accent-glow: rgba(85, 55, 196, 0.55);
--sans: "IBM Plex Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
--mono: "IBM Plex Mono", Consolas, "SFMono-Regular", Menlo, monospace;
--measure: 66ch;
--wide: 1080px;
--gutter: clamp(18px, 4vw, 40px);
--topbar: 64px;
color-scheme: light;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--paper: #14121C;
--surface: #1C1926;
--ink: #ECE9F3;
--muted: #A39FB3;
--rule: #2C2838;
--rule-strong: #443E57;
--accent: #A497F5;
--accent-ink: #14121C;
--accent-soft: #24203A;
--term-bg: #0F0D16;
--term-ink: #E6E2F0;
--term-dim: #7F7896;
--term-accent: #B7A9F8;
--accent-glow: rgba(164, 151, 245, 0.55);
color-scheme: dark;
}
}
:root[data-theme="dark"] {
--paper: #14121C;
--surface: #1C1926;
--ink: #ECE9F3;
--muted: #A39FB3;
--rule: #2C2838;
--rule-strong: #443E57;
--accent: #A497F5;
--accent-ink: #14121C;
--accent-soft: #24203A;
--term-bg: #0F0D16;
--term-ink: #E6E2F0;
--term-dim: #7F7896;
--term-accent: #B7A9F8;
--accent-glow: rgba(164, 151, 245, 0.55);
color-scheme: dark;
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
margin: 0;
background: var(--paper);
color: var(--ink);
font-family: var(--sans);
font-size: 17px;
line-height: 1.6;
font-feature-settings: "kern" 1, "liga" 1;
}
a { color: var(--accent); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2px; }
a:focus-visible, button:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; }
h1, h2, h3 { font-weight: 600; letter-spacing: -0.015em; line-height: 1.15; margin: 0; text-wrap: balance; }
h1 { font-size: clamp(34px, 5vw, 50px); font-weight: 700; letter-spacing: -0.025em; }
h2 { font-size: clamp(24px, 3vw, 30px); }
h3 { font-size: 19px; }
p { margin: 0; }
p + p { margin-top: 1em; }
code, kbd, .mono { font-family: var(--mono); font-size: 0.92em; }
code { background: var(--accent-soft); padding: 1px 5px; border-radius: 3px; }
ul, ol { margin: 0; padding-left: 1.25em; }
li + li { margin-top: 0.35em; }
img { max-width: 100%; height: auto; display: block; }
hr { border: 0; border-top: 1px solid var(--rule); margin: 0; }
.wrap { max-width: var(--wide); margin: 0 auto; padding: 0 var(--gutter); }
.prose { max-width: var(--measure); }
.muted { color: var(--muted); }
.small { font-size: 15px; }
.num { font-variant-numeric: tabular-nums; }
/* ── Top bar, pinned ─────────────────────────────── */
.top { position: sticky; top: 0; z-index: 20; border-bottom: 1px solid var(--rule); background: var(--paper); }
.top .wrap { display: flex; align-items: center; justify-content: space-between; gap: 24px; min-height: var(--topbar); flex-wrap: wrap; padding-top: 8px; padding-bottom: 8px; }
.brand { display: inline-flex; align-items: center; gap: 10px; color: var(--ink); text-decoration: none; font-weight: 600; font-size: 18px; letter-spacing: 0.01em; }
.brand svg { width: 26px; height: 26px; }
.nav { display: flex; gap: 22px; flex-wrap: wrap; }
.nav a { color: var(--muted); text-decoration: none; font-size: 15px; font-weight: 500; }
.nav a:hover { color: var(--ink); text-decoration: underline; }
.nav .btn-try { background: var(--accent); color: var(--accent-ink); padding: 8px 14px; border-radius: 4px; font-weight: 600; font-size: 14px; line-height: 1.2; }
.nav .btn-try:hover { color: var(--accent-ink); text-decoration: none; filter: brightness(1.08); }
@keyframes pulse-ring { 0% { box-shadow: 0 0 0 0 var(--accent-glow); } 70% { box-shadow: 0 0 0 16px transparent; } 100% { box-shadow: 0 0 0 0 transparent; } }
.btn.pulse { border-color: var(--accent); background: var(--accent-soft); animation: pulse-ring 1.2s ease-out 3; }
/* ── Sections ────────────────────────────────────── */
section { padding: clamp(56px, 8vw, 96px) 0; border-top: 1px solid var(--rule); scroll-margin-top: var(--topbar); }
section:first-of-type { border-top: 0; }
.section-head { margin-bottom: 28px; }
.section-head p { margin-top: 12px; color: var(--muted); max-width: var(--measure); }
/* ── Hero ────────────────────────────────────────── */
.hero { padding-top: clamp(40px, 6vw, 72px); }
.hero-grid { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); gap: clamp(28px, 5vw, 64px); align-items: start; }
.hero-brand { display: inline-flex; align-items: center; gap: 0.32em; color: var(--ink); font-weight: 600; font-size: clamp(48px, 7vw, 76px); letter-spacing: 0.01em; line-height: 1; }
.hero-brand svg { width: 1.05em; height: 1.05em; flex: none; }
.hero-title { font-size: clamp(20px, 2.4vw, 25px); font-weight: 700; letter-spacing: -0.01em; margin-top: 30px; }
.hero .lead { font-size: 20px; line-height: 1.5; margin-top: 10px; max-width: 34em; }
.actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-top: 30px; }
.btn { display: inline-flex; align-items: center; gap: 8px; padding: 11px 18px; border-radius: 4px; font-weight: 600; font-size: 16px; text-decoration: none; border: 1px solid transparent; line-height: 1.2; }
.btn-primary { background: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { filter: brightness(1.08); text-decoration: none; }
.btn-secondary { background: transparent; color: var(--ink); border-color: var(--rule-strong); }
.btn-secondary:hover { border-color: var(--ink); text-decoration: none; }
.btn .ver { font-family: var(--mono); font-weight: 500; font-size: 14px; opacity: 0.85; }
.actions .plain { font-size: 15px; }
.term { background: var(--term-bg); color: var(--term-ink); border-radius: 6px; padding: 18px 20px; font-family: var(--mono); font-size: 13.5px; line-height: 1.5; overflow-x: auto; border: 1px solid var(--rule-strong); }
.term pre { margin: 0; white-space: pre-wrap; }
.term .dim { color: var(--term-dim); }
.term .acc { color: var(--term-accent); }
.term-cap { margin-top: 12px; font-size: 14px; color: var(--muted); max-width: 46ch; }
.shot { margin-top: clamp(40px, 6vw, 64px); }
.shot img { border: 1px solid var(--rule-strong); border-radius: 6px; width: 100%; }
.shot figcaption, .fig figcaption { font-size: 14px; color: var(--muted); margin-top: 10px; }
figure { margin: 0; }
/* ── About ───────────────────────────────────────── */
.split { display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: clamp(28px, 5vw, 72px); align-items: start; }
.split .fig img { border: 1px solid var(--rule-strong); border-radius: 18px; }
/* ── Feature lists ───────────────────────────────── */
.cols { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0; }
.cols > div { padding: 0 24px; border-left: 1px solid var(--rule); }
.cols > div:first-child { padding-left: 0; border-left: 0; }
.cols > div:last-child { padding-right: 0; }
.cols h3 { margin-bottom: 10px; padding-bottom: 8px; border-bottom: 1px solid var(--rule); }
.cols ul { list-style: disc; padding-left: 1.1em; }
.cols li { font-size: 16px; }
.cols li::marker { color: var(--accent); }
.cols li + li { margin-top: 0.5em; }
/* ── Steps ───────────────────────────────────────── */
.steps { counter-reset: step; list-style: none; padding: 0; max-width: var(--measure); }
.steps li { position: relative; padding-left: 52px; }
.steps li + li { margin-top: 20px; }
.steps li::before { counter-increment: step; content: counter(step); position: absolute; left: 0; top: 0; width: 34px; height: 34px; border: 1px solid var(--rule-strong); border-radius: 50%; display: grid; place-items: center; font-family: var(--mono); font-size: 14px; color: var(--ink); background: var(--surface); }
.steps strong { font-weight: 600; }
/* ── Table ───────────────────────────────────────── */
.tablewrap { overflow-x: auto; margin-top: 20px; }
table { border-collapse: collapse; width: 100%; font-size: 15px; }
th, td { text-align: left; vertical-align: top; padding: 11px 14px 11px 0; border-bottom: 1px solid var(--rule); }
th { font-weight: 600; font-size: 13px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
td:first-child { font-family: var(--mono); font-size: 14px; max-width: 16em; }
tbody tr:last-child td { border-bottom: 1px solid var(--rule-strong); }
/* ── Compare ─────────────────────────────────────── */
.compare th:first-child, .compare td:first-child { font-family: var(--sans); font-size: 15px; max-width: none; }
.compare th:not(:first-child), .compare td:not(:first-child) { text-align: center; padding-left: 10px; padding-right: 10px; white-space: nowrap; }
.compare th.us, .compare td.us { background: var(--accent-soft); font-weight: 600; }
.compare .yes { color: var(--accent); font-weight: 700; }
.compare .no { color: var(--muted); }
.compare .note { font-size: 14px; color: var(--muted); }
.compare-cap { font-size: 14px; color: var(--muted); margin-top: 12px; }
/* ── Gallery ─────────────────────────────────────── */
.gallery { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px 24px; }
.gallery img { border: 1px solid var(--rule-strong); border-radius: 6px; width: 100%; }
.gallery figcaption { font-size: 14px; color: var(--muted); margin-top: 8px; }
@media (max-width: 700px) { .gallery { grid-template-columns: 1fr; } }
/* ── Lightbox ────────────────────────────────────── */
.zoomable { cursor: zoom-in; }
.lightbox { position: fixed; inset: 0; z-index: 50; background: rgba(10, 8, 16, 0.92); display: flex; align-items: center; justify-content: center; padding: 24px; cursor: zoom-out; }
.lightbox[hidden] { display: none; }
.lightbox img { max-width: 100%; max-height: 100%; border-radius: 6px; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5); }
.lb-nav { position: absolute; top: 50%; transform: translateY(-50%); width: 44px; height: 44px; border: 0; border-radius: 50%; background: rgba(255, 255, 255, 0.14); color: #fff; font-size: 30px; line-height: 1; cursor: pointer; display: grid; place-items: center; padding: 0 0 4px; }
.lb-nav:hover { background: rgba(255, 255, 255, 0.26); }
.lb-prev { left: 18px; }
.lb-next { right: 18px; }
@media (max-width: 700px) { .lb-nav { width: 36px; height: 36px; font-size: 24px; } .lb-prev { left: 8px; } .lb-next { right: 8px; } }
/* ── Facts ───────────────────────────────────────── */
.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0; border-top: 1px solid var(--rule-strong); border-bottom: 1px solid var(--rule-strong); }
.facts div { padding: 18px 16px 18px 0; border-bottom: 1px solid var(--rule); }
.facts dt { font-size: 13px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.facts dd { margin: 4px 0 0; font-size: 22px; font-weight: 600; font-variant-numeric: tabular-nums; }
/* ── Download ────────────────────────────────────── */
.dl { border: 1px solid var(--rule-strong); border-radius: 6px; background: var(--surface); }
.dl-row { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: 20px; padding: 22px 24px; align-items: center; }
.dl-row + .dl-row { border-top: 1px solid var(--rule); }
.dl-row h3 { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.dl-row h3 .ver { font-family: var(--mono); font-weight: 500; font-size: 15px; color: var(--muted); }
.dl-row p { color: var(--muted); font-size: 15px; margin-top: 4px; }
.dl-links { display: flex; flex-wrap: wrap; gap: 10px; justify-content: flex-end; }
.dl-links .btn { padding: 9px 14px; font-size: 15px; }
.try { margin-top: 22px; font-size: 16px; }
/* ── Footer ──────────────────────────────────────── */
footer { border-top: 1px solid var(--rule); padding: 36px 0 48px; color: var(--muted); font-size: 15px; }
footer .wrap { display: flex; flex-wrap: wrap; gap: 12px 24px; justify-content: space-between; align-items: center; }
footer nav { display: flex; flex-wrap: wrap; gap: 8px 20px; }
footer a { color: var(--muted); }
footer a:hover { color: var(--ink); }
@media (max-width: 860px) {
.hero-grid, .split, .dl-row { grid-template-columns: 1fr; }
.cols { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 28px; }
.cols > div:nth-child(odd) { border-left: 0; padding-left: 0; }
.cols > div:nth-child(even) { padding-right: 0; }
.split .fig { max-width: 260px; }
.dl-links { justify-content: flex-start; }
}
@media (max-width: 520px) {
body { font-size: 16px; }
.cols { grid-template-columns: 1fr; row-gap: 24px; }
.cols > div { border-left: 0; padding: 0; }
.cols > div + div { border-top: 1px solid var(--rule); padding-top: 20px; }
.nav { gap: 14px; }
:root { --topbar: 96px; }
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } * { transition: none !important; } .btn.pulse { animation: none; box-shadow: 0 0 0 3px var(--accent); } }
</style>
</head>
<body>
<header class="top">
<div class="wrap">
<a class="brand" href="#top" aria-label="Haven">
<svg viewBox="0 0 100 100" aria-hidden="true"><polygon points="50,3 93,28 93,72 50,97 7,72 7,28" fill="none" stroke="currentColor" stroke-width="8" stroke-linejoin="round"/></svg>
Haven
</a>
<nav class="nav" aria-label="Sections">
<a href="#about">About</a>
<a href="#features">Features</a>
<a href="#compare">Compare</a>
<a href="#setup">Setup</a>
<a href="#security">Security</a>
<a href="#screenshots">Gallery</a>
<a href="#download">Download</a>
<a href="https://github.com/ancsemi/Haven">GitHub</a>
<a class="btn-try" id="try-now" href="#top">Try it now</a>
</nav>
</div>
</header>
<main id="top">
<section class="hero">
<div class="wrap">
<div class="hero-grid">
<div>
<div class="hero-brand" aria-hidden="true">
<svg viewBox="0 0 100 100"><polygon points="50,3 93,28 93,72 50,97 7,72 7,28" fill="none" stroke="currentColor" stroke-width="8" stroke-linejoin="round"/></svg>
Haven
</div>
<h1 class="hero-title">A chat server you run yourself.</h1>
<p class="lead">Haven is a free, open source alternative to Discord. It runs on your own computer, your friends join with a link, and there's no company in the middle.</p>
<div class="actions">
<a class="btn btn-primary" href="https://github.com/ancsemi/Haven/archive/refs/tags/v4.6.0.zip">Download Haven <span class="ver">4.6.0</span></a>
<a class="btn btn-secondary" id="try-server" href="https://haven.moviethingy.xyz/?invite=da0b9be7">Join the community server</a>
<a class="plain" href="https://github.com/ancsemi/Haven">Source on GitHub</a>
</div>
</div>
<div>
<div class="term" role="img" aria-label="Haven's start-up message in a terminal: HAVEN is running, with the local and network addresses">
<pre><span class="dim">$</span> <span class="acc">Start Haven.bat</span>
<span class="acc">⬡ HAVEN is running</span>
------------------------------------
Name: Haven
Local: https://localhost:3000
Network: https://192.168.1.20:3000
Admin: you
------------------------------------
<span class="dim">Send your friends the link.</span></pre>
</div>
<p class="term-cap">What you see when you start it up.</p>
</div>
</div>
<figure class="shot">
<img class="zoomable" src="img/haven-4-6-default.jpg" width="1918" height="1010" alt="Haven's default theme: channels and DMs on the left, the member list on the right">
</figure>
</div>
</section>
<section id="about">
<div class="wrap">
<div class="prose">
<div class="section-head">
<h2>About</h2>
</div>
<p>Haven works like a Discord server, but you host it yourself. Download the zip, run it, and it gives you a link your friends can use from a browser, the desktop app or the Android app.</p>
<p>There's no company in the middle. No account with us, no email or phone number, and nothing to pay for. Your messages, files and voice calls stay on your own server.</p>
<p>Haven has been in development since February 2026. It's a spare-time project and it'll always be free.</p>
<p>Many helpful people with a shared passion for free, unfiltered and private chat have come together to improve it. Every change is public, with a <a href="https://github.com/ancsemi/Haven/blob/main/CHANGELOG.md">changelog</a> updated every release.</p>
</div>
</div>
</section>
<section id="features">
<div class="wrap">
<div class="section-head">
<h2>Features</h2>
<p>The short version. The <a href="https://github.com/ancsemi/Haven/blob/main/GUIDE.md">guide</a> covers everything.</p>
</div>
<div class="cols">
<div>
<h3>Chat</h3>
<ul>
<li>Text channels, threads and forums</li>
<li>Voice chat</li>
<li>Screen sharing, with more than one stream at a time</li>
<li>Watch videos and listen to music together</li>
<li>Encrypted direct messages</li>
<li>Reactions, stickers, polls and GIFs</li>
</ul>
</div>
<div>
<h3>Coming from Discord</h3>
<ul>
<li>Import your whole server, including threads, pins and attachments</li>
<li>Ferry bridges a Haven channel to a Discord channel, so friends who stay behind can still talk to you</li>
<li>Roles and permissions that work the way you're used to</li>
</ul>
</div>
<div>
<h3>Customization</h3>
<ul>
<li>26 themes with customizable effects, or write your own CSS</li>
<li>Bots and webhooks</li>
<li>Sign in with your own identity provider</li>
<li>Guest access, invite links and channel codes</li>
<li>Available in 8 languages</li>
</ul>
</div>
<div>
<h3>Extras</h3>
<ul>
<li>Rich Presence: show what you're playing or listening to</li>
<li>Built-in games with leaderboards</li>
<li>Flash games, through Ruffle</li>
<li>Backups and updates from the settings page</li>
</ul>
</div>
</div>
<figure class="shot">
<img class="zoomable" src="img/haven-features.jpg" width="1915" height="1005" alt="A community channel in the Braid theme with the member list open" loading="lazy">
</figure>
</div>
</section>
<section id="compare">
<div class="wrap">
<div class="section-head">
<h2>Compared to the others</h2>
<p>How Haven stacks up against the usual suspects.</p>
</div>
<div class="tablewrap">
<table class="compare">
<thead>
<tr><th></th><th class="us">Haven</th><th>Discord</th><th>Matrix (Element)</th><th>Fluxer</th><th>Signal</th></tr>
</thead>
<tbody>
<tr><td>Self-hosted, you own the data</td><td class="us yes">&#10003;</td><td class="no">&#10007;</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td><td class="no">&#10007;</td></tr>
<tr><td>No account with a company</td><td class="us yes">&#10003;</td><td class="no">&#10007;</td><td class="note">If self-hosted</td><td class="yes">&#10003;</td><td class="no">&#10007;</td></tr>
<tr><td>No email or phone number needed</td><td class="us yes">&#10003;</td><td class="no">&#10007;</td><td class="note">Depends on server</td><td class="no">&#10007;</td><td class="no">&#10007;</td></tr>
<tr><td>End-to-end encrypted DMs</td><td class="us yes">&#10003;</td><td class="no">&#10007;</td><td class="yes">&#10003;</td><td class="no">&#10007;</td><td class="yes">&#10003;</td></tr>
<tr><td>Voice chat</td><td class="us yes">&#10003;</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td></tr>
<tr><td>Screen sharing</td><td class="us yes">&#10003;</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td><td class="note">Desktop only</td></tr>
<tr><td>Open source</td><td class="us yes">&#10003;</td><td class="no">&#10007;</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td></tr>
<tr><td>Themes and custom CSS</td><td class="us note">26 themes + CSS</td><td class="note">Limited</td><td class="note">Depends on client</td><td class="note">CSS</td><td class="no">&#10007;</td></tr>
<tr><td>Group channels</td><td class="us yes">&#10003;</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td><td class="note">Groups only</td></tr>
<tr><td>Free, no paid tier</td><td class="us yes">&#10003;</td><td class="note">Freemium</td><td class="yes">&#10003;</td><td class="note">Freemium</td><td class="yes">&#10003;</td></tr>
<tr><td>Join from a browser, nothing to install</td><td class="us yes">&#10003;</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td><td class="no">&#10007;</td></tr>
<tr><td>Works in a phone's browser</td><td class="us yes">&#10003;</td><td class="note">Pushes you to the app</td><td class="yes">&#10003;</td><td class="yes">&#10003;</td><td class="no">&#10007;</td></tr>
<tr><td>Import your history from Discord</td><td class="us yes">&#10003;</td><td class="note">n/a</td><td class="note">Via a bridge</td><td class="no">&#10007;</td><td class="no">&#10007;</td></tr>
</tbody>
</table>
</div>
<p class="compare-cap">Free tiers and default hosted setups, as of September 2026.</p>
</div>
</section>
<section id="setup">
<div class="wrap">
<div class="section-head">
<h2>Setup</h2>
<p>No terminal, no config files. There's a Docker image too.</p>
</div>
<ol class="steps">
<li><strong>Download the zip</strong> and unzip it anywhere.</li>
<li><strong>Double-click <code>Start Haven.bat</code></strong> (<code>start.sh</code> on Mac and Linux). It sets everything up and prints your link. If Node.js isn't installed yet, it offers to install it for you.</li>
<li><strong>Send the link to your friends.</strong> They can use a browser, the desktop app or the Android app.</li>
</ol>
<p class="prose muted" style="margin-top: 26px;">To reach friends outside your home network you'll need to open a port or use a tunnel. The guide covers both, and there's a one-click tunnel in the admin settings.</p>
</div>
</section>
<section id="security">
<div class="wrap">
<div class="section-head">
<h2>Security and privacy</h2>
<p>How it actually works.</p>
</div>
<div class="split">
<div class="prose">
<ul>
<li><strong>Your data lives on the computer running the server,</strong> in one folder. Back it up, move it or delete it whenever you want.</li>
<li><strong>Direct messages are end-to-end encrypted.</strong> The server stores them but can't read them, and neither can the admin.</li>
<li><strong>Channel messages aren't end-to-end encrypted.</strong> Whoever runs the server can read them, just like a Discord admin can. Join servers run by people you trust, or run your own.</li>
<li><strong>Connections use HTTPS.</strong> Haven creates its own certificate the first time it starts, and you can swap in a real one.</li>
<li><strong>No telemetry.</strong> The server doesn't send usage data or crash reports anywhere.</li>
<li><strong>There's no central Haven server and no Haven account.</strong> Nothing to leak.</li>
</ul>
</div>
<figure class="fig">
<img class="zoomable" src="img/haven-phone-welcome.jpg" width="480" height="1040" alt="Haven's welcome screen in a phone browser" loading="lazy">
<figcaption>Joining from a phone browser. No app needed.</figcaption>
</figure>
</div>
<div class="prose">
<h3 style="margin-top: 36px;">What the server connects to</h3>
<p class="muted small" style="margin-top: 6px;">Every outside service a Haven server contacts, and when.</p>
</div>
<div class="tablewrap">
<table>
<thead>
<tr><th>Who</th><th>When</th><th>Why</th><th>Off switch</th></tr>
</thead>
<tbody>
<tr><td>api.github.com</td><td>When an admin clicks Check for updates</td><td>To see the latest version</td><td>Don't click it</td></tr>
<tr><td>Google Firebase, through the Haven push relay</td><td>When someone uses the Android app with push notifications on</td><td>To deliver the notification. The message title and text pass through it</td><td>Settings, Security</td></tr>
<tr><td>tenor.googleapis.com, api.giphy.com</td><td>When someone searches for a GIF</td><td>The search goes through your server, not the person's browser</td><td>Only when used</td></tr>
<tr><td>The site behind a posted link</td><td>When a message contains a link</td><td>Your server fetches the preview and the image, so your friends' browsers never touch that site</td><td>Per link</td></tr>
<tr><td>discord.com</td><td>Only if you set up Import or Ferry</td><td>To copy or bridge your Discord server</td><td>Off until you set it up</td></tr>
<tr><td>Spotify, Last.fm, Steam</td><td>Only for people who link an account</td><td>To show what they're playing or listening to</td><td>Per person</td></tr>
<tr><td>unicode.org</td><td>Monthly, only if you turn on emoji updates</td><td>New emoji</td><td>Off by default</td></tr>
<tr><td>ipify.org, portchecker.io</td><td>When you run the connectivity test or use dynamic DNS</td><td>To find your public address</td><td>Only when used</td></tr>
</tbody>
</table>
</div>
</div>
</section>
<section id="numbers">
<div class="wrap">
<div class="section-head">
<h2>By the numbers</h2>
<p id="stats-caption">As of 9 September 2026.</p>
</div>
<dl class="facts">
<div><dt>Started</dt><dd data-stat="started">Feb 2026</dd></div>
<div><dt>Releases</dt><dd class="num" data-stat="releases">156</dd></div>
<div><dt>Commits</dt><dd class="num" data-stat="commits">1,228</dd></div>
<div><dt>Contributors</dt><dd class="num" data-stat="contributors">20</dd></div>
<div><dt>GitHub stars</dt><dd class="num" data-stat="stars">542</dd></div>
<div><dt>License</dt><dd>AGPL-3.0</dd></div>
</dl>
</div>
</section>
<section id="download">
<div class="wrap">
<div class="section-head">
<h2>Download</h2>
<p>Only the host needs the server. Everyone else can join from the app or any browser.</p>
</div>
<div class="dl">
<div class="dl-row">
<div>
<h3>Haven Server <span class="ver">4.6.0</span></h3>
<p>Windows, macOS and Linux. Needs Node.js. About 5 MB.</p>
</div>
<div class="dl-links">
<a class="btn btn-primary" href="https://github.com/ancsemi/Haven/archive/refs/tags/v4.6.0.zip">Download .zip</a>
<a class="btn btn-secondary" href="https://github.com/ancsemi/Haven/blob/main/GUIDE.md">Setup guide</a>
</div>
</div>
<div class="dl-row">
<div>
<h3>Haven Desktop <span class="ver">1.4.30</span></h3>
<p>Adds per-app audio sharing, mid-call device switching and a system tray. It can run the server too.</p>
</div>
<div class="dl-links">
<a class="btn btn-secondary" href="https://github.com/ancsemi/Haven-Desktop/releases/download/v1.4.30/Haven-Setup-1.4.30.exe">Windows installer</a>
<a class="btn btn-secondary" href="https://github.com/ancsemi/Haven-Desktop/releases/download/v1.4.30/Haven-1.4.30.AppImage">Linux AppImage</a>
<a class="btn btn-secondary" href="https://github.com/ancsemi/Haven-Desktop/releases/download/v1.4.30/haven-desktop_1.4.30_amd64.deb">Linux .deb</a>
</div>
</div>
<div class="dl-row">
<div>
<h3>Haven for Android</h3>
<p>A native app with chat, voice and push notifications.</p>
</div>
<div class="dl-links">
<a class="btn btn-secondary" href="https://play.google.com/store/apps/details?id=com.havenapp.mobile">Get it on Google Play</a>
</div>
</div>
</div>
<p class="try">Want to try it first? Drop in to <a href="https://haven.moviethingy.xyz/?invite=da0b9be7">the community server</a> to say hi, or just to explore Haven in your browser. Throwaway accounts welcome! The channel code is <code>da0b9be7</code> if you need it.</p>
</div>
</section>
<section id="support">
<div class="wrap">
<div class="prose">
<div class="section-head">
<h2>Support</h2>
</div>
<p>Haven is free and always will be. Donations are always appreciated, but never needed. If you'd like to chip in, you can <a href="https://ko-fi.com/ancsemi">buy me a coffee on Ko-fi</a>.</p>
</div>
</div>
</section>
<section id="screenshots">
<div class="wrap">
<div class="section-head">
<h2>Gallery</h2>
</div>
<div class="gallery">
<figure><img class="zoomable" src="img/haven-screen-share.jpg" width="1200" height="591" alt="Screen sharing with voice chat and more than one stream" loading="lazy"></figure>
<figure><img class="zoomable" src="img/haven-theme-chat.jpg" width="1200" height="593" alt="The login screen with the theme picker" loading="lazy"></figure>
<figure><img class="zoomable" src="img/haven-theme-crt.jpg" width="1916" height="1006" alt="A DM window and profile card in the CRT theme" loading="lazy"></figure>
<figure><img class="zoomable" src="img/haven-theme-braid.jpg" width="1918" height="1009" alt="A chat channel in the Braid theme" loading="lazy"></figure>
</div>
</div>
</section>
</main>
<div class="lightbox" id="lightbox" role="dialog" aria-modal="true" aria-label="Enlarged screenshot" tabindex="-1" hidden>
<button class="lb-nav lb-prev" type="button" aria-label="Previous screenshot">&#8249;</button>
<img alt="">
<button class="lb-nav lb-next" type="button" aria-label="Next screenshot">&#8250;</button>
</div>
<script>
// Screenshots open full size. The arrow keys, or the arrows on screen, step
// through every screenshot on the page; click anywhere else or press Escape
// to close.
(() => {
const box = document.getElementById('lightbox');
if (!box) return;
const big = box.querySelector('img');
const shots = Array.from(document.querySelectorAll('img.zoomable'));
let current = -1;
const show = (i) => {
current = (i + shots.length) % shots.length;
const img = shots[current];
big.src = img.currentSrc || img.src; big.alt = img.alt;
};
const open = (img) => { show(shots.indexOf(img)); box.hidden = false; document.body.style.overflow = 'hidden'; box.focus(); };
const close = () => { box.hidden = true; big.removeAttribute('src'); document.body.style.overflow = ''; };
shots.forEach((img) => {
img.tabIndex = 0; img.setAttribute('role', 'button'); img.setAttribute('aria-label', 'Enlarge screenshot');
img.addEventListener('click', () => open(img));
img.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); open(img); } });
});
box.querySelector('.lb-prev').addEventListener('click', (e) => { e.stopPropagation(); show(current - 1); });
box.querySelector('.lb-next').addEventListener('click', (e) => { e.stopPropagation(); show(current + 1); });
box.addEventListener('click', close);
document.addEventListener('keydown', (e) => {
if (box.hidden) return;
if (e.key === 'Escape') close();
else if (e.key === 'ArrowRight' || e.key === 'ArrowDown') { e.preventDefault(); show(current + 1); }
else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { e.preventDefault(); show(current - 1); }
});
})();
</script>
<script>
// "Try it now" in the bar: back to the top, then light up the community server button.
(() => {
const btn = document.getElementById('try-now');
const target = document.getElementById('try-server');
if (!btn || !target) return;
let timer = null;
btn.addEventListener('click', (e) => {
e.preventDefault();
window.scrollTo({ top: 0, behavior: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth' });
clearTimeout(timer);
target.classList.remove('pulse');
void target.offsetWidth;
target.classList.add('pulse');
target.focus({ preventScroll: true });
timer = setTimeout(() => target.classList.remove('pulse'), 3800);
});
})();
// "By the numbers" refreshes itself from GitHub's public API. If the request
// fails (offline, rate limited, blocked), the figures baked into the page stay.
(async () => {
const api = 'https://api.github.com/repos/ancsemi/Haven';
const headers = { Accept: 'application/vnd.github+json' };
const fmt = (n) => Number(n).toLocaleString('en-US');
const set = (k, v) => { const el = document.querySelector('[data-stat="' + k + '"]'); if (el && v != null && v !== '') el.textContent = v; };
const apply = (d) => {
set('stars', fmt(d.stars)); set('releases', fmt(d.releases)); set('commits', fmt(d.commits)); set('contributors', fmt(d.contributors));
if (d.started) set('started', new Date(d.started).toLocaleDateString('en-US', { month: 'short', year: 'numeric' }));
const cap = document.getElementById('stats-caption');
if (cap) cap.textContent = 'Live from GitHub.';
};
const cacheKey = 'haven_site_stats_v1';
try {
const c = JSON.parse(localStorage.getItem(cacheKey) || 'null');
if (c && Date.now() - c.t < 3600000) { apply(c.d); return; }
} catch { /* no cache */ }
// Counting endpoints: ask for one item and read the last page number.
const count = async (path) => {
const r = await fetch(api + '/' + path + '?per_page=1', { headers });
if (!r.ok) throw new Error(String(r.status));
const m = (r.headers.get('Link') || '').match(/[?&]page=(\d+)>;\s*rel="last"/);
return m ? Number(m[1]) : (await r.json()).length;
};
try {
const r = await fetch(api, { headers });
if (!r.ok) throw new Error(String(r.status));
const repo = await r.json();
const [releases, commits, contributors] = await Promise.all([count('releases'), count('commits'), count('contributors')]);
const d = { stars: repo.stargazers_count, releases, commits, contributors, started: repo.created_at };
try { localStorage.setItem(cacheKey, JSON.stringify({ t: Date.now(), d })); } catch { /* no storage */ }
apply(d);
} catch { /* keep the baked-in numbers */ }
})();
</script>
<footer>
<div class="wrap">
<nav aria-label="Project links">
<a href="https://github.com/ancsemi/Haven">Server on GitHub</a>
<a href="https://github.com/ancsemi/Haven-Desktop">Desktop on GitHub</a>
<a href="https://github.com/ancsemi/Haven/blob/main/GUIDE.md">Setup guide</a>
<a href="https://github.com/ancsemi/Haven/blob/main/CHANGELOG.md">Changelog</a>
<a href="https://ancsemi.github.io/Haven/support.html">Help and support</a>
<a href="https://github.com/ancsemi/Haven/issues">Report a bug</a>
<a href="https://github.com/ancsemi/Haven/blob/main/LICENSE">AGPL-3.0</a>
</nav>
<span>Your server, your rules.</span>
</div>
</footer>
</body>
</html>