Files
ESP32_Serial_Swiss_Army_Knife/docs/agent/current-state.md
T
Commander1024 117c694cd4 Add SSH Console Ownership Boundary (Phase 8D.4)
Implement transport-qualified session identity and immutable owner
adapters
for SSH console lifecycle and output-drain operations. Add focused host
tests
covering admission, stale identities, deferred actions, completion
races,
prompts, backpressure, and slot reuse. Update Phase 8D documentation and
current-state tracking.
2026-09-06 09:07:11 +02:00

35 KiB
Raw Blame History

Current project state

This file is working memory. Update it during active work and before handoff; do not treat it as a permanent design record.

Development state

  • 8D.4 implemented / host-tested / build-verified (2026-09-06), target pending: User separately requested the small console boundary. Added transport-qualified identity and immutable output-drain/lifecycle owner callbacks in admin_ssh_console, retained the SSH admission API in ssh_transport, and kept two shared slots, one dispatcher and all capacities/stacks. Shared completion scratch now has nonblocking ownership; occupied-slot replacement and deferred input fail closed. python3 tests/admin_console_boundary/run.py and existing policy suite pass; independent review found no defects. pio run passes in 43.61 s, 95,084 B RAM / 1,627,173 B flash (-424 / +1,448 B versus recorded latest 8D.3). See docs/phase8d4_implementation.md for contracts, host-test limits, accounting and target checklist. No browser admin routes, uploads or 8D.5 work. Prior M1 sign-off stands; numeric reserves and 8D.4 target regression remain pending. Stop for validation/user decision before separately requested 8D.5.

  • 8D.3/M1 completed by explicit user sign-off (2026-09-06): User closes M1 after post-soak evidence, superseding all older pending/blocked/in-progress statements below. Four broker clients remain active at 115200 baud (SSH writer; USB/two web observers), both SSH routes and two cookie sessions active. Post-soak internal/DMA/PSRAM free 38,656 / 30,900 / 8,138,320 B, minima 13,756 / 6,000 / 8,072,744 B, largest 25,600 / 25,600 / 7,995,392 B; SSH stack minimum-free 16,288 B. Minima/largest unchanged from earlier loaded sample; no reported transport failures. Record/evidence limits in docs/phase8d3_implementation.md. Sample is still connected, not disconnected cleanup; exact soak duration/revision not supplied. Unrecorded detailed checks do not reopen signed-off M1 or become claims of execution. Numeric reserves remain open; empty-Enter fix target retest remains unrecorded separately. Wait for a separate 8D.4 request; no M2 implementation performed.

  • 8D.3 both-role target login confirmed / mixed-client evidence (2026-09-05): User reports successful HTTPS user+admin login after Origin fix; previous login blocker is resolved. Settled internal/DMA/PSRAM free 71,204 / 63,448 / 8,247,744 B. Mixed load free 33,868 / 26,112 / 8,089,060 B, minima 13,756 / 6,000 / 8,072,744 B, largest 25,600 / 25,600 / 7,995,392 B; SSH stack minimum-free 16,288 B. 115200 baud, four broker clients (SSH sole writer, USB + two web observers), user/admin SSH active. No reported web transport or SSH I/O errors; 6 login attempts/3 invalid credentials/1 logout, zero security rejections. Two identical loaded heap samples are not a soak/leak or reserve proof. Full details/provenance in docs/phase8d3_implementation.md. M1 validation in progress, not signed off.

  • Admin SSH empty Enter fix (2026-09-05): User's empty line was classified as UART0-restricted because remote_command_allowed required argc>0. Changed only helper classification to allow empty input through normal quiet IDF handling; currentness and physical-only commands remain protected. New python3 tests/admin_ssh_policy/run.py passes 15 cases using production helper/installed parser. Build passes 21.04 s, 95,508 B RAM / 1,625,725 B flash (+20 B flash). Not uploaded or target-tested. Ask for empty Enter/normal-command smoke on next flash; no 8D.4 refactor.

  • 8D.3 Origin-null fix (2026-09-05), target retest pending: User confirmed challenge200/login403 with Origin: null, same-origin Fetch Metadata and pre-login cookie; post-attempt counters show 0 password attempts and 7 security rejections. Root cause is non-CORS fetch POST under no-referrer. Login fetch now uses mode:'cors'; app helper uses cors for POST tickets/logout, unchanged GET mode. Same-origin credentials/fixed paths/redirect denial/CSP/no-referrer and strict server Origin/CSRF remain intact. Login CSP hash updated atomically. Both UI suites and cookie-policy suite pass; build 95,508 B RAM / 1,625,705 B flash, 14.30 s (+16 B flash). No upload; actual Firefox Origin header/login/serial/logout retest and M1 acceptance still pending. See implementation record; supersedes the speculative diagnosis below.

  • 8D.3 target login blocker (2026-09-05): User supplied clean-boot/60-second settled telemetry, then reports both user/admin login rejected with the page's HTTP-403 message. Record in docs/phase8d3_implementation.md: internal free/min/largest 69,004/66,752/31,744 B; DMA 61,248/58,996/31,744 B; PSRAM 8,223,116/8,218,204/8,126,464 B; SSH stack minimum-free 18,464 B; no active sessions/broker clients, UART stopped. Boot counters precede login attempts; no post-attempt result yet. Exact flashed revision not supplied. Production login-renderer/CSP test passes. Browser warnings name other script hashes (possibly injected scripts), plus denied favicon/file URL; do not relax CSP based on these alone. Need failed endpoint/status/error code and nonsecret Origin/Sec-Fetch-Site. Investigation hypothesis: same-origin fetch mode plus no-referrer policy may serialize POST Origin as null; confirm wire headers before changing request policy. M1 blocked, not signed off. No corrective firmware change for this report yet.

  • 8D.3 live cutover implemented / host-tested / build-verified (2026-09-05), M1 target pending: Resumed another agent's uncommitted completed server/browser implementation; preserved it, verified installed IDF header/upgrade semantics, and fixed right-aligned pending-buffer cleanup with an actual-IDF-reader regression. Cookie login/logout is live, Basic/cache removed. Final build 95,508 B RAM / 1,625,689 B flash (+248 / +23,764 B versus 8D.2). See docs/phase8d3_implementation.md. This supersedes older inert/planned statements below. Stop for M1 target/browser sign-off before 8D.4; numeric reserve gates remain open.

  • 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.

  • 8D.2 target samples received: User reports post-flash clean boot and full client mix at 115200 baud, recorded in docs/phase8d2_implementation.md. Loaded free internal/DMA/PSRAM 39,200 / 31,444 / 8,138,284 B, minima 18,784 / 11,028 / 8,109,712 B, SSH stack minimum-free 16,296 B. Four broker clients confirmed, SSH writer; no reported SSH I/O or web failures. One cumulative SSH broker revocation and an initial console usage error retained without diagnosis. Target validation is in progress, not signed off; lifecycle/revocation/soak/cleanup evidence remains pending. No 8D.3 request.

  • 8D.2 implemented / host-tested / build-verified (2026-09-05), target regression pending: User requested 8D.2 only after 8D.0/8D.1 sign-off, with numeric reserve gates still pending. Session-bound serial tickets/slots, session-specific cleanup, and account/global store invalidation are implemented; Basic remains the only public auth route. This supersedes older next-task and dormant-ticket-binding statements below. See docs/phase8d2_implementation.md. Stop before 8D.3.

  • 8D.1 validated by user sign-off (2026-09-05): Following boot/full-client-mix samples, the user reports a long-lasting command with output at full 115200-baud line speed and no dropped broker packets, and explicitly closes 8D.1 validation. This supersedes older in-progress/not-target-verified statements below. Detailed duration/command/byte totals were not supplied; do not invent them or reopen the signed-off phase solely for unrecorded regression details. Numeric reserve gates remain open; cookie authentication remains dormant/planned. Stop before 8D.2 until requested. See docs/phase8d_baseline.md for evidence and sign-off.

  • 8D.1 target validation in progress (2026-09-05): User supplied fresh-boot (UART stopped) and full-client-mix (115200 baud, SSH writer, admin SSH, USB and two web observers) samples, recorded separately in docs/phase8d_baseline.md. Loaded internal/DMA/PSRAM free bytes 41,420 / 33,664 / 8,162,824, minima 19,384 / 11,628 / 8,115,028; SSH stack minimum-free 16,288 B. No reported SSH I/O or web failures; one cumulative SSH broker revocation retained without diagnosis. These supersede older statements below that no target samples exist, but are not full 8D.1 sign-off. Exact changed-firmware hash, lifecycle/HTTPS restart and soak/cleanup coverage remain pending. No continuation to 8D.2 requested.

  • 8D.0 sign-off (2026-09-05): User marked the baseline validated and identified tested source as latest checked-in revision d8999cd4a96e477fabd392ced02d810c3cd22d0f. Current samples and authoritative sign-off are in docs/phase8d_baseline.md; they supersede older baseline measurements/provenance notes below. User attributes SSH I/O errors to out-of-spec 460400-baud testing, distinct from transcript UART configuration 460800. Numeric reserve approval and 8D.1 post-change target validation remain pending; do not reopen 8D.0 solely for unrecorded checklist details or claim those checks were executed.

