Add full README with setup, API, and env docs

Made-with: Cursor
This commit is contained in:
Michaël
2026-02-26 18:35:48 -03:00
parent 3734365463
commit c005e4755b

175
README.md Normal file
View File

@@ -0,0 +1,175 @@
# Sats Faucet
A Lightning faucet for the Nostr ecosystem. Nostr users can claim a small, randomized sats payout on a cooldown schedule. The app uses **NIP-98** for authenticated requests, **LNbits** for Lightning payouts, and enforces anti-abuse rules (account age, activity score, per-pubkey and per-IP cooldowns).
## Features
- **Nostr auth** — Connect via NIP-07 (browser extension) or external signer; all claim requests are signed with NIP-98.
- **Entropy payouts** — Weighted random payout buckets (e.g. 10, 25, 50, 100 sats); quote is locked so users cant re-roll by refreshing.
- **Anti-abuse** — Account age (e.g. 14 days), activity score, per-pubkey cooldown (e.g. 7 days), per-IP cooldown and limits, daily budget cap.
- **Transparency** — Public stats: pool balance, total paid, claim counts, recent payouts (anonymized).
- **Deposits** — Lightning address and LNURLp QR for community funding.
- **Operational controls** — `FAUCET_ENABLED`, `EMERGENCY_STOP`, minimum wallet balance guard.
## Tech stack
| Layer | Stack |
|----------|--------|
| Frontend | React, TypeScript, Vite, Framer Motion |
| Backend | Node.js, Express, TypeScript |
| Database | SQLite (default) or PostgreSQL |
| Payments | LNbits (Lightning) |
| Auth | NIP-98 (Nostr HTTP Auth) |
## Prerequisites
- **Node.js** 18+
- **LNbits** instance with a wallet and admin key
- **Nostr** signer (e.g. browser extension with NIP-07) for testing claims
## Quick start
### 1. Clone and install
```bash
git clone https://git.azzamo.net/Michilis/SatsFaucet.git
cd SatsFaucet
npm install
cd backend && npm install
cd ../frontend && npm install
```
### 2. Backend configuration
```bash
cd backend
cp .env.example .env
# Edit .env: set HMAC_IP_SECRET, JWT_SECRET, LNbits keys, deposit address, etc.
```
Required at minimum:
- `HMAC_IP_SECRET` — min 32 chars (for IP hashing)
- `JWT_SECRET` — min 32 chars
- `LNBITS_BASE_URL`, `LNBITS_ADMIN_KEY`, `LNBITS_WALLET_ID`
- `DEPOSIT_LIGHTNING_ADDRESS`, `DEPOSIT_LNURLP` (for the deposit section)
See `backend/.env.example` for all options (payout weights, cooldowns, eligibility thresholds, Nostr relays).
### 3. Database
**SQLite (default):** ensure `backend/data` exists; migrations create the DB.
```bash
cd backend
npm run migrate
```
**PostgreSQL:** set `DATABASE_URL` in `.env` and run:
```bash
cd backend
npm run migrate
```
### 4. Frontend configuration
```bash
cd frontend
cp .env.example .env
# For local dev, VITE_API_URL=http://localhost:3001 is usually correct
```
### 5. Run in development
**Terminal 1 — backend:**
```bash
npm run dev:backend
# or: cd backend && npm run dev
```
**Terminal 2 — frontend:**
```bash
npm run dev:frontend
# or: cd frontend && npm run dev
```
Open the frontend URL (e.g. `http://localhost:5173`), connect Nostr, enter a Lightning address, and use “Check” then “Confirm” to claim.
## Production build
```bash
npm run build
# Builds backend (TypeScript → dist/) and frontend (Vite → frontend/dist/)
```
Run the backend (serves API; optionally serve frontend from same host or use a static host):
```bash
npm start
# or: cd backend && npm start
```
Set `TRUST_PROXY=true` when behind a reverse proxy (nginx, Caddy, etc.) so client IPs are read from `X-Forwarded-For`.
## Project structure
```
├── backend/ # Express API
│ ├── src/
│ │ ├── db/ # SQLite/Pg, migrations, schema
│ │ ├── middleware/ # NIP-98, auth, IP, rate limit
│ │ ├── routes/ # claim, auth, public, user
│ │ └── services/ # eligibility, LNbits, Nostr, quote
│ ├── .env.example
│ └── package.json
├── frontend/ # React SPA
│ ├── src/
│ │ ├── components/
│ │ ├── contexts/
│ │ ├── hooks/
│ │ ├── pages/
│ │ └── styles/
│ ├── .env.example
│ └── package.json
├── context/ # Design/overview docs
├── package.json # Root scripts
└── README.md
```
## API overview
| Method | Endpoint | Auth | Description |
|--------|-------------------|--------|--------------------|
| GET | `/health` | — | Health check |
| GET | `/config` | — | Public config |
| GET | `/stats` | — | Balance, counts |
| GET | `/deposit` | — | Deposit address/QR |
| POST | `/claim/quote` | NIP-98 | Get payout quote |
| POST | `/claim/confirm` | NIP-98 | Confirm and pay |
Claim flow: frontend calls `POST /claim/quote` with Lightning address; if eligible, backend returns `quote_id` and `payout_sats`. Frontend then calls `POST /claim/confirm` with `quote_id` to execute the payout. Quotes are short-lived to prevent re-rolling.
## Environment variables (summary)
- **Security:** `HMAC_IP_SECRET`, `JWT_SECRET`, `NIP98_MAX_SKEW_SECONDS`, `NONCE_TTL_SECONDS`, `TRUST_PROXY`, `ALLOWED_ORIGINS`
- **Faucet:** `FAUCET_ENABLED`, `EMERGENCY_STOP`, payout weights and bucket sats, `DAILY_BUDGET_SATS`, `MIN_WALLET_BALANCE_SATS`
- **Eligibility:** `MIN_ACCOUNT_AGE_DAYS`, `MIN_ACTIVITY_SCORE`, `MIN_NOTES_COUNT`, `MIN_FOLLOWING_COUNT`, etc.
- **Cooldowns:** `COOLDOWN_DAYS`, `IP_COOLDOWN_DAYS`, `MAX_CLAIMS_PER_IP_PER_PERIOD`
- **Nostr:** `NOSTR_RELAYS`, `RELAY_TIMEOUT_MS`, `METADATA_CACHE_HOURS`
- **LNbits:** `LNBITS_BASE_URL`, `LNBITS_ADMIN_KEY`, `LNBITS_WALLET_ID`, `DEPOSIT_LIGHTNING_ADDRESS`, `DEPOSIT_LNURLP`
See `backend/.env.example` for full list and defaults.
## Security notes
- LNbits keys and secrets stay on the backend; the frontend never sees them.
- IPs are stored only as HMAC hashes (using `HMAC_IP_SECRET`).
- NIP-98 enforces method, URL, timestamp, and nonce; nonces are checked for replay.
- Use HTTPS and a reverse proxy in production; set `TRUST_PROXY` and restrict `ALLOWED_ORIGINS`.
## License
MIT (or as specified in the repository).