Keysat Docs
Get started · Introduction

Welcome to Keysat.

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.

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.

These docs cover both ends:

Architecture

Keysat is the licensing layer sitting on top of your existing payments stack. Three boxes:

The key word is offline. Once a license is issued, your software does not need to phone home to verify it. The verification is a pure function of the license bytes and the public key. This is the same model used by signed JWTs, except wrapped in a small fixed-width format that’s comfortable to print on a receipt.

Why offline matters. Online license servers are a single point of failure for every customer who ever bought your software. With Keysat, if your Start9 disappears tomorrow, every previously-issued license still verifies. That’s sovereignty.

Products & policies

You declare two things in Keysat: products and policies.

A product 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 entitlements catalog — the typed list of feature slugs your software cares about, plus their display names and descriptions. Policies pick entitlements from this catalog.

A policy is a license template attached to a product. It specifies:

FieldMeaning
duration_secondsHow long the license is valid. 0 means perpetual.
grace_secondsExtra time after expiry before the verifier rejects.
max_machinesSeat cap. 0 means unlimited.
is_trialSets a TRIAL bit so your app can show a "trial" banner.
is_recurring + renewal_period_daysAuto-renew on a cycle (weekly / monthly / annual / custom). The daemon mints a fresh invoice + signed license per cycle.
entitlementsSubset of the product’s catalog this policy grants. Baked into the signed license.
metadata.marketing_bulletsOperator-authored ✓ items rendered on the buy-page tier card. Pure marketing copy — not enforced.
metadata.hidden_entitlementsSlugs 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.

A product can have one policy or many. 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 ?policy=<slug> URL hint, then the highlighted ("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 tier_rank in the API).

You can also attach private policies 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.

Discount codes

Four kinds:

KindWhat it does
percentBuyer appends ?code=FOUNDERS50 to the purchase URL; price drops by N%.
fixed_satsLike above, but a flat amount comes off. Denominated in the code’s discount_currency (sats / USD / EUR), so the same code can sit on top of multi-currency products.
set_priceOverrides 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.
free_licenseNo payment at all. Buyer redeems the code via POST /v1/redeem and gets a signed license back.

Codes can be capped at N uses, dated to expire, restricted to one product (and optionally to a subset of policies 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.

Codes can also be marked featured — a "launch special" mode. A featured code:

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.

Revocation strategy

This is the one piece of the architecture that requires a design decision from you.

Because verification is offline, a license that was once issued continues to verify forever — even if you mark it as revoked in the admin UI. The verifier in your app doesn’t know about your admin actions.

You have three options:

You decide the policy. 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.

Operator tiers

Keysat itself ships under a tiered self-license. The daemon runs out of the box at the free Creator tier with caps that are generous for a solo developer; paid Pro and Patron 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.

As of this writing, Creator caps at 5 products / 5 policies per product / 10 active discount codes, 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:

Where to next