Files
ESP32_Serial_Swiss_Army_Knife/docs/phase8d_plan.md
T

56 KiB
Raw Blame History

Phase 8D — Incremental web administration plan

8D.14 current implementation (2026-09-09): Separately user-authorized Display settings implemented and host/build verified; target sign-off pending. Typed dim/off edits and explicit Apply/Save/Load/Defaults/Reset use generation-checked public UI ownership shared with CLI, with NVS outside critical sections and Reset commit-before-RAM publication. Serial-style Display view, preserved terminal/lease lifecycle, bounded login-bound operation results/no automatic replay.30 handlers/six sockets; no task/timer/stack-size/queue/schema/I2C/assets changes. Display7+shared HTTP, UI111+C/HTML/CSP, lifecycle23 and broad dispatcher/auth/owner/transport/throughput regressions PASS. Actual pre-edit build100,100 RAM/1,748,513 flash → final100,196/1,765,233 (+96/+16,720 B), pio26.09s PASS, CPU160 confirmed. Chromium geometry blocked by sandbox, target save/reboot/absent-panel/buttons-concurrency and heap/stack margins pending. Exact implementation and target checklist. No target/full-M3 sign-off, reserve approval or 8D.15/later work. Supersedes historical no-8D.14-authorization statements below; prior scoped functional acceptance stands.

Latest functional acceptance (2026-09-08): User signs off implemented 8D.12/8D.13 and Settings presentation refinements after boot/full-client-mix telemetry. Supersedes target-pending/no-signoff statements for this scope below, not historical build/test evidence or unreported checklist limits. Acceptance record retains all samples and counters: web sole writer + web/SSH/USB observers, both admins; no web send/queue/protocol failures or SSH handshake/auth/IO failures, but rejected input and one logout/disconnect retained without diagnosis. Loaded internal/DMA free31,512/23,756 B, minima2,276/156 B, largest20,480 B. Low conservative lifetime minima remain a resource follow-up, not approved reserves or proof of allocation failure. No full M3 claim or next-phase implementation authorization; 8D.14 awaits a separate request.

Current implementation (2026-09-08): User authorized 8D.12 and 8D.13 together, backend and Network UI delivered. 8D.12 covers nonsecret STA/AP/profile/mDNS edits and persistence; 8D.13 adds explicit secret replacement/disabled-STA clear and connection controls. Profile selection means selecting a configuration to edit; connection control is canonical Next profile, not explicit-index selection. 27 handlers/six sockets, one bounded slot/timer, no task/stack/queue/schema growth. Backend/cookie Network PASS, UI agent97+renderer/CSP/review PASS, lifecycle agent21 PASS; backend P3 queue-drop-counter finding fixed. Parent integrated suites and build PASS: 24.99 s, 99,548 B RAM / 1,742,437 B flash (+288/+36,656 vs legacy-cleanup baseline). Parent UI97/CSP, lifecycle21, Network/HTTP policy, canonical console/accounts, transport/tickets, idle/store/diagnostics checks passed; exact attribution below. Target behavior, timer heap/memory floors and HTTPD/dispatcher stack margins remain pending. 8D.12/8D.13 implementation is the exact API/SSID/secret/uncertainty contract and checklist. No Wi-Fi reset/default-secret/export, browser-shell policy widening, 8D.14 work, M3 completion or target sign-off. Supersedes historical next-request restrictions below; previous scoped acceptance stands.

8D.11 implementation history (2026-09-08): User-requested 8D.11 implemented, host-tested/build-verified; target sign-off pending. Accounts fingerprint listing and Ed25519/P256 import/delete/clear use canonical target-checked APIs and the existing dispatcher. Sparse-slot selection regression fixed; 24 handlers/six sockets, no new task/stack-size/queue expansion. Final build 96,076 B RAM / 1,703,685 B flash. 8D.11 record contains API/bounds, test attribution and pending hardware checklist. Supersedes historical wait-for-8D.11 instructions below; 8D.88D.10/M2 remain accepted. No M3 completion or 8D.12 work.

Latest target sign-off (2026-09-08): User reports thorough Serial parameter display/settings and user/account testing, supplies settled boot/full-client-mix evidence, and explicitly says implemented work can be signed off. Implemented 8D.88D.10 are accepted, including 8D.9 UX and both 8D.10 slices. Supersedes target/signoff-pending statements below, not build/test evidence or restrictions. 8D.10 target acceptance record contains all six loaded samples, client mix, counters and evidence limits. Full mix at 230400 baud confirms SSH sole writer + USB/two web observers with both admin routes. Final internal/DMA free 32,556/24,800 B, lifetime minima 19,228/11,472 B, largest 23,552 B; isolated failures retained without diagnosis. Exact revision/browser/durations/individual cases are unspecified; no leak-free-soak or reserve approval inferred. M2 stands; full M3/browser-shell parity is not claimed. Next is 8D.11 only when separately requested; this sign-off authorizes no implementation.

Current slice 2 completion (2026-09-08): 8D.10 implementation is complete, host-tested/build-verified, not target accepted; target validation/full signoff remain pending. Create/password/self workflows use bounded 768-byte admission and periodic credential cleanup (30-second deadline plus one-second timer/scheduling latency); admitted executing work is not cancelled. Protected generation is separate before commit, with no retained retrieval. Self revocation may prevent results: 401/disconnect is uncertain, never grounds for automatic retry. Browser-shell restrictions remain unchanged. Review's only finding, missing generated-route registration, is fixed as an independent optional endpoint with failure isolation/restart coverage, 23 handlers/six sockets. Parent PASS canonical accounts/boundary, parser 294, cookie accounts 9 plus shared, serial-settings 10, transport 25/tickets 12, store/serial and diff check. UI agent 57 plus CSP and route agent lifecycle 15 pass; these are not claims of the parent's additional UI/lifecycle reruns. Parent pio run PASS 25.61 s, 95,908 B RAM / 1,694,237 B flash, +80/+9,880 vs slice 1 and +200/+25,400 vs final 8D.9 UX. Timer runtime costs and heap/stack margins remain unmeasured. Current 8D.10 record contains contracts/evidence/target checklist. No sanitizer validation, assets/device/commit/8D.11 work, M2 reopening, prior-phase signoff or reserve approval inferred.

