plain: one x86_64 target, no host branching

Drops the mbp (Asahi) target and everything that existed only to serve
two machines at once:

  - apple-silicon input, firmware/ (87M of Mac Wi-Fi/BT blobs), hosts/mbp/
  - the mkHost helper and the common-vs-host-module split, merged into one
    `pc` module
  - the osConfig.networking.hostName conditional that generated
    hypr/monitors.conf per host -- the monitor layout, workspace placement
    and panel float rule are now stated once, inline in hyprland.conf
  - the aarch64 nixbuild.net builder and the aarch64 devShell
  - Mac-only hyprland bits: the apple-spi-keyboard device block and the
    touchpad input block

hosts/pc/hardware-configuration.nix moves to the repo root: with one
machine there is no host to namespace it under. The flake output stays
named `pc` so it matches networking.hostName, which means plain
`nixos-rebuild switch --flake ~/nixos` resolves it with no #target.

eww goes too. It only ever drew a battery popup (SUPER+R), and a battery
readout on a desktop with no battery is nothing.

Also fixes the services.logind.suspendKey/hibernateKey/lidSwitch rename
that eval has been warning about; the rendered logind.conf keeps the same
three Handle*=ignore directives.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mads Fagerlund 2026-10-10 19:34:53 +02:00
parent e2085e0e86
commit 64a399597a
No known key found for this signature in database
14 changed files with 103 additions and 409 deletions

View file

@ -1,64 +1,48 @@
# nixos
One flake, two machines. Hyprland + greetd autologin, Zen+Tridactyl, neovim, zsh+starship.
| host | arch | build |
|------|------|-------|
| `mbp` | aarch64 | MacBook M1 Pro (Asahi) |
| `pc` | x86_64 | any UEFI x86 |
One flake, one machine: x86_64 UEFI desktop. Hyprland + greetd autologin,
Zen+Tridactyl, neovim, zsh+starship.
```
flake.nix all config (common + home modules, both hosts)
config/ raw configs: hypr eww fuzzel nvim
hosts/*/hardware-configuration.nix mbp: regenerate on Mac · pc: generic, as-is
firmware/ Mac Wi-Fi/BT blobs — git-add at install. KEEP REPO PRIVATE.
flake.nix all config (the `pc` system module + the `home` module)
hardware-configuration.nix generic x86_64 UEFI profile — mounts by label, as-is
config/ raw configs: hypr fuzzel nvim
```
Set `time.timeZone` in `common`. Rename a host = change the `nixosConfigurations.<name>` key + its `networking.hostName`.
Set `time.timeZone` in the `pc` module. Monitors, workspace placement and
keybinds live in `config/hypr/hyprland.conf`.
## Apply
The flake output is named after the hostname, so no `#target` is needed:
```sh
sudo nixos-rebuild switch --flake ~/nixos#mbp # or #pc
nix flake update && sudo nixos-rebuild switch --flake ~/nixos#mbp # update
sudo nixos-rebuild switch --flake ~/nixos
nix flake update && sudo nixos-rebuild switch --flake ~/nixos # update
```
Roll back = pick an older generation at boot.
## Install: MacBook (Asahi)
## Install
Full/latest guide: <https://github.com/nix-community/nixos-apple-silicon/blob/main/docs/uefi-standalone.md>. **Back up — a bad partition can brick the Mac.**
No hardware generation needed — `hardware-configuration.nix` is generic and
mounts by the labels below. Boot the NixOS ISO, `sudo -i`:
1. macOS Terminal: `curl https://alx.sh | sh` → `r` resize (≥20 GB free) → `f` install **UEFI environment only**, name `NixOS` → recovery: permissive security. Reboots to U-Boot; power off.
2. Installer USB (on a Linux+Nix box): `nix build github:nix-community/nixos-apple-silicon#installer-bootstrap -o installer` then `dd` the ISO to USB.
3. Boot USB → `sudo su`:
```sh
sgdisk /dev/nvme0n1 -n 0:0 -s && sgdisk /dev/nvme0n1 -p # note new 8300 part, e.g. p5
mkfs.ext4 -L nixos /dev/nvme0n1p5
mount /dev/disk/by-label/nixos /mnt && mkdir -p /mnt/boot
mount /dev/disk/by-partuuid/$(cat /proc/device-tree/chosen/asahi,efi-system-partition) /mnt/boot
nixos-generate-config --root /mnt
git clone <private-remote> /mnt/root/nixos && cd /mnt/root/nixos
cp /mnt/etc/nixos/hardware-configuration.nix hosts/mbp/
mkdir -p firmware && cp /mnt/boot/asahi/{all_firmware.tar.gz,kernelcache*} firmware/
git add -f hosts/mbp/hardware-configuration.nix firmware/*
nixos-install --flake .#mbp && reboot
```
## Install: x86
No hardware generation — `hosts/pc/hardware-configuration.nix` is generic (mounts by label). Boot the NixOS ISO, `sudo -i`:
```sh
parted /dev/nvme0n1 -- mklabel gpt \
mkpart ESP fat32 1MiB 512MiB set 1 esp on \
mkpart primary 512MiB 100%
mkfs.fat -F32 -n BOOT /dev/nvme0n1p1
mkfs.ext4 -L nixos /dev/nvme0n1p2
mount /dev/disk/by-label/nixos /mnt && mkdir -p /mnt/boot && mount /dev/disk/by-label/BOOT /mnt/boot
mkfs.fat -F32 -n NIXBOOT /dev/nvme0n1p1
mkfs.ext4 -L NIXROOT /dev/nvme0n1p2
mount /dev/disk/by-label/NIXROOT /mnt && mkdir -p /mnt/boot
mount /dev/disk/by-label/NIXBOOT /mnt/boot
git clone <private-remote> /mnt/root/nixos && cd /mnt/root/nixos
nixos-install --flake .#pc && reboot
```
Exotic storage controller won't boot? Add its module to `availableKernelModules` in `hosts/pc/hardware-configuration.nix` (`nixos-generate-config --show-hardware-config` lists it).
Exotic storage controller won't boot? Add its module to
`availableKernelModules` in `hardware-configuration.nix`
(`nixos-generate-config --show-hardware-config` lists it).
## After first boot
`passwd` (ships `initialPassword = "changeme"`). If you land on a tty instead of Hyprland, change greetd's `uwsm start hyprland` → `Hyprland` in `common`.
`passwd` (ships `initialPassword = "changeme"`). If you land on a tty instead
of Hyprland, change greetd's `uwsm start hyprland` → `Hyprland`.

