Skip to content

feat(core): add api-keys spending limits#519

Merged
dolcalmi merged 33 commits intomainfrom
fix/spending-limits-centralization-no-dashboard
Apr 10, 2026
Merged

feat(core): add api-keys spending limits#519
dolcalmi merged 33 commits intomainfrom
fix/spending-limits-centralization-no-dashboard

Conversation

@dolcalmi
Copy link
Copy Markdown

@dolcalmi dolcalmi commented Apr 8, 2026

Summary

  • Base PR: feat: integrate api-keys spending limits into payment flows and dashboard #463
  • Add atomic check-and-lock spending limits for API keys in core/api-keys
  • Centralize spending limit settlement and reversal across all payment flows in core/api
  • Harden record_spending finalization with amount consistency checks and idempotent retry handling
  • Align api-keys proto codegen/build wiring and JWT api_key_id runtime validation
  • Add BATS tests for hold-invoice reversal and updated limits coverage

Copilot review follow-ups addressed

  • Replaced brittle plugin paths in core/api/src/services/api-keys/proto/buf.gen.yaml
  • Added runtime type-guard for JWT api_key_id in sessionPublicContext
  • Added amount consistency validation during ephemeral finalization in core/api-keys
  • Hardened idempotent finalize behavior when transaction_id already exists
  • Mapped Missing transaction id and Ephemeral reservation not found to ApiKeySpendingRecordError in core/api

Notes

  • apps/dashboard changes are intentionally excluded from this PR and will be submitted separately

bas4r and others added 18 commits March 12, 2026 14:59
…reversals

Unifies account-limit and API-key spending orchestration in payment sends to reduce divergence across flows and ensure reservations are settled consistently. Also reverses API-key spending on Bria payout cancellations and adds focused unit coverage for spending-limit and Bria handler behavior.
@dolcalmi dolcalmi marked this pull request as draft April 8, 2026 03:09
@dolcalmi dolcalmi marked this pull request as ready for review April 8, 2026 20:42
@dolcalmi dolcalmi requested a review from Copilot April 8, 2026 20:43
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds API-key spending limit enforcement and atomic reservation/settlement wiring across Core API payment flows, backed by an api-keys gRPC service change, plus new unit/BATS coverage for reversals and limit enforcement.

Changes:

  • Introduce an atomic check_and_lock_spending flow in core/api-keys and consume it from core/api to reserve spending before executing payments.
  • Centralize post-payment settlement/reversal behavior (record vs reverse) and add reversals for payout cancellation + pending LN failure paths.
  • Add/extend unit tests and BATS integration tests for limit enforcement and hold-invoice cancellation reversal.

Reviewed changes