The implementation/continuation entries below are historical evidence. In particular, slice 1's exclusions, next-slice instruction, 22-handler count and build figures do not describe current slice 2.

Latest implementation (2026-09-08): User-requested 8D.10 first slice is host-tested/build-verified; target pending and phase incomplete. Pre-edit split follows the row below: Accounts list and other-account role/delete now implemented, with conditional target identity checks, existing dispatcher/target notifications and bounded automatic UI completion. Three optional routes, 22 handlers/six sockets, 95,828 B RAM / 1,684,357 B flash. 8D.10 record covers tests/resources/limits. Next is the second 8D.10 slice (create/password/generated-secret/self workflows), not 8D.11. Supersedes historical stop-before-8D.10 instructions; no prior target signoff, M2 reopening or reserve approval inferred. Final 8D.9 UX baseline is recorded in 8D.9.

Latest continuation (2026-09-07): Separately authorized 8D.9 is implemented / reviewed / host-tested / build-verified, with 8D.8/8D.9 target acceptance still pending. 8D.9 record: typed Serial framing/lifecycle/persistence, one session-bound operation slot on the existing dispatcher, manual bounded result recovery, 19 handlers/six sockets; final 95,708 B RAM / 1,666,725 B flash. No new task/queue depth/stack or broker writer semantics. This supersedes older stop-before-8D.9 instructions, not M2 signoff or deferred restrictions, admission followups or unapproved reserves. Stop before separately requested 8D.10; no target signoff inferred.

Previous implementation (2026-09-07): Separately user-authorized 8D.8 is implemented / host-tested / build-verified, with target/browser validation and new phase signoff pending. 8D.8 record: read-only admin Settings/Serial, nonblocking typed snapshot, 17 handlers/six sockets, final 95,580 B RAM / 1,654,529 B flash; exact tests/resources/limits and target checklist recorded. This supersedes older next-8D.8/wait-for-request instructions below, not M2 signoff or evidence. Deferred restrictions, accepted unresolved admission issue and unapproved memory/stack followups remain. Stop before separately requested 8D.9; no new signoff is inferred.

Status: 8D.08D.6 and M1 validated by explicit user sign-off. 8D.7 implemented scope validated and M2 explicitly signed off by the user on 2026-09-07 ("Jupp, sign M2 off"), superseding historical M2-open, target-pending and continuation instructions below without requiring revalidation. Full browser parity is not claimed: self/generated/key/legacy-credential and other owner-specific command restrictions remain deferred; bootstrap/recovery remain permanently UART0-only. Intermittent supported two serial + one admin web admission failures are accepted nonblocking, not fixed. Numeric memory reserves/stack margins remain unapproved. Next is separately requested 8D.8 read-only settings entry and Serial page; sign-off alone authorizes no implementation. See the 8D.7/M2 sign-off and evidence, 8D.6 implementation record, 8D.5 implementation record, 8D.4 implementation record, 8D.3 implementation record and 8D.0 baseline/M1 contract.

This is the execution plan for roadmap Phase 8D. The roadmap retains the feature/security requirements; this document defines small work units, dependencies, and release gates. The administration test matrix remains the final acceptance checklist.

Why this phase is split

The previous all-in-one attempt was rolled back from devel. The user reports a roughly +10k/-1k-line change, repeated agent/time/context interruptions, incomplete validation, broken browser login, and severe memory pressure. These are reported symptoms, not a diagnosed root cause. The separate experimental branch is not the implementation baseline: do not merge/cherry-pick it wholesale or copy its abstractions without a separately scoped review.

Deliver three independently useful milestones before attempting feature completeness:

  1. M1: reliable browser login/logout and the existing serial UI — 8D.08D.3.
  2. M2: browser admin shell alongside uninterrupted serial access — 8D.48D.7.
  3. M3: guided administration, one settings domain at a time — 8D.88D.21.

8D.22 is final integration acceptance, not the first time anyone builds or tries the firmware. Stopping after M1 or M2 is valid incremental delivery, but does not mean all of Phase 8D is complete.

Work-unit rules

  • One numbered chunk per implementation request. Do not interpret “continue Phase 8D” as permission to implement all remaining chunks. Select the first unblocked chunk and state its scope before editing.
  • Each chunk should fit one normal agent session, including review, a bounded build, focused validation, and handoff. Plan for roughly 6090 minutes of implementation and reserve at least the final third for validation/documentation. These are scope limits, not runtime guarantees; split further before coding if the estimate does not fit.
  • Aim for a few hundred changed authored lines in a small source set. An expected change above roughly 600800 authored lines, more than one new task, or several independent subsystem changes triggers a scope review and another split. This is not an incentive to compress code, omit tests, or hide generated changes.
  • Declare allowed files, behavior changes, explicit exclusions, resource deltas, and acceptance checks first. Source sets below are starting points, not permission to refactor every listed module. New files must have a narrow responsibility justified by that chunk.
  • Build the smallest complete increment. Internal preparatory chunks may leave unused interfaces, but must not expose half-protected routes. Do not ship an intermediate cookie-authentication route without CSRF/origin protection, currentness checks, expiry, and logout cleanup.
  • Reuse subsystem APIs, the existing HTTPD ownership model, the canonical command dispatcher, and authored UI assets. Do not introduce a generic web framework, CLI-over-HTTP endpoint, second command registry, second dispatcher, task-per-request model, or speculative settings infrastructure.
  • Do not rename/extract the entire admin_ssh_console module just to give it a generic name. Adapt the smallest necessary boundary and retain existing SSH callers. Do not regenerate src/web_assets_data.* or update vendored xterm dependencies as incidental work.
  • Add focused counters and checks with the feature that needs them, not as a final observability project. New tests must be local/bounded and document their exact command; this repository currently has no automated host test command. Avoid scaffolding a general test platform as part of implementation.
  • Run pio run with a finite tool timeout. A timeout is incomplete validation, not a pass; record it and stop rather than starting overlapping/repeated build jobs. Fix only failures caused by the chunk.
  • End with a reviewed diff and handoff: implemented behavior, build result, memory delta, checks actually run, hardware checks pending, and exact next chunk. Keep docs/agent/current-state.md current. Never include credentials, cookies, CSRF values, tickets, or verifier material in evidence.
  • A chunk is implemented / build-verified / target-verified as separate states. If device access is unavailable, provide the small manual checklist and mark it blocked for target validation. Do not proceed past an M1/M2 gate, or stack further runtime-changing chunks on an unverified predecessor, without an explicit user decision. Never silently promote documented tests to passed tests.
  • No automatic commits, branch changes, uploads, erase, NVS migration, or imports from the abandoned branch. The user chooses commit/revert checkpoints; preserve independently reviewable diffs.

