Releases: jeffevertse/SoundTouch-Device
Release list
v0.6.0 — hardening release
A hardening release: twelve fixes from a review pass over the daemon, plus CI. No new features, no dependency changes.
Robustness
- A stalled stream no longer holds a connection slot. The upstream fetch had no timeout and no link to the request, so a station whose CDN kept the TCP connection open but stopped sending data blocked indefinitely — holding one of the four concurrency slots until TCP keepalive gave up, roughly 11 minutes. Four stalls made
/streamreturn 503 to the speaker itself. The fetch is now tied to the request context, with a 20s idle watchdog that tears down a silent stream. - Upstream errors no longer play as audio. A non-2xx response was relayed to the renderer with its own body and content type, so a 404 HTML page reached the speaker as noise. Now returns 502.
- A bad config can't kill the daemon. Validation runs inside
Load, so an unusableproxy_portfalls through to the.bakcopy instead of failingListenAndServeon a service with no restart-on-failure. The daemon now logs which config file it loaded, or that it fell back to defaults. - Hardware preset sync gives up. A permanently rejected preset previously hit the device API every 5 seconds forever, growing the log on a device with ~20–40 MB of writable space. Now retries with backoff for about 8 minutes, then stops.
Security
/playand/streamreject cross-site GETs. Being GETs, they couldn't use the JSON content-type guard the POST endpoints have, so any page you visited could start playback via a hidden<img src=".../play/1">. Requests a browser labels cross-site are now refused. Non-browser clients — curl, the iOS companion app, the speaker's own renderer — send no such labelling and are unaffected.- CORS applies to every endpoint, so
/statusand/healthzare reachable from a LAN dashboard. The origin policy is unchanged: localhost and private ranges only. /bassreports the clamped level instead of echoing the request, so a slider can't drift out of sync with the speaker.- Duplicate preset ids are rejected. They previously passed validation while lookups returned the first and both were written to the same physical slot.
- Checked type assertion in
localIP, which could panic the daemon during boot.
Packaging
install.shno longer usesxargs -r, a GNU extension BusyBox may lack — underset -ea failure there aborted before the service started, leaving an installed but not running daemon. It also health checks the configuredproxy_portrather than a hardcoded 8099.config.example.json: removed deadiconfields.
CI
The repo now runs gofmt, vet, tests, the race detector, and the armv7 cross-compile on every push and pull request, and retains the built binary per commit.
The binary
soundtouchd-v0.6.0-linux-armv7 is the artifact CI built from this tag, not a local build — the same bytes that are running on the speaker this release was developed against.
sha256 555d9a9c84ee78f04d66ef7b315a3e470fe22f3180a8628448dcb1ec2fddb31e
Verify after downloading both files:
shasum -a 256 -c SHA256SUMSNote that builds are not bit-reproducible across hosts: rebuilding from source with the same Go version produces a functionally identical binary with a different hash, so use this artifact if you want the checksum to match.
Installing
Follow the README's order — snapshot first, and validate from /tmp before persisting:
make backup HOST=<speaker-ip>
make run-tmp HOST=<speaker-ip>
make install HOST=<speaker-ip>make install builds from source. To deploy this exact binary instead, stage it alongside the packaging scripts and run install.sh on the device.
Upgrading from 0.5.0: your config.json is preserved — the installer only seeds a fresh one when none exists. install.sh keeps a single rollback binary, so installing over 0.5.0 replaces the old rollback point; copy it off the device first if you want to keep it.