Something went wrong. Try again.
Instant MicroVMs/Unikernels for macOS and Linux
linux unikernel unikraft osv freebsd netbsd microvm libkrun
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620{ description = "bsdkrun - a Firecracker-style microVM launcher for BSD, Linux, and unikernel guests, on libkrun";
# Both bsdkrun's and the libkrun fork's CI push to this cache — declaring it # here lets `nix build`/`nix develop` substitute libkrun-pvh (and bsdkrun # itself) instead of compiling; nix asks once to trust it. nixConfig = { extra-substituters = [ "https://bsdkrun.cachix.org" ]; extra-trusted-public-keys = [ "bsdkrun.cachix.org-1:KzvN59TR6k15k7Fl7SxTEhxJnE0MvbxLC2HpxdVlC9Q=" ]; };
inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
crane.url = "github:ipetkov/crane";
flake-utils.url = "github:numtide/flake-utils";
# The PVH-enabled libkrun fork — what boots NetBSD/FreeBSD amd64 on # Linux/KVM (stock libkrun only speaks the Linux boot protocol there). # Its CI pushes builds to the `bsdkrun` Cachix cache. Deliberately NO # `nixpkgs.follows`: rewriting the fork's nixpkgs would change its # derivation hash and miss the cache its CI populated — to substitute a # producer's build, consume it with the producer's own locked inputs. libkrun-pvh.url = "github:tsirysndr/libkrun/feat/pvh-boot";
# The Solo5 fork whose hvt tender `bsdkrun solo5` embeds — it adds the # macOS/HVF backend upstream does not have. # # `flake = false` because solo5 is a C project with no flake of its own, # and needs none: nix fetches the repo and hands the source tree to the # derivation below. This is also why a nix build needs no `?submodules=1` # — `library/solo5` is the same commit as a git submodule, for `cargo # build` outside nix, but nix pins it here and in flake.lock instead. # Bump both together: `git submodule update --remote library/solo5` and # `nix flake update solo5`. The e2e-solo5 workflow fails if they drift. solo5 = { url = "github:tsirysndr/solo5/hvf-macos-aarch64"; flake = false; }; };
outputs = { self, nixpkgs, crane, flake-utils, libkrun-pvh, solo5, ... }: # Linux (KVM) uses nixpkgs' libkrun. macOS (Hypervisor.framework) has no # libkrun in nixpkgs, so it links Homebrew's — an *impure* build: # brew install libkrun && nix build --impure .#bsdkrun flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ] (system: let pkgs = import nixpkgs { inherit system; }; inherit (pkgs) lib; isDarwin = pkgs.stdenv.isDarwin;
craneLib = crane.mkLib pkgs; src = craneLib.cleanCargoSource ./.;
# ---- web UI --------------------------------------------------------- # # `bsdkrun ui` serves an SPA compiled into the binary by rust-embed, so # the bundle has to exist before cargo runs.
# node_modules for the web SPA. `bun install` needs the network, so # dependency resolution lives in a fixed-output derivation. # # The tree is NOT platform-independent: native optional deps (e.g. # @tailwindcss/oxide-*) are installed per-OS/CPU, so each system needs # its own hash. # # Updating: change web/package.json or web/bun.lock, set the current # system's hash below to lib.fakeHash, run `nix build`, and copy the # hash Nix reports on mismatch back in. Repeat per platform. webNodeModules = pkgs.stdenv.mkDerivation { pname = "bsdkrun-web-node-modules"; version = "0.1.0";
src = lib.fileset.toSource { root = ./web; fileset = lib.fileset.unions [ ./web/package.json ./web/bun.lock ]; };
nativeBuildInputs = [ pkgs.bun ];
dontConfigure = true;
buildPhase = '' runHook preBuild export HOME=$(mktemp -d) bun install --frozen-lockfile --no-progress runHook postBuild '';
installPhase = '' runHook preInstall mv node_modules $out runHook postInstall '';
dontFixup = true;
outputHashMode = "recursive"; outputHashAlgo = "sha256"; # These genuinely differ per system — the three below are three # distinct trees, which is the per-platform caveat above in practice. outputHash = { aarch64-darwin = "sha256-HBlPd3BIT0bhXsqQtgzf2x9L4p+UHRST+cAuPTvIAdY="; x86_64-linux = "sha256-817iL8whjah44OISnq2vwyCeYrAeGVlJxk4j0ri4ACw="; aarch64-linux = "sha256-dAAh/6mXbzHSiqqFDjoNlw9rLHB4CWqYMdpiSoLApbY="; }.${system}; };
# The built SPA. Embedded into the bsdkrun binary at compile time. # ---- bsdkrun pack: the Go half ------------------------------------ # Built as its own derivation and copied into core/src/pack-bin # before cargo runs, exactly as webUi is copied into web/dist. # # core/build.rs would otherwise run `go build` itself, which cannot # work here: a nix build has no network, so fetching modules fails # and build.rs degrades to shipping a binary with no pack support at # all. buildGoModule fetches them in a fixed-output derivation # instead, which is the one place a nix build is allowed to. # # vendorHash covers those modules. If a dependency changes, nix will # report the expected value; regenerate it with: # cd pack && go mod vendor -o /tmp/v && nix hash path --sri /tmp/v packBin = pkgs.buildGoModule { pname = "bsdkrun-pack"; version = "0.1.0";
src = ./pack; vendorHash = "sha256-c1/3o8pfZ3td2iXE+o3r2aE3i8PuSLe0IEkIisgvR0A=";
# Matches what core/build.rs builds: a static binary with no libc # dependency, which is what makes it safe to embed and exec. env.CGO_ENABLED = "0"; ldflags = [ "-s" "-w" ];
# The binary has to carry the name rust_embed looks for. postInstall = '' mv $out/bin/pack $out/bin/bsdkrun-pack 2>/dev/null || true '';
# There are tests, but they shell out to docker for the buildkit # ones; the unit tests that matter run in CI. doCheck = false; };
# ---- bsdkrun-ci: the CI runner ------------------------------------ # The Go half of `bsdkrun ci`, embedded like pack. Deliberately *no* # `replace ../sdk/go` any more: a path replace lands in the vendor # FOD at hash-pin time and freezes there, so SDK changes invisible to # go.sum silently compiled against the old SDK (one real breakage: # `CreateStreaming undefined` in CI while the method sat in the same # commit). The runner inlines its thin driver instead — see # ci/driver.go. # # On a dependency change the reliable vendorHash procedure is nix's # own: build once, copy the "got:" hash from the mismatch error. ciBin = pkgs.buildGoModule { pname = "bsdkrun-ci"; version = "0.1.0";
src = ./ci; # Bumped when `ci` gained the spindle-compatible server: importing # tangled.org/core's spindle packages pulls in casbin, sqlite, # openbao, the AWS SDK and more. Computed with the procedure above: # cd ci && go mod vendor -o /tmp/v && nix hash path --sri /tmp/v vendorHash = "sha256-OvXL0d+f1xUqQ2LK/m6VTzEMRCwRI9dmWoA9FPyU6BQ=";
# Built like a release binary: with the spindle-compatible server, so # `bsdkrun ci serve` can stand in for a spindle from a nix install # too. That needs cgo, since spindle's storage is SQLite through # mattn/go-sqlite3. Unlike pack — which is embedded and exec'd and so # must not depend on the host libc — this binary is built by nix for # the closure it ships in, so cgo costs nothing here. env.CGO_ENABLED = "1"; tags = [ "spindle" ]; ldflags = [ "-s" "-w" ];
# The binary has to carry the name rust_embed looks for. postInstall = '' mv $out/bin/ci $out/bin/bsdkrun-ci 2>/dev/null || true '';
doCheck = false; };
# ---- solo5-hvt: the Solo5 tender ---------------------------------- # `bsdkrun solo5` runs MirageOS unikernels through this, and it is not # a libkrun guest: the tender drives Hypervisor.framework (macOS) or # KVM (Linux) in its own process. bsdkrun embeds it, so an end user # needs no Solo5 install. # # Its own derivation, for the same reason as packBin: crane's # `cleanCargoSource` keeps only Rust and Cargo files, so # `library/solo5` never reaches the cargo build. core/build.rs then # finds no configure.sh there, leaves `core/src/solo5-bin` untouched # (rather than clobbering it), and rust_embed bakes in whatever # `preBuild` put there — which is this. # # The source is the `solo5` flake input, NOT `./library/solo5`: a # flake does not fetch git submodules, so a plain `nix build` would # otherwise get an empty directory and fail at configure. The # submodule and the input are the same commit — see the input's # comment for how to bump them together. solo5Tender = pkgs.stdenv.mkDerivation { pname = "solo5-hvt"; version = "0.9.0-bsdkrun";
src = solo5;
# pkg-config + libseccomp: the hvt tender installs a seccomp filter # on Linux (hvt_seccomp_linux.c), and solo5's configure.sh fails # outright without the headers. Darwin needs neither — it has no # seccomp, which the tender documents as a gap rather than papering # over. nativeBuildInputs = [ pkgs.pkg-config ]; buildInputs = lib.optionals (!isDarwin) [ pkgs.libseccomp ];
postPatch = '' # scripts/gen_version_h.sh derives the version from `git describe` # and dies outside a git tree — which a nix source always is. Its # documented fallback is this file, the release-tarball case. cat > include/version.h.distrib <<'EOF' /* Generated by bsdkrun's flake.nix, do not edit */ #ifndef __VERSION_H__ #define __VERSION_H__ #define SOLO5_VERSION "bsdkrun-nix" #endif EOF '';
# --disable-toolchain stops before the cross-compiler and bindings, # which would want ld.lld and llvm-objcopy to emit ELF on a Mach-O # host. Building a unikernel needs those; running one does not, and # this only ever runs one. --disable-elftool likewise: bsdkrun reads # the manifest note itself (core/src/solo5.rs). configurePhase = '' runHook preConfigure ./configure.sh --disable-toolchain --disable-elftool runHook postConfigure '';
buildPhase = '' runHook preBuild make -j"$NIX_BUILD_CORES" runHook postBuild '';
installPhase = '' runHook preInstall install -Dm755 tenders/hvt/solo5-hvt $out/bin/solo5-hvt runHook postInstall '';
# The tender's own link step signs it with the hypervisor # entitlement, but nix's stdenv puts no `codesign` on PATH at all # here — so point it at the real one, the same /usr/bin/codesign # this flake uses to sign bsdkrun itself. preConfigure = lib.optionalString isDarwin '' mkdir -p .codesign-shim cat > .codesign-shim/codesign <<'EOF' #!/bin/sh exec /usr/bin/codesign "$@" EOF chmod +x .codesign-shim/codesign export PATH="$PWD/.codesign-shim:$PATH" '';
# ...and sign it again here, which is the signature that actually # survives: nix's darwin fixupPhase re-signs every binary in $out # ad-hoc, silently dropping the entitlements the link step applied. # postFixup runs after it. Without this the tender builds, installs # and then dies at run time with # HVF: hv_vm_create() failed: 0xfae94007 # which names the entitlement but not the step that removed it. # (`bsdkrun solo5` re-signs the tender when it extracts it, so this # matters most for `nix run .#solo5-hvt` — using the tender directly.) postFixup = lib.optionalString isDarwin '' printf '%s' ${lib.escapeShellArg entitlements} > entitlements.plist /usr/bin/codesign --entitlements entitlements.plist --force \ --sign - "$out/bin/solo5-hvt" '';
meta = with lib; { description = "Solo5 hvt tender (Hypervisor.framework on macOS, KVM on Linux)"; homepage = "https://github.com/tsirysndr/solo5"; license = licenses.isc; mainProgram = "solo5-hvt"; platforms = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ]; }; };
webUi = pkgs.stdenv.mkDerivation { pname = "bsdkrun-web"; version = "0.1.0";
src = ./web;
nativeBuildInputs = [ pkgs.bun pkgs.nodejs ];
configurePhase = '' runHook preConfigure cp -r ${webNodeModules} node_modules chmod -R u+w node_modules patchShebangs node_modules export HOME=$(mktemp -d) runHook postConfigure '';
buildPhase = '' runHook preBuild bun run build runHook postBuild '';
installPhase = '' runHook preInstall cp -r dist $out runHook postInstall ''; };
# Linux: the PVH libkrun fork's flake (built with BLK=1 NET=1 there). # Beyond stock libkrun it adds the PVH direct-boot path that NetBSD's # MICROVM and FreeBSD's FIRECRACKER amd64 kernels need. Only defined # for Linux systems — never forced on darwin (guarded by !isDarwin). libkrun = libkrun-pvh.packages.${system}.default;
# macOS: Homebrew's libkrun. During an *impure* build (BSDKRUN_IMPURE=1 + # `nix build --impure`) we import it into the store so the sandboxed build # can link it (a bare /opt/homebrew path isn't visible in the sandbox). # During *pure* evaluation — e.g. `flakehub-push`, which inspects every # system's drvPath on a Linux runner where /opt/homebrew is absent and # absolute-path access is forbidden — `builtins.getEnv` returns "", so we # fall back to a plain path string and never touch the forbidden path. brewLibkrun = builtins.path { path = /opt/homebrew/opt/libkrun; name = "libkrun-brew"; };
libkrunPrefix = if isDarwin then (if builtins.getEnv "BSDKRUN_IMPURE" != "" then "${brewLibkrun}" else "/opt/homebrew/opt/libkrun") else "${libkrun}";
# Tools bsdkrun shells out to at runtime. On macOS the system versions are # fine (and losetup/gvproxy don't apply), so we only wrap on Linux. runtimeDeps = with pkgs; [ curl gnutar gzip xz cpio ] ++ lib.optionals (!isDarwin) [ util-linux gvproxy ];
# The entitlement libkrun requires on macOS (Hypervisor.framework). entitlements = '' <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.security.hypervisor</key> <true/> <key>com.apple.security.cs.disable-library-validation</key> <true/> </dict> </plist> '';
commonArgs = { inherit src; pname = "bsdkrun"; version = "0.11.4"; strictDeps = true; # Explicit even though the workspace's `default-members` is this # package: the daemon is a member too, and nothing here should build # it by accident. cargoExtraArgs = "-p bsdkrun";
# llvm (llvm-config) + libclang for bindgen-based crates. nativeBuildInputs = [ pkgs.pkg-config pkgs.llvmPackages.llvm ]; buildInputs = lib.optionals (!isDarwin) [ libkrun ];
LIBKRUN_PREFIX = libkrunPrefix; LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
# rust-embed reads web/dist at compile time, and cleanCargoSource # strips the whole web directory, so drop the pre-built SPA back in # before cargo runs. Without this the binary compiles fine and ships # build.rs's "UI not built" placeholder. preBuild = '' mkdir -p web cp -r ${webUi} web/dist chmod -R u+w web/dist
# rust_embed embeds whatever is here. build.rs finds no `go` on # PATH and leaves this alone rather than overwriting it, so the # binary that lands in bsdkrun is the one nix built. mkdir -p core/src/pack-bin cp ${packBin}/bin/bsdkrun-pack core/src/pack-bin/bsdkrun-pack chmod -R u+w core/src/pack-bin
# And for the CI runner, embedded the same way. mkdir -p core/src/ci-bin cp ${ciBin}/bin/bsdkrun-ci core/src/ci-bin/bsdkrun-ci chmod -R u+w core/src/ci-bin
# Same deal for the Solo5 tender: build.rs finds no # library/solo5 in the cleaned source, so it leaves this alone # rather than rebuilding it, and rust_embed bakes in the tender # nix built. mkdir -p core/src/solo5-bin cp ${solo5Tender}/bin/solo5-hvt core/src/solo5-bin/solo5-hvt chmod -R u+w core/src/solo5-bin ''; };
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
# ---- bsdkrund: the gRPC daemon ------------------------------------ # A workspace member, not a standalone crate: it depends on # `bsdkrun-core` by path, so its source has to be the whole workspace # (`../core` has to exist) and `-p bsdkrun-daemon` is what narrows the # build back down to it. # # It links the engine WITHOUT its `boot` feature, so it stays the pure, # hypervisor-free package it has always been: booting lives in # `bsdkrun-supervisor`, which ships beside it and is the only half that # needs libkrun. daemonArgs = { # NOT `cleanCargoSource`: that keeps only Rust and Cargo files, which # drops proto/bsdkrun.proto and leaves the build script failing with # "Could not make proto path relative". Keep .proto files too. src = lib.cleanSourceWith { src = ./.; name = "source"; filter = path: type: (builtins.match ".*\\.proto$" path != null) || (craneLib.filterCargoSources path type); }; pname = "bsdkrund"; version = "0.1.0"; strictDeps = true; # The workspace's `default-members` is the CLI, so without this the # daemon build would build bsdkrun instead — and want the web bundle. cargoExtraArgs = "-p bsdkrun-daemon";
# libkrun is wired in for `bsdkrun-supervisor`, which overrides these # args below. `bsdkrund` itself takes `bsdkrun-core` without its # `boot` feature, so it links no hypervisor at all — but the two share # this attrset, and an unused LIBKRUN_PREFIX costs nothing. nativeBuildInputs = [ pkgs.pkg-config pkgs.llvmPackages.llvm pkgs.protobuf ]; buildInputs = lib.optionals (!isDarwin) [ libkrun ]; LIBKRUN_PREFIX = libkrunPrefix; LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
# tonic-prost-build shells out to protoc to compile the .proto. # PROTOC_INCLUDE isn't needed today (nothing imports a well-known # type), but without it the first `import "google/protobuf/*.proto"` # would fail only under Nix, which is a confusing way to find out. PROTOC = "${pkgs.protobuf}/bin/protoc"; PROTOC_INCLUDE = "${pkgs.protobuf}/include"; };
daemonArtifacts = craneLib.buildDepsOnly daemonArgs;
# ---- bsdkrun-supervisor ------------------------------------------- # The half of the daemon that links libkrun, split out so `bsdkrund` # itself does not have to. Same source tree, different package. supervisorArgs = daemonArgs // { pname = "bsdkrun-supervisor"; version = "0.11.4"; cargoExtraArgs = "-p bsdkrun-supervisor";
# The supervisor carries the embedded Solo5 tender (its core dep # turns the `solo5` feature on, so daemon-driven solo5 boots work). # Same trick as the bsdkrun package: build.rs finds no library/solo5 # in the cleaned source and leaves this copy alone. preBuild = '' mkdir -p core/src/solo5-bin cp ${solo5Tender}/bin/solo5-hvt core/src/solo5-bin/solo5-hvt chmod -R u+w core/src/solo5-bin ''; };
supervisorArtifacts = craneLib.buildDepsOnly supervisorArgs;
bsdkrun-supervisor = craneLib.buildPackage (supervisorArgs // { cargoArtifacts = supervisorArtifacts;
nativeBuildInputs = supervisorArgs.nativeBuildInputs ++ lib.optionals (!isDarwin) [ pkgs.makeWrapper ]; postInstall = lib.optionalString (!isDarwin) '' wrapProgram $out/bin/bsdkrun-supervisor \ --prefix PATH : ${lib.makeBinPath runtimeDeps} '';
meta = with lib; { description = "Runs one bsdkrun command in its own process, for bsdkrund (not a user-facing tool)"; homepage = "https://github.com/tsirysndr/bsdkrun"; license = licenses.mit; mainProgram = "bsdkrun-supervisor"; platforms = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ]; }; });
bsdkrund = craneLib.buildPackage (daemonArgs // { cargoArtifacts = daemonArtifacts;
# No `bsdkrun` wrapper: the daemon needs no CLI on PATH at all. It # links the engine and supervises machines by re-exec'ing itself, so # the only thing it still shells out to is the runtime tools below. nativeBuildInputs = daemonArgs.nativeBuildInputs ++ lib.optionals (!isDarwin) [ pkgs.makeWrapper ]; postInstall = lib.optionalString (!isDarwin) '' wrapProgram $out/bin/bsdkrund \ --prefix PATH : ${lib.makeBinPath runtimeDeps} '';
meta = with lib; { description = "Token-authenticated gRPC daemon for running bsdkrun machines on a remote VPS or bare-metal KVM host"; homepage = "https://github.com/tsirysndr/bsdkrun"; license = licenses.mit; mainProgram = "bsdkrund"; platforms = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ]; }; });
bsdkrun = craneLib.buildPackage (commonArgs // { inherit cargoArtifacts;
nativeBuildInputs = commonArgs.nativeBuildInputs ++ lib.optionals (!isDarwin) [ pkgs.makeWrapper ];
postInstall = lib.optionalString (!isDarwin) '' wrapProgram $out/bin/bsdkrun \ --suffix PATH : ${lib.makeBinPath runtimeDeps} '';
# Re-sign with the hypervisor entitlement AFTER Nix's own darwin # signing (postFixup runs after fixupPhase), so the entitlement sticks. postFixup = lib.optionalString isDarwin '' printf '%s' ${lib.escapeShellArg entitlements} > entitlements.plist /usr/bin/codesign --entitlements entitlements.plist --force \ --sign - "$out/bin/bsdkrun" '';
meta = with lib; { description = "Firecracker-style microVM launcher for BSD, Linux, and unikernel guests on libkrun"; homepage = "https://github.com/tsirysndr/bsdkrun"; license = licenses.mit; mainProgram = "bsdkrun"; platforms = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" ]; }; }); in { checks = { inherit bsdkrun bsdkrund bsdkrun-supervisor;
bsdkrun-clippy = craneLib.cargoClippy (commonArgs // { inherit cargoArtifacts; cargoClippyExtraArgs = "--all-targets"; });
bsdkrun-fmt = craneLib.cargoFmt { inherit src; };
bsdkrund-clippy = craneLib.cargoClippy (daemonArgs // { cargoArtifacts = daemonArtifacts; cargoClippyExtraArgs = "--all-targets -- -D warnings"; });
};
packages.default = bsdkrun; packages.bsdkrun = bsdkrun; packages.bsdkrund = bsdkrund; packages.bsdkrun-supervisor = bsdkrun-supervisor; # The SPA on its own, for serving from something other than `bsdkrun ui`. packages.web = webUi; # The Solo5 tender on its own — useful for running a unikernel with # plain `solo5-hvt`, and for checking the tender builds without # waiting for the whole Rust build behind it. packages.solo5-hvt = solo5Tender;
apps.default = flake-utils.lib.mkApp { drv = bsdkrun; }; apps.bsdkrund = flake-utils.lib.mkApp { drv = bsdkrund; };
# `nix develop` — toolchain + libkrun (Linux) + everything bsdkrun needs, # plus zig/cargo-zigbuild for cross-building guest agents. devShells.default = craneLib.devShell { checks = self.checks.${system};
LIBKRUN_PREFIX = libkrunPrefix; LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
packages = with pkgs; # protobuf so the daemon's build script can run protoc too. # bun + node to build the web UI (`make web`). # go builds pack/, which core/build.rs compiles and embeds. [ pkg-config llvmPackages.llvm cargo-zigbuild zig protobuf bun nodejs go ] # core/build.rs builds the Solo5 tender from library/solo5 in a # plain `cargo build`, which needs make and (on Linux) libseccomp # — without them the build only warns and the tender is silently # left out of the binary. ++ [ gnumake ] ++ lib.optionals (!isDarwin) [ libkrun libseccomp ] ++ runtimeDeps; }; });}