23aa121afb
Five-page sweep to match the current daemon state. install.html: - Step 6 (first product): "Price (sats)" → reflects the currency picker (sats / USD / EUR) shipped in migration 0010. - Step 7 (first policy): drop the "default slug is consumed by public flow" myth — buy page renders a tier picker for any product with ≥2 public policies. Add references to entitlements catalog, hide-on-buy toggles, marketing bullets, recurring subscriptions, and the drag-to-reorder policy grid. integrate.html: - "Three official SDKs" → "Four official SDKs" + Go tab + install snippet. Notes the daemon's cross-check fixtures assert byte-for-byte parity across all four. - Admin API table: drop "by npub" from the licenses search description (backend supports it; UI hasn't surfaced it yet since the purchase flow doesn't capture npubs). operate.html: - Backups section: drop the imaginary `/data/issuer-key.pem` file — the signing keypair lives in the `server_keys` SQLite table, not in a PEM file on disk. Mention the self-license file path (`/data/keysat-license.txt`). - Rotation: drop the "v0.1 doesn't support / v0.2 will" framing; rotation isn't on the v0.2 / v0.3 roadmap and the v0.1 caveat is misleading. Update steps to reflect SQLite-as-keystore. - Webhook troubleshooting: point at the dedicated Webhooks → Failed (DLQ) view rather than the audit log. pricing.html: - Creator: 21,000 sats one-time → Free forever (matches actual master Keysat configuration). - Pro: 250,000 sats/yr → 100,000 sats/yr (recurring). Note recurring + tier upgrades have shipped; only Zaprite remains v0.3. - Patron: 500,000 sats/yr → 250,000 sats one-time perpetual. Differentiation rewritten: perpetual license + direct 1:1 support (not just "Pro with a badge"). - Active discount-code cap: 5 → 10 (real cap). - New "Prices shown are a snapshot" note pointing at the canonical live source (keysat.xyz#tiers + the buy page). - Updated unlicensed-caps line to show 5/5/10 with units. wire-format.html: - Replace the entirely-fabricated "KS-base32-blob with KSAT magic bytes" layout with the actual LIC1 envelope: `LIC1-<base32 payload>-<base32 signature>` split on dashes. - Document BOTH payload versions: v1 (legacy 74-byte fixed) and v2 (current default, 83-byte head + variable entitlements table). Field offsets, flag bits, signature scope all match the daemon source. - Drop the bogus Crockford-base32 + dash-grouping sections — the daemon uses RFC 4648 base32 with single-dash structure separators, not grouped-dashes for readability. - Drop the fabricated hex-dump worked example. - Porting section now points at `licensing-service/tests/crosscheck/` (the actual fixtures location) instead of a Python-SDK path. - Versioning policy: clarify envelope-tag vs payload-version cadence.
163 lines
9.8 KiB
HTML
163 lines
9.8 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Keysat Docs — Operate</title>
|
|
<link rel="icon" type="image/svg+xml" href="assets/favicon.svg">
|
|
<link rel="stylesheet" href="docs.css">
|
|
</head>
|
|
<body>
|
|
|
|
<div class="topnav">
|
|
<a href="https://keysat.xyz" class="brand" title="Back to keysat.xyz"><img src="assets/keysat-mark.svg" alt=""><span>Keysat</span></a>
|
|
<span class="docs-tag">Docs</span>
|
|
</div>
|
|
|
|
<div class="layout">
|
|
<aside class="side">
|
|
<div class="group">
|
|
<div class="glabel">Get started</div>
|
|
<a href="index.html">Introduction</a>
|
|
<a href="install.html">Install & setup</a>
|
|
<a href="integrate.html">Integrate the SDK</a>
|
|
<a href="agent.html">Agent integration</a>
|
|
</div>
|
|
<div class="group">
|
|
<div class="glabel">Concepts</div>
|
|
<a href="index.html#architecture">Architecture</a>
|
|
<a href="index.html#products-policies">Products & policies</a>
|
|
<a href="index.html#discounts">Discount codes</a>
|
|
<a href="index.html#revocation">Revocation strategy</a>
|
|
</div>
|
|
<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">Operate</div>
|
|
<a href="operate.html#backups" class="active">Backups</a>
|
|
<a href="operate.html#migrate">Migrate hardware</a>
|
|
<a href="operate.html#troubleshooting">Troubleshooting</a>
|
|
</div>
|
|
</aside>
|
|
|
|
<main class="prose">
|
|
<div class="crumb">Operate · Day-to-day</div>
|
|
<h1>Operate.</h1>
|
|
<p class="lead">Backups, migration, recovery, and the things that go wrong. The "you didn’t expect to need this page until you needed it" page.</p>
|
|
|
|
<h2 id="backups">Backups</h2>
|
|
<p>StartOS handles backups for you. By default, every service in your StartOS install is included in the same backup snapshot — you set the destination once (encrypted external drive, S3-compatible cloud, etc.) and StartOS schedules nightly snapshots.</p>
|
|
|
|
<p>The Keysat backup payload is intentionally tiny. It contains:</p>
|
|
|
|
<ul>
|
|
<li>The SQLite database (<code>/data/keysat.db</code>), which holds the Ed25519 signing keypair in the <code>server_keys</code> table along with all products, policies, licenses, invoices, audit log, webhook subscribers, and operator settings.</li>
|
|
<li>Migration history.</li>
|
|
<li>The self-license file at <code>/data/keysat-license.txt</code>, if you've activated a paid Keysat tier.</li>
|
|
</ul>
|
|
|
|
<p>That’s it. No log files (those rotate locally), no caches.</p>
|
|
|
|
<div class="callout warn">
|
|
<i data-lucide="alert-triangle"></i>
|
|
<p><strong>Verify your backup destination at least once.</strong> Restoring on a fresh Start9 with a corrupted backup is exactly the wrong moment to discover that your destination wasn’t actually configured. StartOS → Settings → Backups → Test.</p>
|
|
</div>
|
|
|
|
<p>Your BTCPay store data and your Bitcoin wallet are separate backups, handled by the BTCPay and Bitcoin Core packages respectively. Keep them on the same backup destination so they restore in lockstep.</p>
|
|
|
|
<h2 id="migrate">Migrating to new hardware</h2>
|
|
<p>The full migration path:</p>
|
|
|
|
<ol>
|
|
<li>On the old Start9, ensure your most recent backup is complete and includes Keysat. Confirm the destination is writable and that snapshots have finished.</li>
|
|
<li>On the new Start9, complete first-time setup with a fresh password. Don’t install any services yet.</li>
|
|
<li>StartOS → Settings → Backups → Restore. Point at the same destination. Pick the most recent snapshot.</li>
|
|
<li>StartOS restores all services in dependency order. Keysat will restore alongside BTCPay and Bitcoin Core. Bitcoin will need to re-sync if you’re using Bitcoin Core (consider <a href="https://utxo.live">utxo.live</a> for assumeutxo to skip IBD).</li>
|
|
<li>Once Keysat is running on the new box, your purchase URLs change — the LAN/Tor hostnames are different. Update any links you’ve published.</li>
|
|
</ol>
|
|
|
|
<p>The signing keypair restores along with the database, so all previously-issued licenses verify identically against the same public key. You don’t need to re-distribute the public key to your customers.</p>
|
|
|
|
<h2 id="signing-key">Rotating the signing key</h2>
|
|
<p>You generally don’t want to rotate the signing key — doing so invalidates every license you’ve ever issued. There is no admin-UI affordance for rotation today; the key is generated once on first start (and persisted to the <code>server_keys</code> SQLite table) and stays there for the life of the instance.</p>
|
|
|
|
<p>If you absolutely need to rotate (e.g. you suspect the keypair has leaked off the box):</p>
|
|
|
|
<ol>
|
|
<li>Stop Keysat.</li>
|
|
<li>Drop the row in the <code>server_keys</code> table (or move the database aside entirely if you also want to start clean).</li>
|
|
<li>Restart Keysat — it will generate a fresh keypair on first run.</li>
|
|
<li>Re-issue all active licenses to existing customers using the new key. The admin UI doesn’t support bulk re-issuance yet; this is a manual SQL + scripted-API exercise.</li>
|
|
<li>Push a software update that swaps the embedded public key in your downstream apps.</li>
|
|
</ol>
|
|
|
|
<p>A future release may support rolling rotation (two keys verifying during a transition window). It's not on the v0.2 / v0.3 roadmap.</p>
|
|
|
|
<h2 id="troubleshooting">Troubleshooting</h2>
|
|
|
|
<h3 id="t-btcpay-url">"Invalid BTCPay URL" when clicking Connect BTCPay</h3>
|
|
<p>Keysat is selecting a BTCPay URL that isn’t reachable from your browser. This usually means the picked URL is a StartTunnel-local <code>10.59.x.x</code> address rather than your LAN/mDNS one.</p>
|
|
<p>Fix: open BTCPay’s service page in a separate tab, copy the URL it shows under "Network — mDNS" or "Network — LAN", and confirm Keysat is using a similar shape. If you’re on Tor only, BTCPay needs to expose its admin UI over Tor too.</p>
|
|
|
|
<h3 id="t-payment-method">"Payment method unavailable" on first invoice creation</h3>
|
|
<p>BTCPay rejects the invoice request because the store has no configured wallet. Open BTCPay, find your store, and configure either an on-chain wallet or a Lightning node before retrying.</p>
|
|
|
|
<h3 id="t-webhook">Webhook deliveries failing</h3>
|
|
<p>In the admin UI go to <strong>Webhooks</strong> — failed deliveries past the 10-attempt retry budget land in the "Failed" filter (the DLQ), with the response status and an inline "Retry" button. The audit log is a secondary source. Common causes:</p>
|
|
<ul>
|
|
<li>Endpoint URL no longer reachable. Hit it manually with <code>curl</code> from your laptop to confirm.</li>
|
|
<li>Endpoint rejecting on signature mismatch. Verify your endpoint is HMAC-validating against the secret you registered with.</li>
|
|
<li>Endpoint timing out. Endpoints have a 10s deadline. Move slow work behind a queue on your end.</li>
|
|
</ul>
|
|
|
|
<h3 id="t-db-locked">"database is locked" errors in logs</h3>
|
|
<p>Almost always a sign that two daemon instances are racing on the same SQLite file — usually because of a misconfigured supervisor. Confirm only one Keysat container is running. If you’re seeing this on a fresh install with no customizations, file a bug report against the package version you’re running.</p>
|
|
|
|
<h3 id="t-time-skew">Licenses verifying as "expired" immediately after issue</h3>
|
|
<p>Clock skew. Either the issuing host or the verifying host has the wrong time. Run NTP. StartOS keeps your Start9 in sync automatically; the issue is usually on the verifier side (e.g. an air-gapped buyer machine).</p>
|
|
|
|
<h2 id="logs">Reading the logs</h2>
|
|
<p>Keysat logs to stdout, captured by StartOS. Tail them from the StartOS dashboard — Service page → Logs → Live tail.</p>
|
|
|
|
<p>Useful log lines to grep for:</p>
|
|
|
|
<table class="t">
|
|
<thead><tr><th>Pattern</th><th>What it means</th></tr></thead>
|
|
<tbody>
|
|
<tr><td><code>license issued</code></td><td>Successful license issuance. Includes license id and product.</td></tr>
|
|
<tr><td><code>btcpay webhook received</code></td><td>BTCPay delivered an event. Followed by a "settled" or "expired" disposition line.</td></tr>
|
|
<tr><td><code>auth failed</code></td><td>Bad admin API key on a request to <code>/v1/admin/*</code>.</td></tr>
|
|
<tr><td><code>signature mismatch</code></td><td>BTCPay webhook arrived with the wrong HMAC. Either misconfigured or actively malicious.</td></tr>
|
|
<tr><td><code>migration applied</code></td><td>A schema migration ran on startup. Normal during package updates.</td></tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<h2 id="getting-help">Getting help</h2>
|
|
<p>If you’re stuck:</p>
|
|
|
|
<ul>
|
|
<li>File an issue at <a href="https://github.com/keysat-xyz/keysat/issues">github.com/keysat-xyz/keysat/issues</a>. Include the package version (visible in the StartOS service page) and any relevant log lines.</li>
|
|
<li>Email <a href="mailto:licensing@keysat.xyz">licensing@keysat.xyz</a> for security-sensitive issues you don’t want to disclose publicly.</li>
|
|
</ul>
|
|
</main>
|
|
|
|
<aside class="toc">
|
|
<div class="label">On this page</div>
|
|
<a href="#backups">Backups</a>
|
|
<a href="#migrate">Migrate hardware</a>
|
|
<a href="#signing-key">Rotate signing key</a>
|
|
<a href="#troubleshooting">Troubleshooting</a>
|
|
<a href="#logs">Reading the logs</a>
|
|
<a href="#getting-help">Getting help</a>
|
|
</aside>
|
|
</div>
|
|
|
|
<script src="https://unpkg.com/lucide@latest"></script>
|
|
<script>lucide.createIcons();</script>
|
|
</body>
|
|
</html>
|