Project plan

This commit is contained in:
2026-06-14 17:45:12 +02:00
parent 4eed701419
commit 638815af2e
3 changed files with 359 additions and 27 deletions

98
todo.md Normal file
View File

@@ -0,0 +1,98 @@
# Plainpages — implementation TODO
Build order is top → bottom; each phase is roughly independent and testable.
Conventions: **write tests first** (node --test for units, Playwright for E2E),
tear down test containers after runs, keep deps minimal, pin all versions, run
everything via Docker.
## 0. Housekeeping / primitives
- [ ] Decide JWT verify approach: `node:crypto` (RS256/ES256 via `createPublicKey({format:"jwk"})`) vs add `jose` — justify if adding.
- [ ] Cookie helpers: parse `Cookie` header, build `Set-Cookie` (HttpOnly, Secure, SameSite).
- [ ] Request context type threaded to handlers: `{ req, res, url, params, query, user|null, roles }`.
- [ ] Error templates: add 403 + 500 (404 exists).
- [ ] Config/env loader: Ory endpoints, cookie/CSRF secret, JWKS location, ports.
## 1. Building blocks — extract from `html-css-foundation/` (no Ory needed; render mock data)
- [ ] Move `styles.css` + `auth.css` into `public/css/`; reconcile with existing `style.css`.
- [ ] Lucide icon sprite → `views/partials/icons.ejs`.
- [ ] App-shell partial (sidebar + topbar + content slot).
- [ ] Nav-tree partial — recursive, header/leaf × clickable/static, counts, `aria-current`.
- [ ] Filter-bar partial — GET form (search, segmented, selects, chips, daterange, applied pills).
- [ ] Data-table partial — sortable headers, row-select, badges, kebab row actions.
- [ ] Pagination partial — rows-per-page + page numbers, query-param driven.
- [ ] Form-field partials (input/label/hint/error) + auth-card partial.
- [ ] Menu/popover + theme-switch partials (pure CSS `details`/`summary`).
- [ ] Helper `composeNav(fragments, override, roles)` → merged, permission-filtered tree.
- [ ] Helper `parseListQuery(url)``{ q, filters, sort, page, pageSize }`.
- [ ] Helper `paginate(total, page, pageSize)` → page model.
- [ ] Unit tests for all helpers (first).
- [ ] Replace placeholder `index` with the app-shell dashboard.
## 2. Plugin host
- [ ] `definePlugin()` + manifest types: `id`, `basePath`, `nav[]`, `routes[] {method, path, permission?, handler}`.
- [ ] Discovery: scan `plugins/`, import each `plugin.ts` default export, validate.
- [ ] Router: match method+path under `basePath`, resolve path params, run permission gate, call handler with context.
- [ ] Per-plugin view resolver (`plugins/<id>/views/*.ejs`).
- [ ] Per-plugin static serving: `plugins/<id>/public/``/public/<id>/`.
- [ ] `config/menu.ts` central override: reorder/rename/hide/group + branding (app name, logo, default theme).
- [ ] Wire branding into the app shell.
- [ ] Tests: discovery, routing, param matching, permission gate, nav merge + filter.
## 3. Ory stack — compose + config
- [ ] `postgres` service (pinned tag); separate DB/schema per Kratos/Keto/Hydra.
- [ ] `kratos` service (pinned) + `migrate`; identity schema (traits: email, name).
- [ ] Kratos self-service flows (login, registration, recovery, verification, settings) → return URLs at our themed pages.
- [ ] Kratos OIDC/SSO providers (Google/Microsoft/SAML) config (placeholders + secrets via env).
- [ ] Kratos session settings (cookie name, lifespan, sliding refresh).
- [ ] Kratos tokenizer template `plainpages`: claims `{ sub, email, roles }`, `ttl ≈ 10m`, `jwks_url` signer, `claims_mapper_url` (Jsonnet reading `metadata_admin.roles`).
- [ ] Generate + mount the JWT signing JWKS; document key rotation.
- [ ] `keto` service (pinned) + `migrate`; namespaces in OPL (`role`, `group`, resource permissions).
- [ ] `hydra` service (pinned) + `migrate`; issuer + login/consent URLs → our app.
- [ ] Split dev (`compose.override.yml`) vs prod (`compose.yml`) wiring; health checks + `depends_on` ordering.
## 4. Auth — identity, session JWT, guards
- [ ] Kratos public client (fetch): init/get/submit flows, `whoami`, `whoami?tokenize_as=plainpages`.
- [ ] Kratos admin client (fetch): identity CRUD + `metadata_admin` update.
- [ ] Keto client (fetch): `check`, list/expand relations, write/delete tuples.
- [ ] Render Kratos flows: fetch flow → render fields against our themed pages → POST to `flow.ui.action` (Kratos handles its CSRF), map field errors/messages.
- [ ] SSO buttons → Kratos OIDC flows.
- [ ] Login completion: read roles from Keto → write `metadata_admin` projection → tokenize → set JWT cookie.
- [ ] JWT middleware: verify signature via cached JWKS, validate `exp`/`iss`/`aud` (+clock skew), build context (user, roles).
- [ ] JWKS fetch + cache + rotation handling.
- [ ] Guards: `requireSession` (validate JWT), `can(role)` (claim, in-process), `check(relation, object)` (live Keto).
- [ ] Session re-mint on TTL expiry (re-read roles from Keto).
- [ ] Logout: revoke Kratos session + clear cookie.
- [ ] Secure cookie flags; CSRF for our own POST forms.
- [ ] Tests: JWT verify (valid/expired/bad-sig), guard behavior, login→projection→tokenize flow (Ory mocked).
## 5. Built-in admin screens (writes go only to Keto/Kratos)
- [ ] Users: list (Kratos identities) with filter/sort/pagination; create/edit/deactivate/delete; trigger recovery.
- [ ] Groups: Keto subject sets — list/create/delete + membership management.
- [ ] Roles & permissions: Keto relations — assign roles to users/groups; "effective access" view via Keto expand.
- [ ] Wire into the menu (admin section, permission-gated).
- [ ] Tests: CRUD flows (Ory mocked) + permission gating.
## 6. Hydra — OAuth2/OIDC provider (can ship after the rest)
- [ ] Login-challenge handler: authenticate via Kratos session, accept/reject.
- [ ] Consent-challenge handler: show / auto-accept first-party, grant scopes, accept/reject.
- [ ] OAuth2 client registration (admin UI or CLI).
- [ ] Tests: authorization-code login+consent happy path; token + refresh.
## 7. Example plugin (reference)
- [ ] Reference plugin (e.g. people directory or scheduling): list page fetching upstream data, a form that forwards writes upstream, permission-gated nav.
- [ ] Verify the full plugin contract end-to-end against the README.
## 8. Testing & CI
- [ ] node --test units across helpers / router / nav / auth (tests-first throughout).
- [ ] **Playwright full E2E**: login (password + mocked SSO), menu filtering by role, users/groups/permissions CRUD, a plugin page, logout.
- [ ] E2E harness: bring up the full compose stack, seed Keto roles + a test identity, **tear down after**.
- [ ] Typecheck + tests green in Docker (`docker compose run --rm web …`).
## 9. Production, security, ops
- [ ] `compose.yml` prod: Ory + Postgres, secrets via env, no source mount.
- [ ] Security headers; secure/HttpOnly/SameSite cookies; CSRF; clock-skew tolerance.
- [ ] Optional revocation denylist for instant role/session revoke.
- [ ] Structured logging / basic observability. use @larvit/log for OTLP compability - but add subtasks and stuff for supporting incoming trace id etc from a reverse-proxy etc.
- [ ] JWT signing-key rotation runbook.
- [ ] Refresh README `Layout` + drop `_(planned)_` markers as pieces land.