Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
description = "Brig and the Linux microVM runtime it drives";

inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";

outputs =
{ self, nixpkgs }:
let
systems = [
"x86_64-linux"
"aarch64-linux"
];
forAll = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
in
{
packages = forAll (
pkgs:
let
brig = pkgs.callPackage ./nix/brig.nix { };
urunc = pkgs.callPackage ./nix/urunc.nix { };
brig-guest = pkgs.callPackage ./nix/guest.nix { };
brig-runtime = pkgs.callPackage ./nix/runtime.nix { inherit brig urunc brig-guest; };
in
{
inherit
brig
urunc
brig-guest
brig-runtime
;
default = brig-runtime;
}
);

nixosModules.default = import ./nix/module.nix;

# A NixOS machine with the module on, for a run with network:
# nix build .#nixosConfigurations.demo.config.system.build.vm
# then ./result/bin/run-demo-vm. The VM test under checks has no
# network, so this is where a sandbox boots on a NixOS host.
nixosConfigurations.demo = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
self.nixosModules.default
(
{ pkgs, ... }:
{
services.brig.enable = true;
services.brig.package = self.packages.x86_64-linux.brig-runtime;
virtualisation.vmVariant.virtualisation = {
memorySize = 4096;
cores = 2;
diskSize = 8192;
graphics = false;
forwardPorts = [
{
from = "host";
host.port = 2222;
guest.port = 22;
}
];
};
services.openssh.enable = true;
services.openssh.settings.PermitRootLogin = "yes";
users.users.root.initialPassword = "demo";
system.stateVersion = "25.11";
}
)
];
};

checks = forAll (
pkgs:
let
brig-runtime = self.packages.${pkgs.system}.brig-runtime;
in
{
module = pkgs.callPackage ./nix/test.nix {
module = self.nixosModules.default;
inherit brig-runtime;
};
}
);
};
}
121 changes: 121 additions & 0 deletions nix/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Brig with its Linux runtime, from Nix

The flake at the root of this repository builds Brig together with the
runtime it drives on Linux, so one `nix build` gives a host everything a
sandbox needs.

| Package | What it is |
| --- | --- |
| `brig` | `brig`, `brigd` and `brig-broker`, built from the Brig release. The same expression as `pkgs/by-name/br/brig` in nixpkgs. |
| `urunc` | `urunc` and `containerd-shim-urunc-v2`, built from the commit the bundle release pins. |
| `brig-guest` | The guest kernel, the initrd, the signed checksum files and the static monitors (`hvi`, `cloud-hypervisor`, `virtiofsd`), taken from the bundle release tarball. |
| `brig-runtime` | The default. `brig` wrapped with the environment the bundle's `brig-env.sh` sets, `brig-containerd` to run containerd with the bundle's config, and a systemd unit. containerd, nerdctl and the CNI plugins come from nixpkgs. |

Only root drives this stack, as with the bundle's node-wide install. There
is no rootless path.

## Install on a host with nix

```sh
nix build github:NOFireAI/brig-standalone-linux#brig-runtime
sudo cp result/lib/systemd/system/brig-containerd.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now brig-containerd
sudo ./result/bin/brig doctor
sudo ./result/bin/brig run ubuntu ~/demo
```

`nix profile install` on the same output puts `brig` on your PATH with its
shell completions. Run it under `sudo` with the full path, since `sudo`
resets PATH.

The host needs `/dev/kvm` and the `vhost_vsock` module:

```sh
sudo modprobe vhost_vsock
echo vhost_vsock | sudo tee /etc/modules-load.d/brig.conf
```

## NixOS

The flake exports a module. With it, the unit, the kernel modules and the
directories are declared, and `brig` is on PATH:

```nix
{
inputs.brig-runtime.url = "github:NOFireAI/brig-standalone-linux";
outputs = { nixpkgs, brig-runtime, ... }: {
nixosConfigurations.host = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
brig-runtime.nixosModules.default
{
services.brig.enable = true;
services.brig.package = brig-runtime.packages.x86_64-linux.brig-runtime;
}
];
};
};
}
```

`nix flake check` runs a NixOS VM test of the module: the unit comes up,
the socket appears, `brig doctor` finds the runtime and the boot assets.
The test VM has no network, so it boots no sandbox. It needs KVM on the
host that runs it.

For a sandbox boot on NixOS with network, the flake has a demo machine:

