From 1331de9c3eea22a504330228360639d1376767ec Mon Sep 17 00:00:00 2001 From: Keysat Date: Fri, 19 Jun 2026 13:59:42 -0500 Subject: [PATCH] Update Current state: SDK expiry parity resolved; daemon 0.2.0:61 live --- AGENTS.md | 43 ++++++++++++++++++++----------------------- ROADMAP.md | 1 + 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 2af764a..d25dd8e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -104,27 +104,24 @@ Operator-specific memories at `~/.claude/projects/-Users-macpro-Projects-keysat/ 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. -## Current state (2026-06-18) +## Current state (2026-06-19) -- **Live / canonical: `0.2.0:60`** — universal s9pk at `files.keysat.xyz/keysat.s9pk` (byte-verified) + GitHub - release `v0.2.0-60` + registry-registered; installed on the live box `immense-voyage.local` and serving - (master `licensing.keysat.xyz` returns 200 post-restart). Migrations through 0025; four SDKs published; two - public sites (keysat.xyz, docs.keysat.xyz) live. All repos synced to **both** GitHub + gitea. - `keysat-registry-landing` remotes deleted by the operator. - -- **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. +- **Live / canonical: `0.2.0:61`** — universal s9pk (x86_64 + aarch64) at `files.keysat.xyz/keysat.s9pk` + (byte-verified) + GitHub release `v0.2.0-61` + registry-registered; installed on `immense-voyage.local`, + master `licensing.keysat.xyz` returns 200. Migrations through 0025; four SDKs + two public sites + (keysat.xyz, docs.keysat.xyz) live. All repos on **GitHub + gitea**. +- **This session — adversarial self-license pressure-test (security-auditor → exerciser → reviewer) → two + fixes shipped in `:61`.** Both in `refresh_self_tier_from_db` (see guides/licensing-tiers.md): (1) the + unsigned `licenses.entitlements_json` column could *widen* the daemon's own tier past its signed key — any + box-owner with any valid key could self-upgrade to Patron via a DB edit; now clamped to a signed **ceiling** + (DB narrows, never widens; `clamp_to_signed_ceiling`). (2) An expired/tampered self-license lingered until + restart; now re-verified each refresh and demoted like revoked/suspended. Crypto + offline master key + confirmed sound (no signature-forgery path). Commit messages kept **generic** per operator request. +- **Also this session — SDK offline-expiry parity resolved (source).** Python (`Verifier.verify_with_time`) + Go + (`ParseAndVerifyAt` + `ErrExpired`) now reject expired keys offline, matching Rust/TS; tests + examples + + READMEs updated, all green, pushed to GitHub + gitea. **Not yet published** to PyPI / go-proxy (→ ROADMAP). +- **Next (priority):** 1) publish Python + Go SDK releases (PyPI bump + Go semver tag) so the expiry fix reaches + consumers. 2) email Start9 re: license + 0.4.x build flow (gates registry submission). 3) eval P2 hardening + (XFF rate-limit, dep bumps, admin/public port split). 4) split `audit:read` scope. +- **Tests/build:** daemon `cargo test` green (~125 across 8 suites, incl. 5 new self-license clamp unit tests); + wrapper `tsc` clean; Python SDK pytest 14 green + Go SDK `go test` green (both incl. new expiry tests). No CI. diff --git a/ROADMAP.md b/ROADMAP.md index 4009e17..ce9aa5f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -91,3 +91,4 @@ Longer-term backlog. Near-term state lives in `AGENTS.md` → Current state. - Re-test `KEYSAT_INTEGRATION.md` against a fresh downstream app to confirm a clean one-shot SDK integration. - **Add an automated regression test for multi-profile webhook routing** (adjudicated 2026-06-17 → DO, low blast radius — replaces the parked "manual Zaprite sandbox pass"). The routing is a deterministic provider-id→profile primary-key lookup with an anti-forgery re-fetch backstop, so the manual sandbox ceremony isn't worth it — but the path-keyed route (`/v1/{provider}/webhook/:provider_id` → `handle_for_provider`) currently has zero automated coverage on the money path. Plan: in `tests/api.rs`, reuse the two-provider fixture (~:3958), POST a Settled webhook to `/v1/zaprite/webhook/{provider-A-id}`, assert only profile A settles (B untouched; an unknown path-id 404s). Existing mock seam, no external account, runs in `cargo test`. Effort S. +- **Publish the Python + Go SDK releases carrying offline-expiry rejection.** Code landed 2026-06-19 (Python `Verifier.verify_with_time`, Go `ParseAndVerifyAt` + `ErrExpired`, both with tests, examples, and READMEs updated, mirroring Rust/TS `verify_with_time`/`verifyWithTime`) and is pushed to GitHub + gitea — but consumers won't get it until a release: bump `pyproject.toml` + build/twine-upload to PyPI (Python); push a new semver tag (Go, proxy.golang.org picks it up). Additive, non-breaking — minor bump. Consider matching the Rust/TS SDK versions if they're being cut too. (Online `/v1/validate` already enforces expiry for every SDK, so unupgraded offline integrators are the only exposure.)