Add Standalone Login Page Renderer

Add a hash-bound, no-store login document with focused C and Node host
tests. Keep rendering inert until the 8D.3 authentication cutover.
This commit is contained in:
2026-09-05 18:45:12 +02:00
parent 00f226dc59
commit 4435a7fddd
9 changed files with 542 additions and 4 deletions
+1
View File
@@ -71,6 +71,7 @@ This is a semantic map, not a complete file inventory. Start here, then read the
- Security constraints: Basic-auth cache hits still revalidate principal currentness; the browser's combined Connect/Disconnect control closes the WebSocket and pauses automatic reconnect until Connect is selected. Changes to the authored inline loader must update its hard-coded CSP hash in the same change.
- Session-store boundary: admitted HTTPS start initializes four static records; failed start/accepted stop disables and wipes them. 8D.2 binds tickets/slots to non-reused session IDs (zero only for Basic); transport-specific cleanup and account/global revocation invalidate store records before socket cleanup. No cookie route yet. RNG/SHA/database calls run outside short portMUX sections; ID/expiry/epoch checks reject stale work. Run `python3 tests/web_session_store/run.py` and its `--serial` integration mode.
- 8D.3 preparation: `src/web_auth_parse.{c,h}` provides inert, allocation-free origin/cookie/login-JSON parsing; no HTTP caller yet. Test with `python3 tests/web_auth_parse/run.py`. These helpers do not authenticate or replace HTTP header/method/CSRF policy.
- 8D.3 login rendering: `src/web_login_ui.{c,h}` contains a standalone no-store login document and hash-bound script; no registered route or live caller. `python3 tests/web_login_ui/run.py` checks production C rendering, CSP and Node DOM/fetch doubles. Existing `web_ui.c` and generated assets are unchanged.
- Asset constraint: `web_assets_data.c` is checked-in generated input to the build; do not hand-edit or regenerate casually.
## SSH
+12 -1
View File
@@ -4,6 +4,8 @@ This file is working memory. Update it during active work and before handoff; do
## Development state
- **8D.3 inert login renderer completed (2026-09-05):** User requested continuation after the parser split. Added standalone `web_login_ui.{c,h}` plus production-renderer/Node tests; no live route or Basic-auth change. Build and focused suites pass; 8D.3/M1 remains incomplete. Remaining work is the atomic server/app cutover, then mandatory browser/target validation. See active task below.
- **8D.3 preparatory parser split (2026-09-05):** User requested continuation. Per the plan's 600800-line scope review, selected inert private request parsing before the larger atomic login/logout cutover. `src/web_auth_parse.{c,h}` and focused host tests added; no live HTTP callers or authentication changes. See active task below. 8D.3/M1 is **not complete**; Basic remains active. Prior 8D.2 user sign-off stands; numeric reserves remain open.
- **8D.2 validated by user sign-off (2026-09-05):** User explicitly closed 8D.2 after post-flash boot and full-client-mix samples. This supersedes older target-pending/in-progress statements below. Evidence and limits are in `docs/phase8d2_implementation.md`; unrecorded regression details are not claims of execution or blockers to the signed-off checkpoint. Numeric reserves remain open. Wait for a separate 8D.3 request.
@@ -58,7 +60,16 @@ These observations should be checked when touching the relevant area; they are n
- Confirm task-local Newlib standard-stream behavior if ESP-IDF/Newlib configuration changes; admin SSH command output relies on dispatcher-task stream redirection.
- If HTTPD concurrency configuration changes, add locking around the boot-local Basic-authentication cache.
## Active Task - Phase 8D.3 Inert Request Parsing
## Active Task - Phase 8D.3 Inert Login Rendering
- **Scope:** Second permitted preparatory split after scope review of remaining challenge/throttle/HTTP-route/application work. Added only `src/web_login_ui.{c,h}`, CMake registration, `tests/web_login_ui/` and documentation. Standalone renderer has no live HTTP caller or URI registration; Basic cache/auth, existing app and serial protocol, capacities, task stacks and generated assets are unchanged. No upload, erase, commit or branch change.
- **Behavior when integrated:** No fetch on page load; explicit Sign in obtains challenge with `X-Login-Bootstrap: 1`, then POSTs JSON with CSRF. Same-origin credentials/mode, no-store fetch, redirect rejection, 512-byte response bound and UTF-8 field/body limits. Generic safe-text errors, bounded Retry-After display/manual backoff, no automatic credential retry, fixed success navigation to `/`. Inputs disabled while pending; password fields/references cleared best-effort, attempt aborted on every exit, 15-second deadline, pagehide/pageshow generation guards. No localStorage/cookie access or logging; JavaScript/browser memory cannot be securely wiped.
- **Policy/resources:** Standalone 7,387-byte HTML document plus terminator in its object; no protected asset dependencies or template interpolation. Five additional headers (no-store, nosniff, no-referrer, frame denial, matching SHA-256 CSP), below existing eight-header limit. Rendering itself allocates no module heap/session/task. Unused renderer leaves final linked RAM/flash **95,260 / 1,601,925 B**, unchanged versus 8D.2 and parser split. Future linked/read-only data, HTTPD send/TLS and runtime stack/heap costs remain cutover measurements; no runtime reserve approval implied.
- **Validation:** `python3 tests/web_login_ui/run.py` passes production-C NULL/header/send-failure checks, exact rendered CSP hash and eight Node VM behavior groups. `python3 tests/web_auth_parse/run.py` passes 268 cases; existing store and `--serial` host modes pass. Independent review caught/fixed unread error-body lifetime and re-entered password retention; regression tests cover abort signals/modeled streams and pending-input cleanup. Final `pio run` passed in **8.25 seconds**, unchanged sizes. `git diff --check` passed. No real browser/CSP enforcement/bfcache, HTTPD sockets, TLS or target exercise.
- **Next exact work:** Continue **8D.3 atomic live cutover**, not more login rendering or 8D.4. Reuse prepared parser/renderer/store/serial binding. Implement bounded challenge/throttle and complete cookie/session/CSRF/Origin policy, replace Basic for every app/asset/status/ticket/upgrade route together, add `/login`, challenge/login/session/logout routes, and integrate existing application's session validation/logout/401 recovery without changing explicit serial reconnect. Retain no-live-eviction ticket policy cutover, fail-closed initialization and account/session-specific revocation. Duplicate-header detection and two Set-Cookie behavior must be verified against actual IDF HTTPD, not assumed. Keep response header/route/body budgets explicit. Then M1 target/browser pause and resource evidence; no M2 work.
- **Remaining gates:** Full M1 checklist in plan/baseline applies only once live: both roles/fresh and former Basic profiles, direct IP/mDNS, challenge/session/capacity/throttle/Origin/CSRF errors, logout/account switch/expiry/reboot/revocation isolation, repeated serial cycles, UART0/USB/user+admin SSH and soak/cleanup memory. Numeric reserves remain pending; 8D.2 user sign-off stands.
## Previous Task - Phase 8D.3 Inert Request Parsing
- **Scope:** Private allocation-free helpers for canonical Host/Origin comparison, unique named 64-lowercase-hex cookie extraction, and strict login JSON. CMake registration plus `tests/web_auth_parse/`; no live route, UI, store, transport, task, socket, stack-size or generated-asset change. Split before coding because full challenge/throttle/routes/UI/tests cutover exceeds the work-unit guideline. No commit, upload or erase.
- **Contracts:** Exact bounded byte spans; canonical origin max 128 bytes after optional default-port normalization, ASCII DNS/IPv4 authorities only (IPv6 rejected), mandatory matching HTTPS Origin. Cookie header max 1024 bytes, strict unquoted values even for unrelated cookies. Login body max 512 bytes, exactly username/password strings, decoded limits 16/64 bytes; proper UTF-8/JSON escape/surrogate validation. Unknown/duplicate fields and NUL rejected. Database policy remains authoritative. Failures clear complete outputs; successful credentials and original body require caller wiping. No dynamic allocation or persistent state.
+5 -3
View File
@@ -1,6 +1,6 @@
# Phase 8D — Incremental web administration plan
Status: **8D.0 through 8D.2 validated by user sign-off on 2026-09-05. 8D.2 implemented, host-tested and build-verified. Reserve budgets remain pending. 8D.3 has an inert request-parser preparatory split; its live cutover and 8D.48D.22 remain planned.** See the [8D.0 baseline and M1 contract](phase8d_baseline.md); no browser authentication cutover is claimed.
Status: **8D.0 through 8D.2 validated by user sign-off on 2026-09-05. 8D.2 implemented, host-tested and build-verified. Reserve budgets remain pending. 8D.3 has inert request-parser and login-renderer preparatory splits; its live cutover and 8D.48D.22 remain planned.** See the [8D.0 baseline and M1 contract](phase8d_baseline.md); no browser authentication cutover is claimed.
This is the execution plan for [roadmap Phase 8D](roadmap.md#phase-8--role-based-users-and-administrative-access). The roadmap retains the feature/security requirements; this document defines small work units, dependencies, and release gates. The [administration test matrix](user_administration_tests.md#planned-phase-8d-integrated-web-administration) remains the final acceptance checklist.
@@ -92,6 +92,8 @@ If 8D.3 exceeds the work-unit limit, first split out inert login-page rendering
**Preparatory split (2026-09-05):** Scope review selected private request parsing first; the complete challenge/throttle/route/browser/test change exceeds the authored-line work-unit target. Added allocation-free `src/web_auth_parse.{c,h}` with no live HTTP callers: bounded canonical same-origin comparison, unique cookie extraction and strict UTF-8 login JSON decoding. `python3 tests/web_auth_parse/run.py` passes **268 cases** against production C; both existing session-store host modes pass. Final `pio run` passes in **7.50 seconds** and reports **95,260 B RAM / 1,601,925 B flash**, unchanged from 8D.2 because helpers are not live linked paths. No route/task/socket/stack-size/asset changes. No target/browser validation, runtime reserve approval or M1 completion is implied. Continue within **8D.3**, with the full atomic cutover still pending; see `docs/agent/current-state.md` for exact integration obligations.
**Login-renderer preparatory split (2026-09-05):** Added `src/web_login_ui.{c,h}` with no live caller/route, leaving Basic and existing serial UI unchanged. Standalone 7,387-byte HTML plus terminator has no protected asset dependencies, five security headers including no-store and exact script-hash CSP. Explicit-only challenge/login flow, bounded request/response handling, safe errors/manual backoff, disabled pending inputs, best-effort password/reference cleanup and generation-safe page-exit cancellation. Review fixes abort every attempt on exit (including unread error bodies) and clear re-entered passwords. `python3 tests/web_login_ui/run.py` passes production C rendering/failure checks, exact CSP hash and eight Node VM groups; parser and both session test modes also pass. Final `pio run` passes in **8.25 seconds**, unchanged **95,260 B RAM / 1,601,925 B flash**: unused renderer costs are not live-linked/runtime costs yet. No new task/socket/route/stack-size/module heap or generated-asset change. No real-browser/HTTPD/hardware validation or M1 completion. **Next is the atomic live 8D.3 cutover using both prepared pieces**, followed by the mandatory M1 target gate; no additional login-rendering split is needed.
## M2 — Reuse the admin shell, then expose it
### 8D.4 — Small transport-neutral console boundary
@@ -161,11 +163,11 @@ Update the roadmap and user/command documentation to distinguish completed featu
## Progress and next-request template
Progress: **8D.0 through 8D.2 validated by user sign-off; 8D.2 implemented / host-tested / build-verified. Reserve gates pending. 8D.3 inert parser preparation implemented; live cutover and 8D.48D.22 planned.** Record incremental results in `docs/agent/current-state.md`, retaining the [baseline](phase8d_baseline.md) and cumulative resource measurements as work proceeds. The baseline records user-provided evidence and sign-off; this does not imply completion of later browser-authentication acceptance checks.
Progress: **8D.0 through 8D.2 validated by user sign-off; 8D.2 implemented / host-tested / build-verified. Reserve gates pending. 8D.3 inert parser and login-renderer preparation implemented; live cutover and 8D.48D.22 planned.** Record incremental results in `docs/agent/current-state.md`, retaining the [baseline](phase8d_baseline.md) and cumulative resource measurements as work proceeds. The baseline records user-provided evidence and sign-off; this does not imply completion of later browser-authentication acceptance checks.
Suggested next request:
> Continue Phase 8D.3 after its inert request-parser split. Complete login/logout authentication atomically (or split inert login rendering first if needed), preserve 8D.2 sign-off and open numeric reserve gates, and stop for M1 target/browser validation before 8D.4.
> Continue Phase 8D.3 with the atomic live login/logout cutover; request parsing and standalone login rendering are prepared and host-tested. Preserve 8D.2 sign-off and open numeric reserve gates, and stop for M1 target/browser validation before 8D.4.
For later chunks: