The /setup/migrate/{deviceIP} reference table covered only the legacy
self/proxied/original mode selectors, with a one-line "Custom service
URL" mention of target_url. The wizard has been writing literal
per-field URLs via marge_url / stats_url / sw_update_url / bmx_url
for weeks; external API callers had nothing to read.
Expanded the table into three blocks with precedence rules:
1. Top-level params — method, target_url, proxy_url with the
four migration mechanisms (xml / telnet / resolv, hosts marked
deprecated).
2. Per-field implementation mode — the legacy self/proxied/original
family, kept for API back-compat with a note that the UI no
longer sets them.
3. Per-field literal URL overrides — marge_url / stats_url /
sw_update_url / bmx_url with a "literal wins over mode" rule
and the soundcork-suffix-propagates-to-envswitch note.
Three example curl invocations (canonical XML, soundcork telnet,
resolv with HTTPS) replace the old proxy=original-only snippet up
top.
Also added stub reference entries for POST /setup/telnet-probe and
the internal GET /probe/{token}[/*] catch-all — the SSH-less
reachability check the wizard runs automatically in its pre-flight
panel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The guide still described the pre-wizard UI: "SSH status, CA trust
status, and connection test results before letting you apply the
redirect" and two methods (XML / DNS). The migration tab now opens
with the state card + Plan card + Customize three-axis form + visible
pre-flight panel, and a third transport (Telnet:17000) lets users
without SSH access migrate too.
Updates:
- Step 3 retitled "Enable shell access on each speaker" with two
sub-sections: SSH (the richest option, required for XML / DNS /
CA install) and Telnet:17000 (the SSH-less fallback, no setup
required, HTTP-only).
- Step 5 rewritten to walk through the actual UI:
* the state card's three rows (Transports, Migration State,
Preconditions) with the action affordances inline
* the Plan card — target URL with Save as default, per-field
Service URLs editor with validation and soundcork-mode,
account pairing, and Apply Suggested Plan
* the visible pre-flight checks panel with its three or four
checks per method and the Proceed Anyway / Cancel branch
* Customize this migration with three independent axes
- Step 6 mentions the auto-expand of Customize on Apply success
and the per-transport reboot picking.
- Rollback section adds the telnet-only "reboot reverts the
runtime layer if envswitch isn't written" property, plus the
rename to "Revert to Defaults" matching the button label.
The image reference (ui-migration.png) stays pointing at the
existing screenshot; a fresh capture is needed once the wizard is
final but the surrounding prose is now accurate either way.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The feasibility analysis (§§1–8) was written before any of the wizard
shipped, and §7 forecast the surface area roughly. The migration tab
grew considerably during implementation — three-axis state model,
Plan card with per-field URL editor and validation, Customize
three-axis form, visible pre-flight panel, account pairing folded
into the wizard, and the SSH-less round-trip probe — none of which
the original §7 captures faithfully.
Added §9 "What actually shipped (post-implementation addendum)" with:
§9.1 Three-axis state model (per-axis migration booleans, IsPaired,
the state-card layout)
§9.2 Plan card per-field URL editor (single source of URL overrides
for both XML and Telnet, live optimistic preview)
§9.3 Customize three-axis form (URL flip / DNS / CA radios driving
applyCustomPlan)
§9.4 Pre-flight panel (visible check list, decision tree, override
affordances)
§9.5 Telnet round-trip probe (the SSH-less reachability check via
swUpdateUrl flip + :8090/swUpdateCheck trigger + probe-token
registry)
§9.6 Backend additions worth knowing (applyURLOverrides, parser,
option allow-list, telnet timeout bumps)
§9.7 Future probe candidates (pushCustomerSupportInfoToMarge;
running the round-trip probe on SSH-capable speakers too)
§§1–8 stay verbatim as the historical feasibility record, with a
forward-pointer at the head of §7 so readers know the as-shipped
state is documented further down.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wire up X-Real-IP / X-Forwarded-For / True-Client-IP support for
deployments fronted by a reverse proxy, while staying safe on flat-LAN
deployments where a malicious speaker could spoof those headers
directly.
Two new fields on `datastore.Settings`:
* TrustForwardedHeaders (bool, default false) — opt-in switch.
* TrustedProxyCIDRs ([]string, default `["127.0.0.0/8", "::1/128"]`)
— only requests whose immediate TCP peer falls in one of these
blocks may have their source IP rewritten from forwarded headers.
Loopback default matches the documented same-host nginx layout in
docs/guides/HTTPS-SETUP.md.
New middleware in `pkg/service/handlers/middleware_realip.go`:
* TrustedRealIP wraps `chi/middleware.RealIP` with a trusted-peer
gate. When the immediate TCP peer is in the allowlist, chi's
parsing handles the actual header → IP rewrite. When it isn't
(e.g. a speaker sending forwarded headers itself), we ignore the
headers and r.RemoteAddr stays as-is.
* ParseTrustedProxyCIDRs converts string CIDRs into *net.IPNet,
applying the loopback default on empty input and erroring loudly
on invalid entries.
Server.TrustedRealIPMiddleware() returns the middleware (or nil) by
reading the live settings; the router setup in
cmd/soundtouch-service/main.go installs it as the very first
middleware so SnapshotMiddleware and downstream handlers see the
correct r.RemoteAddr.
HandleMargePowerOn now prefers r.RemoteAddr over the body's
self-reported `<IPAddress>` for outbound credential push:
* The body field is treated as a hint only — a malicious LAN speaker
could set it to any value; using it for outbound HTTP requests is
the SSRF surface the previous zeroconf hardening was guarding
against from the sink side. Fixing it at the source as well closes
the gap entirely.
* When body IP and TCP source disagree, a log line names both and
the device ID so the discrepancy is investigable.
* RemoteAddr is unparseable → fall back to the body so we don't
silently drop the priming.
docs/guides/HTTPS-SETUP.md gains a follow-up note next to the existing
nginx snippet explaining the new flag, the loopback-only default, and
the explicit warning against enabling the flag on a flat-LAN
deployment without a real proxy.
Eleven test cases in middleware_realip_test.go lock in the gate
behaviour: trusted peers honoured for X-Real-IP / X-Forwarded-For /
no-headers / IPv6, untrusted peers' headers ignored, garbage values
rejected, ParseTrustedProxyCIDRs covers default / override / invalid.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Building on the strict literal-IP validator from the previous commit,
make the runtime error self-explanatory so anyone tripping on a
hostname URL can fix it in one shot:
* Errors now lead with the offending zeroconf URL and the rejected
host, so wrapping by GetInfo / PushCredentials / pushSimplifiedToken
doesn't bury the actual bad value.
* The "host must be a literal IP" error suggests two concrete one-liner
resolutions (`getent hosts <name>` and `dig +short <name>`) so the
user has a copy-paste fix.
* The "host is not on a local network" error names the accepted ranges
(loopback / RFC1918 private / link-local v4+v6) so the user knows
what they're allowed to pass.
docs/guides/SOUNDTOUCH-SERVICE.md gains a bullet under Security
Considerations explaining the constraint and the rationale (LAN-resident
SSRF surface), so the strict behaviour is documented rather than a
surprise.
The 17 TestValidateZcBaseURL cases still pass — only the message bodies
changed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New section §8 records what is currently known about which devices and
firmware our migrateViaTelnet flow handles end-to-end, derived from the
six community sources catalogued in TELNET-COMMAND-REFERENCE.md plus our
issue threads.
* §8.1 — proven to work end-to-end (ST 10, 20, 300, Wave III, Wave IV on
FW 27.0.6 with multi-reporter agreement).
* §8.2 — proven to need the PairAccount telnet fallback (ST Portable,
BST20 Portable: /setMargeAccount missing or wedged on those firmware
builds).
* §8.3 — likely to fail (SA-5 on FW 9.x with the older shell generation;
newer ST Portable builds with shrunk command set). The preflight +
abort-on-first-rejection design ensures these fail cleanly, leaving no
half-configured state.
* §8.4 — unverified targets that are expected to work but lack concrete
captures (ST 30, ST 520, Wave Music System I/II).
* §8.5 — flags the apparent contradiction between S5's enumerated
"valid roots" on ST 10 / FW 27.0.6 (which omits envswitch) and #221's
successful envswitch use on the same firmware. Most plausible reading:
S5 is a non-exhaustive probe, not a negative claim; preflight catches
any real absence.
* §8.6 — maps every failure mode to its observable outcome and the unit
test that exercises it.
* §8.7 — TL;DR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Synthesises every Bose SoundTouch port-17000 telnet command we have evidence
for, across six community sources: flarn2006's 2014 root-shell post,
Sam Hobbs's 2016 ST 10 setup-mode walkthrough, izndgroup's 2021 reissue,
sijeffrey's 2017 `bose` remote-control script, the 2026 r/bose telnet
probing thread (FW 27.0.6 ST 10), and our own #221 / #236 / soundcork#141
findings.
Groups the commands by family — `key` (front-panel button emulation, the
addition the Reddit thread brought in), `network` (WiFi profile management),
`sys` (verbs + the XML-tag-keyed `sys configuration` setter our migration
uses), `envswitch` (parallel persistence layer), `getpdo` (PDO read), `scm`,
`ws`, `swupdate`, and the historic shell-unlock commands. Each entry notes
firmware-era availability so implementations know whether to expect
"Command not found" on newer builds.
Records the four top-level command roots that S5 confirmed reachable on a
vanilla FW 27.x ST 10 (`key`, `net`, `sys`, `getpdo`), and flags that
`envswitch` works on other ST 20 / Wave models running the same firmware
family — a per-model variation the migration's preflight already handles.
Cross-linked from TELNET-MIGRATION-METHOD.md §2 and indexed in SUMMARY.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an SSH-free third migration path that drives the SoundTouch device's
diagnostic shell on TCP port 17000, plus a hardened replacement for the
fragile /setMargeAccount HTTP pairing call.
* `pkg/telnet` — new reusable, dependency-free client (sibling of `pkg/ssh`)
with deadline-driven Dial / Probe / SendCommand / Close. Mock-server tests
cover happy path, command-not-found, mid-stream close, and the wedged-device
read-timeout scenario.
* `setup.MigrationMethodTelnet` — runs `sys configuration` for all four URLs
plus the parallel `envswitch boseurls set` persistence layer that otherwise
wins on reboot, then verifies with `getpdo CurrentSystemConfiguration`.
Aborts on the first non-OK response so configuration is never half-written.
No SSH backup or rw pre-flight (the path is SSH-free by design).
* `setup.PairAccount` — probes :8090/supportedURLs first, time-bounds
POST /setMargeAccount aggressively (5s connect / 12s total) to avoid the
hangs reported in #236, and falls back to `envswitch accountid set <id>`
over telnet when the HTTP endpoint is missing or wedged. Returns a
PairAccountResult breadcrumb so the UI can show which path actually
succeeded.
* `setup.Reboot(deviceIP, method)` — gains a RebootMethod selector;
RebootMethodSSH stays the default (preserving prior behavior),
RebootMethodTelnet sends `sys reboot` over a fresh telnet session and
treats the inevitable socket-close as success.
* New endpoints on `/setup`:
- GET /account-id-suggestions/{deviceId} — returns the device's current
margeAccountUUID (from :8090/info) plus known account IDs from the
datastore, so the UI can offer reuse.
- POST /pair-account/{deviceId}?account_id=NNNNNNN — invokes PairAccount;
the existing reboot endpoint reads ?method=ssh|telnet from the query
string.
* Helpers `IsValidAccountID` (exactly 7 digits) and `GenerateAccountID`
(crypto/rand, retries on collision against a known-IDs list).
Documentation in docs/analysis/TELNET-MIGRATION-METHOD.md is updated to match
the implementation: bare-URL convention for `soundtouch-service`, no automatic
`sys reboot` (user-initiated via the existing button with a method selector),
and the realised package layout. The /etc/hosts method is intentionally not
exposed in the new flow.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents the SSH-free third migration path on top of the device's diagnostic
shell, synthesised from #221, #236, scheilch/opencloudtouch#167,
deborahgu/soundcork#228, and deborahgu/soundcork#141.
Captures the URL configuration command sequence, the dual persistence layers
(`sys configuration` + `envswitch boseurls set`), the `/setMargeAccount`
failure modes (404, hang, post-migration 502 on power_on) with their bounded
fallbacks, port-17000 preflight requirements, and account-ID sourcing rules
(reuse from `:8090/info`, pick from `DataStore.ListAccounts`, or 7-digit
manual/randomized entry). Cross-links the new doc from
DEVICE-REDIRECT-METHODS.md, marks the `/etc/hosts` method as deprecated, and
fixes the existing margeServerUrl example to use our service's bare-URL
convention with an explicit note for soundcork's `/marge` sub-path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a "Docker Compose (recommended for home servers and VMs)" section
to Step 1, pointing users to the existing docker-compose.yml and
.env.example. Clarifies the purpose of docker-compose.ci.yml (CI tests
only) and docker-compose.override.yml (local modifications, not in VCS).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- resolveIP now returns (string, error): error when result did not come
from the device's own SSH ping (service-side fallback or total
failure)
- migrateViaResolvConf and parseTargetURLAndResolveIP abort on error,
preventing a bad IP from being written to the device
- GetMigrationSummary captures the error in ResolveIPError and falls
back
to the hostname for the preview display; XML migration is unaffected
- Web UI shows a warning box with the error and a docs link when
resolution
is uncertain; migrate button stays enabled for the XML method
- Add hostname resolution troubleshooting section to TROUBLESHOOTING.md
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy 5 screenshots from _/screenshots/ into docs/images/ and wire them
into the migration guide (Settings, Devices, Sync, Migration tabs) and
the device initial setup guide (speaker AP mode Wi-Fi page). Replace the
images README wishlist with a table of what is actually present.
Also correct the AP mode IP address (192.0.2.1, verified on ST10) and
update the Settings step to match actual UI labels (Target Domain, DNS
Bind Address).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the placeholder MIGRATION-GUIDE.md (which had a "planned to be"
header, a nonexistent install.sh reference, and 9 broken screenshot links)
with a complete, image-free step-by-step walkthrough covering all 6 steps:
install, configure URL, enable SSH via USB stick, discover/sync, migrate
(XML or DNS/DHCP), and verify.
Add the Migration Guide to the README docs section and link to it from
the Survival Guide.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rewrite README.md to be concise and tool-focused (no code snippets),
clearly presenting all five tools and their use cases. Expand the
soundtouch-service section to cover both user scenarios and redirect
method trade-offs.
Rewrite SURVIVAL-GUIDE.md around the same two scenarios with step-by-step
instructions. Remove deprecated hosts-file method from all user-facing
docs; update MIGRATION-SAFETY.md, HTTPS-SETUP.md, and SOUNDTOUCH-SERVICE.md
to reflect only the two supported methods (XML redirect and DNS/DHCP).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Endpoints:
- POST /streaming/music/musicprovider/{id}/trial/is_eligible (reuses
is_eligible handler)
- POST /bmx/tunein/v1/favorite/{stationID} with datastore persistence
(SaveTuneInFavorite)
- DELETE /bmx/tunein/v1/favorite/{stationID} (DeleteTuneInFavorite)
- POST /bmx/core02/svc-bmx-adapter-orion/prod/orion/token (anonymous
Orion token)
- GET /bmx-icons/* serving embedded static/media assets (media.bose.io)
- GET /ced/* serving embedded firmware index, release notes, and 10
app-help XMLs (downloads.bose.com)
Add media.bose.io and downloads.bose.com to DNS redirect lists (setup.go
both domain slices, dns.go shouldIntercept list, main.go getDomains
map). Document implemented endpoints in
tests/interactions_20260502_missing_external.md; mark rows 0246–0247 as
self/☑ in the interactions table.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add CAPTURE-MIGRATION-TRAFFIC.md with step-by-step migration runbook
- Include session trace from first interactive ST10 migration run
- Genericize example IP addresses in BOSE-APP-ADB-Emulator.md and CAPTURE-DEVICE-PAIRING.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add scripts/android/ with setup-mitm-avd.sh (one-time) and start-mitm-session.sh (per-session)
- Move frida Dockerfile to scripts/android/; extract frida-server + SSL scripts via Docker
- Use native macOS mitmproxy app for capture (Docker NAT blocks emulator traffic)
- Add native-connect-hook.js to Frida launch — required for Bose app's native networking
- Document verified AP mode Wi-Fi provisioning endpoint (POST :8090/addWirelessProfile)
- Correct factory reset sequences for ST10/ST20 from official Bose guides
- Remove old scripts/setup-mitm-avd.sh and scripts/start-mitm-session.sh (moved to android/)
- Add session trace with lessons learned from first interactive capture run
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces a standalone `soundtouch-backup` CLI with three subcommands:
- `all`: authenticates with the Bose cloud, backs up account data, then
reads device IPs from devices.xml and backs up each reachable speaker
- `cloud`: fetches account profile, devices, sources, presets, and full
endpoint from streaming.bose.com
- `local`: backs up each speaker via HTTP API (12 endpoints) and
optionally via SSH (individual files + /opt/Bose/etc/ and
/mnt/nv/BoseApp-Persistence/1/ directories)
Also centralises pkg/service/ssh → pkg/ssh so both the service and the
backup tool share the same SSH client; adds ReadFile and ReadDir
methods, and handles the firmware quirk where cat exits 1 on empty
files.
Output is a single dated .tar.gz or .zip archive.
Example flow:
```shell
gesellix@Mac Bose-SoundTouch % go run ./cmd/soundtouch-backup all --output _/cloud-backup --email user@example.com
Password:
Authenticating as user@example.com...
✓ Authenticated (account ID: 1234567)
✓ email address (107 bytes)
✓ devices (1492 bytes)
✓ sources (1111 bytes)
✓ presets (2585 bytes)
✓ full account (55037 bytes)
Found 2 device(s) in cloud account, attempting local backup...
✓ ST20: 12 files via HTTP
⚠ ST20: SSH skipped /etc/remote_services (Process exited with status 1)
⚠ ST20: SSH empty file /mnt/nv/remote_services
✓ ST20: 64 files via SSH
✓ ST10: 12 files via HTTP
⚠ ST10: SSH empty file /etc/remote_services
⚠ ST10: SSH skipped /mnt/nv/remote_services (Process exited with status 1)
✓ ST10: 48 files via SSH
Archive written: _/cloud-backup/soundtouch-backup-2026-05-02.tar.gz (141 files)
```
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Attempting to request amazon_music:access with a standard application
client ID (amzn1.application-oa2-client.*) returns HTTP 400
lwa-invalid-parameter-bad-scope from the LWA authorization endpoint.
The scope is gated to Amazon Music partner device client IDs.
Revert scope to "profile" (working state) and document the confirmed
blocker with the exact error. Path forward: Amazon Music partner
registration for a device client ID; one-line change to AmazonScopes
when available.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Amazon bridge: fall back to sync/legacy on any error from
SetMusicServiceOAuthAccount (not only error 1029); timeouts from
unresponsive speakers no longer silently skip the fallback chain
- Amazon bridge: reduce speaker client timeout from 30s to 5s for
faster failure on local network calls
- marge: resolveSourceName now prefers SourceName/DisplayName over
SourceKeyAccount, so Amazon (and Spotify) sources show the account
holder's name instead of the raw account ID
- docs: update amazon-music-oauth.md with real-world test results;
music-api.amazon.com returns 401 because standard LWA apps lack
music::* partner scopes — infrastructure is complete but streaming
is blocked pending Amazon partner access
- docs: add SELF-HOSTING.md and MUSIC-SERVICES.md user guides; link
both in SUMMARY.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Mark status as Implemented
- Add "Trying It Out" section: LWA app setup, service flags, OAuth flow,
account verification, speaker priming, DNS requirement, site_id open question
- Fix stale endpoint table entry (no longer a stub)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Mark ZeroConf Spotify priming and 404 handler as addressed in both docs
- Remove stale "Remaining gaps" and "Already adopted" tracking tables from
community-tools.md; detail now lives in PARITY-SOUNDCORK.md
- Update PARITY-SOUNDCORK.md summary to reflect Groups and ZeroConf as done;
add cross-reference to community-tools.md
- Rename remaining "gesellix" project references to "AfterTouch" throughout
community-tools.md (URLs and author attribution unchanged)
- Add soundcork-stockholm-app (entry 7) to community projects list
- Correct DNS priority entry: built-in DNS server requires no external tools
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the simplified tokenType=accesstoken push with the full Spotify
Connect ZeroConf protocol: GET getInfo to fetch the speaker's 768-bit DH
public key, derive AES-128-CTR + HMAC-SHA1 keys from the shared secret,
and POST an encrypted LoginCredentials protobuf blob. Speakers that
receive a proper blob can self-refresh their Spotify session
independently, eliminating the need for periodic re-priming on token
expiry. Falls back to the raw token approach automatically when getInfo
fails, preserving compatibility with older firmware.
SHA1 is mandated by the Spotify Connect ZeroConf protocol spec for DH key derivation. This cannot be changed without breaking protocol compatibility.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Implement full SoundTouch app flow for Spotify registration in the Web UI.
- Update `/mgmt/spotify/init` to pass `accountID` via OAuth `state`.
- Add "Connect Spotify" button to Local Account tab in Web UI with polling.
- Implement legacy and Marge-sync fallbacks for speaker notifications (Error 1029).
- Add support for parsing multi-error XML responses (`<errors>`) from speakers.
- Add `NotifySourcesUpdated` to client for triggering manual source synchronization.
- Improve test coverage for error parsing and Spotify initialization handlers.
Co-authored-by: Junie <junie@jetbrains.com>
feat: improve Bose SoundTouch parity, Spotify integration, and data
reliability
- Update XML marshaling for ServicePreset and ServiceRecent to match
Bose parity requirements.
- Add support for adding music sources via
`/streaming/account/{account}/source`.
- Implement HandleBoseAccountToken for Spotify OAuth code exchange and
token persistence.
- Implement atomic file writes in the datastore to prevent data
corruption.
- Add startup logic to initialize default sources for existing devices.
- Expand test coverage with new parity regression and Spotify
integration tests.
---------
Co-authored-by: Junie <junie@jetbrains.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
- Enhance initial and full data synchronization to better align with
upstream services.
- Update data structures in 'pkg/models' to support missing fields
(e.g., SecretType for Spotify).
- Improve 'datastore' persistence logic for presets, recents, and
sources.
- Add comprehensive regression tests for sync and datastore operations.
- Update documentation on parity status and improvements.
Co-authored-by: Junie <junie@jetbrains.com>
Co-authored-by: Junie <junie@jetbrains.com>
- XML Refactoring: Transitioned from manual string concatenation to
structured XML marshaling using specialized Go models to match upstream
API responses exactly.
- Service Enhancements: Implemented robust device discovery via power_on
handling, improved source metadata persistence, and standardized ID
generation logic.
- Parity & Consistency: Fixed data loss and formatting mismatches for
lastplayedat, serialNumber, and nested <source> elements.
- Infrastructure & Testing: Added a comprehensive suite of regression
and parity reproduction tests, centralized common XML constants, and
documented progress.
Co-authored-by: Junie <junie@jetbrains.com>
Fix for:
```
Liquid Exception: Liquid syntax error (line 27): Tag '{% // Response: 200 OK %}' was not properly terminated with regexp: /\%\}/ in REQUEST_RECORDING_CONCEPT.md
/usr/local/bundle/gems/liquid-4.0.4/lib/liquid/block_body.rb:132:in `raise_missing_tag_terminator': Liquid syntax error (line 27): Tag '{% (Liquid::SyntaxError)
// Response: 200 OK
%}' was not properly terminated with regexp: /\%\}/
```
- Add RFC-compliant wildcard certificates (*.api.bose.io, *.api.bosecm.com) for automatic API coverage
- Include additional Bose production domains (worldwide.bose.com, music.api.bose.com, bose-prod.apigee.net)
- Implement TLS certificate request logging and wildcard domain matching logic
- Add detailed TLS handshake debugging with connection state tracking
- Wrap TLS listener with logging to capture certificate selection and handshake failures
- Update documentation with wildcard certificate coverage and debugging features
- Normalize test data to use consistent local IP addresses
This enables automatic coverage of all current and future Bose API subdomains
while providing comprehensive TLS debugging for DNS redirection troubleshooting.