Sign inSign up

minmoto/api

By minmoto

•Updated about 6 hours ago

repository for minmo- api

Image
0

10K+

minmoto/api repository overview

⁠Minmo API

The Minmo API provides centralized access to Minmo services including agents, Lightning, and currency swap functionality.

⁠Core Functionality

  • Multi-tenant Organizations: Isolated organization resources with role-based access control
  • Authentication & Authorization: Hexclave hosted auth, Minmo-issued Nostr JWTs, and Hexclave-managed API keys
  • Currency Swap Service: KES ↔ BTC swap operations with M-Pesa and Lightning Network integration
  • Usage Tracking & Billing: Real-time API monitoring, rate limiting, and usage analytics
  • API Management: Create and manage API keys with service-specific permissions

⁠Quick Start

# Start infrastructure and console service
npm run setup:dev
npm run dev:cs

# Access services
# - Console API: http://localhost:5000/api/v1/
# - API Documentation: http://localhost:5000/api/docs

⁠Key API Endpoints

⁠Authentication
  • POST /auth/register - Register new user account
  • POST /auth/login - User login with JWT tokens
  • GET /auth/user-info - Get current user and organizations
⁠Organizations & API Keys
  • POST /organizations - Create new organization
  • POST /organizations/:id/api-keys - Create API key
  • GET /organizations/:id/api-keys - List API keys
⁠Swap Service
  • POST /swap/quote - Get currency swap quote
  • POST /swap/onramp - Create KES → BTC swap
  • POST /swap/offramp - Create BTC → KES swap
⁠Wallet balance conversions

Breez Spark wallets convert between their bitcoin and stable (USDB) balances through two endpoints. direction is to_stable (BTC → USD) or to_bitcoin (USD → BTC), and amount is in source units: sats for to_stable, USDB base units for to_bitcoin.

  • POST /teams/:teamId/wallets/:walletId/conversions/quote with { direction, amount } or { direction, convertAll: true } returns a provider estimate (quotedAmount, estimatedSourceAmount, estimatedDestinationAmount, feeSats, expiresAt).
  • POST /teams/:teamId/wallets/:walletId/conversions executes it. Send { direction, convertAll: true } to convert the live source balance, or { direction, amount, quotedAmount, tokenIdentifier } from a reviewed quote.

Both require the bitcoin:wallet_send permission. Executing also requires an Idempotency-Key header: a retry with the same key and request replays a completed conversion, while reusing it for a different request or after a failed attempt returns 409. Partial conversions pay the wallet's own Spark address through the provider's conversion path. The API prepares a fresh estimate on submission and rejects it if the estimated spend exceeds the requested source amount; amounts received may differ from the estimate as rates move. For to_bitcoin, the provider may round a conversion up to the whole balance rather than leave unconvertible token dust; the quote reports this as convertsEntireBalance.

to_stable with convertAll uses the live BTC balance when the request executes, and a full-balance amount must equal the live balance. Manual stabilization preserves the wallet's automatic conversion policy and threshold. to_bitcoin is refused with auto_conversion_enabled while the wallet's stable balance policy is on, because the provider would convert the bitcoin back to USD. Send disableAutoConversion: true to turn the policy off and reconnect the wallet before converting. A pending manual conversion does not signal a provider failure or pause the wallet.

If the provider reports a failed automatic conversion, the API records stableBalance.autoConversionPaused and stops that wallet's active SDK instance. Wallet reads remain available, and existing token holdings are not converted back to BTC. A normal wallet edit preserves the pause. To resume, review the provider outcome, then use the Console's Resume conversions action or PATCH the wallet with enabled stable balance settings and resumeAutoConversion: true. The API refuses to resume while a conversion is pending or needs a refund, including an older unresolved conversion. Support must reconcile the provider outcome before the wallet can resume. When automatic conversion is off, normal wallet connections do not unset the active stable label or start a conversion worker, so manually held stable tokens remain held.

⁠Durable rate events and delivery retries

OTC quote/update events persist the original rate and quote timestamp in their payload. Delivery and replay do not depend on FX cache keys, expiry, or provider availability. Older events without payloads recover their original quote from the timestamp and rate already stored in their deduplication key. Invalid legacy records remain pending for investigation; they are not replaced with current rates. Rate-expiry events retain a null payload.

Failed domain-event deliveries use an exponential retry delay starting at five seconds and capped at five minutes, using locked_until as the next eligible attempt time. Later eligible events can continue while failures wait. Inspect events.last_error, attempt_count, and locked_until for persistent failures.

⁠Database connection budget

Each API process opens at most POSTGRES_POOL_SIZE TypeORM connections (default 10) plus BREEZ_POSTGRES_POOL_SIZE Breez Spark storage connections (default 2), shared across wallet SDKs. Swap automation runs at most SWAP_AUTOMATION_CONCURRENCY workers across polling and event prompts. All workers take their per-swap advisory locks on one shared pooled session, so automation pins a single connection however many workers run. Each running worker still needs a pooled connection for its queries, so the worker count must stay below the pool size. Config validation enforces this, and the unset default is 4 capped at one below the pool size.

Development and staging share the testing-managed-postgres Cloud SQL instance. Terraform configures db-g1-small, whose documented default is max_connections = 50, with 3 reserved for superusers. Verify the actual limits after applying the tier change; these allocations assume one API process per environment. Their Terraform tfvars set TypeORM pools of 5 for dev and 10 for staging, with Breez pools of 2 and 4 respectively. Both run 2 automation workers. At 4 connections with 2 workers, background automation and pollers left API requests queued for seconds in pg-pool.connect:

ClientConnections
Dev API: TypeORM 5 + Breez 27
Staging API: TypeORM 10 + Breez 414
BTCPay (dev and staging)2
NBXplorer1
Planned total24 of 47
Headroom for migrations and diagnostics23

