Repository source: docs/plans/app_sessions/app_sessions_step2_feature_description.md
Debug mode: enabled (debug log)
Effort study: railspike-effort / v3/app_sessions (ledger); standing enrollment, protocol P2
Extended estimates: enabled
Cycle estimate: 0.90
Status: Approved Step 2 (2026-11-03T02:26Z, maintainer: "Approved step 2.").
Problem
Sign-in state lives in PHP session files, ini defaults and a cookie the browser may refuse, so members and operators are silently signed out, or never signed in, in ways that look different each time.
User stories
As an operator, I want to stay signed in until I sign out so that Deploy and other operator tools work after an idle evening.
As a member on an iPhone, I want sign-in to work over http after visiting over https so that the site behaves the same on either address.
As a returning member with a saved key, I want the switch to new sessions to sign me back in silently so that I never notice it.
As a member who signs out, I want that session to end on the server at once so that a copied cookie stops working.
As a contributor, I want tests to sign in through the real cookie so that a passing test means sign-in actually works.
Core requirements
One answer to "who is signed in" for every request carrying the session cookie, with no route list. Keyless readers get no cookie, so static pages stay static.
A session lasts its stated lifetime (400 days, as today) unless signed out. Sign-out ends it on the server immediately. Signing in again as the same identity keeps the existing session. A request in flight during sign-in or sign-out never signs the browser back in or out by accident.
Sign-in behaves the same over http and https without forcing https: each scheme has its own cookie, and an http cookie never authorizes an https request. The testing Approve cookie gets the same treatment.
No PHP session functions remain. An existing
PHPSESSIDis expired on first contact; browsers with a saved key sign back in through the existing background sign-in and resume page; cold POST/API requests get today's signed-out response.The existing sign-in API responses stay byte-compatible for browsers and agents. Server-side workarounds listed in Step 1 (rotation forwarding, start allowlist/resume,
$_SESSION-prefilled tests) are removed.
Delivery scope
Work type: application change, with tests and documentation.
Documentation: this cycle's plans and debug log, the study ledger,
docs/reference/authentication_modes.md, status notes onsession_reauthentication_reference.mdandsession_rotation_race_decision.md, and a new concept entry.Dependency delta: zero (PHP's built-in SQLite and random functions).
Out of scope (separate work): Deploy page Sign in button, lobby-gate diagnostic log removal, client-side challenge-queue simplification, operator-delegation reintegration, "sign out other browsers" UI.
Completion boundary
Normal entry: sign in from Account, Lobby, Access history or the resume page, over http or https.
Outcome: the signed-in identity is recognized on every route, including Deploy POSTs, for the session lifetime; sign-out ends it.
Recovery: after the cutover or a cleared cookie, a browser with a saved key signs back in silently on its next page load; operators use the existing Access history sign-in.
Release: focused and full suites run with failures compared to baseline; real cookie round trips pass over http and https; the iPhone check below is done or explicitly recorded as pending.
Known user and environment
iPhone Safari over http, holding an https-era Secure
PHPSESSID. Entry: open the site over http and sign in. Next action without help: none needed; if a sign-in fails, Account's sign-in retries. Verify: sign in, reload, and see the signed-in account over http, then over https. Evidence: a real iPhone Safari check by the maintainer; a Chromium replay of the shadowing condition is supporting evidence only.Operator idle past 24 minutes. Entry: Tools › Deploy after a long idle. Verify: Deploy POST succeeds without signing in again. Evidence: a test with a session idle well past PHP's old default.
Risks
Test conversion volume (12 files, 49 sites in
LocalAppSmokeTest). Impact: slice overruns. Validate first by converting one file with a shared signed-in-request helper. Mitigation: mechanical helper; split if the pilot shows otherwise.SQLite write contention. Impact: slow or failed requests if every request writes "last seen". Validate with a concurrent-request check early. Mitigation: avoid a write per request.
Session store leaking into exports or backups. Impact: tokens leave the server. Validate by checking archive/backup inputs before building. Mitigation: store only token hashes, outside exported paths.
Unmerged branches adding session reads (operator delegation, revoked-key sessions). Impact: merge conflicts later. Mitigation: one resolver to call; note both branches in the closeout.
Shared component inventory
Sign-in APIs (
/api/auth_challenge,/api/authenticate_identity,/api/auth_status,/api/clear_identity): reuse; same responses, new storage.Browser sign-in
private_site_auth.js, resume page, Access history sign-in partial,auth_navigation.js,member_approval.js, Messages access: reuse unchanged.Viewer lookups in
Application,RouteServices,DeployController,UserAccessController,IdentityHintController: switch to the shared resolver.Static/high-traffic and docs cookie checks: reuse unchanged; they keep working because keyless readers get no cookie.
Testing Approve cookie,
identity_hintcookie: reuse; Approve cookie gains per-scheme naming.New: session store and resolver. Nothing existing holds app-owned session state.
User flow
A member with a saved key opens any page over http or https.
The browser signs in through the existing flow; the server issues a session cookie for that scheme.
Every later request with that cookie is recognized, on any route.
Sign-out ends the session on the server and clears the cookie.
A browser still holding an old
PHPSESSIDloses it on its first request and signs back in silently.
Success criteria
No PHP session function or
$_SESSIONuse remains insrc/; no session route list remains.Tests drive real cookie round trips; none pre-fill session state.
Signed-in state survives a simulated idle longer than PHP's old 24-minute default and lasts to the stated lifetime.
Sign-in works over http after an https visit (Chromium replay of the shadowing case; iPhone check recorded).
Sign-out makes a copied cookie unusable on the next request.
Signing in repeatedly as the same identity does not grow the session store per visit.
Concepts touched
Viewer session (sign-in): created as a new entry in
docs/concepts/.Static publication: used unchanged.
Enhancement requests
Step 1 workaround inventory (2026-11-03): recorded in Step 1; server-side items in scope here, client items deferred.
Approval and study checkpoint
Step 2 handoff 2026-11-03T02:08:04Z: whole-cycle 0.90 reaffirmed for scope v1/Option A. Requirements match the Step 1 recommendation; the explicit iPhone acceptance condition was already implied.
Baseline
e6493e2ff4869f8bf98e5f43b3f6388814742c26; R1 (all three hashes re-verified); P2. Claude (claude-opus-5-5), single exposed rater. No implementation, measured labor or accepted outcome. Awaiting Approved Step 2; records uncommitted.
— Claude · 2026-11-03 UTC · Model: claude-opus-5-5