Fix doc drift; document no-enforce-mode and universal publish

Corrections surfaced by doc-auditor + start9-spec-checker:
- testing.md: api suite 47 -> 54
- payments.md: FK enforcement confirmed at db/mod.rs:29
- startos-packaging.md: publish.sh now ships a universal s9pk
- licensing-tiers.md: record enforce-mode retirement and Creator caps
Refresh Current state for the StartOS submission-blocker work.
This commit is contained in:
Keysat
2026-06-13 06:40:06 -05:00
parent 6d4efc8a33
commit 9c3ddc01e7
5 changed files with 61 additions and 20 deletions
+34 -11
View File
@@ -90,8 +90,10 @@ Operator-specific memories at `~/.claude/projects/-Users-macpro-Projects-licensi
## Open TODOs ## Open TODOs
- Extend `publish.sh` to build + upload aarch64 (arm builds fine; only x86 ships - Verify the universal multi-arch publish end-to-end: `publish.sh` now runs
today), or narrow the manifest's arch claim. `riscv` target unverified. `make universal` (one `keysat.s9pk`, both arches) instead of x86-only; the first
real publish must confirm the registry index lists both arches. `riscv` target
unverified (not in the manifest, so `make universal` excludes it).
- StartOS Community Registry submission criteria — Start9 hasn't published the - StartOS Community Registry submission criteria — Start9 hasn't published the
checklist; reach out directly when ready. checklist; reach out directly when ready.
- Registry icon doesn't render in the StartOS marketplace (see `guides/startos-packaging.md`). - Registry icon doesn't render in the StartOS marketplace (see `guides/startos-packaging.md`).
@@ -105,15 +107,34 @@ Operator-specific memories at `~/.claude/projects/-Users-macpro-Projects-licensi
- **Live**: server `immense-voyage.local` runs daemon `0.2.0:54` (migrations - **Live**: server `immense-voyage.local` runs daemon `0.2.0:54` (migrations
00200022). Registry `registry.keysat.xyz` publishes `:54`; four SDKs published; 00200022). Registry `registry.keysat.xyz` publishes `:54`; four SDKs published;
`keysat.xyz` + `docs.keysat.xyz` deployed. **Prod is still `:54` — this `keysat.xyz` + `docs.keysat.xyz` deployed. **Prod is still `:54` — the prior
session's two P1 fixes are committed to source but NOT yet built/installed/ session's two P1 fixes are committed to source but NOT yet built/installed/
published. Next release builds `:55`.** published. Next release builds `:55`.**
- **This session (UNCOMMITTED across 4 repos; docs + StartOS packaging, no daemon
logic changed)** — doc-auditor + start9-spec-checker + 2 reviewer passes, all
approved/no blockers; `tsc` + `bash -n` clean. By repo:
- *root* (Gitea): `testing.md` api 47→54; `payments.md` FK confirmed
(`db/mod.rs:29`); `startos-packaging.md` + this block updated for universal
publish; `licensing-tiers.md` gained the "no enforce mode / Creator caps" note.
- *keysat-docs* (Gitea): `integrate.html` phantom `GET /v1/licenses/{id}/status`
→ real `POST /v1/validate` w/ `key`. **Needs `deploy-sites.sh docs` to go live.**
- *keysat daemon* (GitHub+gitea): new `instructions.md` (Start9-required);
manifest `packageRepo` + `docsUrls[1]` dead-link fixes; `v0.2.0.ts` stale-header
removed; `activateLicense.ts`/`showCredentials.ts` enforce-mode drift cleaned
(enforce retired — `self_license.rs:15`).
- *go SDK* (Gitea): README v0.1→v0.2.
- *operator-local* `~/.keysat/publish.sh` (gitignored, NOT committed): x86-only →
`make universal` (one `keysat.s9pk`, both arches). **Pending a verification build.**
All 4 StartOS submission blockers now addressed. Left for operator decision:
`integrate.html` BTCPay-only prereq/refund copy (no Zaprite mention). Commit =
4 per-repo commits (root, keysat-docs, go SDK are Gitea-only; daemon is
GitHub+gitea — also `git push gitea main`).
- **`:52`/`:53` = multi-provider/merchant-profile model**: data model + backend - **`:52`/`:53` = multi-provider/merchant-profile model**: data model + backend
resolution shipped and audited sound; resolution/CRUD query surface has tests. resolution shipped and audited sound; resolution/CRUD query surface has tests.
Both `:54` P0s (provider-injection test seam; Zaprite webhook-forgery re-confirm) Both `:54` P0s (provider-injection test seam; Zaprite webhook-forgery re-confirm)
remain fixed; live purchase + settle paths sound. remain fixed; live purchase + settle paths sound.
- **Done this session (source only, awaiting `:55`)** — the two open P1s: - **Unshipped source work (awaiting `:55`)** — two P1s from the prior session:
1. **Settle-amount tripwire.** `get_invoice_status` now returns 1. **Settle-amount tripwire.** `get_invoice_status` now returns
`ProviderInvoiceSnapshot { status, amount }`; `audit_settle_amount` (shared by `ProviderInvoiceSnapshot { status, amount }`; `audit_settle_amount` (shared by
webhook + reconcile issue paths) WARNs + writes an `invoice.amount_mismatch` webhook + reconcile issue paths) WARNs + writes an `invoice.amount_mismatch`
@@ -150,19 +171,21 @@ Operator-specific memories at `~/.claude/projects/-Users-macpro-Projects-licensi
errors return plain-text not JSON (breaks SDK `JSON.parse`); product `slug` has errors return plain-text not JSON (breaks SDK `JSON.parse`); product `slug` has
no validation (empty/300-char/meta chars stored); `GET /v1/admin/products` no validation (empty/300-char/meta chars stored); `GET /v1/admin/products`
returns 405 though OpenAPI documents it; dep advisories (`sqlx`→≥0.8.1 returns 405 though OpenAPI documents it; dep advisories (`sqlx`→≥0.8.1
RUSTSEC-2024-0363, `rustls-webpki`→≥0.103.12); **4 StartOS submission blockers** RUSTSEC-2024-0363, `rustls-webpki`→≥0.103.12); **4 StartOS submission blockers**
missing `instructions.md`, dead `packageRepo` (`…/keysat-startos``…/keysat`) + (spec-checker-verified) all addressed and staged, pre-build — manifest
`docsUrls` (`/docs/``/licensing-service/docs/`) manifest links, aarch64 `packageRepo` (`…/keysat-startos``…/keysat`) and `docsUrls[1]`
declared-but-not-shipped; no CI + fmt/clippy/prettier unenforced. (`docs/INTEGRATION.md``KEYSAT_INTEGRATION.md`, the real repo-root file) fixed;
`instructions.md` written (reviewer + doc-auditor signed off); aarch64 now shipped
via `publish.sh` `make universal` (one s9pk, both arches — pending a verification
build); no CI + fmt/clippy/prettier unenforced.
- **Deferred (P3+ — bulk or later decision)**: `/v1/purchase` 400 vs - **Deferred (P3+ — bulk or later decision)**: `/v1/purchase` 400 vs
`/v1/btcpay/webhook` 503 for the same no-provider cause; undocumented required `/v1/btcpay/webhook` 503 for the same no-provider cause; undocumented required
`kind` on discount-codes; field-naming drift (`license_id`/`id`, machines `key` `kind` on discount-codes; field-naming drift (`license_id`/`id`, machines `key`
vs `license_key`, `redeem`/`purchase` `product` vs `validate` `product_slug`); vs `license_key`, `redeem`/`purchase` `product` vs `validate` `product_slug`);
migration self-heal `_sqlx_migrations` allowlist foot-gun; 2 KB unauth Zaprite migration self-heal `_sqlx_migrations` allowlist foot-gun; 2 KB unauth Zaprite
payload WARN-log; outbound-webhook SSRF (operator-only); stale payload WARN-log; outbound-webhook SSRF (operator-only); re-register the master
`versions/v0.2.0.ts:3-4` "NOT YET WIRED" comment; re-register the master Zaprite Zaprite webhook at the path-keyed URL; registry icon non-render (known platform limit);
webhook at the path-keyed URL; registry icon non-render (known platform limit);
optional fmt/prettier standalone commit. optional fmt/prettier standalone commit.
- **Tests/build**: `cargo check` clean (1 intentional deprecation warning); full - **Tests/build**: `cargo check` clean (1 intentional deprecation warning); full
+11
View File
@@ -12,6 +12,17 @@ paths:
The daemon licenses **itself** via its own licensing scheme — the operator runs a The daemon licenses **itself** via its own licensing scheme — the operator runs a
master Keysat that issues the license this instance validates. master Keysat that issues the license this instance validates.
## Always boots — no enforce mode
Enforce mode was **retired**: there is no `KEYSAT_LICENSE_ENFORCE` build flag and
`check_at_boot` (`license_self.rs`) always returns `Ok`. A missing/invalid
self-license logs a warning and the daemon runs at the free **Creator** tier
(`Tier::Unlicensed`, surfaced as "Creator" in the admin UI) with Creator caps —
**5 products, 5 policies per product, 10 active discount codes**. Activating a
license lifts those caps and unlocks `recurring_billing` + `zaprite_payments`.
Treat any "marketplace build refuses to start without a license" wording in code
comments or copy as stale.
## Live entitlements ## Live entitlements
Tier gates must read **LIVE** entitlements from `licenses.entitlements` (refreshed Tier gates must read **LIVE** entitlements from `licenses.entitlements` (refreshed
+5 -3
View File
@@ -111,9 +111,11 @@ response carries no parseable positive amount. Regression tests in `tests/api.rs
`get_merchant_profile_for_product` bug; prefer a subquery or qualify columns. `get_merchant_profile_for_product` bug; prefer a subquery or qualify columns.
`tests/api.rs::merchant_profile_provider_resolution_queries_round_trip` exercises `tests/api.rs::merchant_profile_provider_resolution_queries_round_trip` exercises
the resolution surface — keep it green when touching these queries. the resolution surface — keep it green when touching these queries.
- FK enforcement is **not** guaranteed at runtime: `PRAGMA foreign_keys = ON` in a - FK enforcement is **ON**: the sqlx pool sets `.foreign_keys(true)` per
migration is connection-scoped. Confirm the pool sets it per-connection before connection (`db/mod.rs:29`), so cascade/constraint behavior is safe to rely on
relying on cascade/constraint behavior (e.g. deleting a profile with products). (e.g. deleting a profile with products). A bare `PRAGMA foreign_keys = ON` in a
migration would be connection-scoped and is not what guarantees this — the pool
option is.
- Known: after a `:52`+ install the master Zaprite webhook still points at the - Known: after a `:52`+ install the master Zaprite webhook still points at the
legacy URL — works via back-compat, needs re-register for per-provider isolation. legacy URL — works via back-compat, needs re-register for per-provider isolation.
- `unlimited_merchant_profiles` entitlement gates profile count (Creator = 1, - `unlimited_merchant_profiles` entitlement gates profile count (Creator = 1,
+8 -5
View File
@@ -38,7 +38,7 @@ silently no-ops an install whose version equals what's already installed. Run
## Release (operator-local scripts, in `~/.keysat/`, gitignored) ## Release (operator-local scripts, in `~/.keysat/`, gitignored)
``` ```
~/.keysat/publish.sh # version-gate → make x86 → FileBrowser upload → registry register → GitHub mirror ~/.keysat/publish.sh # version-gate → make universal → FileBrowser upload → registry register → GitHub mirror
~/.keysat/deploy-sites.sh landing docs # push static sites; accepts: landing | docs | registry-landing ~/.keysat/deploy-sites.sh landing docs # push static sites; accepts: landing | docs | registry-landing
``` ```
@@ -49,10 +49,13 @@ Credentials: `~/.keysat/filebrowser.env` (`chmod 600`); env `KEYSAT_FB_USER`,
## Arch & manifest ## Arch & manifest
- Both `x86_64` and `aarch64` build cleanly (arm verified 2026-06-12). The - Both `x86_64` and `aarch64` build cleanly (arm verified 2026-06-12). `publish.sh`
manifest declares `['x86_64', 'aarch64']` but `publish.sh` only uploads x86_64 — now builds a single universal `keysat.s9pk` via `make universal` (both arches in
**don't claim multi-arch as shipped** until publish handles both. (`riscv` target one package — the registry holds one s9pk per version, so a per-arch split won't
exists, unverified.) work), matching the manifest's `['x86_64', 'aarch64']` claim. **Pending a
verification build**: the combined universal pack hasn't been run end-to-end yet;
confirm the registry index lists both arches on the first publish. (`riscv` target
exists, unverified; not in the manifest, so `make universal` excludes it.)
- Manifest `license` is `LicenseRef-Keysat-1.0`, matching the package-root - Manifest `license` is `LicenseRef-Keysat-1.0`, matching the package-root
`LICENSE` SPDX id. `LICENSE` SPDX id.
+3 -1
View File
@@ -54,7 +54,9 @@ webhook settle-confirmation guard (see [payments](payments.md)).
The 3 `:52`-era known-failing tests are **resolved**: the two `paid_purchase_*` The 3 `:52`-era known-failing tests are **resolved**: the two `paid_purchase_*`
greened via the seam, the dead `payment_provider_preference_round_trip` deleted. greened via the seam, the dead `payment_provider_preference_round_trip` deleted.
api suite is now **47 pass / 0 fail**; all other suites green. api suite is now **54 pass / 0 fail** (47 after the `:52` fixes, plus the
post-`:54` scoped-key role-boundary and settle-tripwire tests); all other
suites green.
## Cross-language wire-format tests ## Cross-language wire-format tests