getS3FirmwareUrl and getFileName).
Release API
The app fetches release metadata from:/releases is the phone app’s API endpoint; no releases literal appears in the
firmware. The device itself resolves releases through the templates
{base}/package/{}/{}/{}.json (package manifest) and {base}/devices/{mac}/ota.{base}/devices/{mac}/ota and
{base}/devices/{mac}/ota?updated.
The
s3_bucket / s3_product_dir / s3_branch_dir / size_bytes split is how the
phone app parses the release JSON — none of those field names appear in the firmware.
On-device the firmware instead consumes a single pre-composed s3_dir_url, with the
product/branch paths baked in as the constant strings totem_compass,
totem_compass/totem and totem_compass/pre_alpha. The release fields actually referenced
by the firmware are release_code, release_id, sha256_hash, s3_dir_url,
package_json_url, ota_branch and branch_name.URL construction
…/totem/5.0.2/firmware_v5.0.2.bin).
The construction order was confirmed from the raw Hermes opcodes (a HermesInternal.concat
call of exactly ten operands ending in the filename), not only the decompiler output.
On-device OTA vs app download
The URL above is the phone app’s direct-download construction. The device’s own OTA flow is materially different: it fetches a package manifest and installs a package into a local “repo” rather than pulling a singlefirmware_v<code>.bin by URL.
The
firmware_v<code>.bin filename does not appear anywhere in the firmware image (no
firmware_v literal in any segment or the ELF); it is a phone-app construction. It is
corroborated instead by the actual downloaded artifact — the downloaded firmware_v5.0.3.bin
(and earlier firmware_v5.0.2.bin) is byte-identical to the object at the constructed URL.
Verification
The downloaded object matched the API’s own integrity fields exactly:
The v5.0.2 download (release 335) matched its record the same way (1,692,352 B,
a6d05597…c7bf8896).
The image begins with 0xE9 and carries the ESP-IDF app-descriptor magic 0xABCD5432,
confirming a genuine ESP32 application image.
The path is scoped by
release_code (the /5.0.3/ segment), but the object is a plain
static S3 key with no content-hash or immutable prefix — so that fixed per-version URL
keeps serving whatever bytes currently sit at the key (it is mutable) until the object is
replaced. Release metadata (including the pre_alpha branch) is served openly by the app’s
releases API.