feat(core): initialize NaxOS declarative NixOS distribution, modules, and ISO installer
Test NaxOS Module Configurations / test-modules (push) Failing after 6m10s

This commit is contained in:
Lukas Holzner
2026-09-03 23:53:18 +02:00
parent 88a638955b
commit 5da58ae6d7
28 changed files with 2247 additions and 2 deletions
+111
View File
@@ -0,0 +1,111 @@
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.services.naxos.gitops;
# Safe switch script with canary validation and automatic rollback
naxosRebuildWrapper = pkgs.writeShellScriptBin "naxos-rebuild-safe" ''
set -euo pipefail
CONFIG_DIR="''${CONFIG_DIR:-/etc/naxos/repo}"
LOG_FILE="/var/log/naxos-rebuild.log"
echo "[$(date -Iseconds)] Starting NaxOS declarative rebuild..." | tee -a "$LOG_FILE"
cd "$CONFIG_DIR"
# Pre-flight check with nix flake check / nixos-rebuild dry-build
echo "Running dry build validation..."
if ! nixos-rebuild build --flake .#naxos 2>&1 | tee -a "$LOG_FILE"; then
echo "Dry-build failed! Configuration aborted without changing running system." | tee -a "$LOG_FILE"
exit 1
fi
# Record current generation
PREV_GEN=$(readlink -f /nix/var/nix/profiles/system)
echo "Applying system switch..."
if nixos-rebuild switch --flake .#naxos 2>&1 | tee -a "$LOG_FILE"; then
echo "System switch succeeded." | tee -a "$LOG_FILE"
# Canary health check: verify management daemon and storage pools are responsive
if systemctl is-active --quiet naxos-api.service; then
echo "Canary verification passed: NaxOS daemon active." | tee -a "$LOG_FILE"
exit 0
else
echo "WARNING: NaxOS daemon failed canary test! Initiating safe automatic rollback..." | tee -a "$LOG_FILE"
"$PREV_GEN/bin/switch-to-configuration" switch
exit 2
fi
else
echo "Switch command failed! Rolling back to $PREV_GEN..." | tee -a "$LOG_FILE"
"$PREV_GEN/bin/switch-to-configuration" switch
exit 3
fi
'';
# Automated remote push/pull service
naxosGitSync = pkgs.writeShellScriptBin "naxos-git-sync" ''
set -euo pipefail
REPO_DIR="/etc/naxos/repo"
if [ ! -d "$REPO_DIR/.git" ]; then
echo "Git repository not initialized in $REPO_DIR."
exit 0
fi
cd "$REPO_DIR"
if [ -n "''${NAXOS_REMOTE_URL:-}" ]; then
echo "Synchronizing with remote GitOps repository..."
git fetch origin main || true
git push origin main || true
fi
'';
in {
options.services.naxos.gitops = {
enable = mkEnableOption "NaxOS GitOps Configuration Synchronization Engine";
remoteUrl = mkOption {
type = types.nullOr types.str;
default = null;
example = "ssh://git@git.lholz.de:2222/naxos/naxos-config.git";
description = "Remote Git repository URL for automated backup, auditing, and disaster recovery.";
};
branch = mkOption {
type = types.str;
default = "main";
description = "GitOps target branch.";
};
tokenFile = mkOption {
type = types.nullOr types.str;
default = null;
description = "Path to Gitea/Git access token or SSH private key.";
};
autoPushOnCommit = mkOption {
type = types.bool;
default = true;
description = "Automatically push to remote repository whenever web dashboard commits a change.";
};
};
config = mkIf cfg.enable {
environment.systemPackages = with pkgs; [
git
naxosRebuildWrapper
naxosGitSync
];
# Create config repo directory structure
systemd.tmpfiles.rules = [
"d /etc/naxos 0755 root root -"
"d /etc/naxos/repo 0750 root root -"
"d /var/log 0755 root root -"
];
};
}
+77
View File
@@ -0,0 +1,77 @@
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.services.naxos.selfUpdate;
updateScript = pkgs.writeShellScriptBin "naxos-self-update" ''
set -euo pipefail
CONFIG_DIR="''${CONFIG_DIR:-/etc/naxos/repo}"
LOG_FILE="/var/log/naxos-update.log"
echo "[$(date -Iseconds)] Checking for NaxOS updates..." | tee -a "$LOG_FILE"
if [ ! -d "$CONFIG_DIR/.git" ]; then
echo "Configuration directory is not a git repository. Skipping." | tee -a "$LOG_FILE"
exit 0
fi
cd "$CONFIG_DIR"
git fetch origin main
LOCAL_HASH=$(git rev-parse HEAD)
REMOTE_HASH=$(git rev-parse origin/main)
if [ "$LOCAL_HASH" = "$REMOTE_HASH" ]; then
echo "System is already up to date ($LOCAL_HASH)." | tee -a "$LOG_FILE"
exit 0
fi
echo "New updates detected ($LOCAL_HASH -> $REMOTE_HASH). Updating flake inputs and pulling..." | tee -a "$LOG_FILE"
git merge origin/main --ff-only
echo "Rebuilding and applying configuration..." | tee -a "$LOG_FILE"
naxos-rebuild-safe
'';
in {
options.services.naxos.selfUpdate = {
enable = mkEnableOption "NaxOS Automated Background Self-Update Service";
schedule = mkOption {
type = types.str;
default = "*-*-* 04:00:00"; # Daily at 4:00 AM
description = "Systemd calendar expression for scheduled update checks.";
};
channel = mkOption {
type = types.enum [ "stable" "beta" "nightly" ];
default = "stable";
description = "Update release channel.";
};
};
config = mkIf cfg.enable {
environment.systemPackages = [ updateScript ];
systemd.services.naxos-self-update = {
description = "NaxOS Self-Update Check & Apply";
path = [ pkgs.git pkgs.nix pkgs.systemd pkgs.bash ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${updateScript}/bin/naxos-self-update";
};
};
systemd.timers.naxos-self-update = {
description = "NaxOS Scheduled Self-Update Timer";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = cfg.schedule;
Persistent = true;
RandomizedDelaySec = "1800"; # 30 min random jitter
};
};
};
}
+96
View File
@@ -0,0 +1,96 @@
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.services.naxos.core;
in {
options.services.naxos.core = {
enable = mkEnableOption "NaxOS Core Appliance Base";
hostName = mkOption {
type = types.str;
default = "naxos";
description = "Appliance hostname.";
};
hostId = mkOption {
type = types.str;
default = "8425f3a1";
description = "32-bit Host ID required for OpenZFS safety locking.";
};
timeZone = mkOption {
type = types.str;
default = "Europe/Berlin";
description = "System timezone.";
};
};
config = mkIf cfg.enable {
networking.hostName = cfg.hostName;
networking.hostId = cfg.hostId;
time.timeZone = cfg.timeZone;
# Flakes and Nix CLI enablement
nix.settings = {
experimental-features = [ "nix-command" "flakes" ];
auto-optimise-store = true;
};
# High-Performance Storage & Network Kernel Tuning
boot.kernel.sysctl = {
# BBR Congestion Control
"net.core.default_qdisc" = "fq";
"net.ipv4.tcp_congestion_control" = "bbr";
# High-bandwidth 10G/25G network buffer tuning
"net.core.rmem_max" = 67108864;
"net.core.wmem_max" = 67108864;
"net.ipv4.tcp_rmem" = "4096 87380 33554432";
"net.ipv4.tcp_wmem" = "4096 65536 33554432";
"net.core.netdev_max_backlog" = 10000;
# Storage & VM writeback tuning for ZFS
"vm.swappiness" = 10;
"vm.dirty_background_ratio" = 5;
"vm.dirty_ratio" = 10;
# File handles limit
"fs.file-max" = 2097152;
};
# Core system tools
environment.systemPackages = with pkgs; [
curl
wget
git
htop
btop
tmux
jq
pciutils
usbutils
ethtool
iperf3
rsync
];
# Security & Firewall defaults
networking.firewall = {
enable = true;
allowPing = true;
allowedTCPPorts = [ 22 80 443 ];
};
# SSH Server with modern secure defaults
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "prohibit-password";
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
};
};
};
}
+80
View File
@@ -0,0 +1,80 @@
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.services.naxos.users;
in {
options.services.naxos.users = {
enable = mkEnableOption "NaxOS Declarative User Management";
adminUser = mkOption {
type = types.str;
default = "admin";
description = "Primary administrator account name.";
};
adminSshKeys = mkOption {
type = types.listOf types.str;
default = [];
description = "Public SSH keys for the administrator.";
};
users = mkOption {
type = types.attrsOf (types.submodule {
options = {
description = mkOption {
type = types.str;
default = "";
description = "User full name or comment.";
};
isAdmin = mkOption {
type = types.bool;
default = false;
description = "Whether the user has sudo/wheel privileges.";
};
sshKeys = mkOption {
type = types.listOf types.str;
default = [];
description = "Public SSH authorized keys.";
};
extraGroups = mkOption {
type = types.listOf types.str;
default = [];
description = "Additional Linux groups.";
};
smbAccess = mkOption {
type = types.bool;
default = true;
description = "Whether the user can access SMB shares.";
};
};
});
default = {};
description = "Appliance user accounts.";
};
};
config = mkIf cfg.enable {
users.mutableUsers = true;
# Admin user creation
users.users.${cfg.adminUser} = {
isNormalUser = true;
description = "NaxOS Primary Administrator";
extraGroups = [ "wheel" "docker" "video" "render" "users" ];
openssh.authorizedKeys.keys = cfg.adminSshKeys;
shell = pkgs.bashInteractive;
};
# Additional users
users.users = mapAttrs (name: ucfg: {
isNormalUser = true;
description = ucfg.description;
extraGroups = (if ucfg.isAdmin then [ "wheel" ] else []) ++ ucfg.extraGroups ++ [ "users" ];
openssh.authorizedKeys.keys = ucfg.sshKeys;
}) cfg.users;
security.sudo.wheelNeedsPassword = false;
};
}