Compare commits

..

20 Commits

Author SHA1 Message Date
524dafd513 wip 2026-05-20 22:55:27 +02:00
db1e9c15ac chore: run formatter 2026-05-12 10:36:25 +02:00
f4b9eff715 temp: workaround for nixpkgs#514705 2026-05-12 10:35:53 +02:00
325b863238 feat: justfile recipe improvements 2026-05-10 00:08:17 +02:00
79a67284af chore: bump lockfile
- bump flake inputs
- bump claude-code to 2.1.138
2026-05-09 23:38:56 +02:00
fae6b25137 temp: dirty-frag (CVE-2026-43284, CVE-2026-43500) 2026-05-09 23:38:51 +02:00
4a59f6b57c feat: enable one password on fortress 2026-05-06 15:07:01 +02:00
4771d8c7d6 chore: run linter 2026-05-06 15:03:16 +02:00
2fc05cdfd0 chore: remove deprecated nix.nix 2026-05-04 11:13:39 +02:00
ac0958db60 chore: bump lockfile 2026-05-04 09:35:10 +02:00
5f8f1ee138 chore: update claude-code to v2.1.126 2026-05-04 09:33:19 +02:00
9f38c2a25b chore: bump lockfile 2026-04-30 11:07:57 +02:00
b42c838957 chore: bump claude-code to v2.1.121 2026-04-28 13:19:51 +02:00
089f6e8b86 chore: bump lockfile 2026-04-28 13:19:25 +02:00
6772afb845 merge: harden ephvm 2026-04-24 14:14:29 +02:00
e9755d41c6 feat: tighten ephvm perms, zstd compress qcow2 2026-04-24 14:13:01 +02:00
68411d9459 feat: prune vm-guest module 2026-04-24 14:12:57 +02:00
7fd5b790ff feat: ephvm-run.sh virtio devices, require kvm 2026-04-24 14:12:52 +02:00
37bca1fdd1 feat: ephvm-run.sh resilience 2026-04-24 14:12:48 +02:00
75ca09949c feat: harden ephvm-run.sh 2026-04-24 14:12:42 +02:00
17 changed files with 307 additions and 289 deletions

View File

@@ -3,11 +3,23 @@
{ {
config, config,
lib, lib,
pkgs,
inputs, inputs,
... ...
}: }:
let let
cfg = config.features.bootloader; cfg = config.features.bootloader;
keyDir = "/etc/secrets/initrd";
mkIpString =
{
address,
gateway,
netmask,
interface,
...
}:
"${address}::${gateway}:${netmask}::${interface}:none";
in in
{ {
imports = [ inputs.lanzaboote.nixosModules.lanzaboote ]; imports = [ inputs.lanzaboote.nixosModules.lanzaboote ];
@@ -23,15 +35,88 @@
default = "systemd-boot"; default = "systemd-boot";
}; };
plymouth.enable = lib.mkEnableOption "plymouth boot splash"; configurationLimit = lib.mkOption {
type = lib.types.int;
default = 10;
};
consoleFont = lib.mkOption {
type = lib.types.str;
default = "ter-v32n";
};
resumeDevice = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
};
initrdSsh = {
enable = lib.mkEnableOption "remote LUKS unlock via ssh in initrd";
networkModule = lib.mkOption {
type = lib.types.str;
};
ip = {
enable = lib.mkEnableOption "static IP for initrd (otherwise DHCP)";
address = lib.mkOption {
type = lib.types.str;
};
gateway = lib.mkOption {
type = lib.types.str;
};
netmask = lib.mkOption {
type = lib.types.str;
default = "255.255.255.0";
};
interface = lib.mkOption {
type = lib.types.str;
};
};
authorizedKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
};
}; };
config = lib.mkIf cfg.enable ( config = lib.mkIf cfg.enable (
lib.mkMerge [ lib.mkMerge [
{ {
boot.loader.efi.canTouchEfiVariables = true; boot.loader.efi.canTouchEfiVariables = true;
# request the largest framebuffer uefi offers; plymouth inherits it
boot.loader.systemd-boot.consoleMode = "max"; # lanzaboote inherits editor + configurationLimit from systemd-boot.*
boot.loader.systemd-boot = {
editor = false;
inherit (cfg) configurationLimit;
};
boot.initrd.systemd.enable = true;
# block simpledrm so fbcon defers until the gpu driver binds; avoids
# the simpledrm -> real-driver fbcon transition that mangles console
# text and leaves the luks prompt typing offset from the visible
# surface. hosts must put the gpu driver in initrd (nixos-hardware
# does this for amd; manual hardware.amdgpu.initrd.enable on others)
boot.kernelParams = [ "initcall_blacklist=simpledrm_platform_driver_init" ];
# verbose boot: kernel messages and systemd unit lines visible end
# to end. trade-off: the luks prompt will be interleaved with the
# last few "Starting/Started ..." lines (no upstream fix exists
# without plymouth). boot.initrd.verbose is a no-op under
# systemd-initrd, so not set here.
# readable luks prompt at panel-native dpi
console = {
earlySetup = true;
font = cfg.consoleFont;
packages = [ pkgs.terminus_font ];
};
} }
(lib.mkIf (cfg.mode == "systemd-boot") { (lib.mkIf (cfg.mode == "systemd-boot") {
@@ -46,26 +131,41 @@
}; };
}) })
(lib.mkIf cfg.plymouth.enable { (lib.mkIf (cfg.resumeDevice != null) {
# plymouth needs systemd-initrd to render the luks prompt cleanly boot.resumeDevice = cfg.resumeDevice;
boot.initrd.systemd.enable = true; })
# host is responsible for early-KMS so plymouth lands on the gpu driver, (lib.mkIf cfg.initrdSsh.enable {
# not simpledrm (e.g. hardware.amdgpu.initrd.enable on amd hosts) boot.initrd.systemd.settings.Manager.DefaultDeviceTimeoutSec = "infinity";
boot.plymouth.enable = true;
stylix.targets.plymouth.logoAnimated = false;
boot.kernelParams = [ boot.initrd.availableKernelModules = [ cfg.initrdSsh.networkModule ];
"quiet"
"splash" boot.kernelParams = lib.mkIf cfg.initrdSsh.ip.enable [
"loglevel=3" "ip=${mkIpString cfg.initrdSsh.ip}"
"rd.systemd.show_status=false"
"rd.udev.log_level=3"
"udev.log_priority=3"
"plymouth.force-scale=1"
]; ];
boot.consoleLogLevel = 0;
boot.initrd.verbose = false; boot.initrd.network = {
enable = true;
ssh = {
enable = true;
port = 22;
hostKeys = [
"${keyDir}/ssh_host_rsa_key"
"${keyDir}/ssh_host_ed25519_key"
];
inherit (cfg.initrdSsh) authorizedKeys;
};
};
# forward LUKS password prompt to the ssh session (systemd-initrd idiom)
boot.initrd.systemd.users.root.shell = "/bin/systemd-tty-ask-password-agent";
boot.initrd.systemd.network.networks = lib.mkIf (!cfg.initrdSsh.ip.enable) {
"10-initrd" = {
matchConfig.Driver = cfg.initrdSsh.networkModule;
networkConfig.DHCP = "yes";
};
};
}) })
] ]
); );

