Skip to content

PraneethGunas/aegis-wallet

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

139 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Aegis β€” The Agentic Bitcoin Wallet

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.


How It Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  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.


Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   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) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Budget Enforcement

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.

MCP Server (aegis-wallet)

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>]

Tools (7)

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.

Key Features

  • 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 (like lnget --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.

Tech Stack

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

Getting Started

Prerequisites

  • Node.js 22+ (nvm install 22)
  • Docker + Docker Compose

1. Start LND + litd

docker compose up -d

2. Fund the wallet and open a channel

docker exec litd lncli newaddress p2tr
# Send mainnet sats to this address, then:
docker exec litd lncli openchannel --node_key <peer_pubkey> --local_amt 20000

3. Backend

cd backend && npm install && npm run dev    # http://localhost:3001

4. Frontend

cd web && npm install && npm run dev        # http://localhost:3000

5. Create wallet + pair Claude

  1. Open http://localhost:3000 β†’ create wallet with passkey
  2. Set spending limit with slider β†’ "Generate credential"
  3. Copy the setup message β†’ paste into Claude Desktop

Project Structure

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

API Endpoints

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

Macaroon Permissions

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.


Security

  • 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.


License

MIT


References

About

Seedless Bitcoin wallet with AI agent spending via Lightning macaroons. Passkey-derived keys, Claude as financial agent via MCP.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors