x402-rack 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.claude/plans/20260326-rack-stack-architecture.md +11 -0
- data/.rubocop.yml +3 -0
- data/CLAUDE.md +6 -0
- data/DESIGN.md +30 -194
- data/README.md +9 -2
- data/docs/architecture.md +119 -0
- data/docs/client-integration.md +98 -0
- data/docs/ecosystem.md +83 -0
- data/docs/operations/deployment.md +95 -0
- data/docs/operations/performance.md +79 -0
- data/docs/operations/treasury.md +62 -0
- data/docs/process-flow/brc105-gateway.md +106 -0
- data/docs/process-flow/{pay_gateway.md → pay-gateway.md} +11 -12
- data/docs/process-flow/proof-gateway.md +90 -0
- data/docs/schemes/brc-105.md +118 -0
- data/docs/schemes/bsv-pay.md +73 -0
- data/docs/schemes/bsv-proof.md +85 -0
- data/docs/security.md +96 -0
- data/lib/x402/bsv/brc105_gateway.rb +171 -0
- data/lib/x402/bsv/gateway.rb +8 -15
- data/lib/x402/bsv/pay_gateway.rb +8 -1
- data/lib/x402/bsv/prefix_store.rb +91 -0
- data/lib/x402/bsv/proof_gateway.rb +104 -16
- data/lib/x402/bsv.rb +2 -0
- data/lib/x402/protocol/challenge.rb +11 -2
- data/lib/x402/version.rb +1 -1
- metadata +17 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7c88a0b6770421ac5fa4d83a410a9951f9544af70325fe467bc29676b7d0957f
|
|
4
|
+
data.tar.gz: b0f8f30ff3a63b6a83794a148e4e8e8aed60555e337c70a6925539147b2a4850
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8208b0343e9f84a4d7f998a42d574627f92c6457f40a4d8732aa3ed6ff7383789410fc895a43118db49e45bde9a31fb79283524e3b7c4677dcff1465d92119cd
|
|
7
|
+
data.tar.gz: d65d2ed290d6c097a3a0bccfda18f2634c458e59dace5b4a86ae478616525483adbeb5ba7b62d112f2bfd4475c0a0668772aa1baf2c92e16c52f547bda6f591e
|
|
@@ -302,3 +302,14 @@ The middleware itself has no dependency on `bsv-wallet` or `bsv-sdk`. Only the g
|
|
|
302
302
|
6. **The client chooses.** Multiple challenge headers, client picks. Payment content negotiation.
|
|
303
303
|
|
|
304
304
|
7. **State lives on-chain.** No server-side nonce pools, no session tracking, no balance ledgers. The blockchain is the state (with one exception: BRC-105's derivation prefix tracking, which is the BRC-100 wallet's concern).
|
|
305
|
+
|
|
306
|
+
## Future: Wallet Dashboard UI
|
|
307
|
+
|
|
308
|
+
A mountable Rack endpoint (e.g. `/wallet`) serving a simple web UI for the server-side wallet. Reads from the same `bsv-wallet` instance the gateways use, providing operator visibility into:
|
|
309
|
+
|
|
310
|
+
- **Balances** — per-basket totals (nonces, fees, revenue)
|
|
311
|
+
- **Transaction history** — recent actions with labels, status
|
|
312
|
+
- **Output browser** — UTXOs by basket with tags, spendable status
|
|
313
|
+
- **Certificate management** — issued/held certificates
|
|
314
|
+
|
|
315
|
+
Similar pattern to Sidekiq's web UI or Rails' ActiveStorage dashboard — an optional mountable app that gives operators a window into the wallet without touching the settlement flow. The wallet gem provides the engine; this provides the dashboard. Not a user-facing wallet UI — an operator tool for monitoring and debugging the payment infrastructure.
|
data/.rubocop.yml
CHANGED
data/CLAUDE.md
CHANGED
|
@@ -37,6 +37,12 @@ Standard Ruby gem layout generated by `bundle gem`:
|
|
|
37
37
|
|
|
38
38
|
- `lib/x402.rb` — main entry point, defines `X402` module
|
|
39
39
|
- `lib/x402/version.rb` — version constant
|
|
40
|
+
- `lib/x402/middleware.rb` — pure dispatcher (no blockchain knowledge)
|
|
41
|
+
- `lib/x402/bsv/gateway.rb` — base class for template-based gateways
|
|
42
|
+
- `lib/x402/bsv/pay_gateway.rb` — Coinbase v2 headers, server broadcasts
|
|
43
|
+
- `lib/x402/bsv/proof_gateway.rb` — merkleworks headers, client broadcasts
|
|
44
|
+
- `lib/x402/bsv/brc105_gateway.rb` — BSV Association BRC-105, BRC-29 derivation (no inheritance from Gateway)
|
|
45
|
+
- `lib/x402/bsv/prefix_store.rb` — pluggable replay protection for BRC-105 derivation prefixes
|
|
40
46
|
- `sig/x402.rbs` — RBS type signatures
|
|
41
47
|
- `x402-rack.gemspec` — gem specification (dependencies defined here, not in Gemfile)
|
|
42
48
|
|
data/DESIGN.md
CHANGED
|
@@ -1,216 +1,52 @@
|
|
|
1
|
-
# x402-rack Design
|
|
1
|
+
# x402-rack Design
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
### Middleware as Dispatcher (`X402::Middleware`)
|
|
6
|
-
|
|
7
|
-
The Rack middleware is a **pure dispatcher** — the gatekeeper. It has no blockchain knowledge. It:
|
|
8
|
-
|
|
9
|
-
1. Matches incoming requests against protected routes
|
|
10
|
-
2. Polls each configured gateway for challenge headers, returns all of them in the 402 response
|
|
11
|
-
3. Checks which proof/payment header the client sent, dispatches to the matching gateway
|
|
12
|
-
4. The gateway returns allow/deny — the middleware serves or rejects accordingly
|
|
13
|
-
|
|
14
|
-
The middleware never decodes transactions, checks mempool, broadcasts, or interacts with any blockchain network. It manages HTTP headers, route matching, and dispatch.
|
|
15
|
-
|
|
16
|
-
**The gatekeeper MUST NOT sign transactions or hold private keys.**
|
|
17
|
-
|
|
18
|
-
### Gateways (`X402::BSV::ProofGateway`, `X402::BSV::PayGateway`)
|
|
19
|
-
|
|
20
|
-
Gateways are pluggable backends that handle chain-specific settlement. They **can** hold keys and sign transactions — they are separate components from the gatekeeper. Each gateway:
|
|
21
|
-
|
|
22
|
-
- Builds challenge data (including partial transaction templates)
|
|
23
|
-
- Verifies and settles proofs
|
|
24
|
-
- Interacts with ARC and/or a treasury service via the BSV wallet
|
|
25
|
-
|
|
26
|
-
The gateway interface:
|
|
27
|
-
|
|
28
|
-
```ruby
|
|
29
|
-
# #challenge_headers(rack_request, route) → Hash
|
|
30
|
-
# #proof_header_names → Array<String>
|
|
31
|
-
# #settle!(header_name, proof_payload, rack_request, route) → result
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
The boundary test: could someone write `X402::EVM::Gateway` implementing this interface without touching `lib/x402/`? If yes, the separation is correct.
|
|
35
|
-
|
|
36
|
-
### Multi-Protocol Support
|
|
37
|
-
|
|
38
|
-
Different x402 ecosystems use different HTTP headers. A server can send **multiple challenge headers** simultaneously — the client picks the one it can satisfy. This is payment content negotiation.
|
|
39
|
-
|
|
40
|
-
| Scheme | Challenge header | Proof header | Receipt header |
|
|
41
|
-
|--------|-----------------|--------------|----------------|
|
|
42
|
-
| BSV-pay (ours) | `Payment-Required` | `Payment-Signature` | `Payment-Response` |
|
|
43
|
-
| BSV-proof (merkleworks) | `X402-Challenge` | `X402-Proof` | — |
|
|
44
|
-
|
|
45
|
-
Our PayGateway uses the Coinbase v2 headers (`Payment-*`) — the standard x402 ecosystem language. The ProofGateway uses the merkleworks `X402-*` headers. Header namespaces are reserved per ecosystem: `Payment-*` (Coinbase v2 / ours), `X402-*` (merkleworks), `x-bsv-*` (BRC-105 / BSV Association).
|
|
46
|
-
|
|
47
|
-
## Unified Template Model
|
|
48
|
-
|
|
49
|
-
### Both gateways produce partial transaction templates
|
|
50
|
-
|
|
51
|
-
The challenge includes a **partial transaction template** that the client extends by adding funding inputs (and optionally change outputs). This model unifies the two BSV schemes.
|
|
52
|
-
|
|
53
|
-
**Base behaviour** (`X402::BSV::Gateway`): build a partial tx with the payment output (amount to payee) and an OP_RETURN request binding output.
|
|
54
|
-
|
|
55
|
-
**ProofGateway override**: prepends the nonce UTXO input at index 0, signed with `SIGHASH_SINGLE | ANYONECANPAY | FORKID (0xC3)`. This locks the payment output (output 0) while allowing the client to append inputs and outputs freely.
|
|
56
|
-
|
|
57
|
-
**PayGateway**: inherits the base behaviour. Payment output + OP_RETURN binding, no nonce.
|
|
58
|
-
|
|
59
|
-
The client's job is identical regardless of scheme: add funding inputs, sign, and either broadcast (BSV-proof) or hand to the server (BSV-pay). The delegator fits the same way in both flows.
|
|
60
|
-
|
|
61
|
-
### Progressive enhancement via `extra.partialTx`
|
|
62
|
-
|
|
63
|
-
For the PayGateway's `Payment-Required` challenge (Coinbase v2 format), the partial tx template is carried in the `extra` field of the `accepts` entry:
|
|
64
|
-
|
|
65
|
-
```json
|
|
66
|
-
{
|
|
67
|
-
"x402Version": 2,
|
|
68
|
-
"resource": { "url": "/api/expensive" },
|
|
69
|
-
"accepts": [
|
|
70
|
-
{
|
|
71
|
-
"scheme": "exact",
|
|
72
|
-
"network": "bsv:mainnet",
|
|
73
|
-
"amount": "100",
|
|
74
|
-
"asset": "BSV",
|
|
75
|
-
"payTo": "1A1zP1...",
|
|
76
|
-
"maxTimeoutSeconds": 60,
|
|
77
|
-
"extra": {
|
|
78
|
-
"partialTx": "<base64 of partial tx template>"
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
]
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
**Basic client** (any x402 v2 client): ignores `extra.partialTx`, constructs a tx from scratch using `payTo` + `amount`.
|
|
3
|
+
Full documentation is in [`docs/`](docs/). This file serves as an index.
|
|
86
4
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
The template is an optimisation, not a requirement. `payTo` + `amount` are always sufficient.
|
|
90
|
-
|
|
91
|
-
### Request binding via OP_RETURN
|
|
92
|
-
|
|
93
|
-
The partial tx template includes an OP_RETURN output binding the payment to the specific request:
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
Output 0: payment (amount to payee)
|
|
97
|
-
Output 1: OP_RETURN <SHA256(method + path + query)>
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
At settlement, the gateway recomputes the hash and verifies it matches. Prevents template redirection between endpoints. Cheap (~30 bytes), on-chain, verifiable. Configurable strict/permissive mode.
|
|
101
|
-
|
|
102
|
-
### Why 0xC3 for the nonce signature
|
|
103
|
-
|
|
104
|
-
`SIGHASH_SINGLE | ANYONECANPAY | FORKID`:
|
|
105
|
-
- `SIGHASH_SINGLE`: commits only to `output[input_index]` — the nonce at input 0 protects only output 0 (the payment)
|
|
106
|
-
- `ANYONECANPAY`: excludes other inputs — funding and fee inputs can be appended freely
|
|
107
|
-
- `FORKID`: BSV fork ID flag (required)
|
|
108
|
-
|
|
109
|
-
Using `0xC1` (`SIGHASH_ALL | ANYONECANPAY`) would commit to ALL outputs, breaking extensibility.
|
|
110
|
-
|
|
111
|
-
## Two BSV Schemes
|
|
112
|
-
|
|
113
|
-
### BSV-proof (merkleworks x402 spec)
|
|
114
|
-
|
|
115
|
-
Client broadcasts, server checks mempool. Proof-of-payment model.
|
|
116
|
-
|
|
117
|
-
**Headers**: `X402-Challenge` / `X402-Proof`
|
|
118
|
-
|
|
119
|
-
**Challenge**: merkleworks JSON format including a pre-signed partial tx template (Profile B) with nonce UTXO at input 0 signed with `0xC3`, payment output at output 0, plus request binding metadata, expiry, and `require_mempool_accept: true`.
|
|
120
|
-
|
|
121
|
-
**Settlement**: gateway verifies tx structure, checks nonce spent at input 0, checks payment output, queries ARC for mempool visibility.
|
|
122
|
-
|
|
123
|
-
**Why client broadcasts** (per Rui at merkleworks): broadcasting is settlement, not authorisation. Server-side broadcast pushes the server towards a stateful payment processor. Client-side broadcast keeps it stateless.
|
|
124
|
-
|
|
125
|
-
**Requires**: treasury (nonce provision + template signing) + ARC (mempool queries).
|
|
126
|
-
|
|
127
|
-
### BSV-pay (our BSV-native scheme)
|
|
128
|
-
|
|
129
|
-
Server broadcasts via ARC. Uses Coinbase v2 header spec.
|
|
130
|
-
|
|
131
|
-
**Headers**: `Payment-Required` / `Payment-Signature` / `Payment-Response`
|
|
132
|
-
|
|
133
|
-
**Challenge**: Coinbase v2 `PaymentRequired` with BSV in `accepts` array, `extra.partialTx` carrying the template (payment output + OP_RETURN binding).
|
|
134
|
-
|
|
135
|
-
**Settlement**: gateway verifies payment output, verifies OP_RETURN binding, broadcasts to ARC (`X-WaitFor: SEEN_ON_NETWORK`, 5s timeout). ARC 200 → allow. ARC error → relay to client.
|
|
136
|
-
|
|
137
|
-
**No nonces needed**: ARC is the replay gate. Each tx can only be accepted once.
|
|
138
|
-
|
|
139
|
-
**Requires**: ARC only. No treasury, no nonce provision.
|
|
140
|
-
|
|
141
|
-
### Comparison
|
|
142
|
-
|
|
143
|
-
| | BSV-proof (merkleworks) | BSV-pay (ours) |
|
|
144
|
-
|---|---|---|
|
|
145
|
-
| Header spec | Merkleworks `X402-*` | Coinbase v2 `Payment-*` |
|
|
146
|
-
| Challenge header | `X402-Challenge` | `Payment-Required` |
|
|
147
|
-
| Proof header | `X402-Proof` | `Payment-Signature` |
|
|
148
|
-
| Receipt header | — | `Payment-Response` |
|
|
149
|
-
| Template contains | Nonce input (signed 0xC3) + payment output | Payment output + OP_RETURN binding |
|
|
150
|
-
| Who broadcasts | Client | Server (via ARC) |
|
|
151
|
-
| Nonce needed | Yes (challenge binding) | No (ARC is replay gate) |
|
|
152
|
-
| Request binding | Yes (in challenge metadata) | Yes (OP_RETURN in template) |
|
|
153
|
-
| Settlement check | Mempool visibility query | ARC broadcast response |
|
|
154
|
-
| Treasury needed | Yes | No |
|
|
155
|
-
| Minimum infrastructure | Treasury + ARC | ARC only |
|
|
156
|
-
| Ecosystem compatibility | Merkleworks BSV clients | Any x402 v2 client |
|
|
5
|
+
## Architecture
|
|
157
6
|
|
|
158
|
-
|
|
7
|
+
The middleware is a pure dispatcher — no blockchain knowledge, no keys. Gateways handle settlement.
|
|
159
8
|
|
|
160
|
-
|
|
161
|
-
|-----------|---------------|-------|
|
|
162
|
-
| **Gatekeeper** (`X402::Middleware`) | HTTP dispatch, route matching | No — MUST NOT hold keys |
|
|
163
|
-
| **Gateway** (`X402::BSV::*Gateway`) | Challenge templates, settlement, ARC interaction | Via wallet |
|
|
164
|
-
| **BSV Wallet** (`bsv-wallet` gem) | Key management, UTXO tracking, signing | Yes — the security boundary |
|
|
165
|
-
| **Treasury** (wallet role) | Mints nonce UTXOs, signs templates | Via wallet's nonce basket |
|
|
166
|
-
| **Delegator** (separate service) | Adds fee inputs, signs only fee inputs | Yes — but not our concern |
|
|
167
|
-
| **Client** (browser + CWI wallet) | Extends template, signs funding inputs | Yes — client's wallet |
|
|
9
|
+
→ [docs/architecture.md](docs/architecture.md)
|
|
168
10
|
|
|
169
|
-
|
|
11
|
+
## Settlement Schemes
|
|
170
12
|
|
|
171
|
-
|
|
13
|
+
Three BSV payment schemes:
|
|
172
14
|
|
|
173
|
-
|
|
15
|
+
- **BSV-pay** — server broadcasts via ARC. Coinbase v2 headers. Minimal infrastructure.
|
|
16
|
+
→ [docs/schemes/bsv-pay.md](docs/schemes/bsv-pay.md)
|
|
174
17
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
| Treasury | `x402-nonces` | `createAction` (mint nonces, sign templates), `listOutputs` |
|
|
178
|
-
| Delegator | `x402-fees` | `signAction` (sign fee inputs), `listOutputs` |
|
|
179
|
-
| Payment receipt | `x402-revenue` | `internalizeAction` (accept payments), `listOutputs` |
|
|
18
|
+
- **BSV-proof** — client broadcasts, server checks mempool. Merkleworks headers. Nonce-bound.
|
|
19
|
+
→ [docs/schemes/bsv-proof.md](docs/schemes/bsv-proof.md)
|
|
180
20
|
|
|
181
|
-
|
|
21
|
+
- **BRC-105** — BRC-29 derived addresses, AtomicBEEF transactions. BSV Association headers. Optional BRC-103 auth.
|
|
22
|
+
→ [docs/schemes/brc-105.md](docs/schemes/brc-105.md)
|
|
182
23
|
|
|
183
|
-
|
|
184
|
-
x402-rack (no keys, no wallet dependency in middleware)
|
|
185
|
-
└── X402::BSV::*Gateway → bsv-wallet (BRC-100 interface)
|
|
186
|
-
└── bsv-sdk (primitives)
|
|
187
|
-
```
|
|
24
|
+
## Security
|
|
188
25
|
|
|
189
|
-
|
|
26
|
+
HMAC payToSig, nonce provenance verification (0xC3), OP_RETURN binding, threat model.
|
|
190
27
|
|
|
191
|
-
|
|
28
|
+
→ [docs/security.md](docs/security.md)
|
|
192
29
|
|
|
193
|
-
|
|
30
|
+
## Operations
|
|
194
31
|
|
|
195
|
-
|
|
32
|
+
- **Deployment**: configuration, ARC setup, rate limiting → [docs/operations/deployment.md](docs/operations/deployment.md)
|
|
33
|
+
- **Performance**: benchmarks, scaling trade-offs, ARC bottleneck → [docs/operations/performance.md](docs/operations/performance.md)
|
|
34
|
+
- **Treasury**: nonce UTXO lifecycle, expiry, miner sweep → [docs/operations/treasury.md](docs/operations/treasury.md)
|
|
196
35
|
|
|
197
|
-
|
|
36
|
+
## Ecosystem
|
|
198
37
|
|
|
199
|
-
|
|
38
|
+
Coinbase v2, merkleworks, BRC-105. Header namespaces. Our position.
|
|
200
39
|
|
|
201
|
-
|
|
40
|
+
→ [docs/ecosystem.md](docs/ecosystem.md)
|
|
202
41
|
|
|
203
|
-
|
|
42
|
+
## Client Integration
|
|
204
43
|
|
|
205
|
-
|
|
44
|
+
bsv-x402, BRC-100/CWI, BSV Browser, retry logic, fee delegation.
|
|
206
45
|
|
|
207
|
-
|
|
46
|
+
→ [docs/client-integration.md](docs/client-integration.md)
|
|
208
47
|
|
|
209
|
-
|
|
210
|
-
1. Extract the gateway interface from the middleware
|
|
211
|
-
2. Implement `X402::BSV::Gateway` base class (payment output + OP_RETURN template)
|
|
212
|
-
3. Implement `X402::BSV::ProofGateway` (merkleworks compatibility)
|
|
213
|
-
4. Implement `X402::BSV::PayGateway` (BSV-native, ARC broadcast, Coinbase v2 headers)
|
|
214
|
-
5. Refactor middleware to multi-gateway dispatch
|
|
48
|
+
## Process Flows
|
|
215
49
|
|
|
216
|
-
|
|
50
|
+
- **PayGateway**: [docs/process-flow/pay-gateway.md](docs/process-flow/pay-gateway.md)
|
|
51
|
+
- **ProofGateway**: [docs/process-flow/proof-gateway.md](docs/process-flow/proof-gateway.md)
|
|
52
|
+
- **BRC105Gateway**: [docs/process-flow/brc105-gateway.md](docs/process-flow/brc105-gateway.md)
|
data/README.md
CHANGED
|
@@ -32,6 +32,12 @@ X402.configure do |config|
|
|
|
32
32
|
X402::BSV::PayGateway.new(
|
|
33
33
|
arc_url: "https://arc.taal.com",
|
|
34
34
|
arc_api_key: "..."
|
|
35
|
+
),
|
|
36
|
+
# BRC-105 gateway (BSV Association payment protocol)
|
|
37
|
+
X402::BSV::BRC105Gateway.new(
|
|
38
|
+
key_deriver: BSV::Wallet::KeyDeriver.new(server_private_key),
|
|
39
|
+
prefix_store: X402::BSV::PrefixStore::Memory.new,
|
|
40
|
+
arc_client: arc_client
|
|
35
41
|
)
|
|
36
42
|
]
|
|
37
43
|
|
|
@@ -49,12 +55,13 @@ use X402::Middleware
|
|
|
49
55
|
4. Middleware dispatches the proof to the matching gateway for settlement
|
|
50
56
|
5. Gateway verifies and settles — middleware serves or rejects
|
|
51
57
|
|
|
52
|
-
|
|
58
|
+
Three BSV settlement schemes are supported:
|
|
53
59
|
|
|
54
60
|
- **BSV-pay** (Coinbase v2 headers) — server broadcasts via ARC. No nonces, minimal infrastructure.
|
|
55
61
|
- **BSV-proof** (merkleworks x402) — client broadcasts, server checks mempool. Nonce-bound, request-binding.
|
|
62
|
+
- **BRC-105** (BSV Association `x-bsv-*` headers) — BRC-29 key derivation for unique payment addresses. Works standalone or composes with BRC-103 mutual authentication.
|
|
56
63
|
|
|
57
|
-
|
|
64
|
+
BSV-pay and BSV-proof produce partial transaction templates that clients extend. BRC-105 uses a different model — the client builds the entire transaction using BRC-29 derived addresses. See [DESIGN.md](DESIGN.md) for details.
|
|
58
65
|
|
|
59
66
|
## Development
|
|
60
67
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
## Middleware as Dispatcher (`X402::Middleware`)
|
|
4
|
+
|
|
5
|
+
The Rack middleware is a **pure dispatcher** — the gatekeeper. It has no blockchain knowledge. It:
|
|
6
|
+
|
|
7
|
+
1. Matches incoming requests against protected routes
|
|
8
|
+
2. Polls each configured gateway for challenge headers, returns all of them in the 402 response
|
|
9
|
+
3. Checks which proof/payment header the client sent, dispatches to the matching gateway
|
|
10
|
+
4. The gateway returns allow/deny — the middleware serves or rejects accordingly
|
|
11
|
+
|
|
12
|
+
The middleware never decodes transactions, checks mempool, broadcasts, or interacts with any blockchain network. It manages HTTP headers, route matching, and dispatch.
|
|
13
|
+
|
|
14
|
+
**The gatekeeper MUST NOT sign transactions or hold private keys.**
|
|
15
|
+
|
|
16
|
+
## Gateways
|
|
17
|
+
|
|
18
|
+
Gateways are pluggable backends that handle chain-specific settlement. They **can** hold keys and sign transactions — they are separate components from the gatekeeper. Each gateway:
|
|
19
|
+
|
|
20
|
+
- Builds challenge data (including partial transaction templates)
|
|
21
|
+
- Verifies and settles proofs
|
|
22
|
+
- Interacts with ARC and/or a treasury service via the BSV wallet
|
|
23
|
+
|
|
24
|
+
### Gateway Interface
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
# #challenge_headers(rack_request, route) → Hash
|
|
28
|
+
# #proof_header_names → Array<String>
|
|
29
|
+
# #settle!(header_name, proof_payload, rack_request, route) → result
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The boundary test: could someone write `X402::EVM::Gateway` implementing this interface without touching `lib/x402/`? If yes, the separation is correct.
|
|
33
|
+
|
|
34
|
+
### Built-in Gateways
|
|
35
|
+
|
|
36
|
+
- **`X402::BSV::PayGateway`** — Coinbase v2 headers, server broadcasts via ARC. See [schemes/bsv-pay.md](schemes/bsv-pay.md).
|
|
37
|
+
- **`X402::BSV::ProofGateway`** — merkleworks headers, client broadcasts, server checks mempool. See [schemes/bsv-proof.md](schemes/bsv-proof.md).
|
|
38
|
+
- **`X402::BSV::BRC105Gateway`** — BSV Association `x-bsv-*` headers, BRC-29 derived addresses, AtomicBEEF transactions. See [schemes/brc-105.md](schemes/brc-105.md).
|
|
39
|
+
|
|
40
|
+
## Payment Content Negotiation
|
|
41
|
+
|
|
42
|
+
Different x402 ecosystems use different HTTP headers. A server can send **multiple challenge headers** simultaneously — the client picks the one it can satisfy.
|
|
43
|
+
|
|
44
|
+
| Scheme | Challenge headers | Proof header | Receipt header |
|
|
45
|
+
|--------|------------------|--------------|----------------|
|
|
46
|
+
| BSV-pay (ours) | `Payment-Required` | `Payment-Signature` | `Payment-Response` |
|
|
47
|
+
| BSV-proof (merkleworks) | `X402-Challenge` | `X402-Proof` | — |
|
|
48
|
+
| BRC-105 (BSV Association) | `x-bsv-payment-satoshis-required`, `x-bsv-payment-derivation-prefix`, `x-bsv-payment-identity-key`* | `x-bsv-payment` | `x-bsv-payment-result` |
|
|
49
|
+
|
|
50
|
+
\* `x-bsv-payment-identity-key` is omitted when BRC-103 middleware is present upstream.
|
|
51
|
+
|
|
52
|
+
Header namespaces are reserved per ecosystem:
|
|
53
|
+
- `Payment-*` — Coinbase v2 / our PayGateway
|
|
54
|
+
- `X402-*` — merkleworks / our ProofGateway
|
|
55
|
+
- `x-bsv-*` — BRC-105 / BSV Association (our BRC105Gateway)
|
|
56
|
+
|
|
57
|
+
## Transaction Models
|
|
58
|
+
|
|
59
|
+
### Template-based (PayGateway, ProofGateway)
|
|
60
|
+
|
|
61
|
+
Both template-based gateways produce **partial transaction templates** that the client extends by adding funding inputs (and optionally change outputs).
|
|
62
|
+
|
|
63
|
+
**Base behaviour** (`X402::BSV::Gateway`): build a partial tx with the payment output (amount to payee) and an OP_RETURN request binding output.
|
|
64
|
+
|
|
65
|
+
**ProofGateway override**: prepends the nonce UTXO input at index 0, signed with `SIGHASH_SINGLE | ANYONECANPAY | FORKID (0xC3)`. This locks the payment output (output 0) while allowing the client to append inputs and outputs freely.
|
|
66
|
+
|
|
67
|
+
**PayGateway**: inherits the base behaviour. Payment output + OP_RETURN binding, no nonce.
|
|
68
|
+
|
|
69
|
+
The client's job is identical for template-based gateways: add funding inputs, sign, and either broadcast (BSV-proof) or hand to the server (BSV-pay).
|
|
70
|
+
|
|
71
|
+
### Derivation-based (BRC105Gateway)
|
|
72
|
+
|
|
73
|
+
BRC105Gateway uses a fundamentally different approach — **no partial transaction template**. Instead:
|
|
74
|
+
|
|
75
|
+
1. The server advertises a derivation prefix (random nonce) and its identity key
|
|
76
|
+
2. The client derives a unique payment address using BRC-29 (BRC-42 key derivation with protocol ID `[2, "3241645161d8"]` and key ID `"#{prefix} #{suffix}"`)
|
|
77
|
+
3. The client builds the entire transaction independently, paying to the derived address
|
|
78
|
+
4. The server re-derives the expected address and verifies the payment output
|
|
79
|
+
|
|
80
|
+
This eliminates the need for OP_RETURN binding, payTo HMAC, or any shared transaction state. BRC105Gateway does not inherit from `Gateway` — it uses composition via `KeyDeriver`.
|
|
81
|
+
|
|
82
|
+
### Request Binding via OP_RETURN
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
Output 0: payment (amount to payee)
|
|
86
|
+
Output 1: OP_RETURN "x402" <SHA256(method + path + query)>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The `x402` protocol tag makes payments discoverable on-chain. The SHA-256 hash binds the payment to the specific HTTP request. Configurable strict/permissive mode.
|
|
90
|
+
|
|
91
|
+
### Why 0xC3 for the Nonce Signature
|
|
92
|
+
|
|
93
|
+
`SIGHASH_SINGLE | ANYONECANPAY | FORKID`:
|
|
94
|
+
- `SIGHASH_SINGLE`: commits only to `output[input_index]` — the nonce at input 0 protects only output 0 (the payment)
|
|
95
|
+
- `ANYONECANPAY`: excludes other inputs — funding and fee inputs can be appended freely
|
|
96
|
+
- `FORKID`: BSV fork ID flag (required)
|
|
97
|
+
|
|
98
|
+
Using `0xC1` (`SIGHASH_ALL | ANYONECANPAY`) would commit to ALL outputs, breaking extensibility.
|
|
99
|
+
|
|
100
|
+
## Component Boundaries
|
|
101
|
+
|
|
102
|
+
| Component | Responsibility | Keys? |
|
|
103
|
+
|-----------|---------------|-------|
|
|
104
|
+
| **Gatekeeper** (`X402::Middleware`) | HTTP dispatch, route matching | No — MUST NOT hold keys |
|
|
105
|
+
| **Gateway** (`X402::BSV::*Gateway`) | Challenge templates, settlement, ARC interaction | Via wallet |
|
|
106
|
+
| **BSV Wallet** (`bsv-wallet` gem) | Key management, UTXO tracking, signing | Yes — the security boundary |
|
|
107
|
+
| **Treasury** (wallet role) | Mints nonce UTXOs, signs templates | Via wallet's nonce basket |
|
|
108
|
+
| **Delegator** (separate service) | Adds fee inputs, signs only fee inputs | Yes — but not our concern |
|
|
109
|
+
| **Client** (browser + CWI wallet) | Extends template, signs funding inputs | Yes — client's wallet |
|
|
110
|
+
|
|
111
|
+
### Dependency Chain
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
x402-rack (no keys, no wallet dependency in middleware)
|
|
115
|
+
└── X402::BSV::*Gateway → bsv-wallet (BRC-100 interface)
|
|
116
|
+
└── bsv-sdk (primitives)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The middleware itself has no dependency on `bsv-wallet` or `bsv-sdk`. Only the gateway classes do.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Client Integration
|
|
2
|
+
|
|
3
|
+
## bsv-x402 (JavaScript/TypeScript)
|
|
4
|
+
|
|
5
|
+
The client library wraps `fetch()` and handles the x402 payment flow transparently:
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
import { x402Fetch } from 'bsv-x402'
|
|
9
|
+
|
|
10
|
+
const response = await x402Fetch('https://api.example.com/paid-endpoint')
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
When the server responds with `402 Payment Required`, the library:
|
|
14
|
+
|
|
15
|
+
1. Parses the challenge header (`Payment-Required` or `X402-Challenge`)
|
|
16
|
+
2. Constructs a payment transaction via `window.CWI` (BRC-100 wallet)
|
|
17
|
+
3. Extends the `extra.partialTx` template if present (or builds from scratch)
|
|
18
|
+
4. Broadcasts the transaction (ProofGateway) or hands it to the server (PayGateway)
|
|
19
|
+
5. Retries the original request with the proof header
|
|
20
|
+
|
|
21
|
+
The app developer just uses `x402Fetch` in place of `fetch`. The UI doesn't flicker — the data just arrives.
|
|
22
|
+
|
|
23
|
+
**Repository**: [sgbett/bsv-x402](https://github.com/sgbett/bsv-x402)
|
|
24
|
+
**npm**: `bsv-x402`
|
|
25
|
+
|
|
26
|
+
## BRC-100 and `window.CWI`
|
|
27
|
+
|
|
28
|
+
[BRC-100](https://github.com/bitcoin-sv/BRCs/blob/master/wallet/0100.md) is a vendor-neutral wallet-to-application interface. Compliant wallets inject a `window.CWI` object into web pages — analogous to `window.ethereum` in the Ethereum ecosystem.
|
|
29
|
+
|
|
30
|
+
Key methods used by the x402 client:
|
|
31
|
+
- `createAction()` — construct and sign transactions
|
|
32
|
+
- `signAction()` — sign previously created transactions
|
|
33
|
+
- `listOutputs()` — find available UTXOs for funding
|
|
34
|
+
|
|
35
|
+
Because this uses BRC-100 (not a browser-specific API), it works with any compliant wallet.
|
|
36
|
+
|
|
37
|
+
## BSV Browser
|
|
38
|
+
|
|
39
|
+
[BSV Browser](https://github.com/bsv-blockchain/bsv-browser) is the reference BRC-100 wallet implementation. It exposes `window.CWI` and is the primary target for testing the end-to-end flow.
|
|
40
|
+
|
|
41
|
+
The planned integration path:
|
|
42
|
+
1. User visits a site protected by x402-rack
|
|
43
|
+
2. BSV Browser's `window.CWI` is available
|
|
44
|
+
3. `bsv-x402` intercepts the 402 and calls `CWI.createAction()`
|
|
45
|
+
4. Payment happens seamlessly — the user sees the content load
|
|
46
|
+
|
|
47
|
+
## Client Behaviour by Scheme
|
|
48
|
+
|
|
49
|
+
### BRC105Gateway (BRC-105)
|
|
50
|
+
|
|
51
|
+
1. Client receives `x-bsv-payment-satoshis-required`, `x-bsv-payment-derivation-prefix`, and `x-bsv-payment-identity-key` headers
|
|
52
|
+
2. Client chooses a random derivation suffix
|
|
53
|
+
3. Derives payment address using BRC-29: `KeyDeriver.derive_public_key([2, "3241645161d8"], "#{prefix} #{suffix}", server_identity_key)`
|
|
54
|
+
4. Builds a transaction paying to the derived P2PKH address
|
|
55
|
+
5. Encodes the transaction as AtomicBEEF (BRC-95), base64
|
|
56
|
+
6. Sends `x-bsv-payment` header with JSON: `{ "derivationPrefix": "...", "derivationSuffix": "...", "transaction": "<base64 AtomicBEEF>" }`
|
|
57
|
+
7. Server verifies derivation, broadcasts via ARC, returns `x-bsv-payment-result`
|
|
58
|
+
|
|
59
|
+
**BRC-103 authenticated mode**: the client already has the server's identity key from the BRC-103 handshake — the `x-bsv-payment-identity-key` header is omitted. The client's authenticated identity key is used as the BRC-29 counterparty, binding the payment to the mutual-auth session.
|
|
60
|
+
|
|
61
|
+
**BRC-100 wallet integration**: BRC-105 clients can use `wallet.createAction()` to build the transaction and `wallet.internalizeAction()` on the server to process it. The derivation prefix/suffix flow integrates natively with BRC-100's payment handling.
|
|
62
|
+
|
|
63
|
+
### PayGateway (BSV-pay)
|
|
64
|
+
|
|
65
|
+
1. Client receives `Payment-Required` header
|
|
66
|
+
2. Reads `extra.partialTx` template (if present)
|
|
67
|
+
3. Extends template with funding inputs, signs
|
|
68
|
+
4. Sends `Payment-Signature` header with the raw tx
|
|
69
|
+
5. Server broadcasts via ARC
|
|
70
|
+
6. Client receives 200 + `Payment-Response` receipt
|
|
71
|
+
|
|
72
|
+
**On failure**: present "payment failed, retry?" option to the user. This is the simple deployment path.
|
|
73
|
+
|
|
74
|
+
### ProofGateway (BSV-proof)
|
|
75
|
+
|
|
76
|
+
1. Client receives `X402-Challenge` header
|
|
77
|
+
2. Reads `partial_tx_b64` template (Profile B, if present)
|
|
78
|
+
3. Extends template with funding inputs, signs
|
|
79
|
+
4. **Broadcasts the transaction to the BSV network**
|
|
80
|
+
5. Sends `X402-Proof` header with txid + rawtx
|
|
81
|
+
6. Server checks mempool, serves content
|
|
82
|
+
|
|
83
|
+
**On failure**: implement automatic retry logic. The client has already broadcast — the tx is either in mempool or it isn't. Retry the proof submission if the first attempt was a timing issue.
|
|
84
|
+
|
|
85
|
+
This is the enterprise path — the client handles broadcasting, the server stays stateless.
|
|
86
|
+
|
|
87
|
+
## Fee Delegation
|
|
88
|
+
|
|
89
|
+
For clients that don't hold BSV (zero-preload), a delegator service adds fee inputs:
|
|
90
|
+
|
|
91
|
+
1. Client constructs partial tx (payment output only, no fee inputs)
|
|
92
|
+
2. Client signs with `SIGHASH_ALL | ANYONECANPAY | FORKID` (`0xC1`)
|
|
93
|
+
3. Client sends partial tx to the delegator
|
|
94
|
+
4. Delegator appends fee inputs, signs only its fee inputs
|
|
95
|
+
5. Delegator returns completed transaction
|
|
96
|
+
6. Client proceeds with normal proof/payment flow
|
|
97
|
+
|
|
98
|
+
The delegator is between the client and the network — the server never talks to it. BSV transaction fees are negligible (1-50 sats), so fee delegation is a convenience, not a requirement.
|
data/docs/ecosystem.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Ecosystem
|
|
2
|
+
|
|
3
|
+
## x402 Implementations
|
|
4
|
+
|
|
5
|
+
Three x402 ecosystems exist with different conventions. Our middleware supports all of them via the multi-gateway dispatch model.
|
|
6
|
+
|
|
7
|
+
### Coinbase x402 v2 (broad ecosystem)
|
|
8
|
+
|
|
9
|
+
- **Headers**: `Payment-Required` / `Payment-Signature` / `Payment-Response`
|
|
10
|
+
- **Flow**: client signs authorisation → facilitator broadcasts (verify → serve → settle)
|
|
11
|
+
- **Replay protection**: chain-native (EIP-712 nonces, Solana blockhash)
|
|
12
|
+
- **Request binding**: resource URL only (no method/path/query hash)
|
|
13
|
+
- **Multi-chain**: `accepts` array for multi-chain/multi-scheme negotiation
|
|
14
|
+
- **Spec**: https://docs.x402.org
|
|
15
|
+
- **Our PR**: https://github.com/coinbase/x402/pull/1844 (BSV scheme spec)
|
|
16
|
+
|
|
17
|
+
### Merkleworks x402 (BSV-specific)
|
|
18
|
+
|
|
19
|
+
- **Headers**: `X402-Challenge` / `X402-Proof`
|
|
20
|
+
- **Flow**: client broadcasts → server checks mempool (proof-of-payment)
|
|
21
|
+
- **Replay protection**: 1-sat nonce UTXO (single-spend at consensus layer)
|
|
22
|
+
- **Request binding**: strong (method, path, query, headers hash, body hash)
|
|
23
|
+
- **Spec**: https://github.com/ruidasilva/merkleworks-x402-spec
|
|
24
|
+
- **Reference impl**: https://github.com/merkleworks/x402-bsv
|
|
25
|
+
|
|
26
|
+
### BRC-105 (BSV Association BRC)
|
|
27
|
+
|
|
28
|
+
- **Headers**: `x-bsv-payment-version` / `x-bsv-payment-satoshis-required` / `x-bsv-payment-derivation-prefix` / `x-bsv-payment`
|
|
29
|
+
- **Flow**: authenticated (BRC-103/104) payment with derivation-based unique addresses
|
|
30
|
+
- **Replay protection**: server-tracked derivation prefixes (server-side state)
|
|
31
|
+
- **Identity**: requires BRC-103 mutual authentication (but could work without — see below)
|
|
32
|
+
- **Spec**: https://github.com/bitcoin-sv/BRCs/blob/master/payments/0105.md
|
|
33
|
+
|
|
34
|
+
## Header Namespace Reservations
|
|
35
|
+
|
|
36
|
+
| Namespace | Ecosystem |
|
|
37
|
+
|-----------|-----------|
|
|
38
|
+
| `Payment-*` | Coinbase v2 / our PayGateway |
|
|
39
|
+
| `X402-*` | Merkleworks / our ProofGateway |
|
|
40
|
+
| `x-bsv-*` | BRC-105 / BSV Association |
|
|
41
|
+
|
|
42
|
+
These namespaces must not overlap. When designing new headers or gateway types, check which namespace the target ecosystem uses.
|
|
43
|
+
|
|
44
|
+
## Our Position
|
|
45
|
+
|
|
46
|
+
Our PayGateway implements the Coinbase v2 header spec with BSV as the settlement network. This makes BSV a first-class citizen in the broader x402 ecosystem — any Coinbase-compatible server or client can interoperate with us.
|
|
47
|
+
|
|
48
|
+
We also support merkleworks via a separate ProofGateway that uses the `X402-*` headers.
|
|
49
|
+
|
|
50
|
+
Our BRC105Gateway implements the BSV Association's native payment protocol using `x-bsv-*` headers, enabling interoperability with BRC-100 wallets and the broader BSV ecosystem tooling.
|
|
51
|
+
|
|
52
|
+
Coinbase will likely gatekeep BSV from their ecosystem — our conformance is about making it easy for others to integrate BSV as a supported network.
|
|
53
|
+
|
|
54
|
+
## BRC-105: Standalone and Authenticated Modes
|
|
55
|
+
|
|
56
|
+
BRC-105 assumes BRC-103/104 mutual authentication, but `BRC105Gateway` supports both modes:
|
|
57
|
+
|
|
58
|
+
### Standalone mode (no BRC-103)
|
|
59
|
+
|
|
60
|
+
The gateway advertises its identity key in the `x-bsv-payment-identity-key` challenge header. The client uses this for BRC-29 key derivation. Counterparty is `"anyone"` — anonymous payments similar to PayGateway but using BSV-native headers.
|
|
61
|
+
|
|
62
|
+
- No handshake required
|
|
63
|
+
- Transport security handled by TLS
|
|
64
|
+
- Replay protection via server-tracked derivation prefixes
|
|
65
|
+
|
|
66
|
+
### Authenticated mode (with BRC-103 middleware)
|
|
67
|
+
|
|
68
|
+
When BRC-103 middleware is present upstream in the Rack stack, the gateway reads the client's authenticated identity key from `env['brc103.identity_key']` and uses it as the BRC-29 derivation counterparty. The identity key challenge header is omitted (the client already has the server's key from the BRC-103 handshake).
|
|
69
|
+
|
|
70
|
+
- Full BRC-105 compliance
|
|
71
|
+
- Payment bound to authenticated identity
|
|
72
|
+
- Stronger replay protection (session nonces + prefix tracking)
|
|
73
|
+
|
|
74
|
+
The gateway detects the mode automatically — no configuration change required. This allows composing `BRC103Middleware + BRC105Gateway` for the full authenticated flow, or using `BRC105Gateway` alone for the simpler anonymous path.
|
|
75
|
+
|
|
76
|
+
## Related Projects
|
|
77
|
+
|
|
78
|
+
- **x402-rack** (this gem): server-side Rack middleware
|
|
79
|
+
- **bsv-x402** (npm): client-side fetch wrapper ([sgbett/bsv-x402](https://github.com/sgbett/bsv-x402))
|
|
80
|
+
- **bsv-sdk** (gem): BSV primitives — keys, transactions, scripts, ARC
|
|
81
|
+
- **bsv-wallet** (gem): BRC-100 wallet interface
|
|
82
|
+
- **BSV Browser**: BRC-100 wallet with `window.CWI` ([bsv-blockchain/bsv-browser](https://github.com/bsv-blockchain/bsv-browser))
|
|
83
|
+
- **402index.io**: x402 endpoint aggregator with payment flow examples
|