Baseline and resource gates

8D.0 must record the actual baseline revision and configuration before resource budgets become acceptance criteria. The current memory notes report 94,532 bytes linked RAM and 1,599,765 bytes flash for an mDNS-enabled build; these are historical reference values, not a fresh measurement or sufficient runtime headroom.

For every chunk that changes allocation, concurrency, or routes:

  • Inventory static internal RAM, normal and worst-case internal heap, PSRAM, task count/stack sizes, request/response scratch buffers, queue depth, HTTPD handlers, sockets/TLS connections, and session/ticket capacity. Record limits before increasing any of them. Cookie sessions, serial sockets, admin sockets, and outstanding tickets are different resources.
  • Use UART0 memory to record internal/PSRAM free, minimum-free, and largest-block values at the same defined workload points: settled boot, login, serial connected, admin connected when available, full supported concurrent workload, and after repeated close/logout/reconnect. Compare both each chunk and cumulative growth against the baseline. Collect relevant task stack high-water marks where available; explicitly record missing instrumentation.
  • Set numeric internal-heap/largest-block/stack reserve floors and per-chunk incremental budgets from baseline measurements and actual allocation sizes in 8D.0. No invented “safe free heap” constant and no percentage-of-total-RAM substitute. If the floor cannot be measured, mark the gate pending rather than guessing.
  • Account for two simultaneous browser WebSockets when serial and admin coexist, plus HTTPS requests and existing SSH connections. Capacity rejection must be explicit and must not silently evict an active serial client/writer to admit an admin socket. Do not merely raise HTTPD/lwIP limits until a page happens to work.
  • Prefer bounded PSRAM payload storage only where cache-disable/lifetime constraints allow it; retain required internal control structures/stacks. Record fallback behavior: opportunistic internal fallback must not consume the recovery reserve unnoticed. Optional web-admin allocation failure must leave current serial, UART0, USB, and SSH paths usable.
  • Do not accept a monotonic heap leak, declining largest-block trend, watchdog/stack fault, unexplained reserve-floor violation, or unrecoverable login failure. Stop and fix/split the current chunk instead of borrowing from future budgets. Supported baud rates and client capacities must not be silently reduced to pass.

Always-on regression smoke check

After each runtime-changing chunk: build; boot and read UART0 status/memory; log in through the currently supported browser authentication path; open serial, explicitly disconnect and reconnect; exercise native USB UART1 access and existing user/admin SSH routes. Add the chunk-specific checks below. Use a fixed small repetition count selected in 8D.0 for routine lifecycle checks and a longer bounded soak at milestone gates; record actual counts and durations, not just “stable.”

M1 — Authentication without changing the rest of the UI

8D.0 — Baseline, browser contract, and resource budget

Scope: Documentation and measurement only. Read the relevant web_server, web_serial_transport, web_ui, user-principal, and memory-reporting paths. Confirm current login/serial operation on rolled-back devel; no investigation of the experimental branch is required.

Deliver: A short baseline record, measured resource table, supported concurrency/socket budget, and exact M1 browser contract: public login assets; protected routes; session/ticket capacities and lifetimes; absolute/idle expiry rules; capacity rejection; cookie renewal; pre-login CSRF bootstrap; strict origin policy; no-store responses; logout-versus-account-wide revocation; and browser error handling. Preserve mDNS and direct-IP access as separate host-only cookie origins. Choose the simplest bounded policy, with no Basic compatibility path by default. Explicitly list the few authentication request/response fields rather than designing all future settings APIs.

Gate: Existing admin and user browser login, serial data, USB, and SSH work; baseline pio run and target memory evidence are recorded. If the rolled-back baseline already fails login, diagnose that in a separate task before changing authentication. If target evidence is unavailable, the budget and runtime gate remain pending.

Record: 8D.0 baseline, browser contract, resource inventory and target checklist. Validated by user sign-off on 2026-09-05, with tested source identified as d8999cd4a96e477fabd392ced02d810c3cd22d0f. Historical baseline build: 94,532 B linked RAM / 1,599,973 B flash. User-provided boot, browser, lifecycle, mixed-client and 15-minute soak/60-second cleanup samples include heap and SSH stack measurements. The user attributes SSH I/O errors to out-of-spec 460400-baud testing; see sign-off for distinction from reported UART configuration. Numeric reserve floors and incremental budgets remain open, without blocking user-approved 8D.0 closure. Do not treat the documented contract as implemented behavior.

8D.1 — Bounded server-side session primitives, not yet browser-facing

Start in: src/web_server.{c,h}, src/user_database.h, src/secure_random.h; add a narrowly owned session module only if needed.

Scope: Fixed-capacity session issue/lookup/expiry/invalidation with digest-only token storage, copied principal and authentication-generation binding, CSRF state, and secret-free capacity/expiry/invalidation counters. Decide ownership/locking explicitly because future console revocation is not necessarily on the HTTPD task. Wipe transient secrets and use existing secure randomness. No login page, HTTP auth cutover, admin route, new permanent task, or settings work.

