Merge feat/remote-builders into uconsole-cm5-incremental

Bidirectional remote building + rename uconsole-cm5 -> uConsole:
- Rename all uconsole-cm5 refs to uConsole (nixosSystem key, paths, module)
- Replace single-machine remote-builder module with multi-machine config
- Enable remote builder on lazyworkhorse -> uConsole (aarch64)
- Enable remote builder on uConsole -> lazyworkhorse (x86_64)
- Add builder system user + SSH key via agenix-rekey (secrets/builder_key.age)
- Add builder_key + gortium_password age secrets to all hosts
- Fix pre-existing bugs: missing modules in lazyworkhorse/cyt-pi,
  SSH config structure (hostKeys outside settings), cyt-pi inline user
- Remove obsolete openclaw-node service + module + age secrets
This commit is contained in:
2026-06-21 22:37:31 -04:00
13 changed files with 158 additions and 183 deletions

View File

@@ -1,64 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.openclaw-node;
openclawPkg = pkgs.openclaw;
in {
options.services.openclaw-node = {
enable = lib.mkEnableOption "OpenClaw Node service";
user = lib.mkOption {
type = lib.types.str;
default = "ai-worker";
description = "User to run the OpenClaw headless node as.";
};
gatewayHost = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
description = "Gateway host (IP or hostname).";
};
gatewayPort = lib.mkOption {
type = lib.types.int;
default = 18789;
description = "Gateway WebSocket port.";
};
gatewayTokenFile = lib.mkOption {
type = lib.types.str;
default = "";
description = "Path to file containing the gateway auth token.";
};
displayName = lib.mkOption {
type = lib.types.str;
default = "lazyworkhorse-host";
description = "Display name for this node (shown in pairing).";
};
};
config = lib.mkIf cfg.enable {
systemd.services.openclaw-node = {
description = "OpenClaw Headless Node Service";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "exec";
User = cfg.user;
Group = cfg.user;
WorkingDirectory = "/home/${cfg.user}";
ExecStart = ''
${pkgs.bash}/bin/bash -c 'export OPENCLAW_GATEWAY_TOKEN=$(cat ${cfg.gatewayTokenFile}) && exec ${openclawPkg}/bin/openclaw node run --host ${cfg.gatewayHost} --port ${toString cfg.gatewayPort} --display-name "${cfg.displayName}"'
'';
Restart = "always";
RestartSec = 5;
};
environment = {
NODE_ENV = "production";
};
};
};
}

View File

@@ -3,69 +3,71 @@ let
cfg = config.services.remoteBuilder;
in {
options.services.remoteBuilder = {
enable = lib.mkEnableOption "remote Nix build machine (lazyworkhorse server)";
enable = lib.mkEnableOption "remote Nix build machine";
buildMachine = {
host = lib.mkOption {
type = lib.types.str;
default = "lazyworkhorse.net";
description = "Hostname or IP of the remote build machine.";
};
sshUser = lib.mkOption {
type = lib.types.str;
default = "ai-worker";
description = "SSH user on the remote build machine.";
};
port = lib.mkOption {
type = lib.types.port;
default = 2424;
description = "SSH port added via ~root/.ssh/config since nix.buildMachines has no sshPort option.";
};
systems = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "aarch64-linux" "x86_64-linux" ];
description = "System types the remote builder can build for.";
};
maxJobs = lib.mkOption {
type = lib.types.int;
default = 16;
description = "Max parallel jobs on the remote builder.";
};
supportedFeatures = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "big-parallel" "nixos-test" "benchmark" ];
description = "Features the remote builder supports.";
};
};
fallbackLocal = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Fall back to local build when remote builder is unreachable.";
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 = [{
hostName = cfg.buildMachine.host;
sshUser = cfg.buildMachine.sshUser;
systems = cfg.buildMachine.systems;
maxJobs = cfg.buildMachine.maxJobs;
supportedFeatures = cfg.buildMachine.supportedFeatures;
}];
nix.buildMachines = map (m: {
hostName = m.hostName;
sshUser = m.sshUser;
sshKey = m.sshKey;
systems = m.systems;
maxJobs = m.maxJobs;
supportedFeatures = m.supportedFeatures;
}) cfg.machines;
nix.extraOptions = lib.optionalString cfg.fallbackLocal ''
builders-use-substitutes = true
fallback = true
'';
# SSH config for the remote builder (since nix.buildMachines has no port option)
programs.ssh.extraConfig = ''
Host ${cfg.buildMachine.host}
HostName ${cfg.buildMachine.host}
Port ${toString cfg.buildMachine.port}
User ${cfg.buildMachine.sshUser}
'';
# 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);
};
}