Combine nebula-snowglobe (NixOS) + main (darwin) into one dual-OS flake - #22
Merged
Conversation
Initial import of the nebula host flake from ~/src/dotfiles onto a bare (orphan) branch: snowglobe-lib-based system config, the GNU Stow dotfiles tree under home/, and the gh (GitHub CLI) system package.
Set background to a neutral #0e0e0e (was cool-navy #101014) and lower background-opacity to 0.92. Pin the libadwaita window surface to #0e0e0e for both active and :backdrop states in corners.css, and add window-theme = ghostty, so the transparent focused window no longer picks up libadwaita's default blue accent tint.
Add a stow-managed git config that signs commits and tags with the SSH key held in 1Password (op-ssh-sign), plus the matching allowed_signers entry so signatures verify locally.
niri has no settings daemon / running xdg-desktop-portal to broadcast a color-scheme, so GTK apps (LibreOffice's gtk3 VCL plugin included) default to light and override any in-app dark theme. Set GTK_THEME=Adwaita:dark as a session variable, plus the gtk-3/4 prefer-dark settings.ini hints.
Backup/Template/Gallery/AutoText/AutoCorrect/dictionary defaulted to ~/.config/libreoffice/4/user/<...>. Seed registrymodifications.xcu so backups go to ~/.local/state and the rest to ~/.local/share, keeping the profile/settings in ~/.config. Exact xcu syntax verified by round-tripping through a live soffice launch.
Claude Code MCP servers launch via npx (e.g. the chrome-devtools-mcp plugin runs 'npx chrome-devtools-mcp@…'), which fails on stock NixOS since no node runtime is on PATH. Add nodejs (node/npm/npx) and bun.
- Wrap stow + libreoffice-paths activation snippets in subshells so their exit/set -u don't abort or leak into the rest of NixOS activation (was silently breaking boot of new generations). - Enable boot.loader.grub.useOSProber so the Windows install on a separate disk shows up in the menu (reason nebula stays on GRUB, not systemd-boot). - Add docs/bootloader-issues-jun-06.md documenting the investigation.
Restore the neovim lua config into the stow tree (home/nvim/) and add
nixosModules/default/neovim.nix providing the LSP servers, formatters,
linters, and tools the config needs on PATH.
- programs.neovim {enable, viAlias, vimAlias} (snowglobe-lib replaces the
nixpkgs module, so no defaultEditor/extraPackages); EDITOR set separately.
- Tools ported from the main branch; dockerfile-language-server-nodejs
renamed to dockerfile-language-server.
- vtsls rebuilt against nodejs_24 so node 22 stays out of the closure.
- gcc added so nvim-treesitter can compile parsers.
The initial encrypted snapshot (prev commit) was captured while Helium was running, so Cookies/Login Data were mid-write. Re-captured with Helium quit for a consistent point-in-time; Preferences/Local State also picked up state Helium flushes on close.
- compare-skip: when the age identity can't be resolved (e.g. 1Password
locked), leave the existing .age untouched instead of re-encrypting — a
fresh age nonce over identical plaintext churned git on every unattended
capture. Warn and tell the user to unlock + re-run to update.
- HELIUM_AGE_OP_REF: use no-colon ${VAR-…} so an explicit empty value really
disables the op route, as the comment claims (${VAR:-…} fell back to the
default on empty).
- diff: decrypt to a temp file and check the exit status before diffing — in a
<(…) process substitution age's failure was lost, so a decrypt error showed
as the whole live file differing. Now prints a clear per-file marker.
Flip nixpkgs ownership: this flake now pins
github:NixOS/nixpkgs/nixos-unstable directly and snowglobe-lib follows
it (inputs.nixpkgs.follows = "nixpkgs"), so there's a single nixpkgs in
the store and we control the rev. Pulled unstable to 567a49d
(2026-06-16)
for the latest security fixes and the newer in-tree NTFS/ntsync driver
off the Windows disk. Default kernel is now 7.1.
corefreq disabled (programs.corefreq.enable = false): its out-of-tree
module fails to build on 7.1 (cppc_perf_fb_ctrs.reference_perf →
reference). Re-enable when upstream catches up.
Notable package changes (nvd /run/booted-system → /run/current-system):
kernel / GPU
linux 7.0.11 -> 7.1
nvidia-open 595.80-7.0.11 -> 595.80-7.1
security-relevant
chromium 149.0.7827.53 -> 149.0.7827.114
electron 40.10.2 -> 40.10.3
apache-httpd 2.4.67 -> 2.4.68
dnsmasq 2.92rel2 -> 2.93
curl 8.20.0 (rebuild)
gnupg 2.4.9 (rebuild)
nss 3.124 -> 3.125
1password-cli 2.34.0 -> 2.34.1
virt
libvirt 12.2.0 -> 12.4.0
qemu-host-cpu-only 11.0.0 -> 11.0.1
filesystems
zfs-user 2.4.2 -> 2.4.3
xfsprogs 6.19.0 -> 7.0.1
desktop / KDE Frameworks
kf6 (kio, kconfig, kirigami, …) 6.26.0 -> 6.27.0
breeze-icons 6.26.0 -> 6.27.0
uwsm 0.26.4 -> 0.26.5
kitty 0.47.1 -> 0.47.4
mangohud 0.8.3 -> 0.8.4
tooling
neovim 0.12.2 -> 0.12.3
gh 2.93.0 -> 2.94.0
fastfetch 2.63.1 -> 2.64.2
statix 2026-05-14 -> 0.5.8 (2026-06-13)
removed
corefreq 2.1.0 (+ unit-corefreq.service)
Closure: 2965 -> 2927 paths (-318.1 MiB).
The PNG logo fell back to ASCII inside tmux because fastfetch hard-blocks image logos when TERM is exactly "screen", and tmux had no default-terminal set (so TERM=screen). - tmux: set default-terminal "tmux-256color" so TERM != "screen" - fastfetch: logo.type kitty-icat (only protocol that renders in ghostty+tmux; native kitty sends a malformed payload, kitty-direct/sixel fall back) - docs: add docs/fastfetch.md and docs/tmux.md (with the ghostty+grim test harness), register both in the CLAUDE.md manuals table
DP-3 (landscape OLED) had its current wallpaper pointing at the portrait 1440x3440 image — it falls back to the global [wallpaper.default] (portrait) whenever the OLED drops its DisplayPort signal and Noctalia re-detects it. - Set DP-3 back to horizontal-wide/nebula-1.jpg and captured the corrected settings.toml snapshot (config/noctalia/settings.toml) - Add Super+Shift+W: re-applies each monitor's intended wallpaper from the snapshot via tomato + 'noctalia msg wallpaper-set' — a panic button for the recurring fallback. Update target with 'noctalia-config capture'.
The previous bind re-applied exact filenames from the snapshot, so it broke the moment the image files were swapped (stale paths -> wallpaper-set on a missing file = no-op). Now it reads each monitor's configured directory ([wallpaper.monitor.<conn>].directory) and sets the first image found there — filename-agnostic, deterministic, survives swapping the wallpapers.
noctalia.lua overrode inactive_border to its surface colour (rgb 131313), lighter than ghostty (#0e0e0e), leaving a faint line between tiled ghostty windows in gapless layouts. Add noctalia-overrides.lua (required last so it wins) setting inactive border to rgb(050505) -- tuned so the opaque border, after the HDR/gamma lift that ghostty content does not get, renders ~rgb13, matching ghostty interior.
`nix flake update` + `nrs`. nvd diff (system gen 72 → 73) shows only one realized package change in the closure; nixpkgs itself was unchanged (26.11.20260616.567a49d both sides), so the other input bumps were no-ops for the built system. Realized change (nvd): hyprland 0.55.0+date=2026-06-19_d486a5f → 0.55.0+date=2026-06-21_5a7078d closure 2952→2952 paths (40 added/40 removed, delta +0, disk +801.4KiB) Inputs bumped in flake.lock: hyprland d486a5f → 5a7078d snowglobe-lib 38e3a18 → a676eee (revCount 1657 → 1663) sops-nix 9ed6585 → 420f8d2 noctalia-shell a7af758 → ca1d211 nixpkgs-stable bd0ff2d → a037402 Jovian-NixOS e509169 → ee34630 nix-index-database 1a2ea89 → 854d04e nix-post-build-hook-queue a670d87 → 3b04dc1
…S3 wake Immediate wake-from-S3 on nebula traced to MSI BIOS "Wake Up Event By: BIOS" overriding all Linux wakeup controls; setting it to OS fixes it. Documents the fix, wake-source diagnostics, and the xHCI/PME red herrings. Claude-Session: https://claude.ai/code/session_01HXHkGCV6M7EDz8xrsE4Wou
… 06-26, noctalia 06-22 → 06-27)
Define Hyprland directly in modules/hosts/nebula/hyprland.nix instead of via
snowglobe-lib.desktop.hyprland.enable. That module force-enabled hyprlock
(which auto-enables hypridle via nixpkgs' hyprlock module) plus
kitty/dolphin/hyprlauncher — none used here (ghostty, fuzzel/Noctalia
launcher, Noctalia lock). hyprlock comes from nixpkgs but the hyprland-packages
overlay swaps hyprutils globally for the flake's newer one, so flake updates
that outpaced nixpkgs' hyprlock broke the build (Seat.cpp CSharedPointer →
bool). The new module re-asserts the shared snowglobe desktop layer
(system.hasDesktop + desktop.{enable,installWaylandDeps}) niri used to provide,
plus programs.fuzzel.
Remove niri entirely (host is Hyprland-only now): the snowglobe niri desktop
module, programs.kanshi + home/kanshi (drove niri displays; fought hl.monitor
under Hyprland), home/niri, swaybg (painted niri's wallpaper; Noctalia does
Hyprland's), and the now-dead kanshi-stop autostart hook. Refresh stale niri
attributions in docs and comments.
The shared git config hardcoded op-ssh-sign at the NixOS system-profile path
(/run/current-system/sw/bin), which is absent on macOS (1Password ships it in
the .app bundle), so every signed commit failed on the Mac. Split the program
into signing-{linux,macos}.inc and select by home-dir prefix (/home vs /Users)
— a reliable OS discriminator since all repos live under $HOME, and gitdir
resolves symlinks so nebula's /etc/nixos rebuild path still matches Linux.
Forward-compatible with the planned single Linux+macOS branch.
nebula-snowglobe lacked the home/gh package that main carries, so switching this checkout to nebula left ~/.config/gh/config.yml a dangling symlink (stow had linked it while on main) — gh then errored on startup and its git credential helper returned nothing, breaking pushes. Add the same config.yml (comment pointed at modules/nixos/dotfiles-stow.nix) so both branches stow an identical gh package and neither dangles. Auth stays in the untracked hosts.yml.
Joins the orphan nebula-snowglobe branch (NixOS host nebula, 140 commits) with main via --allow-unrelated-histories. Both branches were deliberately pre-shaped for this: identical dendritic skeleton (flake-parts + import-tree), disjoint class dirs (modules/darwin/ vs modules/nixos/), and the includeIf gitdir OS-discriminator already shared. Resolutions: - flake.nix: input union. Single nixpkgs on nixos-unstable (NixOS-test-gated; safe for darwin), initially pinned to nebula's prior rev to keep nebula byte-stable. import-tree unified on vic/import-tree. Explicit sops-nix input (snowglobe-lib now follows it) for upcoming darwin secrets support. snowglobe-lib/hyprland/noctalia/tomato pinned at nebula's locked revs (unstable HEAD renamed snowglobe-lib.libvirtd-qemu; bump separately). - modules/flake-parts.nix: systems = aarch64-darwin + x86_64-linux. - modules/packages.nix: main's shape; nebula's packages/ dissolved into pkgs/ per AGENTS.md (aggregator + shell.nix + devshell.nix removed, dev shell gains nil/nom/sops/age/ssh-to-age/stow). kitten/podman/apple-container guarded darwin; wowup/flatpak-user guarded linux. Linux re-export block obsoleted by the systems union. - modules/overlays.nix: union; overlays/podman.nix platform-guarded (Linux keeps nixpkgs' podman); per-package overlays for the migrated pkgs; tomato inline (closes over inputs); wowPath default moved into pkgs/wowup.nix so overlay + packages output stay in sync. - home/: git config = union (aliases/lfs/mergetool + difftastic/delta/ codeberg helper); .zshrc = main's superset with OS guards (direnv/fzf guarded); tmux gains default-terminal tmux-256color; fastfetch takes nebula's glyphs + kernel module, keeps logo.png; ghostty split into shared config + config-file = ?os.conf (per-OS file generated by each ghostty module — follow-up commit); nvim keeps main's tree (nebula had drifted behind) + nebula's snacks notifications keymap; the duplicate <M-B> debug bind was dropped as accidental. - .gitignore union; main's CLAUDE.md/README/.envrc retained (docs rewrite follows). nix flake show --all-systems: all 3 darwin hosts + nebula evaluate.
Stow scoping: home/ deploys wholesale on both OSes, so each dotfiles-stow module now carries an explicit skip list for the other OS's packages (darwin skips the Wayland/freedesktop set; nixos skips the Homebrew/Library/1Password- socket set). Unlisted packages deploy everywhere — the right default for cross-platform CLI configs. Lists mirror each other in comments. NixOS twins for the darwin-only machinery (each existing darwin module untouched unless noted): - nh.nix: nrs/nrb/nrt as real executables via `nh os` (replaces the zsh wheel-gated shell functions; flake dir resolved with readlink -f since nix --flake won't follow the ~/src/dotfiles symlink on nebula). - git.nix: git/gh/git-lfs/delta/diffnav/difftastic (moved out of nebula's configuration.nix; lfs + difftool now referenced by the shared git config). darwin git.nix gains difftastic for the same reason. - direnv.nix + direnv-nom.nix: explicit direnv provisioning (was transitive on nebula) with the nix-direnv stdlib + nom wrapper linked via tmpfiles; wrapper text extracted to lib/direnv-nom-wrapper.nix and shared by both classes. direnv dropped from the nixos stow skip list. - ghostty.nix: programs.ghostty.enable (moved from configuration.nix) + the Linux half of the shared config as a generated os.conf (tmpfiles); darwin ghostty.nix generates the macOS half (window chrome, font 18, cmd+` quick terminal). The stowed config ends with `config-file = ?os.conf` so the per-OS file wins and a missing one is non-fatal. - neovim.nix (nixos): + svelte-language-server (the merged nvim config enables svelte on both OSes now). zsh.nix (nixos): + eza (alias chain). nix flake show --all-systems: all 4 host configurations evaluate.
- modules/darwin/sops.nix (universal): imports sops-nix's darwinModules.sops
with the age identity derived from the host SSH key (ssh-to-age) — no new
key material to manage. Inert until a host defines secrets.
- .sops.yaml: documents the per-host recipient workflow; adds k's recipient
(derived from its ssh_host_ed25519_key) and a creation rule for
modules/hosts/k/secrets.yaml. mini/SOC-Kris-Williams recipients TODO —
derive on each machine.
- modules/hosts/k: encrypted smoke-test secret proving darwin decryption
end-to-end (sops.defaultSopsFile + sops.secrets.sops-smoke-test).
- modules/{darwin,nixos}/gpg.nix: gpg-agent enabled class-wide on both OSes
(moved out of nebula's configuration.nix), enableSSHSupport explicitly
false — SSH auth and git signing stay with the 1Password agent.
- modules/darwin/pass.nix: pass-xdg on macOS (twin of modules/nixos/pass.nix),
backed by the new gpg-agent.
…merge - AGENTS.md: dual-OS overview (4 hosts, two module classes + twins convention), nixos build/eval commands, stow skip lists, config/ snapshot section, secrets workflow, updated file tree, docs/ manuals section. - README: dual-OS intro + NixOS (nebula) setup section (checkout layout, symlink caveat, nrs/nrb/nrt on both OSes). - knowledge/: okf scaffold stubs for the 22 new nixos modules/host/packages; decision records nixos-darwin-unification, stow-os-skip-lists, sops-darwin-ssh-host-keys; log.md milestone entry; okf index regenerated. okf validate: 127 files, 0 errors, 0 warnings.
…, inherit typescript) Remaining statix warnings are pre-existing nebula host-config style (repeated-keys in configuration.nix and the generated hardware-configuration.nix) — left for a dedicated cleanup.
Correctness: - nebula: snowglobe-lib.libvirtd-qemu.enable -> snowglobe-lib.qemu.enable — the flake update (5497cb0) moved snowglobe-lib to f0135ce, which renamed the option (verified 1:1: same libvirtd + qemu_kvm + virt-manager payload). Restores nebula evaluation, nixos-rebuild, and the cross-eval gate. - nebula: permit insecure pnpm-10.29.2 (build-time dep of vesktop via programs.discord; flagged by nixpkgs b5aa0fb) with a dated removal note — second half of unbreaking nebula eval on the updated inputs. - cbissues: strict owner/repo slug validation — $repo was spliced unescaped into a single-quoted command inside the fzf enter binding ($SHELL -c), so a crafted remote URL or argument containing a quote could inject commands. Reject, don't escape. Same validation in cbissue for its API URLs. - cbissues: guard `--state` with a missing value — the loop's trailing shift hit $#==0 and killed the script silently under set -e (cbissue already had the guard for -l). - sudo-1password: SSH_AUTH_SOCK must use the unbraced literal $HOME — NixOS writes sessionVariables into /etc/pam/environment via a plain replaceStrings ["$HOME"] substitution; the braced ${HOME} was written verbatim and resolved by pam_env as a nonexistent pam_env variable, silently breaking the agent socket path for PAM sessions. - packages: restore the aarch64-linux re-export of ccglass + codebase-memory-mcp (deliberate coverage from 371fa1b that the merge dropped; the root systems list doesn't include aarch64-linux). - helium-config: capture's read-failure message no longer claims "not valid JSON" for raw (cat) transforms. - nvim: drop the <leader>n picker bind the merge reintroduced from the stale nebula copy — misc.lua owns <leader>n (same shadowing 2595d6c fixed once). Cleanup: - stow: extract the shared restow/self-heal script body to lib/stow-restow-script.nix (parameterized by home/skip/runAsUser), same pattern as lib/direnv-nom-wrapper.nix — a future healing fix can no longer land on only one OS. - updates.log: untracked (gitignored) — machine-appended rebuild log nothing consumes; history retains the old entries. - tomato/noctalia comments: point at keybindings.lua's toggleGaps instead of the deleted scripts/toggle-gaps.sh. - cbissue/cbissues/helium-config: AGENTS.md shell color convention (RED/GREEN/YELLOW/BLUE/NC) with colored error output. Verified: nebula toplevel cross-eval passes on the updated inputs; all three darwin systems rebuild; cbissue/cbissues/helium-config packages build (shellcheck); nix flake check exit 0; statix/deadnix show only the four pre-existing nebula-config style warnings.
nixpkgs' vesktop deliberately pins CVE-flagged pnpm-10.29.2 (10.29.3+ crashes electron-builder < 26.8.2 at launch), so the nebula whitelist is structural for now. The decision record captures the analysis and the preferred whitelist-free exit — repack upstream's prebuilt AppImage like wowup and set programs.discord.package — plus the re-test cadence on nixpkgs bumps. okf validate: 128 files, 0 errors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One flake for all four machines:
k/mini/SOC-Kris-Williams(nix-darwin, aarch64-darwin) +nebula(NixOS, x86_64-linux) — joining the orphannebula-snowglobebranch (140 commits) with main via--allow-unrelated-historiesso both histories stay reachable.Highlights
nixos-unstable(NixOS-test-gated; safe for darwin). Inputs were subsequentlynix flake updated on-branch; the snowglobe-liblibvirtd-qemu→qemuoption rename and the vesktop/pnpm insecure flag that surfaced are fixed in the review commit.home/): git config unified (aliases + lfs + mergetool + difftastic + delta + codebergopcredential helper), one OS-guarded.zshrc, ghostty split into shared config + per-OS generatedos.conf(config-file = ?os.conf), nvim reconciled on main's newer tree. Per-OS scoping via mirroredskiplists in the two dotfiles-stow modules; the shared restow/self-heal script body lives inlib/stow-restow-script.nix.nh osnrs/nrb/nrt), git, direnv + direnv-nom (was transitive-only on nebula), ghostty; nebula'spackages/dissolved intopkgs/+ overlays per convention (overlays/podman.nixplatform-guarded).ssh-to-age),kcarries an encrypted smoke-test secret (verified decrypting live). gpg-agent + pass-xdg class-wide on both OSes (enableSSHSupport = false— 1Password keeps SSH/signing).docs/; decision recordsnixos-darwin-unification,stow-os-skip-lists,sops-darwin-ssh-host-keys;okf validateclean.cbissues, the PAM${HOME}bug defeating the 1Password sudo socket on nebula, restored aarch64-linux package outputs, a reintroduced duplicate<leader>nkeymap, plus cleanup (shared stow script extraction,updates.loguntracked, stale comment refs, shell color convention).Verification
nix flake checkexit 0; all three darwin systems build;nix store diff-closuresvs the pre-merge system shows only intended additions (sops pipeline, gpg-agent, pass stack, difftastic, os.conf) + channel drift.nix eval .#nixosConfigurations.nebula.config.system.build.toplevel.drvPath— full cross-eval from a Mac passes on the updated inputs.nrsonk: skip lists honored (no Linux configs in$HOME), os.conf linked, gpg-agent loaded,/run/secretsmaterialized.statix/deadnix: only four pre-existing nebula-config style warnings (noted in the lint commit).Post-merge per-machine steps
mini/SOC-Kris-Williams:nrs, thenssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub→ add recipients to.sops.yaml.nebula: switch the checkout fromnebula-snowglobeto main, thennrs(first switch renames the qemu option in place and picks up the direnv/git/nh modules).