Commit Graph

4 Commits

Author SHA1 Message Date
Grant 8eb4a97c6f Gate scoped BTCPay connect to sandbox + non-mainnet
Slices 3-4 of agent-payment-connect: a scoped key carrying the a-la-carte
payment_providers:write scope may connect a BTCPay provider, but only on a
sandbox daemon (KEYSAT_SANDBOX_MODE) and only for a non-mainnet
(regtest/testnet/signet) store. Master may connect any network; disconnect and
production/mainnet reconnect stay master-only. A credential that can repoint
settlement is a fund-redirection key, so the gate is deliberately narrow and
fails closed.

- require_provider_connect: outer gate (sandbox flag) at start_connect
- btcpay/network.rs classify_address_network + client::fetch_onchain_network:
  resolve the store network at finish_connect, fail-closed to mainnet on any
  ambiguity (no on-chain method, non-2xx, non-JSON, unknown prefix), before any
  webhook/persist side effect
- initiator carried across the OAuth round-trip via btcpay_authorize_state
  (migration 0025: scoped_initiator + initiator_actor_hash); scoped connects
  are audited
- the GET callback now returns the error's HTTP status (was a misleading 200 on
  a denied connect)
- openapi.rs documents the BTCPay connect/callback/status/disconnect paths and
  the key-creation scopes field

Validated end-to-end against a live regtest BTCPay. Full suite green; adds gate
+ network unit/integration tests.
2026-06-17 09:31:57 -05:00
Grant be8688de80 Fix OpenAPI spec inaccuracies found by the onboarding test
- GET /v1/admin/licenses requires product_id (uuid), not a slug
- add the /v1/admin/licenses/search path (was referenced, never defined)
- drop the phantom GET /v1/admin/products (only POST exists; list is
  the public GET /v1/products)
- clarify product price_value (write field) vs legacy price_sats
2026-06-16 22:48:09 -05:00
Grant d5885d1d97 Add merchant-onboard scoped-key role for self-serve onboarding
New scoped API-key role granting read + products:write + policies:write +
licenses:write — the least-privilege credential for end-to-end catalog
setup and license issuance (create product, define policies/tiers, issue
licenses against them) without holding the master key.

The catalog write scopes already existed and were enforced on the
endpoints; only the role->scope expansion was missing. So this is a new
Role variant, not a scope-model change. grants() matches scope strings
explicitly (never by :write suffix) so the role can't widen into
settings / payment / merchant-profile / webhook writes, and every
master-only operation stays behind require_admin and so is structurally
unreachable. Existing tier caps still bound it (Creator: 5 products /
5 policies per product).

Migration 0023 rebuilds scoped_api_keys to widen the role CHECK (SQLite
can't alter a CHECK in place); the table has no FKs, so it's a plain
copy/drop/rename. Test covers the full onboard chain under the key's own
credential plus denial of master-only gates and support-only writes.
2026-06-16 18:55:18 -05:00
Grant 257669092b v0.2.0:11 + v0.2.0:12 — Archive, Settings, agent surface, machines redesign
Two release cycles prepared together: v0.2.0:11 (policy archive + safe-
delete cleanup + brand-consistent confirm modals) and v0.2.0:12 (Settings
tab + agent-friendly operator API + machines tab redesign + buyer-facing
copy alignment).

Highlights:

- Migration 0015: policies.archived_at column. Archive button on tier
  cards; safe-delete relaxed to ignore revoked-license tombstones;
  renewal worker refuses archived policies.
- Migration 0016: scoped_api_keys table. Four roles (read-only,
  license-issuer, support, full-admin) with bounded scopes. Master
  admin_api_key still works on every endpoint; scoped keys gated on
  endpoints wired through require_scope().
- New /v1/openapi.json — public, no auth. Curated OpenAPI 3.1 spec
  for agent / SDK discovery.
- New Settings tab: Operator name + Payment providers panel + API
  keys management. Replaces 8 StartOS Actions (Zaprite all, BTCPay
  all, operator name, switch-provider). StartOS Actions pruned to 4
  install-time essentials.
- Machines tab rewritten: global default view grouped by product,
  filter pills with counts, quick-stats row, drill-down via new
  "Machines" button on each Licenses-tab row. New repo helper
  list_machines_admin joins machines x licenses x products
  server-side.
- Branded confirmModal replaces every native window.confirm() call
  in the admin UI (7 callsites).
- Enforce mode killed: KEYSAT_LICENSE_ENFORCE compile-time flag
  retired; daemon always boots; missing self-license -> Creator
  (free) tier. "Unlicensed" label gone from admin UI.
- Zaprite gated on the new zaprite_payments entitlement (renamed
  from card_payments to reflect the broader gateway).
- Creator code cap 5 -> 10.
- KEYSAT_AGENT_GUIDE.md: auth, role-to-scope mapping, error envelope,
  webhook events, worked recipes.
- Buyer-facing copy aligned with new positioning: "Bitcoin-native
  self-hosted software licensing" everywhere on production surfaces.
- Cross-product safety section (Section 9a) added to KEYSAT_INTEGRATION.md.
- 5 new API integration smoke tests covering OpenAPI, scoped API
  keys CRUD, role-elevation guard, and Zaprite-tier gating.

Test count: 83 passing (was 78). All migration tests pass against
0015 and 0016 applied to populated DBs.
2026-05-11 08:45:25 -05:00