GitHub

S60 private-Safeboot recovery contingency

This path is used only after the official-Safeboot live NVS preflight and one controlled Reset 4 attempt both refuse. It does not relax or bypass the official procedure. It creates a separate manifest, evidence directory and explicit recovery mode.

The target partition-table sector remains the byte-exact official sector with SHA-256 619e5a9b645198b25d04949997221f587776965dfd13cbceddf9862c53e655c1. Only the initial factory-partition payload is temporarily different.

Why a source patch is required

Defining STA_SSID1 and STA_PASS1 alone is insufficient. Tasmota Safeboot is FIRMWARE_MINIMAL; its normal SettingsLoad() path deliberately skips default settings initialization. The reviewed patch calls SettingsDefaultSet1() and SettingsDefaultSet2() in recovery Safeboot only. This copies the compiled Wi-Fi values into RAM on every boot while SettingsSave() remains disabled.

With the official 20 KiB NVS table installed, ESP32 Arduino initialization detects the truncated NVS state (ESP_ERR_NVS_NO_FREE_PAGES), erases that new NVS partition and initializes it again. The recovery build does not depend on the old Tasmota Settings blob surviving that erase.

Private offline preparation

Never type the Wi-Fi password into chat, a shell argument or a committed file. Run the local hidden-input helper:

python3 tools/configure_recovery_safeboot.py

It creates this Git-ignored mode-0600 header:

captures/safeboot-recovery/user_config_override.h

Use a clean checkout of pinned Tasmota commit db3ae7e0276fdc38b7aeb241a2a8e33d8ffd6892, then build:

tools/build_recovery_safeboot.sh /tmp/s60-tasmota-recovery-src

The build script:

Create the separate recovery migration manifest:

python3 tools/prepare_recovery_safeboot_migration.py
python3 tools/safeboot_migration_status.py \
  --manifest captures/safeboot-recovery-migration/manifest.json

Do not continue unless both commands say PASS. The status must identify PRIVATE VOLATILE-WIFI RECOVERY IMAGE and the exact official target table.

Guarded live sequence

The plug must still be running Bluetooth from old high ota_1; Berry flash.current_ota() must return 1. Run SaveData 0 again immediately before the new preflight.

Run recovery preflight, stage and commit with the recovery manifest:

python3 tools/serve_safeboot_migration.py preflight \
  --manifest captures/safeboot-recovery-migration/manifest.json \
  --listen-ip <WORKSTATION_LAN_IP> --device-ip 192.168.1.96

python3 tools/serve_safeboot_migration.py stage \
  --manifest captures/safeboot-recovery-migration/manifest.json \
  --listen-ip <WORKSTATION_LAN_IP> --device-ip 192.168.1.96

Recovery preflight records incomplete NVS as expected but still requires the exact source table, high running slot, complete host uploads and a stable NVS hash. Stage writes only inactive old ota_0 and uploads a complete read-back.

Commit retains the same safety lock, explicit risk flag, separate loader and single-use arm token as the official path. Use the exact command printed by the status tool; never invent or reuse a token.

After commit, the plug should boot private recovery Safeboot and reconnect with its reserved address. Upload the pinned official tasmota32c3.bin through its Firmware Upgrade page. Normal Tasmota may reconnect directly or start its supported setup AP; configure Wi-Fi there if required.

Restore exact official Safeboot

After the pinned normal image is stable, require Berry flash.current_ota() == 0. Start the cleanup server:

python3 tools/serve_safeboot_migration.py restore \
  --manifest captures/safeboot-recovery-migration/manifest.json \
  --listen-ip <WORKSTATION_LAN_IP> --device-ip 192.168.1.96 \
  --i-confirm-normal-app-is-stable

Loading the printed closure performs no writes. The separate arm command first requires the exact official table, pinned official running app and private recovery Safeboot hashes. It then writes official Safeboot at 0x010000, uploads every read-back byte to the host, verifies its SHA-256, and erases the remaining factory-partition tail.

Power loss during this cleanup does not overwrite the running canonical app0. Leave that app running and retry cleanup; do not deliberately select a partial factory image. Completion requires restore-report.json with status PASS.

The custom image and its build files contain Wi-Fi credentials. Keep all captures/safeboot-recovery* directories private and never publish them.