v0.114.0 binaries reported version 0.0.0 in the web UI. Two root causes, both fixed here. 1. The release build relied solely on Go's VCS stamping of info.Main.Version and never injected a version. When v0.114.0 was re-released via workflow_dispatch from `main` (one commit past the tag) with a shallow checkout, no tag was reachable, so Go stamped a v0.0.0-<ts>-<sha> pseudo-version. The asset filenames used the validated input version, so the files were named v0.114.0 but reported 0.0.0 at runtime. 2. The `release` and `workflow_dispatch` triggers followed two distinct patterns. On `release` every job's checkout landed on the tagged commit (GITHUB_SHA == tag); on `workflow_dispatch` they all built whatever branch the run started from. So a manual dispatch built the wrong source entirely (binaries and Docker images alike). Changes: - Unify both triggers on the git tag. `validate` resolves the tag once (inputs.tag on dispatch, release.tag_name on a release event), verifies it exists in git, and exposes it as an output. Every other job checks out `ref: needs.validate.outputs.tag`, so the build is always the tagged commit regardless of trigger. The dispatch path now re-releases an existing tag (push the tag first) instead of creating one from a branch; it fails fast if the tag is missing. - Inject -X main.version/commit/date into the release binaries, mirroring the Dockerfile (which has done this since #422). version/commit no longer depend on git stamping; commit is read from the checked-out HEAD (not github.sha, which on dispatch is the branch HEAD). Both binaries and Docker images take the v-prefixed tag (needs.validate.outputs.tag) so the displayed version stays "v0.114.0", matching prior releases. - Guard updateBuildInfo() in all four cmd/*/main.go so an injected version (version != "dev") is never clobbered by a VCS pseudo-version. `go install …@vX.Y.Z` still resolves the tag via build info as before. - Collapse the duplicated `if event_name == workflow_dispatch` tag derivations and route tag/version through needs.validate.outputs.*. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
soundtouch-backup
A standalone tool for backing up Bose SoundTouch data — both your cloud account (presets, devices, sources) and the local filesystem of each speaker — before the Bose cloud services shut down on May 6, 2026.
Overview
| Subcommand | What it backs up |
|---|---|
all |
Cloud account and all paired speakers in one step — the recommended starting point |
cloud |
Bose account profile, paired devices, cloud presets, music service sources |
local |
Speaker HTTP API data (presets, sources, volume, …) and optionally device filesystem files via SSH |
Output is a single .tar.gz archive (or .zip) with a dated root directory.
Building
make build-backup
# binary: ./build/soundtouch-backup
Or install alongside the other tools:
make install
Usage
Combined backup (recommended)
The all command is the simplest way to capture everything: it authenticates with the Bose cloud, backs up your account data, then reads the IP addresses from devices.xml and backs up each reachable speaker over HTTP.
# Interactive — prompts for email and password
soundtouch-backup all
# Non-interactive
soundtouch-backup all --email you@example.com --password secret
# Include SSH filesystem backup for each speaker
soundtouch-backup all --ssh
# Environment variables
BOSE_EMAIL=you@example.com BOSE_PASSWORD=secret soundtouch-backup all --ssh
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--email |
-e |
— | Bose account email ($BOSE_EMAIL) |
--password |
--pw |
— | Bose account password ($BOSE_PASSWORD) |
--ssh |
on | Also capture filesystem files via SSH for each speaker | |
--output |
-o |
soundtouch-backup-YYYY-MM-DD.tar.gz |
Output archive path |
--format |
tar.gz |
Archive format: tar.gz or zip |
Speakers that are offline or unreachable at the time of backup are skipped with a ✗ warning; the cloud data is still saved.
Cloud backup
Backs up data from your Bose account at streaming.bose.com. Credentials are prompted interactively if not supplied as flags.
# Interactive — prompts for email, masked password input
soundtouch-backup cloud
# Non-interactive
soundtouch-backup cloud --email you@example.com --password secret
# Environment variables (avoids secrets in shell history)
BOSE_EMAIL=you@example.com BOSE_PASSWORD=secret soundtouch-backup cloud
# Zip output
soundtouch-backup cloud --format zip --output my-bose-cloud.zip
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--email |
-e |
— | Bose account email ($BOSE_EMAIL) |
--password |
--pw |
— | Bose account password ($BOSE_PASSWORD) |
--output |
-o |
soundtouch-backup-YYYY-MM-DD.tar.gz |
Output archive path ($SOUNDTOUCH_BACKUP_OUTPUT) |
--format |
tar.gz |
Archive format: tar.gz or zip |
What gets fetched
| File in archive | Source endpoint |
|---|---|
cloud/emailaddress.xml |
GET /streaming/account/{id}/emailaddress |
cloud/devices.xml |
GET /streaming/account/{id}/devices |
cloud/sources.xml |
GET /streaming/account/{id}/sources |
cloud/presets.xml |
GET /streaming/account/{id}/presets/all |
cloud/full.xml |
GET /streaming/account/{id}/full (may overlap with the above; skipped if 4xx) |
Local backup
Backs up each speaker over its HTTP API on port 8090. With --ssh, also captures key filesystem files via SSH.
# Auto-discover all speakers on the local network
soundtouch-backup local
# Specific speaker
soundtouch-backup local --host 192.0.2.11
# Multiple speakers
soundtouch-backup local --host 192.0.2.11 --host 192.0.2.10
# Include SSH filesystem backup
soundtouch-backup local --ssh
# Longer discovery window on busy networks
soundtouch-backup local --discover-timeout 10s
Flags
| Flag | Short | Default | Description |
|---|---|---|---|
--host |
-H |
— | Speaker host/IP, repeatable ($SOUNDTOUCH_HOST) |
--port |
-p |
8090 |
Speaker HTTP port ($SOUNDTOUCH_PORT) |
--discover |
-d |
auto | Force mDNS/UPnP discovery |
--discover-timeout |
5s |
Discovery timeout | |
--ssh |
on | Also capture filesystem files via SSH | |
--output |
-o |
soundtouch-backup-YYYY-MM-DD.tar.gz |
Output archive path |
--format |
tar.gz |
Archive format: tar.gz or zip |
What gets fetched via HTTP
| File | Device endpoint |
|---|---|
info.xml |
/info |
name.xml |
/name |
presets.xml |
/presets |
sources.xml |
/sources |
now_playing.xml |
/now_playing |
volume.xml |
/volume |
bass.xml |
/bass |
balance.xml |
/balance |
capabilities.xml |
/capabilities |
network_info.xml |
/networkInfo |
clock_display.xml |
/clockDisplay |
zone.xml |
/getZone |
Endpoints that return HTTP 4xx (not supported on the device model) are silently skipped.
What gets fetched via SSH (--ssh)
SSH connects as root@<host>:22 with an empty password, which is the default for SoundTouch firmware.
Individual files:
| Remote path | Notes |
|---|---|
/etc/hosts |
DNS redirect state |
/etc/resolv.conf |
DNS resolver configuration |
/etc/remote_services |
Service registration (post-migration only) |
/mnt/nv/remote_services |
Alternative location for remote services |
Directories (all regular files recursively):
| Remote path | Contents |
|---|---|
/opt/Bose/etc/ |
Full Bose configuration directory, including SoundTouchSdkPrivateCfg.xml |
/mnt/nv/BoseApp-Persistence/1/ |
Persisted app state |
Missing files and directories are silently skipped with a ⚠ warning.
Archive structure
Both subcommands write into a single dated archive:
soundtouch-backup-2026-05-02/
├── cloud/
│ ├── emailaddress.xml
│ ├── devices.xml
│ ├── sources.xml
│ └── presets.xml
└── local/
├── A_Sound_Machine/
│ ├── info.xml
│ ├── presets.xml
│ ├── sources.xml
│ ├── volume.xml
│ ├── …
│ └── ssh/
│ ├── etc/
│ │ ├── hosts
│ │ └── resolv.conf
│ ├── opt/Bose/etc/
│ │ └── SoundTouchSdkPrivateCfg.xml
│ └── mnt/nv/BoseApp-Persistence/1/
└── Sound_Machinechen/
└── …
Running cloud and local separately produces two archives. To combine them, use the same --output path for both invocations — each adds its own subdirectory so they won't collide (.tar.gz does not support appending; use --format zip if you need a single archive from two runs, or just keep them separate).
See also
- Cloud Shutdown Survival Guide — full migration context
soundtouch-cli— live device controlsoundtouch-service— local cloud replacement