* chore(aquamarine): take the NPU stack from nixos-unstable-small
* refactor(aquamarine): load the NPU driver via hardware.cpu.intel.npu
Assisted-by: DeepSeek Harness (claude-opus-5-5)
Hosts now accept only the two CanoKeys' resident SSH keys for ryan, plus the romantic recovery key for root; per-host user keys stop working. Darwin builds work again: the shared base no longer sets nixpkgs.config next to nix-darwin's nixpkgs.pkgs.
Assisted-by: DeepSeek Harness (claude-opus-5-5)
Assisted-by: DeepSeek Harness (deepseek-flash)
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)
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)
* 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)
- 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)
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)
Enable the existing remote-builder module on shoryu, shushou and
youko, and authorize their SSH host keys on aquamarine.
Kana and Ruby are VM guests with no need to build; leave their local
store and build behavior unchanged.
Assisted-by: DeepSeek Harness (deepseek-flash)
Run nginx on youko as a loopback pull-through HTTP cache for
cache.nixos.org and cache.numtide.com, exposed by Caddy at
https://nix-cache.writefor.fun.
Assisted-by: DeepSeek Harness (deepseek-flash)
systemd-tmpfiles-setup only orders itself after local-fs.target, but the nofail /data submounts can complete later. It then sees /data as the read-only fallback tmpfs and fails to create the Transmission, Immich, RustFS and restic directories at boot.
Add RequiresMountsFor for the data mounts and cover the generated unit dependency with an eval test.
* feat(nixpak): sandbox six desktop apps
* chore(nixpak): trim OBS plugin set
* fix(nixpak): enable OBS on aarch64
* fix(nixpak): let imv read the full home directory
* fix(home): drop redundant freerdp package
* fix(nixpak): grant OBS access to V4L2 camera devices
* fix(nixpak): expose V4L2 sysfs to OBS
* fix(nixpak): expose NVIDIA encode library to OBS
* docs(nixpak): record OBS interactive validation
* docs(nixpak): record OBS RTMP validation
* fix(nixpak): run MuseScore via XWayland and PulseAudio
* docs(nixpak): explain app sandboxing rationale
* fix(nixpak): route MuseScore ALSA through PipeWire
* refactor(nixpak): split policy checks into per-app suites
Make hardening/tests/policy.nix a small loader that auto-discovers one suite per app under hardening/tests/policy. Shared checks live in policy-lib.nix and common expectations are generated by mkAppPolicy; app files only declare differences and special cases. The loader also discovers wrappers from hardening/nixpaks and fails when wrapper/suite inventory or check names diverge.
Assisted-by: DeepSeek Harness (deepseek-flash)
* fix(nixpak): reject duplicate per-app policy checks
mkAppPolicy merges generated and custom checks, so an extraChecks key could silently override a standard assertion. Fail explicitly when a per-app check name is defined twice.
Assisted-by: DeepSeek Harness (deepseek-flash)
Switch to the native deb package from nixpkgs-patched (#560225), remove the AppImage/FHS bwrap layer, and enable native Wayland while keeping the X11 fallback.
Keep only WeChat state under one app-scoped HOME and bind the host Documents and Downloads directories like the other desktop apps. User-selected files also go through the XDG FileChooser portal. Remove the launcher, activation script, and compatibility symlinks.
Assisted-by: DeepSeek Harness (deepseek-flash)
* refactor(nixpak): unify desktop app sandboxes
Move common GUI, portal, and network policy into reusable modules and migrate browsers plus Zoom/WeChat/QQ/Telegram to the same boundary.
Assisted-by: DeepSeek Harness (deepseek-flash)
* fix(nixpak): stop exposing the system D-Bus to apps
* fix(nixpak): bind only the PulseAudio socket
* feat(nixpak): run apps in a new session tied to the launcher
* feat(nixpak): start apps from an allowlisted environment
* feat(nixpak): isolate app networking with pasta
* fix(nixpak): keep Firefox on the host network
Assisted-by: Claude Code (claude-opus-5-5)
* chore(nix): update nixpkgs to e62746b and fix 26.11 breakage
Bump nixpkgs to e62746b and preservation to 00be7f7, then adapt to the removals and renamed options in the new revision: drop the removed boot.initrd.systemd.enable settings, trim the stale emSign CA blacklist on Linux while keeping it for the older Darwin input, and migrate Avahi publishing to settings.publish. Work around sunshine still setting the legacy Avahi options and keep dsh-nu on its tested Nushell 0.115.1. Patch NUR dsh's pnpm call to pass nodejs = null, and update pyclipsync for the stdenv.hostPlatform.system rename.
* chore(nix): refresh flake inputs for 26.11
- update nixpkgs-darwin to the same e62746b revision as nixpkgs
- make dsh-nu follow main nixpkgs and patch its testedWith manifest for the resolved Nushell
- refresh floating inputs; update lanzaboote, tuios, and ponytail
- drop the nixpkgs-2505 and rust-overlay workarounds
- keep pyclipsync on main and simplify the cross-platform CA blacklist
- adopt home-manager's Nushell loadSessionVariables default
* fix(gitea): use ROOT_URL for nixpkgs 26.11
nixpkgs removed the legacy settings.server.DOMAIN option; the existing ROOT_URL already carries the public URL.
* chore(nix): update dsh-nu to 0.7.0
Assisted-by: DeepSeek Harness (deepseek-flash)
Reading the host's install script evaluated each guest, which pulls the private secrets input, so
CI could not evaluate the suite at all.
Assisted-by: DeepSeek Harness 0.2.1-rc.2 (deepseek-flash)
* chore(agents): build dsh-nu from its own flake
NUR now keeps only dsh, so a plugin release needs `nix flake update dsh-nu` instead of a NUR package update.
* feat(agents): use the shell's own dialect; allow 5 s retry backoff
Assisted-by: DeepSeek Harness (deepseek-flash)
* feat(dsh): make Nushell the only shell
Assisted-by: DeepSeek Harness (deepseek-flash)
* docs(agents): wait for CI in the background
Pushes to a PR often start multi-minute CI runs, and an agent that holds
the turn open on a foreground watch reports nothing until it ends. Require
the pushed head and running checks to be reported first, the watch to run
in the background, and the result to be collected after independent work
or after ending the turn.
Assisted-by: DeepSeek Harness (deepseek-flash)
* docs(agents): keep the shell-handoff rule platform-neutral
The global rules carry no Windows assumptions: drop the cmd, pwsh, and
wsl handoff examples and keep the POSIX forms plus the dsh refusal.
Assisted-by: DeepSeek Harness (deepseek-flash)
* docs(dsh): keep the shell-swap docs POSIX-only
This repository manages Linux and Darwin hosts, not Windows: drop the
Windows handoff examples and terminal-backend wording from dsh.md. The home
patch also disabled pwsh-sandbox redundantly because dsh-base already
disables that row on POSIX; keep only the active bash-sandbox disable and
verify the composed rows with --dump-config.
Assisted-by: DeepSeek Harness (deepseek-flash)
* docs(agents): keep the global rules runtime-neutral
Global rules are loaded by every agent CLI, so they must not describe one
runtime's behavior. Drop the dsh-specific sentence and wording while keeping
the expectation to prefer Nushell structured pipelines and Python. Runtime
behavior stays documented in home/base/tui/agents/dsh.md.
Assisted-by: DeepSeek Harness (deepseek-flash)
* docs(agents): default shell work to Nushell
Keep the Nushell preference explicit in the runtime-neutral global rules, and
keep runtime behavior in runtime docs. Bash stays a narrow fallback for tasks
Nushell cannot do.
Assisted-by: DeepSeek Harness (deepseek-flash)
* docs(agents): tighten global-rule scope and CI guidance
Keep global rules runtime- and project-neutral, and state that boundary in the
file. Remove the duplicated CI turn-steering text in favour of the Communication
rule, resolve the watch run before watching instead of relying on
gh pr checks --watch alone, name the working-directory parameter generically,
and distinguish a bounded retry backoff from a fixed completion wait.
Assisted-by: DeepSeek Harness (deepseek-flash)
* docs(agents): never estimate a CI run's duration
A CI run has no predictable length, so do not turn "it looks short" into a
reason to wait. The rule is unconditional: report the head and started checks,
start a background watch, and end the turn; collect the result on completion.
The eval scenario now uses an unknown duration.
Assisted-by: DeepSeek Harness (deepseek-flash)
* docs(agents): move maintenance guidance to the README
The runtime/project boundary is guidance for editing the rules, not a rule
agents need loaded every session. The README's Maintaining global rules
section already owns that audience; the loaded rules keep only behavior.
Assisted-by: DeepSeek Harness (deepseek-flash)
* refactor(dsh): render the home patch from a YAML template
Move the home-layer cordis patch into dsh-cordis.patch.yml, substitute the
bundle and Nushell store paths with replaceVars, and link the rendered file
through home.file.source. This module is the only writer of
~/.dsh/cordis.patch.yml, the current host has no such file, and the profile
patch is a separate layer.
Assisted-by: DeepSeek Harness (deepseek-flash)
* refactor(dsh): install the shell plugin through NUR withPlugins
Replace the local runCommand bundle, peer symlink, and home-layer YAML file
with the plugin package and dsh.withPlugins from ryan4yin/nur-packages#43. The
package wrapper adds a generated --patch overlay after the profile layer, so
dsh no longer writes or manages ~/.dsh/cordis.patch.yml. The NUR input is
temporarily pinned to the PR branch and its commit; point it back at main once
the NUR PR merges.
Assisted-by: DeepSeek Harness (deepseek-flash)
* refactor(nix): expose the NUR package set as nurPkgs
Pass one per-system NUR package set through specialArgs instead of repeating
nur-ryan4yin.packages.${pkgs.stdenv.hostPlatform.system} at each call site.
The agents, rime, and idols-ruby modules now use the short binding.
Assisted-by: DeepSeek Harness (deepseek-flash)
* refactor(nix): use nurPkgs in package overlays
The overlays receive specialArgs too, so they can use the per-system NUR
package set instead of rebuilding the long input path from prev. This leaves
only the definition in outputs/default.nix.
Assisted-by: DeepSeek Harness (deepseek-flash)
* refactor(nix): name the per-system NUR set myNurPkgs
Leave room for consuming another NUR repository later, and match the existing
mylib/myvars naming.
Assisted-by: DeepSeek Harness (deepseek-flash)
* chore(dsh): consume the merged NUR package set
nur-ryan4yin now points at main after ryan4yin/nur-packages#43 was squash
merged; the lock records the merge commit. No branch ref remains.
Assisted-by: DeepSeek Harness (deepseek-flash)
* fix(dsh): pass the web profile explicitly
The withPlugins launcher prepends --patch, which historically prevented dsh
from auto-expanding "dsh web" into "--profile web". Pass the profile
explicitly in the Just recipe and the verification commands so they work with
both plain dsh and the wrapper.
Assisted-by: DeepSeek Harness (deepseek-flash)
* chore(dsh): pick up the wrapped-profile fix
Update nur-ryan4yin to the squash merge that keeps an implicit profile name
first, so "dsh web" and "just dsh-web" work through the wrapper again.
Assisted-by: DeepSeek Harness (deepseek-flash)
* chore(dsh): pick up the plugin peer-link fix
Update nur-ryan4yin to the squash merge that links the dsh peer tree at
node_modules/@deepseek-ai instead of nesting it one level deeper. This is the
import failure that stopped nushell-executor from activating after deploy.
Assisted-by: DeepSeek Harness (deepseek-flash)
* feat(aquamarine): make the iGPU usable for media and OpenVINO
The R9700s stay reserved for inference: VA-API/QSV transcoding moves to the iGPU media
engine, and `openvino-gpu` works around ROCm's OpenCL loader shadowing the plugin's
`ocl-icd` (WA-038).
Assisted-by: DeepSeek Harness 0.2.1-rc.2 (deepseek-v4.1-flash)
* docs(aquamarine): document OpenVINO on the iGPU
How to run models on the iGPU: the `openvino-gpu` wrapper, the encoder staticization Whisper needs
on `GPU`, compile/steady timings and memory, and the language and cache caveats.
Assisted-by: DeepSeek Harness 0.2.1-rc.2 (deepseek-v4.1-flash)
The goal is speech recognition on the NPU for vinput, and sherpa-onnx cannot
deliver it: every model it loads keeps a dynamic time axis, which the NPU
compiler rejects outright. Qwen3-ASR and SenseVoice both core-dumped in
vpux-compiler ("Got non broadcastable dimensions pair"), and reshaping the ONNX
inputs does not help because the dynamic dimensions sit inside the graph
(WA-037). Whisper is the only family OpenVINO publishes fully static IRs for,
and its fixed 30 s window is exactly what the compiler accepts.
The host now runs an OpenAI-compatible /v1/audio/transcriptions service backed
by whisper-large-v3-turbo int8 on the NPU: 1.5 s for 11 s of audio against
2.1 s on CPU, with the compiled graph cached in /var/cache so only a cold cache
pays the ~60 s NPU compile. vinput on the desktop sends raw PCM to it through a
small provider script.
Only the NPU driver and compiler still come from the unmerged branch in the
`nixpkgs-npu` input (NixOS/nixpkgs#570230), whose nixpkgs also happens to be
the one where openvino-genai 2026.4.0.0 builds at all.
Assisted-by: DeepSeek Harness 0.2.1-rc.2 (deepseek-v4.1-flash)
Aquamarine builds for ai and shoukei across the three platforms, with both
daemons logging in as root. They authenticate with their passphrase-free SSH
host keys, and ssh_known_hosts now carries each builder's address.
Assisted-by: DeepSeek Harness 0.2.0-rc.2 (deepseek-flash)
On a tmpfs root the flake/fetch cache is empty at every boot, so
`nix run <flake-ref>` re-downloads its input and fails until DNS is up: a reboot
test showed llama-swap's first start failing on that. Other caches write into
RAM meanwhile.
Assisted-by: DeepSeek Harness (deepseek-flash)
* fix(ssh): retry and multiplex LAN connections
* fix(ai): shrink the neighbour-probe fallback to ~1s
Assisted-by: DeepSeek Harness 0.2.0-rc.2 (deepseek-flash)
The tuned package ships tuned-ppd.service with WantedBy=graphical.target, so
activation starts it without /etc/tuned/ppd.conf and switch-to-configuration exits 4.
Assisted-by: DeepSeek Harness (deepseek-flash)
systemd ignores `path` inside [Service], so the unit ran with the default PATH
instead of the coreutils one it asks for.
Assisted-by: DeepSeek Harness (deepseek-flash)
The gateway LAN IPv6 transparent path stalls on TCP flows while IPv4 forwarding remains healthy. Withholding AAAA answers keeps LAN clients on the working fake-IPv4 TProxy path until the IPv6 path is fixed
Assisted-by: DeepSeek Harness (gpt-6.1-sol)
--redact produced a "shareable" template but replaced only the secret and the proxy-subscription urls: rule-provider tokens, private domains, the LAN controller address and the routing rules all stayed. Nothing in the repo consumed it.
Assisted-by: DeepSeek Harness (Qwen3.8-Flash-Next-IQ3_S-ista-text)
Adds the review-comments-and-docs skill (1282-word body) and two evaluation suites: a 21-case
measured suite for the skill, and a harness that runs the two concurrency scenarios previously left
unrun in agents/evals/global-rules.md.
The worker MicroVMs ran `home-manager-ryan.service`, which failed on two of them
(`/nix/var/nix/temproots` permission denied, and no `/home/ryan`), so
`HostSystemdServiceCrashed` fired for days. Mirror the masters and extend their
regression test to all six nodes.
Assisted-by: DeepSeek Harness 0.2.0-rc.2 (deepseek-flash)
* flake.lock: Update
nixpkgs only. The new revision builds VictoriaMetrics with cgo
(CGO_ENABLED=1); its pure-Go zstd decoder rejected a stored values block, so
every small-part merge panicked on youko.
* fix(colmena): address the VM hosts by name, not by their IP
`~/.ssh/config` pins the passphrase-protected builder key with IdentitiesOnly
for 192.168.*, so IP-addressed ssh cannot authenticate without a terminal. The
name resolves through the aliases in /etc/ssh/ssh_config.
* fix(monitoring): repair two alert rules VictoriaMetrics rejects
`on(uid)` matches on a label kube-state-metrics no longer emits, and
`!= ON(__name__, instance)` treats every pg_settings series as a duplicate.
They now join on (namespace, pod) and compare with `changes(...[5m])`.
* fix(monitoring): keep disk latency alerts off mechanical disks
Both rules averaged per-op latency with no device or sample guard, so the 4 TB
USB HDD volumes paged constantly (>100 ms/write in 26% of the last 30 days).
They now require a non-rotational physical device and >=5 ops/s.
Assisted-by: DeepSeek Harness 0.2.0-rc.2 (deepseek-flash)
* docs(agents): state the boundaries the rules only implied
* docs(agents): grow the scenario tables and record the method
* docs(agents): record the evaluation rounds and their size cost
* docs(agents): require disclosure of AI-assisted contributions
* docs(agents): correct safety boundaries and eval records
Assisted-by: DeepSeek Harness (qwen3.8-flash-next iq3, gpt-6-luna & gpt-6.1-sol)
* fix(microvm): pin guest IPv6 identity to a cluster ULA
The ISP retires delegated prefixes, so a guest that registers its WAN prefix
loses it and Cilium never refreshes the node address. Derive the identity from
the IPv4 octets inside fd05:5::/64 and trust that ULA in the guest firewall.
* feat(mihomo): answer AAAA and keep the tailnet off the TUN
fake-ip needs one pool per family, and the family a client picks does not pick the
egress family. Exclude the cluster ULA and the Tailscale ranges from the TUN; the ssh
pin and the WA-028 record cover the fake-AAAA stall.
* feat(mihomo): generate the config from the repo, not a copy in the live dir
`just mihomo-gen` runs generate.nu from the module and validates with the core
the service runs. ~/.config/mihomo keeps only sources.yaml and config.yaml.
* fix(mihomo): keep the fake IPv6 pool out of fc00::/7
The v6 fake pool is a lookup key, not the egress path, so it can be a public range: the
shipped ULA default reads as "local" to browsers and prompts for local-network access on
every site. The gvisor stack override goes with it -- mips is upstream's default, and
nftables rules out the kernel stack here.
* feat(mihomo): sniff on the gateway, not on the desktop
LAN clients using DoH dial the IPs they resolved, and a node may not reach an IP a
foreign resolver handed out. The gateway recovers the name from the handshake; on the
desktop the fake-ip table already names every flow. Its binary is a static release
build, not a package: update it by hand and validate the live config with the new core.
* fix(mihomo): name the Singapore nodes instead of matching 新
The single-char keyword also matched 新西兰, so the Singapore group held a New
Zealand node and url-test flipped the exit country mid-session.
* feat(mihomo): keep ranges that can never be proxied out of TUN
The list lives in policy.yaml as tun_exclude_address; sources.yaml may override
it. 198.18.0.0/15 stays in TUN: that is the fake-ip range itself.
* feat(mihomo): match Valve's content servers by ASN
The 20 hand-copied prefixes had drifted from what AS32590 announces. The rule
type needs GeoLite2-ASN.mmdb, so geox-url.asn points at the same mirror.
* feat(mihomo): render the gateway config from the same sources
The hand-kept copy had drifted: a manual sync carried the desktop's secret onto
the gateway and dropped external-ui-url. --redact yields a shareable template.
* fix(mihomo): open the link DNS takeover only once the core serves it
The core opens :1053 after parsing rules, and rule parsing is where missing
geodata gets fetched. Flipping at fork time made that fetch SERVFAIL, the config
fatal, and the unit unstartable.
* fix(justfile): keep local endpoints out of the agent proxy
With only HTTP_PROXY set, a request to this host's own llama-swap endpoint went
to mihomo. Node matches exact hosts only, so the literals carry it; the CIDRs
serve curl and Go.
* feat(justfile): seed mihomo geodata into the service state dir
So a cold start never needs the download that the DNS takeover made unstartable.
The comment trim dropped them. Without a domain rule above the Valve IP ranges,
steamcommunity.com and steampowered.com match those ranges and go DIRECT.
Steam picks download caches from the IP it sees on the CM login. Pin the CM hosts
and content CDN DIRECT, keep the community and store proxied, and add Valve's
AS32590 ranges because Steam dials cached CM IPs without a lookup. Sources in
README.md.
BCM4378/brcmfmac intermittently fails Wi-Fi association (ASSOC-REJECT /
authentication timeout) on this laptop. NetworkManager misreads the failure
as a bad key and re-prompts; whichever client reconnects (Noctalia, nmtui,
nmcli) then saves the answer as a duplicate "<SSID> 1" profile. The problem
predates the Noctalia v5 shell and is driven by the driver, not the shell.
- disable Wi-Fi powersave on shoukei, a common source of brcmfmac auth timeouts
- WORKAROUNDS.md WA-024: the powersave mitigation and its removal condition
- WORKAROUNDS.md WA-025: the pending upstream brcmfmac port_authorized fix
- document how to recover a wedged supplicant without rebooting, and that
deleting an active NM profile is what turned a reconnect into a dead link
Refs: https://patchew.org/linux/20260915-brcmfmac-4way-handshake-offload-authenticated-event-v2-1-fbb9cc7ac177@jannau.net/
* feat(shoukei): run x86/x86_64 apps and games via muvm+FEX
Apple Silicon's 16K pages mean FEX cannot run natively and qemu-user cannot
run complex x86_64 binaries. Wire the maintained rowanG077/nix-x86-on-aarch64
runtime (FEX inside a 4K muvm microVM) into shoukei so games, Steam, Wine and
other x86/x86_64 programs work, and let the module own the x86 ELF handlers
instead of qemu-user.
The input is pinned to our fork (ryan4yin/nix-x86-on-aarch64) at the reviewed
commit. Dropping boot.binfmt.emulatedSystems also removes x86_64 from
nix.settings.extra-platforms, so the qemu-user/uncached-x86-build gap is
recorded as WA-022.
- flake.nix: add nix-x86-on-aarch64 input (WA-021)
- hosts/12kingdoms-shoukei/game/: programs.x86-on-arm + steam/wine, 6 GiB VM
- hardware-configuration.nix: drop emulatedSystems/preferStaticEmulators
- apple-silicon.nix: drop the now-redundant box64
- docs: WA-012 resolved; WA-021 and WA-022 added; host README updated
* refactor(nixpkgs): drop the repo-wide pkgs-x64 package set
Every remaining pkgs-x64 consumer runs only on x86_64-linux hosts, where the
native pkgs already carries the same allowUnfree config
(modules/nixos/base/nix.nix) and overlays (modules/base/overlays.nix), so
replace the references with pkgs and drop the extra nixpkgs instantiation
(which also removes an eval on aarch64-darwin).
media.nix keeps the native Zoom app on x86_64 hosts only, since zoom-us has
no aarch64 Linux build.
* feat(restic): verify the repository after each backup
* feat(youko): snapshot the restic store hourly
* docs(backup): document the snapshot and check layers