Files
SatsFaucet/context/backend_overview.md
Michaël 3734365463 first commit
Made-with: Cursor
2026-02-26 18:33:00 -03:00

360 lines
6.0 KiB
Markdown

# 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.