Compare commits

..

2 commits

Author SHA1 Message Date
Jay Looney
630f9b0074 even more backups of things 2025-10-28 16:11:45 -05:00
Jay Looney
b8d125d448 backing up the working dir 2025-10-28 16:10:19 -05:00
78 changed files with 1863 additions and 1575 deletions

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
hosts/lithium/semi-secret-vars.nix filter=git-agecrypt diff=git-agecrypt

4
.gitignore vendored
View file

@ -1,4 +0,0 @@
result
result-*
localnotes.md
*.qcow2

View file

@ -1,3 +0,0 @@
{
"git.enabled": false
}

View file

@ -5,7 +5,7 @@
## Overview ## Overview
This repository manages **multiple NixOS systems** using a shared modular configuration. This repository maanges **multiple NixOS systems** using a shared modular configuration.
It's designed to be **secure, composable, and automated** using modern Nix tooling. It's designed to be **secure, composable, and automated** using modern Nix tooling.
- **Laptop ("neon")**: Portable KVM/Swiss-Army Knife - **Laptop ("neon")**: Portable KVM/Swiss-Army Knife
@ -79,6 +79,9 @@ nix build .#nixosConfigurations.installIso.config.system.build.images.iso
sudo mount -o loop result/iso/nixos-*.iso mnt sudo mount -o loop result/iso/nixos-*.iso mnt
ls mnt ls mnt
umount mnt umount mnt
# Manipulate sops-nix secrets
nix-shell -p sops --run "sops secrets/example.yaml"
``` ```
## Design Goals ## Design Goals
@ -110,6 +113,10 @@ umount mnt
``` ```
## Configuring and Accessing Secrets
I need to rip out agecrypt and go pure SOPS.
## References ## References
- [@shazow](https://github.com/shazow/) and https://github.com/shazow/nixfiles/ - [@shazow](https://github.com/shazow/) and https://github.com/shazow/nixfiles/

70
flake.bak Normal file
View file

@ -0,0 +1,70 @@
{
description = "Configuration for NixOS";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
nixos-hardware.url = "github:nixos/nixos-hardware";
home-manager.url = "github:nix-community/home-manager";
home-manager.inputs.nixpkgs.follows = "nixpkgs";
sops-nix.url = "github:Mic92/sops-nix";
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
# flake-parts.url = "github:hercules-ci/flake-parts";
};
# https://nix.dev/tutorials/nix-language.html#named-attribute-set-argument
outputs = inputs@{self, nixpkgs, nixos-hardware, home-manager, sops-nix, ...}:
let
lib = import ./lib { inherit (nixpkgs) lib; };
in
{
# NOTE: Run `nix flake show` to see what this flake has to offer.
# TODO: Enable automated formatting with numtide/treefmt-nix
# `nixos-rebuild switch --flake .#<hostname>`
nixosConfigurations = {
neon = nixpkgs.lib.nixosSystem {
specialArgs = { inherit inputs; };
system = "x86_64-linux";
modules = [
./hosts/neon
];
};
#lithium = nixpkgs.lib.nixosSystem {
#specialArgs = { inherit inputs; };
#system = "x86_64-linux";
#modules = [
#./hosts/lithium
#];
#};
lithium = lib.mkSystem {
users = [
{ name = "jml"; }
];
};
};
homeConfigurations = {
"jml" = home-manager.lib.homeManagerConfiguration {
modules = [
./users/jml/home.nix
];
};
};
# TODO: Implement a dev shell for working on this repository.
# Personally I'm using hx, nil, and git-agecrypt
# The only thing totally necessary which isn't captured by the flake inputs
# is git-agecrypt so that should be included here or somehow in the flake.
devShells.x86_64-linux.default = pkgs.mkShell {
buildInputs = with pkgs; [
nixpkgs-fmt
sops
age
git-agecrypt
];
shellHook = ''
echo "ready to rock"
'';
};
# `nix run .#name` and `nix build .#name`
# packages = {};
# `home-manager switch`
# homeConfigurations = {};
};
}

555
flake.lock generated
View file

@ -1,80 +1,12 @@
{ {
"nodes": { "nodes": {
"base16": {
"inputs": {
"fromYaml": "fromYaml"
},
"locked": {
"lastModified": 1755819240,
"narHash": "sha256-qcMhnL7aGAuFuutH4rq9fvAhCpJWVHLcHVZLtPctPlo=",
"owner": "SenchoPens",
"repo": "base16.nix",
"rev": "75ed5e5e3fce37df22e49125181fa37899c3ccd6",
"type": "github"
},
"original": {
"owner": "SenchoPens",
"repo": "base16.nix",
"type": "github"
}
},
"base16-fish": {
"flake": false,
"locked": {
"lastModified": 1754405784,
"narHash": "sha256-l9xHIy+85FN+bEo6yquq2IjD1rSg9fjfjpyGP1W8YXo=",
"owner": "tomyun",
"repo": "base16-fish",
"rev": "23ae20a0093dca0d7b39d76ba2401af0ccf9c561",
"type": "github"
},
"original": {
"owner": "tomyun",
"repo": "base16-fish",
"rev": "23ae20a0093dca0d7b39d76ba2401af0ccf9c561",
"type": "github"
}
},
"base16-helix": {
"flake": false,
"locked": {
"lastModified": 1752979451,
"narHash": "sha256-0CQM+FkYy0fOO/sMGhOoNL80ftsAzYCg9VhIrodqusM=",
"owner": "tinted-theming",
"repo": "base16-helix",
"rev": "27cf1e66e50abc622fb76a3019012dc07c678fac",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "base16-helix",
"type": "github"
}
},
"base16-vim": {
"flake": false,
"locked": {
"lastModified": 1732806396,
"narHash": "sha256-e0bpPySdJf0F68Ndanwm+KWHgQiZ0s7liLhvJSWDNsA=",
"owner": "tinted-theming",
"repo": "base16-vim",
"rev": "577fe8125d74ff456cf942c733a85d769afe58b7",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "base16-vim",
"rev": "577fe8125d74ff456cf942c733a85d769afe58b7",
"type": "github"
}
},
"crane": { "crane": {
"locked": { "locked": {
"lastModified": 1754269165, "lastModified": 1731098351,
"narHash": "sha256-0tcS8FHd4QjbCVoxN9jI+PjHgA4vc/IjkUSp+N3zy0U=", "narHash": "sha256-HQkYvKvaLQqNa10KEFGgWHfMAbWBfFp+4cAgkut+NNE=",
"owner": "ipetkov", "owner": "ipetkov",
"repo": "crane", "repo": "crane",
"rev": "444e81206df3f7d92780680e45858e31d2f07a08", "rev": "ef80ead953c1b28316cc3f8613904edc2eb90c28",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -90,11 +22,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1762276996, "lastModified": 1758287904,
"narHash": "sha256-TtcPgPmp2f0FAnc+DMEw4ardEgv1SGNR3/WFGH0N19M=", "narHash": "sha256-IGmaEf3Do8o5Cwp1kXBN1wQmZwQN3NLfq5t4nHtVtcU=",
"owner": "nix-community", "owner": "nix-community",
"repo": "disko", "repo": "disko",
"rev": "af087d076d3860760b3323f6b583f4d828c1ac17", "rev": "67ff9807dd148e704baadbd4fd783b54282ca627",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -103,30 +35,14 @@
"type": "github" "type": "github"
} }
}, },
"firefox-gnome-theme": {
"flake": false,
"locked": {
"lastModified": 1758112371,
"narHash": "sha256-lizRM2pj6PHrR25yimjyFn04OS4wcdbc38DCdBVa2rk=",
"owner": "rafaelmardojai",
"repo": "firefox-gnome-theme",
"rev": "0909cfe4a2af8d358ad13b20246a350e14c2473d",
"type": "github"
},
"original": {
"owner": "rafaelmardojai",
"repo": "firefox-gnome-theme",
"type": "github"
}
},
"flake-compat": { "flake-compat": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1747046372, "lastModified": 1696426674,
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra", "owner": "edolstra",
"repo": "flake-compat", "repo": "flake-compat",
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -135,22 +51,6 @@
"type": "github" "type": "github"
} }
}, },
"flake-compat_2": {
"flake": false,
"locked": {
"lastModified": 1751685974,
"narHash": "sha256-NKw96t+BgHIYzHUjkTK95FqYRVKB8DHpVhefWSz/kTw=",
"ref": "refs/heads/main",
"rev": "549f2762aebeff29a2e5ece7a7dc0f955281a1d1",
"revCount": 92,
"type": "git",
"url": "https://git.lix.systems/lix-project/flake-compat.git"
},
"original": {
"type": "git",
"url": "https://git.lix.systems/lix-project/flake-compat.git"
}
},
"flake-parts": { "flake-parts": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "nixpkgs-lib": [
@ -159,11 +59,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1754091436, "lastModified": 1730504689,
"narHash": "sha256-XKqDMN1/Qj1DKivQvscI4vmHfDfvYR2pfuFOJiCeewM=", "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "67df8c627c2c39c41dbec76a1f201929929ab0bd", "rev": "506278e768c2a08bec68eb62932193e341f55c90",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -172,61 +72,21 @@
"type": "github" "type": "github"
} }
}, },
"flake-parts_2": { "flake-utils": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "systems": "systems"
"nvf",
"nixpkgs"
]
}, },
"locked": { "locked": {
"lastModified": 1760948891, "lastModified": 1731533236,
"narHash": "sha256-TmWcdiUUaWk8J4lpjzu4gCGxWY6/Ok7mOK4fIFfBuU4=", "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "hercules-ci", "owner": "numtide",
"repo": "flake-parts", "repo": "flake-utils",
"rev": "864599284fc7c0ba6357ed89ed5e2cd5040f0c04", "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "hercules-ci", "owner": "numtide",
"repo": "flake-parts", "repo": "flake-utils",
"type": "github"
}
},
"flake-parts_3": {
"inputs": {
"nixpkgs-lib": [
"stylix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1756770412,
"narHash": "sha256-+uWLQZccFHwqpGqr2Yt5VsW/PbeJVTn9Dk6SHWhNRPw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "4524271976b625a4a605beefd893f270620fd751",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"fromYaml": {
"flake": false,
"locked": {
"lastModified": 1731966426,
"narHash": "sha256-lq95WydhbUTWig/JpqiB7oViTcHFP8Lv41IGtayokA8=",
"owner": "SenchoPens",
"repo": "fromYaml",
"rev": "106af9e2f715e2d828df706c386a685698f3223b",
"type": "github"
},
"original": {
"owner": "SenchoPens",
"repo": "fromYaml",
"type": "github" "type": "github"
} }
}, },
@ -252,23 +112,6 @@
"type": "github" "type": "github"
} }
}, },
"gnome-shell": {
"flake": false,
"locked": {
"lastModified": 1748186689,
"narHash": "sha256-UaD7Y9f8iuLBMGHXeJlRu6U1Ggw5B9JnkFs3enZlap0=",
"owner": "GNOME",
"repo": "gnome-shell",
"rev": "8c88f917db0f1f0d80fa55206c863d3746fa18d0",
"type": "github"
},
"original": {
"owner": "GNOME",
"ref": "48.2",
"repo": "gnome-shell",
"type": "github"
}
},
"home-manager": { "home-manager": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -276,11 +119,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1762964643, "lastModified": 1759337100,
"narHash": "sha256-RYHN8O/Aja59XDji6WSJZPkJpYVUfpSkyH+PEupBJqM=", "narHash": "sha256-CcT3QvZ74NGfM+lSOILcCEeU+SnqXRvl1XCRHenZ0Us=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "827f2a23373a774a8805f84ca5344654c31f354b", "rev": "004753ae6b04c4b18aa07192c1106800aaacf6c3",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -301,42 +144,49 @@
"rust-overlay": "rust-overlay" "rust-overlay": "rust-overlay"
}, },
"locked": { "locked": {
"lastModified": 1762205063, "lastModified": 1737639419,
"narHash": "sha256-If6vQ+KvtKs3ARBO9G3l+4wFSCYtRBrwX1z+I+B61wQ=", "narHash": "sha256-AEEDktApTEZ5PZXNDkry2YV2k6t0dTgLPEmAZbnigXU=",
"owner": "nix-community", "owner": "nix-community",
"repo": "lanzaboote", "repo": "lanzaboote",
"rev": "88b8a563ff5704f4e8d8e5118fb911fa2110ca05", "rev": "a65905a09e2c43ff63be8c0e86a93712361f871e",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-community", "owner": "nix-community",
"ref": "v0.4.3", "ref": "v0.4.2",
"repo": "lanzaboote", "repo": "lanzaboote",
"type": "github" "type": "github"
} }
}, },
"mnw": { "microvm": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
],
"spectrum": "spectrum"
},
"locked": { "locked": {
"lastModified": 1758834834, "lastModified": 1758113222,
"narHash": "sha256-Y7IvY4F8vajZyp3WGf+KaiIVwondEkMFkt92Cr9NZmg=", "narHash": "sha256-Q5i/qaj6v6F4N1Q5gI/4aL0IEEUE/LjQuwcA8L5IOMc=",
"owner": "Gerg-L", "owner": "astro",
"repo": "mnw", "repo": "microvm.nix",
"rev": "cfbc7d1cc832e318d0863a5fc91d940a96034001", "rev": "b9206e245c07c0782beff58e1e94bb48b2531d15",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "Gerg-L", "owner": "astro",
"repo": "mnw", "repo": "microvm.nix",
"type": "github" "type": "github"
} }
}, },
"nixos-hardware": { "nixos-hardware": {
"locked": { "locked": {
"lastModified": 1762847253, "lastModified": 1759261527,
"narHash": "sha256-BWWnUUT01lPwCWUvS0p6Px5UOBFeXJ8jR+ZdLX8IbrU=", "narHash": "sha256-wPd5oGvBBpUEzMF0kWnXge0WITNsITx/aGI9qLHgJ4g=",
"owner": "nixos", "owner": "nixos",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "899dc449bc6428b9ee6b3b8f771ca2b0ef945ab9", "rev": "e087756cf4abbe1a34f3544c480fc1034d68742f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -345,13 +195,64 @@
"type": "github" "type": "github"
} }
}, },
"nixos-secrets": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1759474546,
"narHash": "sha256-ZOlrxrAb5x5yM13P16sZowXb8AbEH02cZxets6SmkkA=",
"ref": "refs/heads/main",
"rev": "b28dde5afb05bf107905eff5c38908463fde5c59",
"shallow": true,
"type": "git",
"url": "ssh://forgejo@git.garage.systems/jml/nixos-secrets.git"
},
"original": {
"shallow": true,
"type": "git",
"url": "ssh://forgejo@git.garage.systems/jml/nixos-secrets.git"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1762977756, "lastModified": 1757967192,
"narHash": "sha256-4PqRErxfe+2toFJFgcRKZ0UI9NSIOJa+7RXVtBhy4KE=", "narHash": "sha256-/aA9A/OBmnuOMgwfzdsXRusqzUpd8rQnQY8jtrHK+To=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0d7c15863b251a7a50265e57c1dca1a7add2e291",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1730741070,
"narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d063c1dd113c91ab27959ba540c0d9753409edf3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1759381078,
"narHash": "sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "c5ae371f1a6a7fd27823bc500d9390b38c05fa55", "rev": "7df7ff7d8e00218376575f0acdcc5d66741351ee",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -361,75 +262,6 @@
"type": "github" "type": "github"
} }
}, },
"noctalia": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1764122160,
"narHash": "sha256-JZ51AW7zKgqlZp+oqt3Y7thglv23TPjgG1XiGBFWhr8=",
"owner": "noctalia-dev",
"repo": "noctalia-shell",
"rev": "3c5dfd87db582bf9056d83f41d53b90ba08023c6",
"type": "github"
},
"original": {
"owner": "noctalia-dev",
"repo": "noctalia-shell",
"type": "github"
}
},
"nur": {
"inputs": {
"flake-parts": [
"stylix",
"flake-parts"
],
"nixpkgs": [
"stylix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1758998580,
"narHash": "sha256-VLx0z396gDCGSiowLMFz5XRO/XuNV+4EnDYjdJhHvUk=",
"owner": "nix-community",
"repo": "NUR",
"rev": "ba8d9c98f5f4630bcb0e815ab456afd90c930728",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "NUR",
"type": "github"
}
},
"nvf": {
"inputs": {
"flake-compat": "flake-compat_2",
"flake-parts": "flake-parts_2",
"mnw": "mnw",
"nixpkgs": [
"nixpkgs"
],
"systems": "systems"
},
"locked": {
"lastModified": 1762622004,
"narHash": "sha256-NpzzgaoMK8aRHnndHWbYNKLcZN0r1y6icCoJvGoBsoE=",
"owner": "notashelf",
"repo": "nvf",
"rev": "09470524a214ed26633ddc2b6ec0c9bf31a8b909",
"type": "github"
},
"original": {
"owner": "notashelf",
"repo": "nvf",
"type": "github"
}
},
"pre-commit-hooks-nix": { "pre-commit-hooks-nix": {
"inputs": { "inputs": {
"flake-compat": [ "flake-compat": [
@ -440,14 +272,15 @@
"nixpkgs": [ "nixpkgs": [
"lanzaboote", "lanzaboote",
"nixpkgs" "nixpkgs"
] ],
"nixpkgs-stable": "nixpkgs-stable"
}, },
"locked": { "locked": {
"lastModified": 1750779888, "lastModified": 1731363552,
"narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=", "narHash": "sha256-vFta1uHnD29VUY4HJOO/D6p6rxyObnf+InnSMT4jlMU=",
"owner": "cachix", "owner": "cachix",
"repo": "pre-commit-hooks.nix", "repo": "pre-commit-hooks.nix",
"rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d", "rev": "cd1af27aa85026ac759d5d3fccf650abe7e1bbf0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -456,38 +289,16 @@
"type": "github" "type": "github"
} }
}, },
"quickshell": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1764045583,
"narHash": "sha256-W24ReyRrhOKTKIsuAMkY5hnVlCufGoONM79sjUoyQkk=",
"owner": "outfoxxed",
"repo": "quickshell",
"rev": "e9bad67619ee9937a1bbecfc6ad3b4231d2ecdc3",
"type": "github"
},
"original": {
"owner": "outfoxxed",
"repo": "quickshell",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"disko": "disko", "disko": "disko",
"home-manager": "home-manager", "home-manager": "home-manager",
"lanzaboote": "lanzaboote", "lanzaboote": "lanzaboote",
"microvm": "microvm",
"nixos-hardware": "nixos-hardware", "nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs", "nixos-secrets": "nixos-secrets",
"noctalia": "noctalia", "nixpkgs": "nixpkgs_2",
"nvf": "nvf", "sops-nix": "sops-nix"
"quickshell": "quickshell",
"sops-nix": "sops-nix",
"stylix": "stylix"
} }
}, },
"rust-overlay": { "rust-overlay": {
@ -498,11 +309,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1761791894, "lastModified": 1731897198,
"narHash": "sha256-myRIDh+PxaREz+z9LzbqBJF+SnTFJwkthKDX9zMyddY=", "narHash": "sha256-Ou7vLETSKwmE/HRQz4cImXXJBr/k9gp4J4z/PF8LzTE=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "59c45eb69d9222a4362673141e00ff77842cd219", "rev": "0be641045af6d8666c11c2c40e45ffc9667839b5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -518,11 +329,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1763069729, "lastModified": 1759188042,
"narHash": "sha256-A91a+K0Q9wfdPLwL06e/kbHeAWSzPYy2EGdTDsyfb+s=", "narHash": "sha256-f9QC2KKiNReZDG2yyKAtDZh0rSK2Xp1wkPzKbHeQVRU=",
"owner": "Mic92", "owner": "Mic92",
"repo": "sops-nix", "repo": "sops-nix",
"rev": "a2bcd1c25c1d29e22756ccae094032ab4ada2268", "rev": "9fcfabe085281dd793589bdc770a2e577a3caa5d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -531,38 +342,20 @@
"type": "github" "type": "github"
} }
}, },
"stylix": { "spectrum": {
"inputs": { "flake": false,
"base16": "base16",
"base16-fish": "base16-fish",
"base16-helix": "base16-helix",
"base16-vim": "base16-vim",
"firefox-gnome-theme": "firefox-gnome-theme",
"flake-parts": "flake-parts_3",
"gnome-shell": "gnome-shell",
"nixpkgs": [
"nixpkgs"
],
"nur": "nur",
"systems": "systems_2",
"tinted-foot": "tinted-foot",
"tinted-kitty": "tinted-kitty",
"tinted-schemes": "tinted-schemes",
"tinted-tmux": "tinted-tmux",
"tinted-zed": "tinted-zed"
},
"locked": { "locked": {
"lastModified": 1762264356, "lastModified": 1754675037,
"narHash": "sha256-QVfC53Ri+8n3e7Ujx9kq6all3+TLBRRPRnc6No5qY5w=", "narHash": "sha256-afS08F7lfMUBR4qrBxinN1kuxu+DoHQ5TPNVp9VS/OA=",
"owner": "nix-community", "ref": "refs/heads/main",
"repo": "stylix", "rev": "586577f3015397afacd83bc185454f4cc3c8028f",
"rev": "647bb8dd96a206a1b79c4fd714affc88b409e10b", "revCount": 955,
"type": "github" "type": "git",
"url": "https://spectrum-os.org/git/spectrum"
}, },
"original": { "original": {
"owner": "nix-community", "type": "git",
"repo": "stylix", "url": "https://spectrum-os.org/git/spectrum"
"type": "github"
} }
}, },
"systems": { "systems": {
@ -579,102 +372,6 @@
"repo": "default", "repo": "default",
"type": "github" "type": "github"
} }
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"tinted-foot": {
"flake": false,
"locked": {
"lastModified": 1726913040,
"narHash": "sha256-+eDZPkw7efMNUf3/Pv0EmsidqdwNJ1TaOum6k7lngDQ=",
"owner": "tinted-theming",
"repo": "tinted-foot",
"rev": "fd1b924b6c45c3e4465e8a849e67ea82933fcbe4",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "tinted-foot",
"rev": "fd1b924b6c45c3e4465e8a849e67ea82933fcbe4",
"type": "github"
}
},
"tinted-kitty": {
"flake": false,
"locked": {
"lastModified": 1735730497,
"narHash": "sha256-4KtB+FiUzIeK/4aHCKce3V9HwRvYaxX+F1edUrfgzb8=",
"owner": "tinted-theming",
"repo": "tinted-kitty",
"rev": "de6f888497f2c6b2279361bfc790f164bfd0f3fa",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "tinted-kitty",
"type": "github"
}
},
"tinted-schemes": {
"flake": false,
"locked": {
"lastModified": 1757716333,
"narHash": "sha256-d4km8W7w2zCUEmPAPUoLk1NlYrGODuVa3P7St+UrqkM=",
"owner": "tinted-theming",
"repo": "schemes",
"rev": "317a5e10c35825a6c905d912e480dfe8e71c7559",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "schemes",
"type": "github"
}
},
"tinted-tmux": {
"flake": false,
"locked": {
"lastModified": 1757811970,
"narHash": "sha256-n5ZJgmzGZXOD9pZdAl1OnBu3PIqD+X3vEBUGbTi4JiI=",
"owner": "tinted-theming",
"repo": "tinted-tmux",
"rev": "d217ba31c846006e9e0ae70775b0ee0f00aa6b1e",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "tinted-tmux",
"type": "github"
}
},
"tinted-zed": {
"flake": false,
"locked": {
"lastModified": 1757811247,
"narHash": "sha256-4EFOUyLj85NRL3OacHoLGEo0wjiRJzfsXtR4CZWAn6w=",
"owner": "tinted-theming",
"repo": "base16-zed",
"rev": "824fe0aacf82b3c26690d14e8d2cedd56e18404e",
"type": "github"
},
"original": {
"owner": "tinted-theming",
"repo": "base16-zed",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View file

@ -5,69 +5,26 @@
nixos-hardware.url = "github:nixos/nixos-hardware"; nixos-hardware.url = "github:nixos/nixos-hardware";
home-manager.url = "github:nix-community/home-manager"; home-manager.url = "github:nix-community/home-manager";
home-manager.inputs.nixpkgs.follows = "nixpkgs"; home-manager.inputs.nixpkgs.follows = "nixpkgs";
lanzaboote.url = "github:nix-community/lanzaboote/v0.4.3"; lanzaboote.url = "github:nix-community/lanzaboote/v0.4.2";
lanzaboote.inputs.nixpkgs.follows = "nixpkgs"; lanzaboote.inputs.nixpkgs.follows = "nixpkgs";
sops-nix.url = "github:Mic92/sops-nix"; sops-nix.url = "github:Mic92/sops-nix";
sops-nix.inputs.nixpkgs.follows = "nixpkgs"; sops-nix.inputs.nixpkgs.follows = "nixpkgs";
disko.url = "github:nix-community/disko"; disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs"; disko.inputs.nixpkgs.follows = "nixpkgs";
stylix.url = "github:nix-community/stylix"; microvm.url = "github:astro/microvm.nix";
stylix.inputs.nixpkgs.follows = "nixpkgs"; microvm.inputs.nixpkgs.follows = "nixpkgs";
nixos-secrets.url = "git+ssh://forgejo@git.garage.systems/jml/nixos-secrets.git?shallow=1";
#obsidian-nvim.url = "github:epwalsh/obsidian.nvim"; #nixos-secrets.flake = false; # TODO: Why does flake need to be false?
nvf = { #nixos-secrets.inputs.nixpkgs.follows = "nixpkgs";
url = "github:notashelf/nvf";
inputs.nixpkgs.follows = "nixpkgs";
#inputs.obsidian-nvim.follows = "obsidian-nvim";
};
quickshell = {
url = "github:outfoxxed/quickshell";
inputs.nixpkgs.follows = "nixpkgs";
};
noctalia = {
url = "github:noctalia-dev/noctalia-shell";
inputs.nixpkgs.follows = "nixpkgs";
inputs.quickshell.follows = "quickshell";
};
}; };
# https://nix.dev/tutorials/nix-language.html#named-attribute-set-argument # https://nix.dev/tutorials/nix-language.html#named-attribute-set-argument
outputs = outputs = inputs@{self, nixpkgs, nixos-hardware, home-manager, sops-nix, lanzaboote, disko, microvm, nixos-secrets, ...}:
inputs@{
self,
nixpkgs,
nixos-hardware,
home-manager,
sops-nix,
lanzaboote,
disko,
stylix,
nvf,
...
}:
let let
zwLib = import ./lib { mkSystem = (import ./lib {
inherit nixpkgs home-manager inputs; inherit nixpkgs home-manager inputs;
}; }).mkSystem;
mkSystem = zwLib.mkSystem;
mkHome = zwLib.mkHome;
mkHomeConfigs = zwLib.mkHomeConfigs;
# NOTE: Currently these are exclusively user-profiles which use home-manager.
# Their home-manager specific declarations are at ../users/${username}/home.nix
system = "x86_64-linux"; # TODO: Improve this from only static x86 to dynamic.
homeUserProfiles = {
jml = mkHome {
inherit system; # inputs;
username = "jml";
extraModules = [ nvf.homeManagerModules.default ];
};
};
in in
{ {
lib = {
mkSystem = mkSystem;
};
# NOTE: Run `nix flake show` to see what this flake has to offer. # NOTE: Run `nix flake show` to see what this flake has to offer.
# TODO: Enable automated formatting with something like numtide/treefmt-nix # TODO: Enable automated formatting with something like numtide/treefmt-nix
nixosConfigurations = { nixosConfigurations = {
@ -77,7 +34,12 @@
}; };
lithium = mkSystem { lithium = mkSystem {
hostname = "lithium"; hostname = "lithium";
# extraModules = [ inputs.sops-nix.nixosModules.sops ]; #specialArgs = {inherit inputs;};
extraModules = [
inputs.sops-nix.nixosModules.sops
inputs.nixos-secrets.nixosModules.private-config
];
#extraModules = [ microvm.nixosModules.host ];
users = [ users = [
"jml" "jml"
"breakglass" "breakglass"
@ -88,20 +50,7 @@
users = [ users = [
"jml" "jml"
]; ];
homeUsers = { extraModules = [];
jml = homeUserProfiles.jml.module;
};
#extraModules = [ (import ./overlays) ];
# NOTE: If I'm using a home-manager configuration on a given host,
# I also need to include the relevant modules.
# TODO: Can I instead self-reference the homeConfigurations in this flake?
extraModules = [
stylix.nixosModules.stylix
];
};
cobalt = mkSystem {
hostname = "cobalt";
users = [ "jml" ];
}; };
# `nix build .#nixosConfigurations.installIso.config.system.build.isoImage` # `nix build .#nixosConfigurations.installIso.config.system.build.isoImage`
# https://github.com/nix-community/nixos-generators # https://github.com/nix-community/nixos-generators
@ -113,10 +62,12 @@
specialArgs = {inherit inputs;}; specialArgs = {inherit inputs;};
}; };
}; };
homeConfigurations = {
# For Debugging: `home-manager build --flake .` or `nix build .#homeConfigurations."jml".activationPackage` "jml" = home-manager.lib.homeManagerConfiguration {
# `home-manager switch --flake .#jml` modules = [
# https://nix-community.github.io/home-manager/options.xhtml ./users/jml/home.nix
homeConfigurations = mkHomeConfigs homeUserProfiles; ];
};
};
}; };
} }

