28 Commits

Author SHA1 Message Date
mjallen18
33385d5275 testing done 2025-08-21 20:54:43 -05:00
mjallen18
19bf815be8 end test 2025-08-21 20:52:13 -05:00
mjallen18
0152438472 more cleanup 2025-08-21 20:05:58 -05:00
mjallen18
f9b07deb19 cleanup 2025-08-21 19:45:03 -05:00
mjallen18
6e55d375d2 user updates 2025-08-21 19:40:32 -05:00
mjallen18
0e066cb4d7 move some apps to namespace 2025-08-21 19:06:51 -05:00
mjallen18
bd64283f04 idk 2025-08-21 16:17:17 -05:00
mjallen18
6025b6c4f1 bcfs root? 2025-08-21 16:10:04 -05:00
mjallen18
92b04773b2 move python-steam 2025-08-21 14:26:50 -05:00
mjallen18
783a7a3390 README 2025-08-21 14:17:33 -05:00
mjallen18
0ef4354c1a aarch 2025-08-21 13:24:04 -05:00
mjallen18
192a978d46 fix proxies 2025-08-21 11:45:06 -05:00
mjallen18
a4519904b6 fix pi4 hostname 2025-08-21 11:45:06 -05:00
mjallen18
83a6e45bf4 test 2025-08-20 22:02:24 -05:00
mjallen18
2ba6f3466f clev 2025-08-20 21:30:18 -05:00
mjallen18
b3f5b4b406 more pi stuff 2025-08-20 21:30:18 -05:00
mjallen18
2e680f2519 fix lol 2025-08-20 21:30:18 -05:00
mjallen18
445183f826 pi stuff 2025-08-20 21:30:18 -05:00
mjallen18
aec980e6fe sops but idk 2025-08-20 21:30:18 -05:00
mjallen18
68f732ec4b cleanup 2025-08-20 21:30:18 -05:00
mjallen18
dc382dcfcc cache 2025-08-20 21:30:18 -05:00
mjallen18
b1a06034f1 disable gnome 2025-08-20 21:30:18 -05:00
mjallen18
aa3e8cc263 finally update traefik 2025-08-20 21:30:18 -05:00
mjallen18
b680255bc5 macos 2025-08-20 21:30:18 -05:00
mjallen18
a3f7af4e39 sops 2025-08-20 21:30:18 -05:00
mjallen18
cd5c8a0034 no splash 2025-08-20 21:30:18 -05:00
mjallen18
1f14f020ed ssh 2025-08-20 21:30:18 -05:00
mjallen18
05affb6b1f test 2025-08-18 20:54:03 -05:00
86 changed files with 2121 additions and 2005 deletions

View File

@@ -10,8 +10,8 @@ keys:
- &pi5 age1t2d5scrukk0guva5sr97a8tge5j8kd865adezrcru7p269pzwvpsamkgje
- &deck age1c8qw59ffcq9l77gfmtyc3djtvt3md0u6dwhrjcgsm98ntyf72ufqugj7cg
- &steamdeck age1er5qucsc2mugrzrr7n3xhzv7kemkrqrw4m84r544fkk7nkg5g5eswxkqj0
- &matt_macbook-pro age1xg6mvj3x6s3t8058c6rsk3q4kskvm6nsffwckxkkjzhyn7r6tczqgkj23p
- &macbook-pro age1rdn39ywgzmc8wlsl5lrfe77e652wzjmjx58gx4k2ydghd35kdqvqscrf3h
- &matt_macbook-pro age19daqsncuzeh3j6cwk8uxp6yfj8h0qtz02jxlwwy4v8j0mfgznsvq30440g
- &macbook-pro age19w4zafpwnq9yhzuf8r5te2yhq7xlqj76rcgzcz935hllyrz4yvws4jn6ca
- &nuc age1wurzgc20e6ye79wsg85vvqk4aj3mmc0llxshcy9532ex8f4c6dqql76c78
- &admin_nuc age1luyejgmqjj0esydlr2jxqkg48vexmx57gdz7cy5gq7rz8kf5cups2rnfa9
creation_rules:

156
README.md
View File

@@ -1,50 +1,118 @@
# nixOS Config
# NixOS Configuration Repository
### Common Files
* [flake.nix](./flake.nix)
* [impermenance.nix](./share/impermanence/default.nix)
* [share](./share)
* [overlays](./overlays)
This repository contains my personal NixOS configurations for multiple systems, managed using [Snowfall Lib](https://github.com/snowfallorg/lib) and the Nix Flakes system.
## Overview
This repository provides a centralized, declarative configuration for all my systems, including:
- Desktop PC (AMD)
- NAS server
- Steam Deck
- Intel NUC
- Raspberry Pi 4
- Raspberry Pi 5
- MacBook Pro (NixOS on Apple Silicon)
- MacBook Pro (Darwin/macOS)
## Repository Structure
```
.
├── checks/ # Pre-commit hooks and other checks
├── flake.nix # Main flake configuration
├── homes/ # Home-manager configurations for users
│ ├── aarch64-darwin/ # macOS home configurations
│ ├── aarch64-linux/ # ARM Linux home configurations
│ └── x86_64-linux/ # x86 Linux home configurations
├── modules/ # Reusable configuration modules
│ ├── home/ # Home-manager modules
│ └── nixos/ # NixOS system modules
├── overlays/ # Nixpkgs overlays
├── packages/ # Custom package definitions
├── secrets/ # Encrypted secrets (managed with sops-nix)
└── systems/ # System-specific configurations
├── aarch64-darwin/ # macOS system configurations
├── aarch64-linux/ # ARM Linux system configurations
└── x86_64-linux/ # x86 Linux system configurations
```
## Key Features
- **Modular Design**: Reusable modules for various system components
- **Multi-System Support**: Configurations for different hardware platforms
- **Home Manager Integration**: User environment management
- **Secret Management**: Encrypted secrets with sops-nix
- **Disk Management**: Declarative disk partitioning with disko
- **State Management**: Persistent state management with impermanence
- **Desktop Environments**: Support for GNOME, Hyprland, and COSMIC
- **Hardware-Specific Optimizations**: Tailored configurations for different hardware
## Key Technologies
- [Nix](https://nixos.org/) and [NixOS](https://nixos.org/)
- [Nix Flakes](https://nixos.wiki/wiki/Flakes)
- [Snowfall Lib](https://github.com/snowfallorg/lib)
- [Home Manager](https://github.com/nix-community/home-manager)
- [sops-nix](https://github.com/Mic92/sops-nix)
- [disko](https://github.com/nix-community/disko)
- [impermanence](https://github.com/nix-community/impermanence)
- [lanzaboote](https://github.com/nix-community/lanzaboote) (Secure Boot)
## Notable System Configurations
### Desktop
* [boot.nix](./hosts/desktop/boot.nix)
* [configuration.nix](./hosts/desktop/configuration.nix)
* [hardware-configuration.nix](./hosts/desktop/hardware-configuration.nix)
* [filesystems.nix](./hosts/desktop/filesystems.nix)
* [home.nix](./hosts/desktop/home.nix)
* [sops.nix](./hosts/desktop/sops.nix)
* [specialisations.hyprland](./hosts/desktop/hyprland)
* [specialisations.gnome](./hosts/desktop/gnome)
* [specialisations.cosmic](./hosts/desktop/cosmic)
A powerful AMD-based desktop with gaming capabilities, featuring:
- AMD CPU and GPU optimizations
- Multiple desktop environment options (GNOME, Hyprland, COSMIC)
- Gaming setup with Steam and related tools
### NAS
* [boot.nix](./hosts/nas/boot.nix)
* [configuration.nix](./hosts/nas/configuration.nix)
* [hardware-configuration.nix](./hosts/nas/hardware-configuration.nix)
* [impermenance.nix](./hosts/nas/impermenance.nix)
* [apps.nix](./hosts/desktop/apps.nix)
* [home.nix](./hosts/desktop/home.nix)
* [networking.nix](./hosts/desktop/networking.nix)
* [services.nix](./hosts/desktop/services.nix)
* [sops.nix](./hosts/desktop/sops.nix)
* [ups.nix](./hosts/desktop/ups.nix)
* [samba](./modules/samba)
* nas-apps
* [arrs](./hosts/nas/apps/arrs/default.nix)
* [free-games-claimer](./modules/apps/free-games-claimer)
* [jackett](./modules/apps/jackett)
* [jellyfin](./hosts/nas/apps/jellyfin/default.nix)
* [jellyseerr](./hosts/nas/apps/jellyseerr/default.nix)
* [jackett](./modules/apps/manyfold)
* [mariadb](./modules/apps/mariadb)
* [mealie](./modules/apps/mealie)
* [nextcloud+onlyoffice](./hosts/nas/apps/nextcloud/default.nix)
* [ollama](./hosts/nas/apps/ollama/default.nix)
* [paperless](./hosts/nas/apps/paperless/default.nix)
* [tdarr](./modules/apps/tdarr)
* [traefik](./hosts/nas/apps/traefik/default.nix)
* [wireguard](./modules/apps/your-spotify)
### Raspberry Pi 4
* [configuration.nix](./hosts/pi4/configuration.nix)
* [hardware-configuration.nix](./hosts/pi4/hardware-configuration.nix)
A home server with various self-hosted services:
- Media management (Jellyfin, Jellyseerr)
- Download automation (Sonarr, Radarr, etc.)
- Document management (Paperless)
- File sharing (Samba, Nextcloud)
- AI services (Ollama)
### Raspberry Pi
Configurations for both Pi 4 and Pi 5:
- Hardware-specific optimizations
- Disk partitioning suitable for ARM devices
- Bluetooth and wireless support
### Steam Deck
Custom NixOS configuration for the Steam Deck:
- Integration with Jovian for Steam Deck compatibility
- Gaming optimizations
- Steam ROM Manager
### MacBook Pro
Configurations for both:
- NixOS on Apple Silicon
- nix-darwin for macOS
## Usage
### Building a System Configuration
```bash
# Build and activate a system configuration
sudo nixos-rebuild switch --flake .#hostname
```
### Building a Home Configuration
```bash
# Build and activate a home configuration
home-manager switch --flake .#username@hostname
```
## License
This project is licensed under the MIT License - see the LICENSE file for details.

11
flake.lock generated
View File

@@ -180,16 +180,15 @@
]
},
"locked": {
"lastModified": 1742690494,
"narHash": "sha256-SFacEbSRMoTyWG5VXh4ieofJGge+cLq9lH8ifB+zjBg=",
"owner": "nvmd",
"lastModified": 1755519972,
"narHash": "sha256-bU4nqi3IpsUZJeyS8Jk85ytlX61i4b0KCxXX9YcOgVc=",
"owner": "nix-community",
"repo": "disko",
"rev": "9dc58d4d49c9f74623a06e2fc20cdfd8bb3cbe8b",
"rev": "4073ff2f481f9ef3501678ff479ed81402caae6d",
"type": "github"
},
"original": {
"owner": "nvmd",
"ref": "gpt-attrs",
"owner": "nix-community",
"repo": "disko",
"type": "github"
}

View File

@@ -43,8 +43,8 @@
disko = {
# the fork is needed for partition attributes support
url = "github:nvmd/disko/gpt-attrs";
# url = "github:nix-community/disko";
# url = "github:nvmd/disko/gpt-attrs";
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};

View File

@@ -1,18 +1,22 @@
{ lib, ... }:
let
shellAliases = {
ll = "ls -alh";
update-boot = "sudo nixos-rebuild boot --max-jobs 10 --build-host admin@10.0.1.3";
update-switch = "sudo nixos-rebuild switch --max-jobs 10 --build-host admin@10.0.1.3";
update-flake = "nix flake update pi4-nixpkgs pi4-home-manager pi4-impermanence pi4-sops-nix pi4-nixos-hardware pi4-nixos-raspberrypi pi4-disko --flake /etc/nixos";
update-nas = "nixos-rebuild switch --use-remote-sudo --target-host admin@10.0.1.3 --build-host admin@10.0.1.3 --flake ~/nix-config#jallen-nas";
nas-ssh = "kitten ssh admin@10.0.1.3";
ducks = "du -cksh * | sort -hr | head -n 15";
};
in
{
home.username = "matt";
mjallen = {
shell-aliases = {
enable = true;
flakeInputs = [
"pi4-nixpkgs"
"pi4-home-manager"
"pi4-impermanence"
"pi4-sops-nix"
"pi4-nixos-hardware"
"pi4-nixos-raspberrypi"
"pi4-disko"
];
};
};
sops = {
age.keyFile = "/home/matt/.config/sops/age/keys.txt";
defaultSopsFile = "/etc/nixos/secrets/secrets.yaml";
@@ -50,10 +54,13 @@ in
programs = {
mangohud.enable = lib.mkForce true;
zsh.shellAliases = shellAliases;
};
services = {
nextcloud-client.enable = lib.mkForce true;
nextcloud-client.enable = lib.mkForce false;
kdeconnect = {
enable = false;
indicator = false;
};
};
}

View File

@@ -57,4 +57,12 @@ in
programs = {
zsh.shellAliases = shellAliases;
};
services = {
nextcloud-client.enable = false;
kdeconnect = {
enable = false;
indicator = false;
};
};
}

View File

@@ -15,4 +15,12 @@ in
programs = {
zsh.shellAliases = shellAliases;
};
services = {
nextcloud-client.enable = lib.mkForce false;
kdeconnect = {
enable = false;
indicator = false;
};
};
}

View File

@@ -1,16 +1,27 @@
{ pkgs, ... }:
let
shellAliases = {
update-boot = "sudo nixos-rebuild boot --max-jobs 10";
update-switch = "sudo nixos-rebuild switch --max-jobs 10";
update-flake = "nix flake update nas-nixpkgs nas-authentik-nix nas-cosmic nas-crowdsec nas-home-manager nas-impermanence nas-lanzaboote nas-nixos-hardware nas-sops-nix --flake /etc/nixos";
};
in
{
home.username = "admin";
# mjallen.home.enable = true;
mjallen = {
shell-aliases = {
enable = true;
buildHost = ""; # NAS builds locally
flakeInputs = [
"nas-nixpkgs"
"nas-authentik-nix"
"nas-cosmic"
"nas-crowdsec"
"nas-home-manager"
"nas-impermanence"
"nas-lanzaboote"
"nas-nixos-hardware"
"nas-sops-nix"
];
};
};
sops = {
age.keyFile = "/home/admin/.config/sops/age/keys.txt";
defaultSopsFile = "/etc/nixos/secrets/secrets.yaml";
@@ -60,8 +71,6 @@ in
}
];
};
zsh.shellAliases = shellAliases;
};
# services.nixai = {

View File

@@ -78,6 +78,6 @@ in
mgba
prismlauncher
ryujinx-greemdev
vmware-horizon-client
omnissa-horizon-client
];
}

View File

@@ -1,12 +1,4 @@
{ pkgs, ... }:
let
shellAliases = {
update-boot = "sudo nixos-rebuild boot --max-jobs 10 --build-host admin@10.0.1.3";
update-switch = "sudo nixos-rebuild switch --max-jobs 10 --build-host admin@10.0.1.3";
update-flake = "nix flake update desktop-nixpkgs desktop-chaotic desktop-home-manager desktop-impermanence desktop-lanzaboote desktop-nixos-hardware desktop-sops-nix desktop-steam-rom-manager --flake /etc/nixos";
update-nas = "nixos-rebuild switch --use-remote-sudo --target-host admin@10.0.1.3 --build-host admin@10.0.1.3 --flake ~/nix-config#jallen-nas";
};
in
{
home.username = "matt";
@@ -14,6 +6,19 @@ in
sops = {
enable = true;
};
shell-aliases = {
enable = true;
flakeInputs = [
"desktop-nixpkgs"
"desktop-chaotic"
"desktop-home-manager"
"desktop-impermanence"
"desktop-lanzaboote"
"desktop-nixos-hardware"
"desktop-sops-nix"
"desktop-steam-rom-manager"
];
};
};
services = {
@@ -25,8 +30,6 @@ in
programs = {
password-store.enable = true;
zsh.shellAliases = shellAliases;
};
home.packages = with pkgs; [

View File

@@ -292,7 +292,8 @@ in
"tag +waydroid, class:([Ww]aydroid.*)"
"float, tag:waydroid"
"pin, tag:waydroid"
] ++ cfg.windowRule;
]
++ cfg.windowRule;
plugin = {
touch_gestures = {
@@ -395,16 +396,15 @@ in
};
};
extraConfig =
''
exec-once = dbus-update-activation-environment --systemd --all
exec-once = systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
exec-once = /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
exec-once = xhost +SI:localuser:root
exec-once = nwg-look -a
exec-once = nwg-dock-hyprland -d
''
+ cfg.extraConfig or '''';
extraConfig = ''
exec-once = dbus-update-activation-environment --systemd --all
exec-once = systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP
exec-once = /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
exec-once = xhost +SI:localuser:root
exec-once = nwg-look -a
exec-once = nwg-dock-hyprland -d
''
+ cfg.extraConfig or '''';
};
};
}

View File