Copilot reviewed 52 out of 65 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks updated to pin/override handlebars and related dependency graph changes.
package.json Adds handlebars version pin under resolutions.
core/api/test/unit/servers/event-handlers/bria.spec.ts Unit tests for payout cancellation reversal behavior and tracing on failure.
core/api/test/unit/app/payments/spending-limits.spec.ts Unit tests for withSpendingLimits orchestration across outcomes and settlement methods.
core/api/test/unit/app/payments/api-key-spending.spec.ts Unit tests for lock/settle helpers around api-key spending.
core/api/src/services/api-keys/proto/buf.gen.yaml Buf generation config for api-keys proto outputs.
core/api/src/services/api-keys/proto/api_keys.proto Client-side proto definition for api-keys spending operations.
core/api/src/services/api-keys/proto/api_keys_pb.d.ts Generated TS typings for api-keys protobuf messages.
core/api/src/services/api-keys/proto/api_keys_grpc_pb.js Generated JS gRPC client bindings for api-keys service.
core/api/src/services/api-keys/proto/api_keys_grpc_pb.d.ts Generated TS typings for gRPC service/client.
core/api/src/services/api-keys/index.ts Adds ApiKeysService wrapper with check/record/reverse spending calls.
core/api/src/services/api-keys/grpc-client.ts Adds gRPC client + promisified methods for api-keys service.
core/api/src/services/api-keys/errors.ts Adds mapping from api-keys gRPC errors to domain errors.
core/api/src/servers/middlewares/session.ts Extracts apiKeyId from JWT claims into the GraphQL public context.
core/api/src/servers/index.files.d.ts Extends GraphQL context typing to include apiKeyId.
core/api/src/servers/event-handlers/bria.ts Reverses api-key spending when an on-chain payout is cancelled.
core/api/src/graphql/public/root/mutation/onchain-usd-payment-send.ts Threads apiKeyId through to on-chain USD payment app call.
core/api/src/graphql/public/root/mutation/onchain-usd-payment-send-as-sats.ts Threads apiKeyId through to on-chain USD-as-sats payment app call.
core/api/src/graphql/public/root/mutation/onchain-payment-send.ts Threads apiKeyId through to on-chain BTC payment app call.
core/api/src/graphql/public/root/mutation/onchain-payment-send-all.ts Threads apiKeyId through to on-chain “send all” app call.
core/api/src/graphql/public/root/mutation/lnurl-payment-send.ts Threads apiKeyId through to LNURL payment app call.
core/api/src/graphql/public/root/mutation/ln-noamount-usd-invoice-payment-send.ts Threads apiKeyId through to no-amount USD invoice payment app call.
core/api/src/graphql/public/root/mutation/ln-noamount-invoice-payment-send.ts Threads apiKeyId through to no-amount invoice payment app call.
core/api/src/graphql/public/root/mutation/ln-invoice-payment-send.ts Threads apiKeyId through to invoice payment app call.
core/api/src/graphql/public/root/mutation/ln-address-payment-send.ts Threads apiKeyId through to LN address payment app call.
core/api/src/graphql/public/root/mutation/intraledger-usd-payment-send.ts Threads apiKeyId through to intraledger USD payment app call.
core/api/src/graphql/public/root/mutation/intraledger-payment-send.ts Threads apiKeyId through to intraledger BTC payment app call.
core/api/src/graphql/error-map.ts Maps new api-keys spending errors to GraphQL error classes.
core/api/src/domain/bitcoin/lightning/payments/index.types.d.ts Renames Writeable -> Writable for the type helper.
core/api/src/domain/api-keys/index.types.d.ts Adds domain types and IApiKeysService interface for spending operations.
core/api/src/domain/api-keys/index.ts Exposes api-keys domain errors.
core/api/src/domain/api-keys/errors.ts Adds api-keys domain error types and severity levels.
core/api/src/config/index.ts Exposes api-keys service host/port config values.
core/api/src/config/env.ts Adds API_KEYS_HOST/API_KEYS_PORT env parsing and defaults.
core/api/src/app/wallets/index.types.d.ts Adds optional apiKeyId to payment send argument types.
core/api/src/app/payments/update-pending-payments.ts Reverses api-key spending when pending LN payments fail/are reverted.
core/api/src/app/payments/spending-limits.ts Introduces withSpendingLimits wrapper to coordinate account + api-key limits and settlement.
core/api/src/app/payments/send-on-chain.ts Routes on-chain flows through withSpendingLimits and threads apiKeyId.
core/api/src/app/payments/send-lnurl.ts Threads apiKeyId into LNURL/LN address send app calls.
core/api/src/app/payments/send-lightning.ts Routes LN flows through withSpendingLimits, adds intraledger recipient validation helper, threads apiKeyId.
core/api/src/app/payments/send-intraledger.ts Routes intraledger flows through withSpendingLimits and threads apiKeyId.
core/api/src/app/payments/index.types.d.ts Adds types for api-key spending lock/settlement and execution result contract.
core/api/src/app/payments/api-key-spending.ts Adds helpers for locking and settling api-key spending.
core/api/src/app/errors.ts Adds api-keys domain errors to ApplicationErrors.
core/api-keys/src/limits/mod.rs Adds atomic check-and-lock flow + enhanced record semantics for ephemeral finalization.
core/api-keys/src/limits/error.rs Adds new limit error variants (missing txn id, limit exceeded, ephemeral not found).
core/api-keys/src/grpc/server/mod.rs Adds gRPC handler for CheckAndLockSpending and enriches RecordSpending error mapping.
core/api-keys/src/app/mod.rs Wires new check-and-lock and ephemeral-aware record into the app layer.
core/api-keys/proto/api_keys.proto Updates server proto to add CheckAndLockSpending and ephemeral_id in RecordSpending.
core/api-keys/.sqlx/query-eb63f3d930396dbcdc2f1fae84837a482cab3bec0207c9e9d5df0f2f4e71c66a.json Updates sqlx metadata for the new reservation insert query shape.
core/api-keys/.sqlx/query-b52c808ef2afc69dcd45ddd6889b5a7ab0b025f6103ce6f0aa95fb12d950d005.json Removes stale sqlx query metadata (no longer used).
core/api-keys/.sqlx/query-9c69eb1f61d4e4832bbe570fc3214d9958cb372c32088c0bbf7dbc9b4c6a897b.json Adds sqlx metadata for reservation finalization UPDATE query.
core/api-keys/.sqlx/query-87c9634aa38f95eb1f850b1901153f9cf2e2299c9a4dd5a9e111b2cb1400d283.json Adds sqlx metadata for “already finalized” existence check query.
core/api-keys/.sqlx/query-747d3d564d63f5aeeac3db15cff84bebe11d59fd343c083b745f9ce4ee09ed0d.json Adds sqlx metadata for non-ephemeral record insert query.
core/api-keys/.sqlx/query-484c75fe89364613e436dee47c34376e721165b22f4b49a93ce24a097914e536.json Adds sqlx metadata for api_key_limits ... FOR UPDATE query.
bats/gql/on-chain-payment-send.gql Requests code in error payloads for on-chain send mutation.
bats/gql/lnurl-payment-send.gql Requests code in error payloads for LNURL send mutation.
bats/gql/ln-no-amount-usd-invoice-payment-send.gql Requests code in error payloads for no-amount USD invoice send mutation.
bats/gql/ln-no-amount-invoice-payment-send.gql Requests code in error payloads for no-amount invoice send mutation.
bats/gql/ln-invoice-payment-send.gql Requests code in error payloads for invoice send mutation.
bats/gql/intraledger-usd-payment-send.gql Requests code in error payloads for intraledger USD send mutation.
bats/gql/intraledger-payment-send.gql Requests code in error payloads for intraledger send mutation.
bats/core/api-keys/api-keys-limits.bats Adds BATS suite for api-key limit enforcement across multiple payment types.
bats/core/api-keys/api-keys-hold-invoice-reversal.bats Adds BATS suite verifying spending reversal on hold-invoice cancellation.
Files not reviewed (6)
  • core/api-keys/.sqlx/query-484c75fe89364613e436dee47c34376e721165b22f4b49a93ce24a097914e536.json: Language not supported
  • core/api-keys/.sqlx/query-747d3d564d63f5aeeac3db15cff84bebe11d59fd343c083b745f9ce4ee09ed0d.json: Language not supported
  • core/api-keys/.sqlx/query-87c9634aa38f95eb1f850b1901153f9cf2e2299c9a4dd5a9e111b2cb1400d283.json: Language not supported
  • core/api-keys/.sqlx/query-9c69eb1f61d4e4832bbe570fc3214d9958cb372c32088c0bbf7dbc9b4c6a897b.json: Language not supported
  • core/api-keys/.sqlx/query-b52c808ef2afc69dcd45ddd6889b5a7ab0b025f6103ce6f0aa95fb12d950d005.json: Language not supported
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/api/src/services/api-keys/proto/buf.gen.yaml Outdated
Comment thread core/api-keys/src/limits/mod.rs Outdated
Comment thread core/api-keys/src/limits/mod.rs Outdated
Comment thread core/api/src/servers/middlewares/session.ts
Validate amount consistency during ephemeral spending finalization and strengthen idempotent retry handling for transaction conflicts. Align API integration scripts/config and error mapping so failures surface predictably across services.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 53 out of 67 changed files in this pull request and generated 3 comments.