Based on checked-in source plus README.md and docs/roadmap.md:

  • Hardware characterization, serial service, session broker, USB CDC, Wi-Fi, HTTPS/WebSocket, SSH serial transport, and local display/control are implemented and documented as target-hardware validated.
  • Phase 8A role-based user storage/UART0 administration and Phase 8B role-aware HTTPS/SSH authentication and targeted revocation are documented as target-hardware validated.
  • Phase 8C admin SSH is implemented in source, uses the shared esp_console registry, and has passed target-hardware validation.
  • Phase 8D.08D.3 and M1 are validated by user sign-off; 8D.3 browser login/logout is implemented, host-tested and build-verified, with post-soak evidence and explicit closure recorded. Browser administration remains planned. Follow docs/phase8d_plan.md: one numbered chunk per request, target-validated login/logout (M1) before the browser admin shell (M2), then one typed-settings/control domain at a time (M3). Changing terminal modes must preserve the browser serial broker client and any writer lease. The roadmap retains the full end-state requirements.
  • Security/production hardening, OTA, BLE evaluation, advanced networking, and optional filesystem features remain future roadmap work.
  • Reserved OTA, coredump, NVS-key, and storage partitions do not imply those runtime features are implemented.

Recent memory audit

  • Fixed failed-initialization ownership leaks for wolfSSH, partial HTTPS startup, and TinyUSB teardown. Failed teardown now retains ownership and blocks unsafe duplicate initialization.
  • Serial-service RX/TX stream payloads (16 KiB and 8 KiB effective capacity) now prefer PSRAM with internal fallback; FreeRTOS controls and UART driver buffers remain internal.
  • The 5,360-byte transactional user-database candidate now prefers PSRAM with internal fallback while the live database remains internal. Candidate contents are wiped after each transaction and wiped/freed on initialization or recovery failure.
  • UART and admin-SSH completion formatter buffers were reduced from 2 KiB to 1 KiB each; current worst-case output is 890 bytes and overflow remains fail-closed.
  • Linked RAM fell from 99,508 to 92,188 bytes (7,320 bytes). PSRAM placement of serial payloads additionally removes about 24 KiB of normal internal-heap pressure on the target.
  • pio run passes. A preliminary target run reports significantly more free memory and stable, improved operation after these changes. This is useful evidence but not completion of Phase 8C validation.
  • The reviewed mDNS-enabled build uses 94,532 bytes of linked static RAM, 2,344 bytes above the earlier 92,188-byte baseline, and 1,599,765 bytes of flash. Minimizing the managed component saved 112 bytes of linked RAM and about 5.9 KiB flash versus the first mDNS build. Its 4 KiB task stack remains internal, while checked-in settings move general mDNS allocations to PSRAM and disable unused browse, component CLI, AP/ETH, and multiple-instance features. Runtime heap impact still requires target measurement.
  • Remaining targeted checks include stored/migrated/recovered user-database mutations, USB enumeration, HTTPS start/stop failure recovery where injectable, SSH initialization/login, completion display, and sustained multi-transport serial traffic while checking memory telemetry.

