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/CHANGELOG.md +9 -0
- package/CODE_OF_CONDUCT.md +31 -0
- package/CONTRIBUTING.md +141 -0
- package/DECISIONS.md +54 -0
- package/LICENSE +21 -0
- package/README.md +207 -0
- package/SECURITY.md +69 -0
- package/SPEC-NOTES.md +369 -0
- package/SPEC.md +474 -0
- package/VERIFICATION.md +943 -0
- package/dist/chunk-I7YXGTRZ.js +1167 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +494 -0
- package/dist/index.d.ts +404 -0
- package/dist/index.js +182 -0
- package/dist/ui/assets/index-DOE5bD9P.js +101 -0
- package/dist/ui/assets/index-De4zKRDL.css +1 -0
- package/dist/ui/index.html +15 -0
- package/docs/RECORDING.md +75 -0
- package/docs/SDK.md +192 -0
- package/docs/demo.gif +0 -0
- package/docs/demo.mjs +145 -0
- package/docs/demo.tape +15 -0
- package/package.json +79 -0
- package/taximeter.config.example.json +16 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Taximeter
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Introduce the local x402 payment meter: exact append-only accounting, scoped budgets,
|
|
8
|
+
streaming proxy, fetch SDK, CLI reports and exports, and a prebuilt local dashboard.
|
|
9
|
+
Supports x402 v1/v2 exact EVM EIP-3009 authorizations without custody or settlement.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Code of conduct
|
|
2
|
+
|
|
3
|
+
Taximeter welcomes contributions from people with different backgrounds,
|
|
4
|
+
identities, abilities, and levels of experience. Everyone participating in
|
|
5
|
+
project issues, pull requests, reviews, and discussions must treat others with
|
|
6
|
+
respect.
|
|
7
|
+
|
|
8
|
+
Discuss the work, explain your reasoning, and accept corrections when the
|
|
9
|
+
evidence changes. Give useful feedback without personal attacks. Respect other
|
|
10
|
+
people's time and their right to decline a request. Ask before sharing private
|
|
11
|
+
messages or personal information.
|
|
12
|
+
|
|
13
|
+
Harassment, threats, discriminatory remarks, sexualized attention, doxxing,
|
|
14
|
+
impersonation, and repeated unwanted contact are not acceptable. Disagreement
|
|
15
|
+
about a technical decision does not justify abuse. These expectations apply
|
|
16
|
+
when someone represents the project outside the repository as well.
|
|
17
|
+
|
|
18
|
+
Maintainers may edit or remove harmful content, close disruptive conversations,
|
|
19
|
+
restrict participation, or ban a participant. They should explain an action
|
|
20
|
+
when doing so is appropriate and avoid exposing private information. Decisions
|
|
21
|
+
should consider the behavior, its impact, and whether it is repeated.
|
|
22
|
+
|
|
23
|
+
Use GitHub's reporting controls to report harmful content. For sensitive
|
|
24
|
+
concerns, contact a maintainer through an available private channel; do not post
|
|
25
|
+
personal details in a public issue. Reports should include the relevant links
|
|
26
|
+
and enough context to understand what happened. Maintainers should share a
|
|
27
|
+
report only with people needed to address it and must not retaliate against
|
|
28
|
+
someone for reporting a concern in good faith.
|
|
29
|
+
|
|
30
|
+
This is the project's own conduct policy. Security vulnerabilities have a
|
|
31
|
+
separate private reporting process in [SECURITY.md](SECURITY.md).
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Contributing to Taximeter
|
|
2
|
+
|
|
3
|
+
Taximeter observes agent payments, records them locally, and gates supported
|
|
4
|
+
authorizations against budgets. Start with [SPEC.md](SPEC.md), then read
|
|
5
|
+
[SPEC-NOTES.md](SPEC-NOTES.md) for the supported protocol and
|
|
6
|
+
[DECISIONS.md](DECISIONS.md) for implementation choices.
|
|
7
|
+
|
|
8
|
+
## Development
|
|
9
|
+
|
|
10
|
+
Use Node.js 20 or 22 and npm. The package is TypeScript with ESM exports. CI is
|
|
11
|
+
configured for both Node versions on Linux and Windows. Dependencies and tooling
|
|
12
|
+
are pinned; keep `package-lock.json` consistent with `package.json`.
|
|
13
|
+
|
|
14
|
+
From a checkout, run the same sequence as CI:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm ci
|
|
18
|
+
npm run typecheck
|
|
19
|
+
npm run lint
|
|
20
|
+
npm test
|
|
21
|
+
npm run build
|
|
22
|
+
npm pack
|
|
23
|
+
npm run check:package
|
|
24
|
+
npm run smoke:package
|
|
25
|
+
git status --porcelain
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The build creates the Node package in `dist/` and the prebuilt React dashboard in
|
|
29
|
+
`dist/ui/`. A package consumer must not need to build the dashboard. On a clean
|
|
30
|
+
checkout, the final command must print nothing. During development, it should
|
|
31
|
+
show only your intentional changes. Do not commit generated bundles, coverage,
|
|
32
|
+
tarballs, local configuration, or SQLite state.
|
|
33
|
+
|
|
34
|
+
Use `npm run format` to apply Biome formatting. Keep LF line endings as specified
|
|
35
|
+
in `.gitattributes`. Check the package contents and compressed size with
|
|
36
|
+
`npm pack --dry-run`; the release tarball must stay below 2 MB.
|
|
37
|
+
|
|
38
|
+
## Changes that preserve trust
|
|
39
|
+
|
|
40
|
+
- Preserve unknown HTTP traffic byte for byte. Record a diagnostic when a format
|
|
41
|
+
cannot be parsed; do not guess the payment fields or rewrite its payload.
|
|
42
|
+
- Store monetary amounts as integer strings. Use `BigInt` for arithmetic and
|
|
43
|
+
convert to a display string only at the rendering edge. Never combine assets
|
|
44
|
+
or networks, infer exchange rates, or use floating point for money.
|
|
45
|
+
- Parse external inputs with Zod. Keep strict TypeScript types and
|
|
46
|
+
`noUncheckedIndexedAccess`; do not introduce `any` to silence a type error.
|
|
47
|
+
- Keep policy and ledger derivations pure and synchronous. Evaluate policy and
|
|
48
|
+
reserve capacity within the same SQLite transaction before forwarding.
|
|
49
|
+
- Keep events and outcomes append-only. Preserve uncertainty, retry identity,
|
|
50
|
+
concurrent attempt accounting, and original attribution.
|
|
51
|
+
- Never add custody, private-key handling, payment signing, settlement,
|
|
52
|
+
telemetry, hosted accounts, or runtime metadata lookups. Runtime networking
|
|
53
|
+
is limited to forwarding the caller's traffic and the local dashboard.
|
|
54
|
+
|
|
55
|
+
Read the primary x402 specification and published package types before changing
|
|
56
|
+
a wire parser. Record changed assumptions and supported subsets in
|
|
57
|
+
`SPEC-NOTES.md`. An internal model field is not necessarily a protocol field.
|
|
58
|
+
|
|
59
|
+
## Tests and review
|
|
60
|
+
|
|
61
|
+
For policy changes, write the failing behavioral test first. Add regressions for
|
|
62
|
+
traffic corruption, arithmetic, replay, concurrency, and settlement failures when
|
|
63
|
+
changing those paths. Keep policy and ledger coverage at least 90%; coverage is
|
|
64
|
+
a floor, not a substitute for asserting observable behavior.
|
|
65
|
+
|
|
66
|
+
Fixtures use synthetic authorizations and localhost servers. Tests must not
|
|
67
|
+
require wallets, keys, funds, a facilitator, or an external API. Keep cleanup
|
|
68
|
+
bounded to directories created by the test. Dashboard changes should be checked
|
|
69
|
+
in light and dark themes at both desktop and narrow mobile widths.
|
|
70
|
+
|
|
71
|
+
Golden CSV snapshots live in `test/__snapshots__/`. Regenerate a snapshot only
|
|
72
|
+
for an intentional output change that is explained in the pull request and
|
|
73
|
+
accepted in review. Never update a snapshot, remove a test, or relax an assertion
|
|
74
|
+
simply to make a failure disappear.
|
|
75
|
+
|
|
76
|
+
Open a focused pull request with the problem, resulting behavior, and actual
|
|
77
|
+
validation results. Explain compatibility changes and known limitations. Use
|
|
78
|
+
Conventional Commits, such as `fix(proxy): preserve response trailers`. Update
|
|
79
|
+
the relevant docs when behavior or configuration changes. Report vulnerabilities
|
|
80
|
+
privately as described in [SECURITY.md](SECURITY.md).
|
|
81
|
+
|
|
82
|
+
## Versioning and releases
|
|
83
|
+
|
|
84
|
+
For a user-visible change, run `npm run changeset` and commit the generated
|
|
85
|
+
changeset with the change. Describe its effect on users and choose the appropriate
|
|
86
|
+
version bump. Documentation-only changes may omit a changeset when they do not
|
|
87
|
+
change package behavior.
|
|
88
|
+
|
|
89
|
+
The release workflow uses Changesets on `main` to prepare version changes and
|
|
90
|
+
publish after the release pull request is merged. With no pending changesets,
|
|
91
|
+
the action also publishes an unpublished version; the **first push to `main`
|
|
92
|
+
with working npm credentials can publish v0.1.0 directly**. Enable credentials
|
|
93
|
+
only when that initial release is intended. Maintainers configure npm
|
|
94
|
+
credentials separately. `npm run version-packages` updates package versions and
|
|
95
|
+
the changelog; `npm run release` builds and runs Changesets publishing. Publishing
|
|
96
|
+
requires maintainer authorization and credentials. The presence of that workflow
|
|
97
|
+
does not mean any package version has already been published.
|
|
98
|
+
|
|
99
|
+
### npm publishing credentials
|
|
100
|
+
|
|
101
|
+
The current workflow uses token authentication. Create an npm **granular access
|
|
102
|
+
token** with package **Read and write** permissions and **Bypass two-factor
|
|
103
|
+
authentication** enabled for unattended publishing. Give it permission to publish
|
|
104
|
+
the intended package and an appropriate expiration. Follow the
|
|
105
|
+
[npm token setup instructions](https://docs.npmjs.com/creating-and-viewing-access-tokens/).
|
|
106
|
+
|
|
107
|
+
For the first publication of a new unscoped package, it cannot yet be selected
|
|
108
|
+
individually in npm's token settings. A temporary bootstrap token needs the
|
|
109
|
+
**All Packages** selection to permit creating it. Replace it with a token limited
|
|
110
|
+
to `taximeter` after the package exists, or use trusted publishing for later releases.
|
|
111
|
+
|
|
112
|
+
In the repository's **Settings → Secrets and variables → Actions**, add a
|
|
113
|
+
**repository secret** named exactly `NPM_TOKEN`. Use the token as its value.
|
|
114
|
+
An Actions variable or a Dependabot secret will not populate `secrets.NPM_TOKEN`.
|
|
115
|
+
An environment secret needs a matching job environment; this workflow does not
|
|
116
|
+
select one. Organization secrets must grant this repository access. See
|
|
117
|
+
[GitHub's Actions secret instructions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions).
|
|
118
|
+
|
|
119
|
+
`changesets/action@v1` configures npm authentication from this secret. Its publish
|
|
120
|
+
hook checks that the secret is present and that npm accepts the credentials
|
|
121
|
+
before building or publishing. This identity check does not prove package write
|
|
122
|
+
permission or 2FA bypass; npm validates those during publication. Version PRs
|
|
123
|
+
can still be prepared without npm credentials. Do not commit a token to `.npmrc`
|
|
124
|
+
or paste one into an issue or chat.
|
|
125
|
+
|
|
126
|
+
After adding or correcting the secret, rerun the failed Release job. A local
|
|
127
|
+
`npm login` does not authenticate a GitHub-hosted runner. `ENEEDAUTH` combined
|
|
128
|
+
with `No NPM_TOKEN or OIDC available` means the runner received no usable
|
|
129
|
+
authentication; the successful build and dependency-comment warnings do not
|
|
130
|
+
resolve that missing configuration.
|
|
131
|
+
|
|
132
|
+
Trusted publishing is a separate setup, not enabled by adding `GITHUB_TOKEN`.
|
|
133
|
+
It requires npm-side trust, a supported npm CLI, and `id-token: write` on the
|
|
134
|
+
workflow. The present hook deliberately requires `NPM_TOKEN`; migrate both the
|
|
135
|
+
hook and workflow together when adopting
|
|
136
|
+
[npm trusted publishing](https://docs.npmjs.com/trusted-publishers/).
|
|
137
|
+
The package must already exist before trust can be configured; see the
|
|
138
|
+
[npm trust prerequisites](https://docs.npmjs.com/cli/v11/commands/npm-trust/).
|
|
139
|
+
|
|
140
|
+
Contributions are provided under the project's [MIT license](LICENSE). Follow the
|
|
141
|
+
[code of conduct](CODE_OF_CONDUCT.md) in issues, reviews, and other project spaces.
|
package/DECISIONS.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Decisions
|
|
2
|
+
|
|
3
|
+
- 2026-09-08 — The hosted release log passed the build but failed with npm `ENEEDAUTH`; Changesets explicitly reported neither `NPM_TOKEN` nor OIDC. Keep the existing token-based authentication route and add a publish-only presence/identity check, preserving credential-free version PR creation. Document the exact repository-secret setup. A local code change cannot supply the owner's npm credential, and an identity check cannot attest package write permissions.
|
|
4
|
+
- 2026-09-08 — Replace the SPEC §6 demo placeholder with a 20-second Windows terminal recording. Native ConPTY capture and the standalone agg renderer work without WSL. Only the compact GIF ships in the package; capture tools and intermediate files remain local development artifacts.
|
|
5
|
+
- Release packaging includes all relative README/SDK/contributor documentation targets and the example configuration. The package checker verifies these links and the explicit whitelist; source trees, tests, local state, and UI source maps stay excluded.
|
|
6
|
+
- The release workflow can publish the first unpublished version on a credentialed `main` push when no changesets remain. Contributor instructions identify this initial-release behavior; no push or registry publication is performed during local verification.
|
|
7
|
+
- The demo's unpaid v2 challenge branch is exercised before its signed synthetic replays, including the required `resource.url`, so demonstration code follows the same verified protocol contract.
|
|
8
|
+
|
|
9
|
+
Recorded through the implementation audit on **2026-09-08**. The consolidated
|
|
10
|
+
deviation inventory and detailed protocol evidence are in
|
|
11
|
+
[SPEC-NOTES.md](SPEC-NOTES.md#implemented-deviations-and-release-limits).
|
|
12
|
+
This log records the reasons for those choices and corrections made during the
|
|
13
|
+
build; successful verification output belongs in [VERIFICATION.md](VERIFICATION.md).
|
|
14
|
+
|
|
15
|
+
- 2026-09-08 — Work follows SPEC §8 on `feat/taximeter-v0.1.0`; each numbered step gets a full passing suite and a Conventional Commit.
|
|
16
|
+
- Pin better-sqlite3 12.8.0: version 13 requires Node 22, while this release promises Node 20 support.
|
|
17
|
+
- ESM only: a second CJS runtime adds packaging complexity without serving the primary CLI or modern fetch integrations.
|
|
18
|
+
- Runtime requests are limited to forwarding traffic and the local dashboard API. Development dependency installation and protocol research are build activities.
|
|
19
|
+
- Biome's automatic configuration migration emitted `preset: none`; corrected it to `recommended` immediately so lint remains a real quality gate.
|
|
20
|
+
- Initial dependency audit found advisories in uuid 11.1.0 and tsup's esbuild 0.27.x. Pin uuid 11.1.1 and override tsup's esbuild to 0.28.1; validate with the full suite and build. Keep Changesets 2.x for Node 20 compatibility.
|
|
21
|
+
- SQLite adds append-only `outcomes` and `diagnostics` tables beside `events`; this preserves original payment authorizations while recording settlement uncertainty and parser diagnostics. The migration is bundled as text from the tracked SQL source.
|
|
22
|
+
- Idempotency binds the network/asset/payer/nonce identity to immutable authorization details; conflicting authorizations receive separate conservative reservations rather than bypassing policy through nonce reuse.
|
|
23
|
+
- Zod 4 applies inner defaults even through `.partial()`. Configuration patch schemas therefore have no defaults; a regression test verifies later layers cannot reset earlier sibling options.
|
|
24
|
+
- Independent ledger tests initially expected checksum-case output and `settlement_unknown: true` on blocked rows. Correct those expectations: EVM identities are canonical lowercase, and a replay blocked before forwarding has no uncertain settlement. Exact-value and deduplication assertions remain intact.
|
|
25
|
+
- Track individual forwarding attempts in append-only outcomes. Any unresolved attempt retains capacity; confirmation anywhere wins. An unconfirmed retry outside a rolling window must reacquire capacity, with `attemptedAt` separate from the original immutable timestamp.
|
|
26
|
+
- Runtime storage failures fail open and emit a visible local stderr warning: preserving the agent's traffic is the stated highest priority. Budget guarantees require working local storage. The original reservation survives a failed outcome write.
|
|
27
|
+
- CLI precedence is flags > environment > explicit `--config` file > cwd file > home file > defaults. Relative database paths resolve against cwd; `~` expands locally. Explicit config files layer rather than replace lower-priority settings.
|
|
28
|
+
- `reset --yes` archives the database under an atomic CLI lock. Stop all SDK writers first; the CLI lock coordinates CLI instances, while SQLite coordinates payment reservations. Reports, exports, and doctor use an in-memory empty ledger when the requested path does not yet exist, avoiding accidental file creation.
|
|
29
|
+
- CSV includes every derived payment row. `amount` is its counted contribution (zero for blocked rows and authorizations whose known attempts all failed); `authorizedAmount` preserves the original proposal. Sum `amount` separately by network/asset to reproduce dashboard ledger totals. Formula-like text cells are escaped; monetary cells remain exact integer strings. Invoice export is a standalone printable HTML statement, with no PDF generation or claim of independent settlement verification.
|
|
30
|
+
- SDK inspection clones only legacy 402 bodies, capped at 64 KiB and 100 ms, then returns the original response object. Slow/oversized unknown challenges are diagnosed and passed through. Request bodies are never consumed by the meter. Put `withMeter` inside the payment wrapper so signed retries are visible.
|
|
31
|
+
- Dashboard reads share the ledger derivations and exact CSV accounting. The loopback API rejects foreign Host/Origin values, cross-site requests, mutations, ambiguous duplicate query options, and paths outside its bundled UI directory.
|
|
32
|
+
- Dashboard typography uses system grotesque and monospace stacks: remote Google Fonts would violate the runtime network invariant. Financial chart coordinates and proportional widths use BigInt and become strings only when rendered.
|
|
33
|
+
- The SDK cannot observe redirects or retries inside the supplied fetch transport. Preserve transport behavior, diagnose visible redirects, and avoid correlating redirected v1 challenges to the initial URL. Host-sensitive callers can request `redirect: "error"` or expose each hop to the wrapper.
|
|
34
|
+
- Release review found truncated rejected-upgrade responses could leave clients hanging. Add a failing regression first, then mirror the ordinary response's abort/error cleanup.
|
|
35
|
+
- Preserve the original HTTP request path and query while validating the routing authority separately. WHATWG URL normalization of encoded dot segments would otherwise change opaque or signature-sensitive traffic; HTTP and upgrade regressions cover both proxy modes.
|
|
36
|
+
- The first dashboard shows the latest 30 events and a trailing 24-hour timeline. Rows remain readable and tables scroll on shorter or narrow screens; exact totals and clear settlement labels take priority over fitting every row into every viewport.
|
|
37
|
+
|
|
38
|
+
## Scope and release audit
|
|
39
|
+
|
|
40
|
+
- Support `exact` EVM EIP-3009 only: v1 `base` and `base-sepolia`, and v2 positive `eip155:<chain-id>` networks. Permit2, ERC-7710, and other transfer methods have different authorization semantics and stay transparent with diagnostics. Optional or variable-length signatures are structurally accepted according to the published 2.25.0 types; Taximeter never verifies them.
|
|
41
|
+
- Bound v1 challenge context to URL, method, task, agent, Authorization, and Cookie, with a five-minute TTL and at most 1,000 contexts per intake instance. Require an unambiguous matching asset. This avoids inventing missing replay fields or sharing challenges across distinct request identities. The SDK does not cache redirected challenges under the original URL.
|
|
42
|
+
- Limit inspected v1 bodies to 64 KiB and encoded payment header text to 65,536 characters. The SDK additionally stops challenge observation after 100 ms. The proxy streams its bounded body tap without holding the response; compressed legacy challenges pass through unmetered. These limits prioritize intact, responsive traffic over metering every possible challenge.
|
|
43
|
+
- Keep local metadata only for Base and Base Sepolia USDC. Unknown assets use exact atomic strings with `decimalsKnown: false`; `decimals: 0` is an internal placeholder, never a token-metadata claim. Runtime metadata, RPC, and price calls would violate the network invariant.
|
|
44
|
+
- Keep every budget separate by network and asset contract. `maxSingleAsset: "USDC"` makes the scalar per-payment cap's scope explicit, and optional budget `network` narrows a rule. Add nullable budgets/caps and fixed `1h`, `24h`, `7d`, and `30d` windows without introducing exchange rates or cross-chain combined balances.
|
|
45
|
+
- Matching settlement evidence must use the event's network and, when provided, its payer and amount. A reported success with a nonempty transaction can confirm on any response status; an explicit failure on a 5xx remains uncertain. Missing evidence, disconnects, and malformed responses retain the reservation. Confirmation on any attempt wins over later contradictions; otherwise an unresolved attempt wins over a failed attempt.
|
|
46
|
+
- Interpret explicit `upstream` as an origin: an incoming origin-form path replaces any configured path prefix. Preserve encoded path/query bytes while keeping leading `//` on that fixed origin. This rule is documented so callers do not silently route `/weather` to an assumed `/v1/weather`.
|
|
47
|
+
- Forward HTTPS CONNECT as an unmetered encrypted tunnel, with no certificate installation or TLS interception. Gate recognized signed HTTP upgrade handshakes but do not claim to inspect subsequent stream frames. Payload-byte transparency excludes hop-by-hop headers and transfer framing that an HTTP proxy must reconstruct.
|
|
48
|
+
- Keep the dashboard timeline to the current UTC hour plus the preceding 23 hourly buckets; do not invent a persistent process-session model. Show each asset's active global budget separately from the all-time ledger total. Show 30 recent events and 10 recent diagnostics; keep original payment JSON in JSON export rather than the live summary API.
|
|
49
|
+
- Add `TAXIMETER_DB`, `TAXIMETER_PORT`, and `TAXIMETER_DASHBOARD_PORT` between explicit flags and configuration files. This follows the brief's environment-variable naming while making precedence explicit. Allow port `0` for local tests and ephemeral listeners. An injected SDK ledger uses supplied configuration plus defaults, avoiding hidden user-file dependencies for embedded callers.
|
|
50
|
+
- `doctor` is deliberately local. With a nonexistent database it checks an empty in-memory SQLite ledger and configuration; it does not claim to test that future path's write permissions, wallet access, or upstream/facilitator readiness. Existing malformed config remains a visible error.
|
|
51
|
+
- Protocol fixtures use deterministic synthetic authorizations and settlement results derived from documented envelopes. A structural parser test is not a real payment, wallet integration, or chain-settlement test. Record this boundary instead of presenting synthetic fixture success as live-network proof.
|
|
52
|
+
- Rechecked the npm name on **2026-09-08**: `npm view taximeter` returned `E404`, so no fallback name was needed. The README keeps the required `npx taximeter start` command and labels registry publication as pending; source quickstart commands use `npm ci`, `npm run build`, and `node dist/cli/index.js start`. Validate the local `taximeter-0.1.0.tgz` in a fresh temporary directory before claiming packaged startup; record the actual result in `VERIFICATION.md`.
|
|
53
|
+
- Generate the initial `CHANGELOG.md` with Changesets using a transient, uncommitted `0.0.0` version and an initial minor changeset, then restore the intended `0.1.0` through `npm run version-packages` and resynchronize the lockfile. This produces a generated changelog without changing the release version or rewriting commit history. The demo uses local synthetic payments: its recording script demonstrates 20 accepted payments and a blocked twenty-first payment without making a transfer. The initial GIF placeholder was subsequently replaced by the Windows recording documented above.
|
|
54
|
+
- Repository visibility, description, and npm publication are maintainer-managed release settings. The intended repository description is **A taximeter for your AI agents.** A successful local build does not establish that registry publication or repository configuration has completed.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Taximeter contributors
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Taximeter
|
|
2
|
+
|
|
3
|
+
A taximeter for your AI agents.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx taximeter start
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Taximeter records supported agent payments, checks budgets before forwarding them,
|
|
10
|
+
and keeps an exact local ledger. **This is the unpublished v0.1.0 source release.**
|
|
11
|
+
The registry command above becomes available after publication; the three-command
|
|
12
|
+
source quickstart below works today. Requires Node 20 or newer.
|
|
13
|
+
|
|
14
|
+
## A 20-second demo
|
|
15
|
+
|
|
16
|
+

|
|
17
|
+
|
|
18
|
+
This local recording shows 20 payments passing and payment 21 being blocked at
|
|
19
|
+
an exact total of 140 atomic units. It uses synthetic x402 envelopes and never
|
|
20
|
+
moves money. [Record the demo](docs/RECORDING.md) with the included simulation.
|
|
21
|
+
|
|
22
|
+
## Why
|
|
23
|
+
|
|
24
|
+
Visa moves money; Ramp organizes the spending around it.
|
|
25
|
+
Payment rails similarly move agent payments, while Taximeter keeps a durable local
|
|
26
|
+
record of which task spent what.
|
|
27
|
+
It adds exact rolling budgets, attribution, and a statement you can keep.
|
|
28
|
+
|
|
29
|
+
## Quickstart
|
|
30
|
+
|
|
31
|
+
Open a terminal in this source checkout, with Node 20+ and npm installed.
|
|
32
|
+
|
|
33
|
+
1. Install the pinned dependencies.
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
npm ci
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
2. Build the CLI and dashboard.
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
npm run build
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
3. Start the meter with its default configuration.
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
node dist/cli/index.js start
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
You should now see:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
Taximeter 0.1.0
|
|
55
|
+
Proxy: http://127.0.0.1:8402
|
|
56
|
+
Dashboard: http://127.0.0.1:8403
|
|
57
|
+
Point an HTTP-proxy-aware agent at http://127.0.0.1:8402.
|
|
58
|
+
HTTPS CONNECT is unmetered; use --upstream or withMeter for HTTPS payments.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Open [the local dashboard](http://127.0.0.1:8403). It explains how to connect an
|
|
62
|
+
agent before the first payment arrives. Stop the process with Ctrl+C.
|
|
63
|
+
The prebuilt npm package starts directly; installing it does not build the frontend.
|
|
64
|
+
Dependency download time depends on the connection. See [measured verification](VERIFICATION.md).
|
|
65
|
+
|
|
66
|
+
Choose the connection mode your agent supports:
|
|
67
|
+
|
|
68
|
+
| Mode | How to connect | What is visible |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| HTTP forward proxy | Configure the client's HTTP proxy as `http://127.0.0.1:8402`. `HTTP_PROXY` works only in clients that honor it. | Plain HTTP payment requests and replies. |
|
|
71
|
+
| Explicit upstream | Start with `--upstream` set to the real HTTP(S) API origin, then use the local proxy URL as the agent's API base URL. | HTTP or HTTPS upstream payments, without intercepting TLS. |
|
|
72
|
+
| SDK | Put `withMeter(fetch, options)` **inside** the payment wrapper. See [SDK examples](docs/SDK.md). | Requests made through the supplied transport. |
|
|
73
|
+
| HTTPS CONNECT | A proxy-aware HTTPS client may open a tunnel. | Encrypted bytes pass through, with an unmetered diagnostic. Payment headers are invisible. |
|
|
74
|
+
|
|
75
|
+
In upstream mode, `/data` replaces any path prefix in the configured upstream URL.
|
|
76
|
+
Use the API origin as the upstream and keep its path in the request. Native fetch
|
|
77
|
+
does not universally honor proxy environment variables. SDK transports can also
|
|
78
|
+
follow redirects or retry internally: these hidden requests are outside host policy
|
|
79
|
+
checks. Use `redirect: "error"` when each destination must be checked.
|
|
80
|
+
|
|
81
|
+
From the checkout, use `node dist/cli/index.js` in place of the installed `taximeter`
|
|
82
|
+
or `txm` command. The CLI provides:
|
|
83
|
+
|
|
84
|
+
| Command | Result |
|
|
85
|
+
| --- | --- |
|
|
86
|
+
| `taximeter start` | Proxy on port 8402; dashboard on port 8403. |
|
|
87
|
+
| `taximeter report` | Exact totals, separately by network and asset. |
|
|
88
|
+
| `taximeter report --json` | Totals as integer strings. Filters: `--task`, `--agent`, `--host`. |
|
|
89
|
+
| `taximeter export --csv statement.csv` | A new CSV file; refuses to overwrite an existing file. |
|
|
90
|
+
| `taximeter export --json statement.json` | Event history, raw authorizations, and totals. With no format, JSON goes to stdout. |
|
|
91
|
+
| `taximeter export --invoice statement.html` | A printable HTML spending statement. |
|
|
92
|
+
| `taximeter doctor` | Local configuration and SQLite checks; no network probes. |
|
|
93
|
+
| `taximeter reset --yes` | Archives the ledger. Stop **all CLI and SDK writers** first. |
|
|
94
|
+
|
|
95
|
+
## How it works
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
agent process taximeter upstream
|
|
99
|
+
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
|
|
100
|
+
│ fetch(...) │ ──────► │ proxy (rail │ ─────► │ API server │
|
|
101
|
+
│ │ │ adapter: x402) │ │ (402 flow) │
|
|
102
|
+
└──────────────┘ ◄────── │ │ │ ◄───── └──────────────┘
|
|
103
|
+
│ ▼ │
|
|
104
|
+
│ policy engine │ ← budgets, allow/deny
|
|
105
|
+
│ │ │
|
|
106
|
+
│ ▼ │
|
|
107
|
+
│ ledger (SQLite)│
|
|
108
|
+
│ │ │
|
|
109
|
+
│ ▼ │
|
|
110
|
+
│ dashboard + API │ → localhost UI, CSV/JSON export
|
|
111
|
+
└──────────────────┘
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The meter recognizes x402 v1/v2 `exact` EVM EIP-3009 authorizations and reserves
|
|
115
|
+
their amount before forwarding the payment replay.
|
|
116
|
+
Policy evaluation and reservation share one SQLite transaction, so concurrent
|
|
117
|
+
payments cannot all consume the same remaining capacity.
|
|
118
|
+
Append-only payment, outcome, and diagnostic records derive every total with BigInt.
|
|
119
|
+
The local dashboard polls that ledger every second and exports the same counted amounts.
|
|
120
|
+
|
|
121
|
+
An initial 402 challenge is an offer, not spend. Reported settlement counts as
|
|
122
|
+
confirmed; missing evidence stays **unknown** and remains reserved. Known failures
|
|
123
|
+
and blocked attempts do not contribute to totals. Taximeter does not independently
|
|
124
|
+
verify the upstream's settlement report.
|
|
125
|
+
|
|
126
|
+
Repeated EIP-3009 authorizations count once across retries, using network, contract,
|
|
127
|
+
payer, nonce, amount, recipient, and validity bounds. Conflicting authorization
|
|
128
|
+
details receive separate reservations. Each unresolved forwarding attempt keeps
|
|
129
|
+
capacity reserved; an old unconfirmed retry must reacquire capacity in the current
|
|
130
|
+
window. [Protocol notes](SPEC-NOTES.md) explain these rules and the supported subset.
|
|
131
|
+
|
|
132
|
+
CSV `amount` is the counted contribution, including zero for blocked/known failed
|
|
133
|
+
rows. `authorizedAmount` retains the original amount. Sum `amount` with BigInt
|
|
134
|
+
**separately by network and asset** to reproduce dashboard ledger totals; a rolling
|
|
135
|
+
budget figure can differ from the all-time ledger total. Import monetary CSV columns
|
|
136
|
+
as text to prevent a spreadsheet application from rounding them.
|
|
137
|
+
|
|
138
|
+
## Configuration
|
|
139
|
+
|
|
140
|
+
No file is required. Start from [the example](taximeter.config.example.json) when
|
|
141
|
+
needed. Precedence, highest first: flags, environment, explicit `--config` file,
|
|
142
|
+
`taximeter.config.json` in the working directory, `~/.taximeter/config.json`, defaults.
|
|
143
|
+
Layers merge by scope; unspecified sibling settings remain intact. Unknown keys and
|
|
144
|
+
invalid values are rejected with Zod.
|
|
145
|
+
|
|
146
|
+
| Option | Default | Meaning |
|
|
147
|
+
| --- | --- | --- |
|
|
148
|
+
| `budgets.perTask` | `{"amount":"5000000","asset":"USDC"}` | 5 USDC per task, all time. `null` disables it. |
|
|
149
|
+
| `budgets.perAgent` | `{"amount":"50000000","asset":"USDC","window":"24h"}` | 50 USDC per agent in a rolling 24 hours. `null` disables it. |
|
|
150
|
+
| `budgets.global` | `{"amount":"100000000","asset":"USDC","window":"24h"}` | 100 USDC per network/asset in a rolling 24 hours. `null` disables it. |
|
|
151
|
+
| Each budget's `amount` | Scope default above | Nonnegative integer string in atomic units, at most 78 digits. |
|
|
152
|
+
| Each budget's `asset` | `USDC` | Trusted local symbol or exact contract address. |
|
|
153
|
+
| Each budget's `network` | Omitted | Optional exact network such as `eip155:8453`; otherwise evaluate each network separately. |
|
|
154
|
+
| Each budget's `window` | Scope default above | `1h`, `24h`, `7d`, or `30d`; omitted means all time. |
|
|
155
|
+
| `policy.allowHosts` | `[]` | Empty allows all; otherwise exact case-insensitive hostname matches. No wildcards. |
|
|
156
|
+
| `policy.denyHosts` | `[]` | Exact denied hostnames; denial takes priority. |
|
|
157
|
+
| `policy.allowPayTo` | `[]` | Empty allows all recipients; otherwise exact case-insensitive addresses. |
|
|
158
|
+
| `policy.maxSinglePayment` | `"1000000"` | One USDC per authorization by default; `null` disables the cap. |
|
|
159
|
+
| `policy.maxSingleAsset` | `USDC` | Asset to which the single-payment cap applies. |
|
|
160
|
+
| `ports.proxy` | `8402` | Loopback HTTP listener; `0` selects an available port. |
|
|
161
|
+
| `ports.dashboard` | `8403` | Loopback dashboard listener; `0` selects an available port. |
|
|
162
|
+
| `db` | `~/.taximeter/ledger.db` | SQLite path; relative paths resolve against the working directory. |
|
|
163
|
+
| `upstream` | Omitted | HTTP(S) destination for explicit local base-URL mode; no URL credentials. |
|
|
164
|
+
|
|
165
|
+
All commands accept `--db` and `--config`. Start also accepts `--proxy-port`,
|
|
166
|
+
`--dashboard-port`, and `--upstream`. Environment overrides are `TAXIMETER_DB`,
|
|
167
|
+
`TAXIMETER_PORT`, and `TAXIMETER_DASHBOARD_PORT`.
|
|
168
|
+
|
|
169
|
+
Missing task/agent labels share an **Unattributed** bucket. Supply `Taximeter-Task`
|
|
170
|
+
and `Taximeter-Agent` headers, or SDK options. The local USDC registry recognizes
|
|
171
|
+
Base and Base Sepolia; other contracts remain separate atomic-unit balances with
|
|
172
|
+
unknown decimals. Default USDC budgets do not cap unknown assets.
|
|
173
|
+
|
|
174
|
+
A denied replay receives HTTP 402 before it reaches the upstream. For a cap of
|
|
175
|
+
140 atomic units already fully consumed, the response is:
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"error": "blocked_by_taximeter",
|
|
180
|
+
"reason": "global_budget",
|
|
181
|
+
"budget": "140",
|
|
182
|
+
"spent": "140",
|
|
183
|
+
"remaining": "0"
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Reasons are `host_denied`, `host_not_allowed`, `recipient_not_allowed`,
|
|
188
|
+
`max_single_payment`, `per_task_budget`, `per_agent_budget`, and `global_budget`.
|
|
189
|
+
Host/recipient denials have `budget: null` and `remaining: null`.
|
|
190
|
+
|
|
191
|
+
## What this is not
|
|
192
|
+
|
|
193
|
+
- A wallet or facilitator: it never holds keys or funds, signs, verifies, or settles payments.
|
|
194
|
+
- A cloud service: no accounts, telemetry, analytics, or external font requests.
|
|
195
|
+
- A multi-user service: no authentication, tenancy, or RBAC. Both listeners bind to loopback.
|
|
196
|
+
- A universal payment parser: Permit2, ERC-7710, non-EVM rails, Stripe MPP, and cards are outside v0.1.0.
|
|
197
|
+
- A security boundary against a bypassing agent: unsupported traffic passes through with diagnostics. CONNECT and post-upgrade frames are unmetered. Storage failure also passes traffic and prints a warning, so budget guarantees require working storage.
|
|
198
|
+
|
|
199
|
+
Raw payment authorizations stay in the local audit ledger and JSON export. Treat
|
|
200
|
+
them as sensitive. [Security guidance](SECURITY.md) describes the boundary and how
|
|
201
|
+
to report a problem.
|
|
202
|
+
|
|
203
|
+
## Contributing / license
|
|
204
|
+
|
|
205
|
+
Read [CONTRIBUTING.md](CONTRIBUTING.md), [the decisions](DECISIONS.md), and
|
|
206
|
+
[the specification](SPEC.md). Test and release evidence is in [VERIFICATION.md](VERIFICATION.md).
|
|
207
|
+
Licensed under [MIT](LICENSE).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
Taximeter never holds private keys or funds, signs a payment, or settles a
|
|
4
|
+
transaction. It observes payment authorizations supplied by the caller and records
|
|
5
|
+
upstream-reported outcomes. It does not independently verify signatures or
|
|
6
|
+
on-chain settlement.
|
|
7
|
+
|
|
8
|
+
## Supported version
|
|
9
|
+
|
|
10
|
+
Security fixes currently target the `0.1.0` release line. No older release line
|
|
11
|
+
is maintained. This policy describes support; it is not a claim that the package
|
|
12
|
+
has already been published or independently audited.
|
|
13
|
+
|
|
14
|
+
## Report a vulnerability privately
|
|
15
|
+
|
|
16
|
+
Use [GitHub's private vulnerability report for this repository](https://github.com/Ding808/taximeter/security/advisories/new).
|
|
17
|
+
Include the affected version, operating system and Node version, impact,
|
|
18
|
+
reproduction steps, and a minimal synthetic example. Include a suggested fix if
|
|
19
|
+
you have one. Do not put exploit details, payment authorizations, private data,
|
|
20
|
+
or credentials in a public issue.
|
|
21
|
+
|
|
22
|
+
If private reporting is unavailable, open a public issue asking the maintainer
|
|
23
|
+
to enable private vulnerability reporting, without disclosing the vulnerability.
|
|
24
|
+
Maintainers will assess the report, coordinate a fix and disclosure, and credit
|
|
25
|
+
the reporter with their consent. No response-time guarantee is offered.
|
|
26
|
+
|
|
27
|
+
## Trust boundary
|
|
28
|
+
|
|
29
|
+
Taximeter is a cooperative local meter. It is not an enforcement boundary
|
|
30
|
+
against a hostile agent or another process running as the same user.
|
|
31
|
+
|
|
32
|
+
- Unsupported or malformed payment forms pass through with a diagnostic.
|
|
33
|
+
Supported parsing is narrower than the full x402 protocol; see
|
|
34
|
+
[SPEC-NOTES.md](SPEC-NOTES.md).
|
|
35
|
+
- HTTPS CONNECT tunnels encrypted bytes without metering their payments.
|
|
36
|
+
Taximeter does not install a certificate authority or intercept TLS.
|
|
37
|
+
- The SDK must wrap the transport inside the payment wrapper. Redirects and
|
|
38
|
+
retries performed internally by that transport are not individually visible
|
|
39
|
+
to the meter. The SDK therefore cannot promise host policy enforcement on
|
|
40
|
+
hidden redirect hops.
|
|
41
|
+
- Runtime storage failures fail open to preserve the caller's traffic and emit
|
|
42
|
+
a local stderr warning. Budget enforcement and ledger completeness depend on
|
|
43
|
+
working storage. Investigate the "storage unavailable" warning before trusting totals.
|
|
44
|
+
- An agent can bypass a local proxy or SDK wrapper. Budgets do not restrict
|
|
45
|
+
transfers made outside the metered path.
|
|
46
|
+
|
|
47
|
+
The services bind to loopback. The dashboard additionally checks Host and Origin,
|
|
48
|
+
rejects cross-site requests, and exposes a read-only API. These checks reduce
|
|
49
|
+
unwanted browser access; they do not authenticate local processes or replace
|
|
50
|
+
operating-system account isolation. Do not expose either service through a
|
|
51
|
+
public tunnel or reverse proxy.
|
|
52
|
+
|
|
53
|
+
## Local data
|
|
54
|
+
|
|
55
|
+
The ledger stores raw signed payment authorizations for auditability. They are
|
|
56
|
+
not private keys, but they can be sensitive and may remain usable under their
|
|
57
|
+
payment rail's rules. Treat the database, its `-wal` and `-shm` siblings, backups,
|
|
58
|
+
and JSON exports as sensitive local data. CSV and invoice exports omit raw
|
|
59
|
+
authorizations but still contain payment and attribution information.
|
|
60
|
+
|
|
61
|
+
Keep local state out of source control and avoid sharing real ledger files in
|
|
62
|
+
bug reports. Use synthetic fixtures and redact resource URLs and labels when
|
|
63
|
+
they contain private information. The dashboard event summary omits raw
|
|
64
|
+
authorizations, while an explicit JSON export retains the audit records.
|
|
65
|
+
|
|
66
|
+
Taximeter makes no telemetry, analytics, metadata, or balance requests. Its
|
|
67
|
+
runtime network activity consists of forwarding caller traffic and serving the
|
|
68
|
+
local dashboard. Dependency installation and release publishing are separate
|
|
69
|
+
development operations.
|