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.
@@ -0,0 +1,943 @@
1
+ # Release verification
2
+
3
+ The release passed the local clean-checkout audit on `feat/taximeter-v0.1.0`.
4
+ The hosted release build also completed; publishing requires npm credentials as
5
+ described below. Public npm publication is not claimed.
6
+
7
+ ## Behavioral evidence
8
+
9
+ At audit commit `336e8e8`, the full suite passed **298 tests in 17 files** on both Node 20 and Node 22.
10
+ Both `src/ledger` and `src/policy` had **100% statements, branches, functions,
11
+ and lines**. Typecheck and Biome reported zero errors; lint reported zero warnings.
12
+
13
+ | Requirement | Automated evidence |
14
+ | --- | --- |
15
+ | 100 payments total exactly | `test/proxy.test.ts`: 100 v1 and 100 v2 replays each yield integer string `700`. |
16
+ | Payment 21 blocked at a 20-payment cap | Proxy tests check HTTP 402, all five documented JSON fields, a blocked event, and no upstream replay. A 100-request concurrent race permits exactly 20. |
17
+ | Unknown traffic preserved | Proxy/SDK tests compare binary, gzip, large bodies, request bodies, duplicate cookies, trailers, encoded paths, and unrecognized protocol payloads. |
18
+ | CLI CSV equals dashboard totals | `test/server.test.ts` writes a real CLI CSV and compares its BigInt sums with a live API response across blocked/failed rows and multiple assets. |
19
+ | Deterministic replay | Ledger tests replay events/outcomes and compare exact derived totals. |
20
+ | No floating monetary conversion | `test/money-invariant.test.ts` parses every source/UI file and rejects Number/parseFloat/parseInt calls; large-value tests cross the safe-integer boundary. |
21
+
22
+ ## Dashboard evidence
23
+
24
+ Local Chromium checks exercised all six views in empty and populated states at
25
+ 1440×1200 and 390×844, with both light and dark browser color schemes. All eight
26
+ combinations fit the viewport horizontally, downloaded CSV successfully, and made
27
+ no external requests. Screenshots were visually inspected for the live ledger,
28
+ mobile empty state, timeline, and export preview. Thirty rows are available;
29
+ shorter screens and narrow tables scroll.
30
+
31
+ The included demo ran successfully against an in-memory ledger and local fixture:
32
+
33
+ ```text
34
+ Payment 20 | allowed | total 140 atomic units
35
+ Payment 21 | BLOCKED | total 140 atomic units
36
+ {"error":"blocked_by_taximeter","reason":"global_budget","budget":"140","spent":"140","remaining":"0"}
37
+ Exact ledger total: 140 atomic units. One blocked event recorded.
38
+ ```
39
+
40
+ SDK examples were executed against ordinary loopback traffic, including the real
41
+ `@x402/fetch` 2.25.0 wrapper with its supported client classes. No signing, actual
42
+ payment, wallet, facilitator, or chain access was involved.
43
+
44
+ ## Release audit
45
+
46
+ Audited source commit: `336e8e85bc61c176ddcea82a17b438d80193978a`. Date: **2026-09-08**.
47
+ Two independent Git clones were created outside the working repository on Windows.
48
+ Node **20.20.2** and **22.23.2** each used npm **11.6.2** and their own native
49
+ SQLite installation. All audit commands below exited zero. Captured standard output
50
+ and error streams follow below. Machine-specific paths, line endings, and trailing
51
+ whitespace are normalized; command results and diagnostic messages are retained.
52
+
53
+ The startup harness runs the literal source command from the clean checkout, and
54
+ the literal npx command from a fresh directory containing only the tarball. Each
55
+ startup uses an empty home, an isolated empty npm cache, no Taximeter configuration
56
+ file, and no Taximeter environment overrides. It checks the default listeners,
57
+ ledger, API and prebuilt assets, then intentionally stops its own child processes.
58
+ This is an installation smoke test, not publication to npm.
59
+
60
+ The two builds produced the same **158,229-byte**, 25-file tarball at the audited
61
+ commit. This evidence document is committed afterward, so later packages include
62
+ its transcripts and have a different archive hash/size; the runtime source and
63
+ lockfile do not change when the evidence is appended. The package check enforces
64
+ the 2,000,000-byte limit on every produced archive.
65
+
66
+ The README's three source commands took about **13.3 seconds** of combined command
67
+ time on Node 20 in this environment (normal development dependency cache). The
68
+ separate npx install/start check used a fresh npm cache and completed in **6.7 seconds**.
69
+ These measurements are local observations, not a guarantee of download speed.
70
+
71
+ Dependency installation reports deprecation notices for transitive prebuild-install
72
+ and glob; npm's audit reports zero vulnerabilities. Vite reports two removable
73
+ annotation comments in Zod. Those notices are retained below. Typecheck has zero
74
+ errors, and Biome has zero errors and zero warnings.
75
+
76
+ ### Windows / Node v20.20.2
77
+
78
+ Clean checkout: `<TEMP>/taximeter-audit-node20/checkout`.
79
+
80
+ #### git clone (fresh checkout)
81
+
82
+ ```text
83
+ Cloning into '<TEMP>/taximeter-audit-node20/checkout'...
84
+ done.
85
+ ```
86
+
87
+ Exit: 0. Elapsed: 426 ms.
88
+
89
+ #### node --version
90
+
91
+ ```text
92
+ v20.20.2
93
+ ```
94
+
95
+ Exit: 0. Elapsed: 70 ms.
96
+
97
+ #### npm --version
98
+
99
+ ```text
100
+ 11.6.2
101
+ ```
102
+
103
+ Exit: 0. Elapsed: 239 ms.
104
+
105
+ #### npm ci
106
+
107
+ ```text
108
+ npm warn deprecated prebuild-install@7.1.3: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
109
+ npm warn deprecated glob@10.5.0: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
110
+
111
+ added 295 packages, and audited 296 packages in 6s
112
+
113
+ 58 packages are looking for funding
114
+ run `npm fund` for details
115
+
116
+ found 0 vulnerabilities
117
+ ```
118
+
119
+ Exit: 0. Elapsed: 5937 ms.
120
+
121
+ #### npm run typecheck
122
+
123
+ ```text
124
+
125
+ > taximeter@0.1.0 typecheck
126
+ > tsc --noEmit
127
+
128
+ ```
129
+
130
+ Exit: 0. Elapsed: 5677 ms.
131
+
132
+ #### npm run lint
133
+
134
+ ```text
135
+
136
+ > taximeter@0.1.0 lint
137
+ > biome check --error-on-warnings .
138
+
139
+ Checked 57 files in 56ms. No fixes applied.
140
+ ```
141
+
142
+ Exit: 0. Elapsed: 897 ms.
143
+
144
+ #### npm test
145
+
146
+ ```text
147
+
148
+ > taximeter@0.1.0 test
149
+ > vitest run --coverage
150
+
151
+
152
+ RUN v3.2.7 <TEMP>/taximeter-audit-node20/checkout
153
+ Coverage enabled with v8
154
+
155
+ ✓ test/config.test.ts (19 tests) 18ms
156
+ ✓ test/fixture.test.ts (2 tests) 83ms
157
+ ✓ test/attempts.test.ts (2 tests) 19ms
158
+ ✓ test/x402.test.ts (74 tests) 98ms
159
+ ✓ test/policy.test.ts (21 tests) 28ms
160
+ ✓ test/core.test.ts (2 tests) 66ms
161
+ ✓ test/scaffold.test.ts (1 test) 2ms
162
+ ✓ test/ledger.test.ts (29 tests) 202ms
163
+ ✓ test/proxy-upgrade.test.ts (3 tests) 66ms
164
+ ✓ test/ui.test.ts (14 tests) 54ms
165
+ ✓ test/hygiene.test.ts (5 tests) 662ms
166
+ ✓ test/money-invariant.test.ts (2 tests) 121ms
167
+ ✓ test/export.test.ts (15 tests) 75ms
168
+ ✓ test/sdk.test.ts (25 tests) 464ms
169
+ ✓ test/cli.test.ts (32 tests) 563ms
170
+ ✓ test/server.test.ts (29 tests) 976ms
171
+ ✓ test/proxy.test.ts (23 tests) 1386ms
172
+ ✓ native HTTP payment proxy > 100 simulated x402 v1 payments produce the exact integer total 700 442ms
173
+ ✓ native HTTP payment proxy > 100 simulated x402 v2 payments produce the exact integer total 700 311ms
174
+
175
+ Test Files 17 passed (17)
176
+ Tests 298 passed (298)
177
+ Start at 01:27:53
178
+ Duration 3.14s (transform 2.65s, setup 0ms, collect 13.17s, tests 4.88s, environment 3ms, prepare 4.75s)
179
+
180
+ % Coverage report from v8
181
+ ---------------|---------|----------|---------|---------|-----------------------
182
+ File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
183
+ ---------------|---------|----------|---------|---------|-----------------------
184
+ All files | 95.54 | 91.66 | 97.46 | 95.54 |
185
+ src | 100 | 97.95 | 100 | 100 |
186
+ assets.ts | 100 | 100 | 100 | 100 |
187
+ config.ts | 100 | 100 | 100 | 100 |
188
+ core.ts | 100 | 96.87 | 100 | 100 | 72
189
+ index.ts | 100 | 100 | 100 | 100 |
190
+ model.ts | 100 | 100 | 100 | 100 |
191
+ src/cli | 80.2 | 72.09 | 66.66 | 80.2 |
192
+ index.ts | 0 | 100 | 100 | 0 | 2-12
193
+ program.ts | 84.61 | 71.42 | 60 | 84.61 | 62-86,155-157
194
+ src/config | 98.36 | 97.05 | 100 | 98.36 |
195
+ io.ts | 98.36 | 97.05 | 100 | 98.36 | 17
196
+ src/export | 100 | 96.55 | 100 | 100 |
197
+ index.ts | 100 | 96.55 | 100 | 100 | 47
198
+ src/ledger | 100 | 100 | 100 | 100 |
199
+ derive.ts | 100 | 100 | 100 | 100 |
200
+ store.ts | 100 | 100 | 100 | 100 |
201
+ src/policy | 100 | 100 | 100 | 100 |
202
+ index.ts | 100 | 100 | 100 | 100 |
203
+ src/proxy | 90.84 | 73.17 | 100 | 90.84 |
204
+ index.ts | 90.84 | 73.17 | 100 | 90.84 | ...67,279-280,285-286
205
+ src/rails | 100 | 96.33 | 100 | 100 |
206
+ schemas.ts | 100 | 100 | 100 | 100 |
207
+ types.ts | 0 | 0 | 0 | 0 |
208
+ x402.ts | 100 | 96.26 | 100 | 100 | 43,111,121,225
209
+ src/sdk | 91.27 | 93.65 | 100 | 91.27 |
210
+ index.ts | 91.27 | 93.65 | 100 | 91.27 | 97-103,140-145
211
+ src/server | 96.96 | 91 | 100 | 96.96 |
212
+ index.ts | 100 | 97.95 | 100 | 100 | 90
213
+ lifecycle.ts | 89.41 | 79.16 | 100 | 89.41 | 31-36,56-58
214
+ schema.ts | 100 | 100 | 100 | 100 |
215
+ state.ts | 98.75 | 88.88 | 100 | 98.75 | 39
216
+ ---------------|---------|----------|---------|---------|-----------------------
217
+ ```
218
+
219
+ Exit: 0. Elapsed: 6173 ms.
220
+
221
+ #### npm run build
222
+
223
+ ```text
224
+
225
+ > taximeter@0.1.0 build
226
+ > tsup && vite build
227
+
228
+ CLI Building entry: src/index.ts, src/cli/index.ts
229
+ CLI Using tsconfig: tsconfig.json
230
+ CLI tsup v8.5.1
231
+ CLI Using tsup config: <TEMP>/taximeter-audit-node20/checkout\tsup.config.ts
232
+ CLI Target: node20
233
+ CLI Cleaning output folder
234
+ ESM Build start
235
+ ESM dist\chunk-I7YXGTRZ.js 44.76 KB
236
+ ESM dist\index.js 5.34 KB
237
+ ESM dist\cli\index.js 17.91 KB
238
+ ESM ⚡️ Build success in 24ms
239
+ DTS Build start
240
+ DTS ⚡️ Build success in 2485ms
241
+ DTS dist\cli\index.d.ts 20.00 B
242
+ DTS dist\index.d.ts 14.10 KB
243
+ vite v6.4.3 building for production...
244
+ transforming...
245
+ node_modules/zod/v4/core/util.js (330:0): A comment
246
+
247
+ "// Wrapped in a `@__PURE__` IIFE: esbuild never tree-shakes a top-level initializer that contains a member access on `Number`, so the bare object literal survived into every bundle."
248
+
249
+ in "node_modules/zod/v4/core/util.js" contains an annotation that Rollup cannot interpret due to the position of the comment. The comment will be removed to avoid issues.
250
+ node_modules/zod/v4/core/regexes.js (70:0): A comment
251
+
252
+ "/** Anchors a pattern source. The interpolation lives here rather than at the call site because
253
+ * esbuild will not drop a `@__PURE__` call whose own argument interpolates a variable, but it
254
+ * will drop `anchor(dateSource)`. Keeping it inline pinned `date` into every bundle. */"
255
+
256
+ in "node_modules/zod/v4/core/regexes.js" contains an annotation that Rollup cannot interpret due to the position of the comment. The comment will be removed to avoid issues.
257
+ ✓ 128 modules transformed.
258
+ rendering chunks...
259
+ computing gzip size...
260
+ ../dist/ui/index.html 0.57 kB │ gzip: 0.35 kB
261
+ ../dist/ui/assets/index-De4zKRDL.css 15.43 kB │ gzip: 3.89 kB
262
+ ../dist/ui/assets/index-DOE5bD9P.js 307.37 kB │ gzip: 92.40 kB
263
+ ✓ built in 1.08s
264
+ ```
265
+
266
+ Exit: 0. Elapsed: 6451 ms.
267
+
268
+ #### npm pack
269
+
270
+ ```text
271
+ npm notice
272
+ npm notice 📦 taximeter@0.1.0
273
+ npm notice Tarball Contents
274
+ npm notice 304B CHANGELOG.md
275
+ npm notice 1.7kB CODE_OF_CONDUCT.md
276
+ npm notice 5.0kB CONTRIBUTING.md
277
+ npm notice 11.9kB DECISIONS.md
278
+ npm notice 1.1kB LICENSE
279
+ npm notice 10.9kB README.md
280
+ npm notice 3.7kB SECURITY.md
281
+ npm notice 27.5kB SPEC-NOTES.md
282
+ npm notice 23.4kB SPEC.md
283
+ npm notice 3.8kB VERIFICATION.md
284
+ npm notice 45.8kB dist/chunk-I7YXGTRZ.js
285
+ npm notice 20B dist/cli/index.d.ts
286
+ npm notice 18.3kB dist/cli/index.js
287
+ npm notice 14.4kB dist/index.d.ts
288
+ npm notice 5.5kB dist/index.js
289
+ npm notice 15.4kB dist/ui/assets/index-De4zKRDL.css
290
+ npm notice 307.4kB dist/ui/assets/index-DOE5bD9P.js
291
+ npm notice 566B dist/ui/index.html
292
+ npm notice 42B docs/demo.gif
293
+ npm notice 4.9kB docs/demo.mjs
294
+ npm notice 202B docs/demo.tape
295
+ npm notice 1.7kB docs/RECORDING.md
296
+ npm notice 9.5kB docs/SDK.md
297
+ npm notice 1.9kB package.json
298
+ npm notice 461B taximeter.config.example.json
299
+ npm notice Tarball Details
300
+ npm notice name: taximeter
301
+ npm notice version: 0.1.0
302
+ npm notice filename: taximeter-0.1.0.tgz
303
+ npm notice package size: 158.2 kB
304
+ npm notice unpacked size: 515.4 kB
305
+ npm notice shasum: ff654760a19721c626f03932d5bd1f325402d89e
306
+ npm notice integrity: sha512-GxdzY44Z6T4gA[...]7ov2pHlY/oykA==
307
+ npm notice total files: 25
308
+ npm notice
309
+ taximeter-0.1.0.tgz
310
+ ```
311
+
312
+ Exit: 0. Elapsed: 1171 ms.
313
+
314
+ #### npm run check:package
315
+
316
+ ```text
317
+
318
+ > taximeter@0.1.0 check:package
319
+ > node scripts/check-package.mjs
320
+
321
+ Package verified: taximeter-0.1.0.tgz
322
+ 158229 bytes compressed; 515398 bytes unpacked; 25 files.
323
+ Both CLI aliases, ESM entry point, declarations, and prebuilt UI are present.
324
+ 26 relative Markdown file links resolve inside the package.
325
+ Files whitelist honored; no UI source maps, source tree, tests, dependencies, or local state.
326
+ ```
327
+
328
+ Exit: 0. Elapsed: 1344 ms.
329
+
330
+ #### node docs/demo.mjs
331
+
332
+ ```text
333
+ Taximeter | local simulation | no money moves
334
+ Budget: 140 atomic units. Each payment: 7 atomic units.
335
+ Payment 1 | allowed | total 7 atomic units
336
+ Payment 2 | allowed | total 14 atomic units
337
+ Payment 3 | allowed | total 21 atomic units
338
+ Payment 4 | allowed | total 28 atomic units
339
+ Payment 5 | allowed | total 35 atomic units
340
+ Payment 6 | allowed | total 42 atomic units
341
+ Payment 7 | allowed | total 49 atomic units
342
+ Payment 8 | allowed | total 56 atomic units
343
+ Payment 9 | allowed | total 63 atomic units
344
+ Payment 10 | allowed | total 70 atomic units
345
+ Payment 11 | allowed | total 77 atomic units
346
+ Payment 12 | allowed | total 84 atomic units
347
+ Payment 13 | allowed | total 91 atomic units
348
+ Payment 14 | allowed | total 98 atomic units
349
+ Payment 15 | allowed | total 105 atomic units
350
+ Payment 16 | allowed | total 112 atomic units
351
+ Payment 17 | allowed | total 119 atomic units
352
+ Payment 18 | allowed | total 126 atomic units
353
+ Payment 19 | allowed | total 133 atomic units
354
+ Payment 20 | allowed | total 140 atomic units
355
+ Payment 21 | BLOCKED | total 140 atomic units
356
+ {"error":"blocked_by_taximeter","reason":"global_budget","budget":"140","spent":"140","remaining":"0"}
357
+ Exact ledger total: 140 atomic units. One blocked event recorded.
358
+ ```
359
+
360
+ Exit: 0. Elapsed: 2059 ms.
361
+
362
+ #### node dist/cli/index.js start (fresh home)
363
+
364
+ ```text
365
+
366
+ > taximeter@0.1.0 smoke:package
367
+ > node scripts/smoke-package.mjs --source
368
+
369
+ Smoke runtime: v20.20.2
370
+ Smoke workspace: <TEMP>/taximeter-package-smoke
371
+ PASS: isolated home and npm cache; no Taximeter config file or environment overrides.
372
+ $ node dist/cli/index.js start
373
+ Taximeter 0.1.0
374
+ Proxy: http://127.0.0.1:8402
375
+ Dashboard: http://127.0.0.1:8403
376
+ Point an HTTP-proxy-aware agent at http://127.0.0.1:8402.
377
+ HTTPS CONNECT is unmetered; use --upstream or withMeter for HTTPS payments.
378
+ PASS: default proxy 8402 and dashboard 8403; default ledger created in the fresh home.
379
+ PASS: validated empty dashboard summary and default budget without configuration.
380
+ PASS: prebuilt dashboard HTML and 2 local JS/CSS assets served with correct MIME types.
381
+ PASS: smoke HTTP requests stayed on loopback; browser network behavior is verified separately.
382
+ PASS: owned CLI and launcher processes stopped.
383
+ PASS: verified temporary workspace removed.
384
+ ```
385
+
386
+ Exit: 0. Elapsed: 943 ms.
387
+
388
+ #### npx ./taximeter-0.1.0.tgz start (fresh cwd and home)
389
+
390
+ ```text
391
+
392
+ > taximeter@0.1.0 smoke:package
393
+ > node scripts/smoke-package.mjs
394
+
395
+ Smoke runtime: v20.20.2
396
+ Smoke workspace: <TEMP>/taximeter-package-smoke
397
+ PASS: isolated home and npm cache; no Taximeter config file or environment overrides.
398
+ $ npx ./taximeter-0.1.0.tgz start
399
+ npm warn deprecated prebuild-install@7.1.3: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
400
+ Taximeter 0.1.0
401
+ Proxy: http://127.0.0.1:8402
402
+ Dashboard: http://127.0.0.1:8403
403
+ Point an HTTP-proxy-aware agent at http://127.0.0.1:8402.
404
+ HTTPS CONNECT is unmetered; use --upstream or withMeter for HTTPS payments.
405
+ PASS: default proxy 8402 and dashboard 8403; default ledger created in the fresh home.
406
+ PASS: validated empty dashboard summary and default budget without configuration.
407
+ PASS: prebuilt dashboard HTML and 2 local JS/CSS assets served with correct MIME types.
408
+ PASS: smoke HTTP requests stayed on loopback; browser network behavior is verified separately.
409
+ PASS: owned CLI and launcher processes stopped.
410
+ PASS: verified temporary workspace removed.
411
+ ```
412
+
413
+ Exit: 0. Elapsed: 6662 ms.
414
+
415
+ #### git status --porcelain
416
+
417
+ ```text
418
+ ```
419
+
420
+ Exit: 0. Elapsed: 119 ms. The empty block is the actual empty output.
421
+
422
+ ### Windows / Node v22.23.2
423
+
424
+ Clean checkout: `<TEMP>/taximeter-audit-node22/checkout`.
425
+
426
+ #### git clone (fresh checkout)
427
+
428
+ ```text
429
+ Cloning into '<TEMP>/taximeter-audit-node22/checkout'...
430
+ done.
431
+ ```
432
+
433
+ Exit: 0. Elapsed: 449 ms.
434
+
435
+ #### node --version
436
+
437
+ ```text
438
+ v22.23.2
439
+ ```
440
+
441
+ Exit: 0. Elapsed: 82 ms.
442
+
443
+ #### npm --version
444
+
445
+ ```text
446
+ 11.6.2
447
+ ```
448
+
449
+ Exit: 0. Elapsed: 241 ms.
450
+
451
+ #### npm ci
452
+
453
+ ```text
454
+ npm warn deprecated prebuild-install@7.1.3: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
455
+ npm warn deprecated glob@10.5.0: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
456
+
457
+ added 295 packages, and audited 296 packages in 5s
458
+
459
+ 58 packages are looking for funding
460
+ run `npm fund` for details
461
+
462
+ found 0 vulnerabilities
463
+ ```
464
+
465
+ Exit: 0. Elapsed: 5342 ms.
466
+
467
+ #### npm run typecheck
468
+
469
+ ```text
470
+
471
+ > taximeter@0.1.0 typecheck
472
+ > tsc --noEmit
473
+
474
+ ```
475
+
476
+ Exit: 0. Elapsed: 5301 ms.
477
+
478
+ #### npm run lint
479
+
480
+ ```text
481
+
482
+ > taximeter@0.1.0 lint
483
+ > biome check --error-on-warnings .
484
+
485
+ Checked 57 files in 45ms. No fixes applied.
486
+ ```
487
+
488
+ Exit: 0. Elapsed: 787 ms.
489
+
490
+ #### npm test
491
+
492
+ ```text
493
+
494
+ > taximeter@0.1.0 test
495
+ > vitest run --coverage
496
+
497
+
498
+ RUN v3.2.7 <TEMP>/taximeter-audit-node22/checkout
499
+ Coverage enabled with v8
500
+
501
+ ✓ test/config.test.ts (19 tests) 16ms
502
+ ✓ test/attempts.test.ts (2 tests) 16ms
503
+ ✓ test/fixture.test.ts (2 tests) 63ms
504
+ ✓ test/policy.test.ts (21 tests) 24ms
505
+ ✓ test/x402.test.ts (74 tests) 100ms
506
+ ✓ test/core.test.ts (2 tests) 62ms
507
+ ✓ test/money-invariant.test.ts (2 tests) 151ms
508
+ ✓ test/scaffold.test.ts (1 test) 2ms
509
+ ✓ test/ui.test.ts (14 tests) 47ms
510
+ ✓ test/proxy-upgrade.test.ts (3 tests) 71ms
511
+ ✓ test/ledger.test.ts (29 tests) 218ms
512
+ ✓ test/hygiene.test.ts (5 tests) 611ms
513
+ ✓ test/export.test.ts (15 tests) 62ms
514
+ ✓ test/sdk.test.ts (25 tests) 474ms
515
+ ✓ test/cli.test.ts (32 tests) 489ms
516
+ ✓ test/server.test.ts (29 tests) 732ms
517
+ ✓ test/proxy.test.ts (23 tests) 1369ms
518
+ ✓ native HTTP payment proxy > 100 simulated x402 v1 payments produce the exact integer total 700 418ms
519
+ ✓ native HTTP payment proxy > 100 simulated x402 v2 payments produce the exact integer total 700 334ms
520
+
521
+ Test Files 17 passed (17)
522
+ Tests 298 passed (298)
523
+ Start at 01:28:02
524
+ Duration 3.29s (transform 1.79s, setup 0ms, collect 16.29s, tests 4.51s, environment 3ms, prepare 5.48s)
525
+
526
+ % Coverage report from v8
527
+ ---------------|---------|----------|---------|---------|-----------------------
528
+ File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
529
+ ---------------|---------|----------|---------|---------|-----------------------
530
+ All files | 95.54 | 91.66 | 97.46 | 95.54 |
531
+ src | 100 | 97.95 | 100 | 100 |
532
+ assets.ts | 100 | 100 | 100 | 100 |
533
+ config.ts | 100 | 100 | 100 | 100 |
534
+ core.ts | 100 | 96.87 | 100 | 100 | 72
535
+ index.ts | 100 | 100 | 100 | 100 |
536
+ model.ts | 100 | 100 | 100 | 100 |
537
+ src/cli | 80.2 | 72.09 | 66.66 | 80.2 |
538
+ index.ts | 0 | 100 | 100 | 0 | 2-12
539
+ program.ts | 84.61 | 71.42 | 60 | 84.61 | 62-86,155-157
540
+ src/config | 98.36 | 97.05 | 100 | 98.36 |
541
+ io.ts | 98.36 | 97.05 | 100 | 98.36 | 17
542
+ src/export | 100 | 96.55 | 100 | 100 |
543
+ index.ts | 100 | 96.55 | 100 | 100 | 47
544
+ src/ledger | 100 | 100 | 100 | 100 |
545
+ derive.ts | 100 | 100 | 100 | 100 |
546
+ store.ts | 100 | 100 | 100 | 100 |
547
+ src/policy | 100 | 100 | 100 | 100 |
548
+ index.ts | 100 | 100 | 100 | 100 |
549
+ src/proxy | 90.84 | 73.17 | 100 | 90.84 |
550
+ index.ts | 90.84 | 73.17 | 100 | 90.84 | ...67,279-280,285-286
551
+ src/rails | 100 | 96.33 | 100 | 100 |
552
+ schemas.ts | 100 | 100 | 100 | 100 |
553
+ types.ts | 0 | 0 | 0 | 0 |
554
+ x402.ts | 100 | 96.26 | 100 | 100 | 43,111,121,225
555
+ src/sdk | 91.27 | 93.65 | 100 | 91.27 |
556
+ index.ts | 91.27 | 93.65 | 100 | 91.27 | 97-103,140-145
557
+ src/server | 96.96 | 91 | 100 | 96.96 |
558
+ index.ts | 100 | 97.95 | 100 | 100 | 90
559
+ lifecycle.ts | 89.41 | 79.16 | 100 | 89.41 | 31-36,56-58
560
+ schema.ts | 100 | 100 | 100 | 100 |
561
+ state.ts | 98.75 | 88.88 | 100 | 98.75 | 39
562
+ ---------------|---------|----------|---------|---------|-----------------------
563
+ ```
564
+
565
+ Exit: 0. Elapsed: 6277 ms.
566
+
567
+ #### npm run build
568
+
569
+ ```text
570
+
571
+ > taximeter@0.1.0 build
572
+ > tsup && vite build
573
+
574
+ CLI Building entry: src/index.ts, src/cli/index.ts
575
+ CLI Using tsconfig: tsconfig.json
576
+ CLI tsup v8.5.1
577
+ CLI Using tsup config: <TEMP>/taximeter-audit-node22/checkout\tsup.config.ts
578
+ CLI Target: node20
579
+ CLI Cleaning output folder
580
+ ESM Build start
581
+ ESM dist\chunk-I7YXGTRZ.js 44.76 KB
582
+ ESM dist\index.js 5.34 KB
583
+ ESM dist\cli\index.js 17.91 KB
584
+ ESM ⚡️ Build success in 20ms
585
+ DTS Build start
586
+ DTS ⚡️ Build success in 2017ms
587
+ DTS dist\cli\index.d.ts 20.00 B
588
+ DTS dist\index.d.ts 14.10 KB
589
+ vite v6.4.3 building for production...
590
+ transforming...
591
+ node_modules/zod/v4/core/util.js (330:0): A comment
592
+
593
+ "// Wrapped in a `@__PURE__` IIFE: esbuild never tree-shakes a top-level initializer that contains a member access on `Number`, so the bare object literal survived into every bundle."
594
+
595
+ in "node_modules/zod/v4/core/util.js" contains an annotation that Rollup cannot interpret due to the position of the comment. The comment will be removed to avoid issues.
596
+ node_modules/zod/v4/core/regexes.js (70:0): A comment
597
+
598
+ "/** Anchors a pattern source. The interpolation lives here rather than at the call site because
599
+ * esbuild will not drop a `@__PURE__` call whose own argument interpolates a variable, but it
600
+ * will drop `anchor(dateSource)`. Keeping it inline pinned `date` into every bundle. */"
601
+
602
+ in "node_modules/zod/v4/core/regexes.js" contains an annotation that Rollup cannot interpret due to the position of the comment. The comment will be removed to avoid issues.
603
+ ✓ 128 modules transformed.
604
+ rendering chunks...
605
+ computing gzip size...
606
+ ../dist/ui/index.html 0.57 kB │ gzip: 0.35 kB
607
+ ../dist/ui/assets/index-De4zKRDL.css 15.43 kB │ gzip: 3.89 kB
608
+ ../dist/ui/assets/index-DOE5bD9P.js 307.37 kB │ gzip: 92.40 kB
609
+ ✓ built in 866ms
610
+ ```
611
+
612
+ Exit: 0. Elapsed: 5365 ms.
613
+
614
+ #### npm pack
615
+
616
+ ```text
617
+ npm notice
618
+ npm notice 📦 taximeter@0.1.0
619
+ npm notice Tarball Contents
620
+ npm notice 304B CHANGELOG.md
621
+ npm notice 1.7kB CODE_OF_CONDUCT.md
622
+ npm notice 5.0kB CONTRIBUTING.md
623
+ npm notice 11.9kB DECISIONS.md
624
+ npm notice 1.1kB LICENSE
625
+ npm notice 10.9kB README.md
626
+ npm notice 3.7kB SECURITY.md
627
+ npm notice 27.5kB SPEC-NOTES.md
628
+ npm notice 23.4kB SPEC.md
629
+ npm notice 3.8kB VERIFICATION.md
630
+ npm notice 45.8kB dist/chunk-I7YXGTRZ.js
631
+ npm notice 20B dist/cli/index.d.ts
632
+ npm notice 18.3kB dist/cli/index.js
633
+ npm notice 14.4kB dist/index.d.ts
634
+ npm notice 5.5kB dist/index.js
635
+ npm notice 15.4kB dist/ui/assets/index-De4zKRDL.css
636
+ npm notice 307.4kB dist/ui/assets/index-DOE5bD9P.js
637
+ npm notice 566B dist/ui/index.html
638
+ npm notice 42B docs/demo.gif
639
+ npm notice 4.9kB docs/demo.mjs
640
+ npm notice 202B docs/demo.tape
641
+ npm notice 1.7kB docs/RECORDING.md
642
+ npm notice 9.5kB docs/SDK.md
643
+ npm notice 1.9kB package.json
644
+ npm notice 461B taximeter.config.example.json
645
+ npm notice Tarball Details
646
+ npm notice name: taximeter
647
+ npm notice version: 0.1.0
648
+ npm notice filename: taximeter-0.1.0.tgz
649
+ npm notice package size: 158.2 kB
650
+ npm notice unpacked size: 515.4 kB
651
+ npm notice shasum: ff654760a19721c626f03932d5bd1f325402d89e
652
+ npm notice integrity: sha512-GxdzY44Z6T4gA[...]7ov2pHlY/oykA==
653
+ npm notice total files: 25
654
+ npm notice
655
+ taximeter-0.1.0.tgz
656
+ ```
657
+
658
+ Exit: 0. Elapsed: 682 ms.
659
+
660
+ #### npm run check:package
661
+
662
+ ```text
663
+
664
+ > taximeter@0.1.0 check:package
665
+ > node scripts/check-package.mjs
666
+
667
+ Package verified: taximeter-0.1.0.tgz
668
+ 158229 bytes compressed; 515398 bytes unpacked; 25 files.
669
+ Both CLI aliases, ESM entry point, declarations, and prebuilt UI are present.
670
+ 26 relative Markdown file links resolve inside the package.
671
+ Files whitelist honored; no UI source maps, source tree, tests, dependencies, or local state.
672
+ ```
673
+
674
+ Exit: 0. Elapsed: 930 ms.
675
+
676
+ #### node docs/demo.mjs
677
+
678
+ ```text
679
+ Taximeter | local simulation | no money moves
680
+ Budget: 140 atomic units. Each payment: 7 atomic units.
681
+ Payment 1 | allowed | total 7 atomic units
682
+ Payment 2 | allowed | total 14 atomic units
683
+ Payment 3 | allowed | total 21 atomic units
684
+ Payment 4 | allowed | total 28 atomic units
685
+ Payment 5 | allowed | total 35 atomic units
686
+ Payment 6 | allowed | total 42 atomic units
687
+ Payment 7 | allowed | total 49 atomic units
688
+ Payment 8 | allowed | total 56 atomic units
689
+ Payment 9 | allowed | total 63 atomic units
690
+ Payment 10 | allowed | total 70 atomic units
691
+ Payment 11 | allowed | total 77 atomic units
692
+ Payment 12 | allowed | total 84 atomic units
693
+ Payment 13 | allowed | total 91 atomic units
694
+ Payment 14 | allowed | total 98 atomic units
695
+ Payment 15 | allowed | total 105 atomic units
696
+ Payment 16 | allowed | total 112 atomic units
697
+ Payment 17 | allowed | total 119 atomic units
698
+ Payment 18 | allowed | total 126 atomic units
699
+ Payment 19 | allowed | total 133 atomic units
700
+ Payment 20 | allowed | total 140 atomic units
701
+ Payment 21 | BLOCKED | total 140 atomic units
702
+ {"error":"blocked_by_taximeter","reason":"global_budget","budget":"140","spent":"140","remaining":"0"}
703
+ Exact ledger total: 140 atomic units. One blocked event recorded.
704
+ ```
705
+
706
+ Exit: 0. Elapsed: 2003 ms.
707
+
708
+ #### node dist/cli/index.js start (fresh home)
709
+
710
+ ```text
711
+
712
+ > taximeter@0.1.0 smoke:package
713
+ > node scripts/smoke-package.mjs --source
714
+
715
+ Smoke runtime: v22.23.2
716
+ Smoke workspace: <TEMP>/taximeter-package-smoke
717
+ PASS: isolated home and npm cache; no Taximeter config file or environment overrides.
718
+ $ node dist/cli/index.js start
719
+ Taximeter 0.1.0
720
+ Proxy: http://127.0.0.1:8402
721
+ Dashboard: http://127.0.0.1:8403
722
+ Point an HTTP-proxy-aware agent at http://127.0.0.1:8402.
723
+ HTTPS CONNECT is unmetered; use --upstream or withMeter for HTTPS payments.
724
+ PASS: default proxy 8402 and dashboard 8403; default ledger created in the fresh home.
725
+ PASS: validated empty dashboard summary and default budget without configuration.
726
+ PASS: prebuilt dashboard HTML and 2 local JS/CSS assets served with correct MIME types.
727
+ PASS: smoke HTTP requests stayed on loopback; browser network behavior is verified separately.
728
+ PASS: owned CLI and launcher processes stopped.
729
+ PASS: verified temporary workspace removed.
730
+ ```
731
+
732
+ Exit: 0. Elapsed: 866 ms.
733
+
734
+ #### npx ./taximeter-0.1.0.tgz start (fresh cwd and home)
735
+
736
+ ```text
737
+
738
+ > taximeter@0.1.0 smoke:package
739
+ > node scripts/smoke-package.mjs
740
+
741
+ Smoke runtime: v22.23.2
742
+ Smoke workspace: <TEMP>/taximeter-package-smoke
743
+ PASS: isolated home and npm cache; no Taximeter config file or environment overrides.
744
+ $ npx ./taximeter-0.1.0.tgz start
745
+ npm warn deprecated prebuild-install@7.1.3: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
746
+ Taximeter 0.1.0
747
+ Proxy: http://127.0.0.1:8402
748
+ Dashboard: http://127.0.0.1:8403
749
+ Point an HTTP-proxy-aware agent at http://127.0.0.1:8402.
750
+ HTTPS CONNECT is unmetered; use --upstream or withMeter for HTTPS payments.
751
+ PASS: default proxy 8402 and dashboard 8403; default ledger created in the fresh home.
752
+ PASS: validated empty dashboard summary and default budget without configuration.
753
+ PASS: prebuilt dashboard HTML and 2 local JS/CSS assets served with correct MIME types.
754
+ PASS: smoke HTTP requests stayed on loopback; browser network behavior is verified separately.
755
+ PASS: owned CLI and launcher processes stopped.
756
+ PASS: verified temporary workspace removed.
757
+ ```
758
+
759
+ Exit: 0. Elapsed: 5891 ms.
760
+
761
+ #### git status --porcelain
762
+
763
+ ```text
764
+ ```
765
+
766
+ Exit: 0. Elapsed: 105 ms. The empty block is the actual empty output.
767
+
768
+ ## Captured browser QA output
769
+
770
+ The local Chromium check used the final UI CSS/JS produced by the build. It checked
771
+ 48 view/state/theme/width combinations, eight CSV downloads, and zero page errors.
772
+ Screen height varied as described above; horizontal table/chart scrolling is contained.
773
+
774
+ ```text
775
+ PASS empty-light-1440: 1440px page width, no external requests
776
+ PASS empty-light-1440: all six views fit; CSV download works
777
+ PASS empty-light-390: 390px page width, no external requests
778
+ PASS empty-light-390: all six views fit; CSV download works
779
+ PASS empty-dark-1440: 1440px page width, no external requests
780
+ PASS empty-dark-1440: all six views fit; CSV download works
781
+ PASS empty-dark-390: 390px page width, no external requests
782
+ PASS empty-dark-390: all six views fit; CSV download works
783
+ PASS populated-light-1440: 1440px page width, no external requests
784
+ PASS populated-light-1440: all six views fit; CSV download works
785
+ PASS populated-light-390: 390px page width, no external requests
786
+ PASS populated-light-390: all six views fit; CSV download works
787
+ PASS populated-dark-1440: 1440px page width, no external requests
788
+ PASS populated-dark-1440: all six views fit; CSV download works
789
+ PASS populated-dark-390: 390px page width, no external requests
790
+ PASS populated-dark-390: all six views fit; CSV download works
791
+ Screenshots: <CHECKOUT>/tmp/dashboard-qa
792
+ ```
793
+
794
+ ## Limits
795
+
796
+ - Fixtures are synthetic protocol envelopes. Genuine wallet/facilitator/on-chain
797
+ interoperability has not been tested.
798
+ - Windows browser checks use Chromium. Other browser engines, a live Linux
799
+ desktop, and Linux browser rendering have not been exercised here.
800
+ - Native Windows ConPTY capture and agg encoding were executed for the follow-up
801
+ demo recording documented below. VHS and asciinema command-line recipes have not been run.
802
+ - The npm name remains unpublished. The README's source commands work before
803
+ publication; its registry command requires the first public release.
804
+ - Long-running/high-volume ledger performance, disk exhaustion, and OS crash
805
+ recovery have not been stress-tested. Storage failures preserve traffic and
806
+ can prevent enforcement; hidden redirects, unsupported forms, and encrypted
807
+ streams are also outside complete budget visibility.
808
+
809
+ All implementation deviations and pending repository-description/public-release
810
+ actions are listed at the top of [SPEC-NOTES.md](SPEC-NOTES.md).
811
+
812
+ ## Windows demo recording
813
+
814
+ On **2026-09-08**, `docs/demo.gif` was recorded from the local simulation, replacing
815
+ the initial placeholder shown in the historical package audit outputs above.
816
+ Product source and dependencies did not change.
817
+
818
+ The source recording is an asciicast v2 capture. node-pty 1.1.0 runs `cmd.exe`
819
+ through Windows ConPTY to type
820
+ `node docs/demo.mjs`, and capture its actual terminal output at 104 columns by 32
821
+ rows. The recorder asserts the block and exact total and requires shell exit 0.
822
+ agg 1.9.0 renders the capture; the existing FFmpeg 9.0.1 creates the MP4.
823
+
824
+ The official agg Windows executable's SHA-256 matched:
825
+
826
+ ```text
827
+ 810BAF5506E74CA65D8ED85BE3DB58791086C8B7B0A17C9018D7FEDE473F0055
828
+ ```
829
+
830
+ Actual capture output:
831
+
832
+ ```text
833
+ Recorded 52 terminal events over 20 seconds to <RECORDING>/demo.cast
834
+ Demo assertions passed; command shell exited with status 0.
835
+ ```
836
+
837
+ The renderer scans Windows fonts and warned about the unrelated `mstmc.ttf`
838
+ font face. The selected Consolas font rendered correctly; beginning, intermediate,
839
+ and final frames were visually inspected. The full block JSON and exact total fit
840
+ together without horizontal wrapping. The final reading pause is explicitly set
841
+ because agg omits trailing no-op output events. No demo output was replaced.
842
+
843
+ Actual `ffprobe` GIF output:
844
+
845
+ ```json
846
+ {
847
+ "programs": [],
848
+ "stream_groups": [],
849
+ "streams": [
850
+ {
851
+ "width": 1049,
852
+ "height": 653,
853
+ "nb_frames": "42"
854
+ }
855
+ ],
856
+ "format": {
857
+ "duration": "20.000000",
858
+ "size": "96519"
859
+ }
860
+ }
861
+ ```
862
+
863
+ Actual `ffprobe` MP4 output:
864
+
865
+ ```json
866
+ {
867
+ "programs": [],
868
+ "stream_groups": [],
869
+ "streams": [
870
+ {
871
+ "width": 1050,
872
+ "height": 654,
873
+ "nb_frames": "400"
874
+ }
875
+ ],
876
+ "format": {
877
+ "duration": "20.000000",
878
+ "size": "188853"
879
+ }
880
+ }
881
+ ```
882
+
883
+ The GIF copied into `docs/demo.gif` has SHA-256
884
+ `6D98F96ED4C91CA27EA4A786E3B4A142905EDA321112AC97EEEF2075E9C89161`.
885
+ The captured run permits 20 payments, blocks payment 21, returns the documented
886
+ five-field JSON body, and reports exactly `140` atomic units with one blocked
887
+ event. Signatures, responses, and settlement reports remain synthetic.
888
+
889
+ Follow-up checks passed before appending this evidence. Actual output excerpts:
890
+
891
+ ```text
892
+ > taximeter@0.1.0 lint
893
+ > biome check --error-on-warnings .
894
+
895
+ Checked 57 files in 39ms. No fixes applied.
896
+
897
+ > taximeter@0.1.0 check:package
898
+ > node scripts/check-package.mjs
899
+
900
+ Package verified: taximeter-0.1.0.tgz
901
+ 245338 bytes compressed; 644969 bytes unpacked; 25 files.
902
+ Both CLI aliases, ESM entry point, declarations, and prebuilt UI are present.
903
+ 26 relative Markdown file links resolve inside the package.
904
+ Files whitelist honored; no UI source maps, source tree, tests, dependencies, or local state.
905
+
906
+ Test Files 17 passed (17)
907
+ Tests 298 passed (298)
908
+ Start at 01:53:34
909
+ Duration 2.28s (transform 893ms, setup 0ms, collect 6.12s, tests 4.54s, environment 4ms, prepare 2.68s)
910
+ ```
911
+
912
+ Ledger and policy coverage remained 100% in all four metrics. The final package
913
+ is regenerated after this documentation update and still subject to the enforced
914
+ 2,000,000-byte compressed size limit.
915
+
916
+ ## Release authentication checks
917
+
918
+ The hosted Release job completed the package build, then failed to publish because
919
+ no npm token or OIDC authentication was available. The relevant log output was:
920
+
921
+ ```text
922
+ No NPM_TOKEN or OIDC available - assuming npm is already authenticated
923
+ ENEEDAUTH This command requires you to be logged in to https://registry.npmjs.org
924
+ ```
925
+
926
+ The publish hook now checks for the configured `NPM_TOKEN` and runs an npm identity
927
+ check before building. This hook runs only in Changesets' publishing path, so
928
+ version PR creation still works without npm credentials. Identity validation
929
+ does not certify package write permission or 2FA bypass.
930
+
931
+ Bash syntax validation and an isolated npm stub exercised all branches without
932
+ registry access or publication. Captured output:
933
+
934
+ ```text
935
+ PASS missing-token: exit 1; npm calls 0
936
+ PASS rejected-token: exit 1; npm calls 1
937
+ PASS accepted-token: exit 0; npm calls 2
938
+ PASS release-failure: exit 42; npm calls 2
939
+ PASS all publishing preflight checks; npm was stubbed and nothing was published.
940
+ ```
941
+
942
+ The repository owner must supply a publishing-capable Actions secret before the
943
+ hosted release can succeed. No live publication is represented by these checks.