Gate: Focused local/component checks for lifecycle, capacity, slot reuse, stale principals, and failed initialization; bounded storage accounting and build. Existing Basic-auth behavior remains unchanged. If no executable harness is practical, distinguish code review from target execution and carry the missing checks into 8D.3; do not claim unused code was exercised by a boot test.

Record: Implemented web_session_store plus admitted-start/stop lifecycle hooks; no route uses it yet. python3 tests/web_session_store/run.py and pio run pass. Linked RAM 95,204 B (+672 B), flash 1,600,505 B (+532 B) versus 8D.0; static store/lock symbols total 664 B, no module heap allocation/new task/routes/sockets. Only init/stop are currently retained in the firmware link; host tests exercise the full production module. See the 8D.1 implementation record for exact accounting, review limits and target evidence. Target validated by user sign-off on 2026-09-05, following boot/full-client-mix samples and a reported long-lasting command at full 115200-baud line speed with no dropped broker packets. Numeric reserve gates remain open. Stop before 8D.2 until requested.

8D.2 — Bind existing serial tickets and sockets to a web-session identity

Start in: src/web_serial_transport.{c,h}, src/web_server.{c,h}, the session module from 8D.1, and the existing revocation call sites in src/user_console.c.

Scope: Add generation-safe originating web-session identity to serial tickets/slots, specific-session cleanup, and account-wide invalidation hooks. Keep web-session identity distinct from account authentication generation and transport slot generation. Preserve authoritative currentness checks when best-effort notification fails. Keep the current Basic path working until the atomic cutover; no public cookie-auth route yet.

Gate: Build and existing serial regression. Exercise session-specific versus account-wide cleanup through focused checks where available: one session's logout must not disconnect another session for the same unchanged account; account mutation must invalidate all affected account sessions/tickets. Stale cleanup cannot close a reused slot. Record any dormant paths that require the next chunk's browser validation.

Record: 8D.2 implementation, accounting and target checklist. Distinct session IDs now bind tickets/slots; internal cleanup and account-notification hooks are present, while Basic remains the only public authentication path. Both focused host modes and pio run pass: 95,260 B linked RAM (+56 B), 1,601,925 B flash (+1,420 B) versus 8D.1. No capacity/task/route/asset changes. Target validated by user sign-off on 2026-09-05, following post-flash boot and full-client-mix samples. Sanitizer execution is blocked by missing host runtime libraries; numeric reserve gates remain pending. Stop before 8D.3 until separately requested.

8D.3 — Atomic login/logout cutover with the unchanged serial application

Start in: src/web_server.{c,h}, src/web_ui.{c,h}, session primitives, and the session-bound serial integration.

Scope: Minimal same-origin login page, session status, explicit logout, bounded login throttling, and cookie-based authorization of the existing app/status/ticket routes. Use a host-only __Host- cookie with Secure, HttpOnly, SameSite=Strict, Path=/, no Domain, and explicit lifetime. Enforce the agreed pre-login CSRF protection plus strict origin checks on login, and session CSRF plus strict origin checks on every authenticated mutation including logout and ticket issuance. Keep passwords/tokens transient, preserve security headers/CSP, update the loader hash atomically if it changes, and handle JSON/text safely. Remove browser Basic challenges/cache authorization so stale credentials cannot bypass logout. No admin shell, settings, visual redesign, or new serial protocol.

Gate — M1 (mandatory target/browser pause): Both roles log in; incorrect credentials give a usable error; logout and account switching need no browser credential-cache reset. Refresh/reboot/expiry return to a usable login; no redirect loop or missing login asset. Test fresh and previously Basic-authenticated browser profiles, direct IP and mDNS where available, bounded capacity/backoff, CSRF/origin rejection, current-session logout cleanup, password/role/key changes and deletion/recreation via UART0, and unrelated-session/account isolation. Existing serial data, writer ownership, and explicit reconnect still work. Record memory before/during/after repeated login/serial/logout and simultaneous SSH/USB operation. Stop here for user confirmation before M2.

If 8D.3 exceeds the work-unit limit, first split out inert login-page rendering or private request-parsing helpers. Do not split the live security cutover into an insecure intermediate deployment.

M1 sign-off (2026-09-06): User explicitly completed M1 after successful both-role login, full-client-mix operation and post-soak telemetry. See sign-off/evidence. Minima/largest blocks are unchanged from the earlier loaded sample, with no reported transport failures. Clients remain connected; exact soak duration/revision and disconnected cleanup are not claimed. Numeric reserves stay open; unrecorded detailed checks are evidence limitations, not blockers to the user-approved milestone closure. Wait for a separate 8D.4 request. Older checkpoint notes below are historical.

Live cutover checkpoint (2026-09-05): Implementation, HTTPD boundary, resource accounting and M1 handoff. Cookie login/logout replaces Basic for app/status/ticket routes; explicit pre-101 admission and strict header/Origin/CSRF policy use an isolated version-checked private IDF adapter, not an SDK patch. Resumed another agent's implementation and fixed pending-buffer wiping to preserve right-aligned unread data. All five focused suites and pio run pass. Final 95,508 B RAM / 1,625,689 B flash, +248/+23,764 B versus 8D.2. No hardware/browser execution or reserve-floor approval. Stop for mandatory M1 acceptance before 8D.4. The following preparatory records are historical, superseded for current implementation status.

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

Implementation checkpoint (2026-09-06): 8D.4 implementation, resource accounting and target sign-off. Implemented / host-tested / build-verified / validated by explicit user sign-off after boot/full-client-mix evidence and successful empty Enter and soak testing. Owner callbacks and transport-qualified identity retain the existing two shared slots and SSH API, with no browser routes. Build 95,084 B RAM / 1,627,173 B flash, -424 / +1,448 B versus recorded latest 8D.3. Numeric reserves remain open; unrecorded detailed checks do not reopen this user-approved closure. Wait for a separate 8D.5 request.

