initial commit

This commit is contained in:
sid 2026-02-23 20:50:47 +01:00
commit c094b5770c
113 changed files with 6879 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
result
target

43
.sops.yaml Normal file
View file

@ -0,0 +1,43 @@
keys:
- &host_16ach6 age1km907lx69fwvmwgt7rspkuyxtkdrhr7r7t0mw20e5rymsu364exs3rl28q
- &host_sid age1ytfze9tv5l80ujqfd66xp97w2u0lq8jrx45ulf0szey8ny0t837sdktdzf
- &host_rv2 age1j6s2ec3ltm9004fhvmd7xqq0zna2fr4m8kw4f235r9k0hfryjctq050vs2
- &host_pc age1zdd344x69n8umt2qjjvz8pjnt43lacvvqfdquc5jqz4x9x7pnu3sg0as0k
- &host_rx4 age10mkn2jgwpue7gy92vwjqp4j4j9qyh5enrzw8j82v872yflawvvkqhqde34
- &host_vde age1mc07jayz4dpwenh06fzlcgfzk5t7ln0z3n65emwlm5r7nq59m4jstd7y8u
- &user_sid age19yeqvv28fgrtk6jsh3xyaf0lch86kna6rcz4dwe962yyyyevu30sx474xy
creation_rules:
- path_regex: hosts/16ach6/secrets/secrets.yaml$
key_groups:
- age:
- *user_sid
- *host_16ach6
- path_regex: hosts/sid/secrets/secrets.yaml$
key_groups:
- age:
- *user_sid
- *host_sid
- path_regex: hosts/rv2/secrets/secrets.yaml$
key_groups:
- age:
- *user_sid
- *host_rv2
- path_regex: hosts/pc/secrets/secrets.yaml$
key_groups:
- age:
- *user_sid
- *host_pc
- path_regex: hosts/rx4/secrets/secrets.yaml$
key_groups:
- age:
- *user_sid
- *host_rx4
- path_regex: hosts/vde/secrets/secrets.yaml$
key_groups:
- age:
- *user_sid
- *host_vde
- path_regex: users/sid/home/secrets/secrets.yaml$
key_groups:
- age:
- *user_sid

3
README.md Normal file
View file

@ -0,0 +1,3 @@
# NixOS and Home Manager configurations
This repository is a collection of personal NixOS configurations with standalone Home Manager powered by [*synix*](https://git.sid.ovh/sid/synix).

18
apps/deploy/default.nix Normal file
View file

@ -0,0 +1,18 @@
{
writeShellApplication,
jq,
...
}:
let
name = "deploy";
text = builtins.readFile ./${name}.sh;
in
writeShellApplication {
inherit name text;
meta.mainProgram = name;
runtimeInputs = [
jq
];
}

83
apps/deploy/deploy.sh Normal file
View file

@ -0,0 +1,83 @@
#!/usr/bin/env bash
# defaults
FLAKE_URI="."
CONFIG_FILE="./deploy.json"
ACTION="switch"
USE_SUDO=true
DO_BUILD=true
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS] [ACTION]
Arguments:
ACTION switch | boot | test (Default: switch)
Options:
-f, --flake URI URI of the flake (Default: $FLAKE_URI)
-c, --config FILE Deployment config file (Default: $CONFIG_FILE)
--no-sudo Do not pass sudo-related flags to nixos-rebuild.
--skip-build Skip the explicit 'build' step before deployment.
-h, --help Show this help.
EOF
}
_status() { echo -e "\033[0;34m> $1\033[0m"; }
success() { echo -e "\033[0;32m$1\033[0m"; }
error() { echo -e "\033[0;31mError: $1\033[0m" >&2; exit 1; }
while [[ $# -gt 0 ]]; do
case "$1" in
switch|boot|test) ACTION="$1"; shift ;;
-f|--flake) FLAKE_URI="$2"; shift 2 ;;
-c|--config) CONFIG_FILE="$2"; shift 2 ;;
--no-sudo) USE_SUDO=false; shift ;;
--skip-build) DO_BUILD=false; shift ;;
-h|--help) usage; exit 0 ;;
*) error "Invalid argument '$1'" ;;
esac
done
command -v jq &> /dev/null || error "jq is not installed."
[ -f "$CONFIG_FILE" ] || error "Config '$CONFIG_FILE' not found."
BUILD_HOST=$(jq -r '.buildHost // "localhost"' "$CONFIG_FILE")
[[ "$BUILD_HOST" =~ ^(127\.0\.0\.1|::1)$ ]] && BUILD_HOST="localhost"
mapfile -t HOSTS < <(jq -r '.hosts[]' "$CONFIG_FILE")
[ ${#HOSTS[@]} -eq 0 ] && error "No hosts defined in $CONFIG_FILE"
echo "Action: $ACTION"
echo "Flake: $FLAKE_URI"
echo "Builder: $BUILD_HOST"
echo "Targets: ${HOSTS[*]}"
if [ "$DO_BUILD" = true ]; then
_status "Building configurations..."
for host in "${HOSTS[@]}"; do
echo "------------------------------------------------"
echo "Building host '$host':"
CMD=("nixos-rebuild" "build" "--flake" "${FLAKE_URI}#${host}")
[[ "$BUILD_HOST" != "localhost" ]] && CMD+=("--build-host" "$BUILD_HOST")
"${CMD[@]}" || error "Build failed for $host"
success "Build for host '$host' successful."
done
fi
_status "Deploying to targets..."
for host in "${HOSTS[@]}"; do
echo "------------------------------------------------"
echo "Deploying to host '$host':"
CMD=("nixos-rebuild" "$ACTION" "--flake" "${FLAKE_URI}#${host}" "--target-host" "$host")
[[ "$BUILD_HOST" != "localhost" ]] && CMD+=("--build-host" "$BUILD_HOST")
[[ "$USE_SUDO" = true ]] && CMD+=("--sudo" "--ask-sudo-password")
"${CMD[@]}" || error "Activation failed for $host"
success "Host '$host' updated."
done
success "Deployment complete."

1821
flake.lock generated Normal file

File diff suppressed because it is too large Load diff

186
flake.nix Normal file
View file

@ -0,0 +1,186 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
nixpkgs-old-stable.url = "github:nixos/nixpkgs/nixos-25.05";
nixpkgs-old-old-stable.url = "github:nixos/nixpkgs/nixos-24.11";
home-manager.url = "github:nix-community/home-manager/release-25.11";
home-manager.inputs.nixpkgs.follows = "nixpkgs";
synix.url = "git+https://git.sid.ovh/sid/synix.git?ref=release-25.11";
# synix.url = "git+file:///home/sid/src/synix";
synix.inputs.nixpkgs.follows = "nixpkgs";
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
nixvim.url = "github:nix-community/nixvim/nixos-25.11";
nixvim.inputs.nixpkgs.follows = "nixpkgs";
nur.url = "github:nix-community/NUR";
nur.inputs.nixpkgs.follows = "nixpkgs";
sops-nix.url = "github:Mic92/sops-nix";
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
stylix.url = "github:nix-community/stylix/release-25.11";
stylix.inputs.nixpkgs.follows = "nixpkgs";
nix-flatpak.url = "github:gmodena/nix-flatpak/?ref=latest";
anyrun.url = "github:anyrun-org/anyrun";
anyrun.inputs.nixpkgs.follows = "nixpkgs";
kidex.url = "github:Kirottu/kidex";
kidex.inputs.nixpkgs.follows = "nixpkgs";
pre-commit-hooks.url = "github:cachix/git-hooks.nix";
pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs";
winapps.url = "github:winapps-org/winapps";
winapps.inputs.nixpkgs.follows = "nixpkgs";
gen-dmc.url = "github:kmein/gen-dmc/pull/3/head";
gen-dmc.inputs.nixpkgs.follows = "nixpkgs";
multios-usb.url = "github:Mexit/MultiOS-USB";
multios-usb.inputs.nixpkgs.follows = "nixpkgs";
};
outputs =
{
self,
nixpkgs,
home-manager,
...
}@inputs:
let
inherit (self) outputs;
supportedSystems = [
"x86_64-linux"
];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
lib = nixpkgs.lib.extend (final: prev: inputs.synix.lib or { });
mkNixosConfiguration =
system: modules:
nixpkgs.lib.nixosSystem {
inherit system modules;
specialArgs = {
inherit inputs outputs lib;
};
};
in
{
packages = forAllSystems (system: import ./pkgs nixpkgs.legacyPackages.${system});
overlays = import ./overlays { inherit inputs; };
nixosModules = import ./modules/nixos;
homeModules = import ./modules/home;
devShells = forAllSystems (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
default = import ./shell.nix { inherit pkgs; };
kicad = pkgs.mkShell {
buildInputs = [
(pkgs.python313.withPackages (
p: with p; [
kicad
requests
wxpython
]
))
];
};
}
);
nixosConfigurations = {
"16ach6" = mkNixosConfiguration "x86_64-linux" [ ./hosts/16ach6 ];
nuc8 = mkNixosConfiguration "x86_64-linux" [ ./hosts/nuc8 ];
pc = mkNixosConfiguration "x86_64-linux" [ ./hosts/pc ];
rv2 = mkNixosConfiguration "x86_64-linux" [ ./hosts/rv2 ];
};
homeConfigurations = {
"sid@16ach6" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
extraSpecialArgs = {
inherit inputs outputs;
};
modules = [
./users/sid/home
./users/sid/home/hosts/16ach6
];
};
"sid@nuc8" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
extraSpecialArgs = {
inherit inputs outputs;
};
modules = [
./users/sid/home
./users/sid/home/hosts/nuc8
];
};
"sid@pc" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
extraSpecialArgs = {
inherit inputs outputs;
};
modules = [
./users/sid/home
./users/sid/home/hosts/pc
];
};
"sid@rv2" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages.x86_64-linux;
extraSpecialArgs = {
inherit inputs outputs;
};
modules = [
./users/sid/home
./users/sid/home/hosts/rv2
];
};
};
formatter = forAllSystems (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
config = self.checks.${system}.pre-commit-check.config;
inherit (config) package configFile;
script = ''
${pkgs.lib.getExe package} run --all-files --config ${configFile}
'';
in
pkgs.writeShellScriptBin "pre-commit-run" script
);
checks = forAllSystems (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
flakePkgs = self.packages.${system};
in
{
pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
src = ./.;
hooks = {
nixfmt.enable = true;
};
};
build-packages = pkgs.linkFarm "flake-packages-${system}" flakePkgs;
}
);
};
}

7
hosts/16ach6/boot.nix Normal file
View file

@ -0,0 +1,7 @@
{
boot.loader.systemd-boot = {
enable = true;
configurationLimit = 20;
};
boot.loader.efi.canTouchEfiVariables = true;
}

77
hosts/16ach6/default.nix Normal file
View file

