x402-rack 0.1.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 +7 -0
- data/.claude/plans/20260325-bsv-module.md +384 -0
- data/.claude/plans/20260326-rack-stack-architecture.md +304 -0
- data/.rspec +3 -0
- data/.rubocop.yml +33 -0
- data/CLAUDE.md +47 -0
- data/DESIGN.md +216 -0
- data/LICENSE.txt +56 -0
- data/README.md +74 -0
- data/Rakefile +18 -0
- data/docs/process-flow/pay_gateway.md +69 -0
- data/lib/x402/bsv/gateway.rb +148 -0
- data/lib/x402/bsv/pay_gateway.rb +160 -0
- data/lib/x402/bsv/proof_gateway.rb +145 -0
- data/lib/x402/bsv.rb +5 -0
- data/lib/x402/configuration.rb +89 -0
- data/lib/x402/errors.rb +17 -0
- data/lib/x402/middleware.rb +86 -0
- data/lib/x402/protocol/base64url.rb +19 -0
- data/lib/x402/protocol/challenge.rb +63 -0
- data/lib/x402/protocol/proof.rb +36 -0
- data/lib/x402/protocol/request_binding.rb +32 -0
- data/lib/x402/settlement_result.rb +14 -0
- data/lib/x402/verification/protocol_checks.rb +43 -0
- data/lib/x402/version.rb +5 -0
- data/lib/x402.rb +24 -0
- data/sig/x402.rbs +4 -0
- metadata +140 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# Architecture: The Rack Stack for BSV HTTP Services
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
HTTP services monetised with BSV require several orthogonal concerns to be handled: identity, payment, fee sponsorship, and settlement. These concerns compose naturally as a Rack middleware stack, where each layer handles one responsibility and passes context downstream.
|
|
6
|
+
|
|
7
|
+
This plan defines the component parts and their boundaries. It is deliberately not concerned with implementation specifics — it maps the jigsaw pieces and how they fit together.
|
|
8
|
+
|
|
9
|
+
## The Stack
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
┌─────────────────────────────────────────────┐
|
|
13
|
+
│ TLS (transport security) │ Not our concern — handled by web server
|
|
14
|
+
├─────────────────────────────────────────────┤
|
|
15
|
+
│ BRC-104 Auth Middleware (optional) │ Identity — who is this?
|
|
16
|
+
├─────────────────────────────────────────────┤
|
|
17
|
+
│ X402 Middleware (gatekeeper) │ Payment gating — have they paid?
|
|
18
|
+
├─────────────────────────────────────────────┤
|
|
19
|
+
│ Application │ Business logic — serve the resource
|
|
20
|
+
└─────────────────────────────────────────────┘
|
|
21
|
+
|
|
22
|
+
┌──────────────┐
|
|
23
|
+
│ Gateways │ Settlement — how do they pay?
|
|
24
|
+
└──────┬───────┘
|
|
25
|
+
│
|
|
26
|
+
┌───────────┼───────────┐
|
|
27
|
+
│ │ │
|
|
28
|
+
┌───┴────┐ ┌───┴────┐ ┌────┴────┐
|
|
29
|
+
│ BSV │ │ BSV │ │ BRC-105 │ ... extensible
|
|
30
|
+
│ Proof │ │ Pay │ │ Gateway │
|
|
31
|
+
└───┬────┘ └───┬────┘ └────┬────┘
|
|
32
|
+
│ │ │
|
|
33
|
+
└───────────┼───────────┘
|
|
34
|
+
│
|
|
35
|
+
┌──────┴───────┐
|
|
36
|
+
│ BSV Wallet │ Server-side wallet (BRC-100 interface)
|
|
37
|
+
│ (bsv-wallet │ Manages keys, UTXOs, signing
|
|
38
|
+
│ gem) │ via baskets: nonces, fees, revenue
|
|
39
|
+
└──────┬───────┘
|
|
40
|
+
│
|
|
41
|
+
┌──────┴───────┐
|
|
42
|
+
│ bsv-sdk │ Primitives — keys, txs, scripts, ARC
|
|
43
|
+
└──────────────┘
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Layer 1: Identity (BRC-103/104)
|
|
47
|
+
|
|
48
|
+
**What**: Mutual authentication between client and server. Establishes who is making the request.
|
|
49
|
+
|
|
50
|
+
**Rack middleware**: Verifies `x-bsv-auth-*` headers, validates BRC-103 signatures over the request (method, path, query, whitelisted headers, body). Places the authenticated identity into the Rack env.
|
|
51
|
+
|
|
52
|
+
**Provides to downstream**: `env['brc103.identity_key']` (33-byte compressed public key), session nonces, certificate data.
|
|
53
|
+
|
|
54
|
+
**Optional**: Not all payment flows require identity. Anonymous payments (merkleworks x402, BSV-pay) work without this layer. BRC-105 payments require it.
|
|
55
|
+
|
|
56
|
+
**Not our gem**: This would be a separate gem (`brc104-rack` or similar). Our x402 middleware doesn't depend on it — it just benefits from it when present.
|
|
57
|
+
|
|
58
|
+
## Layer 2: Payment Gating (X402 Middleware)
|
|
59
|
+
|
|
60
|
+
**What**: The gatekeeper. Decides whether a request should be served based on payment status.
|
|
61
|
+
|
|
62
|
+
**Responsibilities**:
|
|
63
|
+
- Route matching — is this request to a protected endpoint?
|
|
64
|
+
- Challenge issuance — poll gateways, return 402 with challenge headers
|
|
65
|
+
- Proof dispatch — detect which proof header arrived, dispatch to the matching gateway
|
|
66
|
+
- Access control — gateway says allow/deny, middleware serves or rejects
|
|
67
|
+
|
|
68
|
+
**Does NOT do**:
|
|
69
|
+
- Sign transactions
|
|
70
|
+
- Hold private keys
|
|
71
|
+
- Decode blockchain data
|
|
72
|
+
- Talk to ARC or any network service
|
|
73
|
+
- Know what chain or protocol is being used
|
|
74
|
+
|
|
75
|
+
**Rack env it reads**: route configuration, gateway list. Optionally `env['brc103.identity_key']` (passes through to gateways that need it).
|
|
76
|
+
|
|
77
|
+
**Rack env it sets**: settlement result (for downstream logging/receipt headers).
|
|
78
|
+
|
|
79
|
+
**This is our gem**: `x402-rack`.
|
|
80
|
+
|
|
81
|
+
## Layer 3: Settlement (Gateways)
|
|
82
|
+
|
|
83
|
+
**What**: The bridge between HTTP and blockchain. Each gateway knows one settlement protocol.
|
|
84
|
+
|
|
85
|
+
**Responsibilities**:
|
|
86
|
+
- Build challenge data (headers + partial tx templates)
|
|
87
|
+
- Verify and settle proofs/payments
|
|
88
|
+
- Interact with external services (ARC, treasury, wallets)
|
|
89
|
+
- Can hold keys and sign transactions
|
|
90
|
+
|
|
91
|
+
**Gateway types identified so far**:
|
|
92
|
+
|
|
93
|
+
### BSV PayGateway (our BSV-native scheme — Coinbase v2 headers)
|
|
94
|
+
- **Challenge**: `PaymentRequired` JSON with BSV in `accepts` array + partial tx template (payment output only)
|
|
95
|
+
- **Settlement**: verify tx structure, broadcast via ARC, return `Payment-Response`
|
|
96
|
+
- **Headers**: `Payment-Required` / `Payment-Signature` / `Payment-Response`
|
|
97
|
+
- **Needs**: ARC only
|
|
98
|
+
- **Identity**: not required
|
|
99
|
+
- **Ecosystem**: standard x402 v2 — interoperable with any Coinbase-compatible client
|
|
100
|
+
|
|
101
|
+
### BSV ProofGateway (merkleworks x402)
|
|
102
|
+
- **Challenge**: pre-signed partial tx template (nonce at input 0 with 0xC3, payment output at output 0) + merkleworks challenge JSON (binding, expiry)
|
|
103
|
+
- **Settlement**: verify tx structure, check mempool via ARC
|
|
104
|
+
- **Headers**: `X402-Challenge` / `X402-Proof`
|
|
105
|
+
- **Needs**: treasury (nonce key) + ARC
|
|
106
|
+
- **Identity**: not required
|
|
107
|
+
- **Ecosystem**: merkleworks BSV-specific
|
|
108
|
+
|
|
109
|
+
### BRC-105 Gateway (future)
|
|
110
|
+
- **Challenge**: derivation prefix + satoshis required (multiple `x-bsv-payment-*` headers)
|
|
111
|
+
- **Settlement**: verify derivation, validate tx via BRC-100 wallet (`internalizeAction`)
|
|
112
|
+
- **Headers**: `x-bsv-payment-*` / `x-bsv-payment`
|
|
113
|
+
- **Needs**: BRC-100 wallet
|
|
114
|
+
- **Identity**: REQUIRED (reads `env['brc103.identity_key']` for derivation)
|
|
115
|
+
- **Ecosystem**: BSV Association BRC standard
|
|
116
|
+
|
|
117
|
+
## Server-Side Wallet (`bsv-wallet` gem)
|
|
118
|
+
|
|
119
|
+
### One wallet, multiple roles
|
|
120
|
+
|
|
121
|
+
The treasury, delegator, and payment receipt functions are not separate services — they are **roles** a single wallet plays, distinguished by which baskets and keys they use.
|
|
122
|
+
|
|
123
|
+
| Role | Basket | Key | Operations |
|
|
124
|
+
|------|--------|-----|------------|
|
|
125
|
+
| Treasury | `x402-nonces` | Nonce key | `createAction` (mint nonces, sign templates), `listOutputs` |
|
|
126
|
+
| Delegator | `x402-fees` | Fee key | `signAction` (sign fee inputs), `listOutputs` |
|
|
127
|
+
| Payment receipt | `x402-revenue` | Payee key | `internalizeAction` (accept payments), `listOutputs` |
|
|
128
|
+
|
|
129
|
+
### BRC-100 as the API boundary
|
|
130
|
+
|
|
131
|
+
The wallet exposes a BRC-100 interface (or the subset we need). Gateways talk to the wallet exclusively through these methods. They never touch keys directly. The wallet is the security boundary.
|
|
132
|
+
|
|
133
|
+
**BRC-100 methods we need** (subset of the full 28+ method spec):
|
|
134
|
+
- `createAction` — construct and sign transactions (nonce minting, template signing)
|
|
135
|
+
- `signAction` — sign previously created transactions (fee input signing)
|
|
136
|
+
- `internalizeAction` — accept incoming transactions (payment receipt)
|
|
137
|
+
- `listOutputs` — query UTXOs by basket (find nonces, fee UTXOs, revenue)
|
|
138
|
+
- `getPublicKey` — derive keys (BRC-29 derivations for BRC-105, nonce keys)
|
|
139
|
+
|
|
140
|
+
### Where it lives
|
|
141
|
+
|
|
142
|
+
The `bsv-ruby-sdk` repo (`sgbett/bsv-ruby-sdk`) already has:
|
|
143
|
+
- A monorepo pattern with multiple gemspecs (`bsv-sdk`, `bsv-attest`)
|
|
144
|
+
- A basic wallet at `lib/bsv/wallet/wallet.rb` (single-key, UTXO funding + signing)
|
|
145
|
+
- Full key management including BIP-32 HD derivation
|
|
146
|
+
- Transaction construction with BIP-143 sighash (FORKID)
|
|
147
|
+
- ARC provider
|
|
148
|
+
- BEEF serialisation
|
|
149
|
+
|
|
150
|
+
The `bsv-wallet` gem adds a third gemspec to this repo:
|
|
151
|
+
- BRC-100 interface layer over the existing primitives
|
|
152
|
+
- Basket-based UTXO tracking (nonces, fees, revenue)
|
|
153
|
+
- Multi-key support
|
|
154
|
+
- Builds on the existing `BSV::Wallet::Wallet` as the internal engine
|
|
155
|
+
|
|
156
|
+
### Deployment options
|
|
157
|
+
|
|
158
|
+
The BRC-100 interface works regardless of deployment:
|
|
159
|
+
|
|
160
|
+
1. **In-process** — `require 'bsv-wallet'`, gateway calls wallet methods directly. Simplest. Keys in the same process.
|
|
161
|
+
2. **Local service** — wallet runs as a separate process, gateways call via HTTP. Process isolation.
|
|
162
|
+
3. **Remote service** — wallet on a different machine. Network boundary. Strongest isolation.
|
|
163
|
+
|
|
164
|
+
Same interface, different transports. Start with in-process, separate later if needed.
|
|
165
|
+
|
|
166
|
+
### Separation from middleware
|
|
167
|
+
|
|
168
|
+
The wallet gem has a strict public API (BRC-100 methods only). The `x402-rack` gem depends on `bsv-wallet` but only through this interface. The middleware itself (`X402::Middleware`) never talks to the wallet — only gateways do.
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
x402-rack (no keys, no wallet dependency)
|
|
172
|
+
└── X402::Middleware
|
|
173
|
+
└── X402::BSV::ProofGateway ──→ BSV::Wallet (BRC-100 API)
|
|
174
|
+
└── X402::BSV::PayGateway ────→ BSV::Wallet (BRC-100 API, for ARC only)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## External Services
|
|
178
|
+
|
|
179
|
+
### ARC (BSV transaction processor)
|
|
180
|
+
- Broadcasts transactions and returns acceptance/rejection
|
|
181
|
+
- Queries mempool visibility for already-broadcast txs
|
|
182
|
+
- Used by: wallet (via `bsv-sdk` ARC provider)
|
|
183
|
+
- API: https://docs.bsvblockchain.org/important-concepts/details/spv/broadcasting
|
|
184
|
+
|
|
185
|
+
### Delegator (client-side concern)
|
|
186
|
+
- Adds fee inputs to client-constructed partial txs, signs only its fee inputs
|
|
187
|
+
- Used by: clients (not servers). The server stack never talks to the delegator.
|
|
188
|
+
- Separate service entirely — could be operated by the gateway operator or a third party
|
|
189
|
+
- Could itself be powered by a `bsv-wallet` instance in the "fees" role
|
|
190
|
+
|
|
191
|
+
## How Identity Flows Through the Stack
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
Request arrives
|
|
195
|
+
│
|
|
196
|
+
▼
|
|
197
|
+
BRC-104 Auth Middleware
|
|
198
|
+
│ Verifies x-bsv-auth-* headers
|
|
199
|
+
│ Sets env['brc103.identity_key'] = "02abc..."
|
|
200
|
+
│ Sets env['brc103.certificates'] = [...]
|
|
201
|
+
│
|
|
202
|
+
▼
|
|
203
|
+
X402 Middleware
|
|
204
|
+
│ Reads route config → protected, costs 100 sats
|
|
205
|
+
│ No proof header → issue challenges
|
|
206
|
+
│ Passes env (including identity) to each gateway
|
|
207
|
+
│
|
|
208
|
+
├── PayGateway.challenge_headers(request, route)
|
|
209
|
+
│ Returns Payment-Required header (v2 JSON with BSV in accepts)
|
|
210
|
+
│
|
|
211
|
+
├── ProofGateway.challenge_headers(request, route)
|
|
212
|
+
│ Returns X402-Challenge header (merkleworks format)
|
|
213
|
+
│
|
|
214
|
+
├── BRC105Gateway.challenge_headers(request, route)
|
|
215
|
+
│ Reads env['brc103.identity_key']
|
|
216
|
+
│ If present → returns x-bsv-payment-* headers (with derivation prefix)
|
|
217
|
+
│ If absent → returns {} (declines — can't work without identity)
|
|
218
|
+
│
|
|
219
|
+
▼
|
|
220
|
+
402 Response with all non-empty challenge headers
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
A client with a BRC-100 wallet and BRC-103 auth gets all three challenge options. A standard x402 v2 client gets the `Payment-Required` header and can pay with BSV. A merkleworks-specific client uses `X402-Challenge`. The client picks whichever it supports.
|
|
224
|
+
|
|
225
|
+
## How Payment Flows Through the Stack
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
Request arrives with proof header
|
|
229
|
+
│
|
|
230
|
+
▼
|
|
231
|
+
BRC-104 Auth Middleware (if present)
|
|
232
|
+
│ Authenticates, sets env['brc103.identity_key']
|
|
233
|
+
│
|
|
234
|
+
▼
|
|
235
|
+
X402 Middleware
|
|
236
|
+
│ Reads route config → protected, costs 100 sats
|
|
237
|
+
│ Detects proof header
|
|
238
|
+
│
|
|
239
|
+
├── Has Payment-Signature? → dispatch to PayGateway.settle!()
|
|
240
|
+
│ Gateway verifies tx, broadcasts via ARC
|
|
241
|
+
│ Returns: allow + settlement result + Payment-Response header
|
|
242
|
+
│
|
|
243
|
+
├── Has X402-Proof? → dispatch to ProofGateway.settle!()
|
|
244
|
+
│ Gateway verifies tx, checks mempool via ARC
|
|
245
|
+
│ Returns: allow + settlement result
|
|
246
|
+
│
|
|
247
|
+
├── Has x-bsv-payment? → dispatch to BRC105Gateway.settle!()
|
|
248
|
+
│ Gateway verifies derivation + tx via BRC-100 wallet
|
|
249
|
+
│ Returns: allow + settlement result
|
|
250
|
+
│
|
|
251
|
+
▼
|
|
252
|
+
Allow → forward to application (with receipt headers if provided)
|
|
253
|
+
Deny → return error to client
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## What Each Component Knows
|
|
257
|
+
|
|
258
|
+
| Component | Knows about HTTP | Knows about BSV | Knows about identity | Holds keys |
|
|
259
|
+
|-----------|:---:|:---:|:---:|:---:|
|
|
260
|
+
| BRC-104 Auth Middleware | Yes | Signatures only | Yes — its job | Session keys |
|
|
261
|
+
| X402 Middleware | Yes | No | Passes through | No |
|
|
262
|
+
| ProofGateway | Headers only | Via wallet | No | No — wallet holds them |
|
|
263
|
+
| PayGateway | Headers only | Via wallet | No | No — wallet holds them |
|
|
264
|
+
| BRC-105 Gateway | Headers only | Via wallet | Yes (reads from env) | No — wallet holds them |
|
|
265
|
+
| BSV Wallet | No | Yes — its job | No | Yes — the security boundary |
|
|
266
|
+
| ARC | No | Yes | No | No |
|
|
267
|
+
| Delegator | No | Via wallet | No | No — wallet holds them |
|
|
268
|
+
| Client | Yes | Yes | Optionally | Wallet keys |
|
|
269
|
+
|
|
270
|
+
## Gem Boundaries
|
|
271
|
+
|
|
272
|
+
| Gem | Repo | Contains |
|
|
273
|
+
|-----|------|---------|
|
|
274
|
+
| `bsv-sdk` | `sgbett/bsv-ruby-sdk` | Primitives — keys, transactions, scripts, ARC provider |
|
|
275
|
+
| `bsv-wallet` | `sgbett/bsv-ruby-sdk` | BRC-100 wallet interface, basket UTXO tracking, multi-key management. Depends on `bsv-sdk`. |
|
|
276
|
+
| `x402-rack` | `sgbett/x402-rack` | X402::Middleware (gatekeeper), gateway interface, X402::BSV::ProofGateway, X402::BSV::PayGateway. Gateways depend on `bsv-wallet`. |
|
|
277
|
+
| `brc104-rack` (future) | Separate | BRC-104 auth middleware |
|
|
278
|
+
| `bsv-x402` (npm) | `sgbett/bsv-x402` | Client-side fetch wrapper, CWI integration |
|
|
279
|
+
|
|
280
|
+
### Dependency chain
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
x402-rack
|
|
284
|
+
└── bsv-wallet (BRC-100 interface only)
|
|
285
|
+
└── bsv-sdk (primitives)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
The middleware itself has no dependency on `bsv-wallet` or `bsv-sdk`. Only the gateway classes do. A pure middleware deployment (dispatching to remote gateways) wouldn't need the BSV gems at all.
|
|
289
|
+
|
|
290
|
+
## Design Principles
|
|
291
|
+
|
|
292
|
+
1. **Each layer handles one concern.** Auth is auth. Payment is payment. Settlement is settlement. They compose, they don't merge.
|
|
293
|
+
|
|
294
|
+
2. **The gatekeeper is dumb.** It dispatches. It doesn't understand what it's dispatching. Blockchain knowledge lives in gateways.
|
|
295
|
+
|
|
296
|
+
3. **Gateways are pluggable.** New settlement protocols = new gateway classes. No middleware changes.
|
|
297
|
+
|
|
298
|
+
4. **Identity is optional and upstream.** Payment gating works without identity. Identity enriches the options (BRC-105) but doesn't gate them.
|
|
299
|
+
|
|
300
|
+
5. **External services are the gateway's concern.** The middleware never talks to ARC, wallets, or treasuries. Gateways own those relationships.
|
|
301
|
+
|
|
302
|
+
6. **The client chooses.** Multiple challenge headers, client picks. Payment content negotiation.
|
|
303
|
+
|
|
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).
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.1
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
|
|
6
|
+
Style/StringLiterals:
|
|
7
|
+
EnforcedStyle: double_quotes
|
|
8
|
+
|
|
9
|
+
Style/StringLiteralsInInterpolation:
|
|
10
|
+
EnforcedStyle: double_quotes
|
|
11
|
+
|
|
12
|
+
Style/Documentation:
|
|
13
|
+
Enabled: false
|
|
14
|
+
|
|
15
|
+
Metrics/AbcSize:
|
|
16
|
+
Enabled: false
|
|
17
|
+
|
|
18
|
+
Metrics/MethodLength:
|
|
19
|
+
Enabled: false
|
|
20
|
+
|
|
21
|
+
Metrics/CyclomaticComplexity:
|
|
22
|
+
Enabled: false
|
|
23
|
+
|
|
24
|
+
Metrics/ParameterLists:
|
|
25
|
+
Enabled: false
|
|
26
|
+
|
|
27
|
+
Metrics/BlockLength:
|
|
28
|
+
Exclude:
|
|
29
|
+
- "spec/**/*"
|
|
30
|
+
- "*.gemspec"
|
|
31
|
+
|
|
32
|
+
Metrics/CollectionLiteralLength:
|
|
33
|
+
Enabled: false
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project
|
|
6
|
+
|
|
7
|
+
x402-rack is a Ruby gem providing Rack middleware for the x402 protocol (BSV settlement-gated HTTP). Currently in early development (v0.1.0). Requires Ruby >= 3.1.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install dependencies
|
|
13
|
+
bin/setup
|
|
14
|
+
|
|
15
|
+
# Run tests
|
|
16
|
+
bundle exec rake spec
|
|
17
|
+
|
|
18
|
+
# Run a single test file
|
|
19
|
+
bundle exec rspec spec/x402_spec.rb
|
|
20
|
+
|
|
21
|
+
# Run a single example by line number
|
|
22
|
+
bundle exec rspec spec/x402_spec.rb:4
|
|
23
|
+
|
|
24
|
+
# Lint
|
|
25
|
+
bundle exec rubocop
|
|
26
|
+
|
|
27
|
+
# Lint with auto-fix
|
|
28
|
+
bundle exec rubocop -A
|
|
29
|
+
|
|
30
|
+
# Run all checks (tests + lint) — this is the CI default
|
|
31
|
+
bundle exec rake
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Architecture
|
|
35
|
+
|
|
36
|
+
Standard Ruby gem layout generated by `bundle gem`:
|
|
37
|
+
|
|
38
|
+
- `lib/x402.rb` — main entry point, defines `X402` module
|
|
39
|
+
- `lib/x402/version.rb` — version constant
|
|
40
|
+
- `sig/x402.rbs` — RBS type signatures
|
|
41
|
+
- `x402-rack.gemspec` — gem specification (dependencies defined here, not in Gemfile)
|
|
42
|
+
|
|
43
|
+
## Code Style
|
|
44
|
+
|
|
45
|
+
- Double quotes for strings (enforced by RuboCop)
|
|
46
|
+
- `frozen_string_literal: true` magic comment on all Ruby files
|
|
47
|
+
- Target Ruby version: 3.1
|
data/DESIGN.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# x402-rack Design Notes (DRAFT)
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
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`.
|
|
86
|
+
|
|
87
|
+
**Smart client** (BSV-aware): reads `extra.partialTx`, extends the template by adding funding inputs and signing.
|
|
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 |
|
|
157
|
+
|
|
158
|
+
## Component Boundaries
|
|
159
|
+
|
|
160
|
+
| Component | Responsibility | Keys? |
|
|
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 |
|
|
168
|
+
|
|
169
|
+
### Server-side wallet
|
|
170
|
+
|
|
171
|
+
The `bsv-wallet` gem (in the `sgbett/bsv-ruby-sdk` monorepo) provides a BRC-100 interface. Gateways talk to the wallet exclusively through this API — they never touch keys directly. The wallet is the security boundary.
|
|
172
|
+
|
|
173
|
+
One wallet, multiple roles via baskets:
|
|
174
|
+
|
|
175
|
+
| Role | Basket | Operations |
|
|
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` |
|
|
180
|
+
|
|
181
|
+
### Dependency chain
|
|
182
|
+
|
|
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
|
+
```
|
|
188
|
+
|
|
189
|
+
## Ecosystem Context
|
|
190
|
+
|
|
191
|
+
**Coinbase x402 v2** (broad ecosystem): client signs authorisation, facilitator broadcasts. Headers: `Payment-Required` / `Payment-Signature` / `Payment-Response`.
|
|
192
|
+
|
|
193
|
+
**Merkleworks x402** (BSV-specific): client broadcasts, server checks mempool. Headers: `X402-Challenge` / `X402-Proof`.
|
|
194
|
+
|
|
195
|
+
**BRC-105** (BSV Association BRC, future): mutual auth (BRC-103) + derivation-based payments. Headers: `x-bsv-payment-*`.
|
|
196
|
+
|
|
197
|
+
Our middleware supports all header conventions via the multi-gateway dispatch model. Our PayGateway speaks the standard Coinbase v2 language, making BSV a first-class citizen in the broader x402 ecosystem.
|
|
198
|
+
|
|
199
|
+
## Client Side
|
|
200
|
+
|
|
201
|
+
The x402 flow requires a client that intercepts 402 responses, parses challenges, extends transaction templates, handles fee delegation, and presents proof/payment. This is handled by [`bsv-x402`](https://www.npmjs.com/package/bsv-x402) — a separate JavaScript/TypeScript library ([`sgbett/bsv-x402`](https://github.com/sgbett/bsv-x402) on GitHub).
|
|
202
|
+
|
|
203
|
+
The client wraps `fetch()` and uses BRC-100 (`window.CWI`) to interact with compliant BSV wallets for transaction construction and signing. See that project's documentation for architecture and integration details.
|
|
204
|
+
|
|
205
|
+
## Current State
|
|
206
|
+
|
|
207
|
+
The middleware (`X402::Middleware`), configuration, and protocol layer (challenge/proof structures, request binding, base64url encoding) are implemented. BSV-specific logic currently lives in `X402::Verification::SettlementChecks` and needs to migrate into the gateway classes.
|
|
208
|
+
|
|
209
|
+
Next steps:
|
|
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
|
|
215
|
+
|
|
216
|
+
See [`.claude/plans/20260325-bsv-module.md`](.claude/plans/20260325-bsv-module.md) for the detailed implementation plan and [`.claude/plans/20260326-rack-stack-architecture.md`](.claude/plans/20260326-rack-stack-architecture.md) for the full rack stack architecture.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Open BSV License Version 5 – granted by BSV Association, Grafenauweg 6, 6300
|
|
2
|
+
Zug, Switzerland (CHE-427.008.338) ("Licensor"), to you as a user (henceforth
|
|
3
|
+
"You", "User" or "Licensee").
|
|
4
|
+
|
|
5
|
+
For the purposes of this license, the definitions below have the following
|
|
6
|
+
meanings:
|
|
7
|
+
|
|
8
|
+
"Bitcoin Protocol" means the protocol implementation, cryptographic rules,
|
|
9
|
+
network protocols, and consensus mechanisms in the Bitcoin White Paper as
|
|
10
|
+
described here https://protocol.bsvblockchain.org.
|
|
11
|
+
|
|
12
|
+
"Bitcoin White Paper" means the paper entitled 'Bitcoin: A Peer-to-Peer
|
|
13
|
+
Electronic Cash System' published by 'Satoshi Nakamoto' in October 2008.
|
|
14
|
+
|
|
15
|
+
"BSV Blockchains" means:
|
|
16
|
+
(a) the Bitcoin blockchain containing block height #556767 with the hash
|
|
17
|
+
"000000000000000001d956714215d96ffc00e0afda4cd0a96c96f8d802b1662b" and
|
|
18
|
+
that contains the longest honest persistent chain of blocks which has been
|
|
19
|
+
produced in a manner which is consistent with the rules set forth in the
|
|
20
|
+
Network Access Rules; and
|
|
21
|
+
(b) the test blockchains that contain the longest honest persistent chains of
|
|
22
|
+
blocks which has been produced in a manner which is consistent with the
|
|
23
|
+
rules set forth in the Network Access Rules.
|
|
24
|
+
|
|
25
|
+
"Network Access Rules" or "Rules" means the set of rules regulating the
|
|
26
|
+
relationship between BSV Association and the nodes on BSV based on the Bitcoin
|
|
27
|
+
Protocol rules and those set out in the Bitcoin White Paper, and available here
|
|
28
|
+
https://bsvblockchain.org/network-access-rules.
|
|
29
|
+
|
|
30
|
+
"Software" means the software the subject of this licence, including any/all
|
|
31
|
+
intellectual property rights therein and associated documentation files.
|
|
32
|
+
|
|
33
|
+
BSV Association grants permission, free of charge and on a non-exclusive and
|
|
34
|
+
revocable basis, to any person obtaining a copy of the Software to deal in the
|
|
35
|
+
Software without restriction, including without limitation the rights to use,
|
|
36
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
37
|
+
Software, and to permit persons to whom the Software is furnished to do so,
|
|
38
|
+
subject to and conditioned upon the following conditions:
|
|
39
|
+
|
|
40
|
+
1 - The text "© BSV Association," and this license shall be included in all
|
|
41
|
+
copies or substantial portions of the Software.
|
|
42
|
+
2 - The Software, and any software that is derived from the Software or parts
|
|
43
|
+
thereof, must only be used on the BSV Blockchains.
|
|
44
|
+
|
|
45
|
+
For the avoidance of doubt, this license is granted subject to and conditioned
|
|
46
|
+
upon your compliance with these terms only. In the event of non-compliance, the
|
|
47
|
+
license shall extinguish and you can be enjoined from violating BSV's
|
|
48
|
+
intellectual property rights (incl. damages and similar related claims).
|
|
49
|
+
|
|
50
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
51
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES REGARDING ENTITLEMENT,
|
|
52
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
53
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS THEREOF BE LIABLE FOR ANY CLAIM,
|
|
54
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
55
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
56
|
+
DEALINGS IN THE SOFTWARE.
|