Compare commits

..

44 Commits

Author SHA1 Message Date
ca3faf40cf Empty initrd for display, add rp1_dsi module
ALL RP1 hardware (gpio, clocks, dsi) sits behind the RP1 PCIe
southbridge, which isn't ready until ~12s. Loading any display
module in initrd (~3s) crashes the system. Nothing in initrd now:
- boot.initrd.kernelModules: empty (nothing touches RP1 early)
- boot.kernelModules: vc4, panel_cwu50, rp1_dsi (stage-2)
2026-06-09 23:14:44 -04:00
4f1864af70 Revert vc4/panel_cwu50 to boot.kernelModules (stage-2 loading)
Loading via boot.kernelModules ensures they're loaded after
initrd, by which time RP1 PCIe southbridge is initialized (~12s).
This avoids the 'Cannot find any crtc' error from premature probe.

- boot.kernelModules: explicit vc4 + panel_cwu50 (stage-2)
- boot.initrd.kernelModules: only ocp8178_bl (backlight)
2026-06-09 22:46:43 -04:00
67418f037a Rex timing: remove vc4/panel_cwu50 from boot.kernelModules too
Let udev auto-load display modules when RP1 PCIe southbridge
appears on the bus (~12s). This is exactly how Rex's Debian
image works — no forced loading in initrd or stage-2.

- boot.initrd.kernelModules: only ocp8178_bl (backlight only)
- boot.kernelModules: no display modules (udev handles it)
- SDDM disabled (Hyprland kept for manual start)
2026-06-09 22:44:45 -04:00
fb01e4be1d SDDM disabled, Hyprland kept (manual start only)
- Removed SDDM display manager (was blocking boot when display fails)
- Kept Hyprland installed for manual start (hyprland command)
- Display modules load in stage-2 via boot.kernelModules (Rex timing)
2026-06-09 22:39:13 -04:00
f57f2846ff Fix duplicate boot.kernelModules, merge vc4/panel_cwu50 into existing list 2026-06-09 22:20:27 -04:00
288b7538d5 Fix CM5 display: delay vc4/panel_cwu50 to after RP1 PCIe init
Loading display drivers in initrd (~3s) before RP1 PCIe southbridge
is ready (~12s) causes 'Cannot find any crtc or sizes' because the
DSI encoder isn't reachable. Rex loads these via udev late in boot.

- boot.initrd.kernelModules: only ocp8178_bl (backlight, RP1-independent)
- boot.kernelModules: vc4 + panel_cwu50 loaded in stage 2 after RP1
2026-06-09 21:42:24 -04:00
fb7c3e6424 Fix RNS setup.py license_files tuple for setuptools 80.x 2026-06-09 20:49:16 -04:00
6961027218 Fix reticulum build: add sourceRoot for PyPI package 2026-06-09 20:47:45 -04:00
8bf44a066e Add nixpkgs-uconsole pinned to commit that base image used
The base image (nixos-uconsole v1.1.0) was built with nixpkgs
commit 54170c54449ea4d6725efd30d719c5e505f1c10e (2026-04-10),
NOT the flake.lock pin (89dbf01df7 from Dec 2025). By pinning to
the exact commit the image used, the kernel derivation hash should
match what's already in the store.
2026-06-09 20:38:05 -04:00
644c9a7645 uConsole: use mkUConsoleSystem instead of manual nixosSystem
Using nixos-uconsole.lib.mkUConsoleSystem ensures the exact same
module composition as the pre-built image, so the kernel derivation
hash matches what'\''s already in the store (6.12.47).
2026-06-09 20:16:31 -04:00
52fa439409 Align pins with nixos-uconsole v1.1.0 for kernel cache hit
- nixos-uconsole: main -> v1.1.0 (tag)

