Commit Graph

152 Commits

Author SHA1 Message Date
lilleman cfeee10fa8 Wrap each popover menu and give it a caller-named id
CI / full-gate (push) Successful in 2m43s
2026-08-05 01:58:11 +02:00
lilleman 5d9bdebf59 Close the popup menus on an outside click, via the popover API
CI / full-gate (push) Successful in 2m40s
2026-08-05 01:37:58 +02:00
lilleman 64d1387df2 Scope the profile-menu assertion to the menu itself
CI / full-gate (push) Successful in 2m38s
Mirror / github-mirror (push) Successful in 7s
2026-08-05 01:08:09 +02:00
lilleman 3c64621515 Remove the dead Profile link from the sidebar profile menu
CI / full-gate (push) Successful in 2m39s
2026-08-05 01:01:14 +02:00
lilleman 2b09635ed5 Hold the one-verb rule in AGENTS.md instead of a unit test
CI / full-gate (push) Successful in 2m38s
Mirror / github-mirror (push) Successful in 6s
2026-08-05 00:52:09 +02:00
lilleman 34b668d49f Use one verb per action in the English UI: sign in, sign out, create account
CI / full-gate (push) Successful in 2m39s
2026-08-05 00:34:05 +02:00
lilleman 46548ae758 Narrow the settings-cog test assertions and record the icon-registry contract
CI / full-gate (push) Successful in 2m37s
Mirror / github-mirror (push) Successful in 6s
2026-08-05 00:20:34 +02:00
lilleman 8f7ab55267 Remove the Settings cog and its Preferences menu from the sidebar footer
CI / full-gate (push) Successful in 2m37s
2026-08-05 00:08:35 +02:00
lilleman 7948596105 New todos
CI / full-gate (push) Successful in 7s
Mirror / github-mirror (push) Successful in 6s
2026-08-04 23:48:26 +02:00
lilleman 89a234b841 Add a todo for guarding the double-clicked submit without client JS
CI / full-gate (push) Successful in 2m38s
2026-08-04 10:40:37 +02:00
lilleman 93139ea058 Stability round two: no language links on POST-rendered pages, scoped observers, Vary only where it varies
CI / full-gate (push) Successful in 2m37s
2026-08-04 00:10:31 +02:00
lilleman 245d1ad5b5 Add i18n support: per-locale catalogs, URL-driven locale, translated core and examples
CI / full-gate (push) Successful in 2m37s
2026-08-03 22:37:27 +02:00
lilleman f38b5373bd Say user throughout, noting Ory's identity naming in the docs 2026-08-03 17:41:40 +02:00
lilleman 9966b6bd46 Record the authorization vocabulary decision in AGENTS.md 2026-08-03 17:41:40 +02:00
lilleman 04fe5b1e06 Cut the security-model threat table down to what the code cannot show 2026-08-03 17:41:40 +02:00
lilleman 3486e0ad00 Rename the Keto User namespace to Identity, matching Kratos 2026-08-03 17:41:40 +02:00
lilleman 8f9f79ac30 Document the users, groups and roles model in README 2026-08-03 17:41:40 +02:00
lilleman 4b4ac178ab Record the open CSRF token-binding decision as a todo item 2026-08-03 17:41:40 +02:00
lilleman 5a5803b265 Review fixes: denylist-conditional revoke, Ory secret wiring, exp guard test 2026-08-03 17:41:40 +02:00
lilleman a2204782fa Review fixes: re-mint path, real session lifetime, full secret checklist 2026-08-03 17:41:40 +02:00
lilleman eb5aafdfaa Document the auth security model in README 2026-08-03 17:41:40 +02:00
lilleman f5f4455b81 Browser E2E for the admin OAuth2-clients screen
CI / full-gate (push) Successful in 2m50s
2026-08-02 16:03:30 +02:00
lilleman ae1479f55b Renovate: set GITHUB_COM_TOKEN for authenticated github.com lookups
CI / full-gate (push) Successful in 2m33s
2026-08-02 15:43:38 +02:00
lilleman 7c66599f35 Todo and agents updates
CI / full-gate (push) Successful in 6s
Mirror / github-mirror (push) Successful in 6s
2026-08-02 14:11:33 +02:00
lilleman 145db5b4cd CI: auto-release a version tag when Renovate merges a dependency update
CI / full-gate (push) Successful in 2m33s
Mirror / github-mirror (push) Successful in 2s
2026-07-05 20:49:49 +02:00
lilleman 67d8a095a5 CI: Renovate for gated, self-hosted dependency updates
CI / full-gate (push) Successful in 2m30s
Mirror / github-mirror (push) Successful in 3s
2026-07-04 21:51:04 +02:00
lilleman 476ef6fce2 README-dockerhub: clone-free quick start - self-contained compose, Ory config extracted from the image
CI / full-gate (push) Successful in 2m30s
Mirror / github-mirror (push) Successful in 0s
2026-07-04 17:45:44 +02:00
lilleman 93fa751d6d Add README-dockerhub.md - the hand-maintained Docker Hub repo description
CI / full-gate (push) Successful in 2m29s
2026-07-04 17:37:02 +02:00
lilleman cc886936ed CI: sync release images to Docker Hub on vX.Y.Z tag push
CI / full-gate (push) Successful in 2m31s
Mirror / github-mirror (push) Successful in 2s
Release / retag-image (push) Successful in 14s
2026-07-04 17:08:31 +02:00
lilleman 058280934b CI: re-tag the gated image as semver + latest on vX.Y.Z tag push
CI / full-gate (push) Successful in 3m36s
Mirror / github-mirror (push) Successful in 2s
Release / retag-image (push) Successful in 2s
2026-07-04 08:35:48 +02:00
lilleman 50006dd1a7 CI: gate builds + pushes app image to Gitea registry tagged with the commit hash
CI / full-gate (push) Successful in 2m32s
Mirror / github-mirror (push) Successful in 0s
2026-07-03 16:50:37 +02:00
lilleman 6e60df7008 CI: mirror main + tags to GitHub after every merge to main; note true home in README
CI / full-gate (push) Successful in 2m28s
Mirror / github-mirror (push) Successful in 2s
2026-07-03 15:47:56 +02:00
lilleman 7a3161d3ff README: document the merge gate on main (PR-only, required CI, fast-forward-only); check off todo
CI / full-gate (push) Successful in 2m33s
2026-07-03 14:50:54 +02:00
lilleman 94654a2adc CI: Gitea Actions runs the full gate (ci.sh) on push to any branch except main; document runner setup
CI / full-gate (push) Failing after 0s
2026-07-02 14:24:19 +02:00
lilleman 535902e69b Split handleRequest into pipeline + internal route table; auth/OAuth2 endpoints become named handlers in src/auth/routes.ts 2026-07-02 13:05:54 +02:00
lilleman 8621cf24b6 todo: note the missing e2e for the admin plugin's OAuth2-clients (Hydra) screen 2026-07-02 09:10:55 +02:00
lilleman e8ea911b80 Move admin screens (users/groups/roles/oauth2-clients) into a drop-in example plugin; add the ctx.system capability surface 2026-07-02 08:01:15 +02:00
lilleman 2202bdbaa0 config/ becomes an empty drop-in mount; examples/ mirrors the mount dirs; plugins & config import the host via #plugin-api/#menu-config subpath imports 2026-07-01 23:19:48 +02:00
lilleman fe97c3854a Move reference plugin to examples/scheduling-plugin; plugins/ ships empty as a drop-in mount point, e2e bind-mounts the example 2026-06-27 00:02:26 +02:00
lilleman d8cf257940 Move plugin-contract.md into README's Building plugins section; remove docs/, repoint all references 2026-06-26 23:12:40 +02:00
lilleman 2b88bf1c0d Add todo.md: src reorg done, plus logger redundancy + i18n follow-ups 2026-06-24 00:24:52 +02:00
lilleman a9f25a7692 Remove completed todo.md + html-css-foundation mockups; strip dead §N phase refs from comments/docs (simplify visual E2E to drop the mockup-comparison oracle) 2026-06-23 22:49:28 +02:00
lilleman e22d24aa8a §10 - one menu everywhere (buildPluginChrome) + shell on every page; instructional starter dashboard; Kratos-native email docs
Collapse the three nav builders into buildPluginChrome: chrome.bestHref does longest-prefix matching so deep admin routes mark their leaf. Delete adminNav; buildConfirmModel and the admin model builders take the resolved nav. The same role-filtered sidebar now renders signed in or out, collapsing to a burger on narrow screens.

