README: add developer Quick-start; refresh stale sections

- New "Quick start" block at top — pitch line matches the keysat.xyz
  hero, links to docs.keysat.xyz, and lists the four SDK install
  commands (TS / Rust / Python / Go) so a GitHub visitor sees the
  install path before the StartOS-package internals.
- Tagline updated to drop "Bitcoin-native" in favor of "payment
  channels you control" (matches the landing-page de-emphasis).
- First-run flow step 6: drop the "default slug is canonical" myth.
  Multi-tier ladders are first-class.
- Actions list: drop npub from license search (UI surfaces only
  email + invoice id; backend supports npub but the purchase flow
  hasn't shipped buyer npub capture yet).
- Limitations section: drop "no recurring subscriptions" and "no
  tier upgrades" — both shipped in v0.2. Add Zaprite-pending caveat.
- YAML quick-reference: expand the features block to reflect the
  current daemon (multi-currency, marketing bullets, hidden
  entitlements, featured discounts, multi-policy scope, recurring
  subs, tier upgrades, webhook DLQ, scoped API keys, OpenAPI spec,
  Creator/Pro/Patron self-licensing tiers). Add SDK list.
This commit is contained in:
Grant
2026-05-11 20:04:05 -05:00
parent aaf8bddfe4
commit 9e772fdd4c
+61 -17
View File
@@ -5,9 +5,37 @@
<h1 align="center">Keysat</h1>
<p align="center">
Bitcoin-native self-hosted licensing service for software creators. Runs on Start9.
Self-hosted licensing server. Sell software on payment channels you control,
verify licenses offline, keep the keys + customer list on your hardware. Runs on Start9.
</p>
<p align="center">
<a href="https://keysat.xyz">keysat.xyz</a> &middot;
<a href="https://docs.keysat.xyz">docs.keysat.xyz</a> &middot;
<a href="https://github.com/keysat-xyz/keysat/releases">Releases</a>
</p>
---
## Quick start
**Operator (install Keysat on your Start9):** add `registry.keysat.xyz` to your StartOS marketplace and install. Sideload the `.s9pk` from [GitHub releases](https://github.com/keysat-xyz/keysat/releases/latest) if you prefer. See [Install &amp; setup](https://docs.keysat.xyz/install.html) for the full walkthrough.
**Developer (verify a license in your software):** four official SDKs ship today, all wire-compatible against the same cross-check fixtures in [`licensing-service/tests/crosscheck/`](licensing-service/tests/crosscheck/).
| Language | Install |
|---|---|
| TypeScript | `npm install @keysat/licensing-client` |
| Rust | `cargo add keysat-licensing-client` |
| Python | `pip install keysat-licensing-client` |
| Go | `go get github.com/keysat-xyz/keysat-client-go` |
See [Integrate the SDK](https://docs.keysat.xyz/integrate.html) for the five-line verifier pattern.
**Operator agent / automation:** the daemon exposes an OpenAPI 3.1 spec, scoped API keys with role-based access, and outbound webhooks. See [Agent integration](https://docs.keysat.xyz/agent.html).
---
> **About this README.** Keysat is a from-scratch service authored for
> StartOS — there is no upstream project to differ from. The canonical
> implementation is this package and the Rust daemon it wraps
@@ -87,10 +115,12 @@ mandatory.
4. Set your **operator name** (shown on the public homepage and in
buyer-facing receipts).
5. Create one or more **products** — each represents something you sell.
6. Create at least one **policy** per product. The policy slugged
`default` is consumed by the standard public purchase flow; other
slugs are used for manual issuance. Policies define duration, grace
period, seat cap, entitlements, trial flag, and price overrides.
6. Create at least one **policy** per product. Multi-tier ladders
(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. Policies define duration, grace period, seat
cap, entitlements, recurring cadence, trial flag, price overrides,
marketing bullets, and per-entitlement hide-on-buy-page toggles.
7. Optionally create **discount / referral / free-license codes** (see
`Create discount code` action).
8. Share the public service URL with buyers.
@@ -146,7 +176,7 @@ Grouped as displayed in the dashboard.
**Licenses**
- *Issue license manually* — comp / press / grandfathered keys.
- *Search licenses* — by email, Nostr npub, or BTCPay invoice id.
- *Search licenses* — by email or BTCPay invoice id.
- *Suspend license* — reversible lockout.
- *Unsuspend license*.
- *Revoke license* — terminal kill.
@@ -192,15 +222,13 @@ the container automatically.
## Limitations and Differences
Known v0.1 limitations:
Known current limitations:
- **No buyer self-service portal.** Buyers cannot log in to view their licenses, transfer to a new machine, or recover a lost key without contacting the operator. Use *Search licenses* to recover.
- **No recurring subscriptions.** Time-limited licenses expire and require a manual repurchase. BTCPay supports recurring billing but Keysat does not yet model auto-renewal.
- **No license tier upgrade in place.** A buyer who got Standard cannot be upgraded to Pro on the existing key — they need a new key.
- **No bulk / volume licensing.** "Buy 10 keys at once with discount" is not built in.
- **No in-dashboard list views.** Operators query large datasets via the admin API key rather than a paginated UI.
- **Webhook delivery retries are bounded.** A subscriber down past the retry window will miss events. BTCPay invoice reconciliation runs as a background poll so dropped *payment* webhooks are recovered.
- **Hardware fingerprinting is client-supplied.** Keysat does not derive fingerprints itself; the buyer-side SDK passes whatever the integrator chose.
- **Buyer self-service recovery is by-design minimal.** Buyers can re-derive a lost license at `/recover` using (invoice id, buyer email). They cannot transfer between machines without contacting the operator (use *Free a machine seat* in the admin / agent API).
- **No bulk / volume licensing UI.** "Buy 10 keys at once with discount" is not built into the buy page. Operators can issue N comp licenses via the admin API in a loop.
- **Webhook delivery retries are bounded.** A subscriber down past the 10-attempt retry window lands in the dead-letter queue (visible in admin UI → Webhooks → Failed). BTCPay invoice reconciliation runs as a background poll so dropped *payment* webhooks are recovered.
- **Hardware fingerprinting is client-supplied.** Keysat does not derive fingerprints itself; the buyer-side SDK passes whatever the integrator chose. The fingerprint is bound on first activate and enforced thereafter.
- **Card payments not shipped.** The Zaprite payment provider is in design for v0.3 — operators on Pro / Patron will get a card-payment option alongside BTCPay. Until then, payments are BTC / Lightning only.
## What Is Unchanged from Upstream
@@ -267,7 +295,7 @@ firstRun:
severity: critical
runs: configureBtcpay
features:
paymentRail: btcpay-server
paymentRail: btcpay-server # zaprite planned for v0.3 (card payments)
signing: ed25519
offlineVerification: true
multiSeat: true
@@ -275,8 +303,24 @@ features:
expiry: true
gracePeriod: true
entitlements: true
discountCodes: [percent, fixed_sats, free_license]
entitlementsCatalog: per-product # typed slugs with display names + descriptions
hiddenEntitlements: per-policy # license-granted but hidden from buy page
marketingBullets: per-policy # operator-authored ✓ items on tier cards
multiCurrency: [SAT, USD, EUR] # auto-converted at invoice creation
discountCodes: [percent, fixed_sats, set_price, free_license]
featuredDiscounts: true # launch-special, auto-applies on the buy page
multiPolicyDiscountScope: true # one code can apply to N policies
recurringSubscriptions: true # auto-renew with trials + grace
tierUpgrades: true # in-place tier upgrade with proration
outboundWebhooks: true
webhookDlq: true # failed deliveries retryable from admin UI
auditLog: true
selfLicensingTier: stub-v0.1
scopedApiKeys: [read-only, license-issuer, support, full-admin]
openapiSpec: /v1/openapi.json
selfLicensingTier: [Creator, Pro, Patron]
sdks:
- typescript: "@keysat/licensing-client (npm)"
- rust: "keysat-licensing-client (crates.io)"
- python: "keysat-licensing-client (PyPI)"
- go: "github.com/keysat-xyz/keysat-client-go"
```