Docs polish: active-pill sync, license-sidebar bug fix, pricing standardized, ~70 em-dashes removed

- docs.js (new): sync sidebar .active pill with location.hash on load, click, and hashchange so in-page anchor links (Architecture, Discount codes, Backups, etc.) update the pill instead of leaving it stuck on whatever was statically marked
- Wire docs.js into every page just before </body>
- license.html: sidebar Project/Operate order matches every other page (Project first)
- pricing.html: rewritten to use the standard docs layout (full sidebar groups, prose main, breadcrumb) instead of a one-off shell that felt detached from the rest of the docs
- Reference section: remove Admin API + SDKs anchor links (they masqueraded as separate pages but just scrolled within integrate.html); Wire format stands alone
- Pricing copy: Zaprite reframed as "expanded payment options including card payment capabilities", "shipping in v0.3" removed (it shipped), Patron rephrased as perpetual (never expires or renews)
- "Toggling inactive" cap-evasion language replaced — admin UI exposes delete only, no soft-disable affordance for products
- ~70 em-dashes removed across 8 pages using a small pattern set (elaboration→period, list-intro→colon, tight clarification→comma, parentheticals→parens). Decorative stamp ornaments and references to actual third-party UI labels are kept verbatim.
This commit is contained in:
Keysat
2026-05-12 09:25:57 -05:00
parent 348a0b9f13
commit 87fd4f32e3
9 changed files with 285 additions and 220 deletions
+12 -13
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Keysat Docs Integrate the SDK</title>
<title>Keysat Docs: Integrate the SDK</title>
<link rel="icon" type="image/svg+xml" href="assets/favicon.svg">
<link rel="stylesheet" href="docs.css">
</head>
@@ -33,8 +33,6 @@
<div class="group">
<div class="glabel">Reference</div>
<a href="wire-format.html">Wire format</a>
<a href="integrate.html#api">Admin API</a>
<a href="integrate.html#sdks">SDKs</a>
</div>
<div class="group">
<div class="glabel">Project</div>
@@ -52,18 +50,18 @@
<main class="prose">
<div class="crumb">Get started · Integrate the SDK</div>
<h1>Integrate the SDK.</h1>
<p class="lead">Wire Keysat licenses into your software in under an afternoon. The verifier is pure-function, offline, and ships in five lines. What you do with the result refuse to start without a license, unlock specific features, just show a "supporter" badge is your call. The SDK is the primitive; the business model is yours.</p>
<p class="lead">Wire Keysat licenses into your software in under an afternoon. The verifier is pure-function, offline, and ships in five lines. What you do with the result (refuse to start without a license, unlock specific features, just show a "supporter" badge) is your call. The SDK is the primitive; the business model is yours.</p>
<h2 id="prereq">Prerequisites</h2>
<p>Before you start, you should have:</p>
<ul>
<li>A Keysat installation running on your Start9 &mdash; see <a href="install.html">Install &amp; setup</a>.</li>
<li>BTCPay Server connected to Keysat &mdash; ditto.</li>
<li>A Keysat installation running on your Start9; see <a href="install.html">Install &amp; setup</a>.</li>
<li>BTCPay Server connected to Keysat; ditto.</li>
<li>At least one product defined in the admin UI.</li>
</ul>
<h2 id="sdks">Pick an SDK</h2>
<p>Four official SDKs ship today. They are wire-compatible &mdash; a license issued by your Keysat verifies identically in any of them. Cross-check fixtures in the daemon repo prove each SDK accepts the same bytes the daemon mints.</p>
<p>Four official SDKs ship today. They are wire-compatible. A license issued by your Keysat verifies identically in any of them. Cross-check fixtures in the daemon repo prove each SDK accepts the same bytes the daemon mints.</p>
<div class="lang-tabs" role="tablist">
<button class="active" data-lang="ts">TypeScript</button>
@@ -88,11 +86,11 @@ poetry add keysat-licensing-client</pre>
<pre class="code lang-pane" data-lang="go" style="display:none"><span class="c">// go.mod</span>
go get github.com/keysat-xyz/keysat-client-go
<span class="c">// stdlib only no third-party Go dependencies</span></pre>
<span class="c">// stdlib only: no third-party Go dependencies</span></pre>
<p>If your language isn&rsquo;t covered, see <a href="wire-format.html">Wire format</a>. The format is small and porting takes about an afternoon.</p>
<h2 id="embed">Step 1 Embed your public key</h2>
<h2 id="embed">Step 1: Embed your public key</h2>
<p>In the admin UI, open <strong>Overview</strong> and copy the issuer public key from the "Embed your public key" card. (Or fetch it from <code>GET /v1/issuer/public-key</code>.) Paste it into your application&rsquo;s source code as a compile-time constant.</p>
<pre class="code lang-pane" data-lang="ts"><span class="k">const</span> <span class="f">ISSUER_PEM</span> = <span class="s">`-----BEGIN PUBLIC KEY-----
@@ -110,7 +108,7 @@ MCowBQYDK2VwAyEAmz7q8r4t1v…h3k2pXq9wL
<p><strong>Embed it. Don&rsquo;t fetch it.</strong> The whole point of offline verification is that your software can&rsquo;t be tricked by a network-level attacker. If you fetch the public key at runtime, you&rsquo;re back to trusting a server.</p>
</div>
<h2 id="verify">Step 2 Verify a license at startup</h2>
<h2 id="verify">Step 2: Verify a license at startup</h2>
<p>Read the user&rsquo;s license key from wherever you store it (a file in their data directory, the OS keychain, an env var) and verify it on application start.</p>
<pre class="code lang-pane" data-lang="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>;
@@ -167,7 +165,7 @@ result = verifier.<span class="f">verify</span>(license_key_from_user)
</tbody>
</table>
<h2 id="errors">Step 3 Handle errors gracefully</h2>
<h2 id="errors">Step 3: Handle errors gracefully</h2>
<p>Verification can fail for benign reasons (the user hasn&rsquo;t pasted a license yet) or hostile ones (someone tampered with a license file). Distinguish them in your UX:</p>
<pre class="code lang-pane" data-lang="ts"><span class="k">try</span> {
@@ -198,7 +196,7 @@ result = verifier.<span class="f">verify</span>(license_key_from_user)
show_input_error()</pre>
<h2 id="renewals">Renewals &amp; revocation</h2>
<p>Keysat licenses are signed at issue time and do not phone home. If a license is revoked in the admin UI, the existing key continues to verify in your app &mdash; that&rsquo;s the trade-off for offline.</p>
<p>Keysat licenses are signed at issue time and do not phone home. If a license is revoked in the admin UI, the existing key continues to verify in your app. That&rsquo;s the trade-off for offline.</p>
<p>If you need revocation, ship a thin <em>online</em> check that runs on a cadence (e.g. once a week) against your Keysat&rsquo;s revocation feed:</p>
@@ -232,7 +230,7 @@ result = verifier.<span class="f">verify</span>(license_key_from_user)
<div class="callout">
<i data-lucide="key-round"></i>
<p><strong>You decide the policy.</strong> Many indie developers ship no revocation at all. Once a key is sold, it stays valid &mdash; refunds happen offline via BTCPay. That&rsquo;s perfectly reasonable.</p>
<p><strong>You decide the policy.</strong> Many indie developers ship no revocation at all. Once a key is sold, it stays valid. Refunds happen offline via BTCPay. That&rsquo;s perfectly reasonable.</p>
</div>
<h2 id="api">Admin API</h2>
@@ -284,5 +282,6 @@ result = verifier.<span class="f">verify</span>(license_key_from_user)
b.addEventListener('click', () => setLang(b.dataset.lang));
});
</script>
<script src="docs.js"></script>
</body>
</html>