-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Problem Statement
The current fmcd API exposes Fedimint's internal module structure (mint, lightning, onchain) through nested, module-based routing (/v2/ln/invoice, /v2/mint/spend, etc.). This requires users to understand Fedimint's architecture to use the API effectively.
In contrast, phoenixd (ACINQ's Lightning server) demonstrates that a minimalist, action-oriented API focusing on user intentions (pay, receive money) drives adoption and reduces support burden.
Proposed Solution
Redesign fmcd's API to adopt phoenixd's simplicity while maintaining Fedimint-specific capabilities:
- Reduce from 30+ to ~15 core endpoints
- Implement universal
/payendpoint that handles all payment types transparently - Remove versioning and module nesting from paths
- Add two-tier authentication (read-only vs payment operations)
- Standardize responses with user-friendly naming
Design Document
FMCD API Redesign: Learning from Phoenixd
Executive Summary
This document analyzes the API design differences between phoenixd (ACINQ's Lightning server) and fmcd (Fedimint client daemon), proposing a new API design that combines phoenixd's simplicity with fmcd's Fedimint-specific requirements. The key finding is that phoenixd succeeds through its minimalist, action-oriented API design that focuses on core payment operations without exposing internal complexity. FMCD can adopt this approach while maintaining its multi-federation capabilities.
Key Recommendations
- Simplify endpoint structure - Move from nested module-based routing to flat, action-oriented endpoints
- Unify payment operations - Single
/payendpoint that handles all payment types transparently - Streamline authentication - Adopt phoenixd's simple password-based auth with clear access levels
- Improve response consistency - Standardize response formats across all endpoints
- Hide complexity - Abstract away Fedimint internals from the public API
Detailed API Comparison
1. Overall API Philosophy
| Aspect | Phoenixd | FMCD (Current) | Analysis |
|---|---|---|---|
| Endpoint Structure | Flat, action-based (/payinvoice, /createinvoice) |
Nested, module-based (/v2/ln/invoice, /v2/mint/spend) |
Phoenixd's flat structure is more intuitive |
| API Versioning | No versioning in URLs | /v2/ prefix everywhere |
Phoenixd keeps it simple, versions via headers if needed |
| Operation Focus | User actions (pay, receive, check balance) | Technical modules (mint, ln, onchain) | Phoenixd focuses on what users want to do |
| Complexity Exposure | Hides Lightning complexity | Exposes Fedimint modules | Phoenixd provides better abstraction |
2. Authentication Comparison
| Aspect | Phoenixd | FMCD (Current) |
|---|---|---|
| Method | Basic Auth with two levels | Basic Auth with single password |
| Access Levels | full-access and limited-access |
All or nothing |
| Password Storage | In config file | In config file |
| WebSocket Auth | Via protocol headers | Custom WebSocket auth |
Phoenixd's Approach:
// Two distinct access levels
val fullAccessPassword: String
val limitedAccessPassword: String
// Different endpoints require different access
authenticate("full-access") {
post("payinvoice") { ... } // Requires full access
}
authenticate {
get("getinfo") { ... } // Works with limited access
}3. Core Payment Operations
Phoenixd's Unified Approach
Phoenixd provides multiple payment endpoints that all follow a similar pattern:
/payinvoice- Pay a Lightning invoice/payoffer- Pay a BOLT12 offer/paylnaddress- Pay a Lightning Address/lnurlpay- Pay via LNURL
Key Design Pattern: Each payment type has its own endpoint, but they all return the same response structure.
FMCD's Current Approach
FMCD exposes the underlying module structure:
/v2/ln/pay- Lightning payments/v2/mint/spend- Ecash operations/v2/onchain/withdraw- On-chain operations
Problem: Users need to understand Fedimint's internal architecture to use the API.
4. Response Structure Comparison
Phoenixd's Response
{
"paymentId": "uuid",
"paymentHash": "hash",
"preimage": "preimage",
"recipientAmountSat": 1000,
"routingFeeSat": 10
}FMCD's Response
{
"operation_id": "id",
"payment_type": "Lightning",
"contract_id": "contract",
"fee": 10,
"preimage": "preimage"
}Analysis: Phoenixd uses clearer field names and consistent structure. FMCD exposes internal concepts like "contract_id" that users don't need to know about.
5. Error Handling
| Aspect | Phoenixd | FMCD |
|---|---|---|
| Error Format | Simple text with HTTP status | Complex error objects |
| User Feedback | Clear, actionable messages | Technical error details |
| Status Codes | Consistent HTTP status mapping | Mixed approaches |
Proposed New API Design for FMCD
Design Principles
- User-Centric: Focus on what users want to accomplish, not how Fedimint works internally
- Simple by Default: Hide complexity behind sensible defaults
- Progressive Disclosure: Advanced features available but not required
- Consistency: Uniform patterns across all endpoints
Proposed Endpoint Structure
# Core Payment Operations (Public API)
POST /pay # Universal payment endpoint
POST /invoice # Create invoice
GET /balance # Get balance across all federations
GET /info # Basic node information
# Payment Status
GET /payment/{id} # Get payment details
GET /payments # List payments with filtering
# On-chain Operations
POST /deposit # Generate deposit address
POST /withdraw # Withdraw to on-chain address
# Federation Management (Admin API)
POST /federation/join # Join a federation
GET /federations # List federations
DELETE /federation/{id} # Leave a federation
# Advanced Operations (Optional)
POST /ecash/mint # Direct ecash operations
POST /ecash/melt # Direct ecash operations
Universal Payment Endpoint
The /pay endpoint should handle all payment types automatically:
// Request
POST /pay
{
"destination": "lnbc...|lnurl1...|bob@strike.me|offer1...",
"amount_sat": 1000, // Optional, uses invoice amount if not specified
"comment": "Pizza payment", // Optional
"federation_id": "fed123" // Optional, auto-selects if not specified
}
// Response
{
"payment_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"amount_sat": 1000,
"fee_sat": 10,
"destination_type": "lightning_invoice",
"completed_at": "2024-01-20T15:30:00Z",
"preimage": "abc123..." // Only for Lightning
}Simplified Invoice Creation
// Request
POST /invoice
{
"amount_sat": 1000,
"description": "Coffee payment",
"expiry_seconds": 3600 // Optional, defaults to 24 hours
}
// Response
{
"invoice_id": "inv_123",
"invoice": "lnbc1000...",
"amount_sat": 1000,
"expires_at": "2024-01-20T16:30:00Z",
"status": "pending"
}Balance Response
// Response
GET /balance
{
"total_sat": 50000,
"available_sat": 49000,
"pending_sat": 1000,
"federations": [
{
"id": "fed123",
"name": "My Federation",
"balance_sat": 30000
}
]
}Migration Plan
Phase 1: API Wrapper Layer (Week 1-2)
- Create new simplified endpoints that wrap existing functionality
- Map new endpoint requests to existing internal handlers
- Transform responses to new simplified format
- Deploy alongside existing
/v2API
Phase 2: Internal Refactoring (Week 3-4)
- Refactor internal core to align with new API structure
- Consolidate payment logic into universal payment handler
- Simplify error handling and response generation
- Update documentation
Phase 3: Testing & Documentation (Week 5)
- Comprehensive testing of new endpoints
- Update API documentation
- Create migration guide for existing users
- Add deprecation notices to old endpoints
Phase 4: Gradual Migration (Week 6+)
- Mark
/v2endpoints as deprecated - Provide 3-month migration period
- Monitor usage and assist users with migration
- Remove old endpoints in next major version
Implementation Roadmap
Immediate Actions (Sprint 1)
- Create
/payuniversal payment endpoint - Implement simplified
/invoiceendpoint - Add
/balanceendpoint with federation breakdown - Implement two-tier authentication system
Short-term Goals (Sprint 2-3)
- Refactor response structures for consistency
- Implement streaming endpoints for real-time updates
- Add comprehensive error messages
- Create API documentation site
Medium-term Goals (Sprint 4-6)
- Deprecate module-specific endpoints
- Add webhook support for payment notifications
- Implement rate limiting and API keys
- Add metrics and monitoring endpoints
Long-term Vision
- GraphQL API option for complex queries
- Plugin system for custom modules
- Multi-language SDKs
- API gateway for enterprise features
Technical Implementation Details
1. New Router Structure
// src/api/routes.rs
pub fn create_router(state: AppState) -> Router {
Router::new()
// Public endpoints (limited access)
.route("/info", get(handlers::get_info))
.route("/balance", get(handlers::get_balance))
.route("/invoice", post(handlers::create_invoice))
.route("/payment/:id", get(handlers::get_payment))
// Protected endpoints (full access)
.route("/pay", post(handlers::universal_pay))
.route("/withdraw", post(handlers::withdraw))
// Admin endpoints
.nest("/federation", federation_routes())
// Apply auth middleware
.layer(auth_middleware)
.with_state(state)
}2. Universal Payment Handler
// src/handlers/pay.rs
pub async fn universal_pay(
State(app): State<AppState>,
Json(req): Json<PayRequest>,
) -> Result<Json<PayResponse>, ApiError> {
// Detect payment type
let payment_type = detect_payment_type(&req.destination)?;
// Route to appropriate handler
match payment_type {
PaymentType::Invoice(invoice) => pay_invoice(app, invoice, req.amount_sat).await,
PaymentType::LnUrl(url) => pay_lnurl(app, url, req.amount_sat).await,
PaymentType::LnAddress(addr) => pay_ln_address(app, addr, req.amount_sat).await,
PaymentType::Offer(offer) => pay_offer(app, offer, req.amount_sat).await,
PaymentType::Ecash(notes) => pay_ecash(app, notes).await,
}
}3. Simplified Response Types
// src/api/types.rs
#[derive(Serialize)]
pub struct PayResponse {
pub payment_id: Uuid,
pub status: PaymentStatus,
pub amount_sat: u64,
pub fee_sat: u64,
pub destination_type: String,
pub completed_at: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preimage: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Serialize)]
#[serde(rename_all = "lowercase")]
pub enum PaymentStatus {
Pending,
Completed,
Failed,
}Benefits of the New Design
For Users
- Easier to learn - Fewer endpoints to understand
- Intuitive operations - Endpoints match user intentions
- Consistent experience - Uniform patterns across all operations
- Better error messages - Clear, actionable feedback
For Developers
- Simpler integration - Less complexity to handle
- Cleaner SDKs - Easier to build client libraries
- Better documentation - Simpler API is easier to document
- Future-proof - Room to grow without breaking changes
For Maintainers
- Cleaner codebase - Less duplication and clearer structure
- Easier testing - Fewer edge cases to handle
- Better monitoring - Clearer metrics and logging
- Reduced support burden - Simpler API means fewer user issues
Conclusion
By adopting phoenixd's design philosophy while maintaining Fedimint's unique capabilities, FMCD can provide a significantly improved developer experience. The proposed design:
- Reduces complexity from 30+ endpoints to ~15 core endpoints
- Improves usability with intuitive, action-based operations
- Maintains flexibility for advanced users and future features
- Ensures compatibility with gradual migration path
The key insight from phoenixd is that simplicity drives adoption. Users shouldn't need to understand the underlying protocol to make payments. By hiding Fedimint's complexity behind a clean API, FMCD can become the go-to solution for developers wanting to integrate Fedimint into their applications.
Appendix: API Endpoint Mapping
| Current FMCD Endpoint | Proposed New Endpoint | Notes |
|---|---|---|
/v2/ln/invoice |
/invoice |
Simplified path |
/v2/ln/pay |
/pay |
Universal payment |
/v2/mint/spend |
/pay |
Handled by universal pay |
/v2/mint/reissue |
/ecash/mint |
Advanced operation |
/v2/onchain/withdraw |
/withdraw |
Simplified path |
/v2/onchain/deposit-address |
/deposit |
Simplified name |
/v2/admin/join |
/federation/join |
Clearer grouping |
/v2/admin/info |
/info |
Public endpoint |
/v2/admin/backup |
/backup |
Admin endpoint |
/v2/admin/restore |
/restore |
Admin endpoint |