2
git-agecrypt.toml Normal file
View file

@ -0,0 +1,2 @@
[config]
"hosts/lithium/semi-secret-vars.nix" = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP2SVmZ3iJF/rviKhTgkZOvu1fWr6G29K4u6yaxjZn4H jay@lithium"]

View file

@ -1,21 +0,0 @@
Device Specific Hardware Details`
```shell
[nix-shell:~]$ lspci -nn
00:00.0 Host bridge [0600]: Intel Corporation Xeon E3-1200 v6/7th Gen Core Processor Host Bridge/DRAM Registers [8086:5904] (rev 02)
00:02.0 VGA compatible controller [0300]: Intel Corporation HD Graphics 620 [8086:5916] (rev 02)
00:04.0 Signal processing controller [1180]: Intel Corporation Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem [8086:1903] (rev 02)
00:14.0 USB controller [0c03]: Intel Corporation Sunrise Point-LP USB 3.0 xHCI Controller [8086:9d2f] (rev 21)
00:14.2 Signal processing controller [1180]: Intel Corporation Sunrise Point-LP Thermal subsystem [8086:9d31] (rev 21)
00:15.0 Signal processing controller [1180]: Intel Corporation Sunrise Point-LP Serial IO I2C Controller #0 [8086:9d60] (rev 21)
00:15.1 Signal processing controller [1180]: Intel Corporation Sunrise Point-LP Serial IO I2C Controller #1 [8086:9d61] (rev 21)
00:16.0 Communication controller [0780]: Intel Corporation Sunrise Point-LP CSME HECI #1 [8086:9d3a] (rev 21)
00:17.0 SATA controller [0106]: Intel Corporation Sunrise Point-LP SATA Controller [AHCI mode] [8086:9d03] (rev 21)
00:1c.0 PCI bridge [0604]: Intel Corporation Sunrise Point-LP PCI Express Root Port #1 [8086:9d10] (rev f1)
00:1c.5 PCI bridge [0604]: Intel Corporation Sunrise Point-LP PCI Express Root Port #6 [8086:9d15] (rev f1)
00:1f.0 ISA bridge [0601]: Intel Corporation Sunrise Point-LP LPC Controller [8086:9d58] (rev 21)
00:1f.2 Memory controller [0580]: Intel Corporation Sunrise Point-LP PMC [8086:9d21] (rev 21)
00:1f.3 Audio device [0403]: Intel Corporation Sunrise Point-LP HD Audio [8086:9d71] (rev 21)
00:1f.4 SMBus [0c05]: Intel Corporation Sunrise Point-LP SMBus [8086:9d23] (rev 21)
02:00.0 Network controller [0280]: Intel Corporation Wireless 8260 [8086:24f3] (rev 3a)
```

View file

@ -1,6 +0,0 @@
{}:
{
# Default to systemd-boot
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
}

View file

@ -1,26 +0,0 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page
# and in the NixOS manual (accessible by running nixos-help).
{ config, pkgs, ... }:
{
networking.hostName = "cobalt"; # Define your hostname.
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
networking.networkmanager.enable = true;
services.xserver.xkb = {
layout = "us";
variant = "";
};
fonts.packages = with pkgs; [
nerd-fonts.fira-code
nerd-fonts.iosevka
atkinson-hyperlegible
];
system.stateVersion = "25.05";
}

View file

@ -1,20 +0,0 @@
{ inputs, ... }:
{
imports = [
./boot.nix
../../modules/nixos/base.nix
../../modules/nixos/audio.nix
#../../modules/nixos/desktop.nix
# https://github.com/NixOS/nixos-hardware/blob/master/README.md#using-nix-flakes-support
# TODO: This module doesn't exist yet.
#inputs.nixos-hardware.nixosModules.asus-zenbook-ux390u
/home/jml/Workspace/nixos-hardware/asus/zenbook/ux390ua
./hardware-configuration.nix
./configuration.nix
../../modules/nixos/gaming.nix
../../modules/nixos/desktop/xfce
#../../modules/nixos/desktop/niri
];
zw.gaming.enable = true;
}

View file

@ -1,41 +0,0 @@
# 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.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[ (modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/959b3c06-58a2-45be-b2d6-275c489c31f8";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/2EC2-D03D";
fsType = "vfat";
options = [ "fmask=0077" "dmask=0077" ];
};
swapDevices =
[ { device = "/dev/disk/by-uuid/0239ee2a-484b-4a17-b1e9-02fd35df851f"; }
];
# 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
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp0s20f0u1u3.useDHCP = lib.mkDefault true;
# networking.interfaces.wlp2s0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View file

@ -7,29 +7,20 @@ This is my primary homelab host/NAS, previously powered by TrueNAS Scale/k3s.
Even with fully declarative Nix/Nixpkgs/NixOS at the end of the day there are Even with fully declarative Nix/Nixpkgs/NixOS at the end of the day there are
still some actions that need to be taken manually. still some actions that need to be taken manually.
- secrets configuration for `sops-nix` - secrets configuration (both for SOPS and git-agecrypt semi-secrets)
- kanidm user management - kanidm user management
- tailscale auth key - tailscale auth key
- jellyfin configuration via web-ui - jellyfin configuration via web-ui
## Secrets and "Private Information" ## Semi-Secrets
Originally I had used two providers of secrets, `sops-nix` and `git-agecrypt`, `semi-secret-vars.nix` is using [git-agecrypt](https://github.com/vlaci/git-agecrypt)
and the reasoning for that was, with `git-agecrypt` I could directly encrypt an and following a pattern I discovered here:
entire `.nix` file, and use it to conceal an arbitrary amount of my nix config. - https://github.com/nyawox/arcanum/blob/4629dfba1bc6d4dd2f4cf45724df81289230b61a/var/README.md
The #1 thing I was using it for was hiding details about the domain names that - https://github.com/vlaci/git-agecrypt
power various services. I know that's not real security, and domains aren't
really private, but server logs prove that not including a domain in a GH repo
means you get dramatically fewer spurious requests.
The reason for using `git-agecrypt` against a whole nix file like that was most Essentially there are some details I won't want exposed in the repository, but
importantly because it allowed me to *just use nix variables*. Compared to the I do want them available to all my nix modules. The main one being the domain.
invocationss SOPS & `sops-nix` require, it can be a lot more simple for setting
values like a domain name.
Now I'm going all in on `sops-nix` as the exclusive manager of secrets, and While it's not really a secret in the way a password is, consider this effort a
maintaining a separate flake which contains private nix configuration details. mitigation against ddos attacks and automated requests and login attempts.
There are still issues with this, and now my overall nix config is essentially
fractured between "flake-A" and "flake-B", which gives me all the same issues
that any other software project faces with that arrangement. But I dislike
using `git-agecrypt` even more than I dislike those problems.

View file

@ -1,6 +0,0 @@
{}:
{
# Default to systemd-boot
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
}

View file

@ -1,7 +1,8 @@
{ config, pkgs, ... }: { config, pkgs, lib, ... }:
{ {
sops.defaultSopsFile = ./secrets/common.yaml; #sops.defaultSopsFile = ./secrets/common.yaml;
networking.hostName = "lithium"; networking.hostName = "lithium";
networking.domain = lib.mkForce config.vars.domain;
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
zfs zfs
]; ];

View file

@ -1,16 +1,47 @@
{ inputs, ... }: { inputs, ... }:
#let
#secretsPath = builtins.toString inputs.nixos-secrets;
#in
{ {
#sops = {
#defaultSopsFile = "${secretsPath}/${config.hostname}/secrets.yaml";
#defaultSopsFile = "${secretsPath}/global/secrets.yaml";
#};
imports = [ imports = [
./boot.nix
../../modules/nixos/base.nix ../../modules/nixos/base.nix
inputs.sops-nix.nixosModules.sops #inputs.sops-nix.nixosModules.sops
#import ./sops.nix ( {inherit inputs;} )
./sops.nix
./hardware.nix ./hardware.nix
./configuration.nix ./configuration.nix
./semi-secret-vars.nix
./services/caddy.nix ./services/caddy.nix
./services/tailscale.nix ./services/tailscale.nix
./services/kanidm.nix ./services/kanidm.nix
./services/jellyfin.nix ./services/jellyfin.nix
./services/uptime-kuma.nix ./services/uptime-kuma.nix
./services/file-shares.nix ./services/file-shares.nix
./services/miniflux
./services/forgejo.nix
./services/immich.nix
./services/calibre-web.nix
# Monitoring
./services/monitoring
./services/smartd.nix
# Game Servers
./services/palworld
# TODO: Add Karakeep with yt-dlp + https://news.ycombinator.com/item?id=45595084
# Services running in virtual machines
#./microvms
#./microvms/palworld
]; ];
} }

View file

@ -0,0 +1,18 @@
{ config, pkgs, lib, ... }:
{
microvm.autostart = [
"palworld-server"
];
microvm = {
interfaces = [
{ type = "user"; id = "main-net"; }
{ type = "macvtap"; id = "vm-palworld"; }
];
# Interface Name on the Host
# Ethernet Address of MicroVM's interface.
# Locally administered have one of 2/6/A/E in the second nibble.
#interfaces = [{type = "tap";id = "vm-palworld";mac = "02:00:00:00:00:01";}];
};
}

View file

@ -0,0 +1,107 @@
{ config, pkgs, lib, ... }:
{
# Host Firewall
networking.firewall.allowedUDPPorts = [ 8211 ];
#networking.nat = {
#enable = true;
#enableIPv6 = true;
#externalInterface = "eth0";
#internalInterfaces = [ "microvm" ];
#};
microvm.vms.palworld-server = {
# Basic Requirements
# https://docs.palworldgame.com/getting-started/requirements
#hypervisor = "qemu";
vcpu = 4;
memory = 16348;
# Networking
interfaces = [{ type = "user"; id = "main-net"; }];
# Interface Name on the Host
# Ethernet Address of MicroVM's interface.
# Locally administered have one of 2/6/A/E in the second nibble.
#interfaces = [{type = "tap";id = "vm-palworld";mac = "02:00:00:00:00:01";}];
#forwardPorts = [
#{ proto = "udp"; from = "host"; host.port = 8211; guest.port = 8211; }
# Optional: If you need RCON or other ports, add them here
# { proto = "tcp"; from = "host"; host.port = 25575; guest.port = 25575; }
#];
# Persistent Data
sharedDirectories = [
{
source = "/var/lib/palworld-data";
target = "/var/lib/palworld-server";
readonly = false;
}
];
# VM NixOS Configuration
config = {
imports = [ pkgs.nixosModules.notDetected ];
networking.hostName = "palworld-vm";
time.timeZone = "America/Chicago";
environment.systemPackages = with pkgs; [
steamcmd
#glibc
#gnumake
#cff
];
# Pre-VM-Start
binScripts.tap-up = lib.mkAfter ''
${lib.getExe' pkgs.iproute2 "ip"} link set dev 'vm-ixp-as11201p' master 'ixp-peering'
'';
# Service Definition
systemd.services.palworld-dedicated = {
description = "Palworld Dedicated Server";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
Type = "simple";
User = "palworld";
Group = "palworld";
# Working Directory points to where steamcmd installs the server
WorkingDirectory = "/var/lib/palworld-server/Pal/Binaries/Win64";
ExecStart = ''
${pkgs.steam-run}/bin/steam-run ${pkgs.bash}/bin/bash -c '\
${pkgs.steamcmd}/bin/steamcmd \
+force_install_dur /var/lib/palworld-server \
+login anonymous \
+app_update 2394010 validate \
+quit \
&& \
./PalServer.sh -userperfthreads -NoAsyncLoadingThread -UseNvidiaServers -nosteamclient \
-Players=8 -Port=8211 -queryport=27015 -PublicPort=8211 -PublicIP=\"\" -RCONEnabled=False
'
'';
Restart = "on-failure";
RestartSec = "5s";
LimitNPROC = 10000;
LimitNOFILE = 100000;
};
};
# User and Group Configuration
users.users.palworld = {
isSystem = true;
group = "palworld";
createHome = false;
};
users.groups.palworld = {};
# Firewall Configuration
networking.firewall.allowedUDPPorts = [ 8211 ];
# Ensure correct permissions for shared directory
systemd.tmpfiles.rules = [
"d /var/lib/palworld-server 0755 palworld palworld -"
];
};
};
}

View file

@ -0,0 +1,50 @@
{ config, lib, pkgs, ... }:
{
microvm.vms.valheim = {
autoStart = true;
memorySize = 4096;
vcpu = 2;
forwardPorts = [
{ from = "host"; hostPort = 2456; guestPort = 2456; proto = "udp"; }
{ from = "host"; hostPort = 2457; guestPort = 2457; proto = "udp"; }
];
# NOTE: For games with large save files, choose a path in "/tank" for
# storage.
sharedDirectories = [
{
hostPath = "/srv/game-data-valheim";
guestPath = "/data";
tag = "valheim-data";
readOnly = false;
}
];
packages = [ pkgs.steamcmd pkgs.steam-run ];
users.users.valheim = {
isNormalUser = true;
home = "/home/valheim";
extraGroups = [ "wheel" ];
};
systemd.services.valheim = {
description = "Valheim Dedicated Server";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
workingDirectory = "/data";
ExecStart = ''
${pkgs.steam-run}/bin/steam-run ./valheim_server.x86_64 \
-name "Valheim NixOS" \
-port 2456 \
-world "FlatEarth" \
-password "secret" \
-public 1
'';
Restart = "always";
User = "valheim";
};
};
};
}

View file

@ -0,0 +1,8 @@
{ inputs, ... }:
# let secretsPath = builtins.toString inputs.nixos-secrets; in
{
#imports = [ inputs.nixos-secrets.nixosModules.private-config ];
# Enables a whole littany of private settings.
private-config.enable = true;
}

View file

@ -1,27 +1,30 @@
kanidm: kanidm:
admin-password: ENC[AES256_GCM,data:wNE9qWAjfp8tf29sn1Q6GYrbw8g=,iv:uzg971jGIVyEkEbcOm2W8dy4wVgWiL+4Ph/f/bnieI0=,tag:/yY1okvnJLYGw2OLBd2Zdg==,type:str] admin-password: ENC[AES256_GCM,data:Hvmo6YG2ZCoYdQOBOyPiS2XAm6I=,iv:qKu5vlT0HEqK3Mx3zgAA0OUA+B63rEXnq/P059mrweI=,tag:K+iTc8R30ClP8egzIEtXKA==,type:str]
idm-admin-password: ENC[AES256_GCM,data:jIWaXUgHjhp0bP/DrF1m+plzcvE=,iv:nNpIkg9FTbCncih1/pAk4o7teuk7Gf/nPXyrnpFx4no=,tag:WhhsjtEdyS3Zw4F7uF9APg==,type:str] idm-admin-password: ENC[AES256_GCM,data:Dvz2o6gY/G3igJFhaIQ4gj/OG/8=,iv:hDE+y8SKqRU8tNxnd7q4CE3GfOHOkMcODpqc43KiPfc=,tag:fU8AyFokVUhERUvi6W6bYw==,type:str]
forgejo-admin-password: ENC[AES256_GCM,data:d7a2pzSpaeZ0CQ==,iv:XB6Y41egclWzmyZe3g2Z9U1NcCilw3VTZNlym94h3IU=,tag:vR6jrXiBciPXzUmvxHzQNw==,type:str]
miniflux:
oauth2_client_secret: ENC[AES256_GCM,data:tZk6Ru5MQk+VJ/ulZNtKirL2lfLmOeVKsDbJfLly1SBwXC6HdjBx+yAFCc7YVX+2,iv:GIIMhV/sIALjoUZsMMXAJl968owAlULJ1JLpShqa3RM=,tag:c2i+LvGJKQm82fsUEHF/BA==,type:str]
sops: sops:
age: age:
- recipient: age1mv8xtvkuuw3hphq5ytaekz7p8a4kht79uajyhy534uy9e5472fhqj5zpxu - recipient: age1mv8xtvkuuw3hphq5ytaekz7p8a4kht79uajyhy534uy9e5472fhqj5zpxu
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5cFlReGMxV1R3QW1Vd1RU YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4WUlyeStvZ28xVmV4VlNK
WTgzNm5tbGhld3RMTGpMU1M2eVdoU0hmZGtNCmZLSTNOMk9IMDh6K2svRmRveGw3 d1J1Z0twaTFzSEFENit0S210eTJFYTR1c1FRCnQvcFRCMUtFMkJxUDlRUTNxcTlK
dlFUZ2lzTDBJWnBSTEhVTmVDOHVTdW8KLS0tIDhLWVZWSVJYM2x4YTlZWWZZZVNh dE5aM3U4Y0xvRE5jZlhrbjVtVk5rOHMKLS0tIHZISVlVcW9yT3hWcUtsVmN5TmRv
T2drb0p6TjZrZldpU0VUd0xmcVJUSk0KMjX3vr/74/HU7fmulefUHiNzwX8LcAes bXk5aSs4Wk9nM1p5d3FVSFBFKzVYZVEKYP2KQZIYm+zuI6OTfy85cEhj3gJWoKNu
ob3fabhMk9lmbuQk21rpoWbz3PNTfCQH63q+h7gLJTCCW2ISTvh/KQ== jxd8vxwSbDmsXQK+mT8MsA9s+A9AhzGcZQ0rIQM/yKWFKSXt4kJ9rg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age148yre4vaxp6lm59rft24te46szawqyguf8znkrtpq7ud8tpteauqxkwyjl - recipient: age148yre4vaxp6lm59rft24te46szawqyguf8znkrtpq7ud8tpteauqxkwyjl
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpK3VvNUhHNmJUQVZlREJl YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHQk1kYjU1bXZ0aUc3cGVQ
c1oyZWx4ekNEM0VqL3NKanNmSmZtQXNpcWdVCmpWcklLVnhWUisvcGZzV1NHN3p4 N1hwclNpRkJWQlVhaU9EOEFNRkRQMmhLakNjCjAwbE1UYkxxbzNxQjV5Y1I0TU0r
bW4wL09wL01XaHpveGdmbU4rbEp5NmsKLS0tIElhQVRmS05xUmJIZlI0S1dyWGhV cmZBUTZsTzMxZmFZZUxnRmw3T205aDAKLS0tIGdJTHBHVmk4TmlBQ1RHYkJ3aWw3
OGtKdHVwbWY2akJTQkF4YzlnNWQzNU0K81PyJ1tOvwOohNu9iUkS8vE7UXFRnJab bktSejFIdS8wS2R4RHRFMEwwb2pBN1kK8EoLo1E/DiFmpCf/v0kGLPcqIB1qZDd1
8OLHtzX7FrkIH8rO2D5vEL9gPmxUtNKc9Ad3sndQls/yfg4wJAYedA== Uf7ccMFqvQH8wGVuyqqwiZ2SconvK7hHC5U9qgi6bZa/t4aW8eeU/g==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2025-06-07T02:02:46Z" lastmodified: "2025-10-03T06:48:03Z"
mac: ENC[AES256_GCM,data:7mWOon8Hs2oU40l1dx4tVE8yXgcKoxfUAzY8zbtTzXqCOhzTzhY4OZAZiu4RiUSIOm7dMdQbH9zSx0j+5e2k9QflfJDDM3rWfunTa7L8Bm8k9b/WjS0Fnb7OV0InO6tLxQwkTamMcc7ORrKxwHB5PwuXD+efeWNXveHo5GYgF+M=,iv:seh2Pzt+AmmxyD5hwh3VkLQTDMq0Gh9mV6J3QrtxcmM=,tag:jpXn2fht8wArB2KD/ZmbyA==,type:str] mac: ENC[AES256_GCM,data:tglsrKi8Ydifc08LLA/KHzqWI3u9+Tn9kkERI3XKp/vSy4a90T2fU9f/lcyYbOXXuOOCL364wYq7ETmsTzOmb5Eo2Wtcu9Hsn820rLXuxNu7kyz9os/0eL/IThrPQtKov4/IdzoGQCqZvd9kcxTN1UPlRnd9aiHwsKmoFFFNZlE=,iv:y9+JQRXZf/6GabaQ1nQk3dL7qKMQxyzvIsXU2yVmlZo=,tag:klmWeIP068BpsSi8u/tE4A==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.10.2 version: 3.10.2

View file

@ -0,0 +1,10 @@
ADMIN_USERNAME=ENC[AES256_GCM,data:1b9RG0hCr3mV+Q==,iv:+P9LxMk7gLvCvVgrzRgHhk7s3kMnMNkWyT8cyBe0msI=,tag:2TVfhdVToEsz4mF/UrrP9w==,type:str]
ADMIN_PASSWORD=ENC[AES256_GCM,data:3HUEKnmGJ9vRSA==,iv:UpY8iFBIT01QUFINo3Cn7dT6q0uDbbDNuU2K7hjfaRY=,tag:LizvS3rkk7PHQVTLRtmbqw==,type:str]
sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0TWxwa21pNmxJczd1Y3l2\nZjIzYWJ2TEk0VlZGTXN2a3lvaGR3Q2cwNXlBClN5dXJyMmxFWnA5UytVUjErZ1Ju\nZHY5emdBd2plM1dvRWNwaE5ydW9IbGcKLS0tIEFtUFZnUkllajhDVTU4ejlTN09n\nN0VTWi9lb1UxQm1OTGwzU2dkc25XR1EKlsqqZCJ6YTseRoxaoQRcnnqg0Q6zDezP\ni8lVt4WCK/mIXiqASNf0EaunujxftSg4g5s1PoUzdh2RWRXjjUk6Rw==\n-----END AGE ENCRYPTED FILE-----\n
sops_age__list_0__map_recipient=age1mv8xtvkuuw3hphq5ytaekz7p8a4kht79uajyhy534uy9e5472fhqj5zpxu
sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCY1pnNWdpUGNmTXQ5VHY4\nazJaSUVnMEd4cTVIa3p1NWJBWHAyMEhuMXprCjk3MENuUDJ3OE13bUhJKzNxV1Yr\nZmMwMUtyZldodDRBRkRIUnN5cCswVHcKLS0tIE43RmFHQU0zMitrcmtKaGRMTXZZ\nZWdCOHNVaXp2WjNWUUtTNmVjQTdQMkUK7dbXoDOAxppQi3nmp0S7q130BBvxtdo/\nuaC5FuUXijp0U+Xrdkq3Qukv/u4b7E17mw3zMgHIaxf8w0W/XmTcXQ==\n-----END AGE ENCRYPTED FILE-----\n
sops_age__list_1__map_recipient=age148yre4vaxp6lm59rft24te46szawqyguf8znkrtpq7ud8tpteauqxkwyjl
sops_lastmodified=2025-07-22T01:07:11Z
sops_mac=ENC[AES256_GCM,data:I17CIK+OqttRpVc9U3Xdefnb95PFcFpCykLSxoVvO74NCvYcHYaB09Wnbt5X46u3TtX1ZmDr85WNzWocu+rM1kdJFXnGI/N9UiCgnam66QCwIlIckCldDGsnPbHpkeB7SFgCJ+1GOz19poPu2RJ41b28NRxNdV4+9HB15Xz6OcM=,iv:7u28+i8HA6wv9SuTSjCGr5o0Wak6zmUvSj3z3CzxT6U=,tag:GXEbSyBGkPSveD0zREBEyQ==,type:str]
sops_unencrypted_suffix=_unencrypted
sops_version=3.10.2

View file

@ -0,0 +1,8 @@
age-encryption.org/v1
-> ssh-ed25519 rhvgyQ 8V5ehsrqPR8s2joIfdpZRYDQpwH5BXI1GgQ/Qcb/Wg4
ZKRZkXT0uPbXzuXLsteW31GsKzZy1deUl1GdWeQB+4U
-> "f<f<DW--grease AQ] z5_)RUB7 2>&DjVar
Lhe9DbPHOqqKQ9HDhJB2xbIkrsxFGm39Yzr1J+ZbJnWYx5FCdGCCIexmv3GJy94t
--- qKkjS2aEWavCLldEwi4MUTlDoQuIu9tSRr5yoeZVQhs
b¾Z~lŠU"Uãp­µ¥ vÉñ¿³$?ƒ<EFBFBD>æ;:ZÖuÒ^öËÅŠQ/MBÉf]Ξ)4PYáî-ßäê«Ü» <EFBFBD>áØŸl¨Á¯&eâû2©ÈOUqXóMD<EFBFBD>SÜä7ÁE¿ðÀÉæ&us¿ð<EFBFBD>ÁU÷#Êdø#ØÿÀÂWõ9Øãó^k.ÜÝ0áü7«ðhŸ°©s4CRIwUÅ aʯMÀwi·U<EFBFBD>riÿkªb}yVÒÈÜüÏ>©¿rް[ƒÝ8s

View file

@ -0,0 +1,11 @@
# hosts/lithium/services
The idea is that each `*.nix` or each `./*/default.nix` file would contain all
necessary details for a service to bring itself up and be running.
One thing I have overlooked thus far is nothing tests for the existence of a
reverse proxy and bails out if one isn't available. Practically if caddy isn't
running, most of these services should also not run, or at the very least, the
blocks pertaining to setting up reverse proxy details don't need to run.
There's a way of doing that with things like lib.mkDefault and so forth.

View file

@ -0,0 +1,65 @@
{ config, ... }:
let
homelabDomain = inputs.nixos-secrets.homelabDomain;
certDir = config.security.acme.certs."${homelabDomain}".directory;
in
{
sops.secrets."cloudflare/dns_api_token" = {
mode = "0440";
group = config.services.caddy.group;
restartUnits = [ "caddy.service" "ddclient.service" ];
};
# TODO: Consider defining reverse proxy all in one location.
# All the ports and domains would be visible in one place.
security.acme = {
acceptTerms = true;
defaults = {
# NOTE: Uncomment the following line for testing, comment for production.
server = "https://acme-staging-v02.api.letsencrypt.org/directory";
dnsProvider = "cloudflare";
dnsResolver = "1.1.1.1:53";
dnsPropagationCheck = true;
credentialFiles = {
CLOUDFLARE_DNS_API_TOKEN_FILE = config.sops.secrets."cloudflare/dns_api_token".path;
};
group = config.services.caddy.group;
#reloadServices = [ "caddy" ];
email = "admin+acme@${homelabDomain}"; # NOTE: This email is /dev/null;
#keyType = "ec384";
};
};
services.ddclient = {
enable = true;
protocol = "cloudflare";
usev4 = "webv4, webv4=https://cloudflare.com/cdn-cgi/trace, web-skip='ip='";
username = "token";
#secretsFile = config.sops.secrets."cloudflare/dns_api_token".path;
passwordFile = config.sops.secrets."cloudflare/dns_api_token".path;
zone = homelabDomain;
domains = [
homelabDomain
"*.${homelabDomain}"
"id.${homelabDomain}"
"status.${homelabDomain}"
"grafana.${homelabDomain}"
"feeds.${homelabDomain}"
"git.${homelabDomain}"
"tv.${homelabDomain}"
"demo.${homelabDomain}" # Testing to see if the DNS record is set.
];
};
# NOTE: Issue a single cert /w subdomain wildcard
# At the expense of individual service security, some public details about
# attack surface remain slightly more private in https://crt.sh/
security.acme.certs."${homelabDomain}" = {
#group = config.services.caddy.group;
domain = "${homelabDomain}";
extraDomainNames = [ "*.${homelabDomain}" ];
};
# Nginx useACMEHost provides the DNS-01 challenge.
# security.acme.certs."${homelabDomain}".directory
}

View file

@ -0,0 +1,20 @@
{ inputs, config, pkgs, lib, ... }:
let
homelabDomain = inputs.nixos-secrets.homelabDomain;
svcDomain = "audiobooks.${homelabDomain}";
svcPort = config.services.audiobookshelf.port; # Prevent a Conflict
in
{
services.caddy.virtualHosts."${svcDomain}".extraConfig = ''
reverse_proxy :${svcPort}
'';
services.audiobookshelf = {
enable = true;
openFirewall = true;
port = 8000;
# NOTE: Path to AudioBookShelf config & metadata inside of `/var/lib`
dataDir = "audiobookshelf";
};
}

View file

@ -1,13 +1,22 @@
{ config, pkgs, ... }: { inputs, config, pkgs, lib, ... }:
let
homelabDomain = inputs.nixos-secrets.homelabDomain;
certDir = config.security.acme.certs."${homelabDomain}".directory;
in
{ {
sops.secrets.caddy_env = { services.nginx.enable = lib.mkForce false;
sopsFile = ../secrets/caddy.env;
format = "dotenv"; sops.secrets.cloudflare_env = {
mode = "0440"; mode = "0440";
owner = config.services.caddy.user; sopsFile = "${inputs.nixos-secrets}/lithium/cloudflare.env";
format = "dotenv";
group = config.services.caddy.group; group = config.services.caddy.group;
restartUnits = [ "caddy.service" ]; restartUnits = [ "caddy.service" ];
}; };
# TODO: Revert to using Caddy DNS for the whole thing.
# TODO: Add another cloudflare DDNS provider.
# TODO: Add Metrics with Prometheus & Grafana
services.caddy = { services.caddy = {
enable = true; enable = true;
package = pkgs.caddy.withPlugins { package = pkgs.caddy.withPlugins {
@ -16,26 +25,33 @@
"github.com/mholt/caddy-dynamicdns@v0.0.0-20250430031602-b846b9e8fb83" "github.com/mholt/caddy-dynamicdns@v0.0.0-20250430031602-b846b9e8fb83"
"github.com/caddy-dns/cloudflare@v0.2.1" "github.com/caddy-dns/cloudflare@v0.2.1"
]; ];
# NOTE: Built on 9/30/2025
# NOTE: Built on 6/4/2025 hash = "sha256-xuwNkxZop+RnzFtM9DEwah95nPSyx8KgM+Eu4EJ9kqI=";
hash = "sha256-swskhAr7yFJX+qy0FR54nqJarTOojwhV2Mbk7+fyS0I=";
}; };
# NOTE: Use Staging CA while testing, check `systemctl status caddy` # NOTE: Use Staging CA while testing, check `systemctl status caddy`
# to see if everything is working. # to see if everything is working.
# acmeCA = "https://acme-staging-v02.api.letsencrypt.org/directory"; # acmeCA = "https://acme-staging-v02.api.letsencrypt.org/directory";
# TODO: Add Metrics with Prometheus & Grafana environmentFile = config.sops.secrets.cloudflare_env.path;
environmentFile = config.sops.secrets.caddy_env.path; # NOTE: DNS provider settings
# https://caddy.community/t/how-to-use-dns-provider-modules-in-caddy-2/8148
globalConfig = '' globalConfig = ''
# acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN} #acme_dns cloudflare {$CLOUDFLARE_DNS_API_TOKEN}
dynamic_dns { dynamic_dns {
provider cloudflare {env.CLOUDFLARE_API_TOKEN} provider cloudflare {$CLOUDFLARE_DNS_API_TOKEN}
check_interval 30m
ttl 5m
domains { domains {
${config.networking.domain} @ ${homelabDomain} @
} }
dynamic_domains dynamic_domains
} }
''; '';
}; };
networking.firewall.allowedTCPPorts = [ 80 443 ]; networking.firewall = {
allowedTCPPorts = [ 80 443 ];
allowedUDPPorts = [ 443 ];
};
} }

View file

@ -0,0 +1,53 @@
{ inputs, config, pkgs, lib, ... }:
let
homelabDomain = inputs.nixos-secrets.homelabDomain;
#certDir = config.security.acme.certs."${homelabDomain}".directory;
svcDomain = "books.${homelabDomain}";
svcHttpPort = config.services.calibre-web.listen.port;
web_data_dir = "calibre-web";
# TODO: I want the actual media stored in the tank.
library_path = "/tank/media/library/books";
#library_path = "/var/lib/calibre-library";
in
{
# TODO: This isn't the right place for this, but we need to guarantee that a
# media group exists.
users.users.calibre-web.extraGroups = [ "media" ];
users.groups.media = {};
services.caddy.virtualHosts."${svcDomain}".extraConfig = ''
reverse_proxy localhost:8883
'';
# reverse_proxy :${toString svcHttpPort}
# encode {
# zstd
# gzip
# minimum_length 1024
# }
# '';
# NOTE: Needs some manual setup in Web-UI and I ecountered issues connecting even with firewall enabled.
# The following command is what I used to forward the port:
# ssh -f -N -L localhost:8883:localhost:8883 jml@lithium
services.calibre-web = {
enable = true;
listen.port = 8883;
# NOTE: Don't need to open calibre-web port, it's served by reverse_proxy
openFirewall = true; # TODO: Temporarily opened to allow configuration from inside my network.
user = "calibre-web";
group = "calibre-web";
# Either absolute path or directory name under "/var/lib"
# /tank/media/library/books
dataDir = web_data_dir;
options = {
enableBookUploading = true;
enableBookConversion = true;
# NOTE: If I don't already have an extant calibreLibrary, I need to leave this null or the app won't launch.
calibreLibrary = library_path;
};
};
}

View file

@ -0,0 +1,145 @@
{ config, pkgs, lib, ... }:
let
homelabDomain = config.networking.domain;
svcDomain = "git.${homelabDomain}";
theme = pkgs.fetchzip {
url = "https://github.com/catppuccin/gitea/releases/download/v1.0.2/catppuccin-gitea.tar.gz";
hash = "sha256-rZHLORwLUfIFcB6K9yhrzr+UwdPNQVSadsw6rg8Q7gs=";
stripRoot = false;
};
svcHttpPort = config.services.forgejo.settings.server.HTTP_PORT;
assetsDir = "${config.services.forgejo.stateDir}/custom/public/assets";
in
{
# NOTE: Periodically come update the catpuccin theme.
# `-auto` will automatically switch between latte and mocha modes.
services.forgejo.settings.ui = {
DEFAULT_THEME = "catpuccin-teal-auto";
THEMES = builtins.concatStringsSep "," (
[ "auto" ]
++ (map (name: lib.removePrefix "theme-" (lib.removeSuffix ".css" name)) (
builtins.attrNames (builtins.readDir theme)
))
);
};
# TODO: Setup a PostgreSQL Server.
# Inspiration here: https://github.com/nyawox/arcanum/blob/4629dfba1bc6d4dd2f4cf45724df81289230b61a/nixos/servers/forgejo.nix#L64
#sops-secrets.postgres-forgejo = {
#sopsFile = ../secrets/forgejo.yaml;
#};
services.caddy.virtualHosts."${svcDomain}".extraConfig = ''
reverse_proxy :${toString svcHttpPort}
'';
services.forgejo = {
enable = true;
# database.type = "postgres";
settings = {
default.APP_NAME = "GitGarage";
server = {
DOMAIN = svcDomain;
ROOT_URL = "https://${svcDomain}";
HTTP_PORT = 3000;
};
# NOTE: Actions support is based on: https://github.com/nektos/act
#actions = {
#ENABLED = true;
#DEFAULT_ACTIONS_URL = "github";
#};
actions.ENABLED = false;
# NOTE: Registration is handled with kanidm.
# Registration button link is at /user/sign_up
service = {
REGISTER_EMAIL_CONFIRM = false;
DISABLE_REGISTRATION = false;
ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
SHOW_REGISTRATION_BUTTON = false;
REQUIRE_SIGNIN_VIEW = false;
# TODO: Consider setting up emails.
ENABLE_NOTIFY_MAIL = false;
};
openid = {
ENABLE_OPENID_SIGNIN = true;
ENABLE_OPENID_SIGNUP = true;
WHITELISTED_URIS = "id.${homelabDomain}";
};
# TODO: Literally review all server settings, and link the forgejo documentation.
# Also perhaps include every setting here explicitly.
oauth2_client = {
REGISTER_EMAIL_CONFIRM = false;
ENABLE_AUTO_REGISTRATION = true;
ACCOUNT_LINKING = "login";
USERNAME = "nickname";
UPDATE_AVATAR = true;
OPENID_CONNECT_SCOPES = "openid email profile";
};
repository = {
DEFAULT_PRIVATE = "private";
DEFAULT_BRANCH = "main";
ENABLE_PUSH_CREATE_USER = true;
ENABLE_PUSH_CREATE_ORG = true;
};
mailer.ENABLED = false;
};
};
# TODO: Finish Configuring the kandim oauth for forgejo....
services.kanidm.provision.systems.oauth2.forgejo = {
displayName = "forgejo";
# TODO: Get this from Forgejo
# originUrl = "https://git.${homelabDomain}/user/oauth2/${homelabDomain}/callback";
originUrl = "${config.services.forgejo.settings.server.ROOT_URL}/user/oauth2/kanidm/callback";
originLanding = "https://git.${homelabDomain}/";
#basicSecretFile = "TODO!SETME";
scopeMaps."git.users" = [
"openid"
"email"
"profile"
"groups"
];
# WARNING: PKCE is currently not supported by gitea/forgejo,
# see https://github.com/go-gitea/gitea/issues/21376
allowInsecureClientDisablePkce = true;
preferShortUsername = true;
claimMaps.groups = {
joinType = "array";
valuesByGroup."git.admins" = [ "admin" ];
};
};
systemd.services.forgejo = {
preStart =
lib.mkAfter # bash
''
echo "Installing Catppuccin Assets"
rm -rf ${assetsDir}
mkdir -p ${assetsDir}
ln -sf ${theme} ${assetsDir}/css
'';
};
#sops.secrets.forgejo-runner-token = {};
#services.gitea-actions-runner = {
#package = pkgs.forgejo-runner;
#instances.default = {
#enable = true;
#name = "monolith";
#url = "https://${serviceDomain}";
#tokenFile = config.sops.secrets.forgejo-runner-token.path;
# NOTE: I don't want huge images if it can be avoided.
# https://nektosact.com/usage/runners.html
#labels = [
#"ubuntu-latest:docker://node:16-bullseye-slim"
#"ubuntu-22.04:docker://node:16-bullseye-slim"
#];
#};
#};
# TODO: Consider automatically creating admin account and password...
# https://wiki.nixos.org/wiki/Forgejo#Ensure_users
# Might be necessary to generate a token for kanidm
sops.secrets."forgejo/admin-password".owner = "forgejo";
}

View file

@ -0,0 +1,42 @@
{ pkgs, ... }:
{
# TODO
# systemd.services.<name>.serviceConfig.{MemoryMax,CPUQuota}
systemd.services.valheim-server = {
description = "Valheim dedicated server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
};
serviceConfig = {
Type = "simple";
User = "valheim";
Group = "valheim";
ExecStart = "${pkgs.steamcmd}/bin/steamcmd +login anonymous +force_install_dir /home/valheim/server +app_update 896660 validate +exit && /home/valheim/server/valheim_server.x86_64";
};
users.users.valheim = {
isSystemUser = true;
group = "valheim";
home = "/home/valheim";
};
users.groups.valheim = {};
networking.firewall = {
allowedTCPPorts = [ 7777 2456 ];
allowedUDPPorts = [ 7777 2457 ];
};
services.restic.backups.gameservers = {
user = "root";
# TODO: Pick a real backup directory.
repository = "/backup/gameservers";
paths = [
"/var/lib/terraria"
"/home/valheim/server"
];
timeConfig = {
OnCalendar = "daily";
};
};
}

View file

@ -0,0 +1,7 @@
{ config, pkgs, ... }:
{
services.home-assistant = {
enable = true;
openFirewall = true;
};
}

View file

@ -0,0 +1,87 @@
{ inputs, config, pkgs, lib, ... }:
let
homelabDomain = inputs.nixos-secrets.homelabDomain;
svcDomain = "photos.${homelabDomain}";
photoStorageDir = "/tank/shares/photos";
svcPort = config.services.immich.port;
# https://docs.immich.app/install/config-file/
jsonSettings = {
server.externalDomain = "https://${svcDomain}";
oauth = {
enabled = true;
issuerUrl = "https://"; # TODO: the kanidm url?
clientId = "immich";
clientSecret = config.sops.placeholder."immich/oauth2_client_secret";
scope = "openid email profile";
signingAlgorithm = "ES256";
storageLabelClaim = "email";
buttonText = "Login with Kanidm";
autoLaunch = true;
mobileOverrideEnabled = true;
mobileRedirectUri = "https://${svcDomain}/api/oauth/mobile-redirect/";
};
};
in
{
# NOTE: The following repo contains a highly mature immich setup on nixos.
# https://github.com/xinyangli/nixos-config/blob/a8b5bea68caea573801ccfdb8ceacb7a8f2b0190/machines/agate/services/immich.nix
services.caddy.virtualHosts."${svcDomain}".extraConfig = ''
reverse_proxy :${toString svcPort}
'';
# NOTE: Primarily to contain DB_PASSWORD to make it possible to backup and restore the DB.
# sops.secrets.immich_env = {
# sopsFile = ../../secrets/immich.env;
# format = "dotenv";
# mode = "0440";
# owner = "immich";
# group = "immich";
# restartUnits = [ "immich.service" ];
# };
sops.secrets."immich/oauth2_client_secret" = { };
sops.templates."immich.json" = {
mode = "0440";
owner = config.services.immich.user;
group = config.services.immich.group;
content = builtins.toJSON jsonSettings;
};
users.users.immich = {
isSystemUser = true;
};
users.groups.immich = {};
systemd.tmpfiles.rules = [
"d ${photoStorageDir} 0770 immich immich -"
];
# TODO: Setup mTLS for external / non-tailscale VPN immich access.
# https://github.com/alangrainger/immich-public-proxy/blob/main/docs/securing-immich-with-mtls.md
# TODO: Consider immich-public-proxy for generating "share" links
# https://github.com/alangrainger/immich-public-proxy
services.immich = {
enable = true;
openFirewall = true;
port = 2283; # default
#secretsFile = config.sops.secrets.immich_env.path;
# TODO: Build this directory with permissions for the immich user.
mediaLocation = "/tank/shares/photos";
environment = {
IMMICH_CONFIG_FILE = config.sops.templates."immich.json".path;
};
};
services.kanidm.provision.systems.oauth2.immich = {
displayName = "immich";
originUrl = "https://${svcDomain}/oauth2/oidc/callback";
originLanding = "https://${svcDomain}/";
basicSecretFile = config.sops.secrets."immich/oauth2_client_secret".path;
scopeMaps."immich.users" = [
"openid"
"email"
"profile"
];
preferShortUsername = true;
};
}

View file

@ -1,33 +1,91 @@
{ config, pkgs, lib, ... }: { inputs, config, pkgs, lib, ... }:
let let
svcDomain = "id.${config.networking.domain}"; homelabDomain = inputs.nixos-secrets.homelabDomain;
caddyCertsRoot = "${config.services.caddy.dataDir}/.local/share/caddy/certificates"; svcDomain = "id.${homelabDomain}";
caddyCertsDir = "${caddyCertsRoot}/acme-v02.api.letsencrypt.org-directory"; kanidmCertDir = "/var/lib/kanidm/certs";
certsDir = "/var/lib/kanidm/certs"; caddyCertStore = "${config.services.caddy.dataDir}/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/${svcDomain}";
#kcertloc = "${caddyCertsStore}/${svcDomain}/";
certRenewalScript = pkgs.writeShellScript "copy-kanidm-cert-hook" ''
set -Eeuo pipefail
mkdir -p ${kanidmCertDir}
cp ${caddyCertStore}/${svcDomain}.crt ${kanidmCertDir}/cert.pem
cp ${caddyCertStore}/${svcDomain}.key ${kanidmCertDir}/key.pem
chown kanidm:kanidm ${kanidmCertDir}/*.pem
${pkgs.systemd}/bin/systemctl restart kanidm.service
'';
kanidmCertCopier = "kanidm-cert-copier";
in in
{ {
# NOTE: Domains are serious when they are the root of identity/authnz.
# Recommendation from Kanidm docs for "Maximum" security is to maintain
# Both `example.com` and `id.example-auth.com`, the latter for idm infra exclusively.
# I consider that to be untenable and even more risky.
# The next recommendation is to follow a pattern like so
# id.example.com
# australia.id.example.com
# id-test.example.com
# australia.id-test.example.com
# Example of yoinking certs from caddy: # Example of yoinking certs from caddy:
# https://github.com/marcusramberg/nix-config/blob/e558914dd3705150511c5ef76278fc50bb4604f3/nixos/kanidm.nix#L3 # https://github.com/marcusramberg/nix-config/blob/e558914dd3705150511c5ef76278fc50bb4604f3/nixos/kanidm.nix#L3
# TODO: If possible, consider specifying the cert location here instead of the following kludge. # TODO: If possible, consider specifying the cert location here instead of the following kludge.
services.caddy.virtualHosts."${svcDomain}".extraConfig = '' services.caddy.virtualHosts."${svcDomain}".extraConfig = ''
reverse_proxy :8443 { reverse_proxy :8443 {
header_up Host {host}
header_up X-Real-IP {http.request.header.CF-Connecting-IP}
transport http { transport http {
tls_server_name ${svcDomain} tls_server_name ${svcDomain}
} }
} }
''; '';
# NOTE: Attempted kludge due to caddy generating (and therefore owning the certs) # NOTE: Cleanup old rules
# systemd.tmpfiles.rules = lib.filter(rule: ! (lib.strings.hasPrefix "C ${kanidmCertDir}" rule)) config.systemd.tmpfiles.rules;
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d ${certsDir} 0750 kanidm caddy -" "d ${kanidmCertDir} 0750 kanidm kanidm -"
"C ${certsDir}/cert.pem - kanidm - - ${caddyCertsDir}/${svcDomain}/${svcDomain}.crt"
"C ${certsDir}/key.key - kanidm - - ${caddyCertsDir}/${svcDomain}/${svcDomain}.key"
]; ];
systemd.services.kanidm = { # NOTE: Include automation for copying cert files on renewal.
after = [ "systemd-tmpfiles-setup.service" ]; # systemd.services.caddy.serviceConfig = {
requires = [ "caddy.service" "systemd-tmpfiles-setup.service" ]; # ExecStartPost = [
# "${certRenewalScript}/bin/copy-kanidm-cert-hook"
# ];
# ExecReload = [
# "${pkgs.caddy}/bin/caddy reload --config ${config.services.caddy.configFile}"
# "${certRenewalScript}/bin/copy-kanidm-cert-hook"
# ];
# };
systemd.services.${kanidmCertCopier} = {
description = "Copy Caddy certificates for Kanidm";
requires = [ "caddy.service" ];
after = [ "caddy.service" ];
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = "${certRenewalScript}";
}; };
};
# systemd.services.caddy.wantedBy = [ "multi-user.target" ];
# systemd.services.caddy.wants = [ kanidmCertCopier ];
systemd.services.caddy.reloadTriggers = [ kanidmCertCopier ];
systemd.timers.kanidm-cert-copier-daily = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "5min";
OnCalendar = "daily";
Unit = kanidmCertCopier;
};
};
# systemd.services.kanidm = {
# after = [ kanidmCertCopier ];
# requires = [ kanidmCertCopier ];
# };
users.users.kanidm.extraGroups = [ users.users.kanidm.extraGroups = [
"caddy" "caddy"
]; ];
@ -43,6 +101,7 @@ in
}; };
}; };
services.kanidm = { services.kanidm = {
package = pkgs.kanidmWithSecretProvisioning_1_7; package = pkgs.kanidmWithSecretProvisioning_1_7;
enableServer = true; enableServer = true;
@ -51,10 +110,13 @@ in
# domain, origin, tls_chain, tls_key # domain, origin, tls_chain, tls_key
domain = svcDomain; domain = svcDomain;
origin = "https://${svcDomain}"; origin = "https://${svcDomain}";
tls_chain = "${certsDir}/cert.pem"; tls_chain = "${kanidmCertDir}/cert.pem";
tls_key = "${certsDir}/key.key"; tls_key = "${kanidmCertDir}/key.pem";
# tls_chain = "${caddyCertStore}/${svcDomain}.crt";
# tls_key = "${caddyCertStore}/${svcDomain}.key";
# NOTE: Optional Settings # NOTE: Optional Settings
# TODO: Configure the rest of the binding properly, should be 363 and maybe 8443
ldapbindaddress = "127.0.0.1:3636"; # For Jellyfin LDAP integration. ldapbindaddress = "127.0.0.1:3636"; # For Jellyfin LDAP integration.
#trust_x_forwarded_for = true; #trust_x_forwarded_for = true;
@ -74,6 +136,7 @@ in
home_alias = "name"; home_alias = "name";
}; };
# TODO: Migrate the secrets from here to `nixos-secrets`
# NOTE: There are manual steps required as root to allow a user to set # NOTE: There are manual steps required as root to allow a user to set
# their own credentials, or to confiugre an account as posix. As-is this # their own credentials, or to confiugre an account as posix. As-is this
# module doesn't support provisioning a complete user /w credentials. # module doesn't support provisioning a complete user /w credentials.
@ -82,7 +145,9 @@ in
# https://kanidm.github.io/kanidm/stable/accounts/authentication_and_credentials.html#onboarding-a-new-person--resetting-credentials # https://kanidm.github.io/kanidm/stable/accounts/authentication_and_credentials.html#onboarding-a-new-person--resetting-credentials
provision = { provision = {
enable = true; enable = true;
autoRemove = false; autoRemove = true;
acceptInvalidCerts = true;
adminPasswordFile = config.sops.secrets."kanidm/admin-password".path; adminPasswordFile = config.sops.secrets."kanidm/admin-password".path;
idmAdminPasswordFile = config.sops.secrets."kanidm/idm-admin-password".path; idmAdminPasswordFile = config.sops.secrets."kanidm/idm-admin-password".path;
@ -98,6 +163,8 @@ in
"git.users" "git.users"
"git.admins" "git.admins"
"tv.users" "tv.users"
"immich.users"
"miniflux.users"
]; ];
}; };
}; };
@ -107,6 +174,8 @@ in
"git.admins" = {}; "git.admins" = {};
"tv.users" = {}; "tv.users" = {};
"tv.admins" = {}; "tv.admins" = {};
"immich.users" = {};
"miniflux.users" = {};
}; };
}; };
}; };

View file

@ -0,0 +1,79 @@
{ config, pkgs, ... }:
let
homelabDomain = config.networking.domain;
svcDomain = "feeds.${homelabDomain}";
svcPort = "8081"; # Prevent a Conflict
in
{
services.caddy.virtualHosts."${svcDomain}".extraConfig = ''
reverse_proxy :${svcPort}
'';
# NOTE: Ensure the user exists ahead of trying to give secret permissions to that user.
users.users.miniflux = {
isSystemUser = true;
group = "miniflux";
createHome = false;
};
users.groups.miniflux = {};
sops.secrets.miniflux_env = {
sopsFile = ../../secrets/miniflux_admin_credentials.env;
format = "dotenv";
mode = "0440";
owner = "miniflux";
group = "miniflux";
restartUnits = [ "miniflux.service" ];
};
sops.secrets."miniflux/oauth2_client_secret" = {
owner = "miniflux";
group = "kanidm";
mode = "0440";
restartUnits = [ "miniflux.service" "kanidm.service" ];
};
#services.kanidm.provision = {
#groups = {};
#systems.oauth2.miniflux = {
#displayName = "Miniflux Feed Reader";
#originUrl = "https://${fqdn}/callback";
#public = true; # enforces PKCE
#preferShortUsername = true;
#scopeMaps.pages_users = ["openid" "email" "profile"];
#claimMaps."${permissionsMap}".valuesByGroup.pages_admin = ["admin"];
#};
#};
# NOTE: Currently requires some web-interface configuration
services.miniflux = {
enable = true;
adminCredentialsFile = config.sops.secrets.miniflux_env.path;
config = {
BASE_URL = "https://${svcDomain}";
#CREATE_ADMIN = 0;
#DISABLE_LOCAL_AUTH = 1;
OAUTH2_PROVIDER = "oidc";
OAUTH2_CLIENT_ID = "miniflux";
OAUTH2_CLIENT_SECRET_FILE = config.sops.secrets."miniflux/oauth2_client_secret".path;
OAUTH2_REDIRECT_URL = "https://${svcDomain}/oauth2/oidc/callback";
OAUTH2_OIDC_DISCOVERY_ENDPOINT = "https://id.${homelabDomain}/oauth2/openid/miniflux";
#OAUTH2_USER_CREATION = 1;
CLEANUP_FREQUENCY = 48;
LISTEN_ADDR = "localhost:${svcPort}";
};
};
services.kanidm.provision.systems.oauth2.miniflux = {
displayName = "miniflux";
originUrl = "https://${svcDomain}/oauth2/oidc/callback";
originLanding = "https://${svcDomain}/";
basicSecretFile = config.sops.secrets."miniflux/oauth2_client_secret".path;
scopeMaps."miniflux.users" = [
"openid"
"email"
"profile"
"groups"
];
# WARNING: PKCE is currently not supported by gitea/forgejo,
# see https://github.com/go-gitea/gitea/issues/21376
allowInsecureClientDisablePkce = true;
preferShortUsername = true;
};
}

View file

@ -0,0 +1,14 @@
# hosts/lithium/services/monitoring
This is a Grafana/Prometheus Monitoring Stack.
Why? Basically for the sake of it.
## Diagram
```mermaid
````
## References
- https://gist.github.com/rickhull/895b0cb38fdd537c1078a858cf15d63e
- https://xeiaso.net/blog/prometheus-grafana-loki-nixos-2020-11-20/

View file

@ -0,0 +1,6 @@
{ ... }: {
imports = [
./grafana.nix
./prometheus.nix
];
}

View file

@ -0,0 +1,26 @@
{ inputs, config, pkgs, ... }:
let
homelabDomain = inputs.nixos-secrets.homelabDomain;
#svcDomain = "grafana.${config.networking.domain}";
svcDomain = "grafana.${homelabDomain}";
svcPort = config.services.grafana.settings.server.http_port;
in
{
services.caddy.virtualHosts."${svcDomain}".extraConfig = ''
reverse_proxy :${toString svcPort}
'';
services.grafana = {
enable = true;
settings = {
server = {
http_addr = "127.0.0.1";
http_port = 3001;
enforce_domain = true;
enable_gzip = true;
domain = svcDomain;
};
analytics.reporting_enabled = false; # NOTE: Disable Telemetry
};
};
}

View file

@ -0,0 +1,4 @@
auth_enabled: false
server:
http_listen_port: 3100

View file

@ -0,0 +1,15 @@
{ ... }:
{
services.loki = {
enable = true;
#configFile = "./loki-local-config.yaml";
# Nix Object representing the data that might otherwise be in a YAML config
# https://github.com/grafana/loki/blob/main/cmd/loki/loki-local-config.yaml
configuration = {
auth_enabled = false;
server = {
};
};
};
}

View file

@ -0,0 +1,36 @@
{ config, pkgs, ... }:
let
#svcDomain = "status.${config.networking.domain}";
svcPort = config.services.prometheus.exporters.node.port;
in
{
#services.caddy.virtualHosts."${svcDomain}".extraConfig = ''
#reverse_proxy :${svcPort}
#'';
services.prometheus = {
enable = true;
port = 9090;
#globalConfig.scrape_interval = "10s"; # "1m"
exporters = {
# Export data about this host
node = {
enable = true;
enabledCollectors = [ "systemd" ];
port = 9091;
};
};
# Read data from the export
scrapeConfigs = [
{
job_name = "node-lithium";
static_configs = [{
targets = [ "localhost:${toString svcPort}" ];
}];
}
];
};
}

View file

@ -0,0 +1,79 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.kanidm;
authDomain = "auth.${config.networking.domain}";
certsDir = config.security.acme.certs."${authDomain}".directory;
in
{
# TODO: Pull in the appropriate sops-nix secrets and get this baby rolling.
# https://github.com/search?q=language%3ANix+services.kanidm&type=code
services.kanidm = {
# NOTE: Pin a specific kanidm version, we don't want issues from auto-updating.
package = pkgs.kanidm_1_6;
enableServer = true;
# TODO: Initial kanidm setup.
# I sort of want users to be able to create their own accounts and what I
# don't want is for any of their account information to be leaked here as
# it can be used for remote logins.
# So kanidm accounts aside from the administration will be "impure".
# I vastly prefer people being able to set their own credentials:
# https://kanidm.github.io/kanidm/stable/accounts/authentication_and_credentials.html#onboarding-a-new-person--resetting-credentials
provision = {
enable = true;
autoRemove = false;
# TODO: Add secrets from `sops-nix`.
adminPasswordFile = "TODO!SETME";
idmAdminPasswordFile = "TODO!SETME";
persons = {
# https://kanidm.github.io/kanidm/stable/accounts/authentication_and_credentials.html#resetting-person-account-credentials
# Needs to be a member of idm_people_admins and idm_high_privilege to prevent idm_service_desk from tampering.
zenware = {
displayName = "zenware";
legalName = "zenware";
mailAddresses = [ "zenware@${config.networking.domain} "];
groups = [
"idm_high_privilege"
"git.users"
"git.admins"
];
};
# TODO: Make an idm_service_desk account.
};
groups = {
# This group is `git` because it could be forgejo, gitea, etc.
"git.users" = {};
"git.admins" = {};
};
systems.oauth2 = {
forgejo = {
displayName = "forgejo";
originUrl = "TODO!SETME";
originLanding = "TODO!SETME";
basicSecretFile = "TODO!SETME";
scopeMaps."git.users" = [
"openid"
"email"
"profile"
];
# WARNING: PKCE is currently not supported by gitea/forgejo,
# see https://github.com/go-gitea/gitea/issues/21376
allowInsecureClientDisablePkce = true;
preferShortUsername = true;
claimMaps.groups = {
joinType = "array";
valuesByGroup."git.admins" = [ "admin" ];
};
};
};
};
#enableClient = false;
clientSettings = {
uri = "https://${authDomain}";
verify_hostnames = true;
verify_ca = true;
};
};
}

View file

@ -0,0 +1,2 @@
[/Script/Pal.PalGameWorldSettings]
OptionSettings=(Difficulty=None,RandomizerType=None,RandomizerSeed="",bIsRandomizerPalLevelRandom=False,DayTimeSpeedRate=1.000000,NightTimeSpeedRate=1.000000,ExpRate=1.000000,PalCaptureRate=1.100000,PalSpawnNumRate=1.100000,PalDamageRateAttack=1.000000,PalDamageRateDefense=1.000000,PlayerDamageRateAttack=1.000000,PlayerDamageRateDefense=1.000000,PlayerStomachDecreaceRate=0.800000,PlayerStaminaDecreaceRate=0.900000,PlayerAutoHPRegeneRate=1.100000,PlayerAutoHpRegeneRateInSleep=1.200000,PalStomachDecreaceRate=0.900000,PalStaminaDecreaceRate=1.000000,PalAutoHPRegeneRate=1.000000,PalAutoHpRegeneRateInSleep=1.000000,BuildObjectHpRate=1.000000,BuildObjectDamageRate=1.000000,BuildObjectDeteriorationDamageRate=1.000000,CollectionDropRate=1.000000,CollectionObjectHpRate=1.000000,CollectionObjectRespawnSpeedRate=1.000000,EnemyDropItemRate=1.000000,DeathPenalty=1,bEnablePlayerToPlayerDamage=False,bEnableFriendlyFire=False,bEnableInvaderEnemy=True,bActiveUNKO=False,bEnableAimAssistPad=True,bEnableAimAssistKeyboard=True,DropItemMaxNum=3000,DropItemMaxNum_UNKO=100,BaseCampMaxNum=128,BaseCampWorkerMaxNum=15,DropItemAliveMaxHours=1.000000,bAutoResetGuildNoOnlinePlayers=False,AutoResetGuildTimeNoOnlinePlayers=72.000000,GuildPlayerMaxNum=20,BaseCampMaxNumInGuild=4,PalEggDefaultHatchingTime=4.500000,WorkSpeedRate=1.100000,AutoSaveSpan=30.000000,bIsMultiplay=False,bIsPvP=False,bHardcore=False,bPalLost=False,bCharacterRecreateInHardcore=False,bCanPickupOtherGuildDeathPenaltyDrop=False,bEnableNonLoginPenalty=True,bEnableFastTravel=True,bIsStartLocationSelectByMap=True,bExistPlayerAfterLogout=False,bEnableDefenseOtherGuildPlayer=False,bInvisibleOtherGuildBaseCampAreaFX=False,bBuildAreaLimit=False,ItemWeightRate=1.000000,CoopPlayerMaxNum=4,ServerPlayerMaxNum=32,ServerName="GameNight Pals",ServerDescription="",AdminPassword="PenQueen",ServerPassword="penking",PublicPort=8211,PublicIP="",RCONEnabled=False,RCONPort=25575,Region="",bUseAuth=True,BanListURL="https://api.palworldgame.com/api/banlist.txt",RESTAPIEnabled=False,RESTAPIPort=8212,bShowPlayerList=False,ChatPostLimitPerMinute=30,CrossplayPlatforms=(Steam,Xbox,PS5,Mac),bIsUseBackupSaveData=True,LogFormatType=Text,SupplyDropSpan=90,EnablePredatorBossPal=True,MaxBuildingLimitNum=0,ServerReplicatePawnCullDistance=15000.000000,bAllowGlobalPalboxExport=True,bAllowGlobalPalboxImport=False,EquipmentDurabilityDamageRate=1.000000,ItemContainerForceMarkDirtyInterval=1.000000)

View file

@ -0,0 +1,53 @@
{ config, pkgs, lib, ... }:
let
palworldSettings = builtins.readFile ./PalWorldSettings.ini;
in
{
# NOTE: Partly inspired by: https://github.com/pocketpairjp/palworld-dedicated-server-docker
networking.firewall.allowedUDPPorts = [ 8211 ];
users.users.palworld = {
isSystemUser = true;
createHome = true;
home = "/home/palworld";
homeMode = "750";
group = "palworld";
extraGroups = [ "steamcmd" ];
};
users.groups.palworld = {};
systemd.services.palworld = {
enable = true;
description = "PalWorld Dedicated Server";
#after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStartPre = ''
${pkgs.steamcmd}/bin/steamcmd +login anonymous \
+app_update 2394010 validate +quit
'';
ExecStart = ''
${pkgs.steam-run}/bin/steam-run \
.steam/steam/Steamapps/common/PalServer/PalServer.sh -publiclobby \
-useperfthreads -NoAsyncLoadingThread -UseMultithreadForDS
'';
WorkingDirectory = "/home/palworld";
Restart = "always";
RuntimeMaxSec = "1d"; # NOTE: This thing has memory leaks, restart to save our selves.
User = "palworld";
};
};
# NOTE: Config is stashed at the following directory.
# /home/palworld/.steam/steam/Steamapps/common/PalServer/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini
# TODO: There are benefits to including the meat of the configuration inside the 'nix' file.
# Namely that it will result in actually updating the config when I rebuild.
environment.etc."palworld/PalWorldSettings.ini" = {
target = "/home/palworld/.steam/steam/Steamapps/common/PalServer/Pal/Saved/Config/LinuxServer/PalWorldSettings.ini";
text = palworldSettings;
mode = "0644";
user = "palworld";
group = "palworld";
};
}

View file

@ -0,0 +1,39 @@
{ config, ... }:
{
services.smartd = {
enable = true;
devices = [
{
device = "/dev/disk/by-id/ata-CT500MX500SSD1_2206E607D6AA";
}
{
device = "/dev/disk/by-id/ata-CT500MX500SSD1_2206E607D728";
}
{
device = "/dev/disk/by-id/ata-ST16000NM001G-2KK103_ZL2B73HT";
}
{
device = "/dev/disk/by-id/ata-ST16000NM001G-2KK103_ZL2PSELL";
}
{
device = "/dev/disk/by-id/ata-ST16000NM001G-2KK103_ZL2B4RSM";
}
{
device = "/dev/disk/by-id/ata-ST16000NM001G-2KK103_ZL23XYMM";
}
{
device = "/dev/disk/by-id/nvme-Samsung_SSD_960_EVO_500GB_S3X4NB0K244331X";
}
{
device = "/dev/disk/by-id/nvme-Samsung_SSD_960_EVO_500GB_S3X4NB0K244303V";
}
];
};
services.prometheus.exporters.smartctl = {
enable = config.services.smartd.enable;
openFirewall = config.services.smartd.enable;
# https://github.com/prometheus-community/smartctl_exporter?tab=readme-ov-file#why-is-root-required-cant-i-add-a-user-to-the-disk-group
user = "root";
};
}

View file

@ -13,6 +13,8 @@ in
enable = true; enable = true;
# NOTE: NixOS Attributes here resolve into these ENV vars: # NOTE: NixOS Attributes here resolve into these ENV vars:
# https://github.com/louislam/uptime-kuma/wiki/Environment-Variables # https://github.com/louislam/uptime-kuma/wiki/Environment-Variables
# settings = {}; settings = {
PORT = "4000";
};
}; };
} }

15
hosts/lithium/sops.nix Normal file
View file

@ -0,0 +1,15 @@
{ inputs, ... }:
let
secretsPath = builtins.toString inputs.nixos-secrets;
in
{
#imports = [ inputs.sops-nix.nixosModules.sops ];
sops = {
#defaultSopsFile = "${secretsPath}/${config.hostname}/secrets.yaml";
#defaultSopsFile = "${secretsPath}/global/secrets.yaml";
# TODO: Make this test the hostname.
#defaultSopsFile = "${secretsPath}/lithium/secrets/common.yaml";
defaultSopsFile = "${secretsPath}/lithium/secrets.yaml";
};
}

View file

@ -1,6 +0,0 @@
{}:
{
# Default to systemd-boot
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
}

View file

@ -1,10 +1,9 @@
{ inputs, ... }: { inputs, ... }:
{ {
imports = [ imports = [
./boot.nix
../../modules/nixos/base.nix ../../modules/nixos/base.nix
../../modules/nixos/audio.nix ../../modules/nixos/audio.nix
../../modules/nixos/desktop ../../modules/nixos/desktop.nix
# https://github.com/NixOS/nixos-hardware/blob/master/README.md#using-nix-flakes-support # https://github.com/NixOS/nixos-hardware/blob/master/README.md#using-nix-flakes-support
inputs.nixos-hardware.nixosModules.gpd-pocket-3 inputs.nixos-hardware.nixosModules.gpd-pocket-3
# override from nixos-hardware # override from nixos-hardware

View file

@ -1,20 +0,0 @@
# titanium
This is my primary workstation / gaming pc.
It will generally be the most out of sync with the repo, as there will be a lot
of software I experiment with, which I simply forget to commit here. Everything
of importance will find it's way to this repo.
## Non-Deterministic Post-Install Steps
Rearrange Monitors in Gnome Display Settings
Use a fido2 key (YubiKey) to decrypt luks
```bash
sudo -E -s systemd-cryptenroll --fido2-device=auto /dev/disk/by-partlabel/disk-main-luks
```
## Installing Remotely
```bash
nix run github:nix-community/nixos-anywhere -- --flake .#titanium <ssh-addr>
```

View file

@ -6,18 +6,18 @@
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
sbctl # Secure-Boot sbctl # Secure-Boot
helix nil # nice for editing '.nix' helix nil # nice for editing '.nix'
(discord.override { withVencord = true; }) discord
signal-desktop signal-desktop
obs-studio obs-studio
halloy # IRC
gimp3
nyxt # browser
]; ];
services.udev.extraRules = ''
SUBSYSTEM=="usb", ATTRS{idVendor}=="14ed", ATTRS{idProduct}=="1012", MODE="0666", GROUP="audio"
'';
# Hardware Specific programs... # Hardware Specific programs...
#programs.ryzen-monitor-ng.enable = true; #programs.ryzen-monitor-ng.enable = true;
#programs.rog-control-center.enable = true; #programs.rog-control-center.enable = true;
services.openssh.enable = true; services.openssh.enable = true;
services.tailscale.enable = true; services.tailscale.enable = true;
networking.firewall.trustedInterfaces = [ "tailscale0" ]; networking.firewall.trustedInterfaces = [ "tailscale0" ];
system.stateVersion = "25.11"; system.stateVersion = "25.05";
} }

View file

@ -1,41 +1,17 @@
{ inputs, pkgs, ... }: { inputs, ... }:
let
nixpkgs = inputs.nixpkgs;
in
{ {
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
nixpkgs.overlays = (import (../../overlays) {inherit nixpkgs;});
imports = [ imports = [
../../modules/nixos/base.nix ../../modules/nixos/base.nix
../../modules/nixos/audio.nix ../../modules/nixos/audio.nix
../../modules/nixos/desktop ../../modules/nixos/desktop.nix
../../modules/nixos/gaming.nix ../../modules/nixos/gaming.nix
inputs.nixos-hardware.nixosModules.asus-rog-strix-x570e inputs.nixos-hardware.nixosModules.asus-rog-strix-x570e
./hardware.nix ./hardware.nix
./configuration.nix ./configuration.nix
./nvidia.nix ./nvidia.nix
inputs.lanzaboote.nixosModules.lanzaboote
./secure-boot.nix ./secure-boot.nix
inputs.disko.nixosModules.disko #inputs.disko.nixosModules.disko
./disko.nix #./disko.nix
./game-emulation.nix
#./meetings.nix
]; ];
zw.gaming.enable = true;
stylix = {
#enable = true;
# catppuccin-mocha
base16Scheme = "${pkgs.base16-schemes}/share/themes/catppuccin-mocha.yaml";
# image = ./path.png; polarity = "dark"; # /etc/stylix/palette.html
# TODO: Add Atkinson Hyperlegible Next, Mono, and also a good Serif font.
# https://search.nixos.org/packages?channel=unstable&show=atkinson-hyperlegible-next&query=atkinson
# fonts = {
# serif = {};
# sansSerif = {};
# monospace = {};
# emoji = {};
# };
};
} }

View file

@ -1,59 +0,0 @@
{ config, lib, pkgs, ... }:
let
retroarchWithCores = (
pkgs.retroarch.withCores (
cores: with cores; [
# Multi-Emulators
mame # Atari / Nintendo / Sega / etc.
# Sega
genesis-plus-gx # Sega Genesis
# Nintendo
mesen # NES
bsnes # Super Nintendo
mupen64plus # Nintendo 64 - Maybe simple64 some day.
dolphin # GameCube
mgba # GameBoy / Color / Advance
#melonds # Nintendo DS
#citra # Nintendo 3DS
# Sony
swanstation #duckstation # PlayStation
beetle-psx-hw
pcsx2 # PlayStation 2 -- Is actually "LRPS2"
#rpcs3 # PlayStation 3
ppsspp # PlayStation Portable
# Commodore
vice-x64 # C64
]
)
);
in
{
environment.systemPackages = [
retroarchWithCores
#pkgs.retroarch-full
#pkgs.emulationstation-de
pkgs.gnome-bluetooth
];
hardware.xone.enable = true; # Xbox Controller Driver
hardware.xpadneo.enable = true; # Xbox Controller Driver
hardware.enableAllFirmware = true;
hardware.bluetooth = {
enable = true;
powerOnBoot = true;
settings = {
General = {
Experimental = true;
FastConnectable = true;
};
Policy = {
AutoEnable = true;
};
};
};
}

View file

@ -8,44 +8,41 @@
[ (modulesPath + "/installer/scan/not-detected.nix") [ (modulesPath + "/installer/scan/not-detected.nix")
]; ];
boot.initrd.systemd.enable = true; boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
#boot.initrd.luks.devices.FOO.crypttabExtraOpts = ["fido2-device=auto"];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" "sr_mod" ];
boot.initrd.kernelModules = [ ]; boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ]; boot.kernelModules = [ "kvm-amd" ];
# boot.blacklistedKernelModules = [ "nouveau" ];
boot.extraModulePackages = [ ]; boot.extraModulePackages = [ ];
fileSystems."/" = fileSystems."/" =
{ #device = "/dev/disk/by-uuid/bac9b4de-d201-4008-9e97-3954417aab65"; { device = "/dev/disk/by-uuid/0b3de117-c34f-4cc6-81db-5b84ea46cd51";
fsType = "btrfs"; fsType = "btrfs";
options = [ "subvol=root" ]; options = [ "subvol=root" ];
}; };
#boot.initrd.luks.devices."crypted".device = "/dev/disk/by-uuid/16b8ccb6-0102-4348-bb1b-d8d68bfb4d23"; boot.initrd.luks.devices."crypted".device = "/dev/disk/by-uuid/0ccc4028-c27e-4259-ade9-a2b2081722cb";
fileSystems."/nix" =
{ #device = "/dev/disk/by-uuid/bac9b4de-d201-4008-9e97-3954417aab65";
fsType = "btrfs";
options = [ "subvol=nix" ];
};
fileSystems."/.swapvol" = fileSystems."/.swapvol" =
{ #device = "/dev/disk/by-uuid/bac9b4de-d201-4008-9e97-3954417aab65"; { device = "/dev/disk/by-uuid/0b3de117-c34f-4cc6-81db-5b84ea46cd51";
fsType = "btrfs"; fsType = "btrfs";
options = [ "subvol=swap" ]; options = [ "subvol=swap" ];
}; };
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/219D-4579";
fsType = "vfat";
options = [ "fmask=0077" "dmask=0077" ];
};
fileSystems."/home" = fileSystems."/home" =
{ #device = "/dev/disk/by-uuid/bac9b4de-d201-4008-9e97-3954417aab65"; { device = "/dev/disk/by-uuid/0b3de117-c34f-4cc6-81db-5b84ea46cd51";
fsType = "btrfs"; fsType = "btrfs";
options = [ "subvol=home" ]; options = [ "subvol=home" ];
}; };
fileSystems."/boot" = fileSystems."/nix" =
{ #device = "/dev/disk/by-uuid/E076-75D6"; { device = "/dev/disk/by-uuid/0b3de117-c34f-4cc6-81db-5b84ea46cd51";
fsType = "vfat"; fsType = "btrfs";
options = [ "fmask=0077" "dmask=0077" ]; options = [ "subvol=nix" ];
}; };
swapDevices = [ ]; swapDevices = [ ];
@ -57,7 +54,7 @@
networking.useDHCP = lib.mkDefault true; networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp4s0.useDHCP = lib.mkDefault true; # networking.interfaces.enp4s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp5s0.useDHCP = lib.mkDefault true; # networking.interfaces.enp5s0.useDHCP = lib.mkDefault true;
# networking.interfaces.tailscale0.useDHCP = lib.mkDefault true; # networking.interfaces.wlp3s0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;

View file

@ -1,4 +0,0 @@
{ ... }:
{
programs.zoom-us.enable = true;
}

View file

@ -14,12 +14,9 @@
# https://github.com/NixOS/nixpkgs/blob/master/pkgs/os-specific/linux/nvidia-x11/generic.nix#L65 # https://github.com/NixOS/nixpkgs/blob/master/pkgs/os-specific/linux/nvidia-x11/generic.nix#L65
nixpkgs.config.nvidia.acceptLicense = true; nixpkgs.config.nvidia.acceptLicense = true;
hardware.nvidia = { hardware.nvidia = {
# TODO: Consider legacy drivers.
# https://discourse.nixos.org/t/cant-use-nvidia-offload-mode/27791/8
package = config.boot.kernelPackages.nvidiaPackages.latest; package = config.boot.kernelPackages.nvidiaPackages.latest;
modesetting.enable = true; modesetting.enable = true;
# Open Source Drivers: https://github.com/NVIDIA/open-gpu-kernel-modules#compatible-gpus open = true;
open = false;
nvidiaSettings = true; nvidiaSettings = true;
powerManagement.enable = false; powerManagement.enable = false;
powerManagement.finegrained = false; powerManagement.finegrained = false;

View file

@ -1,4 +1,5 @@
{ pkgs, lib, ... }: { { pkgs, lib, inputs, ... }: {
imports = with inputs; [ lanzaboote.nixosModules.lanzaboote ];
environment.systemPackages = [ pkgs.sbctl ]; environment.systemPackages = [ pkgs.sbctl ];
boot.loader.systemd-boot.enable = lib.mkForce false; boot.loader.systemd-boot.enable = lib.mkForce false;
boot.lanzaboote.enable = true; boot.lanzaboote.enable = true;

View file

@ -1,113 +1,48 @@
{ nixpkgs, home-manager, inputs, ... }:
{ {
nixpkgs, # It's not really that I care about whether a system is a desktop system or
home-manager, # a server system, but moreso that I care about whether a system is headless or not.
inputs, # I also care about things like if it's darwin, or wsl.
... mkSystem = {
}:
let
allOverlays = import (../overlays) { inherit nixpkgs; };
getPkgs =
system:
import nixpkgs {
inherit system;
overlays = allOverlays;
};
in
{
mkSystem =
{
hostname, hostname,
system ? "x86_64-linux", system ? "x86_64-linux",
users ? [], users ? [],
extraModules ? [ ], extraModules ? []
homeUsers ? { },
extraSpecialArgs ? { },
}: }:
let let
pkgs_with_overlays = import nixpkgs { hostModule = import ../hosts/${hostname} { inherit inputs; };
inherit system; userModules = map (name:
overlays = allOverlays;
};
hostModule = import ../hosts/${hostname} {
inherit inputs;
pkgs = pkgs_with_overlays;
};
userModules = map (
name:
import ../users/${name} { import ../users/${name} {
pkgs = pkgs_with_overlays; pkgs = nixpkgs.legacyPackages.${system};
lib = nixpkgs.lib; lib = nixpkgs.lib;
} }
) users; ) users;
formattedHomeUsers = nixpkgs.lib.mapAttrs (username: moduleList: { homeUserNames = builtins.filter (name:
imports = moduleList; builtins.pathExists ../users/${name}/home.nix
}) homeUsers; ) users;
homeUsers = nixpkgs.lib.listToAttrs (map (name: {
name = name;
value = import ../users/${name}/home.nix {
username = name;
pkgs = nixpkgs.legacyPackages.${system};
lib = nixpkgs.lib;
};
}) homeUserNames);
in in
nixpkgs.lib.nixosSystem { nixpkgs.lib.nixosSystem {
inherit system; inherit system;
modules = [ specialArgs = { inherit inputs hostname; };
hostModule modules = [ hostModule ]
]
++ userModules ++ userModules
++ extraModules ++ extraModules
++ ( ++ (if homeUserNames != [] then [
if homeUsers != { } then
[
home-manager.nixosModules.home-manager home-manager.nixosModules.home-manager
{ {
#home-manager.useGlobalPkgs = true; # NOTE: Incompatible with nixpkgs.{config,overlays}
home-manager.useUserPackages = true;
home-manager.backupFileExtension = "hm-bak"; home-manager.backupFileExtension = "hm-bak";
home-manager.users = homeUsers;
# Directly inject the module lists? (isn't this the problem?)
home-manager.users = formattedHomeUsers;
home-manager.extraSpecialArgs = { inherit inputs; };
} }
] ] else []);
else };
[ ]
);
specialArgs = {
inherit inputs hostname;
}
// extraSpecialArgs;
};
getUserHomeModule =
username: pkgs: inputs:
import ../users/${username}/home.nix {
inherit username pkgs inputs;
lib = nixpkgs.lib;
};
/**
This function returns an attribute set { module, config }.
*/
mkHome =
{
username,
system ? "x86_64-linux",
extraModules ? [ ],
}:
let
pkgs_with_overlays = getPkgs system;
moduleList = [
(import ../users/${username}/home.nix {
inherit inputs username;
pkgs = pkgs_with_overlays;
lib = nixpkgs.lib;
})
]
++ extraModules;
in
{
module = moduleList;
config = home-manager.lib.homeManagerConfiguration {
pkgs = pkgs_with_overlays;
modules = moduleList;
};
};
mkHomeConfigs = userProfiles: nixpkgs.lib.mapAttrs (username: profile: profile.config) userProfiles;
} }