Clearly incomplete or transitional areas

  • Phase 8C hardware validation passed, including route separation, shared command serialization, history/completion, prompts, output backpressure, revocation during queued work, deferred SSH lifecycle/reboot actions, and full concurrent transport operation. At 460800 baud with SSH and WebSocket clients in parallel, substantial packet drops and slow display controls were observed under load, without memory exhaustion; no baud-rate reduction is planned.
  • Current HTTPS has no web-based user administration and gives both roles the same status/terminal routes.
  • Browser authentication now uses cookie login/logout without Basic fallback. M1 target checkpoint is signed off; browser administrative routes remain future separately requested M2 work.
  • NVS encryption, secure boot/flash encryption review, production certificate/provisioning policy, and OTA are not implemented. HTTPS login has a bounded global five-verifications/60-second throttle, not comprehensive cross-transport DoS protection.

Known inconsistencies

These observations should be checked when touching the relevant area; they are not automatically bugs requiring unrelated cleanup.

  • Some source comments still call shared commands UART0-only or call the current local status/control task read-only.
  • USER_DATABASE_LOAD_EMPTY is only an initialization/failure sentinel at the checked-in revision: every successful user_database_init() path returns STORED or MIGRATED_LEGACY, so main.c's successful "new empty" log branch is unreachable.
  • SSH startup is currently gated on successful web_security initialization even though SSH uses separate host-key material. Needs verification: whether this coupling is intentional recovery policy or an accidental startup dependency.

