Skip to content

v0.1.0 — v0.1 wedge

Choose a tag to compare

@fardinvahdat fardinvahdat released this 12 May 11:16
· 102 commits to main since this release
cb4193d

0.1.0 — 2026-05-12

The v0.1 wedge: a local proxy + timeout-reconciliation engine that catches the canonical coinbase/x402#1062 symptom — buyer is debited on-chain but the facilitator/server thinks the payment failed. Verified end-to-end on real Base Sepolia + the live x402.org/facilitator (tx 0x116ccf73…ba52). 215 tests, GitHub Actions CI on Node 20 + 22, Apache-2.0.

Added

  • Initial project scaffold (X402-2)
  • CLAUDE.md operating manual for Claude Code sessions
  • TESTING.md defining test requirements per change type
  • SPEC.md, ARCHITECTURE.md, DECISIONS.md skeletons
  • CONTRIBUTING.md, dogfood-notes.md skeletons
  • .env.example configuration template
  • .github/PULL_REQUEST_TEMPLATE.md enforcing test checklist
  • Apache 2.0 license
  • Dogfood rig on Base Sepolia (X402-3): Hono + x402-hono@1.2.0 server (src/dogfood/app.ts, api/[...all].ts for Vercel, scripts/dev-server.ts for local), paying client via x402-fetch@1.2.0 (scripts/dogfood-client.ts), env validation with mainnet-RPC guard (src/dogfood/config.ts), and 20 unit tests covering config validation and 402 challenge generation. Verified end-to-end against the real x402.org/facilitator on Base Sepolia — paid GET returned 200 with on-chain settlement tx 0x8b53a04d…b3428.
  • Public Vercel production deploy (X402-3) on the v1 branch alias at x402trace-dogfood-git-v1-fardinvahdats-projects.vercel.app serving / (static landing) and /api/weather (x402-gated). Second on-chain settlement against the real x402.org/facilitator from this production endpoint: tx 0xc5758bf2…6cbf.
  • Five-failure-mode reproduction harness for X402-4 (scripts/failure-modes.ts, pnpm dogfood:failure-modes <n>). Captures verbatim server/client/facilitator error strings for: wrong chain ID, expired validBefore, insufficient USDC, malformed signature, facilitator unavailable. Full transcripts + diagnoses in dogfood-notes.md § Failure modes.
  • Week-1 pain synthesis (X402-6) in dogfood-notes.md § Top painful moments: 9 ranked pains + 5 wedge candidates (A reconciliation, B inspect+doctor, C bazaar-check, D cross-facilitator, E proxy substrate) — pulls from X402-3 dogfood capture, X402-4 failure modes, X402-5 GitHub catalogue, and the Notion Validation evidence page. CLAUDE.md updated to point future sessions at the table for feature-scope work.
  • v0.1 wedge accepted (X402-7) — ADR-001 in DECISIONS.md: local HTTP proxy substrate + timeout-reconciliation engine on top. Scope tightened for the 5-week timeline: Base Sepolia only, single facilitator profile (x402.org/facilitator), exact EVM scheme only, detect-and-notify (no auto-refund in v0.1). CLAUDE.md status updated from "tentative" to "accepted." Bazaar diagnostics, cross-facilitator drift, inspect/doctor/versions deferred to v0.2+.
  • v0.1 implementation spec (X402-8) filled in at SPEC.md: problem, solution, user flow (x402trace proxy --upstream <url> --reconcile + sample RECONCILED ⚠ settled-but-server-thinks-not output), v0.1 scope/v0.2 stretch lists, 5 success-criteria checkboxes, explicit out-of-scope non-goals, and per-competitor differentiation against xpay / x402scan / x402lint / x402-watch / zauth / PaySentry. Linked from CLAUDE.md.
  • v0.1 architecture (X402-9) finalized in ARCHITECTURE.md: 5-component decomposition (Proxy src/proxy/, Decoder src/decoder/, Chain src/chain/, Reconciliation src/reconciliation/, CLI src/cli/) with one-example data flow traced through every component for happy / rejected / timeout outcomes; TypeScript interface stubs ready to become source code (PaymentRequirements, PaymentPayload, FacilitatorResponse, PaymentExchange, ExchangeOutcome, ChainTransfer, ReconciliationResult); canonical JSONL record format (the file is the API); full configuration precedence table; v0.2 extension points named per feature so no v0.1 component is rewritten for v0.2.
  • Proxy core (X402-10) in src/proxy/: forward HTTP proxy that captures every exchange to a JSONL log + in-process event bus. Five modules — proxy.ts (server + forwarding via fetch), types.ts (ProxyEvent / CapturedRequest / CapturedResponse / ExchangeOutcome discriminated union + isLikelyX402Exchange heuristic), event-bus.ts (subscribe-with-queue-and-drop-counter pub-sub), jsonl-sink.ts (append-only writer), id.ts (per-exchange UUIDs). Hop-by-hop headers stripped per RFC; non-UTF8 bodies base64-encoded; upstream timeout returns 502 + proxy.error event. Deliberately dumb — no x402 parsing here (that's X402-11). 18 new unit tests + a 4-test integration that pipes the dogfood client through the proxy at the X402-3 dogfood rig (mock-facilitator-backed) and asserts both the 402 and the paid 200 are captured to JSONL with the right discriminants. pnpm proxy --upstream <url> runs it; full npx x402trace proxy plumbing arrives in X402-14.
  • x402 message decoder (X402-11) in src/decoder/: pure-function parsers that turn raw ProxyEvents into typed DecodedEvents. Six modules — types.ts (PaymentRequirements, PaymentAuthorization, PaymentPayload, FacilitatorResponse, DecodedEvent discriminated union), parse.ts (parseChallengeBody, parsePaymentHeader, parseSettlementHeader, version detection — uses x402/schemes's exact.evm.decodePayment for the v1 fast path, hand-rolled fallback for v2), redact.ts (signature redaction by default, --log-secrets opt-in), format.ts (formatHuman and formatJson), decoder.ts (createDecoder() stream consumer), index.ts. Includes src/decoder/schema.md documenting the JSONL contract. Handles both v1 (X-PAYMENT / X-PAYMENT-RESPONSE) and v2 (PAYMENT-SIGNATURE / PAYMENT-RESPONSE) header surfaces. 22 new unit tests covering v1 + v2 parsers, redaction, formatting + a 4-test integration that asserts the proxy→decoder→PaymentExchange pipeline produces the right structured events from a paid flow. Stdout sample of the readable output is captured in dogfood-notes.md § "Decoder readable-output sample" per acceptance criterion; pnpm decoder:demo reproduces it.
  • Base RPC client (X402-12) in src/chain/: thin viem wrapper over Base Sepolia. Five modules — types.ts (ChainTransfer + VerifyTransferResult 7-variant discriminated union: confirmed / pending / reverted / not_found / wrong_recipient / wrong_amount / wrong_token), abi.ts (Base Sepolia USDC address 0x036CbD…CF7e + Transfer + EIP-3009 AuthorizationUsed event ABIs), retry.ts (exponential backoff, 3 attempts default per ARCHITECTURE.md), client.ts (createChainClient with verifyTransfer, getTransferByTxHash, subscribeUsdcTransfers AsyncIterable, getBlockNumber), index.ts. Read-only — never holds private keys, never broadcasts. subscribeUsdcTransfers enriches each Transfer event with the matching AuthorizationUsed.nonce from the same tx so the reconciliation engine can match by EIP-3009 nonce. 16 unit tests with mocked transport covering all 7 verify variants + the retry helper + 4 e2e tests against live Base Sepolia (gated by X402_E2E=1; verified against the two real X402-3 settlement txs 0x8b53a04d…b3428 and 0xc5758bf2…6cbf).
  • GitHub Actions CI (X402-18).github/workflows/ci.yml runs pnpm typecheck, pnpm lint, pnpm test, pnpm build on every PR + push to v1 / staging / main. Node 20 + 22 matrix, pnpm-store cached via actions/setup-node@v4, 10-min job timeout, in-progress runs cancelled when a fresher commit lands on the same ref. pnpm test auto-skips tests/e2e/chain-live.test.ts (gated on X402_E2E=1), so the live-Base-Sepolia suite never burns testnet funds in CI. Companion .github/workflows/release.yml is the X402-19 substrate: tag-triggered (v*.*.*), runs the same quality bar, builds, and has the npm-publish step commented out until X402-19 wires the NPM_TOKEN secret. The CI badge in README.md (placeholder since X402-16) lights up on the first run.
  • Unit-test coverage hardening (X402-17) — Week-4 risk-reduction pass on the decoder, reconciliation, and CLI. Per the ticket spec ("chase risk reduction, not coverage targets"), targeted the gaps that break silently: (a) the streaming decoder's dispatch logic (createDecoder().decode()) had zero direct tests — new tests/unit/decoder-decoder.test.ts (11 tests) covers settlement-header parse errors, the outcome.rawPaymentResponseHeader fallback path, and proxy.error passthrough; (b) base64 + unicode edge cases on parsePaymentHeader / parseChallengeBody / parseSettlementHeader mirroring the coinbase/x402#865 surface area — CJK + emoji in description and extra.name round-trip, padding-stripped base64 tolerated, JSON primitives rejected, URL-safe base64 contract documented; (c) reconciliation extractErrorReason paths (non-JSON rawBody, JSON without error, missing rawBody), the unknown proxy outcome branch the X402-15 demo relies on, chain-transfer race conditions (arrival before pending join, duplicate transfers after match); (d) x402trace proxy env-var-vs-flag precedence — extracted resolveProxyConfig(opts, env) as a pure function from proxy-command.ts (small refactor for testability), then pinned the contract from ARCHITECTURE.md § Configuration with 28 unit tests across X402TRACE_UPSTREAM, X402TRACE_PORT, X402TRACE_LOG, X402TRACE_RECONCILE (including non-truthy strings), BASE_RPC_URL, X402TRACE_WATCH_TIMEOUT_MS, X402TRACE_UPSTREAM_TIMEOUT_MS. Pipeline: 215 tests / 4 e2e skipped (+57 vs the X402-16 baseline). Coverage: reconciliation 97% → 100% lines/branches/funcs, decoder 79% → 90%, src/cli 80.8% → 82%.
  • README rewrite (X402-16): replaced the pre-release skeleton with a 115-line README that hits all seven X402-16 required sections — elevator pitch, the problem (with the verbatim 502 the buyer sees), 30-second quickstart (git clonepnpm installcp .env.example .env./examples/e2e-timeout-reconciliation.sh), how-it-works (4 bullets + ASCII flow diagram of proxy → server → facilitator → chain → engine), CLI reference (links to --help rather than duplicating), v0.2 roadmap pulled from SPEC.md § 5, differentiation, contributing, license. Three badges added: Apache-2.0 license (live), GitHub Actions CI (placeholder — wires up automatically when X402-18 lands the workflow), npm version (placeholder — wires up automatically when X402-19 publishes x402trace@0.1.0). The captured X402-15 settlement tx 0x116ccf73…ba52 is linked from the quickstart's "what success looks like" output block; the asciinema cast is referenced by relative path for local asciinema play until X402-23 uploads it for an embeddable URL.
  • End-to-end testnet demo (X402-15) in examples/e2e-timeout-reconciliation.sh + examples/README.md + recorded asciinema cast at examples/cast/e2e-timeout-reconciliation.cast. The flagship reproduction of the canonical #1062 scenario, runnable against real Base Sepolia + the real x402.org/facilitator. Choreography: dogfood server with new DEMO_SLEEP_MS=10000 knob sleeps 10s during the x402-hono paymentMiddleware's protected handler — x402-hono verifies before the handler and settles after, so the post-handler /settle still broadcasts on-chain even though the proxy in front gave up at 5s. x402trace proxy --reconcile --upstream-timeout-ms 5000 returns 502 to the client (the canonical "I thought it failed" signal), pends the exchange, watches Base Sepolia via the chain client's subscribeUsdcTransfers, matches the EIP-3009 nonce, and emits RECONCILED ⚠ settled-but-server-thinks-not with the live tx hash. Verified end-to-end on Base Sepolia 2026-05-12 with on-chain settlement tx 0x116ccf73…ba52 (block 41402768) — captured in the committed asciinema cast; reconcile gap from proxy timeout to chain-detected was 11.9 seconds. New --upstream-timeout-ms CLI flag on x402trace proxy exposes ProxyOptions.upstreamTimeoutMs to the surface. New demoSleepMs / demoFailAfterSleep fields on DogfoodConfig plus DEMO_SLEEP_MS / DEMO_FAIL_AFTER_SLEEP env wiring in loadServerConfig (demoFailAfterSleep is documented but NOT used in the canonical demo because x402-hono skips /settle on 5xx responses). 9 new tests — 8 unit tests covering env validation (positive/negative/zero/non-numeric) and CLI help inclusion, plus 1 hermetic integration test (tests/integration/demo-timeout-reconciliation.test.ts) that reproduces the full demo storyline against the mock facilitator with a synthetic ChainTransfer. Pipeline: 158 tests / 4 e2e skipped, typecheck + lint clean. README rewritten with a quick-demo hero section.
  • CLI binary (X402-14) in src/cli/ + src/cli.ts: the user-facing surface for v0.1, composing the four substrates (Proxy / Decoder / Chain / Reconciliation) into a single x402trace command. Two subcommands — x402trace proxy --upstream <url> [--port 8402] [--log human|json] [--log-file <path>] [--log-secrets] [--reconcile] [--rpc-url <url>] [--watch-timeout-ms <n>] runs the live pipeline; x402trace inspect <jsonl-log-file> [--log human|json] [--watch-timeout-ms <n>] replays a captured log and re-runs reconciliation offline. Built on commander@14. Modules — index.ts (commander dispatch + runCli testable entry), proxy-command.ts (live pipeline wiring; opens a second JsonlSink against the same log path for chain.transfer and reconcile.result records — closes the X402-13 deferred acceptance bullet "All reconciliation events written to a local JSONL log file"), inspect-command.ts (offline replay), replay.ts (JSONL reader driving a virtual-clock engine via the new engine.tick() sweep method), format-result.ts (human + JSON renderers for ReconciliationResult — the canonical RECONCILED ⚠ settled-but-server-thinks-not headline per SPEC.md § 3), color.ts (TTY + NO_COLOR-aware ANSI helpers; no chalk dep), exit-codes.ts (0 success / 1 usage / 2 runtime per the X402-14 ticket). 28 new unit tests + 1 smoke test that drives the full proxy + decoder + JSONL pipeline against the dogfood rig and replays the captured log through inspect. Pipeline: 149 tests / 4 e2e skipped, typecheck + lint clean. scripts/proxy.ts is now a thin shim that defers to runCli; pnpm proxy and pnpm x402trace both work, and npx x402trace will work once published.
  • Timeout reconciliation engine (X402-13) in src/reconciliation/: the headline feature of the v0.1 wedge. Four modules — types.ts (PendingExchange + ReconciliationResult 4-variant discriminated union: settled_on_chain / not_settled / value_mismatch / recipient_mismatch), match.ts (pure matchPendingAgainstTransfer checking (payer, payee, value, nonce) exact-equality; case-insensitive on addresses + nonce), engine.ts (createReconciliationEngine({watchTimeoutMs?, sweepIntervalMs?, now?}) → Engine with three ingest methods for proxy / decoder / chain streams, in-memory pending-set with two-half-join semantics, periodic sweep for not_settled after watchTimeoutMs (default 60_000), injectable clock for tests), index.ts. 17 unit tests covering the match function + engine lifecycle (rejected outcomes flag, paid outcomes don't, settled_on_chain emit, not_settled timeout, value/recipient mismatch, arrival-order independence) + 2-test integration that reproduces the canonical #1062 scenario end-to-end: mock facilitator rejects → engine emits settled_on_chain when matching ChainTransfer arrives. Pipeline: 120 tests / 4 e2e skipped, no new deps.
  • Local mock x402 facilitator (X402-3) at src/dogfood/mock-facilitator.ts implementing v1 POST /verify and POST /settle with canned-success responses. Unblocks integration testing without on-chain USDC and provides the test harness TESTING.md calls for. scripts/mock-facilitator.ts is the standalone entry; tests/integration/dogfood-paid-flow.test.ts asserts the full 402 → signed retry → 200 + X-PAYMENT-RESPONSE flow against it.
  • Shared src/dogfood/http-adapter.ts for mounting a Hono app on a Node http server (used by dev-server.ts, mock-facilitator.ts, and the integration test). Avoids a separate @hono/node-server dep.
  • Minimal tsconfig.json (strict ESM NodeNext), vitest.config.ts, eslint.config.js (flat), and .prettierrc.json scoped to what X402-3 needed. X402-10 will tighten and broaden these.

Changed

Deprecated

Removed

Fixed

Security