- nixos-raspberrypi: v1.20260517.0 -> v1.20260317.0 (matches nixos-uconsole v1.1.0's flake.lock)

- Keeps nixos-raspberrypi.inputs.nixpkgs.follows = nixos-uconsole/nixpkgs

This ensures the kernel derivation hash matches the pre-built image's kernel already in the uConsole store.
2026-06-09 19:52:40 -04:00
db2deda941 fix(uconsole): align nixos-raspberrypi nixpkgs with nixos-uconsole
Make nixos-raspberrypi follow nixos-uconsole's nixpkgs so the kernel
uses the same nixpkgs that the uconsole Cachix was built with.
2026-06-09 19:34:31 -04:00
7f7634c1b1 fix(uconsole): stop following nixpkgs for nixos-uconsole and nixos-raspberrypi
Remove inputs.nixpkgs.follows so these inputs use their own pinned nixpkgs.
This lets the uConsole build use the nixos-uconsole Cachix cache for the kernel,
avoiding 4h kernel recompilation on every config change.
2026-06-09 19:23:53 -04:00
1fa1cfaa76 fix(uconsole): apply CM5 display fixes from nixos-uconsole comparison
- Add hardware.graphics.enable = true (Mesa GPU required for VC4)
- Add boot.kernelParams with console=tty1 (console on fb0 not ttyAMA0)
- Fix console.font to ter-v24n with proper package (match nixos-uconsole)
- Merge GPIO 10/11 from nixos-uconsole extra-config (audio amp)
- Bump stateVersion to 25.11
2026-06-08 23:40:19 -04:00
4d2cba77e1 fix(display): use [pi5] config.txt section instead of [cm5]
Rex's Trixie images use [pi5] for the CM5 display overlays
(clockworkpi-uconsole-cm5, vc4-kms-v3d-pi5). The nixos-uconsole
module generates [cm5] which the firmware may not apply on
some CM5 EEPROM configurations. Adding explicit [pi5] section
ensures the display overlays are always loaded.
2026-06-07 22:49:01 -04:00
8d54e5e8fa fix(display): use kernel default console font instead of ter-v24n
The ter-v24n Terminus font renders garbled on CM5 DSI 720x1280
panel (green stretched line across screen). Switching to the
kernel's built-in Lat2-Terminus16 font for correct text rendering.
2026-06-07 22:10:46 -04:00
42e5d4dd2d fix(uconsole): disable GPIO 23 service (causes CM5 freeze) + open SSH
GPIO 23 service used gpioset 0 23=1 on BCM2712 chip 0 instead
of the RP1 chip (base 512), writing to a critical pin and
freezing the system. Commented out until correct chip is confirmed.
SSH opened with PasswordAuthentication + PermitRootLogin for
first deployment. Lock down after initial boot.
2026-06-07 21:57:13 -04:00
682402e0e6 fix(uconsole): use nixpkgs default nix instead of lix dev
lix dev (20260606) is incompatible with nixpkgs 25.11 stable.
Still on other hosts. This avoids the lix build failure.
2026-06-07 10:08:35 -04:00
9c4b50b4c3 fix(config): remove meshtastic from packages — not in nixpkgs 25.11 2026-06-07 06:47:32 -04:00
8e395729ff fix(overlay): avoid recursion with final.lib, use prev.lib.optionalAttrs
Replace recursive `final.lib.optionalAttrs` with `prev.lib.optionalAttrs`
using the `?` operator to check for meshtastic existence safely.
This allows the overlay to work with both stable (25.11) and unstable.
2026-06-07 06:45:16 -04:00
346b41995f fix(overlay): make meshtastic conditional for stable nixpkgs (25.11)
meshtastic package doesn't exist in nixpkgs 25.11 stable.
Use optionalAttrs to skip the override when not available,
allowing the flake to build against both stable and unstable.
2026-06-07 06:43:09 -04:00
68900ca7b3 fix: switch to nixpkgs 25.11 stable to match pre-built uConsole kernel
The uConsole CM5 has kernel 6.12.47 in its store from the
nixos-uconsole-cm5-v1.1.0 image (built with nixpkgs 25.11).
By switching to the same nixpkgs version, Nix will reuse
the existing kernel instead of rebuilding it from source.
2026-06-07 06:37:25 -04:00
38eea77fd9 fix(uconsole): remove useless udev rule for voltage_min_design
sysfs node is read-only (0444), even root cannot write.
The AXP228 PMU manages power path in hardware.
Practical fix: disconnect batteries during heavy builds.
2026-06-06 23:01:14 -04:00
b7b5ef0b53 fix(uconsole): lower voltage_min_design to 2.7V to prevent random power-off under heavy CPU load 2026-06-06 22:54:59 -04:00
f0954efcaa fix(sddm): enable wayland support for Hyprland 2026-06-06 21:29:51 -04:00
b0be414649 revert(nixos-uconsole): follow unstable nixpkgs again
Cachix has no CM5 kernel anyway, so sticking to stable doesn't
save build time. Might as well stay on unstable for latest pkgs.
2026-06-06 21:27:47 -04:00
31dd0f36d4 feat(uconsole): add Hyprland + SDDM display manager
Enables Hyprland Wayland compositor with XWayland support
and SDDM display manager for graphical desktop on the uConsole CM5.
2026-06-06 21:23:15 -04:00
653c69fcfd fix(flake): detach nixos-uconsole nixpkgs from unstable to hit Cachix
nixos-uconsole's Cachix cache is built with nixpkgs-stable (25.11).
Following our unstable caused a full kernel rebuild every time.
By using nixos-uconsole's pinned nixpkgs, future builds will
download the pre-compiled kernel from the cache instead of
compiling it locally on the CM5.
2026-06-06 21:07:37 -04:00
a9b95c5d48 fix(config): use libgpiod instead of gpiod for gpioset
The nixpkgs attribute for userspace GPIO tools is 'libgpiod',
not 'gpiod'. This provides the gpioset binary used by the
GPIO 23 internal USB hub service.
2026-06-06 17:27:12 -04:00
6771c9882a fix(hw-config): use mkForce for filesystems to avoid disko conflict
Disko auto-generates fileSystems with by-partlabel paths, but
for manual install via loop devices we need by-label paths.
mkForce ensures our paths win during evaluation.
2026-06-06 17:23:50 -04:00
897f470a16 fix(disko): use /dev/mmcblk0 instead of wrong by-path
platform-fe340000.mmc doesn't exist on the uConsole CM5.
The eMMC is at /dev/mmcblk0 in normal boot mode.
2026-06-06 16:51:46 -04:00
eaf879c4d1 fix(disko-config): use disko.devices.disk instead of top-level disk
disko module defines options under `disko.devices.disk.*`, not
`disk.*`. This was causing evaluation error:
"The option 'disk' does not exist. Did you mean 'disko'?"
2026-06-06 16:39:46 -04:00
486758e51a feat(uConsole): add disko, backlight fix, GPIO 23, mt7921u
- Add disko flake input + partition config (/boot/firmware, /, /home)
- Add cm5-backlight-fix service as display fallback
- Add enable-gpio23-usb-hub service for internal USB hub
- Add mt7921u kernel module for MediaTek AC1200 WiFi
- Add gpiod package for GPIO userspace control
2026-06-06 16:38:41 -04:00
34cc0a161a fix: override meshtastic to skip runtime deps check (tabulate 0.10.0 incompatibility) 2026-06-06 10:17:04 -04:00
a51e095717 feat: enable aarch64 cross-build on lazyworkhorse (QEMU binfmt + extra-platforms) 2026-06-06 09:16:23 -04:00
9ebbb1c0c6 fix: bump nixos-raspberrypi to v1.20260517.0 (matches nixos-uconsole tested version) 2026-06-05 23:38:21 -04:00
7f11da1878 fix: let nixos-raspberrypi manage kernel version (patches incompatible with linuxPackages_latest) 2026-06-05 23:33:10 -04:00
29cc20bb04 fix: add wants=network-online.target to rnsd and kismet services to silence eval warnings 2026-06-05 22:58:09 -04:00
1617ac9149 fix: migrate from deprecated kernelboot to kernel bootloader for nixos-raspberrypi 2026-06-05 22:57:26 -04:00
24f15c98cd fix: add format=setuptools to all reticulum overlay python packages 2026-06-05 22:46:54 -04:00
bdd6d03739 fix: use mkForce for PermitRootLogin to override upstream module default 2026-06-05 22:45:59 -04:00
a0a6663793 fix: use mkForce for PasswordAuthentication to override upstream module default 2026-06-05 22:45:30 -04:00
b66ffadb79 fix: add missing 'keys' to uConsole module args 2026-06-05 22:43:53 -04:00
db2bd1d157 feat: add uConsole CM5 host configuration with Reticulum mesh stack
- New NixOS host 'uConsole' for ClockworkPi CM5 portable terminal
- flake.nix: add nixos-uconsole and nixos-raspberrypi inputs
- Imports: nixos-uconsole.nixosModules.uconsole-cm5,
  nixos-raspberrypi.nixosModules.raspberry-pi-5.base
- Full package list: base tools, HAM radio, SDR/RF, mesh/LoRa,
  security tools, GPS/maps
- Reticulum stack (rns 1.2.9, lxmf 0.9.8, nomadnet 1.1.1) built
  from PyPI via overlays/reticulum.nix
- systemd services: rnsd (Reticulum daemon), kismet (Wi-Fi IDS)
- Kernel modules for SDR (rtl-sdr, dvb) and USB WiFi
- Follows existing host config conventions (cyt-pi as template)
2026-05-20 14:34:15 -04:00
29 changed files with 663 additions and 1954 deletions

4
.gitmodules vendored
View File

@@ -1,7 +1,3 @@
[submodule "assets/compose"]
path = assets/compose
url = ssh://git@code.lazyworkhorse.net:2222/gortium/compose.git
[submodule "assets/dotfiles"]
path = assets/dotfiles
url = ssh://git@code.lazyworkhorse.net:2222/gortium/dotfiles.git
branch = master

Submodule assets/dotfiles deleted from f45387456b

106
assets/ollama/Dockerfile Normal file
View File

@@ -0,0 +1,106 @@
# ollama-gfx906/Dockerfile
#
# Custom ollama image with ROCm 6.1 + gfx906 (MI50) support.
# The official ollama/rocm image ships ROCm 7.2 which dropped gfx906.
# This uses v0.23.2's native CMake build system with AMDGPU_TARGETS including gfx906.
#
# Build: docker build -t ollama/ollama:rocm-gfx906 ai/ollama
FROM rocm/dev-ubuntu-22.04:6.1.2-complete AS builder
# Build dependencies (CMake, Ninja, Go)
ARG CMAKEVERSION=3.31.2
ARG NINJAVERSION=1.12.1
ARG GOLANG_VERSION=1.22.0
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
curl git ccache build-essential pkg-config unzip \
&& rm -rf /var/lib/apt/lists/*
# Install CMake from official binaries
RUN curl -fsSL https://github.com/Kitware/CMake/releases/download/v${CMAKEVERSION}/cmake-${CMAKEVERSION}-linux-x86_64.tar.gz \
| tar xz -C /usr/local --strip-components 1
# Install Ninja
RUN curl -fsSL -o /tmp/ninja.zip \
https://github.com/ninja-build/ninja/releases/download/v${NINJAVERSION}/ninja-linux.zip \
&& unzip /tmp/ninja.zip -d /usr/local/bin && rm /tmp/ninja.zip
# Install Go
RUN curl -fsSL https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz \
| tar xz -C /usr/local
ENV PATH=/usr/local/go/bin:$PATH
ARG OLLAMA_VERSION=v0.23.2
RUN git clone --depth 1 --branch ${OLLAMA_VERSION} https://github.com/ollama/ollama.git /build
WORKDIR /build
# ROCm paths
ENV HIP_PATH=/opt/rocm
ENV ROCM_PATH=/opt/rocm
ENV CMAKE_GENERATOR=Ninja
ENV LDFLAGS=-s
# Step 1: Build CPU backends with GCC (no ROCm preset)
# Pre-set CMAKE_HIP_COMPILER="" to prevent check_language(HIP) from
# finding a HIP compiler (it searches /opt/rocm even without PATH).
# Remove /opt/rocm from PATH to prevent find_program from finding hipcc.
RUN mkdir -p build-cpu && \
PATH=/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
cmake -B build-cpu -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_HIP_COMPILER="" \
-DCMAKE_INSTALL_PREFIX=/build/dist && \
cmake --build build-cpu --target ggml-cpu -- -l $(nproc) && \
cmake --install build-cpu --component CPU --strip && \
echo "=== CPU install ===" && \
(find /build/dist/lib/ollama -type f -o -type l 2>&1 | head -20 || echo "empty")
# Step 2: Build HIP backend with ROCm preset + gfx906 target only
# The ROCm 6 preset enables HIP language detection (enable_language(HIP))
# which ensures GPU kernels are properly compiled for gfx906.
# OLLAMA_RUNNER_DIR=rocm from the preset, so HIP goes to lib/ollama/rocm/
# Need CMAKE_PREFIX_PATH so find_package(hip) finds hip-config.cmake
# at /opt/rocm/lib/cmake/hip/hip-config.cmake.
RUN mkdir -p build-hip && \
cmake -B build-hip \
--preset 'ROCm 6' \
-DAMDGPU_TARGETS="gfx906:xnack-" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH="/opt/rocm" && \
cmake --build build-hip --target ggml-hip -- -l $(nproc) && \
cmake --install build-hip --component HIP --strip && \
echo "=== HIP install ===" && \
find /build/dist/lib/ollama -type f -o -type l | head -20
# Step 3: Build Go binary (GCC for CGo linking)
ENV CGO_ENABLED=1
RUN go build -trimpath -ldflags="-X=github.com/ollama/ollama/version.Version=${OLLAMA_VERSION}" -o /build/dist/ollama .
# ---------- Runtime image ----------
FROM ubuntu:24.04
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
ca-certificates curl libstdc++6 libgomp1 libvulkan1 libopenblas0 \
&& rm -rf /var/lib/apt/lists/*
# Copy ROCm 6.1 runtime libraries
# These are needed at runtime by ggml-hip via LD_LIBRARY_PATH
COPY --from=builder /opt/rocm/lib/ /opt/rocm/lib/
COPY --from=builder /opt/rocm/share/ /opt/rocm/share/
# Copy ollama binary + all backends (CPU + HIP)
# CPU install: /build/dist/lib/ollama/libggml-*.so
# HIP install: /build/dist/lib/ollama/rocm/libggml-hip.so
COPY --from=builder /build/dist/ollama /usr/bin/ollama
COPY --from=builder /build/dist/lib/ollama/ /usr/lib/ollama/
RUN ldconfig
ENV LD_LIBRARY_PATH=/opt/rocm/lib:/usr/lib/ollama/rocm:/usr/lib/ollama
ENV HSA_OVERRIDE_GFX_VERSION=9.0.6
ENV HCC_AMDGPU_TARGET=gfx906
ENV HSA_ENABLE_SDMA=0
EXPOSE 11434
ENTRYPOINT ["/bin/ollama"]
CMD ["serve"]

301
flake.lock generated
View File

@@ -23,84 +23,7 @@
"type": "github"
}
},
"agenix-rekey": {
"inputs": {
"devshell": "devshell",
"flake-parts": "flake-parts",
"nixpkgs": [
"nixpkgs"
],
"pre-commit-hooks": "pre-commit-hooks",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1774522439,
"narHash": "sha256-GvINrdGznE7mGlDNjW0/PMgOJlC+Nl9MkfxALB4QvWs=",
"owner": "oddlama",
"repo": "agenix-rekey",
"rev": "8b9c179bc1300ab130c90f2d25426bf0e7a2b58d",
"type": "github"
},
"original": {
"owner": "oddlama",
"repo": "agenix-rekey",
"type": "github"
}
},
"argononed": {
"flake": false,
"locked": {
"lastModified": 1729566243,
"narHash": "sha256-DPNI0Dpk5aym3Baf5UbEe5GENDrSmmXVdriRSWE+rgk=",
"owner": "nvmd",
"repo": "argononed",
"rev": "16dbee54d49b66d5654d228d1061246b440ef7cf",
"type": "github"
},
"original": {
"owner": "nvmd",
"repo": "argononed",
"type": "github"
}
},
"devshell": {
"inputs": {
"nixpkgs": [
"agenix-rekey",
"nixpkgs"
]
},
"locked": {
"lastModified": 1728330715,
"narHash": "sha256-xRJ2nPOXb//u1jaBnDP56M7v5ldavjbtR6lfGqSvcKg=",
"owner": "numtide",
"repo": "devshell",
"rev": "dd6b80932022cea34a019e2bb32f6fa9e494dfef",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1751685974,
@@ -114,64 +37,6 @@
"url": "https://git.lix.systems/lix-project/flake-compat/archive/main.tar.gz"
}
},
"flake-compat_3": {
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"agenix-rekey",
"nixpkgs"
]
},
"locked": {
"lastModified": 1733312601,
"narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"agenix-rekey",
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
@@ -193,37 +58,16 @@
"type": "github"
}
},
"home-manager_2": {
"inputs": {
"nixpkgs": [
"nixpkgs-uconsole"
]
},
"locked": {
"lastModified": 1779506708,
"narHash": "sha256-QOD/CNm196nCJRheux/URi4/HE66fthdOMqCJoPP1Y0=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "3ee51fbdac8c8bdfe1e7e1fcaba6520a563f394f",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "release-25.11",
"repo": "home-manager",
"type": "github"
}
},
"lix": {
"inputs": {
"flake-compat": "flake-compat_2",
"flake-compat": "flake-compat",
"nix2container": "nix2container",
"nix_2_18": "nix_2_18",
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-regression": "nixpkgs-regression",
"pre-commit-hooks": "pre-commit-hooks_2"
"pre-commit-hooks": "pre-commit-hooks"
},
"locked": {
"lastModified": 1774721317,
@@ -300,80 +144,6 @@
"type": "github"
}
},
"nixos-images": {
"inputs": {
"nixos-stable": [
"nixos-raspberrypi",
"nixpkgs"
],
"nixos-unstable": [
"nixos-raspberrypi",
"nixpkgs"
]
},
"locked": {
"lastModified": 1747747741,
"narHash": "sha256-LUOH27unNWbGTvZFitHonraNx0JF/55h30r9WxqrznM=",
"owner": "nvmd",
"repo": "nixos-images",
"rev": "cbbd6db325775096680b65e2a32fb6187c09bbb4",
"type": "github"
},
"original": {
"owner": "nvmd",
"ref": "sdimage-installer",
"repo": "nixos-images",
"type": "github"
}
},
"nixos-raspberrypi": {
"inputs": {
"argononed": "argononed",
"flake-compat": "flake-compat_3",
"nixos-images": "nixos-images",
"nixpkgs": [
"nixpkgs-uconsole"
]
},
"locked": {
"lastModified": 1781324200,
"narHash": "sha256-JWqxN2Yle86+4Q+GFh12SvB92ZyLeqalVsN9lfMh6eQ=",
"owner": "gortium",
"repo": "nixos-raspberrypi",
"rev": "721a6e9e67dca3a23133db650b87018646bca3e6",
"type": "github"
},
"original": {
"owner": "gortium",
"ref": "cm5-cross-v1",
"repo": "nixos-raspberrypi",
"type": "github"
}
},
"nixos-uconsole": {
"inputs": {
"nixos-raspberrypi": [
"nixos-raspberrypi"
],
"nixpkgs": [
"nixpkgs-uconsole"
]
},
"locked": {
"lastModified": 1781476310,
"narHash": "sha256-jY6ujqLXNAWJGvt+pAuw1Wg/OiHRGd1B1Z7Czhiq7Q4=",
"owner": "gortium",
"repo": "nixos-uconsole",
"rev": "38a7fcbffbf2d2e122bc1e1c634fe25f66ecda13",
"type": "github"
},
"original": {
"owner": "gortium",
"ref": "pr/dcs-panel-detection",
"repo": "nixos-uconsole",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1705033721,
@@ -406,22 +176,6 @@
"type": "github"
}
},
"nixpkgs-uconsole": {
"locked": {
"lastModified": 1780952837,
"narHash": "sha256-Fwd1+spDtQ0hDyBwme6ufG3n4mY0UrjjFdYHv+G/Hds=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e820eb4a444b46a19b2e03e8dfd2359439ff30fe",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1774386573,
@@ -439,29 +193,6 @@
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"agenix-rekey",
"nixpkgs"
]
},
"locked": {
"lastModified": 1735882644,
"narHash": "sha256-3FZAG+pGt3OElQjesCAWeMkQ7C/nB1oTHLRQ8ceP110=",
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "a5a961387e75ae44cc20f0a57ae463da5e959656",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"pre-commit-hooks_2": {
"flake": false,
"locked": {
"lastModified": 1769939035,
@@ -480,13 +211,8 @@
"root": {
"inputs": {
"agenix": "agenix",
"agenix-rekey": "agenix-rekey",
"home-manager": "home-manager_2",
"lix": "lix",
"nixos-raspberrypi": "nixos-raspberrypi",
"nixos-uconsole": "nixos-uconsole",
"nixpkgs": "nixpkgs_2",
"nixpkgs-uconsole": "nixpkgs-uconsole"
"nixpkgs": "nixpkgs_2"
}
},
"systems": {
@@ -503,27 +229,6 @@
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"agenix-rekey",
"nixpkgs"
]
},
"locked": {
"lastModified": 1735135567,
"narHash": "sha256-8T3K5amndEavxnludPyfj3Z1IkcFdRpR23q+T0BVeZE=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "9e09d30a644c57257715902efbb3adc56c79cf28",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",

282
flake.nix Normal file → Executable file
View File

@@ -2,44 +2,34 @@
description = "Gortium infra flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
nixpkgs.url = "github:nixos/nixpkgs?ref=25.11";
agenix = {
url = "github:ryantm/agenix";
inputs.darwin.follows = "";
inputs.nixpkgs.follows = "nixpkgs";
};
agenix-rekey = {
url = "github:oddlama/agenix-rekey";
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
lix = {
url = "git+https://git.lix.systems/lix-project/lix?ref=main";
inputs.nixpkgs.follows = "nixpkgs";
};
nixpkgs-uconsole.url = "github:NixOS/nixpkgs/nixos-25.11";
nixpkgs-uconsole = {
url = "github:nixos/nixpkgs/54170c54449ea4d6725efd30d719c5e505f1c10e";
};
nixos-uconsole = {
url = "github:gortium/nixos-uconsole/pr/dcs-panel-detection";
url = "github:nixos-uconsole/nixos-uconsole/v1.1.0";
inputs.nixpkgs.follows = "nixpkgs-uconsole";
inputs.nixos-raspberrypi.follows = "nixos-raspberrypi";
};
nixos-raspberrypi = {
url = "github:gortium/nixos-raspberrypi/cm5-cross-v1";
inputs.nixpkgs.follows = "nixpkgs-uconsole";
};
home-manager = {
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs-uconsole";
};
dotfiles = {
url = "git+https://code.lazyworkhorse.net/gortium/dotfiles.git";
flake = false;
url = "github:nvmd/nixos-raspberrypi/v1.20260317.0";
inputs.nixpkgs.follows = "nixos-uconsole/nixpkgs";
};
};
outputs = { self, nixpkgs, agenix, agenix-rekey, lix
, nixpkgs-uconsole, nixos-uconsole, nixos-raspberrypi, dotfiles
, home-manager
, ... }@inputs:
outputs = { self, nixpkgs, agenix, disko, lix, nixos-uconsole, nixos-raspberrypi, ... }@inputs:
let
system = "x86_64-linux";
keys = import ./lib/keys.nix;
@@ -54,197 +44,16 @@
pkgs = import nixpkgs {
inherit system overlays;
config.allowUnfree = true;
config.permittedInsecurePackages = [ "openclaw-2026.3.12" ];
config.permittedInsecurePackages = [
"openclaw-2026.3.12"
];
};
devShell = import ./shells/nix_dev.nix {
inherit pkgs system agenix;
};
##############################################################################
# CROSS-COMPILE WORKAROUNDS — packages that fail aarch64 cross-compile
#
# These packages need NATIVE COMPILATION on the uConsole itself (aarch64).
# They cannot cross-compile from x86_64 for various reasons listed below.
# We work around them in the overlay until we set up distributed builds
# with the uConsole as a native aarch64 builder.
#
# ==== Cross-compile failures ====
#
# libcamera / libcamera-rpi / libpisp:
# meta.platforms excludes aarch64. pipewire hard-depends on them in nixos-25.11.
# Fix: empty meta.platforms + strip from pipewire buildInputs.
#
# gjs:
# Need native display (GTK3/4 tests) for cross-compile configure.
# Fix: meson -Dskip_gtk_tests=true.
#
# hyprland:
# Qt6Quick missing from aarch64 qtdeclarative, breaks hyprland-qt-support.
# Fix: wrapRuntimeDeps=false (Qt UI components disabled, WM still works).
#
# boost.mpi:
# Boost.Build has no b2 architecture alternatives for ARM.
# Fix: useMpi=false.
#
# perl-ldap (perlPackages.perlldap):
# Module::Install requires Perl dynamic loading (Fcntl) which is
# unavailable in cross-compiled Perl.
# Fix: stripped from john.s propagatedBuildInputs.
#
# john (John the Ripper):
# Indirectly affected — depends on perl-ldap for perl utility scripts.
# Fix: perl-ldap stripped from propagatedBuildInputs (john still works,
# just loses sha-dump.pl etc. LDAP support).
#
# gss (GNU Generic Security Service):
# autogen.sh fails cross-compile. Pulled by mailutils → emacs-pgtk.
# Fix: emacs withMailutils=false.
#
# emacs-pgtk:
# Indirectly affected — depends on mailutils which depends on gss.
# Fix: withMailutils=false (no mail/IMAP within emacs).
#
# qtquick3d (Qt6):
# Qt::Quick not available in aarch64 cross-compile qtdeclarative.
# cmake skips build, ninja has no install target.
# Fix: removed js8call, switched wireshark → wireshark-cli.
#
# js8call:
# REMOVED from system packages. Depends on Qt6 multimedia → qtquick3d.
#
# wireshark-qt:
# SWITCHED to wireshark-cli. Same Qt6 multimedia → qtquick3d chain.
#
# neovim:
# `libnlua0.so` built for aarch64, luajit (x86_64) tries to load it
# during codegen (preload_nlua.lua). No clean override option.
# Fix: remove from system packages + install via native build
# once uConsole is set up as remote builder.
#
# clamav:
# cmake try_run() + Rust proc-macro can't find native linker in
# cross-compile (cc crate uses cross CC, no cc in PATH for build
# scripts). Chain: clamav → system-path → etc → dbus → polkit.
# Fix: remove from system packages; clamscan available from server.
#
# ==== Remote builder setup (bidirectional) — TODO ====
# To eliminate cross-compile exceptions, set up distributed builds:
# 1. Create a dedicated `builder` user on both hosts (no shell, home=/var/empty)
# 2. Add the same SSH key to both hosts (symmetric)
# 3. On lazyworkhorse — `nix.buildMachines` pointing to uConsole for aarch64-linux
# 4. On uConsole — `nix.buildMachines` pointing to lazyworkhorse for x86_64-linux
# 5. Remove the uconsoleCrossOverlay workarounds above
# 6. Nix auto-dispatches derivations by `system` — no per-package exceptions needed
# Example buildMachines config:
# Server dispatches aarch64 builds to uConsole (4 cores, less power):
# nix.buildMachines = [{
# hostName = "uConsole.local";
# systems = ["aarch64-linux"];
# maxJobs = 4;
# sshUser = "builder";
# sshKey = "/etc/ssh/builder_key";
# }];
# uConsole dispatches x86_64 builds to server (36 cores, 256GB RAM):
# nix.buildMachines = [{
# hostName = "lazyworkhorse.net";
# port = 2424;
# systems = ["x86_64-linux"];
# maxJobs = 36;
# sshUser = "builder";
# sshKey = "/etc/ssh/builder_key";
# }];
# ==== How to build natively on uConsole ====
# To native-compile these on the uConsole:
# 1. Add uConsole as a remote builder (nix.buildMachines)
# 2. Set nix.extra-platforms = [ "aarch64-linux" ] on server
# 3. Remove the overlay workarounds below
# 4. Packages will auto-dispatch to uConsole for native builds
##############################################################################
uconsoleCrossOverlay = final: prev: {
libcamera = prev.libcamera.overrideAttrs (_: { meta.platforms = []; });
libcamera-rpi = prev.libcamera-rpi.overrideAttrs (_: { meta.platforms = []; });
libpisp = prev.libpisp.overrideAttrs (_: { meta.platforms = []; });
pipewire = prev.pipewire.overrideAttrs (old: {
buildInputs = builtins.filter
(x: !(x?pname && x.pname == "libcamera"))
(old.buildInputs or []);
mesonFlags = builtins.filter
(flag: !(builtins.isString flag && builtins.match ".*libcamera.*" flag != null))
(old.mesonFlags or []) ++ [ "-Dlibcamera=disabled" ];
});
gjs = prev.gjs.overrideAttrs (old: {
mesonFlags = (old.mesonFlags or []) ++ [ "-Dskip_gtk_tests=true" ];
});
hyprland = prev.hyprland.override { wrapRuntimeDeps = false; };
boost = prev.boost.override { useMpi = false; };
# perl-ldap cannot cross-compile (Module::Install needs dynamic loading)
xdg-desktop-portal-hyprland = prev.xdg-desktop-portal-hyprland.overrideAttrs (old: {
preConfigure = (old.preConfigure or "") + ''
cmakeFlags="$cmakeFlags -Dhyprwayland-scanner_DIR=${prev.buildPackages.hyprwayland-scanner}/lib/cmake/hyprwayland-scanner" 2>/dev/null || true
export PKG_CONFIG_PATH="${prev.buildPackages.hyprwayland-scanner}/lib/pkgconfig:$PKG_CONFIG_PATH"
'';
});
emacs-pgtk = prev.emacs-pgtk.override { withMailutils = false; };
# perl-ldap fails cross-compile (Module::Install needs dynamic loading)
# Strip it from john deps -- the perl scripts that need it are not critical
john = prev.john.overrideAttrs (old: {
propagatedBuildInputs = builtins.filter
(x: x?pname && x.pname != "perl-ldap")
(old.propagatedBuildInputs or []);
});
# clamav: removed from system packages (see note above).
};
uconsoleRpiPipewireOverlay = final: prev: {
pipewire = prev.pipewire.overrideAttrs (old: {
buildInputs = builtins.filter
(x: !(x?pname && x.pname == "libcamera"))
(old.buildInputs or []);
mesonFlags = builtins.filter
(flag: !(builtins.isString flag && builtins.match ".*libcamera.*" flag != null))
(old.mesonFlags or []) ++ [ "-Dlibcamera=disabled" ];
});
};
uconsoleBaseModules = [
in
{
nixpkgs.buildPlatform = "x86_64-linux";
nixpkgs.hostPlatform = "aarch64-linux";
nixpkgs.config.allowUnfree = true;
boot.loader.raspberry-pi.bootloader = "kernel";
nixpkgs.overlays = [ uconsoleCrossOverlay (import ./overlays/reticulum.nix) ];
}
nixos-raspberrypi.nixosModules.nixpkgs-rpi
({ config, lib, pkgs, ... }: {
nixpkgs.overlays = [ uconsoleRpiPipewireOverlay ];
})
nixos-raspberrypi.nixosModules.raspberry-pi-5.base
nixos-raspberrypi.lib.inject-overlays
nixos-raspberrypi.lib.inject-overlays-global
nixos-uconsole.nixosModules.uconsole-cm5
./modules/nixos/hardware/uconsole-cm5-aio-v2.nix
({ config, lib, pkgs, inputs, ... }: let
lixCross = import inputs.nixpkgs-uconsole {
localSystem = { system = "x86_64-linux"; };
crossSystem = { system = "aarch64-linux"; };
overlays = [ inputs.lix.overlays.default ];
};
in { nix.package = lixCross.lix; })
inputs.home-manager.nixosModules.home-manager
agenix.nixosModules.default
agenix-rekey.nixosModules.default
./hosts/uconsole-cm5/configuration.nix
./hosts/uconsole-cm5/hardware-configuration.nix
./modules/nixos/services/remote-builder.nix
./modules/nixos/services/wireguard-client.nix
./modules/nixos/services/clamav.nix
./modules/nixos/security/ai-worker-restricted.nix
./users/gortium/gortium.nix
./users/ai-worker/ai-worker.nix
];
in {
nixosConfigurations = {
lazyworkhorse = nixpkgs.lib.nixosSystem {
specialArgs = { inherit system self keys paths inputs; };
@@ -253,22 +62,22 @@
nixpkgs.overlays = overlays;
nixpkgs.config.allowUnfree = true;
nixpkgs.config.rocmSupport = true;
nixpkgs.config.permittedInsecurePackages = [ "openclaw-2026.3.12" ];
nixpkgs.config.permittedInsecurePackages = [
"openclaw-2026.3.12"
];
nix.package = lix.packages.${system}.default;
}
inputs.home-manager.nixosModules.home-manager
agenix.nixosModules.default
./hosts/lazyworkhorse/configuration.nix
./hosts/lazyworkhorse/hardware-configuration.nix
./modules/nixos/filesystem/hoardingcow-mount.nix
./modules/nixos/services/docker_manager.nix
./modules/nixos/filesystem/poup-16t-disk.nix
./modules/nixos/services/ollama_init_custom_models.nix
./modules/nixos/services/open_code_server.nix
./modules/nixos/services/clamav.nix
./modules/nixos/services/ollama_init_custom_models.nix
./modules/nixos/services/openclaw_node.nix
./modules/nixos/security/ai-worker-restricted.nix
./users/gortium/gortium.nix
./users/ai-worker/ai-worker.nix
./users/gortium.nix
./users/ai-worker.nix
];
};
@@ -283,42 +92,27 @@
}
./hosts/cyt-pi/configuration.nix
./hosts/cyt-pi/hardware-configuration.nix
./modules/nixos/services/remote-builder.nix
./modules/nixos/services/wireguard-client.nix
./users/gortium/gortium.nix
];
};
uconsole-cm5 = nixpkgs-uconsole.lib.nixosSystem {
system = "aarch64-linux";
specialArgs = {
inherit self keys paths inputs;
nixos-raspberrypi = nixos-raspberrypi;
isCM4 = false;
};
modules = uconsoleBaseModules;
uConsole = nixos-uconsole.lib.mkUConsoleSystem {
variant = "cm5";
specialArgs = { inherit self keys paths inputs nixos-raspberrypi; };
modules = [
{
nixpkgs.overlays = overlays;
nixpkgs.config.allowUnfree = true;
nixpkgs.config.permittedInsecurePackages = [
"openclaw-2026.3.12"
];
}
disko.nixosModules.disko
./hosts/uConsole/configuration.nix
./hosts/uConsole/hardware-configuration.nix
./hosts/uConsole/disko-config.nix
];
};
};
agenix-rekey = agenix-rekey.configure {
userFlake = self;
nixosConfigurations = self.nixosConfigurations;
};
devShells.${system}.default = devShell;
packages.${system} = {
uconsole-cm5-image = (nixos-raspberrypi.lib.nixosSystem {
system = "aarch64-linux";
specialArgs = {
inherit self keys inputs;
nixos-raspberrypi = nixos-raspberrypi;
isCM4 = false;
};
modules = uconsoleBaseModules ++ [
nixos-raspberrypi.nixosModules.sd-image
];
}).config.system.build.sdImage;
};
};
}

View File

@@ -50,7 +50,7 @@
User = "gortium";
Group = "kismet";
ExecStart = ''
${pkgs.kismet}/bin/kismet -c panda --log-prefix=/home/gortium/kismet_logs --no-nc-ui
${pkgs.kismet}/bin/kismet -c panda --log-base=/home/gortium/kismet_logs --no-nc-ui
'';
Restart = "always";
RestartSec = "10s";

View File

@@ -9,8 +9,12 @@
hoardingcow-mount.enable = true;
# Flakesss
nix.settings.experimental-features = [ "nix-command" "flakes" "flake-self-attrs" "ca-derivations" ];
nix.settings.experimental-features = [ "nix-command" "flakes" "flake-self-attrs" ];
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
nix.gc = {
@@ -49,12 +53,24 @@
networking.networkmanager.enable = true; # Easiest to use and most distros use this by default.
networking.hostId = "deadbeef";
# WireGuard VPN client -- module, always up, connects to wg-easy server
gortium.wireguard-client = {
enable = true;
vpnIp = "10.8.0.3/24";
# WireGuard VPN client -- always up, connects to wg-easy server
# Create age-encrypted secrets before deploying (run on the host):
# echo -n "<private_key>" | agenix -e secrets/wireguard_private_key.age
# echo -n "<preshared_key>" | agenix -e secrets/wireguard_preshared_key.age
networking.wireguard.interfaces = {
wg0 = {
ips = [ "10.8.0.3/24" ];
privateKeyFile = config.age.secrets.wireguard_private_key.path;
peers = [
{
publicKey = "rY9zII3AOm8rog2rv02PyA3Bq7zdvTOGkZapfCV1DkE=";
presharedKeyFile = config.age.secrets.wireguard_preshared_key.path;
allowedIPs = [ "10.8.0.0/24" ];
endpoint = "vpn.lazyworkhorse.net:51820";
persistentKeepalive = 25;
}
];
};
};
# Set your time zone.
@@ -166,9 +182,9 @@
settings = {
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
# ============================================================
# ClamAV antivirus — daemon, hourly updates, daily scan, on-access
# ============================================================
# Additional hardening settings below in SERVER HARDENING section
};
hostKeys = [
{
path = "/etc/ssh/ssh_host_ed25519_key";
type = "ed25519";
@@ -337,16 +353,6 @@
# networking.firewall.enable = false;
# =============================================================================
# ============================================================
# ClamAV antivirus — daemon, hourly updates, daily scan, on-access
# ============================================================
gortium.clamav = {
enable = true;
enableDaemon = true;
onAccessScanning = true;
dailyScanTime = "03:00";
};
# SERVER HARDENING - Firewall, Fail2ban, SSH, Kernel
# =============================================================================

286
hosts/uConsole/configuration.nix Executable file
View File

@@ -0,0 +1,286 @@
{ config, lib, pkgs, paths, self, keys, ... }:
let
# Backlight fallback for CM5 display quirk
# The kernel driver usually handles this, but some boots need a kick
backlightFixScript = pkgs.writeShellScript "backlight-fix" ''
# Try sysfs backlight control
for bl in /sys/class/backlight/*/brightness; do
if [ -f "$bl" ]; then
max=$(cat "$(dirname "$bl")/max_brightness" 2>/dev/null || echo 100)
echo "$max" > "$bl" 2>/dev/null || true
fi
done
'';
in
{
# Basic Host Info
networking.hostName = "uConsole";
time.timeZone = "America/Montreal";
i18n.defaultLocale = "en_CA.UTF-8";
# System State
system.stateVersion = "25.11";
# Boot & Hardware (migrated to kernel bootloader per nixos-raspberrypi deprecation notice)
boot.loader.raspberry-pi.bootloader = "kernel";
# kernel managed by nixos-raspberrypi module — don't override, patches are version-specific
# boot.kernelPackages = pkgs.linuxPackages_latest;
# Kernel parameters matching nixos-uconsole CM5 module
# console=tty1 is critical — without it, console output goes to ttyAMA0 not fb0
boot.kernelParams = [
"8250.nr_uarts=1"
"console=tty1"
];
# Enable Mesa GPU drivers — REQUIRED for VC4 display pipeline to initialize
hardware.graphics.enable = true;
# Console font sized for the 5" 720x1280 display (from nixos-uconsole base module)
console = {
earlySetup = true;
font = "ter-v24n";
packages = with pkgs; [ terminus_font ];
};
# Networking
networking.networkmanager.enable = true;
services.openssh = {
enable = true;
# TODO: lock down after first deployment
settings.PermitRootLogin = lib.mkForce "yes";
settings.PasswordAuthentication = lib.mkForce true;
};
# User
users.users.gortium = {
isNormalUser = true;
extraGroups = [ "wheel" "networkmanager" "video" "dialout" "kismet" ];
openssh.authorizedKeys.keys = [
keys.users.gortium.main
keys.users.gortium.gitea
];
};
security.sudo.extraRules = [
{
users = [ "gortium" ];
commands = [
{
command = "ALL";
options = [ "NOPASSWD" ];
}
];
}
];
# ============================================================
# Package groups
# ============================================================
environment.systemPackages = with pkgs; [
# ===== Base =====
emacs-pgtk
git
ripgrep
fd
htop
tmux
neovim
libgpiod # GPIO control (for internal USB hub, AIO modules)
# ===== HAM Radio =====
js8call
wsjtx
fldigi
pat # Winlink client
direwolf # AX.25 packet modem
chirp # Radio programming tool
hamlib # Ham radio control libraries
trustedqsl # Logbook of the World (LoTW)
# ===== SDR / RF =====
sdrpp # SDR++ spectrum analyzer
gqrx # SDR receiver GUI
rtl-sdr # RTL-SDR drivers & utilities
inspectrum # Offline signal analysis
soapysdr-with-plugins # SoapySDR + hardware support plugins
# ===== Mesh / LoRa =====
# meshtastic not available in nixpkgs 25.11 stable; install manually:
# nix shell nixpkgs#meshtastic -c meshtastic
reticulumStack # Reticulum Network Stack (rnsd, rnsh, rncp, rnx, rnpath, etc.)
lxmf # LXMF messaging protocol
nomadnet # Nomad Network client
# ===== Security =====
nmap
aircrack-ng
kismet # Wi-Fi monitor / IDS
bettercap # MITM/network attack framework
wireshark # Packet analyzer
hashcat # GPU password cracker
john # John the Ripper
sqlmap # SQL injection tool
# ===== GPS / Maps =====
foxtrotgps
viking # GPS map editor
gpsbabel # GPS data conversion
];
# Packages noted but not in unstable nixpkgs:
# - metasploit: unfree; install manually via Git clone
# - burpsuite: unfree Java app (Community Edition available for download)
# - sidechannel: not a distinct PyPI package; functionality covered by
# the Reticulum stack. For LXMF GUI client, install Sideband manually
# from github.com/markqvist/Sideband
# ============================================================
# 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-base=/home/gortium/kismet_logs --no-nc-ui";
Restart = "always";
RestartSec = "10s";
};
};
# ============================================================
# Kernel modules for SDR, radio, and WiFi
# ============================================================
boot.kernelModules = [
"mt7921u" # MediaTek MT7921 USB WiFi (uConsole AC1200)
"88x2bu" # Realtek 8812/8821BU USB WiFi (common adapter)
"rtl8xxxu" # RTL8188/8192/8723 USB WiFi
"rtl2832_sdr" # RTL-SDR kernel module
"dvb_usb_rtl28xxu" # RTL-SDR DVB-T
# Display drivers — loaded AFTER RP1 PCIe southbridge init (~12s)
# NOTHING in initrd — ALL RP1 hardware is behind PCIe
"panel_cwu50" # uConsole DSI panel driver
"vc4" # VideoCore 4 KMS GPU driver
"rp1_dsi" # RP1 DSI bridge driver
];
boot.blacklistedKernelModules = [ ];
# Rien dans initrd pour le display — tout RP1 est derrière PCIe
boot.initrd.kernelModules = lib.mkForce [ ];
# ============================================================
# 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: open ports for Reticulum (optional)
# ============================================================
networking.firewall.allowedTCPPorts = [ 22 ]; # SSH only
networking.firewall.allowedUDPPorts = [ ];
# 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.
# ============================================================
# Hyprland Wayland compositor (manual start)
# No SDDM — boot to console, user starts Hyprland with command
# Display modules (vc4/panel_cwu50) load late after RP1 PCIe init
# ============================================================
programs.hyprland = {
enable = true;
xwayland.enable = true;
};
# SDDM disabled — was blocking boot when display isn't ready
# services.displayManager.sddm = {
# enable = true;
# wayland.enable = true;
# };
# ============================================================
# CM5 Config.txt Fix: use [pi5] section (not [cm5])
# Rex's images use [pi5], the CM5 firmware may not detect [cm5]
# ============================================================
# Merge nixos-uconsole GPIO config with our [pi5] overrides
# GPIO 10/11 are from nixos-uconsole configtxt.nix (audio amplifier)
# [pi5] section fixes the CM5 detection issue — firmware matches [pi5] not [cm5]
hardware.raspberry-pi.extra-config = ''
[all]
gpio=10=ip,np
gpio=11=op,dh
[pi5]
dtparam=pciex1=off
dtoverlay=clockworkpi-uconsole-cm5
dtoverlay=dwc2,dr_mode=host
dtoverlay=vc4-kms-v3d-pi5,cma-384
dtparam=nohdmi1=off
'';
# ============================================================
# CM5 Display Backlight Fix
# The kernel driver initializes backlight, but some boots fail.
# This service kicks it after boot as a reliable fallback.
# ============================================================
systemd.services.cm5-backlight-fix = {
description = "CM5 Display Backlight Fix";
after = [ "multi-user.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${backlightFixScript}";
};
};
# ============================================================
# Internal USB Hub Enable (GPIO 23) — DISABLED
# This service freeze the CM5 because gpioset 0 23=1 writes
# to the wrong GPIO chip (BCM2712 native, not RP1).
# Enable manually after boot once the correct chip is confirmed:
# gpioset 0 23=1 # on chip 0 (BCM2712, CORE_VOLT or critical)
# gpioset 512 23=1 # on chip 512 (RP1, likely correct)
# ============================================================
# systemd.services.enable-gpio23-usb-hub = {
# description = "Enable Internal USB Hub (GPIO 23)";
# before = [ "network.target" ];
# wantedBy = [ "multi-user.target" ];
# serviceConfig = {
# Type = "oneshot";
# RemainAfterExit = true;
# ExecStart = "${pkgs.libgpiod}/bin/gpioset 0 23=1";
# ExecStop = "${pkgs.libgpiod}/bin/gpioset 0 23=0";
# };
# };
}

