diff --git a/AGENTS.md b/AGENTS.md index 26843a2..2af764a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -95,9 +95,11 @@ Operator-specific memories at `~/.claude/projects/-Users-macpro-Projects-keysat/ - `riscv` build target is unverified and not declared in the manifest; the wrapper `Makefile` now pins `ARCHES` to `x86 arm` so no target (even a bare `make`) attempts it. Revisit only if a riscv StartOS target appears. -- StartOS Community Registry submission — remaining gap is a `prepare.sh` for the clean-Debian - first build (plus the on-box manual verification); functional criteria otherwise pass. Detail - in ROADMAP. Submission criteria themselves still unpublished; reach out when ready. +- StartOS Community Registry submission — `prepare.sh` shipped (2026-06-18). Submission is + **email-based** (no PR, no form): mail `submissions@start9labs.com` a link to the public wrapper + repo; Start9 builds-from-source on a clean box → Community Beta → production-on-reply. Resolve two + unknowns with Start9 *before* submitting: (1) source-available `LicenseRef-Keysat-1.0` acceptability, + (2) whether the 0.4.x build still invokes `prepare.sh`. On-box manual verification still pending. Detail in ROADMAP. - Split `audit:read` out of the blanket `:read` scope into its own tier so a Read-only scoped key can read dashboards/licenses but NOT the full audit log (`api/api_keys.rs::Role::grants`). Deferred from the scoped-keys session. @@ -110,17 +112,19 @@ Operator-specific memories at `~/.claude/projects/-Users-macpro-Projects-keysat/ public sites (keysat.xyz, docs.keysat.xyz) live. All repos synced to **both** GitHub + gitea. `keysat-registry-landing` remotes deleted by the operator. -- **Shipped `:60` — Zaprite auto-charge silent-lapse fix.** `try_auto_charge_zaprite` suppresses manual-pay - only for a settled status (`PAID`/`COMPLETE`/`OVERPAID`, via the new `zaprite_charge_settled` helper + unit - test); any other/unknown status falls through to the manual-pay pay link. Allowlist by design. No schema/SDK change. -- **Decided — Keysat sends no buyer email.** Buyer email + the per-profile SMTP send path are dropped - (`plans/keysat-smtp-emails.md` superseded; dormant `merchant_profiles.smtp_*` flagged in code). The surviving - kernel, operator failure alerts, is reframed onto StartOS notifications/health (ROADMAP "Operability & alerts"). -- **Docs reconciled:** false "Keysat emails the license" claim removed from docs.keysat.xyz; `HOW_IT_WORKS.md` - corrected (subscriptions + merchant profiles shipped, no email); new design-checker-clean Merchant profiles - docs section. `unlimited_merchant_profiles` confirmed live on Pro + Patron policies (Creator excluded). -- **Next (priority):** 1) automated multi-profile webhook routing test (ROADMAP, Effort S). 2) split `audit:read` - from the blanket `:read` scope. 3) operator-alerts-via-StartOS (verify the start-sdk 1.3.2 API first). - 4) registry-submission `prepare.sh` + on-box verification. -- **Tests/build:** `cargo check` + wrapper `tsc` clean; `zaprite_charge_settled` unit test green; full suite - through 0025 last green. Pre-existing docs.css radius drift (10px vs 12px `r-lg`, 3 spots) noted; not blocking. +- **This session — full eval + three P1 fixes (all committed & pushed).** Ran the five-agent `/full-eval` + (evaluator, security-auditor, exerciser, doc-auditor, start9-spec-checker); report in `EVALUATION.md` + (no P0s; strong crypto/auth/webhook posture). Fixed all three P1s: (1) crosscheck harness `run_ts.mjs` + hardcoded `/sessions/...` path → resolves relative to repo (keysat-root); (2) Rust SDK + `keysat-docs` + imported `licensing_client` not `keysat_licensing_client` — fixed, plus two latent bugs it masked (example's + undeclared `anyhow` → stdlib; doctest `include_str!` of a missing file → inline PEM); (3) added + `licensing-service-startos/prepare.sh` clean-Debian build bootstrap. Reviewer-approved; verified green. +- **Registry submission mechanism researched.** Email-based (no PR/form) — see Open TODOs + ROADMAP. Two + blocking unknowns to clear with Start9 first: license acceptability + whether 0.4.x still uses `prepare.sh`. +- **Prior context still current:** `:60` Zaprite silent-lapse fix shipped; Keysat sends no buyer email + (SMTP path dormant); docs reconciled; `unlimited_merchant_profiles` live on Pro+Patron (not Creator). +- **Next (priority):** 1) email Start9 re: license + 0.4.x build flow (gates the whole submission). 2) eval + P2 hardening — XFF rate-limit bypass, dep-advisory bumps, admin/public port split (ROADMAP "Security & + hardening"). 3) automated multi-profile webhook routing test (Effort S). 4) split `audit:read` scope. +- **Tests/build:** daemon `cargo test` ~117–131 green across 8 suites; wrapper `tsc` clean; Rust SDK + `cargo build --examples` + doctest now green; crosscheck harness passes end-to-end. No CI enforces any of it. diff --git a/ROADMAP.md b/ROADMAP.md index bcba1b2..4009e17 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -28,12 +28,18 @@ Longer-term backlog. Near-term state lives in `AGENTS.md` → Current state. ## Packaging & distribution -- Start9 Community Registry submission — a 2026-06-17 spec check found the wrapper passes the functional - criteria (manifest, interfaces, health check, backup/restore, BTCPay dep, actions). Remaining gap before - submission: add a `prepare.sh` to set up a clean Debian box for the first build (copy the one from - `hello-world-startos`), then run the on-box manual verification (install / backup / restore / logs). - Submission criteria themselves remain unpublished; reach out to Start9 when ready. (Icon-render and the - source-available license are intentionally not treated as blockers.) +- **Start9 Community Registry submission.** Mechanism (researched 2026-06-18): **email-based, not a PR or + form.** Mail `submissions@start9labs.com` (the 0.3.5.x docs say `submissions@start9.com` — addresses are + inconsistent) a link to the public wrapper repo (+ detailed README); both wrapper and upstream source must + be public. Start9 snapshots the repo, **builds from source on a clean Debian box** (`prepare.sh` + `make`; a + failed first build bounces the submission), installs + tests on real hardware (metadata, install/uninstall, + interfaces, health, backup/restore, low-resource device), lands it in Community **Beta**, and promotes to + production when you reply asking. Updates follow the same loop. `start-cli s9pk publish` is **self-hosted-registry + only** — unrelated to community intake. `prepare.sh` shipped this session (`licensing-service-startos/prepare.sh`). + **Clear with Start9 before submitting:** (1) is the custom source-available `LicenseRef-Keysat-1.0` acceptable + (docs conflict: "source available" vs "Open Source License") — highest-leverage; a hard No blocks regardless of + build-readiness; (2) does the 0.4.x build flow still invoke `prepare.sh` (a 0.3.5.x concept, absent from 0.4.x + docs). Then the on-box manual verification. Functional criteria otherwise pass (2026-06-17 spec check). ## Operability & alerts @@ -53,6 +59,30 @@ Longer-term backlog. Near-term state lives in `AGENTS.md` → Current state. The dormant `merchant_profiles.smtp_*` columns (migration 0020) are now dead weight — left in place (a removal migration isn't worth it) and flagged in `src/merchant_profiles.rs`. +## Security & hardening (2026-06-18 full-eval P2s; EVALUATION.md has full detail but is overwritten each run, so the durable list lives here) + +- **X-Forwarded-For rate-limit bypass.** Login/recover/validate buckets key off the raw first XFF value + (`api/auth.rs:137`, `api/recover.rs:65`, `api/admin.rs:63`, `api/validate.rs:95`); rotating XFF defeats the + throttle. First confirm whether the StartOS front proxy overwrites XFF (decides real-world reachability), then + derive client IP from the trusted-proxy connection with a peer-socket fallback. +- **Dependency advisories** (mechanical, low-risk): bump `sqlx 0.7.4` → ≥0.8.1 (RUSTSEC-2024-0363), + `rustls-webpki 0.101.7` → ≥0.103.13 (RUSTSEC-2026-0098/0099/0104), update start-sdk for the wrapper's + `fast-xml-parser` (GHSA-5wm8-gmm8-39j9); re-run `cargo audit` / `npm audit`. +- **Admin UI co-located with the public API** on the single `:8080` interface (`startos/interfaces.ts`) — operator + can't network-isolate admin. Split the admin SPA + `/v1/admin/*` onto their own port/interface. +- **Webhook-endpoint registration accepts `file://` / loopback URLs** (`webhooks.rs:106`, admin-gated) — add a + scheme + host allowlist (reject non-http(s), loopback, link-local). +- **Runtime-prepared SQL** in `db/repo.rs` + `subscriptions.rs` (no compile-time column check; this class already + 500'd every paid purchase once on `:52`) — migrate the money-path queries to compile-checked `sqlx::query!`. +- **`rate_buckets` grows unbounded** (`rate_limit.rs:63`, one row per client IP on a public endpoint, no reaper) — + add a reaper mirroring the session/redemption reapers in `main.rs`. +- **No CI.** Stand up one job (`cargo test && cargo clippy && tsc --noEmit`, ideally `cargo fmt --check`); the suite + is good but unenforced, so green depends on the operator remembering to run it before `publish.sh`. +- Doc-drift P3 cluster (each one-liners, see EVALUATION.md): BUILDING.md Node 20→22 / Rust 1.75→1.88, broken docs + `/changelog` footer link, README "Show credentials" → "Show admin API key", PORTING_SDK stale (Python/Go shipped; + crate renamed), testing.md stale test counts, and the `unlimited_merchant_profiles` guide/code-comment ("still + needs adding") vs AGENTS ("confirmed live") contradiction — resolve with a live `GET /v1/products/keysat/policies`. + ## Licensing model - Evaluate Elastic License v2 vs the current custom `LicenseRef-Keysat-1.0` (parked decision). diff --git a/docs/guides/startos-packaging.md b/docs/guides/startos-packaging.md index 1cb40ee..c6901c6 100644 --- a/docs/guides/startos-packaging.md +++ b/docs/guides/startos-packaging.md @@ -28,6 +28,18 @@ npm run prettier # prettier --write startos (NOT enforced; see testing.md) Auth for `make install` is the developer key at `~/.startos/developer.key.pem` (private — never commit/share). +## Clean-box build bootstrap (`prepare.sh`) + +`prepare.sh` (in `licensing-service-startos/`) installs every HOST prerequisite a +fresh Debian/Ubuntu box needs before `make`: apt prereqs (build-essential, jq, git, +squashfs-tools(-ng)), Node 22 (NodeSource), Docker (+ QEMU binfmt for cross-arch), +and **start-cli** via the official installer — +`curl -fsSL https://start9.com/start-cli/install.sh | sh` (drops the binary in +`~/.local/bin`). Rust is **not** installed on the host; the daemon compiles inside +the Dockerfile (`FROM rust:1.88`). Idempotent; apt-based only. Caveat: `prepare.sh` +is a 0.3.5.x submission convention — the 0.4.x docs never mention it, so Start9's +0.4.x build flow may not actually invoke it (confirm before relying on it). + ## ALWAYS: bump the version before building Edit `startos/versions/v0.2.0.ts` — increment `version: '0.2.0:N'` and prepend a