Skip to content

Releases: jeffevertse/SoundTouch-Device

v0.6.0 — hardening release

Choose a tag to compare

@jeffevertse jeffevertse released this 01 Aug 17:56

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 /stream return 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 unusable proxy_port falls through to the .bak copy instead of failing ListenAndServe on 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

  • /play and /stream reject 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 /status and /healthz are reachable from a LAN dashboard. The origin policy is unchanged: localhost and private ranges only.
  • /bass reports 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.sh no longer uses xargs -r, a GNU extension BusyBox may lack — under set -e a failure there aborted before the service started, leaving an installed but not running daemon. It also health checks the configured proxy_port rather than a hardcoded 8099.
  • config.example.json: removed dead icon fields.

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 SHA256SUMS

Note 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.