Ippon (一本, "one point", "decisive victory")
Minibits Ippon is a minimalistic ecash and Lightning wallet implementing the Cashu protocol. It can be operated as a REST API server (for hosted deployments and AI agents connecting over HTTP) or as a local CLI (for AI agents that install and control it directly via standard I/O).
A live Ippon instance is running at ippon.minibits.cash:
- Swagger UI — API explorer (
GET /v1/)- OpenAPI 3.0 JSON — raw spec (
GET /v1/json)- Wallet info — wallet service info (
GET /v1/info)- MCP server — MCP server to make usage by AI agents easier (
POST /mcp)Also available as Tor hidden service (requires Tor Browser or a Tor-enabled client):
eaqmg2oqhay5btz5v75bgknfb3x4q4vesfijjzvgkbgocn5fvhkntwad.onion
⚠️ This instance is provided for testing and evaluation purposes only. It is an alpha software, use it at your own risk, with small amounts only. Do not store funds you cannot afford to lose.
Ippon is designed primarily for non-human systems — especially AI agents — that require instant, automated capability to receive and send micropayments without human intervention.
Minibits Ippon is intended for server-side deployment, ideally by a Cashu mint operator as a complementary service to their mint. It is therefore a fully custodial solution optimized for short-lived, single-purpose wallets with low balances.
To minimize complexity and provide an extremely simple API that AI agents can easily understand and use, Ippon adheres to strict design constraints:
Ippon supports one or more Cashu mints configured via the MINT_URLS environment variable (comma-separated list). All mints share a single unit (e.g. sat), keeping the API simple. Each wallet is bound to exactly one mint at creation time; the first listed mint is the default. Operators control which mints are accepted, keeping custody scope well-defined.
AI agents lack reliable long-term secret storage. Ippon wallets are expected to be short-lived, hold minimal balances, and should be emptied upon session completion. Seed-based wallets introduce unnecessary complexity and operational burden that neither agents nor wallet operators really need.
The wallet exposes a very simple REST API with verbose, intuitive route and parameter names that hide ecash internals where possible. To make consuming the API by AI agents more natural, the ippon_mcp project provides an MCP server facade. Alternatively, operators can run Ippon in CLI mode (INTERACTION_MODE=cli) where the wallet is controlled through standard I/O commands — useful for AI agents that can install and spawn a local process instead of connecting to a hosted server.
Wallet access relies on a bare-bones access_key generated by the wallet server at creation time and later passed in the request’s Authorization header as a Bearer token. Given the short wallet lifetime and the expectation that a new wallet is created and funded for each longer session, there is no need for token refresh or revocation. To minimize necessity for AI agents to keep secrets, MCP server safeguards wallet access_key, linking it to agent's session_id.
Due to the above constraints, the primary safeguard is a combination of rate limiting and of strict limits on maximum wallet balance and payment amounts. Limits are double-tiered:
-
Global limits set by the wallet operator on number of created wallets per ip address, max wallet balance and max ecash or lightning payment
-
Optional per-wallet balance and payment limits defined at wallet creation time. This prevents funding or outgoing payments from exceeding intended task scope.
The API is versioned under /v1/ and uses standard HTTP methods with JSON payloads.
All authenticated endpoints require a Bearer access_key in the Authorization header (except wallet creation and public info).
Returns machine-readable information about the wallet service, including status, supported unit, mint URL, and global limits (balance/payment caps and rate limits).
| Authorization | Request Type | Response Type |
|---|---|---|
| N/A | N/A | InfoResponse |
Example:
curl -X GET http://localhost:3001/v1/infoCreates a new short-lived wallet. Optionally accepts an initial Cashu token to fund it immediately or a name for identification.
| Authorization | Request Type | Response Type |
|---|---|---|
| N/A | WalletCreateRequest | WalletResponse |
Example:
curl -X POST http://localhost:3001/v1/wallet \
-H "Content-Type: application/json" \
-d '{"name": "agent-007-session-001", "token": "cashuBeyJ..."}'Retrieves details of the current wallet, including its name, unit, mint, confirmed balance, and pending balance.
| Authorization | Request Type | Response Type |
|---|---|---|
| Bearer access_key | N/A | WalletResponse |
Example:
curl -X GET http://localhost:3001/v1/wallet \
-H "Authorization: Bearer abc123_access_key"Requests a Lightning invoice for funding the wallet with a specified amount. The wallet automatically handles the mint quote and ecash issuance once the invoice is paid.
| Authorization | Request Type | Response Type |
|---|---|---|
| Bearer access_key | WalletDepositRequest | WalletDepositResponse |
Example:
curl -X POST http://localhost:3001/v1/wallet/deposit \
-H "Authorization: Bearer abc123_access_key" \
-H "Content-Type: application/json" \
-d '{"amount": 10000, "unit": "msat"}'Checks the status of a previously requested deposit quote (e.g., whether the invoice was paid and ecash minted).
| Authorization | Request Type | Response Type |
|---|---|---|
| Bearer access_key | N/A | WalletDepositResponse |
Example:
curl -X GET http://localhost:3001/v1/wallet/deposit/quote_abc123xyz \
-H "Authorization: Bearer abc123_access_key"Generates and exports an ecash token for a specific amount (or pays a Cashu payment request if provided). The wallet handles any necessary swaps and marks proofs as pending to prevent double-spending. Potential mint fees are fully paid by the sender (including fees paid by the receiver when swapping the received token for fresh one). Optionally allows locking the token to a specific receiver's pubkey to prevent unauthorized party to steal it.
| Authorization | Request Type | Response Type |
|---|---|---|
| Bearer access_key | WalletSendRequest | WalletSendResponse |
Example:
curl -X POST http://localhost:3001/v1/wallet/send \
-H "Authorization: Bearer abc123_access_key" \
-H "Content-Type: application/json" \
-d '{"amount": 5000, "unit": "msat", "memo": "Complete this task for me"}'Checks the current state of an exported Cashu token (e.g., whether it has been spent/swapped by the recipient).
| Authorization | Request Type | Response Type |
|---|---|---|
| Bearer access_key | WalletCheckRequest | WalletCheckResponse |
Example:
curl -X POST http://localhost:3001/v1/wallet/check \
-H "Authorization: Bearer abc123_access_key" \
-H "Content-Type: application/json" \
-d '{"token": "cashuB..."}'Decodes a Cashu token, Cashu payment request, or Lightning invoice and returns structured information.
| Authorization | Request Type | Response Type |
|---|---|---|
| Bearer access_key | WalletDecodeRequest | WalletDecodeResponse |
Example:
curl -X POST http://localhost:3001/v1/wallet/decode \
-H "Authorization: Bearer abc123_access_key" \
-H "Content-Type: application/json" \
-d '{"type": "CASHU_TOKEN_V4", "data": "cashuB..."}'Pays an external Lightning invoice (or Lightning address) using the wallet's ecash balance. The wallet handles melt quote, fees, and returns any change.
| Authorization | Request Type | Response Type |
|---|---|---|
| Bearer access_key | WalletPayRequest | WalletPayResponse |
Example:
curl -X POST http://localhost:3001/v1/wallet/pay \
-H "Authorization: Bearer abc123_access_key" \
-H "Content-Type: application/json" \
-d '{"bolt11_request": "lnbc50u...", "amount": 5000, "unit": "msat"}'Checks the status of a melt quote / payment operation (e.g., whether the invoice was successfully paid).
| Authorization | Request Type | Response Type |
|---|---|---|
| Bearer access_key | N/A | WalletPayResponse |
Example:
curl -X GET http://localhost:3001/v1/wallet/pay/etkpOx_SLLNEMOX2oEyk6y1ePKVp9sdUxc3k03bI \
-H "Authorization: Bearer abc123_access_key"Imports and processes an external Cashu token. The wallet validates token with the mint, and swaps it for a new ecash token.
| Authorization | Request Type | Response Type |
|---|---|---|
| Bearer access_key | WalletReceiveRequest | WalletReceiveResponse |
Example:
curl -X POST http://localhost:3001/v1/wallet/receive \
-H "Authorization: Bearer abc123_access_key" \
-H "Content-Type: application/json" \
-d '{"token": "cashuB..."}'Returns the current fiat exchange rate for the wallet's unit (e.g., satoshis per USD).
| Authorization | Request Type | Response Type |
|---|---|---|
| Bearer access_key | N/A | RateResponse |
Example:
curl -X GET http://localhost:3001/v1/rate/USD \
-H "Authorization: Bearer abc123_access_key"CLI mode is intended for local, self-hosted use — typically by an AI agent that installs Ippon and interacts with it via standard I/O instead of HTTP. Data is stored in a local SQLite file; no PostgreSQL or network server is required.
INTERACTION_MODE=cli— starts a readline REPL instead of an HTTP server.DATABASE_ENGINE=sqlite— stores data in a local file (DATABASE_FILE_PATH, default~/.ippon/database.sqlite).- All responses are JSON lines on stdout; the prompt (
>) and info messages go to stderr so they don't interfere with programmatic parsing. - Wallet access keys are short 6-character alphanumeric strings displayed as
xxx-xxx(e.g.adg-08m). Both formats (adg08mandadg-08m) are accepted as input.
# 1. Install dependencies
yarn install
# 2. Copy and configure environment
cp .env.example .env
# Set DATABASE_ENGINE=sqlite, INTERACTION_MODE=cli, MINT_URLS, UNIT, limits, etc.
# 3. Build
yarn build
# 4. Set up SQLite schema (run once, and again after switching engines)
yarn db:setup
# 5. Start the CLI
DATABASE_ENGINE=sqlite INTERACTION_MODE=cli node dist/index.js
# or, combining steps 4 and 5:
yarn start:localAll commands are typed at the > prompt (or piped via stdin). Every response is a single JSON object on stdout.
| Command | Description |
|---|---|
info |
Service info: mints, unit, global limits |
wallet create [name] [mint_url] |
Create a new wallet; mint must be in MINT_URLS; prints access_key |
wallet list |
List all wallets with balances |
wallet <key> balance |
Show wallet balance and details |
wallet <key> deposit <amount> |
Create a Lightning invoice to fund the wallet |
wallet <key> deposit-check <quote_id> |
Check deposit status; auto-mints ecash when paid |
wallet <key> send <amount> [lock_pubkey] |
Export a Cashu token (optionally P2PK-locked) |
wallet <key> receive <token> |
Import a Cashu token |
wallet <key> pay <bolt11|ln_address> [amount] |
Pay a Lightning invoice or Lightning address |
wallet <key> pay-check <quote_id> |
Check payment status |
wallet <key> sync |
Sync pending proofs with the mint |
decode <data> |
Decode a Cashu token, Cashu request, or BOLT11 invoice |
help |
Show available commands |
exit |
Quit |
> wallet create agent-session-1
{"access_key":"adg-08m","name":"agent-session-1","mint":"https://mint.minibits.cash/Bitcoin","unit":"sat","balance":0,"pending_balance":0}
> wallet adg-08m deposit 100
{"quote":"abc123...","request":"lnbc1000n...","state":"UNPAID","expiry":1234567890}
> wallet adg-08m deposit-check abc123...
{"quote":"abc123...","request":"lnbc1000n...","state":"PAID","expiry":1234567890}
> wallet adg-08m balance
{"access_key":"adg-08m","name":"agent-session-1","mint":"https://mint.minibits.cash/Bitcoin","unit":"sat","balance":100,"pending_balance":0}
> wallet adg-08m pay lnbc500n...
{"quote":"xyz987...","amount":50,"fee_reserve":1,"state":"PAID","payment_preimage":"...","expiry":1234567890}
Each command can be piped as a single stdin line; the process exits cleanly after completing it:
# Create a wallet and capture the access key
KEY=$(echo "wallet create bot" | DATABASE_ENGINE=sqlite INTERACTION_MODE=cli LOG_LEVEL=error node dist/index.js 2>/dev/null \
| node -e "process.stdin.setEncoding('utf8');let d='';process.stdin.on('data',c=>d+=c).on('end',()=>console.log(JSON.parse(d).access_key))")
# Receive a token
echo "wallet $KEY receive cashuB..." | DATABASE_ENGINE=sqlite INTERACTION_MODE=cli LOG_LEVEL=error node dist/index.js 2>/dev/null
# Pay an invoice
echo "wallet $KEY pay lnbc..." | DATABASE_ENGINE=sqlite INTERACTION_MODE=cli LOG_LEVEL=error node dist/index.js 2>/dev/nullThe full interactive API reference — including all request bodies, response schemas, and enum values — is served directly by the running Ippon instance:
- Swagger UI (interactive):
GET /v1/ - Raw OpenAPI 3.0 JSON:
GET /v1/json
The spec is generated at runtime from the route definitions and is always in sync with the code.
All request and response TypeScript types used in the route tables above are defined in src/routes/routeTypes.ts.
Note on amounts: Despite the wallet operating with a single unit, amounts are always declared together with
unitto keep values unambiguous (unitmust always equal the wallet's configuredMintUnit).
Minibits Ippon is written in TypeScript and runs on Node.js (v24+). It uses Fastify as the HTTP server and Prisma ORM with either PostgreSQL (API/server mode) or SQLite (CLI/local mode).
API mode (hosted)
- Node.js v24+
- PostgreSQL
- A running Cashu mint
CLI mode (local)
- Node.js v24+
- A running Cashu mint (public mints work; no self-hosted infrastructure needed)
yarn install
cp .env.example .env
# Edit .env: set DATABASE_ENGINE=postgresql, DATABASE_URL, INTERACTION_MODE=api, MINT_URLS, etc.
yarn db:setup # copies schema, runs prisma generate + db push
yarn build
yarn start:prodyarn install
cp .env.example .env
# Edit .env: set DATABASE_ENGINE=sqlite, INTERACTION_MODE=cli, MINT_URLS, etc.
# DATABASE_FILE_PATH defaults to ~/.ippon/database.sqlite
yarn db:setup # creates ~/.ippon/, copies schema, runs prisma generate + db push
yarn build
yarn start:local # db:setup + CLI start combinedSwitching engines: re-run
yarn db:setupafter changingDATABASE_ENGINE. This regenerates the Prisma client for the new engine.
# Development (auto-reload, reads INTERACTION_MODE from .env)
yarn start:dev
# Production build
yarn build
# Production start (API or CLI depending on .env)
yarn start:prod
# One-shot local CLI (sets up SQLite and starts the REPL)
yarn start:localThe test suite uses Vitest and covers three layers:
| File | Scope |
|---|---|
src/__tests__/nostrService.test.ts |
Unit — pubkey normalisation (npub / x-only hex / compressed hex) |
src/__tests__/walletService.test.ts |
Unit — WalletService methods with Prisma and cashu-ts mocked |
src/__tests__/publicRoutes.test.ts |
Integration — unauthenticated routes (GET /v1/info, POST /v1/wallet) |
src/__tests__/protectedRoutes.test.ts |
Integration — all authenticated routes via Fastify inject() |
# Run once
yarn test
# Watch mode
yarn test:watchAll external I/O (Prisma, cashu-ts Wallet, getEncodedTokenV4, fetch) is mocked; no database or mint connection is required.
| Variable | Description | Default |
|---|---|---|
DATABASE_ENGINE |
Database backend: postgresql or sqlite |
postgresql |
DATABASE_URL |
PostgreSQL connection string (required when DATABASE_ENGINE=postgresql) |
— |
DATABASE_FILE_PATH |
SQLite file path, supports ~ expansion (used when DATABASE_ENGINE=sqlite) |
~/.ippon/database.sqlite |
INTERACTION_MODE |
Runtime mode: api (HTTP server) or cli (stdio REPL) |
api |
PORT |
HTTP server port (API mode only) | 3001 |
LOG_LEVEL |
Log verbosity (trace, debug, info, warn, error) |
debug |
MINT_URLS |
Comma-separated list of supported Cashu mint URLs. First entry is the default. | — |
UNIT |
Wallet unit (sat or msat) |
sat |
MAX_BALANCE |
Global max wallet balance (in unit) | 100000 |
MAX_SEND |
Global max ecash send amount | 50000 |
MAX_PAY |
Global max Lightning payment amount | 50000 |
SERVICE_STATUS |
Status string returned by /v1/info (API mode) |
operational |
SERVICE_HELP |
Help URL returned by /v1/info (API mode) |
— |
SERVICE_TERMS |
Terms URL returned by /v1/info (API mode) |
— |
RATE_LIMIT_MAX |
Default max requests per window (API mode) | 100 |
RATE_LIMIT_CREATE_WALLET_MAX |
Max wallet creations per window per IP (API mode) | 3 |
RATE_LIMIT_WINDOW |
Rate-limit time window (API mode) | 1 minute |
All configured values are printed to stderr at startup regardless of mode, making it easy to verify the active configuration.
The companion minibits_ippon_mcp project provides an MCP server that wraps the wallet API for AI agent integration. It manages session lifecycle and safeguards the wallet access key so that agents never handle it directly.
- lock token to pubkey
- pay cashu payment request
- add transactions model and API