Files
keysat-root/keysat-design-system/uploads/index.html
T
Keysat 843ff0e5d7 Initial backup of root workspace files
Glue files not covered by subproject repos: top-level docs, logo,
keysat-design-system, and crosscheck tests. Subproject folders are
gitignored (each has its own Gitea remote).
2026-06-12 17:51:40 -05:00

507 lines
18 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Keysat — Bitcoin-paid software licensing, self-hosted on Start9</title>
<meta name="description" content="Keysat is a self-hosted, Bitcoin-paid software licensing server. Sell licenses to your own software using BTCPay Server on your own Start9. You own the keys, the customer list, and the payment rails.">
<meta property="og:title" content="Keysat — Bitcoin-paid software licensing">
<meta property="og:description" content="Self-hosted, Bitcoin-paid software licensing. Run on your own Start9. You own the signing key, the customer records, and the payment rails.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://keysat.xyz">
<meta property="og:image" content="https://keysat.xyz/assets/keysat-thumbnail.png">
<link rel="icon" type="image/png" href="/assets/icon.png">
<style>
:root {
--accent: #f59e0b; /* amber, matches Bitcoin orange family */
--accent-strong: #d97706;
--bg: #0d0f14;
--bg-card: #14171f;
--bg-elev: #1c2029;
--fg: #e8eaf0;
--fg-strong: #ffffff;
--muted: #9aa0ab;
--border: #2a2f3a;
--code-bg: #1c2029;
--max-w: 64rem;
}
@media (prefers-color-scheme: light) {
:root {
--bg: #fafaf7;
--bg-card: #ffffff;
--bg-elev: #f4f3ee;
--fg: #1a1d23;
--fg-strong: #000000;
--muted: #58606e;
--border: #e3e1da;
--code-bg: #f0eee6;
}
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", Roboto, sans-serif;
line-height: 1.6;
background: var(--bg);
color: var(--fg);
-webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-strong); text-decoration: underline; }
/* ---------- header ---------- */
header.site {
position: sticky; top: 0; z-index: 10;
background: rgba(13,15,20,0.85);
backdrop-filter: saturate(180%) blur(8px);
border-bottom: 1px solid var(--border);
}
@media (prefers-color-scheme: light) {
header.site { background: rgba(250,250,247,0.85); }
}
header.site .inner {
max-width: var(--max-w);
margin: 0 auto;
padding: 0.75rem 1.5rem;
display: flex; align-items: center; gap: 1rem;
}
header.site .brand {
display: flex; align-items: center; gap: 0.6rem;
font-weight: 700; color: var(--fg-strong); text-decoration: none;
}
header.site .brand img { width: 32px; height: 32px; }
header.site nav { margin-left: auto; display: flex; gap: 1.25rem; font-size: 0.92rem; }
header.site nav a { color: var(--muted); }
header.site nav a:hover { color: var(--fg-strong); text-decoration: none; }
/* ---------- hero ---------- */
section.hero { padding: 6rem 1.5rem 4rem; text-align: center; }
section.hero .inner { max-width: var(--max-w); margin: 0 auto; }
section.hero img.logo {
width: 96px; height: 96px;
margin-bottom: 1.5rem;
filter: drop-shadow(0 6px 24px rgba(245,158,11,0.25));
}
section.hero h1 {
font-size: clamp(2rem, 5vw, 3.25rem);
font-weight: 800;
line-height: 1.15;
margin: 0 0 1rem;
color: var(--fg-strong);
letter-spacing: -0.015em;
}
section.hero h1 .accent { color: var(--accent); }
section.hero p.lede {
font-size: clamp(1.05rem, 2.2vw, 1.35rem);
color: var(--muted);
max-width: 38rem;
margin: 0 auto 2rem;
}
.cta-row { display: flex; gap: 0.75rem; justify-content: center; flex-wrap: wrap; }
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
font-weight: 600;
text-decoration: none;
border: 1px solid transparent;
transition: all 0.15s;
font-size: 0.95rem;
}
.btn:hover { text-decoration: none; }
.btn.primary { background: var(--accent); color: #000; border-color: var(--accent); }
.btn.primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
.btn.ghost { background: transparent; color: var(--fg-strong); border-color: var(--border); }
.btn.ghost:hover { background: var(--bg-elev); border-color: var(--muted); color: var(--fg-strong); }
/* ---------- generic section ---------- */
section.block { padding: 4rem 1.5rem; }
section.block .inner { max-width: var(--max-w); margin: 0 auto; }
section.block h2 {
font-size: clamp(1.5rem, 3vw, 2.1rem);
font-weight: 700;
margin: 0 0 0.5rem;
color: var(--fg-strong);
letter-spacing: -0.01em;
}
section.block .subtitle {
color: var(--muted);
max-width: 36rem;
margin: 0 0 2.5rem;
font-size: 1.05rem;
}
section.alt { background: var(--bg-card); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
/* ---------- value-prop grid ---------- */
.value-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
gap: 1.25rem;
}
.value-grid .item {
background: var(--bg-elev);
border: 1px solid var(--border);
border-radius: 0.625rem;
padding: 1.25rem 1.4rem;
}
.value-grid .item h3 {
margin: 0 0 0.4rem;
font-size: 1rem;
color: var(--fg-strong);
}
.value-grid .item .icon {
font-size: 1.4rem;
margin-bottom: 0.5rem;
display: block;
}
.value-grid .item p {
margin: 0; color: var(--muted); font-size: 0.92rem; line-height: 1.55;
}
/* ---------- how it works ---------- */
ol.flow {
list-style: none;
padding: 0;
margin: 0;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
gap: 1.25rem;
}
ol.flow li {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 0.625rem;
padding: 1.25rem;
position: relative;
counter-increment: step;
}
ol.flow li::before {
content: counter(step);
position: absolute;
top: -0.65rem; left: 1.25rem;
background: var(--accent);
color: #000;
width: 1.7rem; height: 1.7rem;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-weight: 700; font-size: 0.95rem;
}
ol.flow { counter-reset: step; }
ol.flow li h3 {
margin: 0.5rem 0 0.4rem;
color: var(--fg-strong);
font-size: 1rem;
}
ol.flow li p {
margin: 0; color: var(--muted); font-size: 0.92rem; line-height: 1.55;
}
/* ---------- code block ---------- */
pre.code {
background: var(--code-bg);
border: 1px solid var(--border);
border-radius: 0.5rem;
padding: 1rem 1.25rem;
overflow-x: auto;
font-size: 0.875rem;
line-height: 1.55;
margin: 0.75rem 0 1.25rem;
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}
code.inline {
background: var(--code-bg);
border: 1px solid var(--border);
padding: 0.1em 0.4em;
border-radius: 0.25rem;
font-family: ui-monospace, SFMono-Regular, monospace;
font-size: 0.92em;
}
/* ---------- two-column ---------- */
.two-col {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
align-items: start;
}
@media (max-width: 800px) {
.two-col { grid-template-columns: 1fr; gap: 1.25rem; }
}
.two-col h3 { color: var(--fg-strong); margin-top: 0; }
/* ---------- footer ---------- */
footer.site {
background: var(--bg-card);
border-top: 1px solid var(--border);
padding: 2.5rem 1.5rem;
}
footer.site .inner {
max-width: var(--max-w); margin: 0 auto;
display: flex; gap: 2rem; flex-wrap: wrap; justify-content: space-between;
color: var(--muted); font-size: 0.9rem;
}
footer.site .brand {
display: flex; align-items: center; gap: 0.5rem;
font-weight: 600; color: var(--fg-strong);
}
footer.site .brand img { width: 24px; height: 24px; }
footer.site .links {
display: flex; gap: 1.5rem; flex-wrap: wrap;
}
footer.site .links a { color: var(--muted); }
footer.site .links a:hover { color: var(--fg-strong); }
/* ---------- pill list ---------- */
ul.pillets { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 0.5rem; }
ul.pillets li {
border: 1px solid var(--border);
background: var(--bg-elev);
color: var(--fg);
padding: 0.35rem 0.75rem;
border-radius: 999px;
font-size: 0.82rem;
}
</style>
</head>
<body>
<header class="site">
<div class="inner">
<a class="brand" href="#top"><img src="/assets/icon.png" alt=""><span>Keysat</span></a>
<nav>
<a href="#why">Why</a>
<a href="#how">How it works</a>
<a href="#integrate">For developers</a>
<a href="#install">Install</a>
</nav>
</div>
</header>
<section class="hero" id="top">
<div class="inner">
<img src="/assets/icon.png" alt="" class="logo">
<h1>Bitcoin-paid software licensing,<br><span class="accent">self-hosted</span> on Start9.</h1>
<p class="lede">
Keysat is the licensing server you run on your own Start9. Buyers pay in Bitcoin via your own BTCPay. Your software verifies signed keys offline. You own the signing key, the customer list, and the payment rails — no SaaS, no middleman, no platform risk.
</p>
<div class="cta-row">
<a class="btn primary" href="#install">Install Keysat</a>
<a class="btn ghost" href="#how">How it works</a>
</div>
</div>
</section>
<section class="block alt" id="why">
<div class="inner">
<h2>What this enables</h2>
<p class="subtitle">A complete sell-your-software stack, sovereign end-to-end.</p>
<div class="value-grid">
<div class="item">
<span class="icon"></span>
<h3>Bitcoin payments, your store</h3>
<p>BTCPay Server on your own Start9 takes the payment. Lightning settles in seconds. Funds go straight to your wallet — no intermediary holds them.</p>
</div>
<div class="item">
<span class="icon">🔐</span>
<h3>You own the signing key</h3>
<p>The Ed25519 keypair lives on your hardware. Every license you issue is signed by it. There's no third party who could mint or revoke licenses.</p>
</div>
<div class="item">
<span class="icon">📡</span>
<h3>Offline verification</h3>
<p>Your software verifies licenses against an embedded public key. No network call. Your customers' apps work even if your Keysat goes offline.</p>
</div>
<div class="item">
<span class="icon">🎫</span>
<h3>Trials, expiries, seats, entitlements</h3>
<p>Per-product policies for time-limited licenses, multi-seat caps, trial flags, feature entitlements baked into the key.</p>
</div>
<div class="item">
<span class="icon">🏷️</span>
<h3>Discount &amp; referral codes</h3>
<p>Percent-off, fixed-sats-off, or free-license codes (no payment). Run launch promos, comp keys for press, track partner campaigns.</p>
</div>
<div class="item">
<span class="icon">🛠️</span>
<h3>SDKs in your language</h3>
<p>Rust, TypeScript, Python — wire-compatible offline verifiers. Five lines of code in your app and you're verifying real signatures.</p>
</div>
</div>
</div>
</section>
<section class="block" id="how">
<div class="inner">
<h2>How it works</h2>
<p class="subtitle">Five steps, end to end.</p>
<ol class="flow">
<li>
<h3>Install Keysat on your Start9</h3>
<p>Sideload the <code class="inline">.s9pk</code>, or install from <a href="https://registry.keysat.xyz">registry.keysat.xyz</a>. Keysat declares BTCPay Server as a dependency, so you'll have BTCPay running too.</p>
</li>
<li>
<h3>Connect BTCPay in one click</h3>
<p>Click "Connect BTCPay" in the StartOS Actions tab. You authorize once on your BTCPay's consent page; Keysat auto-detects your store and registers a webhook. No API keys to copy.</p>
</li>
<li>
<h3>Define your products + policies</h3>
<p>In the Keysat web UI: declare your product, set its price in sats, define a policy (duration, seat cap, trial flag, entitlements). The policy slugged <code class="inline">default</code> drives your public purchase flow.</p>
</li>
<li>
<h3>Embed your public key in your software</h3>
<p>Copy your Keysat public key into your app's source. Add the SDK (<code class="inline">pip install</code>, <code class="inline">cargo add</code>, <code class="inline">npm install</code>). Five lines of integration code verifies a license at startup.</p>
</li>
<li>
<h3>Share your purchase URL</h3>
<p>Your buyers hit your public Keysat URL, pay in Bitcoin, get a signed license key delivered. Their copy of your software boots up licensed. You see the sale in your audit log.</p>
</li>
</ol>
</div>
</section>
<section class="block alt" id="integrate">
<div class="inner">
<h2>Wiring it into your app</h2>
<p class="subtitle">A working offline check is five lines.</p>
<div class="two-col">
<div>
<h3>Python</h3>
<pre class="code">pip install keysat-licensing-client
from keysat_licensing_client import Verifier, PublicKey
verifier = Verifier(PublicKey.from_pem(ISSUER_PEM))
ok = verifier.verify(license_key_from_user)
print("licensed for", ok.product_id)</pre>
</div>
<div>
<h3>Rust</h3>
<pre class="code">[dependencies]
licensing-client = "0.1"
use licensing_client::{Verifier, PublicKeyPem};
let pk = PublicKeyPem::from_str(ISSUER_PEM)?;
let verifier = Verifier::new(pk);
let ok = verifier.verify(&license_key)?;
println!("licensed: {}", ok.product_id);</pre>
</div>
<div>
<h3>TypeScript / JavaScript</h3>
<pre class="code">npm install @keysat/licensing-client
import { Verifier, PublicKey } from '@keysat/licensing-client'
const verifier = new Verifier(PublicKey.fromPem(ISSUER_PEM))
const ok = verifier.verify(licenseKeyFromUser)
console.log('licensed:', ok.productId)</pre>
</div>
<div>
<h3>Other languages</h3>
<p>Any language with Ed25519 + base32 (Go, Java, Swift, C#, C++, …) can verify Keysat keys. The wire format is fully documented; thin SDKs can be ported in a few hours. Go and Java/Swift SDKs are on the roadmap.</p>
<ul class="pillets">
<li>Go (planned)</li>
<li>Java/Kotlin (planned)</li>
<li>Swift (planned)</li>
<li>C#/.NET (planned)</li>
<li>C++ (planned)</li>
</ul>
</div>
</div>
</div>
</section>
<section class="block" id="install">
<div class="inner">
<h2>Install</h2>
<p class="subtitle">From the marketplace, or sideload directly.</p>
<div class="two-col">
<div>
<h3>From the marketplace</h3>
<p>Add the Keysat marketplace to your Start9:</p>
<pre class="code">https://registry.keysat.xyz</pre>
<p>StartOS dashboard → Marketplace → Add → paste the URL above. Keysat will appear; click Install.</p>
</div>
<div>
<h3>Sideload</h3>
<p>If you'd rather not add the marketplace:</p>
<ol style="margin: 0.5rem 0 0; padding-left: 1.25rem; color: var(--muted)">
<li>Download the latest <code class="inline">keysat_x86_64.s9pk</code> from <a href="https://github.com/keysat-xyz/keysat-startos/releases">GitHub releases</a>.</li>
<li>StartOS dashboard → Sideload → drag the file in.</li>
<li>Click Install.</li>
</ol>
</div>
</div>
<h3 style="margin-top: 2.5rem">Then once installed</h3>
<ol class="flow" style="margin-top: 1rem">
<li>
<h3>Run "Connect BTCPay"</h3>
<p>One click; Keysat handles the rest.</p>
</li>
<li>
<h3>Set your operator name</h3>
<p>What buyers see on receipts and the public homepage.</p>
</li>
<li>
<h3>Open the admin web UI</h3>
<p>Click "Launch UI" on the Keysat service in StartOS. Paste your admin API key. Create your first product, policy, and discount code from there.</p>
</li>
</ol>
</div>
</section>
<section class="block alt">
<div class="inner">
<h2>Sovereign by default</h2>
<div class="two-col">
<div>
<h3>What you keep</h3>
<ul class="pillets">
<li>Signing keypair</li>
<li>Customer email / npub list</li>
<li>Sale records</li>
<li>Audit log</li>
<li>BTCPay invoice history</li>
<li>Webhook subscribers</li>
<li>Bitcoin (your wallet)</li>
</ul>
<p style="margin-top:1rem;color:var(--muted)">Backed up automatically by StartOS as part of your normal backup routine.</p>
</div>
<div>
<h3>What's outside the box</h3>
<ul class="pillets">
<li>No Stripe</li>
<li>No Gumroad</li>
<li>No Paddle</li>
<li>No Cryptlex / Keygen / LicenseSpring</li>
<li>No SaaS subscription fees</li>
<li>No platform decisions about who you can sell to</li>
</ul>
<p style="margin-top:1rem;color:var(--muted)">Source-available license; one-time payment in sats; everything ships with you when you migrate Start9 hardware.</p>
</div>
</div>
</div>
</section>
<footer class="site">
<div class="inner">
<a href="#top" class="brand"><img src="/assets/icon.png" alt=""><span>Keysat</span></a>
<div class="links">
<a href="https://registry.keysat.xyz">Marketplace</a>
<a href="https://github.com/keysat-xyz/keysat">Source</a>
<a href="https://github.com/keysat-xyz/keysat/blob/main/docs/INTEGRATION.md">Integration docs</a>
<a href="mailto:licensing@keysat.xyz">Contact</a>
</div>
<div>© Keysat. Source-available; not open-source.</div>
</div>
</footer>
</body>
</html>