Files
plainpages/todo.md

9.2 KiB
Raw Blame History

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.

North-star / MVP. Done = a developer can clone, run one command, get a working register/login, and start hacking on their own plugin — no manual key generation, no hand-edited Ory config, no DB setup. Everything below serves that; the one-command bootstrap (§3) and the example plugin (§7) are what make the MVP real. Hydra/SSO are explicitly post-MVP.

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 from lucide-static (dep added) → views/partials/icons.ejs; serve/inline only the icons used.
  • 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

  • Specify the plugin contract (big job, do first — it's the product's main API surface). Write it down as the authoritative reference: the full manifest shape; the RequestContext handed to handlers and what's guaranteed stable; contract versioning (a apiVersion/engines-style field so a plugin declares the host it targets, and the host refuses or warns on mismatch); conflict rules (two plugins claiming the same basePath, nav slot, or permission name → defined, loud resolution, not last-write-wins); the local dev/test story (how an author runs + tests one plugin in isolation against the host). Audience is experienced devs: optimise for a powerful, predictable, clearly-documented API. Crash-isolation (a bad plugin can't take down the host) is a nice-to-have, not a blocker — fail loud at boot/discovery over sandboxing at runtime.
  • 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 (secrets via env). None enabled by default — a clean clone runs password-only; a provider activates purely by supplying its env creds.
  • 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.
  • One-command bootstrap (the MVP bar): docker compose up brings up web + all Ory services + Postgres with zero manual prep. Commit working default Ory configs; auto-run migrations on first boot; auto-generate the JWKS signing key if absent; seed an admin identity + its Keto roles + a demo password (admin/admin) idempotently. Land an OPL/namespace bootstrap so Keto answers checks out of the box.
  • First-run banner / log line printing the login URL + seeded admin creds, with a clear "change these before production" warning.
  • Document the only things that can't be auto-generated: third-party SSO provider client id/secret (optional — password login works without them) and production secrets (real cookie/CSRF secret + signing key, supplied via env, replacing the dev throwaways). Everything else must work from a clean clone.

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. Render per configured provider only: derive the list from Kratos' enabled OIDC providers (no creds ⇒ no button); hide the whole SSO section when none are configured. No code change needed to add/remove a provider — config only.
  • 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.