Drop the ALSA bridge nothing crosses, and the ffmpeg 8 behind it: 34 MiB - #10
Merged
Merged
Conversation
`read -r ovmf qemu` took both store paths from one line that `tr` had joined without a trailing newline. read reports failure at an end of input it did not reach through a newline, and under `set -e` that ended the script right after the builds, before QEMU ever started: the cold boot this script exists for has not been reaching the firmware. One read per line of `nix build --print-out-paths` output instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012DuejctChFnXVcRB1VWncC
tests/xrdp-session.nix proves a session comes up, through xrdp-sesrun, which never opens an audio channel. Nothing so far exercised the thing the appliance is for: sound leaving the VM over RDP. This test is an RDP client - FreeRDP with its fake sound backend, logging every block it receives - that connects to the appliance's own xrdp, has the session's PulseAudio play noise into its default sink, and requires Wave PDUs in PCM at the client. Then it disconnects, reconnects, and requires them again, since the session outlives the client and the sink has to find the new chansrv socket. xrdp 0.10 keeps those sockets under /run/xrdp/<uid>, not /tmp/.xrdp as the upstream NixOS test still assumes, and chansrv stops listening while a sink is connected; the client's own log is the readiness signal. The driver runs every command under `set -euo pipefail`, so nothing here pipes into a reader that exits early, and the client is matched by name, because `pgrep -f` finds the driver's own `bash -c` wrapper. Like the other VM tests it is evaluated by CI and run by hand; it passes on this commit's production image. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012DuejctChFnXVcRB1VWncC
The PulseAudio module writes /etc/alsa/conf.d/99-pulseaudio.conf, which
makes pcm.default and ctl.default alsa-plugins' `pulse` types. That file
was the production image's only reference to alsa-plugins, and
alsa-plugins its only reference to ffmpeg 8, through the a52 encoder and
the lavrate resampler that nothing here configures:
toplevel -> etc -> etc-alsa-conf.d-99-pulseaudio.conf
-> alsa-plugins-1.2.12 -> ffmpeg-8.0-lib
-> ffmpeg-8.0-data, libva, libvdpau, openapv
closure, each side measured in a fresh sandboxed store:
1,222,866,800 -> 1,187,190,640 bytes (-35,676,160), 579 -> 572 paths
No ffmpeg-specific change: ffmpeg 8 leaves because the one thing holding
it does. The ffmpeg 4 that stays is Spotify's own.
Nothing crosses the bridge. The production kernel has no sound support,
so there is no ALSA device to reach; xrdp's sink is a PulseAudio module;
and Spotify 1.2.74, the one program with an ALSA driver, constructs its
PulseAudio driver first - dlopen("libpulse.so.0"), which its wrapper puts
on the library path, then a threaded main loop and pa_context_connect -
and builds the ALSA one only when that fails, which is exactly when a
bridge into PulseAudio cannot help.
Checked: tests/xrdp-audio.nix passes with the bridge and without it - PCM
at the client, and again after a disconnect and reconnect - and the image
without it cold-boots under OVMF to an RDP answer. What a VM test cannot
do is log in to Spotify; a track playing on Hyper-V through a reconnect is
the acceptance that remains.
forbiddenDependenciesRegexes gains alsa-plugins. The debug image keeps
the file, with its stock ALSA kernel and alsa-utils. The README's trim
table gains this row and the sudo row 36a369e never added, so its total
is today's measurement: 1.11 GiB, 572 paths.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012DuejctChFnXVcRB1VWncC
The ALSA bridge is out, so the ceilings come down with it rather than leaving 34 MiB of room for something else to grow into. Both files come from 345c420 on a clean tree - tools/closure.sh baseline, then tools/telemetry.sh --record - each in an empty store with the sandbox probe passing first. closure 1,222,866,800 -> 1,187,190,640 bytes, 579 -> 572 paths VHDX apparent 1,719,664,640 -> 1,686,110,208 VHDX allocated 1,410,596,864 -> 1,374,699,520 release asset 520,302,296 -> 505,700,521 The closure matches the experiment's fresh-store measurement to the byte. Kernel, modules and initrd are unchanged. homeReleaseBytes moves by 237 bytes because the home VHDX's headers carry GUIDs qemu-img draws at random, which the reproducibility census already found. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012DuejctChFnXVcRB1VWncC
The README asked for one thing no VM test here can give: Spotify, logged in, playing through RDP on real Hyper-V and again after a reconnect. It did, on the image built at 3d6793c - store path 7z7pn736..., the derivation tools/cold-boot.sh booted - with sound after each of several reconnects. Documentation only; the image is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012DuejctChFnXVcRB1VWncC
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.
Summary
The PulseAudio module writes
/etc/alsa/conf.d/99-pulseaudio.conf, which makespcm.defaultandctl.defaultalsa-plugins'pulsetypes. In the production image that file was the only reference to alsa-plugins. alsa-plugins, in turn, was the only reference to ffmpeg 8: itsa52encoder andlavrateresampler link libavcodec, and nothing here configures either. The whole chain istoplevel → etc → etc-alsa-conf.d-99-pulseaudio.conf → alsa-plugins-1.2.12 → ffmpeg-8.0-lib → ffmpeg-8.0-data, libva, libvdpau, openapv.This PR turns the file off in prod and guards against its return. ffmpeg 8 leaves because the one thing holding it does. There is no ffmpeg-specific change, so the two were never mixed in one experiment.
ffmpeg-8.0-libopenapv-0.2.0.4alsa-plugins-1.2.12libva-2.22.0ffmpeg-8.0-datalibvdpau-1.5Reachability predicted 35,675,544 bytes. The measured saving is 616 bytes more, which is the smaller
/etcmetadata.Why nothing needs the bridge
SOUND = no, so nothing can reach an ALSA device, bridged or not.module-xrdp-sink→ chansrv's socket → RDPSND.dlopen("libpulse.so.0")(its wrapper puts the library on the path), then a threaded main loop, thenpa_context_connectwith the result checked. It builds the ALSA driver only when that fails, and if ALSA fails too it logs "Unable to initialize sounddriver, using dummy." So the only road to ALSA is a PulseAudio that cannot be reached, and a bridge into PulseAudio cannot fix that. This was read off the binary's disassembly, and the Hyper-V acceptance below confirms it.The debug image keeps the file, along with its stock ALSA kernel and alsa-utils.
Commits
7092563tools/cold-boot.shworks again.read -r ovmf qemuread both store paths from one line thattrhad joined without a trailing newline.readfails at such an end of input, and underset -ethe script stopped right after its builds: on main it never reached QEMU. It now reads one path per line.a5a38b4tests/xrdp-audio.nix, new flake checkspotibox-xrdp-audio. This is the first test that is an actual RDP client:xrdp-sink, and the client must log Wave PDUs inWAVE_FORMAT_PCM.What it took to make the test reliable:
/run/xrdp/<uid>; the upstream NixOS test still looks in/tmp/.xrdp.set -euo pipefail, so nothing pipes into a reader that exits early.pgrep -fmatches the driver's ownbash -cwrapper, so the client is matched by name.345c420The removal.profiles/modes/prod.nix:environment.etc."alsa/conf.d/99-pulseaudio.conf".enable = lib.mkForce false, andalsa-pluginsadded toforbiddenDependenciesRegexes.3d6793cBoth budgets re-recorded from 345c420. They come fromtools/closure.sh baselineandtools/telemetry.sh --record, each run in an empty store with the sandbox probe passing first. The closure ceiling goes from 1,247,324,136 to 1,210,934,452.638d716README records the Hyper-V acceptance. Documentation only; the image store path is unchanged.closureBytesclosurePathskernelBytes,modulesBytes,initrdBytesvhdxApparentBytesvhdxBuilderAllocatedBytesreleaseByteshomeReleaseBytesValidation
closure.sh baselineon 345c420, to the byte.spotibox-basic,spotibox-xrdp-session(Spotify's window maximised at 1280 wide) andspotibox-xrdp-audioall pass on this branch.spotibox-xrdp-audioalso passes on a5a38b4, where prod still has the bridge.tools/cold-boot.shboots the VHDX under OVMF, through systemd-boot and an initrd waiting for the home seed. xrdp answers an X.224 connection request after about 80 s, without KVM.nix flake check --no-buildpasses,qubix-manifest-jsonmatchesmanifest.json, andtools/closure.sh checkpasses against the new budget.shellcheckis clean ontools/cold-boot.sh.qubixctl -Command recreate -ImageSource wsl, keeping the home disk. It reports aswsl:spotibox-baseline-2026-09-28-12-g3d6793c, and its store path is7z7pn736…, the same derivation the cold boot booted. Spotify plays through mstsc, and plays again after several disconnect and reconnect cycles.Not in this PR
🤖 Generated with Claude Code
https://claude.ai/code/session_012DuejctChFnXVcRB1VWncC
Generated by Claude Code