```sh
nix build .#nixosConfigurations.demo.config.system.build.vm
./result/bin/run-nixos-vm # root, password demo, ssh on port 2222
```

Three things only showed up on NixOS, because a single-user nix store on
Debian is writable by root and `/usr/sbin` and `/lib` exist there:

- nerdctl writes a lock and one conflist per sandbox network into the CNI
config directory, so that directory is `/var/lib/brig/agent/cni/net.d`,
seeded from the store when `brig-containerd` starts.
- urunc runs `/usr/sbin/mke2fs` by default to build the overlay-block
rootfs. `urunc.toml` points it at the nixpkgs `mke2fs`.
- urunc binds `/lib`, `/lib64` and `/usr/lib` into the monitor's rootfs for
a dynamically linked monitor. NixOS has no `/lib` or `/usr/lib`, and the
monitors here are static. `nix/urunc-optional-libdirs.patch` skips a
directory that is not there, as upstream already does for `/lib64`.

## Why some pieces are binaries

Three things in `brig-guest` are not built here. The guest kernel comes from
`hull-assets`, whose repository is not public. The initrd is assembled by
`scripts/build-bundle.sh` from `urunit` and busybox. `hvi` is a static build
from `brig-sh/hull`. All three are taken from the bundle release tarball,
pinned by the sha256 its signed `checksums.txt` lists.

`virtiofsd` and `cloud-hypervisor` are in nixpkgs, but the nixpkgs builds
are dynamically linked. `urunc` pivots into a monitor rootfs of its own
before it spawns them, and a dynamically linked binary cannot find its
interpreter in there. The bundle carries static builds, so those are used.

## State and paths

Same as the bundle's node-wide install: state under `/var/lib/brig/agent`,
the containerd socket at `/run/brig/containerd.sock`, the CNI bridge
`brig0` on `10.44.0.0/24`.

## Tested

Debian 12 on a GCP `n2-standard-4` with nested virtualization and
single-user nix, on 1 November 2026: `brig doctor` all `ok`, `brig run
ubuntu` booted, DNS and `apt-get update` worked in the guest, a file
written under `/work` appeared on the host, and the unit came back after a
reboot.

NixOS 26.11, the demo machine above, run as a qemu VM on that Debian host
on 2 November 2026, so the sandbox was a third level of virtualization:
`brig doctor` all `ok`, `brig run ubuntu` pulled the image and booted, the
guest agent answered within seconds, `brig sh` ran commands in the guest in
about a second, DNS resolved, a file written under `/work` appeared on the
host.

Not tested: aarch64, a NixOS host on real hardware.
86 changes: 86 additions & 0 deletions nix/brig.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
{
lib,
stdenv,
buildGoModule,
fetchFromGitHub,
installShellFiles,
versionCheckHook,
nix-update-script,
}:

buildGoModule (finalAttrs: {
pname = "brig";
version = "0.5.0";

src = fetchFromGitHub {
owner = "brig-sh";
repo = "brig";
tag = "v${finalAttrs.version}";
hash = "sha256-HPkr/fHTi8QHIR09PVTDMAPefdtV6K9kYXVLdtbPupY=";
};

vendorHash = "sha256-513twbhAd5XxpvGisWoCGwmIFtK0fypViOOBpMNjNEQ=";

subPackages = [
"cmd/brig"
"cmd/brigd"
"cmd/brig-broker"
];

env.CGO_ENABLED = 0;

# The binary reads the tag and commit the Go toolchain embeds from a git
# checkout. A tarball carries none, so the values are passed here, the way
# the upstream Makefile does for a worktree. Without them `brig version`
# prints "dev".
ldflags = [
"-s"
"-w"
"-X github.com/brig-sh/brig/internal/buildinfo.gitCommit=8059e00a8a8741a94177aa19626886f63eb396e1"
"-X github.com/brig-sh/brig/internal/buildinfo.gitCommitTime=2026-10-31T12:08:40Z"
"-X github.com/brig-sh/brig/internal/buildinfo.gitDescribe=v${finalAttrs.version}-0-g8059e00"
];

nativeBuildInputs = [ installShellFiles ];

# The tests are written for a source checkout: one of them expects a test
# binary built from a tarball to carry no commit. The stamped values above
# are for the shipped binaries only.
preCheck = ''
ldflags="-s -w"
'';

postInstall = lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
installShellCompletion --cmd brig \
--bash <($out/bin/brig completion bash) \
--fish <($out/bin/brig completion fish) \
--zsh <($out/bin/brig completion zsh)
'';

# On darwin the secret store tests write to the real login keychain.
doCheck = !stdenv.hostPlatform.isDarwin;

nativeInstallCheckInputs = [ versionCheckHook ];
versionCheckProgramArg = "version";
doInstallCheck = true;

passthru.updateScript = nix-update-script { };

meta = {
description = "Run coding agents in a microVM sandbox";
longDescription = ''
Brig boots a coding agent in a microVM and hands it a project directory
and named credentials, nothing else. This package holds the brig CLI,
the brigd session daemon and the brig-broker credential broker. It does
not include a runtime: on Linux Brig drives nerdctl with the urunc shim,
on macOS the hull runtime. `brig doctor` reports what is missing, and
https://brig.sh/docs/install/ says how to install the runtime.
'';
homepage = "https://brig.sh";
changelog = "https://github.com/brig-sh/brig/releases/tag/v${finalAttrs.version}";
license = lib.licenses.asl20;
mainProgram = "brig";
maintainers = with lib.maintainers; [ pmoust ];
platforms = lib.platforms.unix;
};
})
76 changes: 76 additions & 0 deletions nix/guest.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
lib,
stdenvNoCC,
fetchurl,
}:

