Skip to content

About

ESP32-S3 signed wake authority: Wake-on-LAN over LAN HTTP and tailnet UDP (MicroLink/Tailscale-compatible tunnel), observer telemetry, nonce replay protection

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

microlink-wake — ESP32-S3 signed Wake-on-LAN authority

An ESP32-S3 firmware (ESP-IDF v5.3) that wakes a machine on the LAN with a HMAC-signed Wake-on-LAN request, reachable two ways:

  • LAN HTTP on port 48320 — /v1/health, /v1/observer, POST /v1/wake
  • Tailnet UDP on port 48320 via a MicroLink tunnel (a Tailscale-compatible WireGuard peer implementation, vendored in components/microlink), with the same signed transcript carried as a line-oriented datagram

Extras: live observer telemetry (target probe, RSSI, heap/PSRAM, tunnel state, boot diagnostics persisted in NVS), a 32-entry nonce replay cache, and a self-healing link supervisor (gateway probe → WiFi reconnect; tunnel down → rebind ×3 → reboot).

Compatibility note: the wire-visible protocol names (auth scheme, header names, schema strings, discovery probe, authority identity) are compile-time constants with neutral defaults, documented in docs/PROTOCOL.md. They can be overridden per build for an authority that runs pre-rename firmware — see "Wire name configuration" in docs/PROTOCOL.md.

Hardware assumptions

  • ESP32-S3 dev board (e.g. CH343-USB bridge class), mains powered
  • 16 MB flash (partition table in partitions.csv assumes it: two 3 MB OTA slots + SPIFFS + coredump)
  • Octal PSRAM (8 MB) — the board's PSRAM is OCTAL; quad mode fails PSRAM init and boot-loops. sdkconfig.defaults already sets CONFIG_SPIRAM_MODE_OCT=y. Do not "fix" it to quad.

Build & flash

idf.py set-target esp32s3
idf.py build
idf.py -p <port> flash monitor

Deliberate single-pass flashing: this project uses CONFIG_ESPTOOLPY_FLASHSIZE_16MB with a custom partition table whose offsets must match the bootloader written on the first flash. If you change partitions.csv or flash geometry, erase the whole flash first (idf.py erase-flash) and re-flash everything in one pass — OTA data and NVS offsets are otherwise stale.

Configuration

Two git-ignored headers hold everything device-specific:

File Contents
main/secrets.h HMAC_SECRET_HEX (256-bit shared key), Tailscale auth key, tailnet device name
main/target_config.h WiFi SSIDs + password, static LAN IPs/mask/gateway, WOL broadcast address, target machine MAC, priority tailnet peer IP

Copy the .example files and fill in real values:

cp main/secrets.h.example main/secrets.h
cp main/target_config.h.example main/target_config.h

Both are in .gitignore. The HMAC secret must match the secret your client signs with (openssl rand -hex 32 to generate one).

Client usage

python3 tools/wake_client.py --ip <board-tailnet-ip>            # wake over the tunnel
python3 tools/wake_client.py --ip <board-lan-ip> --mode http    # wake over LAN HTTP
python3 tools/wake_client.py --ip <board-ip> --observer         # telemetry JSON

The secret is supplied via --secret-hex-file, the WAKE_SECRET_HEX env var, or an interactive prompt — never embedded in the script (see tools/README.md).

Security model

  • Every wake request carries a transcript — method \n target \n timestamp \n nonce \n body-SHA256 — signed with HMAC-SHA256 under the shared secret; the header form is Authorization: <scheme> <hex> (scheme name configurable, see docs/PROTOCOL.md). Verification uses a constant-time compare.
  • Clock skew is accepted only within ±60 s of the board's SNTP-synced clock, and the board refuses to serve before its clock is plausibly synced, so captured requests expire quickly.
  • A 32-entry nonce replay cache rejects reuse of a nonce within the skew window (cache rotates FIFO).
  • Request bodies are size-capped (4096 bytes) and digest-checked before the signature is evaluated.
  • Why the config headers are git-ignored: secrets.h holds the live HMAC key (anyone holding it can wake the target) and a live Tailscale auth key (anyone holding it can join your tailnet). target_config.h holds your LAN topology and the target's MAC. None of it belongs in version control.

Wire compatibility

docs/PROTOCOL.md is the byte-exact contract, written against the neutral default names. The names are configurable at compile time (see "Wire name configuration" there), so the firmware can match an authority running pre-rename firmware without touching any client.

About

ESP32-S3 signed wake authority: Wake-on-LAN over LAN HTTP and tailnet UDP (MicroLink/Tailscale-compatible tunnel), observer telemetry, nonce replay protection

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages