Skip to content

feat(nix): package the whisper-stt server - #449

Merged
EtienneLescot merged 1 commit into
claude/nix-pipewire-helperfrom
claude/nix-whisper-stt
Aug 21, 2026
Merged

feat(nix): package the whisper-stt server#449
EtienneLescot merged 1 commit into
claude/nix-pipewire-helperfrom
claude/nix-whisper-stt

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #448. Base is claude/nix-pipewire-helper, because both touch flake.nix and nix/package.nix. Retarget to main once #448 lands.

What this does

Packages electron/native/whisper-stt, the on-device speech-to-text server behind captions. Without it resolveWhisperServer finds nothing, openscreen captions fails, and the AI edition's transcription pump never starts. Last of the three native components #419 listed as deliberately unpackaged.

The model is deliberately not packaged. modelManager.ts downloads a GGML file from HuggingFace into userData on first use, checksums it, and replaces a stale copy. That is a runtime cache the user owns; baking a multi-gigabyte blob into the store would be wrong even if the sandbox allowed the download.

The awkward part, and why the CMakeLists is untouched

CMakeLists.txt pulls whisper.cpp, cpp-httplib and nlohmann/json with FetchContent at configure time — over a network the build does not have.

Patching it would have been the wrong answer. Those pins are deliberate and documented in the file: one of them exists because a build once picked up OpenSSL from the host and shipped a binary that died in the Windows loader before main(). A nix-only fork of that logic would drift from what every other platform builds, which is the failure this whole series has been removing.

CMake already provides the override. The three trees are fetched here and handed over through FETCHCONTENT_SOURCE_DIR_<name>, with FETCHCONTENT_FULLY_DISCONNECTED=ON so a missed one fails loudly rather than reaching for the network.

fetchurl on pinned tags, not fetchFromGitHub — a trade, not a preference. fetchFromGitHub hashes the unpacked tree and is immune to GitHub re-compressing an archive, but its hash cannot be computed or checked without nix, and there is none on the machine this was written on. A tarball hash can be verified by anyone:

curl -sSL https://github.com/ggml-org/whisper.cpp/archive/refs/tags/v1.9.1.tar.gz | sha256sum

If a tag is ever re-compressed, the build fails closed and the fix is one line.

Choices worth reviewing

Vulkan is on, matching what scripts/build-whisper-stt.sh selects for linux-x64/linux-arm64. The alternative is a CPU-only binary — it works, and it is the same class of silent reduction this packaging exists to remove. Unlike the other two derivations, nothing is dlopen'd by soname here (ggml links libvulkan normally), so no RPATH surgery is needed. It does add ggml + Vulkan shader compilation to a job already running ~30 minutes.

OSC_NATIVE_CPU stays off, per the CMakeLists' own warning: it compiles with -march=native for whichever machine ran the build, and a nix package is precisely a thing built once and run elsewhere.

Binary and shared objects install into one directory. Not a nix convention, and deliberate: the CMakeLists sets CMAKE_INSTALL_RPATH to $ORIGIN:$ORIGIN/bin, and scripts/stage-whisper-stt.sh lays them out side by side. Splitting them would mean overriding an RPATH the upstream file chose on purpose, for the sake of a directory name.

Verified

  • All three tarball hashes computed by download; the unpacked top-level directory names (whisper.cpp-1.9.1, cpp-httplib-0.18.1, json-3.11.3) checked against what preConfigure asserts.
  • The model path is runtime-only — confirmed in modelManager.ts, so nothing is needed at build time.
  • Derivation arguments cross-checked against flake.nix; meta.mainProgram set for lib.getExe.
  • Workflow YAML parses; all five run blocks pass bash -n. The wrapper assertion now covers four components.

Not verified, and this one is less certain than #448

Nothing here has been built. Beyond the usual "no nix on this machine", this derivation has more that can only be settled by running it than the PipeWire helper did:

  1. Whether FETCHCONTENT_SOURCE_DIR_* fully satisfies these three declarations, or whether whisper.cpp's own CMake wants git metadata a tarball does not carry.
  2. Whether shaderc alone satisfies vulkan-shaders-gen, or whether it needs more of the Vulkan SDK.
  3. What the Vulkan-enabled ggml build costs in wall clock against the job's 60-minute budget.

I would treat a local nix build .#whisper-stt as worth more than a review pass on this one.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0291d089-3315-47e8-a1ef-cfbf62b1809c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The last native component. Without it resolveWhisperServer finds nothing,
`openscreen captions` fails and the AI edition's transcription pump never starts.

The model is deliberately not packaged. modelManager.ts downloads a GGML file
from HuggingFace into userData on first use, checksums it and replaces a stale
copy; that is a runtime cache the user owns, and baking a multi-gigabyte blob
into the store would be wrong even if the sandbox allowed the download.

The awkward part is that the CMakeLists pulls whisper.cpp, cpp-httplib and
nlohmann/json with FetchContent at configure time, over a network the build does
not have. Patching the CMakeLists was the wrong answer: those pins are deliberate
and documented there -- one of them exists because a build once picked up OpenSSL
from the host and shipped a binary that died in the Windows loader -- and a
nix-only fork of them would drift from what every other platform builds. CMake
already provides the override for this, so the three trees are fetched here and
handed over through FETCHCONTENT_SOURCE_DIR_<name>, with
FETCHCONTENT_FULLY_DISCONNECTED so a missed one fails loudly rather than reaching
for the network.

fetchurl on pinned tags rather than fetchFromGitHub, which is a trade and not a
preference. fetchFromGitHub hashes the unpacked tree and is immune to GitHub
re-compressing an archive, but its hash cannot be computed or checked without
nix, and there is no nix on the machine this was written on. A tarball hash can
be verified by anyone with curl and sha256sum. If a tag is ever re-compressed the
build fails closed and the fix is one line.

Vulkan is on, matching what scripts/build-whisper-stt.sh selects for Linux. The
alternative is a CPU-only binary, which works and is the same class of silent
reduction this packaging exists to remove. Nothing is dlopen'd by soname here --
ggml links libvulkan normally -- so unlike the other two derivations no RPATH
surgery is needed. OSC_NATIVE_CPU stays off per the CMakeLists' own warning: it
compiles for whichever machine ran the build, and a nix package is precisely a
thing built once and run elsewhere.

Co-Authored-By: Claude <noreply@anthropic.com>
@EtienneLescot
EtienneLescot force-pushed the claude/nix-whisper-stt branch from f375375 to 6f7aa7f Compare August 21, 2026 21:21
@EtienneLescot
EtienneLescot merged commit b765aef into claude/nix-pipewire-helper Aug 21, 2026
3 checks passed
@EtienneLescot
EtienneLescot deleted the claude/nix-whisper-stt branch August 21, 2026 21:31
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.

1 participant