@ -0,0 +1,77 @@
{
inputs,
outputs,
...
}:
{
imports = [
./boot.nix
./hardware.nix
./packages.nix
./secrets
./virtualisation.nix
# ./winapps.nix # trying windows-oci for now
# ./wireguard.nix # TODO: use NM for client config
../../users/sid
inputs.synix.nixosModules.common
inputs.synix.nixosModules.device.laptop
inputs.synix.nixosModules.hyprland
inputs.synix.nixosModules.i2pd
inputs.synix.nixosModules.openssh
inputs.synix.nixosModules.windows-oci
# outputs.nixosModules.anything-llm-oci
outputs.nixosModules.appimage
outputs.nixosModules.common
# outputs.nixosModules.docker # conflicts with `virtualisation.podman.dockerCompat`
outputs.nixosModules.docs
outputs.nixosModules.syncthing
outputs.nixosModules.tailscale
outputs.nixosModules.wine
];
networking.hostName = "16ach6";
services = {
envfs.enable = true;
i2pd.enable = true;
openssh.enable = true;
windows-oci = {
# enable = true; # FIXME
sharedVolume = "/home/sid/pub";
};
};
boot.binfmt.emulatedSystems = [
"aarch64-linux"
];
virtualisation.waydroid.enable = true;
# sudo waydroid init
# sudo systemctl enable --now waydroid-container.service
# waydroid session start
# waydroid app launch com.foo.bar
normalUsers = {
sid = {
extraGroups = [
"audio"
"dialout"
"floppy"
"input"
"lp"
"networkmanager"
"video"
];
};
};
programs.steam.enable = true;
boot.enableContainers = true;
system.stateVersion = "24.11";
}

49
hosts/16ach6/hardware.nix Normal file
View file

@ -0,0 +1,49 @@
{
inputs,
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
# inputs.nixos-hardware.nixosModules.lenovo-ideapad-16ach6
];
boot.initrd.availableKernelModules = [
"nvme"
"xhci_pci"
"ahci"
"usb_storage"
"sd_mod"
"sdhci_pci"
];
boot.initrd.kernelModules = [ "amdgpu" ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-label/ROOT";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-label/BOOT";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
swapDevices = [ { device = "/dev/disk/by-label/SWAP"; } ];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
hardware.graphics.enable = true;
}

10
hosts/16ach6/packages.nix Normal file
View file

@ -0,0 +1,10 @@
{ pkgs, ... }:
{
environment = {
systemPackages = with pkgs; [
evtest
linuxConsoleTools
];
};
}

View file

@ -0,0 +1,5 @@
{ inputs, ... }:
{
imports = [ inputs.synix.nixosModules.sops ];
}

View file

