- OS: NixOS
- Window manager: Hyprland
- Status bar: Waybar
- Terminal: Kitty
- Shell: Zsh
- Current theme: Catppuccin
- Font: Iosevka/IosevkaTerm
- Editor: Neovim
- File Sync: Rsync
| Desktop |
|---|
![]() |
| Terminal + Tmux |
|---|
![]() |
| Neovim |
|---|
![]() |
The base system -- browser (Vivaldi), file manager (Yazi), desktop (Hyprland), terminal (Ghostty), editor (Neovim) -- is configured and installed the normal home-manager way, same as always.
Everything else that's more of an occasional tool than a daily driver
(compilers, IDEs, CAD/office/media apps, ...) is wrapped instead: see
home/common/lib/sandbox-apps.nix and its use in
home/common/optional/apps/default.nix. Each wrapped command is a small
script that runs nix shell nixpkgs#<pkg> --command <bin> "$@", so the
actual package is only fetched/built the first time you run it, and isn't
part of every home-manager generation.
Any command that isn't installed at all gets searched automatically: the
shell's command_not_found_handle/command_not_found_handler (see
home/common/core/shells/{bash,zsh}.nix) forwards it to
comma, backed by a prebuilt
nix-index-database
so no local index needs to be built. It only runs interactively (a real
terminal, COMMA_ASK_TO_CONFIRM=true set in home/common/core/default.nix
makes it confirm before running anything), and the mistyped/unknown
command is only ever passed through as an argument, never interpolated
into a shell string.
- NixOS (I use 24.11)
- Disk space
- Patience
- Knowledge
- Stubborness
# -- If you are me --
mkdir ~/Desktop
mkdir ~/Documents
mkdir ~/Downloads
mkdir ~/Pictures
mkdir ~/Projects
mkdir ~/School
mkdir ~/Videos
# -------------------
cd /etc/nixos/
sudo nixos-rebuild switch --flake '.#<host>'You will also have to copy over ~/.secrets. The SSH identity is no longer a manual copy -- it's decrypted from
hosts/common/users/mrgeotech/secrets/ssh.yamlvia sops-nix on rebuild; see "SSH key setup & migration" below.
SSH auth (servers and GitHub alike) uses one Ed25519 identity, sops-encrypted
in hosts/common/users/mrgeotech/secrets/ssh.yaml and decrypted to
~/.ssh/id_ed25519 on rebuild -- see home/common/core/cli/ssh.nix for the
client config (it also enables post-quantum-hybrid key exchange) and
hosts/common/users/mrgeotech/default.nix for the sops wiring. A systemd
service derives ~/.ssh/id_ed25519.pub from that private key automatically
on every rebuild, so it can never go stale.
The same key signs commits (home/common/core/cli/git.nix, gpg.format = ssh), so GitHub shows commits as "Verified" -- but that requires
registering the public key with GitHub twice, under two separate
sections of Settings > SSH and GPG keys:
- Authentication Key -- lets you push/pull over SSH.
- Signing Key -- lets GitHub verify commit signatures.
The same ~/.ssh/id_ed25519.pub content goes in both; adding it as one
does not automatically add it as the other.
Decrypting any secret in this repo (the SSH key included) requires the
age private key at ~/.config/sops/age/keys.txt matching the recipient in
.sops.yaml. That file is deliberately not in the repo -- it's the one
thing every machine needs copied to it out of band.
Only needed once, ever -- e.g. bootstrapping this whole scheme for the first time, or after the age key is confirmed lost with no backup (see "Recovering from a lost age key" below).
mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txtTake the public key it prints (age1...) and put it in .sops.yaml's
keys: list, then generate and install the SSH key per
hosts/common/users/mrgeotech/secrets/README.md.
This is the normal case -- same SSH key and same secrets everywhere, just like the four hosts in this flake already share one identity.
- Securely copy the existing
~/.config/sops/age/keys.txtfrom a machine that already works to the new one, at the same path, mode600. "Securely" means scp/an encrypted USB drive/a password manager -- never paste it through anything that logs or a chat tool. - Clone this repo and
sudo nixos-rebuild switch --flake '.#<host>'.
That's it -- no new keys to generate. The same ~/.ssh/id_ed25519 shows up
on the new machine because it's the same encrypted secret, decrypted with
the same age key.
To replace the key everywhere (e.g. you suspect it leaked) without touching the age identity:
ssh-keygen -t ed25519 -a 100 -C "<your GitHub email>" -f /tmp/id_ed25519 -N ""
{
echo "id_ed25519: |"
sed 's/^/ /' /tmp/id_ed25519
} > /tmp/ssh-secret.yaml
mv /tmp/ssh-secret.yaml hosts/common/users/mrgeotech/secrets/ssh.yaml
sops encrypt --in-place hosts/common/users/mrgeotech/secrets/ssh.yaml
rm /tmp/id_ed25519*Commit and push, then on every machine: git pull && sudo nixos-rebuild switch --flake '.#<host>'. Register the new public key with GitHub (as
both an Authentication Key and a Signing Key -- see above) and any
servers' authorized_keys before removing the old one from them, so you
don't lock yourself out mid-rotation.
If ~/.config/sops/age/keys.txt doesn't exist anywhere and there's no
backup, every secret encrypted to the old recipient (not just the SSH key)
is permanently unrecoverable -- there is no way around generating a new
age identity and re-creating each secret's plaintext from scratch:
- Generate a new age key (see "Brand new setup" above) and update the
keys:entry in.sops.yaml. - For each file under
secrets/, replace its content and re-encrypt withsops encrypt --in-place <file>(you're providing fresh plaintext, not decrypting the old one -- that's the part that's unrecoverable). - Copy the new
keys.txtto every machine and rebuild each one.
- Dileep Kishore's nix config The framework my NixOS distro is based off of
- EmergentMind's nix config: Structure, reference and some documentation
- Misterio77's nix config: Structure and reference
- VimJoyer: Whose YouTube videos aided me in beginning with Nix and persevering through challenges



