diff --git a/.gitignore b/.gitignore index 3ca9e3b..c7487d5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ # Local secrets and accounts (for example mattermost.env): never commit. *.env + +# Nix build outputs. +/result +/result-* diff --git a/CLAUDE.md b/CLAUDE.md index 97651dc..50aebd1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -61,6 +61,8 @@ requests, check `GET /slots?model=ornith-1.5-35b-a3b` so you do not evict someon any work done, and report the exit status and last lines. - Single test: `cargo test -p `. - `make audit` runs `cargo deny check advisories`; it needs the network. +- `nix build` (on a host with Nix, such as straylight) builds every binary; `nix build + .#tools-image` the tool image; `nix develop` gives the toolchain. See `docs/dependencies.md`. - `make verify-device` (from M2a task 13) runs the ignored tests in `crates/loopd/tests/device.rs` against straylight through a private `inferproxy`. It uses Ornith slot 0. `tools/check-m3a-device.sh` runs `brokerd`, `loopd` and an approval end to end against it. diff --git a/docs/dependencies.md b/docs/dependencies.md index 7dfe53e..6e222b4 100644 --- a/docs/dependencies.md +++ b/docs/dependencies.md @@ -12,3 +12,15 @@ Every external crate has a row here. `scripts/check-dep-docs.sh` enforces it. | `rustls` | 0.23.45 | `gatewayd` | TLS to Mattermost over the tailnet (M4a, owner's decision). `default-features = false` with `ring`, `std`, `tls12`. With its dependencies on Linux: `ring` (Apache-2.0 AND ISC; builds C and assembly), `rustls-webpki` and `untrusted` (ISC), `rustls-pki-types`, `subtle` (BSD-3-Clause), `zeroize`, `once_cell`, `getrandom`, `libc`, `cfg-if`. Apache-2.0 OR ISC OR MIT. | | `rustls-native-certs` | 0.8.4 | `gatewayd` | The host's trusted certificates, so a CA installed on the host is trusted too; adds `openssl-probe`. Apache-2.0 OR ISC OR MIT. | | `zeroize` | 1.9.0 | `gatewayd` | Wipes a secret's memory when it is dropped (`Secret`). Already a dependency of `rustls`. Apache-2.0 OR MIT. | + +## Build inputs + +`flake.nix` has one input, `nixpkgs`, pinned to `nixos-26.05` in `flake.lock`: the release the +owner's NixOS hosts run, so a host's config can use the flake with +`inputs.boxmaker.inputs.nixpkgs.follows = "nixpkgs"`. It has Rust 1.95.0, the workspace's +`rust-version`. Nix fetches it when building, never the harness when running, so it is not in +`docs/egress.md`. + +- `nix build` builds every binary (`result/bin/`); the tests are not run there, only in `make gate`. +- `nix build .#tools-image` builds the tool containers' image (`deploy/tools-image.nix`). +- `nix develop` gives a shell with `cargo`, `rustc`, `clippy`, `rustfmt`, `cargo-deny` and `make`. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..7669fd9 --- /dev/null +++ b/flake.lock @@ -0,0 +1,25 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1790218706, + "narHash": "sha256-qdM4Skrqq9Xps3OPNi9PWC6/hrWK9eDx5DUAUUaoz+o=", + "rev": "c508844df6c28fa6dabc1b6af70f3ccbd65c5201", + "type": "tarball", + "url": "https://releases.nixos.org/nixos/26.05/nixos-26.05.10529.c508844df6c2/nixexprs.tar.zst" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-26.05", + "type": "indirect" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..5a4edc8 --- /dev/null +++ b/flake.nix @@ -0,0 +1,76 @@ +{ + description = "boxmaker: a sovereign personal agent harness"; + + inputs.nixpkgs.url = "nixpkgs/nixos-26.05"; + + outputs = + { self, nixpkgs }: + let + systems = [ "x86_64-linux" ]; + forAllSystems = nixpkgs.lib.genAttrs systems; + version = + if self ? shortRev then + self.shortRev + else if self ? dirtyShortRev then + self.dirtyShortRev + else + "dev"; + in + { + packages = forAllSystems ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + lib = pkgs.lib; + # Only what the build reads: no target/, no .state/, no docs. + src = lib.cleanSourceWith { + src = ./.; + filter = + path: type: + let + rel = lib.removePrefix (toString ./. + "/") (toString path); + in + rel == "Cargo.toml" || rel == "Cargo.lock" || rel == "crates" || lib.hasPrefix "crates/" rel; + }; + # Every binary of the workspace: the roles, bxctl and toolkit. + boxmaker = pkgs.rustPlatform.buildRustPackage { + pname = "boxmaker"; + inherit version src; + cargoLock.lockFile = ./Cargo.lock; + # The tests run in `make gate`: they need Unix sockets under /tmp and scripts they write + # and run, which the build sandbox is not checked to allow. + doCheck = false; + meta = { + description = "a sovereign personal agent harness"; + mainProgram = "bxctl"; + }; + }; + # The image every tool container runs from; see the file for how it is loaded. + tools-image = import ./deploy/tools-image.nix { inherit pkgs; }; + in + { + inherit boxmaker tools-image; + default = boxmaker; + } + ); + + devShells = forAllSystems ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + default = pkgs.mkShell { + nativeBuildInputs = [ + pkgs.cargo + pkgs.rustc + pkgs.clippy + pkgs.rustfmt + pkgs.cargo-deny + pkgs.gnumake + ]; + }; + } + ); + }; +}