@@ -12,7 +12,7 @@ in
config = lib.mkIf cfg.enable {
programs.hyprlock = {
enable = false;
enable = true;
settings = {
background = [
{

View File

@@ -52,10 +52,14 @@
nh = {
enable = true;
flake = "/etc/nixos";
clean = {
enable = true;
extraArgs = "--keep 5";
};
};
micro = {
enable = true;
enable = lib.mkDefault true;
settings = {
autoindent = true;
autosu = true;
@@ -66,7 +70,7 @@
};
tmux = {
enable = true;
enable = lib.mkDefault true;
terminal = "screen-256color";
sensibleOnTop = true;
focusEvents = true;
@@ -109,8 +113,8 @@
nextcloud-client.enable = lib.mkDefault true;
pass-secret-service.enable = lib.mkDefault true;
kdeconnect = {
enable = true;
indicator = true;
enable = lib.mkDefault true;
indicator = lib.mkDefault true;
};
};
}

View File

@@ -2,6 +2,7 @@
config,
pkgs,
system,
namespace,
...
}:
let
@@ -9,6 +10,7 @@ let
x86_only = with pkgs; [
vscode-extensions.redhat.vscode-xml
];
open-remote-ssh = pkgs.${namespace}.open-remote-ssh;
in
{
programs = {
@@ -39,7 +41,7 @@ in
# open-remote-ssh
# nix-vscode-extensions.open-vsx.jeanp413.open-remote-ssh
# open-vsx.jeanp413.open-remote-ssh
open-remote-ssh
]
++ (if !isArm then x86_only else [ ])
++ pkgs.vscode-utils.extensionsFromVscodeMarketplace [
@@ -138,6 +140,8 @@ in
"*.db" = "default";
};
};
"enable-proposed-api" = [ "jeanp413.open-remote-ssh" ];
};
};
};

View File

@@ -80,9 +80,9 @@ in
"custom/right-end"
];
# modules-right = [
# modules-right = [
# "tray"
# "custom/left-end" ] ++
# "custom/left-end" ] ++
# cfg.modules-right ++
# [ "custom/right-end" ];
@@ -323,262 +323,262 @@ in
format = "&nbsp";
tooltip = false;
};
} // cfg.extraModules;
}
// cfg.extraModules;
};
# * { font-size: 13px; }
# window.eDP-1 * { font-size: 10px; }
style =
''
.blink_me {
animation: blinker 1s linear infinite;
style = ''
.blink_me {
animation: blinker 1s linear infinite;
}
@keyframes blinker {
50% {
color: ${nord.aurora.nord11};
}
}
@keyframes blinker {
50% {
color: ${nord.aurora.nord11};
}
}
* {
font-family:
Jetbrains Mono Nerd Font,
monospace;
font-size: 14px;
min-height: 0;
}
* {
font-family:
Jetbrains Mono Nerd Font,
monospace;
font-size: 14px;
min-height: 0;
}
#waybar {
background: transparent;
color: ${nord.snowStorm.nord6};
margin: 5px 5px;
}
#waybar {
background: transparent;
color: ${nord.snowStorm.nord6};
margin: 5px 5px;
}
#workspaces {
background-color: ${nord.polarNight.nord0};
${defaultBorderRadius}
${defaultOpacity}
${defaultCenterOptions}
margin-left: 0.6rem;
}
#workspaces {
background-color: ${nord.polarNight.nord0};
${defaultBorderRadius}
${defaultOpacity}
${defaultCenterOptions}
margin-left: 0.6rem;
}
#workspaces button {
color: ${nord.frost.nord10};
${defaultBorderRadius}
padding: 0.4rem;
}
#workspaces button {
color: ${nord.frost.nord10};
${defaultBorderRadius}
padding: 0.4rem;
}
#workspaces button.active {
color: ${nord.frost.nord8};
${defaultBorderRadius}
}
#workspaces button.active {
color: ${nord.frost.nord8};
${defaultBorderRadius}
}
#workspaces button:hover {
color: ${nord.frost.nord7};
${defaultBorderRadius}
}
#workspaces button:hover {
color: ${nord.frost.nord7};
${defaultBorderRadius}
}
#workspaces button.focused {
color: ${nord.snowStorm.nord6};
background: ${nord.aurora.nord13};
${defaultBorderRadius}
}
#workspaces button.focused {
color: ${nord.snowStorm.nord6};
background: ${nord.aurora.nord13};
${defaultBorderRadius}
}
#workspaces button.urgent {
color: ${nord.polarNight.nord0};
background: ${nord.snowStorm.nord6};
${defaultBorderRadius}
}
#workspaces button.urgent {
color: ${nord.polarNight.nord0};
background: ${nord.snowStorm.nord6};
${defaultBorderRadius}
}
#tooltip {
background: ${nord.polarNight.nord0};
border-color: ${nord.polarNight.nord0};
${defaultBorderRadius}
border-width: 1rem;
border-style: solid;
}
#tooltip {
background: ${nord.polarNight.nord0};
border-color: ${nord.polarNight.nord0};
${defaultBorderRadius}
border-width: 1rem;
border-style: solid;
}
#window {
color: ${nord.aurora.nord15};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultBorderRadius}
${defaultCenterOptions}
margin-left: 4rem;
margin-right: ${toString cfg.windowOffset}rem;
}
#window {
color: ${nord.aurora.nord15};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultBorderRadius}
${defaultCenterOptions}
margin-left: 4rem;
margin-right: ${toString cfg.windowOffset}rem;
}
/* make window module transparent when no windows present */
#window.empty {
background-color: transparent;
}
/* make window module transparent when no windows present */
#window.empty {
background-color: transparent;
}
#custom-weather {
color: ${nord.frost.nord10};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#custom-weather {
color: ${nord.frost.nord10};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#battery {
color: ${nord.aurora.nord15};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
min-width: 3rem;
}
#battery {
color: ${nord.aurora.nord15};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
min-width: 3rem;
}
#clock {
color: ${nord.frost.nord9};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#clock {
color: ${nord.frost.nord9};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
/* ------------- */
/* ------------- */
#idle_inhibitor {
color: ${nord.frost.nord10};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
padding-right: 1rem;
}
#idle_inhibitor {
color: ${nord.frost.nord10};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
padding-right: 1rem;
}
#idle_inhibitor:hover {
background: ${nord.polarNight.nord3};
}
#idle_inhibitor:hover {
background: ${nord.polarNight.nord3};
}
#network {
color: ${nord.aurora.nord15};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
padding-right: 15px;
}
#network {
color: ${nord.aurora.nord15};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
padding-right: 15px;
}
#network:hover {
background: ${nord.polarNight.nord3};
}
#network:hover {
background: ${nord.polarNight.nord3};
}
#bluetooth {
color: ${nord.frost.nord9};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#bluetooth {
color: ${nord.frost.nord9};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#bluetooth:hover {
background: ${nord.polarNight.nord3};
}
#bluetooth:hover {
background: ${nord.polarNight.nord3};
}
#wireplumber.source {
color: ${nord.frost.nord8};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#wireplumber.source {
color: ${nord.frost.nord8};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#wireplumber.source.muted {
animation-name: blinker;
animation-duration: 2s;
animation-timing-function: linear;
animation-iteration-count: infinite;
padding-right: 1rem;
}
#wireplumber.source.muted {
animation-name: blinker;
animation-duration: 2s;
animation-timing-function: linear;
animation-iteration-count: infinite;
padding-right: 1rem;
}
#wireplumber.source:hover {
background: ${nord.polarNight.nord3};
}
#wireplumber.source:hover {
background: ${nord.polarNight.nord3};
}
#wireplumber.sink {
color: ${nord.frost.nord7};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#wireplumber.sink {
color: ${nord.frost.nord7};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#wireplumber.sink.muted {
animation-name: blinker;
animation-duration: 5s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
#wireplumber.sink.muted {
animation-name: blinker;
animation-duration: 5s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
#wireplumber.sink:hover {
background: ${nord.polarNight.nord3};
}
#wireplumber.sink:hover {
background: ${nord.polarNight.nord3};
}
#keyboard-state.numlock {
color: ${nord.frost.nord8};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#keyboard-state.numlock {
color: ${nord.frost.nord8};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#keyboard-state.capslock {
color: ${nord.frost.nord9};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#keyboard-state.capslock {
color: ${nord.frost.nord9};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#temperature.gpu {
color: ${nord.frost.nord10};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#temperature.gpu {
color: ${nord.frost.nord10};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0;
}
#temperature.gpu:hover {
background: ${nord.polarNight.nord3};
}
#temperature.gpu:hover {
background: ${nord.polarNight.nord3};
}
#temperature {
color: ${nord.frost.nord9};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0
}
#temperature {
color: ${nord.frost.nord9};
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
border-radius: 0
}
/* ------------- */
/* ------------- */
#tray {
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
${defaultBorderRadius}
margin-right: 0.6rem;
}
#tray {
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${defaultCenterOptions}
${defaultBorderRadius}
margin-right: 0.6rem;
}
/* ------------- */
/* ------------- */
#custom-left-end {
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${borderLeft}
}
#custom-left-end {
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${borderLeft}
}
#custom-right-end {
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${borderRight}
}
''
+ cfg.extraModulesStyle or '''';
#custom-right-end {
background-color: ${nord.polarNight.nord0};
${defaultOpacity}
${borderRight}
}
''
+ cfg.extraModulesStyle or '''';
};
};
}

View File

@@ -0,0 +1,57 @@
{
config,
lib,
...
}:
let
cfg = config.mjallen.shell-aliases;
in
{
options.mjallen.shell-aliases = {
enable = lib.mkEnableOption "Common shell aliases";
buildHost = lib.mkOption {
type = lib.types.str;
default = "admin@10.0.1.3";
description = "Build host for nixos-rebuild commands";
};
flakeInputs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "List of flake inputs to update";
};
extraAliases = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
description = "Additional host-specific aliases";
};
};
config = lib.mkIf cfg.enable {
programs.zsh.shellAliases = {
# Common file operations
ll = "ls -alh";
ducks = "du -cksh * | sort -hr | head -n 15";
# NixOS rebuild commands
update-boot =
"sudo nixos-rebuild boot --max-jobs 10"
+ lib.optionalString (cfg.buildHost != "") " --build-host ${cfg.buildHost}";
update-switch =
"sudo nixos-rebuild switch --max-jobs 10"
+ lib.optionalString (cfg.buildHost != "") " --build-host ${cfg.buildHost}";
# Flake update command
update-flake = lib.mkIf (
cfg.flakeInputs != [ ]
) "nix flake update ${lib.concatStringsSep " " cfg.flakeInputs} --flake /etc/nixos";
# NAS management
update-nas = "nixos-rebuild switch --use-remote-sudo --target-host admin@10.0.1.3 --build-host admin@10.0.1.3 --flake ~/nix-config#jallen-nas";
nas-ssh = "kitten ssh admin@10.0.1.3";
}
// cfg.extraAliases;
};
}

View File

@@ -65,20 +65,19 @@ in
];
home = {
file =
{
"Desktop/.keep".text = "";
"Documents/.keep".text = "";
"Downloads/.keep".text = "";
"Music/.keep".text = "";
"Pictures/.keep".text = "";
"Videos/.keep".text = "";
}
// lib.optionalAttrs (cfg.icon != null) {
".face".source = cfg.icon;
".face.icon".source = cfg.icon;
"Pictures/${cfg.icon.fileName or (builtins.baseNameOf cfg.icon)}".source = cfg.icon;
};
file = {
"Desktop/.keep".text = "";
"Documents/.keep".text = "";
"Downloads/.keep".text = "";
"Music/.keep".text = "";
"Pictures/.keep".text = "";
"Videos/.keep".text = "";
}
// lib.optionalAttrs (cfg.icon != null) {
".face".source = cfg.icon;
".face.icon".source = cfg.icon;
"Pictures/${cfg.icon.fileName or (builtins.baseNameOf cfg.icon)}".source = cfg.icon;
};
homeDirectory = mkDefault cfg.home;

View File

