Skip to main content

arcaeon-meter

Meter your agent tool in 3 lines: keys, monthly caps, usage you can bill from. Pure stdlib. SQLite counts. Keys hashed at rest. No billing infrastructure required.

pip install arcaeon-meter

Why

You shipped a tool agents actually call. Day two you need what every API business needs on day one: per-customer keys, a free tier with a real cap, and a usage record you can invoice from. The usual answer is a gateway product, a billing platform, and an afternoon of webhooks — heavy for a tool that charges cents.

arcaeon-meter is the small version: a keys file, a SQLite counter, and a decorator. Caps are enforced (a denial is a typed exception, never a silent pass), counts survive concurrent processes, and export() hands your billing flow exactly what an invoice run wants.

Use

from arcaeon_meter import Meter
meter = Meter("keys.json")

@meter.metered
def my_tool(query, _meter_key=None): ...

Callers pass their key as _meter_key. Every call is checked against the keys file and counted against the key's monthly cap. Denials raise MeterDenied with a machine-readable reason (over_cap, revoked, unknown_key, missing_key) — catch it and answer however your surface answers.

Prefer explicit control? Same check, no exception:

result = meter.check(key)          # -> Allowance | Denied, both truth-testable
if result:
    ...                            # result.used, result.cap, result.remaining
else:
    ...                            # result.reason == "over_cap", result.used, ...

meter.check(key, record=False) peeks (validates + reports usage) without counting.

Keys

python -m arcaeon_meter keys add --plan free --cap 100 --label alice
# key_id: 3f9a1c2b7d40
# secret: am_Vq...   <- shown ONCE, never stored

python -m arcaeon_meter keys list
python -m arcaeon_meter keys revoke 3f9a1c2b7d40

Secrets are random (~192 bits) and stored only as sha256 hashes — leaking keys.json does not leak keys. Revocation marks the entry (kept for audit) and takes effect in a running server without a restart (the file is re-read on change). Caps can live per key (--cap, --unlimited) or per plan (Meter(plans={"free": 100})); a key with no resolvable cap is denied, not unlimited — the meter fails closed.

Billing handoff

meter.usage(key_or_id)         # one key: used / cap / remaining this month
meter.export(fmt="csv")        # whole roster for the month, invoice-ready

Export columns: key_id, label, plan, used, monthly_cap, revoked, month — every key appears, zero-usage rows included, secrets never. The Stripe recipe is deliberately a how-to, not code: at month close, export(month="2026-08", fmt="csv"), then for each row create an invoice item (used × your unit price, or a flat plan price with used as the line description) against the customer you mapped to label/key_id when you minted the key. That's the whole integration; this library stays out of the money path on purpose.

HTTP in one line

app.add_middleware(meter.asgi_middleware())   # FastAPI / Starlette / any ASGI

Checks Authorization: Bearer <key>, answers 401 (missing/unknown/revoked) or 429 (over cap, with X-Meter-Cap / X-Meter-Used headers) with a JSON reason, and stashes the Allowance at scope["arcaeon_meter"] for your handlers. It speaks raw ASGI — no framework import, nothing extra to install.

Metering you can audit (optional)

meter = Meter("keys.json", ledger="usage.log.jsonl")   # pip install arcaeon-meter[ledger]

Every grant and denial appends a hash-chained row via arcaeon-ledger, so the usage record you bill from is tamper-evident: edit a row mid-history and verify() names the exact line. When a customer disputes an invoice, you have a chained record, not a mutable counter. Soft dependency — only needed if you pass ledger=.

Concurrency

Counts live in SQLite (WAL mode); check-then-increment runs inside an IMMEDIATE transaction, so parallel workers neither lose counts nor double-grant the last slot under a cap. The test suite proves both with two real OS processes contending on one database.

What it enforces / what it doesn't

Enforces: key validity, revocation, and monthly caps — on every call that goes through the meter.

Doesn't: an in-process wrapper meters what the meter saw. Code that calls your inner function directly bypasses it — the voluntary-path problem, and no library-level wrapper escapes it. Narrow it by metering at your real boundary (the ASGI middleware, if HTTP is your edge). And payment is not included: caps stop over-use, but money moves in your billing flow — this library feeds it and stays out of it. If you need crypto-native machine payments (x402 et al.), that's a different rail; keyed metering is the one that bills today.

MIT. Built by Arcaeon — the evidence layer for AI.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

arcaeon_meter-0.1.0.tar.gz (17.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

arcaeon_meter-0.1.0-py3-none-any.whl (15.8 kB view details)

Uploaded Python 3

File details

Details for the file arcaeon_meter-0.1.0.tar.gz.

File metadata

  • Download URL: arcaeon_meter-0.1.0.tar.gz
  • Upload date:
  • Size: 17.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.3

File hashes

Hashes for arcaeon_meter-0.1.0.tar.gz
Algorithm Hash digest
SHA256 fce7fa205ae0b345a0c4f042bdc68aadfdedd21a35ec80f849104ec6c487cf76
MD5 d407a07ecfca406473bcec9ffaeefe9f
BLAKE2b-256 9a6c29056e5ef5f0353eba1b22977f202e05e1bb8225095e165335d6e3e9b682

See more details on using hashes here.

File details

Details for the file arcaeon_meter-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: arcaeon_meter-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.3

File hashes

Hashes for arcaeon_meter-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3b2015de316715b4f0557853f8875a9a9ff0ad03fe2bd21597eff75d5c653419
MD5 aa62b9c55dc4e872d83563ec0ba4a982
BLAKE2b-256 d80e6cebe329155ea50dc48f08717c4b8f51775299ed142bc6d884ac053475b2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page