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
|
// writes it to /data/keysat-license.txt, and swaps its runtime tier
|
||||||
// to Licensed without a restart.
|
// to Licensed without a restart.
|
||||||
//
|
//
|
||||||
// In permissive builds (the default for local `make x86`) the daemon
|
// The daemon always boots regardless of license state (enforce mode was
|
||||||
// will start regardless and this action just records the tier. In
|
// retired — see license_self.rs::check_at_boot). With no valid self-license
|
||||||
// enforce builds (compiled with KEYSAT_LICENSE_ENFORCE=1, used for
|
// it runs at the free Creator tier with Creator caps; this action records
|
||||||
// the marketplace .s9pk) the daemon refuses to start without a valid
|
// the license and lifts those caps without a restart.
|
||||||
// license, and this action is the bootstrap path: install Keysat,
|
|
||||||
// run this action with your activation key, then start the service.
|
|
||||||
|
|
||||||
import { sdk } from '../sdk'
|
import { sdk } from '../sdk'
|
||||||
import { store } from '../fileModels/store'
|
import { store } from '../fileModels/store'
|
||||||
@@ -36,9 +34,9 @@ export const activateLicense = sdk.Action.withInput(
|
|||||||
async () => ({
|
async () => ({
|
||||||
name: 'Activate Keysat license',
|
name: 'Activate Keysat license',
|
||||||
description:
|
description:
|
||||||
'Activate this Keysat install. Required for marketplace builds; ' +
|
'Activate this Keysat install. Optional — Keysat runs at the free ' +
|
||||||
'optional but recommended for source-built dev installs (signals support, ' +
|
'Creator tier without it. Activating lifts the Creator caps, unlocks ' +
|
||||||
'and lets the admin UI show your tier).',
|
'recurring billing + Zaprite payments, and shows your tier in the admin UI.',
|
||||||
warning: null,
|
warning: null,
|
||||||
allowedStatuses: 'only-running',
|
allowedStatuses: 'only-running',
|
||||||
group: 'License',
|
group: 'License',
|
||||||
@@ -80,7 +78,6 @@ export const activateLicense = sdk.Action.withInput(
|
|||||||
product_id?: string
|
product_id?: string
|
||||||
expires_at?: number
|
expires_at?: number
|
||||||
entitlements?: string[]
|
entitlements?: string[]
|
||||||
mode: string
|
|
||||||
}
|
}
|
||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
@@ -132,7 +129,6 @@ export const showLicenseStatus = sdk.Action.withoutInput(
|
|||||||
expires_at?: number
|
expires_at?: number
|
||||||
entitlements?: string[]
|
entitlements?: string[]
|
||||||
reason?: string
|
reason?: string
|
||||||
mode: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (j.tier === 'licensed') {
|
if (j.tier === 'licensed') {
|
||||||
@@ -146,20 +142,19 @@ export const showLicenseStatus = sdk.Action.withoutInput(
|
|||||||
message:
|
message:
|
||||||
`License id: ${j.license_id}\n` +
|
`License id: ${j.license_id}\n` +
|
||||||
`Expires: ${exp}\n` +
|
`Expires: ${exp}\n` +
|
||||||
`Entitlements: ${ents}\n` +
|
`Entitlements: ${ents}`,
|
||||||
`Build mode: ${j.mode}`,
|
|
||||||
result: null,
|
result: null,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
version: '1',
|
version: '1',
|
||||||
title: 'Unlicensed',
|
title: 'Creator (free tier)',
|
||||||
message:
|
message:
|
||||||
`Reason: ${j.reason || 'no license configured'}\n` +
|
`This install is running at the free Creator tier.\n` +
|
||||||
`Build mode: ${j.mode}\n\n` +
|
`Reason: ${j.reason || 'no license configured'}\n\n` +
|
||||||
(j.mode === 'enforce'
|
`Creator caps: 5 products, 5 policies per product, 10 active ` +
|
||||||
? 'This is a marketplace build that requires a valid license to run. Use the "Activate Keysat license" action to bootstrap.'
|
`discount codes. Activating a license lifts these caps and unlocks ` +
|
||||||
: 'This is a permissive (dev) build. The daemon will keep running. Activate a license to see your tier reflected here.'),
|
`recurring billing + Zaprite payments (the "Activate Keysat license" action).`,
|
||||||
result: null,
|
result: null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
// Action: reveal the auto-generated admin API key.
|
// Action: reveal the auto-generated admin API key.
|
||||||
//
|
//
|
||||||
// The operator rarely needs this — every other action in StartOS already
|
// The operator needs this on first install to sign into the admin web UI
|
||||||
// carries the key for them — but it's useful if they want to script against
|
// (until they set a web UI password); afterward it's mainly for scripting
|
||||||
// the admin HTTP API directly.
|
// 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
|
// The BTCPay webhook secret used to live in the StartOS store; it now lives
|
||||||
// inside the daemon's own SQLite database, generated automatically during
|
// inside the daemon's own SQLite database, generated automatically during
|
||||||
@@ -35,9 +36,11 @@ export const showCredentials = sdk.Action.withoutInput(
|
|||||||
version: '1',
|
version: '1',
|
||||||
title: 'Admin API key',
|
title: 'Admin API key',
|
||||||
message:
|
message:
|
||||||
`Used as 'Authorization: Bearer <key>' against /v1/admin/*. All ` +
|
`This is your admin API key — the 'Authorization: Bearer <key>' ` +
|
||||||
`StartOS actions already supply this for you — only export it if ` +
|
`credential for /v1/admin/*. Use it to sign into the admin web UI on ` +
|
||||||
`you intend to script against the admin API from outside the box.`,
|
`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: {
|
result: {
|
||||||
type: 'single',
|
type: 'single',
|
||||||
value: storeData.admin_api_key,
|
value: storeData.admin_api_key,
|
||||||
|
|||||||
@@ -15,13 +15,15 @@ export const manifest = setupManifest({
|
|||||||
id: 'keysat',
|
id: 'keysat',
|
||||||
title: 'Keysat Licensing',
|
title: 'Keysat Licensing',
|
||||||
license: 'LicenseRef-Keysat-1.0',
|
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',
|
upstreamRepo: 'https://github.com/keysat-xyz/keysat',
|
||||||
marketingUrl: 'https://keysat.xyz',
|
marketingUrl: 'https://keysat.xyz',
|
||||||
donationUrl: null,
|
donationUrl: null,
|
||||||
docsUrls: [
|
docsUrls: [
|
||||||
'https://github.com/keysat-xyz/keysat/blob/main/README.md',
|
'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 },
|
description: { short, long },
|
||||||
// A single data volume holds the SQLite database (which in turn holds the
|
// 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.
|
// 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`,
|
||||||
// NOT YET WIRED INTO `versions/index.ts` — this file sits ready to
|
// with `v0_1_0` in `other` so installs on 0.1.0:N can upgrade. Routine
|
||||||
// use when we cut v0.2.0:0 from the alpha-iteration line. To
|
// wrapper updates bump the downstream revision here (`0.2.0:N`) before
|
||||||
// activate:
|
// each build/publish; see startos-packaging.md.
|
||||||
// 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.
|
|
||||||
//
|
//
|
||||||
// Version-string format reminder: ExVer is `<upstream>:<downstream>`.
|
// Version-string format reminder: ExVer is `<upstream>:<downstream>`.
|
||||||
// The `<upstream>` bump from 0.1.0 → 0.2.0 marks the milestone; the
|
// The `<upstream>` bump from 0.1.0 → 0.2.0 marks the milestone; the
|
||||||
|
|||||||
Reference in New Issue
Block a user