Context
Follow-up to the billing implementation in #2 and the audit in #1.
The current Phase 1 implementation is approved and working correctly. This issue tracks the non-blocking hardening work identified during maintainer review.
Workstreams
1. Live USD/sat exchange rate
Replace the operator-maintained MUXLL_USD_PER_SAT value with a live Fx.usdPerSat() service.
Proposed behavior:
- Fetch the current USD/BTC rate using provider fallback:
- Kraken
- Coinbase
- Binance
- Convert the USD/BTC rate into USD per satoshi.
- Cache successful rates for approximately one hour.
- Avoid making an external request for every top-up.
- Preserve
MUXLL_TOPUP_BUFFER_PCT as protection against price movement.
- Define and document behavior when every provider is unavailable:
- fail closed, or
- use the last known rate with a maximum stale age.
- Decide whether
MUXLL_USD_PER_SAT remains available as an explicit operator override or emergency fallback.
Acceptance criteria
balance.topup uses the FX service when calculating the required sats.
- Provider fallback order is deterministic and tested.
- Cache expiration and reuse are tested.
- Complete provider failure has explicit, tested behavior.
- Invalid or non-positive provider responses are rejected.
- No top-up is quoted using an undefined or silently invalid rate.
2. Store all ledger USD values as integer nano-dollars
Complete the integer-money representation across the ledger.
Current state:
balances.credit_nusd uses integer nano-dollars.
usage.cost_usd, usage.charge_usd, and credits.amount_usd use SQLite REAL values.
Required changes:
- Store usage cost and charge amounts as integer nano-dollars.
- Store credit history amounts as integer nano-dollars.
- Convert to decimal USD only at the public API boundary.
- Add a migration strategy for existing SQLite ledger files.
- Update comments and documentation so they accurately describe the storage format.
Acceptance criteria
- All persisted USD monetary values use integer nano-dollars.
- Repeated credits and charges do not accumulate floating-point drift.
- Existing balance, usage history, and credit history behavior remains unchanged at the API boundary.
- Migration behavior is covered by tests.
AGENTS.md, ledger.ts, and billing documentation accurately describe the final representation.
3. Upgrade @earendil-works/pi-ai to 0.83.0
The workspace currently uses ^0.80.3. Because this is a 0.x dependency, the current range does not upgrade to 0.83.0.
Required work:
- Review the
0.81.x through 0.83.0 changelog.
- Confirm existing subpath imports remain supported.
- Check whether any root imports need to move to
@earendil-works/pi-ai/compat.
- Update the dependency version consistently across all workspace packages.
- Update the lockfile.
- Run typecheck, lint, and the complete test suite.
Acceptance criteria
- Every workspace package uses the same compatible
pi-ai version.
- No deprecated or moved root API is imported incorrectly.
- Typecheck and lint pass.
- The complete test suite passes without regressions.
Out of scope
The following remain intentionally deferred to billing Phase 2:
- Fixed-rate model pricing
byModel pricing overrides
byPubkey pricing overrides
Refs #1
Follow-up to #2
Context
Follow-up to the billing implementation in #2 and the audit in #1.
The current Phase 1 implementation is approved and working correctly. This issue tracks the non-blocking hardening work identified during maintainer review.
Workstreams
1. Live USD/sat exchange rate
Replace the operator-maintained
MUXLL_USD_PER_SATvalue with a liveFx.usdPerSat()service.Proposed behavior:
MUXLL_TOPUP_BUFFER_PCTas protection against price movement.MUXLL_USD_PER_SATremains available as an explicit operator override or emergency fallback.Acceptance criteria
balance.topupuses the FX service when calculating the required sats.2. Store all ledger USD values as integer nano-dollars
Complete the integer-money representation across the ledger.
Current state:
balances.credit_nusduses integer nano-dollars.usage.cost_usd,usage.charge_usd, andcredits.amount_usduse SQLiteREALvalues.Required changes:
Acceptance criteria
AGENTS.md,ledger.ts, and billing documentation accurately describe the final representation.3. Upgrade
@earendil-works/pi-aito 0.83.0The workspace currently uses
^0.80.3. Because this is a0.xdependency, the current range does not upgrade to0.83.0.Required work:
0.81.xthrough0.83.0changelog.@earendil-works/pi-ai/compat.Acceptance criteria
pi-aiversion.Out of scope
The following remain intentionally deferred to billing Phase 2:
byModelpricing overridesbyPubkeypricing overridesRefs #1
Follow-up to #2