Restructure project documentation
Separate operator guides from development contracts, relocate implementation notes under `docs/devel/`, and update cross-references and hardware tooling paths. Add getting-started, user administration, and firmware update guides while preserving firmware behavior.
This commit is contained in:
@@ -41,4 +41,4 @@ pio device monitor -b 115200
|
|||||||
|
|
||||||
The first migration from the former default/factory layout to the custom partition table requires `pio run --target erase`. Erasing destroys persisted configuration and credentials; never run it without explicit user approval.
|
The first migration from the former default/factory layout to the custom partition table requires `pio run --target erase`. Erasing destroys persisted configuration and credentials; never run it without explicit user approval.
|
||||||
|
|
||||||
No automated host test command is defined in the repository. Hardware validation procedures live in `docs/electrical_tests.md` and `docs/user_administration_tests.md`; do not claim they passed unless actually performed.
|
No automated host test command is defined in the repository. Hardware validation procedures live in `docs/electrical_tests.md` and `docs/devel/user_administration_tests.md`; do not claim they passed unless actually performed.
|
||||||
|
|||||||
@@ -4,105 +4,66 @@
|
|||||||
|
|
||||||
ESP32-S3 firmware for a secure, multi-transport RS-232 adapter. It operates one MAX3243-backed UART1 serial port and safely shares it between native USB CDC-ACM, an HTTPS/WebSocket browser terminal, and SSH: one connected client can write while others observe. The firmware also provides persistent serial and Wi-Fi configuration, a UART0 recovery console, and hardware diagnostics; it is not a general-purpose router, captive portal, or unauthenticated TCP serial server.
|
ESP32-S3 firmware for a secure, multi-transport RS-232 adapter. It operates one MAX3243-backed UART1 serial port and safely shares it between native USB CDC-ACM, an HTTPS/WebSocket browser terminal, and SSH: one connected client can write while others observe. The firmware also provides persistent serial and Wi-Fi configuration, a UART0 recovery console, and hardware diagnostics; it is not a general-purpose router, captive portal, or unauthenticated TCP serial server.
|
||||||
|
|
||||||
## Initial hardware target
|
## Supported hardware
|
||||||
|
|
||||||
- ESP32-S3-DevKitC-1-compatible development board
|
- ESP32-S3-DevKitC-1-compatible development board
|
||||||
- ESP32-S3-WROOM-1-N16R8 module
|
- ESP32-S3-WROOM-1-N16R8 module
|
||||||
- 16 MB flash
|
- 16 MB flash
|
||||||
- 8 MB octal PSRAM
|
- 8 MB octal PSRAM
|
||||||
- Adafruit MAX3243 full-pinout RS-232 breakout; the male connector version is preferred (see [Hardware wiring](docs/wiring.md) for supported connector variants)
|
- Adafruit MAX3243 full-pinout RS-232 breakout; the male connector version is preferred (see [Hardware wiring](docs/wiring.md) for supported connector variants)
|
||||||
|
- Optional 128×64 I²C OLED and three buttons, as described in the wiring guide
|
||||||
## Development status
|
|
||||||
|
|
||||||
Hardware characterization, serial/USB/Wi-Fi/HTTPS/SSH and local display/control are implemented and hardware-validated. **Phase 8 role-based users and administration is complete:** 8A–8C were target-hardware validated and the user explicitly signed off tested firmware at **8D.22 (2026-09-13)**. See the [roadmap](docs/roadmap.md#phase-8--role-based-users-and-administrative-access--complete) and its [consolidated acceptance evidence](docs/roadmap.md#phase8-acceptance-evidence). Very low internal/DMA lifetime minima remain a nonblocking headroom follow-up, not an approved reserve. The reduced Phase 9 focuses on intermittent trusted-network use, standard no-dump/silent-panic defaults and a short [operational checklist](docs/security_operations.md), without dependency patches or upgrades. **The reduced Phase 9 is complete by user sign-off on 2026-09-18.** The user waived a new device check based on prior Phase 8 validation; application code is unchanged, but crash-diagnostic defaults changed. No new hardware or panic-test pass is claimed.
|
|
||||||
|
|
||||||
**Phase 10 application firmware upload is complete by explicit user acceptance on 2026-09-18:** upload works and normal operation is verified. This does not claim specific fault-injection, NVS before/after comparison, power-loss or wired-recovery checks. Administrators can upload a locally built application from **Settings → HTTPS / Reboot → Firmware update**. Install this OTA-enabled firmware by wire first; future uploads use `.pio/build/esp32-s3-devkitc-1-n16r8/firmware.bin`. See [firmware update and recovery](docs/roadmap.md#phase10-update-and-recovery) for compatibility, interruption and validation limits.
|
|
||||||
|
|
||||||
### Browser administration
|
|
||||||
|
|
||||||
Cookie login/logout supports both roles. Administrators also have an explicitly opened Admin shell sharing the canonical dispatcher, and typed **Serial, Accounts/password/SSH authorized keys, Network, Display, Broker, SSH, and HTTPS/Reboot** settings. Serial/Wi-Fi/client/writer quick controls reuse the same settings controllers. Navigation preserves terminal sessions and serial writer ownership; explicit transfer is confirmed and generation-safe. Browser-shell permissions remain narrower than typed Settings or UART0/admin SSH.
|
|
||||||
|
|
||||||
Network uses UTF-8 text/reversible hex SSIDs, explicit password Keep/Replace/disabled-STA Clear (never AP clear), RAM edits and explicit Save. Saved secrets are never prefilled/exported. Next profile follows enabled priority order, not the editor's selected index. Network `accepted` means owner admission, not online/DNS completion; disruption may precede acknowledgement.
|
|
||||||
|
|
||||||
HTTPS and SSH identity settings display public fingerprints and confirm both service and identity generations before rotation. HTTPS rotation commits before stop/restart and invalidates web logins; SSH stops before commit/restart and can disconnect clients even when persistence fails. A committed identity is never rolled back on restart failure. A lost response or timeout is not cancellation: inspect state rather than automatically replaying. Verify changed trust on UART0 with `web certificate info` or `ssh host-key info` before renewing browser trust/known_hosts; HTTPS restart requires fresh login. Browser identity reset/recovery/export is excluded; canonical CLI recovery remains available.
|
|
||||||
|
|
||||||
Keep UART0 ready for administrative recovery and native USB for network-independent UART1 access. USB is not an admin console, and whole-device reboot interrupts every transport. [Web administration contracts](docs/web_administration.md) cover API bounds, ownership, permissions, uncertainty and recovery. Dedicated typed network diagnostics and the unimplemented browser-session/USB control expansion were removed from scope; existing shell diagnostics and SSH settings remain.
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- [Hardware wiring](docs/wiring.md): hardware profile, GPIO assignments, connector guidance, and safety notes.
|
Start with [Getting started](docs/getting_started.md) for installation, first-time configuration, and USB or network access.
|
||||||
- [Electrical tests](docs/electrical_tests.md): OLED/buttons, MAX3243, UART loopback, and session-broker verification procedures.
|
|
||||||
- [Role-based user database and UART0 administration](docs/user_administration_tests.md): user provisioning and administration, HTTPS/SSH authentication, session revocation, and reusable integrated web-administration regression procedures (not execution evidence).
|
|
||||||
- [Web administration](docs/web_administration.md): current bounded API/owner and recovery contracts; [roadmap acceptance evidence](docs/roadmap.md#phase8-acceptance-evidence) records sign-off and telemetry limits.
|
|
||||||
- [Firmware update](docs/roadmap.md#phase-10--simple-admin-web-firmware-upload): admin-only application upload, NVS preservation, uncertain outcomes, wired recovery, acceptance evidence and reusable regression checks.
|
|
||||||
- [Security operations](docs/security_operations.md): trusted-network use, credentials, identity verification, shutdown and recovery; physical-extraction limits and lightweight upstream maintenance.
|
|
||||||
- [Command reference](docs/command_reference.md): UART0/admin-SSH administration, serial, broker, USB, Wi-Fi, mDNS, web, SSH, and diagnostic commands.
|
|
||||||
- [Dual-stack networking and discovery](docs/roadmap.md#phase-12--advanced-network-integration): DHCPv4 hostname publication, STA SLAAC/link-local IPv6, HTTPS/SSH DNS-SD and dual-stack access; complete by explicit user validation, including fresh boot and the full client mix at 230400 baud. Includes recorded heap/stack measurements, evidence limits, saved-config requirements and the narrowly guarded mDNS membership fix.
|
|
||||||
|
|
||||||
## Flash partition layout
|
- [Hardware wiring](docs/wiring.md): GPIO assignments, power safety, cables, USB connectors, OLED, and buttons.
|
||||||
|
- [Electrical tests](docs/electrical_tests.md): safe hardware bring-up, loopback, flow control, and shared-serial checks.
|
||||||
The N16R8 target has 16 MiB flash and 8 MiB octal PSRAM. PlatformIO uses the custom [`partitions.csv`](partitions.csv) layout:
|
- [Command reference](docs/command_reference.md): administration, serial, USB, Wi-Fi, discovery, SSH, and diagnostics.
|
||||||
|
- [Web administration](docs/web_administration.md): browser Serial, Admin, Settings, writer control, and reconnecting after changes.
|
||||||
| Partition | Offset | Size | Purpose |
|
- [User administration](docs/user_administration.md): accounts, passwords, SSH client keys, and local recovery.
|
||||||
|---|---:|---:|---|
|
- [Firmware update](docs/firmware_update.md): application upload through the browser, update precautions, and wired recovery.
|
||||||
| `nvs` | `0x009000` | 512 KiB | Serial, Wi-Fi, mDNS hostname, local-display, role-based user, HTTPS identity, and SSH host-key data |
|
- [Security operations](docs/security_operations.md): trusted-network use, identity verification, credentials, shutdown, and recovery.
|
||||||
| `otadata` | `0x089000` | 8 KiB | Active OTA-slot selection metadata |
|
|
||||||
| `phy_init` | `0x08B000` | 4 KiB | Optional PHY initialization data |
|
|
||||||
| `nvs_key` | `0x08C000` | 4 KiB | Reserved for future encrypted-NVS keys |
|
|
||||||
| `coredump` | `0x08D000` | 128 KiB | Reserved for flash core dumps |
|
|
||||||
| `ota_0` | `0x0B0000` | 4 MiB | Primary application/OTA slot |
|
|
||||||
| `ota_1` | `0x4B0000` | 4 MiB | Alternate application/OTA slot |
|
|
||||||
| `storage` | `0x8B0000` | 7488 KiB | Reserved for future LittleFS web assets, logs, and files |
|
|
||||||
|
|
||||||
Application offsets are aligned to the ESP32-S3's required 64 KiB boundary. The final storage partition ends at `0x1000000`, exactly the end of the 16 MiB flash chip.
|
|
||||||
|
|
||||||
Admin HTTPS application upload uses the inactive OTA slot and `otadata`; NVS and other data partitions are untouched by the updater. It does not provide remote downloads, automatic rollback/health confirmation, core-dump handling, NVS encryption or filesystem mounting. Image validation does not guarantee a working application; compatible storage schemas and wired recovery remain the owner's responsibility.
|
|
||||||
|
|
||||||
### One-time migration from the default partition table
|
|
||||||
|
|
||||||
The previous 1 MiB factory application began at `0x10000`, which now lies inside the enlarged NVS range. A normal upload does not erase stale data in that range. When first switching to this layout, erase the flash completely:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pio run --target erase
|
|
||||||
pio run --target upload
|
|
||||||
pio device monitor -b 115200
|
|
||||||
```
|
|
||||||
|
|
||||||
This removes saved serial configuration and all other flash contents. The firmware recreates NVS with safe defaults. **Do not erase for subsequent ordinary wired or web application updates.** This destructive migration is only for the former default/factory layout, not for installing the updater on an existing custom-layout device.
|
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
|
Install PlatformIO Core and run from the project root:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pio run
|
pio run
|
||||||
```
|
```
|
||||||
|
|
||||||
The small Phase 9 sets `CONFIG_ESP_COREDUMP_ENABLE_TO_NONE=y` and `CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT=y` in `sdkconfig.defaults`. An existing generated `sdkconfig.esp32-s3-devkitc-1-n16r8` can retain earlier selections: check its resolved values after configuration, or change them using `pio run --target menuconfig`. Defaults alone do not enforce existing configurations. Silent panic reboot removes panic register/backtrace output; ordinary logs remain. No existing flash contents are erased, and stored secrets remain unencrypted.
|
The [getting-started guide](docs/getting_started.md#2-install-firmware-by-wire) covers prerequisites and first installation. The configured target is `esp32-s3-devkitc-1-n16r8`.
|
||||||
|
|
||||||
## Upload and monitor
|
## Upload and monitor
|
||||||
|
|
||||||
Connect the board's **USB-to-UART** port for firmware upload and the UART0 development console, then run:
|
Connect the board's **USB-to-UART** port for firmware upload and the UART0 recovery console:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pio run --target upload
|
pio run --target upload
|
||||||
pio device monitor -b 115200
|
pio device monitor -b 115200
|
||||||
```
|
```
|
||||||
|
|
||||||
The firmware provides an interactive UART0 console at `serial-tool>`. Run `help` for available commands. The USB-to-UART bridge normally appears as `/dev/ttyUSB*`; it is separate from the native USB CDC serial transport, which normally appears as `/dev/ttyACM*`.
|
If necessary, add `--upload-port` with your actual bridge device path. The bridge normally appears as `/dev/ttyUSB*`; it is separate from native USB CDC (`/dev/ttyACM*`), which carries UART1 data. At `serial-tool>`, press Enter and run `help`.
|
||||||
|
|
||||||
The console supports session history, line editing, cursor movement, and hierarchical Tab completion. After an unattended boot, attach an ANSI-capable terminal and press Enter once to enable enhanced editing; this avoids blocking while no terminal is attached.
|
**Do not erase for routine updates.** Erasing destroys all persisted configuration and credentials. Migration from the former default/factory layout to the custom OTA layout is a separate destructive step; see [partition migration](docs/getting_started.md#partition-migration-and-erasing). Subsequent application-only updates can use the [web updater](docs/firmware_update.md).
|
||||||
|
|
||||||
Serial, Wi-Fi, and mDNS hostname edits remain in RAM until explicitly saved with `serial save`, `wifi save`, or `mdns save`. Authenticated admin SSH sessions expose the shared operational administration registry, including interactive secrets, TLS/SSH identity management, network diagnostics, and deferred reboot/SSH lifecycle commands. Create the first administrator on UART0 with `user add <username> admin` (optionally `--generate`). Explicit recovery of an unavailable user database remains UART0-only and rebuilds it empty; it refuses a healthy database. An administrator also cannot generate a replacement password for its own account over SSH, preventing the one-time value from being lost when that mutation revokes the session. Legacy web credential commands and `user bootstrap` are removed.
|
## Before using network access
|
||||||
|
|
||||||
## Security notes
|
Create the first administrator on trusted UART0 with `user add <username> admin`; there is no default shared login. Use a separate `user` account for SSH serial access: SSH `admin` opens device administration instead. Verify HTTPS and SSH fingerprints locally before trusting a new or changed identity.
|
||||||
|
|
||||||
The HTTPS interface uses a device-specific self-signed certificate and a same-origin login page with bounded server-side cookie sessions; HTTP Basic is no longer accepted. Open `/` or `/login`, sign in with a user-database password, and use **Sign out** before switching accounts. Four sessions have a one-hour absolute lifetime, including active serial connections; logout closes only that session's serial access. Login is globally limited to five credential verifications per 60 seconds, with explicit capacity/backoff errors. Direct-IP and mDNS access use separate host-only Secure/HttpOnly/SameSite=Strict cookies. Non-browser clients also require cookies, strict Origin and CSRF for mutations rather than Basic credentials. There is no plaintext HTTP or TCP serial listener. SSH accepts role-based passwords and authorized Ed25519/ECDSA P-256 public keys. User passwords are stored as salted PBKDF2-HMAC-SHA256 verifiers, but the HTTPS private key, SSH private key, and Wi-Fi credentials remain recoverable from unencrypted application-owned NVS blobs. Offline password guessing and stale append-oriented flash copies also remain possible. The reserved `nvs_key` partition does not enable encryption. This firmware is not resistant to physical flash or RAM extraction; the completed reduced Phase 9 deliberately does not add that protection.
|
Serial, Wi-Fi, mDNS hostname, and display edits need their explicit Save commands to survive reboot. Account and device-identity changes persist immediately. USB host baud/framing choices do not change physical UART1 settings.
|
||||||
|
|
||||||
|
Use a trusted LAN or the password-protected device AP, not Internet exposure. HTTPS uses a device-specific self-signed certificate and password login; SSH accepts passwords and supported public keys. Browser logins expire after one hour, even during active use. **Disconnect** is not sign-out: use **Sign out** when finished.
|
||||||
|
|
||||||
|
The firmware is **not resistant to physical flash or RAM extraction**. Passwords are stored as salted PBKDF2-HMAC-SHA256 verifiers, but Wi-Fi secrets and private device keys remain recoverable from unencrypted NVS; offline password guessing and old flash copies also remain possible. Keep local ports physically controlled. Read [Security operations](docs/security_operations.md) before deployment.
|
||||||
|
|
||||||
|
## Development documentation
|
||||||
|
|
||||||
|
[Developer index](docs/devel/README.md) · [Roadmap and acceptance history](docs/devel/roadmap.md) · [Current development state](docs/agent/current-state.md)
|
||||||
|
|
||||||
|
Implementation contracts, regression procedures, PCB research, and the flash layout live under `docs/devel/`. Agent working memory lives under `docs/agent/`. Historical acceptance records are not claims that every fault or hardware test has passed; the carrier PCB remains a draft, not fabrication-ready.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the [GNU General Public License version 3 only](LICENSE) (`GPL-3.0-only`). Third-party components remain subject to their respective licenses. The integration baseline uses Espressif registry components `espressif/mdns` `1.12.0`, `wolfssl/wolfssl` `5.8.2~1`, and `wolfssl/wolfssh` `1.4.20`; review upstream security releases before production use.
|
This project is licensed under the [GNU General Public License version 3 only](LICENSE) (`GPL-3.0-only`). Third-party components remain subject to their respective licenses. The integration baseline uses Espressif registry components `espressif/mdns` `1.12.0`, `wolfssl/wolfssl` `5.8.2~1`, and `wolfssl/wolfssh` `1.4.20`; review upstream security releases before production use.
|
||||||
|
|
||||||
### Legacy credential removal
|
|
||||||
|
|
||||||
Missing user storage is persisted as an empty database; no shared credential is imported or synchronized. Existing valid v1 user records retain their accounts, roles, IDs, verifiers and keys without a schema change. HTTPS `web_sec/material` upgrades valid 1,392-byte v1 storage to 1,340-byte TLS-only v2, retaining exact certificate/key DER, fingerprint and generation, and committing before publication. Invalid records or migration failures fail closed rather than triggering fallback replacement. `web certificate rotate --force` remains available; `web reset --force` replaces TLS identity only, not users.
|
|
||||||
|
|
||||||
**Downgrade warning:** older v1-only firmware cannot read v2 HTTPS material. Logical NVS replacement is not a secure flash wipe; historical plaintext credentials can remain in flash. This cleanup requires no factory/partition erase. See [consolidated compatibility and evidence limits](docs/roadmap.md#phase8-legacy-credential-compatibility); Phase 8 acceptance is recorded there.
|
|
||||||
|
|||||||
+16
-16
@@ -25,7 +25,7 @@ SSH role=admin ------> shared administration dispatcher <------ UART0
|
|||||||
|
|
||||||
## Typed Network settings
|
## Typed Network settings
|
||||||
|
|
||||||
`web_network_settings` admits bounded current-admin operations to the existing ID-only dispatcher; `wifi_manager` remains radio/event/reannouncement owner and `mdns_service` owns independent hostname persistence. Zero-wait secret-free projections never copy saved PSKs onto HTTPD. Conditional compare/merge preserves omitted secrets, queue admission precedes Wi-Fi publication, and edits require explicit Save. Wi-Fi Load is stored-only; mDNS may load deterministic defaults. SSIDs remain byte-reversible; AP clear is denied. One login-bound slot and one-second timer bound queued secret retention to 30 seconds plus scheduling latency, not cancellation after owner admission. `accepted` is not online/DNS completion. See [Network contracts](../web_administration.md#network).
|
`web_network_settings` admits bounded current-admin operations to the existing ID-only dispatcher; `wifi_manager` remains radio/event/reannouncement owner and `mdns_service` owns independent hostname persistence. Zero-wait secret-free projections never copy saved PSKs onto HTTPD. Conditional compare/merge preserves omitted secrets, queue admission precedes Wi-Fi publication, and edits require explicit Save. Wi-Fi Load is stored-only; mDNS may load deterministic defaults. SSIDs remain byte-reversible; AP clear is denied. One login-bound slot and one-second timer bound queued secret retention to 30 seconds plus scheduling latency, not cancellation after owner admission. `accepted` is not online/DNS completion. See [Network contracts](../devel/web_administration.md#network).
|
||||||
|
|
||||||
## Startup and initialization
|
## Startup and initialization
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ The broker drains serial input even with no clients. A full client output stream
|
|||||||
|
|
||||||
Binary serial WebSocket output uses the IDF-5.5.0-pinned `web_httpd_ws_send_binary` adapter: one owner-only session-override send of a bounded header+payload copy (516 bytes of local scratch, 512-byte payload). Existing generation validation and one outstanding work item per slot remain required. Non-full sends immediately replace that session's send override with a reject-only guard, mark it closing and shut down the socket before deferred cleanup; this prevents automatic SDK control replies from reentering TLS after incomplete output. HTTPD retains TLS destruction ownership. Text/control and admin output retain the SDK sender. One send call does not imply one TLS record, packet or peer receipt; the user signed off drop-free230400-baud full-client-mix operation at160MHz. Runtime stack-margin validation remains a separate follow-up; evidence and acceptance scope are in `current-state.md`.
|
Binary serial WebSocket output uses the IDF-5.5.0-pinned `web_httpd_ws_send_binary` adapter: one owner-only session-override send of a bounded header+payload copy (516 bytes of local scratch, 512-byte payload). Existing generation validation and one outstanding work item per slot remain required. Non-full sends immediately replace that session's send override with a reject-only guard, mark it closing and shut down the socket before deferred cleanup; this prevents automatic SDK control replies from reentering TLS after incomplete output. HTTPD retains TLS destruction ownership. Text/control and admin output retain the SDK sender. One send call does not imply one TLS record, packet or peer receipt; the user signed off drop-free230400-baud full-client-mix operation at160MHz. Runtime stack-margin validation remains a separate follow-up; evidence and acceptance scope are in `current-state.md`.
|
||||||
|
|
||||||
Active-client counter snapshots expose ID/type/pending/HWM/UART/queued/read/dropped. HWM is maintained independently of web tracing and counter clear seeds current occupancy. Read means transport handoff, not peer receipt. Disconnect removes the client row while global totals retain traffic and unread-output discards. Independent default-disabled web performance capture uses two fixed slot records and nonwrapping epoch/generation fences; toggles fence in-flight samples, disable freezes aggregates, clear preserves enable state. Binary-only timestamps bound reservation-entry to callback-entry before locking, synchronous send calls and completion to broker-read return; idle and intervening work prevent scheduler-only/backlog-at-completion claims. No new instrumentation allocations or scheduling/buffer changes. Exact fields, overhead and capture contracts: [throughput diagnostics](../web_throughput_diagnostics.md).
|
Active-client counter snapshots expose ID/type/pending/HWM/UART/queued/read/dropped. HWM is maintained independently of web tracing and counter clear seeds current occupancy. Read means transport handoff, not peer receipt. Disconnect removes the client row while global totals retain traffic and unread-output discards. Independent default-disabled web performance capture uses two fixed slot records and nonwrapping epoch/generation fences; toggles fence in-flight samples, disable freezes aggregates, clear preserves enable state. Binary-only timestamps bound reservation-entry to callback-entry before locking, synchronous send calls and completion to broker-read return; idle and intervening work prevent scheduler-only/backlog-at-completion claims. No new instrumentation allocations or scheduling/buffer changes. Exact fields, overhead and capture contracts: [throughput diagnostics](../devel/web_throughput_diagnostics.md).
|
||||||
|
|
||||||
### Clients to RS-232
|
### Clients to RS-232
|
||||||
|
|
||||||
@@ -106,31 +106,31 @@ TinyUSB callbacks enqueue/copy data and state; the transport task owns broker li
|
|||||||
|
|
||||||
`web_server` owns HTTPS on port 443 with a persisted self-signed P-256 identity and IDF's dual-stack listener. Shared Host/Origin parsing accepts canonical bracketed IPv6 literals (no interface zones) while preserving same-origin/session binding; DNS discovery does not confer certificate trust. `web_serial_transport` mediates two fixed WebSocket slots through the broker; HTTPD owns socket sends/close, the transport task owns broker IO. Four outstanding serial tickets, four cookie sessions, one optional admin WebSocket and six total HTTPD sockets are distinct limits; LRU is disabled. Current handler capacity is 40. Base HTTPS can serve authenticated non-WebSocket routes if optional serial/admin transport initialization fails.
|
`web_server` owns HTTPS on port 443 with a persisted self-signed P-256 identity and IDF's dual-stack listener. Shared Host/Origin parsing accepts canonical bracketed IPv6 literals (no interface zones) while preserving same-origin/session binding; DNS discovery does not confer certificate trust. `web_serial_transport` mediates two fixed WebSocket slots through the broker; HTTPD owns socket sends/close, the transport task owns broker IO. Four outstanding serial tickets, four cookie sessions, one optional admin WebSocket and six total HTTPD sockets are distinct limits; LRU is disabled. Current handler capacity is 40. Base HTTPS can serve authenticated non-WebSocket routes if optional serial/admin transport initialization fails.
|
||||||
|
|
||||||
Cookie login/logout replaces Basic/cache. Digest-only records carry copied principals, CSRF state, absolute expiry and nonreused originating-session IDs. Strict same-origin/CSRF mutations and session/principal checks gate admission; logout invalidates its session before transport cleanup, account mutations invalidate only the affected account, and ongoing currentness is authoritative. Authentication initialization failure gates HTTPS; failed start/accepted stop wipes records. RNG/SHA/database calls run outside short spinlocks with post-call epoch/identity revalidation. [Authentication contract](../web_administration.md#authentication-and-admission).
|
Cookie login/logout replaces Basic/cache. Digest-only records carry copied principals, CSRF state, absolute expiry and nonreused originating-session IDs. Strict same-origin/CSRF mutations and session/principal checks gate admission; logout invalidates its session before transport cleanup, account mutations invalidate only the affected account, and ongoing currentness is authoritative. Authentication initialization failure gates HTTPS; failed start/accepted stop wipes records. RNG/SHA/database calls run outside short spinlocks with post-call epoch/identity revalidation. [Authentication contract](../devel/web_administration.md#authentication-and-admission).
|
||||||
|
|
||||||
`web_httpd_adapter` is the sole private IDF 5.5.0 boundary for duplicate headers, admission-before-101, consumed-scratch wiping, staged optional URI registration, combined binary sends and owner-only idle sweeps. Re-audit its version guard on SDK upgrades. HTTPD debug logging must not expose headers/tickets. `web_diagnostics` independently observes public post-TLS callbacks using six metadata records and a default-disabled 32-event ring; it cannot see preaccept/in-progress/failed TLS. [Admission diagnostics](../web_admission_diagnostics.md).
|
`web_httpd_adapter` is the sole private IDF 5.5.0 boundary for duplicate headers, admission-before-101, consumed-scratch wiping, staged optional URI registration, combined binary sends and owner-only idle sweeps. Re-audit its version guard on SDK upgrades. HTTPD debug logging must not expose headers/tickets. `web_diagnostics` independently observes public post-TLS callbacks using six metadata records and a default-disabled 32-event ring; it cannot see preaccept/in-progress/failed TLS. [Admission diagnostics](../devel/web_admission_diagnostics.md).
|
||||||
|
|
||||||
`web_httpd_idle` uses one one-second timer, six rows and at most one queued owner probe. Current-owner shutdown follows 15 seconds of observed ordinary idle, exempting actual WebSockets/async/pending input. Owner delays prevent hard timeout guarantees; accepted-but-lost work stays reserved until successful destruction, failed stop retains ownership. [Idle lifecycle contract](../https_idle_cleanup.md).
|
`web_httpd_idle` uses one one-second timer, six rows and at most one queued owner probe. Current-owner shutdown follows 15 seconds of observed ordinary idle, exempting actual WebSockets/async/pending input. Owner delays prevent hard timeout guarantees; accepted-but-lost work stays reserved until successful destruction, failed stop retains ownership. [Idle lifecycle contract](../devel/https_idle_cleanup.md).
|
||||||
|
|
||||||
`web_ui`/`web_login_ui` own authored documents/scripts and hash-bound CSP loaders; authentication documents/app are no-store. Checked-in generated xterm/logo assets are compiled, not regenerated by ordinary builds. Browser Serial/Admin/Settings navigation changes view/input only, preserving serial client/lease and hidden output draining. Session-identity changes require a clean document; pagehide/restore revalidates before exposing buffers. One shared quick-settings host/controller preserves drafts, stale selections and pending uncertainty. [Terminal and console contract](../web_administration.md#terminal-and-console-ownership).
|
`web_ui`/`web_login_ui` own authored documents/scripts and hash-bound CSP loaders; authentication documents/app are no-store. Checked-in generated xterm/logo assets are compiled, not regenerated by ordinary builds. Browser Serial/Admin/Settings navigation changes view/input only, preserving serial client/lease and hidden output draining. Session-identity changes require a clean document; pagehide/restore revalidates before exposing buffers. One shared quick-settings host/controller preserves drafts, stale selections and pending uncertainty. [Terminal and console contract](../devel/web_administration.md#terminal-and-console-ownership).
|
||||||
|
|
||||||
### Browser administration and HTTPS lifecycle
|
### Browser administration and HTTPS lifecycle
|
||||||
|
|
||||||
`web_admin_transport`/`web_admin_tickets` add one optional admin socket, two session-bound tickets and admission to the same two remote-console slots shared with SSH, never a broker client. HTTPD owns a 1,552-byte PSRAM-only payload and IO; a 20 ms timer queues at most one poll. Current-owner shutdown avoids queued reusable socket pointers. Detach fences submissions and only successful HTTPD stop retires old work. Console/owner checks enforce currentness before sensitive work; unsupported parsed shell commands reject before effects. [Browser-shell policy](../web_administration.md#browser-shell-policy).
|
`web_admin_transport`/`web_admin_tickets` add one optional admin socket, two session-bound tickets and admission to the same two remote-console slots shared with SSH, never a broker client. HTTPD owns a 1,552-byte PSRAM-only payload and IO; a 20 ms timer queues at most one poll. Current-owner shutdown avoids queued reusable socket pointers. Detach fences submissions and only successful HTTPD stop retires old work. Console/owner checks enforce currentness before sensitive work; unsupported parsed shell commands reject before effects. [Browser-shell policy](../devel/web_administration.md#browser-shell-policy).
|
||||||
|
|
||||||
`web_lifecycle_settings` uses one original-login slot and send-return → nonreused-ID HTTPD callback → existing dispatcher for self-cutting HTTPS/reboot actions. Two-second ACK and 30-second dequeue bounds precede admission, not receipt/completion. Accepted-but-lost callbacks retain one reservation through failed stop; only callback/successful destruction retires it. Conditional stop/restart reserve saturated lifecycle generation; restart retains ownership through stop/start. Conditional reboot invokes canonical `esp_restart()` outside locks, never HTTPD self-stop or console-cleanup waits.
|
`web_lifecycle_settings` uses one original-login slot and send-return → nonreused-ID HTTPD callback → existing dispatcher for self-cutting HTTPS/reboot actions. Two-second ACK and 30-second dequeue bounds precede admission, not receipt/completion. Accepted-but-lost callbacks retain one reservation through failed stop; only callback/successful destruction retires it. Conditional stop/restart reserve saturated lifecycle generation; restart retains ownership through stop/start. Conditional reboot invokes canonical `esp_restart()` outside locks, never HTTPD self-stop or console-cleanup waits.
|
||||||
|
|
||||||
`web_server_replace_identity` reserves service before identity and retains both through commit → reserved stop/start. Direct security and canonical CLI/browser-shell paths share task-bound nonreused identity reservations. Crypto/NVS run outside short security/service locks; commit precedes publication/wipe. Precommit failure leaves identity/HTTPD/logins unchanged; postcommit lifecycle failure never rolls back identity and can leave served/stored fingerprints different. Failed stop skips start and retains canonical recovery. Public service/security projections are separate observations, not authorization. [HTTPS ownership, generation and recovery contract](../web_administration.md#https-and-reboot).
|
`web_server_replace_identity` reserves service before identity and retains both through commit → reserved stop/start. Direct security and canonical CLI/browser-shell paths share task-bound nonreused identity reservations. Crypto/NVS run outside short security/service locks; commit precedes publication/wipe. Precommit failure leaves identity/HTTPD/logins unchanged; postcommit lifecycle failure never rolls back identity and can leave served/stored fingerprints different. Failed stop skips start and retains canonical recovery. Public service/security projections are separate observations, not authorization. [HTTPS ownership, generation and recovery contract](../devel/web_administration.md#https-and-reboot).
|
||||||
|
|
||||||
### Admin firmware upload
|
### Admin firmware upload
|
||||||
|
|
||||||
`web_firmware_update` streams a raw admin-only application image through HTTPD into the inactive OTA slot using standard ESP-IDF APIs. Existing Origin/CSRF/session checks precede body IO; raw length is bounded before HTTPD's narrowed length is trusted, SDK image validation and final principal currentness precede boot selection. One 4KiB buffer and a transient reboot task bound application storage; NVS and partition layout are untouched. HTTPD is occupied during upload, so normal web traffic can stall; deadlines bound receive progress, not flash-operation latency.
|
`web_firmware_update` streams a raw admin-only application image through HTTPD into the inactive OTA slot using standard ESP-IDF APIs. Existing Origin/CSRF/session checks precede body IO; raw length is bounded before HTTPD's narrowed length is trusted, SDK image validation and final principal currentness precede boot selection. One 4KiB buffer and a transient reboot task bound application storage; NVS and partition layout are untouched. HTTPD is occupied during upload, so normal web traffic can stall; deadlines bound receive progress, not flash-operation latency.
|
||||||
|
|
||||||
The server transition and identity reservations exclude competing lifecycle work. A separate atomic gate excludes ordinary reboot paths without depending on HTTPS initialization, preserving UART0 recovery. Successful response schedules delayed reset while retaining reservations; failed response after selection retains the selected-image latch but releases resources for manual reboot. A subsequent upload is refused until reset. No automatic retry, rollback, signing infrastructure or dependency patching. [Contract and validation limits](../roadmap.md#phase-10--simple-admin-web-firmware-upload).
|
The server transition and identity reservations exclude competing lifecycle work. A separate atomic gate excludes ordinary reboot paths without depending on HTTPS initialization, preserving UART0 recovery. Successful response schedules delayed reset while retaining reservations; failed response after selection retains the selected-image latch but releases resources for manual reboot. A subsequent upload is refused until reset. No automatic retry, rollback, signing infrastructure or dependency patching. [Contract and validation limits](../devel/roadmap.md#phase-10--simple-admin-web-firmware-upload).
|
||||||
|
|
||||||
### SSH
|
### SSH
|
||||||
|
|
||||||
Typed SSH settings use the existing ID dispatcher and original-login result slot, never HTTPD wolfSSH calls or owner waits. Conditional lifecycle/session controls compare a saturated service generation and exact nonreused session ID under canonical locks. `ssh_transport_replace_identity` reserves service then identity before stop, retaining the command mutex across stop → commit → conditional restart. Failed stop skips mutation/start; failed persistence may follow disconnection; committed identity is never rolled back after restart failure. Only the SSH owner frees context after all slots retire, and start rejects orphan handles. Direct security/CLI/deferred SSH callers share task-bound identity reservations; crypto/NVS run outside security locks. HTTPS remains available, so no self-cutting HTTP ACK gate is needed. [SSH contracts](../web_administration.md#ssh).
|
Typed SSH settings use the existing ID dispatcher and original-login result slot, never HTTPD wolfSSH calls or owner waits. Conditional lifecycle/session controls compare a saturated service generation and exact nonreused session ID under canonical locks. `ssh_transport_replace_identity` reserves service then identity before stop, retaining the command mutex across stop → commit → conditional restart. Failed stop skips mutation/start; failed persistence may follow disconnection; committed identity is never rolled back after restart failure. Only the SSH owner frees context after all slots retire, and start rejects orphan handles. Direct security/CLI/deferred SSH callers share task-bound identity reservations; crypto/NVS run outside security locks. HTTPS remains available, so no self-cutting HTTP ACK gate is needed. [SSH contracts](../devel/web_administration.md#ssh).
|
||||||
|
|
||||||
`ssh_transport` uses wolfSSH on port 22 with one explicitly dual-stack IPv6 wildcard listener and two fixed session/handshake slots. Peer formatting preserves IPv6 interface scope; IPv4 shares the listener, not a second socket. Initialization calls `wolfSSH_Init()` in the caller before task creation; after that, one owner task pinned to core 1 exclusively owns runtime contexts/sessions and wolfSSH calls. It enforces bounded handshakes, authentication attempts, receive work, and session buffers.
|
`ssh_transport` uses wolfSSH on port 22 with one explicitly dual-stack IPv6 wildcard listener and two fixed session/handshake slots. Peer formatting preserves IPv6 interface scope; IPv4 shares the listener, not a second socket. Initialization calls `wolfSSH_Init()` in the caller before task creation; after that, one owner task pinned to core 1 exclusively owns runtime contexts/sessions and wolfSSH calls. It enforces bounded handshakes, authentication attempts, receive work, and session buffers.
|
||||||
|
|
||||||
@@ -160,9 +160,9 @@ NVS is not encrypted. Password verifiers improve password storage, but Wi-Fi cre
|
|||||||
|
|
||||||
## Typed Accounts and Serial settings
|
## Typed Accounts and Serial settings
|
||||||
|
|
||||||
`web_account_settings` owns one login-bound slot for create/password/role/delete/key operations; HTTPD reads compact zero-wait metadata and queues only IDs. The database compares target username/account-ID/auth-generation inside its mutation lock and shares canonical invariant/commit logic. Successful commands target-revoke, including self; result loss is uncertain. A one-second timer wipes queued non-executing credentials after 30 seconds plus scheduling latency; admitted work wipes locals on return. Generated password delivery is a separate no-retained-retrieval POST, not mutation; key listing is fingerprint-only with stable sparse indices. [Accounts contracts](../web_administration.md#accounts-and-authorized-keys).
|
`web_account_settings` owns one login-bound slot for create/password/role/delete/key operations; HTTPD reads compact zero-wait metadata and queues only IDs. The database compares target username/account-ID/auth-generation inside its mutation lock and shares canonical invariant/commit logic. Successful commands target-revoke, including self; result loss is uncertain. A one-second timer wipes queued non-executing credentials after 30 seconds plus scheduling latency; admitted work wipes locals on return. Generated password delivery is a separate no-retained-retrieval POST, not mutation; key listing is fingerprint-only with stable sparse indices. [Accounts contracts](../devel/web_administration.md#accounts-and-authorized-keys).
|
||||||
|
|
||||||
`web_serial_settings` queues bounded typed operations to the same dispatcher, retaining one original-login result and a 30-second dequeue check. Apply/Defaults are RAM-only; Save persists device working state. Reconfiguration can discard serial-service pending bytes while broker clients/lease/output remain. Snapshot reads are zero-wait and consistent; `/api/status` reports unavailable running state as null. Settings navigation preserves both terminals; bounded completion checks and manual uncertainty recovery never replay mutations. [Typed API/lifetime](../web_administration.md#typed-settings-api-and-operation-lifetime).
|
`web_serial_settings` queues bounded typed operations to the same dispatcher, retaining one original-login result and a 30-second dequeue check. Apply/Defaults are RAM-only; Save persists device working state. Reconfiguration can discard serial-service pending bytes while broker clients/lease/output remain. Snapshot reads are zero-wait and consistent; `/api/status` reports unavailable running state as null. Settings navigation preserves both terminals; bounded completion checks and manual uncertainty recovery never replay mutations. [Typed API/lifetime](../devel/web_administration.md#typed-settings-api-and-operation-lifetime).
|
||||||
|
|
||||||
## Console architecture
|
## Console architecture
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ For SSH, standard output/error is redirected to the invoking session's bounded o
|
|||||||
|
|
||||||
Admin SSH `exit`, remote reboot, SSH stop/disconnect, and host-key rotate/reset use deferred control. The control task waits up to ten seconds for command state plus administration and transport application buffers to clear, then adds a short delay; this is a bounded best-effort heuristic, not peer-delivery confirmation. UART0 invokes these actions synchronously. User mutations and their revocations are not part of this mechanism. UART0 linenoise and the SSH editor consume the same manually maintained completion matcher and candidate formatter, so the two administration routes cannot drift in offered or displayed ambiguous completions; the hints can still drift from command registration and are not an authorization list.
|
Admin SSH `exit`, remote reboot, SSH stop/disconnect, and host-key rotate/reset use deferred control. The control task waits up to ten seconds for command state plus administration and transport application buffers to clear, then adds a short delay; this is a bounded best-effort heuristic, not peer-delivery confirmation. UART0 invokes these actions synchronously. User mutations and their revocations are not part of this mechanism. UART0 linenoise and the SSH editor consume the same manually maintained completion matcher and candidate formatter, so the two administration routes cannot drift in offered or displayed ambiguous completions; the hints can still drift from command registration and are not an authorization list.
|
||||||
|
|
||||||
Browser stop/reboot uses this same owner-adapter control path. Exact forced certificate rotation instead uses the typed queue union and immutable `dispatcher_actions` mask to hand off after drain/200 ms to the existing 12 KiB dispatcher, not the 4 KiB control stack. Pending input is discarded through execution and an executing slot remains reserved across self-detach. Canonical shared service/identity replacement preserves commit/stop/start failure semantics. Parsed other-account interactive add/password and forced role/delete are allowed. Canonical Wi-Fi commands are also allowed, including hidden secret prompts and disruptive changes; these use the ordinary owner queue and may cut off the browser before output arrives, without a deferred acknowledgement guarantee. Browser self/generated/key/recovery, mDNS mutations and restricted SSH shell actions remain denied; typed Settings has separate permissions. Post-prompt currentness is operation admission, not an atomic session-liveness/NVS guarantee. [Browser policy and recovery](../web_administration.md#browser-shell-policy).
|
Browser stop/reboot uses this same owner-adapter control path. Exact forced certificate rotation instead uses the typed queue union and immutable `dispatcher_actions` mask to hand off after drain/200 ms to the existing 12 KiB dispatcher, not the 4 KiB control stack. Pending input is discarded through execution and an executing slot remains reserved across self-detach. Canonical shared service/identity replacement preserves commit/stop/start failure semantics. Parsed other-account interactive add/password and forced role/delete are allowed. Canonical Wi-Fi commands are also allowed, including hidden secret prompts and disruptive changes; these use the ordinary owner queue and may cut off the browser before output arrives, without a deferred acknowledgement guarantee. Browser self/generated/key/recovery, mDNS mutations and restricted SSH shell actions remain denied; typed Settings has separate permissions. Post-prompt currentness is operation admission, not an atomic session-liveness/NVS guarantee. [Browser policy and recovery](../devel/web_administration.md#browser-shell-policy).
|
||||||
|
|
||||||
### Shared IPv4/IPv6 diagnostics
|
### Shared IPv4/IPv6 diagnostics
|
||||||
|
|
||||||
@@ -217,9 +217,9 @@ Configuration modules generally choose RAM defaults without erasing incompatible
|
|||||||
|
|
||||||
## Typed Display and Broker settings
|
## Typed Display and Broker settings
|
||||||
|
|
||||||
`web_display_settings` queues IDs; `local_status_ui` owns a nonwrapping configuration generation and zero-wait writer reservation shared with CLI/legacy Apply. NVS runs outside critical sections. Save stabilizes RAM, Load preserves fallback behavior, Reset commits before RAM publication. Buttons/diagnostic holds change independent activity state, not config generation. Settings need an available UI task, not an attached OLED. [Display contract](../web_administration.md#serial-and-display).
|
`web_display_settings` queues IDs; `local_status_ui` owns a nonwrapping configuration generation and zero-wait writer reservation shared with CLI/legacy Apply. NVS runs outside critical sections. Save stabilizes RAM, Load preserves fallback behavior, Reset commits before RAM publication. Buttons/diagnostic holds change independent activity state, not config generation. Settings need an available UI task, not an attached OLED. [Display contract](../devel/web_administration.md#serial-and-display).
|
||||||
|
|
||||||
`web_broker_settings` exposes compact zero-wait rows and confirmed writer assignment through one login-isolated slot. The broker atomically snapshots clients/writer/lease version and compares selected target/version inside the force-writer lock before effects. Three-bit slot/29-bit client generations retire rather than wrap; a separate saturated 32-bit lease generation advances before advisory event delivery and survives counter clear. Saturation blocks conditional assignment, not ordinary request/release/disconnect/recovery force. Contextual refresh never silently rebases explicit selections or clears sticky stale/absence latches; deliberate reselection is required. [Broker/context contracts](../web_administration.md#broker-and-contextual-controls).
|
`web_broker_settings` exposes compact zero-wait rows and confirmed writer assignment through one login-isolated slot. The broker atomically snapshots clients/writer/lease version and compares selected target/version inside the force-writer lock before effects. Three-bit slot/29-bit client generations retire rather than wrap; a separate saturated 32-bit lease generation advances before advisory event delivery and survives counter clear. Saturation blocks conditional assignment, not ordinary request/release/disconnect/recovery force. Contextual refresh never silently rebases explicit selections or clears sticky stale/absence latches; deliberate reselection is required. [Broker/context contracts](../devel/web_administration.md#broker-and-contextual-controls).
|
||||||
|
|
||||||
## Local UI and hardware boundaries
|
## Local UI and hardware boundaries
|
||||||
|
|
||||||
@@ -247,4 +247,4 @@ Hardware diagnostics are synchronous console commands. RS-232 tests own the phys
|
|||||||
|
|
||||||
`user_database_init(load_result)` has no credential input. Missing storage is persisted empty; `user_database_recover_empty()` is the unavailable-only destructive recovery API. Valid v1 user bytes load without rewriting or account changes. The private `v1_admin_marker` retains its byte position and is derived from administrator count during mutations; it is not a public bootstrap state, new role or schema change. No user migration/bootstrap/synchronization API remains.
|
`user_database_init(load_result)` has no credential input. Missing storage is persisted empty; `user_database_recover_empty()` is the unavailable-only destructive recovery API. Valid v1 user bytes load without rewriting or account changes. The private `v1_admin_marker` retains its byte position and is derived from administrator count during mutations; it is not a public bootstrap state, new role or schema change. No user migration/bootstrap/synchronization API remains.
|
||||||
|
|
||||||
`web_security` owns TLS only. A private reader validates 1,392-byte v1 `web_sec/material`, copies exact key/certificate DER, fingerprint and generation into 1,340-byte v2, commits, then publishes. Temporary v1 credential-bearing input is wiped; no public legacy credential type/getter/rotation remains. Malformed/unknown records and read/validation/commit failures fail closed, with no fallback regeneration or overwrite of rejected records. Missing material may be generated; explicit reset replaces TLS only. Downgrade to v1-only firmware is incompatible. Logical NVS replacement is not secure flash erasure. Contracts/evidence: [legacy compatibility](../roadmap.md#phase8-legacy-credential-compatibility).
|
`web_security` owns TLS only. A private reader validates 1,392-byte v1 `web_sec/material`, copies exact key/certificate DER, fingerprint and generation into 1,340-byte v2, commits, then publishes. Temporary v1 credential-bearing input is wiped; no public legacy credential type/getter/rotation remains. Malformed/unknown records and read/validation/commit failures fail closed, with no fallback regeneration or overwrite of rejected records. Missing material may be generated; explicit reset replaces TLS only. Downgrade to v1-only firmware is incompatible. Logical NVS replacement is not secure flash erasure. Contracts/evidence: [legacy compatibility](../devel/roadmap.md#phase8-legacy-credential-compatibility).
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ This is a semantic map, not a complete file inventory. Start here, then read the
|
|||||||
|
|
||||||
- `sdkconfig.defaults` explicitly selects no core dumps and silent panic reboot; existing generated configs can override defaults. README documents verification. No dependency overlays, custom crypto policy or version changes are part of this phase.
|
- `sdkconfig.defaults` explicitly selects no core dumps and silent panic reboot; existing generated configs can override defaults. README documents verification. No dependency overlays, custom crypto policy or version changes are part of this phase.
|
||||||
- User accepted the reduced Phase 9 on 2026-09-18 and waived a new hardware check; this is not a claim of a new hardware-test pass.
|
- User accepted the reduced Phase 9 on 2026-09-18 and waived a new hardware check; this is not a claim of a new hardware-test pass.
|
||||||
- [Operational checklist](../security_operations.md) and [scope/acceptance](../roadmap.md#phase-9--security-and-production-hardening): intermittent trusted-network use, unchanged authentication/recovery, no physical-extraction protection. Keep upgrades separate and do not restore the abandoned extensive patchset.
|
- [Operational checklist](../security_operations.md) and [scope/acceptance](../devel/roadmap.md#phase-9--security-and-production-hardening): intermittent trusted-network use, unchanged authentication/recovery, no physical-extraction protection. Keep upgrades separate and do not restore the abandoned extensive patchset.
|
||||||
|
|
||||||
## Secure randomness
|
## Secure randomness
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ This is a semantic map, not a complete file inventory. Start here, then read the
|
|||||||
- Data path: `transport -> broker -> serial service -> UART1`; reverse data is fanned out per client.
|
- Data path: `transport -> broker -> serial service -> UART1`; reverse data is fanned out per client.
|
||||||
- Ownership: client IDs are slot/generation-safe; events are advisory and can drop, so use snapshots as authority.
|
- Ownership: client IDs are slot/generation-safe; events are advisory and can drop, so use snapshots as authority.
|
||||||
- Lifecycle: one permanent task and eight preallocated client slots; slow output drops only for the affected client.
|
- Lifecycle: one permanent task and eight preallocated client slots; slow output drops only for the affected client.
|
||||||
- Diagnostics: `broker counters` adds active-client ID/type/pending/HWM/UART/queued/read/dropped rows; clear seeds HWM from pending, disconnect removes rows but retains global discard accounting. `tests/session_broker_diagnostics/run.py`; capture before disconnect, never use consuming `broker read` as a probe. Semantics/recipe: `docs/web_throughput_diagnostics.md`.
|
- Diagnostics: `broker counters` adds active-client ID/type/pending/HWM/UART/queued/read/dropped rows; clear seeds HWM from pending, disconnect removes rows but retains global discard accounting. `tests/session_broker_diagnostics/run.py`; capture before disconnect, never use consuming `broker read` as a probe. Semantics/recipe: `docs/devel/web_throughput_diagnostics.md`.
|
||||||
|
|
||||||
## Native USB CDC
|
## Native USB CDC
|
||||||
|
|
||||||
@@ -68,12 +68,12 @@ This is a semantic map, not a complete file inventory. Start here, then read the
|
|||||||
**Responsibility:** serve authenticated HTTPS UI/API, issue WebSocket tickets, and adapt browser serial sessions to broker clients.
|
**Responsibility:** serve authenticated HTTPS UI/API, issue WebSocket tickets, and adapt browser serial sessions to broker clients.
|
||||||
|
|
||||||
- Files: `src/web_server.{h,c}`, `src/web_serial_transport.{h,c}`, `src/web_ui.{h,c}`, `src/web_console.{h,c}`
|
- Files: `src/web_server.{h,c}`, `src/web_serial_transport.{h,c}`, `src/web_ui.{h,c}`, `src/web_console.{h,c}`
|
||||||
- Ordinary HTTPS idle cleanup: `src/web_httpd_idle.{c,h}`, owner sweep in `web_httpd_adapter.{c,h}`, lifecycle/TLS composition in `web_server.c`; `tests/web_httpd_idle/run.py`. Independent of diagnostics/optional transports: 15-second observed idle, one-second timer/one queued probe, six rows, actual WS/async/pending exemptions, safe current-owner shutdown and stop/restart fencing. No LRU/socket/timeout/stack increase. SDK queue/owner-delay and regression contract: `docs/https_idle_cleanup.md`.
|
- Ordinary HTTPS idle cleanup: `src/web_httpd_idle.{c,h}`, owner sweep in `web_httpd_adapter.{c,h}`, lifecycle/TLS composition in `web_server.c`; `tests/web_httpd_idle/run.py`. Independent of diagnostics/optional transports: 15-second observed idle, one-second timer/one queued probe, six rows, actual WS/async/pending exemptions, safe current-owner shutdown and stop/restart fencing. No LRU/socket/timeout/stack increase. SDK queue/owner-delay and regression contract: `docs/devel/https_idle_cleanup.md`.
|
||||||
- Independent throughput diagnostics: `web_serial_transport.{c,h}` owns two fixed per-slot binary-TX aggregates and epoch fences; `web_console.c` exposes default-disabled `web performance enable|disable|show|clear`. Queue-entry/callback-entry, synchronous-send and completion/drain-return estimates, not peer receipt or scheduler-only latency. `tests/web_serial_performance/run.py`; resource/evidence limits and UART0 paired capture: `docs/web_throughput_diagnostics.md`.
|
- Independent throughput diagnostics: `web_serial_transport.{c,h}` owns two fixed per-slot binary-TX aggregates and epoch fences; `web_console.c` exposes default-disabled `web performance enable|disable|show|clear`. Queue-entry/callback-entry, synchronous-send and completion/drain-return estimates, not peer receipt or scheduler-only latency. `tests/web_serial_performance/run.py`; resource/evidence limits and UART0 paired capture: `docs/devel/web_throughput_diagnostics.md`.
|
||||||
- Storage compatibility: `user_database` persists missing storage empty and preserves valid v1 user bytes; private derived `v1_admin_marker`, no public bootstrap/migration/sync APIs. `web_security` privately migrates v1 1392-byte material to TLS-only v2 1340-byte material, exact identity/generation retained, commit before publish, fail closed without fallback overwrite. Credential commands removed; user generated passwords and TLS rotation remain. Contracts, downgrade and evidence limits: [legacy compatibility](../roadmap.md#phase8-legacy-credential-compatibility).
|
- Storage compatibility: `user_database` persists missing storage empty and preserves valid v1 user bytes; private derived `v1_admin_marker`, no public bootstrap/migration/sync APIs. `web_security` privately migrates v1 1392-byte material to TLS-only v2 1340-byte material, exact identity/generation retained, commit before publish, fail closed without fallback overwrite. Credential commands removed; user generated passwords and TLS rotation remain. Contracts, downgrade and evidence limits: [legacy compatibility](../devel/roadmap.md#phase8-legacy-credential-compatibility).
|
||||||
- Security files: `src/web_security.{h,c}`, `src/web_cookie_auth.{h,c}`, `src/web_session_store.{h,c}`, `src/web_auth_parse.{h,c}`. Private IDF boundary: `src/web_httpd_adapter.{h,c}`.
|
- Security files: `src/web_security.{h,c}`, `src/web_cookie_auth.{h,c}`, `src/web_session_store.{h,c}`, `src/web_auth_parse.{h,c}`. Private IDF boundary: `src/web_httpd_adapter.{h,c}`.
|
||||||
- HTTP policy/UI: `web_cookie_auth` + `web_auth_parse` enforce bounded cookie/Origin/CSRF/admin admission; `web_login_ui.{c,h}` serves login, `web_ui.c` owns session-fenced Serial/Admin/Settings and shared quick controllers. Tests: `tests/web_cookie_auth/run.py` (domain variants), `tests/web_auth_parse/run.py`, `tests/web_login_ui/run.py`, `tests/web_ui_session/run.py`.
|
- HTTP policy/UI: `web_cookie_auth` + `web_auth_parse` enforce bounded cookie/Origin/CSRF/admin admission; `web_login_ui.{c,h}` serves login, `web_ui.c` owns session-fenced Serial/Admin/Settings and shared quick controllers. Tests: `tests/web_cookie_auth/run.py` (domain variants), `tests/web_auth_parse/run.py`, `tests/web_login_ui/run.py`, `tests/web_ui_session/run.py`.
|
||||||
- Admission diagnostics: `web_diagnostics.{c,h}`, `tests/web_diagnostics/run.py`; six post-TLS records/32-event opt-in ring, no HTTPD off-owner inspection. [Contract](../web_admission_diagnostics.md).
|
- Admission diagnostics: `web_diagnostics.{c,h}`, `tests/web_diagnostics/run.py`; six post-TLS records/32-event opt-in ring, no HTTPD off-owner inspection. [Contract](../devel/web_admission_diagnostics.md).
|
||||||
- Identity/lifecycle: `web_server_replace_identity()` + `web_security` reserve service before identity; commit before reserved stop/start, no rollback after commit. `web_lifecycle_settings.{c,h}` owns original-login ID/ACK handoff. Tests: `tests/web_security/run.py`, `tests/web_admin_transport/server_lifecycle.py`.
|
- Identity/lifecycle: `web_server_replace_identity()` + `web_security` reserve service before identity; commit before reserved stop/start, no rollback after commit. `web_lifecycle_settings.{c,h}` owns original-login ID/ACK handoff. Tests: `tests/web_security/run.py`, `tests/web_admin_transport/server_lifecycle.py`.
|
||||||
- Asset files: authored/generated boundary in `src/web_assets_data.{h,c}`, `web_assets/SOURCES.md`, `web_assets/generate_embedded_assets.py`
|
- Asset files: authored/generated boundary in `src/web_assets_data.{h,c}`, `web_assets/SOURCES.md`, `web_assets/generate_embedded_assets.py`
|
||||||
- Interfaces: web init/start/stop/snapshots; HTTP handlers; ticket mint/consume; attach/detach; targeted session revocation
|
- Interfaces: web init/start/stop/snapshots; HTTP handlers; ticket mint/consume; attach/detach; targeted session revocation
|
||||||
@@ -89,18 +89,18 @@ This is a semantic map, not a complete file inventory. Start here, then read the
|
|||||||
|
|
||||||
- Files: `src/web_admin_transport.{c,h}`, `src/web_admin_tickets.{c,h}`, protected registration/lifecycle in `web_server.c`, revocation through `web_serial_transport_revoke_*`, diagnostics in `web_console.c`.
|
- Files: `src/web_admin_transport.{c,h}`, `src/web_admin_tickets.{c,h}`, protected registration/lifecycle in `web_server.c`, revocation through `web_serial_transport_revoke_*`, diagnostics in `web_console.c`.
|
||||||
- Routes: CSRF-protected admin-only `POST /api/admin/ws-ticket`; ordinary `GET /ws/admin` with cookie/Origin/ticket/shared-console admission before explicit 101. Admin UI entry is explicit; no admin broker client. One socket, two tickets, existing two shared console slots; six total HTTPD sockets, LRU disabled; current overall capacity is 40 URI handlers.
|
- Routes: CSRF-protected admin-only `POST /api/admin/ws-ticket`; ordinary `GET /ws/admin` with cookie/Origin/ticket/shared-console admission before explicit 101. Admin UI entry is explicit; no admin broker client. One socket, two tickets, existing two shared console slots; six total HTTPD sockets, LRU disabled; current overall capacity is 40 URI handlers.
|
||||||
- Currentness/policy: `admin_ssh_console_open_available()` shares two slots with runtime SSH; transport-qualified tokens and owner adapters revalidate outside console locks before commands/prompts. Parsed browser policy remains narrower than typed Settings; [shell contract](../web_administration.md#browser-shell-policy). Tests: `tests/admin_console_boundary/{run,accounts,lifecycle}.py`, `tests/admin_ssh_policy/run.py`, `tests/web_admin_transport/run.py --tickets`, `tests/web_cookie_auth/run.py --admin`.
|
- Currentness/policy: `admin_ssh_console_open_available()` shares two slots with runtime SSH; transport-qualified tokens and owner adapters revalidate outside console locks before commands/prompts. Parsed browser policy remains narrower than typed Settings; [shell contract](../devel/web_administration.md#browser-shell-policy). Tests: `tests/admin_console_boundary/{run,accounts,lifecycle}.py`, `tests/admin_ssh_policy/run.py`, `tests/web_admin_transport/run.py --tickets`, `tests/web_cookie_auth/run.py --admin`.
|
||||||
- Ownership: 20 ms ESP timer queues at most one HTTPD poll, no new task; HTTPD owns 1,552 B PSRAM-only payload and IO. Closure uses HTTPD-owned `shutdown`, not IDF's reusable-pointer queued close. Detach fences submitters; only successful HTTPD stop retires queued state before restart. Session/principal currentness and generation checks protect all sensitive boundaries.
|
- Ownership: 20 ms ESP timer queues at most one HTTPD poll, no new task; HTTPD owns 1,552 B PSRAM-only payload and IO. Closure uses HTTPD-owned `shutdown`, not IDF's reusable-pointer queued close. Detach fences submitters; only successful HTTPD stop retires queued state before restart. Session/principal currentness and generation checks protect all sensitive boundaries.
|
||||||
|
|
||||||
## Firmware upload
|
## Firmware upload
|
||||||
|
|
||||||
- `src/web_firmware_update.{c,h}`: admin raw `POST /api/firmware`, cookie/Origin/CSRF admission via `web_cookie_auth`, registration in `web_server`, UI in `web_ui`. Standard SDK OTA into inactive app only; bounded4KiB buffer, validated raw length/header/final image and principal before boot selection. No NVS/layout writes or vendor patches.
|
- `src/web_firmware_update.{c,h}`: admin raw `POST /api/firmware`, cookie/Origin/CSRF admission via `web_cookie_auth`, registration in `web_server`, UI in `web_ui`. Standard SDK OTA into inactive app only; bounded4KiB buffer, validated raw length/header/final image and principal before boot selection. No NVS/layout writes or vendor patches.
|
||||||
- HTTPD handles streaming synchronously; upload is deliberately disruptive to web traffic. Server/identity reservations fence lifecycle changes; atomic ordinary-reboot gate also covers console/SSH/browser/button reset paths. Success-response schedules delayed reset; response failure after selection latches uploads until deliberate manual reboot. No blind retries.
|
- HTTPD handles streaming synchronously; upload is deliberately disruptive to web traffic. Server/identity reservations fence lifecycle changes; atomic ordinary-reboot gate also covers console/SSH/browser/button reset paths. Success-response schedules delayed reset; response failure after selection latches uploads until deliberate manual reboot. No blind retries.
|
||||||
- Tests: `tests/web_firmware_update/run.py` (active-build SDK5.5.0 headers and actual begin/abort contract), `tests/web_ui_session/run.py`, existing auth/lifecycle tests. [Usage, acceptance limits and reusable regression checks](../roadmap.md#phase-10--simple-admin-web-firmware-upload). Phase 10 complete by explicit user acceptance on 2026-09-18: upload works and normal operation verified; no specific fault/NVS-comparison/power-loss/recovery pass implied. Initial install by wire; subsequent upload is application `firmware.bin`, not full-flash image.
|
- Tests: `tests/web_firmware_update/run.py` (active-build SDK5.5.0 headers and actual begin/abort contract), `tests/web_ui_session/run.py`, existing auth/lifecycle tests. [Usage, acceptance limits and reusable regression checks](../devel/roadmap.md#phase-10--simple-admin-web-firmware-upload). Phase 10 complete by explicit user acceptance on 2026-09-18: upload works and normal operation verified; no specific fault/NVS-comparison/power-loss/recovery pass implied. Initial install by wire; subsequent upload is application `firmware.bin`, not full-flash image.
|
||||||
|
|
||||||
## Typed settings source and regression map
|
## Typed settings source and regression map
|
||||||
|
|
||||||
HTTPD reads zero-wait projections and queues only IDs to the existing dispatcher. One original-login slot per domain; canonical owners compare/reserve at execution. [API/lifetime and failure contracts](../web_administration.md#typed-settings-api-and-operation-lifetime).
|
HTTPD reads zero-wait projections and queues only IDs to the existing dispatcher. One original-login slot per domain; canonical owners compare/reserve at execution. [API/lifetime and failure contracts](../devel/web_administration.md#typed-settings-api-and-operation-lifetime).
|
||||||
|
|
||||||
| Domain | API / canonical owner | Focused source tests |
|
| Domain | API / canonical owner | Focused source tests |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
|||||||
@@ -2,10 +2,16 @@
|
|||||||
|
|
||||||
Working memory, not an implementation timeline. Source is authoritative; begin with [code map](code-map.md), [architecture](architecture.md) and [decisions](design-decisions.md).
|
Working memory, not an implementation timeline. Source is authoritative; begin with [code map](code-map.md), [architecture](architecture.md) and [decisions](design-decisions.md).
|
||||||
|
|
||||||
|
## Documentation reorganization handoff
|
||||||
|
|
||||||
|
- Human-facing guides live in `docs/`; implementation contracts and development evidence live in `docs/devel/`; durable agent memory remains in `docs/agent/`. Agent/test web-contract references target `docs/devel/web_administration.md`, not the top-level operator guide.
|
||||||
|
- README was refined from the user's already-thinned version, not restored from Git. New `docs/getting_started.md`, `docs/user_administration.md`, and `docs/firmware_update.md` provide operating procedures; `docs/web_administration.md` is now a user guide and its original contract is preserved under `docs/devel/`. Flash layout and PCB research moved into the developer archive; contextual test/hardware/provenance READMEs remain beside artifacts.
|
||||||
|
- Documentation relocation adds no new device evidence and does not change historical acceptance limits. Firmware, generated assets, CAD and historical hash JSON are untouched. Hardware scripts have only relocated-note reference/protection adjustments; Python syntax was checked without executing generators. Documentation links/anchors and `git diff --check` were checked; no firmware build, upload, erase, or hardware test was performed.
|
||||||
|
|
||||||
## Carrier PCB A1 — revised placement and CAD integration
|
## Carrier PCB A1 — revised placement and CAD integration
|
||||||
|
|
||||||
- `hardware/PCB/serial-carrier.kicad_pcb`: routed 82 × 80 mm two-layer draft; 36 × 19 mm top-left notch, ESP32 shifted down for bottom USB access, RS-232 bare PCB front flush with carrier top. Exact ESP32 body/USB/antenna envelope remains pending user measurements; RF reserve is provisional.
|
- `hardware/PCB/serial-carrier.kicad_pcb`: routed 82 × 80 mm two-layer draft; 36 × 19 mm top-left notch, ESP32 shifted down for bottom USB access, RS-232 bare PCB front flush with carrier top. Exact ESP32 body/USB/antenna envelope remains pending user measurements; RF reserve is provisional.
|
||||||
- Adafruit STEP metal geometry extracted and validated; provenance/dimensions in `hardware/PCB/db9-mechanical-notes.md`. Flange front sits 1.930 mm inside carrier edge, screw-lock mouths 2.870 mm outside, shell nose 3.870 mm outside. Case wall plane is not yet fixed. Seven board-local Dwgs.User projections added; rerun `add_db9_envelope.py` and `add_device_silkscreen.py` after moving modules.
|
- Adafruit STEP metal geometry extracted and validated; provenance/dimensions in `docs/devel/hardware/PCB/db9-mechanical-notes.md`. Flange front sits 1.930 mm inside carrier edge, screw-lock mouths 2.870 mm outside, shell nose 3.870 mm outside. Case wall plane is not yet fixed. Seven board-local Dwgs.User projections added; rerun `add_db9_envelope.py` and `add_device_silkscreen.py` after moving modules.
|
||||||
- Placement PDF and SVG/PNG/3D previews refreshed. Fresh PCB, independent routing, preservation and CAD validators pass: 38/38 connections, 565 segments, 22 vias, 0 physical DRC violations/unrouted, one expected provisional U2 footprint parity warning. Not fabrication-ready; no hardware qualification or Gerbers. Existing project/schematic/library work preserved; board saves must use `SaveBoard(..., True)` and require checking editor lock. Prior A0 preserved in validation backup.
|
- Placement PDF and SVG/PNG/3D previews refreshed. Fresh PCB, independent routing, preservation and CAD validators pass: 38/38 connections, 565 segments, 22 vias, 0 physical DRC violations/unrouted, one expected provisional U2 footprint parity warning. Not fabrication-ready; no hardware qualification or Gerbers. Existing project/schematic/library work preserved; board saves must use `SaveBoard(..., True)` and require checking editor lock. Prior A0 preserved in validation backup.
|
||||||
|
|
||||||
## Dual-stack diagnostics follow-up — implemented, device validation pending
|
## Dual-stack diagnostics follow-up — implemented, device validation pending
|
||||||
@@ -18,7 +24,7 @@ Working memory, not an implementation timeline. Source is authoritative; begin w
|
|||||||
|
|
||||||
## Phase 12 COMPLETE — explicit user validation
|
## Phase 12 COMPLETE — explicit user validation
|
||||||
|
|
||||||
- User states: “I just validated Phase 12 successfully.” Record acceptance, not a pending phase. Canonical device evidence and reusable regression guidance: [Phase 12](../roadmap.md#phase-12--advanced-network-integration). No unreported DHCP capture, DNS-zone update, exhaustive address/service transition, fault/soak or byte-integrity pass is implied.
|
- User states: “I just validated Phase 12 successfully.” Record acceptance, not a pending phase. Canonical device evidence and reusable regression guidance: [Phase 12](../devel/roadmap.md#phase-12--advanced-network-integration). No unreported DHCP capture, DNS-zone update, exhaustive address/service transition, fault/soak or byte-integrity pass is implied.
|
||||||
- User validated IPv6 ping/HTTPS and Avahi AAAA; Fedora authselect dual-family mDNS resolved the normal hostname lookup issue. Fresh-boot and full-mix captures show healthy reported service lifecycle. At 230400 baud 8N1 RTS/CTS: two SSH sessions with IPv6 ULA peers (user observer/admin console), two serial WebSockets (writer/observer), active browser admin, USB observer; four broker clients, one writer, zero pending/event counts at capture.
|
- User validated IPv6 ping/HTTPS and Avahi AAAA; Fedora authselect dual-family mDNS resolved the normal hostname lookup issue. Fresh-boot and full-mix captures show healthy reported service lifecycle. At 230400 baud 8N1 RTS/CTS: two SSH sessions with IPv6 ULA peers (user observer/admin console), two serial WebSockets (writer/observer), active browser admin, USB observer; four broker clients, one writer, zero pending/event counts at capture.
|
||||||
- Memory table preserved in roadmap. Fresh internal8/DMA/PSRAM free: 65,880 / 58,124 / 8,196,732 B. Full-mix free: 35,408 / 27,652 / 8,111,952 B; lifetime minima: 8,528 / 772 / 8,072,612 B; largest blocks: 22,528 / 22,528 / 7,995,392 B. Capability pools overlap; minima are conservative per-region lifetime sums, not simultaneous reserves. DMA772 remains a watch item, not proof of OOM or an acceptance blocker. SSH stack20,480B minimum-free18,476→16,284B.
|
- Memory table preserved in roadmap. Fresh internal8/DMA/PSRAM free: 65,880 / 58,124 / 8,196,732 B. Full-mix free: 35,408 / 27,652 / 8,111,952 B; lifetime minima: 8,528 / 772 / 8,072,612 B; largest blocks: 22,528 / 22,528 / 7,995,392 B. Capability pools overlap; minima are conservative per-region lifetime sums, not simultaneous reserves. DMA772 remains a watch item, not proof of OOM or an acceptance blocker. SSH stack20,480B minimum-free18,476→16,284B.
|
||||||
- SSH two successful handshakes, no auth/handshake/timeouts/I/O failures; RX68 accepted65 rejected3, TX161,209B, broker revocations2. Web serial RX27B accepted, TX318,230B across782 frames; send/queue/protocol failures0. Browser admin RX106/TX3,721B with no reported send/queue failures. Do not reinterpret nonzero rejected bytes/writer denials as proven transport loss or claim zero UART/observer drops without their counters.
|
- SSH two successful handshakes, no auth/handshake/timeouts/I/O failures; RX68 accepted65 rejected3, TX161,209B, broker revocations2. Web serial RX27B accepted, TX318,230B across782 frames; send/queue/protocol failures0. Browser admin RX106/TX3,721B with no reported send/queue failures. Do not reinterpret nonzero rejected bytes/writer denials as proven transport loss or claim zero UART/observer drops without their counters.
|
||||||
@@ -45,14 +51,14 @@ Working memory, not an implementation timeline. Source is authoritative; begin w
|
|||||||
- mDNS action API can silently drop queued work; missing families disable each pass, available families repair/re-probe every 30 seconds. Transient stale/zero A window remains until processed; AAAA follows valid-address semantics including deprecated addresses. Upstream calls can block, so polling is not a hard deadline. The later user acceptance establishes the reported device/AAAA behavior, not exhaustive multicast fault/transition testing.
|
- mDNS action API can silently drop queued work; missing families disable each pass, available families repair/re-probe every 30 seconds. Transient stale/zero A window remains until processed; AAAA follows valid-address semantics including deprecated addresses. Upstream calls can block, so polling is not a hard deadline. The later user acceptance establishes the reported device/AAAA behavior, not exhaustive multicast fault/transition testing.
|
||||||
- New **narrow** `cmake/mdns_membership.*` overlay fixes verified mDNS 1.12.0 per-family multicast reference imbalance and failed-PCB join cleanup. Only a build-local source copy is changed; version/source hash guarded, managed source immutable. This is not the abandoned Phase 9 patch set. Membership/CMake tests include negative controls and repeated transitions.
|
- New **narrow** `cmake/mdns_membership.*` overlay fixes verified mDNS 1.12.0 per-family multicast reference imbalance and failed-PCB join cleanup. Only a build-local source copy is changed; version/source hash guarded, managed source immutable. This is not the abandoned Phase 9 patch set. Membership/CMake tests include negative controls and repeated transitions.
|
||||||
- Final `pio run` PASS: **94,396 B RAM / 1,853,025 B flash** (+176 / +5,380 versus recorded Phase 10); not runtime headroom. Local saved sdkconfig mDNS capacity changed to 2; durable defaults pin IPv4/IPv6 and two services. Initial bool-atomic target failure fixed with portMUX; final target build includes overlay. Existing SDK Kconfig notes remain.
|
- Final `pio run` PASS: **94,396 B RAM / 1,853,025 B flash** (+176 / +5,380 versus recorded Phase 10); not runtime headroom. Local saved sdkconfig mDNS capacity changed to 2; durable defaults pin IPv4/IPv6 and two services. Initial bool-atomic target failure fixed with portMUX; final target build includes overlay. Existing SDK Kconfig notes remain.
|
||||||
- Host suites PASS: Wi-Fi, mDNS, membership, SSH dual-stack and existing management/runtime/security, auth parser689 (host + actual lwIP), cookie variants, Network settings (after updated netif fakes), UI171+CSP, HTTPS lifecycle45+status8+identity, firmware88+SDKcontract, broker diagnostics and session-store/serial. No network packet, hardware, fault-injection or new high-speed serial pass is implied. Canonical contracts, limits and device checklist: [Phase 12](../roadmap.md#phase-12--advanced-network-integration).
|
- Host suites PASS: Wi-Fi, mDNS, membership, SSH dual-stack and existing management/runtime/security, auth parser689 (host + actual lwIP), cookie variants, Network settings (after updated netif fakes), UI171+CSP, HTTPS lifecycle45+status8+identity, firmware88+SDKcontract, broker diagnostics and session-store/serial. No network packet, hardware, fault-injection or new high-speed serial pass is implied. Canonical contracts, limits and device checklist: [Phase 12](../devel/roadmap.md#phase-12--advanced-network-integration).
|
||||||
|
|
||||||
## Phase 10 COMPLETE — explicit user acceptance, 2026-09-18
|
## Phase 10 COMPLETE — explicit user acceptance, 2026-09-18
|
||||||
|
|
||||||
- User confirmed after firmware upload implementation and the concise-UI fix: “That works perfectly. And the usual operation is also verified.” Acceptance establishes that upload works and normal operation is verified. Do not infer specific fault-injection, NVS before/after comparisons, power-loss or recovery passes. The roadmap's compact regression guidance is reusable, not an acceptance blocker.
|
- User confirmed after firmware upload implementation and the concise-UI fix: “That works perfectly. And the usual operation is also verified.” Acceptance establishes that upload works and normal operation is verified. Do not infer specific fault-injection, NVS before/after comparisons, power-loss or recovery passes. The roadmap's compact regression guidance is reusable, not an acceptance blocker.
|
||||||
- Latest concise-UI change was copy-only; reported UI regression **169 groups PASS**. No rebuild after that text change. The integration build below is historical, not validation of a newly rebuilt UI or this documentation update.
|
- Latest concise-UI change was copy-only; reported UI regression **169 groups PASS**. No rebuild after that text change. The integration build below is historical, not validation of a newly rebuilt UI or this documentation update.
|
||||||
|
|
||||||
- User authorized simple OTA implementation; initial Git clean. New web_firmware_update module with standard SDK APIs, raw POST /api/firmware, admin cookie/Origin/CSRF and final principal currentness. Settings → HTTPS / Reboot has File/XHR upload progress, confirmation, session fencing and no automatic retries. Only inactive app/otadata writes; NVS/layout untouched. No signatures/antirollback/automaticrollback, dependencies or generated assets changed. Consolidated procedure, contract and regression guidance: [Phase 10](../roadmap.md#phase-10--simple-admin-web-firmware-upload); standalone guide removed.
|
- User authorized simple OTA implementation; initial Git clean. New web_firmware_update module with standard SDK APIs, raw POST /api/firmware, admin cookie/Origin/CSRF and final principal currentness. Settings → HTTPS / Reboot has File/XHR upload progress, confirmation, session fencing and no automatic retries. Only inactive app/otadata writes; NVS/layout untouched. No signatures/antirollback/automaticrollback, dependencies or generated assets changed. Consolidated procedure, contract and regression guidance: [Phase 10](../devel/roadmap.md#phase-10--simple-admin-web-firmware-upload); standalone guide removed.
|
||||||
- 4KiB internal buffer + transient2048B-stack reboot owner allocated before erase; 10s stall/120s receive-loop budget, not totalflashdeadline. HTTPD synchronously blocks other web work during upload; networkserial maystall/drop, reboot disruptsall. No task/request/socket lifetime capture after handler. Service/identity reservation and atomic ordinary-reboot gate cover UI/UART0/SSH/browser/localbutton paths. Failed response after bootselect schedules noautomaticreset; selected latch rejects further uploads409, manual reboot available. Successful response schedules500ms reboot retaining reservations.
|
- 4KiB internal buffer + transient2048B-stack reboot owner allocated before erase; 10s stall/120s receive-loop budget, not totalflashdeadline. HTTPD synchronously blocks other web work during upload; networkserial maystall/drop, reboot disruptsall. No task/request/socket lifetime capture after handler. Service/identity reservation and atomic ordinary-reboot gate cover UI/UART0/SSH/browser/localbutton paths. Failed response after bootselect schedules noautomaticreset; selected latch rejects further uploads409, manual reboot available. Successful response schedules500ms reboot retaining reservations.
|
||||||
- Review fixed two actualSDK5.5.0 edge cases: failed esp_ota_begin maypublishlivehandle beforeeraseerror (abortthat handle); rawContentLength64 canwrapHTTPDsize_t32 (overflow-safe actualslotbound/strictdecimal/equality check beforebody/erase). End consumes handle evenerror. SDKvalidation followed by exactparsedimage length includingSHA; basic header requiresS3appdescriptor/hash. Unrelated old/privateSDK code unpatched.
|
- Review fixed two actualSDK5.5.0 edge cases: failed esp_ota_begin maypublishlivehandle beforeeraseerror (abortthat handle); rawContentLength64 canwrapHTTPDsize_t32 (overflow-safe actualslotbound/strictdecimal/equality check beforebody/erase). End consumes handle evenerror. SDKvalidation followed by exactparsedimage length includingSHA; basic header requiresS3appdescriptor/hash. Unrelated old/privateSDK code unpatched.
|
||||||
- Parent final pio PASS **94,220 B RAM / 1,847,645 B flash**, +24RAM/+19,080flash vsPhase9, not runtimeheadroom. Parent newbackend88cases+actualSDKbeginfailurecontract, UI169groups+CSP, serverlifecycle44, admin25, consolelifecycle, SSHruntime, cookielifecyclePASS. Additionalbase/admin/display/lifecyclecookie, idle18, SSHmanagement/runtime/security agentPASS after adding missing rebootfake to adminfixture (no productionchange). Independent review final noactionablefindings; realbuilt firmware parsed with SDKmetadata bothOTAoffsets (notdeviceflashproof).
|
- Parent final pio PASS **94,220 B RAM / 1,847,645 B flash**, +24RAM/+19,080flash vsPhase9, not runtimeheadroom. Parent newbackend88cases+actualSDKbeginfailurecontract, UI169groups+CSP, serverlifecycle44, admin25, consolelifecycle, SSHruntime, cookielifecyclePASS. Additionalbase/admin/display/lifecyclecookie, idle18, SSHmanagement/runtime/security agentPASS after adding missing rebootfake to adminfixture (no productionchange). Independent review final noactionablefindings; realbuilt firmware parsed with SDKmetadata bothOTAoffsets (notdeviceflashproof).
|
||||||
@@ -67,7 +73,7 @@ Working memory, not an implementation timeline. Source is authoritative; begin w
|
|||||||
- **Reduced Phase 9 complete by explicit user sign-off.** User waived a new whole-phase device check based on prior Phase 8 validation. Application code was unchanged, but no-core-dump/silent-panic defaults changed. Do not record the waived check as executed or claim a new panic/hardware pass.
|
- **Reduced Phase 9 complete by explicit user sign-off.** User waived a new whole-phase device check based on prior Phase 8 validation. Application code was unchanged, but no-core-dump/silent-panic defaults changed. Do not record the waived check as executed or claim a new panic/hardware pass.
|
||||||
- Small scope: standard `sdkconfig.defaults` options, source-reviewed [operational checklist](../security_operations.md), README/roadmap guidance. No dependency patches, crypto policy replacement, allocator hooks, SDK migration, encryption, eFuse or partition changes. User abandoned the extensive earlier Phase 9 and restored baseline `f40c09c`; do not resurrect it.
|
- Small scope: standard `sdkconfig.defaults` options, source-reviewed [operational checklist](../security_operations.md), README/roadmap guidance. No dependency patches, crypto policy replacement, allocator hooks, SDK migration, encryption, eFuse or partition changes. User abandoned the extensive earlier Phase 9 and restored baseline `f40c09c`; do not resurrect it.
|
||||||
- Historical Phase 9 build: normal `pio run` PASS on PlatformIO6.12.0 / IDF5.5.0 / original20241119 toolchains, **94,196 B linked RAM / 1,828,565 B flash**. Generated configuration confirmed no dumps/silent panic; compilation inputs had no abandoned overlays/crypto guard. Existing generated config already selected these options. Defaults do not override saved sdkconfig; README explains verification. No new build is implied by this documentation consolidation.
|
- Historical Phase 9 build: normal `pio run` PASS on PlatformIO6.12.0 / IDF5.5.0 / original20241119 toolchains, **94,196 B linked RAM / 1,828,565 B flash**. Generated configuration confirmed no dumps/silent panic; compilation inputs had no abandoned overlays/crypto guard. Existing generated config already selected these options. Defaults do not override saved sdkconfig; README explains verification. No new build is implied by this documentation consolidation.
|
||||||
- **Phase 8 complete:** 8A–C target validated; explicit 8D.22 user acceptance on 2026-09-13. Completion/telemetry and legacy compatibility now live in [roadmap acceptance](../roadmap.md#phase8-acceptance-evidence) and [storage compatibility](../roadmap.md#phase8-legacy-credential-compatibility). Separate acceptance/legacy history documents removed; active [web contracts](../web_administration.md) and [regression procedures](../user_administration_tests.md) retained.
|
- **Phase 8 complete:** 8A–C target validated; explicit 8D.22 user acceptance on 2026-09-13. Completion/telemetry and legacy compatibility now live in [roadmap acceptance](../devel/roadmap.md#phase8-acceptance-evidence) and [storage compatibility](../devel/roadmap.md#phase8-legacy-credential-compatibility). Separate acceptance/legacy history documents removed; active [web contracts](../devel/web_administration.md) and [regression procedures](../devel/user_administration_tests.md) retained.
|
||||||
- Post-acceptance baseline includes PSRAM-only ping payload/user snapshot allocations and refined web quick panels. Prior focused host/geometry/build checks passed; those are not additional hardware claims. Preserve lazy-allocation failure isolation and unchanged serial hot path.
|
- Post-acceptance baseline includes PSRAM-only ping payload/user snapshot allocations and refined web quick panels. Prior focused host/geometry/build checks passed; those are not additional hardware claims. Preserve lazy-allocation failure isolation and unchanged serial hot path.
|
||||||
|
|
||||||
## Evidence limits and follow-ups
|
## Evidence limits and follow-ups
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ Only constraints supported by implementation or current project documentation be
|
|||||||
|
|
||||||
**Decision:** `local_status_ui` owns a nonwrapping configuration generation and a zero-wait reservation shared by typed Display and CLI/legacy Apply. NVS runs outside timing critical sections; Save stabilizes selected bytes and Reset commits defaults before RAM publication. Load retains canonical fallback. Buttons/diagnostic holds update activity, not configuration generation.
|
**Decision:** `local_status_ui` owns a nonwrapping configuration generation and a zero-wait reservation shared by typed Display and CLI/legacy Apply. NVS runs outside timing critical sections; Save stabilizes selected bytes and Reset commits defaults before RAM publication. Load retains canonical fallback. Buttons/diagnostic holds update activity, not configuration generation.
|
||||||
|
|
||||||
**Consequence:** Compare before mutation, never overwrite intervening CLI edits, and do not make physical panel presence a configuration prerequisite. A successful config API is not proof of display IO. [Owner and persistence contract](../web_administration.md#serial-and-display).
|
**Consequence:** Compare before mutation, never overwrite intervening CLI edits, and do not make physical panel presence a configuration prerequisite. A successful config API is not proof of display IO. [Owner and persistence contract](../devel/web_administration.md#serial-and-display).
|
||||||
|
|
||||||
## Typed Network edits preserve manager ownership and current secret bytes
|
## Typed Network edits preserve manager ownership and current secret bytes
|
||||||
|
|
||||||
**Decision:** HTTPD uses zero-wait secret-free projections and an ID-only dispatcher. Wi-Fi compare/merge/whole-candidate validation and queue-before-publication preserve omitted secrets and reject stale changes; its manager alone owns radio/reannouncement. mDNS generation/persistence is independent. Save stabilizes selected RAM; Wi-Fi Load is stored-only, not fallback-secret generation.
|
**Decision:** HTTPD uses zero-wait secret-free projections and an ID-only dispatcher. Wi-Fi compare/merge/whole-candidate validation and queue-before-publication preserve omitted secrets and reject stale changes; its manager alone owns radio/reannouncement. mDNS generation/persistence is independent. Save stabilizes selected RAM; Wi-Fi Load is stored-only, not fallback-secret generation.
|
||||||
|
|
||||||
**Consequence:** Keep SSIDs reversible bytes, omitted/Replace/disabled-STA Clear distinct, AP clear denied, and Next profile separate from editor selection. mDNS may report RAM applied but reannouncement not queued, without rollback. A one-second queued-secret timer is not hard cancellation; `accepted` is not online/DNS. Recovery and no automatic replay are correctness requirements. [Network contract](../web_administration.md#network).
|
**Consequence:** Keep SSIDs reversible bytes, omitted/Replace/disabled-STA Clear distinct, AP clear denied, and Next profile separate from editor selection. mDNS may report RAM applied but reannouncement not queued, without rollback. A one-second queued-secret timer is not hard cancellation; `accepted` is not online/DNS. Recovery and no automatic replay are correctness requirements. [Network contract](../devel/web_administration.md#network).
|
||||||
|
|
||||||
## One broker mediates all production serial transports
|
## One broker mediates all production serial transports
|
||||||
|
|
||||||
@@ -32,9 +32,9 @@ Only constraints supported by implementation or current project documentation be
|
|||||||
|
|
||||||
**Consequence for future changes:** Do not replace fan-out with a blocking shared queue. Any added transport must tolerate partial/no-progress reads and expose drop/backpressure counters.
|
**Consequence for future changes:** Do not replace fan-out with a blocking shared queue. Any added transport must tolerate partial/no-progress reads and expose drop/backpressure counters.
|
||||||
|
|
||||||
**Throughput observation and controlled experiments:** The initial diagnostic baseline used CPU160MHz; a CPU240MHz-only experiment reduced but did not eliminate browser queue overflow. Combining binary WebSocket header/payload into one bounded session-override send eliminated reported drops, and the user signed off230400-baud full-client-mix operation after returning to160MHz. Retain the combined send, not the frequency increase; evidence and limits are in `current-state.md`. Preserve scheduling/priorities and 4096/512-byte broker/web buffers while gathering per-client HWM/drop attribution and independent opt-in web binary-TX timing. Fixed-slot epoch/generation-fenced aggregates avoid stale attribution; no new runtime allocations. Clear preserves queued data and seeds broker HWM; disconnected rows disappear while global discard counts remain. Callback timestamps precede the transport lock; synchronous send return is not peer receipt. Completion-to-read intervals include broker/mutex/control work and possible idle, even when the first read is nonempty; never label them pure scheduling latency or proof of backlog at completion. Compare enabled/disabled target captures before drawing overhead conclusions. Contracts and reproduction: [throughput diagnostics](../web_throughput_diagnostics.md).
|
**Throughput observation and controlled experiments:** The initial diagnostic baseline used CPU160MHz; a CPU240MHz-only experiment reduced but did not eliminate browser queue overflow. Combining binary WebSocket header/payload into one bounded session-override send eliminated reported drops, and the user signed off230400-baud full-client-mix operation after returning to160MHz. Retain the combined send, not the frequency increase; evidence and limits are in `current-state.md`. Preserve scheduling/priorities and 4096/512-byte broker/web buffers while gathering per-client HWM/drop attribution and independent opt-in web binary-TX timing. Fixed-slot epoch/generation-fenced aggregates avoid stale attribution; no new runtime allocations. Clear preserves queued data and seeds broker HWM; disconnected rows disappear while global discard counts remain. Callback timestamps precede the transport lock; synchronous send return is not peer receipt. Completion-to-read intervals include broker/mutex/control work and possible idle, even when the first read is nonempty; never label them pure scheduling latency or proof of backlog at completion. Compare enabled/disabled target captures before drawing overhead conclusions. Contracts and reproduction: [throughput diagnostics](../devel/web_throughput_diagnostics.md).
|
||||||
|
|
||||||
**Relevant files:** `src/session_broker.c`, `src/session_broker.h`, `docs/roadmap.md`
|
**Relevant files:** `src/session_broker.c`, `src/session_broker.h`, `docs/devel/roadmap.md`
|
||||||
|
|
||||||
## Physical UART ownership and logical writer ownership remain separate
|
## Physical UART ownership and logical writer ownership remain separate
|
||||||
|
|
||||||
@@ -50,13 +50,13 @@ Only constraints supported by implementation or current project documentation be
|
|||||||
|
|
||||||
**Decision:** Broker/SSH/WebSocket slots, originating web sessions, queued admin operations and account principals carry distinct generation/identity fences. Browser cookie-session identity is not interchangeable with account identity. Invalidate session records before socket cleanup and retain authoritative currentness checks even when notifications fail.
|
**Decision:** Broker/SSH/WebSocket slots, originating web sessions, queued admin operations and account principals carry distinct generation/identity fences. Browser cookie-session identity is not interchangeable with account identity. Invalidate session records before socket cleanup and retain authoritative currentness checks even when notifications fail.
|
||||||
|
|
||||||
**Consequence:** Never turn selected transport IDs into arbitrary broker IDs/fds, rebase a stale confirmation or wrap a published token. Exhausted broker/SSH slots retire; operation/reservation IDs do not reuse; saturated service/lease generations fence ABA without disabling canonical recovery. Reboot invalidates old logins. Validate/reserve at the owner immediately before effects, not snapshot-check/unlock/unconditional mutation. [Broker](../web_administration.md#broker-and-contextual-controls) and [service](../web_administration.md#service-lifecycle-and-identity-rotation) contracts.
|
**Consequence:** Never turn selected transport IDs into arbitrary broker IDs/fds, rebase a stale confirmation or wrap a published token. Exhausted broker/SSH slots retire; operation/reservation IDs do not reuse; saturated service/lease generations fence ABA without disabling canonical recovery. Reboot invalidates old logins. Validate/reserve at the owner immediately before effects, not snapshot-check/unlock/unconditional mutation. [Broker](../devel/web_administration.md#broker-and-contextual-controls) and [service](../devel/web_administration.md#service-lifecycle-and-identity-rotation) contracts.
|
||||||
|
|
||||||
## Confirmed writer transfer compares a lease version inside the broker lock
|
## Confirmed writer transfer compares a lease version inside the broker lock
|
||||||
|
|
||||||
**Decision:** Compare the connected nonreused target and separate lease generation inside the force-writer lock. A client ID alone cannot fence release/reacquire ABA. Lease generation saturates, survives counter clear and advances before advisory event delivery, potentially twice for force transfer.
|
**Decision:** Compare the connected nonreused target and separate lease generation inside the force-writer lock. A client ID alone cannot fence release/reacquire ABA. Lease generation saturates, survives counter clear and advances before advisory event delivery, potentially twice for force transfer.
|
||||||
|
|
||||||
**Consequence:** Saturation rejects typed assignment but preserves ordinary request/release/disconnect and recovery force. Accepted serial TX is not recalled. Live contextual refresh must retain the selected versions and sticky stale/absence state until explicit reselection. [Wrap and UI contract](../web_administration.md#broker-and-contextual-controls).
|
**Consequence:** Saturation rejects typed assignment but preserves ordinary request/release/disconnect and recovery force. Accepted serial TX is not recalled. Live contextual refresh must retain the selected versions and sticky stale/absence state until explicit reselection. [Wrap and UI contract](../devel/web_administration.md#broker-and-contextual-controls).
|
||||||
|
|
||||||
## UART0 is the physical recovery authority
|
## UART0 is the physical recovery authority
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ Only constraints supported by implementation or current project documentation be
|
|||||||
|
|
||||||
**Consequence for future changes:** Network failures or credential corruption must not remove UART0 recovery. Do not expose unauthenticated first-admin provisioning or recovery through web or admin SSH without an explicit security redesign.
|
**Consequence for future changes:** Network failures or credential corruption must not remove UART0 recovery. Do not expose unauthenticated first-admin provisioning or recovery through web or admin SSH without an explicit security redesign.
|
||||||
|
|
||||||
**Relevant files:** `src/main.c`, `src/admin_ssh_console.c`, `src/user_console.c`, `docs/roadmap.md`
|
**Relevant files:** `src/main.c`, `src/admin_ssh_console.c`, `src/user_console.c`, `docs/devel/roadmap.md`
|
||||||
|
|
||||||
## Admin SSH and user SSH are different routes
|
## Admin SSH and user SSH are different routes
|
||||||
|
|
||||||
@@ -96,31 +96,31 @@ Only constraints supported by implementation or current project documentation be
|
|||||||
|
|
||||||
**Identity replacement:** Compare/reserve service then task-bound nonreused identity token, shared by canonical/direct callers. Crypto/NVS run outside security locks. HTTPS commits before stop/start; SSH stops before commit/restart. Failed SSH stop skips mutation/start; persistence failure can follow client disconnection. Committed material is never rolled back on restart failure. SSH owner retains context until all slots retire; start rejects orphan handles. Public fingerprint projections do not authorize mutation, and stored/served HTTPS identity can differ after failed stop.
|
**Identity replacement:** Compare/reserve service then task-bound nonreused identity token, shared by canonical/direct callers. Crypto/NVS run outside security locks. HTTPS commits before stop/start; SSH stops before commit/restart. Failed SSH stop skips mutation/start; persistence failure can follow client disconnection. Committed material is never rolled back on restart failure. SSH owner retains context until all slots retire; start rejects orphan handles. Public fingerprint projections do not authorize mutation, and stored/served HTTPS identity can differ after failed stop.
|
||||||
|
|
||||||
**Consequence:** Explicitly communicate partial effects, trusted UART0 fingerprint verification and fresh HTTPS login after restart. Preserve CLI recovery/reset but do not add browser Reset/export as if it were ordinary rotation. Native USB is independent UART1, not administration or uninterrupted reboot. [Lifecycle/identity contract](../web_administration.md#service-lifecycle-and-identity-rotation).
|
**Consequence:** Explicitly communicate partial effects, trusted UART0 fingerprint verification and fresh HTTPS login after restart. Preserve CLI recovery/reset but do not add browser Reset/export as if it were ordinary rotation. Native USB is independent UART1, not administration or uninterrupted reboot. [Lifecycle/identity contract](../devel/web_administration.md#service-lifecycle-and-identity-rotation).
|
||||||
|
|
||||||
## Authentication uses copied principals and fail-safe currentness checks
|
## Authentication uses copied principals and fail-safe currentness checks
|
||||||
|
|
||||||
**Decision:** Network sessions retain secret-free copied principals. Database/account ID/auth generation and originating web-session identity must be current at admission, before sensitive input and during reconciliation. Best-effort target notifications supplement, never replace, these checks and cannot roll back committed mutations.
|
**Decision:** Network sessions retain secret-free copied principals. Database/account ID/auth generation and originating web-session identity must be current at admission, before sensitive input and during reconciliation. Best-effort target notifications supplement, never replace, these checks and cannot roll back committed mutations.
|
||||||
|
|
||||||
**Consequence:** Shared remote-console slots require transport-qualified tokens and immutable owner adapters. Validate owner currentness outside console locks, then recheck identity. Owner-side HTTPD/SSH IO and generation-safe cleanup remain mandatory; session liveness checks do not cancel executing handlers. Browser-shell permissions are parsed and narrower than typed Settings. [Authentication](../web_administration.md#authentication-and-admission), [console policy](../web_administration.md#browser-shell-policy).
|
**Consequence:** Shared remote-console slots require transport-qualified tokens and immutable owner adapters. Validate owner currentness outside console locks, then recheck identity. Owner-side HTTPD/SSH IO and generation-safe cleanup remain mandatory; session liveness checks do not cancel executing handlers. Browser-shell permissions are parsed and narrower than typed Settings. [Authentication](../devel/web_administration.md#authentication-and-admission), [console policy](../devel/web_administration.md#browser-shell-policy).
|
||||||
|
|
||||||
## Typed serial mutations share the administration dispatcher
|
## Typed serial mutations share the administration dispatcher
|
||||||
|
|
||||||
**Decision:** Typed domains queue IDs to the existing serialized dispatcher, never CLI strings or secrets. One original-login slot per domain and a nonreused ID fence stale work; session/deadline checks precede canonical admission. Results are replaceable observations, not durable history/idempotency.
|
**Decision:** Typed domains queue IDs to the existing serialized dispatcher, never CLI strings or secrets. One original-login slot per domain and a nonreused ID fence stale work; session/deadline checks precede canonical admission. Results are replaceable observations, not durable history/idempotency.
|
||||||
|
|
||||||
**Consequence:** No automatic mutation replay, including after navigation, timeout or logout. Ordinary deadlines limit dequeue admission, not execution. Accounts/Network queued-secret timers wipe only non-executing inputs; locals wipe after admitted work returns. Explicit RAM/NVS/reset semantics and partial-effect uncertainty must match each canonical owner. [API bounds and lifetime](../web_administration.md#typed-settings-api-and-operation-lifetime).
|
**Consequence:** No automatic mutation replay, including after navigation, timeout or logout. Ordinary deadlines limit dequeue admission, not execution. Accounts/Network queued-secret timers wipe only non-executing inputs; locals wipe after admitted work returns. Explicit RAM/NVS/reset semantics and partial-effect uncertainty must match each canonical owner. [API bounds and lifetime](../devel/web_administration.md#typed-settings-api-and-operation-lifetime).
|
||||||
|
|
||||||
## Typed account selection is checked inside the database mutation lock
|
## Typed account selection is checked inside the database mutation lock
|
||||||
|
|
||||||
**Decision:** Target username/account ID/auth generation compare occurs inside the canonical mutation lock for role/delete/password/key changes. HTTPD uses compact zero-wait secret-free projections, not the blocking CLI snapshot. Successful changes notify only the target's web/SSH sessions, including self.
|
**Decision:** Target username/account ID/auth generation compare occurs inside the canonical mutation lock for role/delete/password/key changes. HTTPD uses compact zero-wait secret-free projections, not the blocking CLI snapshot. Successful changes notify only the target's web/SSH sessions, including self.
|
||||||
|
|
||||||
**Consequence:** Separate generated-value delivery from mutation and retain no retrieval history; context-bound saved acknowledgement is UX, not receipt proof. Self-revocation can deny results, so 401/disconnect cannot mean success or cancellation. Key slots are stable and sparse, fingerprint-only on output; import shares canonical validation. Final-admin invariants and UART0 provisioning/recovery remain. [Accounts contract](../web_administration.md#accounts-and-authorized-keys).
|
**Consequence:** Separate generated-value delivery from mutation and retain no retrieval history; context-bound saved acknowledgement is UX, not receipt proof. Self-revocation can deny results, so 401/disconnect cannot mean success or cancellation. Key slots are stable and sparse, fingerprint-only on output; import shares canonical validation. Final-admin invariants and UART0 provisioning/recovery remain. [Accounts contract](../devel/web_administration.md#accounts-and-authorized-keys).
|
||||||
|
|
||||||
## Browser authentication has a narrow version-pinned HTTPD boundary
|
## Browser authentication has a narrow version-pinned HTTPD boundary
|
||||||
|
|
||||||
**Decision:** `web_httpd_adapter` alone accesses private IDF 5.5.0 parsed-header/session state. Reject duplicate/ambiguous headers; defer 101 until cookie/Origin/ticket/transport admission; wipe consumed scratch while preserving unread bytes. Stage optional Settings descriptor/name allocations before publication. Compile header/ticket debug logging out.
|
**Decision:** `web_httpd_adapter` alone accesses private IDF 5.5.0 parsed-header/session state. Reject duplicate/ambiguous headers; defer 101 until cookie/Origin/ticket/transport admission; wipe consumed scratch while preserving unread bytes. Stage optional Settings descriptor/name allocations before publication. Compile header/ticket debug logging out.
|
||||||
|
|
||||||
**Consequence:** Re-audit SDK assumptions on upgrade; never patch around Origin `null` by weakening same-origin policy. Browser authentication POST uses CORS mode with fixed same-origin URLs/credentials because no-referrer non-CORS POST can serialize Origin as null. Digest-only cookie/challenge sessions replace Basic without fallback or live-record eviction. CSP loader hashes and authored scripts change atomically. Navigation preserves terminals/lease, while session-identity changes require a clean document before showing retained buffers. [Authentication and terminal contracts](../web_administration.md#authentication-and-admission).
|
**Consequence:** Re-audit SDK assumptions on upgrade; never patch around Origin `null` by weakening same-origin policy. Browser authentication POST uses CORS mode with fixed same-origin URLs/credentials because no-referrer non-CORS POST can serialize Origin as null. Digest-only cookie/challenge sessions replace Basic without fallback or live-record eviction. CSP loader hashes and authored scripts change atomically. Navigation preserves terminals/lease, while session-identity changes require a clean document before showing retained buffers. [Authentication and terminal contracts](../devel/web_administration.md#authentication-and-admission).
|
||||||
|
|
||||||
## Security material and configuration use bounded, versioned NVS records
|
## Security material and configuration use bounded, versioned NVS records
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ Only constraints supported by implementation or current project documentation be
|
|||||||
|
|
||||||
**Rationale/evidence:** Serial, Wi-Fi, local UI, web security, users, and SSH security each validate schema/size and own their namespace. User/security mutations build and validate candidate state before committing it; security modules avoid silently replacing an established identity. The live user database remains internal while its 5,360-byte candidate is a persistent PSRAM-preferred allocation with internal fallback and is wiped after every transaction.
|
**Rationale/evidence:** Serial, Wi-Fi, local UI, web security, users, and SSH security each validate schema/size and own their namespace. User/security mutations build and validate candidate state before committing it; security modules avoid silently replacing an established identity. The live user database remains internal while its 5,360-byte candidate is a persistent PSRAM-preferred allocation with internal fallback and is wiped after every transaction.
|
||||||
|
|
||||||
**Consequence for future changes:** Add schema versions and transactional candidate validation. Do not overwrite unknown records automatically; provide explicit migration/reset behavior. Preserve the distinct persistence contracts: explicit save/load/default/reset for working configuration and per-blob commit-before-live-install for user and identity mutation. Keep candidate ownership mutex-local and wipe/free it on initialization or recovery failure. Recheck external-buffer staging in the flash/NVS implementation when upgrading from the pinned ESP-IDF 5.5 baseline. Legacy credential synchronization and reconciliation are removed. Missing user storage commits empty; valid user v1 bytes remain compatible, with private `v1_admin_marker` derived from admin count, not a public bootstrap contract. HTTPS v1 (1,392 bytes) migrates through a private validated reader to TLS-only v2 (1,340 bytes), preserving exact DER/fingerprint/generation and committing before publication. Failures fail closed without fallback regeneration or overwriting rejected records. See [legacy compatibility](../roadmap.md#phase8-legacy-credential-compatibility).
|
**Consequence for future changes:** Add schema versions and transactional candidate validation. Do not overwrite unknown records automatically; provide explicit migration/reset behavior. Preserve the distinct persistence contracts: explicit save/load/default/reset for working configuration and per-blob commit-before-live-install for user and identity mutation. Keep candidate ownership mutex-local and wipe/free it on initialization or recovery failure. Recheck external-buffer staging in the flash/NVS implementation when upgrading from the pinned ESP-IDF 5.5 baseline. Legacy credential synchronization and reconciliation are removed. Missing user storage commits empty; valid user v1 bytes remain compatible, with private `v1_admin_marker` derived from admin count, not a public bootstrap contract. HTTPS v1 (1,392 bytes) migrates through a private validated reader to TLS-only v2 (1,340 bytes), preserving exact DER/fingerprint/generation and committing before publication. Failures fail closed without fallback regeneration or overwriting rejected records. See [legacy compatibility](../devel/roadmap.md#phase8-legacy-credential-compatibility).
|
||||||
|
|
||||||
**Relevant files:** `src/serial_config.c`, `src/wifi_config.c`, `src/mdns_config.c`, `src/mdns_service.c`, `src/local_ui_config.c`, `src/web_security.c`, `src/user_database.c`, `src/ssh_security.c`
|
**Relevant files:** `src/serial_config.c`, `src/wifi_config.c`, `src/mdns_config.c`, `src/mdns_service.c`, `src/local_ui_config.c`, `src/web_security.c`, `src/user_database.c`, `src/ssh_security.c`
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ Only constraints supported by implementation or current project documentation be
|
|||||||
|
|
||||||
**Consequence for future changes:** Do not remove these definitions as a performance cleanup. Any re-enablement needs target-hardware concurrency testing with simultaneous USB, WebSocket, SSH, and serial traffic plus watchdog/stack telemetry.
|
**Consequence for future changes:** Do not remove these definitions as a performance cleanup. Any re-enablement needs target-hardware concurrency testing with simultaneous USB, WebSocket, SSH, and serial traffic plus watchdog/stack telemetry.
|
||||||
|
|
||||||
**Relevant files:** `CMakeLists.txt`, `src/CMakeLists.txt`, `docs/roadmap.md`, relevant `sdkconfig.defaults` crypto settings
|
**Relevant files:** `CMakeLists.txt`, `src/CMakeLists.txt`, `docs/devel/roadmap.md`, relevant `sdkconfig.defaults` crypto settings
|
||||||
|
|
||||||
## Embedded web assets are checked-in generated artifacts
|
## Embedded web assets are checked-in generated artifacts
|
||||||
|
|
||||||
|
|||||||
+26
-22
@@ -1,10 +1,14 @@
|
|||||||
# Command reference
|
# Command reference
|
||||||
|
|
||||||
UART0 and authenticated `admin` SSH sessions use the same registered command implementations through one serialized dispatcher. Admin SSH exposes the full operational registry, including interactive prompts, network diagnostics, reboot, and HTTPS/SSH material mutation. Create the first administrator through normal `user add <username> admin` on physical UART0; explicit recovery of an unavailable database is UART0-only. Admin SSH also rejects generating a replacement password for its own account so the one-time value cannot be lost when the session is revoked. Run `help` for root commands and `<group> help` for a group summary. Configuration changes are RAM-only unless explicitly saved.
|
UART0 and authenticated `admin` SSH sessions use the same registered command implementations through one serialized dispatcher. Admin SSH exposes the full operational registry, including interactive prompts, network diagnostics, reboot, and HTTPS/SSH material mutation. Create the first administrator through normal `user add <username> admin` on physical UART0; explicit recovery of an unavailable database is UART0-only. Admin SSH also rejects generating a replacement password for its own account so the one-time value cannot be lost when the session is revoked. Run `help` for root commands and `<group> help` for a group summary. For serial, Wi-Fi, mDNS, and display settings, use the documented save/reset commands to persist working changes. Do not assume account or certificate/key changes are RAM-only.
|
||||||
|
|
||||||
Browser admin uses the same dispatcher with a [narrower parsed frontend policy](web_administration.md#browser-shell-policy), independent of typed Settings permissions. It supports bounded deferred `reboot`, `web stop`, exact `web certificate rotate --force` and owner-relative `exit`. Drain (up to ten seconds plus 200 ms) is best-effort application-buffer acknowledgement, not peer receipt or an execution deadline; pending input is discarded. Certificate work runs on the existing dispatcher through the shared service-before-identity reservation, commits before stop/restart and never rolls back a committed identity after lifecycle failure. Failed stop retains ownership and skips start. Verify changed trust through UART0 `web certificate info`, recover with UART0/admin SSH `web stop` / `web start`, then sign in freshly. HTTPS-only actions leave SSH/native USB/UART0 independent; reboot affects every transport and loses unsaved RAM.
|
## Choosing an administration connection
|
||||||
|
|
||||||
Browser `web` allows only status/stop/exact forced certificate rotation; `mdns` allows status only. Browser `wifi` supports the canonical commands, including bare `wifi` status, settings, hidden secret prompts and explicit `wifi ap show-secret`. Secret input is not echoed, retained in history or completed. Wi-Fi changes can disconnect the browser before a result arrives, without deferred drain or cancellation; reconnect and inspect before retrying, or recover through UART0. Browser `user` allows status/list/show and interactive add/password plus forced role/delete for **other accounts**, not self/generated/key/recovery commands. Restricted SSH stop/disconnect/reset/host-key mutation remains unavailable in the browser shell. Typed Accounts/Network/SSH settings separately provide their documented bounded workflows; this is not shell parity. See [web administration](web_administration.md) for lifecycle/API ownership and uncertainty.
|
Keep physical UART0 available for recovery when changing network access, accounts, or service identities. Browser Admin uses a restricted command set, separate from the permissions of the Settings dialogs. It supports `reboot`, `web stop`, exact `web certificate rotate --force`, and `exit`. These actions briefly wait for queued output before disconnecting; this is not proof that your browser received the result, and pending input is discarded.
|
||||||
|
|
||||||
|
Certificate replacement is saved before HTTPS restarts. A restart failure does not restore the old identity. Verify the fingerprint through UART0 `web certificate info`; recover with UART0/admin SSH `web stop` / `web start`, then trust the new certificate and sign in again. HTTPS-only actions leave SSH, native USB, and UART0 independent. Reboot affects every transport and loses unsaved RAM settings.
|
||||||
|
|
||||||
|
Browser `web` allows only status/stop/exact forced certificate rotation; `mdns` allows status only. Browser `wifi` supports the canonical commands, including bare `wifi` status, settings, hidden secret prompts and explicit `wifi ap show-secret`. Secret input is not echoed, retained in history or completed. Wi-Fi changes can disconnect the browser before a result arrives, without deferred drain or cancellation; reconnect and inspect before retrying, or recover through UART0. Browser `user` allows status/list/show and interactive add/password plus forced role/delete for **other accounts**, not self/generated/key/recovery commands. Restricted SSH stop/disconnect/reset/host-key mutation remains unavailable in the browser shell. The Accounts, Network, and SSH Settings dialogs offer additional actions; an action available in Settings is not necessarily available in the browser shell. See the [browser user guide](web_administration.md).
|
||||||
|
|
||||||
## System
|
## System
|
||||||
|
|
||||||
@@ -14,7 +18,7 @@ Browser `web` allows only status/stop/exact forced certificate rotation; `mdns`
|
|||||||
| `reboot` | Drain console output briefly and restart the ESP32; refused while a firmware upload or another reboot owns exclusion. |
|
| `reboot` | Drain console output briefly and restart the ESP32; refused while a firmware upload or another reboot owns exclusion. |
|
||||||
| `exit` | Close the current administrative SSH or browser session after its acknowledgement drains; unavailable on UART0. Browser `exit` leaves serial connected. Ctrl+D on an empty administrative command line does the same. |
|
| `exit` | Close the current administrative SSH or browser session after its acknowledgement drains; unavailable on UART0. Browser `exit` leaves serial connected. Ctrl+D on an empty administrative command line does the same. |
|
||||||
|
|
||||||
Firmware upload is an admin **Settings → HTTPS / Reboot** action, not a shell command. Ordinary UART0/admin-SSH/browser-shell/local-display and typed reboot paths are gated against an active upload. If upload boot selection succeeded but its response failed, no automatic restart is scheduled: the selected image remains, another admissible upload returns 409 until reset, and a deliberate manual `reboot` is permitted after reservations release. Inspect first; a lost response is not cancellation. See [firmware update and wired recovery](roadmap.md#phase10-update-and-recovery). Do not erase for an ordinary update; native USB is UART1 access, not recovery administration.
|
Firmware upload is an admin **Settings → HTTPS / Reboot** action, not a shell command. Reboot requests are refused while an upload is active. If the new image was selected for boot but the upload response failed, no automatic restart is scheduled. The selected image remains, and further upload attempts return 409 until reset; a deliberate manual `reboot` is permitted once the upload has released the service. Inspect first; a lost response is not cancellation. See the [browser user guide](web_administration.md) for firmware upload and recovery. Do not erase for an ordinary update; native USB is UART1 access, not recovery administration.
|
||||||
|
|
||||||
## Role-based users
|
## Role-based users
|
||||||
|
|
||||||
@@ -34,9 +38,9 @@ Firmware upload is an admin **Settings → HTTPS / Reboot** action, not a shell
|
|||||||
| `user key clear <username> --force` | Delete all public keys for an account. |
|
| `user key clear <username> --force` | Delete all public keys for an account. |
|
||||||
| `user recover --force` | When normal user-database initialization failed, explicitly replace only its blob with an empty database; UART0-only, refuses a healthy database. |
|
| `user recover --force` | When normal user-database initialization failed, explicitly replace only its blob with an empty database; UART0-only, refuses a healthy database. |
|
||||||
|
|
||||||
Usernames must match `[a-z][a-z0-9_-]{0,15}`. Passwords contain 12–64 printable ASCII characters. The fixed database supports eight users and three SSH keys per user; initial key types are `ssh-ed25519` and `ecdsa-sha2-nistp256`. A key may be assigned to multiple accounts but cannot be duplicated within one account. Password verifiers, salts, raw key blobs, and passwords are absent from ordinary status output. `Ctrl-C` cancels a password or key prompt, and generated passwords are shown once.
|
Usernames must match `[a-z][a-z0-9_-]{0,15}`. Passwords contain 12–64 printable ASCII characters. The fixed database supports eight users and three SSH keys per user; supported key types are `ssh-ed25519` and `ecdsa-sha2-nistp256`. A key may be assigned to multiple accounts but cannot be duplicated within one account. Password verifiers, salts, raw key blobs, and passwords are absent from ordinary status output. `Ctrl-C` cancels a password or key prompt, and generated passwords are shown once.
|
||||||
|
|
||||||
Missing `user_db/database` storage is committed empty. On UART0 run `user add <username> admin`, optionally with `--generate`, to create the first administrator. There is no bootstrap command, imported shared credential, or synchronization with HTTPS material. Existing valid v1 user databases load unchanged, including previously migrated role-`user` accounts; no account is silently promoted.
|
A device with no stored user database starts with an empty database. On UART0 run `user add <username> admin`, optionally with `--generate`, to create the first administrator. There is no default shared login. Existing valid user databases retain their accounts and roles; no account is silently promoted.
|
||||||
|
|
||||||
`user recover --force` is destructive and succeeds only while the database is unavailable. It rebuilds only the user blob empty, importing no credentials; then create an administrator with normal `user add` on UART0. It refuses a healthy initialized database, including a healthy empty one, and does not erase unrelated NVS data. Successful password, role, key and delete operations invalidate only that username's outstanding tickets and active web/SSH sessions; unrelated users remain connected.
|
`user recover --force` is destructive and succeeds only while the database is unavailable. It rebuilds only the user blob empty, importing no credentials; then create an administrator with normal `user add` on UART0. It refuses a healthy initialized database, including a healthy empty one, and does not erase unrelated NVS data. Successful password, role, key and delete operations invalidate only that username's outstanding tickets and active web/SSH sessions; unrelated users remain connected.
|
||||||
|
|
||||||
@@ -50,9 +54,9 @@ Missing `user_db/database` storage is committed empty. On UART0 run `user add <u
|
|||||||
| `display save` / `display load` | Save the working aging settings to NVS or load them. |
|
| `display save` / `display load` | Save the working aging settings to NVS or load them. |
|
||||||
| `display defaults` / `display reset` | Apply 300/600-second defaults in RAM, or save defaults first and then apply them. |
|
| `display defaults` / `display reset` | Apply 300/600-second defaults in RAM, or save defaults first and then apply them. |
|
||||||
|
|
||||||
When both transitions are enabled, `off-seconds` must be greater than `dim-seconds`. Applying settings counts as local UI activity. At normal boot, an initialized OLED shows a bounded five-second identity animation before the status UI begins; it scrolls the device name in yellow and draws the compact upright-terminal logo in blue. A missing OLED remains nonfatal; after reconnecting it safely, one new button press requests a bounded reprobe and is consumed without navigating.
|
When both transitions are enabled, `off-seconds` must be greater than `dim-seconds`. Applying settings counts as local UI activity. At normal boot, an initialized OLED shows a bounded five-second identity animation before the status UI begins; it scrolls the device name in yellow and draws the compact upright-terminal logo in blue. A missing OLED remains nonfatal. With the local UI task running and the I²C bus initialized, one new button press requests a bounded reprobe and is consumed without navigating. If `debug display status` reports `bus=unavailable`, correct the wiring with power off and reboot instead.
|
||||||
|
|
||||||
Display settings require an available local UI task, not an attached panel. CLI and browser **Settings → Display** share the public configuration owner; concurrent mutations can report busy, and browser operations reject an intervening configuration edit rather than overwrite it. Save persists working RAM, not browser drafts. Load selects defaults when saved storage is absent/incompatible without rewriting NVS. Reset storage failure leaves RAM unchanged (commit-before-publication, no RAM rollback). Browser timeout/navigation does not cancel already-admitted work; Check Result and Refresh before retrying. [Display settings contract](web_administration.md#serial-and-display).
|
Display settings require a running local UI task, not an attached panel. Concurrent console/browser changes can report busy or reject an outdated browser edit. Save persists the device's working settings, not unsent browser drafts. Load selects defaults when saved storage is absent or incompatible without rewriting it. If Reset cannot save defaults, the working settings remain unchanged. A browser timeout or navigation does not cancel an accepted action; use **Check Result** and **Refresh** before retrying.
|
||||||
|
|
||||||
## Serial service
|
## Serial service
|
||||||
|
|
||||||
@@ -80,7 +84,7 @@ Defaults are 115200 baud, 8 data bits, no parity, one stop bit, no flow control,
|
|||||||
| `broker read <client-id> [maximum-bytes]` | Read queued serial output for a client. |
|
| `broker read <client-id> [maximum-bytes]` | Read queued serial output for a client. |
|
||||||
| `broker events <client-id>` | Show ownership and connection events for a client. |
|
| `broker events <client-id>` | Show ownership and connection events for a client. |
|
||||||
|
|
||||||
Each client has a generation-safe ID. There can be one writer and multiple observers; a slow observer loses only its own queued output. HWM is bounded by 4,096 bytes; read counts transport handoff, not peer receipt. Disconnected client rows disappear, while global totals retain traffic and unread-output discards until cleared. Capture counters before disconnect. `broker read` consumes queued data and must not be used as a throughput diagnostic probe. See [Web throughput diagnostics](web_throughput_diagnostics.md).
|
Each client has a generation-safe ID. There can be one writer and multiple observers; a slow observer loses only its own queued output. HWM is bounded by 4,096 bytes; read counts transport handoff, not peer receipt. Disconnected client rows disappear, while global totals retain traffic and unread-output discards until cleared. Capture counters before disconnect. `broker read` consumes queued data and must not be used as a throughput diagnostic probe. See [Web throughput diagnostics](devel/web_throughput_diagnostics.md).
|
||||||
|
|
||||||
## Native USB CDC-ACM
|
## Native USB CDC-ACM
|
||||||
|
|
||||||
@@ -134,11 +138,9 @@ Link-local **probe destinations** require a scope on the ESP32: `%sta`, `%ap`, a
|
|||||||
|
|
||||||
`nslookup` without a selector performs separate A and AAAA queries. The installed resolver returns one selected address per family (`CONFIG_LWIP_DNS_MAX_HOST_IP=1`), not a complete DNS RRset. Missing records, DNS timeouts and server failures can share the same SDK error; partial success is displayed rather than hidden. Unicast names require a usable configured DNS server; SLAAC alone does not provision one in this firmware. `.local` uses the existing lwIP mDNS query support. No DNS server or RA/DHCP policy is changed by these commands.
|
`nslookup` without a selector performs separate A and AAAA queries. The installed resolver returns one selected address per family (`CONFIG_LWIP_DNS_MAX_HOST_IP=1`), not a complete DNS RRset. Missing records, DNS timeouts and server failures can share the same SDK error; partial success is displayed rather than hidden. Unicast names require a usable configured DNS server; SLAAC alone does not provision one in this firmware. `.local` uses the existing lwIP mDNS query support. No DNS server or RA/DHCP policy is changed by these commands.
|
||||||
|
|
||||||
Traceroute sends one eight-byte echo probe per hop with a one-second absolute receive deadline; it can stop up to 999 microseconds early to avoid the SDK's zero-millisecond/infinite-wait conversion. Hop addresses are numeric. ICMPv6 replies are checked against the actual IDF raw framing/checksum, destination and probe identity; IPv6 extension headers/fragments in replies or quotes are unsupported and ignored. Filtering/rate limiting can produce `*` even when ordinary connectivity works. DNS, scheduler and send latency are outside the receive deadline.
|
Traceroute sends one eight-byte echo probe per hop and waits up to approximately one second for a reply. Hop addresses are numeric. IPv6 extension headers/fragments in replies or quoted packets are unsupported and ignored. Filtering/rate limiting can produce `*` even when ordinary connectivity works. DNS, scheduler and send latency are outside the receive deadline.
|
||||||
|
|
||||||
Ping retains the SDK's bounded-count transient task and existing PSRAM event queue. Its console wait is bounded to `count * 2 + 2` seconds after start, but unrelated ICMP can extend the SDK receive loop: after a console timeout, further pings are refused until the old END event arrives. END finishes callback production; SDK resource teardown may briefly overlap a subsequent session. No IPv6 TTL is printed because the installed SDK does not provide a valid received IPv6 hop-limit profile. These commands serialize the administration dispatcher while executing; they do not run on HTTPD or change the broker/serial path.
|
Ping waits up to `count * 2 + 2` seconds in the console after starting. After a console timeout, further pings may be refused until the previous probe session finishes. IPv6 replies do not include a received TTL/hop-limit value in the output. Network diagnostics occupy the shared administration dispatcher while executing, so other administrative commands wait; serial traffic uses a separate path.
|
||||||
|
|
||||||
Focused host/SDK and surface tests: `tests/network_diagnostics/` and `tests/network_diagnostics_surfaces/`. Real IPv4/IPv6/scoped ping, DNS and multi-hop traceroute remain device-validation tasks for this follow-up.
|
|
||||||
|
|
||||||
## mDNS
|
## mDNS
|
||||||
|
|
||||||
@@ -151,7 +153,7 @@ Focused host/SDK and surface tests: `tests/network_diagnostics/` and `tests/netw
|
|||||||
|
|
||||||
When the Wi-Fi station has IPv4 or a preferred IPv6 address, the Wi-Fi manager announces `sak-<suffix>.local` and advertises available HTTPS/SSH services through DNS-SD. This is STA-only, local-link discovery, not certificate or host-key trust. The default suffix is the lower-case hexadecimal STA MAC address. Suffixes may contain lowercase ASCII letters, digits, and internal hyphens only. Changing a suffix queues a best-effort reannouncement and updates the DHCPv4 hostname (`sak-<suffix>`, without `.local`) for subsequent DHCP exchanges; it does not force a lease restart. A configured DHCP/DNS server may publish that name in its own zone.
|
When the Wi-Fi station has IPv4 or a preferred IPv6 address, the Wi-Fi manager announces `sak-<suffix>.local` and advertises available HTTPS/SSH services through DNS-SD. This is STA-only, local-link discovery, not certificate or host-key trust. The default suffix is the lower-case hexadecimal STA MAC address. Suffixes may contain lowercase ASCII letters, digits, and internal hyphens only. Changing a suffix queues a best-effort reannouncement and updates the DHCPv4 hostname (`sak-<suffix>`, without `.local`) for subsequent DHCP exchanges; it does not force a lease restart. A configured DHCP/DNS server may publish that name in its own zone.
|
||||||
|
|
||||||
`wifi status` and browser Network status distinguish IPv4 absence and IPv6 link-local/ULA/GUA availability. `wifi` / `wifi status` and the web Network settings dialog additionally list up to three actual preferred IPv6 addresses, labelled link-local, ULA or GUA; these are copied with the flags, not inferred from enabled IPv6 support. A link-local destination needs the client's interface as its zone. To separate client address preference from IPv6 reachability, run `ping -6 -c 3 sak-1024.local` on an IPv6-capable client; choosing IPv4 with plain `ping` does not mean the device lacks IPv6. If lookup fails, inspect AAAA resolution (for example `avahi-resolve-host-name -6 sak-1024.local` where Avahi is installed), then test the numeric address from `wifi status` directly. Link-local-only connectivity counts as `ONLINE` and can retire the fallback AP after the existing stability interval; it does not establish Internet access. HTTPS/WebSocket and SSH support both families; browser IPv6 literals require brackets and cannot contain interface zones. Use the `.local` hostname for link-local browser access where supported by the client. On-device diagnostics support explicit family selection and scoped IPv6 as described above; Linux client syntax and interface scope are separate. mDNS failures do not stop Wi-Fi, UART0, UART1, or native USB access. See [Phase 12](roadmap.md#phase-12--advanced-network-integration) for discovery convergence limits and recorded user acceptance.
|
`wifi status` and browser Network status distinguish IPv4 absence and IPv6 link-local/ULA/GUA availability. `wifi` / `wifi status` and the web Network settings dialog additionally list up to three actual preferred IPv6 addresses, labelled link-local, ULA or GUA; these are copied with the flags, not inferred from enabled IPv6 support. A link-local destination needs the client's interface as its zone. To separate client address preference from IPv6 reachability, run `ping -6 -c 3 sak-1024.local` on an IPv6-capable client; choosing IPv4 with plain `ping` does not mean the device lacks IPv6. If lookup fails, inspect AAAA resolution (for example `avahi-resolve-host-name -6 sak-1024.local` where Avahi is installed), then test the numeric address from `wifi status` directly. Link-local-only connectivity counts as `ONLINE` and can retire the fallback AP after the existing stability interval; it does not establish Internet access. HTTPS/WebSocket and SSH support both families; browser IPv6 literals require brackets and cannot contain interface zones. Use the `.local` hostname for link-local browser access where supported by the client. On-device diagnostics support explicit family selection and scoped IPv6 as described above; Linux client syntax and interface scope are separate. mDNS failures do not stop Wi-Fi, UART0, UART1, or native USB access. Discovery is best-effort and may not update immediately after network or service changes.
|
||||||
|
|
||||||
## HTTPS web terminal
|
## HTTPS web terminal
|
||||||
|
|
||||||
@@ -167,10 +169,14 @@ When the Wi-Fi station has IPv4 or a preferred IPv6 address, the Wi-Fi manager a
|
|||||||
| `web certificate rotate --force` | Replace the HTTPS certificate and private key. Browser admin defers commit and HTTPS restart; both browser routes close and new certificate trust/relogin is required. UART0/admin SSH behavior is unchanged. |
|
| `web certificate rotate --force` | Replace the HTTPS certificate and private key. Browser admin defers commit and HTTPS restart; both browser routes close and new certificate trust/relogin is required. UART0/admin SSH behavior is unchanged. |
|
||||||
| `web reset --force` | Explicitly replace HTTPS certificate/private key only, including missing, incompatible or damaged material; never changes users. |
|
| `web reset --force` | Explicitly replace HTTPS certificate/private key only, including missing, incompatible or damaged material; never changes users. |
|
||||||
|
|
||||||
Use UART0 for quiet throughput captures (authenticated admin SSH also exposes these commands). Performance output includes current-epoch pending age and count/sum/estimated-average/max timings for queue entry to callback entry, synchronous send, and completion to drain return. These are not peer acknowledgements or scheduler-only measurements; nonempty intervals may include idle. No secrets or serial payloads are printed. See [Web throughput diagnostics](web_throughput_diagnostics.md) for exact semantics, saturation limits and the reset/burst/drain/disable/capture-before-disconnect recipe.
|
Use UART0 for quiet throughput captures (authenticated admin SSH also exposes these commands). Performance output includes current-epoch pending age and count/sum/estimated-average/max timings for queue entry to callback entry, synchronous send, and completion to drain return. These are not peer acknowledgements or scheduler-only measurements; nonempty intervals may include idle. No secrets or serial payloads are printed. See [Web throughput diagnostics](devel/web_throughput_diagnostics.md) for exact semantics, saturation limits and the reset/burst/drain/disable/capture-before-disconnect recipe.
|
||||||
|
|
||||||
HTTPS listens on port 443 only. Authenticate with any current user-database username/password; both `user` and `admin` roles receive the existing status and browser-terminal interface. The device serves vendored xterm.js without Internet access. Browser sessions use one-time account-bound tickets, binary WebSocket frames, and the broker's one-writer rule. The combined **Connect**/**Disconnect** control closes the current WebSocket and pauses automatic reconnect when active; after a user-paused disconnect, it changes to **Connect** to resume connection attempts. Account mutations revoke only that account's tickets and sessions.
|
HTTPS listens on port 443 only. Authenticate with any current user-database username/password; both `user` and `admin` roles receive the existing status and browser-terminal interface. The device serves vendored xterm.js without Internet access. Browser sessions use one-time account-bound tickets, binary WebSocket frames, and the broker's one-writer rule. The combined **Connect**/**Disconnect** control closes the current WebSocket and pauses automatic reconnect when active; after a user-paused disconnect, it changes to **Connect** to resume connection attempts. Account mutations revoke only that account's tickets and sessions.
|
||||||
|
|
||||||
|
### Older firmware compatibility
|
||||||
|
|
||||||
|
HTTPS storage upgrades preserve the certificate identity, but older firmware that understands only the v1 storage format cannot read the newer v2 format. Logical replacement of stored credentials or keys is not secure flash erasure. A factory erase is not required for the storage upgrade. See [storage compatibility notes](devel/roadmap.md#phase8-legacy-credential-compatibility) before downgrading firmware.
|
||||||
|
|
||||||
## SSH serial transport
|
## SSH serial transport
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
@@ -185,13 +191,13 @@ HTTPS listens on port 443 only. Authenticate with any current user-database user
|
|||||||
| `ssh host-key rotate --force` | Replace the persistent SSH host key. |
|
| `ssh host-key rotate --force` | Replace the persistent SSH host key. |
|
||||||
| `ssh reset --force` | Explicitly replace invalid or missing SSH material. |
|
| `ssh reset --force` | Explicitly replace invalid or missing SSH material. |
|
||||||
|
|
||||||
SSH listens on port 22 and accepts user-database passwords plus stored `ssh-ed25519` and `ecdsa-sha2-nistp256` public keys. wolfSSH verifies key possession after the database authorizes the username/key pair; unsigned key probes do not complete authentication. A `user` receives the broker-backed UART1 serial stream. An `admin` receives the administration shell instead, does not become a broker client, and cannot acquire a UART1 writer lease.
|
SSH listens on port 22 and accepts user-database passwords plus stored `ssh-ed25519` and `ecdsa-sha2-nistp256` public keys. wolfSSH verifies key possession after the database authorizes the username/key pair; unsigned key probes do not complete authentication. A `user` receives the broker-backed UART1 serial stream and automatically requests writer ownership on connection. If denied, the session remains an observer and its input is discarded. Releasing another writer does not automatically promote it: deliberately assign control through administration or reconnect when the lease is free. An `admin` receives the administration shell instead, does not become a broker client, and cannot acquire a UART1 writer lease.
|
||||||
|
|
||||||
UART0 and admin SSH submit to one bounded queue, and one dispatcher task is the sole caller of `esp_console_run()`. Consequently, SSH commands execute the canonical UART0 handlers and produce the same status and mutation behavior rather than using a second command implementation. Remote output is routed into the authenticated session's bounded output ring; only the SSH transport task accesses wolfSSH.
|
UART0, admin SSH, and browser Admin share one command dispatcher. A long-running command or interactive prompt can therefore delay commands from another administrator.
|
||||||
|
|
||||||
UART0 and admin SSH use shared whole-line Tab completion. A unique/common prefix expands inline; a Tab that cannot extend an ambiguous prefix prints the matching candidates and redraws the unchanged input line instead of cycling candidates. Admin SSH additionally supports four-entry per-session command history with Up/Down, inline cursor editing with Left/Right, Home/End (including Pos1/Ende terminal sequences), Backspace/Delete, Ctrl-C, and visible or no-echo interactive prompts. Its history is RAM-only, private to the session, and wiped on disconnect. Ping callbacks enqueue bounded typed results so all formatting remains on the dispatcher task.
|
UART0 and admin SSH use shared whole-line Tab completion. A unique/common prefix expands inline; a Tab that cannot extend an ambiguous prefix prints the matching candidates and redraws the unchanged input line instead of cycling candidates. Admin SSH additionally supports four-entry per-session command history with Up/Down, inline cursor editing with Left/Right, Home/End (including Pos1/Ende terminal sequences), Backspace/Delete, Ctrl-C, and visible or no-echo interactive prompts. Its history is RAM-only, private to the session, and wiped on disconnect.
|
||||||
|
|
||||||
`exit`, `reboot`, `ssh stop`, session disconnect, and SSH host-key reset/rotation use bounded deferred control. The firmware waits on a best-effort basis for the administration output ring and transport TX buffer to drain before acting; this is not confirmation that the peer received the acknowledgement. The shell stops accepting another command while such an action is pending. SSH host-key replacement or service stop closes all SSH sessions; reconnect and verify the new fingerprint where applicable. TLS certificate management, Wi-Fi secrets, and interactive user passwords/keys are available to authenticated administrators and must therefore be treated as remotely accessible administrative material. `user recover --force` remains UART0-only; `user bootstrap` and all `web credentials` commands are removed. A connected administrator also cannot generate its own replacement password remotely, preventing the one-time password from being lost during self-revocation. SSH does not provide `exec`, SFTP, SCP, forwarding, or subsystems.
|
`exit`, `reboot`, `ssh stop`, session disconnect, and SSH host-key reset/rotation use bounded deferred control. The firmware waits on a best-effort basis for the administration output ring and transport TX buffer to drain before acting; this is not confirmation that the peer received the acknowledgement. The shell stops accepting another command while such an action is pending. SSH host-key replacement or service stop closes all SSH sessions; reconnect and verify the new fingerprint where applicable. TLS certificate management, Wi-Fi secrets, and interactive user passwords/keys are available to authenticated administrators and must therefore be treated as remotely accessible administrative material. `user recover --force` is UART0-only. A connected administrator also cannot generate its own replacement password remotely, preventing the one-time password from being lost during self-revocation. SSH does not provide `exec`, SFTP, SCP, forwarding, or subsystems.
|
||||||
|
|
||||||
## Hardware diagnostics
|
## Hardware diagnostics
|
||||||
|
|
||||||
@@ -217,6 +223,4 @@ UART0 and admin SSH use shared whole-line Tab completion. A unique/common prefix
|
|||||||
| `debug buttons status` | Show the current active-low state of previous/back GPIO10, select/confirm GPIO13, and next GPIO14. |
|
| `debug buttons status` | Show the current active-low state of previous/back GPIO10, select/confirm GPIO13, and next GPIO14. |
|
||||||
| `debug buttons test [seconds]` | Run the bounded button event test for 1–30 seconds; the default is 10 seconds. |
|
| `debug buttons test [seconds]` | Run the bounded button event test for 1–30 seconds; the default is 10 seconds. |
|
||||||
|
|
||||||
Follow the exact wiring in [Electrical tests](electrical_tests.md) before invoking diagnostics. The OLED must be powered from 3.3 V because module I²C pull-ups may connect to `VCC`; verify that all external pull-ups also terminate at 3.3 V. Display diagnostics probe the standard SSD1315-compatible 7-bit `0x3c`/`0x3d` addresses. The currently tested module acknowledges at `0x3c`, whose 8-bit write/read forms are `0x78`/`0x79`; an explicit `scan --force` is available only for the dedicated local-UI bus. Diagnostics initially run at 100 kHz and treat an absent display as nonfatal. RS-232 diagnostics that require UART1 refuse to use it until `serial stop` releases it. The RGB LED shows test state: blue idle, yellow/orange running, green passed, red failed.
|
Follow the exact wiring in [Electrical tests](electrical_tests.md) before invoking diagnostics. The OLED must be powered from 3.3 V because module I²C pull-ups may connect to `VCC`; verify that all external pull-ups also terminate at 3.3 V. Display diagnostics probe the standard SSD1315-compatible 7-bit `0x3c`/`0x3d` addresses. The currently tested module acknowledges at `0x3c`, whose 8-bit write/read forms are `0x78`/`0x79`; an explicit `scan --force` is available only for the dedicated local-UI bus. Diagnostics initially run at 100 kHz and treat an absent display as nonfatal. All RS-232 `debug` commands, including `debug status`, require `serial stop` to release the port. If diagnostic cleanup reports a port fault, reboot before continuing. The RGB LED shows test state: blue idle, yellow/orange running, green passed, red failed.
|
||||||
|
|
||||||
HTTPS storage migration preserves the exact TLS identity and commits TLS-only v2 before publication. Older v1-only firmware cannot read v2. Logical NVS replacement is not secure flash erasure; no factory erase is required. See [legacy removal](roadmap.md#phase8-legacy-credential-compatibility).
|
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# Developer documentation
|
||||||
|
|
||||||
|
This directory collects implementation contracts, research, design decisions,
|
||||||
|
regression procedures, and historical acceptance evidence. It is not the operator
|
||||||
|
manual: start with the [project README](../../README.md) for user documentation,
|
||||||
|
the [command reference](../command_reference.md) for commands, and the
|
||||||
|
[security operations checklist](../security_operations.md) for safe operation.
|
||||||
|
[Wiring](../wiring.md) and [electrical tests](../electrical_tests.md) remain beside
|
||||||
|
the user documentation because hardware safety and bring-up need that context.
|
||||||
|
|
||||||
|
## Firmware contracts and evidence
|
||||||
|
|
||||||
|
- [Flash partition layout](flash_layout.md): storage allocation, reserved partitions, and update boundaries.
|
||||||
|
|
||||||
|
- [Roadmap and acceptance record](roadmap.md): completed phases, retained scope,
|
||||||
|
historical evidence, and limits of acceptance.
|
||||||
|
- [Web administration contracts](web_administration.md): contributor-facing API,
|
||||||
|
ownership, lifecycle, and recovery details.
|
||||||
|
- [User/administration regression procedures](user_administration_tests.md):
|
||||||
|
reusable checks, including historical baselines explicitly marked as superseded.
|
||||||
|
- [HTTPS idle cleanup](https_idle_cleanup.md): HTTPD owner, SDK, and lifecycle contract.
|
||||||
|
- [Web admission diagnostics](web_admission_diagnostics.md): instrumentation and interpretation limits.
|
||||||
|
- [Web throughput diagnostics](web_throughput_diagnostics.md): counters, capture procedures, and evidence limits.
|
||||||
|
- Display design references: [Phase 7 display mockup](phase7_display_mockup.svg)
|
||||||
|
and [Phase 7C icon mockup](phase7c_icon_mockup.svg), not promises of current UI appearance.
|
||||||
|
|
||||||
|
Source code is authoritative for current behavior. A checklist is not a test result;
|
||||||
|
host doubles do not prove target timing, flash durability, power-loss recovery, or
|
||||||
|
physical electrical safety. Preserve dates, revisions, workload details, and explicit
|
||||||
|
limitations when recording new evidence. Overall user acceptance does not establish
|
||||||
|
that every proposed regression procedure was executed.
|
||||||
|
|
||||||
|
Markdown links are document-relative. Unless explicitly stated otherwise, source,
|
||||||
|
build, and test paths in code spans and commands are repository-root-relative.
|
||||||
|
Hardware notes identify retained artifact paths and any different command directory.
|
||||||
|
|
||||||
|
## Tests and asset provenance stay with their artifacts
|
||||||
|
|
||||||
|
Test fixtures, scripts, and contextual READMEs remain under [tests](../../tests/).
|
||||||
|
Examples include the [HTTPD idle suite](../../tests/web_httpd_idle/README.md),
|
||||||
|
[admission diagnostics suite](../../tests/web_diagnostics/README.md),
|
||||||
|
[web administration suite](../../tests/web_admin_transport/README.md), and
|
||||||
|
[browser UI suite](../../tests/web_ui_session/README.md). Read each suite's scope,
|
||||||
|
prerequisites, and execution instructions rather than assuming a single host-test command.
|
||||||
|
These links are navigation, not claims that tests ran during documentation cleanup.
|
||||||
|
|
||||||
|
[Web asset provenance and generation policy](../../web_assets/SOURCES.md) remains
|
||||||
|
with the web assets. Do not hand-edit generated embedded assets or regenerate them
|
||||||
|
as a side effect of documentation changes; preserve upstream licenses and attribution.
|
||||||
|
|
||||||
|
## Hardware research and design notes
|
||||||
|
|
||||||
|
The [contextual PCB README](../../hardware/PCB/README.md) stays with the editable
|
||||||
|
KiCad project, CAD, libraries, exports, and scripts. The board remains a draft,
|
||||||
|
not fabrication-ready. Development notes preserve the hardware-relative substructure:
|
||||||
|
|
||||||
|
- [Adafruit MAX3243 research](hardware/PCB/adafruit-research.md)
|
||||||
|
- [Button selection and evidence](hardware/PCB/button-notes.md)
|
||||||
|
- [DE-9 mechanical research](hardware/PCB/db9-mechanical-notes.md)
|
||||||
|
- [Module footprint notes](hardware/PCB/footprint-notes.md)
|
||||||
|
- [RS-232 reference footprint notes](hardware/PCB/rs232-footprint-notes.md)
|
||||||
|
- [PCB draft design and release gates](hardware/PCB/pcb-draft-notes.md)
|
||||||
|
- [Routing validation and historical evidence](hardware/PCB/validation/pcb-routing-summary.md)
|
||||||
|
|
||||||
|
Original reference/provenance READMEs remain beside their sources:
|
||||||
|
[Adafruit Eagle reference](../../hardware/PCB/reference/README.md) and
|
||||||
|
[DE-9 CAD reference](../../hardware/PCB/reference/db9-cad/README.md).
|
||||||
|
Relocation does not change licenses, validate provisional male-module assumptions,
|
||||||
|
or qualify the board for manufacture. Do not casually rerun destructive generators;
|
||||||
|
use the retained hardware README and each script's documented safeguards.
|
||||||
|
|
||||||
|
## Security maintenance and evidence
|
||||||
|
|
||||||
|
Contributor maintenance includes reviewing relevant ESP-IDF, wolfSSL/wolfSSH, and
|
||||||
|
bundled web-library upstream release/security notes, especially before an update.
|
||||||
|
A relevant issue needs a separate upgrade/mitigation decision and scoped validation;
|
||||||
|
this is not an ongoing CVE audit or a claim that the firmware is vulnerability-free.
|
||||||
|
Dependency patches, crypto upgrades, secure boot, encrypted NVS, and eFuse changes
|
||||||
|
are outside the accepted lightweight operating-checklist scope, not implicit work
|
||||||
|
items or properties of a deployed device. See the [Phase 9 record](roadmap.md#phase-9--security-and-production-hardening).
|
||||||
|
|
||||||
|
The original security checklist's commands and limits were source-reviewed against
|
||||||
|
application sources and the command reference at `f40c09c`. That provenance is not
|
||||||
|
a new build, device test, security audit, or verification of flash/eFuse settings;
|
||||||
|
verify current implementation when behavior changes. Keep the operator checklist
|
||||||
|
focused on actionable use and recovery rather than contributor dependency work.
|
||||||
|
Never put passwords, cookies, tickets, Wi-Fi secrets, private keys, or verifier
|
||||||
|
material in routine logs, documentation captures, or status output.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Flash partition layout
|
||||||
|
|
||||||
|
The N16R8 target has 16 MiB flash and 8 MiB octal PSRAM. PlatformIO uses the custom [`partitions.csv`](../../partitions.csv) layout:
|
||||||
|
|
||||||
|
| Partition | Offset | Size | Purpose |
|
||||||
|
|---|---:|---:|---|
|
||||||
|
| `nvs` | `0x009000` | 512 KiB | Serial, Wi-Fi, mDNS hostname, local-display, role-based user, HTTPS identity, and SSH host-key data |
|
||||||
|
| `otadata` | `0x089000` | 8 KiB | Active OTA-slot selection metadata |
|
||||||
|
| `phy_init` | `0x08B000` | 4 KiB | Optional PHY initialization data |
|
||||||
|
| `nvs_key` | `0x08C000` | 4 KiB | Reserved for future encrypted-NVS keys |
|
||||||
|
| `coredump` | `0x08D000` | 128 KiB | Reserved for flash core dumps |
|
||||||
|
| `ota_0` | `0x0B0000` | 4 MiB | Primary application/OTA slot |
|
||||||
|
| `ota_1` | `0x4B0000` | 4 MiB | Alternate application/OTA slot |
|
||||||
|
| `storage` | `0x8B0000` | 7488 KiB | Reserved; not mounted or used for session recording |
|
||||||
|
|
||||||
|
Application offsets are aligned to the ESP32-S3's required 64 KiB boundary. The final storage partition ends at `0x1000000`, exactly the end of the 16 MiB flash chip.
|
||||||
|
|
||||||
|
Admin HTTPS application upload uses the inactive OTA slot and `otadata`; NVS and other data partitions are untouched by the updater. It does not provide remote downloads, automatic rollback/health confirmation, core-dump handling, NVS encryption or filesystem mounting. Image validation does not guarantee a working application; compatible storage schemas and wired recovery remain the owner's responsibility.
|
||||||
|
|
||||||
|
Partition reservations do not enable their named features. In particular, `nvs_key` does not enable encryption, and the default firmware does not capture core dumps. Existing generated SDK configurations can override `sdkconfig.defaults`; inspect the effective configuration when verifying a build's security properties.
|
||||||
|
|
||||||
|
See [first installation and destructive migration](../getting_started.md#partition-migration-and-erasing) and the [firmware update guide](../firmware_update.md) for operator procedures. Do not change partition offsets as part of an ordinary application update.
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
# Adafruit MAX3243 breakout: authoritative CAD header research
|
# Adafruit MAX3243 breakout: authoritative CAD header research
|
||||||
|
|
||||||
|
Paths in prose point to the retained hardware artifacts or neighboring development notes.
|
||||||
|
Commands keep their stated working directory; repository-root paths remain root-relative.
|
||||||
|
|
||||||
Research date: 2026-09-20. Scope: source retrieval and pad/geometry extraction only; no project schematic or layout modified.
|
Research date: 2026-09-20. Scope: source retrieval and pad/geometry extraction only; no project schematic or layout modified.
|
||||||
|
|
||||||
## Critical product distinction
|
## Critical product distinction
|
||||||
@@ -17,13 +20,13 @@ Authoritative chain:
|
|||||||
3. Retrieved `main` commit **`63c6200bb3ef17d491089cfa6441b1810d79bb20`**, committed 2024-08-12T22:42:27Z. All four local originals were downloaded from this immutable revision, not a moving branch.
|
3. Retrieved `main` commit **`63c6200bb3ef17d491089cfa6441b1810d79bb20`**, committed 2024-08-12T22:42:27Z. All four local originals were downloaded from this immutable revision, not a moving branch.
|
||||||
4. [Product 5988, female](https://www.adafruit.com/product/5988), [product 6253, male](https://www.adafruit.com/product/6253), and [Learn pinouts](https://learn.adafruit.com/adafruit-rs-232-full-pinout-level-shifter-breakout/pinouts) corroborate identity and signal names.
|
4. [Product 5988, female](https://www.adafruit.com/product/5988), [product 6253, male](https://www.adafruit.com/product/6253), and [Learn pinouts](https://learn.adafruit.com/adafruit-rs-232-full-pinout-level-shifter-breakout/pinouts) corroborate identity and signal names.
|
||||||
|
|
||||||
Local files in `reference/`, unchanged upstream contents:
|
Local files in `../../../../hardware/PCB/reference`, unchanged upstream contents:
|
||||||
|
|
||||||
| File | SHA-256 |
|
| File | SHA-256 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `Adafruit RS-232 Full Pinout Level-Shifter Breakout.brd` | `6a7b35ef909f5a2d243c326c34db3957aaa01275bf2aac8e0c749ff18162e870` |
|
| `Adafruit RS-232 Full Pinout Level-Shifter Breakout.brd` | `6a7b35ef909f5a2d243c326c34db3957aaa01275bf2aac8e0c749ff18162e870` |
|
||||||
| `Adafruit RS-232 Full Pinout Level-Shifter Breakout.sch` | `4efbbe0ad14194e4e4563dd5f7dac007dfd52eba636f7390ffb822f3e81cd896` |
|
| `Adafruit RS-232 Full Pinout Level-Shifter Breakout.sch` | `4efbbe0ad14194e4e4563dd5f7dac007dfd52eba636f7390ffb822f3e81cd896` |
|
||||||
| `README.md` | `2825ba741a8ce83abdaf2c7cb9170641473357630ccbee40a0016dcdb5cd80bb` |
|
| `../../../../hardware/PCB/reference/README.md` | `2825ba741a8ce83abdaf2c7cb9170641473357630ccbee40a0016dcdb5cd80bb` |
|
||||||
| `license.txt` | `075dad5e5fc96c27014fabc269f4f5732909cffd178a486f546d982b6cf86b74` |
|
| `license.txt` | `075dad5e5fc96c27014fabc269f4f5732909cffd178a486f546d982b6cf86b74` |
|
||||||
|
|
||||||
Raw URL prefix for each filename (URL-encode spaces):
|
Raw URL prefix for each filename (URL-encode spaces):
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
# Button selection: Omron B3F-1000
|
# Button selection: Omron B3F-1000
|
||||||
|
|
||||||
|
Paths in prose point to the retained hardware artifacts or neighboring development notes.
|
||||||
|
Commands keep their stated working directory; repository-root paths remain root-relative.
|
||||||
|
|
||||||
Verified 2026-09-20 against the Omron datasheet and installed KiCad libraries. This research covers three active-low GPIO-to-GND buttons.
|
Verified 2026-09-20 against the Omron datasheet and installed KiCad libraries. This research covers three active-low GPIO-to-GND buttons.
|
||||||
|
|
||||||
**Integration decision:** the A0 schematic now implements B3F-1000 switches with one **2.2 kΩ, 1%, at least 0.125 W external pull-up** per input. The lower-current/internal-pull-up-only discussion below records the selection rationale, not an unselected schematic option. Firmware is unchanged. Exact resistor MPN, supply tolerance/budget and stock remain release checks.
|
**Integration decision:** the A0 schematic now implements B3F-1000 switches with one **2.2 kΩ, 1%, at least 0.125 W external pull-up** per input. The lower-current/internal-pull-up-only discussion below records the selection rationale, not an unselected schematic option. Firmware is unchanged. Exact resistor MPN, supply tolerance/budget and stock remain release checks.
|
||||||
@@ -100,8 +103,8 @@ Retain firmware debouncing; a mechanical switch is not bounce-free. The device i
|
|||||||
## Evidence and remaining checks
|
## Evidence and remaining checks
|
||||||
|
|
||||||
- [Authoritative Omron datasheet](https://omronfs.omron.com/en_US/ecb/products/pdf/en-b3f.pdf), retrieved 2026-09-20.
|
- [Authoritative Omron datasheet](https://omronfs.omron.com/en_US/ecb/products/pdf/en-b3f.pdf), retrieved 2026-09-20.
|
||||||
- [Local PDF](reference/button-omron-b3f.pdf).
|
- [Local PDF](../../../../hardware/PCB/reference/button-omron-b3f.pdf).
|
||||||
- [Extracted text](reference/button-omron-b3f.txt) for searching; use the PDF for drawing interpretation.
|
- [Extracted text](../../../../hardware/PCB/reference/button-omron-b3f.txt) for searching; use the PDF for drawing interpretation.
|
||||||
- [Rendered page 4](reference/button-omron-b3f-page4.png), visually checked for the internal connections and dimensions.
|
- [Rendered page 4](../../../../hardware/PCB/reference/button-omron-b3f-page4.png), visually checked for the internal connections and dimensions.
|
||||||
|
|
||||||
Before manufacturing: validate the selected pull-up strategy against supply tolerances/budget; check ERC/net assignment and PCB DRC in the actual design; confirm finished holes, enclosure/actuator access and a sample's fit/continuity; confirm procurement. This review verifies datasheet-to-library compatibility, not a completed-board electrical or mechanical test.
|
Before manufacturing: validate the selected pull-up strategy against supply tolerances/budget; check ERC/net assignment and PCB DRC in the actual design; confirm finished holes, enclosure/actuator access and a sample's fit/continuity; confirm procurement. This review verifies datasheet-to-library compatibility, not a completed-board electrical or mechanical test.
|
||||||
+11
-8
@@ -1,5 +1,8 @@
|
|||||||
# DE-9 mechanical reference — pinned Adafruit 5988 STEP
|
# DE-9 mechanical reference — pinned Adafruit 5988 STEP
|
||||||
|
|
||||||
|
Paths in prose point to the retained hardware artifacts or neighboring development notes.
|
||||||
|
Commands keep their stated working directory; repository-root paths remain root-relative.
|
||||||
|
|
||||||
Research: 2026-09-21. **Nominal CAD geometry, not a toleranced connector drawing or physical fit qualification.** No board, schematic, footprint, application dependency or main README changes.
|
Research: 2026-09-21. **Nominal CAD geometry, not a toleranced connector drawing or physical fit qualification.** No board, schematic, footprint, application dependency or main README changes.
|
||||||
|
|
||||||
**User-confirmed assumption:** male and female modules have identical external dimensions; use the external geometry below for mechanical planning of the male module on that basis. This is not independent male-CAD verification and **does not verify the male pin map**. The source explicitly identifies `DE9-F` / `F09HP` (5988). Do not infer contact numbering from gender, the model, or the front view.
|
**User-confirmed assumption:** male and female modules have identical external dimensions; use the external geometry below for mechanical planning of the male module on that basis. This is not independent male-CAD verification and **does not verify the male pin map**. The source explicitly identifies `DE9-F` / `F09HP` (5988). Do not infer contact numbering from gender, the model, or the front view.
|
||||||
@@ -9,25 +12,25 @@ Research: 2026-09-21. **Nominal CAD geometry, not a toleranced connector drawing
|
|||||||
- User's link: https://github.com/adafruit/Adafruit_CAD_Parts/tree/main/5988%20RS-232%20Level%20Shifter
|
- User's link: https://github.com/adafruit/Adafruit_CAD_Parts/tree/main/5988%20RS-232%20Level%20Shifter
|
||||||
- Pinned repository revision: **`5b8a21fb5e2e48478faf8901077be812f5224066`**, 2024-09-05, Noe Ruiz, “Adding 5988 RS-232 Level Shifter”. GitHub path history returned this as the latest change to that directory at retrieval.
|
- Pinned repository revision: **`5b8a21fb5e2e48478faf8901077be812f5224066`**, 2024-09-05, Noe Ruiz, “Adding 5988 RS-232 Level Shifter”. GitHub path history returned this as the latest change to that directory at retrieval.
|
||||||
- Immutable directory: https://github.com/adafruit/Adafruit_CAD_Parts/tree/5b8a21fb5e2e48478faf8901077be812f5224066/5988%20RS-232%20Level%20Shifter
|
- Immutable directory: https://github.com/adafruit/Adafruit_CAD_Parts/tree/5b8a21fb5e2e48478faf8901077be812f5224066/5988%20RS-232%20Level%20Shifter
|
||||||
- Unmodified originals retained in `reference/db9-cad/`: STEP, repository-root `README.md`, repository-root `LICENSE`. `sources.json` records **full immutable raw URLs and SHA-256 hashes**.
|
- Unmodified originals retained in `../../../../hardware/PCB/reference/db9-cad`: STEP, upstream repository-root `../../../../hardware/PCB/reference/db9-cad/README.md`, upstream repository-root `../../../../hardware/PCB/reference/db9-cad/LICENSE`. `../../../../hardware/PCB/reference/db9-cad/sources.json` records **full immutable raw URLs and SHA-256 hashes**.
|
||||||
|
|
||||||
| Original | SHA-256 |
|
| Original | SHA-256 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `5988 RS-232 Level Shifter.step` | `8eab9dc591a985fc987ceec2d7534e5e259263c73bebc64c0c3cd847e135906f` |
|
| `../../../../hardware/PCB/reference/db9-cad/5988 RS-232 Level Shifter.step` | `8eab9dc591a985fc987ceec2d7534e5e259263c73bebc64c0c3cd847e135906f` |
|
||||||
| `LICENSE` | `f60436644e066301d4c2e8f1f0674a157b1f60e4fd5586717b99dde255cc18cd` |
|
| `../../../../hardware/PCB/reference/db9-cad/LICENSE` | `f60436644e066301d4c2e8f1f0674a157b1f60e4fd5586717b99dde255cc18cd` |
|
||||||
| `README.md` | `9ed9beac7600623b7b9bd0d02e1fdcd86610ce22966723179e6c6b618e399dce` |
|
| `../../../../hardware/PCB/reference/db9-cad/README.md` | `9ed9beac7600623b7b9bd0d02e1fdcd86610ce22966723179e6c6b618e399dce` |
|
||||||
|
|
||||||
The CAD repository root license is **MIT, Copyright (c) 2016 Adafruit Industries**; root README merely describes STEP/Fusion/STL parts. No part-specific license file is present in the four-file directory. Retain the original MIT notice and warranty disclaimer with redistribution. This assessment follows the repository's stated license, not an independent audit of any imported vendor geometry. F3D, STL and JPG were listed but not downloaded or used; no image-derived dimensions or mesh fallback were needed.
|
The CAD repository root license is **MIT, Copyright (c) 2016 Adafruit Industries**; root README merely describes STEP/Fusion/STL parts. No part-specific license file is present in the four-file directory. Retain the original MIT notice and warranty disclaimer with redistribution. This assessment follows the repository's stated license, not an independent audit of any imported vendor geometry. F3D, STL and JPG were listed but not downloaded or used; no image-derived dimensions or mesh fallback were needed.
|
||||||
|
|
||||||
The separate Eagle board reference is from `Adafruit-RS-232-Level-Shifter-Breakout-PCB`, commit `63c6200bb3ef17d491089cfa6441b1810d79bb20`; its local `.brd` hash was rechecked (`6a7b35ef909f5a2d243c326c34db3957aaa01275bf2aac8e0c749ff18162e870`). Eagle-derived registration/hole information is attributed to **Limor Fried/Ladyada for Adafruit Industries**, under **CC BY-SA 3.0 Unported**; retain `reference/README.md` and `reference/license.txt`. The Eagle-derived portions of these notes use that license. Do not silently substitute the CAD-parts repository's MIT license for the Eagle design license.
|
The separate Eagle board reference is from `Adafruit-RS-232-Level-Shifter-Breakout-PCB`, commit `63c6200bb3ef17d491089cfa6441b1810d79bb20`; its local `.brd` hash was rechecked (`6a7b35ef909f5a2d243c326c34db3957aaa01275bf2aac8e0c749ff18162e870`). Eagle-derived registration/hole information is attributed to **Limor Fried/Ladyada for Adafruit Industries**, under **CC BY-SA 3.0 Unported**; retain `../../../../hardware/PCB/reference/README.md` and `../../../../hardware/PCB/reference/license.txt`. The Eagle-derived portions of these notes use that license. Do not silently substitute the CAD-parts repository's MIT license for the Eagle design license.
|
||||||
|
|
||||||
## Extraction method and confidence
|
## Extraction method and confidence
|
||||||
|
|
||||||
Used Python/OpenCascade (`cadquery-ocp==7.9.3.1.1`) in the isolated project `.cache/cad-tools/venv`; no application dependencies changed. The initially attempted older OCP wheel was unavailable for this Python; the pinned installed version succeeded.
|
Used Python/OpenCascade (`cadquery-ocp==7.9.3.1.1`) in the isolated project `.cache/cad-tools/venv`; no application dependencies changed. The initially attempted older OCP wheel was unavailable for this Python; the pinned installed version succeeded.
|
||||||
|
|
||||||
- `reference/db9-cad/extract.py`: STEPControl import with assembly placements applied; `BRepBndLib.AddOptimal` with triangulation and shape-tolerance enlargement disabled; analytic planes/cylinders via `BRepAdaptor_Surface`. No tessellation used for measurement.
|
- `../../../../hardware/PCB/reference/db9-cad/extract.py`: STEPControl import with assembly placements applied; `BRepBndLib.AddOptimal` with triangulation and shape-tolerance enlargement disabled; analytic planes/cylinders via `BRepAdaptor_Surface`. No tessellation used for measurement.
|
||||||
- `geometry.json`: all 15 solids and their faces, bounds, validity, analytic cylinder radii/axes, plane normals. All 15 pass `BRepCheck_Analyzer`.
|
- `../../../../hardware/PCB/reference/db9-cad/geometry.json`: all 15 solids and their faces, bounds, validity, analytic cylinder radii/axes, plane normals. All 15 pass `BRepCheck_Analyzer`.
|
||||||
- `validate.py`: checks original hashes, all 12 header centres against actual Eagle XML, board bounds, mounting holes and selected front-face geometry; writes `measurements.json` containing explicit face selections and their bounds.
|
- `../../../../hardware/PCB/reference/db9-cad/validate.py`: checks original hashes, all 12 header centres against actual Eagle XML, board bounds, mounting holes and selected front-face geometry; writes `../../../../hardware/PCB/reference/db9-cad/measurements.json` containing explicit face selections and their bounds.
|
||||||
- For unique PCB/connector bodies, STEP `CLOSED_SHELL` face order is associated with import face order, checking face counts, every surface type and every cylinder radius. Appearance assignments corroborate feature identification; **colour is not a material certification**.
|
- For unique PCB/connector bodies, STEP `CLOSED_SHELL` face order is associated with import face order, checking face counts, every surface type and every cylinder radius. Appearance assignments corroborate feature identification; **colour is not a material certification**.
|
||||||
- Imported solid 12 = STEP `#101` / `Body2`, the main connector. It fuses housing/insert, contacts, flange, shell and front hex posts; its whole-solid bounds are **not metal-only dimensions**. Solids 13/14 = `#102`/`#103` (`Body3`/`Body4`), separate anchor brackets.
|
- Imported solid 12 = STEP `#101` / `Body2`, the main connector. It fuses housing/insert, contacts, flange, shell and front hex posts; its whole-solid bounds are **not metal-only dimensions**. Solids 13/14 = `#102`/`#103` (`Body3`/`Body4`), separate anchor brackets.
|
||||||
- Front shell/flange/hex-post faces have the shared `Opaque(245,245,246)` appearance and the expected geometric continuity. The report measures those **face subsets**, excluding plastic, contacts, PCB and ICs. There is no separate hollow metal shell solid from which to certify wall thickness.
|
- Front shell/flange/hex-post faces have the shared `Opaque(245,245,246)` appearance and the expected geometric continuity. The report measures those **face subsets**, excluding plastic, contacts, PCB and ICs. There is no separate hollow metal shell solid from which to certify wall thickness.
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
# Provisional carrier module footprints
|
# Provisional carrier module footprints
|
||||||
|
|
||||||
These project-local footprints are in `Carrier.pretty`. They are mechanical
|
Paths in prose point to the retained hardware artifacts or neighboring development notes.
|
||||||
|
Commands keep their stated working directory; repository-root paths remain root-relative.
|
||||||
|
|
||||||
|
These project-local footprints are in `../../../../hardware/PCB/Carrier.pretty`. They are mechanical
|
||||||
starting points, **not fabrication-ready connector selections**. Both are now
|
starting points, **not fabrication-ready connector selections**. Both are now
|
||||||
assigned to their matching symbols in the native schematic through the project
|
assigned to their matching symbols in the native schematic through the project
|
||||||
library tables and used in the routed PCB draft. The assembled module 3D models
|
library tables and used in the routed PCB draft. The assembled module 3D models
|
||||||
@@ -37,7 +40,7 @@ silently reverse the numbering.
|
|||||||
|
|
||||||
Origin: top-left corner of the module PCB, viewed from the front/display side,
|
Origin: top-left corner of the module PCB, viewed from the front/display side,
|
||||||
header at the top. User-verified geometry from the current request and the later
|
header at the top. User-verified geometry from the current request and the later
|
||||||
mechanical-evidence section of `README.md`:
|
mechanical-evidence section of `../../../../hardware/PCB/README.md`:
|
||||||
|
|
||||||
| Pad | Signal | X | Y |
|
| Pad | Signal | X | Y |
|
||||||
|---|---|---:|---:|
|
|---|---|---:|---:|
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
# Compact two-layer PCB — A1 draft
|
# Compact two-layer PCB — A1 draft
|
||||||
|
|
||||||
Open **`serial-carrier.kicad_pcb`** in KiCad 10. The routed board belongs to the
|
Paths in prose point to the retained hardware artifacts or neighboring development notes.
|
||||||
existing `serial-carrier.kicad_pro` / `.kicad_sch` project. **Not for fabrication.**
|
Commands keep their stated working directory; repository-root paths remain root-relative.
|
||||||
|
|
||||||
|
Open **`../../../../hardware/PCB/serial-carrier.kicad_pcb`** in KiCad 10. The routed board belongs to the
|
||||||
|
existing `../../../../hardware/PCB/serial-carrier.kicad_pro` / `../../../../hardware/PCB/serial-carrier.kicad_sch` project. **Not for fabrication.**
|
||||||
No Gerbers, production drills or manufacturing archive were generated.
|
No Gerbers, production drills or manufacturing archive were generated.
|
||||||
|
|
||||||
## Size and placement
|
## Size and placement
|
||||||
@@ -35,7 +38,7 @@ component body corners.
|
|||||||
| H4 | (78, 76) | Proposed case mounting hole |
|
| H4 | (78, 76) | Proposed case mounting hole |
|
||||||
|
|
||||||
Outline vertices: (36,0), (82,0), (82,80), (0,80), (0,19), (36,19), closed.
|
Outline vertices: (36,0), (82,0), (82,80), (0,80), (0,19), (36,19), closed.
|
||||||
The previous A0 board is retained in `validation/pcb-a0-before-reposition.kicad_pcb`.
|
The previous A0 board is retained in `../../../../hardware/PCB/validation/pcb-a0-before-reposition.kicad_pcb`.
|
||||||
The board area is **58.76 cm²**; its bounding rectangle is 65.6 cm².
|
The board area is **58.76 cm²**; its bounding rectangle is 65.6 cm².
|
||||||
|
|
||||||
H1–H4 are proposed **Ø3.2 mm NPTH M3 clearance holes**, not a previously agreed
|
H1–H4 are proposed **Ø3.2 mm NPTH M3 clearance holes**, not a previously agreed
|
||||||
@@ -108,7 +111,7 @@ holes. The source models Ø3.000 mm bores but **no threads**; this does not esta
|
|||||||
M3 or 4-40 hardware. The main metal flange is 30.800 × 12.550 × 0.400 mm; the
|
M3 or 4-40 hardware. The main metal flange is 30.800 × 12.550 × 0.400 mm; the
|
||||||
shell envelope including its root/lip is 16.878 × 9.100 × 5.800 mm.
|
shell envelope including its root/lip is 16.878 × 9.100 × 5.800 mm.
|
||||||
|
|
||||||
`add_db9_envelope.py` places the measured metal bounding projections and screw-lock
|
`../../../../hardware/PCB/add_db9_envelope.py` places the measured metal bounding projections and screw-lock
|
||||||
axes on **Dwgs.User**, relative to U2. They are not silkscreen, a courtyard or a
|
axes on **Dwgs.User**, relative to U2. They are not silkscreen, a courtyard or a
|
||||||
case cutout. Run the helper after moving U2; it preserves copper and project
|
case cutout. Run the helper after moving U2; it preserves copper and project
|
||||||
settings. The original STEP and its extraction/validation scripts are retained.
|
settings. The original STEP and its extraction/validation scripts are retained.
|
||||||
@@ -171,11 +174,11 @@ warning. PDF and both previews have been refreshed.
|
|||||||
|
|
||||||
## Review outputs
|
## Review outputs
|
||||||
|
|
||||||
- `validation/pcb-draft.svg` / `pcb-draft.png`: combined copper/outline/fab review.
|
- `../../../../hardware/PCB/validation/pcb-draft.svg` / `../../../../hardware/PCB/validation/pcb-draft.png`: combined copper/outline/fab review.
|
||||||
- `validation/pcb-draft-3d.png`: KiCad render of the carrier and available standard
|
- `../../../../hardware/PCB/validation/pcb-draft-3d.png`: KiCad render of the carrier and available standard
|
||||||
component models. **ESP32, OLED and RS-232 assembled module models are absent**;
|
component models. **ESP32, OLED and RS-232 assembled module models are absent**;
|
||||||
the render is not an assembled-device or enclosure fit check.
|
the render is not an assembled-device or enclosure fit check.
|
||||||
- `pcb-placement-draft.pdf`: **1:1** front placement/pad/outline drawing. Print at
|
- `../../../../hardware/PCB/pcb-placement-draft.pdf`: **1:1** front placement/pad/outline drawing. Print at
|
||||||
100%, disable fit-to-page, and confirm the outer width is 82 mm before use.
|
100%, disable fit-to-page, and confirm the outer width is 82 mm before use.
|
||||||
It includes the known module outlines and nominal STEP-derived DE-9 metal
|
It includes the known module outlines and nominal STEP-derived DE-9 metal
|
||||||
bounding projections; no guessed U1 body or case-wall geometry.
|
bounding projections; no guessed U1 body or case-wall geometry.
|
||||||
@@ -194,7 +197,7 @@ PCB pad assignments, component linkage, dimensions, layer count, nominal thickne
|
|||||||
mounting holes, track/via sizes and KiCad DRC. It never rewrites the PCB or project.
|
mounting holes, track/via sizes and KiCad DRC. It never rewrites the PCB or project.
|
||||||
It reports the single expected U2 parity warning explicitly, and fails on any
|
It reports the single expected U2 parity warning explicitly, and fails on any
|
||||||
other parity issue, DRC violation or unconnected item. Reports are retained in
|
other parity issue, DRC violation or unconnected item. Reports are retained in
|
||||||
`validation/pcb-parity-drc.json` and `pcb-validation-summary.json`.
|
`../../../../hardware/PCB/validation/pcb-parity-drc.json` and `../../../../hardware/PCB/validation/pcb-validation-summary.json`.
|
||||||
|
|
||||||
The second independently checks continuous copper/hole/edge geometry and original
|
The second independently checks continuous copper/hole/edge geometry and original
|
||||||
placement/net/UUID preservation against the initial unrouted snapshot. It needs
|
placement/net/UUID preservation against the initial unrouted snapshot. It needs
|
||||||
@@ -222,7 +225,7 @@ complete capability set. No electrical/hardware/fabrication tests were performed
|
|||||||
## Regeneration and project settings
|
## Regeneration and project settings
|
||||||
|
|
||||||
Normal use is to **edit the native PCB in KiCad**, not regenerate it. The optional
|
Normal use is to **edit the native PCB in KiCad**, not regenerate it. The optional
|
||||||
`generate_pcb_draft.py` recreates the *unrouted* A1 placement and discards manual
|
`../../../../hardware/PCB/generate_pcb_draft.py` recreates the *unrouted* A1 placement and discards manual
|
||||||
PCB work only with explicit `--overwrite`; it refuses an open-project lock. A1 was
|
PCB work only with explicit `--overwrite`; it refuses an open-project lock. A1 was
|
||||||
made by mutating the existing board, not running that generator. All A1 saves use
|
made by mutating the existing board, not running that generator. All A1 saves use
|
||||||
`SaveBoard(..., True)` to preserve user project settings. The original A0 creation
|
`SaveBoard(..., True)` to preserve user project settings. The original A0 creation
|
||||||
@@ -230,7 +233,7 @@ populated the minimal project with rules; this revision leaves those settings an
|
|||||||
the schematic unchanged. Refresh device silkscreen and CAD envelope helpers after
|
the schematic unchanged. Refresh device silkscreen and CAD envelope helpers after
|
||||||
any intentional regeneration or placement/routing changes.
|
any intentional regeneration or placement/routing changes.
|
||||||
|
|
||||||
`route_pcb_draft.py` requires NumPy and pcbnew. By default it never saves; replacing
|
`../../../../hardware/PCB/route_pcb_draft.py` requires NumPy and pcbnew. By default it never saves; replacing
|
||||||
existing tracks requires explicit `--route --overwrite --replace-routes`. Do not
|
existing tracks requires explicit `--route --overwrite --replace-routes`. Do not
|
||||||
run that over manual routing without a backup. It is a bounded initial-draft
|
run that over manual routing without a backup. It is a bounded initial-draft
|
||||||
router, **not** a general-purpose autorouter. See `validation/pcb-routing-summary.md`
|
router, **not** a general-purpose autorouter. See `validation/pcb-routing-summary.md`
|
||||||
+7
-4
@@ -1,7 +1,10 @@
|
|||||||
# MAX3243 carrier — PROVISIONAL female CAD reference / VERIFY MALE
|
# MAX3243 carrier — PROVISIONAL female CAD reference / VERIFY MALE
|
||||||
|
|
||||||
|
Paths in prose point to the retained hardware artifacts or neighboring development notes.
|
||||||
|
Commands keep their stated working directory; repository-root paths remain root-relative.
|
||||||
|
|
||||||
Footprint: `Carrier:MAX3243_Reference_Provisional` in
|
Footprint: `Carrier:MAX3243_Reference_Provisional` in
|
||||||
`Carrier.pretty/MAX3243_Reference_Provisional.kicad_mod`.
|
`../../../../hardware/PCB/Carrier.pretty/MAX3243_Reference_Provisional.kicad_mod`.
|
||||||
Intended only for a <=100 × 100 mm, two-copper-layer carrier PCB draft.
|
Intended only for a <=100 × 100 mm, two-copper-layer carrier PCB draft.
|
||||||
No schematic assignment or edits are made. This footprint does not establish
|
No schematic assignment or edits are made. This footprint does not establish
|
||||||
whole-board fit, connector access, DRC compliance, or fabrication readiness.
|
whole-board fit, connector access, DRC compliance, or fabrication readiness.
|
||||||
@@ -16,7 +19,7 @@ release. Never infer header order from DE-9 gender or mating-face numbering.
|
|||||||
|
|
||||||
Source: [Adafruit PCB repository](https://github.com/adafruit/Adafruit-RS-232-Level-Shifter-Breakout-PCB),
|
Source: [Adafruit PCB repository](https://github.com/adafruit/Adafruit-RS-232-Level-Shifter-Breakout-PCB),
|
||||||
commit `63c6200bb3ef17d491089cfa6441b1810d79bb20`.
|
commit `63c6200bb3ef17d491089cfa6441b1810d79bb20`.
|
||||||
Local original: `reference/Adafruit RS-232 Full Pinout Level-Shifter Breakout.brd`;
|
Local original: `../../../../hardware/PCB/reference/Adafruit RS-232 Full Pinout Level-Shifter Breakout.brd`;
|
||||||
SHA-256 `6a7b35ef909f5a2d243c326c34db3957aaa01275bf2aac8e0c749ff18162e870`
|
SHA-256 `6a7b35ef909f5a2d243c326c34db3957aaa01275bf2aac8e0c749ff18162e870`
|
||||||
was rechecked against the file. See `adafruit-research.md` for provenance and
|
was rechecked against the file. See `adafruit-research.md` for provenance and
|
||||||
female/male distinctions.
|
female/male distinctions.
|
||||||
@@ -25,7 +28,7 @@ CAD-derived geometry adapted from the design by **Limor Fried/Ladyada for Adafru
|
|||||||
Industries**, under **CC BY-SA 3.0 Unported**:
|
Industries**, under **CC BY-SA 3.0 Unported**:
|
||||||
https://creativecommons.org/licenses/by-sa/3.0/ . The CAD-derived footprint and
|
https://creativecommons.org/licenses/by-sa/3.0/ . The CAD-derived footprint and
|
||||||
these CAD-derived notes use that license. Preserve the full upstream notices in
|
these CAD-derived notes use that license. Preserve the full upstream notices in
|
||||||
`reference/README.md` and `reference/license.txt` when redistributing this adaptation.
|
`../../../../hardware/PCB/reference/README.md` and `../../../../hardware/PCB/reference/license.txt` when redistributing this adaptation.
|
||||||
Changes: coordinate transformation, carrier pad convention, NPTH carrier holes,
|
Changes: coordinate transformation, carrier pad convention, NPTH carrier holes,
|
||||||
and provisional annotations; no module circuitry or connector package copied.
|
and provisional annotations; no module circuitry or connector package copied.
|
||||||
|
|
||||||
@@ -114,6 +117,6 @@ verifies radius, angle and centre; it passed without changing geometry. The
|
|||||||
binding emitted the same three startup `PROPERTY_ENUM` assertions documented for
|
binding emitted the same three startup `PROPERTY_ENUM` assertions documented for
|
||||||
other module validation; these did not prevent loading or successful checks.
|
other module validation; these did not prevent loading or successful checks.
|
||||||
No hardware fit test, male electrical verification, board DRC or firmware build
|
No hardware fit test, male electrical verification, board DRC or firmware build
|
||||||
was performed. Existing `validate_footprints.py` was not changed and does not
|
was performed. Existing `../../../../hardware/PCB/validate_footprints.py` was not changed and does not
|
||||||
cover this new footprint. Only this notes file and the new footprint were written;
|
cover this new footprint. Only this notes file and the new footprint were written;
|
||||||
schematic, project and lock files were left untouched.
|
schematic, project and lock files were left untouched.
|
||||||
+26
-23
@@ -1,6 +1,9 @@
|
|||||||
# A1 top-left-notch / bottom-USB routing — current result
|
# A1 top-left-notch / bottom-USB routing — current result
|
||||||
|
|
||||||
Date: 2026-09-21. Actual revised board: `../serial-carrier.kicad_pcb`.
|
Paths in prose point to the retained hardware artifacts or neighboring development notes.
|
||||||
|
Commands keep their stated working directory; repository-root paths remain root-relative.
|
||||||
|
|
||||||
|
Date: 2026-09-21. Actual revised board: `../../../../../hardware/PCB/serial-carrier.kicad_pcb`.
|
||||||
|
|
||||||
**38/38 connections across 17 multi-pad nets; 0 physical DRC violations,
|
**38/38 connections across 17 multi-pad nets; 0 physical DRC violations,
|
||||||
0 unconnected items; exactly 1 expected U2 footprint parity warning.**
|
0 unconnected items; exactly 1 expected U2 footprint parity warning.**
|
||||||
@@ -27,9 +30,9 @@ Coordinates below are local to absolute origin (100,100) mm; all dimensions mm.
|
|||||||
both layers; no tracks, vias or fills inside it. Boundary remains provisional.
|
both layers; no tracks, vias or fills inside it. Boundary remains provisional.
|
||||||
|
|
||||||
The existing A0 board was mutated, **not regenerated**. The one-time
|
The existing A0 board was mutated, **not regenerated**. The one-time
|
||||||
`pcb-a0-before-reposition.kicad_pcb` backup remains byte-for-byte intact.
|
`../../../../../hardware/PCB/validation/pcb-a0-before-reposition.kicad_pcb` backup remains byte-for-byte intact.
|
||||||
`pcb-routing-input.kicad_pcb` is now the A1 unrouted audit input, not A0.
|
`../../../../../hardware/PCB/validation/pcb-routing-input.kicad_pcb` is now the A1 unrouted audit input, not A0.
|
||||||
`pcb-routing-preservation.py` verified all 13 complete serialized footprints
|
`../../../../../hardware/PCB/validation/pcb-routing-preservation.py` verified all 13 complete serialized footprints
|
||||||
identical except the four authorized top-level placements, including schematic
|
identical except the four authorized top-level placements, including schematic
|
||||||
fields, pad/net definitions, electrical UUID paths and all footprint graphics.
|
fields, pad/net definitions, electrical UUID paths and all footprint graphics.
|
||||||
Non-owned board-graphic UUIDs and unrelated graphics are preserved. ESP/RS/RF
|
Non-owned board-graphic UUIDs and unrelated graphics are preserved. ESP/RS/RF
|
||||||
@@ -56,7 +59,7 @@ Two bounded passes, each with 240 seconds / 1,200,000 expansions per search:
|
|||||||
|
|
||||||
1. 31.18 seconds, 38/38, 674 segments / 16 vias, physical DRC clean. One GND
|
1. 31.18 seconds, 38/38, 674 segments / 16 vias, physical DRC clean. One GND
|
||||||
connection required the router's 0.25 mm fallback; strict width validation
|
connection required the router's 0.25 mm fallback; strict width validation
|
||||||
correctly failed. Retained as `pcb-routing-a1-pass1.kicad_pcb` for evidence.
|
correctly failed. Retained as `../../../../../hardware/PCB/validation/pcb-routing-a1-pass1.kicad_pcb` for evidence.
|
||||||
2. 32.44 seconds, 38/38, 565 segments / 22 vias. Short GND connections (<=15 mm)
|
2. 32.44 seconds, 38/38, 565 segments / 22 vias. Short GND connections (<=15 mm)
|
||||||
now route before longer signal fanout; long GND links remain last. All power
|
now route before longer signal fanout; long GND links remain last. All power
|
||||||
and ground now use 0.5 mm; no fallback. This is the saved current result.
|
and ground now use 0.5 mm; no fallback. This is the saved current result.
|
||||||
@@ -92,12 +95,12 @@ Rerouting remains explicitly destructive to existing/manual copper and requires:
|
|||||||
Then rerun the validators. The one-shot `pcb-routing-reposition.py --apply`
|
Then rerun the validators. The one-shot `pcb-routing-reposition.py --apply`
|
||||||
refuses anything except the original A0 snapshot and must **not** be rerun on A1.
|
refuses anything except the original A0 snapshot and must **not** be rerun on A1.
|
||||||
|
|
||||||
Current evidence: `pcb-routing-a1-pass2.log`, `pcb-routing-run.json`,
|
Current evidence: `../../../../../hardware/PCB/validation/pcb-routing-a1-pass2.log`, `../../../../../hardware/PCB/validation/pcb-routing-run.json`,
|
||||||
`pcb-routing-drc.json`, `pcb-routing-all-drc.json`, `pcb-parity-drc.json`,
|
`../../../../../hardware/PCB/validation/pcb-routing-drc.json`, `../../../../../hardware/PCB/validation/pcb-routing-all-drc.json`, `../../../../../hardware/PCB/validation/pcb-parity-drc.json`,
|
||||||
`pcb-validation-summary.json`, `pcb-routing-check.json`,
|
`../../../../../hardware/PCB/validation/pcb-validation-summary.json`, `../../../../../hardware/PCB/validation/pcb-routing-check.json`,
|
||||||
`pcb-routing-reposition.json`, and `pcb-routing-preservation.json`.
|
`../../../../../hardware/PCB/validation/pcb-routing-reposition.json`, and `../../../../../hardware/PCB/validation/pcb-routing-preservation.json`.
|
||||||
The current post-integration board SHA-256 is recorded in
|
The current post-integration board SHA-256 is recorded in
|
||||||
`pcb-routing-check.json`; the CAD projection additions do not change copper.
|
`../../../../../hardware/PCB/validation/pcb-routing-check.json`; the CAD projection additions do not change copper.
|
||||||
|
|
||||||
**Still a draft, not fabrication-ready.** ESP32 geometry/antenna/USB access,
|
**Still a draft, not fabrication-ready.** ESP32 geometry/antenna/USB access,
|
||||||
module pinout/header fit, full assembly/height clearances, power integrity and
|
module pinout/header fit, full assembly/height clearances, power integrity and
|
||||||
@@ -118,7 +121,7 @@ Date: 2026-09-20. KiCad 10.0.6; system `/usr/bin/python3`.
|
|||||||
|
|
||||||
## Saved result
|
## Saved result
|
||||||
|
|
||||||
Actual copper routing is saved in `../serial-carrier.kicad_pcb`, not just a preview
|
Actual copper routing is saved in `../../../../../hardware/PCB/serial-carrier.kicad_pcb`, not just a preview
|
||||||
or an exported proposal. **38/38 required spanning-tree connections across 17
|
or an exported proposal. **38/38 required spanning-tree connections across 17
|
||||||
multi-pad nets routed; KiCad reports 0 unconnected items, 0 errors, 0 warnings.**
|
multi-pad nets routed; KiCad reports 0 unconnected items, 0 errors, 0 warnings.**
|
||||||
Intentional single-pad `unconnected-(...)` nets retain their original names and
|
Intentional single-pad `unconnected-(...)` nets retain their original names and
|
||||||
@@ -155,7 +158,7 @@ exclusions were added or changed. Existing project rules specify 0.25 mm minimum
|
|||||||
clearance, 0.5 mm copper-edge clearance, 0.25 mm minimum track width and 0.7 mm
|
clearance, 0.5 mm copper-edge clearance, 0.25 mm minimum track width and 0.7 mm
|
||||||
minimum via diameter.
|
minimum via diameter.
|
||||||
|
|
||||||
Independent continuous-geometry audit (`pcb-routing-check.json`):
|
Independent continuous-geometry audit (`../../../../../hardware/PCB/validation/pcb-routing-check.json`):
|
||||||
|
|
||||||
| Check | Measured minimum | Requirement |
|
| Check | Measured minimum | Requirement |
|
||||||
|---|---:|---:|
|
|---|---:|---:|
|
||||||
@@ -179,11 +182,11 @@ with the input board. Subsequent integration also ran fresh schematic-parity DRC
|
|||||||
and added missing symbol metadata fields to the PCB without changing copper.
|
and added missing symbol metadata fields to the PCB without changing copper.
|
||||||
That check reports exactly one expected U2 footprint warning (provisional PCB
|
That check reports exactly one expected U2 footprint warning (provisional PCB
|
||||||
footprint versus intentionally blank schematic assignment); no net mismatch.
|
footprint versus intentionally blank schematic assignment); no net mismatch.
|
||||||
See `pcb-parity-drc.json` and `pcb-validation-summary.json`.
|
See `../../../../../hardware/PCB/validation/pcb-parity-drc.json` and `../../../../../hardware/PCB/validation/pcb-validation-summary.json`.
|
||||||
|
|
||||||
## Method and bounded passes
|
## Method and bounded passes
|
||||||
|
|
||||||
`../route_pcb_draft.py` uses a two-layer 8-direction raster A* search, an explicit
|
`../../../../../hardware/PCB/route_pcb_draft.py` uses a two-layer 8-direction raster A* search, an explicit
|
||||||
via cost, exact-pad-centre terminal stubs, conservative foreign-copper and hole
|
via cost, exact-pad-centre terminal stubs, conservative foreign-copper and hole
|
||||||
obstacles, and only collinear path compression. It preserves the L-shaped edge
|
obstacles, and only collinear path compression. It preserves the L-shaped edge
|
||||||
and RF reservation. Per-net minimum spanning trees connect every physical pad,
|
and RF reservation. Per-net minimum spanning trees connect every physical pad,
|
||||||
@@ -232,18 +235,18 @@ the script exit status.
|
|||||||
|
|
||||||
## Evidence and remaining limitations
|
## Evidence and remaining limitations
|
||||||
|
|
||||||
- `pcb-routing-input.kicad_pcb`: unrouted input snapshot, after baseline DRC save.
|
- `../../../../../hardware/PCB/validation/pcb-routing-input.kicad_pcb`: unrouted input snapshot, after baseline DRC save.
|
||||||
- `pcb-routing-baseline.json`: 38 unconnected / no violations before routing.
|
- `../../../../../hardware/PCB/validation/pcb-routing-baseline.json`: 38 unconnected / no violations before routing.
|
||||||
- `pcb-routing-pass1-drc.json`, `pcb-routing-pass2-drc.json`: intermediate DRC.
|
- `../../../../../hardware/PCB/validation/pcb-routing-pass1-drc.json`, `../../../../../hardware/PCB/validation/pcb-routing-pass2-drc.json`: intermediate DRC.
|
||||||
- `pcb-routing-pass2.log`, `pcb-routing-pass3.log`: bounded routing progress.
|
- `../../../../../hardware/PCB/validation/pcb-routing-pass2.log`, `../../../../../hardware/PCB/validation/pcb-routing-pass3.log`: bounded routing progress.
|
||||||
- `pcb-routing-run.json`: final saved-route metrics and connection list.
|
- `../../../../../hardware/PCB/validation/pcb-routing-run.json`: final saved-route metrics and connection list.
|
||||||
- `pcb-routing-drc.json`, `pcb-routing-all-drc.json`: final KiCad DRC evidence.
|
- `../../../../../hardware/PCB/validation/pcb-routing-drc.json`, `../../../../../hardware/PCB/validation/pcb-routing-all-drc.json`: final KiCad DRC evidence.
|
||||||
- `pcb-routing-check.py`, `pcb-routing-check.json`: repeatable independent audit.
|
- `../../../../../hardware/PCB/validation/pcb-routing-check.py`, `../../../../../hardware/PCB/validation/pcb-routing-check.json`: repeatable independent audit.
|
||||||
- `pcb-routing-dry-run.log`: successful final no-save reproduction.
|
- `../../../../../hardware/PCB/validation/pcb-routing-dry-run.log`: successful final no-save reproduction.
|
||||||
|
|
||||||
Routing-only DRC-saved snapshot SHA-256 (before integration copied schematic
|
Routing-only DRC-saved snapshot SHA-256 (before integration copied schematic
|
||||||
metadata fields): `bf94b6c9b00c43a97f933054758a2c9d6541981ae09f2f285dcdb567bd73d9b1`.
|
metadata fields): `bf94b6c9b00c43a97f933054758a2c9d6541981ae09f2f285dcdb567bd73d9b1`.
|
||||||
The current post-integration hash is recorded by `pcb-routing-check.json`;
|
The current post-integration hash is recorded by `../../../../../hardware/PCB/validation/pcb-routing-check.json`;
|
||||||
geometry, pad-net linkage and copper are unchanged by the metadata copy.
|
geometry, pad-net linkage and copper are unchanged by the metadata copy.
|
||||||
|
|
||||||
**Still DRAFT / NOT FOR FABRICATION.** Provisional module/connector geometry,
|
**Still DRAFT / NOT FOR FABRICATION.** Provisional module/connector geometry,
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
# Bounded ordinary HTTPS idle retention
|
# Bounded ordinary HTTPS idle retention
|
||||||
|
|
||||||
|
Source/build/test paths in code spans and commands are relative to the repository root
|
||||||
|
unless a different working directory is stated; Markdown links are relative to this document.
|
||||||
|
|
||||||
Current owner/SDK contract. The user accepted idle cleanup as working; [overall acceptance](roadmap.md#phase8-acceptance-evidence) records current status and evidence limits. No individual unreported soak, fault or reserve result is inferred.
|
Current owner/SDK contract. The user accepted idle cleanup as working; [overall acceptance](roadmap.md#phase8-acceptance-evidence) records current status and evidence limits. No individual unreported soak, fault or reserve result is inferred.
|
||||||
|
|
||||||
## Evidence and scope
|
## Evidence and scope
|
||||||
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
@@ -1,5 +1,8 @@
|
|||||||
# Implementation roadmap
|
# Implementation roadmap
|
||||||
|
|
||||||
|
Source/build/test paths in code spans and commands are relative to the repository root
|
||||||
|
unless a different working directory is stated; Markdown links are relative to this document.
|
||||||
|
|
||||||
This document tracks the implementation and hardware-validation plan for the ESP32 Serial Swiss Army Knife. It describes intended sequencing rather than a release schedule. Completion requires implementation, documentation and build validation, plus relevant hardware tests or an explicitly recorded user waiver. A waiver is not a hardware-test pass.
|
This document tracks the implementation and hardware-validation plan for the ESP32 Serial Swiss Army Knife. It describes intended sequencing rather than a release schedule. Completion requires implementation, documentation and build validation, plus relevant hardware tests or an explicitly recorded user waiver. A waiver is not a hardware-test pass.
|
||||||
|
|
||||||
## Status legend
|
## Status legend
|
||||||
@@ -58,7 +61,7 @@ Implemented and hardware-validated:
|
|||||||
- RGB status indication for diagnostic state.
|
- RGB status indication for diagnostic state.
|
||||||
- Phase 0 hardware diagnostics, including signal status, are isolated under the `debug` CLI submenu.
|
- Phase 0 hardware diagnostics, including signal status, are isolated under the `debug` CLI submenu.
|
||||||
|
|
||||||
Detailed procedures are in [Electrical tests](electrical_tests.md) and [Hardware wiring](wiring.md).
|
Detailed procedures are in [Electrical tests](../electrical_tests.md) and [Hardware wiring](../wiring.md).
|
||||||
|
|
||||||
### Phase 1 — Serial-service foundation
|
### Phase 1 — Serial-service foundation
|
||||||
|
|
||||||
@@ -166,7 +169,7 @@ Implemented and target-hardware validated:
|
|||||||
- Confirmation-protected local controls for service lifecycle, Wi-Fi reconnect/profile rotation, writer release, display off, and reboot; the UI cannot assign a writer or edit secrets/configuration.
|
- Confirmation-protected local controls for service lifecycle, Wi-Fi reconnect/profile rotation, writer release, display off, and reboot; the UI cannot assign a writer or edit secrets/configuration.
|
||||||
- Persistent dim/off settings, bounded boot animation, consumed wake presses, stuck-button quarantine, and one-shot confirmations.
|
- Persistent dim/off settings, bounded boot animation, consumed wake presses, stuck-button quarantine, and one-shot confirmations.
|
||||||
- Missing, disconnected, or unresponsive display hardware remains nonfatal and can recover through one bounded reprobe.
|
- Missing, disconnected, or unresponsive display hardware remains nonfatal and can recover through one bounded reprobe.
|
||||||
- Concurrent serial, UART0, USB, WebSocket, SSH, and injected display/button fault behavior validated. See [Electrical tests](electrical_tests.md) and [Hardware wiring](wiring.md).
|
- Concurrent serial, UART0, USB, WebSocket, SSH, and injected display/button fault behavior validated. See [Electrical tests](../electrical_tests.md) and [Hardware wiring](../wiring.md).
|
||||||
|
|
||||||
### Phase 8 — Role-based users and administrative access — Complete
|
### Phase 8 — Role-based users and administrative access — Complete
|
||||||
|
|
||||||
@@ -241,7 +244,7 @@ Small implementation:
|
|||||||
|
|
||||||
- Keep the baseline PlatformIO/ESP-IDF and managed-library versions and upstream sources. No generated dependency patches, custom SSH state machine, crypto-policy replacement, allocator hooks, or SDK migration in this phase.
|
- Keep the baseline PlatformIO/ESP-IDF and managed-library versions and upstream sources. No generated dependency patches, custom SSH state machine, crypto-policy replacement, allocator hooks, or SDK migration in this phase.
|
||||||
- Explicitly disable core dumps and select silent panic reboot through standard `sdkconfig.defaults` options. Keep ordinary logging; reduced crash diagnostics are a deliberate trade-off. Existing generated configurations must be checked because defaults do not override saved selections.
|
- Explicitly disable core dumps and select silent panic reboot through standard `sdkconfig.defaults` options. Keep ordinary logging; reduced crash diagnostics are a deliberate trade-off. Existing generated configurations must be checked because defaults do not override saved selections.
|
||||||
- Follow the [operational checklist](security_operations.md): unique credentials, trusted identity verification, no Internet forwarding, networking off/power off after use, and existing rotation/recovery procedures.
|
- Follow the [operational checklist](../security_operations.md): unique credentials, trusted identity verification, no Internet forwarding, networking off/power off after use, and existing rotation/recovery procedures.
|
||||||
- Review upstream maintenance releases occasionally and before planned upgrades. Evaluate reachable issues against actual use; any stock dependency upgrade is a separate build/device-tested task, not a reason to accumulate local backports. Preserve existing license notices.
|
- Review upstream maintenance releases occasionally and before planned upgrades. Evaluate reachable issues against actual use; any stock dependency upgrade is a separate build/device-tested task, not a reason to accumulate local backports. Preserve existing license notices.
|
||||||
|
|
||||||
**Excluded:** secure boot, NVS/flash/PSRAM encryption, eFuse changes, partition changes, exhaustive CVE/zeroization audits, new authentication throttling, and production certification. Existing authentication, session isolation and recovery behavior remain unchanged. There is no protection against physical extraction or replacement of firmware. Phase 10 uses administrator-selected firmware, without a separate image-signing system.
|
**Excluded:** secure boot, NVS/flash/PSRAM encryption, eFuse changes, partition changes, exhaustive CVE/zeroization audits, new authentication throttling, and production certification. Existing authentication, session isolation and recovery behavior remain unchanged. There is no protection against physical extraction or replacement of firmware. Phase 10 uses administrator-selected firmware, without a separate image-signing system.
|
||||||
@@ -272,8 +275,8 @@ Implemented scope:
|
|||||||
|
|
||||||
#### Phase10 update and recovery
|
#### Phase10 update and recovery
|
||||||
|
|
||||||
1. Keep **USB-to-UART** available for UART0 administration/flashing; native USB CDC is network-independent UART1 access, not administration. Install the updater by wire first: older firmware without the route cannot install its own first web updater. The custom OTA layout is required; [one-time old-layout migration](../README.md#one-time-migration-from-the-default-partition-table) is separate and destructive.
|
1. Keep **USB-to-UART** available for UART0 administration/flashing; native USB CDC is network-independent UART1 access, not administration. Install the updater by wire first: older firmware without the route cannot install its own first web updater. The custom OTA layout is required; [one-time old-layout migration](../getting_started.md#partition-migration-and-erasing) is separate and destructive.
|
||||||
2. Build trusted, schema-compatible `esp32-s3-devkitc-1-n16r8` firmware with `pio run`; choose **`.pio/build/esp32-s3-devkitc-1-n16r8/firmware.bin`**, never a merged image or a renamed non-application file. Observe the [downgrade warning](../README.md#legacy-credential-removal).
|
2. Build trusted, schema-compatible `esp32-s3-devkitc-1-n16r8` firmware with `pio run`; choose **`.pio/build/esp32-s3-devkitc-1-n16r8/firmware.bin`**, never a merged image or a renamed non-application file. Observe the [downgrade warning](#phase8-legacy-credential-compatibility).
|
||||||
3. Save desired RAM-only settings and record nonsecret configuration/public HTTPS/SSH fingerprints. Arrange a maintenance window and stable power/network; sign in as `admin` over trusted HTTPS, open **Settings → HTTPS / Reboot → Firmware update**, choose the file and confirm **Upload and reboot…**.
|
3. Save desired RAM-only settings and record nonsecret configuration/public HTTPS/SSH fingerprints. Arrange a maintenance window and stable power/network; sign in as `admin` over trusted HTTPS, open **Settings → HTTPS / Reboot → Firmware update**, choose the file and confirm **Upload and reboot…**.
|
||||||
4. Wait for validation/reboot, not just 100% transmitted bytes. Restore network reachability, reload and explicitly sign in; verify the running application, saved configuration and identities before another upload.
|
4. Wait for validation/reboot, not just 100% transmitted bytes. Restore network reachability, reload and explicitly sign in; verify the running application, saved configuration and identities before another upload.
|
||||||
5. A lost response, timeout, page close, sign-out or browser abort proves neither cancellation nor failure. The UI locks uncertain outcomes without replay; reload clears only browser locks, not the device latch. Inspect through UART0 and deliberately reboot if appropriate: any later reset can boot an already selected image.
|
5. A lost response, timeout, page close, sign-out or browser abort proves neither cancellation nor failure. The UI locks uncertain outcomes without replay; reload clears only browser locks, not the device latch. Inspect through UART0 and deliberately reboot if appropriate: any later reset can boot an already selected image.
|
||||||
@@ -314,7 +317,7 @@ The device is not intended to become a general-purpose router. Captive-portal in
|
|||||||
- mDNS 1.12.0 exposes no readiness acknowledgement and can silently drop queued family actions. Absent families are disabled on each one-second owner pass; available families are re-enabled/reprobed on a 30-second repair cadence. Upstream synchronous calls can delay this cadence. There can be a transient stale/zero-A response window after IPv4 loss before disable is processed. AAAA records follow upstream **valid-address** semantics, including deprecated-but-still-valid addresses, not preferred-only filtering. Client caches expire independently.
|
- mDNS 1.12.0 exposes no readiness acknowledgement and can silently drop queued family actions. Absent families are disabled on each one-second owner pass; available families are re-enabled/reprobed on a 30-second repair cadence. Upstream synchronous calls can delay this cadence. There can be a transient stale/zero-A response window after IPv4 loss before disable is processed. AAAA records follow upstream **valid-address** semantics, including deprecated-but-still-valid addresses, not preferred-only filtering. Client caches expire independently.
|
||||||
- `cmake/mdns_membership.*` applies one version/hash-guarded, build-local source overlay to mDNS 1.12.0: balance per-family multicast leaves and unwind joins after PCB creation failure. Managed sources and dependency versions remain unchanged. An upstream mismatch fails configuration for deliberate review; see `tests/mdns_membership/README.md` for maintenance/removal and regression evidence. This is not the abandoned Phase 9 patch set.
|
- `cmake/mdns_membership.*` applies one version/hash-guarded, build-local source overlay to mDNS 1.12.0: balance per-family multicast leaves and unwind joins after PCB creation failure. Managed sources and dependency versions remain unchanged. An upstream mismatch fails configuration for deliberate review; see `tests/mdns_membership/README.md` for maintenance/removal and regression evidence. This is not the abandoned Phase 9 patch set.
|
||||||
- `sdkconfig.defaults` explicitly enables IPv4/IPv6 and increases mDNS service capacity from one to two. Existing saved configurations override defaults: verify `CONFIG_LWIP_IPV4=y`, `CONFIG_LWIP_IPV6=y`, `CONFIG_MDNS_MAX_SERVICES=2` and STA-only predefined mDNS interfaces before building. The local N16R8 saved configuration was updated accordingly. No new task, transport socket, broker slot, serial buffer, partition, filesystem or generated web asset was added.
|
- `sdkconfig.defaults` explicitly enables IPv4/IPv6 and increases mDNS service capacity from one to two. Existing saved configurations override defaults: verify `CONFIG_LWIP_IPV4=y`, `CONFIG_LWIP_IPV6=y`, `CONFIG_MDNS_MAX_SERVICES=2` and STA-only predefined mDNS interfaces before building. The local N16R8 saved configuration was updated accordingly. No new task, transport socket, broker slot, serial buffer, partition, filesystem or generated web asset was added.
|
||||||
- The accepted baseline initially retained IPv4-only traceroute and limited family/scope selection. The later requested [dual-stack diagnostics follow-up](command_reference.md#dual-stack-network-diagnostics) adds `-4`/`-6` to ping/traceroute/nslookup across UART0, admin SSH and browser Admin, scoped IPv6 probes and explicit A+AAAA lookup. This follow-up is built/host-tested, not covered retroactively by the acceptance capture below. DHCPv6, infrastructure DNS registration of SLAAC addresses, and new IPv6 resolver provisioning remain outside scope.
|
- The accepted baseline initially retained IPv4-only traceroute and limited family/scope selection. The later requested [dual-stack diagnostics follow-up](../command_reference.md#dual-stack-network-diagnostics) adds `-4`/`-6` to ping/traceroute/nslookup across UART0, admin SSH and browser Admin, scoped IPv6 probes and explicit A+AAAA lookup. This follow-up is built/host-tested, not covered retroactively by the acceptance capture below. DHCPv6, infrastructure DNS registration of SLAAC addresses, and new IPv6 resolver provisioning remain outside scope.
|
||||||
|
|
||||||
#### Validation evidence and acceptance
|
#### Validation evidence and acceptance
|
||||||
|
|
||||||
@@ -375,7 +378,7 @@ Every phase should satisfy the following before being marked complete, with any
|
|||||||
6. Failure, disconnect, stop/start, reboot, and stale-session cleanup paths are exercised.
|
6. Failure, disconnect, stop/start, reboot, and stale-session cleanup paths are exercised.
|
||||||
7. Runtime memory, stack, socket, and queue bounds are measured under maximum supported concurrency.
|
7. Runtime memory, stack, socket, and queue bounds are measured under maximum supported concurrency.
|
||||||
8. Secrets remain absent from ordinary status output and logs.
|
8. Secrets remain absent from ordinary status output and logs.
|
||||||
9. [Command reference](command_reference.md), [Hardware wiring](wiring.md), electrical tests, and this roadmap are updated as applicable.
|
9. [Command reference](../command_reference.md), [Hardware wiring](../wiring.md), electrical tests, and this roadmap are updated as applicable.
|
||||||
10. New dependencies receive security, maintenance, and GPL-3.0 compatibility review.
|
10. New dependencies receive security, maintenance, and GPL-3.0 compatibility review.
|
||||||
|
|
||||||
## Explicitly deferred work
|
## Explicitly deferred work
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
# User administration and authentication tests
|
# User administration and authentication tests
|
||||||
|
|
||||||
|
Source/build/test paths in code spans and commands are relative to the repository root
|
||||||
|
unless a different working directory is stated; Markdown links are relative to this document.
|
||||||
|
|
||||||
Reusable procedures, **not execution evidence**. [Phase 8 is accepted](roadmap.md#phase8-acceptance-evidence); overall 8D.22 user sign-off does not assert that every checklist item below ran. Historical 8A/B sections describe their original baselines only; current cookie authentication, removed bootstrap/credentials and UART0-only recovery override them. Use current contracts and the integrated regression section for present behavior. Never log generated/entered passwords, cookies/tickets or private/verifier material.
|
Reusable procedures, **not execution evidence**. [Phase 8 is accepted](roadmap.md#phase8-acceptance-evidence); overall 8D.22 user sign-off does not assert that every checklist item below ran. Historical 8A/B sections describe their original baselines only; current cookie authentication, removed bootstrap/credentials and UART0-only recovery override them. Use current contracts and the integrated regression section for present behavior. Never log generated/entered passwords, cookies/tickets or private/verifier material.
|
||||||
|
|
||||||
## Current Broker management regression procedure — 8D.16
|
## Current Broker management regression procedure — 8D.16
|
||||||
@@ -26,7 +29,7 @@ The [Network contract](web_administration.md#network) defines the current byte c
|
|||||||
|
|
||||||
## Current legacy-removal regression procedure
|
## Current legacy-removal regression procedure
|
||||||
|
|
||||||
The legacy bootstrap/credential/reconciliation instructions in the phase baselines below are **historical only**, superseded by [legacy credential removal](roadmap.md#phase8-legacy-credential-compatibility) and the current [command reference](command_reference.md). Basic authentication is also historical; current HTTPS uses cookie login. The current overrides apply to later Phase 8C/browser procedures too: no `user bootstrap` or `web credentials` command remains, first-admin creation uses normal UART0 `user add`, and recovery rebuilds empty. Never treat the checklist below as evidence of execution.
|
The legacy bootstrap/credential/reconciliation instructions in the phase baselines below are **historical only**, superseded by [legacy credential removal](roadmap.md#phase8-legacy-credential-compatibility) and the current [command reference](../command_reference.md). Basic authentication is also historical; current HTTPS uses cookie login. The current overrides apply to later Phase 8C/browser procedures too: no `user bootstrap` or `web credentials` command remains, first-admin creation uses normal UART0 `user add`, and recovery rebuilds empty. Never treat the checklist below as evidence of execution.
|
||||||
|
|
||||||
1. On a disposable controlled NVS image with only `user_db/database` missing, boot and confirm an empty database is persisted with zero accounts/admins and no imported credential. Reboot and confirm it remains empty. Keep physical UART0 attached; do not factory-erase the device for this test.
|
1. On a disposable controlled NVS image with only `user_db/database` missing, boot and confirm an empty database is persisted with zero accounts/admins and no imported credential. Reboot and confirm it remains empty. Keep physical UART0 attached; do not factory-erase the device for this test.
|
||||||
2. Run `user add maint admin` on UART0, check hidden password confirmation and cancellation, then confirm account/password persistence after reboot. Separately exercise `user add operator user --generate` and secure one-time display. Final-administrator delete/demotion must still fail. Existing SSH own-password generation restrictions and typed browser generated-password support remain unchanged.
|
2. Run `user add maint admin` on UART0, check hidden password confirmation and cancellation, then confirm account/password persistence after reboot. Separately exercise `user add operator user --generate` and secure one-time display. Final-administrator delete/demotion must still fail. Existing SSH own-password generation restrictions and typed browser generated-password support remain unchanged.
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
# Web administration contracts
|
||||||
|
|
||||||
|
Current implementation contracts; [Phase 10 firmware upload is complete by explicit user acceptance on 2026-09-18](roadmap.md#phase-10--simple-admin-web-firmware-upload), confirming upload and normal operation, not specific fault or recovery checks. Phase status and executed-evidence limits belong in the [roadmap](roadmap.md#phase-8--role-based-users-and-administrative-access--complete) and [acceptance record](roadmap.md#phase8-acceptance-evidence), not in implementation timelines. [Regression procedures](user_administration_tests.md#integrated-web-administration-regression-procedure) describe checks, not results. Source is authoritative; start with the [code map](../agent/code-map.md).
|
||||||
|
|
||||||
|
## Authentication and admission
|
||||||
|
|
||||||
|
- HTTPS-only, same-origin login replaces Basic authentication entirely. Four digest-only opaque sessions have absolute one-hour expiry; four pre-login challenges last 120 seconds. Login is globally bounded to five credential attempts per 60 seconds. Live sessions/challenges/tickets are not evicted to admit another client.
|
||||||
|
- Raw tokens travel only in the host-only `__Host-` cookie with Secure, HttpOnly, SameSite=Strict, Path=/ and explicit lifetime; no Domain. Session records retain copied secret-free principals, CSRF state and nonzero, nonreused originating-session IDs. Passwords, raw cookies/tickets, verifiers and private keys never enter routine snapshots/logs/completion/display.
|
||||||
|
- Mutation admission requires current cookie/principal, strict Origin and CSRF validation; administration additionally requires current `admin`. Normal users retain serial/status but cannot invoke administration directly. Authentication POST fetches use CORS mode with fixed same-origin URLs and same-origin credentials: do not accept Origin `null` to compensate for browser no-referrer behavior.
|
||||||
|
- Session-store initialization is part of admitted HTTPS start; authentication failure gates HTTPS. Failed start/accepted stop disables and wipes session state. Logout invalidates only the originating session before socket cleanup; account mutation invalidates that account's sessions/tickets, including deletion/recreation, without revoking unrelated accounts. Currentness checks remain authoritative if best-effort notifications fail.
|
||||||
|
- Four serial tickets and two admin tickets are digest-only, single-use, 30-second, session/principal-bound records. Cookie/Origin/ticket/currentness and transport admission precede explicit WebSocket 101. Store RNG/SHA/database calls run outside short spinlocks; IDs/expiry/epochs fence stale publication without nested store/transport locks.
|
||||||
|
- HTTPD remains bounded to six sockets, two serial WebSockets, one admin WebSocket and 40 method/path handlers; LRU eviction is disabled. Sessions, sockets, tickets and the two shared remote-console slots are separate capacity limits. Optional settings/admin failures preserve unrelated routes where their initialization contract permits; UART0 and native USB remain independent of web readiness.
|
||||||
|
|
||||||
|
`web_httpd_adapter` alone accesses private IDF 5.5.0 HTTPD state. It rejects duplicate/ambiguous headers, postpones 101 until admission, and wipes consumed header scratch while preserving right-aligned unread bytes. Optional Settings registration stages descriptor/name allocations before publishing either, avoiding the pinned public registration failure path. Re-audit these private boundaries on SDK upgrades and same-version SDK patches: the version guard does not detect patches that retain the same version number. HTTPD response headers are pointer-backed, not copied; both `Set-Cookie` value buffers must remain valid and distinct through response send. Do not reuse or wipe those buffers before sending completes. Do not enable header/ticket debug logging. Auth documents, scripts and sensitive responses are no-store with CSP/no-referrer/frame-denial protections; authored loader changes require matching CSP hashes. Generated assets are not a normal documentation/build output.
|
||||||
|
|
||||||
|
## Terminal and console ownership
|
||||||
|
|
||||||
|
Switching Serial/Admin/Settings never creates a second serial client, requests/releases the writer lease or reconnects serial. Hidden connected terminals keep draining; only selected terminal input is sent. Both browser terminals have separate 5,000-line scrollback and 64 KiB callback-accounted pending-output bounds with visible browser-drop counts. Admin input is bounded to 4 KiB admission and 512-byte frames. Admin open/reopen is explicit; admin close is isolated. Disconnect pauses serial reconnect but retains login; logout/expiry/revocation closes affected sockets normally.
|
||||||
|
|
||||||
|
The document binds terminal state to its first validated username/role/session-stable CSRF tuple. A different identity requires a clean document before showing retained output. Pagehide fences sockets/work and hides scrollback until same-session revalidation; admin is not automatically reopened. Fit readiness caches only success and uses at most three generation-fenced animation-frame retries per external request.
|
||||||
|
|
||||||
|
`admin_ssh_console` is the sole `esp_console_run()` caller for UART0, admin SSH and browser admin. Two fixed remote-console slots are shared across SSH/browser, not two per transport. Queue records carry copied principals and transport-qualified generation tokens. Owner currentness is checked outside console locks before commands and during prompts (250 ms polling plus validation/scheduling latency), then identity is rechecked. This is operation admission, not cancellation or rollback of arbitrary executing handlers. Consumed output and retired prompt/history state are wiped.
|
||||||
|
|
||||||
|
HTTPD alone owns browser-admin socket IO and its 1,552-byte PSRAM-only payload. A 20 ms timer queues at most one owner poll, not a new task. Close uses current-owner shutdown, not queued reusable `sock_db *` pointers. Detach fences submitters; queued state retires only after successful HTTPD destruction. Failed destruction retains ownership and prevents unsafe restart/reuse.
|
||||||
|
|
||||||
|
### Browser-shell policy
|
||||||
|
|
||||||
|
Typed Settings permissions do not expand shell permissions. Parsed canonical arguments, not raw prefixes or completion suggestions, control admission:
|
||||||
|
|
||||||
|
- Browser `web` permits only `web status`, `web stop`, and exact `web certificate rotate --force`; certificate info/reset, diagnostics/performance and other web forms are denied.
|
||||||
|
- Browser `wifi` uses the canonical command handler, including bare `wifi` (status), profile/AP settings, persistence, lifecycle and network diagnostics. Profile/AP secret entry uses the shared hidden prompt: no input echo, history or completion of secret bytes; cancellation, disconnect and failed currentness discard the input. Explicit `wifi ap show-secret` reveals the AP password only in the invoking admin terminal; routine status and completion do not reveal credentials. Browser `mdns` still permits only exact `mdns status`. Root and `wifi`-prefixed ping/traceroute/nslookup support the same `-4`/`-6` and device-local IPv6 scope syntax as UART0/admin SSH; see [dual-stack diagnostics](../command_reference.md#dual-stack-network-diagnostics). They run on the shared dispatcher, not HTTPD or the client browser; no extra endpoint or permission expansion is involved.
|
||||||
|
- Browser `user` permits status/list/show and interactive add/password plus forced role/delete for **other accounts only**. Self changes, generated passwords, key commands and recovery are denied there; typed Accounts supports the separately bounded self/generated/key workflows.
|
||||||
|
- Browser SSH stop/disconnect/reset and host-key mutation are denied; typed SSH Settings has its own safe owner path. Do not claim full browser-shell parity.
|
||||||
|
- Browser `reboot` and owner-relative `exit` are supported. First-admin provisioning uses normal `user add` on UART0; unavailable-database recovery is UART0-only. The legacy `user bootstrap` and web credential commands no longer exist.
|
||||||
|
|
||||||
|
Wi-Fi changes use the existing canonical manager path, not deferred browser drain/acknowledgement. Stop, reconnect, profile/AP edits, load/defaults/reset can disconnect network clients before output arrives. A lost result does not cancel admitted work: reconnect and inspect before retrying; use UART0 if networking is unavailable. Changes are RAM-only until `wifi save`, except `wifi reset`, which also persists defaults. Native USB remains independent UART1 access, not an administration console.
|
||||||
|
|
||||||
|
Deferred self-affecting shell actions use the existing bounded drain/control path (up to ten seconds plus a short delay), not guaranteed peer delivery. Browser certificate rotation hands a typed action after drain/200 ms to the existing 12 KiB dispatcher, never crypto/NVS on the 4 KiB control stack. Pending input is discarded through execution, and an executing slot remains reserved across self-detach. UART0/admin SSH retain canonical recovery actions.
|
||||||
|
|
||||||
|
## Typed settings API and operation lifetime
|
||||||
|
|
||||||
|
All routes below are under `/api/settings/`. Each domain has bodyless GET snapshot plus GET/POST operation unless noted. Requests reject queries, malformed/oversized/duplicate/unknown fields and inappropriate bodies; mutation routes require bounded JSON with current admin/Origin/CSRF. GET allows absent Origin but rejects mismatch. Snapshot contention/unavailability is not an empty successful projection. HTTPD performs bounded admission/encoding only; canonical mutations run on the existing four-entry administration dispatcher using **IDs only**, not command strings, credentials, request pointers or socket handles.
|
||||||
|
|
||||||
|
| Domain and source | Snapshot / operation suffix | Request / snapshot / result buffer bounds (bytes) |
|
||||||
|
|---|---|---|
|
||||||
|
| `web_serial_settings`, serial service | `serial` / `serial-operation` | 256 / 256 / 96 |
|
||||||
|
| `web_account_settings`, user database | `accounts` / `account-operation` | 768 / 1024 (accounts), 512 (keys) / 96 |
|
||||||
|
| `web_network_settings`, Wi-Fi + mDNS | `network` / `network-operation` | 768 / 2304 / 128 |
|
||||||
|
| `web_display_settings`, local status UI | `display` / `display-operation` | 256 / 128 / 96 |
|
||||||
|
| `web_broker_settings`, session broker | `broker` / `broker-operation` | 256 / 2048 / 96 |
|
||||||
|
| `web_ssh_settings`, SSH owner/security | `ssh` / `ssh-operation` | 256 / 768 / 96 |
|
||||||
|
| `web_lifecycle_settings`, HTTPS owner/security | `lifecycle` / `lifecycle-operation` | 256 / 320 / 96 |
|
||||||
|
|
||||||
|
Mutation bodies use at most four receive attempts. Each domain retains one original-login-bound pending/result slot, nonreused operation IDs and an executing reservation. Results are replaceable, login-isolated observations, not durable history or idempotency keys. Another tab can replace a completed result. Dispatcher currentness and the 30-second dequeue deadline precede canonical owner admission. **Admitted work may finish after logout, revocation, timeout or navigation.** A failed response, 401 or disappearance of a result proves neither success nor cancellation; inspect current state before any deliberate retry.
|
||||||
|
|
||||||
|
Accounts and Network each use a one-second timer to cancel/wipe queued, non-executing secrets after 30 seconds plus scheduling latency. Shared input wipes on dequeue/rejection; executing locals wipe on return. This is not a hard wall-clock erasure/execution guarantee. Other domain deadlines are dequeue checks, not new cancellation timers. UI fences late/session-changed responses, bounds request/result checking to 15 seconds and never automatically replays/restores mutations. Serial/Accounts/Network/Display/Broker use bounded automatic checks where implemented (at most ten one-second GETs); SSH and HTTPS/Reboot require manual Check Result/Refresh.
|
||||||
|
|
||||||
|
### Serial and Display
|
||||||
|
|
||||||
|
Serial Apply/Defaults change RAM; Save persists working device state, not browser drafts. Load follows canonical defaults/fallback behavior. Reset uses canonical apply/persist/best-effort rollback. Reconfiguration/stop discards serial-service RX/TX and task-local pending data but preserves broker clients, lease and already-fanned output. USB with DTR may restart a stopped service. `/api/status` uses a zero-wait consistent serial snapshot (`running:null` if unavailable).
|
||||||
|
|
||||||
|
Display accepts dim/off timeouts 0–86400 seconds and an expected nonzero configuration generation. All writers, including CLI/legacy Apply, share a zero-wait owner reservation; NVS runs outside critical sections. Save stabilizes selected RAM; Load retains canonical fallback without rewriting NVS; Reset commits defaults **before** RAM publication, leaving RAM unchanged on failure. Buttons/diagnostic holds update activity, not configuration generation. Configuration needs an available UI task, not a physically present panel, and never takes over I2C.
|
||||||
|
|
||||||
|
### Accounts and authorized keys
|
||||||
|
|
||||||
|
The database owns eight accounts, final-admin protection and conditional target username/account-ID/auth-generation checks inside its mutation lock. Create/password/role/delete and key mutations share canonical commit/invariant logic. HTTPD uses zero-wait secret-free projections, not blocking CLI snapshots. Successful mutations request target-only web/SSH revocation; self mutation can revoke access before result retrieval.
|
||||||
|
|
||||||
|
Separate bodyless POST `accounts/generate-password` returns one 24-character value without mutation or retained retrieval. The browser uses a 60-second context-bound saved acknowledgement before separate submission; this is UX, not delivery proof, and JavaScript strings cannot be securely wiped. POST `accounts/keys` returns only slot/type/SHA-256 fingerprints for the selected identity. Key add/delete/clear share the operation slot; public-key import is at most 384 decoded text bytes in the 768-byte request and uses canonical Ed25519/P-256 validation. Three stable key indices may be sparse; never interpret response-array position as an index. No stored key blob, verifier or password is exported.
|
||||||
|
|
||||||
|
### Network
|
||||||
|
|
||||||
|
Wi-Fi config/runtime is one zero-wait consistent projection; mDNS is a separate projection, not cross-domain atomic authorization. Four stable profiles carry enabled/priority/security/SSID/password-configured metadata. `mixed` means WPA2-or-stronger, not open. `announced` is expected STA announcement, not verified DNS. Runtime `ipv6_addresses` contains up to three preferred addresses from the same snapshot as the availability flags, encoded as eight lowercase four-digit hextets without a zone. Network settings display separate link-local, ULA and GUA lists; absent groups show `none`. Link-local access needs the client's interface scope, and address presence does not assert a route or Internet reachability. Encoding uses the existing snapshot only, not netif/driver calls on HTTPD; the 2304-byte response buffer adds 256 bytes of bounded stack storage.
|
||||||
|
|
||||||
|
SSID wire values are reversible **bytes**, maximum 32: printable ASCII, standard single-character JSON escapes and `\u00HH`, with no raw non-ASCII, non-byte Unicode or surrogates. NUL/non-UTF-8 round-trip. UI text is UTF-8-encoded before byte serialization; exact reversible text or literal hex preserves existing bytes and BOM, with no silent replacement/truncation.
|
||||||
|
|
||||||
|
Flat operations select one domain/target:
|
||||||
|
|
||||||
|
- `wifi-patch` + Wi-Fi generation: optional boot policy, AP policy/channel/SSID/password/clear; `profile-patch` additionally selects stable profile 0–3 and optional enabled/priority/security.
|
||||||
|
- `wifi-save|wifi-load` require Wi-Fi generation. `start|stop|reconnect|next-profile` use canonical connection controls, not explicit selected-profile connection.
|
||||||
|
- `mdns-set` requires its generation and suffix; `mdns-save|mdns-load|mdns-defaults` require its generation. Suffix is 1–55 lowercase ASCII letters/digits/hyphens, no leading/trailing hyphen, producing `sak-<suffix>`.
|
||||||
|
|
||||||
|
Omitted fields preserve current bytes under the Wi-Fi mutex. Password Keep omits, Replace is 8–63 printable ASCII bytes, disabled-STA Clear is explicit; replacement and clear cannot coexist. AP clear is always denied, even while off. No saved PSK or length is returned. Wi-Fi compare/merge/whole-candidate validation and required queue admission precede RAM publication. Save stabilizes selected bytes; Load reads stored configuration only and cannot generate fallback secrets. No browser Wi-Fi reset/default-secret/export route exists.
|
||||||
|
|
||||||
|
Edits require explicit Save. Disabled-profile-only edits do not restart active radio; enabled-policy/AP changes follow canonical asynchronous restart. Start/Stop change RAM enabled-at-boot; Reconnect/Next are no-ops while stopped. Next follows enabled priority order with wrap; editor selection is not connection selection. mDNS independently owns generation/persistence; the Wi-Fi manager owns radio/reannouncement. mDNS RAM change followed by queue failure is reported without rollback; its Load may choose deterministic MAC-derived defaults.
|
||||||
|
|
||||||
|
Network result fields are `id/action/state/error`: `pending`, `accepted`, `ok`, `failed`, `cancelled`, `stale`, `invalid`, `loaded_defaults`, `applied_not_queued`, or login-isolated `idle`. `accepted` means RAM/owner-queue admission, **not** association/DHCP/DNS completion. `ok` denotes explicit Save. Loss can precede response; recover via STA/AP and inspect before retrying. UART0 administers recovery; native USB provides network-independent UART1, not an admin console.
|
||||||
|
|
||||||
|
### Broker and contextual controls
|
||||||
|
|
||||||
|
Broker management atomically copies eight compact client rows, writer and lease generation under one zero-wait mutex acquisition, without reading UART payloads or consuming events. Conditional assignment compares the selected nonzero target ID and lease generation under the same force-writer lock before any effects. Stale/absent targets conflict; same-current-target assignment is a no-op only after validation. Accepted serial TX is not recalled by transfer.
|
||||||
|
|
||||||
|
IDs have three slot bits and 29 generation bits; exhausted client slots retire until reboot rather than wrap. The separate 32-bit lease version saturates at UINT32_MAX, survives counter clear and advances on grant/release/revoke before advisory event delivery. Forced transfer can advance twice; it is not a count. Saturation blocks typed assignment but leaves ordinary request/release/disconnect and canonical recovery force available. Reboot invalidates old browser sessions.
|
||||||
|
|
||||||
|
Serial/Wi-Fi/Broker quick views reuse one nonmodal settings host and existing controllers/drafts, not parallel editors. Hover/focus/click/tap never mutate. Network quick mode excludes password controls. Full-page drafts are protected; promotion preserves controller/selection, departure fences reads/timers without cancelling admitted work. Dismissal restores the prior terminal without stealing unrelated focus; explicit Escape/Close returns focus to its trigger.
|
||||||
|
|
||||||
|
Broker contextual reads use one timer five seconds after successful completion and a five-second whole-read deadline, stopping on errors/uncertainty/departure. Explicit target/lease selection is not rebased by refresh. Sticky stale/absence latches require deliberate reselection even if later snapshots match again. Confirmation captures exact versions before session revalidation; guarded aria-disabled controls preserve focus during updates.
|
||||||
|
|
||||||
|
## Service lifecycle and identity rotation
|
||||||
|
|
||||||
|
Service generations are saturated, distinct from identity and session generations, and advance on admitted canonical lifecycle attempts including failures; counter clear/reinit cannot revive stale versions. Public zero-wait metadata authorizes nothing. Conditional controls compare under the canonical owner reservation; no snapshot-check/unconditional-mutate gap is allowed. Reboot invalidates originating logins and boot-local reservation IDs.
|
||||||
|
|
||||||
|
### HTTPS and reboot
|
||||||
|
|
||||||
|
GET `lifecycle` has seven fields: `generation`, `running`, `transitioning`, `controllable`, `identity_generation`, `fingerprint`, `rotatable`. Stored HTTPS fingerprint is 64 lowercase hex SHA-256 digits. Stop/restart/reboot require exactly action + service generation; rotate also requires identity generation. Unknown/duplicate/escaped/coerced fields and zero/saturated versions reject. Unavailable identity yields generation zero/empty fingerprint/not rotatable without removing ordinary service controls.
|
||||||
|
|
||||||
|
Self-cutting HTTP actions use **successful synchronous send return → one nonreused-ID HTTPD callback → existing dispatcher**. Send return is not browser receipt. The callback never waits or performs lifecycle work, and captures no request/fd/reusable operation pointer. A two-second ACK admission deadline and post-validation 30-second dequeue deadline are not execution bounds. Accepted-but-lost HTTPD work retains one reservation until callback arrival or successful HTTPD destruction; failed destruction cannot release it or accumulate more callbacks. The original login/current admin is revalidated before owner admission.
|
||||||
|
|
||||||
|
`web_server_stop_current()`/`web_server_restart_current()` compare and reserve under the server mutex. Restart retains transition ownership through stop/start; failed stop skips start, failed cleanup retains handles and canonical recovery, and reinit preserves the error. `web_server_reboot_current()` reserves the HTTPS generation then uses canonical `esp_restart()` outside locks, not an HTTPD stop wait or self-console cleanup.
|
||||||
|
|
||||||
|
`web_server_replace_identity(service_generation, identity_generation, reset, &committed)` reserves **service before identity**, before any crypto/storage. Both nonzero generations mean conditional healthy-running rotation; both zero retain CLI semantics; conditional Reset is invalid. Canonical CLI/browser-shell rotation and direct security rotate/reset share nonreused task-owner-bound identity reservation tokens. Only the owner can replace once/release; exhaustion fails closed until reboot. Crypto/NVS run outside service/security mutexes and spinlocks; commit precedes live publication and old-key wipe. Reservation remains held while reserved start copies the committed pair.
|
||||||
|
|
||||||
|
HTTPS ordering is **commit → stop → restart**. Precommit generation/RNG/storage failure leaves identity, HTTPD and logins unchanged (service version may advance after admission). Postcommit lifecycle failure never rolls back identity; failed stop can leave old served and new stored fingerprints different. Running replacement restarts HTTPS; canonical stopped rotation stays stopped, while CLI TLS-only reset can recover unavailable identity and starts a stopped service. Browser has no Reset/recovery/export action.
|
||||||
|
|
||||||
|
Save drafts; rotation/restart invalidates all web logins and closes both browser routes. Inspect `web certificate info` through trusted UART0, verify fingerprint before renewing trust, then reload/sign in freshly. Accepting a warning alone is not trusted verification. Use canonical UART0/admin SSH `web stop` / `web start` for retained-server recovery. Network/SSH/USB are not stopped by HTTPS-only operations; whole-device reboot interrupts all transports and loses unsaved RAM.
|
||||||
|
|
||||||
|
### Application firmware upload
|
||||||
|
|
||||||
|
The admin-only firmware card is in **Settings → HTTPS / Reboot**. It sends a raw `POST /api/firmware` with `application/octet-stream`, known length, session cookie, same-origin Origin and `X-CSRF-Token`; it is not a JSON Settings operation or dispatcher/result-slot workflow. Standard SDK OTA APIs stream through a 4 KiB buffer to the inactive application slot and select it only after validation/currentness checks. Only that slot and `otadata` are written; NVS/data partitions are untouched.
|
||||||
|
|
||||||
|
One upload reserves HTTPS lifecycle/identity and excludes ordinary software reboot. The synchronous HTTPD handler can stall/drop browser serial/admin sessions. Its 120-second total receive-loop and ten-second stall checks are not preemptive flash deadlines or uninterrupted-traffic guarantees. Successful response send schedules a delayed reboot, not proof of peer receipt. Failed response after commit leaves the image selected, schedules no automatic reboot, releases reservations for manual reboot, and latches subsequent admissible uploads to 409 `firmware_selected_reboot_required` until reset. Never automatically retry an uncertain outcome.
|
||||||
|
|
||||||
|
See [firmware update](roadmap.md#phase-10--simple-admin-web-firmware-upload) for wired-first installation, exact image selection, manual recovery, acceptance limits and reusable future hardware regression checks. Phase 10 is accepted; there are no signature/version policies or automatic rollback.
|
||||||
|
|
||||||
|
### SSH
|
||||||
|
|
||||||
|
GET `ssh` supplies service/session state plus identity generation, fixed P-256 algorithm, unpadded OpenSSH `SHA256:` base64 fingerprint and rotatable flag. Service actions use exactly `action`, `generation`, `target`; rotate adds `identity_generation` and requires target zero. Start/stop and exact-session disconnect use published state, saturated service generation and the command mutex; exhausted SSH session slots retire rather than wrap. Disconnect success is an owner close request, not completed teardown. HTTPD never calls wolfSSH or waits for the SSH task.
|
||||||
|
|
||||||
|
`ssh_transport_replace_identity()` compares/reserves **service then identity before stop or storage**, holding the existing command mutex across **stop → generate/commit/publish → conditional restart**. Canonical UART0/deferred admin-SSH rotation/reset and direct security mutations share the task-bound nonreused identity reservation. Crypto/NVS run outside security locks/spinlocks. Both nonzero versions select conditional rotation (also while stopped); both zero preserve canonical semantics, with no conditional Reset.
|
||||||
|
|
||||||
|
Failed stop/timeout skips mutation and never starts again; pending owner work is not cancelled. Persistence failure after successful stop may already have disconnected clients and attempts old-identity restart if previously running. Committed replacement remains committed if restart fails. Stopped rotation stays stopped; canonical reset can recover unavailable material/start stopped SSH. Only the SSH owner frees runtime context after all slots retire, before clearing cleanup admission; start rejects orphan handles. wolfSSH copies DER into its context; caller/candidate/superseded private bytes are wiped.
|
||||||
|
|
||||||
|
SSH changes leave invoking HTTPS available, so they use the ordinary ID-dispatcher/result path, not HTTPS's self-cutting ACK gate. UI confirms both versions and all-SSH/session scope, retains stale selection and manual 15-second/no-replay flow. A failed result may represent partial effects. Verify new trust through UART0 `ssh host-key info` before updating known_hosts; no HTTPS relogin is inherently required. Browser Reset/recovery/key export and user authorized-key changes are separate, excluded from host rotation.
|
||||||
|
|
||||||
|
## Diagnostics, recovery and scope boundaries
|
||||||
|
|
||||||
|
See [admission diagnostics](web_admission_diagnostics.md), [ordinary HTTPS idle cleanup](https_idle_cleanup.md), [throughput diagnostics](web_throughput_diagnostics.md) and [legacy storage compatibility](roadmap.md#phase8-legacy-credential-compatibility). Broker read means transport handoff, not peer receipt; capture non-consuming counters before disconnect. TLS `-0x004C` is generic NET_RECV_FAILED, not evidence of OOM. Resource minima and counter observations require attribution, not inferred causes.
|
||||||
|
|
||||||
|
Phase 8D.15's dedicated typed network-diagnostics UI/API was removed: diagnostics remain shell-based, subject to frontend policy. The unimplemented 8D.19 ordinary browser-session/native-USB control expansion was removed; existing SSH controls remain. No full shell parity, browser identity recovery/reset/export, encryption or secure boot is implied by Phase 8 acceptance. Phase 10 application upload is complete by explicit user acceptance of upload and normal operation; unreported fault, NVS-comparison, power-loss and recovery checks are not implied. UART0 is the administrative recovery authority; native USB is binary-transparent, network-independent UART1 access. Neither permits bypassing the broker's single writer or recalling already-admitted work.
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
# Web admission diagnostics
|
# Web admission diagnostics
|
||||||
|
|
||||||
|
Source/build/test paths in code spans and commands are relative to the repository root
|
||||||
|
unless a different working directory is stated; Markdown links are relative to this document.
|
||||||
|
|
||||||
Current default-disabled instrumentation, not an admission fix or an allocation-failure detector. `src/web_diagnostics.{c,h}` observes public synchronous HTTPS create/close callbacks and the four serial/admin ticket/upgrade handlers. It adds no owner task, queued probe or socket-capacity change. See [ordinary idle cleanup](https_idle_cleanup.md) for the independent cleanup policy and [acceptance evidence](roadmap.md#phase8-acceptance-evidence) for user reports.
|
Current default-disabled instrumentation, not an admission fix or an allocation-failure detector. `src/web_diagnostics.{c,h}` observes public synchronous HTTPS create/close callbacks and the four serial/admin ticket/upgrade handlers. It adds no owner task, queued probe or socket-capacity change. See [ordinary idle cleanup](https_idle_cleanup.md) for the independent cleanup policy and [acceptance evidence](roadmap.md#phase8-acceptance-evidence) for user reports.
|
||||||
|
|
||||||
## Usage and interpretation
|
## Usage and interpretation
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
# Web serial throughput diagnostics
|
# Web serial throughput diagnostics
|
||||||
|
|
||||||
|
Source/build/test paths in code spans and commands are relative to the repository root
|
||||||
|
unless a different working directory is stated; Markdown links are relative to this document.
|
||||||
|
|
||||||
## Current contract and accepted fix
|
## Current contract and accepted fix
|
||||||
|
|
||||||
Default-disabled instrumentation measures broker fan-out and binary WebSocket TX, not CPU usage, peer receipt or pure scheduler latency. The user separately accepted the combined binary header/payload-send fix at **160 MHz / 230400 baud with full mix including browser admin**. [Acceptance evidence](roadmap.md#phase8-acceptance-evidence) distinguishes that report from the latest capture and unmeasured reserves. The old frequency-only experiment is not the retained solution.
|
Default-disabled instrumentation measures broker fan-out and binary WebSocket TX, not CPU usage, peer receipt or pure scheduler latency. The user separately accepted the combined binary header/payload-send fix at **160 MHz / 230400 baud with full mix including browser admin**. [Acceptance evidence](roadmap.md#phase8-acceptance-evidence) distinguishes that report from the latest capture and unmeasured reserves. The old frequency-only experiment is not the retained solution.
|
||||||
+16
-10
@@ -4,7 +4,7 @@ These manual procedures verify the OLED and buttons, MAX3243 breakout, UART1 dat
|
|||||||
|
|
||||||
> **Safety:** With power removed, install only the wiring required by the selected test. DE-9 pins 3 (`TX`), 4 (`DTR`), and 7 (`RTS`) are driven outputs. Never connect one of these outputs to another driven output. Keep temporary Dupont wiring short and secure. Power the OLED only from 3.3 V because module-mounted I²C pull-ups may connect SDA and SCL to the OLED `VCC` rail.
|
> **Safety:** With power removed, install only the wiring required by the selected test. DE-9 pins 3 (`TX`), 4 (`DTR`), and 7 (`RTS`) are driven outputs. Never connect one of these outputs to another driven output. Keep temporary Dupont wiring short and secure. Power the OLED only from 3.3 V because module-mounted I²C pull-ups may connect SDA and SCL to the OLED `VCC` rail.
|
||||||
|
|
||||||
## Phase 7 OLED and button bring-up
|
## OLED and button checks
|
||||||
|
|
||||||
Use the exact OLED and button connections in [Hardware wiring](wiring.md). Display diagnostics initially operate I²C at 100 kHz and probe the standard 7-bit `0x3c` and `0x3d` addresses. The connected test module acknowledges at `0x3c`, whose 8-bit write/read forms are `0x78` and `0x79`. A missing or unresponsive display is nonfatal: diagnostics should report it without disrupting UART0 or the serial services.
|
Use the exact OLED and button connections in [Hardware wiring](wiring.md). Display diagnostics initially operate I²C at 100 kHz and probe the standard 7-bit `0x3c` and `0x3d` addresses. The connected test module acknowledges at `0x3c`, whose 8-bit write/read forms are `0x78` and `0x79`. A missing or unresponsive display is nonfatal: diagnostics should report it without disrupting UART0 or the serial services.
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ If the expected address does not respond, treat the result as a nonfatal hardwar
|
|||||||
|
|
||||||
### 3. Initialization and display patterns
|
### 3. Initialization and display patterns
|
||||||
|
|
||||||
Initialize the address observed during the scan. The tested module uses 7-bit `0x3c`, equivalently 8-bit `0x78` (write) and `0x79` (read):
|
Initialize the supported address found by the probe: `0x3c` or `0x3d`. An unexpected scan address is a finding to investigate, not an address accepted by `debug display init`. The tested module uses 7-bit `0x3c`, equivalently 8-bit `0x78` (write) and `0x79` (read):
|
||||||
|
|
||||||
```text
|
```text
|
||||||
debug display init 0x3c
|
debug display init 0x3c
|
||||||
@@ -51,7 +51,7 @@ Confirm that clear and fill affect the full 128×64 area, checker and grid have
|
|||||||
|
|
||||||
### 4. Rendered status/content layout
|
### 4. Rendered status/content layout
|
||||||
|
|
||||||
Run the Phase 7B panel-layout pattern:
|
Run the panel-layout pattern:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
debug display pattern layout
|
debug display pattern layout
|
||||||
@@ -75,7 +75,7 @@ debug display pattern clear
|
|||||||
debug display row 16
|
debug display row 16
|
||||||
```
|
```
|
||||||
|
|
||||||
**Verified result:** row 15 is the last yellow addressable row and row 16 is the first blue addressable row. The two colored areas are separated by a narrow physical black divider, so later UI rendering must treat the 128×16 yellow and 128×48 blue regions as separate panels rather than one visually continuous canvas. Also test another endpoint row if needed with `debug display row <0..63>` to confirm row addressing and orientation.
|
On the tested two-color module, row 15 is the last yellow addressable row and row 16 is the first blue addressable row. The two colored areas are separated by a narrow physical black divider. Confirm that your module matches this layout: the UI uses separate 128×16 yellow and 128×48 blue panels. Also test another endpoint row if needed with `debug display row <0..63>` to confirm row addressing and orientation.
|
||||||
|
|
||||||
### 6. Contrast, inversion, and display-off checks
|
### 6. Contrast, inversion, and display-off checks
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ Confirm that each page remains entirely within the blue content panel. The yello
|
|||||||
|
|
||||||
The UI refreshes at 4 Hz maximum. Status pages are observational and must not expose passwords, credentials, tickets, or key material. A short Select press opens the separate Controls menu described below; merely navigating status pages must not change a service, alter writer ownership, or inject serial data. With UART0, USB CDC, WebSocket, and SSH active, navigate pages and confirm that the display continues to update without disrupting serial traffic or UART0 recovery.
|
The UI refreshes at 4 Hz maximum. Status pages are observational and must not expose passwords, credentials, tickets, or key material. A short Select press opens the separate Controls menu described below; merely navigating status pages must not change a service, alter writer ownership, or inject serial data. With UART0, USB CDC, WebSocket, and SSH active, navigate pages and confirm that the display continues to update without disrupting serial traffic or UART0 recovery.
|
||||||
|
|
||||||
For the OLED-aging policy, leave all three buttons untouched and verify:
|
With the default dim/off settings (check `display status`), leave all three buttons untouched and verify:
|
||||||
|
|
||||||
1. At five minutes, display contrast drops to `1` while the contents remain present.
|
1. At five minutes, display contrast drops to `1` while the contents remain present.
|
||||||
2. At ten minutes, the OLED switches off.
|
2. At ten minutes, the OLED switches off.
|
||||||
@@ -142,7 +142,7 @@ For the OLED-aging policy, leave all three buttons untouched and verify:
|
|||||||
|
|
||||||
`debug display status` may verify an individual transition in a separate timing run. Every display diagnostic counts as activity and holds normal UI rendering for 30 seconds, so do not invoke it between the five- and ten-minute observations of one continuous run. Record any missing, stale, clipped, or implausible status value or incorrect dim/off/wake transition.
|
`debug display status` may verify an individual transition in a separate timing run. Every display diagnostic counts as activity and holds normal UI rendering for 30 seconds, so do not invoke it between the five- and ten-minute observations of one continuous run. Record any missing, stale, clipped, or implausible status value or incorrect dim/off/wake transition.
|
||||||
|
|
||||||
### 9. Local controls (Phase 7D)
|
### 9. Local controls
|
||||||
|
|
||||||
From a status page, use a short Select press to open **Controls**. Previous/next selects an item; Select activates it. The menu includes serial start/stop, Wi-Fi start/stop/reconnect/next-profile, HTTPS start/stop, SSH start/stop, writer revocation, display off, and reboot. `WiFi:next profile` requests the enabled station profile after the active one in priority order and wraps safely; it must never show or change credentials, Wi-Fi profile configuration, serial framing, TLS/SSH keys, or I²C scan controls.
|
From a status page, use a short Select press to open **Controls**. Previous/next selects an item; Select activates it. The menu includes serial start/stop, Wi-Fi start/stop/reconnect/next-profile, HTTPS start/stop, SSH start/stop, writer revocation, display off, and reboot. `WiFi:next profile` requests the enabled station profile after the active one in priority order and wraps safely; it must never show or change credentials, Wi-Fi profile configuration, serial framing, TLS/SSH keys, or I²C scan controls.
|
||||||
|
|
||||||
@@ -159,11 +159,13 @@ For Wi-Fi lifecycle calls, confirm the immediate result says `Requested`, then u
|
|||||||
|
|
||||||
Run these checks with UART0 available. Repeat appropriate stop/revoke cases with USB CDC, WebSocket, and SSH clients connected; verify the intended session/service is interrupted, unrelated recovery paths remain responsive, and no action injects serial data.
|
Run these checks with UART0 available. Repeat appropriate stop/revoke cases with USB CDC, WebSocket, and SSH clients connected; verify the intended session/service is interrupted, unrelated recovery paths remain responsive, and no action injects serial data.
|
||||||
|
|
||||||
### 10. Phase 7E boot animation, persistence, and fault recovery
|
### 10. Boot animation, persistence, and fault recovery
|
||||||
|
|
||||||
On each normal boot with an initialized OLED, first verify the bounded five-second identity animation: the full `ESP32 SERIAL SWISS ARMY KNIFE` name scrolls across the yellow panel; the blue panel shows the compact right-oriented monochrome logo with an upright `>_` prompt and serial/USB cable ends entering from the left. Wi-Fi activity and the cursor blink at the 4 Hz animation cadence. A missing OLED must skip the animation without preventing normal UART0 recovery.
|
On each normal boot with an initialized OLED, first verify the bounded five-second identity animation: the full `ESP32 SERIAL SWISS ARMY KNIFE` name scrolls across the yellow panel; the blue panel shows the compact right-oriented monochrome logo with an upright `>_` prompt and serial/USB cable ends entering from the left. Wi-Fi activity and the cursor blink at the 4 Hz animation cadence. A missing OLED must skip the animation without preventing normal UART0 recovery.
|
||||||
|
|
||||||
Then shorten the delays for a bounded aging-policy test:
|
Record your current `display status` settings before this test: it changes saved dim/off delays and finishes by saving defaults. Restore and save your preferred values afterward if needed.
|
||||||
|
|
||||||
|
Shorten the delays for a bounded dim/off test:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
display status
|
display status
|
||||||
@@ -175,7 +177,7 @@ reboot
|
|||||||
|
|
||||||
After reboot, `display status` must report the stored 5/10-second values. Verify dim at five seconds, off at ten seconds, and one consumed wake press. Run `display load`, then test `display defaults` without saving and confirm 300/600 seconds are restored only in RAM. Run `display reset`, reboot, and confirm the defaults persisted. Verify invalid combinations such as dim `10` with off `5` are rejected without changing the working values. Set either timeout to `0`, save/reboot, and confirm that transition is disabled; finish with `display reset`.
|
After reboot, `display status` must report the stored 5/10-second values. Verify dim at five seconds, off at ten seconds, and one consumed wake press. Run `display load`, then test `display defaults` without saving and confirm 300/600 seconds are restored only in RAM. Run `display reset`, reboot, and confirm the defaults persisted. Verify invalid combinations such as dim `10` with off `5` are rejected without changing the working values. Set either timeout to `0`, save/reboot, and confirm that transition is disabled; finish with `display reset`.
|
||||||
|
|
||||||
Exercise the following fault matrix while keeping UART0 available:
|
Exercise the following fault checks while keeping UART0 available. Button-triggered display recovery requires a running local UI task and an initialized I²C bus. If `debug display status` reports `bus=unavailable`, correct the wiring with power off and reboot; a button press cannot recreate the bus.
|
||||||
|
|
||||||
| Fault/stress | Procedure | Required result |
|
| Fault/stress | Procedure | Required result |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -186,7 +188,11 @@ Exercise the following fault matrix while keeping UART0 available:
|
|||||||
| Real chord | Press two healthy buttons together before either is quarantined. | The chord is ignored and cannot confirm an action. |
|
| Real chord | Press two healthy buttons together before either is quarantined. | The chord is ignored and cannot confirm an action. |
|
||||||
| Repeated actions | Re-enter Controls and repeat start/stop, reconnect, next-profile, writer-revoke, and display-off requests during transitions. | Each confirmation hold executes once; stale state produces an error rather than the opposite action; queue saturation, if reached, reports an error and leaves the Wi-Fi alert/counter visible. |
|
| Repeated actions | Re-enter Controls and repeat start/stop, reconnect, next-profile, writer-revoke, and display-off requests during transitions. | Each confirmation hold executes once; stale state produces an error rather than the opposite action; queue saturation, if reached, reports an error and leaves the Wi-Fi alert/counter visible. |
|
||||||
|
|
||||||
For the final concurrency regression, connect USB CDC, one WebSocket terminal, and one SSH terminal while UART1 carries sustained bidirectional traffic. Navigate the UI, run the persistence checks, exercise service stop/start and writer revocation, and inject the display/stuck-button faults above. Confirm UART0 remains responsive, the UI never appears as a broker client or injects serial bytes, writer ownership is always the expected client or none, and serial/broker drop counters do not increase unexpectedly. Record `memory`, transport counters, `wifi counters`, and `debug display status` before and after the run.
|
For a concurrent-transport check, connect USB CDC, one WebSocket terminal, and one SSH terminal while UART1 carries sustained bidirectional traffic. Navigate the UI, run the persistence checks, exercise service stop/start and writer revocation, and inject the display/stuck-button faults above. Confirm UART0 remains responsive, the UI never appears as a broker client or injects serial bytes, writer ownership is always the expected client or none, and serial/broker drop counters do not increase unexpectedly. Record `memory`, transport counters, `wifi counters`, and `debug display status` before and after the run.
|
||||||
|
|
||||||
|
## Before RS-232 diagnostics
|
||||||
|
|
||||||
|
Disconnect external DE-9 equipment and use UART0 for these checks. After each power-up, run `serial stop` before any RS-232 `debug` command, including `debug status`. The diagnostics refuse access while the serial service owns the port. If cleanup reports a port fault or a UART driver that could not be removed, reboot before continuing.
|
||||||
|
|
||||||
## Configuration A: data and handshake pairs
|
## Configuration A: data and handshake pairs
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# Firmware update
|
||||||
|
|
||||||
|
The administrator can upload a locally built **application binary** through HTTPS. There is no release server, automatic download, or automatic update service.
|
||||||
|
|
||||||
|
Related guides: [Web administration](web_administration.md), [account provisioning](user_administration.md), [command reference](command_reference.md), and [security operations](security_operations.md). Implementation details are in the [web development contract](devel/web_administration.md#application-firmware-upload); historical acceptance and regression guidance remain in [roadmap Phase 10](devel/roadmap.md#phase-10--simple-admin-web-firmware-upload).
|
||||||
|
|
||||||
|
## Before you begin
|
||||||
|
|
||||||
|
- Keep **USB-to-UART** available for UART0 administration and wired flashing. Native USB CDC is UART1 serial access, not the administration/flashing recovery path described here.
|
||||||
|
- Install OTA-enabled firmware **by wire first**. Firmware without the upload route cannot install its own first web updater. The custom OTA partition layout is required. [One-time migration from the old default layout](getting_started.md#partition-migration-and-erasing) is a separate, destructive procedure—not an ordinary update step.
|
||||||
|
- Select trusted firmware for **`esp32-s3-devkitc-1-n16r8`**, compatible with this board, partition layout, and existing stored configuration schemas. Check the [legacy downgrade warning](devel/roadmap.md#phase8-legacy-credential-compatibility): older v1-only firmware cannot read the newer TLS-only HTTPS material. An older build is not automatically a safe recovery choice.
|
||||||
|
- Save desired RAM-only serial, Wi-Fi, hostname, and display settings. Record nonsecret configuration and verified public HTTPS/SSH fingerprints. Keep passwords separately in a password manager; there is no supported NVS backup/restore facility.
|
||||||
|
- Arrange a maintenance window, stable power, and stable network access. Stop work that depends on uninterrupted serial traffic. Use trusted HTTPS and an `admin` account, verifying device identity as described in [security operations](security_operations.md).
|
||||||
|
|
||||||
|
## Build and upload
|
||||||
|
|
||||||
|
1. From the trusted project checkout, build:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pio run
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Select exactly the application output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
.pio/build/esp32-s3-devkitc-1-n16r8/firmware.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not select an ELF, ZIP, merged full-flash binary, bootloader, or partition-table image. Renaming another file to `.bin` does not make it suitable. The browser requires a nonempty `.bin` no larger than **4 MiB**; the device also checks the actual destination capacity and image structure/integrity.
|
||||||
|
3. Sign in as `admin` and open **Settings → HTTPS / Reboot → Firmware update**.
|
||||||
|
4. Choose the file in **Application .bin**, select **Upload and reboot…**, and confirm. Keep power connected.
|
||||||
|
5. Wait for validation and reboot—not just **100%** transmitted bytes. The UI may first report **Upload complete. Validating firmware…**, then **Firmware accepted; rebooting. Reconnect and sign in shortly.**
|
||||||
|
6. Restore network reachability, use **Reconnect after reboot** or reload, and explicitly sign in again. Verify the running application, saved configuration, account access, and HTTPS/SSH identities before another upload. Check UART0 if the result is unclear.
|
||||||
|
|
||||||
|
Upload handling is synchronous and can stall or drop browser serial/admin connections and other web work. UART0 and native USB are independent recovery/data paths, not guarantees of uninterrupted timing during flash operations. Reboot interrupts **all** transports.
|
||||||
|
|
||||||
|
## What is preserved—and what is not guaranteed
|
||||||
|
|
||||||
|
The updater writes only the inactive application slot and OTA selection metadata. It does not rewrite the partition table/bootloader or erase NVS, PHY, storage, or other data partitions. Saved users, credentials, settings, and device identities remain stored. This preserves stored bytes, not compatibility with arbitrary firmware: the uploaded application must understand existing schemas and must not itself erase or incompatibly migrate them.
|
||||||
|
|
||||||
|
The server checks target/header, completeness, exact image length, and SDK image integrity, including the appended SHA-256 digest, before selecting the image. **A digest is not publisher authentication.** Neither validation nor a `.bin` filename proves that the firmware is trusted, fully board-compatible, or able to boot and operate correctly.
|
||||||
|
|
||||||
|
There is **no image-signature/signing-key policy, secure-boot guarantee, anti-rollback/version-downgrade enforcement, automatic rollback, or post-boot health-confirmation mechanism** in this update workflow. There is no partition migration or NVS backup/restore. A valid but broken application can require wired recovery.
|
||||||
|
|
||||||
|
## Lost response, timeout, or failed upload
|
||||||
|
|
||||||
|
**Do not blindly retry or power-cycle an uncertain update.** A lost response, page close, sign-out, timeout, or browser abort proves neither cancellation nor failure. The image may already be selected, and any later reset can boot it.
|
||||||
|
|
||||||
|
- Before boot selection, rejected/incomplete images and receive/write/validation failures abort the upload without selecting the candidate. The inactive slot may already be erased or partially written.
|
||||||
|
- `firmware_commit_failed` means boot-metadata selection failed with SDK transactional uncertainty. Inspect the device before any reset or retry; do not infer that selection definitely stayed unchanged.
|
||||||
|
- A successful response means validation and boot selection succeeded. Successful server send schedules reboot after about 500 ms, but does not prove browser receipt or successful boot.
|
||||||
|
- If sending the response fails **after selection**, the device schedules no automatic reboot and rejects further admissible uploads with **409 `firmware_selected_reboot_required`** until device reset. HTTPS stop/start does not clear this latch. Inspect via UART0 and deliberately reboot when appropriate.
|
||||||
|
- Reloading clears browser-side uncertainty locks only; it does not undo committed work or clear the device's selected-image latch.
|
||||||
|
|
||||||
|
The device's ten-second receive-stall and 120-second receive-loop checks are not hard flash-operation deadlines. Synchronous erase/write/validation can extend elapsed time. The browser's 180-second timeout cannot cancel committed work. Software reboot/lifecycle exclusions cannot prevent physical reset, power loss, or panic.
|
||||||
|
|
||||||
|
### Interpreting errors
|
||||||
|
|
||||||
|
| Response | Meaning / next step |
|
||||||
|
|---|---|
|
||||||
|
| 400 | Malformed, incompatible, or incomplete image/request. Check the build artifact before a deliberate new attempt. |
|
||||||
|
| 401 / 403 | Authentication, Origin/CSRF, or administrator access failure. Restore a valid admin login; if upload had begun, inspect state first. |
|
||||||
|
| 408 `firmware_timeout` | Device receive deadline exceeded. Inspect state and network conditions. |
|
||||||
|
| 409 `firmware_selected_reboot_required` | Image already selected; inspect and deliberately reboot, rather than upload again. |
|
||||||
|
| 413 / 415 | Image exceeds destination capacity or wrong content type. Use the supported application binary/browser workflow. |
|
||||||
|
| 500 `firmware_write_failed` / `firmware_commit_failed` | Flash/selection failure. Use UART0 to inspect; commit failure carries selection uncertainty. |
|
||||||
|
| 503 | Busy, unavailable partition/service, or insufficient resources. Resolve the condition before a deliberate retry. |
|
||||||
|
|
||||||
|
A disconnected browser may never receive the error. Competing web requests may wait during upload rather than promptly report busy.
|
||||||
|
|
||||||
|
## Wired recovery
|
||||||
|
|
||||||
|
If the new application does not boot or no longer serves HTTPS:
|
||||||
|
|
||||||
|
1. Connect the USB-to-UART path and select a known-good **schema- and layout-compatible** project checkout.
|
||||||
|
2. Run the documented wired workflow:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pio run --target upload
|
||||||
|
pio device monitor -b 115200
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Inspect startup, restore connectivity, verify saved settings and identities, and test normal operation before returning to web updates.
|
||||||
|
|
||||||
|
**Do not erase for ordinary updates or recovery.** Erasing destroys persisted configuration and credentials. There is no automatic recovery flashing or rollback. If the partition layout itself needs migration, use the separate migration procedure knowingly rather than treating erase as generic troubleshooting.
|
||||||
|
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
# Getting started
|
||||||
|
|
||||||
|
This guide takes a supported, wired board from its first firmware installation to USB or network serial access. If firmware is already installed, start at [Open the recovery console](#3-open-the-recovery-console). Review [security operations](security_operations.md) before enabling network access.
|
||||||
|
|
||||||
|
## 1. Check the hardware
|
||||||
|
|
||||||
|
Use the ESP32-S3-DevKitC-1-compatible **N16R8** target (16 MB flash, 8 MB octal PSRAM) and the Adafruit MAX3243 breakout described in [Hardware wiring](wiring.md). Follow that GPIO map, not a generic ESP32 pinout.
|
||||||
|
|
||||||
|
- Wire with power disconnected. Power the breakout and optional OLED from **3.3 V**, not 5 V.
|
||||||
|
- Never connect RS-232 voltage directly to ESP32 GPIOs. The MAX3243 is a level shifter, not a galvanic isolator.
|
||||||
|
- Select a straight-through cable for a DCE peer or a suitable null-modem cable for another DTE peer. A gender changer alone does not cross signals.
|
||||||
|
- Use [Electrical tests](electrical_tests.md) to verify a new assembly before attaching valuable equipment. Active RS-232 diagnostics require the serial service to be stopped.
|
||||||
|
|
||||||
|
There are two distinct USB paths:
|
||||||
|
|
||||||
|
| Board connector | Typical Linux device | Use |
|
||||||
|
|---|---|---|
|
||||||
|
| USB-to-UART bridge | `/dev/ttyUSB*` | Firmware installation, UART0 logs, and the `serial-tool>` administration/recovery console |
|
||||||
|
| Native USB | `/dev/ttyACM*` | UART1 serial data shared with browser and SSH clients |
|
||||||
|
|
||||||
|
Names and connector labels vary between boards. Identify the actual port rather than assuming the example device name. Consult the board schematic before powering through both connectors.
|
||||||
|
|
||||||
|
## 2. Install firmware by wire
|
||||||
|
|
||||||
|
Install [PlatformIO Core](https://docs.platformio.org/en/latest/core/installation/index.html), obtain a trusted project checkout, and run these commands from its root. PlatformIO downloads the configured toolchain and dependencies on the first build, so initial setup needs network access.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pio run
|
||||||
|
```
|
||||||
|
|
||||||
|
Connect the **USB-to-UART** port, then upload:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pio run --target upload
|
||||||
|
```
|
||||||
|
|
||||||
|
If automatic port selection is ambiguous, specify your actual bridge port; for example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pio run --target upload --upload-port /dev/ttyUSB0
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not erase for ordinary installation over a compatible layout or for routine updates. If migrating from the former default/factory layout or unrelated firmware, read [Partition migration and erasing](#partition-migration-and-erasing) first. Subsequent application-only updates can use the [browser firmware updater](firmware_update.md).
|
||||||
|
|
||||||
|
## 3. Open the recovery console
|
||||||
|
|
||||||
|
Use the bridge port at **115200 baud**:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pio device monitor -b 115200
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, on Linux with picocom installed and the actual bridge at `/dev/ttyUSB0`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
picocom /dev/ttyUSB0 -b 115200
|
||||||
|
```
|
||||||
|
|
||||||
|
After an unattended boot, press Enter once in an ANSI-capable terminal to enable line editing, history, cursor movement, and hierarchical Tab completion. At `serial-tool>`, run:
|
||||||
|
|
||||||
|
```text
|
||||||
|
help
|
||||||
|
serial status
|
||||||
|
wifi status
|
||||||
|
```
|
||||||
|
|
||||||
|
This console controls the ESP32, not the equipment attached to RS-232. Keep it available while changing network settings or credentials. Physical UART0 access is not protected by network account passwords.
|
||||||
|
|
||||||
|
## 4. Set the equipment's serial parameters
|
||||||
|
|
||||||
|
Match UART1 to the attached equipment. For example, for **115200 baud, 8N1, no hardware flow control**:
|
||||||
|
|
||||||
|
```text
|
||||||
|
serial set baud 115200
|
||||||
|
serial set data-bits 8
|
||||||
|
serial set parity none
|
||||||
|
serial set stop-bits 1
|
||||||
|
serial set flow none
|
||||||
|
serial status
|
||||||
|
serial save
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `serial set flow rts-cts` only when the peer and cable support it. DTR is configured separately with `serial set dtr inactive`, `active`, or `on-connect`; choose the behavior your equipment requires. See the [serial command reference](command_reference.md#serial-service) for supported values.
|
||||||
|
|
||||||
|
Working serial edits are RAM-only until `serial save`. Reconfiguration or stopping the service discards pending serial-service data, so do it while traffic is idle. USB terminal baud/framing choices **do not configure physical UART1**.
|
||||||
|
|
||||||
|
## 5. Use native USB without networking
|
||||||
|
|
||||||
|
Connect the native USB port and open its CDC device with a serial terminal that asserts DTR. This creates the `usb-cdc` client, starts UART1 if needed, and requests writer ownership. If another client already owns it, USB joins as an observer.
|
||||||
|
|
||||||
|
On UART0, inspect:
|
||||||
|
|
||||||
|
```text
|
||||||
|
usb status
|
||||||
|
broker clients
|
||||||
|
```
|
||||||
|
|
||||||
|
Exactly **one client can write** to UART1; others receive the incoming serial stream. Coordinate with the current writer before requesting control with `usb request-writer`; use `usb release-writer` to give it up. A slow observer may lose its own buffered output without blocking other clients.
|
||||||
|
|
||||||
|
Native USB remains usable when network services fail. It is not the account-recovery console and does not provide uninterrupted operation through reboot.
|
||||||
|
|
||||||
|
## 6. Provision network access
|
||||||
|
|
||||||
|
On trusted UART0, create an administrator and, if desired, a separate SSH serial account:
|
||||||
|
|
||||||
|
```text
|
||||||
|
user add maint admin
|
||||||
|
user add operator user
|
||||||
|
```
|
||||||
|
|
||||||
|
Enter unique passwords at the hidden prompts. There is **no default shared login**. Account changes persist immediately. See [User administration](user_administration.md) for password rules, SSH keys, and recovery.
|
||||||
|
|
||||||
|
Inspect `wifi profiles` first to avoid replacing an existing profile. For a new device, this example configures slot 0; replace the example SSID with your trusted network's name:
|
||||||
|
|
||||||
|
```text
|
||||||
|
wifi profile set 0 0 mixed "Your network name"
|
||||||
|
wifi profile secret 0
|
||||||
|
wifi profile enable 0
|
||||||
|
wifi start
|
||||||
|
wifi status
|
||||||
|
```
|
||||||
|
|
||||||
|
Enter the Wi-Fi password at the secret prompt, not in the command line. Connection changes are asynchronous: check `wifi status` again for the resulting connection and addresses. Once correct, run:
|
||||||
|
|
||||||
|
```text
|
||||||
|
wifi save
|
||||||
|
mdns status
|
||||||
|
web certificate info
|
||||||
|
ssh host-key info
|
||||||
|
```
|
||||||
|
|
||||||
|
Record the public identity fingerprints through this trusted local connection. Wi-Fi and mDNS hostname settings have separate Save commands. For device-AP policy and password setup, see the [Wi-Fi reference](command_reference.md#wi-fi); revealing an AP secret with `wifi ap show-secret` should only be done in a private, unrecorded console.
|
||||||
|
|
||||||
|
### Browser
|
||||||
|
|
||||||
|
Open `https://` followed by the hostname reported by `mdns status`, or a reachable IP address. Compare the browser certificate fingerprint with the local `web certificate info` output **before entering credentials**. Sign in and follow the [browser guide](web_administration.md) to connect Serial and manage control. Connecting or reconnecting automatically requests the writer lease; check **Role** before typing.
|
||||||
|
|
||||||
|
### SSH
|
||||||
|
|
||||||
|
Compare the client's host-key fingerprint with `ssh host-key info` before trusting it. Use an interactive SSH connection to the device address with the desired account:
|
||||||
|
|
||||||
|
- `user` accounts open the UART1 stream.
|
||||||
|
- `admin` accounts open device administration, not UART1.
|
||||||
|
|
||||||
|
For example, replace `DEVICE_ADDRESS` with the reported hostname or IP:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssh operator@DEVICE_ADDRESS
|
||||||
|
```
|
||||||
|
|
||||||
|
A serial SSH connection starts UART1 if needed and automatically requests writer ownership. If USB or another client already owns it, SSH remains an observer and typed input is discarded, not queued for later. Check ownership before typing. Releasing the other writer does not automatically promote this session; deliberately assign it control from administration, or reconnect after the lease is free.
|
||||||
|
|
||||||
|
SSH supports interactive shells, not exec commands, file transfer, or forwarding. Use the [SSH reference](command_reference.md#ssh-serial-transport) for connection details. Inspect the writer with `broker clients` on an administration connection; coordinate before deliberately transferring control through [Broker settings](web_administration.md#serial-observer-and-writer) or `broker force-writer <client-id>`.
|
||||||
|
|
||||||
|
## If something does not work
|
||||||
|
|
||||||
|
| Symptom | First checks |
|
||||||
|
|---|---|
|
||||||
|
| No `serial-tool>` prompt | Check that you opened the bridge, not native USB; use 115200 baud and press Enter. |
|
||||||
|
| USB opens but cannot transmit | Check host DTR, `usb status`, and the current writer in `broker clients`. |
|
||||||
|
| Garbled or missing serial data | Check UART1 settings, cable crossing, ground, and RTS/CTS support. Use the safe loopback procedures before changing wiring. |
|
||||||
|
| Browser or SSH is unreachable | Inspect `wifi status`, `web status`, and `ssh status` on UART0; test a reachable numeric address to separate discovery from connectivity. |
|
||||||
|
| Hostname works on one computer only | Check that the other client supports local mDNS resolution for the required IPv4/IPv6 family. See [network discovery](command_reference.md#mdns). |
|
||||||
|
| Forgotten account password | Use normal `user password` on UART0. Do not erase or invoke unavailable-database recovery for a healthy database. |
|
||||||
|
| Lost response after a settings change | Reconnect and inspect actual state before retrying; disconnection does not cancel accepted work. |
|
||||||
|
|
||||||
|
## Partition migration and erasing
|
||||||
|
|
||||||
|
**Erasing the whole flash destroys all persisted configuration, accounts, Wi-Fi credentials, and device identities.** Record what you need to reprovision first. Do not use it as routine troubleshooting or an ordinary update step.
|
||||||
|
|
||||||
|
The first migration from the former default/factory partition layout to this project's custom OTA layout requires an erase. Migration from unrelated firmware or another layout may also require it. Only when deliberately performing such a migration or a complete destructive reset:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pio run --target erase
|
||||||
|
pio run --target upload
|
||||||
|
```
|
||||||
|
|
||||||
|
Then provision the device again and verify its new HTTPS/SSH fingerprints locally. See [flash layout](devel/flash_layout.md) for the partition map and [firmware recovery](firmware_update.md#wired-recovery) for recovery that does not routinely erase NVS.
|
||||||
+12
-11
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
This checklist is for a wireless serial cable used occasionally on a trusted network,
|
This checklist is for a wireless serial cable used occasionally on a trusted network,
|
||||||
not an Internet-facing appliance or a production-security certification.
|
not an Internet-facing appliance or a production-security certification.
|
||||||
It covers existing firmware operations only: no dependency patches, crypto upgrades,
|
It covers provisioning, daily use, credential changes, and recovery with the existing
|
||||||
secure-boot, encrypted-NVS, or eFuse changes are part of this scope.
|
firmware. It does not provide protection against physical extraction or firmware replacement.
|
||||||
|
|
||||||
## Provision once, locally
|
## Provision once, locally
|
||||||
|
|
||||||
@@ -87,14 +87,15 @@ inspect current state on UART0 rather than blindly repeating the operation.
|
|||||||
Treat a lost device or a raw flash copy as potentially exposing stored Wi-Fi secrets,
|
Treat a lost device or a raw flash copy as potentially exposing stored Wi-Fi secrets,
|
||||||
private identities, and password verifiers. Rotation is not a secure flash-erasure claim.
|
private identities, and password verifiers. Rotation is not a secure flash-erasure claim.
|
||||||
|
|
||||||
## Lightweight maintenance
|
## Maintenance precautions
|
||||||
|
|
||||||
Occasionally, and before a planned firmware update, review relevant ESP-IDF,
|
Before installing firmware, review its documented changes and security notices.
|
||||||
wolfSSL/wolfSSH, and bundled web-library upstream release/security notes.
|
If a relevant security issue is reported, stop network use until an appropriate
|
||||||
If a relevant issue appears, stop network use until a separate upgrade decision is made;
|
update or mitigation is available; native USB serial remains an option.
|
||||||
USB serial remains an option. Dependency upgrades and their validation are separate work,
|
Keep wired UART0 recovery available when updating, and do not assume an update
|
||||||
not an endless CVE audit or an implied promise that this firmware is vulnerability-free.
|
preserves configuration unless its migration guidance says so.
|
||||||
|
|
||||||
Commands and limits were checked against application sources and the [command reference](command_reference.md) at `f40c09c`.
|
See the [command reference](command_reference.md) for command details.
|
||||||
This is source-reviewed operating guidance, not a build, hardware test, security audit,
|
This checklist is operating guidance, not a security audit or verification of any
|
||||||
or verification of any individual device's flash/eFuse configuration.
|
individual device's flash/eFuse configuration. Contributor maintenance and evidence
|
||||||
|
notes are in the [developer index](devel/README.md#security-maintenance-and-evidence).
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
# User administration
|
||||||
|
|
||||||
|
Provision accounts locally first, then use the browser or authenticated admin SSH for routine administration.
|
||||||
|
|
||||||
|
Related guides: [Web administration](web_administration.md), [firmware updates](firmware_update.md), [command reference](command_reference.md#role-based-users), [security operations](security_operations.md), and the [web development contract](devel/web_administration.md).
|
||||||
|
|
||||||
|
## Choose roles and keep local recovery
|
||||||
|
|
||||||
|
| Account role | HTTPS | SSH |
|
||||||
|
|---|---|---|
|
||||||
|
| `user` | Serial terminal and status | UART1 serial stream through the broker |
|
||||||
|
| `admin` | Serial, Admin, and Settings | Device administration shell, not UART1 |
|
||||||
|
|
||||||
|
Use a separate `user` account for ordinary SSH serial work. Account roles do not override the broker's one-writer/multiple-observer rule. Admin SSH is not a serial broker client.
|
||||||
|
|
||||||
|
Keep physical **UART0 administration** available on a trusted computer. Native USB CDC is a different, network-independent UART1 data path, not an account-recovery console. Physical local access is not protected by network account authentication.
|
||||||
|
|
||||||
|
## Create the first administrator
|
||||||
|
|
||||||
|
A new device with missing user-database storage starts with a persisted empty database. There is no default shared login, credential import, or `user bootstrap` command. Existing valid user databases retain their accounts unchanged; an older role-`user` account is not silently promoted.
|
||||||
|
|
||||||
|
On UART0:
|
||||||
|
|
||||||
|
```text
|
||||||
|
user status
|
||||||
|
user list
|
||||||
|
user add maint admin
|
||||||
|
user show maint
|
||||||
|
```
|
||||||
|
|
||||||
|
Enter and confirm a unique password at the no-echo prompts. Passwords must contain **12–64 printable ASCII characters**. `Ctrl-C` cancels a prompt. Usernames must match `[a-z][a-z0-9_-]{0,15}`: 1–16 characters, starting with a lowercase letter, followed by lowercase letters, digits, underscore, or hyphen.
|
||||||
|
|
||||||
|
To generate a password instead:
|
||||||
|
|
||||||
|
```text
|
||||||
|
user add maint admin --generate
|
||||||
|
```
|
||||||
|
|
||||||
|
Use one creation form, not both for the same name. Generated passwords are displayed once: save the value in a password manager, avoid terminal recording, and do not share the output. Successful account changes persist immediately; no separate Save command is needed.
|
||||||
|
|
||||||
|
Before testing network login, compare `web certificate info` and `ssh host-key info` from trusted UART0 with the browser/SSH client's fingerprints. Follow [security operations](security_operations.md), then [sign in through HTTPS](web_administration.md#connect-and-sign-in). For SSH, connect using an interactive shell with the chosen account; exec/subsystem and file-transfer workflows are not supported.
|
||||||
|
|
||||||
|
## Add and maintain accounts
|
||||||
|
|
||||||
|
Examples for UART0 or an authenticated admin SSH shell:
|
||||||
|
|
||||||
|
```text
|
||||||
|
user add operator user
|
||||||
|
user list
|
||||||
|
user show operator
|
||||||
|
user password operator
|
||||||
|
```
|
||||||
|
|
||||||
|
The database holds at most **eight accounts**. Account summaries expose roles and public-key fingerprints, not passwords, salts, or verifiers. Passwords cannot be recovered from status output; set a replacement when necessary.
|
||||||
|
|
||||||
|
For an intentional role change or removal:
|
||||||
|
|
||||||
|
```text
|
||||||
|
user role operator admin --force
|
||||||
|
user role operator user --force
|
||||||
|
user delete operator --force
|
||||||
|
```
|
||||||
|
|
||||||
|
These are separate examples, not a sequence to run routinely. Check the target and intended access before using `--force`. The final administrator cannot be deleted or demoted. Password, role, key, and deletion changes invalidate the affected username's outstanding tickets and active web/SSH sessions; unrelated accounts are not intentionally revoked. Your own change may disconnect you before you see its result. Inspect state and test the intended login before repeating an uncertain operation.
|
||||||
|
|
||||||
|
`user password <username> --generate` displays a replacement once. Admin SSH rejects generation for its own account to avoid losing the value during revocation; use trusted UART0 or the supported browser workflow instead.
|
||||||
|
|
||||||
|
### Browser account management
|
||||||
|
|
||||||
|
Sign in as `admin`, open **Settings → Accounts**, and select the intended account for password, role, deletion, or authorized-key operations. Changes commit on successful submission; they are not RAM-only drafts awaiting a global Save.
|
||||||
|
|
||||||
|
The generated-password workflow first displays a new value without changing the account. Store it securely and acknowledge that it is saved, then explicitly submit the create/password change. Generation alone does not set a password; the saved acknowledgement is not proof that the subsequent operation succeeded. Self changes may end your login immediately.
|
||||||
|
|
||||||
|
The browser **Admin shell** has narrower account permissions than Accounts settings: status/list/show and selected interactive changes to other accounts are supported, but self changes, generated passwords, key commands, and database recovery are denied there. Use typed Accounts controls or UART0 rather than trying to bypass these restrictions.
|
||||||
|
|
||||||
|
## Authorize an SSH client key
|
||||||
|
|
||||||
|
1. On a trusted client, generate or choose an OpenSSH **Ed25519** or **ECDSA P-256** key. Keep the private key on that client; import only the single-line `.pub` public key.
|
||||||
|
2. On UART0 or authenticated admin SSH, run:
|
||||||
|
|
||||||
|
```text
|
||||||
|
user key add operator
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Paste the public-key line at the prompt. `Ctrl-C` cancels. Alternatively use the Accounts key-import controls in the browser.
|
||||||
|
4. Run `user show operator` and compare the reported key type and `SHA256:` fingerprint with the client's public key. Test a new SSH login with the intended username before closing local recovery access.
|
||||||
|
|
||||||
|
Each account supports **three keys**. Supported types are `ssh-ed25519` and `ecdsa-sha2-nistp256`; RSA and OpenSSH certificate keys are not supported. Duplicate keys within an account are rejected. The same key can be assigned to multiple accounts, but access then follows the username selected at login.
|
||||||
|
|
||||||
|
Delete by the stable index shown in `user show`, not by its position in a displayed list:
|
||||||
|
|
||||||
|
```text
|
||||||
|
user key delete operator 0 --force
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `user key clear operator --force` only when intentionally removing all that account's keys. Key changes persist immediately and revoke that account's sessions. Changing a password does **not** remove authorized keys; review both after compromise. Authorized client keys are separate from the device's SSH host identity.
|
||||||
|
|
||||||
|
## Forgotten passwords and database failure
|
||||||
|
|
||||||
|
For a healthy database, use UART0 and normal commands such as `user password maint` to restore access. First inspect `user status` and `user list`; do not erase flash or reset identities to repair a forgotten account password.
|
||||||
|
|
||||||
|
Only if database initialization has failed and the database is **unavailable**, deliberate UART0 recovery is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
user recover --force
|
||||||
|
user status
|
||||||
|
user add maint admin
|
||||||
|
```
|
||||||
|
|
||||||
|
**Recovery destroys the user database's contents and rebuilds it empty.** It refuses a healthy database, including a healthy empty database, and is unavailable over SSH/browser. It does not erase unrelated Wi-Fi/serial configuration or HTTPS/SSH identities. Recreate required accounts and authorized keys afterward. Check each command result; storage failures are not evidence of successful recovery.
|
||||||
|
|
||||||
|
`web reset --force` and `ssh reset --force` recover their respective device identities, not user accounts. Neither is a whole-device factory reset.
|
||||||
|
|
||||||
|
## Persistence and security limits
|
||||||
|
|
||||||
|
Keep a private reprovisioning record of usernames/roles, public keys, settings, and verified device fingerprints; store passwords separately. There is no supported configuration/credential/identity backup-export command. Application-only [firmware updates](firmware_update.md) preserve stored bytes, but compatibility with the new firmware still matters.
|
||||||
|
|
||||||
|
NVS credentials are not protected against physical extraction by this workflow. Password verifiers can be attacked offline, and logical replacement does not securely erase historical flash contents. Keep the device and local ports controlled, avoid Internet exposure, and follow the [security checklist](security_operations.md).
|
||||||
+52
-112
@@ -1,138 +1,78 @@
|
|||||||
# Web administration contracts
|
# Web administration
|
||||||
|
|
||||||
Current implementation contracts; [Phase 10 firmware upload is complete by explicit user acceptance on 2026-09-18](roadmap.md#phase-10--simple-admin-web-firmware-upload), confirming upload and normal operation, not specific fault or recovery checks. Phase status and executed-evidence limits belong in the [roadmap](roadmap.md#phase-8--role-based-users-and-administrative-access--complete) and [acceptance record](roadmap.md#phase8-acceptance-evidence), not in implementation timelines. [Regression procedures](user_administration_tests.md#integrated-web-administration-regression-procedure) describe checks, not results. Source is authoritative; start with the [code map](agent/code-map.md).
|
Use the browser to operate the UART1 serial terminal and, with an administrator account, manage the device. Use a trusted LAN or the device's password-protected AP—not an Internet-exposed connection.
|
||||||
|
|
||||||
## Authentication and admission
|
Related guides: [Account provisioning](user_administration.md), [firmware updates](firmware_update.md), [command reference](command_reference.md), and [security operations](security_operations.md). Protocol and implementation details are in the preserved [development contract](devel/web_administration.md).
|
||||||
|
|
||||||
- HTTPS-only, same-origin login replaces Basic authentication entirely. Four digest-only opaque sessions have absolute one-hour expiry; four pre-login challenges last 120 seconds. Login is globally bounded to five credential attempts per 60 seconds. Live sessions/challenges/tickets are not evicted to admit another client.
|
## Connect and sign in
|
||||||
- Raw tokens travel only in the host-only `__Host-` cookie with Secure, HttpOnly, SameSite=Strict, Path=/ and explicit lifetime; no Domain. Session records retain copied secret-free principals, CSRF state and nonzero, nonreused originating-session IDs. Passwords, raw cookies/tickets, verifiers and private keys never enter routine snapshots/logs/completion/display.
|
|
||||||
- Mutation admission requires current cookie/principal, strict Origin and CSRF validation; administration additionally requires current `admin`. Normal users retain serial/status but cannot invoke administration directly. Authentication POST fetches use CORS mode with fixed same-origin URLs and same-origin credentials: do not accept Origin `null` to compensate for browser no-referrer behavior.
|
|
||||||
- Session-store initialization is part of admitted HTTPS start; authentication failure gates HTTPS. Failed start/accepted stop disables and wipes session state. Logout invalidates only the originating session before socket cleanup; account mutation invalidates that account's sessions/tickets, including deletion/recreation, without revoking unrelated accounts. Currentness checks remain authoritative if best-effort notifications fail.
|
|
||||||
- Four serial tickets and two admin tickets are digest-only, single-use, 30-second, session/principal-bound records. Cookie/Origin/ticket/currentness and transport admission precede explicit WebSocket 101. Store RNG/SHA/database calls run outside short spinlocks; IDs/expiry/epochs fence stale publication without nested store/transport locks.
|
|
||||||
- HTTPD remains bounded to six sockets, two serial WebSockets, one admin WebSocket and 40 method/path handlers; LRU eviction is disabled. Sessions, sockets, tickets and the two shared remote-console slots are separate capacity limits. Optional settings/admin failures preserve unrelated routes where their initialization contract permits; UART0 and native USB remain independent of web readiness.
|
|
||||||
|
|
||||||
`web_httpd_adapter` alone accesses private IDF 5.5.0 HTTPD state. It rejects duplicate/ambiguous headers, postpones 101 until admission, and wipes consumed header scratch while preserving right-aligned unread bytes. Optional Settings registration stages descriptor/name allocations before publishing either, avoiding the pinned public registration failure path. Re-audit these private boundaries on SDK upgrades and same-version SDK patches: the version guard does not detect patches that retain the same version number. HTTPD response headers are pointer-backed, not copied; both `Set-Cookie` value buffers must remain valid and distinct through response send. Do not reuse or wipe those buffers before sending completes. Do not enable header/ticket debug logging. Auth documents, scripts and sensitive responses are no-store with CSP/no-referrer/frame-denial protections; authored loader changes require matching CSP hashes. Generated assets are not a normal documentation/build output.
|
1. Provision an account through physical **UART0** first; see [user administration](user_administration.md). Native USB CDC carries UART1 data and is not the administration console.
|
||||||
|
2. Connect to the device's network and open its HTTPS address (port 443), using its configured hostname or reachable IP address. The terminal assets are served by the device; Internet access is not required.
|
||||||
|
3. Before entering credentials, compare the browser's SHA-256 certificate fingerprint with `web certificate info` on trusted UART0. Do not blindly accept an unexpected certificate warning.
|
||||||
|
4. Sign in with your account username and password. Both `user` and `admin` can use Serial; only `admin` can use Admin and Settings. SSH client keys do not replace the browser password.
|
||||||
|
|
||||||
## Terminal and console ownership
|
A login expires **one hour after sign-in**, even while active. There are four login-session slots, two browser serial connections, and one browser admin connection; these are separate limits. Login attempts are globally limited to five per 60 seconds. If admission fails, close unused connections or sign out unused sessions and wait rather than repeatedly submitting credentials.
|
||||||
|
|
||||||
Switching Serial/Admin/Settings never creates a second serial client, requests/releases the writer lease or reconnects serial. Hidden connected terminals keep draining; only selected terminal input is sent. Both browser terminals have separate 5,000-line scrollback and 64 KiB callback-accounted pending-output bounds with visible browser-drop counts. Admin input is bounded to 4 KiB admission and 512-byte frames. Admin open/reopen is explicit; admin close is isolated. Disconnect pauses serial reconnect but retains login; logout/expiry/revocation closes affected sockets normally.
|
**Disconnect** closes the serial connection and pauses its automatic reconnect; **Connect** resumes it. Neither signs you out. Use **Sign out** when finished. If sign-out is not confirmed because the network failed, do not assume server-side logout succeeded; retry when reachable.
|
||||||
|
|
||||||
The document binds terminal state to its first validated username/role/session-stable CSRF tuple. A different identity requires a clean document before showing retained output. Pagehide fences sockets/work and hides scrollback until same-session revalidation; admin is not automatically reopened. Fit readiness caches only success and uses at most three generation-fenced animation-frame retries per external request.
|
## Serial: observer and writer
|
||||||
|
|
||||||
`admin_ssh_console` is the sole `esp_console_run()` caller for UART0, admin SSH and browser admin. Two fixed remote-console slots are shared across SSH/browser, not two per transport. Queue records carry copied principals and transport-qualified generation tokens. Owner currentness is checked outside console locks before commands and during prompts (250 ms polling plus validation/scheduling latency), then identity is rechecked. This is operation admission, not cancellation or rollback of arbitrary executing handlers. Consumed output and retired prompt/history state are wiped.
|
All transports share one physical RS-232 port. Exactly one broker client can write; other clients observe the incoming UART1 stream.
|
||||||
|
|
||||||
HTTPD alone owns browser-admin socket IO and its 1,552-byte PSRAM-only payload. A 20 ms timer queues at most one owner poll, not a new task. Close uses current-owner shutdown, not queued reusable `sock_db *` pointers. Detach fences submitters; queued state retires only after successful HTTPD destruction. Failed destruction retains ownership and prevents unsafe restart/reuse.
|
Connecting or reconnecting Serial automatically requests control. If no other client owns the writer lease, your connection becomes writer. Check **Role** after every reconnect; **Release control** applies to the current connection, not future reconnects.
|
||||||
|
|
||||||
### Browser-shell policy
|
- Check **Role**, **Active writer**, and the connection status before typing. The dashboard's Writer/Observer role is not your account's `admin`/`user` role.
|
||||||
|
- Choose **Request control** to request the writer lease. An administrator account does not automatically make this serial connection the writer. If another client owns control, coordinate with that operator.
|
||||||
|
- Type only when writer status is confirmed. Observer input is disabled.
|
||||||
|
- Choose **Release control** when finished transmitting so another client can write.
|
||||||
|
- Administrators can inspect **Broker** settings or the writer/client quick views and explicitly assign control to a selected current client. Refresh and deliberately reselect after a stale-target warning. Transfer cannot recall bytes already accepted for transmission.
|
||||||
|
|
||||||
Typed Settings permissions do not expand shell permissions. Parsed canonical arguments, not raw prefixes or completion suggestions, control admission:
|
Slow observers can lose their own output without blocking other clients. The browser has bounded output buffering and 5,000 lines of scrollback per terminal; it is not a durable recording. Observe reported drop counts when completeness matters. Serial transport remains binary-transparent: there are no in-band administration escape sequences.
|
||||||
|
|
||||||
- Browser `web` permits only `web status`, `web stop`, and exact `web certificate rotate --force`; certificate info/reset, diagnostics/performance and other web forms are denied.
|
## Serial, Admin, and Settings
|
||||||
- Browser `wifi` uses the canonical command handler, including bare `wifi` (status), profile/AP settings, persistence, lifecycle and network diagnostics. Profile/AP secret entry uses the shared hidden prompt: no input echo, history or completion of secret bytes; cancellation, disconnect and failed currentness discard the input. Explicit `wifi ap show-secret` reveals the AP password only in the invoking admin terminal; routine status and completion do not reveal credentials. Browser `mdns` still permits only exact `mdns status`. Root and `wifi`-prefixed ping/traceroute/nslookup support the same `-4`/`-6` and device-local IPv6 scope syntax as UART0/admin SSH; see [dual-stack diagnostics](command_reference.md#dual-stack-network-diagnostics). They run on the shared dispatcher, not HTTPD or the client browser; no extra endpoint or permission expansion is involved.
|
|
||||||
- Browser `user` permits status/list/show and interactive add/password plus forced role/delete for **other accounts only**. Self changes, generated passwords, key commands and recovery are denied there; typed Accounts supports the separately bounded self/generated/key workflows.
|
|
||||||
- Browser SSH stop/disconnect/reset and host-key mutation are denied; typed SSH Settings has its own safe owner path. Do not claim full browser-shell parity.
|
|
||||||
- Browser `reboot` and owner-relative `exit` are supported. First-admin provisioning uses normal `user add` on UART0; unavailable-database recovery is UART0-only. The legacy `user bootstrap` and web credential commands no longer exist.
|
|
||||||
|
|
||||||
Wi-Fi changes use the existing canonical manager path, not deferred browser drain/acknowledgement. Stop, reconnect, profile/AP edits, load/defaults/reset can disconnect network clients before output arrives. A lost result does not cancel admitted work: reconnect and inspect before retrying; use UART0 if networking is unavailable. Changes are RAM-only until `wifi save`, except `wifi reset`, which also persists defaults. Native USB remains independent UART1 access, not an administration console.
|
Switching views does **not** reconnect Serial or request/release its writer lease. Hidden connected terminals continue receiving output; keyboard input goes only to the selected terminal.
|
||||||
|
|
||||||
Deferred self-affecting shell actions use the existing bounded drain/control path (up to ten seconds plus a short delay), not guaranteed peer delivery. Browser certificate rotation hands a typed action after drain/200 ms to the existing 12 KiB dispatcher, never crypto/NVS on the 4 KiB control stack. Pending input is discarded through execution, and an executing slot remains reserved across self-detach. UART0/admin SSH retain canonical recovery actions.
|
- **Serial** is the connected equipment's UART1 stream.
|
||||||
|
- **Admin** is the ESP32 command console, not the connected equipment. Choose **Open admin** explicitly. Closing Admin leaves Serial connected. One browser admin connection and two remote-console slots shared with admin SSH bound availability.
|
||||||
|
- **Settings** provides typed controls for Serial settings, Accounts, Network, Display, Broker, SSH, and HTTPS / Reboot. Opening a page, selecting a client, or opening a quick view does not apply changes.
|
||||||
|
|
||||||
## Typed settings API and operation lifetime
|
Use the [command reference](command_reference.md) for commands, but do not assume full UART0/SSH parity in the browser shell. Browser account commands are limited to selected operations on other accounts; use **Accounts** for supported self/password-generation/key workflows. Browser SSH identity/service mutations belong in typed **SSH** settings. Browser `web` commands are restricted to status, stop, and certificate rotation; recovery remains on UART0 or reachable admin SSH. Wi-Fi commands include hidden secret prompts and can immediately disconnect you. Avoid explicit secret-display commands in recorded/shared terminals.
|
||||||
|
|
||||||
All routes below are under `/api/settings/`. Each domain has bodyless GET snapshot plus GET/POST operation unless noted. Requests reject queries, malformed/oversized/duplicate/unknown fields and inappropriate bodies; mutation routes require bounded JSON with current admin/Origin/CSRF. GET allows absent Origin but rejects mismatch. Snapshot contention/unavailability is not an empty successful projection. HTTPD performs bounded admission/encoding only; canonical mutations run on the existing four-entry administration dispatcher using **IDs only**, not command strings, credentials, request pointers or socket handles.
|
## Apply, Save, and persistent state
|
||||||
|
|
||||||
| Domain and source | Snapshot / operation suffix | Request / snapshot / result buffer bounds (bytes) |
|
| Area | Operator rule |
|
||||||
|---|---|---|
|
|---|---|
|
||||||
| `web_serial_settings`, serial service | `serial` / `serial-operation` | 256 / 256 / 96 |
|
| Serial settings | Apply and Defaults change working RAM. Save persists the device's working values, not unsent browser drafts. Reconfiguration or stop discards pending serial-service data, though broker clients and writer ownership remain. |
|
||||||
| `web_account_settings`, user database | `accounts` / `account-operation` | 768 / 1024 (accounts), 512 (keys) / 96 |
|
| Network | Wi-Fi edits require explicit Save; hostname/mDNS settings have separate persistence. Start/Stop also change the RAM boot-enabled policy. Save only after checking the desired working state. |
|
||||||
| `web_network_settings`, Wi-Fi + mDNS | `network` / `network-operation` | 768 / 2304 / 128 |
|
| Display | Apply changes working configuration; Save persists it. Load reads saved state; Reset persists defaults. |
|
||||||
| `web_display_settings`, local status UI | `display` / `display-operation` | 256 / 128 / 96 |
|
| Accounts and authorized keys | Successful mutations are committed immediately; there is no separate account Save. They revoke the affected account's network sessions. |
|
||||||
| `web_broker_settings`, session broker | `broker` / `broker-operation` | 256 / 2048 / 96 |
|
| HTTPS/SSH identities | Successful replacement persists immediately. Rotation is not an account-password change. |
|
||||||
| `web_ssh_settings`, SSH owner/security | `ssh` / `ssh-operation` | 256 / 768 / 96 |
|
| Broker/service controls | These operate on live connections/services; they are not saved configuration profiles. |
|
||||||
| `web_lifecycle_settings`, HTTPS owner/security | `lifecycle` / `lifecycle-operation` | 256 / 320 / 96 |
|
|
||||||
|
|
||||||
Mutation bodies use at most four receive attempts. Each domain retains one original-login-bound pending/result slot, nonreused operation IDs and an executing reservation. Results are replaceable, login-isolated observations, not durable history or idempotency keys. Another tab can replace a completed result. Dispatcher currentness and the 30-second dequeue deadline precede canonical owner admission. **Admitted work may finish after logout, revocation, timeout or navigation.** A failed response, 401 or disappearance of a result proves neither success nor cancellation; inspect current state before any deliberate retry.
|
Load, Defaults, and Reset are not synonyms. Read each confirmation and the [command reference](command_reference.md) before discarding configuration. Network quick views intentionally omit password controls; use the full Network page. USB host baud/framing selections do not configure UART1; use Serial settings.
|
||||||
|
|
||||||
Accounts and Network each use a one-second timer to cancel/wipe queued, non-executing secrets after 30 seconds plus scheduling latency. Shared input wipes on dequeue/rejection; executing locals wipe on return. This is not a hard wall-clock erasure/execution guarantee. Other domain deadlines are dequeue checks, not new cancellation timers. UI fences late/session-changed responses, bounds request/result checking to 15 seconds and never automatically replays/restores mutations. Serial/Accounts/Network/Display/Broker use bounded automatic checks where implemented (at most ten one-second GETs); SSH and HTTPS/Reboot require manual Check Result/Refresh.
|
Save desired working settings before reboot or [firmware upload](firmware_update.md). Saved configuration survives an ordinary reboot; unsaved RAM settings and browser drafts may not.
|
||||||
|
|
||||||
### Serial and Display
|
## Disruptive changes and uncertain results
|
||||||
|
|
||||||
Serial Apply/Defaults change RAM; Save persists working device state, not browser drafts. Load follows canonical defaults/fallback behavior. Reset uses canonical apply/persist/best-effort rollback. Reconfiguration/stop discards serial-service RX/TX and task-local pending data but preserves broker clients, lease and already-fanned output. USB with DTR may restart a stopped service. `/api/status` uses a zero-wait consistent serial snapshot (`running:null` if unavailable).
|
Plan a maintenance window and keep UART0 available before changing networking, identities, services, or firmware.
|
||||||
|
|
||||||
Display accepts dim/off timeouts 0–86400 seconds and an expected nonzero configuration generation. All writers, including CLI/legacy Apply, share a zero-wait owner reservation; NVS runs outside critical sections. Save stabilizes selected RAM; Load retains canonical fallback without rewriting NVS; Reset commits defaults **before** RAM publication, leaving RAM unchanged on failure. Buttons/diagnostic holds update activity, not configuration generation. Configuration needs an available UI task, not a physically present panel, and never takes over I2C.
|
- **Network:** profile/AP changes, stop, or reconnect may cut off the result. An accepted operation is not proof of Wi-Fi association, address acquisition, or name resolution. Restore connectivity and inspect actual state before retrying.
|
||||||
|
- **Accounts:** changing your own password, role, or keys may revoke your login before the result appears. See [account management](user_administration.md).
|
||||||
|
- **HTTPS stop/restart:** closes all web logins and both browser terminal routes. A stopped web service cannot be restarted through its own page; use `web start` on UART0 or still-reachable admin SSH.
|
||||||
|
- **HTTPS certificate rotation:** verify the new fingerprint through UART0 before renewing browser trust. A failure after storage commit can leave a new stored identity even if restart fails; a retained server may still serve the old certificate. Do not repeat rotation blindly.
|
||||||
|
- **SSH controls/host-key rotation:** can close SSH sessions. Verify a changed host-key fingerprint with `ssh host-key info` on UART0 before updating client trust. HTTPS is not inherently restarted by SSH rotation.
|
||||||
|
- **Reboot:** interrupts every transport, including native USB and UART operation during restart. Reconnect and sign in explicitly afterward.
|
||||||
|
- **Firmware upload:** can stall or drop browser sessions before reboot. Follow the separate [firmware procedure](firmware_update.md).
|
||||||
|
|
||||||
### Accounts and authorized keys
|
A timeout, lost response, navigation, logout, or revoked login does **not** cancel already admitted work. Settings results are temporary observations, not a durable operation history. Use **Check Result**/**Refresh** where offered (SSH and HTTPS / Reboot require manual checking), then inspect actual device state. Never replay a destructive action merely because its acknowledgement disappeared.
|
||||||
|
|
||||||
The database owns eight accounts, final-admin protection and conditional target username/account-ID/auth-generation checks inside its mutation lock. Create/password/role/delete and key mutations share canonical commit/invariant logic. HTTPD uses zero-wait secret-free projections, not blocking CLI snapshots. Successful mutations request target-only web/SSH revocation; self mutation can revoke access before result retrieval.
|
## Recovery
|
||||||
|
|
||||||
Separate bodyless POST `accounts/generate-password` returns one 24-character value without mutation or retained retrieval. The browser uses a 60-second context-bound saved acknowledgement before separate submission; this is UX, not delivery proof, and JavaScript strings cannot be securely wiped. POST `accounts/keys` returns only slot/type/SHA-256 fingerprints for the selected identity. Key add/delete/clear share the operation slot; public-key import is at most 384 decoded text bytes in the 768-byte request and uses canonical Ed25519/P-256 validation. Three stable key indices may be sparse; never interpret response-array position as an index. No stored key blob, verifier or password is exported.
|
1. If the page or session fails, restore network access, reload, and sign in again. Check identity warnings locally before entering credentials.
|
||||||
|
2. If networking or HTTPS is unavailable, use physical UART0 for status and repair. Reachable admin SSH can also recover HTTPS with `web stop` / `web start` when appropriate.
|
||||||
|
3. Repair forgotten passwords with normal UART0 account commands. `user recover --force` is destructive unavailable-database recovery, not a password-reset shortcut; see [user administration](user_administration.md).
|
||||||
|
4. If a firmware update prevents boot or web access, use [wired recovery](firmware_update.md#wired-recovery) as described in the firmware guide.
|
||||||
|
|
||||||
### Network
|
Native USB remains network-independent UART1 access when network services fail, but cannot repair accounts or identities and does not promise uninterrupted data through flash operations or reboot. Keep local ports physically controlled. See [security operations](security_operations.md) for backup limits, identity recovery, and physical-extraction risks.
|
||||||
|
|
||||||
Wi-Fi config/runtime is one zero-wait consistent projection; mDNS is a separate projection, not cross-domain atomic authorization. Four stable profiles carry enabled/priority/security/SSID/password-configured metadata. `mixed` means WPA2-or-stronger, not open. `announced` is expected STA announcement, not verified DNS. Runtime `ipv6_addresses` contains up to three preferred addresses from the same snapshot as the availability flags, encoded as eight lowercase four-digit hextets without a zone. Network settings display separate link-local, ULA and GUA lists; absent groups show `none`. Link-local access needs the client's interface scope, and address presence does not assert a route or Internet reachability. Encoding uses the existing snapshot only, not netif/driver calls on HTTPD; the 2304-byte response buffer adds 256 bytes of bounded stack storage.
|
|
||||||
|
|
||||||
SSID wire values are reversible **bytes**, maximum 32: printable ASCII, standard single-character JSON escapes and `\u00HH`, with no raw non-ASCII, non-byte Unicode or surrogates. NUL/non-UTF-8 round-trip. UI text is UTF-8-encoded before byte serialization; exact reversible text or literal hex preserves existing bytes and BOM, with no silent replacement/truncation.
|
|
||||||
|
|
||||||
Flat operations select one domain/target:
|
|
||||||
|
|
||||||
- `wifi-patch` + Wi-Fi generation: optional boot policy, AP policy/channel/SSID/password/clear; `profile-patch` additionally selects stable profile 0–3 and optional enabled/priority/security.
|
|
||||||
- `wifi-save|wifi-load` require Wi-Fi generation. `start|stop|reconnect|next-profile` use canonical connection controls, not explicit selected-profile connection.
|
|
||||||
- `mdns-set` requires its generation and suffix; `mdns-save|mdns-load|mdns-defaults` require its generation. Suffix is 1–55 lowercase ASCII letters/digits/hyphens, no leading/trailing hyphen, producing `sak-<suffix>`.
|
|
||||||
|
|
||||||
Omitted fields preserve current bytes under the Wi-Fi mutex. Password Keep omits, Replace is 8–63 printable ASCII bytes, disabled-STA Clear is explicit; replacement and clear cannot coexist. AP clear is always denied, even while off. No saved PSK or length is returned. Wi-Fi compare/merge/whole-candidate validation and required queue admission precede RAM publication. Save stabilizes selected bytes; Load reads stored configuration only and cannot generate fallback secrets. No browser Wi-Fi reset/default-secret/export route exists.
|
|
||||||
|
|
||||||
Edits require explicit Save. Disabled-profile-only edits do not restart active radio; enabled-policy/AP changes follow canonical asynchronous restart. Start/Stop change RAM enabled-at-boot; Reconnect/Next are no-ops while stopped. Next follows enabled priority order with wrap; editor selection is not connection selection. mDNS independently owns generation/persistence; the Wi-Fi manager owns radio/reannouncement. mDNS RAM change followed by queue failure is reported without rollback; its Load may choose deterministic MAC-derived defaults.
|
|
||||||
|
|
||||||
Network result fields are `id/action/state/error`: `pending`, `accepted`, `ok`, `failed`, `cancelled`, `stale`, `invalid`, `loaded_defaults`, `applied_not_queued`, or login-isolated `idle`. `accepted` means RAM/owner-queue admission, **not** association/DHCP/DNS completion. `ok` denotes explicit Save. Loss can precede response; recover via STA/AP and inspect before retrying. UART0 administers recovery; native USB provides network-independent UART1, not an admin console.
|
|
||||||
|
|
||||||
### Broker and contextual controls
|
|
||||||
|
|
||||||
Broker management atomically copies eight compact client rows, writer and lease generation under one zero-wait mutex acquisition, without reading UART payloads or consuming events. Conditional assignment compares the selected nonzero target ID and lease generation under the same force-writer lock before any effects. Stale/absent targets conflict; same-current-target assignment is a no-op only after validation. Accepted serial TX is not recalled by transfer.
|
|
||||||
|
|
||||||
IDs have three slot bits and 29 generation bits; exhausted client slots retire until reboot rather than wrap. The separate 32-bit lease version saturates at UINT32_MAX, survives counter clear and advances on grant/release/revoke before advisory event delivery. Forced transfer can advance twice; it is not a count. Saturation blocks typed assignment but leaves ordinary request/release/disconnect and canonical recovery force available. Reboot invalidates old browser sessions.
|
|
||||||
|
|
||||||
Serial/Wi-Fi/Broker quick views reuse one nonmodal settings host and existing controllers/drafts, not parallel editors. Hover/focus/click/tap never mutate. Network quick mode excludes password controls. Full-page drafts are protected; promotion preserves controller/selection, departure fences reads/timers without cancelling admitted work. Dismissal restores the prior terminal without stealing unrelated focus; explicit Escape/Close returns focus to its trigger.
|
|
||||||
|
|
||||||
Broker contextual reads use one timer five seconds after successful completion and a five-second whole-read deadline, stopping on errors/uncertainty/departure. Explicit target/lease selection is not rebased by refresh. Sticky stale/absence latches require deliberate reselection even if later snapshots match again. Confirmation captures exact versions before session revalidation; guarded aria-disabled controls preserve focus during updates.
|
|
||||||
|
|
||||||
## Service lifecycle and identity rotation
|
|
||||||
|
|
||||||
Service generations are saturated, distinct from identity and session generations, and advance on admitted canonical lifecycle attempts including failures; counter clear/reinit cannot revive stale versions. Public zero-wait metadata authorizes nothing. Conditional controls compare under the canonical owner reservation; no snapshot-check/unconditional-mutate gap is allowed. Reboot invalidates originating logins and boot-local reservation IDs.
|
|
||||||
|
|
||||||
### HTTPS and reboot
|
|
||||||
|
|
||||||
GET `lifecycle` has seven fields: `generation`, `running`, `transitioning`, `controllable`, `identity_generation`, `fingerprint`, `rotatable`. Stored HTTPS fingerprint is 64 lowercase hex SHA-256 digits. Stop/restart/reboot require exactly action + service generation; rotate also requires identity generation. Unknown/duplicate/escaped/coerced fields and zero/saturated versions reject. Unavailable identity yields generation zero/empty fingerprint/not rotatable without removing ordinary service controls.
|
|
||||||
|
|
||||||
Self-cutting HTTP actions use **successful synchronous send return → one nonreused-ID HTTPD callback → existing dispatcher**. Send return is not browser receipt. The callback never waits or performs lifecycle work, and captures no request/fd/reusable operation pointer. A two-second ACK admission deadline and post-validation 30-second dequeue deadline are not execution bounds. Accepted-but-lost HTTPD work retains one reservation until callback arrival or successful HTTPD destruction; failed destruction cannot release it or accumulate more callbacks. The original login/current admin is revalidated before owner admission.
|
|
||||||
|
|
||||||
`web_server_stop_current()`/`web_server_restart_current()` compare and reserve under the server mutex. Restart retains transition ownership through stop/start; failed stop skips start, failed cleanup retains handles and canonical recovery, and reinit preserves the error. `web_server_reboot_current()` reserves the HTTPS generation then uses canonical `esp_restart()` outside locks, not an HTTPD stop wait or self-console cleanup.
|
|
||||||
|
|
||||||
`web_server_replace_identity(service_generation, identity_generation, reset, &committed)` reserves **service before identity**, before any crypto/storage. Both nonzero generations mean conditional healthy-running rotation; both zero retain CLI semantics; conditional Reset is invalid. Canonical CLI/browser-shell rotation and direct security rotate/reset share nonreused task-owner-bound identity reservation tokens. Only the owner can replace once/release; exhaustion fails closed until reboot. Crypto/NVS run outside service/security mutexes and spinlocks; commit precedes live publication and old-key wipe. Reservation remains held while reserved start copies the committed pair.
|
|
||||||
|
|
||||||
HTTPS ordering is **commit → stop → restart**. Precommit generation/RNG/storage failure leaves identity, HTTPD and logins unchanged (service version may advance after admission). Postcommit lifecycle failure never rolls back identity; failed stop can leave old served and new stored fingerprints different. Running replacement restarts HTTPS; canonical stopped rotation stays stopped, while CLI TLS-only reset can recover unavailable identity and starts a stopped service. Browser has no Reset/recovery/export action.
|
|
||||||
|
|
||||||
Save drafts; rotation/restart invalidates all web logins and closes both browser routes. Inspect `web certificate info` through trusted UART0, verify fingerprint before renewing trust, then reload/sign in freshly. Accepting a warning alone is not trusted verification. Use canonical UART0/admin SSH `web stop` / `web start` for retained-server recovery. Network/SSH/USB are not stopped by HTTPS-only operations; whole-device reboot interrupts all transports and loses unsaved RAM.
|
|
||||||
|
|
||||||
### Application firmware upload
|
|
||||||
|
|
||||||
The admin-only firmware card is in **Settings → HTTPS / Reboot**. It sends a raw `POST /api/firmware` with `application/octet-stream`, known length, session cookie, same-origin Origin and `X-CSRF-Token`; it is not a JSON Settings operation or dispatcher/result-slot workflow. Standard SDK OTA APIs stream through a 4 KiB buffer to the inactive application slot and select it only after validation/currentness checks. Only that slot and `otadata` are written; NVS/data partitions are untouched.
|
|
||||||
|
|
||||||
One upload reserves HTTPS lifecycle/identity and excludes ordinary software reboot. The synchronous HTTPD handler can stall/drop browser serial/admin sessions. Its 120-second total receive-loop and ten-second stall checks are not preemptive flash deadlines or uninterrupted-traffic guarantees. Successful response send schedules a delayed reboot, not proof of peer receipt. Failed response after commit leaves the image selected, schedules no automatic reboot, releases reservations for manual reboot, and latches subsequent admissible uploads to 409 `firmware_selected_reboot_required` until reset. Never automatically retry an uncertain outcome.
|
|
||||||
|
|
||||||
See [firmware update](roadmap.md#phase-10--simple-admin-web-firmware-upload) for wired-first installation, exact image selection, manual recovery, acceptance limits and reusable future hardware regression checks. Phase 10 is accepted; there are no signature/version policies or automatic rollback.
|
|
||||||
|
|
||||||
### SSH
|
|
||||||
|
|
||||||
GET `ssh` supplies service/session state plus identity generation, fixed P-256 algorithm, unpadded OpenSSH `SHA256:` base64 fingerprint and rotatable flag. Service actions use exactly `action`, `generation`, `target`; rotate adds `identity_generation` and requires target zero. Start/stop and exact-session disconnect use published state, saturated service generation and the command mutex; exhausted SSH session slots retire rather than wrap. Disconnect success is an owner close request, not completed teardown. HTTPD never calls wolfSSH or waits for the SSH task.
|
|
||||||
|
|
||||||
`ssh_transport_replace_identity()` compares/reserves **service then identity before stop or storage**, holding the existing command mutex across **stop → generate/commit/publish → conditional restart**. Canonical UART0/deferred admin-SSH rotation/reset and direct security mutations share the task-bound nonreused identity reservation. Crypto/NVS run outside security locks/spinlocks. Both nonzero versions select conditional rotation (also while stopped); both zero preserve canonical semantics, with no conditional Reset.
|
|
||||||
|
|
||||||
Failed stop/timeout skips mutation and never starts again; pending owner work is not cancelled. Persistence failure after successful stop may already have disconnected clients and attempts old-identity restart if previously running. Committed replacement remains committed if restart fails. Stopped rotation stays stopped; canonical reset can recover unavailable material/start stopped SSH. Only the SSH owner frees runtime context after all slots retire, before clearing cleanup admission; start rejects orphan handles. wolfSSH copies DER into its context; caller/candidate/superseded private bytes are wiped.
|
|
||||||
|
|
||||||
SSH changes leave invoking HTTPS available, so they use the ordinary ID-dispatcher/result path, not HTTPS's self-cutting ACK gate. UI confirms both versions and all-SSH/session scope, retains stale selection and manual 15-second/no-replay flow. A failed result may represent partial effects. Verify new trust through UART0 `ssh host-key info` before updating known_hosts; no HTTPS relogin is inherently required. Browser Reset/recovery/key export and user authorized-key changes are separate, excluded from host rotation.
|
|
||||||
|
|
||||||
## Diagnostics, recovery and scope boundaries
|
|
||||||
|
|
||||||
See [admission diagnostics](web_admission_diagnostics.md), [ordinary HTTPS idle cleanup](https_idle_cleanup.md), [throughput diagnostics](web_throughput_diagnostics.md) and [legacy storage compatibility](roadmap.md#phase8-legacy-credential-compatibility). Broker read means transport handoff, not peer receipt; capture non-consuming counters before disconnect. TLS `-0x004C` is generic NET_RECV_FAILED, not evidence of OOM. Resource minima and counter observations require attribution, not inferred causes.
|
|
||||||
|
|
||||||
Phase 8D.15's dedicated typed network-diagnostics UI/API was removed: diagnostics remain shell-based, subject to frontend policy. The unimplemented 8D.19 ordinary browser-session/native-USB control expansion was removed; existing SSH controls remain. No full shell parity, browser identity recovery/reset/export, encryption or secure boot is implied by Phase 8 acceptance. Phase 10 application upload is complete by explicit user acceptance of upload and normal operation; unreported fault, NVS-comparison, power-loss and recovery checks are not implied. UART0 is the administrative recovery authority; native USB is binary-transparent, network-independent UART1 access. Neither permits bypassing the broker's single writer or recalling already-admitted work.
|
|
||||||
|
|||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
# Hardware wiring
|
# Hardware wiring
|
||||||
|
|
||||||
This document records the GPIO assignments for the initial hardware profile. Additional profiles should use separate sections rather than treating this GPIO map as universal.
|
Use these connections for the hardware profile below. Do not apply this GPIO map to another board without checking its schematic and pin assignments.
|
||||||
|
|
||||||
## Profile: ESP32-S3-DevKitC-1 N16R8 with Adafruit MAX3243
|
## Profile: ESP32-S3-DevKitC-1 N16R8 with Adafruit MAX3243
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ The breakout's `OFF` pin drives the MAX3243 active-low `!FORCEOFF` input. High o
|
|||||||
|
|
||||||
GPIO8 and GPIO9 are not adjacent on the official J1 header. GPIO3 and GPIO46 lie between them and are boot-strapping pins, so follow the printed GPIO labels rather than counting header positions.
|
GPIO8 and GPIO9 are not adjacent on the official J1 header. GPIO3 and GPIO46 lie between them and are boot-strapping pins, so follow the printed GPIO labels rather than counting header positions.
|
||||||
|
|
||||||
### Development and native USB connectors
|
### Administration and native USB connectors
|
||||||
|
|
||||||
| DevKit connector | ESP32-S3 path | Typical Linux device | Purpose |
|
| DevKit connector | ESP32-S3 path | Typical Linux device | Purpose |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
@@ -100,9 +100,9 @@ The USB-to-UART bridge's DTR/RTS controls serve automatic boot/reset and do not
|
|||||||
|
|
||||||
Native USB CDC DTR controls the lifetime of the `usb-cdc` broker client but is not forwarded to physical DE-9 DTR. Physical DTR follows the `serial` configuration. CDC RTS is status information only; GPIO15/DE-9 RTS remains UART1 receive flow control when `flow=rts-cts` is enabled.
|
Native USB CDC DTR controls the lifetime of the `usb-cdc` broker client but is not forwarded to physical DE-9 DTR. Physical DTR follows the `serial` configuration. CDC RTS is status information only; GPIO15/DE-9 RTS remains UART1 receive flow control when `flow=rts-cts` is enabled.
|
||||||
|
|
||||||
## Phase 7A OLED and button wiring
|
## OLED and button wiring
|
||||||
|
|
||||||
Phase 7A hardware validation used the following connections for the 128×64 I²C OLED and three local buttons. The selected module acknowledges at 7-bit I²C address `0x3c` (8-bit `0x78` write / `0x79` read) and has separate yellow rows 0–15 and blue rows 16–63, divided by a narrow physical black separator:
|
Use the following connections for the 128×64 I²C OLED and three local buttons. The selected module acknowledges at 7-bit I²C address `0x3c` (8-bit `0x78` write / `0x79` read) and has separate yellow rows 0–15 and blue rows 16–63, divided by a narrow physical black separator:
|
||||||
|
|
||||||
| Device connection | ESP32-S3 connection | Electrical behavior | Purpose |
|
| Device connection | ESP32-S3 connection | Electrical behavior | Purpose |
|
||||||
|---|---:|---|---|
|
|---|---:|---|---|
|
||||||
@@ -133,8 +133,8 @@ Before applying power, verify whether the module already includes SDA and SCL pu
|
|||||||
|
|
||||||
## Electrical verification
|
## Electrical verification
|
||||||
|
|
||||||
See [Electrical tests](electrical_tests.md) for Phase 7 OLED/button bring-up, persistent aging settings, optional-display and stuck-button fault recovery, safe loopback wiring, polarity checks, UART flow-control verification, and session-broker/concurrent-transport testing.
|
See [Electrical tests](electrical_tests.md) for OLED/button checks, persistent dim/off settings, optional-display and stuck-button fault recovery, safe loopback wiring, polarity checks, UART flow-control verification, and session-broker/concurrent-transport testing.
|
||||||
|
|
||||||
## Future hardware profiles
|
## Using a different board or transceiver
|
||||||
|
|
||||||
Alternative boards—such as the LILYGO T-Display-S3—or different RS-232 transceivers require separate profiles. Review display, buttons, USB, flash/PSRAM wiring, boot-strapping pins, and onboard peripherals before assigning GPIOs.
|
Alternative boards—such as the LILYGO T-Display-S3—or different RS-232 transceivers require separate profiles. Review display, buttons, USB, flash/PSRAM wiring, boot-strapping pins, and onboard peripherals before assigning GPIOs.
|
||||||
|
|||||||
+14
-11
@@ -6,7 +6,7 @@ This is a native, editable schematic with actual connected nets, not an image
|
|||||||
or a SKiDL-only netlist. **`serial-carrier.kicad_pcb` now contains a routed,
|
or a SKiDL-only netlist. **`serial-carrier.kicad_pcb` now contains a routed,
|
||||||
82 × 80 mm two-copper-layer A1 carrier draft with a top-left antenna notch,
|
82 × 80 mm two-copper-layer A1 carrier draft with a top-left antenna notch,
|
||||||
USB toward the bottom and RS-232 toward the top.** It is **not fabrication-ready**.
|
USB toward the bottom and RS-232 toward the top.** It is **not fabrication-ready**.
|
||||||
See **`pcb-draft-notes.md`** for placement, chosen dimensions/rules, provisional
|
See **[pcb-draft-notes.md](../../docs/devel/hardware/PCB/pcb-draft-notes.md)** for placement, chosen dimensions/rules, provisional
|
||||||
mechanical assumptions and validation. A review drawing is `pcb-placement-draft.pdf`.
|
mechanical assumptions and validation. A review drawing is `pcb-placement-draft.pdf`.
|
||||||
|
|
||||||
The schematic contains the complete ESP32 development-board interface, MAX3243
|
The schematic contains the complete ESP32 development-board interface, MAX3243
|
||||||
@@ -22,24 +22,27 @@ the carrier. All 3.3 V and ground connections are explicit named nets.
|
|||||||
| `serial-carrier.kicad_pro`, `serial-carrier.kicad_sch` | Native KiCad project and A3 single-sheet schematic |
|
| `serial-carrier.kicad_pro`, `serial-carrier.kicad_sch` | Native KiCad project and A3 single-sheet schematic |
|
||||||
| `serial-carrier.pdf` | Schematic review/print export |
|
| `serial-carrier.pdf` | Schematic review/print export |
|
||||||
| `serial-carrier.kicad_pcb` | Routed 82 × 80 mm stepped, two-copper-layer PCB draft |
|
| `serial-carrier.kicad_pcb` | Routed 82 × 80 mm stepped, two-copper-layer PCB draft |
|
||||||
| `pcb-draft-notes.md`, `pcb-placement-draft.pdf` | PCB design decisions/limits and 1:1 placement review |
|
| [pcb-draft-notes.md](../../docs/devel/hardware/PCB/pcb-draft-notes.md), `pcb-placement-draft.pdf` | PCB design decisions/limits and 1:1 placement review |
|
||||||
| `validation/pcb-draft.png`, `validation/pcb-draft-3d.png` | Copper/fab preview and carrier-only 3D render |
|
| `validation/pcb-draft.png`, `validation/pcb-draft-3d.png` | Copper/fab preview and carrier-only 3D render |
|
||||||
| `validate_pcb_draft.py` | Fresh schematic-to-PCB net checks, bounds/layers and DRC/parity report |
|
| `validate_pcb_draft.py` | Fresh schematic-to-PCB net checks, bounds/layers and DRC/parity report |
|
||||||
| `generate_pcb_draft.py`, `route_pcb_draft.py` | Optional destructive-regeneration tools with explicit overwrite guards |
|
| `generate_pcb_draft.py`, `route_pcb_draft.py` | Optional destructive-regeneration tools with explicit overwrite guards |
|
||||||
| `add_device_silkscreen.py` | Refresh board-local OLED/RS-232 outlines and ESP32 header guides after placement/routing; preserves copper and project settings |
|
| `add_device_silkscreen.py` | Refresh board-local OLED/RS-232 outlines and ESP32 header guides after placement/routing; preserves copper and project settings |
|
||||||
| `db9-mechanical-notes.md`, `reference/db9-cad/` | STEP-derived metal dimensions, originals, source licenses and repeatable extraction/validation |
|
| [db9-mechanical-notes.md](../../docs/devel/hardware/PCB/db9-mechanical-notes.md), `reference/db9-cad/` | STEP-derived metal dimensions, originals, source licenses and repeatable extraction/validation |
|
||||||
| `add_db9_envelope.py` | Refresh nominal connector metal projections on Dwgs.User; not a case cutout or assembly clearance |
|
| `add_db9_envelope.py` | Refresh nominal connector metal projections on Dwgs.User; not a case cutout or assembly clearance |
|
||||||
| `Carrier.kicad_sym`, `sym-lib-table` | Project-local module, switch and resistor symbols |
|
| `Carrier.kicad_sym`, `sym-lib-table` | Project-local module, switch and resistor symbols |
|
||||||
| `Carrier.pretty/`, `fp-lib-table` | OLED/ESP32/RS-232-reference provisional footprints; standard KiCad 10 switch/resistor/mounting-hole library references |
|
| `Carrier.pretty/`, `fp-lib-table` | OLED/ESP32/RS-232-reference provisional footprints; standard KiCad 10 switch/resistor/mounting-hole library references |
|
||||||
| `oled-fit-check.svg` | User-fit-verified OLED mechanical template |
|
| `oled-fit-check.svg` | User-fit-verified OLED mechanical template |
|
||||||
| `footprint-notes.md`, `rs232-footprint-notes.md` | Footprint origins, provisional pad/drill choices, validation and RS-232 adaptation attribution |
|
| [footprint-notes.md](../../docs/devel/hardware/PCB/footprint-notes.md), [rs232-footprint-notes.md](../../docs/devel/hardware/PCB/rs232-footprint-notes.md) | Footprint origins, provisional pad/drill choices, validation and RS-232 adaptation attribution |
|
||||||
| `adafruit-research.md`, `button-notes.md`, `reference/` | Manufacturer evidence, pad maps and source/license records |
|
| [adafruit-research.md](../../docs/devel/hardware/PCB/adafruit-research.md), [button-notes.md](../../docs/devel/hardware/PCB/button-notes.md), `reference/` | Manufacturer evidence, pad maps and source/license records |
|
||||||
| `bom-draft.csv` | Draft procurement list and unresolved selections |
|
| `bom-draft.csv` | Draft procurement list and unresolved selections |
|
||||||
| `validate_schematic.py`, `validation/` | Repeatable netlist/firmware/ERC checks and exported evidence |
|
| `validate_schematic.py`, `validation/` | Repeatable netlist/firmware/ERC checks and exported evidence |
|
||||||
| `validate_footprints.py` | Native KiCad footprint geometry regression checks via system Python/pcbnew |
|
| `validate_footprints.py` | Native KiCad footprint geometry regression checks via system Python/pcbnew |
|
||||||
| `generate_schematic.py` | Optional stdlib-only initial design generator; not needed to open/edit in KiCad |
|
| `generate_schematic.py` | Optional stdlib-only initial design generator; not needed to open/edit in KiCad |
|
||||||
|
|
||||||
Keep these files together so project-relative libraries resolve. Standard KiCad
|
Development notes live in [docs/devel](../../docs/devel/README.md); the
|
||||||
|
[routing evidence summary](../../docs/devel/hardware/PCB/validation/pcb-routing-summary.md)
|
||||||
|
is retained there too. Keep CAD, libraries, assets and scripts together here so
|
||||||
|
project-relative libraries resolve. Standard KiCad
|
||||||
10 footprint libraries must be installed for the THT buttons, resistors and mounting holes.
|
10 footprint libraries must be installed for the THT buttons, resistors and mounting holes.
|
||||||
SKiDL 2.3.0 is installed, but no SKiDL dependency is required for this native design.
|
SKiDL 2.3.0 is installed, but no SKiDL dependency is required for this native design.
|
||||||
|
|
||||||
@@ -75,8 +78,8 @@ modify footprints. The generated KiCad files are the editable design artifacts.
|
|||||||
**unassigned in the schematic** until the actual male header and mechanics are
|
**unassigned in the schematic** until the actual male header and mechanics are
|
||||||
verified. The PCB draft assigns `Carrier:MAX3243_Reference_Provisional` on the
|
verified. The PCB draft assigns `Carrier:MAX3243_Reference_Provisional` on the
|
||||||
PCB only, causing one documented footprint-parity warning. Its reference nets
|
PCB only, causing one documented footprint-parity warning. Its reference nets
|
||||||
are routed for review, not male-board qualification. See `adafruit-research.md`
|
are routed for review, not male-board qualification. See [adafruit-research.md](../../docs/devel/hardware/PCB/adafruit-research.md)
|
||||||
and `pcb-draft-notes.md`; this is not a generic DE-9 footprint.
|
and [pcb-draft-notes.md](../../docs/devel/hardware/PCB/pcb-draft-notes.md); this is not a generic DE-9 footprint.
|
||||||
- No-connect crosses mean unused on the carrier, not disconnected internally.
|
- No-connect crosses mean unused on the carrier, not disconnected internally.
|
||||||
Native USB GPIO19/20, UART0 GPIO43/44, memory-conflicting GPIO35–37 and the 5 V
|
Native USB GPIO19/20, UART0 GPIO43/44, memory-conflicting GPIO35–37 and the 5 V
|
||||||
header are not connected to carrier circuitry.
|
header are not connected to carrier circuitry.
|
||||||
@@ -105,7 +108,7 @@ The PDF was rendered and visually inspected for layout/readability. Both local
|
|||||||
footprints loaded in KiCad's native footprint loader. Two retained regression tests
|
footprints loaded in KiCad's native footprint loader. Two retained regression tests
|
||||||
passed with `/usr/bin/python3 -B hardware/PCB/validate_footprints.py`; the binding
|
passed with `/usr/bin/python3 -B hardware/PCB/validate_footprints.py`; the binding
|
||||||
emits startup PROPERTY_ENUM assertions but completes the checks. See
|
emits startup PROPERTY_ENUM assertions but completes the checks. See
|
||||||
`footprint-notes.md` for scope and limitations.
|
[footprint-notes.md](../../docs/devel/hardware/PCB/footprint-notes.md) for scope and limitations.
|
||||||
|
|
||||||
OLED PCB/header/display/mounting-hole geometry was physically fit-verified by
|
OLED PCB/header/display/mounting-hole geometry was physically fit-verified by
|
||||||
the user. This does **not** validate connector drills, male RS-232 geometry,
|
the user. This does **not** validate connector drills, male RS-232 geometry,
|
||||||
@@ -117,7 +120,7 @@ remains (PCB reference footprint versus blank schematic assignment). No added
|
|||||||
DRC exclusions hide this warning. Missing courtyards are ignored by default DRC
|
DRC exclusions hide this warning. Missing courtyards are ignored by default DRC
|
||||||
and remain a manual release check. Run `/usr/bin/python3 -B
|
and remain a manual release check. Run `/usr/bin/python3 -B
|
||||||
hardware/PCB/validate_pcb_draft.py` from the repository root; see
|
hardware/PCB/validate_pcb_draft.py` from the repository root; see
|
||||||
`pcb-draft-notes.md` for the complete verification scope and remaining gates.
|
[pcb-draft-notes.md](../../docs/devel/hardware/PCB/pcb-draft-notes.md) for the complete verification scope and remaining gates.
|
||||||
No firmware build or device tests were run: this PCB work did not modify firmware.
|
No firmware build or device tests were run: this PCB work did not modify firmware.
|
||||||
|
|
||||||
## Evidence and components
|
## Evidence and components
|
||||||
@@ -136,7 +139,7 @@ Adafruit manufacturer CAD starting point:
|
|||||||
https://learn.adafruit.com/adafruit-rs-232-full-pinout-level-shifter-breakout/downloads
|
https://learn.adafruit.com/adafruit-rs-232-full-pinout-level-shifter-breakout/downloads
|
||||||
|
|
||||||
Published female-module Eagle CAD and its header map were retrieved and verified;
|
Published female-module Eagle CAD and its header map were retrieved and verified;
|
||||||
see `adafruit-research.md` for pinned revision, hashes, attribution and geometry.
|
see [adafruit-research.md](../../docs/devel/hardware/PCB/adafruit-research.md) for pinned revision, hashes, attribution and geometry.
|
||||||
The original files and CC BY-SA 3.0 notices are retained in `reference/`; they
|
The original files and CC BY-SA 3.0 notices are retained in `reference/`; they
|
||||||
remain under their upstream license. Omron reference documents retain their
|
remain under their upstream license. Omron reference documents retain their
|
||||||
manufacturer notices. The CAD-parts repository's STEP and MIT license are separately
|
manufacturer notices. The CAD-parts repository's STEP and MIT license are separately
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Add nominal STEP-derived metal bounding projections on Dwgs.User only.
|
"""Add nominal STEP-derived metal bounding projections on Dwgs.User only.
|
||||||
|
|
||||||
Not silkscreen, a courtyard, or a case cutout. See db9-mechanical-notes.md for
|
Not silkscreen, a courtyard, or a case cutout. See ../../docs/devel/hardware/PCB/db9-mechanical-notes.md for
|
||||||
source attribution, selected face subsets and the user-confirmed male/female
|
source attribution, selected face subsets and the user-confirmed male/female
|
||||||
external-geometry equivalence. No module height or case-wall thickness assumed.
|
external-geometry equivalence. No module height or case-wall thickness assumed.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ def symbol_definition(name, embedded=False):
|
|||||||
|
|
||||||
ROOT.joinpath("Carrier.kicad_sym").write_text('(kicad_symbol_lib (version 20231120) (generator "kicad_symbol_editor")\n'+'\n'.join(symbol_definition(name) for name in SPECS)+'\n)\n')
|
ROOT.joinpath("Carrier.kicad_sym").write_text('(kicad_symbol_lib (version 20231120) (generator "kicad_symbol_editor")\n'+'\n'.join(symbol_definition(name) for name in SPECS)+'\n)\n')
|
||||||
ROOT.joinpath("sym-lib-table").write_text('(sym_lib_table (version 7)\n (lib (name "Carrier")(type "KiCad")(uri "${KIPRJMOD}/Carrier.kicad_sym")(options "")(descr "Project-local module interfaces"))\n)\n')
|
ROOT.joinpath("sym-lib-table").write_text('(sym_lib_table (version 7)\n (lib (name "Carrier")(type "KiCad")(uri "${KIPRJMOD}/Carrier.kicad_sym")(options "")(descr "Project-local module interfaces"))\n)\n')
|
||||||
ROOT.joinpath("fp-lib-table").write_text('(fp_lib_table (version 7)\n (lib (name "Carrier")(type "KiCad")(uri "${KIPRJMOD}/Carrier.pretty")(options "")(descr "Provisional module carrier footprints; see footprint-notes.md"))\n (lib (name "Button_Switch_THT")(type "KiCad")(uri "${KICAD10_FOOTPRINT_DIR}/Button_Switch_THT.pretty")(options "")(descr "KiCad standard THT switches"))\n (lib (name "Resistor_THT")(type "KiCad")(uri "${KICAD10_FOOTPRINT_DIR}/Resistor_THT.pretty")(options "")(descr "KiCad standard axial resistors"))\n (lib (name "MountingHole")(type "KiCad")(uri "${KICAD10_FOOTPRINT_DIR}/MountingHole.pretty")(options "")(descr "KiCad standard mounting holes"))\n)\n')
|
ROOT.joinpath("fp-lib-table").write_text('(fp_lib_table (version 7)\n (lib (name "Carrier")(type "KiCad")(uri "${KIPRJMOD}/Carrier.pretty")(options "")(descr "Provisional module carrier footprints; see ../../docs/devel/hardware/PCB/footprint-notes.md"))\n (lib (name "Button_Switch_THT")(type "KiCad")(uri "${KICAD10_FOOTPRINT_DIR}/Button_Switch_THT.pretty")(options "")(descr "KiCad standard THT switches"))\n (lib (name "Resistor_THT")(type "KiCad")(uri "${KICAD10_FOOTPRINT_DIR}/Resistor_THT.pretty")(options "")(descr "KiCad standard axial resistors"))\n (lib (name "MountingHole")(type "KiCad")(uri "${KICAD10_FOOTPRINT_DIR}/MountingHole.pretty")(options "")(descr "KiCad standard mounting holes"))\n)\n')
|
||||||
# Preserve project settings on regeneration.
|
# Preserve project settings on regeneration.
|
||||||
project_file = ROOT / (PROJECT + ".kicad_pro")
|
project_file = ROOT / (PROJECT + ".kicad_pro")
|
||||||
if not project_file.exists():
|
if not project_file.exists():
|
||||||
@@ -140,7 +140,7 @@ for number,name,*_ in MCU:
|
|||||||
mcu_nets[number]=GPIO_NET[int(name[4:])]
|
mcu_nets[number]=GPIO_NET[int(name[4:])]
|
||||||
place("HW678_N16R8","U1","HW678 / S3-N16R8",95.25,109.22,mcu_nets,{"Status":"Header geometry nominal; outline/antenna clearance pending","Pin_numbering":"Left 1-22; right 23-44; both top-to-bottom, antenna up"})
|
place("HW678_N16R8","U1","HW678 / S3-N16R8",95.25,109.22,mcu_nets,{"Status":"Header geometry nominal; outline/antenna clearance pending","Pin_numbering":"Left 1-22; right 23-44; both top-to-bottom, antenna up"})
|
||||||
rs_nets=dict(zip(map(str,range(1,13)),["+3V3","GND","RS_DCD","RS_RX","RS_TX","RS_DTR","RS_DSR","RS_RTS","RS_CTS","RS_RI","RS_VALID","RS_OFF_N"]))
|
rs_nets=dict(zip(map(str,range(1,13)),["+3V3","GND","RS_DCD","RS_RX","RS_TX","RS_DTR","RS_DSR","RS_RTS","RS_CTS","RS_RI","RS_VALID","RS_OFF_N"]))
|
||||||
place("MAX3243_Module_Reference","U2","Adafruit MAX3243 / male 6253",228.6,96.52,rs_nets,{"Status":"VERIFY male header numbering; footprint intentionally unassigned","Datasheet_source":"adafruit-research.md; reference female 5988 CAD"})
|
place("MAX3243_Module_Reference","U2","Adafruit MAX3243 / male 6253",228.6,96.52,rs_nets,{"Status":"VERIFY male header numbering; footprint intentionally unassigned","Datasheet_source":"../../docs/devel/hardware/PCB/adafruit-research.md; reference female 5988 CAD"})
|
||||||
place("OLED_I2C_26mm","DS1","128x64 I2C OLED / 0x3C",228.6,152.4,{"1":"GND","2":"+3V3","3":"OLED_SCL","4":"OLED_SDA"},{"Status":"Module outline/header/display user fit-verified; pad/drill fit provisional"})
|
place("OLED_I2C_26mm","DS1","128x64 I2C OLED / 0x3C",228.6,152.4,{"1":"GND","2":"+3V3","3":"OLED_SCL","4":"OLED_SDA"},{"Status":"Module outline/header/display user fit-verified; pad/drill fit provisional"})
|
||||||
for index,(net,function) in enumerate((("BTN_PREVIOUS","Previous / back"),("BTN_SELECT","Select / confirm"),("BTN_NEXT","Next")),1):
|
for index,(net,function) in enumerate((("BTN_PREVIOUS","Previous / back"),("BTN_SELECT","Select / confirm"),("BTN_NEXT","Next")),1):
|
||||||
y=88.9+(index-1)*35.56
|
y=88.9+(index-1)*35.56
|
||||||
|
|||||||
@@ -126,4 +126,4 @@ summary={
|
|||||||
(OUT/"pcb-validation-summary.json").write_text(json.dumps(summary,indent=2)+"\n")
|
(OUT/"pcb-validation-summary.json").write_text(json.dumps(summary,indent=2)+"\n")
|
||||||
print("PCB draft verified: 82 x 80 mm; 2 copper layers; 0 physical DRC violations; 0 unrouted.")
|
print("PCB draft verified: 82 x 80 mm; 2 copper layers; 0 physical DRC violations; 0 unrouted.")
|
||||||
print("All schematic pad nets match. One expected U2 footprint parity warning remains visible.")
|
print("All schematic pad nets match. One expected U2 footprint parity warning remains visible.")
|
||||||
print("Not fabrication-ready; see pcb-draft-notes.md and ignored checks in the DRC report.")
|
print("Not fabrication-ready; see ../../docs/devel/hardware/PCB/pcb-draft-notes.md and ignored checks in the DRC report.")
|
||||||
|
|||||||
@@ -40,9 +40,13 @@ a0=OUT/'pcb-a0-before-reposition.kicad_pcb'
|
|||||||
a1=ROOT/'serial-carrier.kicad_pcb'
|
a1=ROOT/'serial-carrier.kicad_pcb'
|
||||||
assert sha(a0)==report['a0_snapshot_sha256'], 'A0 snapshot changed'
|
assert sha(a0)==report['a0_snapshot_sha256'], 'A0 snapshot changed'
|
||||||
assert sha(OUT/'pcb-routing-input.kicad_pcb')==report['a1_unrouted_sha256'], 'A1 routing input changed'
|
assert sha(OUT/'pcb-routing-input.kicad_pcb')==report['a1_unrouted_sha256'], 'A1 routing input changed'
|
||||||
# These two handoff documents are intentionally updated during A1 integration.
|
# Historical reports exempt these handoff documents, updated during A1 integration.
|
||||||
|
# Keep the legacy key exemption; new reports use ../../docs/devel/hardware/PCB/
|
||||||
|
# pcb-draft-notes.md and that relocated note must pass the normal hash check.
|
||||||
|
intentional_updates=[name for name in ('README.md','pcb-draft-notes.md')
|
||||||
|
if name in report['protected_file_sha256']]
|
||||||
protected={name:digest for name,digest in report['protected_file_sha256'].items()
|
protected={name:digest for name,digest in report['protected_file_sha256'].items()
|
||||||
if name not in ('README.md','pcb-draft-notes.md')}
|
if name not in intentional_updates}
|
||||||
for name,digest in protected.items():
|
for name,digest in protected.items():
|
||||||
assert sha(ROOT/name)==digest, 'protected file changed: '+name
|
assert sha(ROOT/name)==digest, 'protected file changed: '+name
|
||||||
old,new=parse(a0),parse(a1)
|
old,new=parse(a0),parse(a1)
|
||||||
@@ -88,7 +92,7 @@ assert [x for x in old if isinstance(x,list) and x[0]=='net']==[x for x in new i
|
|||||||
assert not (ROOT/'~serial-carrier.kicad_pcb.lck').exists(), 'PCB reopened before handoff'
|
assert not (ROOT/'~serial-carrier.kicad_pcb.lck').exists(), 'PCB reopened before handoff'
|
||||||
result=dict(a0_snapshot_unchanged=True,a1_unrouted_snapshot_unchanged=True,
|
result=dict(a0_snapshot_unchanged=True,a1_unrouted_snapshot_unchanged=True,
|
||||||
protected_files_unchanged=list(protected),
|
protected_files_unchanged=list(protected),
|
||||||
intentional_documentation_updates=['README.md','pcb-draft-notes.md'],
|
intentional_documentation_updates=intentional_updates,
|
||||||
cad_projection_graphics=len(cad_owned),
|
cad_projection_graphics=len(cad_owned),
|
||||||
footprints_identical_except_authorized_placement=True,
|
footprints_identical_except_authorized_placement=True,
|
||||||
footprint_count=len(old_fp),unrelated_board_graphics_preserved=len(unchanged_old),
|
footprint_count=len(old_fp),unrelated_board_graphics_preserved=len(unchanged_old),
|
||||||
|
|||||||
@@ -69,10 +69,14 @@ def main():
|
|||||||
before=PCB.read_bytes()
|
before=PCB.read_bytes()
|
||||||
assert before==BACKUP.read_bytes(), 'current board differs from A0 snapshot; refusing migration'
|
assert before==BACKUP.read_bytes(), 'current board differs from A0 snapshot; refusing migration'
|
||||||
protected=[ROOT/name for name in ('serial-carrier.kicad_pro','serial-carrier.kicad_sch',
|
protected=[ROOT/name for name in ('serial-carrier.kicad_pro','serial-carrier.kicad_sch',
|
||||||
'serial-carrier.kicad_prl','README.md','pcb-draft-notes.md','fp-lib-table',
|
'serial-carrier.kicad_prl','README.md','fp-lib-table',
|
||||||
'sym-lib-table','Carrier.kicad_sym')]
|
'sym-lib-table','Carrier.kicad_sym')]
|
||||||
protected+=list((ROOT/'Carrier.pretty').glob('*'))
|
protected+=list((ROOT/'Carrier.pretty').glob('*'))
|
||||||
hashes={str(path.relative_to(ROOT)):sha(path) for path in protected if path.is_file()}
|
hashes={str(path.relative_to(ROOT)):sha(path) for path in protected if path.is_file()}
|
||||||
|
# Keep report keys relative to ROOT, including the relocated, required note.
|
||||||
|
# Do not silently omit it through the optional project-file filter above.
|
||||||
|
notes='../../docs/devel/hardware/PCB/pcb-draft-notes.md'
|
||||||
|
hashes[notes]=sha(ROOT/notes)
|
||||||
board=p.LoadBoard(str(PCB))
|
board=p.LoadBoard(str(PCB))
|
||||||
immutable=normalized_footprints(board)
|
immutable=normalized_footprints(board)
|
||||||
net_table=identity(board)[-1]
|
net_table=identity(board)[-1]
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ No firmware build, network access or device operation is performed.
|
|||||||
|
|
||||||
Implementer reports `run.py --tickets` PASS **25 transport / 12 ticket groups**, including certificate owner routing, currentness rejection and commit → stop → start short-circuit/error behavior. The owner's `dispatcher_actions` mask selects the existing 12 KiB dispatcher, not the 4 KiB control task. `tests/admin_console_boundary/run.py` (including `certificate.c`) separately covers typed deferred handoff/pending gate/executing reservation; `lifecycle.py` covers canonical handlers and unchanged SSH/UART0 behavior. Policy, server lifecycle **11**, cookie `--admin` and store `--serial` also pass as reported. Independent review has no actionable findings; sanitizer validation is unavailable due to missing libasan/libubsan. No hardware validation is claimed or performed by this documentation update.
|
Implementer reports `run.py --tickets` PASS **25 transport / 12 ticket groups**, including certificate owner routing, currentness rejection and commit → stop → start short-circuit/error behavior. The owner's `dispatcher_actions` mask selects the existing 12 KiB dispatcher, not the 4 KiB control task. `tests/admin_console_boundary/run.py` (including `certificate.c`) separately covers typed deferred handoff/pending gate/executing reservation; `lifecycle.py` covers canonical handlers and unchanged SSH/UART0 behavior. Policy, server lifecycle **11**, cookie `--admin` and store `--serial` also pass as reported. Independent review has no actionable findings; sanitizer validation is unavailable due to missing libasan/libubsan. No hardware validation is claimed or performed by this documentation update.
|
||||||
|
|
||||||
Current [browser-shell policy](../../docs/web_administration.md#browser-shell-policy) permits selected other-account mutations as well as exact parsed `web status`, `web stop`, `web certificate rotate --force`, and `reboot`/self-close; network mutations and restricted SSH lifecycle/key mutations remain blocked in the shell. Typed Settings permissions are separate. Certificate drain/acknowledgement bounds do not bound queued execution or prove browser receipt. [Acceptance evidence](../../docs/roadmap.md#phase8-acceptance-evidence) records M2 and the later 8D.22 signoff; earlier pending gates below are historical, not current acceptance blockers.
|
Current [browser-shell policy](../../docs/devel/web_administration.md#browser-shell-policy) permits selected other-account mutations as well as exact parsed `web status`, `web stop`, `web certificate rotate --force`, and `reboot`/self-close; network mutations and restricted SSH lifecycle/key mutations remain blocked in the shell. Typed Settings permissions are separate. Certificate drain/acknowledgement bounds do not bound queued execution or prove browser receipt. [Acceptance evidence](../../docs/devel/roadmap.md#phase8-acceptance-evidence) records M2 and the later 8D.22 signoff; earlier pending gates below are historical, not current acceptance blockers.
|
||||||
|
|
||||||
## Earlier results recorded 2026-09-06
|
## Earlier results recorded 2026-09-06
|
||||||
|
|
||||||
8D.7 first slice: `run.py --tickets` passes **23 transport / 12 ticket groups**. Adds WEB stop/reboot owner routing, stale/revoked action rejection and stop-error propagation, pending-input discard before poll and cancellation-during-receive with/without an occupied RX buffer. `python3 tests/admin_console_boundary/lifecycle.py` separately checks the production canonical handlers and unchanged SSH/UART0 behavior. Dependencies remain doubled; no target stop/reboot is executed. See [browser-shell contracts](../../docs/web_administration.md#browser-shell-policy) and [regression procedures](../../docs/user_administration_tests.md#integrated-web-administration-regression-procedure), not additional execution claims.
|
8D.7 first slice: `run.py --tickets` passes **23 transport / 12 ticket groups**. Adds WEB stop/reboot owner routing, stale/revoked action rejection and stop-error propagation, pending-input discard before poll and cancellation-during-receive with/without an occupied RX buffer. `python3 tests/admin_console_boundary/lifecycle.py` separately checks the production canonical handlers and unchanged SSH/UART0 behavior. Dependencies remain doubled; no target stop/reboot is executed. See [browser-shell contracts](../../docs/devel/web_administration.md#browser-shell-policy) and [regression procedures](../../docs/devel/user_administration_tests.md#integrated-web-administration-regression-procedure), not additional execution claims.
|
||||||
|
|
||||||
Final continuation: `run.py --tickets` passes **19 transport / 12 ticket groups**,
|
Final continuation: `run.py --tickets` passes **19 transport / 12 ticket groups**,
|
||||||
including the HTTPD-owned shutdown retry/reuse regression. `server_lifecycle.py`
|
including the HTTPD-owned shutdown retry/reuse regression. `server_lifecycle.py`
|
||||||
@@ -38,7 +38,7 @@ below. Final admin closure uses direct HTTPD-owned `shutdown`, not queued IDF
|
|||||||
session-close work. Parent reports the sequential final firmware build after this
|
session-close work. Parent reports the sequential final firmware build after this
|
||||||
fix passed in **23.55 s**, at **95,580 B RAM / 1,637,273 B flash**, and the final
|
fix passed in **23.55 s**, at **95,580 B RAM / 1,637,273 B flash**, and the final
|
||||||
independent security integration review found no actionable findings. See
|
independent security integration review found no actionable findings. See
|
||||||
[terminal ownership](../../docs/web_administration.md#terminal-and-console-ownership) and [acceptance evidence](../../docs/roadmap.md#phase8-acceptance-evidence) for current contracts and signoff limits.
|
[terminal ownership](../../docs/devel/web_administration.md#terminal-and-console-ownership) and [acceptance evidence](../../docs/devel/roadmap.md#phase8-acceptance-evidence) for current contracts and signoff limits.
|
||||||
|
|
||||||
After the production empty-frame, input-deadline and timer-generation fixes:
|
After the production empty-frame, input-deadline and timer-generation fixes:
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ Coverage includes challenge reuse/consumption/expiry, capacities without evictio
|
|||||||
|
|
||||||
This is **not** the full IDF parser/dispatcher, real handshake/TLS/socket, browser, multicore task or hardware test. The private struct doubles do not prove binary layout; firmware compilation uses the actual pinned headers, and the version guard requires a new audit on SDK changes. Handshake sending and transport revocation are doubled. Actual on-wire pre-101 rejection, frame routing, pipelining/early bytes, cookie/CSP/browser recovery and loaded expiry latency require real-target evidence beyond this harness; M1 and the retained Phase 8D scope have since been accepted. No sanitizer or runtime memory-reserve result is implied.
|
This is **not** the full IDF parser/dispatcher, real handshake/TLS/socket, browser, multicore task or hardware test. The private struct doubles do not prove binary layout; firmware compilation uses the actual pinned headers, and the version guard requires a new audit on SDK changes. Handshake sending and transport revocation are doubled. Actual on-wire pre-101 rejection, frame routing, pipelining/early bytes, cookie/CSP/browser recovery and loaded expiry latency require real-target evidence beyond this harness; M1 and the retained Phase 8D scope have since been accepted. No sanitizer or runtime memory-reserve result is implied.
|
||||||
|
|
||||||
See [authentication contracts](../../docs/web_administration.md#authentication-and-admission), [acceptance evidence and limits](../../docs/roadmap.md#phase8-acceptance-evidence), and [regression procedures](../../docs/user_administration_tests.md#integrated-web-administration-regression-procedure).
|
See [authentication contracts](../../docs/devel/web_administration.md#authentication-and-admission), [acceptance evidence and limits](../../docs/devel/roadmap.md#phase8-acceptance-evidence), and [regression procedures](../../docs/devel/user_administration_tests.md#integrated-web-administration-regression-procedure).
|
||||||
|
|
||||||
## Accounts (8D.10)
|
## Accounts (8D.10)
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ The second command separately exercises production conditional database mutation
|
|||||||
and zero-wait list bodies with NVS/RTOS doubles, including last-admin protection,
|
and zero-wait list bodies with NVS/RTOS doubles, including last-admin protection,
|
||||||
target generation/recreation checks and commit-failure cleanup. It retains the
|
target generation/recreation checks and commit-failure cleanup. It retains the
|
||||||
canonical CLI account tests. These are not end-to-end RTOS/flash/TLS tests.
|
canonical CLI account tests. These are not end-to-end RTOS/flash/TLS tests.
|
||||||
See [Accounts contracts](../../docs/web_administration.md#accounts-and-authorized-keys) and [acceptance evidence](../../docs/roadmap.md#phase8-acceptance-evidence). Timer doubles do not prove hard cleanup latency.
|
See [Accounts contracts](../../docs/devel/web_administration.md#accounts-and-authorized-keys) and [acceptance evidence](../../docs/devel/roadmap.md#phase8-acceptance-evidence). Timer doubles do not prove hard cleanup latency.
|
||||||
|
|
||||||
## Read-only Serial Settings
|
## Read-only Serial Settings
|
||||||
|
|
||||||
@@ -62,4 +62,4 @@ registration. No SDK files are modified. Lifecycle registration/optional failure
|
|||||||
orchestration is separately tested by `tests/web_admin_transport/server_lifecycle.py`.
|
orchestration is separately tested by `tests/web_admin_transport/server_lifecycle.py`.
|
||||||
|
|
||||||
This does not run the full serial task/UART driver, TLS/network dispatcher or a real
|
This does not run the full serial task/UART driver, TLS/network dispatcher or a real
|
||||||
browser. See [typed Settings contracts](../../docs/web_administration.md#typed-settings-api-and-operation-lifetime) and [Serial semantics](../../docs/web_administration.md#serial-and-display). [Acceptance evidence](../../docs/roadmap.md#phase8-acceptance-evidence) records the 8D.22 signoff without inferring individual target comparisons or runtime memory/stack margins.
|
browser. See [typed Settings contracts](../../docs/devel/web_administration.md#typed-settings-api-and-operation-lifetime) and [Serial semantics](../../docs/devel/web_administration.md#serial-and-display). [Acceptance evidence](../../docs/devel/roadmap.md#phase8-acceptance-evidence) records the 8D.22 signoff without inferring individual target comparisons or runtime memory/stack margins.
|
||||||
|
|||||||
@@ -33,4 +33,4 @@ timeouts and all 16 existing failure/restart groups. Canonical CLI routing and
|
|||||||
SSH/browser policy have separate tests. No target heap, stack margin, admission
|
SSH/browser policy have separate tests. No target heap, stack margin, admission
|
||||||
reliability or secrecy of external SDK logging is proven by these tests.
|
reliability or secrecy of external SDK logging is proven by these tests.
|
||||||
|
|
||||||
See [admission diagnostic usage and interpretation](../../docs/web_admission_diagnostics.md#usage-and-interpretation), the [SDK cleanup audit and blind spots](../../docs/web_admission_diagnostics.md#sdk-audit-and-explicit-blind-spots), and [regression entry points and limits](../../docs/web_admission_diagnostics.md#regression-entry-points-and-limits).
|
See [admission diagnostic usage and interpretation](../../docs/devel/web_admission_diagnostics.md#usage-and-interpretation), the [SDK cleanup audit and blind spots](../../docs/devel/web_admission_diagnostics.md#sdk-audit-and-explicit-blind-spots), and [regression entry points and limits](../../docs/devel/web_admission_diagnostics.md#regression-entry-points-and-limits).
|
||||||
|
|||||||
@@ -14,4 +14,4 @@ Requires a C11 host compiler (`CC`, default `cc`) and the installed ESP-IDF 5.5.
|
|||||||
|
|
||||||
18 groups cover all six slots, normal five-second polling, never-used post-TLS connections, buffered/pipelined/incomplete input, slow synchronous processing/purge, failed requests, WS/upgrade/async exemptions, fd/TLS/slot/LRU reuse, shutdown retry, queue failure/loss, bounded submission, early callback completion, stop fences, failed stop/restart and stale/nonwrapping generations. The module is tested without linking diagnostics, and source guards enforce independent policy plus composed TLS callback ordering; the diagnostic and server suites exercise their own integration contracts.
|
18 groups cover all six slots, normal five-second polling, never-used post-TLS connections, buffered/pipelined/incomplete input, slow synchronous processing/purge, failed requests, WS/upgrade/async exemptions, fd/TLS/slot/LRU reuse, shutdown retry, queue failure/loss, bounded submission, early callback completion, stop fences, failed stop/restart and stale/nonwrapping generations. The module is tested without linking diagnostics, and source guards enforce independent policy plus composed TLS callback ordering; the diagnostic and server suites exercise their own integration contracts.
|
||||||
|
|
||||||
These are not real TLS, ESP-IDF scheduler, TCP-over-network or target timing tests. In particular, accepted-but-lost UDP work is tested as a bounded fail-safe condition requiring successful server restart, not as automatic recovery. Full timeout/SDK audit, resource evidence and unperformed hardware checklist: [`docs/https_idle_cleanup.md`](../../docs/https_idle_cleanup.md).
|
These are not real TLS, ESP-IDF scheduler, TCP-over-network or target timing tests. In particular, accepted-but-lost UDP work is tested as a bounded fail-safe condition requiring successful server restart, not as automatic recovery. Full timeout/SDK audit, resource evidence and unperformed hardware checklist: [`docs/devel/https_idle_cleanup.md`](../../docs/devel/https_idle_cleanup.md).
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ survives until its owner releases it after service stop/start.
|
|||||||
`python3 tests/web_admin_transport/server_lifecycle.py` additionally links real
|
`python3 tests/web_admin_transport/server_lifecycle.py` additionally links real
|
||||||
production security and mbedTLS to the production HTTPS owner, with NVS/HTTPD doubles,
|
production security and mbedTLS to the production HTTPS owner, with NVS/HTTPD doubles,
|
||||||
for unchanged identity/storage before commit and no rollback after stop/start failure.
|
for unchanged identity/storage before commit and no rollback after stop/start failure.
|
||||||
See [HTTPS identity contracts](../../docs/web_administration.md#service-lifecycle-and-identity-rotation) and [acceptance evidence and limits](../../docs/roadmap.md#phase8-acceptance-evidence).
|
See [HTTPS identity contracts](../../docs/devel/web_administration.md#service-lifecycle-and-identity-rotation) and [acceptance evidence and limits](../../docs/devel/roadmap.md#phase8-acceptance-evidence).
|
||||||
|
|
||||||
Removed: two credential functions (`show_credentials`, `rotate_credentials`),
|
Removed: two credential functions (`show_credentials`, `rotate_credentials`),
|
||||||
one credential struct type, three username/password capacity/length constants,
|
one credential struct type, three username/password capacity/length constants,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ and inline asset-failure script, not a separately maintained implementation.
|
|||||||
|
|
||||||
Historical 8D.17 run: **126 behavior groups**, plus production renderer/HTML/CSP checks. Seven new quick groups cover mouse/focus/touch activation, single DOM ownership, focused-editor hover stability, Escape/outside dismissal, late reads/expiry, terminal input/drain isolation, full-page draft/promotion preservation, explicit Apply/Save and pending non-replay, Network secret exclusion/strict schema/401. `Element` focus/containment/hover and events are doubles, not browser event-dispatch proof.
|
Historical 8D.17 run: **126 behavior groups**, plus production renderer/HTML/CSP checks. Seven new quick groups cover mouse/focus/touch activation, single DOM ownership, focused-editor hover stability, Escape/outside dismissal, late reads/expiry, terminal input/drain isolation, full-page draft/promotion preservation, explicit Apply/Save and pending non-replay, Network secret exclusion/strict schema/401. `Element` focus/containment/hover and events are doubles, not browser event-dispatch proof.
|
||||||
|
|
||||||
Optional geometry: `WEB_UI_CHROMIUM=/usr/bin/chromium-browser python3 tests/web_ui_session/run.py`. The fixture covers 36 cases at 320/600/900/1200px: full settings and quick panels plus dashboard card hit-testing, overflow/ellipsis, compact controls, status visibility, and full-page restoration. Quick panels use height 360px. The 2026-09-14 cosmetic update passed these Chromium fixtures and all 161 browser behavior groups; fixtures are not live-device validation. See [contextual-control contracts](../../docs/web_administration.md#broker-and-contextual-controls), [acceptance evidence and limits](../../docs/roadmap.md#phase8-acceptance-evidence), and [regression procedures](../../docs/user_administration_tests.md#integrated-web-administration-regression-procedure).
|
Optional geometry: `WEB_UI_CHROMIUM=/usr/bin/chromium-browser python3 tests/web_ui_session/run.py`. The fixture covers 36 cases at 320/600/900/1200px: full settings and quick panels plus dashboard card hit-testing, overflow/ellipsis, compact controls, status visibility, and full-page restoration. Quick panels use height 360px. The 2026-09-14 cosmetic update passed these Chromium fixtures and all 161 browser behavior groups; fixtures are not live-device validation. See [contextual-control contracts](../../docs/devel/web_administration.md#broker-and-contextual-controls), [acceptance evidence and limits](../../docs/devel/roadmap.md#phase8-acceptance-evidence), and [regression procedures](../../docs/devel/user_administration_tests.md#integrated-web-administration-regression-procedure).
|
||||||
|
|
||||||
Coverage:
|
Coverage:
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ These tests model DOM, timers, fetch cancellation and WebSocket events. They do
|
|||||||
not prove real-browser CSP enforcement, script-loading errors, TLS/HTTPD behavior,
|
not prove real-browser CSP enforcement, script-loading errors, TLS/HTTPD behavior,
|
||||||
actual bfcache policy, cookie expiry, server revocation, or hardware serial byte
|
actual bfcache policy, cookie expiry, server revocation, or hardware serial byte
|
||||||
integrity, actual xterm escape parsing, hidden prompts, or desktop/mobile layout.
|
integrity, actual xterm escape parsing, hidden prompts, or desktop/mobile layout.
|
||||||
See [Accounts contracts](../../docs/web_administration.md#accounts-and-authorized-keys) and [acceptance evidence](../../docs/roadmap.md#phase8-acceptance-evidence). The following are historical 8D.10 slice 2 reports, not current totals: parent build PASS 25.61 s, 95,908 B RAM / 1,694,237 B flash.
|
See [Accounts contracts](../../docs/devel/web_administration.md#accounts-and-authorized-keys) and [acceptance evidence](../../docs/devel/roadmap.md#phase8-acceptance-evidence). The following are historical 8D.10 slice 2 reports, not current totals: parent build PASS 25.61 s, 95,908 B RAM / 1,694,237 B flash.
|
||||||
The generated endpoint is independently optionally registered (23 handlers), with
|
The generated endpoint is independently optionally registered (23 handlers), with
|
||||||
route-agent lifecycle 15 PASS for failure isolation/restart. UI 57/CSP and lifecycle
|
route-agent lifecycle 15 PASS for failure isolation/restart. UI 57/CSP and lifecycle
|
||||||
15 results are agent-attributed, not claims of the parent's additional reruns.
|
15 results are agent-attributed, not claims of the parent's additional reruns.
|
||||||
|
|||||||
Reference in New Issue
Block a user