terra: add Ryzen 9 5900X desktop (Hyprland, tailnet, dev tools)
Replaces CachyOS on the OS SSD (Kingston SA400, disko-managed). Dev-data disks (sdc ext4 /mnt/hdd_01, LVM vg_ssd /mnt/ssd_01) stay out of disko and are mounted as plain filesystems so they're never wiped. Desktop split into services/desktop/desktop-hyprland.nix (session: compositor, greeter, audio, portals) and desktop-apps.nix (things darman actually launches, including claude-code — allowlisted alongside the other unfree desktop apps).
This commit is contained in:
@@ -12,6 +12,7 @@ keys:
|
||||
# (ssh-to-age). The host decrypts at runtime using /etc/ssh/ssh_host_ed25519_key.
|
||||
- &jupiter age1zak7glavmg4026p2389fyqe769vqm4jrryknuqckgqq4merz5f7q44rkkt
|
||||
- &neptun age1hp72xyx2cnd05937e4eww95g5kdtn0wsf9j2nypw330pa69gfdxqn0lpkp
|
||||
- &terra age1rfcmu6zh40v4260l9hnf8ajs9vly0s06rx3ey76eu78dp9t7getqyhmkut
|
||||
# mercury (rpi) uses a dedicated age key (SD image, no ssh-host-key delivery);
|
||||
# the private key is dropped on its boot partition after flashing.
|
||||
- &mercury age1cpty7zrgnn6l97upq00w5wa8zcvnkxkdt2jvhlj97jh83exure4slha43t
|
||||
@@ -24,6 +25,9 @@ creation_rules:
|
||||
- path_regex: secrets/neptun\.yaml$
|
||||
key_groups:
|
||||
- age: [ *admin, *neptun ]
|
||||
- path_regex: secrets/terra\.yaml$
|
||||
key_groups:
|
||||
- age: [ *admin, *terra ]
|
||||
- path_regex: secrets/mercury\.yaml$
|
||||
key_groups:
|
||||
- age: [ *admin, *mercury ]
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="application/x-ms-sln">
|
||||
<comment>Visual Studio Solution</comment>
|
||||
<sub-class-of type="text/plain"/>
|
||||
<glob pattern="*.sln" weight="80"/>
|
||||
<magic priority="80">
|
||||
<match type="string" value="Microsoft Visual Studio Solution File" offset="0"/>
|
||||
</magic>
|
||||
</mime-type>
|
||||
<mime-type type="application/x-ms-slnx">
|
||||
<comment>Visual Studio XML Solution</comment>
|
||||
<sub-class-of type="application/xml"/>
|
||||
<glob pattern="*.slnx" weight="80"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
@@ -0,0 +1,47 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
|
||||
# terra — Ryzen 9 5900X / Radeon RX 6800 XT desktop (MSI MS-7A32). Replaces
|
||||
# CachyOS on the OS SSD (Kingston SA400, sdb). Dev-data disks (sdc ext4
|
||||
# /mnt/hdd_01, LVM vg_ssd /mnt/ssd_01) are kept out of disko and mounted here
|
||||
# as plain filesystems so they're never wiped. The leftover ntfs disks
|
||||
# (sda, sdf, nvme0n1) are ignored entirely — not referenced anywhere.
|
||||
{
|
||||
imports = [
|
||||
./hardware-configuration.nix
|
||||
./disk-config.nix # disko: OS-disk (sdb) partitions + filesystems
|
||||
./secrets.nix # sops-nix: darman password, tailscale key
|
||||
../../common.nix # shared base: user / ssh / nix / firewall
|
||||
../../services/vpn/tailscale.nix
|
||||
../../services/desktop/desktop-hyprland.nix
|
||||
../../services/desktop/desktop-apps.nix
|
||||
];
|
||||
|
||||
networking.hostName = "terra";
|
||||
|
||||
# ---- home-manager (user-level config for darman) ----
|
||||
home-manager.useGlobalPkgs = true;
|
||||
home-manager.useUserPackages = true;
|
||||
home-manager.backupFileExtension = "hm-bak";
|
||||
home-manager.users.darman = import ./home.nix;
|
||||
|
||||
# ---- Boot (UEFI) ----
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
hardware.cpu.amd.updateMicrocode = true;
|
||||
|
||||
# ---- Dev-data disks — NOT in disko, mounted read-write, never wiped ----
|
||||
# UUIDs captured from the running CachyOS box; verify after install
|
||||
# (`lsblk -o NAME,UUID,MOUNTPOINT`) in case disko/kernel enumerates differently.
|
||||
fileSystems."/mnt/hdd_01" = {
|
||||
device = "/dev/disk/by-uuid/b8445126-ec6d-4f88-818a-d9e13031d9a4";
|
||||
fsType = "ext4";
|
||||
options = [ "nofail" ];
|
||||
};
|
||||
fileSystems."/mnt/ssd_01" = {
|
||||
device = "/dev/disk/by-uuid/6ca18a9f-27bc-4e58-aea8-de43a0d0ed5d";
|
||||
fsType = "ext4";
|
||||
options = [ "nofail" ];
|
||||
};
|
||||
|
||||
system.stateVersion = "26.05";
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{ ... }:
|
||||
|
||||
# Declarative OS-disk layout (disko). UEFI: GPT with an ESP + ext4 root.
|
||||
# disko both PARTITIONS/FORMATS this disk and generates the NixOS
|
||||
# `fileSystems.*` entries, so hardware-configuration.nix must NOT define
|
||||
# fileSystems for "/" or "/boot".
|
||||
#
|
||||
# ⚠️ This disk is WIPED on install. This is the Kingston SA400 SSD that
|
||||
# currently holds CachyOS (btrfs root+subvols on sdb2, ESP on sdb1).
|
||||
# The dev-data disks (sdc ext4 /mnt/hdd_01, LVM vg_ssd /mnt/ssd_01) and the
|
||||
# leftover ntfs disks (sda, sdf, nvme0n1) are NOT listed here — they are
|
||||
# mounted as plain fileSystems in configuration.nix (or, for the ntfs
|
||||
# disks, ignored entirely) so they are never touched.
|
||||
{
|
||||
disko.devices.disk.os = {
|
||||
type = "disk";
|
||||
device = "/dev/disk/by-id/ata-KINGSTON_SA400S37480G_50026B738072F6C6";
|
||||
content = {
|
||||
type = "gpt";
|
||||
partitions = {
|
||||
ESP = {
|
||||
size = "512M";
|
||||
type = "EF00";
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "vfat";
|
||||
mountpoint = "/boot";
|
||||
mountOptions = [ "umask=0077" ];
|
||||
};
|
||||
};
|
||||
root = {
|
||||
size = "100%";
|
||||
content = {
|
||||
type = "filesystem";
|
||||
format = "ext4";
|
||||
mountpoint = "/";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
# PLACEHOLDER — replaced on install.
|
||||
#
|
||||
# disko (disk-config.nix) owns "/" and "/boot", so this file only carries
|
||||
# kernel modules + platform. nixos-anywhere regenerates it via:
|
||||
# nixos-generate-config --no-filesystems
|
||||
# Standard AMD desktop (SATA SSD, NVMe present but unused) — the generator
|
||||
# should get ahci/nvme right on its own; these are a sane fallback so stage-1
|
||||
# still mounts root if it doesn't.
|
||||
{ config, lib, pkgs, modulesPath, ... }:
|
||||
|
||||
{
|
||||
boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "usb_storage" "usbhid" "sd_mod" "nvme" ];
|
||||
boot.initrd.kernelModules = [ ];
|
||||
boot.kernelModules = [ "kvm-amd" ];
|
||||
boot.extraModulePackages = [ ];
|
||||
|
||||
# NO fileSystems here — disko defines "/" and "/boot".
|
||||
|
||||
swapDevices = [ ];
|
||||
|
||||
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
# home-manager profile for darman on terra. System-level Hyprland enable
|
||||
# (session entry, portals) lives in ../../services/desktop/desktop-hyprland.nix; this
|
||||
# manages the user's own hyprland.conf + session packages.
|
||||
{
|
||||
home.stateVersion = "26.05";
|
||||
|
||||
wayland.windowManager.hyprland = {
|
||||
enable = true;
|
||||
# Starter config — replace with your real dotfiles.
|
||||
settings = {
|
||||
monitor = [ ",preferred,auto,1" ];
|
||||
"$mod" = "SUPER";
|
||||
bind = [
|
||||
"$mod, Return, exec, alacritty"
|
||||
"$mod, Q, killactive"
|
||||
"$mod, D, exec, wofi --show drun"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
programs.git.enable = true;
|
||||
programs.home-manager.enable = true;
|
||||
|
||||
# ---- Dracula theming (GTK + Qt) ----
|
||||
gtk = {
|
||||
enable = true;
|
||||
theme = {
|
||||
name = "Dracula";
|
||||
package = pkgs.dracula-theme;
|
||||
};
|
||||
};
|
||||
|
||||
# dracula-qt5-theme ships only a qt5ct color scheme (no style plugin), so
|
||||
# Qt has to go through qt(5|6)ct rather than a direct style/platformTheme
|
||||
# name. "qtct" pulls in both qt5ct and qt6ct; qt6ct reads its own config
|
||||
# but understands the same scheme file format, so both point at it.
|
||||
qt = {
|
||||
enable = true;
|
||||
platformTheme.name = "qtct";
|
||||
};
|
||||
xdg.configFile."qt5ct/qt5ct.conf".text = ''
|
||||
[Appearance]
|
||||
color_scheme_path=${pkgs.dracula-qt5-theme}/share/qt5ct/colors/Dracula.conf
|
||||
custom_palette=true
|
||||
style=Fusion
|
||||
'';
|
||||
xdg.configFile."qt6ct/qt6ct.conf".text = ''
|
||||
[Appearance]
|
||||
color_scheme_path=${pkgs.dracula-qt5-theme}/share/qt5ct/colors/Dracula.conf
|
||||
custom_palette=true
|
||||
style=Fusion
|
||||
'';
|
||||
|
||||
# Custom mime-info defs (sln/slnx). xdg.mime's update-mime-database only
|
||||
# indexes share/mime/packages inside the hm profile itself, so this has to
|
||||
# be a package in home.packages, not a plain xdg.dataFile.
|
||||
xdg.mime.enable = true;
|
||||
home.packages = [
|
||||
(pkgs.writeTextDir "share/mime/packages/application-x-ms-sln.xml"
|
||||
(builtins.readFile ../../dotfiles/mime/application-x-ms-sln.xml))
|
||||
pkgs.claude-code
|
||||
];
|
||||
|
||||
programs.alacritty = {
|
||||
enable = true;
|
||||
settings = {
|
||||
env.SHELL = "/bin/zsh";
|
||||
terminal.shell = {
|
||||
program = "/bin/zsh";
|
||||
args = [ "-l" ];
|
||||
};
|
||||
window = {
|
||||
padding = { x = 10; y = 10; };
|
||||
opacity = 0.8;
|
||||
};
|
||||
font.normal = {
|
||||
family = "DepartureMono Nerd Font";
|
||||
style = "Regular";
|
||||
};
|
||||
colors.primary = {
|
||||
background = "#222831";
|
||||
foreground = "#ffd369";
|
||||
};
|
||||
hints.enabled = [
|
||||
{
|
||||
hyperlinks = true;
|
||||
regex = "(ipfs:|ipns:|magnet:|mailto:|gemini://|gopher://|https://|http://|news:|file:|git://|ssh:|ftp://)[^\\u0000-\\u001F\\u007F-\\u009F<>\"\\s{-}\\^⟨⟩`]+";
|
||||
command = "xdg-open";
|
||||
mouse.enabled = true;
|
||||
}
|
||||
];
|
||||
keyboard.bindings = [
|
||||
# ESC + CR: nix strings have no \u escape, so fromJSON (which
|
||||
# supports \u001B) is used to get the literal control chars here.
|
||||
{ key = "Return"; mods = "Shift"; chars = builtins.fromJSON ''"\u001B\r"''; }
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{ config, ... }:
|
||||
|
||||
# sops-nix wiring for terra (desktop). Encrypted values in ../../secrets/terra.yaml,
|
||||
# decrypted with terra's own SSH host key (recipient in ../../.sops.yaml).
|
||||
# The host key is pre-generated on the laptop and shipped at install
|
||||
# (nixos-anywhere --extra-files -> /etc/ssh/ssh_host_ed25519_key).
|
||||
{
|
||||
sops.defaultSopsFile = ../../secrets/terra.yaml;
|
||||
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
|
||||
|
||||
sops.secrets.tailscale_authkey = { };
|
||||
|
||||
sops.secrets.darman_password.neededForUsers = true;
|
||||
users.users.darman.hashedPasswordFile = config.sops.secrets.darman_password.path;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
tailscale_authkey: ENC[AES256_GCM,data:5ZaIjgl4d380JGUC+GjhGUeoAkkrS8ky,iv:oRh/7v/od+Mxj+i3z2ouZ0H2NrUYujEiFR+fEODlaNU=,tag:xUbVxCoYqa1eGx2zvVRAnw==,type:str]
|
||||
darman_password: ENC[AES256_GCM,data:aM6jV2KkNlfZBEDvIZ1VpJl+6VSS/zm0AxAohV+WBhcMYdCmIGrmJYArhxNjN5Kk9TyRrLZVNnpzeM5RB6g394NAhznWHumctMN8fiq3x5/YOXVEn9DGxvc+4wj3E3PWWu44nRmdXHo6Wg==,iv:SBh11ZB34TrzZNxKIw5TKLUFvoC65vMVDTuvfjjKd/g=,tag:dYwn+fdbMAQBOHsP9wasjA==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOWkt2MXlGUVZGUlYyRS81
|
||||
TDE2aFg5WGF3ZVcyMGEzSlR3aThwWU1QelNzClFYSll1K0kwMEYvQ3o5U2RKdnpu
|
||||
UVZibUV2Z1FCcCtPRzROaW1RSk85M3cKLS0tIDdnRGpzS2dwYUc4ZlV5M1JlQURy
|
||||
cWVDb1F2TTBSL2psTFdGaGRzb3JCaEEKBYdo2x/fjnU14hWI6ic3t2zmA8JioxVI
|
||||
TEH3G9kPfWgcMdh1ORwykNnfcoSZcYM0EjomFDv5ZVjuZvRLDRDblA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1cekcqyf7073fsytcjxaa9dr9zwkmn4vjg36rv2tgxdglzfv4jvxqvcj6z2
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRZUFTNVJRdTBJbzZIZHQ4
|
||||
VWRRSVJTbHFxNVVGTEZGT1ZqZnduQm1OSXg0CjZTU2p2YU9sUWZQTmJwNUFSVUFG
|
||||
ZmJuOUVoMHZNQjlCeGFOcU9GaE5UUG8KLS0tIGtIL0t2cjM4ekl0clN2bERrRFlH
|
||||
QW8rZHpwRTJYbWxUbnNTNUplL2dRRmcKujP0nL2Tu8/kDALDQsvEpaMLFWeMAtsk
|
||||
sHjKfw8VrrmAR4pQf1dsY+wcyh4FsZxhP3Q+QIVq3eCIXS9PeJkGAg==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1rfcmu6zh40v4260l9hnf8ajs9vly0s06rx3ey76eu78dp9t7getqyhmkut
|
||||
lastmodified: "2026-07-14T19:48:05Z"
|
||||
mac: ENC[AES256_GCM,data:ne2YPbCG2U6ix/C+bua28qAEKEMJ4NkEmrg0qFw3iMs8l4gSKL0wVrEu7H6EimCUohdiZ24g4s8euRU60bPNrM1cKkFnDPfkIY4Lc33dI/JrKahjHrEafhpQNy85i2l7oxM1WnHOGfgkQ/XoicwInDwcM5Z8Ks4Sa1JnhYVwCFc=,iv:VnbreolrFp97Caim+sxB5MGW01ir3vyOTL3fJ8DDWwY=,tag:+4TusjszEXQFCUjP3F/IgQ==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.13.1
|
||||
@@ -0,0 +1,38 @@
|
||||
{ pkgs, lib, ... }:
|
||||
|
||||
# Desktop applications for a workstation host (currently: terra). Split from
|
||||
# desktop-hyprland.nix, which owns the *session* (compositor, greeter, audio,
|
||||
# portals); this owns the things the user actually launches inside it.
|
||||
{
|
||||
# gitkraken / jetbrains-toolbox / steam are unfree. Allow exactly those
|
||||
# rather than flipping allowUnfree globally, so an unfree dep sneaking into
|
||||
# a server host's closure still fails loudly.
|
||||
nixpkgs.config.allowUnfreePredicate = pkg:
|
||||
builtins.elem (lib.getName pkg) [
|
||||
"gitkraken"
|
||||
"jetbrains-toolbox"
|
||||
"steam"
|
||||
"steam-unwrapped"
|
||||
"steam-run"
|
||||
"claude-code"
|
||||
];
|
||||
|
||||
# Steam needs its own module (not just the package): it sets up the FHS
|
||||
# wrapper, the udev rules for controllers, and the 32-bit graphics stack.
|
||||
programs.steam = {
|
||||
enable = true;
|
||||
remotePlay.openFirewall = true; # in-home streaming
|
||||
dedicatedServer.openFirewall = false;
|
||||
};
|
||||
hardware.graphics.enable32Bit = true; # required by steam's 32-bit games
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
zed-editor
|
||||
protonplus # manages Proton-GE / Wine-GE builds for steam + lutris
|
||||
gitkraken
|
||||
jetbrains-toolbox
|
||||
kdePackages.dolphin
|
||||
];
|
||||
|
||||
fonts.packages = [ pkgs.nerd-fonts.departure-mono ]; # alacritty's font, see hosts/terra/home.nix
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
# Hyprland (wayland) desktop: compositor, login manager, audio, portals.
|
||||
# Reusable for any host that wants a local GUI session (currently: terra).
|
||||
{
|
||||
programs.hyprland.enable = true;
|
||||
|
||||
services.greetd = {
|
||||
enable = true;
|
||||
settings.default_session.command =
|
||||
"${pkgs.tuigreet}/bin/tuigreet --time --cmd Hyprland";
|
||||
};
|
||||
|
||||
# Audio (pipewire replaces pulseaudio/jack).
|
||||
security.rtkit.enable = true;
|
||||
services.pipewire = {
|
||||
enable = true;
|
||||
alsa.enable = true;
|
||||
pulse.enable = true;
|
||||
};
|
||||
|
||||
# Screen-share / file-picker portals for wayland apps.
|
||||
xdg.portal = {
|
||||
enable = true;
|
||||
extraPortals = [ pkgs.xdg-desktop-portal-hyprland ];
|
||||
};
|
||||
|
||||
hardware.graphics.enable = true; # OpenGL/Vulkan for the compositor + apps
|
||||
services.libinput.enable = true;
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
alacritty
|
||||
];
|
||||
|
||||
networking.networkmanager.enable = true;
|
||||
}
|
||||
Reference in New Issue
Block a user