View File

@@ -0,0 +1,46 @@
{ lib, ... }:
{
disko.devices.disk.main = {
type = "disk";
device = "/dev/mmcblk0";
content = {
type = "gpt";
partitions = {
boot = {
name = "FIRMWARE";
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot/firmware";
mountOptions = [
"fmask=0022"
"dmask=0022"
];
};
};
root = {
name = "NIXOS_UCM5";
size = "30G";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
mountOptions = [ "noatime" ];
};
};
home = {
name = "NIXOS_HOME";
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/home";
mountOptions = [ "noatime" ];
};
};
};
};
};
}

View File

@@ -0,0 +1,39 @@
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[ (modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "usbhid" "usb_storage" "sdhci_pci" "nvme" ];
boot.initrd.kernelModules = [ ];
boot.extraModulePackages = [ ];
# Filesystems for NixOS install.
# mkForce overrides disko's auto-generated paths so we can use
# filesystem labels (by-label) which work with loop device installs.
# Disko will set its own paths when nixos-anywhere is used.
fileSystems."/" = lib.mkForce {
device = "/dev/disk/by-label/NIXOS_UCM5";
fsType = "ext4";
options = [ "noatime" ];
};
fileSystems."/boot/firmware" = lib.mkForce {
device = "/dev/disk/by-label/FIRMWARE";
fsType = "vfat";
options = [ "fmask=0022" "dmask=0022" ];
};
fileSystems."/home" = lib.mkForce {
device = "/dev/disk/by-label/NIXOS_HOME";
fsType = "ext4";
options = [ "noatime" ];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
hardware.enableRedistributableFirmware = true;
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
}

View File

@@ -1,206 +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;
# 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 aarch64 bootstrap (qtbase/Qt5 cross-compile linker fails)
# fldigi — removed for aarch64 bootstrap (qtbase/Qt5 cross-compile linker fails)
pat # Winlink client
direwolf # AX.25 packet modem
# chirp # Radio programming tool
hamlib # Ham radio control libraries
# trustedqsl # Logbook of the World (LoTW)
# ===== SDR / RF =====
# sdrpp — removed for aarch64 cross-compile bootstrap (glfw/wxPython fails)
# gqrx — removed for aarch64 cross-compile bootstrap (Qt5 cascade fails)
rtl-sdr # RTL-SDR drivers & utilities
# inspectrum # removed for aarch64 bootstrap (Qt5 cross-compile cascade fails)
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
sqlmap # SQL injection tool
# ===== GPS / Maps =====
# foxtrotgps — removed for aarch64 cross-compile bootstrap (GTK2 fails)
# viking — removed for aarch64 cross-compile bootstrap (GTK3 fails)
# gpsbabel # GPS data conversion
];
# ============================================================
# 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 = [ ];
# ============================================================
# 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 = "age1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3290gq"; # dummy — replace after bootstrap
};
# Pipewire overlay: drop libcamera (fixes aarch64 cross-compile — rpi-pisp blocks)
nixpkgs.overlays = [
(final: prev: {
pipewire = prev.pipewire.override { libcamera = null; };
})
];
}

