# 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.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.1 internal session primitives are implemented, host-tested and build-verified, not target-verified; browser login/logout and integrated web administration remain planned. Follow `docs/phase8d_plan.md`: one numbered chunk per request, with baseline/resource budgeting first, 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 still uses HTTP Basic; Phase 8D plans integrated login/logout sessions before exposing administrative browser routes. - NVS encryption, secure boot/flash encryption review, authentication rate limiting, production certificate/provisioning policy, and OTA are not implemented. ## 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. - If HTTPD concurrency configuration changes, add locking around the boot-local Basic-authentication cache. ## Active 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-.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-.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:**