The user confirmed successful empty-Enter and soak testing, closing 8D.4 while leaving numeric reserve gates open and advancing the next planned work to 8D.5.
68 lines
10 KiB
Markdown
68 lines
10 KiB
Markdown
# Phase 8D.4 - Small Console Boundary
|
|
|
|
Status (2026-09-06): **Implemented / host-tested / build-verified / Phase 8D.4 validated by explicit user sign-off.** The user confirmed successful empty Enter and soak testing after the boot/full-client-mix evidence. M1 and previous phase sign-offs stand. Numeric reserve gates remain open. No 8D.5 implementation or browser admin route is included.
|
|
|
|
## Validation Sign-Off (2026-09-06)
|
|
|
|
The user explicitly requested: "Mark Phase 8D.4 as validated - conforming empty enter and the soak test successful". This closes 8D.4 and the previously pending admin-SSH empty-Enter target retest, superseding older pending/in-progress statements in project records.
|
|
|
|
Soak duration, exact flashed revision, post-soak/cleanup telemetry and individual results for other detailed checklist items were not supplied. These remain evidence limitations, not blockers to this user-approved closure or claims that unreported tests were executed. No new agent build or device operation was performed for sign-off. Numeric reserves remain open. Next is **8D.5 only on a separate implementation request**; M2 as a whole is not yet complete.
|
|
|
|
## Scope And Contract
|
|
|
|
Starting revision: `f9ee6eec9cbe06fe5120ee718507eaffd69787a2`, initially clean worktree. Changed production files are `src/admin_ssh_console.{c,h}` and `src/ssh_transport.c`; focused tests are in `tests/admin_console_boundary/`. No console-input changes were necessary because its prompt routing already uses the shared dispatcher.
|
|
|
|
- `admin_ssh_console_open_owned()` accepts a copied transport-qualified token/principal and an immutable firmware-lifetime owner adapter. Existing SSH admission remains available through `admin_ssh_console_open()`, now implemented beside its owner in `ssh_transport.c`.
|
|
- The adapter supplies nonblocking application-output drain checks and deferred lifecycle handling, called by the existing control task outside console locks. SSH validates slot/session/generation and uses existing transport control APIs, never runtime wolfSSH calls from the control task.
|
|
- Two console slots remain shared, not two per transport. Occupied and still-executing slots reject replacement. Future admission must coordinate this pool; this phase does not promise simultaneous browser and full SSH admin capacity.
|
|
- The token includes transport, slot, session ID and generation. Owners serialize input per session, exclusively consume output, maintain transport authentication/expiry, and close on disconnect/revocation. The core retains admission/dispatch principal checks and queued-work identity checks.
|
|
- Completion scratch has a nonblocking claim so different owners cannot race the shared buffer outside the lock. A competing TAB remains unconsumed for retry; stale completion cannot publish into a reused slot.
|
|
- Shell exit uses an owner-relative `SELF_CLOSE` action; existing STOP/DISCONNECT/key actions still mean SSH. Unsupported actions fail before side effects. Deferred input is rejected at both admission and feed, and identity is checked again after the final delay.
|
|
- The same dispatcher, canonical registry, queue, editor/history/prompts, UART0 policy and output ring remain. Five-second output backpressure and ten-second deferred drain plus 200 ms remain bounded best-effort heuristics, not delivery confirmation.
|
|
|
|
No new tasks, browser endpoints, UI changes, serial/broker changes, generated assets, persistence changes, uploads, erases or commits.
|
|
|
|
## Verification And Resources
|
|
|
|
- `python3 tests/admin_console_boundary/run.py`: PASS for production console and extracted production SSH adapter with deterministic host fakes. Covers readiness/admission, cross-transport and stale identities, completion contention and close/reopen during completion, history, queued disconnect/revocation, UART dispatch, executing cleanup, hidden/visible/cancelled/disconnected prompts, exit-to-SELF_CLOSE routing, unsupported actions, queue rejection, drain timeout/delay, SSH action routing and slow output.
|
|
- `python3 tests/admin_ssh_policy/run.py`: PASS, including quiet empty input and physical-only command restrictions.
|
|
- Independent code review found no defects. `git diff --check`: PASS.
|
|
- `pio run`: PASS in **43.61 seconds**, PlatformIO espressif32 6.12.0 / ESP-IDF 5.5.0, N16R8 release. Linked RAM **95,084 B**, flash **1,627,173 B**. Against the recorded latest 8D.3 build (95,508 / 1,625,725 B): **-424 B RAM / +1,448 B flash**. Against recorded 8D.0 (94,532 / 1,599,973 B): **+552 B RAM / +27,200 B flash**. These comparisons use recorded builds, not a fresh baseline rebuild.
|
|
|
|
Static savings come from removing the full SSH snapshot scratch in the console and reading only the matching published slot under the SSH lock. Added costs are transport tags, owner pointers in sessions/control requests, the immutable adapter, and one completion-claim flag. No payload buffer or capacity increase. Two 4 KiB output rings, four-entry command queue, two-entry control queue, four-line per-session history and task stack sizes (dispatcher 12 KiB, UART frontend 6 KiB, control 4 KiB) are unchanged. No new module heap allocation, socket, TLS connection, HTTP handler, web session or ticket capacity. Runtime heap, PSRAM and stack margins were not measured; no reserve approval is inferred from linked RAM.
|
|
|
|
Host fakes do not establish actual FreeRTOS scheduling, task-local stdio redirection, real queue capacities, socket behavior or hardware regression. The command runner and lifecycle operations are doubled; direct production helpers test routing and output separately. Sanitizer execution was unavailable because the host lacks the required runtime library, not a sanitizer pass.
|
|
|
|
## Target Handoff
|
|
|
|
### User-Provided Boot And Full-Client-Mix Evidence (2026-09-06)
|
|
|
|
The user supplied UART0 transcripts labeled clean boot and full client mix after the 8D.4 implementation. Exact flashed revision, settling interval, traffic duration and byte-integrity comparison were not supplied. These are user target observations, not agent device execution or phase sign-off.
|
|
|
|
| Resource (bytes) | Boot free | Boot minimum | Boot largest | Loaded free | Loaded minimum | Loaded largest |
|
|
|---|---:|---:|---:|---:|---:|---:|
|
|
| Internal 8-bit | 71,512 | 60,344 | 31,744 | 34,632 | 20,648 | 25,600 |
|
|
| Internal DMA | 63,756 | 52,588 | 31,744 | 26,876 | 12,892 | 25,600 |
|
|
| External PSRAM | 8,247,940 | 8,241,988 | 8,126,464 | 8,089,284 | 8,077,516 | 7,995,392 |
|
|
|
|
SSH owner stack: 20,480 B configured; minimum-free **18,472 B boot / 16,280 B loaded**. Heap minimum-free values are conservative sums of matching regions' lifetime minima; internal 8-bit and DMA are overlapping capabilities, not additive pools.
|
|
|
|
- Boot: HTTPS/SSH initialized and running with ESP_OK, zero active SSH/cookie/WebSocket sessions and no broker clients. Startup failure and traffic counters zero. mDNS initialized/announced. UART service stopped with 115200 8N1/no flow configured; USB attached but host closed, DTR/RTS false and no broker client. Diagnostic USB host coding 9600 did not change UART1 configuration.
|
|
- Loaded: UART service running at **115200 8N1/no flow** with no pending RX/TX in the sample. Four broker clients: SSH **8 sole writer**, USB **9 observer**, web **10/11 observers**. Two public-key SSH sessions active, one user/broker route and one admin/console route. Admin admission successful, idle, zero output and no RX/TX pending. USB host open with DTR/RTS true.
|
|
- Both browser roles logged in successfully: two cookie sessions and two serial WebSockets, no outstanding tickets/challenges. Two login attempts, zero invalid credentials, throttle/capacity/CSRF/Origin rejections or logouts. HTTPS/SSH/mDNS still running/announced with ESP_OK.
|
|
- SSH: two successful handshakes, zero handshake/auth/admission/I/O failures and zero admin input backpressure. Stream RX/accepted **13/13 B**, rejected **0**, TX **8,235 B**. Cumulative broker revocations **1** is retained without diagnosis; the current snapshot still shows SSH as sole writer and no session revocations.
|
|
- Web: two tickets issued/consumed and two successful WebSocket connects; zero response, service-start, broker, send, queue, protocol or close failures. TX **105 binary frames / 15,760 B**, **5 control frames / 398 B**; no serial RX frames. Both writer requests denied while SSH owns the lease, consistent with observer admission rather than a transport failure.
|
|
|
|
Compared with the earlier 8D.3 mixed-load sample (not its different post-soak sample), current free internal/DMA memory is **764 B higher** and PSRAM **224 B higher**. Internal/DMA minima are **6,892 B higher** and PSRAM minimum **4,772 B higher**; largest blocks are unchanged. SSH stack minimum-free is **8 B lower**. Different transient histories prevent attributing these runtime deltas solely to the refactor or using them as reserve/leak proof.
|
|
|
|
These snapshots alone support startup, both-role login and concurrent transport admission/data activity, not detailed console regressions or soak/cleanup. The subsequent explicit sign-off above confirms successful empty Enter and soak testing and closes 8D.4; unreported checklist details remain evidence limitations.
|
|
|
|
The original target checklist below is retained for regression coverage, not as an outstanding gate to the signed-off phase:
|
|
|
|
1. Boot and capture UART0 `memory`, status and `ssh status`. Verify UART0 recovery and empty Enter/normal commands through admin SSH (empty Enter is now confirmed by user sign-off).
|
|
2. Exercise UART0/admin-SSH serialization, completion/history, visible/hidden/cancelled prompts, and disconnect/revocation while work is queued or a prompt is active. Check reconnect/slot reuse does not receive old output.
|
|
3. Exercise slow-reader backpressure and recovery, `exit`, empty-line Ctrl+D, and the existing deferred SSH stop/disconnect/key/reboot paths as appropriate. Confirm acknowledgement remains best effort and stale sessions cannot act on reused identities.
|
|
4. Run the always-on browser login/serial explicit disconnect/reconnect, USB UART1 and user/admin SSH smoke. Repeat five serial lifecycle cycles per role and collect comparable settled/full-client-mix/cleanup `memory` and SSH stack telemetry at the supported 115200-baud workload.
|
|
|
|
No target checks above were executed by the agent. Exact next chunk is **8D.5: bounded admin WebSocket backend**, only when separately requested after the applicable validation decision.
|