843ff0e5d7
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).
715 lines
30 KiB
HTML
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 & 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(&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>
|