# The guest kernel, the container-initrd and the monitors, taken from the
# Linux runtime bundle release. The kernel is built by hull-assets, whose
# repository is not public, the initrd is assembled by scripts/build-bundle.sh
# from urunit and busybox, and hvi is a static binary from brig-sh/hull. Until
# those build here, the bundle tarball is the source, pinned by the sha256 its
# signed checksums.txt lists.
#
# virtiofsd and cloud-hypervisor come from the same tarball although nixpkgs
# has both: urunc pivots into a monitor rootfs of its own before it spawns
# them, and a dynamically linked binary cannot find its interpreter in there.
# The bundle's builds are static.
#
# brig checks the kernel and initrd against share/guest/SHA256SUMS, and that
# file against the release's checksums.txt, whose cosign signature and
# certificate sit beside it, the way the bundle's installer leaves them.
let
version = "0.1.0-rc21";
base = "https://github.com/NOFireAI/brig-standalone-linux/releases/download/v${version}";
release =
name: hash:
fetchurl {
url = "${base}/${name}";
sha256 = hash;
};
checksums = release "checksums.txt" "441bc9aaded057ed301b9e7bec265c4d75d2c418f32fc75a45dd841736ce67fa";
signature = release "checksums.txt.sig" "bcfb6a50348c2f3a760419c5e2ad290bbe816ee57f77c2653543d7c1b1e83dc6";
certificate = release "checksums.txt.pem" "d2cd0ff6918724d50dbe22d915a3dd9ee5cd3b26a26d4b0d52cdbf950bf84744";
arch =
stdenvNoCC.hostPlatform.go.GOARCH
or (if stdenvNoCC.hostPlatform.isAarch64 then "arm64" else "amd64");
hashes = {
amd64 = "3a376bb1da931c876d512643a205fd6ccefc6a8f6f696bdc6c548a477ef3205f";
arm64 = "bcf280444a78adca58c5e89336edc6c55893f3da6416bc9bc2a103ad0e4c9b36";
};
in
stdenvNoCC.mkDerivation {
pname = "brig-guest";
inherit version;

src = fetchurl {
url = "${base}/brig-standalone-v${version}-linux-${arch}.tar.gz";
sha256 = hashes.${arch};
};

dontBuild = true;
dontFixup = true;

installPhase = ''
runHook preInstall
mkdir -p $out/share $out/bin
cp -r share/guest $out/share/guest
cp ${checksums} $out/share/guest/checksums.txt
cp ${signature} $out/share/guest/checksums.txt.sig
cp ${certificate} $out/share/guest/checksums.txt.pem
install -m 0755 bin/hvi bin/cloud-hypervisor $out/bin/
install -m 0755 libexec/virtiofsd $out/bin/virtiofsd
runHook postInstall
'';

meta = {
description = "Guest kernel, initrd and monitors for Brig sandboxes on Linux";
homepage = "https://github.com/NOFireAI/brig-standalone-linux";
sourceProvenance = [ lib.sourceTypes.binaryNativeCode ];
license = lib.licenses.asl20;
platforms = [
"x86_64-linux"
"aarch64-linux"
];
};
}
Loading