Compare commits

..

1 Commits

Author SHA1 Message Date
d0c9ae96ba feat: enable uconsole-cm5-aio-v2 module on uConsole 2026-06-21 18:39:34 +00:00
19 changed files with 242 additions and 441 deletions

View File

@@ -57,7 +57,6 @@
inherit pkgs system agenix; inherit pkgs system agenix;
}; };
############################################################################## ##############################################################################
# CROSS-COMPILE WORKAROUNDS — packages that fail aarch64 cross-compile # CROSS-COMPILE WORKAROUNDS — packages that fail aarch64 cross-compile
# #
@@ -188,12 +187,6 @@
# perl-ldap fails cross-compile (Module::Install needs dynamic loading) # perl-ldap fails cross-compile (Module::Install needs dynamic loading)
# Strip it from john deps -- the perl scripts that need it are not critical # Strip it from john deps -- the perl scripts that need it are not critical
john = prev.john.overrideAttrs (old: { john = prev.john.overrideAttrs (old: {
nativeBuildInputs = (old.nativeBuildInputs or []) ++ [
prev.buildPackages.python3 # python3 for opencl_generate_dynamic_loader.py
];
configureFlags = (old.configureFlags or []) ++ [
"ac_cv_prog_PYTHON=${prev.buildPackages.python3}/bin/python3"
];
propagatedBuildInputs = builtins.filter propagatedBuildInputs = builtins.filter
(x: x?pname && x.pname != "perl-ldap") (x: x?pname && x.pname != "perl-ldap")
(old.propagatedBuildInputs or []); (old.propagatedBuildInputs or []);
@@ -228,7 +221,7 @@
nixos-raspberrypi.lib.inject-overlays nixos-raspberrypi.lib.inject-overlays
nixos-raspberrypi.lib.inject-overlays-global nixos-raspberrypi.lib.inject-overlays-global
nixos-uconsole.nixosModules.uconsole-cm5 nixos-uconsole.nixosModules.uconsole-cm5
./modules/nixos/hardware/uConsole-aio-v2.nix ./modules/nixos/hardware/uconsole-cm5-aio-v2.nix
({ config, lib, pkgs, inputs, ... }: let ({ config, lib, pkgs, inputs, ... }: let
lixCross = import inputs.nixpkgs-uconsole { lixCross = import inputs.nixpkgs-uconsole {
localSystem = { system = "x86_64-linux"; }; localSystem = { system = "x86_64-linux"; };
@@ -239,8 +232,8 @@
inputs.home-manager.nixosModules.home-manager inputs.home-manager.nixosModules.home-manager
agenix.nixosModules.default agenix.nixosModules.default
agenix-rekey.nixosModules.default agenix-rekey.nixosModules.default
./hosts/uConsole/configuration.nix ./hosts/uconsole-cm5/configuration.nix
./hosts/uConsole/hardware-configuration.nix ./hosts/uconsole-cm5/hardware-configuration.nix
./modules/nixos/services/remote-builder.nix ./modules/nixos/services/remote-builder.nix
./modules/nixos/services/wireguard-client.nix ./modules/nixos/services/wireguard-client.nix
./modules/nixos/services/clamav.nix ./modules/nixos/services/clamav.nix
@@ -262,7 +255,6 @@
} }
inputs.home-manager.nixosModules.home-manager inputs.home-manager.nixosModules.home-manager
agenix.nixosModules.default agenix.nixosModules.default
agenix-rekey.nixosModules.default
./hosts/lazyworkhorse/configuration.nix ./hosts/lazyworkhorse/configuration.nix
./hosts/lazyworkhorse/hardware-configuration.nix ./hosts/lazyworkhorse/hardware-configuration.nix
./modules/nixos/filesystem/hoardingcow-mount.nix ./modules/nixos/filesystem/hoardingcow-mount.nix
@@ -271,12 +263,9 @@
./modules/nixos/services/ollama_init_custom_models.nix ./modules/nixos/services/ollama_init_custom_models.nix
./modules/nixos/services/open_code_server.nix ./modules/nixos/services/open_code_server.nix
./modules/nixos/services/clamav.nix ./modules/nixos/services/clamav.nix
./modules/nixos/services/wireguard-client.nix
./modules/nixos/services/remote-builder.nix
./modules/nixos/security/ai-worker-restricted.nix ./modules/nixos/security/ai-worker-restricted.nix
./users/gortium/gortium.nix ./users/gortium/gortium.nix
./users/ai-worker/ai-worker.nix ./users/ai-worker/ai-worker.nix
./users/builder.nix
]; ];
}; };
@@ -289,8 +278,6 @@
nixpkgs.hostPlatform = "aarch64-linux"; nixpkgs.hostPlatform = "aarch64-linux";
nix.package = lix.packages."aarch64-linux".default; nix.package = lix.packages."aarch64-linux".default;
} }
inputs.home-manager.nixosModules.home-manager
agenix.nixosModules.default
./hosts/cyt-pi/configuration.nix ./hosts/cyt-pi/configuration.nix
./hosts/cyt-pi/hardware-configuration.nix ./hosts/cyt-pi/hardware-configuration.nix
./modules/nixos/services/remote-builder.nix ./modules/nixos/services/remote-builder.nix
@@ -299,7 +286,7 @@
]; ];
}; };
uConsole = nixpkgs-uconsole.lib.nixosSystem { uconsole-cm5 = nixpkgs-uconsole.lib.nixosSystem {
system = "aarch64-linux"; system = "aarch64-linux";
specialArgs = { specialArgs = {
inherit self keys paths inputs; inherit self keys paths inputs;
@@ -318,7 +305,7 @@
devShells.${system}.default = devShell; devShells.${system}.default = devShell;
packages.${system} = { packages.${system} = {
uConsole-image = (nixos-raspberrypi.lib.nixosSystem { uconsole-cm5-image = (nixos-raspberrypi.lib.nixosSystem {
system = "aarch64-linux"; system = "aarch64-linux";
specialArgs = { specialArgs = {
inherit self keys inputs; inherit self keys inputs;

View File

@@ -21,7 +21,14 @@
settings.PermitRootLogin = "prohibit-password"; settings.PermitRootLogin = "prohibit-password";
}; };
# User (defined in users/gortium/gortium.nix) # User
users.users.gortium = {
isNormalUser = true;
extraGroups = [ "wheel" "networkmanager" "kismet" ];
openssh.authorizedKeys.keys = [
# Populate with your public key
];
};
# CYT Project Dependencies (Headless) # CYT Project Dependencies (Headless)
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
@@ -70,12 +77,6 @@
}; };
}; };
# Age secrets
age.secrets.gortium_password = {
file = ../../secrets/gortium_password.age;
};
# CYT Application Service # CYT Application Service
systemd.services.cyt-app = { systemd.services.cyt-app = {
description = "Chasing Your Tail - Target Detector"; description = "Chasing Your Tail - Target Detector";

View File

@@ -9,12 +9,8 @@
hoardingcow-mount.enable = true; hoardingcow-mount.enable = true;
# Flakesss # Flakesss
nix.settings.experimental-features = [ "nix-command" "flakes" "flake-self-attrs" ]; nix.settings.experimental-features = [ "nix-command" "flakes" "flake-self-attrs" "ca-derivations" ];
nix.settings.trusted-users = [ "root" "gortium" ]; nix.settings.trusted-users = [ "root" "gortium" ];
nix.settings.extra-platforms = [ "aarch64-linux" ];
# QEMU binfmt for cross-building aarch64 NixOS targets
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
# Garbage collection # Garbage collection
nix.gc = { nix.gc = {
@@ -170,8 +166,9 @@
settings = { settings = {
PasswordAuthentication = false; PasswordAuthentication = false;
KbdInteractiveAuthentication = false; KbdInteractiveAuthentication = false;
}; # ============================================================
hostKeys = [ # ClamAV antivirus — daemon, hourly updates, daily scan, on-access
# ============================================================
{ {
path = "/etc/ssh/ssh_host_ed25519_key"; path = "/etc/ssh/ssh_host_ed25519_key";
type = "ed25519"; type = "ed25519";
@@ -251,9 +248,7 @@
# Private host ssh key managed by agenix # Private host ssh key managed by agenix
age = { age = {
identityPaths = paths.identities ++ [ identityPaths = paths.identities;
"/etc/ssh/ssh_host_ed25519_key"
];
secrets = { secrets = {
containers_env = { containers_env = {
file = ../../secrets/containers.env.age; file = ../../secrets/containers.env.age;
@@ -276,6 +271,13 @@
mode = "0600"; mode = "0600";
path = "/home/ai-worker/.ssh/ai_ssh_key"; path = "/home/ai-worker/.ssh/ai_ssh_key";
}; };
openclaw_gateway_token = {
file = ../../secrets/openclaw_gateway_token.age;
owner = "root";
group = "ai-worker";
mode = "0440";
path = "/run/secrets/openclaw_gateway_token";
};
wireguard_private_key = { wireguard_private_key = {
file = ../../secrets/wireguard_private_key.age; file = ../../secrets/wireguard_private_key.age;
owner = "root"; owner = "root";
@@ -290,21 +292,17 @@
mode = "0400"; mode = "0400";
path = "/run/secrets/wireguard_preshared_key"; path = "/run/secrets/wireguard_preshared_key";
}; };
builder_key = {
file = ../../secrets/builder_key.age;
owner = "root";
group = "root";
mode = "0600";
path = "/etc/ssh/builder_key";
};
gortium_password = {
file = ../../secrets/gortium_password.age;
owner = "gortium";
group = "gortium";
mode = "0400";
path = "/run/secrets/gortium_password";
}; };
}; };
# OpenClaw Node service (host-side execution for Docker gateway)
services.openclaw-node = {
enable = true;
user = "ai-worker";
gatewayHost = "127.0.0.1";
gatewayPort = 18789;
gatewayTokenFile = "/run/secrets/openclaw_gateway_token";
displayName = "lazyworkhorse-host";
}; };
# Public host ssh key (kept in sync with the private one) # Public host ssh key (kept in sync with the private one)
@@ -569,29 +567,5 @@
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion . # 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? 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 = config.age.secrets.builder_key.path;
systems = [ "aarch64-linux" ];
maxJobs = 4;
}
];
};
# agenix-rekey — automatic re-encryption for each target host
age.rekey = {
masterIdentities = [
"/home/gortium/.ssh/gortium_ssh_key"
];
};
} }

View File

@@ -1,240 +0,0 @@
{ config, lib, pkgs, keys, ... }:
{
networking.hostName = "uConsole";
time.timeZone = "America/Montreal";
i18n.defaultLocale = "en_CA.UTF-8";
system.stateVersion = "25.11";
# Boot & Hardware
boot.loader.raspberry-pi.bootloader = "kernel";
# SSH — root access avec clés gortium + ai-worker
services.openssh = {
enable = true;
settings = {
PermitRootLogin = lib.mkForce "prohibit-password";
PasswordAuthentication = lib.mkForce false;
};
};
users.users.root.openssh.authorizedKeys.keys = with keys; [
users.gortium.main
users.ai-worker.main
];
# Age secret for gortium password (file created by user)
age.secrets.gortium_password = {
file = ../../secrets/gortium_password.age;
};
# WiFi via NetworkManager
networking.networkmanager.enable = true;
# Firmware
hardware.enableRedistributableFirmware = true;
# RealtimeKit for PipeWire audio
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
# Hyprland Wayland compositor (manual start — no SDDM)
programs.hyprland = {
enable = true;
xwayland.enable = true;
};
# HackerGadgets AIO v2 board
hardware.uconsole-cm5-aio-v2 = {
enable = true;
bootRails = {
GPS = false;
LORA = false;
SDR = false;
USB = false;
};
enableGPS = false;
};
# User
users.users.gortium = {
isNormalUser = true;
extraGroups = [ "wheel" "networkmanager" "video" "dialout" "kismet" ];
hashedPasswordFile = config.age.secrets.gortium_password.path;
openssh.authorizedKeys.keys = [
keys.users.gortium.main
keys.users.gortium.gitea
];
};
security.sudo.extraRules = [
{
users = [ "gortium" ];
commands = [{
command = "ALL";
options = [ "NOPASSWD" ];
}];
}
];
# ============================================================
# Package groups
# ============================================================
# ============================================================
# CROSS-COMPILE REMOVALS — packages removed for aarch64 bootstrap
# ============================================================
# These packages fail to cross-compile for aarch64.
# Install them natively AFTER the first successful switch.
#
# Removed: Reason:
# inspectrum — Qt5 cross-compile cascade fails (qtsvg mismatched qtbase deps)
# hashcat — Makefile calls gcc directly (cross-compiler not used)
# neovim — Same as hashcat: Makefile calls gcc directly (cross-compiler not used)
# clamav — cmake try_run + Rust proc-macro linker for aarch64
# sdrpp — glfw/wxPython cross-compile fails
# gqrx — Qt5 cross-compile cascade fails
# emacs-pgtk → emacs-nox — GTK3 + mailutils → gss → shishi chain
# viking — GTK3 GPS map editor
# foxtrotgps — GTK2 GPS app
# js8call — QtQuick3D dep
# wsjtx — qtbase/Qt5 linker fails (collect2: ld returned 1)
# fldigi — same: qtbase/Qt5 linker fails
# gpsbabel — qmake can't find cross-compiler g++
# john — configure script needs python (not in PATH during cross-compile)
# trustedqsl — needs wxWidgets (unavailable in cross-compile)
# chirp — depends on wxPython (fails cross-compile: GTK3 + wx build)
# ============================================================
environment.systemPackages = with pkgs; [
# ===== Base =====
# emacs-pgtk — removed for bootstrap (GTK3 cross-compile fails)
# emacs-nox — removed for bootstrap (depends on mailutils -> gss -> shishi, cross-compile fails)
git
ripgrep
fd
htop
tmux
# ===== HAM Radio =====
# wsjtx # removed for bootstrap - now native (native-only, needs remote build)
# fldigi # removed for bootstrap - now native (native-only, needs remote build)
pat # Winlink client
direwolf # AX.25 packet modem
# chirp # Radio programming tool - now native (native-only, needs remote build)
hamlib # Ham radio control libraries
# trustedqsl # Logbook of the World (LoTW) - now native (native-only, needs remote build)
# ===== SDR / RF =====
# sdrpp # removed for bootstrap - now native (native-only, needs remote build)
# gqrx # removed for bootstrap - now native (native-only, needs remote build)
rtl-sdr # RTL-SDR drivers & utilities
# inspectrum # removed for bootstrap - now native (native-only, needs remote build)
soapysdr-with-plugins # SoapySDR + hardware support plugins
# ===== Mesh / LoRa =====
reticulumStack # Reticulum Network Stack
lxmf # LXMF messaging protocol
nomadnet # Nomad Network client
# ===== Security =====
nmap
aircrack-ng
kismet # Wi-Fi monitor / IDS
bettercap # MITM/network attack framework
wireshark-cli # Packet analyzer
john # John the Ripper - now native
sqlmap # SQL injection tool
# ===== GPS / Maps =====
#foxtrotgps # removed for bootstrap - now native (native-only, needs remote build)
#viking # removed for bootstrap - now native (native-only, needs remote build)
#gpsbabel # GPS data conversion - now native (native-only, needs remote build)
];
# ============================================================
# Reticulum Service (rnsd)
# ============================================================
systemd.services.rnsd = {
description = "Reticulum Network Stack Daemon";
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = "gortium";
Group = "gortium";
ExecStart = "${pkgs.reticulumStack}/bin/rnsd";
Restart = "always";
RestartSec = "10s";
LimitNOFILE = 65536;
};
};
# ============================================================
# Kismet Service (Wi-Fi monitoring / mesh node)
# ============================================================
systemd.services.kismet = {
description = "Kismet Wi-Fi Monitor & IDS";
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
User = "gortium";
Group = "kismet";
ExecStart = "${pkgs.kismet}/bin/kismet -c wlan0 --log-prefix=/home/gortium/kismet_logs --no-nc-ui";
Restart = "always";
RestartSec = "10s";
};
};
# ============================================================
# Kernel modules for SDR and radio
# ============================================================
boot.kernelModules = [
"88x2bu" # Realtek 8812/8821BU USB WiFi
"rtl8xxxu" # RTL8188/8192/8723 USB WiFi
"rtl2832_sdr" # RTL-SDR kernel module
"dvb_usb_rtl28xxu" # RTL-SDR DVB-T
];
# ============================================================
# Extra udev rules for SDR and HAM radio devices
# ============================================================
services.udev.packages = with pkgs; [ rtl-sdr ];
# ============================================================
# Enable IPv6 for Reticulum mesh
# ============================================================
networking.enableIPv6 = true;
# ============================================================
# Firewall
# ============================================================
networking.firewall.allowedTCPPorts = [ 22 ];
networking.firewall.allowedUDPPorts = [ ];
# ============================================================
# Remote builder — dispatches x86_64-linux builds to lazyworkhorse
# ============================================================
services.remoteBuilder = {
enable = true;
machines = [
{
hostName = "lazyworkhorse.net";
port = 2424;
sshUser = "builder";
sshKey = config.age.secrets.builder_key.path;
systems = [ "x86_64-linux" ];
maxJobs = 36;
supportedFeatures = [ "benchmark" "big-parallel" "nixos-test" ];
}
];
};
# Age secret for builder SSH key (remote building)
age.secrets.builder_key = {
file = ../../secrets/builder_key.age;
};
# ============================================================
age.identityPaths = [
"/etc/ssh/ssh_host_ed25519_key"
];
# agenix-rekey — automatic secret re-encryption at deploy time
# ============================================================
age.rekey = {
# Master identities for encrypting secrets (on Thierry's laptop)
masterIdentities = [
"/home/gortium/.ssh/gortium_ssh_key"
];
# uConsole SSH host pubkey — for automatic rekey at build time
# Once uConsole is deployed, replace with actual pubkey from:
# ssh-keyscan uConsole.local | ssh-to-age
hostPubkey = "age1un8td4jzkhg5fm4jja7z3aznskc8hlcm8ky0j2f70tv74rulwgaqtpemn2"; # uConsole CM5 SSH host key
};
# Pipewire overlay: drop libcamera (fixes aarch64 cross-compile — rpi-pisp blocks)
nixpkgs.overlays = [
(final: prev: {
pipewire = prev.pipewire.override { libcamera = null; };
})
];
}

View File

@@ -0,0 +1,62 @@
{ config, lib, pkgs, keys, ... }:
{
networking.hostName = "uConsole";
time.timeZone = "America/Montreal";
i18n.defaultLocale = "en_CA.UTF-8";
system.stateVersion = "25.11";
# SSH — root access avec clés gortium + ai-worker
services.openssh = {
enable = true;
settings = {
PermitRootLogin = lib.mkForce "prohibit-password";
PasswordAuthentication = lib.mkForce false;
};
};
users.users.root.openssh.authorizedKeys.keys = with keys; [
users.gortium.main
users.ai-worker.main
];
# AI worker user (Hermes SSH access)
users.users.ai-worker = {
isNormalUser = false;
shell = pkgs.bash;
openssh.authorizedKeys.keys = with keys; [
users.ai-worker.main
];
};
# Age secret for gortium password (file created by user)
age.secrets.gortium_password = {
file = ../secrets/gortium_password.age;
};
# Password file for gortium (merges with users/gortium/default.nix)
# WiFi via NetworkManager + secret agenix
networking.networkmanager.enable = true;
# Firmware
hardware.enableRedistributableFirmware = true;
# Hyprland Wayland compositor (manual start — no SDDM)
programs.hyprland = {
enable = true;
xwayland.enable = true;
};
# HackerGadgets AIO v2 board — GPIO rail control, aiov2_ctl CLI
hardware.uconsole-cm5-aio-v2 = {
enable = true;
bootRails = {
GPS = false;
LORA = false;
SDR = false;
USB = false;
};
enableGPS = false;
};
}

View File

@@ -9,13 +9,6 @@
ai-worker = { ai-worker = {
main = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAXeGtPPcsP2IYRQNvII41NVWhJsarEk8c4qxs/a5sXf"; 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 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIByG79NM/rk13q3MTow9Ezb8pe3RWoQ76Mv8atASp3Ri";
};
}; };
hosts = { hosts = {
@@ -25,9 +18,5 @@
gitea = ""; gitea = "";
bootstrap = "age1r796v2uldtspawyh863pks74sd2pwcan8j4e4pjzsvkmr3vjja9qpz5ste"; bootstrap = "age1r796v2uldtspawyh863pks74sd2pwcan8j4e4pjzsvkmr3vjja9qpz5ste";
}; };
uConsole = {
main = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICasvsmaHuNVaWnjwuBfoMXyY/6PIqGL8yxstPBb46u4";
bootstrap = "age1un8td4jzkhg5fm4jja7z3aznskc8hlcm8ky0j2f70tv74rulwgaqtpemn2";
};
}; };
} }

View File

@@ -47,7 +47,6 @@ let
}; };
dontUnpack = true; dontUnpack = true;
buildInputs = [ pkgs.python3 ];
installPhase = '' installPhase = ''
mkdir -p $out/bin $out/share/aiov2_ctl/img mkdir -p $out/bin $out/share/aiov2_ctl/img