Items to verify in future work

  • Confirm task-local Newlib standard-stream behavior if ESP-IDF/Newlib configuration changes; admin SSH command output relies on dispatcher-task stream redirection.
  • Re-audit the private HTTPD adapter on SDK changes (including same-version patches): parsed-header layout, right-aligned pending data, explicit handshake/frame installation and log suppression. Host tests do not establish real socket behavior.

Completed Task - Phase 8D.3 Live Authentication Cutover

  • Inherited implementation: web_cookie_auth.{c,h}, web_httpd_adapter.{c,h}, parser optional-cookie validation, server/console/transport integration, browser app/session recovery, and focused suites. No Basic authorization/cache remains. Four one-hour sessions, four 120-second pre-login challenges, five password checks per 60-second window, four non-evicting 30-second tickets/two serial sockets. Fourteen URI slots, unchanged six HTTPS sockets and application task/stack/queue capacities. No generated assets or SDK files changed.
  • HTTPD decision: Other agent chose an isolated private-IDF adapter, not the previously proposed SDK patch. Verified first-only header getters, append-only pointer-backed Set-Cookie (six-header successful login), auto-101-before-handler flow and private frame installation against installed 5.5.0. Serial URI uses ordinary GET until authenticated ticket/currentness/broker admission, then explicit handshake. Exact version guard requires re-audit on update; not a source-hash guarantee. HTTPD logs above ERROR compiled out to avoid secrets/ticket queries. Durable boundary recorded in architecture/design decisions and implementation record.
  • Fix in this continuation: Pending HTTPD bytes are right-aligned. Inherited wipe preserved the wrong end, risking pipelined HTTP/early-frame corruption. Fixed consumed-prefix wipe and tested 0128 pending lengths plus partial reads using extracted installed httpd_recv_pending. Kept all other inherited source work intact.
  • Validation: python3 tests/web_cookie_auth/run.py, parser 268-case suite, login UI eight Node groups, serial app nine Node groups, and store --serial integration mode pass. Cookie suite also executes store tests and extracts installed header getter/setter/pending-reader functions; handshake/network/tasks remain doubled. Final pio run passed 17.62 s, 95,508 B RAM / 1,625,689 B flash; +248/+23,764 B versus 8D.2, +976/+25,716 B versus recorded 8D.0. Auth symbols 637 B before placement padding (including 576 B challenges); Basic cache/key removal offsets much of it. No runtime reserve or stack margin inferred.
  • Handoff (updated at sign-off): docs/phase8d3_implementation.md contains policy, source verification, accounting, user-provided login/mixed-client/post-soak evidence and explicit M1 closure on 2026-09-06. No agent device operation or new build was performed to record sign-off. Earlier missing-check notes are evidence limitations/regression coverage, not blockers to this closure. Numeric reserves remain open. Next implementation is 8D.4 only when separately requested; preserve prior sign-offs and the distinction between connected post-soak data and disconnected cleanup.

