v0.1.0:24 — Keysat licensing service end-to-end

Daemon, StartOS wrapper, admin SPA, public buy/thank-you pages,
discount codes, free-license redemption, Apply-discount UX,
self-licensing, and v0.1.0 release notes.
This commit is contained in:
Grant
2026-05-07 10:33:39 -05:00
parent 432250bffc
commit 6ac118ae70
90 changed files with 14896 additions and 524 deletions
+54 -14
View File
@@ -1,22 +1,30 @@
// Network interfaces exposed by the service.
//
// Two logical interfaces:
// - `api` — the REST API that buyers (purchase flow) and licensed
// software (validate flow) hit. Must be reachable from
// outside the StartOS host if you're selling to the public,
// so we expose it on LAN + Tor + optional clearnet.
// - `webhook` — the BTCPay webhook landing endpoint. Only BTCPay needs to
// reach it; same-host LAN is sufficient.
// Three logical interfaces, all sharing the same internal port (8080).
// The Rust daemon routes by path, and StartOS uses the interface
// concept for *access surfaces* and *display grouping*.
//
// In practice both live on the same HTTP port (8080) because the service
// routes by path. StartOS's interface concept is about *access surfaces*
// and *display grouping*, not separate ports.
// - `api` — the REST API that buyers (purchase flow) and licensed
// software (validate flow) hit. Must be reachable from
// outside the host if you're selling to the public.
// - `webhook` — the BTCPay webhook landing endpoint. Only BTCPay needs
// to reach it; same-host LAN is sufficient.
// - `admin-ui` — the embedded admin web UI (rust-embed at /admin/).
// type: 'ui' so StartOS surfaces a "Launch UI" button.
// Operator should restrict this interface's exposure
// to LAN-only or Tor-only — the public clearnet
// doesn't need to see it. (For v0.2 follow-up: split
// onto a separate port so it can be fully isolated
// from the public api.)
import { sdk } from './sdk'
export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
const apiMulti = sdk.MultiHost.of(effects, 'api-multi')
await apiMulti.bindPort(8080, { protocol: 'http', preferredExternalPort: 443 })
const multi = sdk.MultiHost.of(effects, 'api-multi')
const origin = await multi.bindPort(8080, {
protocol: 'http',
preferredExternalPort: 443,
})
const api = sdk.createInterface(effects, {
name: 'Licensing API',
@@ -26,7 +34,6 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
'the URL you share with customers and bake into your own software ' +
'builds as the licensing endpoint.',
type: 'api',
hasPrimary: true,
masked: false,
schemeOverride: null,
username: null,
@@ -34,5 +41,38 @@ export const setInterfaces = sdk.setupInterfaces(async ({ effects }) => {
query: {},
})
return [await api.export([apiMulti])]
const webhook = sdk.createInterface(effects, {
name: 'BTCPay webhook endpoint',
id: 'webhook',
description:
'The landing URL for BTCPay webhook callbacks. Not intended for ' +
'human use — Keysat registers this URL with BTCPay automatically ' +
'during the one-click "Connect BTCPay" flow.',
type: 'api',
masked: false,
schemeOverride: null,
username: null,
path: '/btcpay',
query: {},
})
const adminUi = sdk.createInterface(effects, {
name: 'Admin Web UI',
id: 'admin-ui',
description:
'Embedded admin dashboard — manage products, policies, discount ' +
'codes, licenses, machines, webhooks, and audit log without ' +
'leaving the browser. Login is gated by your Keysat admin API key. ' +
'Recommended: restrict this interface to LAN or Tor only; the ' +
'public clearnet does not need to reach the admin UI.',
type: 'ui',
masked: false,
schemeOverride: null,
username: null,
path: '/admin',
query: {},
})
const receipt = await origin.export([api, webhook, adminUi])
return [receipt]
})