Commit Graph

159 Commits

Author SHA1 Message Date
renovate-bot 62601a9e90 chore(deps): update playwright to v1.62.1
renovate/artifacts Artifact file update failure
CI / full-gate (push) Failing after 1m21s
Release-Bump: minor
2026-08-01 04:18:13 +00:00
renovate-bot 6559f40142 chore(deps): update renovate/renovate docker tag to v44
CI / full-gate (push) Successful in 2m38s
Mirror / github-mirror (push) Successful in 6s
Release-Bump: major
v0.1.0
2026-07-31 04:18:12 +00:00
renovate-bot d3154819f8 chore(deps): update renovate/renovate docker tag to v43.288.0
CI / full-gate (push) Successful in 2m34s
Mirror / github-mirror (push) Successful in 6s
Release-Bump: minor
v0.0.12
2026-07-30 04:17:51 +00:00
renovate-bot 1cba6d470c chore(deps): update axllent/mailpit docker tag to v1.30.6
CI / full-gate (push) Successful in 2m41s
Mirror / github-mirror (push) Successful in 5s
Release-Bump: patch
v0.0.11
2026-07-29 04:18:24 +00:00
renovate-bot 194c090bd1 chore(deps): update renovate/renovate docker tag to v43.285.3
CI / full-gate (push) Successful in 2m37s
Mirror / github-mirror (push) Successful in 5s
Release-Bump: minor
v0.0.10
2026-07-28 04:17:59 +00:00
renovate-bot ff5094f7e9 chore(deps): update renovate/renovate docker tag to v43.281.1
CI / full-gate (push) Successful in 2m33s
Mirror / github-mirror (push) Successful in 5s
Release-Bump: minor
v0.0.9
2026-07-27 04:18:12 +00:00
renovate-bot 419ee1750b chore(deps): update renovate/renovate docker tag to v43.280.5
CI / full-gate (push) Successful in 2m35s
Mirror / github-mirror (push) Successful in 5s
Release-Bump: patch
v0.0.8
2026-07-26 04:18:07 +00:00
renovate-bot cedac950be chore(deps): update renovate/renovate docker tag to v43.280.4
CI / full-gate (push) Successful in 2m33s
Mirror / github-mirror (push) Successful in 4s
Release-Bump: minor
v0.0.7
2026-07-25 04:17:42 +00:00
renovate-bot ff455f1ef2 chore(deps): update axllent/mailpit docker tag to v1.30.5
CI / full-gate (push) Successful in 3m59s
Mirror / github-mirror (push) Successful in 4s
Release-Bump: patch
v0.0.6
2026-07-25 01:02:53 +00:00
renovate-bot 5bd26d773d chore(deps): update renovate/renovate docker tag to v43.270.0
CI / full-gate (push) Successful in 2m33s
Mirror / github-mirror (push) Successful in 2s
Release-Bump: minor
v0.0.5
2026-07-19 04:17:22 +00:00
renovate-bot 4f60bad119 chore(deps): update axllent/mailpit docker tag to v1.30.4
CI / full-gate (push) Successful in 2m39s
Mirror / github-mirror (push) Successful in 2s
Release-Bump: patch
v0.0.4
2026-07-18 21:13:02 +00:00
renovate-bot aea568ea1c chore(deps): update renovate/renovate docker tag to v43.252.1
CI / full-gate (push) Successful in 2m26s
Mirror / github-mirror (push) Successful in 3s
Release-Bump: minor
v0.0.3
2026-07-06 04:17:39 +00: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
renovate-bot 7c39056188 chore(deps): update node.js to v24.18.0
CI / full-gate (push) Successful in 2m28s
Mirror / github-mirror (push) Successful in 3s
2026-07-05 07:02:10 +00:00
renovate-bot 9719586f51 chore(deps): update axllent/mailpit docker tag to v1.30.3
CI / full-gate (push) Successful in 2m29s
Mirror / github-mirror (push) Successful in 3s
2026-07-05 06:56:22 +00: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
v0.0.2
2026-07-04 17:08:31 +02:00
lilleman e3e582afef README: document the one-time org-package-to-repo link for the Packages tab
CI / full-gate (push) Successful in 2m29s
Mirror / github-mirror (push) Successful in 0s
2026-07-04 09:39:10 +02:00
lilleman 0644ec8f5a CI: nightly registry cleanup - prune hash images not release-tagged nor a branch head
CI / full-gate (push) Successful in 2m40s
Mirror / github-mirror (push) Successful in 0s
2026-07-04 09:18:07 +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
v0.0.1
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 c8981c12d3 CLAUDE.md: import AGENTS.md so Claude Code loads it every session
CI / full-gate (push) Successful in 2m28s
2026-07-03 14:55:13 +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 c78770a713 AGENTS.md: document the todo.md task workflow (interview scope, stability-review loop, PR flow) 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 8afaeccf2c AGENTS.md: sharpen simplicity/typing/comment guidance, prefer state-in-URL over POST 2026-07-02 13:05:54 +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 4adf14f386 docs: restructure README around the plugin-author path, add examples/README, genericize the plugin walkthrough 2026-06-30 23:30:13 +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 de22f51c12 Organize src/ into concern folders (http, auth, admin, plugin-host, ui); co-locate tests, move plugin-api barrel into plugin-host, sync docs + AGENTS layout 2026-06-24 00:23:55 +02:00
lilleman 6d316c4888 Drop .mjs: rename e2e/dev mock servers to .ts, update refs, document the convention in AGENTS.md 2026-06-23 23:54:42 +02:00
lilleman 913bd6813a Consolidate E2E into e2e-tests/ (Dockerfile + compose.{visual,auth,oauth,full,devstack}.yml, WORKDIR /e2e-tests); move ci.sh to repo root 2026-06-23 23:48:05 +02:00
lilleman bb612baa2c Reorganize README for two readers: first-run Quick start + nested ToC, overview-first ordering; document the convention in AGENTS.md 2026-06-23 23:15:24 +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 af097a8885 Verification/recovery: guard the OTP code field against a pasted space (numeric inputmode + digits-only pattern + one-time-code autofill); Kratos doesn't trim, so a space-padded code was rejected as 'invalid or already used' 2026-06-21 23:26:23 +02:00
lilleman 166f38d5dd Merge pull request 'appurl-canonical-host' (#1) from appurl-canonical-host into main
Reviewed-on: #1
2026-06-21 22:18:46 +02:00
lilleman c8b4c3c23b Fix 500 on /login when a Kratos session exists but no app JWT (session_already_available)
Repro: register a new account, then click back from the password/verification step to /login →
500. Registration's `session` hook signs the user in at Kratos but routes to the verification UI,
not /auth/complete, so they hold a Kratos session with NO app JWT — ctx.user is null, the "already
signed in -> /dashboard" short-circuit can't fire, and initialising a login flow makes Kratos return
400 `session_already_available`. The flow-init catch only handled 403/404/410 and 5xx, so the 400
fell through to `throw` -> catch-all 500.

Recover instead: on `session_already_available` (already authenticated at Kratos), 303 to
/auth/complete to mint the JWT from the live session, preserving return_to. A genuinely unexpected
Kratos 400 still surfaces as 500. Verified live: /login (Kratos session, no JWT) now 303s to
/auth/complete, which mints plainpages_jwt and lands on /dashboard.

Also default LOG_LEVEL to debug in the dev override (compose.override.yml) for verbose local logs.

Tests-first: app.test asserts the session-race recovers to /auth/complete (return_to carried) while
an unrelated 400 stays a 500. typecheck + 361 units green.
2026-06-21 22:04:55 +02:00
lilleman 1d198acc97 Canonical host via APP_URL: stop login dumping users on /error from a host mismatch
The from-scratch dev login was broken: open the banner's http://localhost:3000, sign
in as the seeded admin, and you landed on http://127.0.0.1:3000/error "Page not found".
Root cause: the banner/APP_URL said localhost but kratos.yml hard-coded 127.0.0.1, and a
host-scoped Kratos CSRF cookie can't cross localhost<->127.0.0.1, so the cross-host login
POST lost it; Kratos redirected to its error sink, which the app had no route for (404).

Make APP_URL the single source of truth for the public host:
- Canonical-host redirect (app.ts): when APP_URL is set, an off-host GET/HEAD visitor is
  308'd to it (path+query kept) before a flow starts, so the browser, the themed forms and
  the cross-origin Kratos POST share one cookie host. After /public/ so static/health
  checks stay host-agnostic; GET/HEAD only so a 308 never replays a cross-host POST.
- Opt-in (no NODE_ENV / no magic default): unset => no redirect, so a prod deploy that
  forgets APP_URL can't bounce real users to a stale default. The dev stack sets it.
- kratos.yml browser URLs default to localhost (match APP_URL's dev value) and derive from
  ${APP_URL} via compose.override.yml; SERVE_PUBLIC_BASE_URL keeps the dev Ory port.
- Real /error page (views/error.ejs) replaces the catch-all 404 for genuine flow errors.

Tests-first: config (opt-in/validated), app (308 on mismatch, no-redirect on match, static
host-agnostic, POST untouched, /error page), updated kratos.test host pins. New devstack
regression (e2e/devstack-login.spec + compose.e2e-devstack.yml) drives the plain
docker-compose-up topology on the host network: login from localhost works and 127.0.0.1 is
canonicalised; wired into scripts/ci.sh. typecheck + 360 units + full ci.sh (visual 10 ·
auth 1 · oauth 2 · full 7 · devstack 2) green.
2026-06-21 21:52:20 +02:00
lilleman 58398481ca §10 review pass: address the architecture + product reviewers (todo §10); hide the gated Dashboard nav node from anonymous visitors in buildPluginChrome (a no-permission link to /dashboard only dead-ended them at /login) and dedup it into a shared DASHBOARD_NAV (admin-nav.ts, reused by chrome + adminNav). New chrome.signInHref bakes the current page in as return_to for the shell's anonymous Sign-in link (shell.ejs + reference overview.ejs), mirrored as optional ShellModel.signInHref so the typed builder is complete. ctx.chrome is now a lazy, memoized getter (context.ts chrome option = a factory) so a json/redirect handler or the public "/" with a standalone home never composes the global menu — app.ts passes the app-level memoized factory at every site. Default /dashboard prints a "Starter dashboard" note framing the mock-data home as a replaceable demo (signals its inert affordances); stale "until §4" comments fixed. RESERVED_PLUGIN_IDS drift-guard test derives the built-in segments from AUTH_FLOWS + ADMIN_*_BASE + host literals (home stays deliberately unreserved). Refreshed the stale plugin-contract status blurb and documented the chrome.*→partials/shell mapping. Reviewers: architecture + product APPROVE (no addressable findings remain), stability APPROVE (no Critical/High/Medium). typecheck + 356 units + visual(10) + full-flow(7) E2E green. 2026-06-21 01:19:40 +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