Hardens TrustCACertFromBytes against the failure mode behind issue #262 (corrupted /etc/pki/tls/certs/ca-bundle.crt on a SoundTouch 20) and against silent transport-time corruption of our own writes. Three-part change. 1. Atomic write path. The previous flow piped bytes straight into the live bundle via `cat > <path>`; a dropped SSH session or partial write left the device with a half-written trust store and no way to roll back. The new path: - uploads to <bundlePath>.aftertouch.tmp (sibling on the same filesystem, same rw remount), - reads the tmp back over SSH, - validates the readback at the PEM-frame layer + the AfterTouch sentinel bracketing, - atomically `mv`s the tmp into place, - on any verification failure: `rm -f` the tmp; the live bundle is never touched, so there is no rollback semantics to reason about. The .original backup written on first install stays as defense-in-depth (manual recovery for corruption from outside this code path), but it is no longer the primary safety net. 2. New validators in pkg/service/setup/ca_validation.go. - validateCABundleBytes: BEGIN/END marker counts match, every decoded block is a CERTIFICATE with a non-empty body, decoded block count equals BEGIN-marker count (catches a block with unparseable base64 body), trailing non-PEM/non-comment content rejected. - validateAfterTouchLabelBracketing: CALabel appears exactly twice and brackets exactly one CERTIFICATE block. - stripAfterTouchEntries: collapses any number of stale AfterTouch entries from the existing bundle. Older releases reported to have appended without stripping, so long-lived devices can carry several copies; we strip them all and log the cleanup count rather than failing validation. Unpaired sentinels (truncated prior install) surface as a structured anomaly the caller logs and warns about. The validators stay at the PEM-frame layer on purpose — an earlier iteration called x509.ParseCertificate per block and rejected the real ST20 bundle on block 29 (Go 1.23+ disallows negative serial numbers, but Mozilla CCADB still ships ancient CA roots that have them). Shipping that version would have made every legitimate speaker install fail. The corruption mode #262 surfaces at the PEM-framing layer; x509-level checks aren't what we needed. 3. testdata/ca_bundle_st20_pristine.crt is the pristine /etc/pki/tls/certs/ca-bundle.crt captured off a real SoundTouch 20 (firmware 27.0.6.46330.5043500, snapshot 2022-08-04). Mozilla CCADB public dataset, 165 certs, ~251 KB. TestValidateRealSpeakerBundle locks in the cert count and asserts the strip pass is a no-op against a bundle that has never been touched by AfterTouch. Test infrastructure. mockSSH (both the setup-package and the handlers-package copies) now mirrors UploadContent into a private map so a subsequent `cat <path>` on the same path returns what was written there. Lets the tmp-readback step in TrustCACertFromBytes work against tests that only scripted the live-bundle path, without per-test wiring. Two new behavioural tests in setup_test.go: TestTrustCACert_StripsMultipleStaleEntriesSilently (pins the multi-entry cleanup contract) and TestTrustCACert_PostUploadVerificationFailureCleansUpTmp (pins the rollback-free recovery: live bundle untouched, tmp removed). Refs #262. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bose SoundTouch Toolkit
Independent project. Not affiliated with or endorsed by Bose Corporation.
Context: Cloud Shutdown
Bose is shutting down SoundTouch cloud services on May 6, 2026. After that, music service browsing, preset sync, and the official SoundTouch app stop working. This toolkit lets you keep your speakers fully functional.
See the Survival Guide for the full picture.
Tools
soundtouch-service — AfterTouch
A local server that replaces the Bose cloud ("AfterTouch"). Once your speaker is redirected to it, you have full control without any Bose cloud dependency. The built-in web UI at http://localhost:8000 handles all setup — no config files needed to get started.
If you want to run a server for this - no problem. The service is small enough to run on the SoundTouch itself. See the On-Device Installer for instructions.
Two scenarios:
Before shutdown — migrate your existing setup
While the Bose cloud is still running, use soundtouch-backup to save your account data. The local service web UI then helps with the migration so your speaker keeps its presets and credentials.
After shutdown or factory reset — start fresh Create a local account, configure your speakers, and start using them immediately. No Bose infrastructure required.
Redirecting your speaker
The service needs a stable address on your local network (e.g. soundtouch.fritz.box or soundtouch.local). The speaker must then be redirected to resolve the Bose cloud hostnames to that address. Two supported methods:
| Method | How it works | Notes |
|---|---|---|
| XML redirect | Upload a config XML via the Web API | Surgical; covers only registered endpoints; best for testing |
| DNS/DHCP | Serve custom DNS on your network | Covers all devices at once; requires port 53 and TLS |
The web UI walks you through each method. DNS redirect requires HTTPS — the service manages its own CA certificate and the web UI guides you through trusting it on each speaker.
Note: A hosts-file method (direct SSH edits to
/etc/hosts) also exists in the codebase but is deprecated and not exposed in the web UI.
Enabling SSH via USB stick
Some setup steps require SSH access to the speaker. Enable it once per device: create a file named remote_services on a FAT-formatted USB drive (the drive may need its bootable flag set — see SoundCork issue #172), and insert it while the speaker is powered on. After reboot, root SSH is available with no password.
See Device Initial Setup and Migration Guide for step-by-step instructions.
soundtouch-backup
Backs up your Bose cloud account (presets, paired devices, music sources) and each speaker's local state before the shutdown. Run soundtouch-backup all to capture everything in one step; it authenticates with the Bose cloud, then polls each paired speaker over the local network.
See the soundtouch-backup README for usage.
soundtouch-cli
Command-line control of any SoundTouch device: play/pause/volume, presets, source selection, multiroom zones, device discovery, and more. Works entirely over the local network — no cloud dependency. Well-suited for scripting and home automation.
See the CLI Reference for full usage.
soundtouch-web
A standalone web UI for device control — play, pause, volume, preset selection, real-time status — served from a local Go binary. Complements soundtouch-service when you want a dedicated device-control interface separate from the setup/admin UI.
See the soundtouch-web README for usage.
Go library
pkg/client provides a Go API for all SoundTouch device endpoints: media control, volume, presets, sources, zones, real-time WebSocket events, and device discovery. Use it to build your own integrations.
go get github.com/gesellix/bose-soundtouch
See the API Reference and pkg.go.dev for documentation.
Documentation
- Getting Started
- Survival Guide
- Migration Guide
- Device Initial Setup
- Migration & Safety Guide
- CLI Reference
- SoundTouch Service Guide
- HTTPS & CA Setup
- API Reference
Related projects
- SoundCork (Deborah Kaplan et al.) — Python service interception; pioneered the cloud emulation approach this project builds on
- SoundCork Stockholm App — Companion app for SoundCork
- SoundTouch Plus (Todd Lucas) — Home Assistant integration; extensive undocumented API documentation
- ÜberBöse API (Julius) — API research and advanced endpoint discovery
- Bose SoundTouch Hook (Adrian Böckenkamp) —
LD_PRELOADhooking for reverse engineering device internals
Support
- Bug reports: GitHub Issues
- Questions & discussions: GitHub Discussions
Star this project ⭐ if you find it useful!
License
MIT — see LICENSE.
SoundTouch is a trademark of Bose Corporation.