Files not reviewed (7)
  • core/api-keys/.sqlx/query-2f68d83a9706d685c37190a1fe48dbb6735ade28d49d26ee0f4163db2cd5988f.json: Language not supported
  • core/api-keys/.sqlx/query-3dfccb253bd325b5b387477116b421be0dd5b0c10a3e34f7cc28ac315d327368.json: Language not supported
  • core/api-keys/.sqlx/query-484c75fe89364613e436dee47c34376e721165b22f4b49a93ce24a097914e536.json: Language not supported
  • core/api-keys/.sqlx/query-51351754fbad093aa266a1694c6dc3b109364bfa6f0b54a00aa3cce03a488540.json: Language not supported
  • core/api-keys/.sqlx/query-747d3d564d63f5aeeac3db15cff84bebe11d59fd343c083b745f9ce4ee09ed0d.json: Language not supported
  • core/api-keys/.sqlx/query-b52c808ef2afc69dcd45ddd6889b5a7ab0b025f6103ce6f0aa95fb12d950d005.json: Language not supported
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/api/src/app/payments/spending-limits.ts Outdated
Comment thread core/api/src/services/api-keys/errors.ts
Comment thread core/api/src/services/api-keys/errors.ts
dolcalmi added 2 commits April 9, 2026 10:12
Handle missing transaction id and missing ephemeral reservation as spending record errors, and collapse equivalent mappings into grouped switch cases for clarity.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 53 out of 67 changed files in this pull request and generated 2 comments.

