# Web Login Runtime Mismatch

## Metadata

- Date: 2026-07-30
- Status: Resolved; authenticated live runtime verified
- Scope: Live ARR Web process on port 8766
- Confidence: Fact
- Source: Listener/process inspection, Keychain/launcher validation, loopback/LAN HTTP probes, source/config inspection, automated tests and isolated browser verification
- Last verified: 2026-07-31
- Stale trigger: Port-8766 process restart, login credential configuration, route/config change or LAN-address change

## Finding

The first page-failure check found a partial runtime mismatch: PID 37865 was still bound to `0.0.0.0:8766` and returned
HTTP 200 for `/`, `/h5`, `/api/health` and `/assets/app.js` on loopback and the then-current LAN address
`192.168.3.48`, while `/login` alone returned 404. The long-lived Python process predated the new login routes but read
newer static files from disk.

The 23:03 recheck found a complete service outage instead. No process listens on 8766, PID 37865 is gone, and `/`,
`/h5`, `/login`, `/healthz`, `/api/health` and `/assets/styles.css` all fail with connection refused on loopback. The
workstation's Wi-Fi address has also changed to `192.168.3.103`, where root and login probes likewise fail. No matching
Web/worker/ngrok process, 8765/8766/8877 listener or launch agent was found. There is no retained process log, so the
evidence does not prove why PID 37865 exited.

The login change was nevertheless relevant to recovery: current source intentionally refuses startup when
`ARR_WEB_USERNAME` or `ARR_WEB_PASSWORD` is absent. After the operator supplied both values, each was placed in a
separate macOS Keychain item and a credential-free local launcher restored the prior controlled database, OSS,
processing, monthly and company-report inputs. Detached Screen session `arr2-web-8766` owns the sole `*:8766` listener.

On 2026-07-31 the operator requested a credential rotation. Both values were replaced through interactive no-echo
Keychain input, the password item was rebuilt under stable non-credential account metadata, and the credential-free
launcher remained valid. A controlled restart replaced only the resolved Web listener and recreated the same detached
Screen topology; no credential value is recorded here.

## Completed Source Implementation

- `arr_web.auth` validates runtime `ARR_WEB_USERNAME` / `ARR_WEB_PASSWORD` with constant-time comparison and a bounded
  per-client attempt ledger. Missing/invalid configuration fails Web startup closed.
- The authenticated `SessionLedger` issues random server-side sessions and per-session CSRF tokens, expires/revokes
  them and retains `HttpOnly`, `SameSite=Strict` plus optional `Secure` cookie behavior.
- Login assets, `POST /api/login` and minimal `/healthz` are the only anonymous routes. Desktop/H5 documents redirect
  to an allowlisted login target; all business APIs, detailed health, uploads, traces and downloads reject anonymous access.
- Desktop and H5 clients redirect expired sessions to login and expose CSRF logout. Logout starts hidden when an old
  anonymous `/api/session` response lacks `username`; this kept PID 37865's mixed-version UI compatible before activation.
- Caddy Basic Auth was removed; Caddy remains the HTTPS boundary and the Web application owns human authentication.

## Verification

- Python and all three JavaScript files pass syntax checks.
- The final focused auth/Web/company/deployment set passes 33 tests. Full discovery passes 317 tests in 97.311 seconds
  with 10 expected environment/fixture skips and no failures/errors.
- Isolated real-browser verification passed generic wrong-credential feedback, password visibility, successful desktop
  and `/h5` return, desktop/H5 logout, zero console warnings/errors and no horizontal overflow at 375, 768, 1024 and
  1440 CSS pixels.
- Ruby's standard YAML parser loaded the Compose file and deployment contracts confirm login env/readiness/Caddy
  shape. Docker is unavailable on this workstation, so no live Compose expansion or image build is claimed.
- Live activation passes anonymous root 303 and business API 401; exact login, hardened cookie/session/CSRF,
  authenticated desktop/H5/history reads, all five readiness flags, logout and post-logout rejection. Loopback and
  `192.168.3.103` return `/healthz` 200; LAN root redirects to login and LAN `/login` returns 200.
- A 2026-07-31 follow-up found the service still healthy under the same detached Screen process, while DHCP had moved
  the workstation address back to `192.168.3.48`. Loopback and `.48` root return 303 to login, `/healthz` and the login
  document return 200, and the in-app browser rendered the complete login gateway. The prior `.103` URL is now stale.
- The same-day visual follow-up removed the login page's workflow narrative, numbered steps, duplicate brand placement,
  welcome/access introduction and support disclaimers. The live gateway now presents one `ARR Report` heading, the
  `username` and `password` fields, password visibility and the existing submit/error states. Thirty-three focused
  Web/auth/UI tests pass; browser checks at 390x844 and 1280x720 have no horizontal overflow or console warnings/errors.
- The credential-rotation acceptance rejects the prior pair and accepts the new pair, then verifies anonymous root/API
  boundaries, hardened cookie/session/CSRF, desktop/H5, logout revocation, all six current detailed readiness flags and
  loopback plus `192.168.3.48` health. Two read-only snapshots remained 30 daily jobs, 4 monthly versions and 4 company
  jobs; latest creation times predate the 15:30 +08 restart. Nine focused auth/deployment tests pass, and literal scans
  of the workspace, launcher, state logs and process list are clean.
- The initial activation totals were 24 daily jobs, 4 monthly versions and 3 company jobs. No XML upload,
  company-report generation, Booking activation, monthly worker or other business mutation was started by either
  activation or rotation acceptance.

## Activation Result and Operating Boundary

- Current LAN entry as of 2026-07-31: `http://192.168.3.48:8766/`; DHCP may change this address again.
- `/Users/chillishark/.local/bin/arr2-web-8766` is owner-executable only and contains paths/Keychain labels, not secret
  values. Its `--check` mode validates required private inputs without printing them.
- The active Screen session is detached but not a reboot-persistent process manager. A future controlled restart should
  validate the launcher, stop the exact 8766 listener and start one `screen -dmS arr2-web-8766` session.
- The latest requested values were rotated successfully, but the password still matches the username. A future
  strength rotation should use a distinct high-entropy password, update only Keychain and restart once; never commit the
  value or put it in process arguments.

Live login/readiness is complete. A no-PII upload-filename check and one released 5/5 company-report job remain separate
business-mutation acceptance work.
