Falconer is a sophisticated Bitcoin-native AI agent designed to autonomously earn satoshis while maintaining strict security controls through air-gapped PSBT signing and policy enforcement. The project integrates Bitcoin Core, Lightning Network (LNbits), and AI decision-making via Ollama.
Status: Alpha (v0.2.0) - Experimental software with significant architectural design but several critical configuration issues discovered.
Falconer is an autonomous Bitcoin earning system that:
- Uses AI (Ollama) to make earning decisions based on market conditions
- Implements various Bitcoin earning strategies (fee intelligence, mempool monitoring, market analysis)
- Maintains security through policy-enforced transaction limits
- Integrates with n8n for human-in-the-loop funding approval workflow
- Provides air-gapped PSBT creation for transaction signing
- Security First: No hot wallets or direct key management
- Autonomous Earning: AI-driven decision making within policy constraints
- Human Oversight: Policy engine ensures all actions stay within budgets and rules
- Bitcoin Native: Direct Bitcoin Core RPC integration with PSBT workflows
src/falconer/
├── ai/ # AI decision-making and earning strategies
│ ├── agent.py # Main autonomous AI agent orchestrator
│ ├── decision_engine.py # Decision-making logic based on market context
│ ├── market_analyzer.py # Real-time Bitcoin market analysis
│ └── earning_strategies.py # Pluggable earning strategy implementations
│
├── adapters/ # External service integrations
│ ├── bitcoind.py # Bitcoin Core RPC adapter
│ ├── electrs.py # Electrs REST API integration
│ ├── lnbits.py # Lightning Network wallet integration
│ └── mempool.py # Mempool.space API with Tor support
│
├── funding/ # Autonomous funding proposal system
│ ├── manager.py # Funding proposal lifecycle management
│ ├── n8n_adapter.py # n8n webhook integration
│ ├── webhook_server.py # FastAPI webhook receiver for approvals
│ └── schema.py # Pydantic models for funding data
│
├── policy/ # Policy enforcement engine
│ ├── engine.py # Policy validation and transaction blocking
│ └── schema.py # Policy and transaction request models
│
├── wallet/ # Bitcoin transaction management
│ └── psbt.py # PSBT creation, signing, and broadcast
│
├── tasks/ # Background tasks
│ └── fee_brief.py # Fee intelligence report generation
│
├── config.py # Configuration management
├── cli.py # Command-line interface
├── logging.py # Structured logging setup
├── persistence.py # File-based data persistence
├── validation.py # Address validation
└── exceptions.py # Custom exception definitions
- Autonomous Cycle Loop: Runs every 5 minutes analyzing market conditions
- Decision Making: Uses Ollama to make earning decisions (create_service, adjust_pricing, wait, analyze_market)
- Balance Monitoring: Tracks wallet balance and triggers funding proposals when below threshold
- State Management: Tracks active strategies, risk levels, and decision history
- Learning: Maintains decision history and adjusts risk based on success rates
Key Classes:
AIAgent: Main autonomous agentAIAgentState: Current state tracking
- Scores available strategies based on multiple factors
- Makes context-aware decisions based on market opportunity score
- Handles high (>0.7), medium (0.4-0.7), and low (<0.4) opportunity scenarios
- Tracks decision statistics and history
- Analyzes Bitcoin Core blockchain info, mempool status, and fee estimates
- Calculates opportunity scores and confidence levels
- Identifies earning opportunities (fee_intelligence, transaction_optimization, market_analysis)
- Maintains historical data for trend analysis (last 100 records)
- Supports both async and sync API calls
Implements 5 core earning strategies:
- Fee Intelligence (1000 sats base): Generate market fee analysis reports
- Mempool Monitoring (500 sats base): Real-time congestion monitoring
- Transaction Optimization (800 sats base): Fee optimization advice
- Market Analysis (1500 sats base): Trend predictions and insights
- Lightning Services (200 sats base): Lightning Network routing and payments
Dynamic pricing adjusts based on:
- Historical success rates (±20% adjustment)
- Scarcity (time since last use)
- Market demand
- Generates funding proposals when balance drops below threshold
- Calculates expected ROI based on active strategies
- Assesses risk levels from market data
- Manages proposal status transitions (pending → approved → executed)
- Automatically expires old pending proposals (configurable)
- Sends proposals to n8n webhooks for human review/approval
- Implements HMAC-SHA256 signature verification for webhook authenticity
- Formats proposals with human-readable descriptions
- Supports optional bearer token authentication
- Enforces 5-minute webhook timestamp window
- Validates transactions against policy rules before execution
- Enforces:
- Single transaction size limits
- Daily aggregate spending caps
- Destination address whitelisting
- Fee rate limits
- Persistent daily spending tracking
- Logs all policy violations for audit
- Creates PSBT (Partially Signed Bitcoin Transaction) proposals
- Implements UTXO selection (greedy by amount)
- Estimates transaction fees and sizes
- Handles change address generation
- Provides finalization and broadcasting of signed PSBTs
- All transactions require air-gapped signing
bitcoind.py: Bitcoin Core RPC adapter
- JSON-RPC calls with error handling
- Methods: getblockchaininfo, getmempoolinfo, estimatesmartfee, gettransaction, listunspent, etc.
- Persistent HTTP client connection
electrs.py: Electrs REST API
- Address information and UTXO queries
- Transaction lookup and status
- Block information retrieval
- Public key infrastructure support
lnbits.py: Lightning Network wallet
- Invoice creation and payment
- Wallet balance queries
- Payment history and status
- LNURL integration
mempool.py: Mempool space API
- LAN endpoint first (for self-hosted mempool)
- Automatic fallback to Tor endpoint
- Supports SOCKS5 proxy for anonymity
- Configurable via environment variables
-
RPC Interface (bitcoind.py)
- HTTP-based JSON-RPC calls to Bitcoin Core/Knots
- Configurable username/password authentication
- Persistent connection for performance
-
Market Data Sources
- blockchain info (block height, chain state)
- mempool info (size, usage, transaction count)
- fee estimation (estimatesmartfee for multiple confirmation targets)
- raw mempool transactions (for analysis)
-
Transaction Creation
- UTXO enumeration via listunspent
- Raw transaction creation via createrawtransaction
- PSBT creation via walletcreatefundedpsbt
- Transaction broadcasting via sendrawtransaction
Bitcoin Core RPC
↓
BitcoinAdapter (HTTP+JSON-RPC)
↓
├── Market Analyzer (fee/mempool data)
├── PSBT Manager (UTXO selection, TX creation)
├── Fee Brief Task (fee intelligence)
└── Earning Strategies (service generation)
↓
Decision Engine → AI Agent (Ollama) → Action Execution
↓
LNbits (Payments) / Bitcoin TX (Broadcast)
- Request Creation: Application requests transaction with destination, amount, optional fee rate
- Policy Validation: PolicyEngine validates against daily/single-TX limits
- PSBT Generation: PSBTManager creates funded PSBT from available UTXOs
- Air-Gapped Signing: PSBT exported for offline signing on secure device
- Finalization: Once signed, PSBT finalized to raw transaction
- Broadcasting: Raw transaction sent to Bitcoin network via RPC
- No Hot Signing: All private key operations offline
- PSBT Format: Industry-standard Partially Signed Bitcoin Transaction format
- Multi-Sig Ready: Policy engine supports multi-signature validation
- Hardware Wallet Compatible: Works with Ledger, Trezor, etc.
Environment variable based configuration (Pydantic BaseSettings)
Bitcoin Core Settings:
- BITCOIND_SCHEME: http/https
- BITCOIND_HOST_LOCAL: 127.0.0.1
- BITCOIND_HOST_IP: IP or hostname
- BITCOIND_PORT: 8332
- BITCOIND_RPC_USER: RPC username
- BITCOIND_RPC_PASS: RPC password
Electrs Settings:
- ELECTRS_SCHEME, HOST_LOCAL, HOST_IP, PORT
LNbits Settings:
- LNBITS_SCHEME, HOST_LOCAL, HOST_IP, PORT
- LNBITS_API_KEY: Admin key for wallet
- LNBITS_WALLET_ID: Specific wallet ID
Mempool Settings:
- MEMPOOL_MODE: auto|lan|tor
- MEMPOOL_LAN_SCHEME, MEMPOOL_LAN_HOST_LOCAL, MEMPOOL_LAN_PORT
- MEMPOOL_TOR_URL: Onion address
- TOR_SOCKS_URL: SOCKS5 proxy for Tor
Policy Settings:
- POLICY_PATH: Path to policy JSON file
- MAX_DAILY_SPEND_SATS: Daily limit
- MAX_SINGLE_TX_SATS: Single transaction limit
- ALLOWED_DESTINATIONS: CSV of whitelisted addresses
Wallet Settings:
- CHANGE_ADDRESS: Optional specific change address
Logging:
- LOG_LEVEL: DEBUG|INFO|WARNING|ERROR|CRITICAL
- LOG_FILE: Optional log file path
Located in policy/ directory:
dev.policy.json: Development policy (loose limits)staging.policy.json: Staging policyprod.policy.json: Production policy (strict limits)
Example production policy:
{
"version": "1.0",
"budgets": {
"daily_sats_cap": 100000,
"weekly_sats_cap": 500000,
"per_counterparty_caps": {}
},
"actions": {
"allowed_categories": [
"invoice.create",
"pay.invoice",
"http.get.whitelist"
],
"allowlist_domains": [
"lvk6m7wpccq7u7wzb6tkrsolzslfdfile74xuks33ofczahnejaeyoqd.local",
"lvk6m7wpccq7u7wzb6tkrsolzslfdfile74xuks33ofczahnejaeyoqd.onion"
],
"denylist_domains": []
},
"psbt_rules": {
"max_fee_sat_per_vb": 25,
"no_address_reuse": true,
"min_change_value_sats": 15000,
"consolidate_when_feerate_below": 3
},
"splits": {
"ops_percent": 40,
"reserve_psbt_proposal_percent": 60
}
}Config: Main Pydantic BaseSettings class- Single instance loads from .env file
- Validates configuration on instantiation
- Properties auto-compute URLs (bitcoind_url, electrs_url, lnbits_url)
- Field validators ensure consistency (e.g., single_tx < daily_tx)
Severity: CRITICAL
Status: BLOCKING - Code cannot run properly
The following configuration parameters are referenced in code but NOT defined in config.py:
Funding Proposal Parameters:
funding_proposal_enabled- Referenced in ai/agent.py:66, 152funding_proposal_threshold_sats- Referenced in funding/manager.py:26funding_proposal_max_pending- Referenced in ai/agent.py:157, funding/manager.py:33funding_proposal_default_amount_sats- Referenced in funding/manager.py:43funding_proposal_expiry_hours- Referenced in ai/agent.py:408
n8n Webhook Parameters:
n8n_webhook_url- Referenced in funding/n8n_adapter.py:23n8n_webhook_auth_token- Referenced in funding/n8n_adapter.py:24n8n_webhook_secret- Referenced in funding/n8n_adapter.py:25n8n_webhook_timeout_seconds- Referenced in funding/n8n_adapter.py:26
AI/Ollama Parameters:
ollama_model- Used with getattr fallback in ai/agent.py:53 (WORKS but not configurable)ollama_host- Used with getattr fallback in ai/agent.py:54 (WORKS but not configurable)
Impact:
- Funding proposal system will crash at runtime with AttributeError
- n8n integration will crash at runtime with AttributeError
- Ollama integration falls back to hardcoded defaults (works but not user-configurable)
Fix Required: Add missing fields to config.py with appropriate defaults and environment variable bindings.
Severity: HIGH
Status: FUNCTIONAL but not configurable via environment
The Ollama integration uses hardcoded defaults with getattr fallback:
self.ollama_model = getattr(config, 'ollama_model', 'llama3.1:8b')
self.ollama_host = getattr(config, 'ollama_host', 'http://localhost:11434')While this works, it's not user-configurable and doesn't follow project's configuration pattern.
Severity: MEDIUM
Status: POTENTIAL RUNTIME ISSUES
Observed Problems:
-
Blocking Operations in Async Code (market_analyzer.py)
self.bitcoin_adapter.get_blockchain_info()- SYNC call in async contextself.bitcoin_adapter.estimate_smart_fee()- SYNC call in async context- Should use
asyncio.to_thread()for thread pool execution
-
Example (market_analyzer.py:119-130):
# IN ASYNC FUNCTION blockchain_info = self.bitcoin_adapter.get_blockchain_info() # ❌ BLOCKING SYNC CALL mempool_info = self.bitcoin_adapter.get_mempool_info() # ❌ BLOCKING SYNC CALL
-
Correct Pattern Exists (but not used everywhere):
# IN agent.py:131 - CORRECT balance_info = await asyncio.to_thread(self.lnbits_adapter.get_wallet_balance)
Impact:
- Market analysis will block the event loop
- Decision cycles may not run on schedule
- Potential deadlocks under high load
Severity: MEDIUM
Status: PARTIAL COVERAGE
Missing Exception Safety:
-
HTTP Adapter Error Recovery (all adapters)
- BitcoinAdapter, ElectrsAdapter, LNbitsAdapter all use generic exception handlers
- No retry logic on transient failures
- No circuit breaker pattern for cascading failures
-
PSBT Manager (wallet/psbt.py)
_select_utxos()uses greedy algorithm with hardcoded 10k sats buffer- May select insufficient UTXOs under high fee conditions
- No fallback to smaller outputs or dust consolidation
-
Market Analyzer (ai/market_analyzer.py)
- Returns default neutral values on errors (opportunity_score=0.5)
- Hides actual network/connectivity issues
- Could make poor decisions based on stale data
Severity: LOW
Status: MINOR CODE QUALITY
In policy/engine.py:101, there's an inconsistency:
# Line 51: Uses model.dump() for violations
self.persistence.save_policy_violation(violation.model_dump())
# Line 101: Uses dict() for warnings
self.persistence.save_policy_violation(violation.dict()) # ❌ DEPRECATED METHODThe .dict() method is deprecated in Pydantic v2, should use .model_dump() consistently.
Severity: MEDIUM
Status: RESOURCE LEAK POTENTIAL
The AIAgent initialization (ai/agent.py:46-72) creates multiple adapters but doesn't clean them up if initialization fails:
def __init__(self, config: Config):
# ... initializations ...
self.decision_engine = DecisionEngine(config) # ✓
self.market_analyzer = MarketAnalyzer(config) # Creates 3 adapters
self.strategy_manager = EarningStrategyManager(config) # Creates 3 more adapters
if FundingProposalManager and N8nAdapter and config.funding_proposal_enabled:
# If this fails, earlier created adapters are not cleaned up
self.proposal_manager = FundingProposalManager(...)Risk: If funding module initialization fails, zombie HTTP connections remain open.
Severity: LOW
Status: DESIGN ISSUE
MarketAnalyzer keeps in-memory history (last 100 records) that's lost on restart:
self.fee_history: List[Dict[str, Any]] = [] # Lost on restart
self.mempool_history: List[Dict[str, Any]] = [] # Lost on restartThis data should be persisted to PersistenceManager for trend analysis across restarts.
Severity: MEDIUM
Status: FUNCTIONAL BUT RISKY
The JSON extraction from Ollama (ai/agent.py:273-307) uses string manipulation:
json_start = response.find('{') # ❌ Fragile
json_end = response.rfind('}') + 1 # ❌ Fragile
json_str = response[json_start:json_end]If Ollama response contains JSON in explanation text, this breaks. Should use proper JSON decoder with fallback.
Severity: MEDIUM
Status: CONFIGURATION ISSUE
LNbits adapter stores API key in plain HTTP header:
self.client = httpx.Client(
base_url=self.base_url,
headers={"X-Api-Key": self.api_key}, # In HTTP if scheme is http://
timeout=30.0,
)Risk: If using HTTP instead of HTTPS, API keys are transmitted unencrypted.
Mitigation: Enforce HTTPS in production, document this requirement clearly.
Severity: MEDIUM
Status: FEATURE INCOMPLETE
The Mempool adapter supports Tor and SOCKS proxies but:
- No timeout configuration for Tor (uses default 20-30 seconds)
- No retry logic for onion network failures
- No fallback if both LAN and Tor fail gracefully
-
Modular Architecture
- Clear separation of concerns (adapters, AI, policy, wallet)
- Easy to swap implementations (e.g., replace BitcoinAdapter)
- Pluggable earning strategies
-
Security-Focused
- PSBT workflow prevents private key exposure
- Policy engine gates all spending
- Multi-layer validation (policy → PSBT → broadcast)
-
Async-Ready
- Async market analysis and decision loops
- Background task scheduling (5-minute cycles)
- FastAPI webhook server for integrations
-
Well-Documented
- README with architecture overview
- API documentation exists
- Inline logging throughout
-
Testing Infrastructure
- pytest with coverage reporting
- Integration tests
- Mock adapters for testing
-
Configuration Completion
- Add funding proposal parameters to config.py
- Add n8n webhook parameters to config.py
- Add Ollama parameters to config.py
- Environment variable support for all
-
Async/Sync Consistency
- Wrap sync Bitcoin adapter calls with asyncio.to_thread()
- Create async wrappers for blocking operations
- Use asyncio properly throughout
-
Error Recovery
- Implement retry logic with exponential backoff
- Add circuit breaker pattern for failing services
- Implement fallback strategies
-
State Persistence
- Persist market analyzer history
- Save decision history across restarts
- Load previous state on startup
-
Monitoring & Observability
- Add prometheus metrics
- Export decision/execution logs
- Create dashboard for real-time monitoring
-
Testing
- Add more unit tests for core modules
- Integration tests for end-to-end flows
- Load testing for autonomous cycles
✓ Air-gapped PSBT signing prevents hot wallet risk ✓ Policy engine enforces budget limits ✓ HMAC-SHA256 webhook signature verification ✓ Structured logging for audit trails
✗ No rate limiting on webhook endpoints ✗ HTTP could expose LNbits API keys ✗ No encryption for persisted proposal data ✗ No secret rotation mechanism for webhooks
- Enforce HTTPS for all external communications
- Implement API key rotation for LNbits
- Encrypt sensitive data in persistence layer
- Add rate limiting to webhook server
- Implement request signing for webhook verification
- Add audit logging for all approvals/rejections
- Greedy UTXO selection may be inefficient
- Bitcoin adapter connections persist (good) but not pooled
- In-memory history limited to last 100 records
- Ollama responses not cached (each decision queries model)
- Implement UTXO consolidation strategy
- Add connection pooling for HTTP clients
- Cache common Ollama responses
- Use local fee estimate caching
- Implement lazy loading for adapters
| Severity | Issue | Component | Status |
|---|---|---|---|
| CRITICAL | Missing funding/n8n config params | config.py, funding/*, ai/agent.py | Blocking |
| HIGH | Incomplete Ollama config | ai/agent.py | Functional |
| MEDIUM | Async/sync blocking calls | market_analyzer.py, strategies.py | Potential |
| MEDIUM | Missing exception handling | all adapters | Partial |
| MEDIUM | Resource leak on init failure | ai/agent.py | Potential |
| MEDIUM | Mempool Tor not production tested | adapters/mempool.py | Feature |
| MEDIUM | LNbits API key HTTP exposure | adapters/lnbits.py | Configuration |
| LOW | Pydantic dict() vs model_dump() | policy/engine.py | Code Quality |
| LOW | Market analyzer state loss | ai/market_analyzer.py | Design |
| LOW | Fragile JSON parsing | ai/agent.py | Functional |
- Add missing configuration parameters to config.py
- Update .env.example with funding/n8n parameters
- Fix async/sync mixing in market_analyzer.py
- Test funding proposal system end-to-end
- Implement retry logic for adapters
- Add proper exception hierarchy
- Persist market analyzer state
- Create comprehensive integration tests
- Add monitoring and observability
- Implement state persistence across restarts
- Security audit and hardening
- Load and stress testing
- Performance optimization (caching, pooling)
- Advanced strategy implementations
- Multi-wallet support
- Enterprise deployment capabilities