# Phase 2 - Flasher Web

Goal: flash an ESP32 from a browser using Web Serial and a firmware `.bin` uploaded through the Phase 1 controller.

Implemented scope:

- `/flasher` page.
- Firmware catalog loaded from `GET /api/v1/firmware`.
- Firmware cards display name, version, application, compatible hardware, size, and SHA-256.
- Firmware selection.
- Firmware download from `GET /api/v1/firmware/{id}/download`.
- Web Serial connection flow.
- ESP32 flashing flow through Espressif `esptool-js`.
- Progress bar and status messages.
- User-readable errors for unsupported browser/context, denied access, missing port, disconnection, download failure, and write failure.

Out of scope for Phase 2:

- OTA.
- Provisioning.
- Fleet Management.
- Edge OS.
- Firmware generation.

## Secure Context

Web Serial requires a secure browser context. Accessing the VM directly through plain HTTP by IP address is not enough for real Web Serial use:

```text
http://192.168.1.62:88/flasher
```

Observed behavior on that URL:

- `/flasher` loads.
- Firmware catalog loads.
- Web Serial is blocked by browser context.
- The page displays a warning.

Minimal approved test path for Phase 2 is a local SSH tunnel from the Mac:

```bash
ssh -N -L 18888:127.0.0.1:88 codex@192.168.1.62
```

Then open:

```text
http://localhost:18888/flasher
```

Observed behavior through localhost tunnel:

- `/flasher` loads.
- No secure-context warning is shown.
- The Connect ESP32 button is enabled.

For real use through `iot.aeizoon.com`, HTTPS must be configured.

## Flash Address

The current firmware catalog does not yet store a flash address. The Flasher page therefore provides an operational `Flash address` field with default value:

```text
0x0
```

If a provided `.bin` requires a different address, set it manually before flashing.

## HTTPS Validation Route

As of 2026-07-06, HTTPS access is configured through NPM.

Operational URLs:

```text
https://iot.aeizoon.com/
https://iot.aeizoon.com/flasher
http://192.168.1.62:88/   # internal access only
```

Physical Phase 2 validation must be performed from:

```text
https://iot.aeizoon.com/flasher
```

Do not use the localhost tunnel except for punctual diagnostics.

Browser pre-check performed:

- `https://iot.aeizoon.com/flasher` loads.
- Page protocol is `https:`.
- The insecure-context warning is not shown.
- The Connect ESP32 button is enabled by the page.
- Firmware catalog currently loads with `0` items until a real `.bin` is uploaded.

## Physical Validation Adjustment

After testing an ESP32 Ethernet/WiFi AliExpress board through an external USB-TTL adapter, the default baudrate was changed to `115200` because this board was stable at `115200` and unstable at `921600`.

The Flasher also now resolves `Auto-detected` flash size before calling `writeFlash()` instead of passing `detect` directly to `esptool-js`.


## Cache and Fit-Check Adjustment - 2026-07-06

A repeated physical test still showed `File 1 doesn't fit in the available flash`, and the browser log did not include the latest diagnostic messages. This indicated that the HTTPS path was serving an older cached Flasher script.

Applied correction:

- `/flasher` now references `/js/flasher.js?v=phase2-20260706-3`.
- `/flasher` now references `/css/app.css?v=phase2-20260706-3`.
- `flasher.js` logs `Flasher build: phase2-20260706-3` on load.
- Static resources served by the Spring backend are configured with no-cache headers.
- Because NPM may still add external cache headers, asset version query strings must be bumped after Flasher JS/CSS changes.
- Flash size now defaults to `Keep firmware setting`, avoiding the `esptool-js` fit check that can reject a valid image before writing.
- `Auto-detected size check` remains available for diagnostics.

Next validation should use:

- URL: `https://iot.aeizoon.com/flasher`
- Baudrate: `115200`
- Flash size: `Keep firmware setting`
- Confirm first log line: `Flasher build: phase2-20260706-3`
