Nix devshells and packages
The flake lives in ./nix/; flake.nix at the root is a thin entry point that uses flake-parts + import-tree to auto-discover every .nix file under nix/. Each file is a self-contained flake-parts module — adding a devshell, package, or check means dropping a new .nix file in the right subdirectory, no central wiring file to keep in sync (the dendritic pattern).
nix/
├── systems.nix # systems list
├── lib.nix # shared helpers via _module.args.alephLib
├── devshells/{default,sidecar,go,desktop,android,esp32}.nix
├── packages/{aleph,alabama-satellite,aleph-sidecar-src,default}.nix
└── checks/smoke.nixEach module looks like:
_: {
perSystem = { pkgs, alephLib, config, ... }: {
devShells.foo = pkgs.mkShell { ... };
# or: packages.foo = ...;
# or: checks.foo = ...;
};
}Cross-module references go through config.packages.X — e.g. aleph reads config.packages.aleph-sidecar-src directly (the bare server binary it wraps is built inline as a private let binding, not exported).
Devshells
One shell per stack so each stays small and free of cross-stack LD_LIBRARY_PATH collisions:
| Shell | Use for |
|---|---|
nix develop (default) | proto/lint/git only — buf, mise, protoc |
nix develop .#sidecar | packages/aleph/sidecar (Python + ROCm/Vulkan + cmake) |
nix develop .#go | packages/aleph/server, alabama, alabama-satellite |
nix develop .#desktop | packages/satellites/desktop (Wails + GTK/WebKit) |
nix develop .#android | packages/android-satellite (Android SDK/NDK) |
nix develop .#esp32 | packages/satellites/esp32-s3-audio-board (ESP-IDF) |
With direnv installed, each package directory has an .envrc that enters the right shell automatically on cd. For one-shot commands from outside, use nix develop .#<shell> --command <cmd>. Inside any shell, mise provides python/bun/go/uv.
Building and running
nix run github:aleph-garden/aleph#aleph # one-shot install + run (linux)
nix build .#aleph # full linux runtime (server + bundled sidecar source)
nix build .#aleph-windows # full windows/amd64 payload (aleph.exe + sidecar source)
nix flake check # smoke-test every packageOnly full, self-contained outputs are exposed — the bare server binary doesn't run standalone (it needs the sidecar source), so it's never a package on its own.
aleph wraps the server binary with uv, Python, ROCm/Vulkan runtime libs, and the staged sidecar source on PATH/LD_LIBRARY_PATH, and points ALEPH_SIDECAR_DIR at a per-user writable copy under $XDG_STATE_HOME/aleph/sidecar. Envprep then provisions the venv there on first launch.
First-time builds: the Go packages use a placeholder vendorHash. Run nix build .#aleph once, copy the got: sha256-… value Nix prints into nix/packages/aleph.nix, and commit (aleph-windows shares the same hash). Same bootstrap for alabama-satellite.
Keeping the pins fresh is not a manual chore. Every manually-pinned FOD — the three vendorHashes, the two bunDepsHash outputHashes (webui + desktop, keyed on bun.lock), and the mlspp/libdave source sha256s — is exposed as a narrow buildable attr (.#<pkg>.goModules / .bunDeps, .#mlspp.src) so scripts/nix-hash-bump.sh can recompute each one in isolation: it forces the hash to a sentinel, builds just that FOD (never the downstream compile), and reads Nix's got: back into the file. The pre-commit hook (.githooks/pre-commit) blocks a commit that leaves a pin stale on a host with nix; a host without nix (Windows) can only warn, so CI's nix-hash-bump job runs the same script on every PR whose go.sum, bun.lock, or a source pin moved and pushes the bump straight to the branch. A stale pin never reaches main.
Linux dev specifics
GPU on the reference host is RDNA3 (RX 7900 XTX). Sidecar venv is provisioned with uv sync --extra full --extra rocm-amd.
LLM offload uses the xllamacpp wheel (the official llama.cpp engine, run in-process). The server provisions it automatically: uv sync installs the PyPI CPU wheel, then envprep (installXllamacpp) force-reinstalls the host-specific accelerator wheel from xllamacpp's alternate index — on this RDNA3 host, the Vulkan wheel (the rocm-6.4.1 wheel installs but SIGSEGVs at model load on gfx110x; Vulkan loads with full GPU offload). No source build, no DLL swap. To force a variant by hand:
cd packages/aleph/sidecar
uv pip install --python .venv/bin/python --force-reinstall --no-deps \
xllamacpp --index-url https://xorbitsai.github.io/xllamacpp/whl/vulkanThe opt-in whisper.cpp STT engine follows the same pattern: PyPI pywhispercpp is CPU-only, so envprep (binprep/whispercpp) fetches the backend-accelerated wheel — Vulkan on this RDNA3 host — from the pinned solace-assistant/whispercpp-builds release and uv sync --inexact keeps it. It is unrelated to the default Parakeet engine (onnx-asr), which needs no GPU wheel beyond the onnxruntime swap. A host with no matching wheel just can't select whisper.cpp.
Order matters for the heavy ML imports: transformers' lazy submodule loader is not thread-safe, and omnivoice registers Wav2Vec2-derived classes via a metaclass that stalls on the import lock if it races the transformers paths. The sidecar pre-warms these imports serially before any model-thread load (registry/prewarm.py); outside the sidecar, do the same.
Cache locations
Envprep writes versioned artefacts under data/cache/ relative to the repo root (or $ALEPH_SIDECAR_DIR in installed runs):
| Path | Contents |
|---|---|
data/cache/models/parakeet/ | Parakeet TDT 0.6B v3 ONNX export (encoder/decoder/joiner + vocab) snapshot-downloaded from istupakov/parakeet-tdt-0.6b-v3-onnx |