taximeter 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.
package/SPEC-NOTES.md ADDED
@@ -0,0 +1,369 @@
1
+ # Protocol verification notes
2
+
3
+ Research date: **2026-09-08**. These notes record the protocol baseline read before
4
+ implementation, the supported subset, and corrections to [SPEC.md](SPEC.md).
5
+ Amounts below are atomic integer strings. Taximeter does not sign, verify, or
6
+ settle a payment.
7
+
8
+ ## Implemented deviations and release limits
9
+
10
+ This is the release's consolidated deviation inventory. The detailed wire shapes
11
+ and source evidence follow below; these limits apply to both the CLI and SDK
12
+ unless a row names one intake specifically.
13
+
14
+ | Area in the brief | Implemented behavior and reason |
15
+ | --- | --- |
16
+ | Demo placeholder (§6) | The original placeholder is replaced by a 20-second Windows ConPTY recording rendered by agg. The simulation runs locally; no payment is signed or settled. Only the GIF ships in the package; capture tools and intermediate files are local development artifacts. |
17
+ | Broad x402 support (§2, §4) | Support HTTP v1/v2 `exact` EVM **EIP-3009** authorizations. v1 maps only `base` and `base-sepolia`; v2 accepts positive `eip155:<chain-id>` identifiers. Permit2, ERC-7710, other transfer methods, schemes, and networks are diagnosed and passed through. Their different authorization semantics cannot safely share this parser. |
18
+ | Generic 402 JSON and complete replay context (§0, §2) | v1 reads the challenge body and `X-PAYMENT`; v2 reads `PAYMENT-REQUIRED` and `PAYMENT-SIGNATURE`. v1 replays omit asset context and need a previously observed, unambiguous challenge. v2 replays carry `accepted`. A status of 402 alone does not establish x402. |
19
+ | Unbounded challenge observation (§4) | v1 correlation is local to each intake instance, with a five-minute TTL and at most 1,000 cached contexts. The key binds actual URL, method, task, agent, Authorization, and Cookie. Bodies are limited to 64 KiB; payment header text is limited to 65,536 characters. The proxy taps the streaming response without delaying delivery; the SDK additionally limits its cloned body observation to 100 ms. Missing, expired, oversized, slow, compressed proxy bodies, and ambiguous challenges cannot be metered reliably and pass through with a diagnostic. |
20
+ | Token metadata in every event (§3) | Only Base and Base Sepolia USDC have trusted, static six-decimal metadata. Other assets retain exact amounts with `decimalsKnown: false` and an internal `decimals: 0` placeholder; displays say **atomic units**, not zero-decimal tokens. No RPC, token-list, or price lookup runs at runtime. |
21
+ | One USDC balance and scalar maximum (§3) | Every total and budget is scoped separately by normalized network and contract. Symbol budgets match the local registry. `maxSingleAsset`, default `USDC`, scopes `maxSinglePayment`; unknown assets do not inherit a misleading one-USDC threshold. Optional budget `network` narrows a rule further. |
22
+ | One `events` table and the original event fields (§3) | Keep immutable events and add append-only `outcomes` and `diagnostics` tables. Internal fields include `paymentKey`, `decimalsKnown`, `settlementStatus`, `settlement_unknown`, and derived `attemptedAt`; outcomes identify each forwarding attempt. These are Taximeter fields, not invented x402 wire fields. |
23
+ | Deduplication by nonce/transaction/resource within a small window (§4) | A persistent key binds network, asset, payer, nonce, amount, recipient, and both authorization validity bounds. Signature, resource, and observation time are excluded. The same authorization counts once across retries and URLs; changed immutable authorization details retain a separate conservative reservation. |
24
+ | Observed payments equal settled spend (§3, §4) | Reserve recognized authorizations synchronously in the same SQLite transaction as policy evaluation, before forwarding. Upstream-reported success is confirmation evidence, not independent settlement verification. Any confirmed attempt wins; otherwise any unresolved attempt retains capacity. Only an authorization whose known attempts all failed contributes zero. |
25
+ | Retry timing and upstream failure (§4) | `attemptedAt` separates the latest unconfirmed forwarding attempt from immutable `ts`. An unconfirmed replay outside a rolling budget window must reacquire capacity. Missing settlement headers, disconnects, and unconfirmed 5xx responses remain unknown; an explicit matching success header can confirm even on a non-2xx response. |
26
+ | Storage failure while preserving traffic (§4) | Runtime ledger/policy-intake storage failures fail open and emit a visible local warning plus a diagnostic when storage permits. A failed outcome write leaves its original reservation in place. Budget enforcement requires working storage; startup/configuration failures still fail visibly rather than claiming a working service. |
27
+ | Universal zero-code proxy support (§2) | Clients must actually honor their proxy configuration. HTTPS CONNECT is an encrypted byte tunnel and is explicitly unmetered; Taximeter installs no certificate authority. An explicit localhost route with `--upstream`, or the SDK inside the payment wrapper, can inspect HTTP payment messages sent to HTTPS upstreams. |
28
+ | Explicit base URL semantics (§2) | Origin-form requests require `upstream`. Its **origin** supplies the destination; the incoming path replaces any configured upstream path prefix. For example, upstream `https://api.example/v1` plus `/weather` forwards to `https://api.example/weather`. Leading `//` stays on the configured origin. Original encoded paths and query text are preserved separately from URL authority validation. |
29
+ | Byte-identical forwarding and upgraded protocols (§4) | Preserve request/response payload bytes, duplicate end-to-end headers, and trailers, while rebuilding HTTP hop-by-hop headers and transfer framing as a proxy must. Recognized signed HTTP upgrade handshakes can be gated; subsequent upgraded stream frames are unmetered and diagnosed. CONNECT and upgrades do not imply inspection of payments hidden in their streams. |
30
+ | Wrapping an arbitrary payment-enabled fetch (§2) | Compose `wrapFetchWithPayment(withMeter(fetch, options), client)` so each signed replay reaches the meter. The supplied transport's internal retries and redirects are invisible. A response marked `redirected` emits a visibility diagnostic and is never cached as a v1 challenge for the original URL. Host-sensitive callers can request `redirect: "error"` or expose each hop. The wrapper returns the original response and never consumes a request body. |
31
+ | Configuration precedence (§3) | Extend precedence to flags > `TAXIMETER_*` environment > explicit `--config` file > cwd file > home file > defaults. The explicit file layers over automatic files. Environment support is exactly `TAXIMETER_DB`, `TAXIMETER_PORT`, and `TAXIMETER_DASHBOARD_PORT`. Relative database paths resolve from cwd; `~` expands locally. |
32
+ | Additional configuration choices (§3) | `upstream` supports explicit routing; budget windows support `1h`, `24h`, `7d`, and `30d`; nullable budgets and `maxSinglePayment` disable their respective rules; port `0` requests an available port. Config and CLI options remain strict Zod inputs. Host/recipient allowlists are exact, case-insensitive matches, not wildcard patterns. |
33
+ | Reset and local checks (§2) | `reset --yes` archives rather than deletes the ledger and acquires an atomic CLI lock. All SDK writers must be stopped separately; the lock coordinates CLI instances only. Read-only commands use an empty in-memory ledger when the path is absent. `doctor` checks local configuration and SQLite without requesting any upstream; it does not certify wallet/facilitator access or write permission for a nonexistent database path. |
34
+ | Dashboard session and hero (§5) | Timeline shows the current UTC hour and preceding 23 hourly buckets, not an arbitrary process session. The Now hero shows the selected asset's active global budget window; all-time ledger totals are labeled separately. Assets and networks never share one monetary chart total. The latest 30 events and 10 diagnostics are shown; raw payment JSON remains available in JSON export. |
35
+ | Thirty visible rows and Google Fonts (§5) | The event stream keeps 30 recent rows, with scrolling where screen height or width requires it. System grotesque and monospace stacks replace runtime Google Fonts requests to honor the network invariant. Complete light/dark tokens and exact BigInt-derived chart geometry are retained. |
36
+ | Export and invoice format (§2, §5) | CSV includes all derived payment rows: `amount` is the counted contribution, while `authorizedAmount` preserves the original proposal. Blocked and wholly failed payments contribute zero. Sum each network/asset separately to reproduce the dashboard ledger totals. The invoice is standalone printable HTML, with no PDF renderer, exchange-rate conversion, tax calculation, or settlement attestation. |
37
+ | Module and dependency choices (§7) | Ship ESM only, with no optional CommonJS build. Pin `better-sqlite3` 12.8.0 to preserve Node 20 support; version 13 requires Node 22. Exact pins and the audited esbuild override are recorded in `package.json` and the lockfile. |
38
+ | Fixture realism and financial claims (§7) | Fixture envelopes follow primary protocol examples but all authorizations, signatures, and settlement responses are synthetic. Tests exercise parsing, transport, budget races, failures, and exact accounting; no real transfers, wallet signing, facilitator call, or independent chain verification is performed. |
39
+ | Public installation and repository tagline (§1, §6) | `npm view taximeter` returned `E404` on **2026-09-08**, so the package name remains `taximeter` and registry installation is not yet available. The README retains the required `npx taximeter start` release command and provides source-build commands for current use. Fresh-directory startup from the local tarball is documented in `VERIFICATION.md`. Registry publication and repository visibility are maintainer-managed release settings. The intended repository description is **A taximeter for your AI agents.** |
40
+
41
+ These limitations make Taximeter a cooperative local meter. An agent that
42
+ bypasses the intake, uses an unsupported payment method, or hides a replay
43
+ inside a transport can bypass its budget checks. No supported flow holds funds,
44
+ private keys, or payment-signing authority.
45
+
46
+ ## Primary sources and published packages
47
+
48
+ The requested [Coinbase repository README](https://github.com/coinbase/x402/blob/main/README.md)
49
+ now identifies Coinbase's repository as a development fork and points to the
50
+ [x402 Foundation repository](https://github.com/x402-foundation/x402) as the project
51
+ home. The requested files were read, together with the Foundation specifications,
52
+ the HTTP transport specification, documentation, and actual npm tarballs:
53
+
54
+ - [Protocol v1](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v1.md)
55
+ and [protocol v2](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md).
56
+ - [Requested Coinbase v2 specification](https://github.com/coinbase/x402/blob/main/specs/x402-specification-v2.md)
57
+ and [exact EVM scheme](https://github.com/coinbase/x402/blob/main/specs/schemes/exact/scheme_exact_evm.md).
58
+ - [HTTP v2 transport](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md).
59
+ - [x402 documentation](https://docs.x402.org/introduction),
60
+ [migration guide](https://docs.x402.org/guides/migration-v1-to-v2),
61
+ [network and token support](https://docs.x402.org/core-concepts/network-and-token-support),
62
+ [exact scheme](https://docs.x402.org/schemes/exact), and
63
+ [payment identifier extension](https://docs.x402.org/extensions/payment-identifier).
64
+
65
+ All four published packages resolved to **2.25.0**. Their tarballs were downloaded
66
+ with `npm pack`, extracted outside the repository, and their READMEs, package
67
+ exports, and relevant exported declarations were inspected. These are research
68
+ inputs, not runtime dependencies of Taximeter.
69
+
70
+ | Package | Published API inspected | Relevant finding |
71
+ | --- | --- | --- |
72
+ | [@x402/core 2.25.0](https://www.npmjs.com/package/@x402/core/v/2.25.0) | `types`, `types/v1`, `schemas`, `client`, `http`, `server` | Both protocol versions; header helpers; optional fields differ between nominal types and runtime schemas. |
73
+ | [@x402/evm 2.25.0](https://www.npmjs.com/package/@x402/evm/v/2.25.0) | Main types, `exact/client`, `exact/server`, `exact/facilitator`, `v1` | EIP-3009 and Permit2 payloads; legacy network map; local default-asset metadata. |
74
+ | [@x402/fetch 2.25.0](https://www.npmjs.com/package/@x402/fetch/v/2.25.0) | `wrapFetchWithPayment`, `wrapFetchWithPaymentFromConfig` | The supplied fetch is called for the initial request and each signed replay. |
75
+ | [@x402/express 2.25.0](https://www.npmjs.com/package/@x402/express/v/2.25.0) | `paymentMiddleware`, `paymentMiddlewareFromConfig`, `paymentMiddlewareFromHTTPServer`, `ExpressAdapter` | Middleware verifies and settles; Taximeter must not adopt that facilitator role. |
76
+
77
+ The pinned package declarations can also be inspected at
78
+ [@x402/core types](https://unpkg.com/@x402/core@2.25.0/dist/esm/types/index.d.mts),
79
+ [@x402/core v1 types](https://unpkg.com/@x402/core@2.25.0/dist/esm/types/v1/index.d.mts),
80
+ [@x402/evm types](https://unpkg.com/@x402/evm@2.25.0/dist/esm/index.d.mts),
81
+ [@x402/fetch types](https://unpkg.com/@x402/fetch@2.25.0/dist/esm/index.d.mts), and
82
+ [@x402/express types](https://unpkg.com/@x402/express@2.25.0/dist/esm/index.d.mts).
83
+
84
+ ## HTTP wire formats
85
+
86
+ HTTP header names are case-insensitive. Payment headers contain Base64-encoded
87
+ UTF-8 JSON, not the signature alone. A response is not necessarily x402 just
88
+ because its status is 402.
89
+
90
+ | Message | v1 | v2 |
91
+ | --- | --- | --- |
92
+ | Payment requirements | JSON response body with `x402Version: 1` | `PAYMENT-REQUIRED` response header with `x402Version: 2` |
93
+ | Signed payment replay | `X-PAYMENT` request header | `PAYMENT-SIGNATURE` request header |
94
+ | Settlement result | `X-PAYMENT-RESPONSE` response header | `PAYMENT-RESPONSE` response header |
95
+
96
+ In v2, the response body is application content. It may be empty, JSON, HTML, or
97
+ another media type. It is not the authoritative location of payment requirements.
98
+ The published HTTP client checks `PAYMENT-REQUIRED` first and uses a v1 body only
99
+ as the compatibility fallback. It recognizes both settlement header names.
100
+ [HTTP transport source](https://github.com/x402-foundation/x402/blob/main/specs/transports-v2/http.md)
101
+
102
+ ### v1 envelopes
103
+
104
+ The following notation uses `?` for optional fields and describes the wire shape,
105
+ not a TypeScript import from a payment SDK:
106
+
107
+ ```text
108
+ PaymentRequiredV1 {
109
+ x402Version: 1,
110
+ error?: string,
111
+ accepts: PaymentRequirementsV1[]
112
+ }
113
+
114
+ PaymentRequirementsV1 {
115
+ scheme: string,
116
+ network: string,
117
+ maxAmountRequired: string,
118
+ resource: string,
119
+ description: string,
120
+ mimeType?: string,
121
+ outputSchema?: object | null,
122
+ payTo: string,
123
+ maxTimeoutSeconds: number,
124
+ asset: string,
125
+ extra?: object | null
126
+ }
127
+
128
+ PaymentPayloadV1 {
129
+ x402Version: 1,
130
+ scheme: string,
131
+ network: string,
132
+ payload: object
133
+ }
134
+ ```
135
+
136
+ The v1 specification's field table requires `error`; the published 2.25.0 Zod
137
+ schema makes it optional. Its `mimeType`, `outputSchema`, and `extra` are also
138
+ optional even though the nominal declaration is stricter. Taximeter follows the
139
+ compatible runtime shape for these fields. `accepts` must contain at least one
140
+ option; `maxTimeoutSeconds` is a positive number.
141
+
142
+ Legacy networks use names such as `base` and `base-sepolia`. A v1 signed replay
143
+ does **not** carry `asset`, `resource`, `accepted`, or `maxAmountRequired` at its
144
+ top level. The preceding challenge supplies that missing context.
145
+ [v1 source](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v1.md)
146
+
147
+ ### v2 envelopes
148
+
149
+ ```text
150
+ ResourceInfo {
151
+ url: string,
152
+ description?: string,
153
+ mimeType?: string
154
+ }
155
+
156
+ PaymentRequirementsV2 {
157
+ scheme: string,
158
+ network: string,
159
+ amount: string,
160
+ asset: string,
161
+ payTo: string,
162
+ maxTimeoutSeconds: number,
163
+ extra?: object | null
164
+ }
165
+
166
+ PaymentRequiredV2 {
167
+ x402Version: 2,
168
+ error?: string,
169
+ resource: ResourceInfo,
170
+ accepts: PaymentRequirementsV2[],
171
+ extensions?: object | null
172
+ }
173
+
174
+ PaymentPayloadV2 {
175
+ x402Version: 2,
176
+ resource?: ResourceInfo,
177
+ accepted: PaymentRequirementsV2,
178
+ payload: object,
179
+ extensions?: object | null
180
+ }
181
+ ```
182
+
183
+ The v2 amount field is `amount`, not v1's `maxAmountRequired`. The selected option
184
+ is `accepted`, not `accepts`. The required-response resource is a separate object;
185
+ the replay's resource is optional. Networks use CAIP-2 identifiers such as
186
+ `eip155:8453` and `eip155:84532`.
187
+
188
+ Published schemas accept `null` for optional v2 resource/error metadata and
189
+ normalize it to absence. They also permit resource metadata `serviceName`, `tags`,
190
+ and `iconUrl`. These optional fields and unknown extensions do not determine
191
+ spend. The original parsed JSON is retained for auditing, and forwarding does
192
+ not reserialize or rewrite the payment payload.
193
+ [v2 source](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md)
194
+
195
+ ### Exact EVM EIP-3009 payload, both versions
196
+
197
+ ```text
198
+ payload {
199
+ signature: hex-string,
200
+ authorization: {
201
+ from: EVM-address,
202
+ to: EVM-address,
203
+ value: unsigned-decimal-integer-string,
204
+ validAfter: unsigned-decimal-integer-string,
205
+ validBefore: unsigned-decimal-integer-string,
206
+ nonce: 32-byte-hex-string
207
+ }
208
+ }
209
+ ```
210
+
211
+ `value` is the signed token amount. `validAfter` and `validBefore` are Unix seconds
212
+ represented as strings; they are not the observation timestamp. `from` is the
213
+ payer and `to` is the recipient. An exact payment's signed `value` must match the
214
+ selected requirement's amount; its recipient must match `payTo`. Comparisons of
215
+ EVM addresses are case-insensitive. Parsing these relationships is not signature
216
+ verification.
217
+
218
+ The scheme document describes a 65-byte EOA signature, while published
219
+ `ExactEIP3009Payload` declares `signature?` and the implementation supports smart
220
+ account signature paths. A monitor must not assume every supported signature is
221
+ exactly 65 bytes. Unsupported or structurally unrecognized payloads remain
222
+ transparent traffic, with a diagnostic.
223
+ [Exact EVM source](https://github.com/coinbase/x402/blob/main/specs/schemes/exact/scheme_exact_evm.md)
224
+
225
+ The same exact-scheme source also describes Permit2
226
+ (`payload.permit2Authorization` with `permitted.token`, `permitted.amount`, `from`,
227
+ `spender`, decimal `nonce`, `deadline`, and `witness.to`/`witness.validAfter`) and
228
+ ERC-7710 (`delegationManager`, `permissionContext`, `delegator`). Published EVM
229
+ 2.25.0 types include EIP-3009 and Permit2. These are distinct payload formats, not
230
+ aliases for `authorization`. Taximeter's initial adapter does not meter them.
231
+
232
+ ### Settlement responses
233
+
234
+ Both header generations decode to an object with the following fields. There is
235
+ no required `x402Version` in this object:
236
+
237
+ ```text
238
+ SettleResponse {
239
+ success: boolean,
240
+ transaction: string,
241
+ network: string,
242
+ payer?: string,
243
+ errorReason?: string,
244
+ errorMessage?: string,
245
+ amount?: unsigned-decimal-integer-string,
246
+ extensions?: object,
247
+ extensionResponses?: object,
248
+ extra?: object
249
+ }
250
+ ```
251
+
252
+ `transaction` is the wire field. Taximeter maps it to internal `txHash`; it does
253
+ not search for an invented wire `txHash`. Failed settlement may use an empty
254
+ transaction string. v1 uses legacy network names; v2 uses CAIP-2. Optional
255
+ `amount` appears in current v2 types for schemes whose settlement differs from
256
+ their authorization; it is not permission to treat `upto` as `exact`. Published
257
+ types make `payer` optional in both versions, despite the older v1 table.
258
+ [Settlement source](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md)
259
+
260
+ The monitor considers settlement headers on all response statuses. A signed
261
+ replay, HTTP 200, or transaction-looking string alone is not independently
262
+ verified settlement. A recognized success is described as **reported settled**.
263
+ Malformed, missing, or inconsistent settlement evidence remains unknown.
264
+
265
+ ## Supported subset and accounting decisions
266
+
267
+ 1. **Versions and networks.** Meter v1 and v2 HTTP `exact` EVM EIP-3009 payments.
268
+ Normalize v1 `base` to `eip155:8453` and `base-sepolia` to `eip155:84532`.
269
+ Other legacy names are outside the first implementation's mapping. v2 accepts
270
+ EVM CAIP-2 networks. Other schemes, transfer methods, networks, and versions
271
+ pass through with a diagnostic. This bounded subset supports the common USDC
272
+ flow without pretending to understand different authorization semantics.
273
+ 2. **Challenge correlation.** v1 requires an observed compatible challenge.
274
+ Correlation uses actual URL, method, task, agent, Authorization, and Cookie,
275
+ with a five-minute TTL and 1,000-context capacity. It then matches scheme,
276
+ network, recipient, and atomic amount.
277
+ Ambiguous asset choices must not be guessed. A v2 replay can supply its own
278
+ selected requirements, so the initial 402 exchange need not have been seen.
279
+ Host policy uses the actual upstream URL, not an untrusted advertised URL.
280
+ The SDK does not cache a response marked `redirected` under the original URL.
281
+ 3. **Asset identity.** Totals and budget windows are separate for every normalized
282
+ network and asset contract. An asset symbol is a label, not an identity.
283
+ Case-normalized EVM addresses avoid duplicate groups for checksum variants.
284
+ 4. **Decimals.** Neither protocol envelope provides reliable decimals or ticker
285
+ metadata. `extra.name` and `extra.version` are EIP-712 domain fields, not a
286
+ verified symbol/decimals pair. Use a local registry for Base and Base Sepolia
287
+ USDC only. Unknown assets retain exact atomic units, `decimalsKnown: false`,
288
+ and an internal `decimals: 0` placeholder; the UI labels them as atomic units.
289
+ It must not claim that the token has zero decimals. There are no runtime RPC
290
+ or metadata lookups.
291
+ 5. **Local USDC registry.** The six-decimal entries are Base
292
+ `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` and Base Sepolia
293
+ `0x036CbD53842c5426634e7929541eC2318f3dCF7e`, as documented in the
294
+ [official asset table](https://docs.x402.org/core-concepts/network-and-token-support).
295
+ An arbitrary contract advertising `name: "USDC"` does not join these groups.
296
+ 6. **Budget asset scope.** The brief's scalar `maxSinglePayment` defaults to
297
+ verified USDC, made explicit through `maxSingleAsset` with default `USDC`.
298
+ Symbol budgets match trusted local registry entries and are evaluated
299
+ separately for each network/asset pair. There is no cross-chain combined
300
+ dollar balance and no exchange-rate conversion.
301
+ 7. **Authorization and outcomes.** A challenge is an offer, not spend. A forwarded
302
+ recognized authorization consumes conservative budget capacity before the
303
+ asynchronous upstream request completes. Append-only outcome observations
304
+ distinguish reported settlement, known failure, and unknown settlement.
305
+ Outcomes retain separate attempt identities. Confirmation on any attempt
306
+ wins; otherwise any unresolved attempt retains capacity. Only wholly failed
307
+ attempts release a payment's contribution. Missing settlement evidence, a
308
+ disconnect, or an unconfirmed upstream 5xx after forwarding does not silently
309
+ release that capacity. `attemptedAt` permits an unconfirmed retry outside a
310
+ rolling window to reacquire capacity without rewriting the original event.
311
+ 8. **Idempotency.** The principal EIP-3009 identity includes network, asset,
312
+ payer, and nonce, bound to amount, recipient, `validAfter`, and `validBefore`.
313
+ Repeated observations of that authorization count once,
314
+ including observations under a different resource. Nonce alone would collide
315
+ across payers/contracts; transaction hash alone can collapse different
316
+ transfers in one transaction. Resource and wall-clock time alone are not a
317
+ reliable payment identity. The optional protocol payment-identifier extension
318
+ is preserved but is not trusted to override an EIP-3009 authorization identity.
319
+ Signature bytes are not part of the key. Conflicting immutable authorization
320
+ details receive separate conservative reservations rather than a free replay.
321
+ 9. **Concurrency.** Policy evaluation and reservation must be one synchronous
322
+ operation before forwarding. Otherwise concurrent requests can all observe
323
+ the same old total. Totals derive from the event log; settlement observations
324
+ do not mutate earlier rows.
325
+ 10. **Transparency boundary.** Unsupported or malformed traffic is forwarded
326
+ with its payload unchanged and `parse_failed` diagnostics. Storage failures
327
+ also fail open, with a visible local warning; enforcement needs working
328
+ storage. Therefore this is a cooperative
329
+ local meter, not a security boundary against an agent deliberately bypassing
330
+ the proxy or using an unsupported payment form. Raw audit payloads are
331
+ payment authorizations, not private keys; the ledger is local state.
332
+
333
+ ## Additional protocol corrections relative to the brief
334
+
335
+ The comprehensive implementation inventory is at the top of this file. These
336
+ additional corrections concern the research premise itself:
337
+
338
+ | Brief assumption or omission | Resolution |
339
+ | --- | --- |
340
+ | Coinbase repository is the primary project home. | Read the requested files and follow the README's Foundation pointer; preserve source links and research date above. |
341
+ | Nominal TypeScript declarations are the only accepted wire shape. | Published 2.25.0 runtime schemas permit optional/nullish descriptive fields and optional EIP-3009 signatures. Follow compatible schemas without weakening the authorization relationships that determine spend. |
342
+ | The payment rails have no budget support. | Published core 2.25.0 already has client `spendControls`, including a default per-payment cap. Taximeter's distinct contribution is the persistent local ledger, attribution, rolling budgets, and exports. |
343
+
344
+ The HTTPS limitation follows from the transport design: a CONNECT tunnel gives
345
+ the proxy an encrypted stream, while the payment protocol lives inside HTTP
346
+ headers. Taximeter does not install a certificate authority or intercept TLS.
347
+ The package README documents which connection modes are metered.
348
+
349
+ For SDK composition, the intended pattern is
350
+ `wrapFetchWithPayment(withMeter(fetch, options), client)`. The official fetch
351
+ implementation invokes the supplied transport again for the replay, so this
352
+ placement allows the meter to observe the challenge and gate the authorization
353
+ before it reaches the upstream.
354
+ [@x402/fetch source](https://unpkg.com/@x402/fetch@2.25.0/dist/esm/index.mjs)
355
+
356
+ ## Fixture provenance and verification limits
357
+
358
+ Fixture field names, example Base Sepolia token address, and envelope layout
359
+ come from the linked v1/v2 and exact EVM specification examples. Test values,
360
+ nonces, signatures, resource URLs, and transaction identifiers may be adapted
361
+ deterministically for localhost tests. They are **synthetic protocol examples**,
362
+ not captures of real paid requests and not evidence of cryptographic validity.
363
+ The fixture server accepts the documented replay shape without moving money.
364
+
365
+ Parser tests establish structural compatibility and exact accounting;
366
+ integration tests establish forwarding, blocking, idempotency, and failure
367
+ handling. Neither test category establishes on-chain settlement, facilitator
368
+ availability, wallet compatibility, or universal x402 support. Those claims are
369
+ outside the initial implementation and its non-custodial scope.