NixOS configuration for miralda (Framework 13 AMD), managed with clan-core.
Stack: Niri + UWSM + ReGreet · Noctalia shell · ZFS + impermanence · YubiKey PIV age · clan vars
- Nix with flakes enabled
- clan-cli (available in the devShell)
- A configured YubiKey with a PIV age identity (for secret decryption)
- SSH access to
root@miralda.goclan.org
# Enter devShell (direnv picks this up automatically if you use it)
nix develop
# Or with direnv:
direnv allowThe devShell exposes these shell functions:
deploy [boot|switch] # default: switch — build locally, push, activate on miralda
deploy-biene [boot|switch] # same, targets biene (override host with BIENE_HOST=...)
push [remote] [branch] # default: origin main — push via gh auth tokenImpermanence makes ~/.config/git a read-only bind mount, so git push with a credential helper doesn't work. The push shell function works around this by reading gh auth token at runtime and injecting it into the HTTPS remote URL.
First-time setup (run once per machine, persisted by impermanence):
gh auth login # authenticate with GitHub (browser or token)Day-to-day pushing:
push # pushes main to origin
push origin my-branch # pushes a specific branchIf you need gh for other tasks (PRs, issues, releases), use it directly — authentication is handled via the keyring.
-
Boot NixOS installer, partition and format with disko:
nix run github:nix-community/disko -- --mode disko machines/miralda/disko.nix
-
Install NixOS:
nixos-install --flake .#miralda --no-root-password -
Create ZFS blank snapshots (impermanence rollback targets):
zfs snapshot zroot/root@blank zfs snapshot zroot/home@blank
-
Generate clan vars (WiFi credentials, admin/lgo passwords, age identity):
clan vars generate miralda
Each generator prompts for the required inputs (passphrases, YubiKey slot, etc.).
-
Set up YubiKey age identity on miralda after first login:
age-plugin-yubikey --generate --slot 1 > ~/.age/yubikey-identity.txt
The recipient stored in
vars/per-machine/miralda/is used by sops for secret encryption.
# 1. Edit config files
# 2. Deploy (switch = immediate activation):
deploy
# Or stage a boot entry without switching (safe for risky changes):
deploy boot
# 3. Commit and push:
git add -A && git commit
pushSecrets / clan vars: If you change a secrets/ generator, re-run:
clan vars generate miraldaThen redeploy. Use clan machines update miralda if the full inventory evaluation is needed (e.g. after sops key changes).
The YubiKey serves two roles: SSH authentication (via GnuPG agent) and age decryption (PIV-backed identity for sops/clan vars).
For full setup instructions and troubleshooting, see docs/guides/yubikey.md.
| Task | YubiKey needed? |
|---|---|
deploy (local to miralda) |
Yes — SSH to root@miralda.goclan.org uses the GPG auth subkey |
clan vars generate |
Yes — decrypts/re-encrypts secrets with age-plugin-yubikey |
clan machines update |
Yes — both SSH and secret decryption |
Editing config, nix eval, building |
No |
push / gh operations |
No — uses GitHub token, not SSH |
git commit |
No |
If deploy fails with Permission denied (publickey):
# 1. Check the YubiKey is visible:
gpg --card-status
# 2. Check GPG agent is serving SSH keys:
ssh-add -L
# 3. If ssh-add shows nothing, restart the agent:
gpgconf --kill gpg-agent
gpg --card-status # re-triggers agent + card detection
ssh-add -L # should now show the keyIf gpg --card-status fails, the YubiKey isn't plugged in or pcscd isn't running:
sudo systemctl status pcscdflake.nix — devShell, machine composition, module injection
clan.nix — clan metadata, overlays, inventory instances
modules/
base.nix — universal NixOS defaults (EFI boot, Plymouth, flakes, openssh, zsh)
zfs-impermanence.nix — ZFS rollback on boot, boot flags, common persist paths
hardware/yubikey.nix — pcscd, GnuPG agent (pinentry-qt Wayland wrapper), polkit
hardware/printing.nix — CUPS + hplip, SANE scanning
desktop/niri.nix — Niri compositor, UWSM, ReGreet, Pipewire, fonts, Noctalia PAM
desktop/gnome.nix — GNOME/GDM, extensions, shared dconf
roles/laptop.nix — GPU drivers, power management, Framework hw (fprintd, fwupd)
users/lgo.nix — lgo power user: shell, devtools, HM config
users/admin.nix — admin user: SSH keys, impermanence
locale.nix, networking.nix — shared locale/keyboard and ZeroTier/mDNS options
service-modules/
machine-type.nix — @clanarchy/machine-type (laptop, server, vm, rpi)
desktop.nix — @clanarchy/desktop (niri, gnome, kde)
users.nix — @clanarchy/users (lgo, sabine)
yubikey.nix — @clanarchy/yubikey (pcscd, GnuPG, polkit)
printing.nix — @clanarchy/printing (CUPS, SANE, hplip)
software.nix — @clanarchy/software (browsers, email)
machines/miralda/
configuration.nix — identity (hostname, locale, cpu), tablet hw, stateVersion
disko.nix — NVMe partitioning (GPT: 1G ESP + ZFS AES-256-GCM)
stylix.nix — Selenized Black theme, generated wallpaper, cursor
apps.nix — GUI/CLI apps, unfree allowlist, Flatpak, Podman, OBS
wallpapers.nix — Per-workspace nix-anarchy SVG wallpapers
home/ — Home Manager per-user configs
home-modules/ — HM modules: browsers, console tools
secrets/ — Clan vars generators (passwords, WiFi, age identity)
machines/biene/
configuration.nix — identity (hostname, locale, cpu), Fritz!Box wifi, stateVersion
disko.nix — NVMe partitioning (GPT: 1G ESP + ZFS)
stylix.nix — Catppuccin Mocha theme
vars/per-machine/ — Generated secrets (gitignored sensitive values)
sops/ — sops age keys
Syncthing keeps ~/Public in sync across all clan machines. When a new machine joins, add it as a peer in clan.nix:
inventory.instances.syncthing.roles.peer.machines.new-machine = {
settings.folders.public.path = "/home/lgo/Public";
};Then generate its syncthing vars and redeploy both machines:
clan vars generate new-machine
deploy switch # on each machineMachines discover each other automatically via their zerotier IPs stored in clan vars — no manual device ID exchange needed.
GNU General Public License v3.0 — see LICENSE.