# backend_overview.md ## 1. Purpose This document defines the complete backend architecture and behavior for the Sats Faucet application. The backend is responsible for: * Verifying NIP-98 signed authentication. * Enforcing all eligibility rules. * Fetching and caching Nostr account data. * Enforcing cooldowns and anti-abuse constraints. * Generating and locking entropy payout quotes. * Executing Lightning payouts via LNbits. * Maintaining transparency statistics. * Providing operational safety controls. This backend must be production-safe, abuse-resistant, and configurable entirely via environment variables. --- ## 2. Tech Stack Requirements Recommended stack: * Language: Go, TypeScript (Node/Express), or Python (FastAPI) * Database: PostgreSQL * Optional: Redis (rate limiting + nonce replay protection) * LNbits for Lightning payments * Reverse proxy with TLS (nginx or Caddy) The backend must be stateless except for database persistence. --- ## 3. Environment Variables All logic must be driven via .env configuration. Key categories: ### 3.1 Security * JWT_SECRET * HMAC_IP_SECRET * NIP98_MAX_SKEW_SECONDS * NONCE_TTL_SECONDS * TRUST_PROXY * ALLOWED_ORIGINS ### 3.2 Faucet Economics * FAUCET_ENABLED * EMERGENCY_STOP * FAUCET_MIN_SATS * FAUCET_MAX_SATS * PAYOUT_WEIGHT_SMALL * PAYOUT_WEIGHT_MEDIUM * PAYOUT_WEIGHT_LARGE * PAYOUT_WEIGHT_JACKPOT * PAYOUT_SMALL_SATS * PAYOUT_MEDIUM_SATS * PAYOUT_LARGE_SATS * PAYOUT_JACKPOT_SATS * DAILY_BUDGET_SATS * MAX_CLAIMS_PER_DAY * MIN_WALLET_BALANCE_SATS ### 3.3 Eligibility * MIN_ACCOUNT_AGE_DAYS * MIN_ACTIVITY_SCORE * MIN_NOTES_COUNT * MIN_FOLLOWING_COUNT * MIN_FOLLOWERS_COUNT * ACTIVITY_LOOKBACK_DAYS ### 3.4 Cooldowns * COOLDOWN_DAYS * IP_COOLDOWN_DAYS * MAX_CLAIMS_PER_IP_PER_PERIOD ### 3.5 Nostr * NOSTR_RELAYS * RELAY_TIMEOUT_MS * MAX_EVENTS_FETCH * METADATA_CACHE_HOURS ### 3.6 LNbits * LNBITS_BASE_URL * LNBITS_ADMIN_KEY * LNBITS_WALLET_ID * DEPOSIT_LIGHTNING_ADDRESS * DEPOSIT_LNURLP --- ## 4. Database Schema ### 4.1 users * pubkey (PK) * nostr_first_seen_at * notes_count * followers_count * following_count * activity_score * last_metadata_fetch_at * created_at * updated_at ### 4.2 claims * id (PK) * pubkey (FK users.pubkey) * claimed_at * payout_sats * ip_hash * payout_destination_hash * status (pending, paid, failed) * lnbits_payment_hash * error_message ### 4.3 ip_limits * ip_hash (PK) * last_claimed_at * claim_count_period ### 4.4 quotes * quote_id (PK) * pubkey * payout_sats * created_at * expires_at * status (active, consumed, expired) ### 4.5 daily_stats (optional) * date (PK) * total_paid_sats * total_claims * unique_pubkeys All IP addresses must be stored as HMAC(IP, HMAC_IP_SECRET). --- ## 5. NIP-98 Authentication Every protected endpoint must: 1. Extract NIP-98 header or payload. 2. Verify signature against pubkey. 3. Verify HTTP method and URL match signed payload. 4. Verify timestamp within allowed skew. 5. Verify nonce not previously used. 6. Reject if invalid. Nonces must be stored in Redis or DB for NONCE_TTL_SECONDS. --- ## 6. IP Resolution If TRUST_PROXY=true: * Read first valid IP from X-Forwarded-For. Else: * Use request remote address. Then: * Hash IP using HMAC_IP_SECRET. * Never store raw IP. --- ## 7. Eligibility Engine Eligibility flow: 1. Check FAUCET_ENABLED. 2. Check EMERGENCY_STOP. 3. Check LNbits wallet balance >= MIN_WALLET_BALANCE_SATS. 4. Check pubkey cooldown. 5. Check IP cooldown. 6. Fetch or load cached Nostr profile. 7. Compute account age. 8. Compute activity score. 9. Compare with thresholds. Return structured result with denial code if failed. --- ## 8. Nostr Data Fetching For a given pubkey: 1. Query relays in parallel. 2. Fetch: * kind 0 (metadata) * kind 1 (notes) * kind 3 (contacts) 3. Compute: * earliest created_at * notes count in ACTIVITY_LOOKBACK_DAYS * following count Cache results for METADATA_CACHE_HOURS. If no events found: * Deny as account_too_new. --- ## 9. Activity Scoring Example scoring logic: * Has metadata: +10 * Notes >= MIN_NOTES_COUNT: +20 * Following >= MIN_FOLLOWING_COUNT: +10 * Followers >= MIN_FOLLOWERS_COUNT: +10 Score must be deterministic and logged. --- ## 10. Entropy Payout System Weighted random selection: 1. Build array based on configured weights. 2. Generate secure random number. 3. Select payout bucket. Before finalizing quote: * Check daily spend. * Check MAX_CLAIMS_PER_DAY. If budget exceeded: * Either deny or downgrade payout to FAUCET_MIN_SATS. --- ## 11. Claim Flow ### 11.1 POST /claim/quote Input: * lightning_address Steps: * Run eligibility engine. * If eligible, generate payout. * Insert quote record with expiry (e.g., 60 seconds). * Return quote_id and payout_sats. ### 11.2 POST /claim/confirm Input: * quote_id Steps: * Verify quote exists and active. * Re-check cooldown and budget. * Execute LNbits payment. * Update claim record. * Mark quote consumed. Must be idempotent. --- ## 12. LNbits Integration Payout flow: 1. Resolve Lightning address to LNURL. 2. Fetch LNURL payRequest. 3. Call callback with amount in millisats. 4. Handle success/failure response. 5. Store payment hash. On failure: * Mark claim failed. * Do not lock cooldown unless configured. --- ## 13. Public Endpoints GET /health GET /config GET /stats GET /deposit No authentication required. --- ## 14. Logging and Monitoring Each claim attempt must log: * pubkey * ip_hash * eligibility result * payout amount * payment status Metrics to track: * denial reasons count * payout distribution * daily spend --- ## 15. Security Hard Requirements * Strict CORS * Rate limit /claim endpoints * Nonce replay protection * HMAC IP hashing * Admin keys never exposed * All secrets loaded from env --- ## 16. Production Readiness Checklist Backend is complete when: * NIP-98 auth fully verified. * Pubkey and IP cooldown enforced. * Account age check enforced. * Activity score enforced. * Entropy payout cannot be rerolled. * Daily budget cannot be exceeded. * LNbits payout works and errors handled safely. * Emergency stop disables claims instantly. * Logs clearly show denial reasons.