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 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).
@@ -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
+ ![Taximeter allows 20 payments and blocks payment 21](docs/demo.gif)
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.