Files
sapien/README.md
2026-05-14 12:53:37 -05:00

94 lines
2.5 KiB
Markdown

# L484 / Sapien Membership App
Vue frontend with a small Node backend for private membership signup, Nostr sign-in, admin approvals, BTCPay invoices, encrypted local member-card export/import, and NFC card access scaffolding.
## Development
```bash
npm install
npm run dev
```
The dev script runs the API and Vite with `--host`. It also loads `.env.local` and seeds development members when `DEV_SEED_MEMBERS=true`.
## Production / Portainer
The app listens on port `2354` in the provided compose files.
Required production environment:
```bash
PORT=2354
HOST=0.0.0.0
APP_MODE=all
MEMBERSHIP_ENCRYPTION_KEY=<32+ random bytes>
ACCESS_HMAC_KEY=<32+ random bytes>
ACCESS_CONTROLLER_TOKEN=<random controller token>
BTCPAY_SERVER_URL=https://your-btcpay-host
BTCPAY_STORE_ID=<store id>
BTCPAY_API_KEY=<api key>
BTCPAY_WEBHOOK_SECRET=<webhook secret>
DEV_SEED_MEMBERS=false
```
Keep `server/data` on a persistent volume. Do not deploy `.env.local`.
## BTCPay
Create a BTCPay webhook pointing at:
```text
https://your-domain/api/btcpay/webhook
```
Use the same webhook secret as `BTCPAY_WEBHOOK_SECRET`.
The admin payment modal opens a live server-sent event stream at `/api/admin/events`. When BTCPay calls the webhook, the backend marks the invoice paid and pushes a `payment-paid` event to open admin sessions. The status endpoint remains available for explicit refresh/error recovery.
## NFC Door Readiness
The controller-facing endpoint is:
```http
POST /api/access/check
X-Controller-Token: <ACCESS_CONTROLLER_TOKEN>
Content-Type: application/json
{
"doorId": "front-door",
"cardCredential": "scanned-card-secret-or-uid"
}
```
The backend stores only HMACs of card credentials using `ACCESS_HMAC_KEY`. Access is allowed only when:
- the scanned card hash matches an active card,
- the member exists,
- the member access status resolves to `active`.
The response:
```json
{
"allow": true,
"reason": "active_member_card",
"member": {
"membershipId": "L484-2026-XXXXXX",
"fullName": "Member Name",
"status": "active"
}
}
```
Every access check is logged in `server/data/access-logs.json`.
## Security Notes
- The generated user `nsec` is shown once in the browser and is not sent to the backend.
- Member records are encrypted before being saved in `server/data/memberships.json`.
- NFC card credentials are never stored raw.
- Admin APIs require an authorized Nostr public key.
- Controller APIs require `ACCESS_CONTROLLER_TOKEN`.
- Mutating API routes have basic rate limiting.
- Rotate any development BTCPay credentials before production.