View File

@@ -1,30 +0,0 @@
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "usbhid" "usb_storage" "sdhci_pci" "nvme" ];
boot.initrd.kernelModules = [ ];
boot.extraModulePackages = [ ];
# SD card partitions (nixos-uconsole layout)
fileSystems."/" = {
device = "/dev/disk/by-label/NIXOS_SD";
fsType = "ext4";
options = [ "noatime" ];
};
fileSystems."/boot/firmware" = {
device = "/dev/disk/by-label/FIRMWARE";
fsType = "vfat";
options = [ "fmask=0022" "dmask=0022" ];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
hardware.enableRedistributableFirmware = true;
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
}

View File

@@ -1,121 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.gortium.poup16t;
luksName = cfg.luksName;
in
with lib;
{
options.gortium.poup16t = {
enable = mkEnableOption "Poup_16T storage disk (btrfs + LUKS + btrbk snapshots)";
luksUuid = mkOption {
type = types.str;
description = ''
UUID of the LUKS partition on the 16TB disk (WD Red Pro).
Find this by running as root when the disk is connected:
blkid /dev/sdb # or wherever the disk appears
lsblk -o NAME,SIZE,FSTYPE,UUID
Since btrfs is inside LUKS, the FS UUID is hidden use the
LUKS partition UUID from blkid (it'll show TYPE=\"crypto_LUKS\").
'';
example = "00000000-0000-0000-0000-000000000000";
};
luksName = mkOption {
type = types.str;
default = "poup_16t";
description = "Name for the LUKS /dev/mapper/ mapping";
};
mountPoint = mkOption {
type = types.str;
default = "/mnt/Poup_16T";
description = "Mount point for the 16TB data disk";
};
btrfsOptions = mkOption {
type = types.listOf types.str;
default = [ "defaults" "noatime" "compress=zstd:3" "nofail" ];
description = "Mount options for the btrfs filesystem. 'nofail' ensures boot succeeds when disk is disconnected.";
};
btrbk = {
enable = mkOption {
type = types.bool;
default = true;
description = "Enable btrbk snapshot management on this volume";
};
schedule = mkOption {
type = types.str;
default = "daily";
description = "systemd calendar event for btrbk (e.g. 'daily', 'hourly', '*-*-* 00:00:00')";
};
preserveMin = mkOption {
type = types.str;
default = "2d";
description = "btrbk snapshot_preserve_min minimum age before pruning";
};
preserve = mkOption {
type = types.str;
default = "14d 4w 3m";
description = "btrbk snapshot_preserve retention policy (daily, weekly, monthly)";
};
snapshotDir = mkOption {
type = types.str;
default = ".snapshots";
description = "Directory name for snapshots relative to volume root";
};
};
};
config = mkIf cfg.enable {
# Enable btrfs kernel support (no DKMS needed — it's in-tree)
boot.supportedFilesystems = [ "btrfs" ];
# Install btrfs administration tools
environment.systemPackages = with pkgs; [
btrfs-progs # mkfs.btrfs, btrfs, fsck, balance, scrub
btrbk # Snapshot management + rotation
];
# LUKS2 unlock at boot (uses keyfile or prompts if unavailable)
# Since the disk may be disconnected, initrd times out gracefully (~30s)
boot.initrd.luks.devices.${luksName} = {
device = "/dev/disk/by-uuid/${cfg.luksUuid}";
preLVM = false;
allowDiscards = true;
};
# Mount the unlocked mapper device as btrfs
fileSystems.${cfg.mountPoint} = {
device = "/dev/mapper/${luksName}";
fsType = "btrfs";
options = cfg.btrfsOptions;
};
# btrbk — automated snapshot creation and rotation
services.btrbk = mkIf cfg.btrbk.enable {
instances.poup16t = {
onCalendar = cfg.btrbk.schedule;
settings = {
snapshot_preserve_min = cfg.btrbk.preserveMin;
snapshot_preserve = cfg.btrbk.preserve;
volume.${cfg.mountPoint} = {
snapshot_create = "always";
snapshot_dir = cfg.btrbk.snapshotDir;
subvolume = ".";
};
};
};
};
};
}

View File

@@ -1,169 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.hardware.uconsole-cm5-aio-v2;
# GPIO pin map matching the AIO v2 board hardware
# SDR (RTL-SDR): GPIO 7
# LoRa (SX1262) : GPIO 16
# USB Hub Interne: GPIO 23
# GPS (GNSS) : GPIO 27
gpioMap = {
GPS = 27;
LORA = 16;
SDR = 7;
USB = 23;
};
# Generate a script that applies boot rail states via pinctrl
applyRailsScript = pkgs.writeShellScript "apply-aio-v2-rails" (
''
set -e
PINCTRL=${pkgs.raspberrypi-utils}/bin/pinctrl
''
+ concatStringsSep "" (mapAttrsToList (name: pin: ''
if [ "${if cfg.bootRails.${name} then "1" else "0"}" = "1" ]; then
echo "AIO v2: ${name} (GPIO${toString pin}) -> ON"
$PINCTRL set ${toString pin} op dh
else
echo "AIO v2: ${name} (GPIO${toString pin}) -> OFF"
$PINCTRL set ${toString pin} op dl
fi
'') gpioMap)
);
# aiov2_ctl CLI tool -- fetched from GitHub, available as `aiov2_ctl`
aiov2CtlPkg = pkgs.stdenv.mkDerivation rec {
pname = "aiov2_ctl";
version = "0-unstable-2026-06-16";
src = pkgs.fetchFromGitHub {
owner = "hackergadgets";
repo = "aiov2_ctl";
rev = "main";
hash = "sha256-hqOvS1K5pDVXAroUE50i5R9YqRgC2U3fzby6uuB67K0=";
};
dontUnpack = true;
installPhase = ''
mkdir -p $out/bin $out/share/aiov2_ctl/img
cp $src/aiov2_ctl.py $out/bin/aiov2_ctl
chmod +x $out/bin/aiov2_ctl
patchShebangs $out/bin/aiov2_ctl
substituteInPlace $out/bin/aiov2_ctl \
--replace-fail '"/usr/local/share/aiov2_ctl/img/' '"'$out'/share/aiov2_ctl/img/'
cp -r $src/img/* $out/share/aiov2_ctl/img/
'';
meta = {
description = "HackerGadgets uConsole AIO v2 GPIO control and telemetry tool";
homepage = "https://github.com/hackergadgets/aiov2_ctl";
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ ];
platforms = [ "aarch64-linux" ];
};
};
in {
options.hardware.uconsole-cm5-aio-v2 = {
enable = mkEnableOption "HackerGadgets uConsole AIO v2 board support";
bootRails = {
GPS = mkOption {
type = types.bool;
default = false;
description = "Enable GPS module at boot (GPIO 27)";
};
LORA = mkOption {
type = types.bool;
default = false;
description = "Enable LoRa module at boot (GPIO 16)";
};
SDR = mkOption {
type = types.bool;
default = false;
description = "Enable SDR module at boot (GPIO 7)";
};
USB = mkOption {
type = types.bool;
default = false;
description = "Enable internal USB hub at boot (GPIO 23)";
};
};
package = mkOption {
type = types.package;
default = aiov2CtlPkg;
defaultText = literalExpression "aiov2CtlPkg";
description = "aiov2_ctl package to use";
};
enableGPS = mkOption {
type = types.bool;
default = false;
description = ''
Enable GPS UART (/dev/ttyAMA0 at 9600 baud).
Requires enabling UART on the CM5 via boot.kernelParams.
'';
};
enableGUI = mkOption {
type = types.bool;
default = false;
description = ''
Enable the system tray GUI for aiov2_ctl.
Requires a desktop environment with system tray support.
'';
};
};
config = mkIf cfg.enable {
# Package the aiov2_ctl tool + pinctrl
environment.systemPackages = with pkgs; [
cfg.package
raspberrypi-utils # provides pinctrl
];
# Boot rail systemd oneshot service
systemd.services.aiov2-rails-boot = {
description = "Apply AIO v2 GPIO rail boot states";
after = [ "local-fs.target" ];
wants = [ "local-fs.target" ];
before = [ "multi-user.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${applyRailsScript}";
RemainAfterExit = true;
};
};
# GPS configuration
boot.kernelParams = mkIf cfg.enableGPS [ "uart0=on" ];
users.users = mkIf cfg.enableGPS {
gortium = {
extraGroups = [ "dialout" ];
};
};
# GUI autostart (XDG)
systemd.user.services.aiov2-ctl-gui = mkIf cfg.enableGUI {
description = "AIO v2 System Tray Controller";
after = [ "graphical-session.target" ];
wants = [ "graphical-session.target" ];
wantedBy = [ "graphical-session.target" ];
serviceConfig = {
Type = "simple";
ExecStart = "${cfg.package}/bin/aiov2_ctl --gui";
Restart = "on-failure";
RestartSec = 5;
};
environment = {
AIOV2_CTL_DEBUG = "0";
};
};
};
}

View File

@@ -1,240 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.gortium.clamav;
clamavPkg = pkgs.clamav;
clamdConfig = pkgs.writeText "clamd.conf" ''
LogFile /var/log/clamav/clamd.log
LogTime yes
LogVerbose yes
LogSyslog yes
LocalSocket /run/clamav/clamd.sock
TCPSocket 3310
TCPAddr 127.0.0.1
User clamav
AllowSupplementaryGroups yes
${cfg.clamdExtraConfig}
'';
freshclamConfig = pkgs.writeText "freshclam.conf" ''
DatabaseDirectory /var/lib/clamav
LogFile /var/log/clamav/freshclam.log
LogTime yes
LogVerbose yes
LogSyslog yes
User clamav
AllowSupplementaryGroups yes
${cfg.freshclamExtraConfig}
'';
# Daily scan — logging only, no auto-quarantine/delete
scanScript = pkgs.writeShellScript "clamav-daily-scan" ''
set -e
PATHS="${concatStringsSep " " cfg.scanPaths}"
if [ -z "$PATHS" ]; then
echo "No paths configured for daily scan"
exit 0
fi
echo "=== ClamAV daily scan started: $(date) ==="
${clamavPkg}/bin/clamdscan --fdpass --log=/var/log/clamav/daily-scan.log --no-summary $PATHS
echo "=== ClamAV daily scan finished: $(date) ==="
'';
in
{
##### Options #####
options.gortium.clamav = {
enable = mkEnableOption "ClamAV antivirus installs clamav CLI tools";
enableDaemon = mkOption {
type = types.bool;
default = true;
description = ''
Run clamd daemon + freshclam updater + daily scheduled scan.
Set to false on machines where you only want the CLI tools
(clamscan, clamdscan) for manual on-demand scanning.
'';
};
onAccessScanning = mkOption {
type = types.bool;
default = false;
description = ''
Enable on-access scanning via clamonacc (fanotify-based).
Resource-heavy; server use only. Requires enableDaemon = true.
'';
};
scanPaths = mkOption {
type = types.listOf types.str;
default = [
"/home"
"/nix/store"
"/var/lib"
"/etc"
"/tmp"
"/var/tmp"
];
description = "Paths for the daily scheduled scan.";
};
dailyScanTime = mkOption {
type = types.str;
default = "daily";
description = ''
When to run the daily scan. systemd calendar expression
or shortcuts like "daily", "weekly", "04:00".
'';
};
clamdExtraConfig = mkOption {
type = types.lines;
default = "";
description = "Extra lines appended to clamd.conf";
};
freshclamExtraConfig = mkOption {
type = types.lines;
default = "";
description = "Extra lines appended to freshclam.conf";
};
};
##### Implementation #####
config = mkIf cfg.enable {
# 1. Package — always installed when enable = true
environment.systemPackages = [ clamavPkg ];
# Everything below uses mkIf cfg.enableDaemon — conditionalized per attribute
# 2. Users/groups (only if daemon runs)
users.users.clamav = mkIf cfg.enableDaemon {
isSystemUser = true;
group = "clamav";
home = "/var/lib/clamav";
createHome = true;
description = "ClamAV daemon user";
};
users.groups.clamav = mkIf cfg.enableDaemon {};
# 3. Directories (only if daemon runs)
systemd.tmpfiles.rules = mkIf cfg.enableDaemon [
"d /var/lib/clamav 0750 clamav clamav -"
"d /var/log/clamav 0750 clamav clamav -"
"d /run/clamav 0755 clamav clamav -"
];
# 4. ClamAV daemon (clamd)
systemd.services.clamav-daemon = mkIf cfg.enableDaemon {
description = "ClamAV Anti-Virus Daemon";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = [ clamavPkg ];
preStart = ''
mkdir -p /var/lib/clamav /var/log/clamav /run/clamav
chown clamav:clamav /var/lib/clamav /var/log/clamav /run/clamav
'';
serviceConfig = {
Type = "simple";
ExecStart = "${clamavPkg}/bin/clamd --config-file=${clamdConfig}";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
Restart = "on-failure";
RestartSec = "10s";
User = "clamav";
Group = "clamav";
RuntimeDirectory = "clamav";
RuntimeDirectoryMode = "0755";
StateDirectory = "clamav";
StateDirectoryMode = "0750";
LogsDirectory = "clamav";
LogsDirectoryMode = "0750";
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
ReadWritePaths = [
"/var/lib/clamav"
"/var/log/clamav"
"/run/clamav"
];
NoNewPrivileges = true;
};
};
# 5. freshclam (database updater) — hourly via timer
systemd.services.clamav-freshclam = mkIf cfg.enableDaemon {
description = "ClamAV Virus Database Updater";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = [ clamavPkg pkgs.curl ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${clamavPkg}/bin/freshclam --config-file=${freshclamConfig} --daemon-notify=${clamdConfig}";
User = "clamav";
Group = "clamav";
PrivateTmp = true;
ProtectSystem = "full";
NoNewPrivileges = true;
};
};
systemd.timers.clamav-freshclam = mkIf cfg.enableDaemon {
description = "ClamAV database update timer";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "hourly";
Persistent = true;
RandomizedDelaySec = "1800";
};
};
# 6. Daily scan — logging only, no auto-quarantine
systemd.services.clamav-daily-scan = mkIf cfg.enableDaemon {
description = "ClamAV Daily Scheduled Scan";
after = [ "clamav-daemon.service" ];
requires = [ "clamav-daemon.service" ];
path = [ clamavPkg ];
serviceConfig = {
Type = "oneshot";
ExecStart = "${scanScript}";
User = "clamav";
Group = "clamav";
PrivateTmp = true;
ProtectSystem = "strict";
ReadWritePaths = [ "/var/log/clamav" ];
};
};
systemd.timers.clamav-daily-scan = mkIf cfg.enableDaemon {
description = "ClamAV daily scan timer";
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = cfg.dailyScanTime;
Persistent = true;
};
};
# 7. On-access scanning (clamonacc) — needs enableDaemon
systemd.services.clamav-onaccess = mkIf (cfg.enableDaemon && cfg.onAccessScanning) {
description = "ClamAV On-Access Scanner (clamonacc)";
after = [ "clamav-daemon.service" ];
requires = [ "clamav-daemon.service" ];
wantedBy = [ "multi-user.target" ];
path = [ clamavPkg ];
serviceConfig = {
Type = "simple";
ExecStart = "${clamavPkg}/bin/clamonacc --config-file=${clamdConfig} --fdpass --log=/var/log/clamav/clamonacc.log";
Restart = "on-failure";
RestartSec = "10s";
User = "root"; # clamonacc needs root for fanotify
Group = "root";
PrivateTmp = true;
NoNewPrivileges = true;
};
};
};
}

View File

@@ -1,5 +0,0 @@
{
imports = [
./systemd
];
}

View File

@@ -1,71 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.remoteBuilder;
in {
options.services.remoteBuilder = {
enable = lib.mkEnableOption "remote Nix build machine (lazyworkhorse server)";
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.";
};
};
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.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}
'';
};
}

View File

@@ -1,275 +0,0 @@
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.services.stagingVm;
in
{
options.services.stagingVm = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable KVM/libvirt staging VM for compose PR testing";
};
vmName = mkOption {
type = types.str;
default = "compose-test-vm";
description = "Name of the staging VM";
};
memory = mkOption {
type = types.str;
default = "4096";
description = "RAM allocated to the staging VM (MB)";
};
vcpus = mkOption {
type = types.int;
default = 2;
description = "Number of vCPUs for the staging VM";
};
storagePath = mkOption {
type = types.str;
default = "/var/lib/libvirt/images";
description = "Path for libvirt storage pool";
};
dataPath = mkOption {
type = types.str;
default = "/var/lib/staging-vm";
description = "Path for compose test data (PR checkouts, test results)";
};
};
config = mkIf cfg.enable {
# Enable libvirt daemon
virtualisation.libvirtd = {
enable = true;
qemu = {
package = pkgs.qemu_kvm;
runAsRoot = true;
swtpm.enable = true;
ovmf = {
enable = true;
packages = [ pkgs.OVMFFull.fd ];
};
};
};
# Kernel modules + groups already handled in configuration.nix
# libvirt NAT network (192.168.122.0/24)
environment.etc."libvirt/qemu/networks/default.xml" = {
text = ''
<network>
<name>default</name>
<uuid>2b8f7a3c-9e5d-4a1f-bc3d-6e7a8f9b0c1d</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:12:34:56'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
</dhcp>
</ip>
</network>
'';
# Autostart the network so it comes up on boot
mode = "0644";
};
# Ensure the default network is defined and autostarted
systemd.services.libvirtd = {
postStart = ''
${pkgs.libvirt}/bin/virsh net-define /etc/libvirt/qemu/networks/default.xml 2>/dev/null || true
${pkgs.libvirt}/bin/virsh net-autostart default 2>/dev/null || true
${pkgs.libvirt}/bin/virsh net-start default 2>/dev/null || true
'';
};
# Storage directory for VM images
systemd.tmpfiles.rules = [
"d ${cfg.storagePath} 0755 root root -"
"d ${cfg.dataPath} 0755 root root -"
];
# Ensure storage pool exists in libvirt
systemd.services.libvirtd.postStart = mkAfter ''
${pkgs.libvirt}/bin/virsh pool-define-as default dir --target "${cfg.storagePath}" 2>/dev/null || true
${pkgs.libvirt}/bin/virsh pool-autostart default 2>/dev/null || true
${pkgs.libvirt}/bin/virsh pool-start default 2>/dev/null || true
'';
# Firewall: allow traffic from virbr0 to host and outbound NAT
networking.firewall = {
extraCommands = ''
# Allow inbound DHCP/DNS from libvirt guests
iptables -I INPUT -i virbr0 -p udp --dport 67:68 -j ACCEPT
iptables -I INPUT -i virbr0 -p tcp --dport 53 -j ACCEPT
iptables -I INPUT -i virbr0 -p udp --dport 53 -j ACCEPT
# Allow established/related traffic back to guests
iptables -I FORWARD -i virbr0 -o virbr0 -j ACCEPT
iptables -I FORWARD -o virbr0 -j ACCEPT
iptables -I FORWARD -i virbr0 -j ACCEPT
'';
};
# Packages needed for VM management
environment.systemPackages = with pkgs; [
libvirt
qemu_kvm
virt-manager # optional GUI for manual management
OVMFFull
swtpm
];
# Enable docker in the host (already enabled, but ensure for compose testing)
virtualisation.docker.enable = true;
# Helper script: pr-test-vm
# Usage:
# pr-test-vm build — build the staging VM derivation
# pr-test-vm start — boot the VM with a compose PR branch
# pr-test-vm stop — graceful shutdown
# pr-test-vm destroy — force stop + delete VM
# pr-test-vm ssh — SSH into the running VM
systemd.tmpfiles.rules = mkAfter [
"d ${cfg.dataPath}/scripts 0755 root root -"
];
environment.systemPackages = [ (pkgs.writeShellScriptBin "pr-test-vm" ''
set -euo pipefail
DATA="${cfg.dataPath}"
VM_NAME="${cfg.vmName}"
VM_IMAGE="''${DATA}/''${VM_NAME}.qcow2"
VM_PORT=2223
build_vm() {
echo "==> Building NixOS staging VM for compose testing..."
# Build the VM config inline a minimal NixOS with Docker + SSH
cat > /tmp/staging-vm-config.nix << 'NIXEOF'
{ config, pkgs, lib, ... }: {
boot.loader.grub.devices = [ "/dev/vda" ];
boot.loader.timeout = 0;
# Minimal kernel
boot.kernelParams = [ "console=ttyS0" ];
boot.initrd.kernelModules = [ "virtio_blk" "virtio_net" "virtio_pci" ];
# SSH access
services.openssh = {
enable = true;
settings.PasswordAuthentication = false;
settings.PermitRootLogin = "prohibit-password";
};
# Docker for compose testing
virtualisation.docker.enable = true;
# Network (DHCP via virbr0)
networking.useDHCP = true;
networking.firewall.enable = false;
# Users
users.users.root.openssh.authorizedKeys.keys = [
"$(cat /root/.ssh/authorized_keys 2>/dev/null || echo 'ssh-ed25519 AAAAC3... placeholder')"
];
users.users.testrunner = {
isNormalUser = true;
extraGroups = [ "docker" ];
openssh.authorizedKeys.keys = [
"$(cat /root/.ssh/authorized_keys 2>/dev/null || echo 'ssh-ed25519 AAAAC3... placeholder')"
];
};
# Git + compose tools
environment.systemPackages = with pkgs; [ git docker-compose curl ];
system.stateVersion = "24.11";
}
NIXEOF
nixos-rebuild build-vm -I nixpkgs=channel:nixos-unstable \
--arg configuration 'import /tmp/staging-vm-config.nix' \
--out-link "''${DATA}/vm-result"
echo "==> VM built. Run 'pr-test-vm start' to boot."
}
start_vm() {
if [ -f "''${VM_IMAGE}" ]; then
echo "==> Booting existing VM..."
else
echo "==> Creating VM image..."
${pkgs.qemu_kvm}/bin/qemu-img create -f qcow2 "''${VM_IMAGE}" 20G
fi
# Check if already running
if ${pkgs.libvirt}/bin/virsh list --name 2>/dev/null | grep -q "''${VM_NAME}"; then
echo "==> VM already running."
exit 0
fi
${pkgs.qemu_kvm}/bin/qemu-system-x86_64 \
-name "''${VM_NAME}" \
-machine q35,accel=kvm \
-cpu host \
-smp ${toString cfg.vcpus} \
-m ${cfg.memory} \
-drive file="''${VM_IMAGE}",if=virtio,format=qcow2 \
-netdev user,id=net0,hostfwd=tcp::''${VM_PORT}-:22 \
-device virtio-net-pci,netdev=net0 \
-nographic \
-serial mon:stdio \
-pidfile "''${DATA}/''${VM_NAME}.pid" \
-daemonize
echo "==> VM booting... SSH on port ''${VM_PORT}"
echo "==> Wait for it: ssh -p ''${VM_PORT} testrunner@localhost"
}
stop_vm() {
PIDFILE="''${DATA}/''${VM_NAME}.pid"
if [ -f "''${PIDFILE}" ]; then
PID=$(cat "''${PIDFILE}")
kill "''${PID}" 2>/dev/null || true
rm -f "''${PIDFILE}"
echo "==> VM stopped."
else
${pkgs.libvirt}/bin/virsh destroy "''${VM_NAME}" 2>/dev/null || true
echo "==> VM destroyed."
fi
}
ssh_vm() {
exec ssh -p "''${VM_PORT}" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "testrunner@localhost" "$@"
}
# Main dispatch
case "''${1:-help}" in
build) build_vm ;;
start) start_vm ;;
stop) stop_vm ;;
destroy) stop_vm; rm -f "''${VM_IMAGE}"; echo "==> VM deleted." ;;
ssh) shift; ssh_vm "$@" ;;
*)
echo "Usage: pr-test-vm {build|start|stop|destroy|ssh}"
echo ""
echo " build build the NixOS VM derivation"
echo " start boot the VM (create image if needed)"
echo " stop graceful VM shutdown"
echo " destroy stop + delete VM image"
echo " ssh SSH into the running VM"
;;
esac
'') ];
};
}

View File

@@ -1,54 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.gortium.wireguard-client;
in
{
##### Options #####
options.gortium.wireguard-client = {
enable = mkEnableOption "WireGuard VPN client to lazyworkhorse VPN server";
vpnIp = mkOption {
type = types.str;
description = "Assigned VPN IP with CIDR, e.g. \"10.8.0.4/24\"";
example = "10.8.0.4/24";
};
privateKeyFile = mkOption {
type = types.path;
description = "Path to the WireGuard private key (age-encrypted, via agenix)";
};
presharedKeyFile = mkOption {
type = types.nullOr types.path;
default = null;
description = "Path to the WireGuard preshared key (optional, age-encrypted)";
};
};
##### Config #####
config = mkIf cfg.enable {
networking.wireguard.interfaces = {
wg0 = {
ips = [ cfg.vpnIp ];
privateKeyFile = cfg.privateKeyFile;
peers = [
{
# Server public key (lazyworkhorse wg-easy)
publicKey = "rY9zII3AOm8rog2rv02PyA3Bq7zdvTOGkZapfCV1DkE=";
presharedKeyFile = cfg.presharedKeyFile;
# Split-tunnel: only route the VPN subnet
allowedIPs = [ "10.8.0.0/24" ];
endpoint = "vpn.lazyworkhorse.net:51820";
persistentKeepalive = 25;
}
];
};
};
environment.systemPackages = with pkgs; [ wireguard-tools ];
};
}

15
overlays/reticulum.nix Normal file → Executable file
View File

@@ -1,7 +1,8 @@
final: prev: let
python3 = final.python3;
pyPkgs = python3.pkgs;
in {
in
{
reticulumStack = python3.pkgs.buildPythonApplication rec {
pname = "reticulum";
version = "1.2.9";
@@ -11,6 +12,12 @@ in {
inherit version;
sha256 = "554814231c237b9caacf8df669312e57dd7d3f84b6d4810125087d1a79a75d75";
};
patchPhase = ''
# Fix license_files syntax: ("LICENSE") is a string not tuple
# Newer setuptools iterates over it char by char, fails on 'S'
substituteInPlace setup.py \
--replace-fail 'license_files = ("LICENSE")' 'license_files = ("LICENSE",)'
'';
propagatedBuildInputs = with pyPkgs; [ cryptography pyserial ];
doCheck = false;
pythonImportsCheck = [ "RNS" ];
@@ -78,4 +85,8 @@ in {
platforms = platforms.linux;
};
};
}
}
# meshtastic may not exist in all nixpkgs versions (e.g. not in 25.11)
// prev.lib.optionalAttrs (prev ? meshtastic) {
inherit (prev) meshtastic;
}

View File

@@ -1,19 +0,0 @@
--- a/drivers/gpu/drm/panel/panel-cwu50.c
+++ b/drivers/gpu/drm/panel/panel-cwu50.c
@@ -58,5 +58,8 @@
dcs_write_seq(0x72,0x06);
dcs_write_seq(0x75,0x03);
+ /* DSI_INIT0: set 4 lanes (bits[1:0]=11) */
+ dcs_write_seq(0x80,0x03);
dcs_write_seq(0xE0,0x01);
+
dcs_write_seq(0x00,0x00);
dcs_write_seq(0x01,0x47);//VCOM0x47
@@ -721,6 +723,6 @@
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
- dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
ctx->id_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_IN);
if (IS_ERR(ctx->id_gpio)) {

View File

@@ -1 +0,0 @@
/nix/store/7y7rfksqcf5smz59jjixyl56bxq50j9g-nixos-system-uConsole-25.11.20260608.e820eb4

View File

@@ -1,10 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEdoTUQ4QSA4MlFz
SHFjYjJMVHRlTWNGVGI2bHQxc0xRd2tlaExlM0NFMWhlbkR2bVg0CkxxenVTaXkr
eWxybDdCeUM0ejRvZWI4cFZCWm5VczRvZkNnT0d5Y1oyYmsKLT4gK1NmRzVtLWdy
ZWFzZSB3UDI6TyNaCnF4Ylk0QWduaXZxRFBFbDBOZ0dxeGxiWTVCYjRtZTJBRkFC
YU5qaytYWWI4OWl1K1FSdXNlY2JXZjkzak9tTHkKVFlCRlRqY1FVSzFmNS9yZmxF
aEUxelUwNEpKN3VXYi9KUWN4bXFscm5oUEFOajhRZDlERWVYcFgvQQotLS0gK1JI
VERTQjB6d1k3NDQwbjNveXBqcFk1WE96cHlaTTVkTWRMZENPamFJZwpcT1CP/KvU
CsunvfX9RBlSSKuw4eem9N9s3JqJNj4FRQizNx6QzlE1vSME
-----END AGE ENCRYPTED FILE-----

View File

@@ -1,10 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEdoTUQ4QSAycE1Y
YmMvUWZpK2VKQVlqaHFtaERBRGROcFIyL0d6dEVRQmFxLzlqdFZNCkYxWkNIUXRZ
V0dQOG4zY3U3Nk1JelBtY0cwUGdxaEI3dmZaVTZId04rVTQKLT4geV1cZC4wMnst
Z3JlYXNlIDYgOG1IME1xCkQ0RGN1NU1FUWk0Y1RmamNEY0tJWmFQNGdoMkROcGVy
aU5UYVFobVRLMVVUQ1JicUM2c0tSVzRQdEZ0VE5YamQKZUxPeVpLWDZJR0hqemdD
cmkyUUdFZEZKZjBDNGhmNFR6bVUKLS0tIDRQUGR5RGI5UEhGNk5EQWw4dFk0R01k
TUJWOFpleXBUajFPckFmem52cGsKHzn+QnuYLI2NEh5WWZQHrNuvVzYk+kVjsAsn
KNS2dHjvadAopVY2Gypldf1p2RRtmgZkDHaPlNzv5Hk=
-----END AGE ENCRYPTED FILE-----

View File

@@ -8,8 +8,6 @@ let
in
{
"containers.env.age".publicKeys = authorizedKeys;
"gortium_password.age".publicKeys = authorizedKeys;
"home_wifi.age".publicKeys = authorizedKeys;
"lazyworkhorse_host_ssh_key.age".publicKeys = authorizedKeys;
"n8n_ssh_key.age".publicKeys = authorizedKeys;
"openclaw_gateway_token.age".publicKeys = authorizedKeys;

View File

@@ -22,6 +22,7 @@
# - NO access to infra repo (no bind mount)
# - NO sudo access (no nh, nixos-rebuild, nixpkgs-fmt, nix)
# WORKFLOW: SSH from Hermes container, run docker benchmarks, return and save results to /opt/data/ai-optimizer/
services.aiWorkerAccess = true;
# Restricted sudo for ai-worker - security checks only
security.sudo.extraRules = [

View File

@@ -1,6 +1,4 @@
{ pkgs, inputs, config, keys, ... }: {
home-manager.extraSpecialArgs = { inherit (config.networking) hostName; dotfiles = inputs.dotfiles.outPath # flake input (not submodule); };
home-manager.users.gortium = import ./home.nix;
users.users.gortium = {
isNormalUser = true;
extraGroups = [ "wheel" "docker" "video" "render"];
@@ -8,10 +6,9 @@
packages = with pkgs; [
tree
btop
nh
];
shell = pkgs.zsh;
passwordFile = config.age.secrets.gortium_password.path;
ignoreShellProgramCheck = true;
openssh.authorizedKeys.keys = [
keys.users.gortium.main
];

View File

@@ -1,64 +0,0 @@
{ pkgs, lib, config, inputs, hostName, ... }:
let
isUconsole = hostName == "uConsole";
in {
home.username = "gortium";
home.homeDirectory = "/home/gortium";
home.stateVersion = "23.11";
programs.home-manager.enable = true;
home.file = {
# tmux
".tmux.conf".source = "${dotfiles}/tmux/.tmux.conf";
# kitty
".config/kitty/kitty.conf".source = "${dotfiles}/kitty/.config/kitty/kitty.conf";
# nvim
".config/nvim/init.lua".source = "${dotfiles}/nvim/.config/nvim/init.lua";
# starship
".config/starship.toml".source = "${dotfiles}/starship/.config/starship.toml";
# btop
".config/btop/btop.conf".source = "${dotfiles}/btop/.config/btop/btop.conf";
# waybar
".config/waybar/style.css".source = "${dotfiles}/waybar/.config/waybar/style.css";
".config/waybar/config.jsonc".source = "${dotfiles}/waybar/.config/waybar/config.jsonc";
# wofi
".config/wofi/style.css".source = "${dotfiles}/wofi/.config/wofi/style.css";
".config/wofi/config".source = "${dotfiles}/wofi/.config/wofi/config";
# yazi
".config/yazi/yazi.toml".source = "${dotfiles}/yazi/.config/yazi/yazi.toml";
# hyprland — common config
".config/hypr/hyprland.conf".source = "${dotfiles}/hypr/.config/hypr/hyprland.conf";
".config/hypr/hypridle.conf".source = "${dotfiles}/hypr/.config/hypr/hypridle.conf";
".config/hypr/hyprlock.conf".source = "${dotfiles}/hypr/.config/hypr/hyprlock.conf";
".config/hypr/hyprpaper.conf".source = "${dotfiles}/hypr/.config/hypr/hyprpaper.conf";
".config/hypr/mocha.conf".source = "${dotfiles}/hypr/.config/hypr/mocha.conf";
# hyprland — host-specific monitor config
".config/hypr/host/monitors.conf".source =
if isUconsole
then "${dotfiles}/hypr/.config/hypr/hosts/uconsole.conf"
else "${dotfiles}/hypr/.config/hypr/hosts/laptop.conf";
};
home.packages = with pkgs; [
git zsh tmux starship
neovim kitty
btop yazi ripgrep fd fzf
] ++ lib.optionals (!isUconsole) [
waybar wofi swww hyprshot
] ++ lib.optionals isUconsole [
brightnessctl
];
programs.zsh.enable = true;
programs.starship.enable = true;
}