Files
Bose-SoundTouch/cmd/soundtouch-backup
Tobias GesellchenandClaude Opus 4.8 1c6f4c9eb8 fix(release): build the tagged commit and stamp the real version (#525)
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>
2026-06-25 09:33:48 +02:00
..

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

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