A seedless Bitcoin wallet where Claude is your AI financial agent. Spending is enforced cryptographically by Lightning macaroons β not application code. No seed phrase. No 24 words. Your keys live in your device's secure enclave, derived from a passkey.
Built at MIT Bitcoin Hackathon 2026.
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β L1: FUNDING WALLET (Self-Custody) β
β Standard Taproot address on mainnet. β
β Passkey derives your key AND signs txs. β
β Server has ZERO access to funding wallet. β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β You move funds between layers (Face ID) β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β L2: SPENDING (Custodial Lightning) β
β LND + litd node. Claude operates here via MCP. β
β Scoped macaroon = budget ceiling. β
β One slider. One number. LND enforces it. β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Layer 1 (Funding) β A standard Taproot address (P2TR, BIP 86) on Bitcoin mainnet. Your passkey derives the private key AND signs all on-chain transactions in the browser. The key never leaves your device. If our server disappears, you still have your key.
Layer 2 (Spending) β An LND Lightning node wrapped by litd. Claude gets wallet tools via an MCP server with a scoped macaroon tied to a litd account. The macaroon has a hard spending ceiling enforced by LND's RPC middleware β not our code. Claude can pay invoices, fetch L402 APIs, and check balances. When the budget runs out, the invoice is forwarded to your dashboard where you can pay it directly.
Passkey β WebAuthn PRF extension derives keys from your device's secure enclave. No seed phrase is ever generated or stored. On L1, the passkey derives the key and signs. On L2, the passkey authenticates. Recovery = passkey syncs to your new device, wallet regenerates deterministically.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β USER'S BROWSER β
β β
β Secure Enclave βββ Passkey (PRF) βββ Key Derivation β
β funding_key: m/86h/0h/0h/0/0 (Taproot, L1 signer)β
β auth_key: m/84h/0h/0h/0/0 (L2 auth only) β
β On-chain transactions signed HERE. Keys NEVER sent. β
ββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β HTTPS / WSS
ββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββ
β BACKEND SERVER β
β Node.js + Express + WebSocket β
β βββ LND + litd β Lightning payments β
β βββ litd accounts β budget enforcement β
β βββ /agent/pay-direct β user pays when agent can't β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββ ββββββββββββββββββββββββ
β Claude β MCP β aegis-wallet MCP β
β (Desktop / Code / βββββββββΊβ ββ pay_invoice β
β Cowork) β stdio β ββ l402_fetch β
β β β ββ get_balance β
β Claude IS the agent. β β ββ decode_invoice β
β No custom bot code. β β ββ create_invoice β
β β β ββ list_payments β
β β β ββ get_spending_sum. β
β β β ββ macaroon (hidden) β
ββββββββββββββββββββββββ ββββββββββββββββββββββββ
User sets spending limit in UI ($2.50 β 2,500 sats)
β Backend creates litd account with that ceiling
β Bakes scoped macaroon: routerrpc + 5 URI permissions + account caveat
β MCP server receives macaroon via --macaroon CLI arg
Claude calls pay_invoice or l402_fetch via MCP
β MCP attaches scoped macaroon to LND gRPC call
β LND RPC middleware checks: account balance >= amount + fees?
YES β payment routed, balance deducted
NO β rejected at LND layer β MCP sends WebSocket to dashboard
β User sees "Budget exceeded" banner β taps "Pay directly"
β Backend pays with admin macaroon (user's direct payment)
Claude cannot see on-chain funds, node channels, or real balance.
Claude cannot bake new macaroons or escalate its own permissions.
The MCP server is a standalone npm package. Claude Desktop runs it as a subprocess.
npx aegis-wallet --macaroon <base64> [--api-url http://localhost:3001] [--user-id <credential>]| Tool | Description |
|---|---|
pay_invoice(bolt11, purpose, max_cost_sats?) |
Pay a Lightning invoice. Optional per-payment cost cap. |
l402_fetch(url, method?, headers?, body?, max_cost_sats?) |
Fetch URL with automatic L402 payment. Handles 402 β pay β retry in one call. Caches tokens per domain. |
get_balance() |
Check remaining spending balance (sats + USD). |
decode_invoice(bolt11) |
Inspect a BOLT11 invoice before paying. |
create_invoice(amount_sats, memo) |
Generate a Lightning invoice to receive payment. |
list_payments(limit) |
Recent payment history with amounts and fees. |
get_spending_summary() |
Total spent, payment count, remaining balance, cached L402 domains. |
l402_fetchβ One-call L402 flow inspired by lnget. Hit a URL, handle the 402 challenge, pay the invoice, cache the token, retry with auth header. No manual steps.max_cost_satsβ Per-payment safety cap (likelnget --max-cost). Refuses to pay if invoice exceeds it.- Token cache β L402 tokens cached per domain in memory. Avoids re-paying the same service.
- Budget escalation β When LND rejects a payment, the MCP notifies the user's dashboard via WebSocket. User can pay directly with one tap.
| Layer | Technology |
|---|---|
| Frontend | Next.js + Tailwind CSS |
| Backend | Node.js + Express + WebSocket |
| AI Agent | Claude (user's subscription) β no custom agent runtime |
| MCP Server | Node.js (@modelcontextprotocol/sdk) β standalone npm package |
| Lightning | LND + litd (Docker) |
| Passkey | @simplewebauthn/browser + PRF extension |
| Tx Signing | @scure/bip32 + @scure/bip39 + tiny-secp256k1 (in browser) |
| Network | Bitcoin mainnet |
- Node.js 22+ (
nvm install 22) - Docker + Docker Compose
docker compose up -ddocker exec litd lncli newaddress p2tr
# Send mainnet sats to this address, then:
docker exec litd lncli openchannel --node_key <peer_pubkey> --local_amt 20000cd backend && npm install && npm run dev # http://localhost:3001cd web && npm install && npm run dev # http://localhost:3000- Open http://localhost:3000 β create wallet with passkey
- Set spending limit with slider β "Generate credential"
- Copy the setup message β paste into Claude Desktop
aegis/
βββ mcp/ # Standalone MCP server (npm: aegis-wallet)
β βββ index.js # Entry point, CLI arg parsing
β βββ tools.js # 7 wallet tools + L402 token cache
β βββ lnd.js # ln-service gRPC client (SendPaymentSync)
β βββ auth.js # Rate limiting (30 calls/min)
βββ backend/
β βββ src/
β β βββ server.js # Express + WebSocket server
β β βββ routes/ # wallet, agent, ln endpoints
β β βββ services/ # lnd.js, litd.js, mempool.js
β β βββ ws/ # Real-time notifications
β β βββ db/ # SQLite schema + access
βββ web/
β βββ src/
β β βββ app/ # Next.js pages (landing, dashboard)
β β βββ lib/ # passkey.js, bitcoin.js, api.js, ws.js, store.js
β β βββ components/ # AgentSetup, ApprovalBanner, Balance, TxList
βββ CLAUDE.md # Claude Code instructions
βββ PROJECT_SPEC.md # Full technical specification
| Endpoint | Method | Description |
|---|---|---|
/wallet/create |
POST | Register passkey credential (wallet = identity) |
/wallet/balance |
GET | Combined L1 (mempool.space) + L2 (LND) balance |
/wallet/history |
GET | Unified tx history across both layers |
/agent/create |
POST | Create litd account + bake scoped macaroon |
/agent/budget |
PUT | Update spending limit (same macaroon, new ceiling) |
/agent/pay-direct |
POST | User pays a bolt11 directly (admin macaroon) |
/agent/pause |
POST | Freeze agent β macaroon stops working instantly |
/agent/revoke |
POST | Delete litd account β macaroon permanently invalid |
/ln/fund |
POST | Broadcast signed tx to fund Lightning |
/ln/open-channel |
POST | Open channel to default peer |
The agent's scoped macaroon grants exactly these gRPC methods:
| Granted | Purpose |
|---|---|
routerrpc.Router/SendPaymentV2 |
Pay invoices (streaming) |
routerrpc.Router/TrackPaymentV2 |
Track payment status |
lnrpc.Lightning/SendPaymentSync |
Pay invoices (legacy fallback) |
lnrpc.Lightning/DecodePayReq |
Decode BOLT11 invoices |
lnrpc.Lightning/ChannelBalance |
Check spending balance |
lnrpc.Lightning/ListPayments |
View payment history |
lnrpc.Lightning/GetInfo |
Node health check |
lnrpc.Lightning/AddInvoice |
Create invoices to receive |
Plus a litd account caveat (lnd-custom account <id>) that enforces the budget ceiling.
Denied: all on-chain operations, channel management, peer discovery, macaroon baking.
- L1 signing keys derived client-side via WebAuthn PRF β never sent to the server
- L2 macaroon is a scoped bearer token, not a signing key β LND holds Lightning keys
- Budget enforcement is cryptographic (LND RPC middleware), not application logic
- Agent cannot access L1 funds, see node topology, or escalate its own permissions
- Revoking an agent deletes the litd account β macaroon dies instantly
See SECURITY.md for the full security model.
- Lightning Agent Tools β lnget, macaroon-bakery, commerce skills
- L402 for Agents β Agent payment protocol
- litd Accounts β Virtual Lightning accounts
- LND Macaroons β Scoped authentication tokens
- MCP Protocol β Model Context Protocol
- WebAuthn PRF β Passkey-based key derivation
- 402index β Directory of L402-enabled APIs