Add StartOS instructions.md; fix manifest links; clear retired-enforce-mode drift
- instructions.md: new, required for Start9 community-registry submission - manifest: fix dead packageRepo and docsUrls links - versions/v0.2.0.ts: drop stale 'NOT YET WIRED' header - actions: remove retired enforce-mode references; showLicenseStatus no longer reads a nonexistent 'mode' field; relabel the Creator (free) tier
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
# Keysat Licensing — Instructions
|
||||
|
||||
Keysat is a Bitcoin-native, self-hosted licensing service for software
|
||||
creators. You run your own instance, hold your own signing key, and issue
|
||||
Ed25519-signed license keys that your software verifies offline. There is no
|
||||
central authority and no shared database.
|
||||
|
||||
## Before you start
|
||||
|
||||
- **BTCPay Server is required.** Install and start BTCPay Server first — Keysat
|
||||
uses it to take Bitcoin/Lightning payments and confirm settlement. StartOS
|
||||
lists this dependency before it lets you install Keysat.
|
||||
- **A clearnet domain is recommended if you sell to the public**, so buyers
|
||||
anywhere can reach your checkout. LAN/Tor-only works for testing.
|
||||
- **Zaprite is optional** (adds card payments). You connect it later from inside
|
||||
the admin web UI; nothing to do up front.
|
||||
|
||||
## First-time setup
|
||||
|
||||
1. **Get your admin API key.** Open the **Actions** tab and run
|
||||
**Show admin API key**. Copy it — you sign into the admin web UI with it the
|
||||
first time.
|
||||
2. **Open the admin dashboard.** Click **Launch UI** on the **Admin Web UI**
|
||||
interface and paste the admin API key to sign in.
|
||||
3. **(Recommended) Set a real password.** Run the **Set web UI password** action
|
||||
(Actions tab, minimum 12 characters). After this the login page shows a
|
||||
password field; the admin API key keeps working for automation.
|
||||
4. **Connect your payment provider.** In the admin web UI's Settings, use the
|
||||
one-click **Connect BTCPay** flow to authorize Keysat against your BTCPay
|
||||
Server. (Optionally connect Zaprite here too.)
|
||||
5. **Set your operator name** in the admin web UI — it appears on buyer-facing
|
||||
checkout and receipts.
|
||||
6. **Create what you sell.** Use **Create product** for each item, and
|
||||
optionally **Create policy** to set per-product defaults (duration, grace
|
||||
period, entitlements, seat cap, trial flag). A policy slugged `default` is the
|
||||
one the public purchase flow uses.
|
||||
|
||||
Activation is optional. Keysat runs out of the box at the free **Creator** tier
|
||||
(up to 5 products, 5 policies per product, and 10 active discount codes).
|
||||
Activating a license lifts those caps and unlocks recurring billing and Zaprite
|
||||
(card) payments. To activate, get a key at
|
||||
[registry.keysat.xyz](https://registry.keysat.xyz), run the **Activate Keysat
|
||||
license** action, and confirm with **Show Keysat license status**.
|
||||
|
||||
## Selling licenses
|
||||
|
||||
Share your **Licensing API** URL with buyers and bake it into your software as
|
||||
the validation endpoint. Buyers call `POST /v1/purchase`, pay via BTCPay, and
|
||||
Keysat issues a signed license key. Your software validates keys against
|
||||
`POST /v1/validate` — including revocation checks, which return
|
||||
`ok: false` with `reason: "revoked"`.
|
||||
|
||||
The same admin web UI covers manual license issuance (comps, press, trials),
|
||||
suspension/unsuspension, revocation, machine management, discount codes,
|
||||
outbound webhooks, and the audit log.
|
||||
|
||||
## Interfaces and exposure
|
||||
|
||||
- **Licensing API** (`/`) — public-facing. This is the URL you share with
|
||||
customers and bake into your builds.
|
||||
- **Admin Web UI** (`/admin`) — your dashboard. Restrict this interface to LAN or
|
||||
Tor only; the public internet does not need to reach it.
|
||||
- **BTCPay webhook endpoint** (`/btcpay`) — registered with BTCPay automatically
|
||||
during the Connect BTCPay flow. Not for human use.
|
||||
|
||||
## Backups and uninstalling
|
||||
|
||||
Your data volume holds the SQLite database — which contains your server signing
|
||||
key and every license record — and StartOS backs it up automatically. Your
|
||||
self-license at `/data/keysat-license.txt` is included in the backup and
|
||||
survives upgrades and reinstalls.
|
||||
|
||||
**Uninstalling deletes your signing key and all license records.** Once it is
|
||||
gone, previously issued license keys no longer validate against this server. Back
|
||||
up first if you plan to reinstall.
|
||||
|
||||
## Recovery
|
||||
|
||||
- **Locked out of the admin UI?** Run **Set web UI password** to set a new one,
|
||||
or **Show admin API key** to sign in with the key.
|
||||
- **Lost your Keysat license?** Re-run **Activate Keysat license** with your key.
|
||||
|
||||
## More
|
||||
|
||||
Full developer and integration documentation lives in the upstream repository
|
||||
(`README.md` and `KEYSAT_INTEGRATION.md`) and at
|
||||
[keysat.xyz](https://keysat.xyz).
|
||||
@@ -6,12 +6,10 @@
|
||||
// writes it to /data/keysat-license.txt, and swaps its runtime tier
|
||||
// to Licensed without a restart.
|
||||
//
|
||||
// In permissive builds (the default for local `make x86`) the daemon
|
||||
// will start regardless and this action just records the tier. In
|
||||
// enforce builds (compiled with KEYSAT_LICENSE_ENFORCE=1, used for
|
||||
// the marketplace .s9pk) the daemon refuses to start without a valid
|
||||
// license, and this action is the bootstrap path: install Keysat,
|
||||
// run this action with your activation key, then start the service.
|
||||
// The daemon always boots regardless of license state (enforce mode was
|
||||
// retired — see license_self.rs::check_at_boot). With no valid self-license
|
||||
// it runs at the free Creator tier with Creator caps; this action records
|
||||
// the license and lifts those caps without a restart.
|
||||
|
||||
import { sdk } from '../sdk'
|
||||
import { store } from '../fileModels/store'
|
||||
@@ -36,9 +34,9 @@ export const activateLicense = sdk.Action.withInput(
|
||||
async () => ({
|
||||
name: 'Activate Keysat license',
|
||||
description:
|
||||
'Activate this Keysat install. Required for marketplace builds; ' +
|
||||
'optional but recommended for source-built dev installs (signals support, ' +
|
||||
'and lets the admin UI show your tier).',
|
||||
'Activate this Keysat install. Optional — Keysat runs at the free ' +
|
||||
'Creator tier without it. Activating lifts the Creator caps, unlocks ' +
|
||||
'recurring billing + Zaprite payments, and shows your tier in the admin UI.',
|
||||
warning: null,
|
||||
allowedStatuses: 'only-running',
|
||||
group: 'License',
|
||||
@@ -80,7 +78,6 @@ export const activateLicense = sdk.Action.withInput(
|
||||
product_id?: string
|
||||
expires_at?: number
|
||||
entitlements?: string[]
|
||||
mode: string
|
||||
}
|
||||
message: string
|
||||
}
|
||||
@@ -132,7 +129,6 @@ export const showLicenseStatus = sdk.Action.withoutInput(
|
||||
expires_at?: number
|
||||
entitlements?: string[]
|
||||
reason?: string
|
||||
mode: string
|
||||
}
|
||||
|
||||
if (j.tier === 'licensed') {
|
||||
@@ -146,20 +142,19 @@ export const showLicenseStatus = sdk.Action.withoutInput(
|
||||
message:
|
||||
`License id: ${j.license_id}\n` +
|
||||
`Expires: ${exp}\n` +
|
||||
`Entitlements: ${ents}\n` +
|
||||
`Build mode: ${j.mode}`,
|
||||
`Entitlements: ${ents}`,
|
||||
result: null,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
version: '1',
|
||||
title: 'Unlicensed',
|
||||
title: 'Creator (free tier)',
|
||||
message:
|
||||
`Reason: ${j.reason || 'no license configured'}\n` +
|
||||
`Build mode: ${j.mode}\n\n` +
|
||||
(j.mode === 'enforce'
|
||||
? 'This is a marketplace build that requires a valid license to run. Use the "Activate Keysat license" action to bootstrap.'
|
||||
: 'This is a permissive (dev) build. The daemon will keep running. Activate a license to see your tier reflected here.'),
|
||||
`This install is running at the free Creator tier.\n` +
|
||||
`Reason: ${j.reason || 'no license configured'}\n\n` +
|
||||
`Creator caps: 5 products, 5 policies per product, 10 active ` +
|
||||
`discount codes. Activating a license lifts these caps and unlocks ` +
|
||||
`recurring billing + Zaprite payments (the "Activate Keysat license" action).`,
|
||||
result: null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Action: reveal the auto-generated admin API key.
|
||||
//
|
||||
// The operator rarely needs this — every other action in StartOS already
|
||||
// carries the key for them — but it's useful if they want to script against
|
||||
// the admin HTTP API directly.
|
||||
// The operator needs this on first install to sign into the admin web UI
|
||||
// (until they set a web UI password); afterward it's mainly for scripting
|
||||
// the admin HTTP API directly, since every other StartOS action already
|
||||
// carries the key for them.
|
||||
//
|
||||
// The BTCPay webhook secret used to live in the StartOS store; it now lives
|
||||
// inside the daemon's own SQLite database, generated automatically during
|
||||
@@ -35,9 +36,11 @@ export const showCredentials = sdk.Action.withoutInput(
|
||||
version: '1',
|
||||
title: 'Admin API key',
|
||||
message:
|
||||
`Used as 'Authorization: Bearer <key>' against /v1/admin/*. All ` +
|
||||
`StartOS actions already supply this for you — only export it if ` +
|
||||
`you intend to script against the admin API from outside the box.`,
|
||||
`This is your admin API key — the 'Authorization: Bearer <key>' ` +
|
||||
`credential for /v1/admin/*. Use it to sign into the admin web UI on ` +
|
||||
`first install (until you set a web UI password). Every StartOS action ` +
|
||||
`already supplies it for you, so you only need to export it to script ` +
|
||||
`the admin API yourself.`,
|
||||
result: {
|
||||
type: 'single',
|
||||
value: storeData.admin_api_key,
|
||||
|
||||
@@ -15,13 +15,15 @@ export const manifest = setupManifest({
|
||||
id: 'keysat',
|
||||
title: 'Keysat Licensing',
|
||||
license: 'LicenseRef-Keysat-1.0',
|
||||
packageRepo: 'https://github.com/keysat-xyz/keysat-startos',
|
||||
// packageRepo (the s9pk wrapper source) and upstreamRepo (the daemon source)
|
||||
// are the same URL: the StartOS wrapper and the Rust daemon share one monorepo.
|
||||
packageRepo: 'https://github.com/keysat-xyz/keysat',
|
||||
upstreamRepo: 'https://github.com/keysat-xyz/keysat',
|
||||
marketingUrl: 'https://keysat.xyz',
|
||||
donationUrl: null,
|
||||
docsUrls: [
|
||||
'https://github.com/keysat-xyz/keysat/blob/main/README.md',
|
||||
'https://github.com/keysat-xyz/keysat/blob/main/docs/INTEGRATION.md',
|
||||
'https://github.com/keysat-xyz/keysat/blob/main/KEYSAT_INTEGRATION.md',
|
||||
],
|
||||
description: { short, long },
|
||||
// A single data volume holds the SQLite database (which in turn holds the
|
||||
|
||||
@@ -1,27 +1,8 @@
|
||||
// Draft of the v0.2.0 milestone version entry.
|
||||
//
|
||||
// NOT YET WIRED INTO `versions/index.ts` — this file sits ready to
|
||||
// use when we cut v0.2.0:0 from the alpha-iteration line. To
|
||||
// activate:
|
||||
// 1. In `versions/index.ts`:
|
||||
// import { v0_2_0 } from './v0.2.0'
|
||||
// export const versions = VersionGraph.of({
|
||||
// current: v0_2_0,
|
||||
// other: [v0_1_0], // ← so installs on 0.1.0:N can upgrade
|
||||
// })
|
||||
// 2. Build the .s9pk (`make x86`).
|
||||
// 3. Publish via `~/.keysat/publish.sh` (the version-changed gate
|
||||
// will fire because `0.2.0:0` differs from the recorded
|
||||
// `0.1.0:N`).
|
||||
//
|
||||
// Why this draft exists separately:
|
||||
// - The cut is an irreversible release decision for already-installed
|
||||
// operators (downgrade paths exist in StartOS but they're sticky).
|
||||
// - Wiring it in changes how StartOS computes the upgrade dialog
|
||||
// shown to operators on registry refresh — best to QA the
|
||||
// release-notes content in this file before flipping the switch.
|
||||
// - Lets us write the v0.2.0 release notes carefully and then ship
|
||||
// them all at once, rather than amending mid-build.
|
||||
// The v0.2.0 milestone version entry — the current, active version on
|
||||
// the v0.2 line. Wired into `versions/index.ts` as `current: v0_2_0`,
|
||||
// with `v0_1_0` in `other` so installs on 0.1.0:N can upgrade. Routine
|
||||
// wrapper updates bump the downstream revision here (`0.2.0:N`) before
|
||||
// each build/publish; see startos-packaging.md.
|
||||
//
|
||||
// Version-string format reminder: ExVer is `<upstream>:<downstream>`.
|
||||
// The `<upstream>` bump from 0.1.0 → 0.2.0 marks the milestone; the
|
||||
|
||||
Reference in New Issue
Block a user