- align auth docs with the simplified POST /auth/login flow - drop CODE_PEPPER / SMTP / MAIL_FROM / mail.js / request-code references - document the bifrost_joins table and bin/joins.js CLI - OPERATIONS.md: WSL setup, exclude data/.env/node_modules on promote rsync - INSTALL.md: 3-value /etc/fenja/env, drop SMTP prereq Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.4 KiB
6.4 KiB
Checklist
Run the relevant section after every change before considering it done. Items are ordered: if an earlier one fails, stop and fix before testing the rest.
Notation: run on the VPS unless marked [local] or [browser].
A. After any code change (minimum viable smoke test)
sudo systemctl status fenja→active (running)sudo journalctl -u fenja -n 20shows[bifrost] listening on 127.0.0.1:3000, no red errors (there is no[mail] SMTP relay reachableline anymore — the mail stack was removed)- [local]
curl -I https://project-bifrost.fenja.ai/→ 200, withX-Frame-Options: DENYandContent-Security-Policyheaders - [local]
curl -I https://project-bifrost.fenja.ai/timeline.js→ 302,Location: / - [browser, private window] Open
https://project-bifrost.fenja.ai/→ entrance page renders (not timeline)
If all five pass, the site is up and the gate holds.
B. After changes to auth, sessions, cookies, middleware, or src/
Do section A, then:
- [browser, private] Enter invited email → session cookie issued immediately, welcome step appears (no 6-digit code flow anymore)
- [browser] Click "Learn more" on the welcome step → redirected to
/timelineshowing the timeline page - [browser] Hard-refresh (Ctrl+Shift+R) → stays on the timeline (cookie persists)
- [browser, new private window] Visit
/→ entrance appears (no cookie leak between sessions) - [browser] Click logout → lands on entrance → visiting
/timelineredirects to/ - [browser] Visit
/timeline.jsor/vendor/d3-array.min.jsdirectly while logged out → redirects to/ - DevTools → Application → Cookies:
fenja_sessionshowsHttpOnly ✓,Secure ✓(prod only),SameSite=Lax
C. After changes to the entrance form or login endpoint
- Submit a non-invited address → inline "not invited" message, no session issued
- Submit a malformed email (
foo,foo@, empty) → inline error appears, no request sent - Submit > 30 login attempts from the same IP in an hour → rate-limit response (429)
D. After changes to the timeline / protected pages
- [browser] Timeline loads fully: globe visible, 12 event cards, dot-nav at bottom, fonts render as true italic (not system oblique)
- [browser] Scroll works smoothly, card reveal animations fire
- [browser] Dot-nav switches between Timeline / Overview / Archive views
- DevTools console: no CSP violations, no 404s for fonts/vendor files
- DevTools network tab: all
/vendor/*and/fenja/fonts/*requests return 200
E. After changes to CSP, security headers, or Nginx
Do section A with extra attention to:
- Response headers on
/include all six:X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Permissions-Policy,Content-Security-Policy,Strict-Transport-Security - CSP contains at minimum:
default-src 'self',script-src 'self'(nounsafe-inlineon scripts),frame-ancestors 'none' sudo nginx -t→ "syntax is ok" and "test is successful"- [local] Open timeline in a browser → no red CSP violations in DevTools console
curl.exe -X POST https://project-bifrost.fenja.ai/auth/login -H 'Content-Type: application/json' -d '{"email":"nobody@example.com"}'returns quickly (rate-limit zone is functioning; expect 403not_invitedon a real email not on the list)
F. After dependency or Node.js upgrades
sudo -u fenja npm ci --omit=devcompletes without errorssudo -u fenja npm auditreports no high/critical vulnerabilities in production deps- Run section A, then section B (full auth flow)
- Check
node --versionon the VPS is still 20+
G. After Nginx config changes specifically
sudo nginx -tbefore reloading (catches 95% of errors)ls /etc/nginx/sites-enabled/contains onlyproject-bifrost(no shadow configs)- After reload,
curl -I https://project-bifrost.fenja.ai/includesX-Powered-By: Express(proves Nginx is still proxying to Node, not serving static files) - Rate-limit zone still exists:
grep -r "limit_req_zone" /etc/nginx/
H1. After changes to the Join-CTA tracking (bifrost_joins)
- [browser, logged in] Click the final "Join Project Bifrost" CTA → confirmation panel renders (staggered checkmarks appear)
- DevTools network tab:
POST /api/bifrost-joinreturns 200 with{clicked_at: <ms>} sudo -u fenja node /opt/fenja/bin/joins.js listshows a new row with your email and a current timestamp- Click the CTA a second time (refresh the page first): a second row appears — the log is per-click, not per-user
sudo -u fenja node /opt/fenja/bin/joins.js summarygroups by email with correctclick_countsudo -u fenja node /opt/fenja/bin/joins.js statstotals match what you see inlist- Logged out:
curl -X POST https://project-bifrost.fenja.ai/api/bifrost-join→ 401 (auth gate holds)
H. After data/DB changes (schema, migrations)
- Fresh boot creates schema cleanly: stop service,
mv data/fenja.sqlite data/fenja.sqlite.bak, restart, verify it comes up healthy - Restore the backup (
mvit back) before inviting further users - Take a manual backup before deploying:
sudo -u fenja sqlite3 /opt/fenja/data/fenja.sqlite ".backup /opt/fenja/data/pre-change-$(date +%F).sqlite"
I. Deploy readiness (before any push to production)
- Ran locally:
npm install && npm run dev, walked full flow end-to-end - No uncommitted
.envfile in the rsync source - No
data/*.sqlitein the rsync source - On the VPS after deploy: section A passes
- On the VPS after deploy: section B passes
- Tailed
journalctl -u fenja -ffor 30s while refreshing the site → no errors
Red flags that always mean stop
curl -I /returns 200 withoutX-Powered-By: Express→ Nginx is serving static, not proxying to Node. Site is broken.curl -I /timeline.jsreturns 200 → the auth gate is down. Do not invite anyone.- Any
Set-Cookie: fenja_session=...header that lacksHttpOnlyorSecure(in prod) → cookie hardening regression. journalctl -u fenjashowing repeated crashes → something's wrong,systemctl restart fenjawon't fix it.- The entrance page submits the form as a GET with
?email=in the URL → JS isn't running (usually CSP blocking a newly-added inline script).
If any of these happen, revert to the last known-good commit and investigate offline.