@ -0,0 +1,36 @@
wireguard:
wg0:
private-key: ENC[AES256_GCM,data:6G+VkNsoFK1zyurW/xuaw5ZawpGXYdT3YbYMwiYvpsqNiGhB9CMT/0v2HuE=,iv:vg7OcXghMzbQL0NYdnuAue2MC8l6l++TCoXJjGtpk/g=,tag:urVD9LfMtO5c95tHouX7YQ==,type:str]
tailscale:
auth-key: ENC[AES256_GCM,data:u1TCO6pEKnOemhWSnb9UPCURFoKcR0uuipGzwu5QYVtzm7MLtvd5llhha8/H7WYQ,iv:0rwuQ3b6UOJth7YqaLJGNp0OqRYCb/z/HFK0vOY9ACw=,tag:H79JGEfBYB8hNrGZKAxHzg==,type:str]
anything-llm-oci:
openrouter-api-key: ENC[AES256_GCM,data:iEi1ZDGnhNaFjuL/cv/XkMH/GtEgW4cmRPc/PrSgCBcJai2uA2NfhpS4ZJfzvzXyhvCEBVK05932N0PFAkYqryFD4PhGPE6N7g==,iv:tWlM8NlzV9/6vpbIEM0lt39ZJQGm/trEwYbnqpTCpro=,tag:OAUbTc4PbJsy7jqLixZOvw==,type:str]
jwt-secret: ENC[AES256_GCM,data:TBgjAwOH8pzRYxSvGaqaY5kFk0vVQjbKu+i2o3xPl4pRILQrzll0R4Sll5Qu7kW8WqyBBEEsEBBvY0sz2YR6aQ==,iv:8/yViXyTpxdRWthJt4D0KhZJ2+uTKXUV8UZUEsy8+kk=,tag:eWkaFZg2rtqziUAcjdcs1g==,type:str]
sig-key: ENC[AES256_GCM,data:VRFkIK2ywV0b1Dz40XtdcFk3aZ/iIaNxiB4C1zbh8P5EQbkIEE0AcSHlWc3gFwhLEjrAz37D/Js7lmGaR9XLaQ==,iv:pBv/cuciNXbV5IHmNbu8MCwiVK4MSwaBsiJ6SjpXjyU=,tag:VB9RuEC7orBBdR0qECOalQ==,type:str]
sig-salt: ENC[AES256_GCM,data:I3ggthhiehT54ad5O4Y7sqR4yo9Cs2RBnAB3jUem755N3MqjaPhw6PVpE92/UacNfqkMeHVINImUUo/nvuwr1w==,iv:qextgxloGUs0dSDrK29XnF68P89WICywktolqXJpY8k=,tag:9ilT8TKdAKu18J422uhN5Q==,type:str]
syncthing:
gui-pw: ENC[AES256_GCM,data:dDccKohXulosuG4JQzLCtdf0+cY=,iv:Yk41rJqt4y4QhWkcP2upMd4h/orNMYTX4wO0TObrYpI=,tag:X3/ig+Kv2t7Wy8muxX3RGw==,type:str]
sops:
age:
- recipient: age19yeqvv28fgrtk6jsh3xyaf0lch86kna6rcz4dwe962yyyyevu30sx474xy
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxM2pkS3lOaVpuUFdHVy9h
UU9ZcVIyTGlUUVpqdHFzU3llVENyOXV2eTFZCnVwVWlzR0N4QXZsNEZvRFFScHpl
cnBucWp6ZkN2Q3VKMmJMWlhOVVNtYmcKLS0tIG5ENjVtVjhqeWlBMFFRM3RoS0pC
bml1R3djSEgxbDVxZ1Jwc28zQWoycEkKUwt/8zCkhD1b7dVMYd7FHxABjwPhTQxA
Lw1sBePiKQxeZTiWVucMrrHk85omGQEPNECTdhBqF4aOS0glRrwCEQ==
-----END AGE ENCRYPTED FILE-----
- recipient: age1km907lx69fwvmwgt7rspkuyxtkdrhr7r7t0mw20e5rymsu364exs3rl28q
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJNGdEd3hJcTc0QzF5ZitN
SnNTV1NSOWRRV1VTczZmcStjRmJ5Q01mSEZFCi80cFN0TVY0WmJseFVBM1JEaTlK
WmNiWFBMT1dudVp1REsyYU1OUm1haVUKLS0tIFRZdE11WnpNQW1kbEZzNlpSWE5m
T1JDdVlwRVYwLy9ud0EyNldFcXNDaUUKdXq2ulChfK6XBpX/bkP/fz9XCm/YVHkX
QRPemdtP2Sp7VBcAtlWNbXFcr3osRR2nLKxDl+NntEHRCNs3ffnGew==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-01-13T21:05:39Z"
mac: ENC[AES256_GCM,data:aSOlu1iuSDuUdSt6cZhbzorY37ECHqIkz73iPi2Sn6WyDNCsEwn2rJpQxXSDG/O0+HLoyCgkyR9PwrI0Gn0sDAtcPHhVjOQC8656muNEV3fZWBPIJ+K4++xZDAH66L1UN7Y210EnYtYT6pY61jrFz2NWVjd1V9hTcCmbfpySrAA=,iv:gmPRLuMagjY/Dgc3VvurvLz4qgfTsMp/YIgqHXuG6ag=,tag:I5hKLnEXDvMRXOY2YuFG9g==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0

View file

@ -0,0 +1,42 @@
{
inputs,
config,
lib,
pkgs,
...
}:
{
imports = [ inputs.synix.nixosModules.virtualisation ];
virtualisation = {
vfio = {
enable = true;
IOMMUType = "amd";
devices = [
"10de:1f9d"
];
blacklistNvidia = true;
ignoreMSRs = true;
};
libvirtd.deviceACL = [
"/dev/kvm"
"/dev/net/tun"
"/dev/vfio/vfio"
"/dev/null"
"/dev/ptmx"
];
hugepages.enable = true;
quickemu.enable = true;
};
users.extraGroups.libvirtd.members = [ "sid" ];
users.extraGroups.qemu-libvirtd.members = [ "sid" ];
users.extraGroups.kvm.members = [ "sid" ];
systemd.tmpfiles.rules = [ "f /dev/shm/looking-glass 0660 sid libvirtd -" ];
environment.systemPackages = [
pkgs.looking-glass-client
];
}

11
hosts/16ach6/winapps.nix Normal file
View file

@ -0,0 +1,11 @@
{ inputs, pkgs, ... }:
let
inherit (pkgs.stdenv.hostPlatform) system;
in
{
environment.systemPackages = with inputs.winapps.packages."${system}"; [
winapps
winapps-launcher
];
}

View file

@ -0,0 +1,18 @@
{ inputs, ... }:
{
imports = [ inputs.synix.nixosModules.wg-client ];
networking.wg-client = {
enable = true;
interfaces = {
wg0 = {
clientAddress = "10.0.0.2";
peer = {
publicIP = "91.99.172.127";
publicKey = "hRrnXl1heROHfpXkHOmjITUpG/ht3omVsWurLcChIS4=";
};
};
};
};
}

45
hosts/nuc8/README.md Normal file
View file

@ -0,0 +1,45 @@
# Windows 10 installation
> Important: Install Windows 10 *before* NixOS
Before setup, press `SHIFT+F10`. Then, enter the following commands in the terminal window:
```
diskpart
```
Get your drive number with:
```
list disk
```
> most likely `0`
```
select disk 0
clean
convert gpt
create partition efi size=1024
format quick fs=fat32 label="System"
create partition msr size=16
create partition primary
shrink minimum=1024
format quick fs=ntfs label="Windows"
create partition primary
format quick fs=ntfs label="Recovery"
exit
```
Close the terminal and proceed as usual.
After booting into your finished Windows installation, resize the C drive to make some space for your Linux root and swap partitions.
# NixOS config
See [*Autodetection with systemd-boot*](https://nixos.wiki/wiki/Dual_Booting_NixOS_and_Windows).

7
hosts/nuc8/boot.nix Normal file
View file

@ -0,0 +1,7 @@
{
boot.loader.systemd-boot = {
enable = true;
configurationLimit = 10;
};
boot.loader.efi.canTouchEfiVariables = true;
}

46
hosts/nuc8/default.nix Normal file
View file

@ -0,0 +1,46 @@
{ inputs, outputs, ... }:
{
imports = [
./boot.nix
./hardware.nix
./packages.nix
../../users/sid
inputs.synix.nixosModules.bluetooth
inputs.synix.nixosModules.common
inputs.synix.nixosModules.device.desktop
inputs.synix.nixosModules.hyprland
inputs.synix.nixosModules.openssh
inputs.synix.nixosModules.virtualisation
outputs.nixosModules.common
outputs.nixosModules.docs
];
networking.hostName = "nuc8";
services = {
openssh.enable = true;
pipewire.enable = true;
};
normalUsers = {
sid = {
extraGroups = [
"audio"
"floppy"
"input"
"libvirtd"
"lp"
"networkmanager"
"video"
];
};
};
time.hardwareClockInLocalTime = true; # Windows compatibility
system.stateVersion = "24.11";
}

49
hosts/nuc8/disks.sh Normal file
View file

@ -0,0 +1,49 @@
#!/usr/bin/env bash
SSD='/dev/disk/by-id/nvme-Micron_MTFDHBA512TDV_21212F5AAB85'
MNT='/mnt'
SWAP_GB=16
# Helper function to wait for devices
wait_for_device() {
local device=$1
echo "Waiting for device: $device ..."
while [[ ! -e $device ]]; do
sleep 1
done
echo "Device $device is ready."
}
if ! command -v sgdisk &> /dev/null; then
nix-env -iA nixos.gptfdisk
fi
swapoff --all
udevadm settle
wait_for_device $SSD
echo "Partitioning $SSD..."
sgdisk -n5:0:+"$SWAP_GB"G -t5:8200 -c5:SWAP $SSD
sgdisk -n6:0:0 -t6:8304 -c6:ROOT $SSD
partprobe -s $SSD
udevadm settle
wait_for_device ${SSD}-part1 # Windows ESP
wait_for_device ${SSD}-part5
wait_for_device ${SSD}-part6
echo "Formatting partitions..."
mkswap -L SWAP "${SSD}-part5"
mkfs.ext4 -L ROOT "${SSD}-part6"
echo "Mounting partitions..."
mount -o X-mount.mkdir "${SSD}-part6" "$MNT"
mkdir -p "$MNT/boot"
mount "${SSD}-part1" "$MNT/boot"
echo "Enabling swap..."
swapon "${SSD}-part5"
echo "Partitioning and setup complete:"
lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,LABEL

49
hosts/nuc8/hardware.nix Normal file
View file

@ -0,0 +1,49 @@
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"xhci_pci"
"ahci"
"nvme"
"usbhid"
"usb_storage"
"sd_mod"
"rtsx_pci_sdmmc"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-label/ROOT";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-label/SYSTEM";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
swapDevices = [
{ device = "/dev/disk/by-label/SWAP"; }
];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

8
hosts/nuc8/packages.nix Normal file
View file

@ -0,0 +1,8 @@
{ pkgs, ... }:
{
environment = {
systemPackages = with pkgs; [
];
};
}

7
hosts/pc/boot.nix Normal file
View file

@ -0,0 +1,7 @@
{
boot.loader.systemd-boot = {
enable = true;
configurationLimit = 20;
};
boot.loader.efi.canTouchEfiVariables = true;
}

49
hosts/pc/default.nix Normal file
View file

@ -0,0 +1,49 @@
{
inputs,
outputs,
...
}:
{
imports = [
./boot.nix
./hardware.nix
./networking.nix
./packages.nix
./secrets
./services.nix
../../users/sid
inputs.synix.nixosModules.bluetooth
inputs.synix.nixosModules.common
inputs.synix.nixosModules.device.desktop
inputs.synix.nixosModules.hyprland
outputs.nixosModules.common
outputs.nixosModules.docs
# outputs.nixosModules.syncthing
outputs.nixosModules.tailscale
outputs.nixosModules.wine
];
normalUsers = {
sid = {
extraGroups = [
"audio"
"dialout"
"floppy"
"input"
"lp"
"networkmanager"
"video"
];
};
};
programs.steam.enable = true;
boot.enableContainers = true;
system.stateVersion = "25.11";
}

63
hosts/pc/disks.sh Normal file
View file

@ -0,0 +1,63 @@
#!/usr/bin/env bash
SSD='/dev/disk/by-id/nvme-SPCC_M.2_PCIe_SSD_7E1D079A184C00191521'
MNT='/mnt'
SWAP_GB=8
# Helper function to wait for devices
wait_for_device() {
local device=$1
echo "Waiting for device: $device ..."
while [[ ! -e $device ]]; do
sleep 1
done
echo "Device $device is ready."
}
# Function to install a package if it's not already installed
install_if_missing() {
local cmd="$1"
local package="$2"
if ! command -v "$cmd" &> /dev/null; then
echo "$cmd not found, installing $package..."
nix-env -iA "nixos.$package"
fi
}
install_if_missing "sgdisk" "gptfdisk"
install_if_missing "partprobe" "parted"
wait_for_device $SSD
echo "Wiping filesystem on $SSD..."
wipefs -a $SSD
echo "Clearing partition table on $SSD..."
sgdisk --zap-all $SSD
echo "Partitioning $SSD..."
sgdisk -n1:1M:+1G -t1:EF00 -c1:BOOT $SSD
sgdisk -n2:0:+"$SWAP_GB"G -t2:8200 -c2:SWAP $SSD
sgdisk -n3:0:0 -t3:8304 -c3:ROOT $SSD
partprobe -s $SSD
udevadm settle
wait_for_device ${SSD}-part1
wait_for_device ${SSD}-part2
wait_for_device ${SSD}-part3
echo "Formatting partitions..."
mkfs.vfat -F 32 -n BOOT "${SSD}-part1"
mkswap -L SWAP "${SSD}-part2"
mkfs.ext4 -L ROOT "${SSD}-part3"
echo "Mounting partitions..."
mount -o X-mount.mkdir "${SSD}-part3" "$MNT"
mkdir -p "$MNT/boot"
mount -t vfat -o fmask=0077,dmask=0077,iocharset=iso8859-1 "${SSD}-part1" "$MNT/boot"
echo "Enabling swap..."
swapon "${SSD}-part2"
echo "Partitioning and setup complete:"
lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,LABEL

50
hosts/pc/hardware.nix Normal file
View file

@ -0,0 +1,50 @@
{
inputs,
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"nvme"
"xhci_pci"
"ahci"
"usbhid"
"usb_storage"
"sd_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-label/ROOT";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-label/BOOT";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
swapDevices = [ { device = "/dev/disk/by-label/SWAP"; } ];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
hardware.graphics.enable = true;
hardware.nvidia.open = false;
services.xserver.videoDrivers = lib.mkDefault [ "nvidia" ];
}

7
hosts/pc/networking.nix Normal file
View file

@ -0,0 +1,7 @@
{
networking.hostName = "pc";
networking.interfaces.enp6s0.wakeOnLan = {
enable = true;
policy = [ "magic" ];
};
}

10
hosts/pc/packages.nix Normal file
View file

@ -0,0 +1,10 @@
{ pkgs, ... }:
{
environment = {
systemPackages = with pkgs; [
evtest
linuxConsoleTools
];
};
}

View file

@ -0,0 +1,5 @@
{ inputs, ... }:
{
imports = [ inputs.synix.nixosModules.sops ];
}

View file

@ -0,0 +1,28 @@
tailscale:
auth-key: ENC[AES256_GCM,data:ieDjXpk1YJ2+rb5X5dV3NPtr8+FGwcQtdinSbB+SIuyNbLoSogKrutsBqa+v0I5g,iv:0bV4VwRGCf0yIKpR850/CuTvGFUPXOnFaHpWkdyokjk=,tag:vlRo7cZqgYnvSJiCPSutmw==,type:str]
forgejo-runner:
token: ENC[AES256_GCM,data:rDwc/w9RpL/++VXg+YEYTP0CPz+trQp2OP5rHgWrPU0qODh1VjHjJA==,iv:SEFGOTB4YVnZqaJ2Lg87MSPV++8kAgtYMabvqouLuaw=,tag:NvRQHU8yvc6BdyTsnmIqyg==,type:str]
sops:
age:
- recipient: age19yeqvv28fgrtk6jsh3xyaf0lch86kna6rcz4dwe962yyyyevu30sx474xy
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBucExCZjNtNGFnUTlnMjl0
RVpCU1NxazNXSjBma2tTTlIvWDlPcy9EcGxZCmp2WC9xa2ptVkQvaWFYcnRqcHgz
Mk1scjBWY3g1TzNWalNVYVVqN3JLS0UKLS0tIGJQTG42aXFENFdVd0hkWGxLWVVu
STI4aWJxR3A4VUNyek5JMEtHeG1RZUUKKRDWdOXfarN7UZZzIBoSpmGlcWFsyJtX
bZgccbigI6TJpnssTkFT89FysD6i++mmC0mmTeZ/oNOXUk5OuwrCgA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1zdd344x69n8umt2qjjvz8pjnt43lacvvqfdquc5jqz4x9x7pnu3sg0as0k
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNeThiZGhmNTB6Uk1YdGg3
WFlvNGtENnNlOU1wUXJyOWFPb3M2bm5UQVd3CkE0ck81ZjRwa2hIY1hQLzF2VmY3
NWN4Z0x5MVlJY2Z5OGszbnBxd3ZIM1EKLS0tIGlMUUlXN1ZLRUlwRmhCek5ZR29l
OHNTYTFFYTJQeXkzWDN3bE91RFgyMzAKV49+02ik78/chrQ1arlkQZH4G6oeRHCa
Gp/WhuuOUJ7gwERNxhduhl4+IOSGcepgN5EJeTDXppUtiKXvNzmxpA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-02-18T17:43:14Z"
mac: ENC[AES256_GCM,data:1QcpQcLQ/TQwfzzHSGsoveB4HoN5ByCURoJn+TZjXd/szx0dBtUIxzc4ktkQZ388HFgYJ4rqpNudlc4AvYvDJULSpfP7KRADKG1reSuqpInGjU79t5U4Wwp+KJ+o29lulTV4fIqfCuqB9QhD4lqLjMSjnKUx5wkmtPuvIEjvWDw=,iv:T3ygIFwbXA/GLAbRAbQn9AP+V6evdmUCOlUfVbZc4fs=,tag:V7tLIukIAo5jyN/HkrciAw==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0

32
hosts/pc/services.nix Normal file
View file

@ -0,0 +1,32 @@
{
inputs,
outputs,
config,
...
}:
{
imports = [
inputs.synix.nixosModules.openssh
outputs.nixosModules.forgejo-runner
];
services = {
openssh.enable = true;
};
services.forgejo-runner = {
enable = true;
url = "https://git.sid.ovh";
tokenFile = config.sops.templates."forgejo-runner/token".path;
label = "runner";
};
sops = {
secrets."forgejo-runner/token" = { };
templates."forgejo-runner/token".content = ''
TOKEN=${config.sops.placeholder."forgejo-runner/token"}
'';
};
}

7
hosts/rv2/boot.nix Normal file
View file

@ -0,0 +1,7 @@
{
boot.loader.systemd-boot = {
enable = true;
configurationLimit = 10;
};
boot.loader.efi.canTouchEfiVariables = true;
}

58
hosts/rv2/default.nix Normal file
View file

@ -0,0 +1,58 @@
{ inputs, outputs, ... }:
{
imports = [
./boot.nix
./hardware.nix
./packages.nix
./secrets
./services.nix
../../users/sid
inputs.synix.nixosModules.bluetooth
inputs.synix.nixosModules.common
inputs.synix.nixosModules.device.desktop
inputs.synix.nixosModules.hyprland
inputs.synix.nixosModules.virtualisation
outputs.nixosModules.appimage
outputs.nixosModules.common
# outputs.nixosModules.docker # conflicts with `virtualisation.podman.dockerCompat`
outputs.nixosModules.docs
outputs.nixosModules.syncthing
outputs.nixosModules.tailscale
outputs.nixosModules.wine
];
networking.hostName = "rv2";
programs.steam.enable = true;
programs.adb.enable = true;
users.users.sid.extraGroups = [
"adbusers"
"kvm"
];
boot.binfmt.emulatedSystems = [
"aarch64-linux"
];
normalUsers = {
sid = {
extraGroups = [
"audio"
"dialout"
"floppy"
"input"
"libvirtd"
"lp"
"networkmanager"
"video"
];
};
};
system.stateVersion = "25.05";
}

90
hosts/rv2/disks.nix Normal file
View file

@ -0,0 +1,90 @@
{
disko.devices = {
disk = {
root = {
type = "disk";
device = "/dev/nvme0n1";
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
};
zpool = {
zroot = {
type = "zpool";
rootFsOptions = {
mountpoint = "none";
compression = "zstd";
acltype = "posixacl";
xattr = "sa";
atime = "off";
"com.sun:auto-snapshot" = "true";
};
options.ashift = "12";
datasets = {
"root" = {
type = "zfs_fs";
options = {
encryption = "aes-256-gcm";
keyformat = "passphrase";
keylocation = "prompt";
};
mountpoint = "/";
};
"root/nix" = {
type = "zfs_fs";
mountpoint = "/nix";
options.atime = "off";
};
"root/home" = {
type = "zfs_fs";
mountpoint = "/home";
};
"root/swap" = {
type = "zfs_volume";
size = "8G";
content = {
type = "swap";
randomEncryption = true;
};
options = {
volblocksize = "4k";
compression = "off";
logbias = "throughput";
sync = "always";
primarycache = "metadata";
secondarycache = "none";
"com.sun:auto-snapshot" = "false";
};
};
"root/reserved" = {
type = "zfs_fs";
options = {
mountpoint = "none";
reservation = "5G";
};
};
};
};
};
};
}

49
hosts/rv2/disks.sh Normal file
View file

@ -0,0 +1,49 @@
#!/usr/bin/env bash
SSD='/dev/disk/by-id/nvme-TEAM_TM8FPD001T_TPBF2503240010201457'
MNT='/mnt'
SWAP_GB=16
# Helper function to wait for devices
wait_for_device() {
local device=$1
echo "Waiting for device: $device ..."
while [[ ! -e $device ]]; do
sleep 1
done
echo "Device $device is ready."
}
if ! command -v sgdisk &> /dev/null; then
nix-env -iA nixos.gptfdisk
fi
swapoff --all
udevadm settle
wait_for_device $SSD
echo "Partitioning $SSD..."
sgdisk -n5:0:+"$SWAP_GB"G -t5:8200 -c5:SWAP $SSD
sgdisk -n6:0:0 -t6:8304 -c6:ROOT $SSD
partprobe -s $SSD
udevadm settle
wait_for_device ${SSD}-part1 # Windows ESP
wait_for_device ${SSD}-part5
wait_for_device ${SSD}-part6
echo "Formatting partitions..."
mkswap -L SWAP "${SSD}-part5"
mkfs.ext4 -L ROOT "${SSD}-part6"
echo "Mounting partitions..."
mount -o X-mount.mkdir "${SSD}-part6" "$MNT"
mkdir -p "$MNT/boot"
mount "${SSD}-part1" "$MNT/boot"
echo "Enabling swap..."
swapon "${SSD}-part5"
echo "Partitioning and setup complete:"
lsblk -o NAME,FSTYPE,SIZE,MOUNTPOINT,LABEL

50
hosts/rv2/hardware.nix Normal file
View file

@ -0,0 +1,50 @@
{
inputs,
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
inputs.nixos-hardware.nixosModules.common-gpu-amd-southern-islands
];
boot.initrd.availableKernelModules = [
"nvme"
"xhci_pci"
"ahci"
"usb_storage"
"usbhid"
"sd_mod"
];
boot.initrd.kernelModules = [ "amdgpu" ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-label/ROOT";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-label/SYSTEM";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
swapDevices = [
{ device = "/dev/disk/by-label/SWAP"; }
];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

8
hosts/rv2/packages.nix Normal file
View file

@ -0,0 +1,8 @@
{ pkgs, ... }:
{
environment = {
systemPackages = with pkgs; [
];
};
}

View file

@ -0,0 +1,5 @@
{ inputs, ... }:
{
imports = [ inputs.synix.nixosModules.sops ];
}

View file

@ -0,0 +1,30 @@
wireguard:
private-key: ENC[AES256_GCM,data:xUOZdGM2Wbi3ih6yankUMPqot4gDyj6AA4nMQKkHhM0dlsswyxnDQlEsNrQ=,iv:EtScTgdBYAuQUfa2TOMqCcCyVR5D60B8aA67W7uxnK4=,tag:RMd+ZplQDKaEl7qIIGIkoA==,type:str]
tailscale:
auth-key: ENC[AES256_GCM,data:oR4rdZlsq+gA5SMWXZW/2aOLU589EQGyfXl+u/CnXWPNbYRMDdmiHtZO/13PVOjJ,iv:B9RgTEom8naZxDZR9RPoQo3DNQeY4meyFcqqBqSBblA=,tag:BkCxbt67ErdidrLzjkEYnw==,type:str]
syncthing:
gui-pw: ENC[AES256_GCM,data:yu8e1JCzZxu/VIQ4mmyqPNBkxd0=,iv:X8U91uI5VlOluQmpkcdP2b3uf1rTI3j+RcBmK1gBqKI=,tag:SmMqsW+gfSZS/dA8GObnig==,type:str]
sops:
age:
- recipient: age19yeqvv28fgrtk6jsh3xyaf0lch86kna6rcz4dwe962yyyyevu30sx474xy
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3U2Z0UkxBL0xDOEgvNGlJ
SDQxNk9ndFRIZmdvdUZzUUpvZkR0dzZ1Um1FCm1sdFd2VU5CWmdsZk9lTzVqdXpP
ZXYvU3lkVXdxZlZaaGs0K1BBT0t3Z28KLS0tIHUvZ0R1ZTh1a25xQVRLTEFqVGVG
bU5CRm1iZGpZeTRvSjArQlBmQlhQelEKIhbrAQycS6WaCahA0PDPINEq12CKi0Ac
Z3o6puDD1v1QIqAHvZBvn1o2V/xN4gj/jHo73El1BJavgXvMBEneyg==
-----END AGE ENCRYPTED FILE-----
- recipient: age1j6s2ec3ltm9004fhvmd7xqq0zna2fr4m8kw4f235r9k0hfryjctq050vs2
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrd2xwbmYwQytkUi9aY2JH
SUNiZXAwb0lYbFluYWw2eDlJV2RyNGg1bWpjCkpOUlUxSGpXbXl0NjBLZDAwaFF2
UFBuaXhlZzloa0VCZFg1eTFldVQxV1UKLS0tIHVtKyt6czg2NGJNbldsZ1JiVzZa
MUVCWWVHbmVCRnlnRjI0TUt6cFVnazQKZeDi8y5khMHG2uEIXdxSDAU+Eew0AMv3
jiEUyyClSas7BVaJvAGl56cIg1jfjrNEBb5rQD2mISsuM2rIuRNc/Q==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-02-02T12:15:13Z"
mac: ENC[AES256_GCM,data:HpbL6uC0wZTSsjGU4DrQE8NTd+DaImXqvRObReF4uDtBgUlKYmn0/UZIThL1QCMiwUYN/SeOwNtGiT5lH/xZeoBdS683AIGfULqXxPx1EZ3NRBkSmQfayt8ltGJwozitJ59Tipv2buDEEcefCw1aG8l3qrQRc0eM09iOIeoZv5o=,iv:wdn0I7YQ4f3IgdjEZP5MdpOO2WL3dKKVF3RryJZ2ODQ=,tag:0Ri3AoYwN9SuzXo92zf6FA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0

52
hosts/rv2/services.nix Normal file
View file

@ -0,0 +1,52 @@
{
inputs,
outputs,
config,
...
}:
{
imports = [
inputs.synix.nixosModules.openssh
inputs.synix.nixosModules.windows-oci
outputs.nixosModules.forgejo-runner
];
services.openssh.enable = true;
# FIXME:
# connect in weechat:
# /server add local localhost/6667
# /set irc.server.local.password "abc"
# /set irc.server.local.tls off
# Access denied: Bad password?
services.ngircd = {
enable = true;
config = ''
[Global]
Name = irc.local
Info = Minimal ngIRCd Server
Password = yourmom69
'';
};
services.windows-oci = {
# enable = true;
sharedVolume = "/home/sid/pub";
};
time.hardwareClockInLocalTime = true; # Windows compatibility
services.forgejo-runner = {
# enable = true;
url = "https://git.sid.ovh";
# tokenFile = config.sops.templates."forgejo-runner/token".path;
label = "runner";
};
# sops = {
# secrets."forgejo-runner/token" = { };
# templates."forgejo-runner/token".content = ''
# TOKEN=${config.sops.placeholder."forgejo-runner/token"}
# '';
# };
}

View file

@ -0,0 +1,13 @@
{
imports = [
./overlays.nix
];
nixpkgs.config.allowUnfree = true;
# trace: warning: `programs.ssh` default values will be removed in the future.
# Consider setting `programs.ssh.enableDefaultConfig` to false,
# and manually set the default values you want to keep at
# `programs.ssh.matchBlocks."*"`.
programs.ssh.enableDefaultConfig = false;
}

View file

@ -0,0 +1,12 @@
{ outputs, ... }:
{
nixpkgs.overlays = [
outputs.overlays.synix-packages
outputs.overlays.local-packages
outputs.overlays.modifications
outputs.overlays.old-old-stable-packages
outputs.overlays.old-stable-packages
outputs.overlays.unstable-packages
];
}

3
modules/home/default.nix Normal file
View file

@ -0,0 +1,3 @@
{
common = import ./common;
}

View file

@ -0,0 +1,30 @@
{ config, inputs, ... }:
{
imports = [ inputs.synix.nixosModules.anything-llm-oci ];
services.anything-llm-oci = {
enable = true;
environment = {
LLM_PROVIDER = "openrouter";
OPENROUTER_MODEL_PREF = "google/gemini-3-pro-preview";
};
environmentFile = config.sops.templates."anything-llm-oci/environment".path;
};
sops = {
secrets."anything-llm-oci/openrouter-api-key" = { };
# Generate with: nix-shell -p openssl --run "openssl rand -hex 32"
secrets."anything-llm-oci/jwt-secret" = { };
secrets."anything-llm-oci/sig-key" = { };
secrets."anything-llm-oci/sig-salt" = { };
templates."anything-llm-oci/environment".content = ''
OPENROUTER_API_KEY=${config.sops.placeholder."anything-llm-oci/openrouter-api-key"}
JWT_SECRET=${config.sops.placeholder."anything-llm-oci/jwt-secret"}
SIG_KEY=${config.sops.placeholder."anything-llm-oci/sig-key"}
SIG_SALT=${config.sops.placeholder."anything-llm-oci/sig-salt"}
'';
};
}

View file

@ -0,0 +1,6 @@
{
programs.appimage = {
enable = true;
binfmt = true;
};
}

View file

@ -0,0 +1,8 @@
{
imports = [
./nix.nix
./overlays.nix
];
nixpkgs.config.allowUnfree = true;
}

View file

@ -0,0 +1,29 @@
{
nix = {
# TODO: add distributed build support for portuus.de
# distributedBuilds = true;
# buildMachines = [
# {
# hostName = "portuus.de";
# supportedFeatures = [
# "benchmark"
# "big-parallel"
# "kvm"
# "nixos-test"
# ];
# maxJobs = 8;
# system = "x86_64-linux";
# }
# ];
settings = {
# binary caches
# substituters = [
# "https://cache.portuus.de"
# ];
# trusted-public-keys = [
# "cache.portuus.de:INZRjwImLIbPbIx8Qp38gTVmSNL0PYE4qlkRzQY2IAU="
# ];
};
};
}

View file

@ -0,0 +1,12 @@
{ outputs, ... }:
{
nixpkgs.overlays = [
outputs.overlays.synix-packages
outputs.overlays.local-packages
outputs.overlays.modifications
outputs.overlays.old-old-stable-packages
outputs.overlays.old-stable-packages
outputs.overlays.unstable-packages
];
}

13
modules/nixos/default.nix Normal file
View file

@ -0,0 +1,13 @@
{
anything-llm-oci = import ./anything-llm-oci;
appimage = import ./appimage;
common = import ./common;
docker = import ./docker;
docs = import ./docs;
forgejo-runner = import ./forgejo-runner;
monero = import ./monero;
nh = import ./nh;
syncthing = import ./syncthing;
tailscale = import ./tailscale;
wine = import ./wine;
}

View file

@ -0,0 +1,9 @@
{
virtualisation.docker = {
enable = true;
rootless = {
enable = true;
setSocketVariable = true;
};
};
}

View file

@ -0,0 +1,19 @@
{ pkgs, ... }:
{
documentation = {
dev.enable = true;
man = {
man-db.enable = true;
mandoc.enable = false;
generateCaches = true;
};
};
environment.systemPackages = with pkgs; [
synix.cppman
synix.pyman
man-pages
man-pages-posix
];
}

View file

@ -0,0 +1,79 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.forgejo-runner;
inherit (lib)
mkEnableOption
mkIf
mkOption
types
;
in
{
options.services.forgejo-runner = {
enable = mkEnableOption "Nix-based Forgejo Runner service";
url = mkOption {
type = types.str;
description = "Forgejo instance URL.";
};
tokenFile = mkOption {
type = types.path;
description = "Path to EnvironmentFile containing TOKEN=...";
};
instance = mkOption {
type = types.str;
default = "default";
description = "Name of the runner instance.";
};
label = mkOption {
type = types.str;
default = "host";
description = "Runner label.";
};
};
config = mkIf cfg.enable {
nix.settings.trusted-users = [ "gitea-runner" ];
services.gitea-actions-runner = {
package = pkgs.forgejo-runner;
instances."${cfg.instance}" = {
enable = true;
name = "${config.networking.hostName}-nix";
inherit (cfg) url tokenFile;
labels = [ "${cfg.label}:host" ];
hostPackages = with pkgs; [
bash
coreutils
curl
deploy-rs
gitMinimal
gnused
nix
nodejs
openssh
];
settings = {
log.level = "info";
runner = {
capacity = 4;
envs = {
NIX_CONFIG = "extra-experimental-features = nix-command flakes";
NIX_REMOTE = "daemon";
# inherit (config.systemd.services."gitea-runner-${cfg.instance}".environment) HOME;
};
};
};
};
};
};
}

View file

@ -0,0 +1,111 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.monero;
sops = config.sops;
inherit (lib) mkDefault mkIf getExe;
in
{
config = mkIf cfg.enable {
services.monero = {
environmentFile = sops.templates."monero/environment-file".path;
mining.enable = false; # use XMRig + P2Pool
rpc = {
address = mkDefault "127.0.0.1";
port = mkDefault 18081;
user = mkDefault "monero";
password = mkDefault "$MONERO_RPC_PASSWORD";
};
extraConfig = ''
zmq-pub=tcp://127.0.0.1:18083
out-peers=32
in-peers=64
prune-blockchain=1
sync-pruned-blocks=1
add-priority-node=p2pmd.xmrvsbeast.com:18080
add-priority-node=nodes.hashvault.pro:18080
enforce-dns-checkpointing=1
enable-dns-blocklist=1
'';
};
systemd.services.p2pool = {
description = "P2Pool Monero Sidechain Node";
after = [
"monero.service"
"network.target"
];
wantedBy = [ "multi-user.target" ];
path = [ pkgs.p2pool ];
serviceConfig = {
User = "p2pool";
Group = "p2pool";
WorkingDirectory = "/var/lib/p2pool";
ExecStart = "${getExe pkgs.p2pool} --host 127.0.0.1 --wallet ${cfg.mining.address}";
Restart = "always";
RestartSec = 10;
NoNewPrivileges = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
};
};
users.users.p2pool = {
isSystemUser = true;
group = "p2pool";
home = "/var/lib/p2pool";
createHome = true;
};
users.groups.p2pool = { };
services.xmrig = {
enable = true;
settings = {
autosave = true;
cpu = {
enabled = true;
huge-pages = true;
hw-aes = null;
asm = true;
yield = true;
};
opencl.enabled = false;
cuda.enabled = false;
pools = [
{
url = "127.0.0.1:3333";
user = "";
pass = "";
}
];
api.enable = true;
};
};
sops =
let
owner = "monero";
group = "monero";
mode = "0440";
in
{
secrets."monero/rpc-password" = {
inherit owner group mode;
};
templates."monero/environment-file" = {
inherit owner group mode;
content = ''
MONERO_RPC_PASSWORD=${sops.placeholder."monero/rpc-password"}
'';
};
};
};
}

View file

@ -0,0 +1,18 @@
{ config, lib, ... }:
let
# NOTE: Add a "main user" option to normalUsers? This would also set a sane default for the Syncthing module.
user = "sid";
inherit (lib) mkDefault mkForce;
in
{
programs.nh = {
enable = mkDefault true;
clean.enable = mkDefault true;
clean.extraArgs = mkDefault "--keep-since 4d --keep 3";
flake = config.users.users."${user}".home + "/.config/nixos";
};
nix.gc.automatic = mkForce false; # collides with `programs.nh.clean`
}

View file

@ -0,0 +1,86 @@
{ config, lib, ... }:
let
cfg = config.services.syncthing;
guiPort = 8384;
transferPort = 22000;
fqdn = "sync.local";
user = "sid";
dirs = [
"aud"
"doc"
"img"
"vid"
];
allDevices = {
"16ach6" = {
id = "5IPAQ5C-V3KFUMD-NJM74SH-6MD246O-JGYCBN4-F77QG6W-W3WNSCA-NQY37AY";
addresses = [ "tcp://100.64.0.2:${toString transferPort}" ];
};
rv2 = {
id = "JG6BYOJ-AW67R72-VA25U6I-VIZ57HU-3KXMPGY-HTYT2FQ-ZZL6U7B-Z2RWDQ4";
addresses = [ "tcp://100.64.0.11:${toString transferPort}" ];
};
rx4 = {
id = "GBTCUX6-MAXC7NL-IGCJWWE-OEMANRO-BWZGWFU-HHO3NGN-GIUCXJJ-MTWM6QP";
addresses = [ "tcp://100.64.0.10:${toString transferPort}" ];
};
};
inherit (lib) filterAttrs genAttrs mkIf;
inherit (builtins) attrNames toString;
in
{
services.syncthing = {
enable = true;
inherit user;
group = config.users.users.${user}.group;
dataDir = config.users.users.${user}.home;
guiAddress = "0.0.0.0:${toString guiPort}";
guiPasswordFile = config.sops.secrets."syncthing/gui-pw".path;
openDefaultPorts = true;
overrideDevices = true;
overrideFolders = true;
settings = {
devices = filterAttrs (n: v: n != config.networking.hostName) allDevices;
folders = genAttrs dirs (dir: {
path = "${config.users.users.${user}.home}/${dir}";
devices = attrNames cfg.settings.devices;
});
gui = {
inherit user;
};
options = {
urAccepted = -1; # disable usage reports
};
};
};
networking.firewall.interfaces = mkIf config.services.tailscale.enable {
${config.services.tailscale.interfaceName}.allowedTCPPorts = [ guiPort ];
};
networking.hosts."127.0.0.1" = [ fqdn ];
services.nginx = {
enable = true;
virtualHosts."${fqdn}" = {
locations."/" = {
proxyPass = "http://127.0.0.1:${toString guiPort}";
proxyWebsockets = true;
};
};
};
sops.secrets."syncthing/gui-pw" = {
owner = cfg.user;
group = cfg.group;
mode = "0400";
restartUnits = [ "syncthing.service" ];
};
}

View file

@ -0,0 +1,11 @@
{ inputs, ... }:
{
imports = [ inputs.synix.nixosModules.tailscale ];
services.tailscale = {
enable = true;
enableSSH = true;
loginServer = "https://hs.sid.ovh";
};
}

View file

@ -0,0 +1,10 @@
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [
wineWowPackages.waylandFull
winetricks
];
hardware.graphics.enable32Bit = true;
}

45
overlays/default.nix Normal file
View file

@ -0,0 +1,45 @@
{ inputs, ... }:
{
# synix packages accessible through 'pkgs.synix'
synix-packages = final: prev: { synix = inputs.synix.overlays.additions final prev; };
# packages in `pkgs/` accessible through 'pkgs.local'
local-packages = final: prev: { local = import ../pkgs { pkgs = final; }; };
# https://nixos.wiki/wiki/Overlays
modifications =
final: prev:
let
files = [
# ./instaloader.nix
# ./zathura.nix # FIXME: How to use overrideScope?
];
imports = builtins.map (f: import f final prev) files;
in
builtins.foldl' (a: b: a // b) { } imports // inputs.synix.overlays.modifications final prev;
# unstable nixpkgs accessible through 'pkgs.unstable'
unstable-packages = final: prev: {
unstable = import inputs.nixpkgs-unstable {
inherit (final) system;
inherit (prev) config;
};
};
# old-stable nixpkgs accessible through 'pkgs.old-stable'
old-stable-packages = final: prev: {
old-stable = import inputs.nixpkgs-old-stable {
inherit (final) system;
inherit (prev) config;
};
};
# old-old-stable nixpkgs accessible through 'pkgs.old-old-stable'
old-old-stable-packages = final: prev: {
old-old-stable = import inputs.nixpkgs-old-old-stable {
inherit (final) system;
inherit (prev) config;
};
};
}

12
overlays/instaloader.nix Normal file
View file

@ -0,0 +1,12 @@
final: prev:
{
instaloader = prev.instaloader.overrideAttrs (oldAttrs: {
src = prev.pkgs.fetchFromGitHub {
owner = "instaloader";
repo = "instaloader";
rev = "pull/2533/head";
sha256 = "sha256-LMRU49pyAWDdflPbA4cZ9pIdjGNThLWfZWZsQkcvTs4=";
};
});
}

19
overlays/zathura.nix Normal file
View file

@ -0,0 +1,19 @@
# `zathura_core` is not a toplevel package, threfore `prev.zathura_core` is not available
# Maybe `overrideScope` is needed: overrideScope = (scope -> scope -> AttrSet) -> scope
# But I don't know how to use it.
# zathura package definition: https://github.com/NixOS/nixpkgs/blob/nixos-24.11/pkgs/applications/misc/zathura/default.nix
final: prev: {
# error: attribute 'overrideScope' missing
zathura = prev.zathura.overrideScope {
zathura_core = prev.zathura_core.overrideAttrs (oldAttrs: rec {
version = "0.5.4"; # latest version before https://github.com/pwmt/zathura/issues/447
src = prev.fetchFromGitHub {
owner = "pwmt";
repo = "zathura";
rev = version;
hash = "";
};
});
};
}

16
pkgs/default.nix Normal file
View file

@ -0,0 +1,16 @@
{
pkgs ? import <nixpkgs>,
...
}:
{
gitingest = pkgs.python3Packages.callPackage ./gitingest { };
open-webui-desktop = pkgs.callPackage ./open-webui-desktop { };
otp = pkgs.callPackage ./otp { };
pdf2printable = pkgs.callPackage ./pdf2printable { };
transcribe = pkgs.callPackage ./transcribe { };
udiskie-dmenu = pkgs.callPackage ./udiskie-dmenu { };
yt2rss = pkgs.callPackage ./yt2rss { };
# spotify-to-tidal = pkgs.callPackage ./spotify-to-tidal { }; # FIXME
}

100
pkgs/gitingest/default.nix Normal file
View file

@ -0,0 +1,100 @@
{
lib,
buildPythonPackage,
fetchFromGitHub,
# Dependencies
setuptools,
click,
fastapi,
pathspec,
pydantic,
python-dotenv,
slowapi,
starlette,
tiktoken,
tomli,
uvicorn,
loguru,
# Tests
httpx,
jinja2,
gitMinimal,
pytest-asyncio,
pytest-mock,
pytestCheckHook,
python-multipart,
}:
buildPythonPackage rec {
pname = "gitingest";
version = "0.3.1";
pyproject = true;
src = fetchFromGitHub {
owner = "cyclotruc";
repo = "gitingest";
tag = "v${version}";
hash = "sha256-drsncGneZyOCC2GJbrDM+bf4QGI2luacxMhrmdk03l4=";
};
build-system = [
setuptools
];
dependencies = [
click
fastapi
pathspec
pydantic
python-dotenv
slowapi
starlette
tiktoken
tomli
uvicorn
httpx
loguru
];
pythonImportsCheck = [
"gitingest"
];
nativeCheckInputs = [
httpx
jinja2
gitMinimal
pytest-asyncio
pytest-mock
pytestCheckHook
python-multipart
];
doCheck = false;
disabledTests = [
# Tests require network
"test_cli_with_default_options"
"test_cli_with_options"
"test_cli_with_stdout_output"
"test_cli_writes_file"
"test_clone_specific_branch"
"test_include_ignore_patterns"
"test_ingest_with_gitignore"
"test_parse_query_with_branch"
"test_parse_query_without_host"
"test_run_ingest_query"
];
meta = {
changelog = "https://github.com/cyclotruc/gitingest/releases/tag/${src.tag}";
description = "Replace 'hub' with 'ingest' in any github url to get a prompt-friendly extract of a codebase";
homepage = "https://github.com/cyclotruc/gitingest";
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ ];
mainProgram = "gitingest";
};
}

View file

@ -0,0 +1,68 @@
{
lib,
python3,
fetchFromGitHub,
}:
python3.pkgs.buildPythonApplication rec {
pname = "gitingest";
version = "0.3.1";
pyproject = true;
src = fetchFromGitHub {
owner = "coderamp-labs";
repo = "gitingest";
rev = "v${version}";
hash = "sha256-drsncGneZyOCC2GJbrDM+bf4QGI2luacxMhrmdk03l4=";
};
build-system = [
python3.pkgs.setuptools
python3.pkgs.wheel
];
dependencies = with python3.pkgs; [
click
httpx
loguru
pathspec
pydantic
python-dotenv
starlette
strenum
tiktoken
typing-extensions
];
optional-dependencies = with python3.pkgs; {
dev = [
eval-type-backport
pre-commit
pytest
pytest-asyncio
pytest-cov
pytest-mock
];
server = [
boto3
fastapi
prometheus-client
sentry-sdk
slowapi
uvicorn
];
};
pythonImportsCheck = [
"gitingest"
];
meta = {
description = "Replace 'hub' with 'ingest' in any GitHub URL to get a prompt-friendly extract of a codebase";
homepage = "https://github.com/coderamp-labs/gitingest";
changelog = "https://github.com/coderamp-labs/gitingest/blob/${src.rev}/CHANGELOG.md";
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ ];
mainProgram = "gitingest";
};
}

View file

@ -0,0 +1,60 @@
{
lib,
buildNpmPackage,
fetchFromGitHub,
electron,
}:
buildNpmPackage rec {
pname = "open-webui-desktop";
version = "7e54042";
src = fetchFromGitHub {
owner = "open-webui";
repo = "desktop";
rev = "build-e${version}";
hash = "sha256-eW3B8CS2T46Z91JRAlZJ3rNxAru4p7eJwyxn6P20pnA=";
};
npmDepsHash = "sha256-HdkgbcLzY/9T26hpw6Jej8sUWlcIIn1FkJ7IVvG3P4o=";
makeCacheWritable = true;
# Create .npmrc to prevent scripts from running during install
postPatch = ''
cat >> .npmrc << 'EOF'
ignore-scripts=true
EOF
'';
preInstall = ''
sed -i '/postinstall/d' package.json
'';
npmRebuild = false;
buildPhase = ''
export ELECTRON_SKIP_BINARY_DOWNLOAD=1
npm run build
'';
installPhase = ''
mkdir -p $out/opt/${pname}
cp -r out $out/opt/${pname}/
cp -r resources $out/opt/${pname}/
cp -r node_modules $out/opt/${pname}/
mkdir -p $out/bin
makeWrapper ${electron}/bin/electron $out/bin/${pname} \
--add-flags "$out/opt/${pname}/out/main/index.js" \
--run "cd $out/opt/${pname}"
'';
meta = {
description = "Open WebUI Desktop 🌐 (Alpha)";
homepage = "https://github.com/open-webui/desktop";
# license = lib.licenses.TODO;
mainProgram = "open-webui-desktop";
platforms = lib.platforms.linux;
};
}

15
pkgs/otp/default.nix Normal file
View file

@ -0,0 +1,15 @@
{
writeShellScriptBin,
symlinkJoin,
...
}:
let
wrapped = writeShellScriptBin "otp" (builtins.readFile ./otp.sh);
in
symlinkJoin {
name = "otp";
paths = [
wrapped
];
}

25
pkgs/otp/otp.sh Normal file
View file

@ -0,0 +1,25 @@
#!/usr/bin/env bash
DMENU="bemenu"
OPTIONS="github\nth-koeln"
CHOICE=$(echo -e "$OPTIONS" | "$DMENU" -p "OTP:")
get_pass() {
pass otp -c "$1" 2>/dev/null
}
case "$CHOICE" in
"github")
get_pass "www/github.com"
;;
"th-koeln")
get_pass "www/login.th-koeln.de"
;;
*)
echo "Error: Unknown option '$CHOICE'"
exit 1
;;
esac
exit 0

