Author SHA1 Message Date
Emma ThorpeandClaude Opus 5 39434b3929 feat(hosts): add lyrathorpe-console, a living-room games machine
CI / flake (push) Skipped
CI / flake (pull_request) Successful in 7m46s
New x86_64 host for a 4th-gen Core i7 (Haswell) with a GTX 1070 8 GB,
wired to a television and driven with a Bluetooth controller.

Session: greetd gets an initial_session, so the machine autologins into
the gamescope Steam session at boot with no greeter and no keyboard.
Quitting Steam falls back to greetd's default_session (ReGreet), where
the ordinary Sway session is available for keyboard-and-mouse work.

Graphics: the GTX 1070 is Pascal, so it needs driver branch 580
(nvidiaPackages.legacy_580) like the Mac Pro's Quadro P400 -- the
nixpkgs default (production, 595.x) dropped Pascal support.

Games: RetroArch built through retroarch-bare.wrapper with 17 cores
covering NES through PS2, GameCube and Wii; Clone Hero; and Steam with
Proton-GE and protontricks. RetroArch's menu toggle is bound to L3+R3,
without which there is no way to exit a running core on a machine with
no keyboard.

Proton prerequisites beyond what programs.steam already arranges: the
esync file-descriptor hard limit is raised from systemd's default
524288 to 1048576 (soft limit untouched), and Proton-GE is wired in via
extraCompatPackages. vm.max_map_count already defaults high enough in
nixpkgs and needs no override.

Content lives in a shared /srv/games tree created by systemd.tmpfiles,
laid out one directory per system under roms/ using the libretro/ES-DE
naming convention, plus bios/, saves/, states/, a Steam library folder
and Clone Hero's songs. RetroArch is pointed at it declaratively.

