{ description = "PeerSpeak — decentralized P2P voice chat (Rust/iroh/PipeWire/Opus/iced)"; inputs = { # Pinned to the same channel the hosts run (nixos-config tracks # nixos-26.05), so the libraries this shell links and dlopens are built # against the same release as the PipeWire daemon and Vulkan ICD actually # running on the machine. Floating to unstable here would reintroduce # precisely the client/server version skew the pin exists to prevent. nixpkgs.url = "github:nixos/nixpkgs/nixos-26.05"; # The Rust toolchain is pinned SEPARATELY from the system libraries, and # deliberately so. nixpkgs 26.05 ships rustc 1.95.0, but this crate was # developed and verified against 1.97.1 — close enough to build and pass # every test, but not close enough for clippy, which flags a # `collapsible_match` on 1.95 that 1.97 does not. Taking the compiler from # here decouples "which Rust the project targets" from "which release the # audio stack came from", so a nixpkgs bump can never silently move the # compiler under the lint gate again. # # This is the reproducible alternative to rustup: same exact-version # control, but the choice is recorded in flake.lock, so darp5 or a fresh # clone resolves the identical toolchain instead of whatever rustup happens # to fetch that day. rust-overlay = { url = "github:oxalica/rust-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = { nixpkgs, rust-overlay, ... }: let system = "x86_64-linux"; pkgs = import nixpkgs { inherit system; overlays = [ rust-overlay.overlays.default ]; }; # Matches what CachyOS shipped (rust 1:1.97.1-1), which is the toolchain # every green result in the handoff was produced with. # # `default` is the rustup "default" profile — rustc, cargo, rust-std, # rustfmt and clippy — so those are NOT listed separately below. # # rust-src and the windows-gnu target exist for win-cross-build.sh, which # needs `-Z build-std=std,panic_abort` for the self-contained .exe. That # script still expects to run in the peerspeak-win distrobox for the # mingw toolchain; carrying the target here just means the Rust half is # already in place if it is ever driven from the host. rustToolchain = pkgs.rust-bin.stable."1.97.1".default.override { extensions = [ "rust-src" ]; targets = [ "x86_64-pc-windows-gnu" ]; }; # Libraries that iced/winit/wgpu open with dlopen at RUNTIME rather than # linking at build time. Because nothing links them, they never land in # the binary's rpath — under `cargo run` the loader finds them only # through LD_LIBRARY_PATH. Leaving them out builds fine and then panics # at window creation, which is a genuinely confusing failure, so they are # listed explicitly instead of discovered the hard way. runtimeLibs = with pkgs; [ vulkan-loader # wgpu's Vulkan backend (iced's renderer) libxkbcommon # winit keyboard handling wayland # wayland-sys, dlopen'd on a Wayland session libx11 # x11-dl, dlopen'd on the X11 fallback path libxcursor libxrandr libxi ]; # Screen sharing spawns pixelpass as a CHILD PROCESS, and pixelpass in # turn drives GStreamer as a subprocess. That makes these tools a # dependency of peerspeak's own test suite, not just of pixelpass: # `tests/screenshare_host_fault.rs` starts a real pixelpass host, which # aborts at its preflight if gst-launch-1.0 is missing. # # Deliberately duplicated from pixelpass's flake rather than importing it # as an input. The two projects are mutually optional by design — neither # is a dependency of the other, and the coupling is a runtime subprocess # contract. Making one flake consume the other would quietly reintroduce # exactly the build-level dependency that rule exists to prevent. screenshareTools = with pkgs; [ gst_all_1.gstreamer gst_all_1.gst-plugins-base gst_all_1.gst-plugins-good gst_all_1.gst-plugins-bad gst_all_1.gst-plugins-ugly gst_all_1.gst-libav pipewire # pipewiresrc (Wayland capture; ships in this pkg) ]; in { devShells.${system}.default = pkgs.mkShell { nativeBuildInputs = [ rustToolchain ] ++ (with pkgs; [ # The supply-chain gates .gitea/workflows/ci.yml runs, so the same # checks are reproducible locally before a push. These were `cargo # install`ed on the CachyOS side, which does not carry over — those # binaries link that distro's glibc and will not run here. # cargo-deny reads deny.toml; cargo-audit reads .cargo/audit.toml. cargo-audit cargo-deny # Debian packaging (`cargo deb --no-build`). Note the .deb itself # should still be built inside a Debian/Ubuntu distrobox so the # binary links that distro's glibc — see the packaging notes in # Cargo.toml. cargo-deb pkg-config # pipewire-sys and libspa-sys generate their bindings with bindgen, # which needs a real libclang present at build time. clang # audiopus_sys prefers the system libopus via pkg-config but falls # back to a vendored CMake build; cmake keeps that fallback working # rather than failing obscurely inside a build script. cmake # build.rs shells out to `git rev-parse --short=8 HEAD` to stamp # PEERSPEAK_GIT_SHORT into the binary (surfaced in Settings). git ]) ++ screenshareTools ++ [ pkgs.pulseaudio # `pactl`, used by pixelpass's audio routing pkgs.mpv # the screen-share viewer ]; buildInputs = with pkgs; [ alsa-lib # alsa-sys, pulled in by rodio/cpal libopus # audiopus_sys, linked dynamically pipewire # pipewire-sys + libspa-sys: the Linux audio backend ] ++ runtimeLibs; # bindgen finds libclang through this variable specifically — having # clang on PATH is not sufficient. LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath runtimeLibs; # NixOS keeps every GStreamer plugin in its own store path, so the # gst-launch-1.0 that pixelpass spawns discovers them ONLY through this # search path. Same reasoning as hosts/darp5 and hosts/cazen in # nixos-config. GST_PLUGIN_SYSTEM_PATH_1_0 = pkgs.lib.makeSearchPathOutput "lib" "lib/gstreamer-1.0" screenshareTools; # Only greet an interactive shell. shellHook also runs under # `nix develop --command …`, where printing this would interleave the # banner with the command's own output. shellHook = '' if [ -t 1 ]; then echo "peerspeak — rustc $(rustc --version | cut -d' ' -f2) / cargo $(cargo --version | cut -d' ' -f2)" echo " cargo build --release build" echo " cargo test lib tests" echo " cargo clippy --all-targets -- -D warnings lint" echo echo "Screen sharing spawns pixelpass as a child process — it must be" echo "on PATH. Build it from ../pixelpass and add its target/release." fi ''; }; }; }