View file

@ -0,0 +1,39 @@
{
writeShellScriptBin,
pdftk,
texlivePackages,
}:
let
_pdfjam = "${texlivePackages.pdfjam}/bin/pdfjam";
_pdftk = "${pdftk}/bin/pdftk";
in
writeShellScriptBin "pdf2printable" ''
if [ "$#" -ne 2 ]; then
echo "Usage: $0 input.pdf output.pdf"
exit 1
fi
input_pdf="$1"
output_pdf="$2"
even_pdf="even.pdf"
even_rotated_pdf="even_rotated.pdf"
landscape_pdf="landscape.pdf"
odd_pdf="odd.pdf"
# Convert the PDF to landscape
${_pdfjam} --landscape --nup 2x1 "$input_pdf" -o "$landscape_pdf"
# Split the PDF into odd and even pages
${_pdftk} "$landscape_pdf" cat odd output "$odd_pdf"
${_pdftk} "$landscape_pdf" cat even output "$even_pdf"
# Rotate the even pages by 180 degrees
${_pdfjam} --landscape "$even_pdf" --angle 180 --outfile "$even_rotated_pdf"
# Merge the odd and rotated even pages
${_pdftk} A="$odd_pdf" B="$even_rotated_pdf" shuffle A B output "$output_pdf"
rm "$odd_pdf" "$even_pdf" "$landscape_pdf" "$even_rotated_pdf"
''