BTCPay and NBXplorer counts above are planning estimates, not enforced pool caps: their Terraform connection strings do not specify maximum pool sizes. Actual use by these clients reduces the available headroom. Each additional dev API process consumes up to another 7 connections; each additional staging API process consumes up to another 14.

Production also configures db-g1-small, with 15 TypeORM connections plus 4 Breez connections per API process and 4 automation workers. That leaves 28 of the expected 47 non-superuser slots before BTCPay, NBXplorer, migrations, diagnostics, or additional API processes are counted. Workers use the TypeORM pool and must not be added again to the connection total. The shared advisory-lock session occupies one slot within that pool.

Cloud SQL documents the defaults in its database flags reference⁠. Changing the tier can interrupt database availability; schedule the apply accordingly. Before expanding pools or worker concurrency, check the limits and aggregate client usage on each instance:

SHOW max_connections;
SHOW superuser_reserved_connections;

Connections are labelled minmo-api-<env>-typeorm and minmo-api-<env>-breez in pg_stat_activity:

SELECT datname, application_name, state, count(*)
FROM pg_stat_activity
GROUP BY 1, 2, 3
ORDER BY count(*) DESC;

⁠Authentication Methods

  1. JWT Bearer Token: For web UI access
  2. API Key: For programmatic access via X-API-Key header

Minmo Nostr bearer credentials must be access tokens; refresh tokens are accepted only by the refresh endpoint. Authorization reads the current account status and roles, so suspended accounts and revoked roles lose access immediately. Participant actions require any supplied beneficiaryId to match the authenticated Nostr identity. The global /swap/disputed list requires a Minmo admin; team operators use /teams/:teamId/swaps/disputed.

Team API-key permissions are limited to the shared integration permission catalog (API_KEY_POLICY_PERMISSIONS). Global user management permissions cannot be saved or used by team keys, including policies stored before this restriction.

Server code that resolves Lightning addresses must import the helpers from @minmo/core/server/lightning-address. This transport requires HTTPS on port 443, rejects redirects and non-public DNS results, and connects using the validated addresses. Responses are limited to 1 MiB and requests to ten seconds. Browser and mobile consumers continue using the browser-compatible @minmo/core helpers.

⁠Hexclave local authorization projection

JWT requests can authorize entirely from the existing users.roles and auth_identities.metadata.hexclaveAuthorization columns. No authorization table or request-time Hexclave fallback is used. There is no feature flag: the API blocks startup until every active Hexclave identity has either a usable projection with more than five minutes of validity remaining or a locally disabled projection, and until the Hexclave JWT signing keys are loaded. A rolling deployment leaves the previous API revision serving traffic while the first new instance performs this backfill. If safe coverage cannot be established within ten minutes, the new instance fails startup rather than serving against missing or stale authorization state.

Projections expire after ten minutes and fail closed when missing, malformed, stale, disabled, or expired. The repair worker runs every five minutes with a renewable database lease, bounded concurrency, and retry backoff. Rollback is a deployment rollback to the prior API revision; stale projection data is never accepted as a rollback mechanism.

External Hexclave changes are discovered by session refresh, explicit Minmo mutations, and the five-minute repair loop. There is no webhook dependency in this rollout; the ten-minute projection validity bounds authorization staleness and fails closed when repair cannot refresh it. Signing keys also refresh every five minutes outside the request path. An unknown JWT key fails closed until a background refresh discovers it; token verification never fetches JWKS synchronously.

Monitor authorization.projection.coverage and authorization.projection.repair.completed; production readiness requires no pending, stale, expired, malformed, or expiring active projections. Measure the authorization budget from authorization.local.completed logs. synchronousHexclaveCalls must remain zero. Measure the end-to-end Lightning receive outcome separately from wallet.receive.completed; wallet loading, Breez queue, SDK readiness, SDK execution, and response completion timings are reported independently.

Run the direct-to-API authorization benchmark against a warm wallet read route:

AUTHORIZATION_BENCHMARK_URL=https://api.example.com/api/v1/teams/<team-id>/wallets \
AUTHORIZATION_BENCHMARK_TOKEN=<access-token> \
LIGHTNING_RECEIVE_BENCHMARK_URL=https://api.example.com/api/v1/teams/<team-id>/wallets/<wallet-id>/bitcoin/receive \
npm run benchmark:authorization --workspace=@minmo/api

The command performs 20 warmups and at least 100 measured requests, reports authorization and end-to-end P50/P95/P99, requires the API's zero-Hexclave-call confirmation header, and fails when authorization P95 is not below 100ms. Run it with the Hexclave signing key already cached and block unexpected Hexclave user/team/permission traffic at the test boundary. When the optional Lightning receive URL is provided, it separately runs 5 warmups and at least 20 measured invoice requests, reports success/failure counts and P50/P95/P99, and compares the successful provider-inclusive P95 with the one-second target without making that outcome the authorization release gate. Override the request JSON with LIGHTNING_RECEIVE_BENCHMARK_BODY when the target wallet needs a larger amount.

⁠Role-Based Access Control

  • Developer: Create/manage API keys, view usage
  • Finance: View billing and usage data
  • Admin: Full organization and user management

⁠Documentation

For comprehensive documentation, deployment guides, API references, and development setup instructions, visit:

📖 Complete Documentation⁠

This includes:

  • Detailed API documentation
  • Development environment setup
  • Security features and configuration
  • Production deployment guides
  • Usage examples and testing procedures

⁠License

Private - Minmo Internal Use Only

Tag summary

Content type

Image

Digest

sha256:0ebb7d0fe…

Size

519.5 MB

Last updated

about 6 hours ago

docker pull minmoto/api:production-0.5.15