Docs index: refresh against current daemon state
Nine wording fixes: - Hero lede: drop "Bitcoin payment via BTCPay" → "payment via BTCPay" (matches landing-page de-emphasis); split the dense one-paragraph lede into two. - Products: "price in sats" → "price (sats / USD / EUR)"; introduce the entitlements catalog concept. - Policies table: add is_recurring + renewal_period_days, marketing_bullets, hidden_entitlements. Fix "default slug is canonical" myth — multi-tier ladders (Basic / Pro / Max) are first-class and the buy page renders a tier picker for products with 2+ public policies. Note tier_rank + drag-and-drop ordering. Split "private policies" out as a paragraph. - Discounts: add set_price as a 4th kind. Note discount_currency on fixed-amount codes. Add multi-policy scope. Add featured / launch-special section (ribbon, auto-apply, pre-filled input). - Revocation: "v0.2 will ship recurring renewals" → past-tense. Recurring is shipped. - New "Operator tiers" section: explains Keysat self-licenses (Creator / Pro / Patron), notes caps are enforce-on-create (existing rows grandfathered), and lists four canonical sources for the live tier list (keysat.xyz, pricing.html, the public /v1/products/keysat/policies endpoint, the admin /v1/admin/tier endpoint). "As of this writing" framing for the current cap values so they don't go stale silently. - TOC: add #operator-tiers anchor. Pricing.html, install.html, integrate.html, wire-format.html, operate.html — not touched; this is the introduction page only. A separate pass should audit those too.
This commit is contained in:
+32
-8
@@ -47,7 +47,8 @@
|
|||||||
<main class="prose">
|
<main class="prose">
|
||||||
<div class="crumb">Get started · Introduction</div>
|
<div class="crumb">Get started · Introduction</div>
|
||||||
<h1>Welcome to Keysat.</h1>
|
<h1>Welcome to Keysat.</h1>
|
||||||
<p class="lead">Keysat lets independent software creators sell their work on their own terms. You ship software — open source, closed source, free / paid versions, whatever fits — and Keysat handles the buy page, the Bitcoin payment via BTCPay, and a signed license for each buyer. How you use that license inside your software is up to you: a one-time purchase to unlock the whole app, a free + paid split with specific paid features, a tip-jar style supporter badge — all legitimate. The licensing layer is a primitive, not a script.</p>
|
<p class="lead">Keysat lets independent software creators sell their work on their own terms. You ship software — open source, closed source, free / paid versions, whatever fits — and Keysat handles the buy page, payment via BTCPay, and a signed license for each buyer.</p>
|
||||||
|
<p>How you use that license inside your software is up to you: a one-time purchase to unlock the whole app, a free + paid split with specific paid features, a tip-jar style supporter badge — all legitimate. The licensing layer is a primitive, not a script.</p>
|
||||||
|
|
||||||
<p>These docs cover both ends:</p>
|
<p>These docs cover both ends:</p>
|
||||||
|
|
||||||
@@ -86,7 +87,7 @@
|
|||||||
|
|
||||||
<h2 id="products-policies">Products & policies</h2>
|
<h2 id="products-policies">Products & policies</h2>
|
||||||
<p>You declare two things in Keysat: products and policies.</p>
|
<p>You declare two things in Keysat: products and policies.</p>
|
||||||
<p>A <strong>product</strong> is the thing you sell — "Bitcoin Ticker Pro", "Aurora Plugin", whatever. It has a slug, a display name, a description, and a price in sats.</p>
|
<p>A <strong>product</strong> is the thing you sell — "Bitcoin Ticker Pro", "Aurora Plugin", whatever. It has a slug, a display name, a description, and a price (sats / USD / EUR). Each product also carries an <strong>entitlements catalog</strong> — the typed list of feature slugs your software cares about, plus their display names and descriptions. Policies pick entitlements from this catalog.</p>
|
||||||
<p>A <strong>policy</strong> is a license template attached to a product. It specifies:</p>
|
<p>A <strong>policy</strong> is a license template attached to a product. It specifies:</p>
|
||||||
|
|
||||||
<table class="t">
|
<table class="t">
|
||||||
@@ -96,25 +97,37 @@
|
|||||||
<tr><td><code>grace_seconds</code></td><td>Extra time after expiry before the verifier rejects.</td></tr>
|
<tr><td><code>grace_seconds</code></td><td>Extra time after expiry before the verifier rejects.</td></tr>
|
||||||
<tr><td><code>max_machines</code></td><td>Seat cap. <code>0</code> means unlimited.</td></tr>
|
<tr><td><code>max_machines</code></td><td>Seat cap. <code>0</code> means unlimited.</td></tr>
|
||||||
<tr><td><code>is_trial</code></td><td>Sets a <code>TRIAL</code> bit so your app can show a "trial" banner.</td></tr>
|
<tr><td><code>is_trial</code></td><td>Sets a <code>TRIAL</code> bit so your app can show a "trial" banner.</td></tr>
|
||||||
<tr><td><code>entitlements</code></td><td>Free-form list of feature flags baked into the signed key (e.g. <code>core</code>, <code>sync</code>, <code>export</code>).</td></tr>
|
<tr><td><code>is_recurring</code> + <code>renewal_period_days</code></td><td>Auto-renew on a cycle (weekly / monthly / annual / custom). The daemon mints a fresh invoice + signed license per cycle.</td></tr>
|
||||||
|
<tr><td><code>entitlements</code></td><td>Subset of the product’s catalog this policy grants. Baked into the signed license.</td></tr>
|
||||||
|
<tr><td><code>metadata.marketing_bullets</code></td><td>Operator-authored ✓ items rendered on the buy-page tier card. Pure marketing copy — not enforced.</td></tr>
|
||||||
|
<tr><td><code>metadata.hidden_entitlements</code></td><td>Slugs the license still grants but the buy-page card hides — useful when a higher tier uses "Everything in X, plus:" copy and doesn’t want to repeat implied entitlements.</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p>Each product has one policy slugged <code>default</code> — that’s the one consumed by the public purchase URL. You can attach additional named policies for manual issuance: a longer-duration "Lifetime" policy you hand out at conferences, a richer-entitlement "Pro" policy for upsells, etc.</p>
|
<p>A product can have <strong>one policy or many</strong>. Multi-tier ladders (think Basic / Pro / Max) are first-class: when a product has two or more public policies, the buy page renders a tier picker and the buyer chooses before paying. The displayed tier is selected from a <code>?policy=<slug></code> URL hint, then the <code>highlighted</code> ("most popular") policy if any, then the cheapest. Tier ordering on the picker is operator-controlled via drag-and-drop in the admin UI (or <code>tier_rank</code> in the API).</p>
|
||||||
|
<p>You can also attach <strong>private policies</strong> for manual issuance — e.g. a longer-duration "Lifetime" comp for conferences, a richer-entitlement "Internal" tier for support cases. Private policies don’t appear on the buy page; the admin API issues them directly.</p>
|
||||||
|
|
||||||
<h2 id="discounts">Discount codes</h2>
|
<h2 id="discounts">Discount codes</h2>
|
||||||
<p>Three kinds:</p>
|
<p>Four kinds:</p>
|
||||||
|
|
||||||
<table class="t">
|
<table class="t">
|
||||||
<thead><tr><th>Kind</th><th>What it does</th></tr></thead>
|
<thead><tr><th>Kind</th><th>What it does</th></tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td><code>percent</code></td><td>Buyer appends <code>?code=FOUNDERS50</code> to the purchase URL; price drops by N%.</td></tr>
|
<tr><td><code>percent</code></td><td>Buyer appends <code>?code=FOUNDERS50</code> to the purchase URL; price drops by N%.</td></tr>
|
||||||
<tr><td><code>fixed_sats</code></td><td>Like above, but a flat sat amount comes off.</td></tr>
|
<tr><td><code>fixed_sats</code></td><td>Like above, but a flat amount comes off. Denominated in the code’s <code>discount_currency</code> (sats / USD / EUR), so the same code can sit on top of multi-currency products.</td></tr>
|
||||||
|
<tr><td><code>set_price</code></td><td>Overrides the tier price with a flat number, regardless of base. Useful for "first 100 buyers at 25k sats" promos where you want the price to be a specific round number rather than a percentage off.</td></tr>
|
||||||
<tr><td><code>free_license</code></td><td>No payment at all. Buyer redeems the code via <code>POST /v1/redeem</code> and gets a signed license back.</td></tr>
|
<tr><td><code>free_license</code></td><td>No payment at all. Buyer redeems the code via <code>POST /v1/redeem</code> and gets a signed license back.</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<p>Codes can be capped at N uses, dated to expire, restricted to a single product, and tagged with a referrer label so you can see which campaign drove which sales in the audit log.</p>
|
<p>Codes can be capped at N uses, dated to expire, restricted to one product (and optionally to a <strong>subset of policies</strong> on that product — e.g. "applies to Pro and Max but not Basic"), and tagged with a referrer label so you can see which campaign drove which sales in the audit log.</p>
|
||||||
|
<p>Codes can also be marked <strong>featured</strong> — a "launch special" mode. A featured code:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Renders a diagonal "LAUNCH SPECIAL" ribbon + struck-through original price on the matching tier cards on the buy page.</li>
|
||||||
|
<li>Auto-applies for buyers who don’t type any code, with the input pre-filled so they can see what’s been applied.</li>
|
||||||
|
<li>Stops surfacing once it hits its <code>max_uses</code> cap or expires — the ribbon disappears and pricing reverts to standard automatically.</li>
|
||||||
|
</ul>
|
||||||
|
<p>Operator-typed codes always take precedence: a buyer who pastes a non-featured code in the form gets that code instead of the auto-applied featured one.</p>
|
||||||
|
|
||||||
<h2 id="revocation">Revocation strategy</h2>
|
<h2 id="revocation">Revocation strategy</h2>
|
||||||
<p>This is the one piece of the architecture that requires a design decision from you.</p>
|
<p>This is the one piece of the architecture that requires a design decision from you.</p>
|
||||||
@@ -124,7 +137,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li><strong>Don’t support revocation at all.</strong> Many indie developers do this. Once a key is sold, it stays valid. Refunds are still possible — you send sats back via BTCPay; the key still works but the customer agreed to stop using it.</li>
|
<li><strong>Don’t support revocation at all.</strong> Many indie developers do this. Once a key is sold, it stays valid. Refunds are still possible — you send sats back via BTCPay; the key still works but the customer agreed to stop using it.</li>
|
||||||
<li><strong>Periodic online check.</strong> Your app fetches a small revocation list from your Keysat (or a CDN you point at it) once a week / month. Adds a "soft-online" requirement.</li>
|
<li><strong>Periodic online check.</strong> Your app fetches a small revocation list from your Keysat (or a CDN you point at it) once a week / month. Adds a "soft-online" requirement.</li>
|
||||||
<li><strong>Short-lived licenses with renewal.</strong> Issue 30-day licenses; the app fetches a fresh signed token before expiry. v0.2 will ship recurring renewals as a first-class flow.</li>
|
<li><strong>Short-lived licenses with renewal.</strong> Issue 30-day licenses; the app fetches a fresh signed token before expiry. Recurring renewals are first-class in v0.2 — define a policy with <code>is_recurring=true</code> + <code>renewal_period_days</code> and Keysat handles the cycle (invoice → settle → re-sign → webhook).</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="callout">
|
<div class="callout">
|
||||||
@@ -132,6 +145,16 @@
|
|||||||
<p><strong>You decide the policy.</strong> Keysat doesn’t force a particular revocation model. The default is no revocation — that’s the simplest, sovereign-by-default choice. If you need stronger guarantees, layer them on with the patterns above.</p>
|
<p><strong>You decide the policy.</strong> Keysat doesn’t force a particular revocation model. The default is no revocation — that’s the simplest, sovereign-by-default choice. If you need stronger guarantees, layer them on with the patterns above.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h2 id="operator-tiers">Operator tiers</h2>
|
||||||
|
<p>Keysat itself ships under a tiered self-license. The daemon runs out of the box at the free <strong>Creator</strong> tier with caps that are generous for a solo developer; paid <strong>Pro</strong> and <strong>Patron</strong> tiers lift caps and unlock recurring billing + the Zaprite payment gateway. Caps are enforced by the daemon at create-time only — existing resources are always grandfathered if you downgrade.</p>
|
||||||
|
<p>As of this writing, Creator caps at <strong>5 products / 5 policies per product / 10 active discount codes</strong>, and Pro / Patron are unlimited. The exact tier list, prices, entitlements, and any active launch-special discount are operator-controlled on the master Keysat and may change — the canonical sources are:</p>
|
||||||
|
<ul>
|
||||||
|
<li>The live tier cards on <a href="https://keysat.xyz#tiers">keysat.xyz</a> (rendered dynamically from the master Keysat).</li>
|
||||||
|
<li>The <a href="pricing.html">pricing page</a> on these docs for the human-readable breakdown.</li>
|
||||||
|
<li><code>GET https://licensing.keysat.xyz/v1/products/keysat/policies</code> for the machine-readable shape (entitlements, marketing bullets, featured discount, etc.).</li>
|
||||||
|
<li>Your local daemon’s <code>GET /v1/admin/tier</code> for current tier + caps + usage from inside the admin context.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h2 id="next">Where to next</h2>
|
<h2 id="next">Where to next</h2>
|
||||||
<div class="next-grid">
|
<div class="next-grid">
|
||||||
<a class="next-card" href="install.html">
|
<a class="next-card" href="install.html">
|
||||||
@@ -158,6 +181,7 @@
|
|||||||
<a href="#products-policies">Products & policies</a>
|
<a href="#products-policies">Products & policies</a>
|
||||||
<a href="#discounts">Discount codes</a>
|
<a href="#discounts">Discount codes</a>
|
||||||
<a href="#revocation">Revocation strategy</a>
|
<a href="#revocation">Revocation strategy</a>
|
||||||
|
<a href="#operator-tiers">Operator tiers</a>
|
||||||
<a href="#next">Where to next</a>
|
<a href="#next">Where to next</a>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user