@@ -2,11 +2,12 @@
config,
pkgs,
lib,
namespace,
...
}:
with lib;
let
cfg = config.nas-apps.actual;
cfg = config.${namespace}.services.actual;
dataDir = "/data";
hostAddress = "10.0.1.3";
actualUserId = config.users.users.nix-apps.uid;

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
{
options.nas-apps.actual = {
options.${namespace}.services.actual = {
enable = mkEnableOption "actual service";
port = mkOption {

View File

@@ -1,7 +1,12 @@
{ lib, config, ... }:
{
lib,
config,
namespace,
...
}:
with lib;
let
cfg = config.nas-apps.free-games-claimer;
cfg = config.${namespace}.services.free-games-claimer;
in
{
imports = [ ./options.nix ];

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
{
options.nas-apps.free-games-claimer = {
options.${namespace}.services.free-games-claimer = {
enable = mkEnableOption "free-games-claimer docker service";
autoStart = mkOption {

View File

@@ -1,7 +1,12 @@
{ lib, config, ... }:
{
lib,
config,
namespace,
...
}:
with lib;
let
cfg = config.nas-apps.manyfold;
cfg = config.${namespace}.services.manyfold;
in
{
imports = [ ./options.nix ];

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
{
options.nas-apps.manyfold = {
options.${namespace}.services.manyfold = {
enable = mkEnableOption "manyfold docker service";
autoStart = mkOption {

View File

@@ -1,7 +1,12 @@
{ lib, config, ... }:
{
lib,
config,
namespace,
...
}:
with lib;
let
cfg = config.nas-apps.mongodb;
cfg = config.${namespace}.services.mongodb;
in
{
imports = [ ./options.nix ];

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
{
options.nas-apps.mongodb = {
options.${namespace}.services.mongodb = {
enable = mkEnableOption "mongodb docker service";
autoStart = mkOption {

View File

@@ -1,7 +1,12 @@
{ lib, config, ... }:
{
lib,
config,
namespace,
...
}:
with lib;
let
cfg = config.nas-apps.tdarr;
cfg = config.${namespace}.services.tdarr;
in
{
imports = [ ./options.nix ];

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
{
options.nas-apps.tdarr = {
options.${namespace}.services.tdarr = {
enable = mkEnableOption "tdarr docker service";
autoStart = mkOption {

View File

@@ -1,7 +1,12 @@
{ lib, config, ... }:
{
lib,
config,
namespace,
...
}:
with lib;
let
cfg = config.nas-apps.your_spotify;
cfg = config.${namespace}.services.your_spotify;
in
{
imports = [ ./options.nix ];

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
{
options.nas-apps.your_spotify = {
options.${namespace}.services.your_spotify = {
enable = mkEnableOption "your_spotify docker service";
autoStart = mkOption {

View File

@@ -2,11 +2,12 @@
config,
pkgs,
lib,
namespace,
...
}:
with lib;
let
cfg = config.nas-apps.arrs;
cfg = config.${namespace}.services.arrs;
radarrDataDir = "/var/lib/radarr";
downloadDir = "/downloads";
incompleteDir = "/downloads-incomplete";

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
{
options.nas-apps.arrs = {
options.${namespace}.services.arrs = {
enable = mkEnableOption "arrs services";
radarr = {

View File

@@ -2,11 +2,12 @@
config,
lib,
pkgs,
namespace,
...
}:
with lib;
let
cfg = config.nas-apps.crowdsec;
cfg = config.${namespace}.services.crowdsec;
in
{
imports = [ ./options.nix ];

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
{
options.nas-apps.crowdsec = {
options.${namespace}.services.crowdsec = {
enable = mkEnableOption "crowdsec service";
port = mkOption {

View File

@@ -28,7 +28,7 @@ in
programs = {
kdeconnect = {
enable = true;
enable = lib.mkDefault true;
package = pkgs.gnomeExtensions.gsconnect;
};
};

View File

@@ -0,0 +1,92 @@
{
config,
lib,
pkgs,
namespace,
...
}:
let
cfg = config.${namespace}.development;
in
{
options.${namespace}.development = {
enable = lib.mkEnableOption "Common development tools and packages";
includeLanguages = lib.mkOption {
type = lib.types.listOf (
lib.types.enum [
"python"
"c"
"rust"
"nodejs"
]
);
default = [
"python"
"c"
];
description = "Programming languages to include tools for";
};
includeContainers = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Include container development tools";
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages =
with pkgs;
[
# Version control
git
# Build tools
cmake
ninja
binutils
# System utilities
jq
# Text processing
]
++ lib.optionals (builtins.elem "python" cfg.includeLanguages) [
python3
python3Packages.pip
]
++ lib.optionals (builtins.elem "c" cfg.includeLanguages) [
gcc
gdb
]
++ lib.optionals (builtins.elem "rust" cfg.includeLanguages) [
rustc
cargo
]
++ lib.optionals (builtins.elem "nodejs" cfg.includeLanguages) [
nodejs
npm
]
++ lib.optionals cfg.includeContainers [
docker-compose
podman-compose
];
# Enable container support if requested
virtualisation.podman = lib.mkIf cfg.includeContainers {
enable = true;
dockerCompat = true;
autoPrune.enable = true;
defaultNetwork.settings = {
dns_enabled = true;
};
};
# Common development programs
programs = {
nix-ld.enable = lib.mkDefault true;
};
};
}

View File

@@ -2,23 +2,78 @@
config,
lib,
system,
namespace,
...
}:
let
cfg = config.${namespace}.hardware.disko;
isArm = builtins.match "aarch64*" system != null;
rootDisk = "/dev/nvme0n1";
# BTRFS root partition configuration
btrfsRoot = {
name = "btrfs-root";
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ]; # Override existing partition
# Subvolumes must set a mountpoint in order to be mounted,
# unless their parent is mounted
subvolumes = {
"home" = {
mountOptions = [ "compress=zstd" ];
mountpoint = "/home";
};
"root" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/root";
};
"nix" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/nix";
};
"etc" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/etc";
};
"log" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/var/log";
};
};
};
};
# BCacheFS root partition configuration
bcachefsRoot = {
size = "100%";
content = {
type = "bcachefs";
# This refers to a filesystem in the `bcachefs_filesystems` attrset below.
filesystem = "mounted_subvolumes_in_multi";
label = "ssd.ssd1";
extraFormatArgs = [
"--discard"
];
};
};
in
{
config = lib.mkIf isArm {
imports = [ ../options.nix ];
config = lib.mkIf (isArm && cfg.enable) {
disko.devices = {
nodev."/" = {
fsType = "tmpfs";
mountOptions = [
"mode=755"
"defaults"
"size=2G"
];
};
# root disk setup
disk.main = {
type = "disk";
@@ -58,58 +113,33 @@ in
};
};
root = {
name = "btrfs-root";
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ]; # Override existing partition
# Subvolumes must set a mountpoint in order to be mounted,
# unless their parent is mounted
subvolumes = {
"home" = {
mountOptions = [ "compress=zstd" ];
mountpoint = "/home";
};
"root" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/root";
};
"nix" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/nix";
};
"etc" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/etc";
};
"tmp" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/tmp";
};
"log" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/var/log";
};
};
};
root = if cfg.filesystem == "btrfs" then btrfsRoot else bcachefsRoot;
};
};
};
bcachefs_filesystems = lib.mkIf (cfg.filesystem == "bcachefs") {
mounted_subvolumes_in_multi = {
type = "bcachefs_filesystem";
# passwordFile = "/etc/nixos/pool.jwe";
extraFormatArgs = [
"--compression=zstd"
];
subvolumes = {
"/root" = {
mountpoint = "/";
};
"/persistent" = {
mountpoint = "/persistent";
};
"/nix" = {
mountOptions = [
"noatime"
];
mountpoint = "/nix";
};
};
mountpoint = "/partition-root";
};
};
};

View File

@@ -3,5 +3,13 @@ with lib;
{
options.${namespace}.hardware.disko = {
enable = mkEnableOption "enable disko";
filesystem = mkOption {
type = types.enum [
"bcachefs"
"btrfs"
];
default = "btrfs";
description = "Filesystem to use for the root partition";
};
};
}

View File

@@ -9,91 +9,134 @@ let
cfg = config.${namespace}.hardware.disko;
isArm = builtins.match "aarch64*" system != null;
rootDisk = "/dev/nvme0n1";
in
{
imports = [ ../options.nix ];
config = lib.mkIf (cfg.enable && !isArm) {
disko.devices = {
nodev."/" = {
fsType = "tmpfs";
mountOptions = [
"mode=755"
"defaults"
"size=25%"
];
};
# root disk setup
disk.main = {
type = "disk";
device = rootDisk;
imageSize = "32G";
content = {
type = "gpt";
# specify partitions
partitions = {
# /boot
ESP = {
priority = 1;
name = "ESP";
start = "1M";
end = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
name = "btrfs-root";
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ]; # Override existing partition
# Subvolumes must set a mountpoint in order to be mounted,
# unless their parent is mounted
subvolumes = {
"home" = {
mountOptions = [ "compress=zstd" ];
mountpoint = "/home";
};
"root" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/root";
};
"nix" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/nix";
};
"etc" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/etc";
};
"log" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/var/log";
};
};
};
};
};
# BTRFS root partition configuration
btrfsRoot = {
name = "btrfs-root";
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ]; # Override existing partition
# Subvolumes must set a mountpoint in order to be mounted,
# unless their parent is mounted
subvolumes = {
"home" = {
mountOptions = [ "compress=zstd" ];
mountpoint = "/home";
};
"root" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/root";
};
"nix" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/nix";
};
"etc" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/etc";
};
"log" = {
mountOptions = [
"compress=zstd"
"noatime"
];
mountpoint = "/var/log";
};
};
};
};
# BCacheFS root partition configuration
bcachefsRoot = {
size = "100%";
content = {
type = "bcachefs";
# This refers to a filesystem in the `bcachefs_filesystems` attrset below.
filesystem = "mounted_subvolumes_in_multi";
label = "ssd.ssd1";
extraFormatArgs = [
"--discard"
];
};
};
in
{
imports = [ ../options.nix ];
config = lib.mkIf (!isArm && cfg.enable) {
disko.devices = lib.mkMerge [
{
disk = {
main = {
device = rootDisk;
type = "disk";
imageSize = "32G";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "100M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = if cfg.filesystem == "btrfs" then btrfsRoot else bcachefsRoot;
};
};
};
};
bcachefs_filesystems = lib.mkIf (cfg.filesystem == "bcachefs") {
mounted_subvolumes_in_multi = {
type = "bcachefs_filesystem";
# passwordFile = "/etc/nixos/pool.jwe";
extraFormatArgs = [
"--compression=zstd"
];
subvolumes = {
"subvolumes/root" = {
mountpoint = "/";
mountOptions = [
"verbose"
];
};
"subvolumes/persistent" = {
mountpoint = "/persistent";
};
"subvolumes/nix" = {
mountOptions = [
"noatime"
];
mountpoint = "/nix";
};
};
};
};
}
(lib.mkIf (cfg.filesystem == "btrfs") {
nodev."/" = {
fsType = "tmpfs";
mountOptions = [
"mode=755"
"defaults"
"size=25%"
];
};
})
];
};
}

View File

@@ -1,7 +1,12 @@
{ config, lib, ... }:
{
config,
lib,
namespace,
...
}:
with lib;
let
cfg = config.nas-apps.gitea;
cfg = config.${namespace}.services.gitea;
hostAddress = "10.0.1.3";
# localAddress = "10.0.4.18";
# httpPort = 3000;

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
{
options.nas-apps.gitea = {
options.${namespace}.services.gitea = {
enable = mkEnableOption "gitea service";
httpPort = mkOption {

View File

@@ -9,21 +9,6 @@ let
cfg = config.${namespace}.services.home-assistant;
mosquittoPort = 1883;
zigbee2mqttPort = 8080;
# In configuration.nix or a separate file
python-steam = pkgs.python3Packages.buildPythonPackage rec {
pname = "steam";
version = "1.4.4";
pyproject = false;
src = pkgs.fetchPypi {
inherit pname version;
sha256 = "sha256-K1vWkRwNSnMS9EG40WK52NR8i+u478bIhnOTsDI/pS4=";
};
buildInputs = with pkgs.python3Packages; [ setuptools ];
doCheck = false; # no tests in the PyPI tarball
};
in
{
imports = [ ./options.nix ];
@@ -66,6 +51,7 @@ in
"nws"
"ollama"
"onedrive"
"open_router"
"ping"
"radio_browser"
"samsungtv"
@@ -198,7 +184,7 @@ in
gehomesdk
onedrive-personal-sdk
python-roborock
python-steam
pkgs.${namespace}.python-steam
apple-weatherkit
samsungctl
@@ -234,7 +220,7 @@ in
trusted_proxies = [
"172.30.33.0/24"
"10.0.1.4"
"10.0.4.2"
"10.0.1.3"
"10.0.1.18"
"10.0.1.0/24"
];
@@ -256,7 +242,7 @@ in
openhasp = {
plate = {
objects = [
{
{
obj = "p0b1"; # temperature label on all pages
properties = {
"text" = ''{{ states("sensor.thermostat_current_temperature") }}°F'';
@@ -265,8 +251,8 @@ in
{
obj = "p1b2"; # light-switch toggle button
properties = {
"val" = ''{{ 1 if states("light.living_room_lights") == "on" else 0 }}'';
"text" = ''{{ "\uE6E8" if is_state("light.living_room_lights", "on") else "\uE335" | e }}'';
"val" = ''{{ 1 if states("light.living_room_lights") == "on" else 0 }}'';
"text" = ''{{ "\uE6E8" if is_state("light.living_room_lights", "on") else "\uE335" | e }}'';
};
event = {
"up" = {

View File

@@ -1,73 +1,196 @@
{ ... }:
{
# Set up impernance configuration for things like bluetooth
# In this configuration with /etc and /var/log being persistent, only directories outside of that need to be done here. See hardware configuration for all mountpoints.
config,
lib,
namespace,
...
}:
with lib;
let
cfg = config.${namespace}.impermanence;
in
{
imports = [ ./options.nix ];
environment.persistence."/nix/persist/system" = {
hideMounts = true;
directories = [
"/var/lib/bluetooth"
"/var/lib/iwd"
"/var/lib/nixos"
"/var/lib/libvirt"
"/var/lib/waydroid"
"/var/lib/systemd/coredump"
"/etc/NetworkManager/system-connections"
"/var/lib/tailscale"
"/var/lib/homeassistant"
"/var/lib/mosquitto"
"/var/lib/music-assistant"
"/var/lib/postgresql"
"/var/lib/zigbee2mqtt"
config = mkIf cfg.enable {
security.sudo.extraConfig = ''
# rollback results in sudo lectures after each reboot
Defaults lecture = never
'';
system.activationScripts = {
"var-lib-private-permissions" = {
deps = [ "createPersistentStorageDirs" ];
text = ''
mkdir -p /var/lib/private
chmod 0700 /var/lib/private
'';
};
};
boot.initrd.systemd.services.rootfs-cleanup = {
description = "Clean file system root";
wantedBy = [
"initrd.target"
];
after = [
"initrd-root-device.target"
];
before = [
"sysroot.mount"
];
unitConfig.DefaultDependencies = "no";
serviceConfig.Type = "oneshot";
script =
if (hasAttr "/" config.fileSystems) && (config.fileSystems."/".fsType == "btrfs") then
''
# workaround for machines without working rtc battery
# The time may not yet be correctly set, so wait until it is
if [[ $(date '+%s') -lt 1730469314 ]]; then
sleep 30 # this should hopefully be enough
fi
mkdir /btrfs_tmp
mount ${config.fileSystems."/".device} -t btrfs /btrfs_tmp
if [[ -e /btrfs_tmp/root ]]; then
mkdir -p /btrfs_tmp/old_roots
timestamp=$(date --date="@$(stat -c %X /btrfs_tmp/root)" "+%Y-%m-%d_%H:%M:%S")
mv /btrfs_tmp/root "/btrfs_tmp/old_roots/$timestamp"
fi
delete_subvolume_recursively() {
IFS=$'\n'
for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
delete_subvolume_recursively "/btrfs_tmp/$i"
done
btrfs subvolume delete "$1" || rm -rf "$1"
}
for i in $(find /btrfs_tmp/old_roots/ -maxdepth 1 -atime +30); do
delete_subvolume_recursively "$i"
done
btrfs subvolume create /btrfs_tmp/root
umount /btrfs_tmp
''
else if (hasAttr "/" config.fileSystems) && (config.fileSystems."/".fsType == "bcachefs") then
''
# workaround for machines without working rtc battery
# The time may not yet be correctly set, so wait until it is
if [[ $(date '+%s') -lt 1730469314 ]]; then
sleep 30 # this should hopefully be enough
fi
if [[ -e /root_tmp/root ]]; then
mkdir -p /root_tmp/old_roots
timestamp=$(date --date="@$(stat -c %X /root_tmp/root)" "+%Y-%m-%d_%H:%M:%S")
mv /root_tmp/root "/root_tmp/old_roots/$timestamp"
fi
for i in $(find /root_tmp/old_roots/ -maxdepth 1 -atime +30); do
bcachefs subvolume delete $i
done
bcachefs subvolume create /root_tmp/root
''
else
# For tmpfs or other filesystems, do nothing
"";
};
assertions = [
{
directory = "/var/lib/colord";
user = "colord";
group = "colord";
mode = "u=rwx,g=rx,o=";
assertion = hasAttr "/" config.fileSystems;
message = "To use impermanence, you need to define a root volume";
}
{
assertion =
if hasAttr "/" config.fileSystems then
config.fileSystems."/".fsType == "btrfs"
|| config.fileSystems."/".fsType == "bcachefs"
|| config.fileSystems."/".fsType == "tmpfs"
else
false;
message = "rootfs must be btrfs, bcachefs, or tmpfs; not " + config.fileSystems."/".fsType;
}
{
assertion =
if
hasAttr "/" config.fileSystems
&& (config.fileSystems."/".fsType == "btrfs" || config.fileSystems."/".fsType == "bcachefs")
then
any (
t: t == "subvol=root" || t == "subvol=/root" || t == "X-mount.subdir=subvolumes/root"
) config.fileSystems."/".options
else
true;
message = "btrfs or bcachefs rootfs must mount subvolume root";
}
{
directory = "/etc/nix";
user = "root";
group = "root";
mode = "u=rwx,g=rx,o=rx";
}
{
directory = "/var/lib/private/authentik/media";
user = "authentik";
group = "authentik";
mode = "u=rwx,g=,o=";
}
{
directory = "/var/lib/private";
mode = "u=rwx,g=rx,o=";
}
{
directory = "/media/nas";
user = "nas-apps";
group = "jallen-nas";
mode = "u=rwx,g=rx,o=rx";
}
{
directory = "/var/lib/crowdsec";
user = "crowdsec";
group = "crowdsec";
mode = "u=rwx,g=rwx,o=rx";
}
{
directory = "/plugins-storage";
user = "traefik";
group = "traefik";
mode = "u=rwx,g=rwx,o=rx";
assertion = !config.boot.isContainer;
message = "impermanence is not supported in containers";
}
];
files = [
"/etc/machine-id"
];
environment.persistence.${cfg.persistencePath} = {
hideMounts = true;
directories = [
"/var/lib/bluetooth"
"/var/lib/iwd"
"/var/lib/nixos"
"/var/lib/libvirt"
"/var/lib/waydroid"
"/var/lib/systemd/coredump"
"/etc/NetworkManager/system-connections"
"/var/lib/tailscale"
"/var/lib/homeassistant"
"/var/lib/mosquitto"
"/var/lib/music-assistant"
"/var/lib/postgresql"
"/var/lib/zigbee2mqtt"
{
directory = "/var/lib/colord";
user = "colord";
group = "colord";
mode = "u=rwx,g=rx,o=";
}
{
directory = "/etc/nix";
user = "root";
group = "root";
mode = "u=rwx,g=rx,o=rx";
}
{
directory = "/var/lib/private/authentik/media";
user = "authentik";
group = "authentik";
mode = "u=rwx,g=,o=";
}
{
directory = "/var/lib/private";
mode = "u=rwx,g=rx,o=";
}
{
directory = "/media/nas";
user = "nas-apps";
group = "jallen-nas";
mode = "u=rwx,g=rx,o=rx";
}
{
directory = "/var/lib/crowdsec";
user = "crowdsec";
group = "crowdsec";
mode = "u=rwx,g=rwx,o=rx";
}
{
directory = "/plugins-storage";
user = "traefik";
group = "traefik";
mode = "u=rwx,g=rwx,o=rx";
}
];
files = [
"/etc/machine-id"
];
};
};
security.sudo.extraConfig = ''
# rollback results in sudo lectures after each reboot
Defaults lecture = never
'';
}

View File

@@ -0,0 +1,12 @@
{ lib, namespace, ... }:
with lib;
{
options.${namespace}.impermanence = {
enable = mkEnableOption "enable impermanence";
persistencePath = mkOption {
type = types.str;
default = "/nix/persist/system";
description = "Path to the persistence directory";
};
};
}

View File

@@ -0,0 +1,53 @@
{
config,
lib,
pkgs,
namespace,
...
}:
let
cfg = config.${namespace}.monitoring;
in
{
options.${namespace}.monitoring = {
enable = lib.mkEnableOption "Common monitoring and system tools";
includeNetworkTools = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Include network monitoring tools";
};
includePerformanceTools = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Include performance monitoring tools";
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages =
with pkgs;
[
# Basic system monitoring
htop
]
++ lib.optionals cfg.includePerformanceTools [
glances
nmon
iotop
]
++ lib.optionals cfg.includeNetworkTools [
speedtest-cli
iftop
nethogs
tcpdump
wireshark-cli
];
# Enable common system services for monitoring
programs.screen.enable = lib.mkDefault true;
};
}

View File

@@ -9,113 +9,45 @@ let
cfg = config.${namespace}.network;
in
{
options.${namespace}.network = with types; {
hostName = lib.mkOption {
type = str;
default = "nixos";
description = "The hostname of the system.";
};
ipv4 = {
method = mkOption {
type = types.str;
default = "auto";
};
address = lib.mkOption {
type = types.str;
default = "10.0.1.1";
};
gateway = lib.mkOption {
type = types.str;
default = "10.0.1.1";
};
dns = lib.mkOption {
type = types.str;
default = "10.0.1.1";
};
};
};
imports = [
./options.nix
];
config = {
networking = {
hostName = lib.mkForce cfg.hostName;
# Enable Network Manager
networkmanager = {
enable = true;
wifi.powersave = lib.mkDefault false;
settings.connectivity.uri = lib.mkDefault "http://nmcheck.gnome.org/check_network_status.txt";
ensureProfiles = {
environmentFiles = [
config.sops.secrets.wifi.path
];
# Use networkd if enabled
useNetworkd = lib.mkIf cfg.useNetworkd true;
profiles = {
"Joey's Jungle 6G" = {
connection = {
id = "Joey's Jungle 6G";
type = "wifi";
};
ipv4 =
if (cfg.ipv4.method == "auto") then
{
method = "auto";
}
else
{
address1 = cfg.ipv4.address;
dns = cfg.ipv4.dns;
gateway = cfg.ipv4.gateway;
method = "manual";
};
ipv6 = {
addr-gen-mode = "stable-privacy";
method = "auto";
};
wifi = {
mode = "infrastructure";
ssid = "Joey's Jungle 6G";
};
wifi-security = {
key-mgmt = "sae";
psk = "$PSK";
};
};
"Joey's Jungle 5G" = {
connection = {
id = "Joey's Jungle 5G";
type = "wifi";
};
ipv4 =
if (cfg.ipv4.method == "auto") then
{
method = "auto";
}
else
{
address1 = cfg.ipv4.address;
dns = cfg.ipv4.dns;
gateway = cfg.ipv4.gateway;
method = "manual";
};
ipv6 = {
addr-gen-mode = "stable-privacy";
method = "auto";
};
wifi = {
mode = "infrastructure";
ssid = "Joey's Jungle 5G";
};
wifi-security = {
key-mgmt = "sae";
psk = "$PSK";
};
};
};
};
# Set default gateway and nameservers if in manual mode
defaultGateway = lib.mkIf (cfg.ipv4.method == "manual") {
address = cfg.ipv4.gateway;
interface = lib.mkIf (cfg.ipv4.interface != "") cfg.ipv4.interface;
};
nameservers = lib.mkIf (cfg.ipv4.method == "manual") [ cfg.ipv4.dns ];
# Set hostId if provided
hostId = lib.mkIf (cfg.hostId != "") cfg.hostId;
# Configure NAT if enabled
nat = lib.mkIf cfg.nat.enable {
enable = true;
internalInterfaces = cfg.nat.internalInterfaces;
externalInterface = cfg.nat.externalInterface;
enableIPv6 = cfg.nat.enableIPv6;
};
# Configure firewall
firewall = {
enable = cfg.firewall.enable;
allowPing = cfg.firewall.allowPing;
allowedTCPPorts = cfg.firewall.allowedTCPPorts;
allowedUDPPorts = cfg.firewall.allowedUDPPorts;
trustedInterfaces = cfg.firewall.trustedInterfaces;
# Default port ranges for KDE Connect
allowedTCPPortRanges = [
{
from = 1714;
@@ -123,7 +55,70 @@ in
}
];
allowedUDPPortRanges = config.networking.firewall.allowedTCPPortRanges;
# Extra firewall commands
extraCommands = lib.mkIf (cfg.extraFirewallCommands != "") cfg.extraFirewallCommands;
};
# Configure iwd if enabled
wireless.iwd = lib.mkIf cfg.iwd.enable {
enable = true;
settings = cfg.iwd.settings;
};
# Configure NetworkManager
networkmanager = mkMerge [
# Disable NetworkManager when iwd is enabled
(mkIf cfg.iwd.enable {
enable = mkForce false;
wifi.backend = mkForce "iwd";
})
# Enable NetworkManager when wifi is enabled and iwd is disabled
(mkIf (cfg.wifi.enable && !cfg.iwd.enable) {
enable = true;
wifi.powersave = cfg.wifi.powersave;
settings.connectivity.uri = mkDefault "http://nmcheck.gnome.org/check_network_status.txt";
# Configure WiFi profiles if any are defined
ensureProfiles = mkIf (cfg.wifi.profiles != { }) {
environmentFiles = [
config.sops.secrets.wifi.path
];
profiles = mapAttrs (name: profile: {
connection = {
id = name;
type = "wifi";
};
ipv4 =
if (cfg.ipv4.method == "auto") then
{
method = "auto";
}
else
{
address1 = cfg.ipv4.address;
dns = cfg.ipv4.dns;
gateway = cfg.ipv4.gateway;
method = "manual";
};
ipv6 = {
addr-gen-mode = "stable-privacy";
method = "auto";
};
wifi = {
mode = "infrastructure";
ssid = profile.ssid;
};
wifi-security = {
key-mgmt = profile.keyMgmt;
psk = profile.psk;
};
}) cfg.wifi.profiles;
};
})
];
};
};
}

View File

@@ -0,0 +1,162 @@
{
lib,
namespace,
...
}:
with lib;
{
options.${namespace}.network = with types; {
hostName = lib.mkOption {
type = str;
default = "nixos";
description = "The hostname of the system.";
};
ipv4 = {
method = mkOption {
type = types.str;
default = "auto";
description = "Method for IPv4 configuration (auto or manual).";
};
address = lib.mkOption {
type = types.str;
default = "10.0.1.1/24";
description = "IPv4 address with subnet mask (e.g., 10.0.1.1/24).";
};
gateway = lib.mkOption {
type = types.str;
default = "10.0.1.1";
description = "IPv4 default gateway.";
};
interface = lib.mkOption {
type = types.str;
default = "";
description = "Interface for the default gateway (required when using networkd).";
};
dns = lib.mkOption {
type = types.str;
default = "10.0.1.1";
description = "IPv4 DNS server.";
};
};
useNetworkd = mkOption {
type = types.bool;
default = false;
description = "Whether to use systemd-networkd for networking.";
};
nat = {
enable = mkOption {
type = types.bool;
default = false;
description = "Whether to enable NAT.";
};
internalInterfaces = mkOption {
type = types.listOf types.str;
default = [ ];
description = "List of internal interfaces for NAT.";
};
externalInterface = mkOption {
type = types.str;
default = "";
description = "External interface for NAT.";
};
enableIPv6 = mkOption {
type = types.bool;
default = false;
description = "Whether to enable IPv6 NAT.";
};
};
firewall = {
enable = mkOption {
type = types.bool;
default = true;
description = "Whether to enable the firewall.";
};
allowPing = mkOption {
type = types.bool;
default = true;
description = "Whether to allow ICMP ping.";
};
allowedTCPPorts = mkOption {
type = types.listOf types.port;
default = [ ];
description = "List of allowed TCP ports.";
};
allowedUDPPorts = mkOption {
type = types.listOf types.port;
default = [ ];
description = "List of allowed UDP ports.";
};
trustedInterfaces = mkOption {
type = types.listOf types.str;
default = [ ];
description = "List of trusted interfaces.";
};
};
wifi = {
enable = mkOption {
type = types.bool;
default = true;
description = "Whether to enable WiFi configuration.";
};
powersave = mkOption {
type = types.bool;
default = false;
description = "Whether to enable WiFi power saving.";
};
profiles = mkOption {
type = types.attrsOf (
types.submodule {
options = {
ssid = mkOption {
type = types.str;
description = "SSID of the WiFi network.";
};
psk = mkOption {
type = types.str;
default = "$PSK";
description = "PSK environment variable for the WiFi password.";
};
keyMgmt = mkOption {
type = types.str;
default = "sae";
description = "Key management type (e.g., sae, wpa-psk).";
};
};
}
);
default = { };
description = "WiFi network profiles.";
};
};
hostId = mkOption {
type = types.str;
default = "";
description = "Host ID for ZFS and other services.";
};
iwd = {
enable = mkOption {
type = types.bool;
default = false;
description = "Whether to enable iwd for wireless networking.";
};
settings = mkOption {
type = types.attrs;
default = { };
description = "Settings for iwd.";
};
};
extraFirewallCommands = mkOption {
type = types.str;
default = "";
description = "Extra commands for the firewall.";
};
};
}

View File

@@ -3,10 +3,12 @@
nix = {
settings = {
substituters = [
"https://nixos-raspberrypi.cachix.org"
"https://nix-community.cachix.org"
"https://cache.nixos.org/"
];
trusted-public-keys = [
"nixos-raspberrypi.cachix.org-1:4iMO9LXa8BqhU+Rpg6LQKiGa2lsNh/j2oiYLNOQ5sPI="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
warn-dirty = lib.mkForce false;

View File

@@ -8,18 +8,6 @@
with lib;
let
cfg = config.${namespace}.services.ollama;
llamaPackage = pkgs.llama-cpp.overrideAttrs (_old: {
src = pkgs.fetchFromGitHub {
owner = "ggml-org";
repo = "llama.cpp";
rev = "b4920";
sha256 = "sha256-SnQIeY74JpAPRMxWcpklDH5D4CQvAgi0GYx5+ECk2J4=";
};
# Optionally override other attributes if you need to
# version = "my-fork-version";
# pname = "llama-cpp-custom";
});
in
{
imports = [ ./options.nix ];

View File

@@ -1,11 +1,12 @@
{
lib,
config,
namespace,
...
}:
with lib;
let
cfg = config.nas-apps.orca-slicer;
cfg = config.${namespace}.services.orca-slicer;
in
{
imports = [ ./options.nix ];

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
{ lib, namespace, ... }:
with lib;
{
options.nas-apps.orca-slicer = {
options.${namespace}.services.orca-slicer = {
enable = mkEnableOption "orca slicer docker service";
autoStart = mkOption {

View File

@@ -0,0 +1,92 @@
{
config,
lib,
pkgs,
namespace,
...
}:
let
cfg = config.${namespace}.hardware.raspberry-pi;
in
{
options.${namespace}.hardware.raspberry-pi = {
enable = lib.mkEnableOption "Raspberry Pi common configuration";
variant = lib.mkOption {
type = lib.types.enum [
"4"
"5"
];
description = "Raspberry Pi variant (4 or 5)";
};
};
config = lib.mkIf cfg.enable {
# Common Raspberry Pi packages
environment.systemPackages =
with pkgs;
[
libraspberrypi
raspberrypi-eeprom
raspberrypifw
raspberrypiWirelessFirmware
raspberrypi-armstubs
]
++ lib.optionals (cfg.variant == "4") [
i2c-tools
]
++ lib.optionals (cfg.variant == "5") [
erofs-utils
fex
squashfuse
squashfsTools
];
# Common nixpkgs overlays for Raspberry Pi
nixpkgs.overlays = lib.mkAfter [
(_self: super: {
# This is used in (modulesPath + "/hardware/all-firmware.nix") when at least
# enableRedistributableFirmware is enabled
inherit (super) raspberrypiWirelessFirmware;
# Some derivations want to use it as an input,
# e.g. raspberrypi-dtbs, omxplayer, sd-image-* modules
inherit (super) raspberrypifw;
})
];
# Common Bluetooth configuration
systemd.services.btattach = {
before = [ "bluetooth.service" ];
after = [ "dev-ttyAMA0.device" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.bluez}/bin/btattach -B /dev/ttyAMA0 -P bcm -S 3000000";
};
};
# Common hardware settings
hardware.i2c.enable = lib.mkIf (cfg.variant == "4") true;
# Pi 5 specific settings
hardware.graphics.enable32Bit = lib.mkIf (cfg.variant == "5") (lib.mkForce false);
zramSwap.enable = lib.mkIf (cfg.variant == "5") true;
# Pi 5 specific system tags
system.nixos.tags = lib.mkIf (cfg.variant == "5") (
let
bootCfg = config.boot.loader.raspberry-pi;
in
[
"raspberry-pi-${bootCfg.variant}"
bootCfg.bootloader
config.boot.kernelPackages.kernel.version
]
);
# Common programs
programs.kdeconnect.enable = lib.mkDefault false;
# Root user shell configuration
users.users.root.shell = pkgs.zsh;
};
}

View File

@@ -1,7 +1,7 @@
{ lib, ... }:
let
defaultSops = (lib.snowfall.fs.get-file "secrets/pi4-secrets.yaml");
# sharedSops = (lib.snowfall.fs.get-file "secrets/secrets.yaml");
# defaultSops = (lib.snowfall.fs.get-file "secrets/pi4-secrets.yaml");
defaultSops = (lib.snowfall.fs.get-file "secrets/secrets.yaml");
in
{
# Permission modes are in octal representation (same as chmod),

View File

@@ -19,7 +19,7 @@ let
cacheUrl = "http://${serverIp}:9012";
cloudUrl = "http://${config.containers.nextcloud.localAddress}:80";
giteaUrl = "http://${config.containers.gitea.localAddress}:${toString config.containers.gitea.config.services.gitea.settings.server.HTTP_PORT}";
hassUrl = "http://homeassistant.local:8123";
hassUrl = "http://nuc-nixos.local:8123";
immichUrl = "http://${serverIp}:${toString config.services.immich.port}";
jellyfinUrl = "http://${serverIp}:8096";
jellyseerrUrl = "http://${config.containers.jellyseerr.localAddress}:${toString config.containers.jellyseerr.config.services.jellyseerr.port}";

View File

@@ -8,6 +8,18 @@
with lib;
let
cfg = config.${namespace}.user;
isRoot = (cfg.name == "root");
# Common SSH keys used across systems
commonSshKeys = [
# MacBook
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCw9zq8DLGByI5v2gAn95hKNyOsm3g61a2buxu2BBMFysQJgmZPCCLUqRJKhSM5Vm/JOgsAmdpRBRZQoHD+6S844CJHb4v4VIbjkyQgYCuM7Rst2IOZ5QybvsA2/D0nwytZ+HXQqDj2AagUYDbz0gyyIHkDQ5YGBMkvkWz/h1Vci6aoBM7VihEDM4KlWoTVuPeASGM8r5IZ2FS83Djbqo4ov6AYvLMrKB9Z7hmFgH6R3LE0gxOkzbGVXtSuvJyrjvgytoT22UhATjjxSQ9D+YJXXkQoB3lUdg8OoIquUPjMZpl4mR8ffvseWPfcvD1XlD5t+TOHFqKpESO547tlOBYhdpew+NSgAXpamCU6oyV8tDCywLQu2ucxHRn78u6WXzWHkDtffdhzmk6TZaPhWqVHuTGjR4higBgGqUfSaKOMszt+FDRZAr3HtuQ2+zJ8bowK9fW5OqilTtK2HtQqroD9ApegDNbqOz6kGy5IycSXvqPURy/M4lxZxbtBPuemcJs= mattjallen@MacBook-Pro.local"
# Desktop Windows
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDZ2PYPjZddOzR8OJj16G88KcUhCDLkvrEmpUQP0wKHDUuA27HQQ2ORo66asadwGHY3k1VDZ1ei9l9H++SIIeKOaaUr5yZdktvj4POUNtbd9ZhcS7sZU7BSF+NMDM+h3tImh6z0S7mWvRQOUv3ZM+ZER+5xTWJVG1OOJEpb1drxJk6Qz0wbZKSR7TPNFBLLXlVy7hkNYf07RtDyhCCxNB3hJfa8c+oztnWumwDhDQWLqiUXWIU2QH6iRLGl/WYnujtNvVVaV/Hn3JJkS6MM9dnV3cpoIO0+J7+WfsN9rZ0wXt5yY3GhiGXwmcO5eYVli8lHlLWtK7aYSETyry6CBsLbojzOQO5rSqhpwfF2njAAFAQU0UjLc8PahisIuFKCwHH4iyXXOagiv5K1Mc/0Ak+WhhMPee6vV2p7NTyNpXRvouDbWy5cSRH31WgQ9fK5mIGe5v8nGGqtEhUubUkiOgP+H3UbT2V/nTv/TFKdJcKw+WmizvTrxBmaMjWALlkYl+s= mattl@Jallen-PC"
# Desktop NixOS
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPTBMydhOc6SnOdB5WrEd7X07DrboAtagCUgXiOJjLov matt@matt-nixos"
];
in
{
options.${namespace}.user = with types; {
@@ -41,11 +53,58 @@ in
default = null;
description = "Path to the password file for this user account";
};
sshKeys = lib.mkOption {
type = listOf str;
default = [ ];
description = "List of SSH public keys for the user.";
};
enableCommonSshKeys = lib.mkOption {
type = bool;
default = true;
description = "Whether to include common SSH keys used across systems.";
};
uid = lib.mkOption {
type = int;
default = if isRoot then ids.uids.root else 1000;
description = "The user ID for the user account.";
};
packages = lib.mkOption {
type = listOf package;
default = [ ];
description = "List of packages to install for this user.";
};
linger = lib.mkOption {
type = bool;
default = false;
description = "Whether to enable systemd user service persistence.";
};
password = lib.mkOption {
type = nullOr str;
default = null;
description = "Plain text password for the user (development only).";
};
hashedPassword = lib.mkOption {
type = nullOr str;
default = null;
description = "Hashed password for the user.";
};
mutableUsers = lib.mkOption {
type = bool;
default = false;
description = "Whether users are mutable (can be modified after creation).";
};
};
config = {
users.mutableUsers = cfg.mutableUsers;
users.users.${cfg.name} = {
inherit (cfg) name;
inherit (cfg)
name
uid
linger
packages
;
extraGroups = [
"wheel"
@@ -64,14 +123,27 @@ in
"power"
"nix"
"i2c"
] ++ cfg.extraGroups;
]
++ cfg.extraGroups;
group = "users";
home = "/home/${cfg.name}";
isNormalUser = true;
isNormalUser = (!isRoot);
isSystemUser = isRoot;
shell = lib.mkForce pkgs.zsh;
uid = 1000;
hashedPasswordFile = cfg.passwordFile;
} // cfg.extraOptions;
# SSH keys - combine user-specific and common keys
openssh.authorizedKeys.keys = cfg.sshKeys ++ (lib.optionals cfg.enableCommonSshKeys commonSshKeys);
# Authentication - priority: passwordFile > hashedPassword > password
hashedPasswordFile = lib.mkIf (cfg.passwordFile != null) cfg.passwordFile;
hashedPassword = lib.mkIf (
cfg.passwordFile == null && cfg.hashedPassword != null
) cfg.hashedPassword;
password = lib.mkIf (
cfg.passwordFile == null && cfg.hashedPassword == null && cfg.password != null
) cfg.password;
}
// cfg.extraOptions;
};
}

View File

@@ -0,0 +1,45 @@
{
lib,
fetchurl,
vscode-utils,
pkgs,
}:
let
version = "0.0.49";
publisher = "jeanp413";
name = "open-remote-ssh";
in
vscode-utils.buildVscodeMarketplaceExtension {
mktplcRef = {
inherit name publisher version;
};
vsix = fetchurl {
url = "https://open-vsx.org/api/${publisher}/${name}/${version}/file/${publisher}.${name}-${version}.vsix";
sha256 = "sha256-QfJnAAx+kO2iJ1EzWoO5HLogJKg3RiC3hg1/u2Jm6t4=";
};
unpackPhase = ''
${pkgs.unzip}/bin/unzip -q $src
'';
meta = with lib; {
description = "Use any remote machine with a SSH server as your development environment";
longDescription = ''
The Open Remote SSH extension allows you to open a remote folder on any
remote machine, virtual machine, or container with a running SSH server
and take full advantage of VS Code's feature set. This is an open-source
alternative to Microsoft's proprietary Remote SSH extension, designed to
work with VSCodium and other open-source VS Code variants.
Note: This extension requires enabling proposed APIs in VSCodium/Code-OSS.
You need to add "jeanp413.open-remote-ssh" to the "enable-proposed-api"
array in ~/.vscode-oss/argv.json
'';
homepage = "https://github.com/jeanp413/open-remote-ssh";
changelog = "https://github.com/jeanp413/open-remote-ssh/releases";
license = licenses.mit;
platforms = platforms.all;
};
}

View File

@@ -1,5 +1,5 @@
{ python3Packages, fetchFromGitHub, ... }:
python3Packages.buildPythonPackage rec {
python3Packages.buildPythonPackage {
pname = "pipewire-python";
version = "0.2.3";
format = "pyproject";
@@ -12,6 +12,10 @@ python3Packages.buildPythonPackage rec {
};
buildInputs = with python3Packages; [ flit-core ];
nativeBuildInputs = with python3Packages; [ build wheel ];
doCheck = false; # no tests in the PyPI tarball
nativeBuildInputs = with python3Packages; [
build
wheel
];
doCheck = false;
}

View File

@@ -0,0 +1,16 @@
{ python3Packages, fetchPypi, ... }:
python3Packages.buildPythonPackage rec {
pname = "steam";
version = "1.4.4";
pyproject = false;
src = fetchPypi {
inherit pname version;
sha256 = "sha256-K1vWkRwNSnMS9EG40WK52NR8i+u478bIhnOTsDI/pS4=";
};
buildInputs = with python3Packages; [ setuptools ];
doCheck = false; # no tests in the PyPI tarball
}

View File

@@ -11,7 +11,10 @@ python3Packages.buildPythonPackage rec {
sha256 = "sha256-rEmWsCIBGNmDEecVT8O9O5/E0WVpTfA7amFI70DEmiI=";
};
buildInputs = with python3Packages; [ poetry-core hatchling ];
buildInputs = with python3Packages; [
poetry-core
hatchling
];
nativeBuildInputs = with python3Packages; [
aiodns

View File

@@ -1,5 +1,6 @@
jallen-nas:
admin_password: ENC[AES256_GCM,data:0XUblR800UyliA8JfYUZbncDRxiU6eoTaf3i80+OCwJ/31oBhSqj9OtgYeRg3IyURwik1Nk/609IuHjIhly3mgTjOD6Hpzxpag==,iv:0yO3z8ItHRQFeI9JOnFTKhKVHi5u9cMtpglFRlkvYLE=,tag:iUd79iWAJQ9iqP0qolSwfA==,type:str]
nas_pool: ENC[AES256_GCM,data:sx78UwPhgklzf8e/Tjw/2esAspMFTnSj3ahEtv3btVLpotwE9UDMtFGPkkh8r82mGLmzC3dRsWYaOAFW4ioVBpmiochpdAR+QfmOBgsOFQPKdqlF0DDXcElT+qxhJ9Uoo26v0XZwaTdplI9f8OTxCPZtihX4Y27sFNjhCM9loN+IJKqxPFNaK4RRJtFQL5sG2OaiPkSd/Wf082RTW12CNWYRY2RSTCqzKB7YclTcAAwdj72uJTER7uU8Fr4/PpUN7AJ/GGK9OetY5QbkieelwESBqQR12CLCF1/d82CaN/lL8jtuc3Q7QFsRGr28fHBNqOywkJ6aNxxZCKS0fjQGpasroQtt5fv8pNMs7Gu67SiuKGb/z0wKdd5c03sxD/42bSf2aM1vUW8Bm6PGGVk8OT0UWMA9FhbWeVr3HSb6gFktOlC3oRLGPUU3WcdkLAmqCWtSQuVHNx6/s/JDj8UyWqyDCu/7afxKX9gt+I6G+3EWmVs/fi91+tpYd5hyhTBFmOoC+wooVqnGVXtM7cUbWTJS4Ua82zcroHj+KKoX5sOOzOXfI9TVRLccPYlbdSY46467+ycEXsc7cxnigKerO29GhFFoC3pGxwwBcMYWDl8466H7cCDpYohPdY/q9dYuB3LBEzFizmANQ/kGEkZt1I1fydO+sN5ZcTyhnOCHQFI9lYPDsQ6Gztb4n8PEHYdHbp1Zue0p82H8hhgC4v2ne2GezdiYRCl1y2MZvF8CgkwAw/PkX2cb8CAMuG0ZHtOLFwUPjPk2KCe8G6yK0SekE/Zm36wlPz8yLd9S2dLRBcxXZiQEHXqONjVGf3fhbE8QyDZ83xITlRTnbQt1rF0vPrgeJD/m3u4QMo1BrF6EIsCCesQrNPLRRGddh7uyhVX9OKy633OKxLy2Gt1VjfuE77ZaPmNgIjk1geCXuKUFD/BC9DJiknVuajS8tFHIIh7YYUBZY/Q=,iv:ZvI+1L4Zwgwz0t++fvVxX7HXXuS8G8DcKz7WDlq9oS8=,tag:sbXluJh9CQhJH11gk2Ohfg==,type:str]
ups_password: ENC[AES256_GCM,data:tYuJ9nU3E2/Ko6Y=,iv:lQq+g68lKCp1rmPvS/84xGIXHxD9zY5zZrrjEJlY8Hs=,tag:p6McEr+sXGAQyMAz1Kaxfw==,type:str]
authentik-env: ENC[AES256_GCM,data:AzHHGyhoyMp/ebnK6LQ5apBUhQT04SPJrtA6XcdaQ38C+fYuG2ph2iWFb+giafxCe8IXWAYT8CWoeqcspM7CPSAAKgqfVaPhMvjXqLxCY/rpegb5jBD1U6tURhPsH3ADrERk+kCmTV2eUpuV+nluiGM+fRdwhB0zu378HKwhXCpSO4L24aXhe9pxxxaTQzncWH6zW5iaRdouDVr1bAUzLi9BpnmS0ZK/rfLq2whErCeN++Srx6aCgwJ7jaqetBglQkIl3YG6flS8u3vsKtI+RVaNJ5tzrWR/qv0vBy8y1PZEuuXZdiHjn1hjiPE1T31j2+aQdbX70RaJfIt6E4lVtArQHv8PTUDxUoxcnUv52xLTStT5/UdIlNoZjPMwvaknpK7Z0uw9w4j76gmgk06xsxoCpnXIGTm1QpGqviBhgfNs5Va/qi4MBfByaym3UAz9LPHs4keuvJNN8dS0q5OMnRswl14PjIb1MIKB/QCVHvb4hO7eIRiWOkA7nb9LP/y1mjAYslr+I+GNpU8oIYTAvKoMS7ZgC49RoLWytAXUru2I7CqDR9zgPzlDQ9gLPoFKw2uKulpAy0ayQWPcgPA2CFmF+5zdINNSNKn0gRZ/2RTc3DiWmzo4P13EmrOwvkWCkiswFu1d6ctKZFhQnfPuj9LRGp/Os55JpLrreSyRJug6lgR4bPdC3x8sbxNmb5S2Y+4aFfgPXfdCdXs5b+8j28d1d4EoOO/arUzNADz9ODD5esb2g8UC2QtQd0RRYX/qmiM=,iv:YKvFxz3M8HKlg56JfN6uv8hvCFlEbhBkaSQz1v9l3zk=,tag:rz7UixSDqOXH7Ga6mkVYAw==,type:str]
traefik:
@@ -142,7 +143,7 @@ sops:
WmFoMmlNbXUvREZESnU3ZzFMbkNGVjAKGLZWo3E9098b2kOn77U/CGQpo4/mqSXY
5+rZJuC7Iqh+VDW519Cf1go+0k05rgree3IqHXN2/KHX+pC0L6CkyA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-06-11T03:10:55Z"
mac: ENC[AES256_GCM,data:01jMwSJK3zW0vltFj06veiAsJBBSKrN5SJuU87/sfhjrfZHWQ/fOITK4Ihz/d9/J2kA9bMJEM50C3cSBaakdYW4RDkL1P83aOsJLjp/c3WjJ8BApDdzoQzUvkjIVt7qd/jNg13c8d+ofBkeSsZ8T3COrOHCc3RgG961T4Ij9WBY=,iv:cNJUQ8ZEPKPKfUlTYD/Zlfomev3ZC/mnP90Me3ycjMc=,tag:LluaNhFpQbuql208sEyPkA==,type:str]
lastmodified: "2025-08-20T23:28:44Z"
mac: ENC[AES256_GCM,data:ArFy5LYoBr6AhBQkUxGqe2k1wQn/XwRkZYRDTVmFIuCpNWYgTRtUYYBnbvn2FPvpq4avm65/iUKTkvkSnSWMPmj4oCAVOTgZxG0eOlC8c6XEFq0ir9Nz6K8oyStxTcDWfiYlHAxx7EBeGIBrj8TcQHuE2P/PxdPYEJDbnohCRnY=,iv:1tyLcAdfvO/kDE2yZ4kU3l3xCMUVEf9Z2z9X8gjacLY=,tag:H0vcvlmzTaatgtnoiyDvSw==,type:str]
unencrypted_suffix: _unencrypted
version: 3.10.2

View File

@@ -38,128 +38,128 @@ sops:
- recipient: age157jemphjzg6zmk373vpccuguyw6e75qnkqmz8pcnn2yue85p939swqqhy0
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkcmtYM1FKMXd1Y1EvMVhk
UFNSZTVWUGFiKzRsUVhvQWJGa2VJVkxjNkJBCmZBTjNTZlN2NGRZNm8xQkZKazU2
UkRwZjFtYnRTcUlva2k1S05qNzVhMUkKLS0tIDZHR2tWS0RvZGNEZ3hpeVJjUkxN
OHlHdVdGcnpaYnkrRHU4b2xzclN0MlUK53/V+ITz49SUgxVeaxBSh7yITBEqWPlB
uUIkdKVq93XFfpTG4st00Uj6oow9M+QB43bG/hXjfiv7ALDEfT3XaA==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSZnFYM2JnRXd2emJFSm5h
Q3RSYktiLzMxRlBKZnpVOStLaXNHNmtLVVgwCkM5UnZEN24wOUVuNnZyMVhoM3Bx
SFZhQWhEbHB3Q1NRSUt5S3lBYmtWTFEKLS0tIE5uTTB6Q0hrWUtodXkzZkZkSHpG
Q0t2cVV3WFAzZjNrdDZiS1Q5YjJybU0K6biA2WO5121mlrOXI7cuhsfFUCB5fAOi
scwVKg77FjgECrjJIWwlzGrznW5pJaQ5ZIfHMW+wt5zVBTHfwU9Oyw==
-----END AGE ENCRYPTED FILE-----
- recipient: age13g9a4d4jrvckfddpgn8sm4kjtzajr67le56pfdg78ktr5pd09phq32j89u
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkRjVWY0pKNzB3QmpXRGJM
MEticE10YWhKNm94R1VIbWpwUklUV0R5TkZVCkR1bmZEV0hOQWt6WXRlMk9mYmlx
Q3cwMEdGeGs3RGd2NGIzTUhFb2tWVjgKLS0tIFNmVjdXdjBmZGZTYy9RZWxQbFo1
YUp0SHhqejc3dndlOFA4dDB4aXFUb0kKcKAVGwr8t2nsFNcmrkwpYAhdjfdsV9oN
YZqicWUc07Lu05vMS8MomwupAtkwxy+MswWd6jD0nzp8CIfhOgOpjA==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNZGxsMHl0cHFObkJRL2Vu
MThuK24vQ2JtL2ZucG5LYXBzL2pJTjMwcURFClZsMkZCWHptZlpDakl3NW1TRVhN
Q3dkK0FVblZxamhETEhza2dyV0hoQzAKLS0tIFlhK0c3czRCbGU4eWZNRXRNcjN4
MTkvZ3pVa2VrOGlWZzI1MEh6azJXRUUK1g78WdC8kugPyfSNmE/abme0OAux8r6F
HzLnPgFdqbmxMqgEx1kqV10dAdp6JAy31zXHoX/Gb0dK271gRo/1Wg==
-----END AGE ENCRYPTED FILE-----
- recipient: age1wpvfpv5n32lruk7c0da4uaeapsmhjxdvg8z4ljehn06l6g2y0e0sum404l
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNQUpETHZvM0Y3ZHQ1a082
b01xWDI5d0NpREhOd3JVQ1cyRlhzakFTR0FRCmJrMTRYTWhhWERlbFU0cGlqZ010
QzdHSWlxZEtodVB0cXdiYi9MM0h4WW8KLS0tIGNLNlVERk1RY3haaWpRd1RjTkY0
NkhqampldGlwNi85NUxLUFNKaXBwRW8K6DLM1IE1pxpuQ/NA/ywLCocqVxQ/4a9a
e8aNYcwXaD628b5HLotHtgzrjA37txAzfqElzvamOxL8/1IgvZVauQ==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2R3lHQ3hzQW9SNWNiS3RZ
VTFUemJRUnRLcHNWdVVvblRrMk1rVHRDY0JnCjVSWFRYTGorWTRDOU9SMXNhV3BP
akRUaytZYThHMS9HK2lQUTBBQ1FWWGMKLS0tIERnTzBuSlJLVmtSNW8wcHhxWVlt
TnJmQTNFWWtRZ0VQdkZmZ0pjS0dLMm8K5szoKD6/RvBrkUL2P4asRzCq+718jDaz
wuTQ+6Jqcso3caHbJpcf1VlkE/HjaI64cVMlt97j53XT02JyZmxiWw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1jv8ap5zwa49ftv0gg7wqf5ps0e68uuwxe2fekjsn0zkyql964unqyc58rf
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSdUF5VWhVNXJWMWxwb2hI
Mis5aGtXNUdWVnNBMjg2L050eXhYUWRVL0hFCmlSQWtLTlFGNFVITmt6dDcvQ0Rp
cEhVTS83eEpoRitPZ29mQlZnT3RJVmsKLS0tIC94ZHBNRmpQdW5EM1NwQ2tMWFp0
RWIxTnVEZDY4TnZXeDRFeXBkSWluZGcKtvV5fbMxUKZ2dp/unsZcsv+V51vNZkjm
LanIBZBYfbeki2C8b+VPE0ELwtzRQNnGz7DrEzKIV3J2RIscAwwRGQ==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKRnpyRy9Ra2RSeU96K2Qz
MWFjcjZVQnk0NE9QcDhSengzaGVSczFkNFUwCkNuMEhQNENTZUJwYUVVN2FYem5F
OWIrKzRILys5T1FmSDZYRExxT3A1N0EKLS0tIFVsT2NiamY3S0dKL2FwTjZCSFJ6
SjJIVElqeGRqem1PQ2g4WWczSExRQ1kKrV69W1SSfKfE+MicJ7bCTsTTy3op+tWG
in6fgwL24wmk5WYClVQuXhYzPH/GyRTLptzYad+lvOUj2CorwUJ4UA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1pm3fehmmk0vmnrscz9vm96rakn46aaldr5ydpscmde3v9x0k3faswwdzxs
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZUXRFS1RuMytoSmhEY1Uy
b2pzMHhmcmVrcE5QeVd2d3JmN05QQzZ2ZmhzCmcyZDkrSVlpWjRZNTJlek02c29U
b3ZHdWpSNWhjT1BrbGRXVDRUdW9aZTQKLS0tIG53Y0xwZnd5OVJvb0FMNmQzTS9u
aTR0THNqQ3ZGMjMwb2c5aUJVNTRoVGcKusofba5Zk+GQYtvPVFk8kkQjinF4gn3L
rzLJuPql7U/nnFx4obRJEQhrJFXlVrwMmvEyi0Tyz55yH1Y6vZ39aA==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiODBnN2lXUEl2RTVqV3lH
ZEJMbG5sT0Nnd09rNlZOczhxSnNsZXM2SW1vCkR3Zlkvci9UK1d0R2tqbmZlU21l
U2V6S3FhVXVUQXVnc3ZvZW1LYkNOdjgKLS0tIGtoRVRlRVJRTkZCVE9CWTdsb3ZB
Umo1RzBLbkk2c2RmdGN5WWpJeEljMFUKeOqAB6CbuDYc8aNlcQQhxIO1Ms2ding4
JeC/SGoHYIpaKR4oIXz0Lr/Xlu5t8IVfZVlc7CPKdu0UO41qAeQiWw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1mn2afyp9my7y7hcyzum0wdwt49zufnkt8swnyy8pj30cwzs4zvgsthj0lt
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGbU1uN2JBU3FzNkFiS1N2
cE11L2NZUEwrNGkwbVhvRVUveFF2aXhDdTFJCnRBS3VxZmFWQStJS3pTcGpmelhy
Tm5xQXdDT2JraTN0M1JKaUVLNjMydmMKLS0tIHJDWXVPT1dwdzA3TkpIT2xTczVL
N3g2elREZEVCUDc0VFZBaEZWNWVhaDAKrnqiYwyFjQJnzNd1t1iy82JC9CJrBYEG
34DFwUhiPQcXjYtW8DhTOXyhQbDrjf1r2cD5+JId+dpIAmG8MDWRxA==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWMGFLVGJYTlVjNmZQdmtp
TkVjRWx4Z1R2QTAwVmJMTWNCd0pza0NPU2xVCm9UcFQxVzI3RG9WeXJ4VUQraVMv
TUQxRWNFbG1ndzNNMjZvVlhXU1ozU0UKLS0tIGdWeExjWGtJdWhGTWxmS2V3Q0ND
UHl6b2J4U1BNWHZQK0Z1RGtYRVdzNncK+UhQlaLlYcrZl17jBVHLfyEMMG82pn61
V2wtdOZPk1NFNVa1/kTntmfxjK/mV883gVHuUru0HpVIRD8x/lw8Rw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1ykkjw57t3z3deup3gtp7dujyaslskn74e0d9hsmqaha2pj3rvazqgndw5a
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnKzFyMWt1K2t0UXNpeG9G
SlAzMVBSZXI5TUxaMzk4OVY2czBwbUFYTWo4CkdGUjdva3B5Z2ZYYXAyVmJYZWdC
Nmt1S2E3RTdNcXMzY3FWMXhIblRZUTgKLS0tIEpCZDBUTXpHL1FrMXVpUzJFMmw1
U3VqVklnUlFrUndXSEs0QTlYMklTdU0K67wrP/l3xaryQTHZB+UZItMmwt3Pm0nB
l/aJXyIqEXcAuqP36RvzBXQexhFS0q7PbIwjEtAfqK8DGntEsF3PHw==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxaTVCZmR3QjhBTlhjQkpH
R0R4ZXNrQms3MmI5cS92Q0l0OG5IS05iQ1JrCitpRnpiY0wxaXc0NzlBZnhqSFpj
bWFocnRKRDErUTlqNml5OEFTcDkxK1UKLS0tIHgxakcxdTE5TDBrOEFJNGlTRDNz
OHF1ZUFGaFVRNlRPRGhDbTlIeE5PdzQKzhiyVOJ0eB6RcVoiKhRykXrMB0msTO1p
oErds6/XYa5SDABpc7cCMeFX5QYWA6XCquJ1zQ2yquPiBrj1D80Z8g==
-----END AGE ENCRYPTED FILE-----
- recipient: age1t2d5scrukk0guva5sr97a8tge5j8kd865adezrcru7p269pzwvpsamkgje
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCV3UwSWxveDkyUDNtcGFt
YVFQOS81a3NJUng1NmpjVFNncnhTNU5XZjJJCkpKUzV3Kzc2cVlVUDNNWjJ3a0s0
dm95R1lyVCszaGovMjliUmplWGIvN28KLS0tIEN5dHA3bE1WUjJjTlFUTUx1Rm9r
SkhOUXVzbVFRQlRzQTFUYUZjUmM2TkkKZ6OSRYcgUiB0gQLs5xGlrLpjgxYuYXKt
d3MaGf7cL6QeNEADGPeReXgSBJljI5QLTwyrQVCM4WyFL3hGo6v8Ng==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhZ0xVaHlaSTVnb3hKSkhO
NThvL3pzd0lCdjUvZGlUcHlWb1JCRWFUVFNRCnVmVzFvY2F6L0VnNStzNVZ6WjNW
NnFjblNMakVKUWlqd3JGWENOQmtVOGMKLS0tIGV5R0htSVJTRXNnd1hnZk4yOWIv
bk9BSVJxRGZpbldheW5pNzU2UmdsT2cKh18db9FTk5/Ji/SNXGVamyB1yrIo+Xsi
kmeROZP87ALKg7oDX0yvemhhLqfyotzkeOilZEf4cuQwsR2Gq4BZ/w==
-----END AGE ENCRYPTED FILE-----
- recipient: age1c8qw59ffcq9l77gfmtyc3djtvt3md0u6dwhrjcgsm98ntyf72ufqugj7cg
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBybnVTRjRDS1RNbzU2ZjY4
RElxMmcyMVJCbU91cnJock93TFlxREJ5YmxJCml2b1FBWFNMc2VQT1gzK0dUQ1Qy
cDBEbyt0WXRyNVFFemNiV2wwQXV3Z2MKLS0tIHBxbEgxQnRKUTFZT2xldnplZXJ5
dGdJR2JVb1ExWGRQcGR2SWNOVU53eFEKAFyNit0rGZ+z63I/EnJcAmvphZC9aPrJ
7+PY7WNLa5VmgxBmwmUW1vvuZq9cFRU7yXPI9LbMtzTy7ByBxztZyw==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAveFRnL3NhMElqY1ArQS9O
Q1o0ME1sQm1vckFOM0ZkcUF1UzQ4VmhKTlV3CnBIOVZ1Qk40VEFPbHJld1JkQVhs
OEdPVG1TWlZlT1lZMUYvYVFJUTdXK0kKLS0tIFM4N2pONXhWa2hIMGlOcStuZUJ0
dmk1cnJvalUzSUdESldjTVdpQVV5ZVEKNr+5rSc1pQbMIQxtNJyNIm4bFvl5b6pa
DvuAmZd3dSTCRPGwv9m2tkbpSlE5Yp3gDe9Ivi82EAIx9ob8aImXBw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1er5qucsc2mugrzrr7n3xhzv7kemkrqrw4m84r544fkk7nkg5g5eswxkqj0
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1M1dYSTV0UGxtTmh6dHZX
bXB1eElEMlNOUmhoRjNnZmw4STh2d0x4UHg4CnlhdFEwMjlPWWNuMndQMEJCQUtJ
YzN3TG54R2JXNldZQ2loT0xUN3Q5YjAKLS0tIGdSRW1zZmlpOTRFTmFCT0RUMWps
Zk40WkZtZkVVeXcwOXFUVEJINU02RmcK2OK9FnYsvgTG4x0RZFt6NYDlG6fiaVsB
eVs34Ll3xuddxeGBVuQdeX5kxxzqxe1fBKE+IEzsRSMLQgN2Mdqi7g==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMcHpLWWx3cm00ZGp0bkhn
QjN2dVJyRVl5cElObmtWZVdidWYrTTRnd2lrCmVVdERvR0ZzR3FZb0p0YzNQdFFK
NStuMEx6QzIvN2lPSUtseGVtRlU4eHcKLS0tIC9NdzQrVDQzV1JWTXNCbDBRN2Fo
V3dKbnBuNUZyalIvWER4dlRZaDdUL3MKONyd0ZgC5L06aJKohJE+zS3Zy+Msr3Uy
DIM84V0OTuzw05ni61L5pqRL51Kyi61hOjCSNF8+Gm3XalL9NEKrfg==
-----END AGE ENCRYPTED FILE-----
- recipient: age1xg6mvj3x6s3t8058c6rsk3q4kskvm6nsffwckxkkjzhyn7r6tczqgkj23p
- recipient: age19daqsncuzeh3j6cwk8uxp6yfj8h0qtz02jxlwwy4v8j0mfgznsvq30440g
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBreTg0SmtBeHB3MHd2M2w3
YmUvRTM5ZWFwSVNvUDVDRGVHQmxDZ1FkeXprCmxrREpML09FTzFGOFkwOUUwbjVz
ditIODE1b0o0NG5ZekIrTlFoTFFyL2MKLS0tIGVtSTFmTG04MW9QWGJZM0lOdlhM
djlTNTVralMrM2wrZ0Znb0psSkZrejAKr7ydGdN+LQlIgMjWAfGR3EXADwBbhYrd
WsT4TIMH8pemXPAdthgIBSvbBtmTfM2jKFiDXxTEkSmyp8hLxxKJag==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoS1dJenhHZG1jK1hndzFw
UjErT1VMTTNaMHdad09wM1o0QWl1UHVOdG1BCndiajR4aHp2bCtLb1FpWTZLQU9k
dVI2NnpXT3Jnc2FTZG5vNW4rbWVFbUkKLS0tIDRrYUxPZTE1WU9LVmNRVExia0xN
R0JXd3FCUmJTQXkxMXBmcjFNY09YQW8Khz3AliRA5RzNelz+/f6j62qcS2VNJyRg
SwYg0l2BBALvnI2VDj4X4YYQM5YxgNCtSdsb//ijVkypkDm/wTKp3w==
-----END AGE ENCRYPTED FILE-----
- recipient: age1rdn39ywgzmc8wlsl5lrfe77e652wzjmjx58gx4k2ydghd35kdqvqscrf3h
- recipient: age19w4zafpwnq9yhzuf8r5te2yhq7xlqj76rcgzcz935hllyrz4yvws4jn6ca
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLZko4M0xZMVNOb2ZTOXJp
THNUMUpobWIxR2VtTGZzS0p2ekd0VmYwaUhvCjh5Q2hIRXA1dDY3UFhiaDEzZzg1
bTduMTVwK2pIUFdpTmNZdUhzSjJpNDAKLS0tIDFFc3grWG5yak1jT0Q1aER4UXd3
SGtpQm9sZkEySWo2TS9UZ0xlTjE1MXcKC2T7uEnnDr06tt4sajTAuwKECQWIxa21
alsXZe2lCxN6b5kA1jkQO1/y+zEzvn+z9ui1ZaRuDavkMEUj+QH2qg==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMNlhYS0dFd203UXBLMzJa
YUVrZnZYMSt2QXRlbXU3UDhobGFVdmhkUVdJCklpUzZUbFFHNGgxaGltalRjZVlQ
QjU4QXVHbzUzM1JPREJoZUJJdVpLaFEKLS0tIFVRN09qd0NzVzhuSWN0YmQyZ3l4
TkdzdlIyYVRpRE9OaG5EeE1KNDMwTDQKfy3OEtd6iT6md5AIkwuy61xXtjy2XrVL
BZOwFI4qtDN8SgpCTpkIE+Iyv5XvWMTw8v8+MNuas2CYX0GsN5ODyg==
-----END AGE ENCRYPTED FILE-----
- recipient: age1luyejgmqjj0esydlr2jxqkg48vexmx57gdz7cy5gq7rz8kf5cups2rnfa9
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvVzFybisva1lJTm9DaU5L
Y2JlWERmTEY3ZS84NU80RkwzaGVQc0dlMldjCllXU0dkY2R5dVNpblptTGl6bGtE
Q0hwaFdFRHhnMTZwdytjU2xOSWRzeGMKLS0tIHlsSVRnNVlLRzFnbU54L2ppZXFl
MFNLVkdtQkxRbDVBV1pGMU5IZzRNdmMKH5o9sT1uerA9ANpJ8nVB15AERNn61s9h
l514Enz5GwkKjITsWeb6J+k4VtGd2wfoX2YySDOMNUsXnlH1BvFFBw==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnaDZaMnBZc0FzcHlQR2t1
QVhWTDMyZy9qOXB5RDlMVjh1Ly94RzZPNDJBCjNTbG94bTFJdVpEcXAydVlVbHYy
WWV5YTZqeHhJeDYzQWpUUlhnb1dQb28KLS0tIHNjaWx3eTk0ZDJwdWFsZ0k1U1BR
NGdjV1dsRjlZbjhUWllsSU9LQmdkMHMK+7GWHmYubQ2m2YjCjo6VibRHCMHE0SUV
mercB7H9IyoHqDyH+VmfMfsKQyhUzNGx21xNwA/AxkrECl781FJMVw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1wurzgc20e6ye79wsg85vvqk4aj3mmc0llxshcy9532ex8f4c6dqql76c78
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXcDVKdW12dFFEd0tVWVBz
QmJpblJXMWZhbFprZnNvTGhCQzQ3S3Yva0NRCmUyZzJRSVd1SzFzTGN3a01icVJK
c3RxRnZ2MU1sRDU2UmUrZ2Znd0pCTVkKLS0tIFI3V3BrclBITFZGSjFmTjBhS2c1
U1VIMUEyTTdQd25zMVU5OGd1aUhuVkUK8x6JppexvYSvZe1hn7DuO0t/scXlVU07
an4u0gtwNlmn2hz/QxHa1cIxEc/awQIU3AkotjlzhYug2wbtyZTxjw==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCaDBrcW9UNitUSTJJSXJD
Y09ZWmZtTDVaNGphYkV0aWdlRjZVWGQrbEFNClpXaUJpZ2pXeHdKbVZTWjMzdEtW
SGlleEY4a2lnbTVrV2FqQnBQeXRnVnMKLS0tIGh2MDdpcVk0SVZ0eU02bWV4TTc0
VE9jKzk2ZkNNMklDTWh3a2NDakxJNFEKITKH9DQKJYN2PCidfZ5A3ypHe0kurphx
4qtUgLbGMEcuGfHXCMUtzJAzh8k74Ld6AzLGSTHEjjjmIRmw1o6LhQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-05-29T02:27:34Z"
mac: ENC[AES256_GCM,data:GqBTWeckU/ERKV/5OiPuFOSfUXUTEN7OpKKhGbWCl5oTUE1/CkmMheWJy5WiTlk89KAd66+gBK5kG29PQhEOkUcjoLZTdwghOiJVi90+zPdfz1fGkjs570GtNRulEBL13Ld9KRMHbRSOijM33jmgSpHY8Tcb7RzFTVQs6ZpJ6Fk=,iv:groe/8rbUW9PJTyI457u+LQsiBEzc0YKvKpNToTBrdQ=,tag:OvUbSTSR664p+hBa+BQ++A==,type:str]

View File

@@ -4,7 +4,6 @@
}:
{
imports = [
./nix.nix
./homebrew.nix
./programs.nix
./system.nix

View File

@@ -1,17 +0,0 @@
{ ... }:
{
# Auto upgrade nix package and the daemon service.
# services.nix-daemon.enable = true;
# nix.package = pkgs.nix;
# Necessary for using flakes on this system.
nix = {
settings.experimental-features = "nix-command flakes";
};
# The platform the configuration will be used on.
nixpkgs = {
config.allowUnfree = true;
hostPlatform = "aarch64-darwin";
};
}

View File

@@ -15,18 +15,36 @@ in
imports = [
./boot.nix
./hardware-configuration.nix
./networking.nix
# ./networking.nix - moved to modules/nixos/network
./services.nix
];
hardware.asahi = {
enable = true;
useExperimentalGPUDriver = true;
peripheralFirmwareDirectory = ./firmware;
setupAsahiSound = true;
};
${namespace} = {
user = {
name = "matt";
extraGroups = [
"ratbagd"
"input"
"scanner"
"lp"
"video"
"i2c"
];
packages = with pkgs; [
firefox
tree
git
box64
prismlauncher
distrobox
];
};
desktop = {
hyprland = {
enable = true;
@@ -59,36 +77,33 @@ in
};
network = {
hostName = "macbook-pro-nixos";
wifi.enable = false;
iwd = {
enable = true;
settings = {
General = {
EnableNetworkConfiguration = true;
};
Rank = {
BandModifier2_4GHz = 1.0;
BandModifier5GHz = 5.0;
BandModifier6GHz = 10.0;
};
Network = {
AutoConnect = true;
};
};
};
extraFirewallCommands = ''
iptables -I INPUT -m pkttype --pkt-type multicast -j ACCEPT
iptables -A INPUT -m pkttype --pkt-type multicast -j ACCEPT
iptables -I INPUT -p udp -m udp --match multiport --dports 1990,2021 -j ACCEPT
'';
};
};
nixpkgs.config.allowUnsupportedSystem = true;
# Define a user account. Don't forget to set a password with passwd.
users.users.matt = {
isNormalUser = true;
extraGroups = [
"wheel"
"keys"
"networkmanager"
"ratbagd"
"input"
"scanner"
"lp"
"video"
"i2c"
]; # Enable sudo for the user.
shell = pkgs.zsh;
packages = with pkgs; [
firefox
tree
git
box64
prismlauncher
distrobox
];
};
virtualisation = {
containers.enable = true;
podman.enable = true;

View File

@@ -1,7 +1,11 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ lib, modulesPath, ... }:
{
lib,
modulesPath,
...
}:
{
imports = [
@@ -9,7 +13,7 @@
];
boot.initrd.availableKernelModules = [
"uas"
"usb_storage"
"sdhci_pci"
];
boot.initrd.kernelModules = [ ];
@@ -19,69 +23,11 @@
fileSystems."/" = {
device = "none";
fsType = "tmpfs";
};
fileSystems."/root" = {
device = "/dev/disk/by-uuid/adcc14fa-8bf7-4b4b-a9e4-b038993b96cc";
fsType = "btrfs";
options = [
"compress=zstd"
"noatime"
"subvol=root"
];
};
fileSystems."/etc" = {
device = "/dev/disk/by-uuid/adcc14fa-8bf7-4b4b-a9e4-b038993b96cc";
fsType = "btrfs";
options = [
"compress=zstd"
"noatime"
"subvol=etc"
];
};
fileSystems."/tmp" = {
device = "/dev/disk/by-uuid/adcc14fa-8bf7-4b4b-a9e4-b038993b96cc";
fsType = "btrfs";
options = [
"compress=zstd"
"noatime"
"subvol=tmp"
];
};
fileSystems."/nix" = {
device = "/dev/disk/by-uuid/adcc14fa-8bf7-4b4b-a9e4-b038993b96cc";
fsType = "btrfs";
options = [
"compress=zstd"
"noatime"
"subvol=nix"
];
};
fileSystems."/var/log" = {
device = "/dev/disk/by-uuid/adcc14fa-8bf7-4b4b-a9e4-b038993b96cc";
fsType = "btrfs";
options = [
"compress=zstd"
"noatime"
"subvol=log"
];
};
fileSystems."/home" = {
device = "/dev/disk/by-uuid/adcc14fa-8bf7-4b4b-a9e4-b038993b96cc";
fsType = "btrfs";
options = [
"compress=zstd"
"subvol=home"
];
options = [ "mode=755" ];
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/23FA-AD3E";
device = "/dev/disk/by-uuid/E66E-1A03";
fsType = "vfat";
options = [
"fmask=0022"
@@ -89,12 +35,37 @@
];
};
# swapDevices = [
# {
# device = "/tmp/swapfile";
# randomEncryption.enable = true;
# }
# ];
fileSystems."/root" = {
device = "/dev/disk/by-uuid/335f1bb3-6fdb-474e-972c-77b64e930d03";
fsType = "btrfs";
options = [ "subvol=root" ];
};
fileSystems."/etc" = {
device = "/dev/disk/by-uuid/335f1bb3-6fdb-474e-972c-77b64e930d03";
fsType = "btrfs";
options = [ "subvol=etc" ];
};
fileSystems."/nix" = {
device = "/dev/disk/by-uuid/335f1bb3-6fdb-474e-972c-77b64e930d03";
fsType = "btrfs";
options = [ "subvol=nix" ];
};
fileSystems."/var/log" = {
device = "/dev/disk/by-uuid/335f1bb3-6fdb-474e-972c-77b64e930d03";
fsType = "btrfs";
options = [ "subvol=log" ];
};
fileSystems."/home" = {
device = "/dev/disk/by-uuid/335f1bb3-6fdb-474e-972c-77b64e930d03";
fsType = "btrfs";
options = [ "subvol=home" ];
};
swapDevices = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's

View File

@@ -3,118 +3,58 @@
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
{
config,
lib,
pkgs,
namespace,
...
}:
let
user = "matt";
# password = config.sops.secrets."pi4/matt-password".path;
kernelBundle = pkgs.linuxAndFirmware.latest;
in
{
imports = [
./adguard.nix
./boot.nix
./networking.nix
# ./networking.nix - moved to modules/nixos/network
./sops.nix
];
${namespace} = {
hardware.disko.enable = true;
};
nix = {
settings = {
substituters = [
"https://nixos-raspberrypi.cachix.org"
"https://cache.mjallen.dev"
];
trusted-public-keys = [
"nixos-raspberrypi.cachix.org-1:4iMO9LXa8BqhU+Rpg6LQKiGa2lsNh/j2oiYLNOQ5sPI="
"cache.mjallen.dev-1:IzFmKCd8/gggI6lcCXsW65qQwiCLGFFN9t9s2iw7Lvc="
];
};
};
# Configure nixpkgs
nixpkgs = {
overlays = lib.mkAfter [
(_self: _super: {
# This is used in (modulesPath + "/hardware/all-firmware.nix") when at least
# enableRedistributableFirmware is enabled
# I know no easier way to override this package
inherit (kernelBundle) raspberrypiWirelessFirmware;
# Some derivations want to use it as an input,
# e.g. raspberrypi-dtbs, omxplayer, sd-image-* modules
inherit (kernelBundle) raspberrypifw;
})
];
};
programs.zsh.enable = true;
hardware.i2c.enable = true;
services = {
openssh = {
enable = true;
authorizedKeysFiles = [
config.sops.secrets."ssh-keys-public/pi5".path
];
hostKeys = [ ];
};
};
systemd.services.btattach = {
before = [ "bluetooth.service" ];
after = [ "dev-ttyAMA0.device" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.bluez}/bin/btattach -B /dev/ttyAMA0 -P bcm -S 3000000";
};
};
environment = {
systemPackages = with pkgs; [
i2c-tools
libraspberrypi
raspberrypi-eeprom
raspberrypifw
raspberrypiWirelessFirmware
raspberrypi-armstubs
];
};
users = {
mutableUsers = false;
users = {
"${user}" = {
isNormalUser = true;
# hashedPasswordFile = password;
password = lib.mkForce "BogieDudie1";
extraGroups = [
"wheel"
"docker"
"video"
];
shell = pkgs.zsh;
openssh.authorizedKeys.keys = [
# macBook
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCw9zq8DLGByI5v2gAn95hKNyOsm3g61a2buxu2BBMFysQJgmZPCCLUqRJKhSM5Vm/JOgsAmdpRBRZQoHD+6S844CJHb4v4VIbjkyQgYCuM7Rst2IOZ5QybvsA2/D0nwytZ+HXQqDj2AagUYDbz0gyyIHkDQ5YGBMkvkWz/h1Vci6aoBM7VihEDM4KlWoTVuPeASGM8r5IZ2FS83Djbqo4ov6AYvLMrKB9Z7hmFgH6R3LE0gxOkzbGVXtSuvJyrjvgytoT22UhATjjxSQ9D+YJXXkQoB3lUdg8OoIquUPjMZpl4mR8ffvseWPfcvD1XlD5t+TOHFqKpESO547tlOBYhdpew+NSgAXpamCU6oyV8tDCywLQu2ucxHRn78u6WXzWHkDtffdhzmk6TZaPhWqVHuTGjR4higBgGqUfSaKOMszt+FDRZAr3HtuQ2+zJ8bowK9fW5OqilTtK2HtQqroD9ApegDNbqOz6kGy5IycSXvqPURy/M4lxZxbtBPuemcJs= mattjallen@MacBook-Pro.local"
# desktop windows
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDZ2PYPjZddOzR8OJj16G88KcUhCDLkvrEmpUQP0wKHDUuA27HQQ2ORo66asadwGHY3k1VDZ1ei9l9H++SIIeKOaaUr5yZdktvj4POUNtbd9ZhcS7sZU7BSF+NMDM+h3tImh6z0S7mWvRQOUv3ZM+ZER+5xTWJVG1OOJEpb1drxJk6Qz0wbZKSR7TPNFBLLXlVy7hkNYf07RtDyhCCxNB3hJfa8c+oztnWumwDhDQWLqiUXWIU2QH6iRLGl/WYnujtNvVVaV/Hn3JJkS6MM9dnV3cpoIO0+J7+WfsN9rZ0wXt5yY3GhiGXwmcO5eYVli8lHlLWtK7aYSETyry6CBsLbojzOQO5rSqhpwfF2njAAFAQU0UjLc8PahisIuFKCwHH4iyXXOagiv5K1Mc/0Ak+WhhMPee6vV2p7NTyNpXRvouDbWy5cSRH31WgQ9fK5mIGe5v8nGGqtEhUubUkiOgP+H3UbT2V/nTv/TFKdJcKw+WmizvTrxBmaMjWALlkYl+s= mattl@Jallen-PC"
# desktop nixos
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPTBMydhOc6SnOdB5WrEd7X07DrboAtagCUgXiOJjLov matt@matt-nixos"
];
};
root = {
isSystemUser = true;
isNormalUser = false;
shell = pkgs.zsh;
hardware = {
disko.enable = true;
raspberry-pi = {
enable = true;
variant = "4";
};
};
user = {
name = "matt";
password = "BogieDudie1";
mutableUsers = false;
extraGroups = [
"docker"
"video"
];
};
network = {
hostName = "pi4";
ipv4 = {
method = "manual";
address = "10.0.1.2/24";
gateway = "10.0.1.1";
dns = "1.1.1.1";
};
firewall = {
enable = true;
allowPing = true;
allowedTCPPorts = [ 53 ];
allowedUDPPorts = [ 53 ];
};
wifi = {
enable = true;
powersave = false;
};
};
};
# Root user configuration - explicit to avoid conflicts with home-manager
users.users.root = {
isSystemUser = true;
isNormalUser = false;
};
}

View File

@@ -1,11 +1,8 @@
{ lib, config, ... }:
let
hostname = "pi4";
in
{
# Networking configs
networking = {
hostName = hostname;
# hostName = lib.mkForce hostname;
defaultGateway.address = "10.0.1.1";
nameservers = [ "10.0.1.1" ];

View File

@@ -3,121 +3,47 @@
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
{
config,
lib,
pkgs,
namespace,
...
}:
let
user = "matt";
password = config.sops.secrets."pi5/matt-password".path;
kernelBundle = pkgs.linuxAndFirmware.latest;
in
{
imports = [
./boot.nix
./networking.nix
# ./networking.nix - moved to modules/nixos/network
./services.nix
./sops.nix
];
${namespace} = {
hardware.disko.enable = true;
hardware = {
disko.enable = true;
raspberry-pi = {
enable = true;
variant = "5";
};
};
desktop.hyprland.enable = false;
network = {
hostName = "pi5";
};
};
# Enable nix flakes and nix-command tools
nix = {
settings = {
substituters = [
"https://nixos-raspberrypi.cachix.org"
# "https://cache.mjallen.dev"
];
trusted-public-keys = [
"nixos-raspberrypi.cachix.org-1:4iMO9LXa8BqhU+Rpg6LQKiGa2lsNh/j2oiYLNOQ5sPI="
# "cache.mjallen.dev-1:IzFmKCd8/gggI6lcCXsW65qQwiCLGFFN9t9s2iw7Lvc="
];
};
};
# Configure nixpkgs
nixpkgs = {
overlays = lib.mkAfter [
(_self: _super: {
# This is used in (modulesPath + "/hardware/all-firmware.nix") when at least
# enableRedistributableFirmware is enabled
# I know no easier way to override this package
inherit (kernelBundle) raspberrypiWirelessFirmware;
# Some derivations want to use it as an input,
# e.g. raspberrypi-dtbs, omxplayer, sd-image-* modules
inherit (kernelBundle) raspberrypifw;
})
];
};
system.nixos.tags =
let
cfg = config.boot.loader.raspberry-pi;
in
[
"raspberry-pi-${cfg.variant}"
cfg.bootloader
config.boot.kernelPackages.kernel.version
];
systemd.services.btattach = {
before = [ "bluetooth.service" ];
after = [ "dev-ttyAMA0.device" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = "${pkgs.bluez}/bin/btattach -B /dev/ttyAMA0 -P bcm -S 3000000";
};
};
environment = {
systemPackages = with pkgs; [
erofs-utils
fex
libraspberrypi
raspberrypi-eeprom
raspberrypifw
raspberrypiWirelessFirmware
raspberrypi-armstubs
squashfuse
squashfsTools
];
};
hardware.graphics.enable32Bit = lib.mkForce false;
users = {
mutableUsers = false;
users."${user}" = {
isNormalUser = true;
# hashedPasswordFile = password;
password = lib.mkForce "BogieDudie1";
extraGroups = [
"wheel"
"docker"
];
openssh.authorizedKeys.keys = [
# macBook
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCw9zq8DLGByI5v2gAn95hKNyOsm3g61a2buxu2BBMFysQJgmZPCCLUqRJKhSM5Vm/JOgsAmdpRBRZQoHD+6S844CJHb4v4VIbjkyQgYCuM7Rst2IOZ5QybvsA2/D0nwytZ+HXQqDj2AagUYDbz0gyyIHkDQ5YGBMkvkWz/h1Vci6aoBM7VihEDM4KlWoTVuPeASGM8r5IZ2FS83Djbqo4ov6AYvLMrKB9Z7hmFgH6R3LE0gxOkzbGVXtSuvJyrjvgytoT22UhATjjxSQ9D+YJXXkQoB3lUdg8OoIquUPjMZpl4mR8ffvseWPfcvD1XlD5t+TOHFqKpESO547tlOBYhdpew+NSgAXpamCU6oyV8tDCywLQu2ucxHRn78u6WXzWHkDtffdhzmk6TZaPhWqVHuTGjR4higBgGqUfSaKOMszt+FDRZAr3HtuQ2+zJ8bowK9fW5OqilTtK2HtQqroD9ApegDNbqOz6kGy5IycSXvqPURy/M4lxZxbtBPuemcJs= mattjallen@MacBook-Pro.local"
# desktop windows
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDZ2PYPjZddOzR8OJj16G88KcUhCDLkvrEmpUQP0wKHDUuA27HQQ2ORo66asadwGHY3k1VDZ1ei9l9H++SIIeKOaaUr5yZdktvj4POUNtbd9ZhcS7sZU7BSF+NMDM+h3tImh6z0S7mWvRQOUv3ZM+ZER+5xTWJVG1OOJEpb1drxJk6Qz0wbZKSR7TPNFBLLXlVy7hkNYf07RtDyhCCxNB3hJfa8c+oztnWumwDhDQWLqiUXWIU2QH6iRLGl/WYnujtNvVVaV/Hn3JJkS6MM9dnV3cpoIO0+J7+WfsN9rZ0wXt5yY3GhiGXwmcO5eYVli8lHlLWtK7aYSETyry6CBsLbojzOQO5rSqhpwfF2njAAFAQU0UjLc8PahisIuFKCwHH4iyXXOagiv5K1Mc/0Ak+WhhMPee6vV2p7NTyNpXRvouDbWy5cSRH31WgQ9fK5mIGe5v8nGGqtEhUubUkiOgP+H3UbT2V/nTv/TFKdJcKw+WmizvTrxBmaMjWALlkYl+s= mattl@Jallen-PC"
# desktop nixos
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPTBMydhOc6SnOdB5WrEd7X07DrboAtagCUgXiOJjLov matt@matt-nixos"
user = {
name = "matt";
password = "BogieDudie1";
mutableUsers = false;
extraGroups = [ "docker" ];
sshKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOTha0FbV1tkpnJr7xVH78S5MetJH+0o2YrEcuvhL692 root@jallen-nas"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIwoHWOLSTGVif9hAhaMLl0qDA4roIzCNuyR6kyIXDOj admin@jallen-nas"
];
shell = pkgs.zsh;
};
users.root.shell = pkgs.zsh;
network = {
hostName = "pi5";
ipv4 = {
method = "manual";
gateway = "10.0.1.1";
dns = "10.0.1.1";
};
firewall = {
enable = true;
allowPing = true;
};
};
};
zramSwap.enable = true;
}

View File

@@ -1,8 +1,6 @@
{ ... }:
{
services = {
xserver.desktopManager.gnome.enable = true;
shairport-sync = {
enable = false;
openFirewall = true;

View File

@@ -51,17 +51,6 @@ let
];
in
{
# nix = {
# settings = {
# substituters = [
# "https://cache.mjallen.dev"
# ];
# trusted-public-keys = [
# "cache.mjallen.dev-1:IzFmKCd8/gggI6lcCXsW65qQwiCLGFFN9t9s2iw7Lvc="
# ];
# };
# };
chaotic.mesa-git.enable = false;
# Environment configuration
@@ -84,6 +73,7 @@ in
brscan5.enable = false;
# extraBackends = [ pkgsVersion.brscan5 ];
};
flipperzero.enable = true;
};
# Common Configuration

View File

@@ -28,7 +28,7 @@ in
./configuration.nix
./filesystems.nix
./hardware-configuration.nix
# ./networking.nix
# ./networking.nix - moved to modules/nixos/network
./nix.nix
./sops.nix
@@ -38,10 +38,22 @@ in
];
${namespace} = {
hardware.disko.enable = false;
bootloader.lanzaboote.enable = true;
impermanence.enable = true;
desktop.gnome.enable = true;
network = {
hostName = "matt-nixos";
wifi = {
enable = true;
powersave = false;
profiles = {
"Joey's Jungle 6G" = {
ssid = "Joey's Jungle 6G";
keyMgmt = "sae";
};
};
};
};
user = {
passwordFile = passwordFile;

View File

@@ -27,7 +27,8 @@ in
fsType = "bcachefs";
options = [
"noatime"
] ++ defaultLocalOptions;
]
++ defaultLocalOptions;
};
# Network shares

View File

@@ -32,7 +32,8 @@ in
options = [
"subvol=nix"
"noatime"
] ++ defeaultBtrfsOptions;
]
++ defeaultBtrfsOptions;
};
fileSystems."/etc" = {
@@ -41,7 +42,8 @@ in
options = [
"subvol=etc"
"noatime"
] ++ defeaultBtrfsOptions;
]
++ defeaultBtrfsOptions;
};
fileSystems."/root" = {
@@ -50,7 +52,8 @@ in
options = [
"subvol=root"
"noatime"
] ++ defeaultBtrfsOptions;
]
++ defeaultBtrfsOptions;
};
fileSystems."/var/log" = {
@@ -59,7 +62,8 @@ in
options = [
"subvol=log"
"noatime"
] ++ defeaultBtrfsOptions;
]
++ defeaultBtrfsOptions;
};
fileSystems."/home" = {
@@ -67,7 +71,8 @@ in
fsType = "btrfs";
options = [
"subvol=home"
] ++ defeaultBtrfsOptions;
]
++ defeaultBtrfsOptions;
};
fileSystems."/boot" = {

View File

@@ -8,4 +8,4 @@
environment.variables = {
LSFG_DLL_PATH = "/media/matt/data/steam/steamapps/common/Lossless Scaling/Lossless.dll";
};
}
}

View File

@@ -2,6 +2,7 @@
{
${namespace} = {
services = {
# Existing properly namespaced services
immich.enable = true;
jellyfin.enable = true;
jellyseerr.enable = true;
@@ -11,93 +12,92 @@
paperless.enable = true;
traefik.enable = true;
wyoming.enable = true;
# Newly migrated services
actual = {
enable = true;
port = 3333;
localAddress = "10.0.3.18";
dataDir = "/media/nas/main/nix-app-data/actual";
reverseProxy = {
enable = true;
host = "actual.mjallen.dev";
middlewares = [
"crowdsec"
"whitelist-geoblock"
];
};
};
arrs = {
enable = true;
localAddress = "10.0.1.51";
downloadsDir = "/media/nas/main/ssd_app_data/downloads";
incompleteDownloadsDir = "/media/nas/main/ssd_app_data/downloads-incomplete";
moviesDir = "/media/nas/main/movies";
tvDir = "/media/nas/main/tv";
isosDir = "/media/nas/main/isos";
radarr = {
enable = true;
port = 7878;
dataDir = "/media/nas/main/nix-app-data/radarr";
};
sonarr = {
enable = true;
port = 8989;
dataDir = "/media/nas/main/nix-app-data/sonarr";
};
sabnzbd = {
enable = true;
port = 8280;
dataDir = "/media/nas/main/nix-app-data/sabnzbd";
};
deluge = {
enable = true;
port = 8112;
};
jackett = {
enable = true;
port = 9117;
dataDir = "/media/nas/main/nix-app-data/jackett";
};
};
crowdsec = {
enable = true;
port = 9898;
apiAddress = "10.0.1.3";
apiKey = "1daH89qmJ41r2Lpd9hvDw4sxtOAtBzaj3aKFOFqE";
dataDir = "/media/nas/main/nix-app-data/crowdsec";
};
gitea = {
enable = true;
httpPort = 3000;
sshPort = 2222;
localAddress = "10.0.4.18";
dataDir = "/media/nas/main/nix-app-data/gitea";
reverseProxy = {
enable = true;
host = "gitea.mjallen.dev";
middlewares = [
"crowdsec"
"whitelist-geoblock"
];
};
};
free-games-claimer.enable = true;
manyfold.enable = true;
orca-slicer = {
enable = true;
httpPort = "3100";
httpsPort = "3101";
};
tdarr.enable = true;
};
};
nas-apps = {
actual = {
enable = true;
port = 3333;
localAddress = "10.0.3.18";
dataDir = "/media/nas/main/nix-app-data/actual";
reverseProxy = {
enable = true;
host = "actual.mjallen.dev";
middlewares = [
"crowdsec"
"whitelist-geoblock"
];
};
};
arrs = {
enable = true;
localAddress = "10.0.1.51";
downloadsDir = "/media/nas/main/ssd_app_data/downloads";
incompleteDownloadsDir = "/media/nas/main/ssd_app_data/downloads-incomplete";
moviesDir = "/media/nas/main/movies";
tvDir = "/media/nas/main/tv";
isosDir = "/media/nas/main/isos";
radarr = {
enable = true;
port = 7878;
dataDir = "/media/nas/main/nix-app-data/radarr";
};
sonarr = {
enable = true;
port = 8989;
dataDir = "/media/nas/main/nix-app-data/sonarr";
};
sabnzbd = {
enable = true;
port = 8280;
dataDir = "/media/nas/main/nix-app-data/sabnzbd";
};
deluge = {
enable = true;
port = 8112;
};
jackett = {
enable = true;
port = 9117;
dataDir = "/media/nas/main/nix-app-data/jackett";
};
};
crowdsec = {
enable = true;
port = 9898;
apiAddress = "10.0.1.3";
apiKey = "1daH89qmJ41r2Lpd9hvDw4sxtOAtBzaj3aKFOFqE";
dataDir = "/media/nas/main/nix-app-data/crowdsec";
};
gitea = {
enable = true;
httpPort = 3000;
sshPort = 2222;
localAddress = "10.0.4.18";
dataDir = "/media/nas/main/nix-app-data/gitea";
reverseProxy = {
enable = true;
host = "gitea.mjallen.dev";
middlewares = [
"crowdsec"
"whitelist-geoblock"
];
};
};
free-games-claimer.enable = true;
manyfold.enable = true;
orca-slicer = {
enable = true;
httpPort = "3100";
httpsPort = "3101";
};
tdarr.enable = true;
};
}

View File

@@ -1,51 +0,0 @@
{ lib, ... }:
let
inherit (lib) types mkOption;
in
{
options.nas-apps = mkOption {
type = types.attrsOf (
types.submodule (
{ ... }:
{
options = {
enable = mkOption {
type = types.bool;
default = false;
};
port = mkOption {
type = types.int;
default = 80;
};
localAddress = mkOption {
type = types.str;
default = "127.0.0.1";
};
dataDir = mkOption {
type = types.str;
default = "";
};
reverseProxy = {
enable = mkOption {
type = types.bool;
default = false;
};
host = mkOption {
type = types.str;
default = "";
};
middlewares = mkOption {
type = with types; listOf str;
default = [ ];
};
};
};
}
)
);
};
}

View File

@@ -1,4 +1,9 @@
{ pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
configLimit = 50;
kernel = pkgs.linuxPackages; # linuxPackages_latest;
@@ -35,6 +40,8 @@ in
consoleLogLevel = 3;
bootspec.enable = true;
plymouth.enable = lib.mkForce false;
initrd = {
kernelModules = [
"tpm"
@@ -50,7 +57,7 @@ in
clevis = {
enable = true;
devices = {
# "/dev/sde:/dev/sdf:/dev/sdh:/dev/sdi:/dev/sdj".secretFile = "../../../pool.jwe";
"/dev/disk/by-label/nas_pool".secretFile = config.sops.secrets."jallen-nas/nas_pool".path;
};
};
};
@@ -59,6 +66,8 @@ in
binfmt.emulatedSystems = [ "aarch64-linux" ]; # --argstr system aarch64-linux
};
environment.etc."clevis/nas_pool.jwe".source = config.sops.secrets."jallen-nas/nas_pool".path;
zramSwap = {
enable = true;
};

View File

@@ -1,4 +1,3 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
@@ -6,7 +5,6 @@
{
config,
pkgs,
lib,
namespace,
...
}:
@@ -18,8 +16,7 @@
./boot.nix
./apps.nix
./grafana.nix
./networking.nix
./nixpkgs.nix
# ./networking.nix - moved to modules/nixos/network
./ups.nix
./users.nix
./samba.nix
@@ -34,6 +31,15 @@
${namespace} = {
bootloader.lanzaboote.enable = true;
desktop.cosmic.enable = false;
development = {
enable = true;
includeLanguages = [
"python"
"c"
];
includeContainers = true;
};
monitoring.enable = true;
hardware.nvidia = {
enable = true;
enableBeta = true;
@@ -46,10 +52,72 @@
ipv4 = {
address = "10.0.1.3/24";
method = "manual";
gateway = "10.0.1.1";
interface = "wlp6s0";
};
useNetworkd = true;
hostId = "4b501480";
nat = {
enable = true;
internalInterfaces = [ "ve-+" ];
externalInterface = "wlp6s0";
enableIPv6 = true;
};
firewall = {
enable = true;
allowPing = true;
allowedTCPPorts = [
8008 # restic
9000 # authentik
2342 # grafana
51820 # wireguard
1025
1143
10200
10300
8127
9980 # onlyoffice
4000 # netbootxyz
4080 # netbootxyz
3000 # gitea
2222 # gitea ssh
3300
9898
6754 # lubelogger
2283 # immich
4444 # code-server
9012
8192
];
allowedUDPPorts = [
8008 # restic
9000 # authentik
2342 # grafana
51820 # wireguard
1025
1143
10200
10300
8127
9980 # onlyoffice
4000 # netbootxyz
4080 # netbootxyz
3000 # gitea
2222 # gitea ssh
3300
9898
6754 # lubelogger
2283 # immich
4444 # code-server
9012
8192
];
trustedInterfaces = [ "tailscale0" ];
};
};
user = {
name = "admin";
linger = true;
};
};
@@ -65,34 +133,26 @@
systemPackages = with pkgs; [
attic-client
binutils
bcachefs-tools
cryptsetup
clevis
cmake
deconz
duperemove
efibootmgr
ffmpeg
gcc
glances
ipset
jq
llama-cpp
ninja
# inputs.nas-nixai.packages.x86_64-linux.nixai
networkmanagerapplet
nmon
nut
packagekit
pass
protonmail-bridge
protonvpn-cli
python3
python3Packages.llama-cpp-python
qrencode
rcon
sbctl
speedtest-cli
tigervnc
tpm2-tools
tpm2-tss
@@ -102,8 +162,6 @@
# Configure programs
programs = {
virt-manager.enable = true;
nix-ld.enable = true;
screen.enable = true;
coolercontrol = {
enable = true;
nvidiaSupport = true;
@@ -152,58 +210,6 @@
'';
};
# Virtualisation
virtualisation = {
podman = {
enable = true;
dockerCompat = true;
autoPrune.enable = true;
defaultNetwork.settings = {
dns_enabled = true;
};
};
libvirtd.enable = true;
};
# Enable nix flakes and nix-command tools
nix = {
settings = {
substituters = [
"https://nix-community.cachix.org"
"https://cache.nixos.org/"
];
trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
warn-dirty = lib.mkForce false;
experimental-features = lib.mkForce [
"nix-command"
"flakes"
];
trusted-users = [ "@wheel" ];
};
# Garbage collect automatically every week
gc.automatic = lib.mkDefault true;
gc.options = lib.mkDefault "--delete-older-than 30d";
optimise.automatic = lib.mkDefault true;
};
# Nixpkgs configuration
nixpkgs = {
config = {
allowUnfree = lib.mkForce true;
allowUnsupportedSystem = true;
permittedInsecurePackages = [
# ...
];
};
};
nixpkgs.config.allowUnfreePredicate =
pkg:
builtins.elem (lib.getName pkg) [
"vscode-extension-github-copilot"
];
# Additional virtualization beyond what's in development module
virtualisation.libvirtd.enable = true;
}

View File

@@ -1,11 +1,8 @@
{ ... }:
let
defaultOptions = [ "compress=zstd" ];
in
{
fileSystems."/mnt" = {
fileSystems."/media/nas/main" = {
label = "nas_pool";
# device = "/dev/sde:/dev/sdf:/dev/sdh:/dev/sdi:/dev/:sdj";
# device = "/dev/sde:/dev/sdf:/dev/sdh:/dev/sdi:/dev/sdj:/dev/nmve0n1:/dev/nvme1n1";
fsType = "bcachefs";
mountPoint = "/media/nas/main";
};

View File

@@ -1,25 +0,0 @@
{ ... }:
{
# Configure nixpkgs
nixpkgs = {
config = {
# Enable non free
allowUnfree = true;
# enable cuda support
cudaSupport = true;
allowUnfreePredicate =
p:
builtins.all (
license:
license.free
|| builtins.elem license.shortName [
"CUDA EULA"
"cuDNN EULA"
"cuTENSOR EULA"
"NVidia OptiX EULA"
]
) (if builtins.isList p.meta.license then p.meta.license else [ p.meta.license ]);
};
};
}

View File

@@ -1,36 +0,0 @@
Common Settings:
INTERVAL=10
Settings of hwmon6/pwm5: -- chipset?
Depends on hwmon6/temp9_input
Controls hwmon6/fan5_input
MINTEMP=20
MAXTEMP=60
MINSTART=16
MINSTOP=14
MINPWM=14
Settings of hwmon6/pwm4: -- case?
Depends on hwmon2/temp1_input
Controls hwmon6/fan4_input
MINTEMP=20
MAXTEMP=90
MINSTART=60
MINSTOP=45
Settings of hwmon6/pwm3: -- cpu?
Depends on hwmon2/temp1_input
Controls hwmon6/fan3_input
MINTEMP=20
MAXTEMP=90
MINSTART=150
MINSTOP=0
MAXPWM=30
Settings of hwmon6/pwm2: -- cpu?
Depends on hwmon2/temp1_input
Controls hwmon6/fan2_input
MINTEMP=20
MAXTEMP=90
MINSTART=105
MINSTOP=0

View File

@@ -34,6 +34,12 @@ in
group = config.users.users."${user}".group;
};
"jallen-nas/nas_pool" = {
mode = "0600";
owner = config.users.users."${user}".name;
group = config.users.users."${user}".group;
};
"wifi" = {
sopsFile = sharedSops;
};

View File

@@ -1,12 +1,11 @@
{
lib,
namespace,
...
}:
{
imports = [
./boot.nix
./networking.nix
# ./networking.nix - moved to modules/nixos/network
./users.nix
./sops.nix
];
@@ -17,43 +16,36 @@
${namespace} = {
services.home-assistant.enable = true;
hardware.disko.enable = true;
network.hostName = "nuc-nixos";
};
# Enable nix flakes and nix-command tools
nix = {
settings = {
substituters = [
"https://nix-community.cachix.org"
"https://cache.nixos.org/"
];
trusted-public-keys = [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
warn-dirty = lib.mkForce false;
experimental-features = lib.mkForce [
"nix-command"
"flakes"
];
trusted-users = [ "@wheel" ];
hardware.disko = {
enable = true;
# filesystem = "bcachefs";
};
# Garbage collect automatically every week
gc.automatic = lib.mkDefault true;
gc.options = lib.mkDefault "--delete-older-than 30d";
optimise.automatic = lib.mkDefault true;
};
# Nixpkgs configuration
nixpkgs = {
config = {
allowUnfree = lib.mkForce true;
allowUnsupportedSystem = true;
permittedInsecurePackages = [
# ...
];
impermanence.enable = true;
network = {
hostName = "nuc-nixos";
useNetworkd = false;
ipv4 = {
method = "manual";
address = "10.0.1.4/24";
gateway = "10.0.1.1";
dns = "10.0.1.1";
};
wifi = {
enable = true;
profiles = {
"Joey's Jungle 6G" = {
ssid = "Joey's Jungle 6G";
keyMgmt = "sae";
};
};
};
firewall = {
enable = true;
allowPing = true;
allowedTCPPorts = [ 8192 ];
allowedUDPPorts = [ 8192 ];
};
};
};
}

View File

@@ -60,9 +60,5 @@ in
# Further reduce systemd output
systemd = {
services.systemd-udev-settle.enable = false;
extraConfig = ''
ShowStatus=no
DefaultTimeoutStartSec=15s
'';
};
}

View File

@@ -9,8 +9,6 @@
...
}:
{
nixpkgs.config.allowUnfree = lib.mkForce true;
# Define a user account. Don't forget to set a password with passwd.
users.users = {
deck = {

View File

@@ -15,19 +15,30 @@
./boot.nix
./configuration.nix
./jovian.nix
./networking.nix
# ./networking.nix - moved to modules/nixos/network
./sops.nix
];
nixpkgs.config.allowUnfree = true;
${namespace} = {
hardware.disko.enable = true;
impermanence.enable = true;
bootloader.lanzaboote.enable = true;
desktop.gnome.enable = true;
user = {
name = "deck";
};
network.hostName = "steamdeck";
network = {
hostName = "steamdeck";
wifi = {
enable = true;
powersave = false;
profiles = {
"Joey's Jungle 5G" = {
ssid = "Joey's Jungle 5G";
keyMgmt = "sae";
};
};
};
};
};
}

View File

@@ -1,551 +0,0 @@
#!/usr/bin/env python3
"""
Automatic Nix package update checker
Auto-discovers and checks GitHub-based Nix packages for updates
"""
import re
import json
import argparse
import requests
import subprocess
import shutil
from pathlib import Path
from typing import Dict, List, Optional, Tuple, NamedTuple
from dataclasses import dataclass
import sys
class PackageInfo(NamedTuple):
owner: str
repo: str
version: str
rev: str
current_hash: str
package_name: str
file_path: Path
@dataclass
class UpdateResult:
name: str
current_version: str
latest_version: str
has_update: bool
file_path: Path
repo_url: str
current_hash: Optional[str] = None
new_hash: Optional[str] = None
error: Optional[str] = None
class NixPackageChecker:
def __init__(self, search_paths: List[str] = None, max_depth: int = 3):
self.search_paths = search_paths or ["."]
self.max_depth = max_depth
self.session = requests.Session()
self.session.headers.update({'User-Agent': 'nix-package-checker'})
def find_nix_packages(self) -> List[Path]:
"""Auto-discover Nix package files with fetchFromGitHub"""
packages = []
for search_path in self.search_paths:
base_path = Path(search_path)
if not base_path.exists():
continue
# Find .nix files up to max_depth
for depth in range(self.max_depth + 1):
pattern = "**/" * depth + "*.nix"
for nix_file in base_path.glob(pattern):
if self._is_github_package(nix_file):
packages.append(nix_file)
return sorted(set(packages))
def _is_github_package(self, nix_file: Path) -> bool:
"""Check if a .nix file contains fetchFromGitHub"""
try:
content = nix_file.read_text(encoding='utf-8')
return 'fetchFromGitHub' in content and any(
pattern in content for pattern in ['owner =', 'repo =', 'version =']
)
except (UnicodeDecodeError, PermissionError):
return False
def compare_versions(self, current: str, latest: str) -> bool:
"""Compare versions, return True if latest is newer"""
if current == latest:
return False
# Handle HACS-X format
hacs_current = re.match(r'HACS-(\d+)', current)
hacs_latest = re.match(r'HACS-(\d+)', latest)
if hacs_current and hacs_latest:
return int(hacs_latest.group(1)) > int(hacs_current.group(1))
# Handle semantic versioning vX.Y.Z
sem_current = re.match(r'v?(\d+)\.(\d+)\.(\d+)', current)
sem_latest = re.match(r'v?(\d+)\.(\d+)\.(\d+)', latest)
if sem_current and sem_latest:
curr_parts = tuple(map(int, sem_current.groups()))
lat_parts = tuple(map(int, sem_latest.groups()))
return lat_parts > curr_parts
# Fallback to string comparison
return latest > current
def parse_nix_file(self, nix_file: Path) -> Optional[PackageInfo]:
"""Extract package information from a .nix file"""
try:
content = nix_file.read_text(encoding='utf-8')
except (UnicodeDecodeError, PermissionError) as e:
print(f"❌ Error reading {nix_file}: {e}")
return None
# Patterns to extract fields
patterns = {
'owner': r'owner\s*=\s*"([^"]+)"',
'repo': r'repo\s*=\s*"([^"]+)"',
'version': r'version\s*=\s*"([^"]+)"',
'rev': r'rev\s*=\s*(?:"([^"]+)"|([^;"\s]+))',
'hash': r'hash\s*=\s*"([^"]+)"',
# Package name patterns (in order of preference)
'domain': r'domain\s*=\s*"([^"]+)"', # Home Assistant components
'pname': r'pname\s*=\s*"([^"]+)"', # Standard Nix convention
'name': r'name\s*=\s*"([^"]+)"' # Older convention
}
extracted = {}
for field, pattern in patterns.items():
match = re.search(pattern, content)
if match:
if field == 'rev':
# Handle both quoted and unquoted rev values
extracted[field] = match.group(1) or match.group(2)
else:
extracted[field] = match.group(1)
# Validate required fields
required = ['owner', 'repo', 'version']
if not all(field in extracted for field in required):
missing = [f for f in required if f not in extracted]
print(f"⚠️ {nix_file.name}: Missing fields: {missing}")
return None
# Handle rev = version case
rev = extracted.get('rev', extracted['version'])
if rev == 'version':
rev = extracted['version']
# Extract current hash (may not exist for all packages)
current_hash = extracted.get('hash', '')
# Determine package name (priority: domain > pname > name > repo > directory)
package_name = None
for name_field in ['domain', 'pname', 'name']:
if name_field in extracted:
package_name = extracted[name_field]
break
if not package_name:
# Fall back to repo name
package_name = extracted['repo']
# If still no name and it's in a subdirectory, use directory name
if not package_name or package_name == extracted['repo']:
parent_dir = nix_file.parent.name
if parent_dir != '.' and parent_dir != nix_file.parent.parent.name:
package_name = f"{parent_dir}-{extracted['repo']}" if package_name == extracted['repo'] else parent_dir
return PackageInfo(
owner=extracted['owner'],
repo=extracted['repo'],
version=extracted['version'],
rev=rev,
current_hash=current_hash,
package_name=package_name,
file_path=nix_file
)
def get_latest_release(self, owner: str, repo: str) -> Optional[str]:
"""Get latest GitHub release tag"""
url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
try:
response = self.session.get(url, timeout=10)
if response.status_code == 200:
return response.json().get('tag_name')
elif response.status_code == 404:
# Try getting tags if no releases
return self._get_latest_tag(owner, repo)
else:
print(f"⚠️ API error for {owner}/{repo}: {response.status_code}")
return None
except requests.RequestException as e:
print(f"⚠️ Network error for {owner}/{repo}: {e}")
return None
def _get_latest_tag(self, owner: str, repo: str) -> Optional[str]:
"""Fallback to get latest tag if no releases"""
url = f"https://api.github.com/repos/{owner}/{repo}/tags"
try:
response = self.session.get(url, timeout=10)
if response.status_code == 200:
tags = response.json()
return tags[0]['name'] if tags else None
except requests.RequestException:
pass
return None
def get_github_hash(self, owner: str, repo: str, rev: str) -> Optional[str]:
"""Get hash for GitHub source using nix-prefetch-url or nix-prefetch-github"""
# Try nix-prefetch-url first (more commonly available)
if shutil.which('nix-prefetch-url'):
return self._get_hash_with_prefetch_url(owner, repo, rev)
# Fall back to nix-prefetch-github
elif shutil.which('nix-prefetch-github'):
return self._get_hash_with_prefetch_github(owner, repo, rev)
else:
print("⚠️ Neither nix-prefetch-url nor nix-prefetch-github found.")
print(" nix-prefetch-url is included in nix by default")
print(" nix-prefetch-github: nix-env -iA nixpkgs.nix-prefetch-github")
return None
def _get_hash_with_prefetch_url(self, owner: str, repo: str, rev: str) -> Optional[str]:
"""Get hash using nix-prefetch-url with GitHub archive URL"""
# GitHub archive URL format
url = f"https://github.com/{owner}/{repo}/archive/{rev}.tar.gz"
try:
# Use --unpack to match fetchFromGitHub behavior
cmd = ['nix-prefetch-url', '--unpack', url]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
if result.returncode == 0:
# nix-prefetch-url outputs the hash directly (sha256)
hash_value = result.stdout.strip()
return hash_value
else:
print(f"⚠️ nix-prefetch-url failed for {owner}/{repo}@{rev}:")
print(f" URL: {url}")
print(f" Error: {result.stderr.strip()}")
return None
except subprocess.TimeoutExpired:
print(f"⚠️ Timeout fetching hash for {owner}/{repo}@{rev} (60s limit)")
return None
except subprocess.SubprocessError as e:
print(f"⚠️ Error with nix-prefetch-url for {owner}/{repo}@{rev}: {e}")
return None
def _get_hash_with_prefetch_github(self, owner: str, repo: str, rev: str) -> Optional[str]:
"""Get hash using nix-prefetch-github (fallback method)"""
try:
cmd = ['nix-prefetch-github', owner, repo, '--rev', rev]
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
if result.returncode == 0:
# Parse JSON output to get sha256
data = json.loads(result.stdout)
return data.get('sha256')
else:
print(f"⚠️ nix-prefetch-github failed for {owner}/{repo}@{rev}:")
print(f" {result.stderr.strip()}")
return None
except subprocess.TimeoutExpired:
print(f"⚠️ Timeout fetching hash for {owner}/{repo}@{rev}")
return None
except (subprocess.SubprocessError, json.JSONDecodeError) as e:
print(f"⚠️ Error with nix-prefetch-github for {owner}/{repo}@{rev}: {e}")
return None
def update_nix_file(self, pkg_info: PackageInfo, new_version: str, new_hash: str) -> bool:
"""Update a .nix file with new version and hash"""
try:
content = pkg_info.file_path.read_text(encoding='utf-8')
# Create backup
backup_path = pkg_info.file_path.with_suffix('.nix.backup')
backup_path.write_text(content, encoding='utf-8')
# Update version
content = re.sub(
r'(version\s*=\s*)"[^"]+";',
f'\\1"{new_version}";',
content
)
# Update hash
if pkg_info.current_hash:
content = re.sub(
r'(hash\s*=\s*)"[^"]+";',
f'\\1"sha256-{new_hash}";',
content
)
# Write updated content
pkg_info.file_path.write_text(content, encoding='utf-8')
return True
except Exception as e:
print(f"❌ Error updating {pkg_info.file_path}: {e}")
return False
"""Compare versions, return True if latest is newer"""
if current == latest:
return False
# Handle HACS-X format
hacs_current = re.match(r'HACS-(\d+)', current)
hacs_latest = re.match(r'HACS-(\d+)', latest)
if hacs_current and hacs_latest:
return int(hacs_latest.group(1)) > int(hacs_current.group(1))
# Handle semantic versioning vX.Y.Z
sem_current = re.match(r'v?(\d+)\.(\d+)\.(\d+)', current)
sem_latest = re.match(r'v?(\d+)\.(\d+)\.(\d+)', latest)
if sem_current and sem_latest:
curr_parts = tuple(map(int, sem_current.groups()))
lat_parts = tuple(map(int, sem_latest.groups()))
return lat_parts > curr_parts
# Fallback to string comparison
return latest > current
def check_package(self, pkg_info: PackageInfo, fetch_hash: bool = False) -> UpdateResult:
"""Check a single package for updates"""
latest_version = self.get_latest_release(pkg_info.owner, pkg_info.repo)
if latest_version is None:
return UpdateResult(
name=pkg_info.package_name,
current_version=pkg_info.version,
latest_version="unknown",
has_update=False,
file_path=pkg_info.file_path,
repo_url=f"https://github.com/{pkg_info.owner}/{pkg_info.repo}",
current_hash=pkg_info.current_hash,
error="Could not fetch latest release"
)
has_update = self.compare_versions(pkg_info.version, latest_version)
new_hash = None
# Fetch new hash if update available and requested
if has_update and fetch_hash:
print(f" 🔄 Fetching hash for {pkg_info.package_name} ({pkg_info.owner}/{pkg_info.repo}@{latest_version})...")
new_hash = self.get_github_hash(pkg_info.owner, pkg_info.repo, latest_version)
return UpdateResult(
name=pkg_info.package_name,
current_version=pkg_info.version,
latest_version=latest_version,
has_update=has_update,
file_path=pkg_info.file_path,
repo_url=f"https://github.com/{pkg_info.owner}/{pkg_info.repo}",
current_hash=pkg_info.current_hash,
new_hash=new_hash
)
def check_all_packages(self, fetch_hash: bool = False, auto_update: bool = False) -> List[UpdateResult]:
"""Check all discovered packages"""
nix_files = self.find_nix_packages()
if not nix_files:
print("No Nix package files found")
return []
print(f"Found {len(nix_files)} package files")
# Show which hash fetching tool is available
if fetch_hash or auto_update:
if shutil.which('nix-prefetch-url'):
print("Hash fetching: using nix-prefetch-url")
elif shutil.which('nix-prefetch-github'):
print("Hash fetching: using nix-prefetch-github")
else:
print("⚠️ No hash fetching tool available")
results = []
for nix_file in nix_files:
pkg_info = self.parse_nix_file(nix_file)
if pkg_info:
result = self.check_package(pkg_info, fetch_hash=fetch_hash)
results.append(result)
# Auto-update if requested and update available
if auto_update and result.has_update and result.new_hash:
print(f" 🔄 Auto-updating {result.name}...")
if self.update_nix_file(pkg_info, result.latest_version, result.new_hash):
print(f" ✅ Updated {result.file_path}")
else:
print(f" ❌ Failed to update {result.file_path}")
return results
def print_results(results: List[UpdateResult], show_all: bool = True, show_hashes: bool = False):
"""Print results in a nice format"""
if not results:
return
updates_available = [r for r in results if r.has_update]
if show_all:
print(f"\n{'Package':<25} {'Current':<15} {'Latest':<15} {'Status'}")
print("-" * 70)
for result in results:
if result.error:
status = f"{result.error}"
elif result.has_update:
status = "🔄 Update available"
else:
status = "✅ Up to date"
print(f"{result.name:<25} {result.current_version:<15} {result.latest_version:<15} {status}")
# Show file path for default.nix files or when there might be confusion
if result.file_path.name == 'default.nix' or len([r for r in results if r.name == result.name]) > 1:
rel_path = result.file_path.relative_to(Path.cwd()) if result.file_path.is_absolute() else result.file_path
print(f"{'':>25} File: {rel_path}")
# Show hash information if available and requested
if show_hashes and result.has_update and result.new_hash:
print(f"{'':>25} Current hash: {result.current_hash[:16]}..." if result.current_hash else "")
print(f"{'':>25} New hash: sha256-{result.new_hash[:16]}...")
# Summary
print(f"\nSummary:")
print(f" Total packages: {len(results)}")
print(f" Updates available: {len(updates_available)}")
if updates_available:
print(f"\nPackages with updates:")
for result in updates_available:
rel_path = result.file_path.relative_to(Path.cwd()) if result.file_path.is_absolute() else result.file_path
print(f"{result.name}: {result.current_version}{result.latest_version}")
print(f" File: {rel_path}")
print(f" Repo: {result.repo_url}")
if show_hashes and result.new_hash:
print(f" New hash: sha256-{result.new_hash}")
def print_updates_only(results: List[UpdateResult], show_hashes: bool = False):
"""Print only packages with updates"""
updates = [r for r in results if r.has_update]
if not updates:
print("No updates available")
return
print("Updates available:")
for result in updates:
rel_path = result.file_path.relative_to(Path.cwd()) if result.file_path.is_absolute() else result.file_path
print(f" {result.name}: {result.current_version}{result.latest_version}")
print(f" File: {rel_path}")
if show_hashes and result.new_hash:
print(f" New hash: sha256-{result.new_hash}")
elif show_hashes:
print(f" Hash: (not fetched)")
def output_json(results: List[UpdateResult]):
"""Output results as JSON"""
data = {}
for result in results:
data[result.name] = {
"current_version": result.current_version,
"latest_version": result.latest_version,
"has_update": result.has_update,
"file_path": str(result.file_path),
"repo_url": result.repo_url,
"current_hash": result.current_hash,
"new_hash": result.new_hash,
"error": result.error
}
print(json.dumps(data, indent=2))
def main():
parser = argparse.ArgumentParser(
description="Automatically check Nix packages for GitHub updates",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s # Check all packages in current directory
%(prog)s --updates # Show only packages with updates
%(prog)s --fetch-hash # Also fetch new hashes for updates
%(prog)s --auto-update # Automatically update .nix files
%(prog)s --json # Output as JSON
%(prog)s --path ./packages # Check specific directory
%(prog)s --depth 5 # Search deeper directory levels
Requirements:
For hash fetching: nix-prefetch-url (part of nix) or nix-prefetch-github
nix-prefetch-url is preferred and usually already available
"""
)
parser.add_argument('--updates', action='store_true',
help='Show only packages with updates available')
parser.add_argument('--fetch-hash', action='store_true',
help='Fetch new hashes for packages with updates (requires nix-prefetch-url or nix-prefetch-github)')
parser.add_argument('--auto-update', action='store_true',
help='Automatically update .nix files with new versions and hashes')
parser.add_argument('--json', action='store_true',
help='Output results as JSON')
parser.add_argument('--path', action='append', default=[],
help='Search path for .nix files (can be used multiple times)')
parser.add_argument('--depth', type=int, default=3,
help='Maximum directory depth to search (default: 3)')
parser.add_argument('--list', action='store_true',
help='List discovered package files without checking updates')
args = parser.parse_args()
# Auto-update implies fetch-hash
if args.auto_update:
args.fetch_hash = True
# Use provided paths or default to current directory
search_paths = args.path if args.path else ["."]
checker = NixPackageChecker(search_paths=search_paths, max_depth=args.depth)
if args.list:
# Just list discovered files
nix_files = checker.find_nix_packages()
print(f"Discovered {len(nix_files)} package files:")
for nix_file in nix_files:
pkg_info = checker.parse_nix_file(nix_file)
if pkg_info:
rel_path = nix_file.relative_to(Path.cwd()) if nix_file.is_absolute() else nix_file
print(f" {pkg_info.package_name:<25} {pkg_info.owner}/{pkg_info.repo} ({pkg_info.version}) - {rel_path}")
else:
rel_path = nix_file.relative_to(Path.cwd()) if nix_file.is_absolute() else nix_file
print(f" {'(parse failed)':<25} - {rel_path}")
return
# Check for updates
results = checker.check_all_packages(
fetch_hash=args.fetch_hash,
auto_update=args.auto_update
)
if not results:
print("No packages found to check")
return
# Output results
if args.json:
output_json(results)
elif args.updates:
print_updates_only(results, show_hashes=args.fetch_hash)
else:
print_results(results, show_all=True, show_hashes=args.fetch_hash)
# Set exit code based on updates available
updates_available = any(r.has_update for r in results)
sys.exit(1 if updates_available else 0)
if __name__ == "__main__":
main()