Record product→merchant-profile write path; refresh Current state
Document the now-functional product→profile write path in the payments guide (set_product_merchant_profile, post-write pattern, picker gating, double-Option clear). Mark the multi-profile GAP closed, drop the done work-queue item, and note the discovered set_product_entitlements_catalog rows_affected gap.
This commit is contained in:
@@ -101,63 +101,39 @@ Operator-specific memories at `~/.claude/projects/-Users-macpro-Projects-keysat/
|
||||
- Build the admin SPA "API keys" management panel (create w/ role picker, list,
|
||||
revoke) — backend is wired; UI deferred to a design-focused session.
|
||||
|
||||
## Current state (2026-06-13)
|
||||
## Current state (2026-06-16)
|
||||
|
||||
- **Live**: registry `registry.keysat.xyz` now publishes **`0.2.0:55`** — universal
|
||||
multi-arch (x86_64 + aarch64, verified via the registry index + s9pk header),
|
||||
GitHub release `v0.2.0-55`, canonical s9pk at `files.keysat.xyz/keysat.s9pk`.
|
||||
Migrations 0020–0022; four SDKs published; `keysat.xyz` + `docs.keysat.xyz`
|
||||
deployed (docs redeployed this session). **The live server `immense-voyage.local`
|
||||
still runs `:54` until updated from the registry.** `:55` ships the two
|
||||
prior-session P1s (scoped keys, settle-amount tripwire) + this session's packaging
|
||||
work.
|
||||
- **Shipped this session (`:55` published; all committed + pushed)** — doc-auditor +
|
||||
start9-spec-checker + reviewer passes, all approved/no blockers; `tsc`/`bash -n`
|
||||
clean. Landed: **all 4 StartOS submission blockers** (new `instructions.md`;
|
||||
manifest `packageRepo`/`docsUrls` dead-link fixes; **universal multi-arch publish**
|
||||
— `publish.sh` → `make universal`, registry entry carries both arches with no arch
|
||||
restriction); enforce-mode drift cleaned from `activateLicense.ts`/`showCredentials.ts`
|
||||
(enforce retired, `self_license.rs:15`); 5 doc-drift fixes (api 47→54; FK confirmed
|
||||
`db/mod.rs:29`; `integrate.html` phantom `GET /v1/licenses/{id}/status` → `POST
|
||||
/v1/validate`; `v0.2.0.ts` stale header; go README v0.2); **refunds scrubbed from
|
||||
public docs** (Keysat has no refund feature — out-of-band only, v0.3 revoke-on-refund
|
||||
hook is a no-op; admin-UI "handle out of band" disclaimers kept). Remotes: keysat-root
|
||||
→ gitea-only; keysat-docs, daemon, go SDK → GitHub `origin` + gitea backup.
|
||||
- **`:52`/`:53` = multi-provider/merchant-profile model**: data model + backend
|
||||
resolution shipped and audited sound; resolution/CRUD query surface has tests.
|
||||
Both `:54` P0s (provider-injection test seam; Zaprite webhook-forgery re-confirm)
|
||||
remain fixed; live purchase + settle paths sound.
|
||||
|
||||
- **Shipped in `:55` (was the prior session's unshipped P1s)**:
|
||||
1. **Settle-amount tripwire.** `get_invoice_status` now returns
|
||||
`ProviderInvoiceSnapshot { status, amount }`; `audit_settle_amount` (shared by
|
||||
webhook + reconcile issue paths) WARNs + writes an `invoice.amount_mismatch`
|
||||
audit row on drift, then **issues anyway** (advisory, not a gate — a hard gate
|
||||
would fight BTCPay payment tolerance). SAT-only: skips non-SAT (fiat sub
|
||||
renewals) and `None`. Reviewed (caught + fixed a fiat-renewal false-positive).
|
||||
See `docs/guides/payments.md`.
|
||||
2. **Scoped API keys wired.** 58 admin endpoints migrated `require_admin`→
|
||||
`require_scope`; 12 sensitive ones stay master-only (issuer key, provider
|
||||
connect/disconnect, set-password, api-key CRUD, db-info, operator-name,
|
||||
per-license tier change). `require_scope` re-exported from `api::admin`. Role
|
||||
boundary tests added. Boundary documented in `api/api_keys.rs` module doc.
|
||||
|
||||
- **GAP — multi-profile still non-functional end-to-end**: nothing writes
|
||||
`products.merchant_profile_id` (INSERT in `create_product_with_currency` omits
|
||||
it; `update_product_with_currency` has no field; `Product` in `models.rs` lacks
|
||||
it). Resolver fully supports it; only the product→profile **write path** is
|
||||
missing. **Gating piece for multi-profile.**
|
||||
- **Live**: registry `registry.keysat.xyz` publishes **`0.2.0:55`** — universal
|
||||
multi-arch, GitHub release `v0.2.0-55`, canonical s9pk at
|
||||
`files.keysat.xyz/keysat.s9pk`. Migrations 0020–0022; four SDKs published;
|
||||
`keysat.xyz` + `docs.keysat.xyz` deployed. Live server `immense-voyage.local`
|
||||
still runs `:54` until updated from the registry. **This session's work is
|
||||
committed + pushed to `main` but UNRELEASED — no `:56` cut, not built.** (`:55`
|
||||
shipped scoped keys, the settle-amount tripwire, and all 4 StartOS submission
|
||||
blockers — see git log.)
|
||||
- **Shipped this session (daemon `b088bfc`, pushed origin+gitea; unreleased)** —
|
||||
**product→merchant-profile write path, closing the multi-profile GAP.**
|
||||
`Product.merchant_profile_id` + all 4 product SELECTs + `row_to_product`; new
|
||||
`repo::set_product_merchant_profile` (validates profile exists → 404, not FK 500);
|
||||
threaded through `CreateProductReq` (post-write) + `UpdateProductReq`
|
||||
(double-Option, `Some(None)` clears to default); admin SPA profile `<select>`
|
||||
shown only when >1 profile. Mirrors the entitlements-catalog post-write pattern.
|
||||
Reviewer pass: approved, no blockers. **Multi-profile is now functional
|
||||
end-to-end** (resolver was already complete; only the write path was missing).
|
||||
See `docs/guides/payments.md`.
|
||||
|
||||
- **Work queue (next, in order)**:
|
||||
1. **product→merchant-profile picker** (the GAP — add `merchant_profile_id` to
|
||||
`Product` + `repo.rs` SELECT; `set_product_merchant_profile` writer mirroring
|
||||
`set_product_entitlements_catalog`; field on `CreateProductReq`/
|
||||
`UpdateProductReq` applied post-write; profile `<select>` from
|
||||
`GET /v1/admin/merchant-profiles`, shown only when >1 profile; no migration).
|
||||
2. 3 other deferred UIs (rail picker, per-profile SMTP, rail-pref editor);
|
||||
`unlimited_merchant_profiles` on master Pro/Patron policies.
|
||||
3. Deferred this session (now in Open TODOs): split `audit:read` out of the
|
||||
blanket `:read` scope; build the admin "API keys" management SPA panel.
|
||||
1. 3 remaining multi-profile UIs (rail picker, per-profile SMTP, rail-pref
|
||||
editor); `unlimited_merchant_profiles` on master Pro/Patron policies.
|
||||
2. Cut `:56` to ship this session's write path to the registry (bump manifest →
|
||||
`make universal` → `publish.sh`) — bundle with the UIs above if landing soon.
|
||||
3. Deferred (now in Open TODOs): split `audit:read` out of the blanket `:read`
|
||||
scope; build the admin "API keys" management SPA panel.
|
||||
|
||||
- **Discovered this session (P2, unfixed)**: `set_product_entitlements_catalog`
|
||||
has no `rows_affected` guard — a bad product-id silently 200s with stale data
|
||||
(latent since migration 0014; the new profile writer guards this correctly).
|
||||
One-line fix, deferred pending the user's call.
|
||||
|
||||
- **Known debt (P2 — schedule, not urgent)**: no rate-limit on `/v1/purchase` +
|
||||
`/v1/redeem`; rate-limit bucket keys on spoofable `X-Forwarded-For` (bypass
|
||||
|
||||
Reference in New Issue
Block a user