# H12: esptool 5.6.0 source migration Both `reticulum-daemon/build.sh` and `build-esptool.sh` now pin **5.6.0**. Its [pinned dependency declaration](https://github.com/espressif/esptool/blob/v5.6.0/pyproject.toml) requires Python >=3.10 and replaces the old ecdsa dependency with cryptography. The disposable CPython 3.13.5/Linux x86_64 resolution contains 23 packages, with **no ecdsa and no OSV findings**. No suppression was added. This closes the retained source-dependency path; it does not replace or qualify an installed binary. The implementation follows the [upstream migration guide](https://docs.espressif.com/projects/esptool/en/latest/esp32/migration-guide.html) and [supported CLI embedding API](https://docs.espressif.com/projects/esptool/en/latest/esp32/esptool/scripting.html). - Both packaged entry points share the public `esptool.main(argv)` adapter, reviewed-version check and actual ESP32-S3 resource check. Fatal/serial/interrupt errors retain a nonzero exit and stderr detail. The private `_main` IDE exception reporting hook is not initialized by the wrapper. - The v5 stub constructor receives the real ESP32-S3 target with a closed serial object, so preflight exercises resources without opening a port. Build scripts collect esptool and esp-pylib resources/metadata; the frozen qualification script expects the new version. - RNS 1.3.5's downloaded-runner substitution translates command/option/reset spellings while preserving offsets, paths, baud and board settings. Its actual writer exit and independent EEPROM/hash/signature checks remain required. MeshCore/Meshtastic use canonical `write-flash --erase-all`. Default-reset before connect and hard-reset afterward remain the upstream defaults and are tested. - The owned adapter must install **before importing esptool**: new esp-pylib caches the serial enumerator. A disposable PTY test runs the real v5 loader and verifies duplicate-FD use, no pathname reopen, a single appointed inventory entry, rejection of a different device and retention of the owner's FD after borrower close. - Piped progress is plain, newline-delimited output. The backend accepts v5 decimal percentages and legacy integer percentages; values outside 0–100 are ignored. It still captures stderr and preserves the existing conservative retry policy. ## Source evidence Receipts: `/home/archipelago/Reports/21-security-credentials-20261109/esptool-v5/`. - `source-tests-r2.log`: **17/17 passed**, real pinned v5 parser/resources/progress/ entry points and transport doubles, plus existing RNS crypto/EEPROM and negative writer fixtures. The earlier r1 records a test invocation path error, not a pass. - `inventory-tests.log`: **4/4 passed**, including stale pin and missing SHA refusal. - Source UF2 self-test passes; build shell syntax and `git diff --check` pass. - Backend execution is reserved for the parent isolated runner. Focused filter: `mesh::flash::flashing_regression_tests`; new test: `accepts_v5_and_legacy_progress_without_out_of_range_percent`. No backend execution was performed in this worktree. Reproduce source checks in a disposable Python >=3.10 venv with esptool==5.6.0 and the exact runtime requirements, from `reticulum-daemon/`: ```sh python -m unittest -v test_esptool_v5 test_rnode_wrapper ``` The updated dependency evidence lives under `/home/archipelago/Reports/next-release-nine-issues-20261109/`: `python-resolution-linux-x86_64-r2.json`, `python-osv-resolved-r2.json`, `python-license-metadata-r2.json`, `python-graph-evidence-r2/`, and `security-inventory-qualified-r3/`. Every resolved artifact SHA is verified. Unchanged licenses retain their existing exact provenance, including Reticulum's custom restrictions, RNS's exact release-tag license commit and PyInstaller's per-file terms. Nine new/changed artifacts have exact license-file hashes. The inventory has 2,498 components, 18 hashed inputs and zero missing package license declarations. Unchanged non-Python graphs were not rescanned. ## Acceptance still required No release artifact was built, installed, signed or published; no physical serial port was enumerated or flashed. Later artifact qualification must inspect actual frozen contents (including absence of ecdsa), notices and stub files, run both packaged capability/self-tests and `qualify_frozen_rnode.py` in the fixed runtime, and qualify the inherited FD descendant on that artifact. The recorded Python resolution is a point-in-time graph, not a transitive lock or another-platform SBOM. Physical ESP acceptance still needs authorized Heltec v3/v4 MeshCore/Meshtastic and RNode write, reset, conservative retry/failure and post-write verification flows. v5's selected stub and hardware behavior are not proved by a mock writer. The user's prior **RAK blind-fix waiver is specific to RAK** and does not waive ESP acceptance. Maintainer UI/UX review of flashing progress/errors also remains separate. No broad hardware, artifact, release or security-clear claim is made.