View file

@ -0,0 +1,44 @@
{
lib,
python3,
fetchFromGitHub,
}:
python3.pkgs.buildPythonApplication rec {
pname = "spotify-to-tidal";
version = "1.0.4";
pyproject = true;
src = fetchFromGitHub {
owner = "spotify2tidal";
repo = "spotify_to_tidal";
rev = "v${version}";
hash = "sha256-NexwO4Qwv1P58QAgBHPfCf4Q/mhTicWHaRubh8En9AE=";
};
build-system = [
python3.pkgs.setuptools
python3.pkgs.wheel
];
dependencies = with python3.pkgs; [
pytest
pytest-mock
pyyaml
spotipy
sqlalchemy
tidalapi
tqdm
];
pythonImportsCheck = [
"spotify_to_tidal"
];
meta = {
description = "A command line tool for importing your Spotify playlists into Tidal";
homepage = "https://github.com/spotify2tidal/spotify_to_tidal";
license = lib.licenses.agpl3Only;
mainProgram = "spotify-to-tidal";
};
}

View file

@ -0,0 +1,22 @@
{
python3Packages,
...
}:
python3Packages.buildPythonApplication {
pname = "transcribe";
version = "1.0.0";
src = ./.;
pyproject = true;
build-system = [ python3Packages.setuptools ];
propagatedBuildInputs = [ python3Packages.openai ];
doCheck = false;
meta = {
description = "Transcribe a given audio file using the OpenAI API.";
};
}