37
localnotes.md Normal file
View file

@ -0,0 +1,37 @@
Run disko first, then install.
```bash
sudo nix --experimental-features "nix-command flakes" run github:nix-community/disko -- --mode disko /path/to/disko.nix
sudo nixos-install --root /mnt --no-root-passwd --flake .#titanium
```
need to comment out lanzaboote stuff to get it working... :(
then re-enable it after the system is online again.
There is clearly some kind of quirk about boot drives.
We're going to try again, and double-check reading through config this time.
I've already partitioned the drive(s) with disko, but I don't have a reason not to do it again.
There is some reason why if I import disko into my nixos config it creates a conflict between the name of the drives.
```bash
/dev/crypt/mapper
# and a full uuid path
```
I figured it out, after you have the disk paths and stuff setup with `disko` you no longer need to maintain them in `hardware-configuration.nix`
Which.. is actually quite nice to be honest.
## Some Ideas to consider
- https://github.com/Sveske-Juice/declarative-jellyfin
- https://docs.clan.lol
- fido2 luks cryptenroll
- https://discourse.nixos.org/t/using-fido2-luks-with-yubikey-pin/15484
- https://github.com/nix-community/nixhelm/

24
localsecrets.yaml Normal file
View file

@ -0,0 +1,24 @@
cloudflare_api_token: ENC[AES256_GCM,data:IqZ2LkBUyboO9494G3LPFn8prx48SDFluz6u4KDzQfQxTyqiVkORRQ==,iv:5iSnHMFHOEXDxt5/fSQ/83OzG2iU6LQ9OCnah5Qnj1A=,tag:zBQvE9KvW195mLtwjFKWqQ==,type:str]
cloudflare:
api_token: ENC[AES256_GCM,data:ZkiVQeVFHQ==,iv:402yjlUkamfzgAqMQCChR+ui4nsA625GidVIwCOc8bM=,tag:xErz+vXV8en2c3WdHfqUsg==,type:str]
email: ENC[AES256_GCM,data:AnI4i1ar70FvyLzVqiElJTbZ6V12Tw==,iv:VYoK2Q2XmleP2SV9Axy8JPTrPeLjnJBkhaXVuCWGUtA=,tag:uxB9JQJWUwoIQ1QtpbvT8Q==,type:str]
env:
domain_name: ENC[AES256_GCM,data:sGpnauqZ2LSY+Hf34eA=,iv:V1OOAo9zoEAkXDLL2la6DF+FoCj4Lzqg88dbHHSqcXs=,tag:C+0yaNo/TwAtym7NSxNnyA==,type:str]
yubico:
id: ENC[AES256_GCM,data:n80pw3A=,iv:pS/T3JGbVQkmYJ6EnwcdITe9J8knVBJiEn8njxPzAcM=,tag:gwg5pl6ugfBH4qwpcr1cjA==,type:int]
secret_key: ENC[AES256_GCM,data:kax8+XHJwpqFtRdKpkRR5sFhsKawlT7Ru92Umw==,iv:wSs4wdAkHyacKUmIxGfn8KDv1A8yW6E+0eBuXnOzhMA=,tag:Y6Om1OShD6s3dBALseN/qQ==,type:str]
tailscale:
auth_key: ENC[AES256_GCM,data:lIT80TrYfqMa8k6wsxC5BV6sBg6PCw5+AJaUtMhfJuXTIOz48PZKpCFq5z76TenpGpYT1vdqfCGd8Z1UfA==,iv:5LCWydaBX0ap5VScmjSm/QLtkeYy7u0ImVXoaY8URZs=,tag:dZV4s026eS4ME68ET+JxGw==,type:str]
vrising_server_name: ENC[AES256_GCM,data:guRSn4xsGFJ0,iv:spCO2OuFr2JmFL+qboq2dwrpxb7KufxtscZI8/3r9SQ=,tag:qbFV4IIiFmEQW72So6pm7A==,type:str]
valheim_server_name: ENC[AES256_GCM,data:PJV694YqSnOkBw==,iv:yIjgunzkiVdO1pUuAOtKcRI2zhBSb2sfDfM5WrPsVa8=,tag:I18ugPusdBoADlSxYzIYNw==,type:str]
kanidm:
bind_password: ENC[AES256_GCM,data:l9nP,iv:Qtk6hGj+RnLfsNBRp4QHpCo/Oxq+EdVu62xmClPbPNk=,tag:7PRHrMT5kJdtqV2xXZPzZg==,type:int]
tailscale:
auth_key: ENC[AES256_GCM,data:RFno,iv:wG8XyXMQt/BLjp3jW0j509cDmF/V+UqGAFCjJ3twQ3k=,tag:5z1krKBPDWlZGvHleS4rPA==,type:int]
gitea:
secret_key: ENC[AES256_GCM,data:u5EV,iv:UdQaFFC2Rh0aDbqzHdowhTmxrKbOhWEmGmUEjkOUrg4=,tag:MfB6Jbc3IPc8h4VAd4urEg==,type:int]
internal_token: ENC[AES256_GCM,data:K1tX,iv:MhM1IURTet1e919eI9NpitEdfG8WRB33E4GsCmcSDro=,tag:146dEI1/Bj+w5Ul+Sbt9bw==,type:int]
db_password: ENC[AES256_GCM,data:muy+,iv:e0drAD03om5xAmLOUa4IxTFHQ5U1xeK+fgDOhATnMU8=,tag:JVorF+C4+ENhFZ7/0QSwrQ==,type:int]
oauth_client_secret: ENC[AES256_GCM,data:hVM95DQxfS9Af/z+C5AgjpcoHBs=,iv:pq4lXNYXzFIaNUWhmCV5EwD4icjziCWVqpSuIg1FzZQ=,tag:z/lxukCKwgriNLqqeHDFuQ==,type:str]
jellyfin:
oauth_client_secret: ENC[AES256_GCM,data:IQI6FfOga+3bmf+B+/vpGao=,iv:lC/zbax5mgzQBRNIIbeviWBXtMKSegKFJlHdY+bCSyo=,tag:g0BUqfHZrJB2gJUIjLjnuA==,type:str]

View file

@ -1,23 +1,18 @@
{ config, pkgs, lib, ... }: { config, pkgs, lib, ... }:
{ {
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
# TODO: Consider adding a randomized delay.
nix.gc = {
automatic = true;
dates = "weekly";
options = "--delete-older-than 30d";
};
nix.settings = { nix.settings = {
auto-optimise-store = true;
experimental-features = [ "nix-command" "flakes" ]; experimental-features = [ "nix-command" "flakes" ];
}; };
# https://datatracker.ietf.org/doc/html/rfc8375 # Default to systemd-boot
networking.domain = lib.mkDefault "home.arpa"; boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# TODO: Consider enabling automatic-timezoned on laptops that move between TZs # https://datatracker.ietf.org/doc/html/rfc8375
time.timeZone = lib.mkDefault "America/Chicago"; networking.domain = "home.arpa";
services.automatic-timezoned.enable = lib.mkDefault false;
time.timeZone = "America/Chicago";
i18n.defaultLocale = "en_US.UTF-8"; i18n.defaultLocale = "en_US.UTF-8";
i18n.extraLocaleSettings = { i18n.extraLocaleSettings = {
@ -78,39 +73,38 @@
]; ];
# Render colors # Render colors
# TODO: Figure out how to represent those termcap sequences properly. # TODO: Figure out how to represent those termcap sequences properly.
LESS_TERMCAP_mb="\E[1;31m"; # begin bold #LESS_TERMCAP_mb=$'\E[1;31m' # begin bold
LESS_TERMCAP_md="\E[1;36m"; # begin blink #LESS_TERMCAP_md=$'\E[1;36m' # begin blink
LESS_TERMCAP_me="\E[0m"; # reset bold/blink #LESS_TERMCAP_me=$'\E[0m' # reset bold/blink
LESS_TERMCAP_so="\E[01;44;33m"; # begin reverse video #LESS_TERMCAP_so=$'\E[01;44;33m' # begin reverse video
LESS_TERMCAP_se="\E[0m"; # reset reverse video #LESS_TERMCAP_se=$'\E[0m' # reset reverse video
LESS_TERMCAP_us="\E[1;32m"; # begin underline #LESS_TERMCAP_us=$'\E[1;32m' # begin underline
LESS_TERMCAP_ue="\E[0m"; # reset underline #LESS_TERMCAP_ue=$'\E[0m' # reset underline
}; };
}; };
git.enable = true; git.enable = true;
htop.enable = true; htop.enable = true;
command-not-found.enable = false;
bat.enable = true; bat.enable = true;
bandwhich.enable = true; bandwhich.enable = true;
command-not-found.enable = false;
#nix-index.enable = true;
nano.enable = false; nano.enable = false;
neovim = { neovim = {
enable = true; enable = true;
defaultEditor = true; defaultEditor = true;
viAlias = true; viAlias = true;
vimAlias = true; vimAlias = true;
withRuby = true;
withPython3 = true;
withNodeJs = true;
#configure = {};
}; };
}; };
services.openssh.enable = lib.mkDefault false; # Services running on all machines
# services.openssh = { services.avahi.enable = true; # zeroconf/mDNS(.local)
# enable = true;
# settings = {
# PasswordAuthentication = false;
# PermitRootLogin = "no";
# };
# };
} }