hardware-configuration.nix is a placeholder, not a hardware scan: the
machine is not installed yet. It assumes partition labels `nixos` and
`BOOT` and must be replaced with nixos-generate-config output.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 17:06:21 +01:00
17 changed files with 827 additions and 145 deletions
+9 -8
View File
@@ -7,14 +7,15 @@ single flake.
Defined in the host table in [`flake.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/flake.nix):
| Configuration | System | Machine |
| --------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) |
| `lyrathorpe-t400` | `x86_64-linux` | ThinkPad T400 — [install notes](https://docs.lyrapup.pet/nixfiles/hosts/t400/) |
| `lyrathorpe-macpro31` | `x86_64-linux` | Mac Pro 3,1, desktop — [install notes](https://docs.lyrapup.pet/nixfiles/hosts/macpro31/) |
| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) — [notes](https://docs.lyrapup.pet/nixfiles/hosts/edaas/) |
| `lyrathorpe-rpi5` | `aarch64-linux` | Raspberry Pi 5 headless server: Docker host + nginx reverse proxy — [install notes](https://docs.lyrapup.pet/nixfiles/hosts/rpi5/) |
| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) — [notes](https://docs.lyrapup.pet/nixfiles/hosts/darwin/) |
| Configuration | System | Machine |
| --------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) |
| `lyrathorpe-t400` | `x86_64-linux` | ThinkPad T400 — [install notes](https://docs.lyrapup.pet/nixfiles/hosts/t400/) |
| `lyrathorpe-macpro31` | `x86_64-linux` | Mac Pro 3,1, desktop — [install notes](https://docs.lyrapup.pet/nixfiles/hosts/macpro31/) |
| `lyrathorpe-console` | `x86_64-linux` | Living-room games machine (Haswell i7 + GTX 1070) on a television — [install notes](https://docs.lyrapup.pet/nixfiles/hosts/console/) |
| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) — [notes](https://docs.lyrapup.pet/nixfiles/hosts/edaas/) |
| `lyrathorpe-rpi5` | `aarch64-linux` | Raspberry Pi 5 headless server: Docker host + nginx reverse proxy — [install notes](https://docs.lyrapup.pet/nixfiles/hosts/rpi5/) |
| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) — [notes](https://docs.lyrapup.pet/nixfiles/hosts/darwin/) |
Shared layers: `home` (home-manager: shell, git, editor),
`modules/common-nixos.nix` (all NixOS hosts: fonts, nix-ld, caches),
+340
View File
@@ -0,0 +1,340 @@
# Console — living-room games machine
Flake host: `lyrathorpe-console`. Desktop (`portable = false`, imports
`../../modules/desktop.nix`). Files: `configuration.nix`, `nvidia.nix`,
`gaming.nix`, `hardware-configuration.nix`.
A 4th-generation Core i7 (Haswell) on a UEFI board with an NVIDIA GeForce GTX
1070 8 GB, wired to a television. It boots straight into Steam Big Picture and
is driven from the sofa with a Bluetooth controller; keyboard and mouse are
supported but secondary.
## Not installed yet
`hardware-configuration.nix` in this host directory is a **placeholder**, not a
hardware scan. It exists so the flake evaluates in CI and assumes the install
labels its partitions `nixos` (root, ext4) and `BOOT` (ESP, vfat). Replace the
whole file with the output of `nixos-generate-config` run on the machine and
commit that. If the labels do not match, the boot fails on a missing device
rather than touching the wrong disk.
Partition the disk GPT with an ESP (vfat, 512 MB is comfortable). Nothing else
in the host config depends on the disk layout.
## Bootloader
Ordinary PC UEFI firmware, so **systemd-boot** with
`canTouchEfiVariables = true` — unlike the Mac Pro, this board is trusted with
`efibootmgr` NVRAM writes.
`boot.loader.timeout = 0`: the boot menu is unreadable from a sofa and unusable
without a keyboard, so the default entry boots immediately. **Hold space at
power-on** to get the menu back and pick an older generation.
`configurationLimit = 10` stops the ESP filling up.
## Graphics — GTX 1070
Everything driver-related is in [`nvidia.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/hosts/Console/nvidia.nix).
The GTX 1070 is Pascal (GP104), so it is under the same driver constraint as the
Mac Pro's Quadro P400:
- **Driver branch 580** (`nvidiaPackages.legacy_580`), _not_ the nixpkgs default
(`production`, currently 595.x). 580 is the last branch supporting
Maxwell/Pascal/Volta, maintained as an LTS branch until Aug 2028; a newer
branch does not drive this card at all.
- `modesetting.enable = true` — mandatory for Wayland. Without
`nvidia-drm.modeset=1` neither gamescope nor wlroots gets a usable GBM device,
and both the Steam session and Sway fail to start.
- `open = false` — the open kernel modules require Turing or later.
- Sway runs with `--unsupported-gpu`; wlroots refuses the proprietary driver
otherwise. gamescope and cage/ReGreet need no such flag.
- `hardware.graphics.enable32Bit` pulls in the lib32 NVIDIA userspace that
32-bit Steam titles and Proton's 32-bit prefixes link against.
The driver is unfree, so it is **not in the binary cache**: the kernel module is
compiled on the machine. On a Haswell i7 that is a few minutes, not the Mac
Pro's ordeal, but it recurs on every kernel bump. The package names are
allowlisted in `unfreePackages` in [`flake.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/flake.nix).
Verify after a rebuild:
```sh
nvidia-smi
lsmod | grep nvidia # nvidia, nvidia_modeset, nvidia_drm
```
## Session model — autologin into Steam
[`gaming.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/hosts/Console/gaming.nix)
sets `programs.steam.gamescopeSession.enable`, which registers a `steam.desktop`
Wayland session (gamescope wrapping Steam in tenfoot mode) and installs a
`steam-gamescope` launcher. greetd — already present on every Sway host for
ReGreet, see [`../../modules/sway.nix`](https://code.emmathe.dev/lyrathorpe/nixfiles/src/branch/main/modules/sway.nix)
— gets an `initial_session` pointing at that launcher:
```
boot
└─ greetd initial_session (autologin as lyrathorpe)
└─ gamescope --steam -- steam -tenfoot -pipewire-dmabuf
├─ Steam library / Proton titles
├─ [non-Steam] RetroArch
└─ [non-Steam] Clone Hero
quit Steam → greetd default_session → ReGreet → pick Sway (keyboard + mouse)
```
So there is no greeter at boot and no keyboard needed. Quitting Steam drops back
to ReGreet, where the ordinary Sway session is available for everything else.
`services.greetd.restart` defaults to `false` once `initial_session` is set,
which is what stops a logout looping straight back into autologin.
Two details worth knowing:
- The launcher is referenced as `/run/current-system/sw/bin/steam-gamescope`.
The steam module builds that script privately and only adds it to
`environment.systemPackages`, so there is no package attribute to point at.
- `programs.gamescope.capSysNice = true` installs gamescope as a setcap wrapper
in `/run/wrappers/bin` instead of the system profile. That path precedes the
system profile on `PATH`, so `steam-gamescope` still resolves it.
The greeter is **Dvorak**, like every other host here (`modules/sway.nix` forces
`XKB_DEFAULT_VARIANT=dvorak` on cage). Only relevant if someone else has to type
a password.
### Adding RetroArch and Clone Hero to Big Picture
Both are installed system-wide but are not Steam titles. Add each once, from a
Sway session, via Steam's **Games → Add a Non-Steam Game**; they then appear in
Big Picture and inherit Steam Input, so the controller works in them without
further configuration.
## Emulation — RetroArch
`gaming.nix` builds RetroArch through `pkgs.retroarch-bare.wrapper`, which wires
up the packaged assets, core info and joypad autoconfig profiles. Cores:
| System | Core |
| -------------------------------------- | ------------------------ |
| NES | `nestopia` |
| SNES | `snes9x` |
| Game Boy / Color | `gambatte` |
| Game Boy Advance | `mgba` |
| Nintendo 64 | `mupen64plus` |
| Nintendo DS | `melonds` |
| GameCube / Wii | `dolphin` |
| Master System / Game Gear / Mega Drive | `genesis-plus-gx` |
| 32X / Mega CD | `picodrive` |
| Saturn | `beetle-saturn` |
| Dreamcast / NAOMI | `flycast` |
| PlayStation | `beetle-psx-hw` |
| PlayStation 2 | `pcsx2` (LRPS2) |
| PSP | `ppsspp` |
| Arcade | `fbneo`, `mame2003-plus` |
| DOS | `dosbox-pure` |
A handful of settings are applied on every launch via `--appendconfig`, so they
are fixed policy rather than saved preferences — changing them in the UI will
not stick. Everything else stays user-editable as normal.
- `menu_driver = ozone` — the controller-navigable menu.
- `video_fullscreen = true`.
- `input_menu_toggle_gamepad_combo = 2`**L3+R3 opens the RetroArch menu**
from inside a running core. Without a pad combo there is no way to exit a game
without a keyboard. No retro system emulated here has L3/R3 on its own
controller, so the binding cannot collide with a game.
- `system_directory`, `savefile_directory`, `savestate_directory`,
`playlist_directory`, `screenshot_directory`, `thumbnails_directory` and
`rgui_browser_directory` — all pointed at the shared library described below,
rather than scattered through `~/.config/retroarch`.
An unrecognised key in an appended config is ignored **silently**, so those key
names are worth keeping in step with upstream if RetroArch is ever bumped
across a major version.
### BIOS files and expectations
Several cores need BIOS or firmware images that are not redistributable and are
therefore not packaged. Drop them in `/srv/games/bios`, which is RetroArch's
system directory on this host:
- **PlayStation 2** (`pcsx2`) — a PS2 BIOS dump. The core will not boot anything
without one.
- **Saturn** (`beetle-saturn`) — region BIOS images.
- **Dreamcast** (`flycast`) — `dc_boot.bin` / `dc_flash.bin` for most titles.
- **Nintendo DS** (`melonds`) — optional, but DSi mode and some titles want the
real BIOS/firmware.
Be honest about the two heaviest cores. `dolphin` and `pcsx2` are libretro ports
of emulators whose upstream effort goes into their **standalone** builds; the
cores lag on compatibility and are the first place to look when a GameCube, Wii
or PS2 title misbehaves. If a game does not cooperate, add the standalone
emulators to `environment.systemPackages` in `gaming.nix`:
```nix
pkgs.dolphin-emu # GameCube / Wii
pkgs.pcsx2 # PlayStation 2
```
Both are controller-driven and can be added to Big Picture the same way as
RetroArch. The hardware is not the limit here — a GTX 1070 and a Haswell i7 run
PS2 and Wii comfortably.
Five cores (`snes9x`, `genesis-plus-gx`, `picodrive`, `fbneo`,
`mame2003-plus`) carry upstream licences with non-commercial or
no-redistribution-for-profit clauses, so their derivation names are in
`unfreePackages` in `flake.nix`. Nothing else in the core set needs it.
## Games library layout
Content lives under `/srv/games`, deliberately outside any home directory: it is
bulky, it is the thing most likely to move to its own disk, and it is shared
between Steam, RetroArch and Clone Hero rather than owned by one of them.
Mounting a second drive at `/srv/games` is the only change that move needs.
`gaming.nix` creates the tree with `systemd.tmpfiles.rules` at every boot:
```
/srv/games/
├── roms/ # RetroArch content browser opens here
│ ├── nes/ snes/ gb/ gbc/ gba/ n64/ nds/ gc/ wii/
│ ├── mastersystem/ gamegear/ megadrive/ sega32x/ segacd/
│ ├── saturn/ dreamcast/
│ ├── psx/ ps2/ psp/
│ └── arcade/ dos/
├── bios/ # RetroArch system dir: BIOS and firmware
├── saves/ # in-game saves
├── states/ # save states
├── playlists/
├── screenshots/
├── thumbnails/
├── steam/ # second Steam library folder
└── clonehero/
├── songs/
└── backgrounds/
```
Directory names under `roms/` follow the libretro/ES-DE convention, so a scraper
or a second frontend recognises them without anything being renamed.
Everything is `lyrathorpe:users` mode **2775**. The setgid bit matters: the
owning group is carried onto anything created inside, so a second account — or
an `rsync` from another machine — does not leave behind files the TV user cannot
write. The rules create directories if missing and otherwise leave them alone;
nothing here removes or rewrites content.
RetroArch is pointed at these paths declaratively. The other two have to be told
once, in their own UIs:
- **Steam** — Settings → Storage → the `+` control → add `/srv/games/steam` as a
library folder. Games installed there survive a reinstall of the OS.
- **Clone Hero** — set the song library path to `/srv/games/clonehero/songs` from
its settings screen. Clone Hero keeps its own config in `~/.clonehero`.
## Steam and Proton
`programs.steam.enable` already arranges most of what Proton needs, and it is
worth recording so it is not re-litigated:
- `hardware.graphics` with `enable32Bit` — the 32-bit GL/Vulkan userspace
Proton's 32-bit prefixes link against.
- Steam's udev rules (`hardware.steam-hardware.enable`) — controller and
hidraw access, which is also what lets `dualsensectl` talk to a DualSense.
- 32-bit PipeWire ALSA (`services.pipewire.alsa.support32Bit`), derived from
`alsa.enable`, which `gaming.nix` turns on for the older native titles that
talk to ALSA directly rather than through the Pulse shim.
- Wine's fonts — Liberation, DejaVu, FreeFont and the Noto set arrive with
`fonts.enableDefaultPackages` plus `modules/common-nixos.nix`. Liberation is
metric-compatible with the Microsoft core fonts, so text lays out correctly
without shipping unfree `corefonts`.
- `vm.max_map_count` is **1048576** in the nixpkgs default sysctls, above what
DX12 and Unreal titles need. No override required — this is the one people
usually copy from Arch wiki posts and it is already handled.
What is **not** covered by default, and is set explicitly in `gaming.nix`:
- `systemd.settings.Manager.DefaultLimitNOFILE = "1024:1048576"`. esync opens
one eventfd per Wine synchronisation object and runs out against systemd's
default 524288 hard limit in the heaviest titles. Only the hard limit is
raised; the soft limit stays at 1024, because lifting that breaks
`select()`-based programs elsewhere on the system.
- `extraCompatPackages = [ pkgs.proton-ge-bin ]`. The module puts its
`steamcompattool` output on `STEAM_EXTRA_COMPAT_TOOLS_PATHS`, which is what
makes **GE-Proton** appear in the client's compatibility list.
- `protontricks.enable` — winetricks against a Proton prefix, the standard
repair when a title needs a runtime (dotnet, vcrun, Media Foundation) Proton
does not ship.
- `programs.gamemode.enable` — applies the performance CPU governor around games
that request it.
One thing is **not declarative**: Steam Play must be switched on in the client,
once, per account — **Settings → Compatibility → Enable Steam Play for all other
titles**. Nix cannot set this; it lives in Steam's own config.
Verify the Proton side after installing:
```sh
vulkaninfo --summary # 64-bit ICD
nvidia-smi # driver up
ulimit -Hn # expect 1048576
# in a game's launch options, to confirm esync/fsync are active:
# PROTON_LOG=1 %command% → ~/steam-<appid>.log
```
## Controllers
- **Xbox One / Series over Bluetooth** — `hardware.xpadneo.enable`. The
out-of-tree driver; the in-kernel `xpad` handles these badly over Bluetooth
(wrong button mapping, no rumble). The module enables bluez itself.
- **Xbox 360, wired** — in-kernel `xpad`, autoloaded by udev on plug-in.
Nothing to configure. The kernel is built with `CONFIG_JOYSTICK_XPAD=m`,
`CONFIG_JOYSTICK_XPAD_FF=y` (rumble) and `CONFIG_JOYSTICK_XPAD_LEDS=y`. The
360 wireless PC receiver uses the same driver and works the same way.
- **DualSense / DualShock 4** — in-kernel `hid-playstation`, over both USB and
Bluetooth. No driver config. `dualsensectl` is installed for LED, battery and
microphone control; it works because Steam's udev rules grant hidraw access.
- **Clone Hero guitars** — plain USB HID gamepads, handled in-kernel. Nothing to
configure.
`hardware.bluetooth.powerOnBoot` is set so the adapter is up before the Steam
session starts and a pad can reconnect unattended.
`settings.General.Experimental = true` is what enables battery level reporting
for Bluetooth gamepads — it is still behind bluez's experimental flag.
Pair a new controller from Big Picture (**Settings → Controller**), or from a
Sway session with `bluetoothctl`. If a pad connects but no input arrives, check
`journalctl -b -u bluetooth` and confirm `hid_xpadneo` is loaded
(`lsmod | grep xpadneo`).
The Xbox One / Series USB **wireless dongle** is deliberately not configured. It
needs `hardware.xone.enable`, which **blacklists `xpad`** — that would break the
wired 360 pads — as well as `mt76x2u`, and pulls in proprietary dongle firmware.
Not worth the side effects unless that dongle is actually in use, and if it ever
is, the 360 pads have to be re-tested.
## Untested claims
This host has not been built or booted yet. Two things are worth watching on
first boot:
- **gamescope on the proprietary NVIDIA driver.** `gaming.nix` sets
`GBM_BACKEND=nvidia-drm` and `__GLX_VENDOR_LIBRARY_NAME=nvidia` for the
session, which is the standard fix, but the combination has a history of
needing tweaks. If the session dies at startup, switch the greeter back to
interactive by commenting out `services.greetd.settings.initial_session`, log
into Sway, and read `journalctl --user -b`.
- **Television resolution and refresh.** gamescope takes the output's native
mode by default. Add `gamescopeSession.args = [ "-W" "3840" "-H" "2160" "-r"
"60" ]` if a specific mode is wanted.
There is no HDMI-CEC configuration here, so the TV remote will not drive the
box; that needs a Pulse-Eight adapter or a working CEC bridge on the board.
Nothing boots to a splash screen either — Plymouth was left out deliberately, as
early KMS with the proprietary driver makes it unreliable.
## Networking
Wired NetworkManager from `../../modules/desktop.nix`; `modules/ssh.nix` adds
key-only sshd, which is the practical way to administer a machine with no
keyboard attached. The firewall is default-deny (`modules/workstation.nix`);
Steam Remote Play and local network game transfers open their own ports through
`programs.steam`.
+9 -17
View File
@@ -186,14 +186,6 @@ fleet's stock "wheel, with a password" policy. What it does **not** implement:
host aliases, LDAP/SSSD sudoers, `sudoreplay`, and most `Defaults` settings.
Needing any of those means reverting to `security.sudo`.
One exception to the password: the EDaaS box sets
`security.sudo-rs.wheelNeedsPassword = false`. NixOS-WSL ships that default for
`security.sudo` — WSL has no console login, so the trust boundary is the Windows
session and the Linux account password is never one the user chose — and the
option does not carry across to the `security.sudo-rs` module, which defaults to
requiring one. Without the explicit setting, `sudo` on that host prompts for a
password nobody knows.
If a host ever refuses to escalate, get a root shell that does not go through
sudo (`wsl -u root -d NixOS` on the work box; the console or a serial/HDMI login
elsewhere) and roll back with `nixos-rebuild switch --rollback`, or pick the
@@ -357,12 +349,12 @@ Claude to route new memories there.
## Per-host differences
| | Personal Linux (sway) | macOS | Work WSL (EDaaS) |
| --------------------------- | --------------------- | --------------------- | ---------------------------- |
| Auto-tmux | yes (foot/TTY) | yes (iTerm2) | yes (WSL shell) |
| `kubectl` → kubecolor | no (no kubectl) | no | yes (work module) |
| `sudo` implementation | sudo-rs (password) | Apple sudo + Touch ID | sudo-rs (passwordless wheel) |
| git email | `iam@emmathe.dev` | `iam@emmathe.dev` | `…@citrix.com` (work) |
| ssh config managed | yes | yes | no (keeps corporate config) |
| ssh-agent | yes | launchd | yes (work module) |
| GUI / theming (desktop.nix) | yes | no | no |
| | Personal Linux (sway) | macOS | Work WSL (EDaaS) |
| --------------------------- | --------------------- | --------------------- | --------------------------- |
| Auto-tmux | yes (foot/TTY) | yes (iTerm2) | yes (WSL shell) |
| `kubectl` → kubecolor | no (no kubectl) | no | yes (work module) |
| `sudo` implementation | sudo-rs | Apple sudo + Touch ID | sudo-rs |
| git email | `iam@emmathe.dev` | `iam@emmathe.dev` | `…@citrix.com` (work) |
| ssh config managed | yes | yes | no (keeps corporate config) |
| ssh-agent | yes | launchd | yes (work module) |
| GUI / theming (desktop.nix) | yes | no | no |
Generated
+31 -31
View File
@@ -3,16 +3,16 @@
"brew-src": {
"flake": false,
"locked": {
"lastModified": 1786945682,
"narHash": "sha256-VBESSoJccikdhxh3vp3SQeG7cZXTOulMvVkoSqNDEhs=",
"lastModified": 1786348930,
"narHash": "sha256-bCQJkbgsAMDp5HQystZLCq11UHiyEuoWbxKulAPYrh8=",
"owner": "Homebrew",
"repo": "brew",
"rev": "5b90e281d4e0c8fbd6ca4d8358276fb305b8d0bd",
"rev": "3ecc9eff23feebf1bc73846d74e14a122c93b66f",
"type": "github"
},
"original": {
"owner": "Homebrew",
"ref": "6.0.18",
"ref": "6.0.16",
"repo": "brew",
"type": "github"
}
@@ -25,11 +25,11 @@
},
"locked": {
"dir": "pkgs/firefox-addons",
"lastModified": 1787457768,
"narHash": "sha256-cbgeu5NTb6DtB+tNs4E6z6K/1XKKM90gVmlkWMJe+gY=",
"lastModified": 1786853140,
"narHash": "sha256-O880FlUav75Q5aNlg9znyg/avf1X/W7o/cAtZFLtpWc=",
"owner": "rycee",
"repo": "nur-expressions",
"rev": "25cfc8fdc413d73b3a47e3e86dafcad51cf5c9f9",
"rev": "ba9568c9c0df6290dc2f34b032ab4cb575e73788",
"type": "gitlab"
},
"original": {
@@ -135,11 +135,11 @@
]
},
"locked": {
"lastModified": 1787424939,
"narHash": "sha256-O2tBn84NNuHrnqNVxx/XqsXwfYvS1YwBh+7CBnbCYsk=",
"lastModified": 1784288435,
"narHash": "sha256-ReRHaLgr/uVqdD8afFSn+myXIfpHeOhP0yYe0TJqAA8=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "809414f0cdadf82cf11b06c2b29ba9b3168b3297",
"rev": "43b3c1ab9d40fb1dbb008f451988a91e375825e9",
"type": "github"
},
"original": {
@@ -155,11 +155,11 @@
]
},
"locked": {
"lastModified": 1787377438,
"narHash": "sha256-Sxu1NLTD/Ern6hFGLlZmtKCSct3YQXZI/lls8RE1XeM=",
"lastModified": 1786924861,
"narHash": "sha256-hftabkb+73OcGzvwFAjCiQorAhprs9TnU1+FkGO5CIw=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "65258d5c65a250189fde2e35f490d15e064c4c62",
"rev": "09ae1b85a6db412d841d60f924b23f881f0d0a38",
"type": "github"
},
"original": {
@@ -211,11 +211,11 @@
"brew-src": "brew-src"
},
"locked": {
"lastModified": 1787330919,
"narHash": "sha256-LslMncqN7uOOH5S88WZtO/EVt2HwD8ltUnfyANk+mC0=",
"lastModified": 1786686423,
"narHash": "sha256-8q3WdB8o3VUI7rOz1OXfioXIaaWbFTAxRJAkWLlfc0s=",
"owner": "zhaofengli",
"repo": "nix-homebrew",
"rev": "b00218e4aec0e5bf07d61a0bb13f842faa582d7b",
"rev": "ccabf79a6b9845eb72b51ea1d9c7ce3446350df3",
"type": "github"
},
"original": {
@@ -231,11 +231,11 @@
]
},
"locked": {
"lastModified": 1787457452,
"narHash": "sha256-FJh4esFS3zqNNuKwvN3t6wrJGewqp1AUF9DAEvoKPD8=",
"lastModified": 1786852476,
"narHash": "sha256-IM5CYtf86W4w8eUPpKcY/LpdHElmVBtJhaKnoTKxZEA=",
"owner": "nix-community",
"repo": "nix-index-database",
"rev": "c51d5c2ba69c907a34e90c9b6b80cd2b93811745",
"rev": "c7962dc97b45129df8d751bedaf37beb5a17706e",
"type": "github"
},
"original": {
@@ -272,11 +272,11 @@
]
},
"locked": {
"lastModified": 1787144466,
"narHash": "sha256-HHfv2/HkNSKbbSyU9iD/g8lbP6r4tl33sSw1W4rXCk0=",
"lastModified": 1786867632,
"narHash": "sha256-ez+ubZlA1RtdjCB18a6zJ9M4u8qoPDy08EcnsW5M3Xw=",
"owner": "NixOS",
"repo": "nixos-hardware",
"rev": "0471accf8d0a8210b31d947497d179ecc99e0021",
"rev": "ff17823245ab9ff7bcae6acf950bd89cba82c38c",
"type": "github"
},
"original": {
@@ -308,11 +308,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1787414105,
"narHash": "sha256-WncT27+3BOkgTaJZLnCsf3LcYf9RXMuR9ONSN4rzQ7s=",
"lastModified": 1786711500,
"narHash": "sha256-QvnceIGTBeDvDd9oCn+GvdsnkquliuwbVgpiRH68qaQ=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "a9e6d84f9c2f9012f5fe7d964a7851352300e61a",
"rev": "02e08985a27c65ffd33d434eeb2e660a2e4dc84d",
"type": "github"
},
"original": {
@@ -324,11 +324,11 @@
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1787360063,
"narHash": "sha256-dt4WdcvsA8/RCe+VZZwqU0X+XMM3wBbGCWA0/sFWzGo=",
"lastModified": 1786862985,
"narHash": "sha256-FBJRXmbGXiSUDvYEbfLYRkckayyZ6SK1UEqhCrIZ2Cs=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "2c423e03bbafcff28bfadc6781a4a8257f205cb5",
"rev": "e5bdc4a41d4c072fe1e3787eaa0320a384741d44",
"type": "github"
},
"original": {
@@ -347,11 +347,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1787536726,
"narHash": "sha256-aBh5Yk9tX8ZV4k10BJr2fvTq0/+iWGegaCMUOU7YKas=",
"lastModified": 1786873773,
"narHash": "sha256-Hj/nkhKDv0aJly1PAUstrhrgEYn1mVSkLIYMh90r/Pc=",
"owner": "nix-community",
"repo": "nixvim",
"rev": "e2c3f9f36326d07340626847543c557e2b95fb50",
"rev": "b397fb9f6950d57355d62bb92457d223464e0115",
"type": "github"
},
"original": {
+38 -2
View File
@@ -112,13 +112,27 @@
# Unfree packages permitted to be built (replaces blanket allowUnfree).
# The NVIDIA entries are for the Mac Pro's Quadro P400 (hosts/MacPro31/
# nvidia.nix); unfree packages are not in the binary cache, so the
# kernel module is compiled on the host.
# nvidia.nix) and the Console host's GTX 1070 (hosts/Console/nvidia.nix);
# unfree packages are not in the binary cache, so the kernel module is
# compiled on the host. The steam/clonehero entries are the Console
# host's games stack (hosts/Console/gaming.nix).
unfreePackages = [
"claude-code"
"nvidia-x11"
"nvidia-kernel-modules"
"nvidia-settings"
"steam"
"steam-unwrapped"
"steam-run"
"clonehero"
# RetroArch cores whose upstream licences carry a non-commercial or
# no-redistribution-for-profit clause. Everything else in the core set
# is plain free software.
"libretro-snes9x"
"libretro-genesis-plus-gx"
"libretro-picodrive"
"libretro-fbneo"
"libretro-mame2003-plus"
];
# Per-user identity, keyed by username. See README "Users".
@@ -293,6 +307,28 @@
];
};
lyrathorpe-console = {
system = "x86_64-linux";
portable = false;
# Living-room games machine on a television: autologins into the
# gamescope Steam session (hosts/Console/gaming.nix). sway.nix is
# still imported -- greetd/ReGreet is what the Steam session falls
# back to, and Sway is the keyboard-and-mouse session behind it.
modules = [
./hosts/Console/configuration.nix
./modules/desktop.nix
./modules/ssh.nix
inputs.nixos-hardware.nixosModules.common-pc-ssd
inputs.nixos-hardware.nixosModules.common-cpu-intel
./modules/sway.nix
];
users.lyrathorpe.homeModules = [
./home
./users/lyrathorpe/home.nix
./home/desktop.nix
];
};
emmathorpe-edaas = {
system = "x86_64-linux";
modules = [
-1
View File
@@ -15,4 +15,3 @@
- [Dev clusters disposable](dev_clusters_disposable.md) — Lyra's dev clusters are recreatable; mutate/break freely, no confirmation needed
- [Nix shell tooling](nix_shell_tooling.md) — any nixpkgs tool runs ad hoc via `nix run`/`nix shell nixpkgs#<pkg>`; a missing command is never a dead end
- [WSP local build and test](wsp_local_build_and_test.md) — core-services-cloud on this box: dotnet via nix, artifactory creds from `~/.artifactoryenv` sourced per command, how to tell auth failure from a code failure
- [WSP-32957 PIM migration](wsp_32957_pim_migration.md) — AKS RBAC to PIM + AutoPerm decommission; prod is in the Technical Preview subscription, three tenants; resume via `~/code/WSP-32957-CONTINUATION.md`
+1 -3
View File
@@ -9,9 +9,7 @@ Field map for the **WSP (Workspace Platform)** Jira project, to create tickets w
**Issue-type IDs:** Epic `10000`, Story `10004`, Task `10008`, Bug `10123`, Sub-task `10009`.
**Fast path — use Task, not Bug.** A `Task` requires `summary` plus **Task Type** `customfield_15622` (added since this note was first written; the create validator enforces it even though `createmeta` omits it, same trap as Bug's `versions`). Options value=id: Dev Task=34065, CQE Task=34066, Investigation=34067, Security=34068, Maintenance=34069, Release=34070 — use `Maintenance` for refactors and tidy-ups, `Dev Task` for feature work. A `Bug` requires six extra fields (below), so only pick Bug when it must be a Bug. The sibling infra/remediation tickets in WSP are Tasks.
Example Task `additional_fields`: `{"customfield_15622":{"id":"34069"},"components":[{"name":"Multicluster Platform"}]}`
**Fast path — use Task, not Bug.** A `Task` requires only `summary` (project/issuetype auto, reporter defaults to caller). A `Bug` requires six extra fields (below), so only pick Bug when it must be a Bug. The sibling infra/remediation tickets in WSP are Tasks.
**Bug required fields** (enforced by the create validator; note `createmeta` omits `versions` but the API rejects without it):
@@ -1,58 +0,0 @@
---
name: wsp-32957-pim-migration
description: State of the WSP AKS-RBAC-to-PIM migration and AutoPerm decommission, and how to resume it
metadata:
node_type: memory
type: project
---
Long-running epic (August 2026) moving WSP's AKS cluster RBAC off **AutoPerm
Manager**-maintained `wsp-*` groups onto the per-subscription **`CEO-*` Azure PIM**
groups, then retiring AutoPerm. Epic **WSP-32957**; on the critical path for Zensar
L1 on-call (WSP-32193). Work happens in **`~/code/multicluster`**
(`terraform/cluster`, `terraform/cluster-k8s-resources`, `products/*/environments/*`).
**Resume from `~/code/WSP-32957-CONTINUATION.md`** — full state, branch list,
verified object IDs, findings and next steps. Jira is the durable record; that file
is the index. Keep it updated as work lands ([[docs-keep-updated]]).
**Landed:** WSP-33141 (multicluster PR #1808, merged `b047163c`) added object-ID
inputs — `admin_group_oids` on `cluster`, and `cluster_user_group_oids` /
`cluster_viewer_group_oids` / `cluster_superuser_group_oids` on
`cluster-k8s-resources`. Supplying IDs _replaces_ the display-name lookup and leaves
the `data "azuread_group"` unread, which is what will let the legacy groups be
deleted. Nothing sets them yet, so behaviour is unchanged. Repoint branches for test
(WSP-33067) and staging (WSP-33068) are pushed but have **no PR** — both gated on
decisions, not code.
**Facts that cost real effort to establish, do not re-derive:**
- **Production runs in `fc7af6ae-…` (_Workspace Platform Technical Preview_), not
`d6d75d07-…` (_Workspace Platform Production_)**, which holds no clusters. The
epic was wrong about this for its whole life and every production `CEO-*` group
name and object ID had to change. Because `CEO-*` names embed the subscription
name, **always re-verify object IDs against live Entra rather than trusting the
epic table.**
- **Three tenants**, not two: `6f4fe054` (prod, prod JP), `335836de` (staging,
staging JP, test), `3eae2746` (dev). Each `wsp-*` name is a distinct object in
each tenant.
- `wsp-staging-cluster-admins` and `wsp-test-cluster-admins` are **empty**, so
`wsp-owner` is the _only_ path to `cluster-admin` in staging. Never drop it before
`SuperAdmin-*` is proven — hence the staging branch is split into an additive
commit and a cutover commit.
- **No break-glass exists**: `disableLocalAccounts = True` on every cluster; only the
deployment SPNs authenticate non-interactively.
- Graph **PIM-for-Groups is unreadable via `az`** (the CLI's first-party client lacks
`PrivilegedAccess.Read.AzureADGroup`, on both `v1.0` and `beta`). Use the portal or
`Connect-MgGraph -Scopes PrivilegedAccess.Read.AzureADGroup`.
**Watch for:** the epic gets rewritten by James Weldrake between sessions — re-read
the description before acting, and check which child tickets are still live
(WSP-33062/33063/33064/33066 were cancelled 2026-08-24, and dev was put out of
scope). Verified findings have repeatedly contradicted the epic text
([[copilot-review-false-positives]] is the same instinct: check against reality
first).
Queued Slack messages and the leaver report live as `~/code/*.txt` alongside the
continuation file; see the table in it for what has and has not been sent
([[workflow-review-and-comments]] — show them before they go out).
+1 -16
View File
@@ -287,22 +287,7 @@ in
plugins = with pkgs.tmuxPlugins; [
sensible
vim-tmux-navigator # Ctrl-h/j/k/l across vim splits and tmux panes
{
# On WSL, tmux-yank pipes the selection to clip.exe, which decodes its
# stdin as the OEM codepage instead of UTF-8 -- an em dash reaches the
# Windows clipboard as three characters. Route through tmux's own
# buffer instead: with set-clipboard on, tmux emits OSC 52 and the
# terminal takes the text as UTF-8. Windows Terminal honours OSC 52;
# iTerm2 does not by default, hence the runtime guard rather than
# overriding pbcopy/xsel on every host. yank.tmux bakes the command
# into its key bindings when it loads, so this must be set first, which
# is what plugin extraConfig gives us.
plugin = yank;
extraConfig = ''
if-shell 'grep -qi microsoft /proc/version 2>/dev/null' \
"set -g @override_copy_command 'tmux load-buffer -w -'"
'';
}
yank
extrakto # prefix+Tab: fzf-grab paths/URLs/text from the pane into the prompt
{
# Catppuccin Mocha statusline (v2 API: flavour + window options must be
+36
View File
@@ -0,0 +1,36 @@
# Living-room games machine: 4th-gen Core i7 (Haswell) on a UEFI board, wired to
# a television and driven from the sofa with a Bluetooth controller. Desktop host
# -- shared graphical/wired options live in ../../modules/desktop.nix; only
# host-specific settings are here. The games stack (Steam session, RetroArch,
# controllers) is in ./gaming.nix and the GPU in ./nvidia.nix. Install notes:
# see ../../docs/hosts/console.md.
{ ... }:
{
imports = [
./hardware-configuration.nix
./nvidia.nix
./gaming.nix
];
# Haswell: AVX2/FMA/BMI2, i.e. x86-64-v3. Above the fleet default (2), so this
# only records the fact -- no feature flag currently keys off level 3.
features.cpu.microarchLevel = 3;
# Ordinary PC UEFI firmware: systemd-boot, and NVRAM writes are safe here
# (unlike the Mac Pro's Apple EFI, which cannot be trusted with efibootmgr).
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# The boot menu is unreadable from a sofa and unusable without a keyboard.
# Boot the default immediately; hold space at power-on to get the menu back.
boot.loader.timeout = 0;
# Bound the entry list so the ESP does not fill up with old generations.
boot.loader.systemd-boot.configurationLimit = 10;
networking.hostName = "Console-NixOS";
hardware.cpu.intel.updateMicrocode = true;
# See `man configuration.nix` / the stateVersion docs before changing.
system.stateVersion = "26.05";
}
+247
View File
@@ -0,0 +1,247 @@
# The games stack for the living-room machine: the Steam session that the TV
# boots into, RetroArch with its cores, Clone Hero, and the controller plumbing.
#
# Session model. greetd (from ../../modules/sway.nix, which enables it for
# ReGreet) gets an `initial_session`, so the machine autologins into the
# gamescope Steam session at boot -- no keyboard, no greeter, straight to Big
# Picture. Quitting Steam drops back to greetd's `default_session`, i.e. ReGreet,
# where the ordinary Sway session can be picked for keyboard-and-mouse work.
{ pkgs, ... }:
let
# The account the television autologins as. Must match the user declared for
# this host in the flake host table.
tvUser = "lyrathorpe";
# Games library root. Deliberately outside any home directory: content is
# bulky, is the thing most likely to move to its own disk, and is shared
# between Steam, RetroArch and Clone Hero rather than owned by one of them.
# Mounting a second drive at this path is the only change that needs.
gamesRoot = "/srv/games";
# One ROM directory per emulated system. Names follow the libretro/ES-DE
# convention so a scraper or a second frontend recognises them without
# renaming anything.
romSystems = [
"nes"
"snes"
"gb"
"gbc"
"gba"
"n64"
"nds"
"gc"
"wii"
"mastersystem"
"gamegear"
"megadrive"
"sega32x"
"segacd"
"saturn"
"dreamcast"
"psx"
"ps2"
"psp"
"arcade"
"dos"
];
# Everything under the root that is not a ROM directory. RetroArch is pointed
# at these below; Steam and Clone Hero have to be told about theirs in their
# own UIs (see docs/hosts/console.md).
libraryDirs = [
"bios" # RetroArch system directory: BIOS and firmware images
"saves" # in-game saves
"states" # save states
"playlists"
"screenshots"
"thumbnails"
"steam" # add as a Steam library folder from the client
"clonehero/songs"
"clonehero/backgrounds"
];
# RetroArch and the cores this machine is expected to run. The wrapper already
# points RetroArch at the packaged assets, core info and joypad autoconfig
# profiles; `settings` here is merged on top of those.
retroarch = pkgs.retroarch-bare.wrapper {
cores = with pkgs.libretro; [
# Nintendo
nestopia # NES
snes9x # SNES
gambatte # Game Boy / Color
mgba # Game Boy Advance
mupen64plus # Nintendo 64
melonds # Nintendo DS
dolphin # GameCube / Wii
# Sega
genesis-plus-gx # Master System / Game Gear / Mega Drive
picodrive # 32X / Mega CD
beetle-saturn # Saturn
flycast # Dreamcast / NAOMI
# Sony
beetle-psx-hw # PlayStation, hardware renderer
pcsx2 # PlayStation 2 (LRPS2); needs a PS2 BIOS in RetroArch's system dir
ppsspp # PSP
# Arcade and PC
fbneo
mame2003-plus
dosbox-pure
];
settings = {
# Applied on every launch via --appendconfig, so these three are fixed
# policy rather than saved preferences: changing them in the UI will not
# stick. Everything else stays user-editable as usual.
#
# Ozone is the controller-navigable menu; the TV has no keyboard.
menu_driver = "ozone";
video_fullscreen = "true";
# L3+R3 opens the RetroArch menu from inside a running core
# (INPUT_COMBO_L3_R3). Without a pad combo there is no way to exit a game
# without a keyboard, and no retro system this box emulates has L3/R3 on
# its own controller, so the binding cannot collide with a game.
input_menu_toggle_gamepad_combo = "2";
# Point RetroArch at the shared library instead of scattering content and
# state through ~/.config/retroarch. Key names are RetroArch's own; an
# unrecognised key in an appended config is ignored silently, so they are
# worth keeping in step with upstream.
system_directory = "${gamesRoot}/bios";
savefile_directory = "${gamesRoot}/saves";
savestate_directory = "${gamesRoot}/states";
playlist_directory = "${gamesRoot}/playlists";
screenshot_directory = "${gamesRoot}/screenshots";
thumbnails_directory = "${gamesRoot}/thumbnails";
# Where the content browser opens, so loading a game is a couple of
# D-pad presses rather than a walk up from the filesystem root.
rgui_browser_directory = "${gamesRoot}/roms";
};
};
in
{
programs.steam = {
enable = true;
# Registers the "Steam" wayland session (gamescope wrapping Steam in tenfoot
# mode) with the display manager and installs the steam-gamescope launcher.
gamescopeSession.enable = true;
gamescopeSession.env = {
# gamescope has to be pointed at NVIDIA's GBM implementation and GLX
# vendor explicitly; on the proprietary driver it otherwise fails to get a
# usable device and the session dies at startup.
GBM_BACKEND = "nvidia-drm";
__GLX_VENDOR_LIBRARY_NAME = "nvidia";
};
# Remote Play and local network game transfers are the point of a TV box on
# the same LAN as a desktop; both need their ports open.
remotePlay.openFirewall = true;
localNetworkGameTransfers.openFirewall = true;
# Proton-GE, selectable per title in Steam's compatibility settings. Covers
# the titles where Valve's Proton lags on codecs and anti-cheat shims. The
# module puts its steamcompattool output on STEAM_EXTRA_COMPAT_TOOLS_PATHS,
# which is what makes it appear in the client's Proton version list.
extraCompatPackages = [ pkgs.proton-ge-bin ];
# Winetricks against a Proton prefix: the standard repair tool when a title
# needs a runtime (dotnet, vcrun, Media Foundation) that Proton does not ship.
protontricks.enable = true;
};
# Proton prerequisites beyond what programs.steam already arranges.
#
# Already covered by the steam module, recorded here so it is not re-litigated:
# hardware.graphics 32-bit (the lib32 NVIDIA userspace Proton's 32-bit prefixes
# need), Steam's udev rules, 32-bit PipeWire, and the system fonts Wine renders
# with (Liberation and DejaVu arrive with fonts.enableDefaultPackages).
# vm.max_map_count is 1048576 in the nixpkgs default sysctls, which is above
# what DX12/Unreal titles need -- no override required.
#
# What is not covered: esync opens one eventfd per Wine sync object and runs
# out against systemd's default 524288 hard limit in the heaviest titles.
# Raise the hard limit only; the soft limit stays at the default, because
# lifting that breaks select()-based programs elsewhere on the system.
systemd.settings.Manager.DefaultLimitNOFILE = "1024:1048576";
# capSysNice lets gamescope raise its own scheduling priority, which is what
# keeps the compositor smooth while a game saturates the GPU. It installs
# gamescope as a setcap wrapper instead of a plain systemPackages entry;
# /run/wrappers/bin precedes the system profile on PATH, so steam-gamescope
# still resolves it.
programs.gamescope = {
enable = true;
capSysNice = true;
};
# Applies the performance CPU governor (and drops it again) around games that
# ask for it; Steam's Proton builds and most native titles do.
programs.gamemode.enable = true;
# Autologin into the Steam session. The launcher is not exposed as a package
# by the steam module -- it is built inside it and added to
# environment.systemPackages -- so reference it through the system profile.
# greetd's `restart` option defaults to false once initial_session is set,
# which is what stops a logout from looping straight back into autologin.
services.greetd.settings.initial_session = {
command = "/run/current-system/sw/bin/steam-gamescope";
user = tvUser;
};
# Controllers.
#
# Xbox One/Series pads over Bluetooth need xpadneo: the in-kernel xpad driver
# does not handle them well over BT (wrong button mapping, no rumble). The
# module turns on bluez itself; powerOnBoot is set below so the adapter is up
# before the Steam session starts and a pad can reconnect unattended.
#
# Everything else is in-kernel and needs no configuration: wired Xbox 360 pads
# (and the 360 wireless receiver) via xpad, DualSense/DualShock 4 via
# hid-playstation over USB and Bluetooth, and Clone Hero guitars as plain USB
# HID gamepads. xpadneo does not contend with xpad -- it binds Bluetooth HID
# devices, and the 360 pad is not HID-compliant. hardware.xone is deliberately
# left off: it blacklists xpad, which would break the 360 pads.
#
# hidraw access for the PlayStation pads (LED, battery, dualsensectl) comes
# from Steam's udev rules, which programs.steam enables via
# hardware.steam-hardware.
hardware.xpadneo.enable = true;
hardware.bluetooth = {
enable = true;
powerOnBoot = true;
# Battery level reporting for Bluetooth gamepads is still behind bluez's
# experimental flag.
settings.General.Experimental = true;
};
# The games library, created at boot so the directories exist before anything
# tries to write into them. Mode 2775 is setgid: the owning group is carried
# onto anything created inside, so a second account (or an rsync from another
# machine) does not end up with files the TV user cannot write. Directories
# are created if missing and otherwise left alone -- nothing here removes or
# rewrites content.
systemd.tmpfiles.rules =
let
dir = path: "d ${path} 2775 ${tvUser} users -";
in
[
(dir gamesRoot)
(dir "${gamesRoot}/roms")
]
++ map (system: dir "${gamesRoot}/roms/${system}") romSystems
++ map (sub: dir "${gamesRoot}/${sub}") libraryDirs;
# 32-bit ALSA for the older native titles that talk to ALSA directly rather
# than through the PulseAudio shim; programs.steam derives
# pipewire.alsa.support32Bit from this. PipeWire itself and the Pulse shim
# come from ../../modules/workstation.nix.
services.pipewire.alsa.enable = true;
environment.systemPackages = [
retroarch
pkgs.clonehero
pkgs.dualsensectl # DualSense LED/battery/mic control from the shell
pkgs.mangohud # FPS/frametime overlay; use `mangohud %command%` in Steam
pkgs.vulkan-tools # vulkaninfo, for checking the 32/64-bit ICDs Proton needs
];
}
+57
View File
@@ -0,0 +1,57 @@
# PLACEHOLDER -- not generated by nixos-generate-config.
#
# This host has not been installed yet, so there is no real hardware scan to
# commit. The values below are the conventional defaults for a Haswell UEFI
# desktop and assume the install labels its partitions `nixos` (root, ext4) and
# `BOOT` (ESP, vfat) -- see docs/hosts/console.md. They exist so the flake
# evaluates in CI; they are not a description of the actual machine.
#
# Replace this whole file with the output of `nixos-generate-config` run on the
# machine, and commit that. If the labels do not match, the boot fails loudly on
# a missing device rather than touching the wrong disk.
{
config,
lib,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [
"xhci_pci"
"ehci_pci"
"ahci"
"nvme"
"usb_storage"
"usbhid"
"sd_mod"
"sr_mod"
];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-label/BOOT";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
swapDevices = [ ];
networking.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}
+54
View File
@@ -0,0 +1,54 @@
# NVIDIA GeForce GTX 1070 8 GB (Pascal, GP104): proprietary driver for the
# gamescope Steam session and the Sway desktop.
#
# Driver branch: 580 (nvidiaPackages.legacy_580), NOT the nixpkgs default
# (`production`, currently 595.x). 580 is the last branch that supports
# Maxwell/Pascal/Volta -- NVIDIA keeps it as an LTS branch to Aug 2028 -- and a
# newer branch simply will not drive this card. Same constraint as the Mac Pro's
# Quadro P400; see hosts/MacPro31/nvidia.nix.
#
# The driver is unfree, so it is not in the binary cache: the kernel module is
# compiled locally on every kernel bump.
{ config, ... }:
{
# Selects the proprietary driver; the module blacklists nouveau/nvidiafb and
# loads nvidia-uvm via a modprobe softdep. Naming is historical -- this option
# drives the kernel/driver choice on Wayland hosts too, which is why it is set
# on a machine that runs no X server.
services.xserver.videoDrivers = [ "nvidia" ];
hardware.nvidia = {
package = config.boot.kernelPackages.nvidiaPackages.legacy_580;
# Required for Wayland: sets nvidia-drm.modeset=1 (and fbdev=1), without
# which neither gamescope nor wlroots gets a usable GBM device and both the
# Steam session and Sway fail to start.
modesetting.enable = true;
# The open kernel modules need Turing or later; Pascal must use the closed
# ones. Explicit because the option has no default on driver >= 560.
open = false;
};
# The NVIDIA module only puts these in boot.kernelModules when
# services.xserver.enable is true, which is false on this Wayland-only host --
# so load them explicitly rather than relying on udev modalias autoloading.
# nvidia_uvm is deliberately absent: the module's modprobe softdep pulls it in
# after the GPU device exists, which is the supported ordering.
boot.kernelModules = [
"nvidia"
"nvidia_modeset"
"nvidia_drm"
];
# wlroots refuses the proprietary NVIDIA driver unless told to proceed. The
# greeter's compositor (cage) and gamescope have no such check; only Sway
# needs the flag, which the module bakes into the wrapper the session's
# .desktop file runs.
programs.sway.extraOptions = [ "--unsupported-gpu" ];
# 32-bit driver libraries for 32-bit Steam titles and Proton's 32-bit
# prefixes: hardware.graphics.enable32Bit pulls in the matching lib32 NVIDIA
# userspace. programs.steam (./gaming.nix) sets it too; stated here as well so
# the GPU's 32-bit story lives with the rest of the GPU config.
hardware.graphics.enable32Bit = true;
}
-5
View File
@@ -60,11 +60,6 @@
## patch the script
systemd.services.docker-desktop-proxy.script = lib.mkForce ''${config.wsl.wslConf.automount.root}/wsl/docker-desktop/docker-desktop-user-distro proxy --docker-desktop-root ${config.wsl.wslConf.automount.root}/wsl/docker-desktop "C:\Program Files\Docker\Docker\resources"'';
# NixOS-WSL's passwordless wheel default only covers `security.sudo`; the
# sudo-rs swap in common-nixos.nix needs it set again. No console login here,
# and no account password anyone knows.
security.sudo-rs.wheelNeedsPassword = false;
features.swayDesktop.enable = false;
# NOTE: this user's systemd --user lingering -- so the home-manager renovate
+1 -1
View File
@@ -17,7 +17,7 @@
# Headless server: the Sway desktop is intentionally not set up. modules/sway.nix is
# not imported and features.swayDesktop.enable defaults to false (declared in
# modules/features.nix), so this host keeps plain TTY/SSH login.
# system/modules/features.nix), so this host keeps plain TTY/SSH login.
# Raspberry Pi boots via U-Boot + extlinux, not GRUB/systemd-boot. The
# raspberry-pi-5 nixos-hardware profile supplies the kernel, firmware and
+1 -1
View File
@@ -12,7 +12,7 @@ let
in
{
# The features.swayDesktop.enable option is declared in
# modules/features.nix (so headless hosts can read/set it without
# system/modules/features.nix (so headless hosts can read/set it without
# importing this module). This module only provides its implementation.
config = lib.mkIf cfg.enable {
programs.sway = {
+2 -2
View File
@@ -29,10 +29,10 @@
// lib.optionalAttrs (spec ? linger) { inherit (spec) linger; }
) hostUsers;
programs.firefox = lib.mkIf config.features.swayDesktop.enable {
programs.firefox = lib.mkIf (config.features.swayDesktop.enable == true) {
enable = true;
};
programs.thunderbird = lib.mkIf config.features.swayDesktop.enable {
programs.thunderbird = lib.mkIf (config.features.swayDesktop.enable == true) {
enable = true;
};
}