Start in: src/admin_ssh_console.{c,h}, src/console_input.{c,h}, and only the necessary src/ssh_transport.c callers.

Scope: Introduce the minimal transport identity/output/lifecycle boundary needed by a future web frontend. Retain the single dispatcher, fixed queue, line editing/history/completion/prompts, currentness checks, generation tokens, and existing SSH API compatibility where practical. Do not duplicate per-SSH buffers for hypothetical web capacity or rename the whole module. No web endpoint or second dispatcher/task.

Gate: UART0/admin-SSH serialization, hidden prompts, completion/history, deferred actions, disconnect/revocation with queued work, and slow-output behavior regressions pass. Memory delta is explained before adding browser slots. This refactor must stand alone and leave behavior unchanged.

8D.5 — Bounded admin WebSocket backend, no normal UI entry yet

Target sign-off (2026-09-06): User explicitly validates 8D.5 after settled cold-boot telemetry and successful 15-minute full-client-mix active-use soak at 230400 baud, reporting a few broker drops under extremely fast/dmesg output. Evidence and limitations. This supersedes the older pending acceptance/checkpoint notes below. No zero-drop claim or loaded/cleanup telemetry is inferred. Numeric reserves remain open; missing detailed results do not reopen signed-off 8D.5. Wait for a separate 8D.6 request; M2 is not yet complete.

Combined backend checkpoint (2026-09-06): User authorized finishing all of 8D.5, superseding the prerequisite-only pause below. Backend, shared-console allocation, protected admission, revocation, fail-before-side-effect restrictions and local/manual test tooling are implemented / host-tested / build-verified. All relevant host suites pass, including real cookie/store/ticket/transport endpoint integration. Parent reports the sequential final pio run after the HTTPD-owned shutdown/reuse fix passed at 95,580 B RAM / 1,637,273 B flash, 23.55 s. Deltas: +416/+9,224 B versus prerequisite, +496/+10,100 B versus 8D.4, +1,048/+37,300 B versus 8D.0. Final independent security integration review reported no actionable findings. Target runtime/socket measurements, numeric reserves and acceptance remain pending. No device operation or 8D.6/UI/M2 completion. Details and historical build evidence: implementation record. 8D.6 onward remain separately requested work.

Preparatory checkpoint (2026-09-06): 8D.5 prerequisite, validation and handoff. Resumed existing uncommitted console-owner currentness/prompt cleanup work; reviewed and extended production-publication tests. Both console suites and pio run pass: 95,164 B RAM / 1,628,049 B flash, +80 / +876 B versus recorded 8D.4. No routes/tasks/sockets/UI added. Work-unit review keeps the live backend in the next increment within 8D.5; backend/M2 remain incomplete. Target regression or explicit user decision is needed before stacking runtime changes; numeric reserves remain open.

Start in: The console boundary from 8D.4, src/web_server.{c,h}, and a small web-admin transport adapter as justified. Reuse existing HTTPD scheduling patterns without mixing admin data into the serial transport.

Scope: Admin-only, short-lived single-use tickets bound to both current web session and principal; bounded console admission/input/output; session expiry/logout/revocation cleanup. HTTPD owns socket work and the dispatcher owns command execution. No broker client for this route. An absent UI is not authorization: every ticket, upgrade, and sensitive operation is checked on the server. For self-affecting web actions not safely supported yet, explicitly reject before side effects and list the temporary restrictions for 8D.7.

Gate: Focused authenticated test-client or temporary local development-page checks (not a shipped debug endpoint): user-role rejection, admin command/output, prompt/backpressure, stale ticket/slot rejection, cleanup, and concurrent UART0/admin SSH. Admission failure does not remove the serial writer. Build and measure actual socket/console-slot cost. No generic HTTP command runner.

8D.6 — Browser terminal selector and serial-lease preservation

Target sign-off (2026-09-06): User confirms the remaining validation checks after 60-second boot/full-client-mix/partial-cleanup telemetry, and closes 8D.6. Evidence and handoff. Full mix at 230400 baud includes browser/admin SSH and four serial broker clients with one web writer. Internal/DMA lifetime minima 6,516 / 1,580 B remain a numeric-reserve follow-up, not a claim of exhaustion or grounds to reopen sign-off. Exact flashed revision/browser/repetition counts/soak duration were not separately supplied. Latest cosmetic toolbar build passes in 7.60 s, unchanged 95,580 B RAM / 1,646,489 B flash, with 17 UI groups/CSP checks passing. Supersedes older pending notes below. Wait for separately requested 8D.7; M2 remains open.

Implementation checkpoint (2026-09-06, updated after review fixes): 8D.6 implementation, accounting and target checklist. Implemented / host-tested / build-verified; target validation and parent re-review of fixes pending, no phase sign-off or M2 completion. Both P2 findings fixed: session identity changes require a clean document before adopting a new view, and fit caches only successful measurements with three bounded readiness retries. Sixteen production-rendered UI groups and focused UI/auth/store reruns pass; prior admin/console regression results remain recorded. Final finite pio run: 21.35 s, 95,580 B RAM / 1,646,489 B flash, 0 / +9,216 B versus 8D.5; cumulative +1,048 / +46,516 B versus 8D.0; review-only delta 0 / +1,376 B. No backend/capacity/8D.7 restriction/asset change. Numeric reserves remain open; prior sign-offs stand. Stop for validation/user decision before separately requested 8D.7. This supersedes older planned-8D.6 status text in this document.

Start in: src/web_ui.{c,h}, using the completed serial/admin protocols.

Scope: Add admin-only Serial/Admin selection and separate bounded terminal state; no new settings. Keep serial connected and drained/observed while hidden. Keep writer/observer identity and Request control/Release control visible in both modes. Mode changes route displayed output and keyboard input only. Closing the admin route or shell exit leaves serial intact; explicit serial Disconnect retains its documented cleanup/reconnect behavior. Bound scrollback and avoid leaking browser listeners/sockets across switches.