View file

@ -1,22 +1,11 @@
{ pkgs, ... }: { pkgs, ... }:
{ {
imports = [
./calibre.nix
../fonts.nix
];
# TODO: Add options for enabling/switching between different Desktop Environments.
# options = {};
# NOTE: Calibre is enabled this way because it also needs udisks2 for e-readers
# Ideally I move it somewhere else anyway.
zw.calibre.enable = true;
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
yubikey-personalization yubikey-personalization
xdg-desktop-portal-gtk xdg-desktop-portal-gtk
xdg-desktop-portal-hyprland xdg-desktop-portal-hyprland
xwayland xwayland
#rofi-wayland
rofi rofi
waybar waybar
hyprpaper hyprpaper
@ -43,5 +32,10 @@
}; };
# screen sharing /w hyp # screen sharing /w hyp
services.dbus.enable = true; services.dbus.enable = true;
services.avahi.enable = true; # zeroconf/mDNS(.local)
fonts.packages = with pkgs; [
nerd-fonts.fira-code
nerd-fonts.iosevka
atkinson-hyperlegible
];
} }

View file

@ -1,21 +0,0 @@
{ config, lib, pkgs, ... }:
{
imports = [];
options = {
zw.calibre = {
enable = lib.mkEnableOption "Enable Calibre";
};
};
config = {
# NOTE: Without unrar support we can't open ".cbr" files.
environment.systemPackages = with pkgs; [
calibre
];
services.udisks2.enable = true; # Required for eReader Support
};
# NOTE: Consider adding https://github.com/nydragon/calibre-plugins
# especially for DeDRM
}

