AI Spending Security
When using the pay_invoice tool, you are authorizing an AI agent to spend Bitcoin on your behalf. This document covers the security considerations, mandatory safeguards, and best practices for safe AI-driven spending.
Understanding pay_invoice vs L402
| Aspect | L402 (access_l402_resource) | pay_invoice |
|---|---|---|
| Direction | You RECEIVE payments | You SPEND payments |
| Risk Level | Low (inbound funds) | High (outbound funds) |
| Use Case | Monetize your APIs | Pay for external APIs |
| Safeguards Needed | None (you're receiving) | Budget limits required |
Key Distinction: L402 is about receiving payments for your content/APIs. The pay_invoice tool is about spending your Bitcoin to pay for external services.
Risk Model
What Can Go Wrong
-
Unintended Payments
- AI misinterprets user intent and pays wrong invoice
- AI pays more than expected for a service
- AI makes multiple payments when one was intended
-
Malicious Prompt Injection
- Attacker injects payment request into AI context
- Phishing-style invoices embedded in content AI reads
- Social engineering via prompts
-
API Key Compromise
- Wallet API key (Strike, OpenNode, etc.) with withdrawal permissions stolen
- Key exposed in logs, environment dumps, or screenshots
- Key committed to version control
-
Budget Bypass Attempts
- Attackers try to make payments just under limit
- Multiple small payments to drain session budget
- Exploiting timing between budget check and payment
Mandatory Safeguards
1. Dedicated Spending Wallet
NEVER use your main wallet or business funds.
Create a dedicated wallet specifically for AI spending:
Main Wallet (Your Business)
├── Receives customer payments
├── Large balance
└── NEVER used for AI spending
AI Spending Wallet (Separate)
├── Only used by AI agents
├── Small balance (< $100 equivalent)
└── Easy to monitor and refill
Supported wallets:
- LND - Full L402 support (always returns preimage)
- NWC (CoinOS, CLINK) - Full L402 support (returns preimage)
- Strike - Full L402 support (returns preimage via
lightning.preImage) - NWC (Alby) - ✅ Works
- OpenNode - Direct payments only (no L402, no preimage)
- NWC (Primal) - Direct payments only (no preimage support yet)
L402 auto-pay requires preimage. These wallets work:
- LND - Guaranteed L402, self-hosted
- CoinOS (NWC) - Free, web-based
- CLINK (NWC) - Nostr-native
- Alby Hub (NWC) - Self-custody, returns preimage
- Strike - Easy API key setup, returns preimage
OpenNode and Primal do NOT work for L402 - they don't return preimages.
Why: If something goes wrong, you only lose what's in the dedicated account.
2. Multi-Tier Approval System (AI-Proof)
Lightning Enable uses a USD-based multi-tier approval system that AI agents cannot bypass or modify. Configuration is stored in a file that only you can edit.
Configuration File Location
~/.lightning-enable/config.json
On Windows: C:\Users\YourName\.lightning-enable\config.json
Important: The MCP server exposes no tool to modify this file — the USD approval tiers here can only be changed by you editing it directly. (An agent can call configure_budget to tighten the separate runtime sats caps — lower them, never raise them — but it can never loosen any limit or touch this file. See below.) Caveat: an agent that has direct shell or filesystem access to the host could edit the file outside the server — see the threat-model note below.
Default Configuration
On first run, a default config file is created:
{
"currency": "USD",
"tiers": {
"autoApprove": 1.00,
"logAndApprove": 5.00,
"formConfirm": 25.00,
"urlConfirm": 100.00
},
"limits": {
"maxPerPayment": 500.00,
"maxPerSession": 100.00
},
"session": {
"cooldownSeconds": 2,
"requireApprovalForFirstPayment": false
}
}
Payments above the auto-approve threshold use out-of-band confirmation: the server prints a code to its console for the human operator to relay (see below). The defaults provide a sensible balance between UX and safety; tighten them in your config if you want more payments to require confirmation.
How Each Tier Works
| Amount (USD) | Tier | Behavior |
|---|---|---|
| ≤ $1.00 | Auto-Approve | Payment proceeds without any notification |
| $1.00 - $5.00 | Log & Approve | Payment proceeds, logged for your review |
| $5.00 - $25.00 | Form Confirm | Requires confirmation (see below) |
| $25.00 - $100.00 | URL Confirm | Requires explicit confirmation with amount verification |
| > $500.00 | Deny | Payment blocked entirely |
Out-of-Band Confirmation (.NET v1.12.10+, Python v1.12.12+)
When a payment exceeds the auto-approve threshold, the server prints a confirmation code to its console / stderr — the channel the human operator sees, not the AI. The code is never returned in a tool result, so a prompt-injected agent can't read its own code and self-approve.
How confirmation works:
- The agent calls
pay_invoice(oraccess_l402_resource/pay_l402_challenge) for an amount above the auto-approve threshold. - The tool returns a response telling the agent that confirmation is required — but without the code.
- The server prints the confirmation code to its console/stderr, where you (the human) can read it.
- You give the code to the AI, which re-calls the original tool with its confirmation-nonce parameter (
confirmationNoncein .NET,confirmation_noncein Python) to proceed. The separateconfirm_paymenttool can verify a code (it echoes the amount/tool it authorizes) but does not execute the payment — the original tool does.
The confirmation code is bound to the exact amount AND the exact tool it approved — it can't be reused for a different amount or a different tool.
send_onchain always requires this confirmation, even for small amounts, because on-chain payments are irreversible. It also fails closed if the budget service is unavailable.
This ensures you keep control over payments above your auto-approve threshold — the AI can't approve its own payment, because the approving code appears only on the server's console, which the human reads.
Out-of-band confirmation rests on one assumption: the AI runtime cannot read the MCP server's console / stderr or centralized logs, and cannot run shell commands to read them. That holds for the common setup (Claude Desktop / IDE launches the server as a subprocess whose stderr the model never sees). It does not hold if the agent shares a shell or host with the server and can cat the logs or read the process's stderr — there it could read its own confirmation code, and could also edit ~/.lightning-enable/config.json directly. For agents with local shell/filesystem access, run the MCP server somewhere the agent can't read its stderr or files (a separate user/host/container).
Why This Is AI-Proof
- File-Based Configuration - The config lives in your home directory, not in environment variables that could be exposed
- Tighten-Only Runtime Changes - The
configure_budgettool can only lower the per-request / per-session caps at runtime; an agent can never raise them above the limits in your config file - Out-of-Band Confirmation - For payments above the auto-approve threshold, the confirmation code is printed to the server console (where the human sees it) and is never returned to the AI, so the AI can't approve its own payments
- First Payment Protection (opt-in) - When
requireApprovalForFirstPaymentis enabled (it isfalseby default, as shown above), the first payment of each session requires explicit confirmation - Cooldown Periods - Prevents rapid-fire payment attacks
Customizing Your Limits
Edit ~/.lightning-enable/config.json:
{
"currency": "USD",
"tiers": {
"autoApprove": 0.50, // More restrictive: only auto-approve $0.50
"logAndApprove": 2.00, // Log anything above $2.00
"formConfirm": 10.00, // Require confirmation above $10
"urlConfirm": 50.00 // Require amount verification above $50
},
"limits": {
"maxPerPayment": 100.00, // Never pay more than $100 in one payment
"maxPerSession": 50.00 // Never spend more than $50 per session
},
"session": {
"cooldownSeconds": 2,
"requireApprovalForFirstPayment": false
}
}
If you want to require confirmation for ALL payments, set autoApprove to 0. Every payment will then require out-of-band confirmation — the server prints a code to its console and you relay it to the AI to approve each one.
Checking Your Budget Status
Use the get_budget_status tool (read-only) to see current configuration:
get_budget_status
Response:
{
"success": true,
"message": "Budget configuration is READ-ONLY. Edit ~/.lightning-enable/config.json to change limits.",
"tiers": {
"autoApproveUsd": 0.10,
"formConfirmUsd": 10.00,
...
},
"session": {
"spentUsd": 0.45,
"remainingUsd": 99.55
},
"security": {
"aiCanModify": false,
"howToChange": "Edit the config.json file directly. AI agents cannot modify budget limits."
}
}
3. Legacy Environment Variable Limits
For backward compatibility, environment variables still work but config file takes precedence:
# Legacy environment variables (config.json overrides these)
L402_MAX_SATS_PER_REQUEST=100 # Max per single payment
L402_MAX_SATS_PER_SESSION=1000 # Max total per session
Recommended: Use the config file instead - it's more secure and supports USD amounts.
3. API Key Permissions
When creating your wallet API key (Strike, OpenNode, etc.) for AI spending:
- Use withdrawal-only permissions if available
- Create a dedicated key - don't reuse keys
- Rotate keys regularly (monthly recommended)
- Never commit keys to version control
- Never share keys in screenshots or logs
# MCP Configuration - store key securely (use your wallet of choice)
export STRIKE_API_KEY="your-strike-key-here"
# or
export OPENNODE_API_KEY="your-opennode-key-here"
export OPENNODE_ENVIRONMENT="production"
4. Monitoring and Auditing
Review payments after every AI session:
# Use the MCP tool to check payment history
get_payment_history limit=20
Set up monitoring:
- Enable email notifications for withdrawals in your wallet provider (Strike, OpenNode, etc.)
- Review weekly spending totals
- Set up balance alerts (notify when balance drops below X)
- Check for unexpected payment patterns
Environment Variable Checklist
Before using pay_invoice, verify one of the following wallet options is configured:
Strike (recommended):
| Variable | Purpose | Recommended Value |
|---|---|---|
STRIKE_API_KEY | API authentication | API key from dedicated Strike account |
OpenNode (alternative, direct payments only — no L402):
| Variable | Purpose | Recommended Value |
|---|---|---|
OPENNODE_API_KEY | API authentication | Withdrawal-only key from dedicated account |
OPENNODE_ENVIRONMENT | Network selection | production for mainnet |
NWC (for L402 auto-pay):
| Variable | Purpose | Recommended Value |
|---|---|---|
NWC_CONNECTION_STRING | Wallet connection | Connection string from CoinOS, CLINK, or Alby Hub |
LND (for L402 auto-pay, self-hosted):
| Variable | Purpose | Recommended Value |
|---|---|---|
LND_REST_HOST | LND REST API host | localhost:8080 |
LND_MACAROON_HEX | Admin macaroon | Hex-encoded admin macaroon |
Sats-based limits (optional, legacy — prefer config file):
| Variable | Purpose | Recommended Value |
|---|---|---|
L402_MAX_SATS_PER_REQUEST | Per-payment cap | 100-500 sats |
L402_MAX_SATS_PER_SESSION | Session total cap | 1,000-5,000 sats |
What pay_invoice Should NOT Be Used For
Do NOT use pay_invoice if:
- Your wallet balance exceeds
100,000 sats ($100 at 100k sats/$) - You're using production/business funds
- The AI will run unattended for long periods
- You haven't configured budget limits
- You're using a shared or untrusted MCP configuration
- You haven't reviewed the AI agent's capabilities
Recommended for:
- Developer testing and prototyping
- Light interactive use with supervision
- Small, bounded research tasks
- Learning and experimentation
Architecture: We Never Touch Funds
Lightning Enable is API middleware — your payment provider facilitates custody and settlement:
Your Wallet (Strike, LND, NWC, etc.) Lightning Enable MCP Recipient
│ │ │
│ ◄────────── You control ──────────► │ ◄─── Software ───► │
│ │ │
▼ ▼ ▼
Provider custody Just a tool Receives
Your account API middleware payment
Your responsibility No access to funds
Key points:
- Lightning Enable does not hold funds — the payment provider facilitates custody and settlement
- You provide your own provider API key (BYOA model — Strike, LND, NWC, or OpenNode)
- Your payment provider holds your funds (they are licensed and regulated)
- We never see, touch, or control your Bitcoin
- All compliance/KYB/KYC is between you and your provider
Incident Response
If you suspect unauthorized payments:
- Immediately rotate your wallet API key (Strike, OpenNode, etc.)
- Check payment history in your wallet provider's dashboard
- Review what invoices were paid
- Update MCP configuration with new key
- Lower budget limits
- Investigate how the key may have been compromised
Security Recommendations Summary
| Priority | Action |
|---|---|
| Critical | Use dedicated wallet with limited funds |
| Critical | Configure budget limits BEFORE first use |
| High | Use withdrawal-only API key |
| High | Review payment history after sessions |
| Medium | Rotate API keys monthly |
| Medium | Set up balance alerts |
| Low | Log and audit all AI sessions |
Related Documentation
- Spending Guidelines - Recommended budgets by use case
- Legal Considerations - Liability and terms
- AI Agent Integration - MCP setup guide