Handoff: phase-2 foundation + payment-connect spec/roadmap
This commit is contained in:
@@ -114,50 +114,44 @@ Operator-specific memories at `~/.claude/projects/-Users-macpro-Projects-keysat/
|
||||
publish **`0.2.0:57`** — universal multi-arch (x86_64 + aarch64), GitHub release
|
||||
`v0.2.0-57`. Migrations 0020–0023; four SDKs published; `keysat.xyz` +
|
||||
`docs.keysat.xyz` deployed.
|
||||
- **Live server `immense-voyage.local` still runs `:56`** — `:57` shipped to the registry
|
||||
this session, but the box wasn't redeployed (that publish run predated this session's
|
||||
change adding `make install` as `publish.sh` step 5). Run `make install` from
|
||||
`licensing-service-startos/` once to catch the box up to `:57`; future `publish.sh` runs
|
||||
now deploy to the live host automatically (best-effort, non-fatal).
|
||||
- **Shipped this session as `:57`** (feature `d5885d1`, bump `069cf1e`; pushed
|
||||
origin+gitea) — **`merchant-onboard` scoped-key role** for least-privilege self-serve
|
||||
onboarding: read + `products:write` + `policies:write` + `licenses:write` (create
|
||||
product → define policies/tiers → issue licenses, no master key). New `Role` variant
|
||||
only — the catalog write scopes already existed and were enforced since `:55`;
|
||||
`grants()` matches scope strings explicitly (never `:write` suffix) so it can't widen
|
||||
into settings/payment/profile/webhook writes, and every master-only op stays behind
|
||||
`require_admin`. Migration 0023 widens the `scoped_api_keys` role CHECK (no FKs → plain
|
||||
rebuild). **Caveat**: covers catalog + manual issuance fully, but payment-provider
|
||||
connect stays master-only, so buyer-paid purchase still needs a one-time operator step.
|
||||
See `src/api/api_keys.rs`.
|
||||
`:57` shipped the **`merchant-onboard`** scoped role (catalog + license self-serve, no
|
||||
master key; `src/api/api_keys.rs`) — see git log for detail.
|
||||
- **Live box `immense-voyage.local`**: `:57` was deployed this session via `make install`
|
||||
(`start-cli package install` returned clean; StartOS applies the swap async — **not
|
||||
independently confirmed**; verify the StartOS UI shows `0.2.0:57`). `publish.sh` now runs
|
||||
`make install` as step 5, so future ships auto-deploy (best-effort, non-fatal).
|
||||
|
||||
- **In progress — agent-payment-connect (phase 2)**. Approved spec:
|
||||
`plans/agent-payment-connect-scope.md`. Lets a scoped key connect a BTCPay provider, but
|
||||
ONLY on a sandbox daemon and ONLY for a non-mainnet network — never folded into a role
|
||||
(a key that can repoint settlement is a fund-redirection key).
|
||||
- **Foundation committed this session (`3afac07`, origin+gitea; NOT version-bumped)** —
|
||||
slices 1–2 of 5: `Config.sandbox_mode` (env `KEYSAT_SANDBOX_MODE`, never API-settable;
|
||||
surfaced in `/v1/admin/tier`); migration 0024 `scoped_api_keys.extra_scopes`; per-key
|
||||
à-la-carte scopes (`GRANTABLE_EXTRA_SCOPES=["payment_providers:write"]`, granted via
|
||||
`extra_scopes`, in NO role — `grants()` carves it out of full-admin's wildcard;
|
||||
fail-closed parsing). Reviewer pass clean after fixing the full-admin-wildcard P1.
|
||||
- **Pending — slices 3–5**: `require_provider_connect` gate (master→any; scoped+
|
||||
`payment_providers:write`→only if `sandbox_mode` AND non-mainnet); BTCPay OAuth wiring
|
||||
(record `scoped_initiator` in `btcpay_authorize_state` at `start_connect`, network-check
|
||||
at `finish_connect`, migration 0025); Zaprite stays master-only. **The BTCPay on-chain
|
||||
address network detection MUST be validated against a live regtest box** before
|
||||
shipping (classify address prefix `bc1`/`tb1`/`bcrt1`, fail-closed to mainnet; the
|
||||
payment-method id is `BTC-CHAIN` vs `BTC` by version).
|
||||
|
||||
- **Work queue (next, in order)**:
|
||||
1. Deploy `:57` to `immense-voyage.local` (`make install`) when ready.
|
||||
2. Operator data action (needs master key): grant `unlimited_merchant_profiles` to
|
||||
Pro/Patron on the live master (see Open TODOs).
|
||||
3. 3 remaining multi-profile UIs (rail picker, per-profile SMTP, rail-pref editor) —
|
||||
see ROADMAP.
|
||||
4. Split `audit:read` out of the blanket `:read` scope into its own tier (Open TODOs).
|
||||
1. Build gate slices 3–5 (above) — validate the BTCPay address fetch on regtest.
|
||||
2. Confirm `:57` is live on `immense-voyage.local` (StartOS UI).
|
||||
3. Operator data action (master key): grant `unlimited_merchant_profiles` to Pro/Patron.
|
||||
4. 3 multi-profile UIs + split `audit:read` (ROADMAP / Open TODOs).
|
||||
|
||||
- **P2 (unfixed, deferred pending user's call)**: `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); no rate-limit on
|
||||
`/v1/purchase`+`/v1/redeem` (bucket keys on spoofable `X-Forwarded-For`); `422`/`415`
|
||||
return plain-text not JSON (breaks SDK `JSON.parse`); product `slug` unvalidated;
|
||||
`GET /v1/admin/products` 405 vs OpenAPI; dep advisories (`sqlx`→≥0.8.1
|
||||
RUSTSEC-2024-0363, `rustls-webpki`→≥0.103.12); no CI, fmt/clippy/prettier unenforced.
|
||||
- **P2/P3 debt (unchanged)**: `set_product_entitlements_catalog` missing `rows_affected`
|
||||
guard; no rate-limit on purchase/redeem (spoofable XFF); `422`/`415` plain-text not JSON;
|
||||
`slug` unvalidated; `GET /v1/admin/products` 405 vs OpenAPI; dep advisories (`sqlx`→≥0.8.1,
|
||||
`rustls-webpki`→≥0.103.12); no CI / fmt-clippy unenforced; field-naming drift; outbound
|
||||
webhook SSRF; design-contract conformance (see ROADMAP).
|
||||
|
||||
- **P3+ (bulk or later decision)**: `/v1/purchase` 400 vs `/v1/btcpay/webhook` 503 for
|
||||
the same no-provider cause; undocumented required `kind` on discount-codes;
|
||||
field-naming drift (`license_id`/`id`, machines `key` vs `license_key`,
|
||||
`redeem`/`purchase` `product` vs `validate` `product_slug`); migration
|
||||
`_sqlx_migrations` allowlist foot-gun; 2 KB unauth Zaprite payload WARN-log;
|
||||
outbound-webhook SSRF (operator-only); re-register master Zaprite webhook at the
|
||||
path-keyed URL; registry icon non-render (platform limit); design-contract conformance
|
||||
(see ROADMAP); optional fmt/prettier standalone commit.
|
||||
|
||||
- **Tests/build**: `cargo check` + `npm run check` (tsc) clean (1 intentional
|
||||
deprecation warning); full suite green — unit 10, api **57** (incl. the
|
||||
merchant-onboard onboard-chain + master-only-denial test), subscriptions 7, upgrades 9,
|
||||
worker 3, crosscheck 4, migrations 9 (through 0023). No new clippy warnings. FK
|
||||
- **Tests/build**: `cargo check` + `npm run check` clean (1 intentional deprecation
|
||||
warning); full suite green — lib unit **13**, api **59**, subscriptions 7, upgrades 9,
|
||||
worker 3, crosscheck 4, migrations 9 (through 0024). No new clippy warnings. FK
|
||||
enforcement confirmed — sqlx pool sets `foreign_keys(true)` per connection.
|
||||
|
||||
+10
@@ -9,6 +9,16 @@ Longer-term backlog. Near-term state lives in `AGENTS.md` → Current state.
|
||||
- Keysat-side dedup cache for Zaprite contacts (same buyer purchasing recurring twice can create duplicate Zaprite contacts).
|
||||
- Zaprite declined-card / expired-profile failure-body shapes are undocumented — harden `try_auto_charge_zaprite` once observed in production.
|
||||
|
||||
## Agent compatibility & scoped API keys
|
||||
|
||||
- **Agent-delegable payment-provider connect** (approved, not urgent — see
|
||||
`plans/agent-payment-connect-scope.md`). Add an à-la-carte `payment_providers:write` scope
|
||||
(never bundled into `merchant-onboard`), gated by a daemon-level **sandbox-mode flag** as the
|
||||
outer gate (production daemons reject scoped connect entirely) with a **network gate** inner
|
||||
defense (regtest/testnet/signet only, fail-closed to mainnet). BTCPay network is derived from
|
||||
an on-chain address prefix (no `server/info` field exists). Feeds the doc-harness Path 2
|
||||
(regtest buyer-pays). Ships after doc-harness Path 1.
|
||||
|
||||
## Packaging & distribution
|
||||
|
||||
- Start9 Community Registry submission — criteria are unpublished; contact Start9 directly when ready.
|
||||
|
||||
Reference in New Issue
Block a user