noctalia publishes no binary cache, so `flake update` means a local Qt/QML
build every time. quickshell comes from nixpkgs and is cached, so the
nixpkgs follows stays; only the noctalia ref is pinned.
Pinned to the rev already locked, so this rebuilds nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Packages the prebuilt pi release binary and puts it on PATH alongside
Claude Code, Codex and opencode.
pi has no `@file` imports in context files, so the shared agent-skills
fragments are concatenated into ~/.pi/agent/AGENTS.md. Skills need no
wiring: pi already reads ~/.agents/skills.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gpg-agent is the ssh agent here, and signing blocks forever on a pinentry
prompt with no TTY, so git push hung right after "Server accepts key".
The key file has no passphrase, so read it directly instead.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
yolo's desktop is only ever seen through a Moonlight window, so its cursor
needs to be tellable apart from the client's at a glance. Bibata-Modern-Amber
differs in both colour and silhouette from Nordzy.
Threads the theme through an option rather than setting it in one place: it
was hardcoded in cursor.nix, in hl.env, in the setcursor autostart, and twice
more in darkman's mode scripts, which re-set it on every light/dark switch and
would have reverted the override.
Also drops a stale hl.env pointing HYPRCURSOR_THEME at rose-pine-hyprcursor,
which no host installs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
virtio-gpu exposes no NVENC/VAAPI, so sunshine software-encodes. Measured
x264 (superfast+zerolatency) on the 8 vCPUs: 73fps at 2160p against 135fps
at 1440p, so a 60fps client keeps roughly 20% margin.
Also brings the drifted agent-of-empires config upstream, which the
mutableFiles guard required before switching.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
hardware.uinput, enabled by services.sunshine, sets /dev/uinput to
root:uinput 0660 and adds no members to the group. Sunshine runs as a
user service, so it failed to create the virtual mouse, keyboard and
gamepads and the stream carried no input.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Text paste went through `kitty @ send-text`, which has no bracketed
paste, so multi-line clipboard text ran line by line in a shell. Route
text through `kitty @ action paste_from_clipboard` instead and keep
send-text for the image path only.
Over SSH the typed path pointed at the local /tmp and the remote process
could not read it. Read the focused window's foreground processes from
`kitty @ ls`, and when one of them is an ssh client, scp the image to
that host and type the remote path. Nothing is typed into the focused
app but a path, so this still works when the target is a TUI rather than
a shell. The real ssh process is preferred over the `kitten ssh`
wrapper, whose long options the argument parser does not understand.
Also: mktemp instead of a timestamp that collides within a second,
a prune of clip files older than a day, image/png preferred over
whatever format the source advertises first, and the clipboard tools
pinned so the script does not depend on kitty's inherited PATH.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
yolo pinned model to claude-fable-5[1m] while common sets opus[1m].
/model writes the live value into ~/.claude/settings.json, so any switch
away from fable-5 left the file drifted and mutableFiles aborted
activation on the next `nh home switch`. Dropping the override makes the
repo agree with the model actually in use.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The block came from the agent-deck installer and was carried in wholesale
when the config was ported to nix. The tool is not installed on any host
and nothing else references it, and now that the file is rendered
read-only from the nix store the installer could not update its own block
anyway.
It re-declared default-terminal, history-limit and mouse at their existing
values, and escape-time at 0 — which quietly overrode the deliberate 25ms
two sections above it, along with the comment explaining that 0 breaks Alt
keys over SSH. Keeps the parts that do real work: the alternate-screen
override, extended keys, and the mouse scroll and drag-to-copy bindings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tmux copy bindings piped to xclip, which is X11-only and never worked
under Hyprland or over SSH, and set-clipboard was never enabled, so the
vi-mode `y` binding was dead too. Route both through OSC 52 instead.
terminal-features has to name the outer terminal explicitly: tmux skips
OSC 52 silently when the terminfo entry does not claim the capability,
which is the common case for an SSH session on xterm-256color.
Neovim only falls back to OSC 52 when it finds no clipboard tool, but
wl-clipboard is installed on every host, so over SSH it wrote the remote
clipboard. Pin the OSC 52 provider for SSH sessions only.
Moonlight and Sunshine have no clipboard channel in the protocol, so
nothing at the terminal layer can help there. Add kdeconnect on all three
hosts for that, and start its indicator from Hyprland — kdeconnectd is
DBus-activated and nothing else brings it up at login.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
yolo's .claude/settings.json was a full fork of the common file, not the
narrow override its comment claimed: sandbox, defaultMode, voiceEnabled,
$schema and feedbackSurveyState were absent, and five more keys had
diverged. It now holds only yolo's overrides and merges over common via
recursiveUpdate. sandbox.enabled stays pinned false — inheriting it would
newly sandbox every Bash call on a box built for unattended agents.
mutableFiles maps a source back to a repo path for the "bring changes
upstream" hint, which only works inside the flake tree. Generated sources
would print a /nix/store path to copy onto, so add upstreamPath.
rev and rev-deploy set Restart=always with RestartSec=2, which burns
systemd's default 5-starts-per-10s budget and parks the unit in `failed`
until a manual reset-failed. StartLimitIntervalSec=0 lifts the cap.
programs.nh.flake and home.mutableFilesRepoPath become mkDefault so yolo
overrides them plainly instead of with mkForce in three places.
Also clears the outstanding nixfmt and statix findings. The yolo system
derivation hash is unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three separate faults, each producing a black Moonlight stream:
- sunshine's wlr backend takes wlr-screencopy's dmabuf path, which on this
virtio-gpu returns empty buffers. Reproduced locally over 127.0.0.1, so it
is not the network or the client. Switch to kms capture.
- hypridle locked the session and cut dpms, blanking the captured output with
no console to unlock from. Disable it on this host.
- qemu picked the nvidia render node by readdir order, so virgl fell back to
llvmpipe. Pin renderD128 via the VM's args.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Unauthenticated flake input fetches from github.com were hitting 429.
Token lives in /etc/nix/github-token.conf (root:root 600, out-of-band —
never in git or /nix/store) and nix.conf !includes it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
All pre-existing, all mechanical:
- pointerCursor no longer implies generation from the block alone; set enable.
- top-level elixir_1_18 alias is deprecated for the beamPackages set. Same
derivation, 1.18.4 either way.
- pkgs.system -> pkgs.stdenv.hostPlatform.system.
- fzf and atuin both bind Ctrl-R. Atuin is sourced last so it already owned the
key; historyWidget.zsh.command = "" states that explicitly and satisfies
home-manager's conflict check. Behaviour is unchanged and fzf keeps Ctrl-T
and Alt-C.
Keyboard control of the mouse pointer. SUPER+G labels detected targets and
clicks one; SUPER+SHIFT+G falls back to tile -> bisect for anything detection
misses.
Stock wl-kbptr 0.4.1 is unusable on this setup, for two unrelated reasons:
- Target detection is hardcoded for ~1080p. filter_rects() discards any
candidate with height >= 50 or width >= 500, so on a 4K screen at scale = 1
nearly every real control is dropped for being "too big" and only emoji and
avatars survive — a full Slack window produced ~8 labels, all on images.
- On the rotated monitor the pointer landed in the wrong place. move_pointer()
takes output->width/height from xdg_output.logical_size, which is already
rotated, then applies the transform again. Asking for global (4000,1000) on
DP-2 landed at (4286,3726); the double rotation predicts (4301,3736).
Neither is exposed as a config option, hence the overrideAttrs. Sizes in the
config file are likewise doubled: wl-kbptr paints raw cairo onto a layer
surface and never sees a scale factor, so upstream's defaults render half-size.
Both binds are pidof-guarded: wl-kbptr only exits on a keypress, so an overlay
that loses focus first sits there holding an exclusive keyboard grab.
Add two Electron AI desktop apps built from their official upstream
binaries and wire them into the home config (x86_64 only):
- claude-desktop: Anthropic's official Linux .deb, unpacked via dpkg +
autoPatchelfHook. Wrapper disables the SUID sandbox (nix store can't
set it up) and adds the NixOS GL driver path so it hardware-accelerates
instead of falling back to software rendering.
- t3-code: pingdotgg/t3code AppImage via appimageTools.wrapType2, with
desktop entry + hicolor icons.
Both share a new custom.aiApps.deviceScaleFactor option feeding
--force-device-scale-factor; konishi's 4K@1x monitors set it to "1.5",
arrakis stays native.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The yogurt flake input was pinned via git+ssh://, which the Gitea CI
runner can't fetch (no GitHub SSH key), failing the eval job. Switch to
git+https:// — repo is anonymously cloneable, same rev/narHash.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mirror the hosts/common/features/gaming/ layout on the home side: turn the
single gaming.nix into a gaming/ directory. Moonlight (the client
counterpart to the Sunshine host) becomes moonlight.nix, gated by its own
custom.gaming.moonlight option so a host can install just the streaming
client without the full launcher/Proton toolkit (custom.gaming.enable).
- gaming.nix -> gaming/default.nix (imports moonlight.nix)
- gaming/moonlight.nix: custom.gaming.moonlight -> moonlight-qt
- arrakis: import gaming module, set custom.gaming.moonlight = true
(replaces the one-off home.packages line)
- konishi: update import path to the directory
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
twilight re-publishes under the same 1.22t tag, so the pinned hash goes
stale and `nh home switch` fails with a fixed-output hash mismatch. Move
to the stable `default` package (branded zen-beta, 1.21.5b), which uses
proper versioned releases.
- zen-browser.nix: twilight-official -> default
- xdg.nix: zen-twilight.desktop -> zen-beta.desktop mimeapp defaults
- flake.lock: bump zen-browser input
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SDDM auto-logs naps62 in and Hyprland locks immediately via hyprlock
(exec-once), so a cold boot lands on the themeable lock screen instead of
the SDDM greeter. Safe here (no FDE makes SDDM-vs-autologin marginal) and it
lets Moonlight reach the box after a cold boot, not just from suspend, since
Sunshine only runs once a session exists. konishi-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
noctalia's built-in lock screen is hard to theme and opaque to logind
(LockedHint never flips). Swap to hyprlock: clean dark theme, drive it from
hypridle (idle + before-sleep via loginctl lock-session) so logind tracks
lock state, and add a $mod CTRL+L manual lock bind. Applies to both hosts
that share this module (konishi + arrakis).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Big Picture shares the plain `steam` WM_CLASS, so it opened on whatever
monitor the cursor was on rather than the streamed one; match its title
instead and pin to ws3 (DP-1's persistent workspace, what the stream
captures). Move the steam_app_ game rule from ws6 to ws3 too so games and
Big Picture share one streamed workspace and launching a game doesn't flip
DP-1 to a different workspace.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Arm magic-packet WoL on the Intel igc NIC (re-applied every boot) so
Moonlight can wake the box from suspend to stream. Still needs "Wake on
LAN" enabled in BIOS.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Steam's CEF UI intermittently crashed its GPU child (exit_code=8704) on
NVIDIA+Wayland, falling back to CPU rendering and making Big Picture a
slideshow at 4K/HiDPI. Bake -cef-disable-gpu-sandbox into every steam
invocation via extraArgs so it covers all entry points (desktop, CLI,
Sunshine's Big Picture tile) — launch flags only apply on the invocation
that first starts Steam.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Steam/Proton titles run under XWayland with WM_CLASS steam_app_<appid>.
Without a rule they open tiled on whatever monitor Steam is on (the
primary, bottom-left HDMI-A-1). Match steam_app_.* and send them
fullscreen to workspace 6 (DP-1, top-left). Native games use their own
class and can be added individually.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nixpkgs 2026-06-10 -> 2026-06-29. Pulls in Sunshine 2026.516.143833,
which ships critical security fixes (flagged in the Sunshine web UI).
CUDA/NVENC override carries over: verified the new build still probes
h264_nvenc / hevc_nvenc / av1_nvenc.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Default nixpkgs sunshine is built -DSUNSHINE_ENABLE_CUDA=FALSE, so every
NVENC probe died with "Couldn't scale frame" and it fell back to CPU
x264. Fix:
- package = sunshine.override { cudaSupport = true; } — gives it the
CUDA scaling/colour-conversion path. Verified: probe now finds
h264_nvenc / hevc_nvenc / av1_nvenc.
- LD_LIBRARY_PATH=/run/opengl-driver/lib on the service so libcuda.so.1
(NVIDIA driver) is discoverable.
- capSysAdmin = false — Hyprland/wlroots capture via wlr-screencopy needs
no CAP_SYS_ADMIN, and the setcap wrapper's secure-exec mode would strip
LD_LIBRARY_PATH and break libcuda loading.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bring the live ~/.claude/settings.json changes upstream so the
mutable-file guard stops aborting home activation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add LocalForward entries for 47100/47101 to the yolo host, and set
LogLevel=ERROR to silence the INFO-level "channel open failed: connection
refused" noise when a forwarded port has nothing listening yet.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Enable the home-manager programs.nh module (installs nh, sets NH_FLAKE so
`nh home switch` finds this repo). Re-pin zen-browser to the 2026-06-28
revision to clear a stale twilight fixed-output-derivation hash mismatch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
user.nix fetches SSH keys via fetchurl+readFile (import-from-derivation);
--no-build blocks the IFD fetch, failing eval in a fresh store. Evaluating
the toplevel/activation drvPaths permits IFD without building the toplevels.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Job-level continue-on-error still rendered red and skipped statix/deadnix
after nixfmt failed. Step-level lets all three run without failing the job.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>