Skip to content

Drop the ALSA bridge nothing crosses, and the ffmpeg 8 behind it: 34 MiB - #10

Merged
PhysShell merged 5 commits into
mainfrom
claude/spotibox-closure-optimize-koke8p
Sep 30, 2026
Merged

PhysShell merged 5 commits into
mainfrom
claude/spotibox-closure-optimize-koke8p

Conversation

@PhysShell

Copy link
Copy Markdown
Owner

Summary

The PulseAudio module writes /etc/alsa/conf.d/99-pulseaudio.conf, which makes pcm.default and ctl.default alsa-plugins' pulse types. In the production image that file was the only reference to alsa-plugins. alsa-plugins, in turn, was the only reference to ffmpeg 8: its a52 encoder and lavrate resampler link libavcodec, and nothing here configures either. The whole chain is 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.

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.

Leaves the closure Bytes
ffmpeg-8.0-lib 33,821,496
openapv-0.2.0.4 726,200
alsa-plugins-1.2.12 390,456
libva-2.22.0 353,792
ffmpeg-8.0-data 279,344
libvdpau-1.5 103,728
the file itself 528
closure, each side measured in a fresh sandboxed store 1,222,866,800 → 1,187,190,640 (−35,676,160), 579 → 572 paths

Reachability predicted 35,675,544 bytes. The measured saving is 616 bytes more, which is the smaller /etc metadata.

Why nothing needs the bridge

  • No ALSA device. The production kernel is built with SOUND = no, so nothing can reach an ALSA device, bridged or not.
  • xrdp is PulseAudio end to end. Its audio path is module-xrdp-sink → chansrv's socket → RDPSND.
  • Spotify tries PulseAudio first. It is the one program here with an ALSA driver. In 1.2.74 it constructs its PulseAudio driver first: dlopen("libpulse.so.0") (its wrapper puts the library on the path), then a threaded main loop, then pa_context_connect with 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

  • 7092563 tools/cold-boot.sh works again. read -r ovmf qemu read both store paths from one line that tr had joined without a trailing newline. read fails at such an end of input, and under set -e the script stopped right after its builds: on main it never reached QEMU. It now reads one path per line.

  • a5a38b4 tests/xrdp-audio.nix, new flake check spotibox-xrdp-audio. This is the first test that is an actual RDP client:

    • FreeRDP with its fake sound backend connects to the appliance's own xrdp.
    • The session's PulseAudio plays noise into xrdp-sink, and the client must log Wave PDUs in WAVE_FORMAT_PCM.
    • The client then disconnects and reconnects, and must receive PCM again.

    What it took to make the test reliable:

    • xrdp 0.10 keeps chansrv's sockets under /run/xrdp/<uid>; the upstream NixOS test still looks in /tmp/.xrdp.
    • The test driver runs every command under set -euo pipefail, so nothing pipes into a reader that exits early.
    • pgrep -f matches the driver's own bash -c wrapper, so the client is matched by name.
    • The sink becomes the default about 10 s after the channel is up, so the test waits for that instead of racing it.
  • 345c420 The removal.

    • profiles/modes/prod.nix: environment.etc."alsa/conf.d/99-pulseaudio.conf".enable = lib.mkForce false, and alsa-plugins added to forbiddenDependenciesRegexes.
    • README: a new section, The ALSA Bridge Nothing Crosses.
    • README: a trim-table row. It also gets the sudo row that 36a369e never added, so the total is today's measurement: 1.11 GiB, 572 paths.
    • README: the TODO item is closed.
  • 3d6793c Both budgets re-recorded from 345c420. They come from tools/closure.sh baseline and tools/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.

  • 638d716 README records the Hyper-V acceptance. Documentation only; the image store path is unchanged.

Field Before After
closureBytes 1,222,866,800 1,187,190,640
closurePaths 579 572
kernelBytes, modulesBytes, initrdBytes unchanged
vhdxApparentBytes 1,719,664,640 1,686,110,208 (two 16 MiB blocks fewer)
vhdxBuilderAllocatedBytes 1,410,596,864 1,374,699,520
releaseBytes 520,302,296 505,700,521
homeReleaseBytes 420,843 420,606 (qemu-img's random header GUIDs, per the census in #9)

Validation

  • Closure: 1,187,190,640 from the experiment's fresh store, and again from closure.sh baseline on 345c420, to the byte.
  • VM tests: spotibox-basic, spotibox-xrdp-session (Spotify's window maximised at 1280 wide) and spotibox-xrdp-audio all pass on this branch. spotibox-xrdp-audio also passes on a5a38b4, where prod still has the bridge.
  • Cold boot: tools/cold-boot.sh boots 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.
  • CI-equivalent checks: nix flake check --no-build passes, qubix-manifest-json matches manifest.json, and tools/closure.sh check passes against the new budget. shellcheck is clean on tools/cold-boot.sh.
  • Hyper-V acceptance: the image built at 3d6793c was installed with qubixctl -Command recreate -ImageSource wsl, keeping the home disk. It reports as wsl:spotibox-baseline-2026-09-28-12-g3d6793c, and its store path is 7z7pn736…, the same derivation the cold boot booted. Spotify plays through mstsc, and plays again after several disconnect and reconnect cycles.

Not in this PR

  • ffmpeg 4.4 (26.7 MiB) stays. It is Spotify's own: nixpkgs links it next to the client because Spotify wants a libavcodec older than 59.
  • iso-codes, the next candidate in the README's TODO.

🤖 Generated with Claude Code

https://claude.ai/code/session_012DuejctChFnXVcRB1VWncC


Generated by Claude Code

`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
@PhysShell
PhysShell merged commit 1cca477 into main Sep 30, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants