Skip to main content

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

AspectL402 (access_l402_resource)pay_invoice
DirectionYou RECEIVE paymentsYou SPEND payments
Risk LevelLow (inbound funds)High (outbound funds)
Use CaseMonetize your APIsPay for external APIs
Safeguards NeededNone (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

  1. 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
  2. Malicious Prompt Injection

    • Attacker injects payment request into AI context
    • Phishing-style invoices embedded in content AI reads
    • Social engineering via prompts
  3. 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
  4. 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 Wallet Options

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
}
}
Default Thresholds

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)TierBehavior
≤ $1.00Auto-ApprovePayment proceeds without any notification
$1.00 - $5.00Log & ApprovePayment proceeds, logged for your review
$5.00 - $25.00Form ConfirmRequires confirmation (see below)
$25.00 - $100.00URL ConfirmRequires explicit confirmation with amount verification
> $500.00DenyPayment 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:

  1. The agent calls pay_invoice (or access_l402_resource / pay_l402_challenge) for an amount above the auto-approve threshold.
  2. The tool returns a response telling the agent that confirmation is required — but without the code.
  3. The server prints the confirmation code to its console/stderr, where you (the human) can read it.
  4. You give the code to the AI, which re-calls the original tool with its confirmation-nonce parameter (confirmationNonce in .NET, confirmation_nonce in Python) to proceed. The separate confirm_payment tool 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.

Threat-model assumption

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

  1. File-Based Configuration - The config lives in your home directory, not in environment variables that could be exposed
  2. Tighten-Only Runtime Changes - The configure_budget tool can only lower the per-request / per-session caps at runtime; an agent can never raise them above the limits in your config file
  3. 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
  4. First Payment Protection (opt-in) - When requireApprovalForFirstPayment is enabled (it is false by default, as shown above), the first payment of each session requires explicit confirmation
  5. 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
}
}
For Maximum Control

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):

VariablePurposeRecommended Value
STRIKE_API_KEYAPI authenticationAPI key from dedicated Strike account

OpenNode (alternative, direct payments only — no L402):

VariablePurposeRecommended Value
OPENNODE_API_KEYAPI authenticationWithdrawal-only key from dedicated account
OPENNODE_ENVIRONMENTNetwork selectionproduction for mainnet

NWC (for L402 auto-pay):

VariablePurposeRecommended Value
NWC_CONNECTION_STRINGWallet connectionConnection string from CoinOS, CLINK, or Alby Hub

LND (for L402 auto-pay, self-hosted):

VariablePurposeRecommended Value
LND_REST_HOSTLND REST API hostlocalhost:8080
LND_MACAROON_HEXAdmin macaroonHex-encoded admin macaroon

Sats-based limits (optional, legacy — prefer config file):

VariablePurposeRecommended Value
L402_MAX_SATS_PER_REQUESTPer-payment cap100-500 sats
L402_MAX_SATS_PER_SESSIONSession total cap1,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:

  1. Immediately rotate your wallet API key (Strike, OpenNode, etc.)
  2. Check payment history in your wallet provider's dashboard
  3. Review what invoices were paid
  4. Update MCP configuration with new key
  5. Lower budget limits
  6. Investigate how the key may have been compromised

Security Recommendations Summary

PriorityAction
CriticalUse dedicated wallet with limited funds
CriticalConfigure budget limits BEFORE first use
HighUse withdrawal-only API key
HighReview payment history after sessions
MediumRotate API keys monthly
MediumSet up balance alerts
LowLog and audit all AI sessions