Previous 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.
  • Validation: Focused production-C host suite, existing store and --serial modes passed. Independent review found no memory-safety defect; default-port maximum-length normalization was fixed and regression cases added. Final rerun/build results recorded in the plan. Host tests are not real HTTPD/browser, sanitizer or target validation.
  • Resources: Build reports 95,260 B linked RAM / 1,601,925 B flash, unchanged from 8D.2. Unused helpers are not live firmware paths; their future linked flash and request-stack cost must be measured at cutover. No new runtime allocation, task, route, queue or socket. Numeric reserve gates remain pending.
  • Next exact work: Continue 8D.3, not 8D.4: wire the complete challenge/session/login/logout/CSRF/Origin policy and browser recovery atomically, or first take the permitted inert login rendering split if the remaining change still exceeds scope. Never expose partly protected cookie routes. Integration must enforce duplicate HTTP header rejection, bounded complete body reads, content type/method/Fetch Metadata checks, and wipe request buffers on every exit. Bootstrap's permitted missing-Origin GET requires separately validated Host handling, not weakening mutation/upgrade Origin checks. Keep account validation tied to existing database capacities. Remove Basic only with the complete security cutover, then stop for mandatory M1 browser/target sign-off.
  • Target checks: No hardware execution for this inert split. On the live cutover run the full M1 checklist in docs/phase8d_plan.md and docs/phase8d_baseline.md: both roles, usable errors/logout/account switching, expiry/reboot/revocation/isolation, direct IP/mDNS, capacity/throttle/CSRF, serial reconnect, UART0/USB/user+admin SSH and repeat-cycle/soak memory evidence. No browser-authentication success is claimed here.

Previous Task - Phase 8D.2 Serial Session Binding

  • Checkpoint/scope: Clean 93eef0e67641f2672c56692a7785e50f31cf236d; only web_serial_transport.{c,h}, web_session_store.{c,h}, one Basic ticket call in web_server.c, focused host tests and documentation changed. No upload/erase/commit or generated assets. Existing user_console and legacy-sync callers already reach the extended revocation hook.
  • Implementation: Non-reused 64-bit originating IDs in four tickets/two slots, zero explicitly Basic-only. Matching-ID upgrade and exact principal binding, expiry/currentness at sensitive boundaries and existing periodic owner checks. Session-specific invalidation/cleanup, account-name/all-session invalidation even if serial init failed, reserved-slot revocation and non-wrapping ticket publication epoch. No new task, heap allocation, route, capacity or stack-size change; Basic cache/currentness retained.
  • Validation: python3 tests/web_session_store/run.py and python3 tests/web_session_store/run.py --serial pass using OpenSSL SHA-256 and deterministic dependency interleavings. Integration exercises production transport private steps with the real store, not real HTTPD sockets/tasks. Expanded checks cover isolation, mismatched identity fields, expiry/lost notifications/DB failure, close-queue retry, slot reuse and mint/admission races. Optional --serial --sanitize cannot link because this host lacks ASan/UBSan runtime libraries and static archives; not a pass.
  • Build/resources: Final source pio run passed in 7.92 seconds at 95,260 B RAM / 1,601,925 B flash, +56 / +1,420 B versus 8D.1 and +728 / +1,952 B versus 8D.0. Target symbol/DWARF: ticket 96 B x 4 (+32 B array), slot 1,664 B x 2 (+16 B array), new epoch 8 B; store remains 664 B. Bound resolver frame 192 B excluding callees; no runtime stack/heap reserve claim.
  • Target/handoff: pio device list sees device CDC and USB serial adapter, but this diff was not flashed or exercised on target. Follow docs/phase8d2_implementation.md for two-role Basic five-cycle regressions, mutation isolation, five HTTPS restarts, USB/user+admin SSH/UART0 smoke, 15-minute 115200-baud full mix and 60-second cleanup/memory evidence. Numeric floors remain pending. Cookie-route session logout/expiry/origin validation remains dormant until separately requested 8D.3; obtain target sign-off or explicit user decision before stacking it. No later phase work performed.