Gate: Repeated switches preserve the same broker client ID and writer ID; background serial remains observed, with any bounded overflow visible rather than silently stopping observation. Ordinary users retain serial-only navigation and server-side denial. Exercise hidden prompts, line editing/history/completion, resize, admin-route reconnect, full logout, and expired-session UI. Measure simultaneous two-WebSocket plus HTTPS request headroom; no eviction to make the selector work.

8D.7 — Web-shell lifecycle parity and M2 acceptance

M2 sign-off (2026-09-07): User explicitly says "Jupp, sign M2 off" after the implemented-scope validation below and discussion of 8D.8 next. M2 is accepted with the deferred restrictions, nonblocking unresolved admission issue and unapproved numeric reserves/stack margins recorded in the current status. No full parity, new checklist passes or revalidation requirement is implied. Next is 8D.8 read-only settings entry and Serial page only upon a separate request; no implementation is authorized by this sign-off alone. The earlier checkpoints and original scope/gate below are historical planning and evidence, not outstanding conditions for M2 closure.

Historical 8D.7 target sign-off (2026-09-07), before M2 sign-off: User explicitly requests marking 8D.7 validated after thorough testing. Certificate rotation and web start/stop were verified, with lifecycle via UART0/SSH admin/web admin and restart after browser stop via another route. Full mix without broker drops up to 230400 baud after external adapter baud correction is user-reported. Intermittent supported two serial + one admin admission failures have recently not recurred and are accepted nonblocking, not fixed. Evidence and limits. No detailed reboot/individual mutation checklist passes are inferred. This supersedes pending status and next-slice instructions in the historical checkpoints below for all three implemented slices, including other-account operations. Remaining self/generated/key/legacy-credential and other owner parity stays deferred/restricted; numeric reserves and M2 acceptance remained open at this earlier checkpoint.

Second bounded certificate slice: Implementation, separate slice histories and pending target checklist. Exact parsed browser web certificate rotate --force uses a typed request-queue union and immutable owner dispatcher_actions mask: bounded drain/200 ms then nonblocking handoff to the existing 12 KiB dispatcher, not 4 KiB control. Pending input gating, token/principal/session revalidation and executing-slot reservation persist through execution. Transactional certificate commit → stop → start short-circuits errors and retains ownership on failed stop; SSH/UART0 unchanged. No new tasks/depth/routes/assets/stacks; target owner-mask/local-scratch accounting and stack margins unknown, host sizeof is not proof. Parent final pio run PASS 26.32 s, 95,580 B RAM / 1,648,061 B flash: 0 / +1,036 B vs first slice, 0 / +1,572 B vs 8D.6, +1,048 / +48,088 B vs 8D.0. Implementer focused suites pass (transport 25/tickets 12, server 11, boundary including certificate, lifecycle/policy/cookie-admin/store-serial/diff); independent reviewer reports no actionable findings. Sanitizers unavailable (missing libasan/libubsan); no hardware validation. User explicitly authorized stacking the next bounded slice: credential/account, then other owner slices. Other mutations remain blocked; target/M2 acceptance and numeric reserves pending. This supersedes the first-slice next-step/continuation-pending statement below.

First bounded increment history (2026-09-06): Implementation, restrictions and target checklist. Browser reboot and web stop now use existing deferred control with final WEB session/currentness checks and discard of pending input. Other identity/network/account/SSH restrictions remain explicit. Host suites/review/build pass: 95,580 B RAM / 1,647,025 B flash, 0 / +536 B versus 8D.6, final build 12.44 s. No new task/route/capacity. Target regression or explicit continuation decision pending; this is not completed 8D.7/M2. Next slice remains HTTPS identity/certificate handling, not settings. Prior sign-offs stand; numeric reserves remain open.

Start in: The console deferred-control boundary, web lifecycle owner, and only the affected command handlers.

Scope: Close the explicit 8D.5 restrictions for self-terminating web-shell operations (including HTTPS stop/identity changes and reboot where supported by the canonical registry). Reuse bounded deferred acknowledgement/close semantics; never claim application-buffer draining proves browser receipt. Prevent further input during pending actions. Preserve UART0-only bootstrap/recovery and safe policy for one-time self-generated credentials. No typed danger-zone API yet. If this requires several distinct owner changes, split them before implementation and keep unimplemented actions explicitly rejected.

Gate — M2 (mandatory target/browser pause): Browser/UART0/admin-SSH commands serialize with no output or hidden-prompt crossover; queued work is discarded after logout/revocation/slot reuse. Deferred acknowledgement/drain handling is bounded and reconnect behaves as documented; this is not confirmed peer receipt or a deadline for dispatcher queue residence or underlying certificate/NVS/lifecycle execution. Run simultaneous USB, serial WebSocket, admin WebSocket, user SSH, admin SSH, UART0 and UART1 traffic; verify lease retention, recovery availability, and measured memory/stack floors. Web-admin initialization/admission failure must leave M1 serial login and existing non-web paths usable. Stop for user confirmation before settings.

M3 — Typed settings, one domain per chunk

Every row is a separate implementation request, not a batch. Add only the endpoints and UI needed for that row. All typed routes require current admin authorization, bounded bodies/responses, secret-safe encoding, and the established CSRF/origin/no-store policy. Do not send constructed command strings to esp_console_run().

Typed operations must preserve subsystem owner/lock/persistence contracts and coexist safely with console operations. The current admin_command_gate is a narrow user-command wrapper, not an existing global typed-operation serializer; do not assume it solves concurrency. Specify per-domain serialization and committed-mutation notification before adding writes. Reuse a small common mechanism only when a concrete second caller needs it.

