Blob: ERRATA.md
Integration notes
Known issues and workarounds that matter when changing or upgrading the project.
RustCrypto certificate generation
The upstream str0m-rust-crypto certificate generator pulls in AWS-LC, whose
native headers reject Xtensa. The firmware uses a small
provider patch, disables that generator,
and supplies a fresh mbedTLS-generated certificate from the S3. Explicitly
select the configured crypto provider for DTLS; allowing default selection can
activate an unintended backend through Cargo feature unification.
Install only P-256 key exchange in the S3 provider before dimpl validates the configured groups. Allowing its software P-384 exchange test can starve the idle watchdog. Keep P-384 signature verification on mbedTLS; the corresponding RustCrypto startup check also adds substantial startup time. Provider, signature and fingerprint checks remain enabled.
Crypto adapter boundaries
The SHA value-snapshot adapter is specific to the pinned S3 port, whose contexts contain no pointers and release the peripheral lock between calls. Review both properties before changing the SDK or target; checking struct size is insufficient. In-place AES-GCM arguments must derive from the same exclusive Rust pointer. Independent shared/mutable slice reborrows can violate Rust aliasing rules.
The native provider uses mbedTLS for both EC curves. In the pinned build, hardware-assisted P-256 takes longer than software; using one EC backend is a simplicity tradeoff. Recheck matching build options and full connection startup before changing that policy. EC timings do not represent per-audio-packet costs.
Use scripts/build_firmware.py --crypto-self-test for adapter changes, then run
the image on the device and verify live audio/data. The extended tests include
altered signatures, malformed certificates and output bounds. Normal builds
omit their public signature fixtures.
str0m task stack
Keep Rtc behind a Box in the transport. Storing and returning it inline
created large nested stack frames that overflowed a 48 KiB radio task during
initialization. Measure the low-water mark on the S3 after transport upgrades;
host compilation cannot establish the device stack requirement.
The radio task runs on core 0. Full-catalog validation must yield between cache fills so that core's idle task can service its watchdog.
Keep the configured 1 KiB malloc threshold so larger allocations prefer PSRAM. The SDK's 16 KiB setting filled internal RAM with transport allocations. Task stacks, Wi-Fi and the FFT scratch buffer retain their internal memory.
Negotiated data channels
Create application channels using the IDs returned by the SFU. The two endpoints
have separate SCTP associations and can receive different IDs. Stream 0 carries
SFU server events and establishes SCTP before application registration. Reliable
commands use ordered robot; spectrum is unordered with zero retransmits.
The SFU can return only a channel's ID and name, without echoing ordered or
maxRetransmits. Normalize omitted settings from the shared channel profiles;
reject explicit settings that conflict with the requested reliability.
The command queue's 512-byte limit applies after SCTP reassembly. The local str0m/sctp-proto patches enforce earlier receive budgets and align advertised credit with retained DATA. Keep fragment, stream-state and byte bounds together; an SDP message-size attribute alone cannot bound receive memory. Tests cover fragmentation, TSN gaps, stream reset deferral and high negotiated IDs.
N32R16V memory configuration
This board uses 32 MiB octal flash and 16 MiB octal PSRAM at 1.8 V. Retain its OPI/DTR bootloader settings and partition layout. A generic S3 configuration may not boot this module. C6 and C61 are different chips; a C61 transport archive does not establish C6 support.
Rust and ESP-IDF linking
The build supplies espidf_time64 for ESP-IDF 5's time ABI. The C adapter
component is linked with WHOLE_ARCHIVE so references introduced by the Rust
archive resolve correctly. The pinned Xtensa compiler does not support the
compiler-builtins-weak-intrinsics feature; leave it disabled.
The HAL uses the existing CMake SDK through CARGO_CMAKE_BUILD_* metadata.
The pinned bindings generator does not track those inputs for Cargo, so the
build fingerprints its SDK configuration and header context in Rust flags.
Configuration changes then regenerate the bindings instead of reusing stale ones.
Keep the HAL's binstart and libstart features disabled because entry.c owns
app_main. Binding generation uses libclang from the pinned esp-radio
toolchain; make setup-firmware supplies it. Host checks compile the application
with a stub that returns an error if task configuration is called. They do not
run the firmware or compile its HAL path; make build-firmware compiles that
path against the SDK, and device checks validate its behavior.
Worker session cleanup and leases
Publisher replacement and expiry use the application's disposable-session policy. An SFU close failure on an obsolete generation must not block a replacement boot.
For cleanup within the current generation, treat responses 404/410 and an HTTP
200 close_track_error as already closed. The latter can appear at the top level
or on a track or DataChannel item. Keep other failures pending; an internal_error
does not establish absence. See the
SFU error-code clarification.
Keep the earliest pending Durable Object alarm:
ordinary status polling must not postpone controller-lease expiration. The
isolated signaling tests cover both behaviors.
SFU allocation responses can contain successful IDs beside failed items. Save
those IDs before checking the complete response. Optional pendingChannels and
pendingMids fields hold cleanup receipts, separate from usable media. They
survive eviction within the current generation and remain until cleanup succeeds
or that generation is discarded. Before rolling back to a Worker version without
receipt handling, close listeners and replace the board session under the current
version.
Music pack compatibility
Preparation creates a v4 playlist catalog for the 16 MiB music partition.
The firmware also reads v1/v2/v3 single-song packs. V4 contains bounded v3 song
packs; firmware limited to single-song formats cannot read it. When upgrading
an installation with a 4 MiB music partition, flash the partition table that
allocates 16 MiB to music together with the firmware before
using make flash-music. The helper checks the installed partition before writing.
The browser accepts 48-byte spectrum v2 packets, which include a playback revision, and archived 44-byte v1 packets. Deploy compatible Worker/browser code before firmware changes that alter the protocol, and refresh open pages.
The archived C radio and FFT harness require v1 music packs and a partition table that allocates 4 MiB to music. Restore these together with the firmware when rolling back. The archive flash helper supplies the matching layout and v1 music independently of the main catalog.
Runtime metrics
CPU metrics require ESP-IDF's timer-backed U64 runtime counters. Idle runtime is committed when a task switches out. Keep the sampler on core 1 so its wake-up refreshes that core's counters during paused playback.
Temperature conversion uses floating point. Keep it on the pinned sampler task to prevent ESP-IDF's lazy FPU handling from implicitly pinning the radio task.
The browser's telemetry schema accepts an optional hardware object. Missing
hardware measurements display as unavailable.