Files not reviewed (7)
  • core/api-keys/.sqlx/query-2f68d83a9706d685c37190a1fe48dbb6735ade28d49d26ee0f4163db2cd5988f.json: Language not supported
  • core/api-keys/.sqlx/query-3dfccb253bd325b5b387477116b421be0dd5b0c10a3e34f7cc28ac315d327368.json: Language not supported
  • core/api-keys/.sqlx/query-484c75fe89364613e436dee47c34376e721165b22f4b49a93ce24a097914e536.json: Language not supported
  • core/api-keys/.sqlx/query-51351754fbad093aa266a1694c6dc3b109364bfa6f0b54a00aa3cce03a488540.json: Language not supported
  • core/api-keys/.sqlx/query-747d3d564d63f5aeeac3db15cff84bebe11d59fd343c083b745f9ce4ee09ed0d.json: Language not supported
  • core/api-keys/.sqlx/query-b52c808ef2afc69dcd45ddd6889b5a7ab0b025f6103ce6f0aa95fb12d950d005.json: Language not supported
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/api-keys/src/limits/mod.rs Outdated
Comment thread core/api-keys/src/grpc/server/mod.rs
dolcalmi added 2 commits April 9, 2026 12:58
…error mapping

- Refactor record_spending into finalize_ephemeral and record_canonical
  with exhaustive match on (ephemeral, canonical) row states
- Fix UNIQUE constraint violation when canonical txn_id row already exists
  during ephemeral finalization by deleting the orphaned ephemeral row
- Map AmountMismatch and LimitExceeded to gRPC failed_precondition
  instead of internal error in record_spending handler
- Extract reusable helpers: fetch_transaction, delete_transaction,
  rename_transaction, ensure_amount_matches
- Add unit tests for ensure_amount_matches
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 54 out of 68 changed files in this pull request and generated no new comments.

Files not reviewed (7)
  • core/api-keys/.sqlx/query-484c75fe89364613e436dee47c34376e721165b22f4b49a93ce24a097914e536.json: Language not supported
  • core/api-keys/.sqlx/query-54fed150bc898abeb0521a03a99efd4c656219fd7545909263600c3bfdae06bf.json: Language not supported
  • core/api-keys/.sqlx/query-b52c808ef2afc69dcd45ddd6889b5a7ab0b025f6103ce6f0aa95fb12d950d005.json: Language not supported
  • core/api-keys/.sqlx/query-e347e89036d70b33d0a1aed6f51bd80036f5c786d0234d451c485f1e12e5794b.json: Language not supported
  • core/api-keys/.sqlx/query-e66c9622a58cb179025b47328b9ac1a47188a5c8b8048a16d51a6e8a4bdc275a.json: Language not supported
  • core/api-keys/.sqlx/query-eb63f3d930396dbcdc2f1fae84837a482cab3bec0207c9e9d5df0f2f4e71c66a.json: Language not supported
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

dolcalmi added 3 commits April 9, 2026 17:58
… settlement enum

- ErrorWithJournal path in executePaymentViaLn now correctly uses
  reverseSettlement: the ledger is already reverted inline inside
  lockedPaymentViaLnSteps before this result is returned, so
  update-pending-payments will never process it and recordSettlement
  would permanently inflate the spending counter for failed payments
- Remove duplicate SpendingLimitsSettlement const; spending-limits.ts
  now imports and uses ApiKeySpendingSettlementType directly as the
  single source of truth
- Update index.types.d.ts and spending-limits.spec.ts accordingly
- Replace hashtext($1)::bigint with hashtextextended($1, 0) to expand hash space from 32-bit to 64-bit
- Reduces collision probability for pg_advisory_xact_lock keys when many API keys exist
- Requires PostgreSQL 13+ (already in use for api-keys service)
@dolcalmi dolcalmi force-pushed the fix/spending-limits-centralization-no-dashboard branch from 7c2a72f to 6ae17b5 Compare April 10, 2026 00:24
@dolcalmi dolcalmi merged commit bef07a7 into main Apr 10, 2026
29 of 30 checks passed
@dolcalmi dolcalmi deleted the fix/spending-limits-centralization-no-dashboard branch April 10, 2026 03:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants