re/v5.0.3/mpy/*.dis. They do not share a single
string.
App-side facts below come from decompiled Hermes bytecode and from observing the live
API — they cannot be verified against the firmware image. Device-side facts carry
file.dis:line citations into the disassembly.Device-side: how the firmware builds its own URLs
Everything the device fetches hangs offcfg.ota_url and cfg.version.
Where cfg.ota_url comes from
-
project_main.perform_ota()sets it from the branch named inperform.ota(project_main.dis:446): -
Failing that,
f_ota.main.start_otaassembles the same default by concatenation (f_ota_main.dis:1221): -
If the release poll succeeds, the API overwrites it with its
body.endpointstring andcfg.versionwithbody.release_code(get_release_from_api,f_ota_install_ota.dis:672). The server therefore chooses the download host; the on-device S3 URL is only the fallback.
re/v5.0.3/rodata_strings.txt:1816, :1837
and :2123 — http://datapeak-developer.s3.us-east-1.amazonaws.com, the …/{} template
of it, and http://api.totemportal.com. No https:// Totem host appears anywhere; the
only https:// literals in the image are upstream MicroPython defaults
(micropython.org, raw.githubusercontent.com, gitlab.com).
The fetches
_get_endpoints(file_path, s3_dir_url, dest_dir) is the whole of the URL builder
(f_ota_install_ota.dis:1214):
s3_dir_url = cfg.ota_url + '/' + cfg.version and dest_dir = cfg.save_to
(default 'next'). So:
?uid= cache-buster is rand_key(length=8), 8 characters from A–Za–z0–9 seeded by
os.urandom (f_lib_helpers.dis).
The name is not a template — it is whichever entry of the contents.json array
satisfies entry[-4:] == '.bin' (or '.tgz'). The device then reverse-engineers the
version from the resulting URL (f_ota_install_ota.dis:1017):
…_v<code>.<ext> naming convention —
and it assumes it about a string the server supplied, with no validation.
The literal
firmware_v appears nowhere in the image: zero matches across all seven
segments seg0–seg6 and zero across the 94 frozen modules. The filename is a
server-side/app-side convention that the device only parses, never constructs.App-side: the releases API
The phone app fetches release metadata from an endpoint the firmware never mentions:No
releases literal exists in the firmware (grep -l releases *.dis → no matches). The
device’s only API endpoints are {API_ENDPOINT}/devices/{mac}/ota and
…/devices/{mac}/ota?updated, plus {API_ENDPOINT}/events/{mac} and
{API_ENDPOINT}/debug/{mac} for log upload after an update (ota_callback.dis:497).URL construction (app)
…/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 from decompiler output.
Which release fields the firmware actually knows about
Earlier revisions of this page listeds3_dir_url and package_json_url among the
“release fields referenced by the firmware.” Both were wrong:
The product/branch directory names are on-device, as plain strings:
totem_compass and
totem in f_ota/main.py and f_ota/system.py, totem_compass/totem as
perform_ota()’s ota_branch default, and totem_compass/pre_alpha as the default first
argument of the REPL helper project_main.force_ota() (project_main.dis:386).
On-device OTA vs app download
Verification
The object at the app-constructed URL 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.
Since the device never sees sha256_hash, this check confirms that the app’s download
is intact — it says nothing about what the device flashes. See
OTA integrity.
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 — that fixed per-version URL keeps
serving whatever bytes currently sit at the key until the object is replaced. Release
metadata, including the pre_alpha branch, is served openly by the app’s releases API.