tech-debt(auth): replace cookie-promotion glue with a proper session-based auth model #522
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
PR #521 (closing #520) added an
AuthTokenCookieFilterto the backend that promotes anauth_tokencookie to a Basic-authAuthorizationheader so browser-direct/api/*calls authenticate in production (Caddy doesn't translate the cookie the way the dev Vite proxy does).The fix works. It unblocks the deploy. But the underlying auth model is not architecturally sound. This issue captures the tech-debt and frames the requirements for a proper replacement.
Why the current model is debt
Basic <base64(email:password)>string with 24 h TTLSameSite=stricton the cookie (CSRF protection iscsrf.disable()inSecurityConfig).SameSite=lax, CORSallowedOriginsexpanded, third-party SSO added) silently opens CSRF on every write endpoint.login/+page.server.ts,hooks.server.ts,vite.config.ts,AuthTokenCookieFilter, and the Caddy/api/*routing decision./api/users/meround-trip to validate credentials. There is no first-class login endpoint.User need
Functional requirements
FR-AUTH-001 — There SHALL be a first-class
POST /api/auth/loginendpoint that accepts email+password, validates againstapp_users, and on success establishes a session.FR-AUTH-002 — Session establishment SHALL set an opaque session identifier (not a credential) in an
HttpOnly,SameSite=strict,Secure(on HTTPS) cookie. The session record SHALL be stored server-side.FR-AUTH-003 — There SHALL be a
POST /api/auth/logoutendpoint that invalidates the server-side session record. After calling it, requests carrying the same cookie SHALL be treated as unauthenticated (401), even before the cookie'sMax-Ageelapses.FR-AUTH-004 — A password change (existing flow in
PasswordResetService+ future "change own password" if added) SHALL invalidate all other active sessions for the affected user. Optionally, the current session may be preserved or rotated.FR-AUTH-005 — An admin SHALL be able to forcibly invalidate all sessions for a target user (operator break-glass for "this account is compromised"). UI surface deferred — backend capability + audit log entry are in scope here.
FR-AUTH-006 —
AuthTokenCookieFilterand theBasicauth machinery downstream of it SHALL be removed once the new model is live. The SvelteKit login action SHALL stop building and storing aBasic ...cookie value.FR-AUTH-007 — The deployment story SHALL remain the same: SvelteKit form-action login, no client-side JWT handling in browser JS, no extra round-trips per request.
Non-functional requirements
NFR-SEC-101 — No user password (plaintext or base64-encoded) SHALL be present in any cookie sent to the browser at any point after login completes.
NFR-SEC-102 — A stolen session cookie SHALL become inert within ≤ 60 seconds of an operator-initiated invalidation (logout, password change, force-logout).
NFR-SEC-103 — Cross-Site Request Forgery protection on state-changing endpoints (
POST,PUT,PATCH,DELETE) SHALL be enforced by two independent mechanisms: a strict-SameSite cookie AND a server-issued CSRF token bound to the session. Either alone is insufficient.NFR-PERF-101 — Authenticated request overhead SHALL NOT exceed 5 ms (p95) added by session lookup compared to the current in-memory Basic-decode path.
NFR-OBSV-101 — Every login attempt (success and failure), logout, and admin-forced-logout SHALL produce a structured audit log entry (
AuditService) including: timestamp, user ID (or supplied email on failure), source IP, user-agent fingerprint, outcome.NFR-COMPAT-101 — Existing dev tooling SHALL continue to work without code changes:
vite.config.tsproxy, the dev e2e test profile, and thee2eprofile's deterministic admin seed.Acceptance criteria
Constraints & open questions
The existing stack already includes Spring Session JDBC (
spring-session-jdbcperCLAUDE.md→ "Tech Stack") andapp_userslives in Postgres. The infrastructure to back FR-AUTH-001..005 is in-house — no new managed services required. Whether to use Spring Session, JWT (access + refresh), or a custom session table is an implementation decision for the developer.OQ-AUTH-001 — Should logout invalidate ALL of the user's sessions (single-session model) or just the current one (multi-device model)? Impacts FR-AUTH-003 ACs.
OQ-AUTH-002 — Is there a session-idle-timeout requirement separate from the 24 h absolute lifetime? (Background: the current implementation has only
Max-Age, no idle timer.) Impacts NFR-SEC-102.OQ-AUTH-003 — Does the "admin force-logout" capability need a UI surface in this issue, or is the backend capability + audit log sufficient (UI deferred)?
OQ-AUTH-004 — Browser remembers-me checkbox: do we want it now (longer absolute session lifetime when checked) or defer?
Priority & sizing
v1.1.0. Not a blocker forv1.0.0first deploy — #521 is the unblocker. But strongly recommended within the first release cycle after first prod deploy because every day in production with the current model is a day where a stolen cookie equals stolen credentials.Out of scope (won't this issue)
Discovered
During the production-deploy bootstrap of #497. PR #521 added a cookie → header translator that papers over a missing session design. This issue captures the design debt and frames the proper replacement as testable requirements.
— Elicit (req engineering)
🏛 Markus Keller — Senior Application Architect
Observations
backend/pom.xmldoes not declarespring-session-jdbc— andV2__drop_spring_session_tables.sqlexplicitly drops thespring_session/spring_session_attributestables with the comment: "Spring Session JDBC was included as a dependency but never used."CLAUDE.mdis stale here, and the issue inherited that staleness. This is not a one-line decision anymore: re-adding the dependency, the migration, and the configuration is part of the work.SecurityConfig.java:54carries a clear load-bearing comment about CSRF being disabled only because SameSite+CORS holds the fort. That comment is good architecture documentation, and the new design replaces it cleanly — exactly the kind of debt removal an ADR should record.AuthController(/api/auth/forgot-password,/reset-password,/register) is the obvious home for/api/auth/login+/api/auth/logout. No need to invent a new package.AuditServicealready supports rich audit kinds via theAuditKindenum —LOGIN_SUCCESS,LOGIN_FAILED,LOGOUT,ADMIN_FORCE_LOGOUTare additive, low-risk additions.Recommendations
SessionRepository.deleteById()andfindByPrincipalName()(viaFindByIndexNameSessionRepository) — both needed for FR-AUTH-003/004/005. A custom table reinvents indexing, cleanup, serialization, and concurrent-access semantics for no benefit.CookieCsrfTokenRepository.withHttpOnlyFalse()). Synchronizer pattern + SameSite=strict cookie is the textbook "two independent mechanisms" that NFR-SEC-103 calls for. Don't write a custom CSRF filter.docs/architecture/c4/seq-auth-flow.puml(rewrite — cookie promotion is gone); new ADR indocs/adr/;CLAUDE.mdsecurity section (update permissions ifADMIN_SESSIONor similar lands);docs/ARCHITECTURE.mdif the permission table grows. Plus update the stale "Spring Session JDBC" line inCLAUDE.mdregardless. It misled this issue.userdomain (or a newauthpackage). Don't letSessionRegistryServiceleak into other services — admin force-logout calls go throughUserServiceor a dedicatedAuthService, not directly into the session repo.Open Decisions
userpackage, or extract a newauthpackage? Cost of extendinguser: the package already holdsAuthController,InviteController,PasswordResetService— addingLoginController,SessionServicekeeps it cohesive but pushes the package size up. Cost of extractingauth: cleaner separation, but introduces a cross-domain call (AuthService → UserService) and one more module to maintain. Recommend extendinguserfor now; extract when the package exceeds ~25 files.— Markus
👨💻 Felix Brandt — Senior Fullstack Developer
Observations
AuthController.javaalready has/api/auth/{forgot-password,reset-password,register}— adding/api/auth/login+/api/auth/logoutslots in naturally.frontend/src/routes/login/+page.server.tsbuilds Basic auth client-side and round-trips to/api/users/meto validate. That round-trip disappears once the backend has a real login endpoint that returns the user payload in the response body.frontend/src/hooks.server.ts:49-67(userGrouphandle) re-fetches/api/users/meon every SSR request to populateevent.locals.user. With server-side sessions that store the user's principal, this can be replaced by reading the principal directly from the session — eliminating one HTTP round-trip per page render.frontend/src/hooks.server.ts:70-113(handleFetch) and the Vite proxy header-injection logic invite.config.tsboth exist solely to injectAuthorization: Basicfrom the cookie. Both disappear when the cookie carries an opaque session ID that the backend recognizes natively.frontend/src/routes/logout/+page.server.tscurrently just callscookies.delete('auth_token'). It must POST to/api/auth/logoutfirst, then delete the cookie — otherwise the server-side session record lingers.Recommendations
AuthControllerTest:POST /api/auth/loginwith valid credentials returns 200 + user JSON + setsSESSIONcookie. Red first.INVALID_CREDENTIALS(add it).POST /api/auth/logoutwith active session returns 204 and removes the session record. Red first.POST /api/auth/logoutwithout session returns 401.spring_sessiontable): full login → authed request → logout → 401 on next request.PasswordResetServiceTest: afterresetPassword(), allspring_sessionrows for the user are deleted. Red first — this behavior doesn't exist today.LoginPageServerTest(existing pattern in+page.server.test.tsfiles) — login action callsPOST /api/auth/login, no longer hits/api/users/me./api/auth/login: request{ email, password }, response is the existingAppUsershape (same as/api/users/me). Reuse the type — drives the same TypeScript binding via OpenAPI regen.AuthTokenCookieFilteras a fallback. FR-AUTH-006 explicitly says "removed once new model is live." Half-removed filters are how this kind of debt comes back. Delete the class and its test in the same commit that lands the new login endpoint.ErrorCode.INVALID_CREDENTIALSandCSRF_TOKEN_MISSINGin the same PR — frontenderrors.ts+messages/{de,en,es}.jsonupdates. The issue's AC403 with code CSRF_TOKEN_MISSINGrequires this exact code to exist.@RequirePermissiondoes NOT apply to/api/auth/loginor/api/auth/logout. Login is pre-authentication; logout authenticates via session, not a permission. Add to thepermitAll()matchers inSecurityConfig.formLogin(...)fromSecurityConfig— once/api/auth/loginexists, the implicit Spring Security form-login filter is dead code with a confusing surface (it still accepts POST to/login).Open Decisions
/api/auth/loginresponse include the CSRF token in a JSON field, or set it via a separateXSRF-TOKENcookie? Cookie approach (Spring's defaultCookieCsrfTokenRepository.withHttpOnlyFalse()) is one less thing for the frontend to manage and works withuse:enhanceforms. JSON field is more explicit but requires the SvelteKit login action to forward the token into a cookie itself. Recommend the cookie approach unless Nora flags a leakage concern — let her decide.— Felix
🛡 Nora Steiner — Application Security Engineer
Observations
login/+page.server.ts:46storesBasic ${btoa(email:password)}inauth_token. Anyone reading the cookie (XSS bypassing HttpOnly via a browser bug, devtools on a shared machine, a compromised browser extension) gets the user's password directly. The 24hMax-Agemakes this worse: rotating the password does not invalidate the cookie because the cookie is the password. This is a P1 issue, not tech debt — and the issue is correct to flag it.secure: isHttpsruntime branch inlogin/+page.server.ts:50is a footgun. If anything ever serves this app over HTTP in production (misconfigured reverse proxy, internal load balancer terminating TLS upstream of the SvelteKit node), the cookie ships in cleartext. Defaults should besecure: truealways; dev gets HTTPS via mkcert/Caddy local.SecurityConfig:54disables CSRF. The load-bearing comment is honest but the defense is single-layer: SameSite=strict alone. The fail-mode is silent: a cookie config change (e.g., someone "fixing" Safari cross-domain quirks by flipping tolax) opens every POST/PUT/PATCH/DELETE to CSRF with no observable signal.PasswordResetService.resetPassword()updates the password hash and the token's used flag — that's it. The user's oldauth_tokencookie (with the OLD password's base64) still authenticates for up to 24h because the backend never sees the password; it sees the Basic header and does a fresh bcrypt comparison each request. Wait — verify this: if the old cookie contains base64 ofemail:OLD_password, and the bcrypt compare runs against the new hash, the cookie should fail. So technically it does invalidate. But the user has no way to force logout, and a compromised cookie carrying the new password (if it leaked post-reset) is still good. The lack of server-side session state means there's no kill switch.logout/+page.server.tsjustcookies.delete(). A stolen cookie from before logout still authenticates against the backend. The operator has no way to revoke.spring_sessiontables were dropped in V2 — issue's "infrastructure is in-house" claim is stale. This affects the L sizing.Recommendations
Secureflag unconditional in production. Drop theisHttpsbranch. Useapplication-dev.yamlprofile to override only in dev — never a runtime check onurl.protocol.CookieCsrfTokenRepository.withHttpOnlyFalse(). This is the standard synchronizer-token pattern: server setsXSRF-TOKEN(readable by JS), client must echo it inX-XSRF-TOKENheader on writes. Spring Security wires it automatically — write the test that provesPOST /api/personswithout the header returns 403, then enable.ErrorCode.CSRF_TOKEN_MISSINGis mandated by the AC — add toErrorCode.java,errors.ts, and all three i18n files. Also addINVALID_CREDENTIALSandSESSION_EXPIRED.PasswordResetService.resetPassword()in this issue. Issue currently only covers FR-AUTH-004 (change-own-password). Add an explicit FR (or amend FR-AUTH-004 to cover both): afterresetPassword()and after any password change, delete allspring_sessionrows for that user. Failing test first.ErrorCode.SESSION_EXPIREDas distinct fromUNAUTHORIZED. The frontend should show "Du wurdest abgemeldet, bitte erneut anmelden" — see Leonie's note.AuditKind.LOGIN_SUCCESS,LOGIN_FAILED,LOGOUT,ADMIN_FORCE_LOGOUT. Payload must include source IP and user-agent (truncated to 200 chars to bound storage growth). Never log the password attempt —LOGIN_FAILEDpayload is{email, ip, ua}only./api/auth/loginat the application layer with Bucket4j. fail2ban via Caddy logs is too coarse (banning whole IPs, slow to react). Bucket4j gives per-(IP, email) buckets. Default: 5 attempts per 15 min, response is 429 withErrorCode.TOO_MANY_LOGIN_ATTEMPTS. This is an additive NFR I'd add to the issue:delete_returns403_when_csrf_token_missing,login_returns401_when_password_wrong,login_returns429_when_rate_limited,password_reset_invalidates_all_user_sessions,admin_force_logout_invalidates_all_target_sessions_and_audits. Each starts red.auth_tokencookie must be forced to re-login. Don't ship a compat layer that accepts both — that's exactly the kind of "transitional" code that becomes permanent.Open Decisions
Recommendation: code three explicit paths. Don't conflate them.
spring_session(already there:LAST_ACCESS_TIME), so zero schema cost.— Nora ("NullX")
🧪 Sara Holt — Senior QA Engineer
Observations
pom.xmlJaCoCo<minimum>0.88</minimum>). Auth touches many code paths; this work will move the coverage numbers and the PR must hold the line.PasswordResetTestHelper.javaalready exists for e2e to drive deterministic password resets. The new login model needs an analogous test seam — either reuse the pattern (anAuthE2EControlleralready exists) or extend it.NFR-PERF-101("≤5ms p95 added by session lookup") andNFR-SEC-102("≤60s for stolen cookie to become inert") are measurable but the issue doesn't say where the measurement is captured. These need to be wired into the test suite, not assumed.browsermimics "two devices."Recommendations
AuthService.login,AuthService.logout, session invalidation logic, audit-log writes@WebMvcTest)AuthControllerhappy/error paths for both endpoints, CSRF rejection on a sample write controllerbrowser.newContext()twice for the same user, log out from one, verify the other still works (logout-current-only)/api/auth/loginat sustained 50 RPS; measure p95 of the next authenticated GET — this is NFR-PERF-101 evidenceNFR-SEC-102(≤60s for stolen cookie to become inert) is implicitly an "immediate" requirement, not a "within 60s eventual consistency" one — Spring Session JDBC deletes are synchronous. Write the integration test that asserts: at T+0DELETE FROM spring_session WHERE id = ?, at T+0+ε next request returns 401. There's no 60s window in this design — the AC should probably read "≤1 request" rather than "≤60 seconds." Worth tightening with Elicit.ADMIN_USER(or newADMIN_PERMISSION?) operation. Tests:force_logout_returns403_when_caller_has_only_WRITE_ALLforce_logout_returns401_when_unauthenticatedforce_logout_returns200_and_audits_when_admin@ParameterizedTestdriven by a list of write endpoints catches the case where someone adds a new write controller and forgets the CSRF token requirement. Pattern:SessionRepositoryin integration tests. Use Testcontainers + the real Flyway-managedspring_sessiontable. H2 will not behave the same way; mocked repos hide the actual session-store semantics that NFR-SEC-102 depends on.Open Decisions
AuthE2EController(test-profile-only) gain aforce-create-sessionhelper so e2e tests can seed an authenticated session without going through the real login form? Pro: speeds up every E2E test that needs a logged-in user. Con: anothere2e-only surface to maintain and double-check is gated. Recommend yes —PasswordResetTestHelperset the precedent.— Sara
⚙ Tobias Wendt — DevOps & Platform Engineer
Observations
backend/pom.xml— there is nospring-session-jdbcdependency.V2__drop_spring_session_tables.sqlexplicitly drops the tables with the note "Spring Session JDBC was included as a dependency but never used. Authentication is stateless HTTP Basic Auth; sessions are never written." Re-introducing it means: (1) add the Maven dependency, (2) writeV33__recreate_spring_session_tables.sql(Spring Session ships the canonical DDL — copy it), (3) configurespring.session.store-type=jdbcinapplication.yaml. Not a one-liner, but boring and well-documented.AuthTokenCookieFilteris gone, the Vite proxy header-injection invite.config.tsis gone. Three layers removed./actuator/*is already blocked in Caddy (confirmed by recent commit9686e304— actuator block wrapped inhandlefor precedence). Force-logout admin endpoints stay reachable since they live under/api/*, not/actuator/*.spring_sessiontable will hold session IDs that are credential-equivalents. Backup blast radius matters. A leaked nightly pg_dump becomes a session goldmine.Recommendations
auth_tokencookies with a transitional filter: that's the exact "transitional code becomes permanent" anti-pattern. Schedule:spring_sessionexclusion to backup retention policy. Two options:pg_dumpentirely (--exclude-table=spring_session*). Pro: leaked backup ≠ session theft. Con: restoring loses all sessions, which is acceptable.Recommend option 1. Document in
docs/infrastructure/.SELECT COUNT(*) FROM spring_sessionLOGIN_SUCCESS/LOGIN_FAILEDper minuterate({app="backend"} |= "LOGIN_FAILED" [5m]) > 10— likely brute force in progress/api/auth/loginfor fail2ban. Currently Caddy logs/api/*by URI but the auth-specific filter may or may not be in the jail config. Audit the existing fail2ban jail before relying on it as a defense layer (Nora has noted Bucket4j as a better fit anyway).spring-session-jdbcversion explicitly in pom.xml. Don't rely on Spring Boot's BOM transitively — security-relevant dependencies should be pinned and bumped by Renovate.V33__add_spring_session_tables.sqlruns before any code referencing it. Stamp the migration with a comment explaining why we're recreating what V2 dropped — future readers will be confused otherwise.PostgresContainerConfigtest setup runs all migrations (it should — confirm with Sara).Open Decisions
scripts/force-logout-user.sh <email>) hitting the admin API is useful for true break-glass scenarios when the admin UI is down. Cost: one shell script, ~20 lines. Benefit: operator can revoke sessions even if the SvelteKit frontend is broken. Recommend yes — keep it tiny and document indocs/infrastructure/.— Tobi
🎨 Leonie Voss — UI/UX Design & Accessibility
Observations
/login/+page.svelte), error handling will need to map the newINVALID_CREDENTIALSandTOO_MANY_LOGIN_ATTEMPTScodes./login. With server-side logout, this becomes a real action that might fail. Needs a confirmation/feedback state./loginwith no explanation. Particularly disorienting for the 60+ audience./aktivitaeten/) — make sure the rendering doesn't leak a confusing entry likeaudit.adminforcelogoutwith no German label.Recommendations
session_expiredtomessages/{de,en,es}.json. When the SvelteKithandleAuthhook (hooks.server.ts:32-38) redirects an unauthenticated user, append?reason=expiredto the login URL, and render anaria-live="polite"info banner on the login page:/api/auth/logoutfrom the existing logout action, then delete cookie, then redirect. The user sees the same redirect-to-/login. No new UI required — keep it simple. But: if the backend logout call fails, still delete the local cookie and redirect (defense in depth — the user wanted to log out, don't trap them on an error page).min-h-[44px]minimum (WCAG 2.2 SC 2.5.8). Verify on/login/+page.svelte— if it's currentlypy-2 px-4only, that may be under 44px depending on font size. Fix in this PR.INVALID_CREDENTIALSto a generic "E-Mail oder Passwort sind nicht korrekt." — never differentiate "user not found" from "wrong password" (this is also a security concern — Nora agrees).TOO_MANY_LOGIN_ATTEMPTSto: "Zu viele Anmeldeversuche. Bitte warte 15 Minuten und versuche es erneut." Don't expose the exact bucket window if Nora's Bucket4j config changes.CSRF_TOKEN_MISSING403 comes back from a form submission, the SvelteKit error boundary should show: "Diese Seite wurde zu lange offen gelassen. Bitte neu laden und erneut versuchen." with a "Seite neu laden" button. Don't show a generic 403 — that's frightening for non-technical users.Open Decisions
None from the UX angle — all recommendations above are concrete. The admin force-logout UI (OQ-AUTH-003) is a backend-only ticket; UI design for it can wait for v1.2.0.
— Leonie
📋 Elicit — Requirements Engineering (self-review)
I wrote this issue. Re-reading it after Markus, Tobi, and Nora's findings, several things need correcting before implementation can start.
Observations
CLAUDE.mdsays so but the actual code disagrees:V2__drop_spring_session_tables.sqlremoved it as unused, andbackend/pom.xmlhas nospring-session-jdbcdependency. This inflates the assumed simplicity of the work and affects sizing.Recommendations (issue amendments)
MaxInactiveIntervalInSeconds+ Spring Session's filter rotation. Costs zero schema or config complexity. Remove OQ-AUTH-002.Open Decisions
— Elicit
🗳️ Decision Queue — Action Required
4 decisions need your input before implementation starts. The other open questions raised in personas above have concrete recommendations and don't need a separate decision.
Architecture
userpackage, or extract a newauthpackage? Extendinguserkeeps existing controllers (AuthController,InviteController,PasswordResetService) together but grows package size. Extractingauthis cleaner separation but adds a cross-domain call (AuthService → UserService) and a new module to maintain. (Raised by: Markus)Implementation
XSRF-TOKENcookie (Spring default) or JSON field in/api/auth/loginresponse? Cookie approach is one less moving part and works withuse:enhance. JSON field is more explicit but requires the SvelteKit login action to set a separate cookie itself. (Raised by: Felix — recommended cookie approach, deferred to Nora for security review)Scope & Sizing
Operations
Cross-cutting observations all personas converged on (no decision needed — already reflected as recommendations above):
V2__drop_spring_session_tables.sqlremoved it;CLAUDE.mdis stale. Flagged independently by Markus, Tobi, Nora, and Elicit. The issue's sizing and "in-house infrastructure" framing need updating.ErrorCodevalues must be added:INVALID_CREDENTIALS,CSRF_TOKEN_MISSING,SESSION_EXPIRED,TOO_MANY_LOGIN_ATTEMPTS. With matching i18n in de/en/es and frontenderrors.tsmapping (Felix, Nora, Leonie).Module: new auth
XSRF-Token cookie
We split this ticket
📋 Elicit — splitting and closing
This issue is being closed in favour of two scoped replacements, both filed against the new v1.1.0 milestone:
#523 —
feat(auth): server-side session model replacing Basic-auth cookie promotionPhase 1 / cutover. Replaces the cookie-as-credential model with opaque Spring-Session-JDBC-backed sessions. One breaking deploy: every in-flight session is invalidated.
Covers: FR-AUTH-001/002/003/006/007, NFR-SEC-101/102, NFR-PERF-101, NFR-OBSV-101 (partial — login + logout audit kinds), NFR-COMPAT-101.
#524 —
feat(auth): defense-in-depth — CSRF, session revocation, login rate limitPhase 2 / additive. Sits on top of #523. Re-enables Spring Security CSRF (XSRF-TOKEN cookie), invalidates sessions on password change/reset/disable/admin-force-logout, and rate-limits
/api/auth/loginvia Bucket4j.Covers: FR-AUTH-004/005/008/009/010, NFR-SEC-103/104, NFR-OBSV-101 (completion) + NFR-OBSV-102, extends NFR-COMPAT-101.
Blocked by #523.
Why split
Decisions resolved in this discussion (carried forward)
authpackageCorrections to the original issue body (folded into the replacements)
V2__drop_spring_session_tables.sqlremoved it;CLAUDE.mdis stale. Re-introducing the dependency + a new Flyway migration is part of #523's scope (resized from L to L+ accordingly).Closing as superseded by #523 + #524.
— Elicit