Chunk Bounded deliverable and starting source set Focused acceptance gate / exclusions
8D.8 — Read-only settings entry and Serial page Minimal admin Settings navigation, common bounded error handling, and a typed serial snapshot; start in web_server, web_ui, serial_service.h, serial_config.h. Normal users are denied by server, unknown/oversized input fails safely, serial values match UART0. No mutations, schema generator, empty placeholder pages, or all-subsystem snapshot.
8D.9 — Serial edits and persistence Typed framing/lifecycle and explicit apply/save/load/default/reset controls through existing serial APIs. Invalid framing is rejected; working versus persisted state and stop/reconfigure data-discard effects are explicit. Writer ownership semantics stay unchanged; compare CLI/browser edits and reboot persistence. No quick popover yet.
8D.10 — Accounts and passwords User list/create/delete/role/password workflows, final-admin protection, one-time generated-password handling; start in user_database, existing user_console mutation/revocation behavior, and web handlers/UI. No raw database export, verifier fields, or UART0 recovery endpoints. Commit/invalidation behavior matches CLI, unrelated users remain connected, own-account changes have safe reconnect/credential-delivery semantics, secret fields are cleared after use. If CRUD and password UX exceed budget, split at read-only/role/delete versus create/password before editing.
8D.11 — SSH authorized keys List fingerprints and add/delete/clear supported public keys through bounded user APIs. Ed25519/P-256 import, maximum supported length, malformed input, duplicates, targeted revocation, and unchanged SSH authentication behavior. No private-key upload/export or host-identity management.
8D.12 — Network settings without secret mutation Secret-free STA/AP/profile and mDNS settings, non-secret edits, and explicit persistence through wifi_manager, wifi_config, mdns_service/mdns_config. Responses never serialize saved PSKs; validate working/persisted semantics, live hostname changes, and behavior after connection loss. No new manager/task or Wi-Fi blob migration. Split mDNS into a follow-up if needed.
8D.13 — Wi-Fi secrets and connection controls Explicit password replacement/clear semantics, bounded transient input, profile selection/reconnect and AP policy actions using manager-owned operations. Preserve existing secrets when fields are omitted; never prefill saved secrets; document apply/save and likely connection loss; reconnect via STA/AP and verify UART0/USB recovery. No background secret fetch or general credential export.
8D.14 — Display settings Implemented, host/build verified; target pending. Typed local display configuration and explicit persistence via local_ui_config/generation-checked public UI APIs; contract/evidence. Host limits/storage/CLI-generation/activity/lifecycle regressions pass; actual save/reboot, absent-display and concurrent buttons/CLI target checklist remains pending. No I2C ownership changes or electrical diagnostics UI.
8D.16 — Broker client visibility and writer transfer Implemented, host/build verified; independent parent review and target sign-off pending. Admin-only detailed snapshot plus explicit confirmed assignment through existing dispatcher/broker; smallest owner changes add atomic lease-version/target validation and nonwrapping IDs. Contract/evidence. Host stale/disconnected/reused target and lease ABA rejection, one writer, admin denial, bounded/session-isolated results, lifecycle and UI non-mutation regressions pass. Concurrent physical USB/SSH/browser/full-mix and stack-margin checklist remain pending. No transfer on page open/selection; no8D.17/18 popovers/icons.
8D.17 — Serial/Wi-Fi quick popovers Implemented, host/build verified; independent parent review and target sign-off pending. UI-only reuse of completed typed endpoints through one settings DOM/controller, full-page link and shared validation; web_ui. Contract/evidence. Hover/focus/click/tap, Escape/outside dismissal, no opening/selection mutation, explicit Apply/Save, Network password exclusion; draft/session/pending-operation regressions pass. Full-page hover deliberately preserves its existing draft; explicit activation navigates that page. Optional geometry blocked by sandbox; real pointer/keyboard/touch/full-mix checklist pending. No duplicate backend/new settings scope/icons/8D.18.
8D.18 — Client/writer contextual dialogs Implemented, host/build verified; independent parent review and target sign-off pending. UI-only reuse of8D.16 and8D.17's single host for live client popover and confirmed Active writer dialog. Contract/tests/checklist. Native pointer/keyboard/touch entrances; single-flight5-second live refresh/deadline, explicit selection preserved without lease-token renewal, sticky stale/absent rejection, full-page draft protection and focus-safe updates. Ordinary users retain only ordinary status.135 UI groups plus broad broker/auth/lifecycle regressions pass; real browser/device checks pending. No new writer policy/backend/icons/8D.19/later.
8D.19 — Ordinary service/session controls Remaining work dropped by user (2026-09-13). Existing SSH slice retained, host/build verified and independently reviewed; target sign-off pending. Typed SSH status and confirmed exact-session disconnect/start/stop via existing dispatcher/SSH owner, excluding invoking HTTPS-session-cutting actions. SSH contract/tests/resources. Explicit SSH/all-SSH/one-session confirmation; owner lock/service generation/retired session IDs reject stale/reused targets and stop/start ABA. No settings/identity clear. SSH4, cookie SSH6+shared, dispatcher, lifecycle27 and UI143 PASS. No further targeted browser-session or USB controls are planned; the unimplemented remainder is removed, not a prerequisite for final acceptance. Existing SSH controls remain unchanged. No generic broker disconnect or8D.20/21. Target full-mix/heap/stack/recovery checks pending.
8D.20 — Self-affecting service actions and reboot User-authorized HTTPS stop/restart/reboot integration implemented, host/build verified; independent parent review and target sign-off pending. Current-admin typed routes, bounded send-return/HTTPD ID callback/existing dispatcher handoff, canonical generation/reserved lifecycle and shared reset API; explicit Settings HTTPS/Reboot and link to existing Network. Contracts, tests, costs and checklist. PASS lifecycle41, cookie lifecycle8+shared, UI153+HTML/CSP, dispatcher and broad regressions. Tests cover queue/send/lost/late callback/request-lifetime/ABA/login revocation/deadlines/owner failures, no replay/late result adoption, all-client/unsaved-state and accurate UART0/SSH/USB recovery. Final pio100,508 RAM/1,821,505 flash (+104/+13,064 vs pre-phase).39 handlers/six sockets, no new tasks/timers/queue growth/assets/config/identity or unrelated19/21. Real TLS/scheduling/reboot/full-mix/runtime reserves and independent review remain pending.
8D.21 — Security/danger-zone settings Allowed HTTPS+SSH identity implementation complete, host/build verified; SSH independent parent review and all target sign-off pending. Remaining SSH slice reuses19 routes/controller/dispatcher for fixed P-256 fingerprint/algorithm and confirmed rotation, with service+identity reservation shared by canonical CLI/deferred SSH/direct security, retained-context failed-stop safety and manual15-second/no replay. SSH security5+runtime+management5, cookie SSH7/UI158 and broad regressions PASS; final100,556 RAM/1,828,573 flash, CPU160. Current aggregate contract/evidence. Preserved HTTPS slice history: Public stored certificate fingerprint/identity+service generations and confirmed rotation reuse8D.20 routes/ACK slot/dispatcher/UI controller. Shared service-before-identity reservation covers canonical CLI/browser-shell/direct security mutation exclusion through crypto/commit/stop/start; no rollback after commit. Canonical TLS-only reset/recovery retained without duplicate browser Reset. Exact HTTPS contract/tests/resources and pending gates. SSH identity continuation now implemented as described above; no recovery-secret operation added. PASS security17, lifecycle44+two real-mbedTLS/NVS integration groups, cookie lifecycle8+shared/all variants, UI156+HTML/CSP and broad regressions. Final pio100,532 RAM/1,825,073 flash (+24/+3,568 vs audited baseline),39 handlers/six sockets/no new task/timer/queue/assets/config. Confirm fingerprint/both generations, warn changed trust, trusted UART0 verification/fresh login, partial effects and no replay. No private-key/certificate export, browser invalid-material recovery or configuration wipe. Bootstrap/unavailable-database recovery remain UART0-only; NVS encryption/secure boot/OTA/new factory reset excluded. Parent/target trust/persistence/full-mix/runtime reserve gates pending.

