commit cac68780eed755fcbf3081068b192ac9afaf988d Author: samialabri Date: Fri Oct 30 18:51:19 2026 +0400 Kestrove website: home page with downloads, support and privacy pages Co-Authored-By: Claude Opus 5.5 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..391c1d3 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# Kestrove website + +The public website: the home page with downloads, and the support and privacy pages the store listings link to. +It's plain static files with nothing to build, and it loads nothing from other sites. + +- 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. +- For a new Windows version, change the version in the Windows card's link. + +To host it, serve this folder with any web server. `deploy/Caddyfile.snippet` is a Caddy example with the security headers. + +To preview it locally, run `python3 -m http.server 8090` here. diff --git a/assets/fonts/Inter-Latin.woff2 b/assets/fonts/Inter-Latin.woff2 new file mode 100644 index 0000000..d15208d Binary files /dev/null and b/assets/fonts/Inter-Latin.woff2 differ diff --git a/assets/fonts/JetBrainsMono-Latin.woff2 b/assets/fonts/JetBrainsMono-Latin.woff2 new file mode 100644 index 0000000..cd5102a Binary files /dev/null and b/assets/fonts/JetBrainsMono-Latin.woff2 differ diff --git a/assets/iphone.png b/assets/iphone.png new file mode 100644 index 0000000..5acac4b Binary files /dev/null and b/assets/iphone.png differ diff --git a/assets/mac.png b/assets/mac.png new file mode 100644 index 0000000..f17ec2e Binary files /dev/null and b/assets/mac.png differ diff --git a/assets/mark.svg b/assets/mark.svg new file mode 100644 index 0000000..02510fc --- /dev/null +++ b/assets/mark.svg @@ -0,0 +1 @@ + diff --git a/deploy/Caddyfile.snippet b/deploy/Caddyfile.snippet new file mode 100644 index 0000000..88f6554 --- /dev/null +++ b/deploy/Caddyfile.snippet @@ -0,0 +1,16 @@ +# The Kestrove website: static files only. Replace DOMAIN and the root path. +DOMAIN { + encode zstd gzip + root * /srv/kestrove-website + header { + Strict-Transport-Security "max-age=63072000" + X-Content-Type-Options nosniff + Referrer-Policy strict-origin-when-cross-origin + Content-Security-Policy "default-src 'self'; img-src 'self'; style-src 'self'; script-src 'self'; font-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'" + X-Frame-Options DENY + -Server + } + @assets path /assets/* + header @assets Cache-Control "public, max-age=604800" + file_server +} diff --git a/favicon.svg b/favicon.svg new file mode 100644 index 0000000..4a6a8d4 --- /dev/null +++ b/favicon.svg @@ -0,0 +1 @@ + diff --git a/index.html b/index.html new file mode 100644 index 0000000..67e0737 --- /dev/null +++ b/index.html @@ -0,0 +1,122 @@ + + + + + +Kestrove: the password manager you host yourself + + + + + + + + + + + + + + + +
+
+
+
+

The password manager you host yourself.

+

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.

+ +

For iPhone, iPad, Mac, Android, Windows and Linux.

+
+
+ Kestrove on a Mac: a list of logins beside a saved login with its username, hidden password and one-time code. + Kestrove on an iPhone: the vault with logins, a bank, a passport and a card. +
+
+
+ +
+
+

Encrypted before it leaves

Your master password and a Secret Key protect your vault on the device itself. Only encrypted data ever reaches your server.

+

On a server you choose

Run it for yourself, your family or your company, on your own domain. Or skip the server and keep your vault on one device.

+

No tracking

No ads, no analytics and no account with us. Read the privacy policy.

+
+
+ +
+
+

Everything you expect, on everything you use