View File

@@ -105,7 +105,11 @@
# bluetooth # bluetooth
(lib.mkIf cfg.bluetooth.enable { (lib.mkIf cfg.bluetooth.enable {
hardware.bluetooth.enable = true; hardware.bluetooth.enable = true;
services.blueman.enable = true; services.blueman = {
enable = true;
# TEMP:(@janezicmatej) workaround for nixpkgs#514705, fix in nixpkgs#517250
withApplet = false;
};
}) })
# apps # apps

View File

@@ -1,90 +0,0 @@
{
nixos =
{ lib, config, ... }:
let
cfg = config.features.initrd-ssh;
keyDir = "/etc/secrets/initrd";
mkIpString =
{
address,
gateway,
netmask,
interface,
...
}:
"${address}::${gateway}:${netmask}::${interface}:none";
in
{
options.features.initrd-ssh = {
enable = lib.mkEnableOption "initrd ssh";
ip = {
enable = lib.mkEnableOption "static IP for initrd (otherwise DHCP)";
address = lib.mkOption {
type = lib.types.str;
};
gateway = lib.mkOption {
type = lib.types.str;
};
netmask = lib.mkOption {
type = lib.types.str;
default = "255.255.255.0";
};
interface = lib.mkOption {
type = lib.types.str;
};
};
authorizedKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
networkModule = lib.mkOption {
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
boot.initrd.availableKernelModules = [ cfg.networkModule ];
boot.initrd.kernelModules = [ cfg.networkModule ];
boot.kernelParams = lib.mkIf cfg.ip.enable [
"ip=${mkIpString cfg.ip}"
];
boot.initrd.systemd.enable = true;
# remote unlock may take a while; don't let device units give up
boot.initrd.systemd.settings.Manager.DefaultDeviceTimeoutSec = "infinity";
boot.initrd.network = {
enable = true;
ssh = {
enable = true;
port = 22;
hostKeys = [
"${keyDir}/ssh_host_rsa_key"
"${keyDir}/ssh_host_ed25519_key"
];
inherit (cfg) authorizedKeys;
};
};
# systemd-networkd retries DHCP indefinitely, unlike udhcpc
boot.initrd.systemd.network.networks = lib.mkIf (!cfg.ip.enable) {
"10-initrd" = {
matchConfig.Driver = cfg.networkModule;
networkConfig.DHCP = "yes";
};
};
# forward LUKS password prompt to the SSH session
boot.initrd.systemd.users.root.shell = "/bin/systemd-tty-ask-password-agent";
};
};
}

View File

@@ -8,11 +8,6 @@
options.features.power = { options.features.power = {
enable = lib.mkEnableOption "laptop power management"; enable = lib.mkEnableOption "laptop power management";
resumeDevice = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
};
lidSwitch = lib.mkOption { lidSwitch = lib.mkOption {
type = lib.types.str; type = lib.types.str;
default = "suspend-then-hibernate"; default = "suspend-then-hibernate";
@@ -40,8 +35,6 @@
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
boot.resumeDevice = lib.mkIf (cfg.resumeDevice != null) cfg.resumeDevice;
services.logind.settings.Login = { services.logind.settings.Login = {
HandleLidSwitch = cfg.lidSwitch; HandleLidSwitch = cfg.lidSwitch;
HandlePowerKey = cfg.powerKey; HandlePowerKey = cfg.powerKey;

View File

@@ -43,19 +43,15 @@
config = lib.mkIf cfg.enable ( config = lib.mkIf cfg.enable (
lib.mkMerge [ lib.mkMerge [
{ {
services.qemuGuest.enable = true;
services.spice-vdagentd.enable = lib.mkIf (!cfg.headless) true; services.spice-vdagentd.enable = lib.mkIf (!cfg.headless) true;
boot.kernelParams = lib.mkIf cfg.headless [ "console=ttyS0,115200" ]; boot.kernelParams = lib.mkIf cfg.headless [ "console=ttyS0,115200" ];
# 9p autoloads on first mount
boot.initrd.availableKernelModules = [ boot.initrd.availableKernelModules = [
"9p" "9p"
"9pnet_virtio" "9pnet_virtio"
]; ];
boot.kernelModules = [
"9p"
"9pnet_virtio"
];
networking = { networking = {
useDHCP = true; useDHCP = true;
@@ -68,7 +64,6 @@
curl curl
wget wget
htop htop
sshfs
]; ];
} }

120
flake.lock generated
View File

@@ -54,11 +54,11 @@
"base16-helix": { "base16-helix": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1760703920, "lastModified": 1776754714,
"narHash": "sha256-m82fGUYns4uHd+ZTdoLX2vlHikzwzdu2s2rYM2bNwzw=", "narHash": "sha256-E3OAK27smtATTmX45uoTSRsVD+Y+ZiVVfgM/tjpbtYg=",
"owner": "tinted-theming", "owner": "tinted-theming",
"repo": "base16-helix", "repo": "base16-helix",
"rev": "d646af9b7d14bff08824538164af99d0c521b185", "rev": "4d508123037e7851ad36ebf7d9c48b0e9e1eb581",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -106,11 +106,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1776613567, "lastModified": 1777713215,
"narHash": "sha256-gC9Cp5ibBmGD5awCA9z7xy6MW6iJufhazTYJOiGlCUI=", "narHash": "sha256-8GzXDOXckDWwST8TY5DbwYFjdvQLlP7K9CLSVx6iTTo=",
"owner": "nix-community", "owner": "nix-community",
"repo": "disko", "repo": "disko",
"rev": "32f4236bfc141ae930b5ba2fb604f561fed5219d", "rev": "63b4e7e6cf75307c1d26ac3762b886b5b0247267",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -122,11 +122,11 @@
"firefox-gnome-theme": { "firefox-gnome-theme": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1775176642, "lastModified": 1776136500,
"narHash": "sha256-2veEED0Fg7Fsh81tvVDNYR6SzjqQxa7hbi18Jv4LWpM=", "narHash": "sha256-r0gN2brVWA351zwMV0Flmlcd6SGMvYqFbvC3DfKFM8Y=",
"owner": "rafaelmardojai", "owner": "rafaelmardojai",
"repo": "firefox-gnome-theme", "repo": "firefox-gnome-theme",
"rev": "179704030c5286c729b5b0522037d1d51341022c", "rev": "0f8ba203d475587f477e7ae12661bd8459e225b7",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -156,11 +156,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1775087534, "lastModified": 1777988971,
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=", "narHash": "sha256-qIoWPDs+0/8JecyYgE3gpKQxW/4bLW/gp45vow9ioCQ=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b", "rev": "0678d8986be1661af6bb555f3489f2fdfc31f6ff",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -177,11 +177,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1775087534, "lastModified": 1777988971,
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=", "narHash": "sha256-qIoWPDs+0/8JecyYgE3gpKQxW/4bLW/gp45vow9ioCQ=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b", "rev": "0678d8986be1661af6bb555f3489f2fdfc31f6ff",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -273,11 +273,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1776777932, "lastModified": 1778248595,
"narHash": "sha256-0R3Yow/NzSeVGUke5tL7CCkqmss4Vmi6BbV6idHzq/8=", "narHash": "sha256-dhFgEjoeJMYN/7OY6xfxS799YB4IjbbYXTjyGIJyLpc=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "5d5640599a0050b994330328b9fd45709c909720", "rev": "fdb2ccba9d5e1238d32e0c4a3ec1a277efa80c1d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -317,11 +317,11 @@
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
}, },
"locked": { "locked": {
"lastModified": 1776729909, "lastModified": 1778285091,
"narHash": "sha256-wGu/N42PJqrj8ju9GoXdppg4rwaKzZqdAjsgxJbCvfY=", "narHash": "sha256-4YwkGkjvLD0EB7rQGCRA9J/zgwrnTL20dJd7Wmnicj0=",
"owner": "nix-community", "owner": "nix-community",
"repo": "neovim-nightly-overlay", "repo": "neovim-nightly-overlay",
"rev": "ff21a18bde28b4c8ca0bc1f9a5b7186a1b89a3d1", "rev": "cca2a2d1c03f763fdcd7066791363d792313c641",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -333,11 +333,11 @@
"neovim-src": { "neovim-src": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1776727374, "lastModified": 1778266020,
"narHash": "sha256-iP5SviNXW5W+ay4ZmwjDFsfQjfM+fYlUxRlLPHjpwWI=", "narHash": "sha256-qoydKalrn/QGsGYVRicz0Hzb7bfGmV7Z9CnVONXN/Lc=",
"owner": "neovim", "owner": "neovim",
"repo": "neovim", "repo": "neovim",
"rev": "901b3f0c394a53961781ebeee682e64ad690a242", "rev": "b7d8a41d91dcfebe9a5f3d0cf2f0bb0b8d59e32e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -348,11 +348,11 @@
}, },
"nixos-hardware": { "nixos-hardware": {
"locked": { "locked": {
"lastModified": 1775490113, "lastModified": 1778143761,
"narHash": "sha256-2ZBhDNZZwYkRmefK5XLOusCJHnoeKkoN95hoSGgMxWM=", "narHash": "sha256-lkesY6x2X2qxlqLM7CT2iM/0rP2JB7fruPN3h8POXmI=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixos-hardware", "repo": "nixos-hardware",
"rev": "c775c2772ba56e906cbeb4e0b2db19079ef11ff7", "rev": "3bcaa367d4c550d687a17ac792fd5cda214ee871",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -364,11 +364,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1776329215, "lastModified": 1778124196,
"narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=", "narHash": "sha256-pYEytCNic/czazbV9r3tbQ6BZzqRBg/41x2dIC5ymOo=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b86751bc4085f48661017fa226dee99fab6c651b", "rev": "68a8af93ff4297686cb68880845e61e5e2e41d92",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -380,11 +380,11 @@
}, },
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"lastModified": 1774748309, "lastModified": 1777168982,
"narHash": "sha256-+U7gF3qxzwD5TZuANzZPeJTZRHS29OFQgkQ2kiTJBIQ=", "narHash": "sha256-GOkGPcboWE9BmGCRMLX3worL4EMnsnG8MyKmXNeYuhQ=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixpkgs.lib", "repo": "nixpkgs.lib",
"rev": "333c4e0545a6da976206c74db8773a1645b5870a", "rev": "f5901329dade4a6ea039af1433fb087bd9c1fe14",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -395,11 +395,11 @@
}, },
"nixpkgs-master": { "nixpkgs-master": {
"locked": { "locked": {
"lastModified": 1776807375, "lastModified": 1778360830,
"narHash": "sha256-LDnHG0T54OEHyRydmGUlAND8ham0KrRNWjgoS+6GUd4=", "narHash": "sha256-tD44tgf123UcERx3cC91rwefFmGmlTd2M1QdL6d5iLc=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "553ecb1686a2edb75dee44c9f72e1674e6adc26a", "rev": "82cbc979e10cf2b893566a0f259daf5e1f26c887",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -411,11 +411,11 @@
}, },
"nixpkgs-stable": { "nixpkgs-stable": {
"locked": { "locked": {
"lastModified": 1776560675, "lastModified": 1778003029,
"narHash": "sha256-p68udKWWh7+V4ZPpcMDq0gTHWNZJnr4JPI+kHPPE40o=", "narHash": "sha256-q/nkKLDtHIyLjZpKhWk3cSK5IYsFqtMd6UtXF3ddjgA=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e07580dae39738e46609eaab8b154de2488133ce", "rev": "0c88e1f2bdb93d5999019e99cb0e61e1fe2af4c5",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -427,11 +427,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1776548001, "lastModified": 1777954456,
"narHash": "sha256-ZSK0NL4a1BwVbbTBoSnWgbJy9HeZFXLYQizjb2DPF24=", "narHash": "sha256-hGdgeU2Nk87RAuZyYjyDjFL6LK7dAZN5RE9+hrDTkDU=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b12141ef619e0a9c1c84dc8c684040326f27cdcc", "rev": "549bd84d6279f9852cae6225e372cc67fb91a4c1",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -453,11 +453,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1775228139, "lastModified": 1777598946,
"narHash": "sha256-ebbeHmg+V7w8050bwQOuhmQHoLOEOfqKzM1KgCTexK4=", "narHash": "sha256-X239dAGaU1+gfDj8jKH8GzlqKMcxaVfXOio+uzBOkeE=",
"owner": "nix-community", "owner": "nix-community",
"repo": "NUR", "repo": "NUR",
"rev": "601971b9c89e0304561977f2c28fa25e73aa7132", "rev": "5d55af01c0f86be583931fe99207fc56c14134b3",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -550,11 +550,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1776771786, "lastModified": 1777944972,
"narHash": "sha256-DRFGPfFV6hbrfO9a1PH1FkCi7qR5FgjSqsQGGvk1rdI=", "narHash": "sha256-VfGRo1qTBKOe3s2gOv8LSoA6Fk19PvBlwQ1ECN0Evn8=",
"owner": "Mic92", "owner": "Mic92",
"repo": "sops-nix", "repo": "sops-nix",
"rev": "bef289e2248991f7afeb95965c82fbcd8ff72598", "rev": "c591bf665727040c6cc5cb409079acb22dcce33c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -583,11 +583,11 @@
"tinted-zed": "tinted-zed" "tinted-zed": "tinted-zed"
}, },
"locked": { "locked": {
"lastModified": 1776170745, "lastModified": 1778104276,
"narHash": "sha256-Tl1aZVP5EIlT+k0+iAKH018GLHJpLz3hhJ0LNQOWxCc=", "narHash": "sha256-/DSSnU0LLmOTG/OCgGwYpxP6+5YvxRx2g/GhI4x6aCU=",
"owner": "danth", "owner": "danth",
"repo": "stylix", "repo": "stylix",
"rev": "e3861617645a43c9bbefde1aa6ac54dd0a44bfa9", "rev": "18ed8d270231e067fe2739998479ed5d7c659c2c",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -630,11 +630,11 @@
"tinted-schemes": { "tinted-schemes": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1772661346, "lastModified": 1777041405,
"narHash": "sha256-4eu3LqB9tPqe0Vaqxd4wkZiBbthLbpb7llcoE/p5HT0=", "narHash": "sha256-BAGZ7ObFV/9Z61OJZun7ifPyhkuHqNuW1QIhQ8LuzCo=",
"owner": "tinted-theming", "owner": "tinted-theming",
"repo": "schemes", "repo": "schemes",
"rev": "13b5b0c299982bb361039601e2d72587d6846294", "rev": "5f868b3a338b6904c47f3833b9c411be641983a8",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -646,11 +646,11 @@
"tinted-tmux": { "tinted-tmux": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1772934010, "lastModified": 1777169200,
"narHash": "sha256-x+6+4UvaG+RBRQ6UaX+o6DjEg28u4eqhVRM9kpgJGjQ=", "narHash": "sha256-h7dDbIzP5hDr9v97w9PL6jdAgXawmj6krcH+959rqpU=",
"owner": "tinted-theming", "owner": "tinted-theming",
"repo": "tinted-tmux", "repo": "tinted-tmux",
"rev": "c3529673a5ab6e1b6830f618c45d9ce1bcdd829d", "rev": "f798c2dce44ef815bb6b8f05a82135c7942d35ac",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -662,11 +662,11 @@
"tinted-zed": { "tinted-zed": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1772909925, "lastModified": 1777463218,
"narHash": "sha256-jx/5+pgYR0noHa3hk2esin18VMbnPSvWPL5bBjfTIAU=", "narHash": "sha256-Bhkozqtq3BKLqWTlmKm8uAptfX4aRGI8QX3eEL54Vpc=",
"owner": "tinted-theming", "owner": "tinted-theming",
"repo": "base16-zed", "repo": "base16-zed",
"rev": "b4d3a1b3bcbd090937ef609a0a3b37237af974df", "rev": "5768d08ed2e7944a26a958868cdb073cb8856dae",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -55,7 +55,6 @@ in
"git" "git"
"gnupg" "gnupg"
"harmonia" "harmonia"
"initrd-ssh"
"localisation" "localisation"
"neovim" "neovim"
"networkmanager" "networkmanager"
@@ -124,6 +123,7 @@ in
"localisation" "localisation"
"networkmanager" "networkmanager"
"nix-settings" "nix-settings"
"onepassword"
"sway" "sway"
"udev" "udev"
"zsh" "zsh"

View File

@@ -1,5 +1,7 @@
_: { inputs, ... }:
{ {
flake.overlays.default = _: _: { }; flake.overlays.default = final: _prev: {
inherit (inputs.nixpkgs-stable.legacyPackages.${final.stdenv.hostPlatform.system}) mcp-nixos;
};
} }

View File

@@ -13,19 +13,38 @@
documentation.enable = false; documentation.enable = false;
environment.defaultPackages = [ ]; environment.defaultPackages = [ ];
# compressed qcow2, no channel copy # qcow2, no channel copy; post-processed with parallel zstd on qcow2 v3
# (~half the size of zlib v2, faster decompress)
image.modules.qemu = image.modules.qemu =
{ config, modulesPath, ... }: { config, modulesPath, ... }:
{ {
system.build.image = lib.mkForce ( system.build.image = lib.mkForce (
import (modulesPath + "/../lib/make-disk-image.nix") { let
inherit lib config pkgs; rawImage = import (modulesPath + "/../lib/make-disk-image.nix") {
inherit (config.virtualisation) diskSize; inherit lib config pkgs;
inherit (config.virtualisation) diskSize;
inherit (config.image) baseName;
format = "qcow2";
copyChannel = false;
partitionTableType = "legacy";
};
inherit (config.image) baseName; inherit (config.image) baseName;
format = "qcow2-compressed"; in
copyChannel = false; pkgs.runCommand baseName { nativeBuildInputs = [ pkgs.qemu-utils ]; } ''
partitionTableType = "legacy"; mkdir -p $out
} # qemu-img caps -m at 16
cores="''${NIX_BUILD_CORES:-4}"
[ "$cores" -gt 0 ] || cores=4
[ "$cores" -gt 16 ] && cores=16
qemu-img convert \
-f qcow2 \
-O qcow2 \
-c \
-o compression_type=zstd \
-m "$cores" \
${rawImage}/${baseName}.qcow2 \
$out/${baseName}.qcow2
''
); );
}; };
@@ -70,7 +89,7 @@
features.neovim.dotfiles = inputs.nvim; features.neovim.dotfiles = inputs.nvim;
# ensure .config exists with correct ownership before automount # ensure .config exists with correct ownership before automount
systemd.tmpfiles.rules = [ "d /home/matej/.config 0755 matej users -" ]; systemd.tmpfiles.rules = [ "d /home/matej/.config 0700 matej users -" ];
# TODO:(@janezicmatej) replace ssh with virtio-console (hvc0) when qemu 11.0 lands # TODO:(@janezicmatej) replace ssh with virtio-console (hvc0) when qemu 11.0 lands
# https://www.mail-archive.com/qemu-devel@nongnu.org/msg1162844.html # https://www.mail-archive.com/qemu-devel@nongnu.org/msg1162844.html

View File

@@ -10,16 +10,13 @@
inputs.nixos-hardware.nixosModules.framework-16-amd-ai-300-series inputs.nixos-hardware.nixosModules.framework-16-amd-ai-300-series
]; ];
features.bootloader.plymouth.enable = true; features.bootloader.resumeDevice = "/dev/mapper/vg0-swap";
features.desktop.bluetooth.enable = true; features.desktop.bluetooth.enable = true;
features.gnupg.yubikey.enable = true; features.gnupg.yubikey.enable = true;
features.udev = { features.udev = {
ledger.enable = true; ledger.enable = true;
keyboard-zsa.enable = true; keyboard-zsa.enable = true;
}; };
features.power.resumeDevice = "/dev/disk/by-uuid/ff4750e7-3a9f-42c2-bb68-c458a6560540";
boot.kernelParams = [ "pcie_aspm.policy=powersupersave" ];
programs.nix-ld.libraries = options.programs.nix-ld.libraries.default; programs.nix-ld.libraries = options.programs.nix-ld.libraries.default;

View File

@@ -37,10 +37,7 @@
fileSystems."/boot" = { fileSystems."/boot" = {
device = "/dev/disk/by-uuid/42D9-FAFD"; device = "/dev/disk/by-uuid/42D9-FAFD";
fsType = "vfat"; fsType = "vfat";
options = [ options = [ "umask=0077" ];
"fmask=0022"
"dmask=0022"
];
}; };
swapDevices = [ swapDevices = [

View File

@@ -8,7 +8,11 @@
features.nix-settings.towerCache.enable = false; features.nix-settings.towerCache.enable = false;
features.bootloader = { features.bootloader = {
mode = "lanzaboote"; mode = "lanzaboote";
plymouth.enable = true; initrdSsh = {
enable = true;
networkModule = "r8169";
authorizedKeys = userKeys.sshAuthorizedKeys;
};
}; };
features.desktop.bluetooth.enable = true; features.desktop.bluetooth.enable = true;
features.gnupg.yubikey.enable = true; features.gnupg.yubikey.enable = true;
@@ -16,17 +20,14 @@
ledger.enable = true; ledger.enable = true;
keyboard-zsa.enable = true; keyboard-zsa.enable = true;
}; };
features.initrd-ssh = {
networkModule = "r8169";
authorizedKeys = userKeys.sshAuthorizedKeys;
};
# nix store signing # nix store signing
sops.secrets.nix-signing-key.sopsFile = ../../secrets/tower.yaml; sops.secrets.nix-signing-key.sopsFile = ../../secrets/tower.yaml;
nix.settings.secret-key-files = [ config.sops.secrets.nix-signing-key.path ]; nix.settings.secret-key-files = [ config.sops.secrets.nix-signing-key.path ];
boot.kernelParams = [ "btusb.reset=1" ]; boot.kernelParams = [ "btusb.reset=1" ];
# early kms so plymouth lands on amdgpu, not simpledrm # pairs with bootloader's simpledrm initcall blacklist: amdgpu owns fbcon
# from the start, no driver-swap mode-set
hardware.amdgpu.initrd.enable = true; hardware.amdgpu.initrd.enable = true;
services.udisks2.enable = true; services.udisks2.enable = true;

View File

@@ -2,31 +2,20 @@
default: default:
@just --list @just --list
# rebuild and switch # rebuild the system
switch config="": rebuild op="switch" host=`hostname`:
nixos-rebuild switch --flake .{{ if config != "" { "#" + config } else { "" } }} --sudo nixos-rebuild {{op}} --flake .#{{host}} --sudo
# fetch flake inputs
sync:
nix flake prefetch-inputs
# update flake inputs # update flake inputs
update: update:
nix flake update nix flake update
# update flake inputs, rebuild and switch
bump: update switch
# update a package to latest version
update-package pkg:
bash packages/{{pkg}}/update.sh
# update all packages with update scripts # update all packages with update scripts
update-package-all: update-package:
@for script in packages/*/update.sh; do bash "$script"; done @for script in packages/*/update.sh; do bash "$script"; done
# build all packages and hosts # build all packages and hosts
build: check:
nix flake check nix flake check
# build installation iso # build installation iso
@@ -37,10 +26,6 @@ iso:
ephvm *ARGS: ephvm *ARGS:
bash scripts/ephvm-run.sh {{ARGS}} bash scripts/ephvm-run.sh {{ARGS}}
# ssh into running ephemeral VM
ephvm-ssh port="2222":
ssh -p {{port}} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null matej@localhost
# provision a host with nixos-anywhere # provision a host with nixos-anywhere
provision host ip: provision host ip:
#!/usr/bin/env bash #!/usr/bin/env bash
@@ -59,9 +44,9 @@ provision host ip:
ssh root@{{ip}} reboot ssh root@{{ip}} reboot
# deploy config to a remote host # deploy config to a remote host
deploy host remote=host: deploy op="switch" host=`hostname` remote=host:
nixos-rebuild switch --flake .#{{host}} --target-host {{remote}} --sudo --ask-sudo-password nixos-rebuild {{op}} --flake .#{{host}} --target-host {{remote}} --sudo --ask-sudo-password
# garbage collect old generations # garbage collect old generations
clean: clean host=`hostname`:
sudo nix-collect-garbage $(nix eval --raw -f ./nix.nix nix.gc.options) sudo nix-collect-garbage $(nix eval --raw .#nixosConfigurations.{{host}}.config.nix.gc.options)

View File

@@ -87,6 +87,17 @@ nixpkgs.lib.nixosSystem {
{ nixpkgs.config.allowUnfree = true; } { nixpkgs.config.allowUnfree = true; }
{ networking.hostName = name; } { networking.hostName = name; }
# TEMP:(@janezicmatej) temporary mitigation for dirty frag
# blocks esp4/esp6 (CVE-2026-43284) and rxrpc (CVE-2026-43500)
# remove once nixpkgs ships a kernel with f4c50a4034e6 and the rxrpc fix
{
boot.blacklistedKernelModules = [
"esp4"
"esp6"
"rxrpc"
];
}
featureEnableModule featureEnableModule
hostConfig hostConfig
] ]

33
nix.nix
View File

@@ -1,33 +0,0 @@
{
nix = {
settings = {
experimental-features = [
"nix-command"
"flakes"
];
download-buffer-size = 2 * 1024 * 1024 * 1024;
warn-dirty = false;
substituters = [
"https://cache.nixos.org"
"https://nix-community.cachix.org?priority=45"
"http://tower:5000?priority=50"
];
trusted-public-keys = [
"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
"matej.nix-1:TdbemLVYblvAxqJcwb3mVKmmr3cfzXbMcZHE5ILnZDE="
];
};
gc = {
automatic = true;
dates = "monthly";
options = "--delete-older-than 30d";
};
optimise = {
automatic = true;
dates = [ "monthly" ];
};
};
}

View File

@@ -2,7 +2,7 @@
let let
inherit (pkgs) stdenv lib; inherit (pkgs) stdenv lib;
version = "2.1.116"; version = "2.1.138";
# upstream ships platform-native binaries as separate npm packages under # upstream ships platform-native binaries as separate npm packages under
# @anthropic-ai/claude-code-<platform>; the wrapper package is just a # @anthropic-ai/claude-code-<platform>; the wrapper package is just a
@@ -10,19 +10,19 @@ let
sources = { sources = {
"x86_64-linux" = { "x86_64-linux" = {
slug = "linux-x64"; slug = "linux-x64";
hash = "sha256-QEjJ4CRk35TubDNW02Dzcu+EMRLLndJUXJeP3BFT3b8="; hash = "sha256-MGYEPPO4O84Egb5Ym/9f56l+TzPqogpSabosvHTIJZg=";
}; };
"aarch64-linux" = { "aarch64-linux" = {
slug = "linux-arm64"; slug = "linux-arm64";
hash = "sha256-/Hqp8GQx8Hub8K4w0Fnx/AksksY61vRC44XxrJVwF5w="; hash = "sha256-LWBtOAjPDFtLP93TNrsd8bPHJd7VKK6J90CRxUp1/XQ=";
}; };
"x86_64-darwin" = { "x86_64-darwin" = {
slug = "darwin-x64"; slug = "darwin-x64";
hash = "sha256-O3J/ew2fWbUQePs6tHEhK0Q9E3Mx/BDSL7b7NL3FRc8="; hash = "sha256-tkupKzb+XAPmdCRNoT90cfVLKUar3FCTRgufiMVuVPc=";
}; };
"aarch64-darwin" = { "aarch64-darwin" = {
slug = "darwin-arm64"; slug = "darwin-arm64";
hash = "sha256-O41sf7b05SJfXVjszMeTp838mja+PgZ+aEKykLsHeNo="; hash = "sha256-jmB4t11BI1LKanuuXRJv5IBe8a9gSrFvTMP3KarsioU=";
}; };
}; };

View File

@@ -27,14 +27,32 @@ info() {
# globals for cleanup trap # globals for cleanup trap
CLEANUP_OVERLAY="" CLEANUP_OVERLAY=""
CLEANUP_TMPDIR=""
QEMU_PID="" QEMU_PID=""
VM_READY=false
cleanup() { cleanup() {
[ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null && wait "$QEMU_PID" 2>/dev/null [ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null && wait "$QEMU_PID" 2>/dev/null
[ -n "$CLEANUP_OVERLAY" ] && rm -rf "$CLEANUP_OVERLAY" [ -n "$CLEANUP_OVERLAY" ] && rm -rf "$CLEANUP_OVERLAY"
# preserve tmpdir on abnormal exit so the qemu log survives for inspection
if [ -n "$CLEANUP_TMPDIR" ]; then
if [ "$VM_READY" = true ]; then
rm -rf "$CLEANUP_TMPDIR"
else
echo "qemu log preserved: $CLEANUP_TMPDIR/qemu.log" >&2
fi
fi
return 0 return 0
} }
trap cleanup EXIT trap cleanup EXIT
# returns 0 once the guest's sshd is speaking (first bytes are "SSH-")
awaiting_ssh_banner() {
local port="$1"
local banner
banner=$(timeout 2 bash -c "exec 3<>/dev/tcp/localhost/$port; head -c 4 <&3" 2>/dev/null) || return 1
[ "$banner" = "SSH-" ]
}
usage() { usage() {
cat <<EOF cat <<EOF
Usage: ephvm-run.sh [options] Usage: ephvm-run.sh [options]
@@ -55,6 +73,8 @@ EOF
main() { main() {
setup_colors setup_colors
[ "$EUID" -eq 0 ] && die "ephvm-run.sh must not run as root"
local ssh_port="" memory=4G cpus=2 claude=true disk_size="" serial=false local ssh_port="" memory=4G cpus=2 claude=true disk_size="" serial=false
local -a mounts=() local -a mounts=()
@@ -110,15 +130,13 @@ main() {
CLEANUP_OVERLAY=$(mktemp -d) CLEANUP_OVERLAY=$(mktemp -d)
local overlay="$CLEANUP_OVERLAY/overlay.qcow2" local overlay="$CLEANUP_OVERLAY/overlay.qcow2"
qemu-img create -f qcow2 -b "$(realpath "$image")" -F qcow2 "$overlay" "$disk_size" qemu-img create -f qcow2 -b "$(realpath "$image")" -F qcow2 "$overlay" "$disk_size"
drive_arg="file=$overlay,format=qcow2" drive_arg="if=none,id=hd0,file=$overlay,format=qcow2,cache=writeback,aio=threads,discard=unmap,detect-zeroes=unmap"
else else
drive_arg="file=$image,format=qcow2,snapshot=on" drive_arg="if=none,id=hd0,file=$image,format=qcow2,snapshot=on,cache=writeback,aio=threads,discard=unmap,detect-zeroes=unmap"
fi fi
command -v qemu-system-x86_64 &>/dev/null || die "qemu-system-x86_64 not found" command -v qemu-system-x86_64 &>/dev/null || die "qemu-system-x86_64 not found"
[ -r /dev/kvm ] || die "/dev/kvm not readable; kvm is required"
local accel="tcg"
[ -r /dev/kvm ] && accel="kvm"
# auto-allocate ssh port unless serial mode # auto-allocate ssh port unless serial mode
if [ "$serial" = false ] && [ -z "$ssh_port" ]; then if [ "$serial" = false ] && [ -z "$ssh_port" ]; then
@@ -128,28 +146,33 @@ main() {
done done
fi fi
local nic_arg="user" local nic_arg="user,model=virtio-net-pci"
if [ -n "$ssh_port" ]; then if [ -n "$ssh_port" ]; then
nic_arg="user,hostfwd=tcp::${ssh_port}-:22" nic_arg="user,model=virtio-net-pci,hostfwd=tcp:127.0.0.1:${ssh_port}-:22"
fi fi
local -a qemu_args=( local -a qemu_args=(
qemu-system-x86_64 qemu-system-x86_64
-accel "$accel" -accel kvm
-cpu host
-m "$memory" -m "$memory"
-smp "$cpus" -smp "$cpus"
-drive "$drive_arg" -drive "$drive_arg"
-device "virtio-blk-pci,drive=hd0"
-device virtio-rng-pci
-nic "$nic_arg" -nic "$nic_arg"
-nographic -nographic
-sandbox "on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny"
) )
if [ "$accel" != "tcg" ]; then
qemu_args+=(-cpu host)
fi
local fs_id=0 mount_path name tag local fs_id=0 mount_path name tag
for mount_path in "${mounts[@]}"; do for mount_path in "${mounts[@]}"; do
[ -e "$mount_path" ] || die "--mount path does not exist: $mount_path"
mount_path=$(realpath "$mount_path") mount_path=$(realpath "$mount_path")
# qemu parses -virtfs as csv, a comma in the path would inject options
case "$mount_path" in
*,*) die "--mount path may not contain commas: $mount_path" ;;
esac
name=$(basename "$mount_path") name=$(basename "$mount_path")
tag="m_${name:0:29}" tag="m_${name:0:29}"
qemu_args+=( qemu_args+=(
@@ -163,6 +186,9 @@ main() {
mkdir -p "$CLAUDE_CONFIG_DIR" mkdir -p "$CLAUDE_CONFIG_DIR"
local claude_dir local claude_dir
claude_dir=$(realpath "$CLAUDE_CONFIG_DIR") claude_dir=$(realpath "$CLAUDE_CONFIG_DIR")
case "$claude_dir" in
*,*) die "claude config dir may not contain commas: $claude_dir" ;;
esac
qemu_args+=( qemu_args+=(
-virtfs "local,path=$claude_dir,mount_tag=claude,security_model=none,id=fs${fs_id}" -virtfs "local,path=$claude_dir,mount_tag=claude,security_model=none,id=fs${fs_id}"
@@ -171,27 +197,38 @@ main() {
fi fi
info "---" info "---"
info "Accel: $accel" [ -n "$ssh_port" ] && info "SSH: ssh -p $ssh_port matej@localhost"
info "---" info "---"
if [ "$serial" = true ]; then if [ "$serial" = true ]; then
exec "${qemu_args[@]}" exec "${qemu_args[@]}"
fi fi
CLEANUP_TMPDIR=$(mktemp -d)
local qemu_log="$CLEANUP_TMPDIR/qemu.log"
# start qemu in background and auto-ssh # start qemu in background and auto-ssh
"${qemu_args[@]}" &>/dev/null & "${qemu_args[@]}" &>"$qemu_log" &
QEMU_PID=$! QEMU_PID=$!
# throwaway ssh key (vm accepts any key via AuthorizedKeysCommand)
local ssh_key="$CLEANUP_TMPDIR/id_ed25519"
ssh-keygen -t ed25519 -f "$ssh_key" -N "" -q
info "waiting for vm (port $ssh_port)..." info "waiting for vm (port $ssh_port)..."
local attempts=0 local attempts=0
while ! (echo > /dev/tcp/localhost/"$ssh_port") 2>/dev/null; do # poll for the real SSH banner, not TCP accept: qemu's user-mode nic
# accepts host-side the moment qemu starts, well before guest sshd is up
while ! awaiting_ssh_banner "$ssh_port"; do
attempts=$((attempts + 1)) attempts=$((attempts + 1))
[ $attempts -gt 60 ] && die "vm did not become ready in 60s" [ $attempts -gt 120 ] && die "vm did not become ready in 60s"
kill -0 "$QEMU_PID" 2>/dev/null || die "qemu exited unexpectedly" kill -0 "$QEMU_PID" 2>/dev/null || die "qemu exited unexpectedly"
sleep 1 sleep 0.5
done done
VM_READY=true
ssh -p "$ssh_port" -t \ ssh -p "$ssh_port" -t \
-i "$ssh_key" \
-o StrictHostKeyChecking=no \ -o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \ -o UserKnownHostsFile=/dev/null \
-o LogLevel=ERROR \ -o LogLevel=ERROR \