Files
SatsFaucet/README.md
2026-02-26 18:35:48 -03:00

176 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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