+
    +
  • Autofill everywhere

    Fill logins in apps and browsers, and save new ones as you create them.

  • +
  • Passkeys

    Store passkeys and use them to sign in without a password.

  • +
  • One-time codes

    Keep two-factor codes next to the login they belong to, ready to fill.

  • +
  • More than passwords

    Secure notes, cards and identities, organised with tags and favourites.

  • +
  • Easy to move in

    Import from 1Password or a CSV file. Generate strong passwords as you go.

  • +
  • Add devices with a QR code

    Scan a code from your vault to set up a new phone or computer.

  • +
+
+
+ +
+
+
+

Your server, in about five minutes

+

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.

+

Read the hosting guide

+
+
    +
  1. Start your serverPoint a domain at it and run the setup.
  2. +
  3. Create your accountSave your Emergency Kit somewhere safe.
  4. +
  5. Add your devicesScan the QR code with each Kestrove app.
  6. +
+
+
+ +
+
+

Get Kestrove

+

Install it on every device you use.

+ +
+ + + + +

Linux

Snap Store

Coming soon +
sudo snap install kestrove
+

Open a terminal, run this command, then start Kestrove from your applications.

+

Browser extension

Fills from the Kestrove app on your computer.

+
+ Chrome + Firefox +
+
+
+
+
+ + + + diff --git a/legal.css b/legal.css new file mode 100644 index 0000000..4ad9db1 --- /dev/null +++ b/legal.css @@ -0,0 +1,18 @@ +/* Privacy policy and support pages (static; the web vault's CSP allows only same-origin styles). */ +:root { --bg: #f7f8f7; --surface: #ffffff; --text: #16211d; --text2: #4a5a54; --primary: #19604f; --border: #dde3e0; } +@media (prefers-color-scheme: dark) { + :root { --bg: #0f1513; --surface: #161e1b; --text: #e8eeeb; --text2: #a7b5af; --primary: #5cc1a5; --border: #2a3531; } +} +* { box-sizing: border-box; } +body { margin: 0; background: var(--bg); color: var(--text); font: 16px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif; } +main { max-width: 720px; margin: 0 auto; padding: 40px 16px 64px; } +header { display: flex; align-items: center; gap: 12px; margin-bottom: 24px; } +header img { width: 40px; height: 40px; } +h1 { font-size: 28px; line-height: 1.2; margin: 0; letter-spacing: -0.02em; } +h2 { font-size: 19px; margin: 32px 0 8px; } +p, li { color: var(--text); } +.muted { color: var(--text2); font-size: 14px; } +a { color: var(--primary); } +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; } +.home { margin: 0 0 16px; font-size: 15px; } diff --git a/privacy.html b/privacy.html new file mode 100644 index 0000000..e8fd470 --- /dev/null +++ b/privacy.html @@ -0,0 +1,64 @@ + + + + + +Kestrove Privacy Policy + + + + +
+

Kestrove home

+

Privacy Policy

+

Kestrove apps for iPhone, iPad, Mac, Android, Windows and Linux, the Kestrove browser extension, and the Kestrove server. Effective 30 October 2026.

+ +
+

The short version

+

Kestrove does not collect any data about you. There are no accounts with us, no analytics, no advertising and no tracking. Your passwords are encrypted on your own devices and are only ever synced to a server you choose and run.

+
+ +
+

Your vault

+

Everything you store in Kestrove (logins, passwords, passkeys, notes, cards and other items) is encrypted on your device with keys derived from your master password and your Secret Key before it is saved or synced. Neither the developer of Kestrove nor the operator of the server can read it.

+

When you turn on sync, your encrypted vault is sent to the Kestrove server at the address you enter in the app. That server is run by you (or by someone you choose); it is not operated by the developer. It stores encrypted vault data, your devices' public keys and names, and a login verifier that is not your password (OPAQUE). Its operator is responsible for it.

+
+ +
+

What the apps use on your device

+
    +
  • Face ID, Touch ID or fingerprint: to unlock your vault. Biometric data never leaves your device's secure hardware, and Kestrove never receives it.
  • +
  • Camera: only to scan Kestrove setup and sign-in QR codes. Nothing is recorded or stored.
  • +
  • AutoFill, Credential Manager and the browser extension: to offer and fill your saved logins and passkeys in apps and websites. The website or app address is used on your device to find matching items and is not sent anywhere.
  • +
  • Network: only to talk to your own Kestrove server. The one exception is the Windows app, which checks for updates once a day. That check reveals your computer's IP address and the app version to the server that hosts the updates, and nothing from your vault. You can turn it off in Settings.
  • +
+
+ +
+

This website

+

This website has no analytics, cookies or trackers. Like any web server, it may keep short-lived technical logs, including IP addresses, to keep the site running.

+
+ +
+

Sharing and selling

+

No data is shared with or sold to anyone, because no data is collected.

+
+ +
+

Deleting your data

+

Deleting the app removes the data on that device. Your server's operator can remove a device from your account in the web vault (Devices) or delete the server's data entirely.

+
+ +
+

Children

+

Kestrove is not directed at children and collects no personal information from anyone.

+
+ +
+

Changes and contact

+

If this policy changes, the new version is published at this address with a new effective date.

+

Questions: Kestrove support.

+
+
+ + diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..92cdd33 --- /dev/null +++ b/robots.txt @@ -0,0 +1,3 @@ +User-agent: * +Allow: / +Sitemap: https://kestrove.samialabri.com/sitemap.xml diff --git a/site.css b/site.css new file mode 100644 index 0000000..5871c5e --- /dev/null +++ b/site.css @@ -0,0 +1,108 @@ +/* Kestrove website. Colours and type follow kestrove-spec/design/tokens.json. */ +@font-face { font-family: Inter; src: url(/assets/fonts/Inter-Latin.woff2) format("woff2"); font-weight: 100 900; font-display: swap; } +@font-face { font-family: "JetBrains Mono"; src: url(/assets/fonts/JetBrainsMono-Latin.woff2) format("woff2"); font-weight: 100 800; font-display: swap; } +:root { + --bg: #f4f6f4; --surface: #fff; --surface-2: #eef2ef; --border: #d5ddd8; --text: #13201b; --text-2: #4a5852; + --primary: #19604f; --primary-hover: #134d3f; --primary-soft: #dcefe8; --on-primary: #fff; --focus: #2f7dd1; + color-scheme: light dark; +} +@media (prefers-color-scheme: dark) { + :root { + --bg: #0b1210; --surface: #121b18; --surface-2: #18231f; --border: #26352f; --text: #e4ebe7; --text-2: #a9b7b0; + --primary: #5ec6a3; --primary-hover: #7fd5b8; --primary-soft: #173a30; --on-primary: #05201a; --focus: #7cb8ff; + } +} +* { box-sizing: border-box; } +html { scroll-behavior: smooth; scroll-padding-top: 80px; -webkit-text-size-adjust: 100%; } +body { margin: 0; background: var(--bg); color: var(--text); font: 17px/1.6 Inter, -apple-system, "Segoe UI", Roboto, sans-serif; font-feature-settings: "cv11", "ss01"; } +img { max-width: 100%; height: auto; display: block; } +a { color: var(--primary); } +a:hover { color: var(--primary-hover); } +:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 4px; } +h1, h2, h3 { line-height: 1.2; letter-spacing: -0.02em; margin: 0; text-wrap: balance; } +h3 { font-size: 18px; } +p { margin: 0; } +ul, ol { list-style: none; margin: 0; padding: 0; } +.wrap { max-width: 1120px; margin: 0 auto; padding: 0 16px; } +.skip { position: absolute; left: -999px; top: 8px; background: var(--surface); padding: 8px 12px; z-index: 3; } +.skip:focus { left: 8px; } +section { padding: 72px 0; } +.band { background: var(--surface); border-block: 1px solid var(--border); } +.title { font-size: clamp(26px, 4vw, 34px); } +.sub { color: var(--text-2); margin-top: 8px; } +.grid { display: grid; gap: 16px; } +.three { grid-template-columns: repeat(3, minmax(0, 1fr)); } + +.nav { position: sticky; top: 0; z-index: 2; background: color-mix(in srgb, var(--bg) 85%, transparent); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); border-bottom: 1px solid var(--border); } +.nav-in { display: flex; align-items: center; justify-content: space-between; height: 64px; gap: 16px; } +.nav nav { display: flex; align-items: center; gap: 24px; font-size: 15px; font-weight: 500; } +.nav nav a { color: var(--text-2); text-decoration: none; } +.nav nav a:hover { color: var(--text); } +.nav nav a.nav-get { color: var(--primary); font-weight: 600; } +.lockup { display: inline-flex; align-items: center; gap: 10px; color: var(--text); text-decoration: none; font-weight: 700; font-size: 20px; letter-spacing: -0.02em; } + +.hero { padding: 72px 0 80px; overflow: hidden; } +.hero-in { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: 48px; align-items: center; } +.hero h1 { font-size: clamp(38px, 6vw, 58px); letter-spacing: -0.03em; } +.lead { font-size: 19px; color: var(--text-2); margin-top: 20px; max-width: 34em; } +.cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; } +.note { color: var(--text-2); font-size: 15px; margin-top: 14px; } +.btn { display: inline-flex; align-items: center; justify-content: center; min-height: 48px; padding: 10px 20px; border-radius: 10px; border: 1px solid var(--border); background: var(--surface); color: var(--text); font-weight: 600; font-size: 16px; text-decoration: none; transition: background .15s cubic-bezier(.2,.7,.2,1); } +.btn:hover { background: var(--surface-2); color: var(--text); } +.btn.primary { background: var(--primary); border-color: var(--primary); color: var(--on-primary); } +.btn.primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); color: var(--on-primary); } +.shots { position: relative; padding-bottom: 32px; } +.shot-mac { width: 100%; border-radius: 12px; border: 1px solid var(--border); box-shadow: 0 16px 48px rgb(0 0 0 / .14); } +.shot-phone { position: absolute; right: -8px; bottom: 0; width: 25%; border-radius: 18px; border: 1px solid var(--border); box-shadow: 0 12px 32px rgb(0 0 0 / .2); } + +.pillars { padding: 0 0 72px; } +.pillars h2 { font-size: 20px; margin-bottom: 8px; } +.pillars p { color: var(--text-2); } +.features { margin-top: 32px; column-gap: 40px; row-gap: 32px; } +.features h3 { margin-bottom: 6px; } +.features p { color: var(--text-2); } + +.split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 56px; align-items: center; } +.split p { color: var(--text-2); margin-top: 16px; } +.split p .btn { margin-top: 8px; } +.steps { counter-reset: step; display: grid; gap: 12px; } +.steps li { counter-increment: step; position: relative; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 16px 18px 16px 66px; } +.steps li::before { content: counter(step); position: absolute; left: 18px; top: 18px; width: 32px; height: 32px; border-radius: 50%; background: var(--primary-soft); color: var(--primary); font-weight: 700; display: grid; place-items: center; } +.steps strong, .steps span { display: block; } +.steps span { color: var(--text-2); font-size: 15px; } + +.cards { margin-top: 32px; } +.card { position: relative; display: flex; flex-direction: column; gap: 8px; background: var(--bg); border: 1px solid var(--border); border-radius: 16px; padding: 20px; min-width: 0; } +.card > p { color: var(--text-2); font-size: 15px; } +.card > .btn, .card .row { margin-top: auto; } +.card > h3 + p { margin-bottom: 8px; } +.card .row { display: flex; gap: 8px; } +.card .row .btn { flex: 1; } +.card.yours { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); } +.card.soon > .btn { display: none; } +.badge { align-self: flex-start; margin-top: auto; padding: 3px 10px; border-radius: 999px; background: var(--surface-2); color: var(--text-2); font-size: 13px; font-weight: 600; } +.cmd { margin: 4px 0 0; padding: 10px 12px; background: var(--surface-2); border-radius: 8px; overflow-x: auto; font: 14px "JetBrains Mono", ui-monospace, monospace; } +.card.soon .cmd, .card.soon .fine { order: 1; } +.fine { font-size: 14px; color: var(--text-2); } + +footer { padding: 32px 0; } +.foot { display: flex; align-items: center; justify-content: space-between; gap: 16px; } +.foot .lockup { font-size: 17px; } +.foot nav { display: flex; gap: 20px; font-size: 15px; } + +@media (max-width: 860px) { + section { padding: 56px 0; } + .hero { padding: 40px 0 56px; } + .hero-in, .split { grid-template-columns: minmax(0, 1fr); gap: 36px; } + .three { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .shot-phone { right: 0; } +} +@media (max-width: 560px) { + .three { grid-template-columns: minmax(0, 1fr); } + .nav nav a:not(.nav-get) { display: none; } + .cta .btn { flex: 1 1 100%; } +} +@media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto; } + .btn { transition: none; } +} diff --git a/site.js b/site.js new file mode 100644 index 0000000..d312503 --- /dev/null +++ b/site.js @@ -0,0 +1,28 @@ +// Points the main button at this visitor's platform. The download cards in index.html are the only list of +// links: release a platform there and this picks it up. Without JavaScript the page still works. +const NAMES = { ios: "iPhone", mac: "Mac", android: "Android", windows: "Windows", linux: "Linux" }; + +function detect() { + const ua = navigator.userAgent; + if (/iPhone|iPad|iPod/.test(ua) || (/Macintosh/.test(ua) && navigator.maxTouchPoints > 1)) return "ios"; + if (/Android/.test(ua)) return "android"; + if (/Macintosh/.test(ua)) return "mac"; + if (/Windows/.test(ua)) return "windows"; + if (/Linux|X11/.test(ua)) return "linux"; + return null; +} + +const os = detect(); +const card = os && document.querySelector(`.card[data-os="${os}"]`); +if (card) { + card.classList.add("yours"); + const cta = document.getElementById("hero-cta"); + const note = document.getElementById("hero-note"); + const link = card.querySelector("a.btn"); + if (card.classList.contains("soon")) { + note.textContent = `Kestrove for ${NAMES[os]} is coming soon.`; + } else if (link) { + cta.href = link.href; + cta.textContent = link.dataset.label; + } +} diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..6fc95bb --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,6 @@ + + +https://kestrove.samialabri.com/ +https://kestrove.samialabri.com/support.html +https://kestrove.samialabri.com/privacy.html + diff --git a/support.html b/support.html new file mode 100644 index 0000000..1031338 --- /dev/null +++ b/support.html @@ -0,0 +1,40 @@ + + + + + +Kestrove Support + + + + +
+

Kestrove home

+

Kestrove Support

+

Kestrove is a password manager you host yourself: your vault is end-to-end encrypted and syncs only through your own Kestrove server.

+ +
+

Getting started

+
    +
  1. Set up a Kestrove server: self-hosting guide (about five minutes with Docker).
  2. +
  3. Open your server's web address, create your account with the setup code, and save your Emergency Kit.
  4. +
  5. In the web vault choose Devices → Add a device, then scan the QR code with the Kestrove app on your phone or computer.
  6. +
+
+ +
+

Common questions

+

I forgot my master password. Nobody can recover it, including the developer and your server: that is what end-to-end encryption means. If another device is still unlocked, export your data from it.

+

The app says it can't reach the server. Open your server's address in a browser on the same device. If the server moved to a new IP address, update your DNS record.

+

The browser extension says the app isn't available. On a Mac, open the Kestrove app; on Windows and Linux, the Kestrove desktop app. Then click Connect in the extension and confirm the six words match.

+

Autofill doesn't offer Kestrove. iPhone/iPad: Settings → General → AutoFill & Passwords → turn on Kestrove. Android: Settings → Passwords & accounts → Preferred service → Kestrove.

+
+ +
+

Report a problem

+

Open an issue on the Kestrove issue tracker. Never include passwords, your Secret Key or your Emergency Kit.

+

Privacy Policy

+
+
+ +