View File

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

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,11 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEdoTUQ4QSBCWEpO
cG9yNnFpcHFqTkNzTngxU1MxN0NYK0hrZFhUTjVORWFrK3JNd2tZCmtMTGpwQk1E
WlUwL3N6SGRWblpnNEkrWkkyU2hQMkRIK0M3R0pOVEREV3MKLT4gY2osLWdyZWFz
ZSBacSozVVQgUCAxRS1OQSAuKXxDPCoKbStWNW1BZjBZQzNDaTlDbU5EZkxsRWxM
cXJ3dDU1RDNpOXRlV0tzdEp2NUo3S1lhRG5Md0RHTGlJdkFSYmt5YQo4R1hiQWRG
V2VxekJKZwotLS0geG1XSi9VbkhXZHQzcEFVS3hKNzVueXFLa2xnZTc3Q2tJTVZ5
eXJabWk5Ywp6bJCP3s0xxzjE+eTR+cv7ZUnkoliT/n7uIprq1BTn/LIRLkUTUqs3
NiDwrXcoq4/QKd0Dt+8ap3vFAuusjGxRlnYMaRrZie2AGtTV8U7Q7durm9o2K+/4
QzRQ/MtumIQm
-----END AGE ENCRYPTED FILE-----

View File

@@ -12,7 +12,5 @@ in
"home_wifi.age".publicKeys = authorizedKeys; "home_wifi.age".publicKeys = authorizedKeys;
"lazyworkhorse_host_ssh_key.age".publicKeys = authorizedKeys; "lazyworkhorse_host_ssh_key.age".publicKeys = authorizedKeys;
"n8n_ssh_key.age".publicKeys = authorizedKeys; "n8n_ssh_key.age".publicKeys = authorizedKeys;
"builder_key.age".publicKeys = authorizedKeys ++ [ "openclaw_gateway_token.age".publicKeys = authorizedKeys;
keys.hosts.uConsole.bootstrap
];
} }

