8eb4a97c6f
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.
29 lines
1.4 KiB
SQL
29 lines
1.4 KiB
SQL
-- Carry the connect *initiator* through the BTCPay OAuth round trip.
|
|
--
|
|
-- agent-payment-connect (plans/agent-payment-connect-scope.md): a scoped key
|
|
-- bearing `payment_providers:write` may start a BTCPay connect, but only on a
|
|
-- sandbox daemon (outer gate) AND only for a non-mainnet store (inner gate).
|
|
-- The inner gate can only be evaluated at callback time — that's the first
|
|
-- moment we know the store and can resolve its network. So the connect handler
|
|
-- must remember, across the operator's browser round-trip to BTCPay, whether
|
|
-- the initiator was the master key (may connect any network) or a scoped key
|
|
-- (restricted to non-mainnet).
|
|
--
|
|
-- `scoped_initiator`: 0 = master (no network restriction), 1 = scoped key
|
|
-- (callback enforces non-mainnet, fail-closed). Default 0 keeps any in-flight
|
|
-- pre-upgrade state token behaving as a master connect (the only kind that
|
|
-- existed before this migration).
|
|
-- `initiator_actor_hash`: sha256 of the initiating credential, so the callback
|
|
-- can write an audit row attributing the scoped connect without a header.
|
|
--
|
|
-- Additive, one-way (consistent with 0020-0022). The table is also pruned by
|
|
-- timestamp, so any pre-migration rows expire within 30 minutes regardless.
|
|
|
|
PRAGMA foreign_keys = ON;
|
|
|
|
ALTER TABLE btcpay_authorize_state
|
|
ADD COLUMN scoped_initiator INTEGER NOT NULL DEFAULT 0;
|
|
|
|
ALTER TABLE btcpay_authorize_state
|
|
ADD COLUMN initiator_actor_hash TEXT;
|