Reverts 9152e829: QQ's Wayland window still copies through the X11 CLIPBOARD, which xwayland-satellite cannot bridge, so QQ copies never reached Wayland apps.
Assisted-by: DeepSeek Harness (claude-opus-5-5)
QQ copies through the X11 CLIPBOARD even with a Wayland window; without an X server it exited on copy. QQ now shares XWayland's display trust, like WeChat.
Assisted-by: DeepSeek Harness (claude-opus-5-5)
The CanoKey resident SSH key (`ryan@canokey-moe`, the backup once a new primary key is enrolled) is now authorized for ryan and root on every host, and sudo on server and agent hosts accepts its touch through the forwarded agent (`pam_rssh`). Without the key or a forwarded agent, sudo asks for the password and login falls back to the file keys. Only `canokeySshKeys` can satisfy sudo.
Risks to weigh:
- Colmena deploys need one touch per host connection, since the agent offers the CanoKey key first.
- Desktops now forward the agent to every `hostsAddr` alias, as they already did for `192.168.*`; while a session is open, root there can request signatures, each needing a touch.
### Verification
- `just test` exits 0; CI passed on the first commit.
- Evaluated: sudo PAM stack `rssh` → `unix` → `deny` on shoryu, kana, akane and k3s-test-1-master-1, ai-niri keeps `pam_u2f`; shoryu root and ryan list `ryan@canokey-moe`.
- Not yet verified on a running host.
### Manual step after merge
Deploy the servers, then `just niri`; close old multiplexed connections (`ssh -O exit <host>`) and try `sudo true` over SSH.
AI assistance: DeepSeek Harness (claude-opus-5-5).
Assisted-by: DeepSeek Harness (claude-opus-5-5)
* feat(nixpak): sandbox Steam and Heroic in ~/Gaming
idols-ai stops preserving ~/.local/share/Steam, ~/.steam, ~/.config/heroic
and their game data, which must be moved into ~/Gaming before deploying.
Assisted-by: DeepSeek Harness (deepseek-flash)
* refactor(nixpak): replace steam-fix.nu with sandboxed protontricks
Assisted-by: DeepSeek Harness (claude-opus-5-5)
* fix(nixpak): hide host documents and GTK config from game sandboxes
Assisted-by: DeepSeek Harness (claude-opus-5-5)
* docs(ai): locate the Steam library under ~/Gaming
Assisted-by: DeepSeek Harness (claude-opus-5-5)
* fix(umu-game): confine writes to ~/Gaming and keep foreign entries
Assisted-by: DeepSeek Harness (claude-opus-5-5)
* fix(nixpak): keep Steam and Heroic in the application launcher
The wrappers only carried bin/, so neither launcher had a desktop entry
after replacing programs.steam.
Assisted-by: DeepSeek Harness (deepseek-flash)
* fix(nixpak): give the game sandboxes a fontconfig that sees CJK fonts
Steam rendered Chinese as tofu: the FHS layers build their own /etc/fonts,
whose config lists only DejaVu and the XDG font directory, so neither the
host fonts nor the FHS /usr/share/fonts were ever scanned. Point
FONTCONFIG_FILE at a small config that includes /usr/share/fonts, and ship
WenQuanYi Zen Hei and Noto Sans CJK in all three FHS layers.
Assisted-by: DeepSeek Harness (deepseek-flash)
* fix(nixpak): grant the game launchers a StatusNotifier tray icon
Steam and Heroic register a tray icon over D-Bus, and the game sandboxes
granted no StatusNotifierWatcher access, so the desktop shell had nothing
to show for them. Enable desktopIntegration.tray for all three launchers
and teach the policy suites to expect it.
Assisted-by: DeepSeek Harness (deepseek-flash)
* fix(nixpak): drop the duplicate host CJK font and tidy the font overrides
Steam reads the fonts inside its own FHS layer, so the system-wide wqy_zenhei
added for it no longer serves any sandbox. Give gaming-policy its own comment
for FONTCONFIG_FILE instead of leaving it under the compat-tool one, make the
desktop-base value a default, and note the SNI tray grant in the README table.
Assisted-by: DeepSeek Harness (deepseek-flash)
* feat(nixpak): sandbox umu-launcher with ~/Games as its home
umu-run, winetricks and bbe leave the host PATH, so umu launchers
generated before this stop working until rewritten with
`umu-install.nu <name> - <launcher> <gameid>`.
Assisted-by: Claude Agent SDK (claude-opus-5-5)
* refactor(nixpak): mount ~/Gaming as the game sandbox home
idols-ai now preserves ~/Gaming instead of ~/Games, so existing games
vanish from the host after deploy until the preserved ~/Games is moved
to ~/Gaming/Games.
Assisted-by: Claude Agent SDK (claude-opus-5-5)
* docs(nixpak): record umu runtime validation
Assisted-by: Claude Agent SDK (claude-opus-5-5)
Assisted-by: DeepSeek Harness (deepseek-flash)
dsh-nu replaced every shell command with Nushell and refused bash/pwsh
syntax before it spawned; in practice the refusals were too noisy. Drop
the flake input, install plain dsh, and remove the docs describing the
Nushell-only shell.
Assisted-by: DeepSeek Harness (deepseek-flash)
58d1af8f dropped the cache while adding the pull-through caches. Without
it a NUR bump rebuilds its npm deps here, and dsh's npmDepsFetcherVersion
2 fixed output includes the packuments the Huawei npm mirror rewrites, so
the build fails NUR's hash check instead of reusing its CI-built output.
Assisted-by: DeepSeek Harness (deepseek-flash)
dsh 0.2.2-alpha.2 -> 0.2.2-rc.1 and computer-use-linux 0.7.36 ->
0.7.37; cua-driver stays 0.38.0. The nur-packages diff is version bumps
plus regenerated dsh lockfile only, with no module or overlay changes.
Assisted-by: DeepSeek Harness (deepseek-flash)
ICU takes the zone name from the /etc/localtime symlink target path. A bind
mount turns it into a plain file, which keeps the TZif data but loses the
path, so the only identifier left is the ambiguous "CST" abbreviation that
ICU reads from the file and maps to America/Chicago.
Export TZ from time.timeZone and pass TZ/TZDIR through the nixpak env
allowlist.
Assisted-by: DeepSeek Harness (deepseek-flash)
Use the CanoKey for daily authentication on idols-ai and shoukei, with
the existing passwords, passphrases and per-host SSH keys as fallback.
- sudo: pam_u2f with a shared origin (pam://ryan4yin) and a Nix-store
authfile built from myvars.canokeyU2fKeys; the primary key is
registered and touch-to-sudo works on idols-ai.
- SSH: one resident ed25519-sk key (application=ssh:ryan4yin) exported
to ~/.ssh/id_ed25519_sk; GitHub tries it before the per-host key.
- shoukei LUKS: fido2-device=auto with token-timeout=10s. Update
nixos-apple-silicon (linux-asahi 7.2.14), whose initrd adds
thunderbolt_apple and reset_apple_cio so USB-C comes up before unlock.
- GPG: scdaemon disable-ccid so it uses pcscd.
- docs/security-keys.md: setup, PINs, enrollment, backup key and
recovery; deduplicate the FIDO notes left by #515.
Assisted-by: Claude Code (claude-opus-5-5)
* feat(nixpak): pass FIDO2 keys to Firefox, Chromium and Chrome
Assisted-by: DeepSeek Harness (deepseek-flash)
* feat(nixpak): support FIDO2 hotplug via udev-maintained node directory
Replace launch-time hidraw discovery with host udev rules that hard-link
FIDO-tagged hidraw nodes into /dev/fido-hidraw and remove them on unplug.
The sandbox dev-binds that directory and gets static /dev/hidrawN symlinks
into it, so keys plugged in after launch work and no stale node survives a
reused hidraw minor.
Narrow the extra mounts from whole /sys and /run/udev to /sys/class/hidraw,
/run/udev/data and /run/udev/control; the policy suite now forbids the
broad mounts and checks the wrapper and bind follow the fido2 option.
Assisted-by: Claude Code (claude-opus-5-5)
* refactor(nixpak): rewrite FIDO2 bwrap wrapper in Python
The bash argument loop was hard to follow. Python inserts the /dev/hidrawN
symlinks before "--" with a single list splice, and the header now explains
the whole host-udev -> bind -> symlink chain step by step.
Assisted-by: Claude Code (claude-opus-5-5)
* docs(canokey): document phone-only management policy
PCs only use the CanoKey; CanoKey Console management stays in the phone
app and the Admin PIN is never typed on a PC. Record what the config
enforces (sandboxed browsers get no raw USB/CCID, no vendor WebUSB udev
rule) and what it does not (uaccess on the raw USB node, PC/SC access to
the Admin applet, CTAP management from browsers).
Assisted-by: Claude Code (claude-opus-5-5)
* docs(canokey): separate management from use, link CanoKey docs
Assisted-by: Claude Code (claude-opus-5-5)
* fix(nixpak): create /dev/fido-hidraw from the udev rule
NixOS applies /dev tmpfiles entries only at boot; switch-to-configuration
runs systemd-tmpfiles-resetup with --exclude-prefix=/dev. After a switch
without reboot the directory was missing and every `ln` failed with exit
code 1, so no browser saw the key. The add rule now runs `mkdir -p` first.
Also fix the documented trigger command: `udevadm trigger` defaults to
`change`, which the add-only rule ignores.
Assisted-by: Claude Code (claude-opus-5-5)
* refactor(nixpak): keep tmpfiles as the only creator of /dev/fido-hidraw
The /dev tmpfiles units run before systemd-udevd on every boot, so the
directory always exists before coldplug. The mkdir in the udev rule only
covered the first switch without reboot and duplicated the tmpfiles rule;
drop it and document the one-time `systemd-tmpfiles --prefix` command.
Assisted-by: Claude Code (claude-opus-5-5)
* docs(nixpak): record FIDO2 hardware validation in Firefox and Chrome
After a reboot on ai with the CanoKey present at boot, udev linked only
the FIDO interface and WebAuthn works in Firefox and Chrome. Chromium and
plugging the key in after launch remain pending.
Assisted-by: Claude Code (claude-opus-5-5)
Preserve ~/.raft across reboots so Raft Computer keeps its enrollment, machine identity, agent state and app data. ~/.local/bin and ~/.config/gh were already persisted.
Assisted-by: DeepSeek Harness (deepseek-flash)
* feat(agents): align ruby and kana computer-use support
Assisted-by: DeepSeek Harness (deepseek-flash)
* test(agents): keep one full-host test for ruby and kana
Both guests run the same agent configuration, so a second full-host test
does not add coverage.
Assisted-by: DeepSeek Harness (deepseek-flash)
* fix(computer-use): disable cua-driver telemetry before start
The i3 init hook did not reliably disable the default-on setting, so both
hosts could start the daemon with telemetry enabled.
Assisted-by: DeepSeek Harness (deepseek-flash)
* refactor(shells): use native Home Manager Nushell environment
Replace the Bash login-shell wrapper and hand-maintained Nushell PATH with programs.nushell.loadSessionVariables and home.sessionPath. Launch Nushell directly from Kitty, Ghostty, and Zed, with the macOS config-home override and Ghostty's vendor autoload directory.
Upstream feature: https://github.com/nix-community/home-manager/pull/9666
Assisted-by: DeepSeek Harness (deepseek-flash)
* feat(hosts): use Nushell as login shell on Ruby and Kana
Set the per-user shell explicitly and add Nushell to the system shell list. The system default shell and root remain unchanged.
Assisted-by: DeepSeek Harness (deepseek-flash)
* fix(ssh): preserve tuios session marker across SSH
Forward TUIOS_SESSION and accept it on NixOS sshd, so an SSH session opened from inside tuios does not auto-attach a nested remote tuios.
Assisted-by: DeepSeek Harness (deepseek-flash)
* feat(nixos): use Nushell login shell on non-desktop hosts
Enable the Nushell login shell through a shared module imported by the server and agent profiles. Desktop profiles continue to use Bash.
Assisted-by: DeepSeek Harness (deepseek-flash)
* fix(tuios): skip auto-attach in SSH sessions
Remote interactive shells now stay plain Nushell instead of starting a nested TUIOS when the SSH client is already inside a local TUIOS session. Local TUIOS auto-start is unchanged.
Assisted-by: DeepSeek Harness (deepseek-flash)
Update nur-ryan4yin to d82be0b. Audit: only the prebuilt packages cua-driver (0.36.0 -> 0.38.0) and computer-use-linux (0.7.20 -> 0.7.36) changed; the NUR modules and trust-related settings did not move. Only the kana and ruby system closures change.
Assisted-by: DeepSeek Harness (deepseek-flash)
Only Huawei works with prefetch-npm-deps: npmmirror 404s on its doubled
slash, and Tencent answers 567 to its concurrent fetches (WA-049).
Assisted-by: Claude Code (claude-opus-5-5)
- Add the agent profile (modules/nixos/agent.nix: rootless Podman, linger,
nix-ld) with home/linux/tui.nix as its home role; ruby, kana and
aquamarine use it, so hosts no longer import from hosts/idols-ruby.
- Gate nushell's ~/.secrets source behind modules.shell.secrets.enable,
off on agent hosts.
- Persist the TUI home state on aquamarine's tmpfs root.
- Document host profiles in hosts/README.md; add tests/agent-profile and
drop the x86_64 colmena-parity test, which needs mysecrets.
Assisted-by: Claude Code (claude-opus-5-5)
Move standalone repository docs from the root into docs/ and add a documentation index that links the moved docs and the code-local runbooks.
Update Markdown links, source comments, and the root entry points to the new locations. Keep README.md and AGENTS.md at the root as required entry points.
Assisted-by: DeepSeek Harness (deepseek-flash)
Add a dedicated IPython module with startup helpers for JSON, YAML, TOML, and CSV plus a small set of common standard-library imports for interactive use.
Disable SQLite history and stop preserving ~/.ipython: interactive sessions can contain credentials and other sensitive data, so that profile state must not survive reboots implicitly.
Assisted-by: DeepSeek Harness 0.2.2-alpha.2 (deepseek-flash)
The Mystic Light controller drops its LED state on S3 resume without
re-enumerating, so no udev event fires and openrgb.service keeps running
without reloading the profile. Reapply it from a unit ordered after
suspend.target, through the running server.
Assisted-by: Claude Code (claude-opus-5-5)
At boot whisper-asr could start before the LAN address was configured and
fail with "could not bind on any address". Let whisper-asr.socket bind it
with FreeBind and have uvicorn take fd 3, like the exporters.
Assisted-by: Claude Code (claude-opus-5-5)
A default-node hook selects wivrn.sink/wivrn.source whenever they exist,
so the polling wivrn-audio.nu unit, its saved state and `just vr-audio`
go away. Streams follow via linking.follow-default-target.
Assisted-by: Claude Code (claude-opus-5-5)
`just mihomo-geo` copied geodata into the state dir so a cold start never
fetched it. tmpfiles now links v2ray-rules-dat and the DB-IP lite mmdbs
there, so geo-auto-update moves to the gateway config only.
Assisted-by: Claude Code (claude-opus-5-5)
myNurPkgs evaluated a second nixpkgs just for four packages. Apply the
NUR repo's own overlay instead, so they come from the host's pkgs.
Darwin's pkgs now gets nixpkgsConfig too: it set nixpkgs.pkgs, which
ignores nixpkgs.config, so only myNurPkgs carried the npm mirror there.
Assisted-by: Claude Code (claude-opus-5-5)
Both only touch pkgsStatic builds, which nothing on this host pulls in
since qemu-user binfmt was removed; the toplevel drvPath is unchanged.
Assisted-by: Claude Code (claude-opus-5-5)
agenix's darwin module honours age.secrets.<name>.path and applies owner and
mode itself, so the environment.etc links and the postActivation chown/chmod
that re-applied them go away.
Assisted-by: Claude Code (claude-opus-5-5)
WA-023 warns once nixVersions.stable reaches 2.35; WA-046 fails the build
once nixpkgs' xdg-desktop-portal-wlr is no longer the broken 0.8.4, so both
pins get revisited instead of lingering.
Assisted-by: Claude Code (claude-opus-5-5)
hardware.amdgpu.opencl.enable also adds clr, whose OpenCL 2.2 libOpenCL.so.1
lands in /run/opengl-driver/lib, first in OpenVINO's RUNPATH, and shadows the
ocl-icd loader the GPU plugin links. With clr.icd alone the plugin finds
ocl-icd, so the wrapper and WA-038 go.
Assisted-by: Claude Code (claude-opus-5-5)
It is already the sshd module default, and the shared firewall forces
allowedTCPPorts to [ ] anyway; toplevel drvPaths are unchanged.
Assisted-by: Claude Code (claude-opus-5-5)
systemd's 60-fido-id.rules marks the FIDO hidraw node and the CCID USB
device as security tokens, and 70-uaccess.rules grants them to the
active seat. Drop the hand-written rules and the plugdev membership.
Assisted-by: Claude Code (claude-opus-5-5)
Exporters raced networkd for the LAN address at boot; drop the
network-online ordering and unlimited-restart hack and let a FreeBind
socket unit hold the port, passed in via --web.systemd-socket.
Assisted-by: Claude Code (claude-opus-5-5)
The data dir is the module's own DynamicUser StateDirectory (already
/var/lib/victoriametrics -> private/victoriametrics on youko), so the
victoriametrics-data group, its tmpfiles rule and SupplementaryGroups
did nothing. stateDir was set to its default.
Assisted-by: Claude Code (claude-opus-5-5)
Root is tmpfs, so the backing dirs must exist before the /data bind
mounts. initrd tmpfiles rules on /sysroot do that before switch-root,
replacing the activation script.
Assisted-by: Claude Code (claude-opus-5-5)
services.homepage-dashboard runs the nixpkgs package with DynamicUser,
replacing the :latest container, its system user and the activation
script that rsynced the YAML into place. The YAML stays as is and is
linked into /etc/homepage-dashboard.
Assisted-by: Claude Code (claude-opus-5-5)