465 lines
16 KiB
Nix
465 lines
16 KiB
Nix
{
|
|
description = "Unified NixOS flake for mreow (laptop), yarn (desktop), muffin (server)";
|
|
|
|
inputs = {
|
|
# Two channels: unstable for desktops, 25.11 for server.
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
nixpkgs-stable.url = "github:NixOS/nixpkgs/nixos-25.11";
|
|
|
|
home-manager = {
|
|
url = "github:nix-community/home-manager";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
home-manager-stable = {
|
|
url = "github:nix-community/home-manager/release-25.11";
|
|
inputs.nixpkgs.follows = "nixpkgs-stable";
|
|
};
|
|
|
|
# Shared across all hosts
|
|
lanzaboote = {
|
|
url = "github:nix-community/lanzaboote";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
inputs.rust-overlay.follows = "rust-overlay";
|
|
};
|
|
nixos-hardware.url = "github:NixOS/nixos-hardware/master";
|
|
disko = {
|
|
url = "github:nix-community/disko/latest";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
impermanence = {
|
|
url = "github:nix-community/impermanence";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
inputs.home-manager.follows = "home-manager";
|
|
};
|
|
|
|
# Desktop (mreow + yarn)
|
|
rust-overlay = {
|
|
url = "github:oxalica/rust-overlay";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
zen-browser = {
|
|
url = "github:0xc000022070/zen-browser-flake";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
inputs.home-manager.follows = "home-manager";
|
|
};
|
|
firefox-addons = {
|
|
url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
niri = {
|
|
url = "github:sodiboo/niri-flake";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
inputs.nixpkgs-stable.follows = "nixpkgs";
|
|
};
|
|
emacs-overlay = {
|
|
url = "github:nix-community/emacs-overlay";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
inputs.nixpkgs-stable.follows = "nixpkgs";
|
|
};
|
|
nix-flatpak.url = "github:gmodena/nix-flatpak/";
|
|
nix-doom-emacs-unstraightened = {
|
|
url = "github:marienz/nix-doom-emacs-unstraightened";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
inputs.emacs-overlay.follows = "emacs-overlay";
|
|
};
|
|
jovian-nixos = {
|
|
url = "github:Jovian-Experiments/Jovian-NixOS";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
noctalia = {
|
|
url = "github:noctalia-dev/noctalia-shell";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
nix-cachyos-kernel = {
|
|
url = "github:xddxdd/nix-cachyos-kernel/release";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
llm-agents = {
|
|
url = "github:numtide/llm-agents.nix";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
json2steamshortcut = {
|
|
url = "github:ChrisOboe/json2steamshortcut";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
|
|
# Server (muffin) — follows nixpkgs-stable
|
|
nix-minecraft = {
|
|
url = "github:Infinidoge/nix-minecraft";
|
|
inputs.nixpkgs.follows = "nixpkgs-stable";
|
|
};
|
|
vpn-confinement.url = "github:Maroka-chan/VPN-Confinement";
|
|
llamacpp = {
|
|
url = "github:TheTom/llama-cpp-turboquant/feature/turboquant-kv-cache";
|
|
inputs.nixpkgs.follows = "nixpkgs-stable";
|
|
};
|
|
srvos = {
|
|
url = "github:nix-community/srvos";
|
|
inputs.nixpkgs.follows = "nixpkgs-stable";
|
|
};
|
|
deploy-rs = {
|
|
url = "github:serokell/deploy-rs";
|
|
inputs.nixpkgs.follows = "nixpkgs-stable";
|
|
};
|
|
agenix = {
|
|
url = "github:ryantm/agenix";
|
|
inputs.nixpkgs.follows = "nixpkgs-stable";
|
|
inputs.home-manager.follows = "home-manager-stable";
|
|
inputs.darwin.follows = "";
|
|
};
|
|
senior_project-website = {
|
|
url = "github:Titaniumtown/senior-project-website";
|
|
flake = false;
|
|
};
|
|
website = {
|
|
url = "git+https://git.sigkill.computer/titaniumtown/website";
|
|
flake = false;
|
|
};
|
|
trackerlist = {
|
|
url = "github:ngosang/trackerslist";
|
|
flake = false;
|
|
};
|
|
ytbn-graphing-software = {
|
|
url = "git+https://git.sigkill.computer/titaniumtown/YTBN-Graphing-Software";
|
|
};
|
|
arr-init = {
|
|
url = "git+ssh://gitea@git.gardling.com/titaniumtown/arr-init";
|
|
inputs.nixpkgs.follows = "nixpkgs-stable";
|
|
};
|
|
nixpkgs-p2pool-module = {
|
|
url = "github:JacoMalan1/nixpkgs/create-p2pool-service";
|
|
flake = false;
|
|
};
|
|
qbittorrent-metrics-exporter = {
|
|
url = "git+https://codeberg.org/anriha/qbittorrent-metrics-exporter";
|
|
inputs.nixpkgs.follows = "nixpkgs-stable";
|
|
};
|
|
};
|
|
|
|
outputs =
|
|
inputs@{
|
|
self,
|
|
nixpkgs,
|
|
nixpkgs-stable,
|
|
home-manager,
|
|
home-manager-stable,
|
|
lanzaboote,
|
|
nixos-hardware,
|
|
disko,
|
|
impermanence,
|
|
nix-minecraft,
|
|
vpn-confinement,
|
|
srvos,
|
|
deploy-rs,
|
|
agenix,
|
|
arr-init,
|
|
nixpkgs-p2pool-module,
|
|
jovian-nixos,
|
|
...
|
|
}:
|
|
let
|
|
username = "primary";
|
|
system = "x86_64-linux";
|
|
|
|
niriPackage = inputs.niri.packages.${system}.niri-unstable;
|
|
|
|
# --- Desktop-channel pkgs (used by portable homeConfigurations) ---
|
|
desktopPkgs = import nixpkgs { inherit system; };
|
|
|
|
# --- Server (muffin) plumbing ---
|
|
bootstrapPkgs = import nixpkgs-stable { inherit system; };
|
|
patchedStableSrc = bootstrapPkgs.applyPatches {
|
|
name = "nixpkgs-stable-patched";
|
|
src = nixpkgs-stable;
|
|
patches = [
|
|
./patches/nixpkgs/0001-firefox-syncserver-add-postgresql-backend-support.patch
|
|
];
|
|
};
|
|
serverPkgs = import patchedStableSrc {
|
|
inherit system;
|
|
targetPlatform = system;
|
|
buildPlatform = builtins.currentSystem;
|
|
};
|
|
siteConfig = import ./site-config.nix;
|
|
serviceConfigs = import ./hosts/muffin/service-configs.nix { site_config = siteConfig; };
|
|
serverLib = import ./lib {
|
|
inherit inputs;
|
|
lib = nixpkgs-stable.lib;
|
|
pkgs = serverPkgs;
|
|
service_configs = serviceConfigs;
|
|
site_config = siteConfig;
|
|
};
|
|
testSuite = import ./tests/tests.nix {
|
|
pkgs = serverPkgs;
|
|
lib = serverLib;
|
|
inherit inputs;
|
|
site_config = siteConfig;
|
|
config = self.nixosConfigurations.muffin.config;
|
|
};
|
|
|
|
# --- Host builders ---
|
|
|
|
# Desktop: unstable + home-manager-unstable + niri-unstable
|
|
mkDesktopHost =
|
|
hostname:
|
|
nixpkgs.lib.nixosSystem {
|
|
specialArgs = {
|
|
inherit inputs username hostname;
|
|
niri-package = niriPackage;
|
|
site_config = siteConfig;
|
|
};
|
|
modules = [
|
|
home-manager.nixosModules.home-manager
|
|
(
|
|
{ config, ... }:
|
|
{
|
|
home-manager.useUserPackages = true;
|
|
home-manager.sharedModules = [
|
|
inputs.zen-browser.homeModules.twilight
|
|
];
|
|
home-manager.extraSpecialArgs = {
|
|
inherit
|
|
inputs
|
|
hostname
|
|
username
|
|
;
|
|
niri-package = niriPackage;
|
|
homeDirectory = "/home/${username}";
|
|
stateVersion = config.system.stateVersion;
|
|
site_config = siteConfig;
|
|
};
|
|
home-manager.users.${username} = import ./hosts/${hostname}/home.nix;
|
|
}
|
|
)
|
|
./hosts/${hostname}/default.nix
|
|
];
|
|
};
|
|
|
|
# Server: stable + home-manager-stable + srvos + agenix + patched pkgs
|
|
muffinHost = serverLib.nixosSystem {
|
|
inherit system;
|
|
specialArgs = {
|
|
inherit
|
|
username
|
|
inputs
|
|
;
|
|
hostname = "muffin";
|
|
eth_interface = "enp4s0";
|
|
service_configs = serviceConfigs;
|
|
site_config = siteConfig;
|
|
lib = serverLib;
|
|
};
|
|
modules = [
|
|
# SAFETY! port sanity checks
|
|
(
|
|
{ config, lib, ... }:
|
|
let
|
|
publicPorts = lib.attrValues serviceConfigs.ports.public;
|
|
privatePorts = lib.attrValues serviceConfigs.ports.private;
|
|
allPortNumbers = map (p: p.port) (publicPorts ++ privatePorts);
|
|
uniquePortNumbers = lib.unique allPortNumbers;
|
|
|
|
publicTcp = map (p: p.port) (lib.filter (p: p.proto == "tcp" || p.proto == "both") publicPorts);
|
|
publicUdp = map (p: p.port) (lib.filter (p: p.proto == "udp" || p.proto == "both") publicPorts);
|
|
privatePortNumbers = map (p: p.port) privatePorts;
|
|
|
|
fwTcp = config.networking.firewall.allowedTCPPorts;
|
|
fwUdp = config.networking.firewall.allowedUDPPorts;
|
|
|
|
missingTcp = lib.filter (p: !(builtins.elem p fwTcp)) publicTcp;
|
|
missingUdp = lib.filter (p: !(builtins.elem p fwUdp)) publicUdp;
|
|
leakedTcp = lib.filter (p: builtins.elem p fwTcp) privatePortNumbers;
|
|
leakedUdp = lib.filter (p: builtins.elem p fwUdp) privatePortNumbers;
|
|
in
|
|
{
|
|
config.assertions = [
|
|
{
|
|
assertion = (lib.length allPortNumbers) == (lib.length uniquePortNumbers);
|
|
message = "Duplicate port numbers detected in ports.public / ports.private";
|
|
}
|
|
{
|
|
assertion = missingTcp == [ ];
|
|
message = "Public ports missing from allowedTCPPorts: ${builtins.toString missingTcp}";
|
|
}
|
|
{
|
|
assertion = missingUdp == [ ];
|
|
message = "Public ports missing from allowedUDPPorts: ${builtins.toString missingUdp}";
|
|
}
|
|
{
|
|
assertion = leakedTcp == [ ] && leakedUdp == [ ];
|
|
message = "Private ports leaked into firewall allow-lists — TCP: ${builtins.toString leakedTcp}, UDP: ${builtins.toString leakedUdp}";
|
|
}
|
|
];
|
|
}
|
|
)
|
|
|
|
srvos.nixosModules.server
|
|
srvos.nixosModules.mixins-terminfo
|
|
|
|
./hosts/muffin/disk.nix
|
|
./hosts/muffin/default.nix
|
|
|
|
# Firefox-syncserver: swap upstream module + package for patched versions.
|
|
{
|
|
disabledModules = [ "services/networking/firefox-syncserver.nix" ];
|
|
imports = [
|
|
"${patchedStableSrc}/nixos/modules/services/networking/firefox-syncserver.nix"
|
|
];
|
|
nixpkgs.overlays = [
|
|
nix-minecraft.overlay
|
|
(import ./lib/overlays.nix)
|
|
(_final: prev: {
|
|
syncstorage-rs =
|
|
prev.callPackage "${patchedStableSrc}/pkgs/by-name/sy/syncstorage-rs/package.nix"
|
|
{ };
|
|
})
|
|
# NOTE: systemd patch is applied via `systemd.package` in the module
|
|
# list below, not via an overlay. An overlay replaces pkgs.systemd
|
|
# for every consumer, which cascades through udev-check-hook and
|
|
# causes the entire closure (fish, e2fsprogs, valkey, …) to rebuild
|
|
# and re-run flaky test suites in the sandbox. `systemd.package`
|
|
# only injects the patched systemd into the runtime init chain.
|
|
];
|
|
nixpkgs.config.allowUnfreePredicate =
|
|
pkg:
|
|
builtins.elem (nixpkgs-stable.lib.getName pkg) [
|
|
"minecraft-server"
|
|
];
|
|
}
|
|
|
|
# Runtime-only systemd patch: reset FreezerState on inactive/failed
|
|
# transitions so a SIGKILL to a frozen unit doesn't strand
|
|
# FreezerState=frozen (unrecoverable without a reboot, upstream issue
|
|
# #38517). PR #38528 closed only the watchdog path; this closes
|
|
# systemctl kill / OOM / segfault paths too.
|
|
#
|
|
# Applied via systemd.package, not via overlay, so pkgs.systemd stays
|
|
# untouched for every other consumer — no udev-check-hook cascade,
|
|
# no fish/e2fsprogs/valkey rebuild, no flaky-test fallout.
|
|
(
|
|
{ pkgs, ... }:
|
|
{
|
|
systemd.package = pkgs.systemd.overrideAttrs (old: {
|
|
patches = (old.patches or [ ]) ++ [
|
|
./patches/systemd/0001-core-unit-reset-freezer-state-on-inactive-failed.patch
|
|
];
|
|
});
|
|
}
|
|
)
|
|
|
|
lanzaboote.nixosModules.lanzaboote
|
|
arr-init.nixosModules.default
|
|
(import "${nixpkgs-p2pool-module}/nixos/modules/services/networking/p2pool.nix")
|
|
|
|
home-manager-stable.nixosModules.home-manager
|
|
(
|
|
{ ... }:
|
|
{
|
|
home-manager.extraSpecialArgs = {
|
|
site_config = siteConfig;
|
|
};
|
|
home-manager.users.${username} = import ./hosts/muffin/home.nix;
|
|
}
|
|
)
|
|
]
|
|
++ (with nixos-hardware.nixosModules; [
|
|
common-cpu-amd-pstate
|
|
common-cpu-amd-zenpower
|
|
common-pc-ssd
|
|
common-gpu-intel
|
|
]);
|
|
};
|
|
in
|
|
{
|
|
formatter.${system} = nixpkgs.legacyPackages.${system}.nixfmt-tree;
|
|
|
|
nixosConfigurations = {
|
|
mreow = mkDesktopHost "mreow";
|
|
yarn = mkDesktopHost "yarn";
|
|
muffin = muffinHost;
|
|
};
|
|
|
|
# Standalone home-manager profile — usable on any x86_64-linux machine
|
|
# with nix installed (NixOS or not). Activate with:
|
|
# nix run home-manager/master -- switch --flake ".#primary"
|
|
# Ships the shared terminal profile (fish, helix, modern CLI, git).
|
|
homeConfigurations.primary = home-manager.lib.homeManagerConfiguration {
|
|
pkgs = desktopPkgs;
|
|
extraSpecialArgs = {
|
|
site_config = siteConfig;
|
|
};
|
|
modules = [
|
|
./home/profiles/terminal.nix
|
|
{
|
|
home = {
|
|
username = username;
|
|
homeDirectory = "/home/${username}";
|
|
stateVersion = "24.11";
|
|
};
|
|
}
|
|
];
|
|
};
|
|
|
|
deploy.nodes.muffin = {
|
|
hostname = siteConfig.hosts.muffin.alias;
|
|
profiles.system = {
|
|
sshUser = "root";
|
|
user = "root";
|
|
# Deploy guard enforcement lives in the preflight driver (deploy.sh
|
|
# and .gitea/workflows/deploy.yml) — not in activation. Activation-
|
|
# time enforcement is unsafe: deploy-rs sets the new profile pointer
|
|
# before running deploy-rs-activate, so a non-zero activation exit
|
|
# triggers auto-rollback which re-runs switch-to-configuration on the
|
|
# previous generation. That re-activation rotates agenix secrets,
|
|
# reinstalls lanzaboote, and reloads systemd units — side effects we
|
|
# want to avoid when the deploy is supposed to be a no-op blocked by
|
|
# the guard. Blocking before the deploy-rs invocation is the only
|
|
# clean way to leave the running system untouched.
|
|
#
|
|
# Activation uses `switch-to-configuration boot` + a detached finalize
|
|
# (modules/server-deploy-finalize.nix) rather than the default
|
|
# `switch`. The gitea-actions runner driving CI deploys lives on
|
|
# muffin itself; a direct `switch` restarts gitea-runner-muffin mid-
|
|
# activation, killing the SSH session, the CI job, and deploy-rs's
|
|
# magic-rollback handshake. `boot` only touches the bootloader — no
|
|
# service restarts — and deploy-finalize schedules a pid1-owned
|
|
# transient unit that runs the real `switch` (or `systemctl reboot`
|
|
# when kernel/initrd/kernel-modules changed) ~60s later, surviving
|
|
# runner restart because it's decoupled from the SSH session.
|
|
path =
|
|
deploy-rs.lib.${system}.activate.custom self.nixosConfigurations.muffin.config.system.build.toplevel
|
|
''
|
|
# matches activate.nixos's workaround for NixOS/nixpkgs#73404
|
|
cd /tmp
|
|
|
|
$PROFILE/bin/switch-to-configuration boot
|
|
|
|
${nixpkgs-stable.lib.getExe self.nixosConfigurations.muffin.config.system.build.deployFinalize}
|
|
'';
|
|
};
|
|
};
|
|
|
|
checks.${system} = testSuite;
|
|
|
|
packages.${system} = {
|
|
tests = serverPkgs.linkFarm "all-tests" (
|
|
serverPkgs.lib.mapAttrsToList (name: test: {
|
|
name = name;
|
|
path = test;
|
|
}) testSuite
|
|
);
|
|
|
|
# Buildenv of every binary in the portable terminal profile. Install
|
|
# without home-manager via:
|
|
# nix profile install ".#cli-tools"
|
|
cli-tools = self.homeConfigurations.primary.config.home.path;
|
|
}
|
|
// (serverPkgs.lib.mapAttrs' (name: test: {
|
|
name = "test-${name}";
|
|
value = test;
|
|
}) testSuite);
|
|
|
|
lib = serverLib;
|
|
};
|
|
}
|