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