Trust store self-repair, preset sync between phone and computer, group join while waking - #599
Merged
Conversation
… added to Zone forming corrects a member's deviceID from a live /info read on the member itself, because a two-chip chassis announces its wlan0 (SMSC) MAC over mDNS and that is not the SoundTouch ID the firmware keys /setZone on. When that read did not answer in time the correction was skipped and the caller's value went into the group unchanged, so the master enrolled a member nobody answers for: its own zone reads back one member short, and the speaker wakes up showing "Select a source". Two field bundles on the same day show it, one of them twice within half a minute. On a seven-speaker fleet the correction fired for .26 at 18:38:10 and did not fire for that same speaker at 18:38:35, when its :8090 was busy waking, which put that box AND one other into the group under their wlan0 MAC: requestedSlaves=4, verified=2, missing exactly those two addresses. The Wave in issue #544 is the same shape, right after its OTA. So remember, per member IP, what that speaker's firmware last said about itself, and fall back to that instead of to the caller when it cannot be asked right now. The cache is only ever written from a firmware answer, so an id supplied by a caller can never get into it. Release-Note: fix(app): a speaker that is still waking up now joins the group it was added to Refs #544 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tart The bootstrap builds the box's certificate trust store by copying the firmware's own bundle to /tmp, appending STR's root CA and bind-mounting the result over the original. It copied BEFORE unmounting an overlay left by an earlier run, and with that mount still active source and destination are the same inode: the copy reads and writes one file and can leave it empty. What then gets mounted is STR's root and nothing else, and the box stops trusting every public certificate. That failure is silent and total. An ST20 field bundle from today has every https station and the Spotify engine dying seconds apart on the identical "x509: certificate signed by unknown authority", both hosts chaining to DigiCert Global Root G2, which every healthy box trusts. From the outside it reads like a station problem. Unmount first, so the copy always reads the pristine firmware bundle. Install the overlay only once it demonstrably still carries public roots: a box that trusts the internet but not us still plays radio and Spotify, and the agent re-appends its root to the live overlay anyway, while the reverse leaves the speaker unable to reach anything. Report the outcome through log() so the next bundle says which of the two happened. The overlay name is a plain counter now. One hex digit of an md5 collides one time in sixteen, and a collision made both trust stores share a single file. Release-Note: fix(speaker): the speaker keeps trusting internet stations and Spotify after a restart Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A box whose overlaid trust store lost the vendor's public roots fails every outbound TLS handshake with the same terse "certificate signed by unknown authority", https radio and the Spotify engine alike. From a bundle that error is indistinguishable from a station-side problem, because nothing recorded what the box trusts. tls_trust reports, per trust store path, its size, how many certificates are in it, whether STR's own root made it in, and a public_roots_missing verdict. A healthy box shows a three-digit count; one certificate means the overlay holds our root and nothing else, which is the broken state above. dns_status already covers the neighbouring fault class where the box cannot resolve at all; this is the one where it resolves and connects fine and then refuses what it gets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A slot recall was the only play path in the app that logged nothing on any exit. When one failed there was nothing to read on either side: the speaker's log has no incoming request, because the app never got that far, and the app's log was silent too. A bundle taken right after a failing preset press was therefore unreadable (#582: two tiles showing "speaker is still starting" over an idle speaker log). PlaySlot now logs accepted, rejected and failed, and the readiness probe logs its give-up with the ports it tried, how long it waited, and the last error or body it saw. Same file, unrelated: probeSTR overwrote a good deviceID with an empty one from /info unconditionally, while every neighbouring field was guarded against exactly that. A box with an empty deviceID drops out of every zone operation, since members are addressed by it. Refs #582 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…self The previous commit stops the boot script from ever building a trust store that holds STR's root and nothing else. It cannot help the speakers that are already in that state: an over-the-air update replaces the agent binary and nothing else, and the NAND copy of the boot script is only ever refreshed from a physical USB stick. Those boxes would re-break at every boot for good, however many updates they took, and the owner has no way to tell because the symptom is every https station and Spotify failing at once, which reads like a station problem. So the agent repairs it. A minute after start, past the window in which the boot script mounts its own overlay, it counts the certificates each trust store actually serves. A store that carries public roots is left untouched, which is every healthy box and costs two file reads. A store that carries none gets the overlay taken off, which is what reveals the pristine firmware bundle, rebuilt from it with our root appended, and mounted back. Every failure path leaves the firmware's own bundle live rather than the broken overlay: a speaker that trusts the internet but not STR still plays radio and Spotify and gets our root re-appended anyway, while the reverse leaves it unable to reach anything. If the firmware bundle turns out to be empty too, the overlay goes back, so the box never ends up with less than it had. The check is two bounded attempts, not a poll, because this corruption is created at boot and never mid-run. Verified on an ST10: the field state reproduced exactly (one certificate, every handshake refused), agent restarted, store rebuilt 0 -> 158 public roots, and both hosts from the reporter's log answered again - apresolve.spotify.com and the Deutschlandfunk stream. A natural boot after that reports both stores healthy and touches neither. /api/debug/state now reports the repair next to the trust store snapshot, because afterwards a repaired box and a box that was never broken read identically. Release-Note: fix(speaker): a speaker that stopped trusting internet stations and Spotify now repairs itself on the next update Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
rc.local logged the NAND override decision in German. Every other developer-facing line in this repository is English, and this one shows up in each diagnostic bundle's boot_log, where it is read by people who do not speak it. Nothing parses these strings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… the other way round Both screens read the presets once and then kept them. A key reassigned from the phone remote left the desktop tile showing the station it used to hold, next to a status line already naming the new one, and the same happened in reverse. Reported with a screenshot on discussion #597: tile 4 reads "181.FM - Classical Guitar" and says it is playing, while the line above it plays "Radio Art - Guitar Jazz". Both now re-read the list in the background and redraw only when it actually differs, so the common case costs one comparison. The cadence is 15 s and deliberately slower than the transport poll: this only has to catch a person pressing save somewhere else. The read goes to the agent's own preset store rather than the Bose firmware, so it adds nothing to the :8090 load that the status poll is rationed for. Two things fall out of re-reading in the background that did not matter when the list was read once. A read that fails or times out must not blank a grid that is already showing presets, so on the phone a null answer now keeps what is on screen, the way the desktop already guarded its own empty result. And the tapped tile keeps its highlight across a redraw, since losing the marker mid-song would read as the speaker forgetting what it is playing. Verified against the page the speaker actually serves: first load builds the grid, an unchanged re-read leaves it alone, a station renamed elsewhere reaches the tile while the playing marker survives, a failed read changes nothing, and a genuinely empty speaker still draws six empty tiles. Release-Note: fix(app): a preset saved on the phone now appears on the computer straight away, and the other way round Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| continue | ||
| } | ||
| s.logger.Warn("zone: member did not answer its firmware /info, using the deviceID it reported earlier instead of the caller's", | ||
| "ip", slaves[i].IP, "supplied", slaves[i].DeviceID, "cached", cached, "err", err) |
| continue | ||
| } | ||
| s.logger.Warn("zone: member did not answer its firmware /info, using the deviceID it reported earlier instead of the caller's", | ||
| "ip", slaves[i].IP, "supplied", slaves[i].DeviceID, "cached", cached, "err", err) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Sweep of the day's mail and GitHub reports.
A speaker that stopped trusting the internet
An ST20 field bundle had every https station and the Spotify engine dying
seconds apart on the identical
x509: certificate signed by unknown authority,both hosts chaining to a root every healthy box trusts. The bootstrap builds
the box's trust store by copying the firmware's bundle to /tmp, appending STR's
root and bind-mounting the result. It copied BEFORE unmounting an overlay left
by an earlier run, and with that mount active source and destination are the
same inode, so the copy could leave the file empty. What got mounted was STR's
root and nothing else.
Two commits, because the boot-script fix cannot reach the speakers already in
that state: an update replaces the agent binary and nothing else, and the NAND
copy of the boot script is only refreshed from a physical USB stick.
6daccadstops the boot script producing it, on both install paths.df3a351makes the agent repair it, which is what an update can deliver.Every failure path leaves the firmware's own bundle live rather than the
broken overlay, and if the firmware bundle is empty too the overlay goes
back, so a box never ends up with less than it had.
8202845reports what the box actually trusts in the diagnostic, sincefrom a bundle this was indistinguishable from a station-side problem.
Verified on an ST10: field state reproduced exactly (one certificate, every
handshake refused), agent restarted, store rebuilt 0 -> 158 public roots, and
both hosts from the reporter's log answered again. A natural boot afterwards
reports both stores healthy and touches neither.
Presets out of step between the phone and the computer
Reported on discussion #597 with a screenshot: tile 4 shows the station it used
to hold and says it is playing, while the line above it plays the one just
assigned from the phone. Both screens read the presets once and kept them.
Both now re-read in the background and redraw only on an actual difference, at
a cadence deliberately slower than the transport poll and against the agent's
own store rather than the Bose firmware.
Also here
d8dc69ba speaker that is waking still joins the group it was added to.463e460a preset press that fails leaves a trace, so the next bundle cansay why (follow-up to Pressing preset button on SoundTouch 10 is ignored, but the preset in the desktop app works -> now key 4 doesn't work on both the speaker and in the desktop app #582, where the app reached the speaker with nothing
written anywhere).
04ccc6ethe boot log speaks English like the rest of the repo.Refs #597
🤖 Generated with Claude Code