View file

@ -1,40 +0,0 @@
{ config, lib, pkgs, inputs, ... }:
{
# Opinionated Niri Setup - https://yalter.github.io/niri/Important-Software.html
# Consider: https://github.com/sodiboo/niri-flake
# NOTE: Rather than individual components, I'm going to start with a complete desktop shell if possible.
# According to the docs there's a few options: https://yalter.github.io/niri/Getting-Started.html#desktop-environments
# LXQt, many parts of XFCE, COSMIC + `cosmic-ext-extra-sessions`
# And what I actually want to try out is one of DankMaterialShell or Noctalia
programs.niri.enable = true;
environment.systemPackages = with pkgs; [
inputs.noctalia.packages.${system}.default
xwayland-satellite
fuzzel
kitty
fastfetch
];
services.displayManager.sessionPackages = [ pkgs.niri ];
programs.niri.package = pkgs.niri;
# Notification Daemon
#services.mako.enable = true;
#services.mako.settings.default-timeout = 3000;
# Portal - https://wiki.archlinux.org/title/XDG_Desktop_Portal#List_of_backends_and_interfaces
# Authentication Agent (polkit)
#security.polkit.enable = lib.mkDefault true;
# Xwayland
# https://github.com/Supreeeme/xwayland-satellite
#programs.xwayland.enable = lib.mkDefault true;
# Screencasting - https://yalter.github.io/niri/Screencasting.html
# Needs D-Bus, pipewire, `xdg-desktop-portal-gnome`? Or a portal from the above table with screencasting support
}

