# 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, recovery-secret display, network diagnostics, reboot, and HTTPS/SSH material mutation. Initial administrator bootstrap and explicit recovery of an unavailable user database remain physically bound to UART0. 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 ` help` for a group summary. Configuration changes are RAM-only unless explicitly saved. ## System | Command | Description | |---|---| | `memory` | Show free memory, minimum free memory, and largest blocks for internal RAM, DMA-capable RAM, and PSRAM. | | `reboot` | Drain console output briefly and restart the ESP32. | | `exit` | Close the current administrative SSH session after its acknowledgement drains; unavailable on UART0. Ctrl+D on an empty admin SSH command line does the same. | ## Role-based users | Command | Description | |---|---| | `user status` / `user list` | Show database generation, capacity, administrator/bootstrap state, and all secret-free account summaries. | | `user show ` | Show one account's role, ID, authentication generation, and SSH-key fingerprints. | | `user bootstrap` | Set and confirm the `admin` password without echo, then promote the migrated account to `admin`. | | `user bootstrap --generate` | Bootstrap `admin` with a generated 24-character password displayed once. | | `user add ` | Create an account using a bounded no-echo password and confirmation prompt. | | `user add --generate` | Create an account with a generated password displayed once. | | `user delete --force` | Delete an account; the pre-bootstrap migrated `admin` and final administrator are protected. | | `user role --force` | Change a role; the final administrator cannot be demoted. | | `user password ` | Set and confirm a new password without echo. | | `user password --generate` | Replace a password with a generated value displayed once. | | `user key add ` | Prompt on UART0 or authenticated admin SSH for one bounded OpenSSH public-key line. | | `user key add ` | Import a key non-interactively; intended for authenticated admin SSH and also accepted on UART0. | | `user key delete <0..2> --force` | Delete one key by the index shown by `user show`. | | `user key clear --force` | Delete all public keys for an account. | | `user recover --force` | When normal user-database initialization failed, explicitly replace its blob from the current legacy network credential. | 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. On the first Phase 8A boot, the old shared `admin` credential is imported as a role-`user` account, not silently granted administrator rights. Run `user bootstrap` from physical UART0 to establish the administrator. Phase 8B now authenticates HTTPS and SSH passwords through this database and enables stored SSH public keys. Before bootstrap, `web credentials rotate --force` and `web reset --force` synchronize the migrated verifier; after bootstrap, that legacy credential is recovery-only and does not authenticate or alter role-based users. `user recover --force` is a destructive physical recovery operation and succeeds only while the database is unavailable. It replaces the user blob with one role-`user` account derived from the current legacy credential; run `user bootstrap` afterward. It does not erase unrelated NVS data. Successful password, role, key, bootstrap, and delete operations invalidate only that username's outstanding WebSocket tickets and active WebSocket/SSH sessions; unrelated users remain connected. ## Local display | Command | Description | |---|---| | `display status` | Show the runtime aging settings and OLED service state. | | `display set dim-seconds <0..86400>` | Set the RAM inactivity delay before contrast drops to `1`; `0` disables dimming. | | `display set off-seconds <0..86400>` | Set the RAM inactivity delay before the OLED switches off; `0` disables automatic off. | | `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 apply and persist 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. ## Serial service | Command | Description | |---|---| | `serial status` | Show UART1 state, configuration, modem signals, and ownership. | | `serial start` / `serial stop` | Start or release the physical UART1 service. | | `serial set ` | Change the working serial configuration and safely restart a running service. | | `serial save` / `serial load` | Save the working configuration to NVS or load it. | | `serial defaults` / `serial reset` | Apply defaults in RAM, or apply and persist them. | | `serial counters` / `serial clear-counters` | Show or clear serial counters. | Defaults are 115200 baud, 8 data bits, no parity, one stop bit, no flow control, and inactive DTR. Supported values: baud `110`–`1000000`; data bits `7` or `8`; parity `none`, `even`, or `odd`; stop bits `1` or `2`; flow `none` or `rts-cts`; DTR `inactive`, `active`, or `on-connect`; and RTS threshold `1`–`127` bytes. The broker exclusively owns serial data access. ## Session broker | Command | Description | |---|---| | `broker status` / `broker clients` | Show broker state or connected clients. | | `broker counters` / `broker clear-counters` | Show or clear broker counters. | | `broker connect ` / `broker disconnect ` | Create or remove a console test client. | | `broker request-writer ` / `broker release-writer ` | Request or relinquish the single writer lease. | | `broker force-writer ` | Administratively assign or clear the writer lease. | | `broker send-hex ` | Send hexadecimal bytes through a writer client. | | `broker read [maximum-bytes]` | Read queued serial output for a client. | | `broker events ` | 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. ## Native USB CDC-ACM | Command | Description | |---|---| | `usb` / `usb help` | Show USB command usage. | | `usb status` | Show CDC connection, broker role, and runtime state. | | `usb counters` / `usb clear-counters` | Show or clear USB counters. | | `usb request-writer` / `usb release-writer` | Request or release USB writer ownership. | Opening `/dev/ttyACM*` with DTR asserted creates the `usb-cdc` broker client, starts UART1 if needed, and requests writer ownership. It becomes an observer if another client is writer. USB data is binary-transparent. The host's CDC line coding is shown by `usb status` for diagnostics only; it does not alter UART1. Configure physical baud rate, framing, flow control, and DTR explicitly with `serial` commands and persist them with `serial save`. ## Wi-Fi | Command | Description | |---|---| | `wifi status` / `wifi profiles` | Show Wi-Fi state or configured station profiles. | | `wifi start` / `wifi stop` / `wifi reconnect` | Start, stop, or reconnect Wi-Fi. | | `wifi next-profile` | Queue a switch to the enabled station profile after the currently active profile in priority order; wraps safely. | | `wifi profile set ` | Set a station profile. | | `wifi profile secret ` | Set a profile password through a no-echo prompt. | | `wifi profile enable|disable|delete ` | Manage a station-profile slot. | | `wifi ap policy ` | Configure fallback AP behavior. | | `wifi ap ssid ` / `wifi ap channel <1..11>` | Set the AP name or channel. | | `wifi ap secret` / `wifi ap show-secret` | Set or reveal the AP password. | | `wifi save|load|defaults|reset` | Persist, restore, reset in RAM, or reset and persist configuration. | | `wifi counters|clear-counters` | Show or clear Wi-Fi counters. | | `wifi ping [count]` | Send 1–20 IPv4 or IPv6 ICMP probes. | | `wifi nslookup ` | Resolve and display unique IPv4/IPv6 addresses. | | `wifi traceroute [max-hops]` | Run IPv4 ICMP traceroute with up to 30 hops. | `ping`, `nslookup`, and `traceroute` are root aliases. The four station-profile slots use lower priority values first. Edits to a disabled profile's SSID, priority, security mode, or secret are staged in RAM and do not interrupt the current Wi-Fi connection. Enabling or disabling a profile, changing an enabled profile, or changing AP policy/configuration applies the new radio policy and may reconnect Wi-Fi. Use `wifi save` to persist working changes. Passwords are not displayed by ordinary status output. ## mDNS | Command | Description | |---|---| | `mdns status` | Show the configured `sak-.local` hostname and announcement state. | | `mdns suffix ` | Set a 1–55-character lowercase hostname suffix in RAM. | | `mdns save` / `mdns load` | Save the working suffix to its independent NVS record or load it. | | `mdns defaults` / `mdns reset` | Restore the MAC-derived suffix in RAM, or restore and persist it. | When the Wi-Fi station receives an IPv4 address, the Wi-Fi manager announces `sak-.local`. 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 while online causes a best-effort reannouncement; mDNS failures do not stop Wi-Fi, UART0, UART1, or native USB access. ## HTTPS web terminal | Command | Description | |---|---| | `web` / `web help` | Show web-service command usage. | | `web status` | Show HTTPS and WebSocket state. | | `web start` / `web stop` | Start or stop HTTPS service. | | `web counters` / `web clear-counters` | Show or clear web counters. | | `web credentials show` | Display the legacy migration/recovery credential on UART0 or authenticated admin SSH; it is not a role-based network login. | | `web credentials rotate --force` | Replace the legacy recovery credential and synchronize the migrated pre-bootstrap account only. | | `web certificate info` | Display certificate identity and fingerprint. | | `web certificate rotate --force` | Replace the HTTPS certificate and private key. | | `web reset --force` | Explicitly replace missing, incompatible, or damaged legacy credentials and web material. | 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. Selecting **Disconnect** closes the current WebSocket and pauses automatic reconnect; select **Reconnect** to resume connection attempts. Account mutations revoke only that account's tickets and sessions. ## SSH serial transport | Command | Description | |---|---| | `ssh` / `ssh help` | Show SSH command usage. | | `ssh status` | Show service state and resource information. | | `ssh start` / `ssh stop` | Start or stop the SSH server. | | `ssh sessions` | List active SSH sessions with account, user role, authentication method, route, broker role where applicable, and admin-worker state. | | `ssh disconnect ` | Disconnect one SSH session. | | `ssh counters` / `ssh clear-counters` | Show or clear SSH counters. | | `ssh host-key info` | Display the OpenSSH host-key fingerprint. | | `ssh host-key rotate --force` | Replace the persistent SSH host key. | | `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. 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 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. `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. Web recovery credentials/certificates, Wi-Fi secrets, and interactive user passwords/keys are available to authenticated administrators and must therefore be treated as remotely accessible administrative material. `user bootstrap` and `user recover --force` remain 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 | Command | Description | |---|---| | `debug status` | Show MAX3243 driver, receiver, VLD, and shutdown states. It requires UART1 to be stopped. | | `debug transceiver ` | Enable or shut down the MAX3243. | | `debug drivers ` | Drive static TX, DTR, and RTS logic levels for measurement. | | `debug loopback-a` / `debug loopback-b` | Test MAX3243 driver/receiver loopback configurations. | | `debug valid-test` | Verify valid RS-232 voltage detection. | | `debug uart-loopback [8N1|8E1|8O1|8N2|7E1|7O1] [bytes]` | Run a parameterized UART loopback test. | | `debug uart-suite` | Test supported baud rates and frame formats. | | `debug cts-flow-test` / `debug rts-flow-test` | Verify hardware transmit gating or receive backpressure. | | `debug display status` | Show the current display diagnostic state. | | `debug display probe` | Probe the expected OLED addresses 7-bit `0x3c` and `0x3d`, initially using 100 kHz I²C. The tested module responds at `0x3c`. | | `debug display scan --force` | Scan usable 7-bit addresses `0x08`–`0x77` at 100 kHz; use only on this dedicated local-UI bus. | | `debug display init [address]` | Initialize the OLED at 7-bit `0x3c`/`0x3d`, or their 8-bit write/read aliases: `0x78`/`0x79` and `0x7a`/`0x7b`. | | `debug display off` | Turn off the initialized OLED. | | `debug display pattern ` | Draw a full-screen electrical and geometry test pattern; `layout` renders separate status- and content-panel text. | | `debug display row <0..63>` | Draw the selected one-pixel display row for addressing and color-boundary checks. | | `debug display contrast <0..255>` | Set the OLED contrast to the specified bounded value. | | `debug display invert ` | Enable or disable OLED pixel inversion. | | `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. | 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.