7
pkgs/transcribe/setup.py Normal file
View file

@ -0,0 +1,7 @@
from setuptools import setup
setup(
name='transcribe',
version='1.0.0',
scripts=['transcribe'],
)

View file

@ -0,0 +1,47 @@
#!/usr/bin/env python3
import sys
import os
from openai import OpenAI
def transcribe_audio(file_path):
"""Transcribe the given audio file using OpenAI API."""
if not os.environ.get("OPENAI_API_KEY"):
print("Error: OPENAI_API_KEY environment variable is not set.")
return None
if not os.path.exists(file_path):
print(f"Error: File '{file_path}' not found.")
return None
try:
client = OpenAI()
with open(file_path, "rb") as audio_file:
transcription = client.audio.transcriptions.create(
# model="gpt-4o-transcribe",
model="whisper-1",
file=audio_file,
response_format="text"
)
return transcription
except Exception as e:
print(f"Error occurred during transcription: {e}")
return None
def main():
if len(sys.argv) != 2:
print("Usage: transcribe <audio_file_path>")
sys.exit(1)
audio_file_path = sys.argv[1]
transcription = transcribe_audio(audio_file_path)
if transcription:
print(transcription)
if __name__ == "__main__":
main()

1241
pkgs/udiskie-dmenu/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
[package]
name = "udiskie-dmenu"
version = "0.1.0"
edition = "2021"
[dependencies]
notify-rust = "4.5.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View file

@ -0,0 +1,13 @@
{
lib,
rustPlatform,
...
}:
rustPlatform.buildRustPackage {
pname = "udiskie-dmenu";
version = "0.1.0";
cargoLock.lockFile = ./Cargo.lock;
src = lib.cleanSource ./.;
}

View file

