Files
keysat-root/keysat-design-system/ui_kits/marketing/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

715 lines
30 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>
<link rel="stylesheet" href="../../colors_and_type.css">
<style>
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
font-family: var(--font-body);
color: var(--ink-900);
background: var(--cream-100);
background-image:
radial-gradient(rgba(14,31,51,0.025) 1px, transparent 1px),
radial-gradient(rgba(138,111,61,0.022) 1px, transparent 1px);
background-size: 3px 3px, 7px 7px;
background-position: 0 0, 1px 1px;
-webkit-font-smoothing: antialiased;
}
.wrap { max-width: 1180px; margin: 0 auto; padding: 0 32px; }
a { color: var(--ink-900); text-decoration: none; }
a:hover { color: var(--navy-900); }
/* ---------- Header ---------- */
header.site {
position: sticky; top: 0; z-index: 20;
background: rgba(245, 241, 232, 0.85);
backdrop-filter: blur(12px);
border-bottom: 1px solid var(--border-1);
}
header.site .inner {
display: flex; align-items: center; gap: 28px;
padding: 16px 0;
}
header.site .brand {
display: flex; align-items: center; gap: 10px;
font-family: var(--font-display);
font-weight: 500;
font-size: 15px;
letter-spacing: 0.30em;
text-transform: uppercase;
color: var(--navy-900);
}
header.site .brand img { width: 32px; height: 32px; }
header.site nav { margin-left: auto; display: flex; gap: 28px; font-size: 14px; }
header.site nav a { color: var(--ink-700); font-weight: 500; }
header.site nav a:hover { color: var(--navy-900); }
header.site .cta { margin-left: 8px; }
/* Buttons */
.btn {
display: inline-flex; align-items: center; gap: 8px;
font-family: var(--font-body); font-weight: 600; font-size: 14px;
padding: 11px 20px; border-radius: 8px; border: 1px solid transparent;
cursor: pointer; transition: all 120ms var(--ease-standard); line-height: 1;
text-decoration: none;
}
.btn.lg { font-size: 15.5px; padding: 14px 24px; }
.btn.primary { background: var(--navy-800); color: var(--cream-50); border-color: var(--navy-800); }
.btn.primary:hover { background: var(--navy-900); border-color: var(--navy-900); color: var(--cream-50); }
.btn.secondary { background: transparent; color: var(--navy-900); border-color: var(--border-2); }
.btn.secondary:hover { background: var(--cream-200); color: var(--navy-900); }
.btn.ghost { background: transparent; color: var(--navy-900); border: none; }
.btn.ghost:hover { background: rgba(14,31,51,0.06); }
.btn .arrow { transition: transform 200ms var(--ease-standard); }
.btn:hover .arrow { transform: translateX(2px); }
/* ---------- Hero ---------- */
section.hero { padding: 92px 0 72px; }
.hero-grid { display: grid; grid-template-columns: 1.15fr 1fr; gap: 64px; align-items: center; }
.hero .eyebrow {
font-size: 11.5px; font-weight: 700; letter-spacing: 0.18em;
text-transform: uppercase; color: var(--gold-700);
display: inline-flex; align-items: center; gap: 10px; margin-bottom: 22px;
}
.hero .eyebrow::before {
content: ''; display: inline-block; width: 28px; height: 1px; background: var(--gold-500);
}
.hero h1 {
font-family: var(--font-display);
font-size: clamp(44px, 5.4vw, 72px);
font-weight: 500;
line-height: 1.02;
letter-spacing: -0.022em;
color: var(--navy-950);
margin: 0 0 22px;
text-wrap: balance;
}
.hero h1 .gold {
background-image: linear-gradient(to top, var(--gold-400) 0, var(--gold-400) 6px, transparent 6px);
background-repeat: no-repeat;
background-position: 0 95%;
padding-bottom: 2px;
}
.hero p.lede {
font-size: 19px;
line-height: 1.55;
color: var(--ink-700);
max-width: 540px;
margin: 0 0 32px;
}
.hero .cta-row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.hero .trust {
margin-top: 36px;
display: flex; align-items: center; gap: 18px;
font-size: 13px; color: var(--ink-500);
flex-wrap: wrap;
}
.hero .trust span { display: inline-flex; align-items: center; gap: 7px; }
.hero .trust .dot { width: 4px; height: 4px; border-radius: 50%; background: var(--gold-500); }
.hero .trust [data-lucide] { color: var(--navy-700); }
/* Hero visual: mocked license certificate */
.cert {
background: var(--cream-50);
border: 1px solid var(--border-1);
border-radius: 14px;
box-shadow: 0 0 0 1px var(--gold-500) inset, 0 8px 16px rgba(14,31,51,0.10), 0 24px 64px rgba(14,31,51,0.10);
padding: 36px 36px 30px;
position: relative;
transform: rotate(-1.2deg);
max-width: 460px;
margin-left: auto;
}
.cert::before, .cert::after {
content: ''; position: absolute; left: 16px; right: 16px; height: 1px; background: var(--gold-500); opacity: 0.5;
}
.cert::before { top: 16px; }
.cert::after { bottom: 16px; }
.cert .seal {
position: absolute; right: -30px; top: -30px;
width: 88px; height: 88px; border-radius: 50%;
background: var(--cream-50);
box-shadow: 0 0 0 1px var(--gold-500) inset, 0 0 0 5px var(--cream-50), 0 0 0 6px var(--gold-500), var(--shadow-md);
display: flex; align-items: center; justify-content: center;
font-family: var(--font-display); font-weight: 900; font-size: 32px; color: var(--navy-800);
transform: rotate(8deg);
}
.cert .stamp {
font-size: 9.5px; font-weight: 700; letter-spacing: 0.22em;
text-transform: uppercase; color: var(--gold-700);
text-align: center; margin-bottom: 14px;
}
.cert h4 {
font-family: var(--font-display); font-weight: 500; font-size: 22px;
text-align: center; color: var(--navy-900); margin: 0 0 4px; letter-spacing: -0.015em;
}
.cert .sub {
text-align: center; font-size: 12px; color: var(--ink-500); margin-bottom: 22px;
}
.cert .field { font-size: 11px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-500); margin-bottom: 4px; }
.cert .value { font-family: var(--font-mono); font-size: 14px; color: var(--navy-900); margin-bottom: 14px; }
.cert .row { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.cert .sig {
border-top: 1px dashed rgba(14,31,51,0.2);
padding-top: 12px; margin-top: 6px;
font-family: var(--font-mono); font-size: 10.5px; color: var(--ink-500); line-height: 1.5;
word-break: break-all;
}
/* ---------- Section base ---------- */
section.block { padding: 96px 0; }
section.tinted { background: var(--cream-200); position: relative; }
section.tinted::before, section.tinted::after {
content: ''; position: absolute; left: 0; right: 0; height: 1px; background: var(--gold-500); opacity: 0.4;
}
section.tinted::before { top: 0; }
section.tinted::after { bottom: 0; }
.section-head { max-width: 760px; margin-bottom: 56px; }
.section-head .eyebrow {
font-size: 11.5px; font-weight: 700; letter-spacing: 0.18em;
text-transform: uppercase; color: var(--gold-700);
margin-bottom: 14px; display: block;
}
.section-head h2 {
font-family: var(--font-display);
font-size: clamp(32px, 3.6vw, 46px);
font-weight: 500; line-height: 1.05; letter-spacing: -0.022em;
color: var(--navy-950); margin: 0 0 14px;
}
.section-head p {
font-size: 18px; line-height: 1.55; color: var(--ink-700); margin: 0; max-width: 580px;
}
/* ---------- Value grid ---------- */
.value-grid {
display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
background: var(--border-1);
border: 1px solid var(--border-1);
border-radius: 14px; overflow: hidden;
}
.value-grid .item {
background: var(--cream-50);
padding: 32px 28px;
transition: background 150ms;
}
.value-grid .item:hover { background: var(--cream-100); }
.value-grid .icon-wrap {
width: 40px; height: 40px; border-radius: 8px;
background: var(--cream-200);
border: 1px solid var(--border-1);
display: flex; align-items: center; justify-content: center;
margin-bottom: 18px; color: var(--navy-800);
}
.value-grid h3 {
font-family: var(--font-display); font-weight: 700; font-size: 18px;
color: var(--navy-950); margin: 0 0 6px; letter-spacing: -0.01em;
}
.value-grid h3 + .accent-bar {
width: 22px; height: 2px; background: var(--gold-500); margin-bottom: 12px;
}
.value-grid p { margin: 0; font-size: 14.5px; color: var(--ink-700); line-height: 1.55; }
/* ---------- Flow ---------- */
.flow { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; }
.flow .step {
background: var(--cream-50);
border: 1px solid var(--border-1);
border-radius: 12px;
padding: 28px 22px 24px;
position: relative;
box-shadow: var(--shadow-sm);
}
.flow .num {
font-family: var(--font-display); font-weight: 900; font-size: 56px;
color: var(--gold-500); line-height: 1; margin-bottom: 14px;
letter-spacing: -0.04em;
font-variant-numeric: lining-nums;
}
.flow .step h3 {
font-family: var(--font-display); font-weight: 700; font-size: 16px;
color: var(--navy-950); margin: 0 0 6px; letter-spacing: -0.01em;
line-height: 1.25;
}
.flow .step p {
font-size: 13.5px; color: var(--ink-700); line-height: 1.5; margin: 0;
}
/* ---------- Code block ---------- */
.code-card {
background: var(--navy-950);
border-radius: 14px;
overflow: hidden;
box-shadow: var(--shadow-lg);
border: 1px solid var(--navy-900);
}
.code-tabs {
display: flex;
background: var(--navy-900);
border-bottom: 1px solid rgba(245,241,232,0.08);
padding: 0 8px;
}
.code-tabs button {
background: transparent; border: 0;
color: rgba(245,241,232,0.55);
font-family: var(--font-body); font-weight: 500; font-size: 13px;
padding: 14px 18px; cursor: pointer;
border-bottom: 2px solid transparent;
transition: color 120ms;
}
.code-tabs button:hover { color: rgba(245,241,232,0.85); }
.code-tabs button.active {
color: var(--cream-50);
border-bottom-color: var(--gold-500);
}
.code-tabs .install {
margin-left: auto;
padding: 14px 18px;
font-family: var(--font-mono); font-size: 12px;
color: var(--gold-400);
}
pre.code {
margin: 0;
padding: 24px 28px;
font-family: var(--font-mono); font-size: 13.5px;
line-height: 1.7; color: var(--cream-50);
overflow-x: auto;
}
pre.code .c { color: rgba(245,241,232,0.45); }
pre.code .k { color: var(--gold-400); }
pre.code .s { color: #d4b985; }
pre.code .n { color: #a6b7cf; }
pre.code .f { color: var(--cream-50); }
pre.code .p { color: rgba(245,241,232,0.55); }
.code-section { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: start; }
.code-section .pitch h3 { font-family: var(--font-display); font-weight: 700; font-size: 22px; color: var(--navy-950); margin: 0 0 12px; letter-spacing: -0.015em; }
.code-section .pitch p { font-size: 16px; color: var(--ink-700); line-height: 1.55; margin: 0 0 16px; }
.code-section .pitch ul { list-style: none; padding: 0; margin: 24px 0 0; }
.code-section .pitch li { display: flex; align-items: start; gap: 12px; padding: 8px 0; font-size: 14.5px; color: var(--ink-700); }
.code-section .pitch li::before { content: '✓'; color: var(--gold-600); font-weight: 700; flex-shrink: 0; margin-top: 1px; }
/* ---------- Sovereign panel ---------- */
.sov { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.sov .panel {
background: var(--cream-50);
border: 1px solid var(--border-1);
border-radius: 14px;
padding: 32px 32px 28px;
}
.sov .panel.dark {
background: var(--navy-950); color: var(--cream-50);
border: 1px solid var(--navy-900);
}
.sov .panel h3 {
font-family: var(--font-display); font-weight: 700; font-size: 19px;
margin: 0 0 4px; letter-spacing: -0.015em; color: inherit;
}
.sov .panel .sub { font-size: 13px; color: var(--ink-500); margin-bottom: 22px; }
.sov .panel.dark .sub { color: rgba(245,241,232,0.55); }
.sov ul { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.sov li {
font-size: 13px;
padding: 6px 13px; border-radius: 999px;
background: var(--cream-100);
border: 1px solid var(--border-1);
color: var(--ink-700);
}
.sov .panel.dark li {
background: var(--navy-900);
border-color: rgba(245,241,232,0.15);
color: rgba(245,241,232,0.9);
}
.sov .panel.dark li.no::before {
content: '✕ '; color: rgba(245,241,232,0.45); margin-right: 2px;
}
.sov .panel .footnote { font-size: 13px; color: var(--ink-500); margin: 18px 0 0; line-height: 1.55; }
.sov .panel.dark .footnote { color: rgba(245,241,232,0.6); }
/* ---------- Install ---------- */
.install-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.install-card {
background: var(--cream-50); border: 1px solid var(--border-1);
border-radius: 14px; padding: 28px;
}
.install-card.featured { box-shadow: 0 0 0 1px var(--gold-500) inset, var(--shadow-sm); }
.install-card .cap {
display: inline-block; font-size: 10.5px; font-weight: 700;
letter-spacing: 0.18em; text-transform: uppercase; color: var(--gold-700);
margin-bottom: 12px;
}
.install-card h3 {
font-family: var(--font-display); font-weight: 700; font-size: 22px;
color: var(--navy-950); margin: 0 0 8px; letter-spacing: -0.015em;
}
.install-card p { font-size: 14.5px; color: var(--ink-700); margin: 0 0 16px; line-height: 1.55; }
.cmd-card {
background: var(--navy-950); color: var(--cream-50);
border-radius: 10px; padding: 14px 16px;
font-family: var(--font-mono); font-size: 13px;
display: flex; align-items: center; justify-content: space-between;
gap: 12px;
}
.cmd-card .copy {
background: rgba(245,241,232,0.10); color: var(--cream-50);
border: 0; padding: 6px 10px; border-radius: 6px;
font-family: var(--font-body); font-size: 11.5px; cursor: pointer;
transition: background 120ms;
}
.cmd-card .copy:hover { background: rgba(245,241,232,0.20); }
.install-card ol { padding-left: 20px; margin: 8px 0 0; color: var(--ink-700); font-size: 14.5px; line-height: 1.7; }
.install-card ol code { font-family: var(--font-mono); font-size: 0.9em; padding: 2px 5px; background: var(--cream-200); border-radius: 4px; }
/* ---------- Footer ---------- */
footer.site {
background: var(--navy-950); color: var(--cream-300);
padding: 56px 0 36px;
border-top: 1px solid var(--gold-500);
}
footer.site .top { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 36px; margin-bottom: 36px; }
footer.site .col h5 {
font-family: var(--font-body); font-size: 11.5px; font-weight: 700;
letter-spacing: 0.18em; text-transform: uppercase; color: var(--gold-400);
margin: 0 0 14px;
}
footer.site .col a {
display: block; color: rgba(245,241,232,0.7); padding: 4px 0;
font-size: 14px;
}
footer.site .col a:hover { color: var(--cream-50); }
footer.site .brand-block { max-width: 320px; }
footer.site .brand {
display: flex; align-items: center; gap: 10px;
font-family: var(--font-display); font-weight: 500; font-size: 15px; letter-spacing: 0.30em; text-transform: uppercase;
color: var(--cream-50);
margin-bottom: 14px;
}
footer.site .brand img { width: 32px; height: 32px; }
footer.site .tag { font-size: 13.5px; line-height: 1.55; color: rgba(245,241,232,0.65); margin: 0; }
footer.site .bottom {
border-top: 1px solid rgba(245,241,232,0.10);
padding-top: 24px;
display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px;
font-size: 12.5px; color: rgba(245,241,232,0.45);
}
footer.site .bottom a { color: rgba(245,241,232,0.6); }
/* ---------- Responsive ---------- */
@media (max-width: 980px) {
.hero-grid { grid-template-columns: 1fr; gap: 48px; }
.cert { transform: none; max-width: 100%; margin: 0 auto; }
.value-grid { grid-template-columns: repeat(2, 1fr); }
.flow { grid-template-columns: repeat(2, 1fr); }
.code-section, .sov, .install-grid { grid-template-columns: 1fr; }
header.site nav { display: none; }
.section-head h2 { font-size: 36px; }
section.block { padding: 64px 0; }
}
</style>
</head>
<body>
<header class="site">
<div class="wrap inner">
<a class="brand" href="#top">
<img src="../../assets/keysat-mark.svg" alt="">
<span>Keysat</span>
</a>
<nav>
<a href="#why">Why</a>
<a href="#how">How it works</a>
<a href="#integrate">Integrate</a>
<a href="#sovereign">Sovereign</a>
<a href="#install">Install</a>
</nav>
<a href="#install" class="btn primary cta">Install Keysat</a>
</div>
</header>
<section class="hero" id="top">
<div class="wrap hero-grid">
<div>
<div class="eyebrow">Software licensing for Bitcoin creators</div>
<h1>Bitcoin-paid software licensing, <span class="gold">self-hosted</span> on Start9.</h1>
<p class="lede">
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 lg" href="#install">Install Keysat <span class="arrow"></span></a>
<a class="btn secondary lg" href="#how">See how it works</a>
</div>
<div class="trust">
<span><i data-lucide="server" style="width:14px;height:14px"></i> Runs on Start9</span>
<span class="dot"></span>
<span><i data-lucide="bitcoin" style="width:14px;height:14px"></i> Pays via BTCPay</span>
<span class="dot"></span>
<span><i data-lucide="wifi-off" style="width:14px;height:14px"></i> Verifies offline</span>
</div>
</div>
<div>
<div class="cert" role="img" aria-label="Sample license certificate">
<div class="seal"></div>
<div class="stamp">— Certificate of License —</div>
<h4>Sundial 2.0</h4>
<div class="sub">Issued under default policy · single seat · 1 year</div>
<div class="field">License key</div>
<div class="value">KS-9F2A-7C41-XK22-6D8E</div>
<div class="row">
<div>
<div class="field">Issued</div>
<div class="value" style="font-size: 13px">2026-04-22</div>
</div>
<div>
<div class="field">Expires</div>
<div class="value" style="font-size: 13px">2027-04-22</div>
</div>
</div>
<div class="sig">Ed25519 · mz7q8r4t1v…h3k2pXq9wL · ✓ verified offline</div>
</div>
</div>
</div>
</section>
<section class="block tinted" id="why">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">What this enables</span>
<h2>A complete sell-your-software stack, sovereign end-to-end.</h2>
<p>Keysat handles the licensing layer. BTCPay handles payments. Your hardware holds the keys. No third party can mint, revoke, or read your sales records.</p>
</div>
<div class="value-grid">
<div class="item">
<div class="icon-wrap"><i data-lucide="zap"></i></div>
<h3>Bitcoin payments, your store</h3><div class="accent-bar"></div>
<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">
<div class="icon-wrap"><i data-lucide="key-round"></i></div>
<h3>You own the signing key</h3><div class="accent-bar"></div>
<p>The Ed25519 keypair lives on your hardware. Every license is signed by it. There's no third party who could mint or revoke licenses.</p>
</div>
<div class="item">
<div class="icon-wrap"><i data-lucide="wifi-off"></i></div>
<h3>Offline verification</h3><div class="accent-bar"></div>
<p>Your software verifies licenses against an embedded public key. No network call. Customer apps work even if your Keysat goes offline.</p>
</div>
<div class="item">
<div class="icon-wrap"><i data-lucide="ticket"></i></div>
<h3>Trials, expiries, seats, entitlements</h3><div class="accent-bar"></div>
<p>Per-product policies for time-limited licenses, multi-seat caps, trial flags, feature entitlements baked into the key.</p>
</div>
<div class="item">
<div class="icon-wrap"><i data-lucide="tag"></i></div>
<h3>Discount &amp; referral codes</h3><div class="accent-bar"></div>
<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">
<div class="icon-wrap"><i data-lucide="wrench"></i></div>
<h3>SDKs in your language</h3><div class="accent-bar"></div>
<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="wrap">
<div class="section-head">
<span class="eyebrow">How it works</span>
<h2>Five steps, end to end.</h2>
<p>From sideload to first sale in an afternoon. No cloud account to create, no API keys to copy.</p>
</div>
<ol class="flow">
<li class="step"><div class="num">01</div><h3>Install on your Start9</h3><p>Sideload the <code>.s9pk</code>, or install from <code>registry.keysat.xyz</code>. BTCPay comes bundled as a dependency.</p></li>
<li class="step"><div class="num">02</div><h3>Connect BTCPay</h3><p>One click in the StartOS Actions tab. Authorize once on BTCPay's consent page; Keysat registers a webhook automatically.</p></li>
<li class="step"><div class="num">03</div><h3>Define products + policies</h3><p>Declare a product, set its price in sats, define a policy (duration, seat cap, trial, entitlements).</p></li>
<li class="step"><div class="num">04</div><h3>Embed your public key</h3><p>Copy your Keysat public key into your app. Add the SDK. Five lines of code verifies a signature at startup.</p></li>
<li class="step"><div class="num">05</div><h3>Share your purchase URL</h3><p>Buyers hit your public URL, pay in Bitcoin, get a signed license. Their copy of your software boots up licensed.</p></li>
</ol>
</div>
</section>
<section class="block tinted" id="integrate">
<div class="wrap">
<div class="code-section">
<div class="pitch">
<span class="eyebrow" style="color: var(--gold-700); font-size: 11.5px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase;">For developers</span>
<h3 style="margin-top: 14px; font-size: 36px; line-height: 1.1; letter-spacing: -0.022em">Five lines, in the language you already write.</h3>
<p>Keysat licenses are Ed25519-signed and base32-encoded. Verification is pure-function — no network, no daemon, no shared state.</p>
<ul>
<li>Wire-compatible across SDKs</li>
<li>Public key embedded at compile time</li>
<li>Returns product, policy, expiry, entitlements</li>
<li>Source-available, easy to port</li>
</ul>
</div>
<div class="code-card">
<div class="code-tabs">
<button class="active" data-lang="ts">TypeScript</button>
<button data-lang="rs">Rust</button>
<button data-lang="py">Python</button>
<span class="install" id="install-cmd">npm install @keysat/licensing-client</span>
</div>
<pre class="code" id="code-ts"><span class="k">import</span> { <span class="f">Verifier</span>, <span class="f">PublicKey</span> } <span class="k">from</span> <span class="s">'@keysat/licensing-client'</span>
<span class="k">const</span> verifier = <span class="k">new</span> <span class="f">Verifier</span>(
<span class="f">PublicKey</span>.<span class="f">fromPem</span>(ISSUER_PEM)
)
<span class="k">const</span> ok = verifier.<span class="f">verify</span>(licenseKeyFromUser)
console.<span class="f">log</span>(<span class="s">'licensed:'</span>, ok.productId, ok.expires)</pre>
<pre class="code" id="code-rs" style="display:none"><span class="c">// Cargo.toml</span>
<span class="c">// licensing-client = "0.1"</span>
<span class="k">use</span> licensing_client::{<span class="f">Verifier</span>, <span class="f">PublicKeyPem</span>};
<span class="k">let</span> pk = <span class="f">PublicKeyPem</span>::from_str(ISSUER_PEM)<span class="p">?</span>;
<span class="k">let</span> verifier = <span class="f">Verifier</span>::new(pk);
<span class="k">let</span> ok = verifier.verify(&amp;license_key)<span class="p">?</span>;
println!(<span class="s">"licensed: {}"</span>, ok.product_id);</pre>
<pre class="code" id="code-py" style="display:none"><span class="k">from</span> keysat_licensing_client <span class="k">import</span> Verifier, PublicKey
verifier = <span class="f">Verifier</span>(<span class="f">PublicKey</span>.<span class="f">from_pem</span>(ISSUER_PEM))
ok = verifier.<span class="f">verify</span>(license_key_from_user)
<span class="k">print</span>(<span class="s">"licensed for"</span>, ok.product_id)</pre>
</div>
</div>
</div>
</section>
<section class="block" id="sovereign">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">Sovereign by default</span>
<h2>Everything stays on your hardware.</h2>
<p>If you migrate Start9 boxes, all of Keysat goes with you. If Keysat the project disappears, your existing licenses keep verifying — the public key is embedded in your software, the private key is on your machine.</p>
</div>
<div class="sov">
<div class="panel">
<h3>What you keep</h3>
<div class="sub">On your Start9, in your normal backups.</div>
<ul>
<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 class="footnote">Backed up automatically by StartOS as part of your normal backup routine.</p>
</div>
<div class="panel dark">
<h3>What's outside the box</h3>
<div class="sub">Things you don't have to deal with.</div>
<ul>
<li class="no">Stripe</li>
<li class="no">Gumroad</li>
<li class="no">Paddle</li>
<li class="no">Cryptlex</li>
<li class="no">Keygen</li>
<li class="no">LicenseSpring</li>
<li class="no">SaaS subscription fees</li>
<li class="no">Platform decisions about who you sell to</li>
</ul>
<p class="footnote">Source-available license · one-time payment in sats · ships with you when you migrate hardware.</p>
</div>
</div>
</div>
</section>
<section class="block tinted" id="install">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">Install</span>
<h2>From the marketplace, or sideload directly.</h2>
</div>
<div class="install-grid">
<div class="install-card featured">
<span class="cap">Recommended</span>
<h3>From the marketplace</h3>
<p>Add the Keysat marketplace to your Start9, then click Install.</p>
<div class="cmd-card">
<span>https://registry.keysat.xyz</span>
<button class="copy">Copy</button>
</div>
<p style="margin-top: 16px; font-size: 13.5px; color: var(--ink-500)">StartOS dashboard → Marketplace → Add → paste the URL.</p>
</div>
<div class="install-card">
<span class="cap">Alternative</span>
<h3>Sideload</h3>
<p>If you'd rather not add the marketplace:</p>
<ol>
<li>Download <code>keysat_x86_64.s9pk</code> from <a href="#">GitHub releases</a>.</li>
<li>StartOS dashboard → Sideload → drag the file in.</li>
<li>Click Install.</li>
</ol>
</div>
</div>
</div>
</section>
<footer class="site">
<div class="wrap">
<div class="top">
<div class="brand-block">
<div class="brand"><img src="../../assets/keysat-mark.svg" alt=""><span>Keysat</span></div>
<p class="tag">Software licensing for Bitcoin creators. Self-hosted, sovereign, source-available.</p>
</div>
<div class="col">
<h5>Product</h5>
<a href="#why">Why Keysat</a>
<a href="#how">How it works</a>
<a href="#install">Install</a>
<a href="#">Marketplace</a>
</div>
<div class="col">
<h5>Developers</h5>
<a href="#integrate">Integration</a>
<a href="#">SDKs</a>
<a href="#">Wire format</a>
<a href="#">GitHub</a>
</div>
<div class="col">
<h5>Contact</h5>
<a href="mailto:licensing@keysat.xyz">licensing@keysat.xyz</a>
<a href="#">Status</a>
<a href="#">Changelog</a>
</div>
</div>
<div class="bottom">
<span>© Keysat. Source-available; not open-source.</span>
<span>Runs on Start9 · Pays via BTCPay · Verifies offline</span>
</div>
</div>
</footer>
<script src="https://unpkg.com/lucide@latest"></script>
<script>
lucide.createIcons();
// Tab switching for code samples
const installCmds = {
ts: 'npm install @keysat/licensing-client',
rs: 'cargo add licensing-client',
py: 'pip install keysat-licensing-client',
};
document.querySelectorAll('.code-tabs button').forEach(btn => {
btn.addEventListener('click', () => {
const lang = btn.dataset.lang;
document.querySelectorAll('.code-tabs button').forEach(b => b.classList.toggle('active', b === btn));
['ts','rs','py'].forEach(l => {
document.getElementById('code-' + l).style.display = l === lang ? 'block' : 'none';
});
document.getElementById('install-cmd').textContent = installCmds[lang];
});
});
</script>
</body>
</html>