Host everything on the website: hosting guide, setup files, downloads; source code button

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-30 18:55:35 +04:00
co-authored by Claude Opus 5.5
parent cac68780ee
commit 92929e3194
14 changed files with 342 additions and 14 deletions
+2
View File
@@ -1 +1,3 @@
.DS_Store .DS_Store
downloads/*
!downloads/.gitkeep
+4 -3
View File
@@ -5,8 +5,9 @@ It's plain static files with nothing to build, and it loads nothing from other s
- Keep `/support.html` and `/privacy.html` at these addresses: the App Store, Google Play, Chrome and Firefox listings point to them. - Keep `/support.html` and `/privacy.html` at these addresses: the App Store, Google Play, Chrome and Firefox listings point to them.
- To release a platform, edit its card in `index.html`: set the store link, then remove the `soon` class and the badge. The main button picks it up. - To release a platform, edit its card in `index.html`: set the store link, then remove the `soon` class and the badge. The main button picks it up.
- For a new Windows version, change the version in the Windows card's link. - For a new Windows version, put the installer in `downloads/` and change the version in the Windows card's link.
- `host/` holds the server setup files that `host.html` tells people to download. Refresh them from `kestrove-server/selfhost` when those change.
To host it, serve this folder with any web server. `deploy/Caddyfile.snippet` is a Caddy example with the security headers. Installers are not kept in git. Copy them to `downloads/` on the server.
To preview it locally, run `python3 -m http.server 8090` here. To host it, copy this folder to the server and run `docker compose up -d` in `deploy/`. To preview it locally, run `python3 -m http.server 8090` here.
@@ -1,7 +1,6 @@
# The Kestrove website: static files only. Replace DOMAIN and the root path. kestrove.samialabri.com {
DOMAIN {
encode zstd gzip encode zstd gzip
root * /srv/kestrove-website root * /srv/site
header { header {
Strict-Transport-Security "max-age=63072000" Strict-Transport-Security "max-age=63072000"
X-Content-Type-Options nosniff X-Content-Type-Options nosniff
+19
View File
@@ -0,0 +1,19 @@
# The Kestrove website. Run from this folder: docker compose up -d
name: kestrove-website
services:
web:
image: caddy:2.8-alpine
restart: unless-stopped
read_only: true
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ..:/srv/site:ro
- caddy-data:/data
- caddy-config:/config
volumes:
caddy-data:
caddy-config:
View File
+124
View File
@@ -0,0 +1,124 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Host Kestrove</title>
<meta name="description" content="Run your own Kestrove server on any Linux machine with Docker. Setup takes about five minutes.">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="/legal.css">
</head>
<body>
<main>
<p class="home"><a href="/">Kestrove home</a></p>
<header><img src="/favicon.svg" alt=""><h1>Host your own Kestrove</h1></header>
<p>Your vault is encrypted on your devices before it is synced, so your server only ever stores data it can't read. Setup takes about five minutes.</p>
<section>
<h2>What you need</h2>
<ul>
<li>A Linux server or VPS with a public IP address. 1 CPU and 512 MB of RAM is plenty. Any distribution with Docker works.</li>
<li>A domain or subdomain you control, for example <code>vault.example.com</code>.</li>
<li>Ports <strong>80</strong> and <strong>443</strong> open to the internet, so your server can get its HTTPS certificate from Let's Encrypt.</li>
</ul>
</section>
<section>
<h2>1. Point your domain at the server</h2>
<p>At your DNS provider, create an <strong>A record</strong> for your chosen name with your server's IPv4 address. Add an <strong>AAAA record</strong> too if the server has IPv6.</p>
<p>Using Cloudflare? Set the record to <strong>DNS only</strong> (grey cloud), not Proxied. Kestrove gets its own certificate and pins it in the apps, and Cloudflare's proxy would break both.</p>
</section>
<section>
<h2>2. Install Docker</h2>
<p>Skip this if <code>docker compose version</code> already works.</p>
<pre><code>curl -fsSL https://get.docker.com | sudo sh</code></pre>
</section>
<section>
<h2>3. Get the files and run setup</h2>
<pre><code>mkdir kestrove &amp;&amp; cd kestrove
curl -fsSLO https://kestrove.samialabri.com/host/compose.yaml
curl -fsSLO https://kestrove.samialabri.com/host/setup.sh
curl -fsSL https://kestrove.samialabri.com/host/env.example -o .env.example
chmod +x setup.sh
sudo ./setup.sh</code></pre>
<p>The setup asks for your domain and an email address (for certificate notices only), creates a private <code>.env</code> file with a random <strong>setup code</strong>, checks your DNS, starts Kestrove and waits for HTTPS. At the end it prints your address and the setup code.</p>
<details>
<summary>Prefer to do it by hand?</summary>
<pre><code>cp .env.example .env
nano .env # DOMAIN, ACME_EMAIL, and a KV_SETUP_CODE of at least 16 characters
chmod 600 .env
docker compose up -d</code></pre>
</details>
</section>
<section>
<h2>4. Create your account</h2>
<ol>
<li>Open <code>https://your-domain</code> in a browser.</li>
<li>Choose <strong>Create account</strong> and enter the setup code from step 3.</li>
<li>Pick a strong master password. Kestrove then gives you a <strong>Secret Key</strong>.</li>
<li><strong>Save the Emergency Kit</strong> it offers, a PDF with your Secret Key and server address. Print it or store it somewhere safe. Without your master password and Secret Key, nobody can open the vault, including you. There is no reset.</li>
</ol>
<p>The setup code stops working as soon as your account exists. That first account is the server's <strong>admin</strong>.</p>
</section>
<section>
<h2>5. Add your devices</h2>
<p>In the web vault go to <strong>Devices → Add a device</strong>. Then in the Kestrove app on your phone or computer choose <strong>Scan setup QR</strong> and scan it. Each new device needs your master password once.</p>
<p>Get the apps from the <a href="/#download">download section</a>.</p>
</section>
<section>
<h2>Inviting other people</h2>
<p>One server can hold accounts for your family or team. Each person has their own vault, master password and Secret Key. As the admin you manage accounts, but you <strong>can't see, export or reset anyone's passwords</strong>.</p>
<ol>
<li>In the web vault open <strong>Admin → Invite someone</strong>. You can enter their email, so that only that address can use the invite, and choose how long it's valid (1, 7 or 30 days).</li>
<li>Send them the invite link or show the QR code. It works once.</li>
<li>They open the link, or choose <strong>Create a new account</strong> in any Kestrove app and enter the code with their email. Then they pick their own master password.</li>
</ol>
<p>On the same page you can switch <strong>Sign-up</strong> to <em>Open</em>, so anyone who can reach the server may create an account, optionally only with certain email domains. You can also disable, delete or promote accounts. Someone who forgets their master password can't be recovered: delete their account and invite them again.</p>
<p>The web vault's code comes from your server, so whoever runs the server could in principle tamper with it. People who would rather not rely on that should use the Kestrove apps, which don't load code from the server.</p>
</section>
<section>
<h2>Updating</h2>
<pre><code>cd kestrove
docker compose pull &amp;&amp; docker compose up -d</code></pre>
<p><code>KESTROVE_VERSION=latest</code> in <code>.env</code> follows new releases. Set a version number instead, for example <code>1.1.0</code>, to update only when you change it. Back up before a big update.</p>
</section>
<section>
<h2>Backups</h2>
<p>Your data lives in the Docker volume <code>kestrove_data</code>. Take a backup any time, even while Kestrove runs:</p>
<pre><code>docker compose run --rm -T backup &gt; kestrove-$(date +%F).db</code></pre>
<p>Your vault inside the file is still end-to-end encrypted, so it's safe to keep on another machine or in cloud storage. For a nightly backup, run <code>mkdir backups</code>, then add this with <code>crontab -e</code>, changing the path to yours:</p>
<pre><code>30 3 * * * cd /home/you/kestrove &amp;&amp; docker compose run --rm -T backup &gt; backups/kestrove-$(date +\%F).db &amp;&amp; find backups -name 'kestrove-*.db' -mtime +30 -delete</code></pre>
<p><strong>To restore</strong> a backup onto a fresh install, after step 3, change the file name below to yours:</p>
<pre><code>docker compose stop server
docker run --rm -v kestrove_data:/data -v "$PWD":/backup alpine \
sh -c 'cp /backup/kestrove-2026-01-31.db /data/kestrove.db &amp;&amp; rm -f /data/kestrove.db-wal /data/kestrove.db-shm &amp;&amp; chown 65532:65532 /data/kestrove.db'
docker compose start server</code></pre>
<p>Anything changed on the server since the backup is gone from the server. Devices with newer data send it again on their next sync.</p>
</section>
<section>
<h2>Troubleshooting</h2>
<p><strong>Setup warns the domain points elsewhere.</strong> Check the A record's IP, and "DNS only" on Cloudflare. DNS changes can take a few minutes.</p>
<p><strong>The browser shows a certificate error.</strong> Run <code>docker compose logs web</code>. Ports 80 and 443 must be open in both your cloud firewall and <code>ufw</code> or <code>firewalld</code>.</p>
<p><strong>Apps say they can't reach the server.</strong> Open <code>https://your-domain</code> on that device. If the server's IP changed, update the DNS record.</p>
<p><strong>You lost the setup code.</strong> It's <code>KV_SETUP_CODE</code> in <code>.env</code>. If that was empty, run <code>docker compose logs server | grep SETUP_TOKEN</code>.</p>
<p><strong>You want to start over.</strong> <code>docker compose down -v</code> deletes everything, then run <code>./setup.sh</code> again.</p>
<p>To see what's happening, use <code>docker compose logs -f</code> for logs and <code>docker compose ps</code> for status.</p>
</section>
<section>
<h2>What runs</h2>
<p><strong>server</strong> is the sync API. It stores encrypted vault data, device keys and a login verifier that is not your password. It runs as a non-root user on a read-only filesystem.</p>
<p><strong>web</strong> is Caddy. It handles HTTPS certificates from Let's Encrypt, the security headers and the web vault, and it passes sync traffic to the server.</p>
<p>Nothing else is contacted: no telemetry, and no outside services besides Let's Encrypt.</p>
</section>
</main>
</body>
</html>
+69
View File
@@ -0,0 +1,69 @@
# Kestrove: your own end-to-end encrypted password manager server.
#
# ./setup.sh asks two questions, writes .env and starts everything
# docker compose up -d (after filling in .env yourself)
#
# Two containers: `server` (the sync API; it only ever stores encrypted data) and `web` (Caddy with the
# web vault: automatic HTTPS from Let's Encrypt, strict security headers, and /v1 proxied to `server`).
# Full guide: https://kestrove.samialabri.com/host.html
name: kestrove
x-server-image: &server-image ${KESTROVE_REGISTRY:-gitea.samialabri.com/kestrove}/kestrove-server:${KESTROVE_VERSION:-latest}
services:
server:
image: *server-image
restart: unless-stopped
read_only: true
tmpfs: [/tmp]
cap_drop: [ALL]
security_opt: ["no-new-privileges:true"]
volumes:
- data:/data
environment:
RUST_LOG: info
KV_PUBLIC_URL: https://${DOMAIN:?Set DOMAIN in .env (run ./setup.sh)}
KV_TRUST_PROXY: "1"
# One-time code for creating your account in the web vault. Empty: a random one is printed in
# `docker compose logs server` on first start.
KV_SETUP_CODE: ${KV_SETUP_CODE:-}
networks: [internal]
web:
image: ${KESTROVE_REGISTRY:-gitea.samialabri.com/kestrove}/kestrove-web:${KESTROVE_VERSION:-latest}
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
environment:
DOMAIN: ${DOMAIN:?Set DOMAIN in .env (run ./setup.sh)}
ACME_EMAIL: ${ACME_EMAIL:?Set ACME_EMAIL in .env (run ./setup.sh)}
volumes:
- caddy-data:/data
- caddy-config:/config
networks: [internal]
depends_on: [server]
# docker compose run --rm -T backup > kestrove-$(date +%F).db
# A consistent, integrity-checked copy of the database, taken while the server runs. Your vault inside it
# is still end-to-end encrypted: restoring it needs your master password and Secret Key.
backup:
image: *server-image
profiles: [tools]
command: ["backup"]
read_only: true
tmpfs: [/tmp]
cap_drop: [ALL]
security_opt: ["no-new-privileges:true"]
network_mode: none
volumes:
- data:/data
volumes:
data:
caddy-data:
caddy-config:
networks:
internal:
+17
View File
@@ -0,0 +1,17 @@
# Copy to .env (./setup.sh does this for you), then: docker compose up -d
# The address of your Kestrove, e.g. vault.example.com. Create a DNS A record (and AAAA for IPv6) pointing at
# this server first. Using Cloudflare? Set the record to "DNS only" (grey cloud).
DOMAIN=
# Let's Encrypt sends certificate expiry warnings here. Nothing else.
ACME_EMAIL=
# One-time code you enter in the web vault to create your account. At least 16 characters; keep it private.
# It stops working as soon as your account exists. Leave empty to have one printed in the server log.
KV_SETUP_CODE=
# Where the images come from, and which version. "latest" follows releases; pin a version (e.g. 1.1.0)
# to update only when you choose.
KESTROVE_REGISTRY=gitea.samialabri.com/kestrove
KESTROVE_VERSION=latest
Executable
+90
View File
@@ -0,0 +1,90 @@
#!/bin/sh
# Kestrove self-host setup: asks for your domain and email, writes .env, checks DNS and starts Kestrove.
# Safe to run again: it keeps an existing .env unless you choose to replace it.
set -eu
cd "$(dirname "$0")"
say() { printf '%s\n' "$*"; }
ask() { # prompt default -> REPLY
if [ -n "$2" ]; then printf '%s [%s]: ' "$1" "$2"; else printf '%s: ' "$1"; fi
read -r REPLY || REPLY=""
[ -n "$REPLY" ] || REPLY=$2
}
die() { say "setup: $*" >&2; exit 1; }
command -v docker >/dev/null 2>&1 || die "Docker is not installed. See https://docs.docker.com/engine/install/"
docker compose version >/dev/null 2>&1 || die "Docker Compose v2 is missing (the 'docker compose' command)."
docker info >/dev/null 2>&1 || die "Docker is not running, or this user can't use it (try sudo)."
if [ -f .env ]; then
ask "A .env already exists. Keep it? (Y/n)" "Y"
case "$REPLY" in [nN]*) ;; *) KEEP=1 ;; esac
fi
if [ -z "${KEEP:-}" ]; then
say ""
say "Kestrove needs a domain name, e.g. vault.example.com, with a DNS record pointing at this server."
ask "Domain" ""
DOMAIN=$(printf '%s' "$REPLY" | tr 'A-Z' 'a-z' | sed -e 's#^https\{0,1\}://##' -e 's#/.*$##')
printf '%s' "$DOMAIN" | grep -Eq '^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$' || die "'$DOMAIN' is not a domain name"
ask "Email for Let's Encrypt certificate notices" ""
ACME_EMAIL=$REPLY
printf '%s' "$ACME_EMAIL" | grep -Eq '^[^@ ]+@[^@ ]+\.[^@ ]+$' || die "'$ACME_EMAIL' is not an email address"
# 24 characters from an unambiguous alphabet (no 0/O, 1/I/L), in groups of four.
CODE=$(LC_ALL=C tr -dc 'ABCDEFGHJKMNPQRSTUVWXYZ23456789' </dev/urandom | head -c 24 | sed 's/..../&-/g; s/-$//')
umask 077
sed -e "s|^DOMAIN=.*|DOMAIN=$DOMAIN|" -e "s|^ACME_EMAIL=.*|ACME_EMAIL=$ACME_EMAIL|" -e "s|^KV_SETUP_CODE=.*|KV_SETUP_CODE=$CODE|" \
.env.example > .env
say "Wrote .env (readable only by you)."
fi
# shellcheck disable=SC1091
. ./.env
[ -n "${DOMAIN:-}" ] || die ".env has no DOMAIN"
# DNS check: the domain should resolve to one of this machine's public addresses (warn only; NAT and
# proxies make this check imperfect).
PUBLIC_IP=$(curl -fsS -4 --max-time 5 https://api.ipify.org 2>/dev/null || true)
RESOLVED=$( (getent ahostsv4 "$DOMAIN" 2>/dev/null || true) | awk '{print $1; exit}')
[ -n "$RESOLVED" ] || RESOLVED=$( (nslookup "$DOMAIN" 2>/dev/null || true) | awk '/^Address: /{print $2}' | grep -v ':' | tail -1)
if [ -z "$RESOLVED" ]; then
say "Warning: $DOMAIN does not resolve yet. Create its DNS A record, wait a minute, then run this again."
ask "Start anyway? (y/N)" "N"
case "$REPLY" in [yY]*) ;; *) exit 1 ;; esac
elif [ -n "$PUBLIC_IP" ] && [ "$RESOLVED" != "$PUBLIC_IP" ]; then
say "Warning: $DOMAIN points to $RESOLVED, but this server's public address is $PUBLIC_IP."
say " If you use Cloudflare, set the record to \"DNS only\" (grey cloud). HTTPS setup fails otherwise."
ask "Start anyway? (y/N)" "N"
case "$REPLY" in [yY]*) ;; *) exit 1 ;; esac
fi
say "Starting Kestrove…"
docker compose pull -q
docker compose up -d
say ""
say "Waiting for the HTTPS certificate (up to a minute)…"
i=0
until curl -fsS -o /dev/null --max-time 5 "https://$DOMAIN/" 2>/dev/null; do
i=$((i + 1))
if [ "$i" -ge 24 ]; then
say "Not reachable over HTTPS yet. Check: docker compose logs web"
say "Ports 80 and 443 must be open to the internet for Let's Encrypt."
break
fi
sleep 5
done
say ""
say "Kestrove is running at https://$DOMAIN"
say ""
say "Next: open https://$DOMAIN, choose Create account, and enter this setup code:"
say ""
if [ -n "${KV_SETUP_CODE:-}" ]; then
say " $KV_SETUP_CODE"
else
say " (printed in: docker compose logs server | grep SETUP_TOKEN)"
fi
say ""
say "Save your Emergency Kit when the web vault offers it. Then add your phone and computers from the"
say "web vault (Devices > Add a device) by scanning the QR code in the Kestrove apps."
+7 -6
View File
@@ -37,7 +37,7 @@
<p class="lead">Your passwords and passkeys are encrypted on your own devices, then synced through a server you run. That server only ever stores data it can't read.</p> <p class="lead">Your passwords and passkeys are encrypted on your own devices, then synced through a server you run. That server only ever stores data it can't read.</p>
<div class="cta"> <div class="cta">
<a class="btn primary" id="hero-cta" href="#download">See all downloads</a> <a class="btn primary" id="hero-cta" href="#download">See all downloads</a>
<a class="btn" id="hero-more" href="#host" >Run your own server</a> <a class="btn" href="https://gitea.samialabri.com/kestrove">View source code</a>
</div> </div>
<p class="note" id="hero-note">For iPhone, iPad, Mac, Android, Windows and Linux.</p> <p class="note" id="hero-note">For iPhone, iPad, Mac, Android, Windows and Linux.</p>
</div> </div>
@@ -75,7 +75,7 @@
<div> <div>
<h2 class="title">Your server, in about five minutes</h2> <h2 class="title">Your server, in about five minutes</h2>
<p>Kestrove's server runs anywhere Docker does. It comes with a web vault and an admin page for inviting people. The admin can manage accounts but can't read anyone's passwords.</p> <p>Kestrove's server runs anywhere Docker does. It comes with a web vault and an admin page for inviting people. The admin can manage accounts but can't read anyone's passwords.</p>
<p><a class="btn" href="https://gitea.samialabri.com/kestrove/kestrove-server/src/branch/main/selfhost/README.md">Read the hosting guide</a></p> <p><a class="btn" href="/host.html">Read the hosting guide</a></p>
</div> </div>
<ol class="steps"> <ol class="steps">
<li><strong>Start your server</strong><span>Point a domain at it and run the setup.</span></li> <li><strong>Start your server</strong><span>Point a domain at it and run the setup.</span></li>
@@ -87,8 +87,9 @@
<section class="band" id="download"> <section class="band" id="download">
<div class="wrap"> <div class="wrap">
<h2 class="title">Get Kestrove</h2> <div class="dl-head"><div><h2 class="title">Get Kestrove</h2>
<p class="sub">Install it on every device you use.</p> <p class="sub">Install it on every device you use.</p></div>
<a class="btn" href="https://gitea.samialabri.com/kestrove">View source code</a></div>
<!-- To release a platform: put the store link in href, and remove the "soon" class and the badge. --> <!-- To release a platform: put the store link in href, and remove the "soon" class and the badge. -->
<div class="grid three cards"> <div class="grid three cards">
<article class="card soon" data-os="ios"><h3>iPhone and iPad</h3><p>App Store</p><span class="badge">Coming soon</span> <article class="card soon" data-os="ios"><h3>iPhone and iPad</h3><p>App Store</p><span class="badge">Coming soon</span>
@@ -97,8 +98,8 @@
<a class="btn" href="https://apps.apple.com/app/kestrove/idREPLACE_ME" data-label="Download for Mac">Get it on the Mac App Store</a></article> <a class="btn" href="https://apps.apple.com/app/kestrove/idREPLACE_ME" data-label="Download for Mac">Get it on the Mac App Store</a></article>
<article class="card soon" data-os="android"><h3>Android</h3><p>Google Play</p><span class="badge">Coming soon</span> <article class="card soon" data-os="android"><h3>Android</h3><p>Google Play</p><span class="badge">Coming soon</span>
<a class="btn" href="https://play.google.com/store/apps/details?id=com.samialabri.kestrove" data-label="Get it on Google Play">Get it on Google Play</a></article> <a class="btn" href="https://play.google.com/store/apps/details?id=com.samialabri.kestrove" data-label="Get it on Google Play">Get it on Google Play</a></article>
<article class="card" data-os="windows"><h3>Windows</h3><p>Windows 10 and 11, 64-bit. Updates itself.</p> <article class="card" data-os="windows"><h3>Windows</h3><p>Windows 10 and 11, 64-bit.</p>
<a class="btn" href="https://gitea.samialabri.com/kestrove/kestrove-desktop/releases/download/v1.2.0/Kestrove_1.2.0_x64-setup.exe" data-label="Download for Windows">Download installer</a></article> <a class="btn" href="/downloads/Kestrove_1.2.0_x64-setup.exe" data-label="Download for Windows">Download installer</a></article>
<article class="card soon" id="linux" data-os="linux"><h3>Linux</h3><p>Snap Store</p><span class="badge">Coming soon</span> <article class="card soon" id="linux" data-os="linux"><h3>Linux</h3><p>Snap Store</p><span class="badge">Coming soon</span>
<pre class="cmd"><code>sudo snap install kestrove</code></pre> <pre class="cmd"><code>sudo snap install kestrove</code></pre>
<p class="fine">Open a terminal, run this command, then start Kestrove from your applications.</p></article> <p class="fine">Open a terminal, run this command, then start Kestrove from your applications.</p></article>
+4
View File
@@ -16,3 +16,7 @@ a { color: var(--primary); }
section { background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 4px 20px 16px; margin-top: 16px; } section { background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 4px 20px 16px; margin-top: 16px; }
code { font-family: ui-monospace, "JetBrains Mono", monospace; font-size: 0.92em; } code { font-family: ui-monospace, "JetBrains Mono", monospace; font-size: 0.92em; }
.home { margin: 0 0 16px; font-size: 15px; } .home { margin: 0 0 16px; font-size: 15px; }
pre { background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; overflow-x: auto; font-size: 14px; line-height: 1.5; }
pre code { font-size: inherit; }
details { margin: 8px 0; }
summary { cursor: pointer; font-weight: 600; }
+1
View File
@@ -106,3 +106,4 @@ footer { padding: 32px 0; }
html { scroll-behavior: auto; } html { scroll-behavior: auto; }
.btn { transition: none; } .btn { transition: none; }
} }
.dl-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
+1
View File
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://kestrove.samialabri.com/</loc></url> <url><loc>https://kestrove.samialabri.com/</loc></url>
<url><loc>https://kestrove.samialabri.com/host.html</loc></url>
<url><loc>https://kestrove.samialabri.com/support.html</loc></url> <url><loc>https://kestrove.samialabri.com/support.html</loc></url>
<url><loc>https://kestrove.samialabri.com/privacy.html</loc></url> <url><loc>https://kestrove.samialabri.com/privacy.html</loc></url>
</urlset> </urlset>
+2 -2
View File
@@ -16,7 +16,7 @@
<section> <section>
<h2>Getting started</h2> <h2>Getting started</h2>
<ol> <ol>
<li>Set up a Kestrove server: <a href="https://gitea.samialabri.com/kestrove/kestrove-server/src/branch/main/selfhost/README.md">self-hosting guide</a> (about five minutes with Docker).</li> <li>Set up a Kestrove server: <a href="/host.html">hosting guide</a> (about five minutes with Docker).</li>
<li>Open your server's web address, create your account with the setup code, and save your Emergency Kit.</li> <li>Open your server's web address, create your account with the setup code, and save your Emergency Kit.</li>
<li>In the web vault choose <strong>Devices → Add a device</strong>, then scan the QR code with the Kestrove app on your phone or computer.</li> <li>In the web vault choose <strong>Devices → Add a device</strong>, then scan the QR code with the Kestrove app on your phone or computer.</li>
</ol> </ol>
@@ -32,7 +32,7 @@
<section> <section>
<h2>Report a problem</h2> <h2>Report a problem</h2>
<p>Open an issue on <a href="https://gitea.samialabri.com/kestrove/kestrove-spec/issues">the Kestrove issue tracker</a>. Never include passwords, your Secret Key or your Emergency Kit.</p> <p>Email <a href="mailto:REPLACE_WITH_SUPPORT_EMAIL">REPLACE_WITH_SUPPORT_EMAIL</a>. Never include passwords, your Secret Key or your Emergency Kit.</p>
<p class="muted"><a href="/privacy.html">Privacy Policy</a></p> <p class="muted"><a href="/privacy.html">Privacy Policy</a></p>
</section> </section>
</main> </main>