Overview
A plain usage sink ships events outward, best effort and unsigned. The ledger records the same completed calls in a form you can verify later. Each event is hash-chained to the previous one, so editing any past record breaks every link after it. With a signing seed configured, each entry is Ed25519-signed, so the record is attributable to the proxy that wrote it. All of this ships in the Apache-2.0 binary.
Configuration
Configure a ledger usage sink on an ai_proxy origin:
action:
type: ai_proxy
providers:
- name: openai
provider_type: openai
api_key: ${OPENAI_API_KEY}
default_model: gpt-4o-mini
models: [gpt-4o-mini]
usage_sinks:
- type: ledger
path: /var/lib/sbproxy/usage-ledger.jsonl
# Optional 32-byte Ed25519 seed as hex. Resolve from a secret manager.
signing_seed_hex: ${LEDGER_SIGNING_SEED_HEX}
How it works
- Chain.
entry_hash = SHA-256(prev_hash || seq || recorded_at || event). Changing any field of any past entry changes its hash, which no longer matches the next entry'sprev_hash. - Signatures. With a seed set, each entry's digest is Ed25519-signed; a receipt verifies against the published public key and a forged entry does not.
- Exactly-once. The file is replayed on open, so a retried delivery of the same
request_idcollapses to a single entry. - Durability. The ledger file is its own write-ahead log; each entry is written and flushed before the append returns, off the request latency path, so a burst never drops an event.
Verifying
sbproxy ai ledger verify /var/lib/sbproxy/usage-ledger.jsonl \
--signing-seed-hex "$LEDGER_SIGNING_SEED_HEX"
It prints the entry count and exits 0 on success, or names the first failing sequence number and exits 1, so it slots into a cron check or CI gate. Edit a cost in the file and re-run to watch it fail at the mutated sequence.