#190: configurable session lifetime + server-side idle timeout #241

Merged
fable-5 merged 1 commits from feat/190-session-timeouts into main 2026-07-30 11:26:42 +02:00
Collaborator

Implements #190:

  • SESSION_ABSOLUTE_HOURS (default 168 h / 7 days): absolute cap from login, set once at creation, never extended - the sliding 30-day renewal is gone. Cookie maxAge follows it.
  • SESSION_IDLE_HOURS (default 72 h / 3 days): idle bound enforced server-side against lastSeenAt; activity renews it (write-throttled, throttle scales with the idle bound so short windows still renew). Effective idle is capped at the absolute bound.
  • Expired rows (either bound) are deleted on validation; listForUser applies both bounds so idle-dead sessions never show as active in the session list.
  • Existing sessions keep their stored expiresAt as absolute bound after deploy; the idle bound applies to them immediately.
  • Docs: .env.example (incl. VS-NfD reference values 12h/1h for the upcoming hardening guide #227), compose passthrough (empty string = default via loadApiEnv), security.md, ADR 0007 amendment note.

Tested: new sessions.service.db.test.ts covers the three acceptance cases (absolute bound rejected, idle bound rejected before absolute, active use renews idle but never extends absolute) plus creation bound and session-list filtering; api full suite 443 green vs fresh DB; shared 185; typecheck + lint green.

Refs #190 (close manually after green CI + merge).

Generated with Claude Code (Claude Fable 5)

Implements #190: - SESSION_ABSOLUTE_HOURS (default 168 h / 7 days): absolute cap from login, set once at creation, never extended - the sliding 30-day renewal is gone. Cookie maxAge follows it. - SESSION_IDLE_HOURS (default 72 h / 3 days): idle bound enforced server-side against lastSeenAt; activity renews it (write-throttled, throttle scales with the idle bound so short windows still renew). Effective idle is capped at the absolute bound. - Expired rows (either bound) are deleted on validation; listForUser applies both bounds so idle-dead sessions never show as active in the session list. - Existing sessions keep their stored expiresAt as absolute bound after deploy; the idle bound applies to them immediately. - Docs: .env.example (incl. VS-NfD reference values 12h/1h for the upcoming hardening guide #227), compose passthrough (empty string = default via loadApiEnv), security.md, ADR 0007 amendment note. Tested: new sessions.service.db.test.ts covers the three acceptance cases (absolute bound rejected, idle bound rejected before absolute, active use renews idle but never extends absolute) plus creation bound and session-list filtering; api full suite 443 green vs fresh DB; shared 185; typecheck + lint green. Refs #190 (close manually after green CI + merge). Generated with Claude Code (Claude Fable 5)
fable-5 added 1 commit 2026-07-30 11:11:32 +02:00
#190: configurable session lifetime with a server-side idle timeout
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 4m53s
CI / Build container images (pull_request) Successful in 3m55s
CI / Auth e2e pack (pull_request) Successful in 7m53s
CI / Import/export fidelity gate (pull_request) Successful in 55s
CD / Build and push images (push) Successful in 19s
CD / Deploy to Test (push) Successful in 12s
CD / Smoke tests against Test (push) Successful in 1m17s
CD / Promote to Int (push) Successful in 11s
CI / Lint, typecheck, test (push) Successful in 4m53s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 7m37s
CI / Import/export fidelity gate (push) Successful in 52s
db4c5ce9ca
SESSION_ABSOLUTE_HOURS (default 168 h) caps a session's total lifetime
from login: expiresAt is set once at creation and never extended — the
old sliding 30-day renewal is gone. SESSION_IDLE_HOURS (default 72 h)
ends sessions unused for that long, enforced server-side against
lastSeenAt with a write throttle scaled to the idle bound so short idle
windows still renew. Expired rows are removed on validation and the
session list applies both bounds, so idle-dead sessions never show as
active. The cookie maxAge follows the configured absolute bound.

Documented in .env.example (with the VS-NfD reference values for the
upcoming hardening guide #227), compose passes the variables through,
security.md and ADR 0007 record the amendment.

Refs #190

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168Ph5uBmHm8X28CSVpbpnJ
fable-5 merged commit db4c5ce9ca into main 2026-07-30 11:26:42 +02:00
Sign in to join this conversation.
No description provided.