@ -0,0 +1,190 @@
use notify_rust::Notification;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::io::{self, Write};
use std::process::{Command, Stdio};
#[derive(Debug, Clone, Serialize, Deserialize)]
struct DeviceInfo {
label: Option<String>,
isLuks: Option<bool>,
mountPath: Option<String>,
devPath: String,
}
fn main() {
match run_udiskie_info() {
Ok(block_devices) => {
let options = parse_udiskie_info(&block_devices);
if options.is_empty() {
notify_if_err("Nothing to unmount / mount".to_string());
std::process::exit(0);
}
match get_selection(pretty_print(&options)) {
Ok(Some(selected)) => {
let parsed_selection = parse_selection(&selected, &options);
for device in parsed_selection {
process_device(&device);
}
}
Ok(None) => {} // User cancelled input, do nothing
Err(err) => notify_if_err(err),
}
}
Err(err) => notify_if_err(err),
}
}
fn run_udiskie_info() -> Result<Vec<DeviceInfo>, String> {
let output = Command::new("udiskie-info")
.args(&["-C", "-a", "-o", r#""label":"{ui_label}", "isLuks":"{is_luks}", "mountPath": "{mount_path}""#])
.output()
.map_err(|e| e.to_string())?;
if !output.status.success() {
return Err(format!(
"Error running `udiskie-info`: {}",
String::from_utf8_lossy(&output.stderr)
));
}
let stdout = String::from_utf8_lossy(&output.stdout);
parse_udiskie_json(&stdout)
}
fn parse_udiskie_json(json_lines: &str) -> Result<Vec<DeviceInfo>, String> {
json_lines
.lines()
.filter(|line| !line.is_empty())
.map(|line| {
let wrapped_line = format!("{{{}}}", line);
serde_json::from_str(&wrapped_line).map_err(|e| format!("JSON parsing error: {}", e))
})
.collect()
}
fn parse_udiskie_info(devices: &[DeviceInfo]) -> HashMap<String, DeviceInfo> {
let mut options: HashMap<String, DeviceInfo> = HashMap::new();
for device in devices {
if device.devPath.starts_with("/dev/loop") {
// Skip snap packages
continue;
}
let key = device.devPath.clone();
if let Some(existing) = options.get(&key) {
if existing.mountPath.is_some() && device.mountPath.is_none() {
continue;
}
if existing.isLuks.unwrap_or(false) && !device.isLuks.unwrap_or(false) {
continue;
}
}
options.insert(key, device.clone());
}
options
}
fn pretty_print(parsed_info: &HashMap<String, DeviceInfo>) -> String {
let mut output = String::new();
for (dev_path, device) in parsed_info {
let dev_path_padded = format!("{: <9}", dev_path);
let label_or_mount_path = device
.mountPath
.clone()
.or_else(|| device.label.clone())
.unwrap_or_else(|| "<unknown>".to_string());
output.push_str(&format!("{}: {}\n", dev_path_padded, label_or_mount_path));
}
output
}
fn get_selection(options: String) -> Result<Option<String>, String> {
let launcher = std::env::var("UDISKIE_DMENU_LAUNCHER").unwrap_or_else(|_| "dmenu".to_string());
let mut child = Command::new(launcher)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.map_err(|e| e.to_string())?;
if let Some(stdin) = child.stdin.as_mut() {
stdin
.write_all(options.as_bytes())
.map_err(|e| e.to_string())?;
}
let output = child
.wait_with_output()
.map_err(|e| e.to_string())?;
match output.status.code() {
Some(0) | Some(10) => {
let selection = String::from_utf8_lossy(&output.stdout).to_string();
Ok(Some(selection))
}
Some(_) | None => Ok(None),
}
}
fn parse_selection(selection: &str, options: &HashMap<String, DeviceInfo>) -> Vec<DeviceInfo> {
let mut devices = Vec::new();
for line in selection.lines() {
if let Some((dev_path, _label_or_mount)) = line.split_once(":") {
let trimmed_path = dev_path.trim();
if let Some(device) = options.get(trimmed_path) {
devices.push(device.clone());
}
}
}
devices
}
fn notify_if_err(err: String) {
Notification::new()
.summary("Error")
.body(&err)
.show()
.ok();
eprintln!("{}", err);
}
fn process_device(device: &DeviceInfo) {
let udiskie_opt = if device.isLuks.unwrap_or(false) {
"--force"
} else {
""
};
if let Some(mount_path) = &device.mountPath {
run_command(
&format!("udiskie-umount {} \"{}\"", udiskie_opt, mount_path),
Some("Failed to unmount device"),
);
} else if let Some(label) = &device.label {
run_command(
&format!("udiskie-mount {} \"{}\"", udiskie_opt, device.devPath),
Some(&format!("Failed to mount device: {}", label)),
);
} else {
notify_if_err("Unknown device - aborting".to_string());
}
}
fn run_command(command: &str, error_msg: Option<&str>) {
let status = Command::new("sh")
.arg("-c")
.arg(command)
.status()
.unwrap_or_else(|e| panic!("Failed to execute command: {}", e));
if !status.success() {
if let Some(msg) = error_msg {
notify_if_err(msg.to_string());
}
}
}

15
pkgs/yt2rss/default.nix Normal file
View file

@ -0,0 +1,15 @@
{
writeShellScriptBin,
symlinkJoin,
...
}:
let
wrapped = writeShellScriptBin "yt2rss" (builtins.readFile ./yt2rss.sh);
in
symlinkJoin {
name = "create";
paths = [
wrapped
];
}

21
pkgs/yt2rss/yt2rss.sh Normal file
View file

@ -0,0 +1,21 @@
if [ $# -eq 0 ]; then
echo "Usage: $0 <url>"
exit 1
fi
url=$1
# Check if input URL is a playlist URL
if [[ "$url" == *"playlist"* ]]; then
playlist_id=$(echo "$url" | grep -o 'list=[A-Za-z0-9_-]*' | cut -d'=' -f2)
if [ -n "$playlist_id" ]; then
echo "https://www.youtube.com/feeds/videos.xml?playlist_id=$playlist_id"
else
echo "Could not extract playlist ID from URL."
fi
else
curl -s "$url" | grep -o 'https://www.youtube.com/feeds/videos.xml?channel_id=[A-Za-z0-9_-]*' | sort | uniq
fi
exit 0

9
shell.nix Normal file
View file

@ -0,0 +1,9 @@
{
pkgs ? import <nixpkgs> { },
...
}:
pkgs.mkShell {
NIX_CONFIG = "extra-experimental-features = nix-command flakes";
nativeBuildInputs = with pkgs; [ home-manager ];
}

16
users/sid/default.nix Normal file
View file

@ -0,0 +1,16 @@
{ inputs, ... }:
{
imports = [
inputs.synix.nixosModules.normalUsers
];
normalUsers = {
sid = {
extraGroups = [ "wheel" ];
sshKeyFiles = [
./pubkeys/gpg.pub
];
};
};
}

View file

@ -0,0 +1,18 @@
{
inputs,
outputs,
...
}:
{
imports = [
./git.nix
./home.nix
./nixvim.nix
./secrets
inputs.synix.homeModules.common
outputs.homeModules.common
];
}

9
users/sid/home/git.nix Normal file
View file

@ -0,0 +1,9 @@
{
programs.git = {
enable = true;
settings.user = {
name = "sid";
email = "sid@sid.ovh";
};
};
}

12
users/sid/home/home.nix Normal file
View file

@ -0,0 +1,12 @@
{
home = {
username = "sid";
shellAliases = {
gpr = "git remote prune origin";
search-store = "find /nix/store -maxdepth 1 -type d | rg -i";
};
stateVersion = "24.11";
};
}

View file

@ -0,0 +1,48 @@
{ pkgs, ... }:
{
imports = [ ../../hyprland ];
wayland.windowManager.hyprland.settings.monitor = [
"eDP-1, 2560x1600@120, 0x0, 1.25"
"desc:LG Electronics 34GL750 0x000417A0, 2560x1080@60, auto, 1"
"desc:Eizo Nanao Corporation S2433W 24885089, 1920x1200, auto, 1" # , transform, 1"
];
programs.waybar.settings.mainBar.output = "eDP-1";
programs.ssh.matchBlocks = {
arch = {
host = "a arch";
hostname = "192.168.122.105";
port = 22;
user = "sid";
};
pc = {
host = "pc";
hostname = "192.168.178.140";
port = 2299;
user = "sid";
};
};
programs.sftpman.mounts = {
arch = {
host = "192.168.122.105";
user = "sid";
port = 22;
mountPoint = "/home/sid";
};
};
home.shellAliases = {
vbox-up = "VBoxManage startvm ubuntu-sopc --type headless";
vbox-down = "VBoxManage controlvm ubuntu-sopc poweroff";
vbox-list = "VBoxManage list vms --long | grep -e '^Name:' -e '^State:'";
dock-on = "hyprctl keyword monitor eDP-1, disable";
dock-off = "hyprctl keyword monitor eDP-1, enable";
};
services.spotifyd.settings.device_name = "16ach6";
}

View file

@ -0,0 +1,40 @@
{
imports = [ ../../hyprland ];
wayland.windowManager.hyprland.settings.monitor = [
"desc:Eizo Nanao Corporation S2433W 24885089, 1920x1200, 0x0, 1"
"desc:Eizo Nanao Corporation S1921 C9188037, 1280x1024, 1920x0, 1, transform, 1"
];
home.sessionVariables = {
AQ_NO_MODIFIERS = 1;
};
programs.waybar.settings.mainBar.output = "DP-2";
programs.ssh.matchBlocks = {
arch = {
host = "a arch";
hostname = "192.168.122.210";
port = 22;
user = "sid";
};
vde = {
host = "v vde";
hostname = "192.168.188.22";
port = 2299;
user = "sid";
};
};
programs.sftpman.mounts = {
vde = {
host = "192.168.188.22";
user = "sid";
port = 2299;
mountPoint = "/home/sid/.config/nixos";
};
};
services.spotifyd.settings.device_name = "nuc8";
}

View file

@ -0,0 +1,14 @@
{ lib, ... }:
{
imports = [ ../../hyprland ];
wayland.windowManager.hyprland.settings.monitor = [
"DP-3, 2560x1080@144, 1920x0, 1"
"HDMI-A-1, 1920x1080@144, 0x0, 1"
];
programs.waybar.settings.mainBar.output = "DP-3";
home.stateVersion = lib.mkForce "25.11";
}

View file

@ -0,0 +1,34 @@
{
imports = [ ../../hyprland ];
wayland.windowManager.hyprland.settings.monitor = [
"DVI-D-1, 1920x1200, 0x0, 1"
"DVI-I-1, 1280x1024, 1920x0, 1, transform, 1"
];
programs.waybar.settings.mainBar.output = "DVI-D-1";
programs.ssh.matchBlocks = {
arch = {
host = "a arch";
hostname = "192.168.122.12";
port = 22;
user = "sid";
};
vde = {
host = "v vde";
hostname = "192.168.1.224";
port = 2299;
user = "sid";
};
};
programs.sftpman.mounts = {
arch = {
host = "192.168.122.12";
user = "sid";
port = 22;
mountPoint = "/home/sid";
};
};
}

View file

@ -0,0 +1,128 @@
# Manual configuration
The following things are not (yet) automated by Nix. Follow these steps after finishing the installation (including Home Manager).
## Import secrets
It is assumed that your secrets are stored on a LUKS encrypted USB drive partition (sda3 in this case).
```bash
USB=/dev/sda
USER=$(whoami)
HOST=$(cat /etc/hostname)
# Open crypt
sudo mkdir -p /mnt/crypt
sudo cryptsetup open "$USB"3 crypt
sudo mount /dev/mapper/crypt /mnt/crypt
# Copy secrets
sudo rsync -vP /mnt/crypt/gpg-backup.sec.asc /tmp
sudo rsync -vP /mnt/crypt/$HOST/keys.txt /tmp
sudo chown $USER:$USER /tmp/gpg-backup.sec.asc
sudo chown $USER:$USER /tmp/keys.txt
# Import secrets
mkdir -p ~/.config/sops/age && mv /tmp/keys.txt ~/.config/sops/age && chmod 0400 ~/.config/sops/age/keys.txt
gpg --decrypt /tmp/gpg-backup.sec.asc | gpg --import
gpg --edit-key D371C8E7D58F9D1E # replace with your key ID
gpg> trust
Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y
gpg> q
# Close crypt
sudo umount -lf /mnt/crypt
sudo cryptsetup close crypt
```
## Clone password store repository
```bash
git clone ssh://git.portuus.de:2299/home/sid/git/password-store $PASSWORD_STORE_DIR
```
## Librewolf
Librewolf is handled through its Home Manager module. Extensions do not need to be installed, just activated.
- Extensions (allow every extension to run in private windows)
- PassFF
- Preferences
- Behavior of Enter key: Fill and submit
- Behavior of Shift-Enter key: Goto, fill and submit
- floccus
- Add profile
- Nextcloud Bookmarks
- Bookmarks folder: Bookmarks Toolbar
TODO
- all custom search bookmarks lost their keywords
- set Searx as the default search engine
## Element Desktop
- Authentication via username, password and security key
- Settings
- Appearance
- Theme: Dark
- Preferences
- Allow spell check
- Add: German (Germany)
- Keyboard shortcuts
- Use Ctrl + F to search timeline: true
## Thunderbird
The account setup must be done manually, as the `accounts.email` HM module requires setting personal information that would end up being public on the Git web frontend.
- Spelling
- Add Dictionaries...
- German: Download Dictionary
## Spotify
- Authentication via username and password
## Jellyfin Media Player
- Add server
- Authentication via username and password
## OBS-Studio
- Scenes
- Scene
- Sources
- Add: Screen Capture (PipeWire) > OK > Screen 0 (or a window or a region) > OK
- Audio Mixer
- Mic/Aux: Mute
- File
- Settings
- Output
- Recording
- Recording Path: /home/sid/vid/recordings
- Generate File Name without Space
- Video
- Base Resolution: 2560x1600
- Output Resolution: 2560x1600
- Common FPS Values: 60
> TODO: adjust video and audio codecs/quality
> TODO: keyboard shortcuts (currently using waybar tray icon)
## Eduroam
Download the eduroam configuration script [here](https://cat.eduroam.org/?idp=5134&profile=8268).
Execute it:
```bash
nix-shell -p python3 python3Packages.dbus-python --run 'python eduroam-linux-THK-members_of_TH_Koln.py'
```
## Zoom
- Authentication via username and password
- Check "Keep me signed in"

View file

@ -0,0 +1,103 @@
{
inputs,
config,
pkgs,
...
}:
let
mkDir = dir: {
path = config.home.homeDirectory + "/" + dir;
recurse = true;
};
mkDirs = dirs: map mkDir dirs;
dirs = [
"aud"
"dls"
"doc"
"img"
"src"
"vid"
];
inherit (pkgs.stdenv.hostPlatform) system;
in
{
imports = [
inputs.kidex.homeModules.kidex
];
programs.anyrun = {
enable = true;
package = inputs.anyrun.packages."${system}".anyrun-with-all-plugins;
config = {
x = {
fraction = 0.5;
};
y = {
fraction = 0.3;
};
width = {
fraction = 0.3;
};
hideIcons = true;
layer = "overlay";
hidePluginInfo = true;
showResultsImmediately = true;
plugins = with inputs.anyrun.packages."${system}"; [
applications
dictionary
kidex
randr
rink
translate
websearch
];
};
extraCss = ''
#window {
background-color: rgba(0, 0, 0, 0);
}
'';
extraConfigFiles = {
"dictionary.ron".text = ''
Config(
prefix: ":def",
max_entries: 5,
)
'';
"translate.ron".text = ''
Config(
prefix: ":t",
language_delimiter: ">",
max_entries: 3,
)
'';
"randr.ron".text = ''
Config(
prefix: ":dp",
max_entries: 5,
)
'';
# TODO: websearch.ron: set custom search engine
};
};
services.kidex = {
enable = true;
settings = {
ignored = [
"*/.git/*"
"*/.cache/*"
"*/.direnv/*"
];
directories = mkDirs dirs;
};
};
wayland.windowManager.hyprland.settings.bind = [
"$mod, space, exec, anyrun"
];
}

View file

@ -0,0 +1,34 @@
{
inputs,
lib,
pkgs,
...
}:
{
imports = [
# ./anyrun.nix
./flatpak.nix
./fzf-open.nix
./gpg.nix
./hyprland.nix
./librewolf.nix
./newsboat.nix
# ./nextcloud-sync.nix
./obs-studio.nix
./opencode.nix
./packages.nix
./rclone.nix
# ./recoll.nix
./shell-aliases.nix
./spotify-player.nix
./ssh-hosts.nix
./stylix.nix
./vscode.nix
./waybar.nix
./xdg.nix
./yazi.nix
inputs.synix.homeModules.virtualisation
];
}

View file

@ -0,0 +1,38 @@
{ inputs, pkgs, ... }:
{
imports = [
inputs.nix-flatpak.homeManagerModules.nix-flatpak
];
services.flatpak = {
enable = true;
update = {
onActivation = false;
auto = {
enable = true;
onCalendar = "weekly";
};
};
packages = [
{
appId = "org.mypaint.MyPaint";
origin = "flathub";
}
# FIXME: does not start. installed `chat.commet.commetapp.flatpak` manually
{
appId = "im.riot.Riot";
origin = "flathub";
}
{
appId = "us.zoom.Zoom";
origin = "flathub";
}
];
};
home.packages = with pkgs; [
flatpak
flatpak-builder
];
}

View file

@ -0,0 +1,35 @@
{ pkgs, ... }:
let
fzf-dirs = "~/doc ~/img ~/aud ~/dls ~/src ~/.config ~/.local";
fzf-open = pkgs.writeShellScriptBin "fzf-open" ''
fzf --preview="pistol {}" --bind "enter:execute(hyprctl dispatch togglespecialworkspace fzf-open && xdg-open {} > /dev/null 2>&1 &)"
'';
in
{
wayland.windowManager.hyprland = {
settings = {
bind = [
"$mod, Space, togglespecialworkspace, fzf-open"
];
windowrulev2 = [
"float, class:floating"
"size 50% 50%, title:fzf-open"
];
};
extraConfig = ''
workspace = special:fzf-open, on-created-empty:kitty --class=floating -e ${fzf-open}/bin/fzf-open
'';
};
home = {
sessionVariables = {
# FZF_DEFAULT_COMMAND = "rg --files --hidden --glob '!.git/**' ${fzf-dirs}";
FZF_DEFAULT_COMMAND = "rg --files ${fzf-dirs}";
};
packages = [
fzf-open
];
};
}

View file

@ -0,0 +1,12 @@
{ inputs, ... }:
let
key.a.grip = "F8BCC76BE2E55D52C3E92B963ADD3FDD8C153911";
key.e.id = "97BEF39E76001BC0";
in
{
imports = [ inputs.synix.homeModules.gpg ];
services.gpg-agent.sshKeys = [ key.a.grip ];
programs.passwordManager.key = key.e.id;
}

View file

@ -0,0 +1,49 @@
{
inputs,
config,
pkgs,
...
}:
{
imports = [ inputs.synix.homeModules.hyprland ];
wayland.windowManager.hyprland = {
enable = true;
autostart = true;
settings = {
bind =
let
flatpakRun = "${pkgs.flatpak}/bin/flatpak --user run";
wineRun = "wine ${config.home.homeDirectory}/.wine/drive_c";
in
[
"$mod, g, exec, gimp"
"$mod, s, exec, kitty -T spotify -e spotify_player"
"$mod, t, exec, teams-for-linux"
"$mod, v, exec, virt-manager"
"$mod, z, exec, ${flatpakRun} us.zoom.Zoom"
"$mod CTRL, i, exec, ${wineRun}/Program\\ Files/AccessData/FTK\\ Imager/FTK\\ Imager.exe"
"$mod CTRL, m, exec, ${flatpakRun} org.mypaint.MyPaint"
"$mod CTRL, o, exec, obs"
"$mod CTRL, p, exec, otp"
"$mod SHIFT, a, exec, chromium --app=https://ai.portuus.de"
];
windowrule = [
"workspace 4, title:^newsboat$"
"workspace 6, class:^thunderbird$, title:Thunderbird$"
"workspace 7, title:^Jellyfin Media Player$"
"workspace 7, title:^spotify$"
"workspace 8, class:^Element$, title:^Element"
"workspace 9, class:^chrome-ai.portuus.de"
"workspace 10, class:^zoom$, title:^Zoom"
"workspace 10, class:^org.qbittorrent.qBittorrent$"
"workspace 10, title:^Virtual Machine Manager$"
];
exec-once = [
"[workspace 5 silent] librewolf"
"[workspace 6 silent] thunderbird"
];
};
};
}

View file

@ -0,0 +1,13 @@
{ inputs, pkgs, ... }:
let
inherit (pkgs.stdenv.hostPlatform) system;
in
{
programs.librewolf = {
profiles.default.extensions.packages =
with inputs.nur.legacyPackages."${system}".repos.rycee.firefox-addons; [
zotero-connector
];
};
}

View file

@ -0,0 +1,12 @@
{ config, ... }:
{
programs.newsboat = {
extraConfig = ''
urls-source "miniflux"
miniflux-url "https://miniflux.portuus.de/"
miniflux-login "sid"
miniflux-passwordfile "${config.sops.secrets.miniflux.path}"
'';
};
}

View file

@ -0,0 +1,29 @@
{ inputs, config, ... }:
let
mkConnection = dir: {
local = config.home.homeDirectory + "/" + dir;
remote = "/" + dir;
};
mkConnections = dirs: map mkConnection dirs;
connections = [
"aud"
"doc"
"img"
"vid"
];
in
{
imports = [
inputs.synix.homeModules.nextcloud-sync
];
services.nextcloud-sync = {
enable = true;
remote = "cloud.portuus.de";
passwordFile = config.sops.secrets.nextcloud.path;
connections = mkConnections connections;
};
}

View file

@ -0,0 +1,9 @@
{ pkgs, ... }:
{
programs.obs-studio = {
enable = true;
package = pkgs.obs-studio;
plugins = [ pkgs.obs-studio-plugins.wlrobs ];
};
}

View file

@ -0,0 +1,10 @@
{
programs.opencode = {
enable = true;
settings = {
model = "openrouter/qwen/qwen3-coder";
autoshare = false;
autoupdate = false;
};
};
}

View file

@ -0,0 +1,84 @@
{ inputs, pkgs, ... }:
let
inherit (pkgs.stdenv.hostPlatform) system;
in
{
home.packages =
with pkgs;
[
prismlauncher
audacity
drawio
gimp
inkscape
kicad
mermaid-cli
octaveFull
pdfarranger
remmina
spotify
syncthingtray
teams-for-linux
texliveFull
xournalpp
zotero
# inputs.gen-dmc.packages."${system}".gen-dmc
# angryipscanner # FIXME
# autopsy # gradle-7.6.6 is marked as insecure
# jellyfin-media-player # qtwebengine-5.15.19 is marked as insecure
]
# tools
++ [
aichat
compose2nix
duden
ftx-prog
gf
glab
gtkterm
localsend
magic-wormhole
naabu
ocrmypdf
rpi-imager
rustfmt
showmethekey
songrec
speedtest-cli
subfinder
synadm
yt-dlp
inputs.multios-usb.packages."${system}".default
(instaloader.overridePythonAttrs (oldAttrs: {
propagatedBuildInputs = (oldAttrs.propagatedBuildInputs or [ ]) ++ [
python3Packages.browser-cookie3
];
}))
local.gitingest # TODO: PR Nixpkgs
local.otp
local.pdf2printable
local.transcribe
local.yt2rss
synix.bulk-rename
# synix.marker-pdf # FIXME
]
# reverse engineering
# ++ [
# checksec
# ghidra-bin
# ida-free
# ]
# android
++ [
adbfs-rootless
android-tools
scrcpy
];
}

View file

@ -0,0 +1,23 @@
{ config, ... }:
let
sops = config.sops;
in
{
sops.templates.rclone.path = config.xdg.configHome + "/rclone/rclone.conf";
sops.templates.rclone.content = ''
[portuus]
type = webdav
url = https://cloud.portuus.de/remote.php/dav/files/sid/
vendor = nextcloud
user = sid
pass = ${sops.placeholder."rclone/portuus/pass"}
[sciebo]
type = webdav
url = ${sops.placeholder."rclone/sciebo/url"}
vendor = owncloud
user = ${sops.placeholder."rclone/sciebo/user"}
pass = ${sops.placeholder."rclone/sciebo/pass"}
'';
}

Some files were not shown because too many files have changed in this diff Show more