Repository navigation
Conversation
Upstream landed the Sendspin 1.0.0-rc1 protocol along with player-role fixes we want: the receive path moved onto a library-owned protocol task, audio is now gated on the adopted client/state snapshot, and connections drop cleanly on a failed send. Patch 0003 was regenerated for context drift in ws_server.cpp's include block. This bump is NOT mergeable as-is: upstream made client_id a derived value rather than a configured one, which breaks how the Elixir side supplies and advertises player identity. See the PR body. Co-Authored-By: Claude <noreply@anthropic.com>
|
CI on Not this PR's: Hex auditUncontrolled recursion in This PR changes three C++ files — The last CI run on I have not spent the one re-run on it: No upstream fix PR exists to port, so per convention I'm not widening a C++ pin bump to carry an unrelated dep change. Proposed patch, for a separate PR:
Worth confirming 0.17.1 actually carries the fix before relying on it — I could not retrieve the advisory's This PR's: Compile · Format, Test, Dialyzer, ArgusAll four stop on the single error already described in the PR body: ( This is the known blocker: upstream v0.9.3 made Credo and Sobelow passed. Generated by Claude Code |
Upstream watch, 2026-10-19 — still blocked, and two things have changedThe scheduled upstream watch ran again today. Rather than open a second draft blocked on the same question, here is what moved since this PR was opened. The 1. The commits this PR deferred are now taggedThe Deliberately not included section stopped at v0.9.3 because the player-relevant follow-ups were untagged. All five of them now ship in v0.9.4:
v0.9.5 (
2. New finding: the source role is silently being compiled inNot covered by this PR, and worth folding in. Upstream added a source (audio capture) role in the v0.9.x line, and its host build option defaults to option(SENDSPIN_ENABLE_SOURCE "Enable source role (audio capture)" ON)Verified identical at both v0.9.3 (this PR's pin) and v0.9.5. Our Upstream also made the Opus decoder optional in v0.9.0 ( set(SENDSPIN_ENABLE_SOURCE OFF CACHE BOOL "" FORCE)
set(SENDSPIN_ENABLE_OPUS ON CACHE BOOL "" FORCE)I built with those two lines at v0.9.5 and confirmed no source-role objects are produced. 3. Build status at v0.9.5 is identical to this PR'sReproduced today (cmake 3.28.3, g++ 13.3.0, The library and all vendored deps compile clean. The only compile errors are the five in our What is actually neededNothing here is a new decision. It is the same one this PR raised: where the X25519 keypair lives so Where today's work livesI have not pushed to this PR's branch and have not opened a competing PR. Today's findings are on a standalone branch, It carries the v0.9.5 pin, the regenerated patch, the two role lines above, and the same four renames as this PR. With those applied, Either cherry-pick it onto this branch or discard it once this PR is updated; it is not meant to compete with this one. Generated by Claude Code |
Pin
9331ace6428979982c934384702d289958ca125e(v0.8.0)91038b72892596b9ec0cccfec1b9e6ebc58ba148(v0.9.3)61 upstream commits. v0.9.3 is the newest tag;
mainis 9 commits past it (see Deliberately not included below).Upstream removed
SendspinClientConfig::client_id. It is now derived, not configured: the library computes it asbase64url(X25519 public key)from a keypair generated on first boot and persisted through aSendspinPersistenceProvider.Our player wires no persistence provider, and
client.cpp:1560only loads the stored keypair when one is present. So on this pin, as written, the player would mint a fresh keypair — and therefore a fresh 43-charclient_id— on every respawn.That collides with how identity currently flows on our side:
Audio.Storegenerates aclient_idper output and persists it,player.expasses it in as--client-id, and advertises it in the mDNS TXT record asclient_id=…(player.ex:542,:692).input/server.ex:893notes an instance without a matchingclient_idis silently ignored. After this bump the advertised id and the id the player actually presents inclient/hellowould no longer agree.Resolving this is cross-boundary work and a product call, not a mechanical fix, so I stopped rather than guess. Roughly, it needs:
main.cpp(file-backed, per output) so the keypair is stable across respawns.client.client_id()afterstart()andplayer.exadvertises that — instead of being supplied to it.--client-idis left in place and still parsed; only the now-removed config assignment atmain.cpp:712is outstanding.Upstream changes that matter to us
The headline is #140, the Sendspin 1.0.0-rc1 protocol implementation — a wire-format change on an Experimental protocol, which is reason enough to move.
Around it, the connection and receive path was substantially reworked: the receive path now runs on a library-owned protocol task through a shared inbound ring (#148), the connection lifecycle moved onto that task (#149), and the network provider is now called on the main loop only (#189) — all of which touch how our
client.loop()driving inmain.cppbehaves. Connections now drop cleanly on any send that fails after the Noise encrypt (#163) and disconnects route throughdrop_connection()(#162), which should make our reconnect path less prone to wedging.On the player role specifically, audio is now gated on the adopted
client/statesnapshot (#160), andPlayerRole::get_fixed_delay_us()was removed (#158) — we only ever setfixed_delay_usin config, so that one costs us nothing. Thestatic_delayAPI was renamed wholesale tooutput_delay, which is most of the diff inmain.cpphere.Also relevant: IXWebSocket moved to v12.0.1 (#174), and
cmake/host.cmakewas reworked to let a parent project provide thenoise_ctarget (#185).Patch 0003 re-validation
Regenerated. Hunk 1 failed against the new source — upstream added
<ixwebsocket/*>includes plus<cstddef>/<utility>to the include block, so the anchor context drifted. Hunk 2 (the load-bearing hook afterserver_->start()) still applied at an offset.Regenerated against v0.9.3 and verified with
patch -p1 --dry-run(exit 0). Both markers CMake asserts are preserved verbatim:sendspin_host_listener_bound(uint16_t port) __attribute__((weak))sendspin_host_listener_bound(port)Upstream has not absorbed the hook natively, so we still carry it.
Host build
Configure passed (cmake 3.28.3, g++ 13.3.0) — which independently confirms the patch applied cleanly, since
assert_patch_markersis fatal at configure time.libsendspin.aand all vendored deps (microFLAC, micro_opus, noise_c, ixwebsocket) compiled clean. The build then fails only in our ownsrc/main.cpp.Four of the five breakages were the mechanical
static_delay→output_delayrename and are fixed in this PR:on_static_delay_changed→on_output_delay_changedinitial_static_delay_ms→initial_output_delay_msset_static_delay_adjustable→set_output_delay_adjustableget_static_delay_ms→get_output_delay_msOur own CLI flag,
optsfield, and thestatic_delaystdout event name are unchanged — those are the contract with the Elixir side, not library API.The fifth is the
client_idblocker above. It is the only remaining compile error.Deliberately not included
maincarries 9 commits past v0.9.3, several of which are player-relevant: a hard sync measured from now after the sink starves (#198), Opus decoded at 16 bits regardless ofstream/start'sbit_depth(#197), player quota released atstream/clearandstream/end(#195), soft-sync frame drops blended into the neighbor (#194), andclient/hellosent only afterserver/helloarrives (#193).Pinning policy prefers a tag, so this stops at v0.9.3. Worth a follow-up bump once those tag.
Before release
Firmware needs a rebuild (
mise run firmware -- <target>) and rpi3 hardware validation — the protocol change and the sync/connection rework are exactly the kind of thing host builds don't exercise. Nopriv/binaries or build output are included in this PR.🤖 Generated with Claude Code
https://claude.ai/code/session_014r1JZWiyic2CXqS9R3SeWC
Generated by Claude Code