shell.ejs gains menu (default true; menu:false -> single-column .app-bare), docTitle (separate <title> from the topbar, so the body keeps the single <h1>), and hideSignIn (suppress the footer Sign-in on auth pages to avoid a login loop). auth/home/landing now render inside the shell.

Dashboard is a replaceable instructional starter (definePlugin snippet, no mock data). Email stays delegated to Kratos: documented its built-in courier.template_override_path instead of adding web-side SMTP.
2026-06-23 21:26:00 +02:00
lilleman 7bdeb24b7f §10 public pages + menu items, the blessed explicit alias (todo §10); a plugin may mark a page and its menu option public. A no-permission route/nav node is already anonymous-reachable, so per the human's pick this BLESSES that as a first-class, explicit choice (keep the default; add an explicit alias — not a secure-by-default flip). New optional public?: boolean on Route (src/plugin.ts) + NavNode (src/nav.ts) = "open to everyone, signed in or not", honored outright in isAuthorized (router.ts) + filterByRoles (nav.ts), and MUTUALLY EXCLUSIVE with permission — discovery shapeError recursively rejects a route/nav node setting both, failing the boot loud (never silently picks one). public is filter-only (toRenderNode never emits it). The shell (views/partials/shell.ejs) now renders a Sign in link instead of the profile/sign-out block for an anonymous visitor, so a public page in the native shell (ctx.chrome; ctx.user may be null) isn't a broken "Guest / Sign out". Reference plugin demos it: a public /scheduling Overview route + a public "Overview" nav child (the "Scheduling" header now shows for everyone), the shifts list still behind scheduling:read. Hardened the latent gap the shell newly leans on: claimsToUser rejects an empty email like it does an empty sub. Tests-first (348 → 354 units): router/nav/discovery (public open + reject-both + loads), shell (anon → Sign in, no logout form), app (public route anon-200), shifts (overview handler), jwt-middleware (empty email). Docs: plugin-contract.md ("Public pages & menu items" + route shape + shape-error note) + README (menu system + reference snippet). E2E: visual.spec asserts the public Overview is anon-200 + shown in the member's nav while the gated Shifts redirects/filters. stability-reviewer: APPROVE, no Critical/High/Medium (addressed its one Low — the empty-email hardening). typecheck + 354 units + full scripts/ci.sh gate (visual 10 · auth 1 · oauth 2 · full 7) green. 2026-06-20 18:12:46 +02:00
lilleman 7787ed4ea4 §10 split landing into a public "/" + gated "/dashboard", both plugin-replaceable (todo §10 follow-up); per human feedback, "/" is now an ungated public landing (default views/home.ejs: brand + intro + prominent Log in / Create account links, or "go to dashboard" when signed in) and "/dashboard" is the gated post-login app home (anonymous → /login?return_to=/dashboard). Both are fully replaceable via two optional RouteHandlers on PluginManifest — home? (public /) and dashboard? (gated /dashboard) — rendered against the plugin's own views with the native shell via ctx.chrome (full route parity: HEAD, void-return, response hooks, fresh CSRF cookie; a home handler is public so ctx.user may be null). Single-slot + loud: findConflicts errors on >1 owner of either slot (new "home"/"dashboard" kinds), discovery rejects a non-function handler, and "dashboard" is reserved so a plugin folder can't shadow it ("/" can't be shadowed — route paths carry the /<id> prefix). Post-login + already-signed-in redirects and the global Dashboard/People nav hrefs moved to /dashboard. Tests-first (348 units): public-/ + gated-/dashboard + dual plugin-override in app.test; per-slot conflict in plugin.test; non-function/reserved/two-owners in discovery.test. Docs: plugin-contract "The landing pages" section + README. E2E: visual.spec plants a session for /dashboard design-system tests + a cookie-free public-landing test; full-flow repointed to /dashboard. stability-reviewer: APPROVE, no Critical/High/Medium. typecheck + 348 units + visual(10) + full-flow(7) green. 2026-06-20 17:43:01 +02:00
lilleman 2eb5b84ccf §10 gate the dashboard + make "/" replaceable by a plugin (todo §10); "/" is now gated to a signed-in session (anonymous → /login via loginRedirect, query preserved as return_to) and fully replaceable via a new optional home?: RouteHandler on PluginManifest — a handler with the same signature as any route (the most ergonomic shape). The app.ts "/" branch gates first, then renders the single home plugin's handler against its own views/ with the native shell via ctx.chrome (HEAD / void-return / response-hook parity with a plugin route), else the built-in mock-data People list. home mounts at the root above the /<id> namespace, so it can't shadow or be shadowed by a built-in route. Single-slot + loud: findConflicts errors on >1 home (new "home" kind), discovery rejects a non-function home — never last-write-wins. Tests-first (338 → 344 units): app.test.ts gate + home-override; plugin.test.ts home conflict; discovery.test.ts home validation. Docs: plugin-contract.md (manifest table + "The dashboard (home)" section + conflict row), README. E2E: visual.spec plants a dev-signed session (the anonymous plugin-gate probe uses the cookie-free request fixture); all e2e web/gateway healthchecks repointed from the gated "/" to /public/css/styles.css. stability-reviewer: APPROVE, no Critical/High/Medium. typecheck + 344 units + visual(9) + full-flow(7) E2E green. 2026-06-20 17:18:30 +02:00
lilleman df53106a5a §9 test cleanup (todo §9); dropped the one genuine §9-era test overlap. app.test.ts had two /login?return_to= tests for the same surface — the §6 "bakes the return target into the Kratos flow init (OAuth bounce)" and the §9 "first-party deep link wrapped through /auth/complete; absolute target passes through as-is". The §9 test subsumes it: its middle assertion already proves an absolute /oauth2/login?login_challenge= target is handed to initBrowserFlow unchanged (the exact §6 OAuth-bounce contract, labeled as such in the test name + inline comment), plus the new host-relative-wrap + protocol-relative cases. Removed the redundant standalone §6 test, zero coverage lost. The §9 unit files (security-headers/denylist/logger/safe-url + gen-jwks rotateJwks) and the per-field config toggles (SERVICE_NAME/LOG_*/OTLP_*/REVOCATION_*/JWT_CLOCK_SKEW/ORY_TIMEOUT) are one-concern matrices following the file's per-field pattern — no fat (§3 don't-merge-across-distinct-concerns rule). Tests-only, no production code (per the §6/§7/§8 precedent, no stability reviewer). 339 → 338 units; typecheck + tests green. 2026-06-20 16:50:26 +02:00
lilleman c28b3056ca §9 comment-density pass over the §9 accretion; two targeted dedupes (the rest authored dense, per the §6/§7/§8 precedent). (1) logger.ts: the requestStore declaration comment restated the file header (ambient per-request Log → deep modules join the trace without threading) and the accessor docs below it — collapsed to a one-line label. (2) security-headers.ts: dropped the redundant 'applied to every response' note above securityHeaders() (the file header + function name already say it). No stale forward-refs remain; README §9 sections left untouched (concise, _(planned)_ already swept in §8). Docs/comments-only; typecheck + 339 units green. 2026-06-20 16:45:58 +02:00
lilleman 66ea68a91b §9 whole-project arch+product review pass (todo §9); ran systems-architect + product-owner on the whole project (no Critical/High — a converged scaffold) and addressed the in-scope §9 customer-facing/security findings. (1) return_to deep-link login, open-redirect-safe: a gated request hit while signed out (plugin-route gate, requireSession, requireAdmin) bounces to /login?return_to=<host-relative path> via new loginRedirect(ctx) (GET/HEAD, skips /); /login bakes it into the Kratos flow — a host-relative target is wrapped through <origin>/auth/complete?return_to=<path> so the JWT mints before landing, an absolute target (§6 OAuth2 login challenge) passes to Kratos as-is; /auth/complete redirects to the requested page. (2) safeUrl()+localPath() in new pure src/safe-url.ts: safeUrl sanitises an untrusted href/src to relative-or-http(s) (else "#"), exported via plugin-api.ts (closes the contract's "planned for §9" pointer); localPath is the host-relative redirect-allowlist guard for return_to, re-checked at both /login and /auth/complete. (3) honest 503 on Ory-unreachable sign-in (views/503.ejs) instead of the misattributed catch-all 500; expired-flow 4xx still restarts. Tests-first throughout; stability-reviewer APPROVE (addressed its Medium — scoped the 503 catch so a template bug hits the 500 with a stack, not a 503). typecheck + 339 units + full scripts/ci.sh gate green. Deferred with justification: the app.ts route-table refactor (standalone change + §10 prereq), mock dashboard + public-page blessing (§10 lines 139/140), success-flash (known). 2026-06-20 16:39:09 +02:00
lilleman 1118d7a9f7 §9 refresh README Layout (todo §9); the _(planned)_ markers were already dropped as each piece landed (none remain; Status paragraph reflects the built state). Refreshed the drifted Layout block: added the three source modules it was missing — fetch-timeout.ts (withTimeout, the Ory outbound-call deadline wrapper, §8), guards.ts (requireSession/can/check in-handler authz + GuardError, §4), hooks.ts (runBoot/Request/ResponseHooks plugin lifecycle, §2) — plus scripts/ci.sh (the full CI gate, §8). Cross-checked mechanically: every non-test src/*.ts and every top-level dir (bar node_modules) now has a line; public/plugins/examples descriptions still match their contents. Docs-only. 2026-06-20 15:58:37 +02:00