View file

@ -1,9 +0,0 @@
{}:
{
services.displayManager.defaultSession = "xfce";
services.xserver.desktopManager = {
xterm.enable = false;
xfce.enable = true;
};
}

View file

@ -1,8 +0,0 @@
{ pkgs, ... }:
{
fonts.packages = with pkgs; [
nerd-fonts.fira-code
nerd-fonts.iosevka
atkinson-hyperlegible
];
}

View file

@ -1,16 +1,9 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
{ {
#imports = [];
options = {
zw.gaming.enable = lib.mkEnableOption "Enable Gaming";
};
# https://wiki.nixos.org/wiki/Category:Gaming
config = lib.mkIf config.zw.gaming.enable {
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
mangohud mangohud
protonup-qt protonup-qt
lutris # TODO: Having an issue after flake update lutris
bottles bottles
heroic heroic
]; ];
@ -21,11 +14,4 @@
protontricks.enable = true; protontricks.enable = true;
gamescopeSession.enable = true; gamescopeSession.enable = true;
}; };
programs.gamemode.enable = true;
programs.gamescope = {
enable = true;
capSysNice = true;
};
};
} }

View file

@ -1,40 +0,0 @@
{ nixpkgs }:
let
fixCmake = pkg: pkg.overrideAttrs (old: {
cmakeFlakes = (old.cmakeFlags or []) ++ [
(nixpkgs.lib.cmakeFeature "CMAKE_POLICY_VERSION_MINIMUM" "3.10")
];
});
cmake3Overlay = final: prev:
nixpkgs.lib.mapAttrs (
n: pkg:
pkg.overrideAttrs (old: {
cmakeFlags = old.cmakeFlags or [ ] ++ [ (nixpkgs.lib.cmakeFeature "CMAKE_POLICY_VERSION_MINIMUM" "3.10") ];
})
) {
inherit (prev) hpipm;
};
libretroCmake3Overlay = final: prev: {
libretro = prev.libretro // {
thepowdertoy = prev.libretro.thepowdertoy.overrideAttrs(old: {
cmakeFlags = old.cmakeFlags or [ ] ++ [ (nixpkgs.lib.cmakeFeature "CMAKE_POLICY_VERSION_MINIMUM" "3.10") ];
});
tic80 = prev.libretro.tic80.overrideAttrs(old: {
cmakeFlags = old.cmakeFlags or [ ] ++ [ (nixpkgs.lib.cmakeFeature "CMAKE_POLICY_VERSION_MINIMUM" "3.10") ];
});
citra = prev.libretro.citra.overrideAttrs(old: {
cmakeFlags = old.cmakeFlags or [ ] ++ [ (nixpkgs.lib.cmakeFeature "CMAKE_POLICY_VERSION_MINIMUM" "3.10") ];
});
dolphin = prev.libretro.dolphin.overrideAttrs(old: {
cmakeFlags = old.cmakeFlags or [ ] ++ [ (nixpkgs.lib.cmakeFeature "CMAKE_POLICY_VERSION_MINIMUM" "3.10") ];
});
};
};
in
[
cmake3Overlay
libretroCmake3Overlay
]

