148 lines
4.4 KiB
Markdown
148 lines
4.4 KiB
Markdown
# Operations
|
|
|
|
Day-to-day commands for running project-bifrost.
|
|
|
|
## Managing invites
|
|
|
|
All commands run on the VPS as the `fenja` user.
|
|
|
|
```bash
|
|
# Add someone
|
|
sudo -u fenja node /opt/fenja/bin/invite.js add someone@example.com
|
|
|
|
# Remove someone (doesn't kill their active session — see below)
|
|
sudo -u fenja node /opt/fenja/bin/invite.js remove someone@example.com
|
|
|
|
# List everyone
|
|
sudo -u fenja node /opt/fenja/bin/invite.js list
|
|
```
|
|
|
|
Removing an invite stops a user from requesting *new* codes, but doesn't invalidate their existing session cookie (valid 30 days). To kick them out immediately, also run:
|
|
|
|
```bash
|
|
sudo sqlite3 /opt/fenja/data/fenja.sqlite \
|
|
"DELETE FROM sessions WHERE email = 'someone@example.com';"
|
|
```
|
|
|
|
## Reading Join-CTA clicks
|
|
|
|
Every press of the final "Join Project Bifrost" CTA is logged to the `bifrost_joins` table. Use `bin/joins.js` to read it:
|
|
|
|
```bash
|
|
# Every click, newest first (id, email, session)
|
|
sudo -u fenja node /opt/fenja/bin/joins.js list
|
|
|
|
# One row per user, with click count + first/last timestamps
|
|
sudo -u fenja node /opt/fenja/bin/joins.js summary
|
|
|
|
# Full click history for a single user
|
|
sudo -u fenja node /opt/fenja/bin/joins.js for someone@example.com
|
|
|
|
# Totals — clicks + unique users
|
|
sudo -u fenja node /opt/fenja/bin/joins.js stats
|
|
```
|
|
|
|
One row is written per click (the schema uses auto-increment `id`, not email-as-PK), so re-clicks are preserved. For ad-hoc SQL:
|
|
|
|
```bash
|
|
sudo -u fenja sqlite3 /opt/fenja/data/fenja.sqlite \
|
|
"SELECT email, datetime(clicked_at/1000,'unixepoch') FROM bifrost_joins ORDER BY clicked_at DESC;"
|
|
```
|
|
|
|
## Service control
|
|
|
|
```bash
|
|
sudo systemctl status fenja # is it running?
|
|
sudo systemctl restart fenja # restart (after config/code changes)
|
|
sudo journalctl -u fenja -f # live log tail
|
|
sudo journalctl -u fenja -n 100 # last 100 lines
|
|
```
|
|
|
|
## Deploying code changes
|
|
|
|
From your laptop, in WSL, inside the project folder:
|
|
|
|
```bash
|
|
# Push to staging on VPS
|
|
rsync -avz --delete \
|
|
--exclude node_modules --exclude data --exclude .env --exclude .git \
|
|
./ user@project-bifrost.fenja.ai:/tmp/fenja-upload/
|
|
|
|
# Then on the VPS:
|
|
ssh user@project-bifrost.fenja.ai
|
|
sudo rsync -a --delete /tmp/fenja-upload/ /opt/fenja/
|
|
sudo chown -R fenja:fenja /opt/fenja
|
|
rm -rf /tmp/fenja-upload
|
|
|
|
# If package.json changed:
|
|
cd /opt/fenja
|
|
sudo -u fenja npm ci --omit=dev
|
|
|
|
# Restart
|
|
sudo systemctl restart fenja
|
|
sudo journalctl -u fenja -n 20
|
|
```
|
|
|
|
Confirm `[mail] SMTP relay reachable` and `[bifrost] listening` appear in the logs.
|
|
|
|
## Editing secrets (SMTP, pepper)
|
|
|
|
Secrets live in `/etc/fenja/env`, not in the repo.
|
|
|
|
```bash
|
|
sudo nano /etc/fenja/env
|
|
sudo systemctl restart fenja
|
|
```
|
|
|
|
**Never change `CODE_PEPPER` after go-live** — it invalidates every pending code. Not catastrophic (users re-request within 10 min), but avoid unless rotating for security.
|
|
|
|
## Backups
|
|
|
|
Nightly cron at `/etc/cron.d/fenja-backup` snapshots the SQLite file to `/opt/fenja/data/backup-YYYY-MM-DD.sqlite`, keeping 14 days.
|
|
|
|
Manual snapshot:
|
|
|
|
```bash
|
|
sudo -u fenja sqlite3 /opt/fenja/data/fenja.sqlite \
|
|
".backup /opt/fenja/data/backup-manual-$(date +%F).sqlite"
|
|
```
|
|
|
|
Pull a backup to your laptop:
|
|
|
|
```bash
|
|
scp user@project-bifrost.fenja.ai:/opt/fenja/data/backup-YYYY-MM-DD.sqlite .
|
|
```
|
|
|
|
## Quick health checks
|
|
|
|
```powershell
|
|
# From your laptop
|
|
curl.exe -I https://project-bifrost.fenja.ai/ # expect 200
|
|
curl.exe -I https://project-bifrost.fenja.ai/timeline.js # expect 302 → /
|
|
```
|
|
|
|
If either fails, check Nginx (`sudo systemctl status nginx`) and Node (`sudo systemctl status fenja`).
|
|
|
|
## Troubleshooting
|
|
|
|
| Symptom | First thing to check |
|
|
|---|---|
|
|
| Users don't get codes | `journalctl -u fenja -n 50` for SMTP errors |
|
|
| Codes arrive in spam | SPF/DKIM/DMARC records on the sending domain |
|
|
| 502 Bad Gateway | Node crashed — `systemctl status fenja` then `journalctl` |
|
|
| 504 Gateway Timeout | Node running but hung — `systemctl restart fenja` |
|
|
| Nginx config change broke something | `sudo nginx -t` will tell you exactly what |
|
|
| Can't log in with the right code | Clock drift between your machine and the VPS, or pepper mismatch |
|
|
|
|
## File locations
|
|
|
|
```
|
|
/opt/fenja/ code (owned by fenja:fenja)
|
|
/opt/fenja/data/ SQLite + nightly backups
|
|
/etc/fenja/env secrets (root:fenja, 640)
|
|
/etc/systemd/system/fenja.service
|
|
/etc/nginx/sites-available/project-bifrost
|
|
/etc/nginx/sites-enabled/project-bifrost (symlink)
|
|
/etc/letsencrypt/live/project-bifrost.fenja.ai/ TLS certs
|
|
/etc/cron.d/fenja-backup
|
|
```
|