ssh-stamp 1.0.4

SSH to UART bridge firmware core for microcontrollers
Documentation
// SPDX-FileCopyrightText: 2026 Roman Valls Guimera <brainstorm@nopcode.org>
// SPDX-FileCopyrightText: 2026 Julio Beltran Ortega <jubeormk1@gmail.com>
// SPDX-FileCopyrightText: 2026 Gabriel Ku Wei Bin <gabriel.ku@fsfe.org>
// SPDX-FileCopyrightText: 2026 Anthony Tambasco <anthony.tambasco@fastmail.com>
// SPDX-FileCopyrightText: 2026 Marko Malenic <mmalenic1@gmail.com>
//
// SPDX-License-Identifier: GPL-3.0-or-later

//! Platform-agnostic core of `ssh-stamp`.
//!
//! Hosts the SSH state machine, configuration handling, and the
//! [`platform::PlatformServices`] / [`serial::BufferedSerial`] traits that a
//! per-MCU adapter crate (e.g. `ssh-stamp-esp32`) implements.
//!
//! # Architecture
//!
//! `ssh-stamp` is firmware that turns a microcontroller into an SSH-accessible
//! serial bridge. Connect via SSH to the device, and your terminal session is
//! bridged directly to the device's UART.
//!
//! The design separates platform-agnostic logic (this crate) from
//! platform-specific implementations (port crates like `ssh-stamp-esp32`).
//! All hardware access flows through traits defined in [`ssh_stamp_hal`] or
//! through [`platform::PlatformServices`].
//!
//! ## Crate structure
//!
//! - [`ssh_stamp_hal`] — hardware abstraction traits (`WifiHal`,
//!   `NetworkProviderHal`, `OtaActions`, etc.)
//! - [`ssh_stamp_esp32`](https://docs.rs/ssh-stamp-esp32) — ESP32 port:
//!   trait implementations, bootable binary, per-target UART pin assignments
//! - [`ssh_stamp_esp32_boards`](https://docs.rs/ssh-stamp-esp32-boards) —
//!   per-board pin mappings and the board catalog
//! - [`ssh_stamp_ota`] — SFTP-based OTA update server (TLV header parsing,
//!   chunked flash writes, device reset; includes the `packer` host
//!   utility)
//!
//! `ssh-stamp-ota` depends on `ssh-stamp-hal` for [`OtaActions`](ssh_stamp_hal::OtaActions)
//! and is in turn depended on by `ssh-stamp` for SFTP-based updates.
//!
//! ## Repository layout
//!
//! Platform-agnostic crates live at the repository root. Everything
//! specific to one chip manufacturer lives under
//! `boards/ssh-stamp-<manufacturer>/`, one directory per manufacturer:
//!
//! ```text
//! ssh-stamp/
//! ├── src/                          ssh-stamp (this crate)
//! ├── ssh-stamp-hal/                hardware abstraction traits
//! ├── ssh-stamp-ota/                SFTP OTA server and `packer` tool
//! ├── xtask/                        build, flash and test runner
//! └── boards/
//!     └── ssh-stamp-esp/            Espressif
//!         ├── ssh-stamp-esp32/          port crate: HAL impls and firmware binary
//!         ├── ssh-stamp-esp32-boards/   board support: `boards/*.toml` pin maps
//!         └── ssh-stamp-esp32-hil/      hardware-in-the-loop tests
//! ```
//!
//! A new manufacturer gets its own `boards/ssh-stamp-<manufacturer>/`
//! directory with the same three crates; see "Adding a new port" in
//! [`ssh_stamp_hal`].
//!
//! ## Key modules
//!
//! - [`app`] — entry points [`prepare_ap_config`] and [`run_app`]
//! - [`handle`] — SSH event handlers (auth, channels, env vars)
//! - [`serve`] — SSH connection loop
//! - [`serial`] — UART bridge trait and bridge function
//! - [`config`] — [`SSHStampConfig`](crate::config::SSHStampConfig) struct and serialization
//! - [`store`] — Flash load/save/create
//! - [`platform`] — [`PlatformServices`](crate::platform::PlatformServices) trait (save config, reset, OTA)
//!
//! # Hacking
//!
//! ## Architectural invariants
//!
//! - **`src/` is platform-agnostic.** It must not import `esp-hal`,
//!   `esp-radio`, `esp-storage`, or any platform-specific crate. All hardware
//!   access goes through `ssh-stamp-hal` traits or `PlatformServices`.
//! - **Peripherals are owned by the state machine**, not globals. UART is an
//!   exclusive resource consumed by the serial bridge once SSH attaches.
//! - **Dependency graph is acyclic:** `ssh-stamp-hal <- ssh-stamp <-
//!   ssh-stamp-<port>`. `ssh-stamp` must not depend on any port crate.
//!
//! ## Adding a new SSH env var handler
//!
//! Edit `handle::session_env`. Add a new match arm for the variable name.
//! Follow the existing pattern: acquire the config lock, apply the change,
//! set `ctx.config_changed = true`, and call `a.succeed()`.
//!
//! ## Configuration
//!
//! On first boot (`first_login = true`), the device generates a random SSID
//! and WPA2 PSK (printed to the serial console) and accepts any SSH
//! connection. The client provisions a public key via the `SSH_STAMP_PUBKEY`
//! environment variable. Subsequent connections require that key.
//!
//! `WiFi` SSID and PSK can be changed at any time via the `SSH_STAMP_WIFI_SSID`
//! and `SSH_STAMP_WIFI_PSK` env vars. Changes are persisted to flash and the
//! device performs a software reset.
//!
//! The serial bridge line settings follow the same route through the
//! `SSH_STAMP_UART_BAUD`, `SSH_STAMP_UART_DATA_BITS`, `SSH_STAMP_UART_PARITY`
//! and `SSH_STAMP_UART_STOP_BITS` env vars, defaulting to 115200 8N1.
//!
//! ## Testing
//!
//! Host-side OTA TLV tests:
//! ```bash
//! cargo +stable test --package ssh-stamp-ota --target x86_64-unknown-linux-gnu
//! ```
//!
//! Manual testing requires a hardware target, a `WiFi` client, an SSH client,
//! and a serial device connected to the UART pins for bridge testing.
//!
//! [`prepare_ap_config`]: app::prepare_ap_config
//! [`run_app`]: app::run_app
//!
//! ## Build-time configuration
//!
//! The heap and buffer sizes below are declared in `build.rs` via `esp-config`
//! and overridable at build time with the matching environment variable. Note that
//! these are build time config variables, they are not used at runtime.
#![doc = include_str!(concat!(env!("OUT_DIR"), "/ssh_stamp_config_table.md"))]
// `no_std` on device; under `cargo test` the std test harness needs std, same
// pattern as the `ssh-stamp-ota` crate. `src/` stays platform-agnostic either way.
#![cfg_attr(not(test), no_std)]
#![forbid(unsafe_code)]
#![deny(clippy::mem_forget)]
#![deny(unused_imports)]
#![deny(unused_variables)]

extern crate alloc;

pub mod app;
#[cfg(feature = "can")]
pub mod can;
pub mod config;
pub mod errors;
pub mod handle;
pub mod mem_probe;
pub mod platform;
pub mod serial;
pub mod serve;
pub mod settings;
pub mod store;