mirror of
https://github.com/ancsemi/Haven.git
synced 2026-10-26 00:29:14 +00:00
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.
678 lines
39 KiB
HTML
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">✓</td><td class="no">✗</td><td class="yes">✓</td><td class="yes">✓</td><td class="no">✗</td></tr>
|
|
<tr><td>No account with a company</td><td class="us yes">✓</td><td class="no">✗</td><td class="note">If self-hosted</td><td class="yes">✓</td><td class="no">✗</td></tr>
|
|
<tr><td>No email or phone number needed</td><td class="us yes">✓</td><td class="no">✗</td><td class="note">Depends on server</td><td class="no">✗</td><td class="no">✗</td></tr>
|
|
<tr><td>End-to-end encrypted DMs</td><td class="us yes">✓</td><td class="no">✗</td><td class="yes">✓</td><td class="no">✗</td><td class="yes">✓</td></tr>
|
|
<tr><td>Voice chat</td><td class="us yes">✓</td><td class="yes">✓</td><td class="yes">✓</td><td class="yes">✓</td><td class="yes">✓</td></tr>
|
|
<tr><td>Screen sharing</td><td class="us yes">✓</td><td class="yes">✓</td><td class="yes">✓</td><td class="yes">✓</td><td class="note">Desktop only</td></tr>
|
|
<tr><td>Open source</td><td class="us yes">✓</td><td class="no">✗</td><td class="yes">✓</td><td class="yes">✓</td><td class="yes">✓</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">✗</td></tr>
|
|
<tr><td>Group channels</td><td class="us yes">✓</td><td class="yes">✓</td><td class="yes">✓</td><td class="yes">✓</td><td class="note">Groups only</td></tr>
|
|
<tr><td>Free, no paid tier</td><td class="us yes">✓</td><td class="note">Freemium</td><td class="yes">✓</td><td class="note">Freemium</td><td class="yes">✓</td></tr>
|
|
<tr><td>Join from a browser, nothing to install</td><td class="us yes">✓</td><td class="yes">✓</td><td class="yes">✓</td><td class="yes">✓</td><td class="no">✗</td></tr>
|
|
<tr><td>Works in a phone's browser</td><td class="us yes">✓</td><td class="note">Pushes you to the app</td><td class="yes">✓</td><td class="yes">✓</td><td class="no">✗</td></tr>
|
|
<tr><td>Import your history from Discord</td><td class="us yes">✓</td><td class="note">n/a</td><td class="note">Via a bridge</td><td class="no">✗</td><td class="no">✗</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">‹</button>
|
|
<img alt="">
|
|
<button class="lb-nav lb-next" type="button" aria-label="Next screenshot">›</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>
|