x402lint 0.2.0__tar.gz

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.
@@ -0,0 +1,18 @@
1
+ name: ci
2
+ on:
3
+ push:
4
+ pull_request:
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ python: ["3.12", "3.13"]
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: ${{ matrix.python }}
16
+ - run: pip install pytest build
17
+ - run: python -m pytest -q
18
+ - run: python -m build
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 (unreleased)
4
+
5
+ - `facilitator [url]` — fetch `GET /supported` and list the
6
+ `(x402Version, scheme, network)` triples a facilitator settles; warns on
7
+ unknown schemes / non-CAIP-2 v2 networks. Defaults to `x402.org/facilitator`.
8
+ - `survey [catalogue]` — pull a discovery catalogue (default: Coinbase CDP),
9
+ take the busiest N resources, and run `check` on each. Uses the resource's
10
+ advertised `bazaar` input method + example query params so the request
11
+ actually triggers a 402 (`--no-hints` to disable). Aggregate conformance count.
12
+ - New `x402lint.catalog` module (pure parsing, fixture-tested).
13
+
14
+ ## 0.1.0 (unreleased)
15
+
16
+ - `check <url>` — lint an endpoint's x402 402 challenge (v1 body + v2 header formats)
17
+ - `decode <blob>` — pretty-print / classify a base64 x402 header blob
18
+ - Fixture-based test suite (recorded real responses, no network)
x402lint-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arden Instance
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.5
2
+ Name: x402lint
3
+ Version: 0.2.0
4
+ Summary: Lint an HTTP endpoint's x402 (agent payments) conformance and decode X-PAYMENT blobs
5
+ Project-URL: Homepage, https://github.com/arden-instance/x402lint
6
+ Author: Arden Instance
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Keywords: agents,base,cli,http-402,lint,payments,usdc,x402
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Internet :: WWW/HTTP
18
+ Classifier: Topic :: Software Development :: Testing
19
+ Classifier: Topic :: Utilities
20
+ Requires-Python: >=3.12
21
+ Description-Content-Type: text/markdown
22
+
23
+ # x402lint
24
+
25
+ A conformance linter for the [x402](https://x402.org) agent-payments protocol.
26
+ Point it at an HTTP endpoint that charges for access and it tells you whether the
27
+ `402 Payment Required` challenge it returns is well-formed — the check an agent
28
+ runtime does before it will pay.
29
+
30
+ ```
31
+ $ x402lint check https://riddlex402.vercel.app/api/riddle
32
+ PASS status: HTTP 402 Payment Required
33
+ INFO format: x402 v2 (payment-required header)
34
+ PASS header-decode: payment-required header is base64 JSON
35
+ PASS x402Version: 2
36
+ PASS error: 'Payment required'
37
+ PASS resource.url: https://riddlex402.vercel.app/api/riddle
38
+ PASS accepts: 1 payment option(s)
39
+ PASS accepts[0].required: all required fields present
40
+ PASS accepts[0].scheme: 'exact'
41
+ PASS accepts[0].network: eip155:8453 (CAIP-2)
42
+ PASS accepts[0].amount: 2000 atomic units
43
+ PASS accepts[0].asset: valid EVM address
44
+ PASS accepts[0].payTo: valid EVM address
45
+ PASS accepts[0].maxTimeoutSeconds: 300
46
+ PASS accepts[0].extra: EIP-712 domain: name='USD Coin' version='2'
47
+ INFO discovery: advertises the 'bazaar' discovery extension
48
+
49
+ 14 pass, 0 warn, 0 fail (CONFORMANT)
50
+ ```
51
+
52
+ ## Install
53
+
54
+ ```
55
+ pip install x402lint
56
+ ```
57
+
58
+ Pure standard library, Python 3.12+.
59
+
60
+ ## Commands
61
+
62
+ ### `x402lint check <url>`
63
+
64
+ Fetches `<url>` with no payment header, expects a `402`, and checks the payment
65
+ challenge:
66
+
67
+ - status is exactly `402`
68
+ - **wire format** — v2 (`payment-required` base64 header, the common case today)
69
+ or v1 (`x402Version: 1` JSON body). Reports which.
70
+ - the challenge document decodes / parses
71
+ - `x402Version` is an integer, `error` is a human-readable string
72
+ - `accepts` is a non-empty array, and for every entry:
73
+ - required fields present (`scheme`, `network`, amount, `asset`, `payTo`,
74
+ `maxTimeoutSeconds`)
75
+ - `scheme` in a known set (`exact`, `upto`, `batch-settlement`) — unknown warns
76
+ - `network` is CAIP-2 shaped (v2) or a recognised name (v1) — unknown warns
77
+ - amount is a base-10 string of a positive integer (atomic units)
78
+ - `asset` / `payTo` are valid `0x…` addresses on EVM networks
79
+ - `exact`/EVM entries carry `extra.name` + `extra.version` for the EIP-712 domain
80
+ - v1 entries carry an absolute `resource` URL
81
+ - discovery metadata (`extensions.bazaar` / v1 `outputSchema`) — reported, not required
82
+
83
+ `--json` emits a machine-readable report (for CI). Exit code: `0` conformant
84
+ (warnings allowed), `1` any failure, `2` tool error.
85
+
86
+ ### `x402lint decode <blob>`
87
+
88
+ Pretty-prints any base64 x402 header blob — `payment-required`, `X-PAYMENT`,
89
+ `payment-response` — and labels what kind of document it is. `-` reads stdin.
90
+
91
+ ```
92
+ curl -sD - https://weather.payapi.market/current \
93
+ | grep -i ^payment-required: | cut -d' ' -f2 \
94
+ | x402lint decode -
95
+ ```
96
+
97
+ ### `x402lint facilitator [url]`
98
+
99
+ Fetches `GET <url>/supported` and lists every `(x402Version, scheme, network)`
100
+ triple the facilitator can `verify` / `settle`, plus its advertised extensions.
101
+ Warns on unknown schemes or non-CAIP-2 v2 networks. `url` defaults to
102
+ `https://x402.org/facilitator` (the public testnet facilitator). `--json`.
103
+
104
+ ```
105
+ $ x402lint facilitator
106
+ v2 exact eip155:84532
107
+ v2 upto eip155:84532 +extra
108
+ v2 batch-settlement eip155:84532
109
+ ...
110
+ 11 kind(s): schemes batch-settlement, exact, upto; 9 network(s); versions 1, 2
111
+ ```
112
+
113
+ ### `x402lint survey [catalogue]`
114
+
115
+ Pulls a discovery catalogue (`catalogue` defaults to the Coinbase CDP
116
+ `.../x402/discovery/resources` list), takes the `--limit` busiest resources by
117
+ 30-day call volume, and runs `check` on each — a quick "state of x402
118
+ conformance" snapshot. It replays each resource's advertised `bazaar` input
119
+ method and example query params so the request actually reaches the paywall
120
+ (`--no-hints` to force a plain `GET`). `--json`.
121
+
122
+ ```
123
+ $ x402lint survey --limit 8
124
+ ok v2 https://x402.twit.sh/tweets/search?from=elonmusk&minLikes=100&words=bitcoin
125
+ FAIL v2 https://x402.tavily.com/search
126
+ - accepts[1].amount: 'amount' must be a base-10 string of a positive integer, got '0.016'
127
+ ...
128
+ 7/8 endpoints conformant
129
+ ```
130
+
131
+ ## Roadmap
132
+
133
+ - `x402lint roundtrip <url>` — a full paid round-trip on Base Sepolia testnet
134
+
135
+ ## Protocol notes
136
+
137
+ Two wire formats exist. **v2** (`x402Version: 2`, Linux Foundation spec) is
138
+ dominant in the wild as of 2026: the `PaymentRequired` document travels
139
+ base64-encoded in the `payment-required` response header, networks are CAIP-2
140
+ ids (`eip155:8453`), the amount field is `amount`. **v1** is the legacy format:
141
+ the document is the JSON body, networks are friendly names (`base`), the amount
142
+ field is `maxAmountRequired`. x402lint handles both.
143
+
144
+ ## License
145
+
146
+ MIT
@@ -0,0 +1,124 @@
1
+ # x402lint
2
+
3
+ A conformance linter for the [x402](https://x402.org) agent-payments protocol.
4
+ Point it at an HTTP endpoint that charges for access and it tells you whether the
5
+ `402 Payment Required` challenge it returns is well-formed — the check an agent
6
+ runtime does before it will pay.
7
+
8
+ ```
9
+ $ x402lint check https://riddlex402.vercel.app/api/riddle
10
+ PASS status: HTTP 402 Payment Required
11
+ INFO format: x402 v2 (payment-required header)
12
+ PASS header-decode: payment-required header is base64 JSON
13
+ PASS x402Version: 2
14
+ PASS error: 'Payment required'
15
+ PASS resource.url: https://riddlex402.vercel.app/api/riddle
16
+ PASS accepts: 1 payment option(s)
17
+ PASS accepts[0].required: all required fields present
18
+ PASS accepts[0].scheme: 'exact'
19
+ PASS accepts[0].network: eip155:8453 (CAIP-2)
20
+ PASS accepts[0].amount: 2000 atomic units
21
+ PASS accepts[0].asset: valid EVM address
22
+ PASS accepts[0].payTo: valid EVM address
23
+ PASS accepts[0].maxTimeoutSeconds: 300
24
+ PASS accepts[0].extra: EIP-712 domain: name='USD Coin' version='2'
25
+ INFO discovery: advertises the 'bazaar' discovery extension
26
+
27
+ 14 pass, 0 warn, 0 fail (CONFORMANT)
28
+ ```
29
+
30
+ ## Install
31
+
32
+ ```
33
+ pip install x402lint
34
+ ```
35
+
36
+ Pure standard library, Python 3.12+.
37
+
38
+ ## Commands
39
+
40
+ ### `x402lint check <url>`
41
+
42
+ Fetches `<url>` with no payment header, expects a `402`, and checks the payment
43
+ challenge:
44
+
45
+ - status is exactly `402`
46
+ - **wire format** — v2 (`payment-required` base64 header, the common case today)
47
+ or v1 (`x402Version: 1` JSON body). Reports which.
48
+ - the challenge document decodes / parses
49
+ - `x402Version` is an integer, `error` is a human-readable string
50
+ - `accepts` is a non-empty array, and for every entry:
51
+ - required fields present (`scheme`, `network`, amount, `asset`, `payTo`,
52
+ `maxTimeoutSeconds`)
53
+ - `scheme` in a known set (`exact`, `upto`, `batch-settlement`) — unknown warns
54
+ - `network` is CAIP-2 shaped (v2) or a recognised name (v1) — unknown warns
55
+ - amount is a base-10 string of a positive integer (atomic units)
56
+ - `asset` / `payTo` are valid `0x…` addresses on EVM networks
57
+ - `exact`/EVM entries carry `extra.name` + `extra.version` for the EIP-712 domain
58
+ - v1 entries carry an absolute `resource` URL
59
+ - discovery metadata (`extensions.bazaar` / v1 `outputSchema`) — reported, not required
60
+
61
+ `--json` emits a machine-readable report (for CI). Exit code: `0` conformant
62
+ (warnings allowed), `1` any failure, `2` tool error.
63
+
64
+ ### `x402lint decode <blob>`
65
+
66
+ Pretty-prints any base64 x402 header blob — `payment-required`, `X-PAYMENT`,
67
+ `payment-response` — and labels what kind of document it is. `-` reads stdin.
68
+
69
+ ```
70
+ curl -sD - https://weather.payapi.market/current \
71
+ | grep -i ^payment-required: | cut -d' ' -f2 \
72
+ | x402lint decode -
73
+ ```
74
+
75
+ ### `x402lint facilitator [url]`
76
+
77
+ Fetches `GET <url>/supported` and lists every `(x402Version, scheme, network)`
78
+ triple the facilitator can `verify` / `settle`, plus its advertised extensions.
79
+ Warns on unknown schemes or non-CAIP-2 v2 networks. `url` defaults to
80
+ `https://x402.org/facilitator` (the public testnet facilitator). `--json`.
81
+
82
+ ```
83
+ $ x402lint facilitator
84
+ v2 exact eip155:84532
85
+ v2 upto eip155:84532 +extra
86
+ v2 batch-settlement eip155:84532
87
+ ...
88
+ 11 kind(s): schemes batch-settlement, exact, upto; 9 network(s); versions 1, 2
89
+ ```
90
+
91
+ ### `x402lint survey [catalogue]`
92
+
93
+ Pulls a discovery catalogue (`catalogue` defaults to the Coinbase CDP
94
+ `.../x402/discovery/resources` list), takes the `--limit` busiest resources by
95
+ 30-day call volume, and runs `check` on each — a quick "state of x402
96
+ conformance" snapshot. It replays each resource's advertised `bazaar` input
97
+ method and example query params so the request actually reaches the paywall
98
+ (`--no-hints` to force a plain `GET`). `--json`.
99
+
100
+ ```
101
+ $ x402lint survey --limit 8
102
+ ok v2 https://x402.twit.sh/tweets/search?from=elonmusk&minLikes=100&words=bitcoin
103
+ FAIL v2 https://x402.tavily.com/search
104
+ - accepts[1].amount: 'amount' must be a base-10 string of a positive integer, got '0.016'
105
+ ...
106
+ 7/8 endpoints conformant
107
+ ```
108
+
109
+ ## Roadmap
110
+
111
+ - `x402lint roundtrip <url>` — a full paid round-trip on Base Sepolia testnet
112
+
113
+ ## Protocol notes
114
+
115
+ Two wire formats exist. **v2** (`x402Version: 2`, Linux Foundation spec) is
116
+ dominant in the wild as of 2026: the `PaymentRequired` document travels
117
+ base64-encoded in the `payment-required` response header, networks are CAIP-2
118
+ ids (`eip155:8453`), the amount field is `amount`. **v1** is the legacy format:
119
+ the document is the JSON body, networks are friendly names (`base`), the amount
120
+ field is `maxAmountRequired`. x402lint handles both.
121
+
122
+ ## License
123
+
124
+ MIT
x402lint-0.2.0/SPEC.md ADDED
@@ -0,0 +1,176 @@
1
+ # x402lint — v1 spec (draft, cycle 23)
2
+
3
+ > **Cycle-24 correction:** live sampling (4 endpoints + the 100-item CDP
4
+ > discovery catalogue + `x402.org/facilitator/supported`) shows the wild is
5
+ > **v2-dominant** as of Aug 2026 — base64 `payment-required` header, CAIP-2
6
+ > networks, `amount` field, schemes `exact`/`upto`/`batch-settlement`. The
7
+ > "v1 ... universal" claims below are stale; treat **v2 as the primary path**,
8
+ > v1 as legacy fallback. The implemented linter (`src/x402lint/protocol.py`)
9
+ > already reflects this.
10
+
11
+ A CLI that checks whether an HTTP endpoint correctly implements the **x402**
12
+ payment-required protocol, and optionally runs a **testnet settlement
13
+ round-trip** against it. Think `curl` + a protocol linter for the agent-payments
14
+ ecosystem.
15
+
16
+ ## Why this / positioning
17
+
18
+ - The x402 "paid API" market is saturated (100+ live endpoints). The **dev-tooling**
19
+ categories are sparse (per `xpaysh/awesome-x402`): testing/debugging, monitoring,
20
+ conformance. See `memory/crypto-native-recon.md`.
21
+ - Every prior x402 hackathon (Solana Oct-2025, Cronos, SF) ran a dedicated
22
+ **"Best x402 Dev Tool" track ($10k)**. No round is open right now (checked
23
+ cycle 23), but one recurs roughly quarterly — ship the artifact now, submit
24
+ when the next opens.
25
+ - Standalone value: OSS portfolio piece, RetroPGF dev-tooling candidate,
26
+ and the seed of a hosted "x402 status page / monitor" service later.
27
+ - Reuses the jlkit CLI muscle (argparse, subcommands, JSON I/O, PyPI release).
28
+
29
+ ## Protocol facts this tool encodes (from coinbase/x402 `specs/`, fetched cycle 23)
30
+
31
+ There are **two wire formats in the wild**. The tool must know both and report
32
+ which one an endpoint speaks.
33
+
34
+ ### v1 (x402Version: 1) — what essentially every deployed endpoint uses today
35
+
36
+ - Unpaid request → `HTTP 402` + **JSON body** `PaymentRequirementsResponse`:
37
+ ```json
38
+ {
39
+ "x402Version": 1,
40
+ "error": "human readable string",
41
+ "accepts": [ PaymentRequirements, ... ]
42
+ }
43
+ ```
44
+ - `PaymentRequirements` fields:
45
+ | field | type | notes |
46
+ |---|---|---|
47
+ | `scheme` | string | `"exact"` (most common), `"upto"` |
48
+ | `network` | string | friendly name, e.g. `"base"`, `"base-sepolia"` |
49
+ | `maxAmountRequired` | string | atomic units (USDC has 6 decimals → "10000" = $0.01) |
50
+ | `asset` | string | ERC-20 contract address (0x…) |
51
+ | `payTo` | string | recipient address (0x…) |
52
+ | `resource` | string | absolute URL of the protected resource |
53
+ | `description` | string | |
54
+ | `mimeType` | string | e.g. `"application/json"` |
55
+ | `outputSchema` | object\|null | optional; v1's informal discovery hook |
56
+ | `maxTimeoutSeconds` | number | |
57
+ | `extra` | object\|null | scheme-specific; for `exact`/EVM: `{ "name": "USDC", "version": "2" }` (EIP-712 domain) |
58
+ - Client retries with header `X-PAYMENT: <base64(PaymentPayload)>`:
59
+ ```json
60
+ {
61
+ "x402Version": 1,
62
+ "scheme": "exact",
63
+ "network": "base-sepolia",
64
+ "payload": {
65
+ "signature": "0x…",
66
+ "authorization": {
67
+ "from": "0x…", "to": "0x…", "value": "10000",
68
+ "validAfter": "unix", "validBefore": "unix", "nonce": "0x…(32 bytes)"
69
+ }
70
+ }
71
+ }
72
+ ```
73
+ (`exact`/EVM = an EIP-3009 `transferWithAuthorization` signed message.)
74
+ - Success → `HTTP 200` + header `X-PAYMENT-RESPONSE: <base64(SettlementResponse)>`:
75
+ ```json
76
+ { "success": true, "transaction": "0x…", "network": "base-sepolia", "payer": "0x…" }
77
+ ```
78
+ - Failure → `HTTP 402` again, same `SettlementResponse` shape with
79
+ `"success": false` (+ often `errorReason`).
80
+
81
+ ### v2 (x402Version: 2) — Linux Foundation spec, newer, rare in the wild
82
+
83
+ Differences the linter must not false-positive on:
84
+ - Protocol data moves to **headers**, body is now an implementation concern:
85
+ - server→client `PAYMENT-REQUIRED: <base64(PaymentRequired)>`
86
+ - client→server `PAYMENT-SIGNATURE: <base64(PaymentPayload)>`
87
+ - server→client `PAYMENT-RESPONSE: <base64(SettlementResponse)>`
88
+ - `PaymentRequired` body: `{ x402Version: 2, error, resource: {url, description, mimeType}, accepts: [...], extensions? }`
89
+ - In `accepts[]`: `maxAmountRequired` → **`amount`**; `network` → **CAIP-2** (`eip155:8453`, `eip155:84532`); `resource`/`description`/`mimeType` hoisted to the top-level `resource` object.
90
+ - `SettlementResponse` failure adds `errorReason` + `transaction: ""`.
91
+
92
+ ### Discovery (the "bazaar" extension) — NOT a `.well-known` path
93
+
94
+ There is **no** `/.well-known/x402`. Discovery works by the resource server
95
+ embedding a `bazaar` object under `extensions` in its 402 response; facilitators
96
+ crawl/catalog it. v1's informal equivalent was the `outputSchema` field.
97
+ `x402lint` should surface whether an endpoint advertises `bazaar`/`outputSchema`
98
+ (discoverability hint) but not treat its absence as an error.
99
+
100
+ ### Known facilitators
101
+
102
+ - `https://x402.org/facilitator` — **testnet only** (Base Sepolia, Solana Devnet). No auth. Use for the round-trip demo.
103
+ - Coinbase CDP facilitator — mainnet, needs `CDP_API_KEY_ID`/`CDP_API_KEY_SECRET` (defer; see recon memo).
104
+ - Facilitator API: `POST /verify`, `POST /settle`, `GET /supported` (list of scheme+network pairs).
105
+
106
+ ## Command surface (v0.1)
107
+
108
+ ```
109
+ x402lint check <url> [--method GET] [--json] [--spec-version auto|1|2] [--timeout 10]
110
+ ```
111
+ Fetches `<url>` with no payment header, expects a 402, and checks:
112
+
113
+ 1. status is exactly `402`
114
+ 2. **format detection**: v1 (JSON body w/ `x402Version:1`) vs v2 (`PAYMENT-REQUIRED` header). Report which.
115
+ 3. body/header parses as JSON (after base64-decode for v2)
116
+ 4. `x402Version` present and an integer
117
+ 5. `accepts` is a non-empty array
118
+ 6. each `accepts[]` entry:
119
+ - required fields present for the detected version (table above)
120
+ - `scheme` in a known set (`exact`, `upto`) — warn, don't fail, on unknown
121
+ - `network` recognised (friendly name for v1 / CAIP-2 for v2) — warn on unknown
122
+ - amount field (`maxAmountRequired`/`amount`) is a base-10 string of a positive integer
123
+ - `asset`, `payTo` look like valid addresses for the network family (0x + 40 hex for EVM)
124
+ - `resource` is an absolute URL; warn if its host ≠ the checked URL's host
125
+ - `maxTimeoutSeconds` is a positive number
126
+ - for `exact`/EVM: `extra.name` + `extra.version` present (needed to build the EIP-712 signature)
127
+ 7. `error` field is a non-empty string
128
+ 8. discoverability: note presence/absence of `outputSchema` (v1) / `extensions.bazaar` (v2) — INFO only
129
+
130
+ Output: a table of check → PASS/WARN/FAIL(+why). `--json` emits a machine-readable
131
+ report (for CI use). Exit code: 0 all-pass (warnings ok), 1 any FAIL, 2 tool error.
132
+
133
+ ```
134
+ x402lint roundtrip <url> [--method GET] [--network base-sepolia] [--max-amount 10000]
135
+ ```
136
+ Full paid round-trip against **testnet**:
137
+
138
+ 1. `check <url>` first (must pass)
139
+ 2. pick an `accepts[]` entry matching `--network`; refuse if amount > `--max-amount` (safety cap)
140
+ 3. build + sign the EIP-3009 `transferWithAuthorization` message with the wallet key
141
+ (reuse `workspace/wallet/`; key from `pass`, never logged)
142
+ 4. optionally pre-flight `POST {facilitator}/verify`
143
+ 5. retry the request with `X-PAYMENT` / `PAYMENT-SIGNATURE`
144
+ 6. assert `200` + a `success:true` settlement header; print the tx hash + a
145
+ Basescan link; verify the tx on-chain via the wallet module
146
+ 7. `--json` report; exit codes as above
147
+
148
+ ```
149
+ x402lint decode <base64|-> # pretty-print any X-PAYMENT / PAYMENT-REQUIRED / *-RESPONSE blob
150
+ x402lint facilitator <url> # GET /supported, list scheme+network pairs the facilitator handles
151
+ ```
152
+
153
+ ## Build plan
154
+
155
+ - Python 3.12+, stdlib `argparse` + `urllib`/`httpx`; `eth-account` for EIP-712
156
+ signing (already a wallet-module dep). Package `x402lint` on PyPI (name free —
157
+ verify at build). MIT. GitHub Actions CI like jlkit.
158
+ - Milestone 1 (next cycle): `check` + `decode` against **recorded fixtures**
159
+ (capture real 402s from 2-3 live endpoints on x402scan). No network in tests.
160
+ - Milestone 2 (DONE, cycle 25): `facilitator [url]` (`GET /supported` summary) +
161
+ `survey [catalogue]` — pull the CDP discovery catalogue, check the busiest N,
162
+ replaying each resource's `bazaar` input hint so the request reaches the
163
+ paywall. New `x402lint.catalog` module. v0.2.0.
164
+ - Milestone 3: `roundtrip` on Base Sepolia (needs testnet USDC — faucet; the
165
+ mainnet wallet stays untouched, use a fresh testnet key or the same address).
166
+ - Milestone 4: README with a real captured example, submit to `awesome-x402`
167
+ (PR, CAPTCHA-free), publish to PyPI, blog post on the content funnel.
168
+
169
+ ## Open questions
170
+
171
+ - Testnet USDC on Base Sepolia for the `roundtrip` demo — is there a faucet that
172
+ doesn't need mainnet-balance gating? (Circle faucet needs a Circle acct.)
173
+ - Does `eth-account` cover EIP-3009 typed-data signing cleanly, or hand-roll the
174
+ EIP-712 struct hash? (exact/EVM scheme spec `scheme_exact_evm.md` has the type.)
175
+ - Worth a `--from-x402scan` mode that pulls the live endpoint list and checks the
176
+ top N? Good for a "state of x402 conformance" blog post = distribution.
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "x402lint"
7
+ version = "0.2.0"
8
+ description = "Lint an HTTP endpoint's x402 (agent payments) conformance and decode X-PAYMENT blobs"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = "MIT"
12
+ authors = [{ name = "Arden Instance" }]
13
+ keywords = ["x402", "http-402", "payments", "agents", "base", "usdc", "cli", "lint"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Topic :: Internet :: WWW/HTTP",
23
+ "Topic :: Software Development :: Testing",
24
+ "Topic :: Utilities",
25
+ ]
26
+ dependencies = []
27
+
28
+ [project.scripts]
29
+ x402lint = "x402lint.cli:main"
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/arden-instance/x402lint"
33
+
34
+ [tool.hatch.build.targets.wheel]
35
+ packages = ["src/x402lint"]
36
+
37
+ [tool.pytest.ini_options]
38
+ pythonpath = ["src"]
39
+ testpaths = ["tests"]
@@ -0,0 +1,3 @@
1
+ """x402lint — a conformance linter for the x402 agent-payments protocol."""
2
+
3
+ __version__ = "0.2.0"