View File

@@ -1,13 +0,0 @@
{ config, lib, pkgs, keys, ... }: {
users.users.builder = {
isSystemUser = true;
group = "builder";
home = "/var/empty";
createHome = false;
shell = "${pkgs.shadow}/bin/nologin";
openssh.authorizedKeys.keys = with keys; [
users.builder.main
];
};
users.groups.builder = {};
}

View File

@@ -1,7 +1,5 @@
{ pkgs, inputs, config, keys, ... }: { { pkgs, inputs, config, keys, ... }: {
home-manager.extraSpecialArgs = { home-manager.extraSpecialArgs = { inherit (config.networking) hostName; dotfiles = inputs.dotfiles.outPath # flake input (not submodule); };
inherit (config.networking) hostName;
};
home-manager.users.gortium = import ./home.nix; home-manager.users.gortium = import ./home.nix;
users.users.gortium = { users.users.gortium = {
isNormalUser = true; isNormalUser = true;

View File

@@ -2,7 +2,6 @@
let let
isUconsole = hostName == "uConsole"; isUconsole = hostName == "uConsole";
dotfiles = ../../assets/dotfiles;
in { in {
home.username = "gortium"; home.username = "gortium";
home.homeDirectory = "/home/gortium"; home.homeDirectory = "/home/gortium";
@@ -34,9 +33,6 @@ in {
".config/wofi/config".source = "${dotfiles}/wofi/.config/wofi/config"; ".config/wofi/config".source = "${dotfiles}/wofi/.config/wofi/config";
# yazi # yazi
# wallpapers
".config/wallpapers".source = "${dotfiles}/wallpapers/.config/wallpapers";
".config/yazi/yazi.toml".source = "${dotfiles}/yazi/.config/yazi/yazi.toml"; ".config/yazi/yazi.toml".source = "${dotfiles}/yazi/.config/yazi/yazi.toml";
# hyprland — common config # hyprland — common config
@@ -47,45 +43,22 @@ in {
".config/hypr/mocha.conf".source = "${dotfiles}/hypr/.config/hypr/mocha.conf"; ".config/hypr/mocha.conf".source = "${dotfiles}/hypr/.config/hypr/mocha.conf";
# hyprland — host-specific monitor config # hyprland — host-specific monitor config
".config/hypr/hosts/monitors.conf".source = ".config/hypr/host/monitors.conf".source =
if isUconsole if isUconsole
then "${dotfiles}/hypr/.config/hypr/hosts/uconsole.conf" then "${dotfiles}/hypr/.config/hypr/hosts/uconsole.conf"
else "${dotfiles}/hypr/.config/hypr/hosts/laptop.conf"; else "${dotfiles}/hypr/.config/hypr/hosts/laptop.conf";
}; };
programs.bash.enable = true;
programs.zsh = {
enable = true;
initExtra = builtins.readFile "${dotfiles}/zsh/.zshrc";
};
home.packages = with pkgs; [ home.packages = with pkgs; [
git zsh tmux starship git zsh tmux starship
neovim kitty neovim kitty
btop yazi ripgrep fd fzf btop yazi ripgrep fd fzf
htop unzip wget jq ] ++ lib.optionals (!isUconsole) [
hyprland hyprlock hypridle hyprpaper waybar wofi swww hyprshot
waybar wofi dunst ] ++ lib.optionals isUconsole [
libnotify mako brightnessctl
swaynotificationcenter
swww
emacs
udiskie
hyprshade
networkmanagerapplet
pavucontrol
]; ];
xdg.userDirs = { programs.zsh.enable = true;
enable = true; programs.starship.enable = true;
createDirectories = true;
desktop = "$HOME/desktop";
documents = "$HOME/documents";
download = "$HOME/downloads";
music = "$HOME/music";
pictures = "$HOME/pictures";
publicShare = "$HOME/public";
templates = "$HOME/templates";
videos = "$HOME/videos";
};
} }