diff --git a/flake.nix b/flake.nix index ea2a6b6..62cb3cc 100644 --- a/flake.nix +++ b/flake.nix @@ -69,9 +69,11 @@ ./modules/nixos/services/open_code_server.nix ./modules/nixos/services/ollama_init_custom_models.nix ./modules/nixos/services/openclaw_node.nix + ./modules/nixos/services/remote-builder.nix ./modules/nixos/security/ai-worker-restricted.nix ./users/gortium.nix ./users/ai-worker.nix + ./users/builder.nix ]; }; @@ -100,7 +102,9 @@ } nixos-raspberrypi.nixosModules.raspberry-pi-5.base nixos-uconsole.nixosModules.uconsole-cm5 + ./modules/nixos/services/remote-builder.nix ./hosts/uConsole/configuration.nix + ./users/builder.nix ./hosts/uConsole/hardware-configuration.nix ]; }; diff --git a/hosts/lazyworkhorse/configuration.nix b/hosts/lazyworkhorse/configuration.nix index 6f02a2b..9167552 100644 --- a/hosts/lazyworkhorse/configuration.nix +++ b/hosts/lazyworkhorse/configuration.nix @@ -573,5 +573,23 @@ # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion . system.stateVersion = "25.05"; # Did you read the comment? + + # ============================================================ + # Remote builder — dispatches aarch64-linux builds to uConsole + # ============================================================ + services.remoteBuilder = { + enable = true; + machines = [ + { + hostName = "192.168.1.120"; + port = 22; + sshUser = "builder"; + sshKey = "/etc/ssh/builder_key"; + systems = [ "aarch64-linux" ]; + maxJobs = 4; + } + ]; + }; + } diff --git a/hosts/uConsole/configuration.nix b/hosts/uConsole/configuration.nix index 26e0d72..f7c5135 100644 --- a/hosts/uConsole/configuration.nix +++ b/hosts/uConsole/configuration.nix @@ -168,4 +168,23 @@ # Reticulum uses its own encryption and doesn't need open ports # for basic mesh operations (peer-to-peer discovery). # For TCP interfaces, open additional ports as needed. + + # ============================================================ + # Remote builder — dispatches x86_64-linux builds to server + # ============================================================ + services.remoteBuilder = { + enable = true; + machines = [ + { + hostName = "lazyworkhorse.net"; + port = 2424; + sshUser = "builder"; + sshKey = "/etc/ssh/builder_key"; + systems = [ "x86_64-linux" ]; + maxJobs = 36; + supportedFeatures = [ "benchmark" "big-parallel" "nixos-test" ]; + } + ]; + }; + } diff --git a/lib/keys.nix b/lib/keys.nix index ab287d6..39a646a 100644 --- a/lib/keys.nix +++ b/lib/keys.nix @@ -9,6 +9,13 @@ ai-worker = { main = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAXeGtPPcsP2IYRQNvII41NVWhJsarEk8c4qxs/a5sXf"; }; + + builder = { + # Same key on both hosts for bidirectional remote building. + # Generate with: ssh-keygen -t ed25519 -f /etc/ssh/builder_key -N "" + # Replace the placeholder below with the public key (builder_key.pub). + main = "PLACEHOLDER_ADD_BUILDER_PUBKEY_HERE"; + }; }; hosts = { diff --git a/modules/nixos/services/remote-builder.nix b/modules/nixos/services/remote-builder.nix new file mode 100644 index 0000000..8162303 --- /dev/null +++ b/modules/nixos/services/remote-builder.nix @@ -0,0 +1,74 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.remoteBuilder; +in { + options.services.remoteBuilder = { + enable = lib.mkEnableOption "remote Nix build machine"; + + machines = lib.mkOption { + type = lib.types.listOf (lib.types.submodule { + options = { + hostName = lib.mkOption { + type = lib.types.str; + description = "Hostname or IP of the remote build machine."; + }; + port = lib.mkOption { + type = lib.types.port; + default = 22; + description = "SSH port."; + }; + sshUser = lib.mkOption { + type = lib.types.str; + default = "builder"; + description = "SSH user on the remote build machine."; + }; + sshKey = lib.mkOption { + type = lib.types.str; + description = "Path to SSH private key for the builder."; + }; + systems = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ "aarch64-linux" ]; + description = "System types the remote builder can build for."; + }; + maxJobs = lib.mkOption { + type = lib.types.int; + default = 4; + description = "Max parallel jobs on the remote builder."; + }; + supportedFeatures = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ "benchmark" "big-parallel" "nixos-test" ]; + description = "Features the remote builder supports."; + }; + }; + }); + default = []; + description = "List of remote Nix build machines."; + }; + }; + + config = lib.mkIf cfg.enable { + nix.distributedBuilds = true; + nix.buildMachines = map (m: { + hostName = m.hostName; + sshUser = m.sshUser; + sshKey = m.sshKey; + systems = m.systems; + maxJobs = m.maxJobs; + supportedFeatures = m.supportedFeatures; + }) cfg.machines; + + # SSH config for port + key (nix.buildMachines has no port option) + programs.ssh.extraConfig = lib.concatStringsSep "\n" (map (m: '' + Host ${m.hostName} + HostName ${m.hostName} + Port ${toString m.port} + User ${m.sshUser} + IdentityFile ${m.sshKey} + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + '') cfg.machines); + }; +} diff --git a/users/builder.nix b/users/builder.nix new file mode 100644 index 0000000..3c23987 --- /dev/null +++ b/users/builder.nix @@ -0,0 +1,13 @@ +{ config, lib, pkgs, keys, ... }: { + users.users.builder = { + isSystemUser = true; + group = "builder"; + home = "/var/empty"; + createHome = false; + shell = pkgs.nologin; + openssh.authorizedKeys.keys = with keys; [ + users.builder.main + ]; + }; + users.groups.builder = {}; +}