Commit graph

7 commits

Author SHA1 Message Date
Arlind Ukshini
2d06f8e513 /fenjaops: add Remove button for non-admin invites
- New endpoint: DELETE /api/fenjaops/invites/:email, behind
  requireAuth + requireAdmin. Guardrails:
    * refuses if the target email is an admin (demote first via
      bin/invite.js admin remove) — preserves the invariant that
      a compromised admin session can't lock everyone out;
    * refuses if the target email equals the caller's own —
      prevents self-inflicted lockouts from the UI;
    * deletes active sessions for the target email so the user
      is kicked out immediately instead of holding their 30-day
      cookie.
- Admin page: Invites table gains an "Action" column. Non-admin,
  non-self rows show a Remove button (quiet ink outline; crimson
  on hover to cue destructive intent). Admin and self rows show
  an em-dash. Click → browser confirm() → DELETE → load() to
  refresh counts + tables.
- admin.js fetches /auth/me alongside the other payloads so
  render can compare each row's email against the viewer's.
- PROJECT.md and CLAUDE.md updated: the "no web deletion"
  invariant is narrowed to "no web deletion of admins or self"
  to reflect the new capability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:23:12 +02:00
Arlind Ukshini
72590b08bc add mobile view at protected/mobile/ (UA-dispatched)
Desktop is a GSAP/Lenis/d3 animated experience that doesn't hold up
on phones. Rather than retrofitting media queries across 1200+ lines
of scroll-trigger code, add a completely isolated static mobile tree:

- protected/mobile/index.html — one-page static flow covering the
  intro, 12 timeline events, hero, 4 capability cards, Bifrost
  reveal, 3 participation stops, and Join CTA. All copy duplicated
  from the desktop HTML on purpose — a shared data module would
  re-couple the two trees.
- protected/mobile/mobile.css — paper/ink palette, all m-prefixed,
  zero cascade overlap with the desktop CSS.
- protected/mobile/mobile.js — 60-line client: /auth/me check,
  /api/bifrost-join POST + panel swap, /auth/logout. No GSAP, no
  Lenis, no d3.

Routing (server.js):
- GET /timeline now UA-dispatches via MOBILE_UA_RE. Phone UAs get
  the mobile page; everything else gets the desktop page.
- ?view=mobile and ?view=desktop query overrides take precedence
  over the UA sniff — for bad guesses or previewing the other
  version.
- Gating is unchanged: protected/mobile/ is inside protected/ so
  the existing requireAuth + express.static gate covers it.

Docs:
- CLAUDE.md §routing now lists the UA dispatch as step 4.
- PROJECT.md gets a new "Mobile view" section explaining the
  isolation rules (no shared JS/CSS, content duplicated manually).
- CHECKLIST.md gains section H0 with dispatch curl checks, render
  verification on a phone, and an isolation audit that fails if
  mobile classes leak into the desktop HTML or vice versa.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:03:13 +02:00
Arlind Ukshini
cbfb187d16 /fenjaops: admin-only form to invite non-admin users
- POST /api/fenjaops/invites on server.js (requireAuth+requireAdmin).
  Ignores any is_admin field in the body — always stores 0. Records
  the acting admin's email in invited_by so the audit trail shows
  who added whom (CLI adds still record "cli").
- admin/index.html: new "Invite a new user" form panel at the top
  (email + optional first name).
- admin/admin.js: wires the form submit to the POST, shows inline
  success/error, refreshes the tables on success.
- admin/admin.css: form styling matching the existing paper/ink
  palette; mobile stacks.
- Docs: CLAUDE.md, PROJECT.md, OPERATIONS.md, CHECKLIST.md, README.md
  all updated. New non-negotiable property in PROJECT.md: no web
  endpoint can set is_admin=1 or delete an invite — promotion +
  removal stay on bin/invite.js. New CHECKLIST.md section H2 covers
  the page's gating, the invite form, and an escalation-path audit.

Admin promotion and invite deletion remain CLI-only so a compromised
admin session cannot escalate or evict.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 18:07:47 +02:00
Arlind Ukshini
107284801b add hidden /fenjaops admin page (read-only) + is_admin invite flag
- new is_admin column on invites (migration 4) with DEFAULT 0
- requireAdmin middleware returns 404 for non-admins so the route's
  existence isn't leaked; path obscured as /fenjaops (not /admin)
- admin/ dir lives outside public/ and protected/; only reachable via
  the explicit gated mount + /api/fenjaops/{invites,joins} endpoints
- bin/invite.js gains `admin add|remove|list` subcommands
- OPERATIONS.md + CLAUDE.md + PROJECT.md document the hidden URL

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 17:29:19 +02:00
Arlind Ukshini
88863183e1 update docs: minimal env, WSL deploy, join tracking, rsync excludes
- 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>
2026-04-23 17:10:08 +02:00
Arlind Ukshini
d5f578a581 update docs 2026-04-23 15:00:53 +02:00
Arlind Ukshini
1c395c349b Initial commit: project-bifrost auth + timeline 2026-04-22 14:39:16 +02:00