View file

@ -1,14 +0,0 @@
* {
all: unset;
}
.battery-popup {
background-color: #16181a;
padding: 6px 10px;
}
.battery-popup-text {
font-family: monospace;
font-size: 14px;
color: #ddd;
}

View file

@ -1,17 +0,0 @@
(defpoll battery-percent :interval "10s" "scripts/battery-capacity")
(defpoll battery-status :interval "10s" "scripts/battery-status")
(defwidget battery-popup []
(box :class "battery-popup"
:halign "center"
:valign "center"
(label :class "battery-popup-text"
:text "${battery-percent}% ${battery-status}")))
(defwindow battery-popup
:monitor 0
:geometry (geometry :anchor "center")
:stacking "overlay"
:exclusive false
:focusable false
(battery-popup))

View file

@ -1,8 +0,0 @@
#!/bin/sh
# Print the charge % of the first real battery. Portable across machines
# (macsmc-battery on Asahi, BAT0/BAT1 on x86).
for d in /sys/class/power_supply/*; do
[ "$(cat "$d/type" 2>/dev/null)" = "Battery" ] || continue
cat "$d/capacity" 2>/dev/null && exit 0
done
echo "?"

View file

@ -1,7 +0,0 @@
#!/bin/sh
# Print the charging status of the first real battery. Portable across machines.
for d in /sys/class/power_supply/*; do
[ "$(cat "$d/type" 2>/dev/null)" = "Battery" ] || continue
cat "$d/status" 2>/dev/null && exit 0
done
echo "Unknown"

View file

@ -1,12 +1,14 @@
# -- Monitors & input defaults --
# Monitor layout is host-specific; written by the flake per host.
source = ~/.config/hypr/monitors.conf
# -- Monitors --
monitor = desc:Lenovo Group Limited P27q-20 V909LRFR, 2560x1440@60, 0x0, 1.0
monitor = desc:Lenovo Group Limited P27q-20 V909LPNY, 2560x1440@60, 2560x0, 1.0
monitor = desc:Lenovo Group Limited P27q-20 V909LRDD, 2560x1440@60, 5120x0, 1.0
device {
name = apple-spi-keyboard
kb_options = caps:ctrl_modifier,grp:win_space_toggle
}
# Each screen's home workspace; SUPER+ALT+h/l can still move it.
workspace = 1, monitor:desc:Lenovo Group Limited P27q-20 V909LRFR, default:true
workspace = 2, monitor:desc:Lenovo Group Limited P27q-20 V909LPNY, default:true
workspace = 3, monitor:desc:Lenovo Group Limited P27q-20 V909LRDD, default:true
# -- Input --
# TrackPoint (Corne nipple): hold middle button + move to scroll, joystick-style
device {
name = corne-tp
@ -23,13 +25,6 @@ input {
repeat_delay = 300
accel_profile = custom
scroll_points = 0.5 1.0 1.5 2.0 2.5
touchpad {
natural_scroll = true
scroll_factor = 0.9
tap-to-click = false
clickfinger_behavior = true # 2-finger click = right-click, 3-finger = middle (Mac-like)
disable_while_typing = true
}
}
general {
@ -77,14 +72,11 @@ exec-once = uwsm finalize
# also unlocks the keyring (see security.pam.services.hyprlock in flake.nix).
exec-once = hyprlock
exec-once = swaybg -c '#16181a'
exec-once = GDK_BACKEND=wayland eww daemon
exec-once = systemctl --user start hyprpolkitagent
# -- Core --
bind = SUPER, Return, exec, kitty
bind = SUPER, D, exec, fuzzel
bind = SUPER, R, exec, eww open battery-popup
bindr = SUPER, R, exec, eww close battery-popup
bind = SUPER, Q, killactive
bind = SUPER, F, fullscreen, 1
bind = SUPER SHIFT, Space, togglefloating
@ -97,9 +89,10 @@ bind = SUPER, A, exec, caffeine
# Each gets its own app_id so the rule below floats only these and not every
# terminal. Quit with the TUI's own `q`; no toggle wrapper.
#
# The float/size rule is in monitors.conf, generated per host: Hyprland 0.56
# `size` takes ONLY absolute pixels -- percentages are silently ignored (they
# parse fine and then do nothing), so the value depends on the resolution.
# Hyprland 0.56 `size` takes ONLY absolute pixels -- percentages are silently
# ignored (they parse fine and then do nothing), so this is 70% of 2560x1440.
windowrule = match:class ^(panel-wifi|panel-bt)$, float on, size 1792 1440, center on
bind = SUPER, W, exec, kitty --class panel-wifi impala
bind = SUPER, E, exec, kitty --class panel-bt bluetui

View file

@ -1,28 +0,0 @@
# Asahi peripheral firmware (vendored)
`hardware.asahi.peripheralFirmwareDirectory` points here. A **pure** flake
cannot read `/boot/vendorfw`, so the Wi-Fi/Bluetooth firmware must live inside
the repo tree and be git-tracked.
Since nixos-apple-silicon 66d8dd2 the module expects the pre-extracted
`firmware.cpio` (placed on the ESP by the Asahi installer) instead of
`all_firmware.tar.gz` + `kernelcache.*`.
## During install, on the Mac:
```sh
cp /boot/vendorfw/firmware.cpio ~/.dotfiles/firmware/
git -C ~/.dotfiles add -f firmware/firmware.cpio
```
(They're `.gitignore`-exempt via `-f` if needed — but the flake only sees
**tracked** files, so they must be added.)
## ⚠ Keep this repository PRIVATE
This firmware is **non-free and non-redistributable**. Do not push it to a
public remote.
Until you copy these files in, `nixos-rebuild`/`nixos-install` will fail at the
firmware-extraction step — that's expected, same as the placeholder
`hardware-configuration.nix`.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,26 +1,5 @@
{
"nodes": {
"apple-silicon": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1785426242,
"narHash": "sha256-QHAP8KsJQmI+dpNS/wfWAtEBQ0Zby+B0Ty+qZIO/U2w=",
"owner": "nix-community",
"repo": "nixos-apple-silicon",
"rev": "66d8dd2c27f99bd5420c99938b60695aac1785c4",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixos-apple-silicon",
"type": "github"
}
},
"firefox-addons": {
"inputs": {
"nixpkgs": [
@ -43,21 +22,6 @@
"type": "gitlab"
}
},
"flake-compat": {
"locked": {
"lastModified": 1761640442,
"narHash": "sha256-AtrEP6Jmdvrqiv4x2xa5mrtaIp3OEe8uBYCDZDS+hu8=",
"owner": "nix-community",
"repo": "flake-compat",
"rev": "4a56054d8ffc173222d09dad23adf4ba946c8884",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "flake-compat",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
@ -113,7 +77,6 @@
},
"root": {
"inputs": {
"apple-silicon": "apple-silicon",
"firefox-addons": "firefox-addons",
"home-manager": "home-manager",
"linear-tui-src": "linear-tui-src",

272
flake.nix
View file

@ -1,14 +1,9 @@
{
description = "mads — NixOS for Apple Silicon (mbp) and x86 (pc), one flake";
description = "mads — NixOS on x86_64, one flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
apple-silicon = {
url = "github:nix-community/nixos-apple-silicon";
inputs.nixpkgs.follows = "nixpkgs";
};
home-manager = {
url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs";
@ -34,7 +29,7 @@
};
};
outputs = { self, nixpkgs, apple-silicon, home-manager, firefox-addons, zen-browser, linear-tui-src, ... }:
outputs = { self, nixpkgs, home-manager, firefox-addons, zen-browser, linear-tui-src, ... }:
let
# linear-tui, built from the pinned source input above. Upstream ships no
# flake and it is not in nixpkgs, but it has a committed Cargo.lock, so
@ -55,48 +50,17 @@
meta.mainProgram = "linear-tui";
};
# ───────────────────── shared home-manager user ─────────────────────
home = { pkgs, osConfig, ... }: {
# ───────────────────────── home-manager user ─────────────────────────
home = { pkgs, ... }: {
imports = [ zen-browser.homeModules.beta ];
home.stateVersion = "25.11";
programs.eww = {
enable = true;
package = pkgs.eww;
};
# Raw configs that stay files (no good native module).
# `hypr` is recursive so we can drop a host-specific monitors.conf in
# alongside the shared files (hyprland.conf `source`s it).
xdg.configFile = {
"hypr" = { source = ./config/hypr; recursive = true; };
"hypr/monitors.conf".text =
if osConfig.networking.hostName == "mbp"
then ''
monitor = , preferred, auto, 2.0
# Floating TUI panels: 70% wide, full height. Absolute px because
# Hyprland 0.56 ignores percentages in `size`. 1728x1080 logical.
windowrule = match:class ^(panel-wifi|panel-bt)$, float on, size 1210 1080, center on
''
else ''
monitor = desc:Lenovo Group Limited P27q-20 V909LRFR, 2560x1440@60, 0x0, 1.0
monitor = desc:Lenovo Group Limited P27q-20 V909LPNY, 2560x1440@60, 2560x0, 1.0
monitor = desc:Lenovo Group Limited P27q-20 V909LRDD, 2560x1440@60, 5120x0, 1.0
# Each screen's home workspace; SUPER+ALT+h/l can still move it.
workspace = 1, monitor:desc:Lenovo Group Limited P27q-20 V909LRFR, default:true
workspace = 2, monitor:desc:Lenovo Group Limited P27q-20 V909LPNY, default:true
workspace = 3, monitor:desc:Lenovo Group Limited P27q-20 V909LRDD, default:true
# Same panels, sized for 2560x1440 logical.
windowrule = match:class ^(panel-wifi|panel-bt)$, float on, size 1792 1440, center on
'';
"eww".source = ./config/eww;
"fuzzel".source = ./config/fuzzel;
"hypr".source = ./config/hypr;
"fuzzel".source = ./config/fuzzel;
};
# Launcher hygiene. These .desktop entries ship with packages we do want,
# but they are noise in fuzzel. A NoDisplay entry in XDG_DATA_HOME
# shadows the package's own copy, which is the only way to hide one
@ -125,7 +89,7 @@
# Notification daemon. Nothing owned org.freedesktop.Notifications before
# this, so notify-send failed outright: hyprshot's "Screenshot saved"
# toasts went nowhere, and kitty logged a DBus ServiceUnknown error on
# every launch. Colours match the cyberdream kitty theme above.
# every launch. Colours match the cyberdream kitty theme below.
services.mako = {
enable = true;
settings = {
@ -222,7 +186,7 @@
];
};
# Zen browser + Tridactyl, extensions pinned (works on aarch64 + x86).
# Zen browser + Tridactyl, extensions pinned.
programs.zen-browser = {
enable = true;
setAsDefaultBrowser = true;
@ -253,7 +217,7 @@
];
};
};
# zsh + starship, no framework.
programs.zsh = {
enable = true;
@ -263,8 +227,6 @@
};
programs.starship.enable = true;
# SSH auth via the 1Password agent — private keys stay in the vault,
# never on disk or in the Nix store.
programs.ssh = {
@ -294,8 +256,11 @@
home.packages = with pkgs; [ ripgrep fd tridactyl-native lazygit rustc cargo clippy rustfmt rust-analyzer claude-code gcc bluetui impala postgresql fastfetch (linearTui pkgs) ];
};
# ───────────────────── shared system (both hosts) ─────────────────────
common = { pkgs, ... }: {
# ──────────────────────────── the machine ────────────────────────────
pc = { config, pkgs, ... }: {
networking.hostName = "pc";
nixpkgs.config.allowUnfree = true;
nix.settings.experimental-features = [ "nix-command" "flakes" ];
# Trusted so client-side nix config takes effect — substituters and
# remote builders are trusted settings, silently ignored for anyone
@ -315,9 +280,7 @@
# the only form the calling process cannot read.
nix.settings.netrc-file = "/etc/nix/netrc";
# x86_64 derivations build off this machine rather than under local
# qemu emulation, and aarch64 ones go out too when the local
# machine is busy.
# Big builds go out to other machines rather than tying this one up.
#
# The host keys are pinned system-wide because the ssh'ing party is
# the root daemon, which has no known_hosts and never gets a tty to
@ -342,8 +305,8 @@
maxJobs = 16;
supportedFeatures = [ "big-parallel" "kvm" "nixos-test" ];
}
# nixbuild.net, for both architectures. Listed after `builder` so
# the machine we already pay for is preferred.
# nixbuild.net. Listed after `builder` so the machine we already pay
# for is preferred.
#
# Every feature it supports is advertised, not just the two in the
# quickstart: nix only sends a derivation to a builder claiming
@ -358,15 +321,6 @@
maxJobs = 100;
supportedFeatures = [ "benchmark" "big-parallel" "ca-derivations" "kvm" "nixos-test" ];
}
{
hostName = "eu.nixbuild.net";
sshUser = "root";
sshKey = "/home/mads/.ssh/id_ed25519";
protocol = "ssh-ng";
system = "aarch64-linux";
maxJobs = 100;
supportedFeatures = [ "benchmark" "big-parallel" "ca-derivations" "kvm" "nixos-test" ];
}
];
# iwd owns Wi-Fi (driven from impala, which is iwd-only); networkd does
@ -377,6 +331,19 @@
networking.useNetworkd = true; # DHCP on any ethernet/wifi by default
systemd.network.wait-online.anyInterface = true; # one link up is enough
# GigE camera link: static, never a route out. The segment also
# carries AAU's IPv6 RAs, and taking them made v6 traffic default
# out this port into a dead end.
networking.interfaces.enp7s0 = {
useDHCP = false;
ipv4.addresses = [ { address = "192.168.12.1"; prefixLength = 24; } ];
};
systemd.network.networks."40-enp7s0" = {
networkConfig.IPv6AcceptRA = false;
networkConfig.ConfigureWithoutCarrier = true; # camera may be off
linkConfig.RequiredForOnline = "no";
};
# iwd only reads /var/lib/iwd. C = copy once, so a password added to
# the file later (or via impala) is never overwritten.
systemd.tmpfiles.rules = [
@ -454,12 +421,25 @@
};
};
# Desktop: never suspend or hibernate, even if something asks.
systemd.sleep.settings.Sleep = {
AllowSuspend = "no";
AllowHibernation = "no";
AllowHybridSleep = "no";
AllowSuspendThenHibernate = "no";
};
# Ignore the keys that would normally trigger sleep.
services.logind.settings.Login = {
HandleSuspendKey = "ignore";
HandleHibernateKey = "ignore";
HandleLidSwitch = "ignore";
};
xdg.portal = {
enable = true;
extraPortals = [ pkgs.xdg-desktop-portal-hyprland ];
};
# greetd autologins mads straight into Hyprland (via uwsm).
services.greetd = {
enable = true;
@ -469,6 +449,19 @@
};
};
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true; # normal x86 UEFI
# NVIDIA (RTX 20-series or newer → open kernel module).
services.xserver.videoDrivers = [ "nvidia" ];
hardware.graphics.enable = true; # OpenGL/Vulkan userspace (was hardware.opengl)
hardware.nvidia = {
modesetting.enable = true; # required for Wayland/Hyprland
open = true; # open kernel module (Turing+)
nvidiaSettings = true;
package = config.boot.kernelPackages.nvidiaPackages.stable;
};
virtualisation.docker.enable = true;
hardware.bluetooth.enable = true;
@ -536,151 +529,20 @@
home-manager.users.mads = home;
};
mkHost = { system, modules }: nixpkgs.lib.nixosSystem {
inherit system;
modules = [
home-manager.nixosModules.home-manager
common
({ pkgs, ... }: {
nixpkgs.config.allowUnfree = true;
})
] ++ modules;
};
in
{
nixosConfigurations = {
# ── Apple Silicon MacBook (Asahi) ──
mbp = mkHost {
system = "aarch64-linux";
modules = [
apple-silicon.nixosModules.apple-silicon-support
./hosts/mbp/hardware-configuration.nix # generated on the Mac
({ ... }: {
networking.hostName = "mbp";
# Wi-Fi/BT firmware, vendored (a pure flake can't read /boot/asahi).
hardware.asahi.peripheralFirmwareDirectory = ./firmware;
hardware.asahi.enable = true;
boot.loader.systemd-boot.enable = true;
boot.loader.systemd-boot.configurationLimit = 3; # 500M ESP is small
boot.extraModprobeConfig = "options hid_apple fnmode=1";
boot.blacklistedKernelModules = [ "pcspkr" ];
# Root is btrfs (p8). A scrub re-reads every block and verifies its
# checksum, which is the only way bit rot on this SSD surfaces before
# something tries to read the damaged file. Metadata is DUP, so a bad
# metadata block gets repaired from its copy automatically.
services.btrfs.autoScrub = {
enable = true;
interval = "monthly";
fileSystems = [ "/" ];
};
# 15G of RAM and no swap at all: a `nixos-rebuild` forks ~60 nix
# evaluators, and stacked on a browser and a few editor sessions
# that was enough to exhaust it. The kernel then OOM-killed a
# process inside wayland-wm@hyprland.service, and systemd's default
# OOMPolicy=stop tore the whole graphical session down with it.
#
# zram rather than a swapfile, because root is compress=zstd and a
# btrfs swapfile needs its own nodatacow, uncompressed subvolume.
zramSwap = {
enable = true;
memoryPercent = 50; # 7.6G of backing store, ~a third of that in real RAM
};
# Swapping to RAM is cheap, so let the kernel compress anon pages
# rather than evict page cache. At the OOM above, cache had already
# been squeezed to 40M while 13G sat in anon and nothing was
# reclaimable — the default swappiness of 60 gets that backwards here.
boot.kernel.sysctl = {
"vm.swappiness" = 180;
"vm.page-cluster" = 0; # zram has no seek penalty, so no readahead
};
# Build x86_64 derivations (packing-station iso/deploys) via qemu.
boot.binfmt.emulatedSystems = [ "x86_64-linux" ];
# Resolve .local names, so the packing stations can be reached and
# deployed to by hostname instead of by address.
services.avahi = {
enable = true;
nssmdns4 = true;
};
services.resolved.settings.Resolve.MulticastDNS = false; # avahi owns mDNS
# Asahi kernel + Mesa cache, and the Raspberry Pi vendor
# kernel/firmware cache (else both compile from source).
nix.settings = {
extra-substituters = [
"https://nixos-apple-silicon.cachix.org"
"https://nixos-raspberrypi.cachix.org"
];
extra-trusted-public-keys = [
"nixos-apple-silicon.cachix.org-1:8psDu5SA5dAD7qA0zMy5UT292TxeEPzIz8VVEr2Js20="
"nixos-raspberrypi.cachix.org-1:4iMO9LXa8BqhU+Rpg6LQKiGa2lsNh/j2oiYLNOQ5sPI="
];
};
})
];
};
# ── Arbitrary x86_64 PC ──
pc = mkHost {
system = "x86_64-linux";
modules = [
./hosts/pc/hardware-configuration.nix # generated on the PC
({ config, ... }: {
networking.hostName = "pc";
# GigE camera link: static, never a route out. The segment also
# carries AAU's IPv6 RAs, and taking them made v6 traffic default
# out this port into a dead end.
networking.interfaces.enp7s0 = {
useDHCP = false;
ipv4.addresses = [ { address = "192.168.12.1"; prefixLength = 24; } ];
};
systemd.network.networks."40-enp7s0" = {
networkConfig.IPv6AcceptRA = false;
networkConfig.ConfigureWithoutCarrier = true; # camera may be off
linkConfig.RequiredForOnline = "no";
};
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true; # normal x86 UEFI
# Desktop: never suspend or hibernate, even if something asks.
systemd.sleep.settings.Sleep = {
AllowSuspend = "no";
AllowHibernation = "no";
AllowHybridSleep = "no";
AllowSuspendThenHibernate = "no";
};
# Ignore the keys that would normally trigger sleep.
services.logind.suspendKey = "ignore";
services.logind.hibernateKey = "ignore";
services.logind.lidSwitch = "ignore";
# NVIDIA (RTX 20-series or newer → open kernel module).
services.xserver.videoDrivers = [ "nvidia" ];
hardware.graphics.enable = true; # OpenGL/Vulkan userspace (was hardware.opengl)
hardware.nvidia = {
modesetting.enable = true; # required for Wayland/Hyprland
open = true; # open kernel module (Turing+)
nvidiaSettings = true;
package = config.boot.kernelPackages.nvidiaPackages.stable;
};
})
];
};
nixosConfigurations.pc = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
home-manager.nixosModules.home-manager
./hardware-configuration.nix
pc
];
};
# `nix develop` on either arch.
devShells = nixpkgs.lib.genAttrs [ "aarch64-linux" "x86_64-linux" ] (system: {
default = nixpkgs.legacyPackages.${system}.mkShell {
packages = [ nixpkgs.legacyPackages.${system}.nodejs ];
devShells.x86_64-linux.default =
nixpkgs.legacyPackages.x86_64-linux.mkShell {
packages = [ nixpkgs.legacyPackages.x86_64-linux.nodejs ];
};
});
};
}

View file

@ -1,34 +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 = [ "usb_storage" "sdhci_pci" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
# Root lives on nvme0n1p8 (btrfs, subvol @) — the 256G reclaimed from
# the shrunk macOS container. The old ext4 p5 is kept as a rollback until
# it is added to this pool as a second device.
fileSystems."/" =
{ device = "/dev/disk/by-uuid/c828c1a0-ce7b-4e27-b8e4-bce032bb30bf";
fsType = "btrfs";
options = [ "subvol=@" "compress=zstd" "noatime" ];
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/0BB8-A960";
fsType = "vfat";
options = [ "fmask=0022" "dmask=0022" ];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
}