Previous Task - Phase 8D.1 Internal Session Primitives

  • Objective: Phase 8D.1 only. User explicitly requested continuation with remaining reserve/validation gates pending, then supplied a second pre-change full-client-mix sample. No browser auth cutover or continuation to 8D.2.
  • Context: The user reports that the earlier experimental implementation was moved to a separate branch and devel rolled back after repeated agent interruptions, a roughly +10k/-1k-line diff, broken browser login, and severe memory pressure. These symptoms have not been independently diagnosed. Do not import that branch wholesale.
  • Changes completed: Added src/web_session_store.{h,c}, CMake registration and small admitted-HTTPS-start/stop hooks in web_server.c. Four static digest-only sessions, copied principals, separate CSRF/origin state, absolute expiry, generation-safe IDs/epochs, invalidation/prune and count-only snapshot APIs. Database/crypto outside portMUX, post-call revalidation. No issuing HTTP callers, ticket/socket/revocation binding, new routes/tasks, UI change or regenerated assets. Added focused tests/web_session_store/ runner and updated baseline/plan/architecture/code map.
  • Baseline: Clean devel at af89dd1bd96cdd97d8d57eee7a29f68e3874506b, PlatformIO espressif32 6.12.0 / IDF 5.5.0, N16R8 release. pio run passed in 36.08 seconds: 94,532 B linked RAM and 1,599,973 B flash (historical RAM unchanged, flash +208 B, cause not diagnosed). pio device list returned no ports. No agent upload or target/browser test was performed. The user subsequently supplied a runtime heap baseline; stack measurements remain pending.
  • User-provided runtime memory baseline (2026-09-05): Administrative SSH transcript with one browser serial writer (broker 8), one public-key user SSH observer (broker 9), and one public-key admin SSH session; web 1/2, SSH 2/2, zero tickets, no USB broker client listed. Heap bytes in free/minimum-free/largest-block order: internal 8-bit 39,668 / 13,876 / 18,432; internal DMA 31,912 / 6,120 / 18,432; PSRAM 8,138,380 / 8,107,324 / 7,995,392. Accepted as the runtime baseline, not reserve floors or full-workload validation. Flashed revision, baud, duration, traffic and stack margins were not supplied. See docs/phase8d_baseline.md for provenance and remaining checks.
  • Follow-up pre-change full-client-mix sample: Two web sessions (broker 8 writer, 10 observer), USB broker 11 observer, user SSH broker 9 observer and admin SSH; four broker clients, SSH 2/2, web 2/2, no tickets. Internal 8-bit 41,868 / 13,876 / 18,432 B; DMA 34,112 / 6,120 / 18,432 B; PSRAM 8,162,784 / 8,107,324 / 7,995,392 B (free/minimum/largest). Cumulative web/SSH traffic with no reported transport I/O/protocol/queue failures; one web auth failure and two rejected SSH RX bytes retained without diagnosis. This is not timed-soak or byte-integrity evidence; see baseline record.
  • 8D.1 validation/accounting: Started at clean 02fdeee3453654680c11096c9a6224c26233eced. python3 tests/web_session_store/run.py passes with OpenSSL SHA-256, deterministic dependency doubles and injected callback races; no hardware or real multicore scheduling claim. Final pio run passed in 8.90 seconds: 95,204 B RAM (+672 B), 1,600,505 B flash (+532 B). Target object/DWARF: record 152 B × four, state 656 B + lock 8 B = 664 B, no module heap/new task/socket/route cost, Basic cache retained. Only init/stop currently survive firmware linker GC; full primitive flash/stack costs become relevant when future callers are added. Independent review found/fixed store reinit by rejected concurrent start. Post-change target run pending.
  • Planned contract decisions: Four one-hour absolute sessions without idle/sliding renewal, four 120-second pre-login CSRF challenges, four 30-second serial tickets, no live-record eviction and no Basic compatibility after cutover. Strict same-origin mutation/upgrade checks, session-specific logout versus account-wide revocation, bounded global login throttle. These are functional choices, not measured memory budgets or implemented behavior.
  • Next: Stop at 8D.1. Obtain post-change Basic browser/serial/USB/user+admin SSH/UART0 regression and comparable memory/counters/ssh status evidence; five serial disconnect/reconnect cycles per role and HTTPS stop/start checks. Timed soak/cleanup and reserve floors remain pending. Next implementation is 8D.2 session-specific ticket/socket binding, only after checkpoint or explicit user decision; M1/M2 pauses remain. Host tests cover dormant primitives, not browser logout/currentness integration. ID/epoch exhaustion, collision rejection and private memory wiping are source-reviewed only.
  • Risks / things to remember: Six HTTPD client slots include WebSockets; HTTPD infrastructure plus HTTPS and SSH can consume twelve of sixteen descriptors before other services/transients. Existing LRU purge is enabled and must not evict retained serial to admit M2 admin sockets. Nine current route slots have no spare capacity; the proposed M1 contract adds five. Cookie sessions, tickets, sockets and request scratch are separate costs. Only SSH exposes stack margin today. Numeric runtime floors and per-chunk/cumulative budgets remain pending, not guessed.