Scope decision (2026-09-09): Phase 8D.15 has been removed at the user's request. Network diagnostics remain exclusive to the admin shell; no dedicated typed diagnostic endpoints or settings UI are planned. Existing shell transport permissions and implemented Network settings/status remain unchanged. Later phase numbers are retained for stable references; the next planned chunk after 8D.14 is 8D.16, requiring a separate implementation request.

8D.20 authorization clarification (2026-09-13): The user explicitly authorized20 with knowledge that19 completed/reviewed only its SSH slice. Proceed using needed established prerequisites; do not implement unrelated other-service19 as a dependency. The internal owner prerequisite was not full20 delivery; the explicitly authorized continuation now completes API/ACK/dispatcher/UI integration and records pending independent review/target checks in the linked contract.

Dependencies: 8D.8 establishes only the minimal typed-request/UI pattern. 8D.9 follows 8D.8; 8D.11 follows 8D.10; 8D.13 follows 8D.12; 8D.17 follows 8D.9/8D.13; 8D.18 follows 8D.16; 8D.20 follows 8D.7/8D.13/8D.19; 8D.21 follows 8D.20. Independent domains can be reordered by the user, but should not be implemented concurrently against shared web_server/web_ui files.

8D.22 — Final integration acceptance and documentation

No new feature work. Run the complete Phase 8D acceptance matrix, including the accumulated per-chunk regression checks and a bounded concurrent-transport soak at supported workloads. Record the exact revision/configuration, browser(s), client mix, baud rate, test duration/cycle counts, linked RAM/flash, runtime free/minimum/largest internal heap and PSRAM, relevant stack margins, and queue/drop observations. Compare against 8D.0 and milestone measurements. Known high-load serial drops are not permission for new unbounded blocking, hidden lease loss, login failure, or resource exhaustion.

Verify optional web-session/admin/settings initialization failures preserve UART0 and native USB; admin-only failure must not disable otherwise working serial web access. Check logout/expiry/revocation across every added route, and verify no retained Basic path or direct typed endpoint bypasses policy. Review all routine snapshots/logs/DOM status for secret exposure and confirm changes did not regenerate unrelated vendor assets.

Update the roadmap and user/command documentation to distinguish completed features, explicit restrictions, build results, and tests actually passed. Update durable agent architecture/code-map notes only for implemented ownership/contracts. If a check fails, open a bounded repair chunk and rerun affected checks; do not append features or declare the whole phase done with unrecorded failures.

8D.19 scope closure (2026-09-13): The user dropped the remaining ordinary service/session-control expansion as unnecessary for this device's small scope. Retain the delivered SSH controls and their validation requirements; do not implement additional browser-session or USB controls. Later phase numbers remain unchanged. This is a scope reduction, not target sign-off.

Progress and next-request template

Progress: The retained Phase 8D feature scope is implemented. 8D.15 was removed and the unimplemented remainder of 8D.19 is dropped; existing SSH controls remain. Later target validation, runtime resource margins and 8D.22 final integration acceptance/documentation are still pending. Earlier scoped user sign-offs stand. Record incremental results in docs/agent/current-state.md, retaining the baseline and cumulative resource measurements as work proceeds.

Suggested next request:

Work on 8D.22 final integration validation and documentation when requested. Preserve existing scoped sign-offs, admin-shell-only diagnostics, UART0-only recovery and bounded ownership. Validate the retained implementation, record actual versus pending target evidence and resource margins, and reconcile documentation. Do not restore the dropped 8D.19 expansion or add new features.

For later chunks:

Work on Phase 8D.N only. First verify its prerequisites and last target-validation checkpoint. State allowed files, exclusions, resource budget, and focused checks. Split the chunk if it does not fit one session with validation time reserved. Build, report actual versus pending validation and memory deltas, update the handoff, and stop; do not continue to the next chunk.