Document P0 webhook fix + test seam; ship :54; track EVALUATION.md
This commit is contained in:
@@ -58,7 +58,8 @@ tests/crosscheck/ cross-language LIC1 verifier → guides/crypto
|
||||
Note: the daemon (`licensing-service-startos`, repo `keysat`), each SDK, and
|
||||
`plans/` are **separate git repos** — commit code/plan changes in their own repo.
|
||||
The root `Licensing` repo (`keysat-root`) tracks only `AGENTS.md` + `docs/guides/`
|
||||
+ `.claude/rules/`. **Remotes differ per repo**: the daemon's `main` tracks
|
||||
+ `.claude/rules/` + `EVALUATION.md` (the latest full-eval report; overwritten
|
||||
each run, history in git log). **Remotes differ per repo**: the daemon's `main` tracks
|
||||
**GitHub** (`origin`, the public upstream) with a `gitea` backup — plain `git push`
|
||||
goes to GitHub, so also `git push gitea main`; root + plans are **Gitea-only**.
|
||||
Run `git remote -v` (full) and check what the branch tracks before pushing.
|
||||
@@ -97,17 +98,19 @@ Operator-specific memories at `~/.claude/projects/-Users-macpro-Projects-licensi
|
||||
|
||||
## Current state (2026-06-13)
|
||||
|
||||
- **Live**: server `immense-voyage.local` runs daemon `0.2.0:53` (migrations
|
||||
0020–0022 applied). Registry `registry.keysat.xyz` now publishes `:53` too
|
||||
(GitHub release `v0.2.0-53` cut; `files.keysat.xyz` serves the s9pk). Four SDKs
|
||||
- **Live**: server `immense-voyage.local` runs daemon `0.2.0:54` (migrations
|
||||
0020–0022 applied). Registry `registry.keysat.xyz` publishes `:54` too
|
||||
(GitHub release `v0.2.0-54` cut; `files.keysat.xyz` serves the s9pk). Four SDKs
|
||||
published; `keysat.xyz` + `docs.keysat.xyz` deployed.
|
||||
- **`:52`/`:53` = multi-provider/merchant-profile model**: data model + backend
|
||||
resolution shipped and audited sound; the resolution/CRUD query surface now has
|
||||
test coverage. See `docs/guides/payments.md`.
|
||||
- **Purchase-path bug fixed and shipped**: the `:52` ambiguous-column bug (broke
|
||||
*every* paid purchase) was fixed in daemon `31f4670`; `:53` (version bump
|
||||
`8c4bacc`) built, installed to prod, and published to the registry on
|
||||
2026-06-13. The live purchase path works again.
|
||||
- **Two payment-path fixes shipped 2026-06-13**: (a) `:53` fixed the `:52`
|
||||
ambiguous-column bug that broke *every* paid purchase (daemon `31f4670`); (b)
|
||||
`:54` fixed the **P0 Zaprite webhook-forgery** — settle now re-confirms against
|
||||
the provider API before issuing (daemon `783372c`, bump `495fbbf`). Both built,
|
||||
installed to prod, and published to the registry. Live purchase + settle paths
|
||||
are sound.
|
||||
- **GAP — multi-profile is non-functional end-to-end**: nothing in the shipped
|
||||
app writes `products.merchant_profile_id` (the INSERT in
|
||||
`create_product_with_currency` omits it; `update_product_with_currency` has no
|
||||
@@ -118,23 +121,22 @@ Operator-specific memories at `~/.claude/projects/-Users-macpro-Projects-licensi
|
||||
product→profile **write path** is missing. **This is the gating piece for
|
||||
multi-profile** — see the scoped slice below.
|
||||
- **Triage from `EVALUATION.md` (full-eval, 2026-06-13)** — P0/P1 = work queue,
|
||||
P2 = known debt, P3+ = deferred. The report at repo root has file:line evidence.
|
||||
P2 = known debt, P3+ = deferred. The report at repo root has file:line evidence;
|
||||
it's tracked, so re-running full-eval overwrites it and `git log -- EVALUATION.md`
|
||||
preserves prior runs.
|
||||
|
||||
- **Work queue (P0/P1 — do first, in this order)**:
|
||||
1. **[P1] Provider-injection test seam — BEFORE #2's fix.** The prod purchase/
|
||||
settle path has no mock seam (the mock injects into the dead `state.payment`
|
||||
singleton, not `resolve_provider_for_profile_rail` — this is how the `:52`
|
||||
500 shipped). Add an always-compiled `Option<Arc<dyn PaymentProvider>>`
|
||||
override on `AppState`, checked first in `resolve_provider_for_profile_rail`.
|
||||
Greens the two `paid_purchase_*` red tests and gives #2 a regression harness.
|
||||
**Delete** the dead `payment_provider_preference_round_trip` in the same pass.
|
||||
2. **[P0] Zaprite webhook forgery.** The settle-webhook is unauthenticated — a
|
||||
forged `order.change`/`status=PAID` with a buyer-visible order id mints a
|
||||
signed license (the `externalUniqId` "trust anchor" in the comments is never
|
||||
read in the webhook path). Fix: on settle, re-fetch `get_invoice_status` from
|
||||
the provider and require local `pending` state + matching amount/currency
|
||||
before issuing (mirror the reconciler's safe re-fetch). Closes the P1 below
|
||||
too. `payment/zaprite/provider.rs:234-362`, `api/webhook.rs:62-196,121-194`.
|
||||
1. ✅ **SHIPPED in `:54` — Provider-injection test seam.** Added the
|
||||
always-compiled `AppState::provider_override` + `provider_from_row` helper at
|
||||
every resolution site; greened the two `paid_purchase_*` tests; deleted the
|
||||
dead `payment_provider_preference_round_trip`. See `docs/guides/testing.md`.
|
||||
2. ✅ **SHIPPED in `:54` — Zaprite webhook forgery fix.** `webhook.rs::handle_inner`
|
||||
re-fetches `provider.get_invoice_status` and requires `Settled` before any
|
||||
settle-derived action; acks 200 (no issue) when the provider is unreachable.
|
||||
Two regression tests (forged-settle, provider-unreachable). api 47/47.
|
||||
**Still open (auditor P1):** a literal paid-amount/currency check — needs a
|
||||
trait change (`get_invoice_status` returns only a status enum). See
|
||||
`docs/guides/payments.md`. **This is now the top remaining security item.**
|
||||
3. **[P1] Scoped API keys (`ks_…`) are non-functional** — issuable but 403 on
|
||||
every admin endpoint; the `require_admin`→`require_scope` migration was never
|
||||
done. Finish it, or stop advertising/issuing them. `api/api_keys.rs:14`.
|
||||
|
||||
Reference in New Issue
Block a user