View file

@ -0,0 +1,38 @@
{
lib,
stdenv,
fetchFromGitHub,
}:
# Based on:
# https://github.com/NixOS/nixpkgs/blob/nixos-25.05/pkgs/by-name/zs/zsa-udev-rules/package.nix
stdenv.mkDerivation {
pname = "mv7-udev-rules";
version = "unstable-2025-06-20";
src = fetchFromGitHub {
owner = "zenware";
repo = "mv7-udev-rules";
rev = "000000000000000000000000000000000";
hash = "sha256-0000000000000000000000000";
};
# Only copy udev rules
dontConfigure = true;
dontBuild = true;
dontFixup = true;
installPhase = ''
mkdir -p $out/lib/udev/rules.d
cp dist/linux64/99-mv7-mic.rules $out/lib/udev/rules.d/
'';
meta = with lib; {
description = "udev rules for MV7 devices";
license = licenses.mit;
maintainers = with maintainers; [ zenware ];
platforms = platforms.linux;
homepage = "https://github.com/zenware/mv7-udev-rules";
};
}

36
secrets/global.yaml Normal file
View file

@ -0,0 +1,36 @@
cloudflare_api_token: ENC[AES256_GCM,data:IqZ2LkBUyboO9494G3LPFn8prx48SDFluz6u4KDzQfQxTyqiVkORRQ==,iv:5iSnHMFHOEXDxt5/fSQ/83OzG2iU6LQ9OCnah5Qnj1A=,tag:zBQvE9KvW195mLtwjFKWqQ==,type:str]
kanidm:
bind_password: ENC[AES256_GCM,data:l9nP,iv:Qtk6hGj+RnLfsNBRp4QHpCo/Oxq+EdVu62xmClPbPNk=,tag:7PRHrMT5kJdtqV2xXZPzZg==,type:int]
tailscale:
auth_key: ENC[AES256_GCM,data:RFno,iv:wG8XyXMQt/BLjp3jW0j509cDmF/V+UqGAFCjJ3twQ3k=,tag:5z1krKBPDWlZGvHleS4rPA==,type:int]
gitea:
secret_key: ENC[AES256_GCM,data:u5EV,iv:UdQaFFC2Rh0aDbqzHdowhTmxrKbOhWEmGmUEjkOUrg4=,tag:MfB6Jbc3IPc8h4VAd4urEg==,type:int]
internal_token: ENC[AES256_GCM,data:K1tX,iv:MhM1IURTet1e919eI9NpitEdfG8WRB33E4GsCmcSDro=,tag:146dEI1/Bj+w5Ul+Sbt9bw==,type:int]
db_password: ENC[AES256_GCM,data:muy+,iv:e0drAD03om5xAmLOUa4IxTFHQ5U1xeK+fgDOhATnMU8=,tag:JVorF+C4+ENhFZ7/0QSwrQ==,type:int]
oauth_client_secret: ENC[AES256_GCM,data:hVM95DQxfS9Af/z+C5AgjpcoHBs=,iv:pq4lXNYXzFIaNUWhmCV5EwD4icjziCWVqpSuIg1FzZQ=,tag:z/lxukCKwgriNLqqeHDFuQ==,type:str]
jellyfin:
oauth_client_secret: ENC[AES256_GCM,data:IQI6FfOga+3bmf+B+/vpGao=,iv:lC/zbax5mgzQBRNIIbeviWBXtMKSegKFJlHdY+bCSyo=,tag:g0BUqfHZrJB2gJUIjLjnuA==,type:str]
sops:
age:
- recipient: age1mv8xtvkuuw3hphq5ytaekz7p8a4kht79uajyhy534uy9e5472fhqj5zpxu
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuOTRKeDNjSEdnQ2ZRNnI3
dzZJc0xJdFFOUk9tWDJRcHZZSXVUMkpRQUc0CjhGWkNYR1daR2VWT2JOQUFZVlJO
ZGswSU5vaDJLWm5INXp5MlVQYjBCUzQKLS0tIDlrZjN6SGFnNDY4TEN6NjlPN01Y
dGRsTVpBMGVaVytpTm55WEIzNVpHTDQK8p9yexr9L7TwBPu/jKZkdDD2GOt9+nkK
6tcckBjVx/SUIs7U0qbE6DkzCXIWo2Ce5hsk4RE1p91Y4vocaMPxYA==
-----END AGE ENCRYPTED FILE-----
- recipient: age148yre4vaxp6lm59rft24te46szawqyguf8znkrtpq7ud8tpteauqxkwyjl
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyZUI5VFp5R1Z1SU5XSEtt
QndNajRsR0VyOTUrNjljcnlVRW8vRnVid1hjCmhraVA1anFEdUJoa0lMNm5RcjFE
b1o3bzBpRm83VVhWQy94NXNqSjJSekUKLS0tIGNaQno3RzEyYlNPT0RsNzEzaUd6
c2tIcEx4VGpIVXZzcEJoUTRGRjdSTjAKk4C6PQiAZpunDmNitkrTBq6PqRe53mZB
ehF6fL7DTveVwWq02csekQdi7hXShkoH7EEMw1LrEe6IP7oJbvZ3jw==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-06-04T13:43:02Z"
mac: ENC[AES256_GCM,data:Kd+9kPRgwfdJh9rMADopqJyocPg4M3hnvW8LYxV8jcoOGvuK+rBPu0rQF30ZGUq0i7LG7WiqN+m7nDTrl6KcO4WN4IyTqLJ/lheOLiBTpbUv68VEYPEbTEkvTMrfhsaCNk3AUuUI34cle+k9Q0/QgNenVx7yH77VzR3aiYH/hzA=,iv:s6+V8wTYTE6Q28Kyjb2NlyoGRvUaliaeJAn6htQ1Xwo=,tag:ZkO/mKZ/Dk1jit6qgTxzMA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.10.2

56
secrets/temp.yaml Normal file
View file

@ -0,0 +1,56 @@
#ENC[AES256_GCM,data:dqhy221XOImoFPMsVz3eFipnQZRxDvoXAWRW4gT/UKqmJrsZp/CgmISo/yx7d3Y+Otk2mZHPmi8=,iv:jPJ3jL7heD721vNnGveNUNdOOwJngCFFbaKHxQxwu1w=,tag:EX/SqAJa9H1KMUnJHvwBbw==,type:comment]
#ENC[AES256_GCM,data:2QAMIK1AD8iEVsBl/tEiWAAjjpMzjvnVpoIa8h60KSCEsU8nycqndOPCLq3BOJGK0a5qITm0,iv:HOJ0tFMEY+99aDXdWjiHLXhpssSEMuMhby/n2W/0HqA=,tag:bEZ+IruPQYNj8rJXn3kbvA==,type:comment]
cloudflare:
api_token: ENC[AES256_GCM,data:ZkiVQeVFHQ==,iv:402yjlUkamfzgAqMQCChR+ui4nsA625GidVIwCOc8bM=,tag:xErz+vXV8en2c3WdHfqUsg==,type:str]
email: ENC[AES256_GCM,data:AnI4i1ar70FvyLzVqiElJTbZ6V12Tw==,iv:VYoK2Q2XmleP2SV9Axy8JPTrPeLjnJBkhaXVuCWGUtA=,tag:uxB9JQJWUwoIQ1QtpbvT8Q==,type:str]
#ENC[AES256_GCM,data:qcSPXWftLd3ojT2kQrqsZFuIzRSk7otMj+DA945UYvrut1O6mjYrzKwf/IyHUmlaDFh7kenPXgGDwm5VU9w=,iv:wRGpS4aeMO9tpF4vHE6sKMt1LA9YcLW8+G7VBWwYMOo=,tag:VEDXurG64aT43pi0lKLN1w==,type:comment]
env:
domain_name: ENC[AES256_GCM,data:sGpnauqZ2LSY+Hf34eA=,iv:V1OOAo9zoEAkXDLL2la6DF+FoCj4Lzqg88dbHHSqcXs=,tag:C+0yaNo/TwAtym7NSxNnyA==,type:str]
#ENC[AES256_GCM,data:IYmlxtKW06u6orlQii4IfB45O88ofznUvuuzYt0=,iv:YdDbHxqG/Nd9CF9l/BBMEO28VYnTiKvkdXxTbMJjRG8=,tag:y0rADtZhmdYZjiax9YvJzg==,type:comment]
yubico:
id: ENC[AES256_GCM,data:n80pw3A=,iv:pS/T3JGbVQkmYJ6EnwcdITe9J8knVBJiEn8njxPzAcM=,tag:gwg5pl6ugfBH4qwpcr1cjA==,type:int]
secret_key: ENC[AES256_GCM,data:kax8+XHJwpqFtRdKpkRR5sFhsKawlT7Ru92Umw==,iv:wSs4wdAkHyacKUmIxGfn8KDv1A8yW6E+0eBuXnOzhMA=,tag:Y6Om1OShD6s3dBALseN/qQ==,type:str]
#ENC[AES256_GCM,data:Zwum1QmpeQUb4PIxdqY=,iv:stOFFRpaR+kHIfADKS/o//K1+oQ+Y9ANNp5LbrbgW9c=,tag:hUdnj2menajZhM7yuRs0aQ==,type:comment]
tailscale:
auth_key: ENC[AES256_GCM,data:lIT80TrYfqMa8k6wsxC5BV6sBg6PCw5+AJaUtMhfJuXTIOz48PZKpCFq5z76TenpGpYT1vdqfCGd8Z1UfA==,iv:5LCWydaBX0ap5VScmjSm/QLtkeYy7u0ImVXoaY8URZs=,tag:dZV4s026eS4ME68ET+JxGw==,type:str]
#ENC[AES256_GCM,data:TBtQO7kI+rfcy2XlpYCIAkviKS0=,iv:JjLLJqDMFmHtrqgIH9xVlJ6gbeFT7g74kFL8nZH+nCk=,tag:rUpKWnstKtqbJbGLk9dQfw==,type:comment]
vrising_server_name: ENC[AES256_GCM,data:guRSn4xsGFJ0,iv:spCO2OuFr2JmFL+qboq2dwrpxb7KufxtscZI8/3r9SQ=,tag:qbFV4IIiFmEQW72So6pm7A==,type:str]
valheim_server_name: ENC[AES256_GCM,data:PJV694YqSnOkBw==,iv:yIjgunzkiVdO1pUuAOtKcRI2zhBSb2sfDfM5WrPsVa8=,tag:I18ugPusdBoADlSxYzIYNw==,type:str]
example_key: ENC[AES256_GCM,data:RPKzGyhQVMt53UIrbg==,iv:WdJEIrcLjqwyaEiXAOGoGihp0tgr3P0j+coQThRSAqM=,tag:efPXuh06+ZEyRAY6M8O6Vg==,type:str]
#ENC[AES256_GCM,data:hwNzDeFecGqtr1gCdxQ2Dg==,iv:fvPKS3tH5i+HdJhGxJz7Bf0nM/DZDqGYFy23/CTQq14=,tag:ieLl2TJuIj0mPzAHlZeWKw==,type:comment]
example_array:
- ENC[AES256_GCM,data:Wh1JeEYh5XIXqxCi/kc=,iv:3MH0nBTXkYWSf2FTSJAelSR0jPHhsp0AEPH3bq1v6WM=,tag:pGs/aD013fzjgX1mzFWSeQ==,type:str]
- ENC[AES256_GCM,data:0mIilx8XdDOg5oRxcrA=,iv:Tm6E3M8hi3HqTxO/vFO3GVlZ8IgUKjAliYbHwh4SEqw=,tag:g9u7zaBENVdcloWpj6jxBg==,type:str]
example_number: ENC[AES256_GCM,data:5M+hN39uq5hn7A==,iv:vxWdPRafSZ4LYKgjDK8hrqwb7mMjLDxf7/mcb9Fe4r8=,tag:YmTtnwCcnX+xVs09XFLQfA==,type:float]
example_booleans:
- ENC[AES256_GCM,data:AtfqPA==,iv:/Hsubc6st3Htaidi634RAndd9Rl2Ldetzm+YfZ2Vu0o=,tag:TqC6nn7cAWffzuWk0e2tCw==,type:bool]
- ENC[AES256_GCM,data:nkbswos=,iv:FBM0yr8MdotAWwxyD6aHhYPjINDeJ5Yu3PRQAg6ey/8=,tag:oCgE9qJHRNLDboO80dOjgw==,type:bool]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1mv8xtvkuuw3hphq5ytaekz7p8a4kht79uajyhy534uy9e5472fhqj5zpxu
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5cGxrQkpqTGJiaGNtenVG
Y2JhVVpDenhuS050SU9hald0RlRRdGpIK3owCkxSNVAzTVNlbmNGSjNZRVlYb3E3
RVM0ZUVmNkJDRW1NcFpzcjR3QndTaGcKLS0tIElUSCsyQmF1cW52UnVsQlZVT1E0
OUNLVk1uNWJNbUNRS1d0Q3FIYXFTZGcKRRK5AO8fQ835zNUDoseBAMPEIxlmAxXT
kv82umpkOyAKQe4opDrxdaJQwoT4nYqdr26ZXCEhajNo1sit9j1z/g==
-----END AGE ENCRYPTED FILE-----
- recipient: age148yre4vaxp6lm59rft24te46szawqyguf8znkrtpq7ud8tpteauqxkwyjl
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmcUVsQU9MNHExd0QyWlZ2
eVVFd1h4WXJyb0lERFFFbWlqQzFGUFJMT3lJCk1iNXRlVTJjaVBjNjB2LzRIcUdr
dk01QU1hbXZwSk50SXhyWlY3WTM4SEEKLS0tIE95eDEydkExVkU1RUNkVUh0TnU4
cGpKVCt3dnVOcTYycDNGeVNFWTQvM28KNbvMhnu/kee2lvEqzTeg7708H+HwxSlD
hR4rWnf/lvA66DIFP7RQN0neouC3mD71irlhAYTWV9h3J9/0z/De1A==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-06-02T20:31:13Z"
mac: ENC[AES256_GCM,data:YaTn5x0jBvM9Yjj53IClYX11zIYZlrp95IbljXX3E5GJApTL4Mkigv1sPJXwq4jJ67JCQL3bdmLMyvWDfuLd1deVtG1LXD4NFYxFaeUXWgBfPs2IW3l/JakU6kliluZRwe5hwhCNIb/+7t9948HLq5MDIaLU2PDeASK/wySOYno=,iv:3D4o66NrUlxFagO80LSyei8BsKyb6dYN75eHs/MhOpw=,tag:3oNBSmZwGkv8MJduv4Sr8w==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.9.2

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

@ -0,0 +1,16 @@
{ pkgs, lib, ... }:
{
users.users.jay = {
home =
if pkgs.stdenv.isLinux then
lib.mkDefault "/home/jay"
else if pkgs.stdenv.isDarwin then
lib.mkDefault "/Users/jay"
else
abort "Unsupported OS";
} // lib.optionalAttrs pkgs.stdenv.isLinux {
isNormalUser = true;
extraGroups = [ "networkmanager" "wheel" ];
# hashedPassword = "";
};
}

68
users/jay/home.nix Normal file
View file

@ -0,0 +1,68 @@
{ pkgs, lib, ... }:
{
# NOTE: This file contains options that resolve under home-manager.users.<username>
home.stateVersion = "25.05";
home.sessionVariables = {
EDITOR = "hx";
};
home = {
username = "jay";
homeDirectory =
if pkgs.stdenv.isLinux then
lib.mkDefault "/home/jay"
else if pkgs.stdenv.isDarwin then
lib.mkDefault "/Users/jay"
else
abort "Unsupported OS";
};
home.packages = with pkgs; [ ]
# linux only
# TODO: Add a test for linux + desktop environment
++ (lib.optionals pkgs.stdenv.isLinux [
tree
cfspeedtest
ripgrep
helix
nil
])
# linux + desktop manager
#++ (lib.optionals (pkgs.stdenv.isLinux && osConfig.services.desktopManager.enabled != null)
#[
# firefox
#])
# darwin only
++ (lib.optionals pkgs.stdenv.isDarwin [
cfspeedtest
ripgrep
]);
programs.fish.enable = true;
# TODO: Get that working again.
#users.users.jml.shell = pkgs.fish;
programs = {
bat.enable = true;
fzf.enable = true;
jq.enable = true;
btop.enable = true;
};
programs.git = {
enable = true;
userName = "Jay Looney";
userEmail = "jay.m.looney@gmail.com";
aliases = {
ol = "log --oneline";
};
ignores = [ "*~" "*.swp" ];
extraConfig = {
push.default = "simple";
credential.helper = "cache --timeout=7200";
init.defaultBranch = "main";
log.decorate = "full";
log.date = "iso";
merge.conflictStyle = "diff3";
};
};
}

View file

@ -1,9 +1,6 @@
{ pkgs, lib, ... }: { pkgs, lib, ... }:
{ {
programs.fish.enable = true; programs.fish.enable = true;
environment.systemPackages = [
pkgs.home-manager
];
users.users.jml = { users.users.jml = {
shell = pkgs.fish; shell = pkgs.fish;
home = home =

View file

@ -1,10 +1,4 @@
{ { username, pkgs, lib, ... }:
username,
pkgs,
lib,
inputs,
...
}:
{ {
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
# The following line is needed if I start using hyprland Home Manager Module # The following line is needed if I start using hyprland Home Manager Module
@ -25,9 +19,7 @@
else else
abort "Unsupported OS"; abort "Unsupported OS";
}; };
home.packages = home.packages = with pkgs; [ ]
with pkgs;
[ ]
# linux only # linux only
# TODO: Add a test for linux + desktop environment # TODO: Add a test for linux + desktop environment
++ (lib.optionals pkgs.stdenv.isLinux [ ++ (lib.optionals pkgs.stdenv.isLinux [
@ -57,7 +49,7 @@
# Matrix Chat Apps # Matrix Chat Apps
element-desktop.enable = true; element-desktop.enable = true;
#nheko.settings = true; nheko.settings = true;
# Additions from Windows # Additions from Windows
obsidian.enable = true; obsidian.enable = true;
@ -73,11 +65,6 @@
helix.enable = true; helix.enable = true;
zoxide.enable = true; zoxide.enable = true;
fd.enable = true; fd.enable = true;
difftastic.enable = true;
difftastic.git.enable = true;
difftastic.git.diffToolMode = true;
mergiraf.enable = true;
}; };
programs.starship = { programs.starship = {
@ -157,379 +144,23 @@
}; };
}; };
# TODO: figure out how to get config.programs.<name>.enable style
# internal references inside this file.
# There's some quirks with how this is used in lib/default.nix
# TODO: Use mergiraf for conflict resolution in jj too.
programs.jujutsu = {
enable = true;
#enableFishIntegration = true;
settings = {
user = {
name = "Jay Looney";
email = "jay.m.looney@gmail.com";
};
};
};
# TODO: Configure Mergiraf
# https://mergiraf.org/introduction.html
programs.git = { programs.git = {
enable = true; enable = true;
settings = { userName = "Jay Looney";
user = { userEmail = "jay.m.looney@gmail.com";
name = "Jay Looney";
email = "jay.m.looney@gmail.com";
};
# Aliases Inspired by the following:
# https://joel-hanson.github.io/posts/05-useful-git-aliases-for-a-productive-workflow/
# https://gist.github.com/mwhite/6887990
aliases = { aliases = {
la = "!git config -l | grep alias | cut -c 7-";
s = "status -s";
co = "checkout";
cob = "checkout -b";
del = "branch -D";
ol = "log --oneline"; ol = "log --oneline";
br = "branch --format='%(HEAD) %(color:yellow)%(refname:short)%(color:reset) - %(contents:subject) %(color:green)(%(committerdate:relative)) [%(authorname)]' --sort=-committerdate";
save = "!git add -A && git commit -m 'chore: commit save point'";
undo = "reset HEAD~1 --mixed";
done = "!git push origin HEAD";
lg = "!git log --pretty=format:\"%C(magenta)%h%Creset -%C(red)%d%Creset %s %C(dim green)(%cr) [%an]\" --abbrev-commit -30";
a = "add";
ap = "add -p";
}; };
ignores = [ "*~" "*.swp" ];
extraConfig = {
push.default = "simple"; push.default = "simple";
credential.helper = "cache --timeout=7200"; credential.helper = "cache --timeout=7200";
init.defaultBranch = "main"; init.defaultBranch = "main";
log.decorate = "full"; log.decorate = "full";
log.date = "iso"; log.date = "iso";
# NOTE: Initially diff3 was for me, now it's for me and mergiraf automation.
merge.conflictStyle = "diff3"; merge.conflictStyle = "diff3";
}; };
# Cribbed from: https://github.com/gitattributes/gitattributes
attributes = [
# Auto detect files and perform LF normalization
"* text=auto"
# Documents
"*.bibtex text diff=bibtex"
"*.doc diff=astextplain"
"*.DOC diff=astextplain"
"*.docx diff=astextplain"
"*.DOCX diff=astextplain"
"*.dot diff=astextplain"
"*.DOT diff=astextplain"
"*.pdf diff=astextplain"
"*.PDF diff=astextplain"
"*.rtf diff=astextplain"
"*.RTF diff=astextplain"
"*.md text diff=markdown"
"*.mdx text diff=markdown"
"*.tex text diff=tex"
"*.adoc text"
"*.textile text"
"*.mustache text"
"*.csv text eol=crlf"
"*.tab text"
"*.tsv text"
"*.txt text"
"*.sql text"
"*.epub diff=astextplain"
# Graphics
"*.png binary"
"*.jpg binary"
"*.jpeg binary"
"*.gif binary"
"*.tif binary"
"*.tiff binary"
"*.ico binary"
# SVG treated as text by default.
"*.svg text"
# If you want to treat it as binary,
# use the following line instead.
# *.svg binary
"*.eps binary"
# Scripts
"*.bash text eol=lf"
"*.fish text eol=lf"
"*.ksh text eol=lf"
"*.sh text eol=lf"
"*.zsh text eol=lf"
# These are explicitly windows files and should use crlf
"*.bat text eol=crlf"
"*.cmd text eol=crlf"
"*.ps1 text eol=crlf"
# Serialisation
"*.json text"
"*.toml text"
"*.xml text"
"*.yaml text"
"*.yml text"
# Archives
"*.7z binary"
"*.bz binary"
"*.bz2 binary"
"*.bzip2 binary"
"*.gz binary"
"*.lz binary"
"*.lzma binary"
"*.rar binary"
"*.tar binary"
"*.taz binary"
"*.tbz binary"
"*.tbz2 binary"
"*.tgz binary"
"*.tlz binary"
"*.txz binary"
"*.xz binary"
"*.Z binary"
"*.zip binary"
"*.zst binary"
# Text files where line endings should be preserved
"*.patch -text"
# Exclude files from exporting
".gitattributes export-ignore"
".gitignore export-ignore"
".gitkeep export-ignore"
];
# TODO: Merge Gitignores from here: https://github.com/github/gitignore/tree/main/Global
ignores = [
"*~"
"*.swp"
];
}; };
programs.emacs = {
enable = true;
# package = (pkgs.emacs30.pkgs.withPackages (epkgs: [
# epkgs.treesit-grammars.with-grammars (grammars: [
# grammars.tree-sitter-bash
# ])
# epkgs.pretty-sha-path
# ]));
extraConfig = ''
(setq standard-indent 2)
'';
};
# TODO: Implement support for at least
# Nix, Python, Rust, Golang
# TODO: Sort out why TF, `.nix` files tabs are cooked in neovim rn.
# It corrects things on document save, but this line for example started with an 8-long tabstop
programs.nvf = {
enable = true;
# When using the Home-Manager Module for nvf, the settings go into the following attribute set.
# https://notashelf.github.io/nvf/index.xhtml#sec-hm-flakes
settings.vim = {
viAlias = true;
vimAlias = true;
# TODO: For some reason spellcheck is having a very difficult time getting
# a wordlist.
#spellcheck = {
# enable = true;
# programmingWordlist.enable = true;
#};
lsp = {
enable = true;
formatOnSave = true;
lspkind.enable = false;
lightbulb.enable = true;
lspsaga.enable = false;
trouble.enable = true;
lspSignature.enable = false;
otter-nvim.enable = true;
nvim-docs-view.enable = true;
};
languages = {
enableDAP = true;
enableExtraDiagnostics = true;
enableFormat = true;
enableTreesitter = true;
nix = {
enable = true;
lsp.enable = true;
lsp.server = "nixd";
extraDiagnostics.enable = true;
format.enable = true;
format.type = "nixfmt";
treesitter.enable = true;
};
markdown.enable = true;
typst.enable = true;
assembly.enable = true;
bash.enable = true;
clang.enable = true;
python.enable = true;
rust = {
enable = true;
# TODO: null_ls is now deprecated.
# https://github.com/NotAShelf/nvf/issues/1175
# https://github.com/NotAShelf/nvf/blob/main/.github/CONTRIBUTING.md
crates.enable = true;
};
go.enable = true;
zig.enable = true;
ts.enable = true;
html.enable = true;
css.enable = true;
sql.enable = true;
};
visuals = {
nvim-scrollbar.enable = true; # Configurable Visual Scrollbar (Can pair with Cursor, ALE, Diagnostics, Gitsigns, and hlslens)
nvim-web-devicons.enable = true; # Nerdfont Icons for use by other plugins
nvim-cursorline.enable = true; # Highlight Words & Lines on the cursor
cinnamon-nvim.enable = true; # Smooth Scrolling for any movement command.
fidget-nvim.enable = true; # UI for Notifications & LSP Progress Messages
highlight-undo.enable = true; # Highlight changed text after any non-insert actions
indent-blankline.enable = true; # Indentation Guides
};
statusline = {
lualine = {
# Fancy Status Line
enable = true;
theme = "catppuccin";
};
};
theme = {
enable = true;
name = "catppuccin";
style = "mocha";
transparent = false;
};
autopairs.nvim-autopairs.enable = true; # Pair up ", {, (, etc.
# blink-cmp is a compiled rust binary while nvim-cmp is a pure lua plugin...
autocomplete.blink-cmp.enable = true;
# Code Snippets Engine /w support for Lua, VSCode, and SnipMate snippets.
snippets.luasnip.enable = true;
filetree.neo-tree.enable = true; # Filesystem tree sidebar...
tabline.nvimBufferline.enable = true; # Shows buffers as tabs at the top.
treesitter.context.enable = true;
binds = {
whichKey.enable = true; # Shows your available keybindings in a popup
cheatsheet.enable = true; # Searchable in-editor cheatsheet that uses Telescope
};
telescope.enable = true; # Fuzzy Finder, central to many other plugins.
git = {
enable = true;
gitsigns.enable = true; # Git Info in Buffers + Gutters
gitsigns.codeActions.enable = false;
neogit.enable = true; # Interactive Git
};
# TODO: Consider switching to `minimap-nvim` for rust-based minimap.
# codewindow may be tightly integrated with treesitter though...
minimap.codewindow.enable = true;
dashboard.alpha.enable = true; # Greeter
notify.nvim-notify.enable = true; # Fancy Configurable Notification Manager
projects.project-nvim.enable = true;
utility = {
ccc.enable = true; # Color Picker
diffview-nvim.enable = true;
icon-picker.enable = true;
surround.enable = true; # Change Surrounding Delimiter pairs `ysiw)`
leetcode-nvim.enable = true; # Allow solving LeetCode problems directly inside neovim
multicursors.enable = true; # Edit with multiple cursors simultaneously
smart-splits.enable = true; # Split-Pane Management
undotree.enable = true; # Undo history visualizer
nvim-biscuits.enable = true; # Shows the start of a code block from the bottom
motion = {
# NOTE: https://github.com/smoka7/hop.nvim
hop.enable = true; # EasyMotion like, allowing you to jump anywhere in the document with as few keystrokes as possible
leap.enable = true; # Jump to anywhere visible
# TODO: I sort of hate how precognition injects itself in virtual
# lines, but I do like that it can be used to give a reminder.
precognition.enable = false; # Helps with discovering motions to navigate your current buffer
};
images.img-clip.enable = true;
};
# TODO: Get Obsidian Working.
notes = {
# obsidian.enable = true; # neovim fails to build with this enabled.
mind-nvim.enable = true;
todo-comments.enable = true;
};
terminal = {
toggleterm = {
enable = true;
lazygit.enable = true;
};
};
ui = {
borders.enable = true;
noice.enable = true;
colorizer.enable = true;
modes-nvim.enable = false; # this looks terrible with catppuccin
illuminate.enable = true;
breadcrumbs = {
enable = true;
navbuddy.enable = true;
};
smartcolumn = {
enable = true;
setupOpts.custom_colorcolumn = {
nix = "110";
ruby = "120";
java = "130";
go = [
"90"
"130"
];
};
};
fastaction.enable = true;
};
assistant = {
chatgpt.enable = false;
copilot = {
enable = false;
cmp.enable = true;
};
codecompanion-nvim.enable = false;
# avante-nvim.enable = true;
};
session.nvim-session-manager.enable = true; # Save sessions to reopen later
gestures.gesture-nvim.enable = false; # mouse gesture support?
comments.comment-nvim.enable = true; # Fancy commenting
presence.neocord.enable = true; # Discord Rich Presence
};
};
programs.vscode = {
enable = true;
mutableExtensionsDir = true; # mutually exclusive to programs.vscode.profiles
# profiles.default.userSettings = {
# "[nix]"."editor.tabSize" = 2;
# };
};
# services.podman.enable = true; # services.podman.enable = true;
} }