A fully registered, authenticated device can never satisfy the PaidK spend gate, because nothing on the client side obtains or submits payment receipts.
Evidence
Found during ADR 0003 two-phone hardware validation. A healthy device — registered, publishing successfully, device tree visible on 3/3 nodes (quorum ok), TLS-verified against the fleet CA — was refused by all three storage nodes:
Submit failed 402 Payment Required via https://<node-1>:8080
Submit failed 402 Payment Required via https://<node-2>:8080
Submit failed 402 Payment Required via https://<node-3>:8080
B0x submission failed: quorum not met: 0/3 (K=3)
Querying GET /api/v2/paidk/status/{device_id} on each node returned identically:
satisfied=false distinct_operators=0 required_k=3
Zero operator receipts on every node — not partial, not drifted. The gate is behaving as written; the device simply has no way to ever satisfy it.
Cause
/api/v2/paidk/receipt and /api/v2/paidk/status/{device_id} are implemented only in dsm_storage_node/src/api/vault/paidk.rs. Searching the tree, there is no client-side caller of either endpoint — no flow that acquires operator receipts, and none that submits them.
require_paidk gates device-initiated writes (b0x/submit, object put/delete) after authentication and envelope decode but before storage admission, with cert-resync as the explicit exception. So the failure mode is: everything works right up to storage admission, then fails permanently with a status code that reads like a billing problem rather than a missing feature.
Impact
Any device that reaches the b0x/submit path on a PaidK-enforcing node is blocked indefinitely. There is no user-facing or programmatic remedy short of direct database intervention on each node.
Note this is per-node: submit_receipt writes only to the local database with no replication, so a K-of-N quorum write path requires PaidK state established independently on every node in the quorum.
What is needed
A defined client provisioning flow that obtains legitimate operator receipts and submits them — plus a sensible surfacing of the unsatisfied state, since a bare 402 during a send is not diagnosable by a user.
The security properties that flow must have are tracked separately.
Workaround used for testing
For the hardware smoke only, devices.paidk_satisfied was set directly on the three test-fleet nodes for the two devices involved. No payment-receipt rows were created. This is recorded as a test-environment override and is not a fix.
A fully registered, authenticated device can never satisfy the PaidK spend gate, because nothing on the client side obtains or submits payment receipts.
Evidence
Found during ADR 0003 two-phone hardware validation. A healthy device — registered, publishing successfully,
device tree visible on 3/3 nodes (quorum ok), TLS-verified against the fleet CA — was refused by all three storage nodes:Querying
GET /api/v2/paidk/status/{device_id}on each node returned identically:Zero operator receipts on every node — not partial, not drifted. The gate is behaving as written; the device simply has no way to ever satisfy it.
Cause
/api/v2/paidk/receiptand/api/v2/paidk/status/{device_id}are implemented only indsm_storage_node/src/api/vault/paidk.rs. Searching the tree, there is no client-side caller of either endpoint — no flow that acquires operator receipts, and none that submits them.require_paidkgates device-initiated writes (b0x/submit, object put/delete) after authentication and envelope decode but before storage admission, with cert-resync as the explicit exception. So the failure mode is: everything works right up to storage admission, then fails permanently with a status code that reads like a billing problem rather than a missing feature.Impact
Any device that reaches the
b0x/submitpath on a PaidK-enforcing node is blocked indefinitely. There is no user-facing or programmatic remedy short of direct database intervention on each node.Note this is per-node:
submit_receiptwrites only to the local database with no replication, so a K-of-N quorum write path requires PaidK state established independently on every node in the quorum.What is needed
A defined client provisioning flow that obtains legitimate operator receipts and submits them — plus a sensible surfacing of the unsatisfied state, since a bare 402 during a send is not diagnosable by a user.
The security properties that flow must have are tracked separately.
Workaround used for testing
For the hardware smoke only,
devices.paidk_satisfiedwas set directly on the three test-fleet nodes for the two devices involved. No payment-receipt rows were created. This is recorded as a test-environment override and is not a fix.