The everyone-must-update release. Four coordinated changes, each from the security
discussion Ed approved 2026-07-27, plus the remembered-apps polish:
1. SIGNED RELEASES. build-release.ps1 now signs the release zip (ECDSA P-256 /
SHA-256, --sign-update verb) with a private key that lives ONLY at Ed's chosen
location outside the repo; the matching public key is embedded (UpdateSignature)
and the updater REFUSES any release whose .sig asset is missing or does not
verify - a compromised GitHub account can no longer ship code to users. The
signing verb self-checks against the embedded key so a key/embed mismatch fails
the pipeline, and the gate proves the on-disk key matches the embed when present.
2. STRONGER PASSWORDS, ENFORCED (BREAKING). PBKDF2 raised 100k -> 600k (both peers
must derive the same key, so 5.6 cannot stream with pre-5.6 AT ALL - release
notes lead with it). New PasswordStrength rule (>= 8 chars, not an infamous
password) enforced at EVERY door: both password dialogs block weak NEW entries
with concrete plain-English advice; the streaming gate walks an existing weak
password through strengthening; and ForPlainPassword - the single derivation
choke-point shared with the service - refuses weak outright, so no path streams
on a guessable password. Headless service logs the why. Per Ed: painful once,
and this coordinated-update release is the cheapest moment it will ever have.
3. RELAY ADDRESS-PROOF (watch-only). The relay sends every new client address a
random cookie and marks it verified when echoed - a forged source address can
never echo, killing the reflection attack. 5.6 clients echo automatically
(AddrCheck type 10, verbatim, self-limiting); the relay ships watch-only
(logs would-blocks) until the fleet updates, then one flag (--require-addr-check)
enforces. Per-IP entry cap (4) enforced immediately. Relay changes are committed
but NOT deployed to the Pi - they ride the v5.6 release moment.
4. Remembered-apps empty state teaches its lifecycle + manual sentence; About/
release notes written; version bumped to 5.6.
New gate steps: signing round-trip/tamper/wrong-key/embed-match; password rules incl.
the exact "Games" case; AddrCheck verbatim echo. Gate 69/69.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New Options -> Install / Uninstall RemSound on this PC: a per-user self-installer
(%LOCALAPPDATA%\Programs\RemSound, no admin) with optional desktop + Start-menu
shortcuts, login auto-start (reuses StartupAutoStart), Windows Installed-apps
registration, and copy-across of profiles+config, recordings and logs. Install
state is decided by a marker file, not a folder-path guess; the post-install
relaunch hands over foreground via AllowSetForegroundWindow so the installed copy
comes to the front; uninstall uses a batch remover (no PowerShell) and confirms
with two independent tick-boxes. All new dialogs use the house accessible controls
(AccessibleCheckBox, Theme.Heading).
Also: iOS (TestFlight) companion link alongside Android in README + manual;
slimmed-down default cue WAVs; About/RELEASE_NOTES/manual updated; version -> 5.1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Multi-track recording drift fix (Ed's question — can the separate tracks drift over an hour?):
* Root: FlushPeerTracks skipped a peer that produced no samples in a render block, so a peer that
went quiet long enough for its session to be pruned (>4 s idle) would have its track fall behind
and desync. Now every peer track is padded to a full render block each cycle (silence when the
peer produced nothing), so all peer tracks stay sample-locked to the single render clock — they
can't drift apart however long the recording runs, and all end the same length. Same padding for
the single-file bypass path. OnRecordBlockComplete now carries the block's float count.
(The peer tracks are already resampled to the render clock per peer, so this makes peer-to-peer
sync exact; your own "me" track is capture-clocked — same soundcard for capture+playback = same
clock = no drift, different interfaces can drift slightly.)
* Self-test: two new steps — "Per-peer shaping DSP" (PeerDspChain unity/master-off/volume/parametric
+ ParametricToPeaking) and "v5 settings and shaping round-trip" (AppConfig defaults, NamedPeers,
MainTabOrder, parametric PeerShaping, recording default = Both).
* Logging (gated by the logging checkbox): master shaping switch, EQ-mode change, parametric band
add/delete, peer rename/clear/delete, and the applied Appearance settings after Preferences close.
* CLI: --list-profiles and --list-named-peers (read-only), in --help.
* Version bumped to 5.0; About-box changelog, RELEASE_NOTES.md and README updated for v5.
Build clean; --selftest passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Audio cues
- Cues for send/receive on-off, minimise/restore, checkbox tick/untick, and tab switch
- Soft keyboard clicks while typing, with a distinct passkey sound on password fields
- Per-cue "Choose sound" variant picker; "(none)" silences a cue; front-most missing-sound warning
- Send/receive cues take priority over the generic checkbox sound; programmatic ticks stay silent
Preferences
- Redesigned into four tabs (General, Audio cues, Startup behaviour, Update settings)
- Startup behaviour moved in from the Options menu
- NVDA now announces the dialog on open (focus a real named control, not the quiet tab control)
Auto-tune
- Cause-aware: tells device render-callback stalls (more buffer can't fix) apart from genuine
network/buffer starvation, so it no longer pins latency high on chunky onboard cards
- Lowering the target eases the buffer down (glide) instead of trimming it, so no clicks while tuning
Sounds layout
- Shipped defaults moved out of the per-user folder into an install-side "default sounds" folder,
so updates can refresh them; user customs are Browse-picked file paths and are left untouched
- Startup migration removes both legacy sound folders; verified from oldest (v1.0-v3.3) and v3.4 layouts
Quiet automated launches
- New --silent launch flag mutes all cue sounds and suppresses the startup dialogs (migration notice,
update check, Realtek/mic/missing-sound warnings) so test launches never disturb the user
- run-tests / build-release / SelfTest repointed to the new "default sounds" layout
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Andre's three "bigger ideas" from RemSound-smoke-test-agent-brief.md:
- Richer diagnostics: --diagnostics now includes a live localhost audio self-check
(PCM + Opus, with packet/underrun/drop/buffer/latency counters), the most recent
session snapshot parsed from the log (codec, send/receive state, buffer, drops,
heartbeat), and a recent-warnings/errors digest from the log. BuildDiagnosticsReport
gained a runLiveAudioProbe flag so the self-test's privacy check stays fast.
- Headless accessibility audit: new --selftest step constructs the dialogs that can be
built without hardware (Startup behaviour, Recording settings, Preferences) and checks
every actionable control announces a name and that Alt-key mnemonics are unique within
a container. MainForm is out of scope (its constructor opens audio/hotkeys/sockets).
Dialogs that won't construct are skipped, not failed. Currently audits 3, no violations.
- Perf/leak sanity: new --perftest command runs several audio-loopback cycles and reports
whether handle/memory/thread counts stay bounded (handles ratcheting up cycle-on-cycle is
the leak fingerprint, given RemSound's handle-leak history). Lenient thresholds; logs the
numbers for build-to-build comparison. Wired into run-tests.ps1.
- Shared AudioLoopback helper (used by the self-test, diagnostics and perf test) so all
three exercise the identical real capture/encode/network/decode path on test port 47929.
- csproj: the four previously-unconditional cue Content items are now Exists-guarded like
the rest, so a mid-edit sounds\ folder doesn't break the dev build; the gate still
enforces the required cues before release.
Help + manual updated (--perftest, --smoke-test, --config-dir, richer --selftest).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adopted from Andre's RemSound-smoke-test-agent-brief.md - the gaps our pack didn't
already cover:
- --config-dir <folder>: redirect ALL user state (config, profiles, logs, cue
sounds) to an explicit folder for this process only, applied at the very start of
Program.Main before the layout migration runs. Lets a test exercise a real build
without touching the user's live settings (the brief's safety rule 1). Works with
every command. AppConfig gains SetUserDataDirectoryOverride / an override on
UserDataDirectory; CommandLine.TryGetConfigDir parses it early.
- --smoke-test / --smoketest: alias for --selftest, matching the brief's vocabulary.
- run-tests.ps1: a cold-start + clean-close smoke (brief baseline steps 3-4) -
launches the GUI minimized against an isolated --config-dir, confirms it stays up,
that it used the isolated folder (real settings untouched), and that --close shuts
it down with no orphan process. SKIPs cleanly if a RemSound instance is already
running (machine-wide single-instance lock).
Manual + --help updated for both switches.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The test suite, modelled on Andre's Sensor Readout (an in-app self-test + a build
script), runnable as one step before every publish.
Part 1 - in-app multi-step self-test (SelfTest.cs), run by --selftest:
audio round-trip (PCM + Opus over localhost, dedicated test port so it never
clashes with a running instance), encryption right/wrong-password + fingerprint,
packet framing + malformed rejection, client<->server wire-format compatibility,
settings save/reload, profile save/reload (temp folder), diagnostics-report
privacy (never leaks a password), and bundled-resources present. Each step is
timed and reported PASS/FAIL/SKIP; exit 0 only if nothing failed. Replaces the
old single-shot --selftest. RunDiagnostics refactored to expose
BuildDiagnosticsReport(AppConfig) for the privacy step.
Part 2 - run-tests.ps1: builds, then checks the package (sounds, readme, native
opus, framework-dependent, dll version == csproj), the About-box changelog, the
client/server wire contract (relay magic/version/port still match RemPacket),
the CLI surface, and runs --selftest. build-release.ps1 now runs this gate first
and aborts the release if it fails.
Bug caught + fixed: the published release zip carried ZERO cue sounds (startup
sound + connect/disconnect/etc.) - MSBuild's incremental Content-copy marker
skipped sounds\ on a fresh publish. Added an AfterTargets=Publish copy in the
csproj that lands every cue WAV in the published sounds\ folder regardless of
the marker. Verified: a staging publish now contains all 9 cue WAVs.
Manual/help: --selftest description updated (readme.html + MANUAL.md).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Receiver: a plain (Mixed) stream now renders on an active lane when the
receiver is in two-lane (ASIO) mode, instead of being decoded into a ring
nothing reads. Fixes one-way silence ("my mic works for me but not for them").
- Receiver: drift resampler gains a buffer-depth correction term so a bloated
standing buffer eases back to the latency target over a long session.
- App: don't send audio until a peer is genuinely reachable (issue #8); status
no longer shows phantom send traffic with nobody connected.
- App: start-up cue sound (machine-wide toggle + custom path in Preferences).
- App: command-line options (CommandLine.cs) -- --devices, --selftest,
--diagnostics, --log, --close, --profile, --connect, --minimized, --version,
--help. New "Command-line options" manual section (readme.html + MANUAL.md).
- Version 3.9; About-dialog and RELEASE_NOTES updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>