Previous task — mDNS (target checks still pending)

  • Objective: Announce a configurable sak-<suffix>.local hostname through mDNS when Wi-Fi STA has an IPv4 address, without changing the Wi-Fi NVS blob schema.
  • Relevant files: src/mdns_config.{c,h}, src/mdns_service.{c,h}, src/mdns_console.{c,h}, src/wifi_manager.{c,h}, src/main.c, src/CMakeLists.txt, src/idf_component.yml, dependencies.lock, completion and command documentation.
  • Findings: wifi_manager already serializes all meaningful STA transitions through its permanent task; callbacks only enqueue events. This is the appropriate lifecycle owner for mDNS, while a separate configuration module preserves the existing wifi_app/config wire format.
  • Decision: Persist a fixed v1 record under mdns_cfg/config, separate from Wi-Fi configuration. Defaults derive a safe lower-case hexadecimal suffix from the STA MAC. The manager initializes mDNS at most once after validating IP_EVENT_STA_GOT_IP; the managed component's own handlers withdraw/restore the STA responder across connectivity changes, and online hostname changes use mdns_hostname_set() without teardown. Initialization failure is latched instead of retried because the resolved upstream 1.12.0 component has an unsafe partial low-memory initialization path. mDNS errors cannot fail Wi-Fi, UART0, UART1, or native USB.
  • Changes completed: Added the espressif/mdns managed dependency (resolved to 1.12.0 on IDF 5.5), mDNS config/service/console modules, mdns status|suffix|save|load|defaults|reset, completion, CMake integration, and command/architecture documentation. Minimized the component to STA-only responder use, moved general allocations to PSRAM, retained the internal task stack, and removed reconnect-time free/reinit churn. Final pio run passes at 94,532 bytes linked RAM and 1,599,765 bytes flash.
  • Remaining work: Target-hardware verification: associate a station and resolve the default sak-<mac>.local; change/save/load a suffix and confirm live reannouncement plus reboot persistence; stop Wi-Fi or remove the STA lease and confirm the record withdraws. Verify serial, native USB, and UART0 remain available if mDNS initialization fails.
  • Risks / things to remember: Hostnames are STA-only and are intentionally not announced by fallback AP mode. NVS changes to mdns_cfg/config are independent of the unchanged wifi_app/config blob. mDNS remains allocated after first successful initialization (including its internal 4 KiB task stack) to avoid fragmentation and unsafe repeated initialization; measure free/minimum/largest internal heap and mDNS stack margin during reconnect stress.

Handoff template

  • Objective:
  • Relevant files:
  • Findings:
  • Decisions made:
  • Changes completed:
  • Remaining work:
  • Risks / things to remember: