Introduction
This flake manages all Nix-based configurations — NixOS, nix-darwin, and Home Manager — from a single repository.
What’s inside
| Directory | Purpose |
|---|---|
systems/ | NixOS and nix-darwin host configurations (one directory per host) |
homes/ | Home Manager user environments |
modules/ | Reusable NixOS, darwin, home-manager, and devix (dev-env) modules |
packages/ | Custom packages and overrides |
shells/ | Dev shells (nix develop .#go, .#website, .#nix) |
overlays/ | Nixpkgs overlays |
lib/ | Helper functions |
secrets/ | sops-encrypted secrets |
docs/ | This documentation (mdBook) |
See Architecture for the full map, the tag system, and how to add a host or user.
Key technologies
- Stylix — system-wide theming
- Disko — declarative disk partitioning
- Colmena — stateless deployment
- Devenv — reproducible dev environments
- SOPS-nix — secrets management
- Lanzaboote — Secure Boot
- nixos-facter — hardware reports
Architecture
This page is the map of the repository: what lives where, how a host gets built, and where to start when adding something new.
Directory layout
| Path | Purpose |
|---|---|
flake.nix | Inputs and output wiring. Host/user discovery is delegated to systems/ and homes/. |
systems/<arch>/<host>/ | One directory per host, auto-discovered. The directory name is the hostname. |
systems/iso/ | Installer ISO definition and the list of hosts that get an iso-* image. |
modules/nixos/ | NixOS modules (desktop, services, system, security, users, …). |
modules/darwin/ | nix-darwin modules (homebrew, services, system, users). |
modules/home-manager/ | Home Manager modules — the largest tree, see background vs foreground. |
modules/devix/ | Development-environment mechanism: a registry of languages, addons, LSPs, and formatters consumed by editors (helix, zed, opencode). Exposed as homeModules.devix and devenvModules.*. See devix. |
homes/<user>/ | Per-user Home Manager entry points, auto-discovered for standalone homeConfigurations. SSH public keys live in homes/<user>/keys/. |
lib/ | Small helper library layered onto nixpkgs.lib (directory listing, tag predicate, eachSystem). |
overlays/ | Nixpkgs overlays: custom packages, modifications, pkgs.unstable, … |
packages/ | Custom packages: top-level/ (auto-discovered), custom/ (namespaced under pkgs.custom), plus python/kodi/home-assistant scopes and the docs book. |
shells/ | Dev shells (nix develop .#go, .#website, .#nix). |
secrets/ | sops-encrypted secrets. Recipients (age keys per user and host) are declared in secrets/.sops.yaml. |
scripts/ | Repo tooling (package bump script, CI checks) — driven via the justfile. |
docs/ | This mdBook. Published via GitHub Pages, buildable with nix build .#docs. |
justfile | Task runner: just --list shows all daily commands. |
How a host is built
flake.nix
└─ systems/default.nix scans systems/<arch>/ for host directories
├─ arch contains "darwin"? ─ nix-darwin's darwinSystem
│ └─ modules/darwin + home-manager + stylix + sops + homes/nixos.nix
└─ otherwise ─────────────── lib.nixosSystem
└─ modules/nixos + home-manager + facter + homes/nixos.nix
└─ homes/nixos.nix instantiates home-manager for
homes.users (default: the primary user), each importing
homes/<user>/ + modules/home-manager
networking.hostNameis forced to the directory name.- A
facter.jsonin the host directory is picked up automatically (hardware detection via nixos-facter). - A
nixpkgs.nixin the host directory supplies per-host nixpkgsconfig, merged over the shared one. This is the only way to set it:pkgsis instantiated insystems/default.nixoutside the module system, so a host cannot usenixpkgs.configin itsconfiguration.nix(the nixpkgs module rejects it). Seesystems/x86_64-linux/kyuubi/nixpkgs.nix, which permits an insecure Broadcom wifi driver for that host alone. users.primaryUsermust be set in every host config; the user account itself comes frommodules/nixos/users/(ormodules/darwin/users/).- Standalone
homeConfigurations.<user>(for non-NixOS machines) are generated from thehomes/<user>/directories, independent of any host.
The tag system
Hosts describe themselves with a list of tags instead of toggling dozens of options:
# systems/x86_64-linux/madara/configuration.nix
system.tags = [ "desktop" "workstation" "development" "management" ];
- The known tags are enumerated in
modules/shared/tags.nix— the option type rejects anything else at eval time, so a typo or a tag without consumers cannot slip in silently. Current tags:desktop,laptop,workstation,development,management,nas,gaming,server(each with a one-line meaning in that file). - Modules query tags through
config.lib.tags.hasTag "<tag>"(the predicate lives inlib/has-tag.nix; the option is declared undermodules/{nixos,darwin}/system/tags/). - Tag modules under
modules/{nixos,darwin}/system/tags/translate tags into concrete settings (e.g.desktopenables the desktop environment stack,managementinstalls colmena,gamingenables Steam). Server roles key on theservertag — tailscale exit-node/connector/SSH advertising and the no-sound-stack default — never on the absence of an interactive tag: a workstation or laptop withoutdesktopis still somebody’s machine. - Colmena reuses the same tags for deployment targeting:
colmena apply --on @desktop(wired viacolmena.deployment.tags). - Home Manager cannot read
config.lib.tagswithout infinite recursion, somodules/home-manager/imports.niximports the predicate directly againstosConfigand maps tags onto its ownhosts.{desktop,laptop,workstation,development}.enableoptions. Inside home-manager modules, gate onconfig.hosts.*.enable.
Background vs foreground
modules/home-manager/ is split by session type, not by program category:
background/— everything that works on a headless machine or over SSH: shell, git, editors, CLI tools, AI tooling, user services. Imported unconditionally.foreground/— everything that needs a display: desktop environments, bars, GUI programs, fonts-for-GUI. Gated behindforeground.enable, which defaults from thedesktoptag (darwin), thegui.enableoption (NixOS), ortrue(standalone home-manager).theming/— stylix wiring, wallpaper, color scheme; enabled with thedesktoptag.development/— policy: which languages/tools frommodules/devix(the mechanism) are turned on fordevelopment-tagged hosts.
devix
Development-environment mechanism: describe a language once — its language servers, formatters and indentation — and every editor that consumes the description configures itself from it. Stylix’s idea, applied to dev tooling.
It is documented in its own section, starting at devix: concepts, usage, adding a language, adding an editor, and a generated support matrix and option reference.
The one thing worth repeating here is the split this repository depends on:
modules/devix is pure mechanism and enables nothing, while
modules/home-manager/development/ is the policy that decides which languages
are on for which hosts.
Adding a host
- Create
systems/<arch>/<hostname>/default.nix(plusconfiguration.nix,hardware-configuration.nixorfacter.json, and optionallydisk.nixfor disko). The directory is discovered automatically. - Set
users.primaryUserandsystem.tagsin the config. - Add the host’s age key to
secrets/.sops.yamland re-encrypt:just secrets-rekey(key comes from the host’s SSH key, see sops). - For colmena deploys, set
colmena.deployment.targetHost. - If the host should get an installer image, add it to the list in
systems/iso/default.nix— thenjust iso <hostname>.
Adding a user
- Create
homes/<user>/default.nix(auto-discovered ashomeConfigurations.<user>); put SSH public keys inhomes/<user>/keys/. - Create
modules/nixos/users/<user>.nix(auto-imported; seemirza.nixfor the pattern). - Set
users.primaryUser = "<user>"on the hosts that belong to them, and add their age key tosecrets/.sops.yaml.
Custom options cheat sheet
Options defined by this flake (as opposed to upstream NixOS/HM options):
| Option | Defined in | Meaning |
|---|---|---|
system.tags | modules/{nixos,darwin}/system/tags/ | Host capability tags (see above) |
users.primaryUser | modules/{nixos,darwin}/users/ | The one human this machine belongs to |
users.users.<u>.isAdmin | modules/nixos/users/ | wheel + virtualisation groups |
homes.users / homes.enable | homes/nixos.nix | Which users get home-manager |
gui.enable | modules/nixos/programs/ | “This host has GUI applications” (feeds foreground.enable) |
desktop-environment.enable | modules/nixos/desktop/ | Desktop environment stack |
hosts.{desktop,laptop,workstation,development}.enable | modules/home-manager/imports.nix | HM-side mirror of the tags |
devix.* | modules/devix/ | Development environments — see the devix section |
foreground.enable | modules/home-manager/foreground/ | GUI-facing home config |
theming.{enable,scheme,image} | modules/home-manager/theming/, modules/nixos/system/theming.nix | Stylix scheme/wallpaper |
rssh.enable, yubikey.enable, cachix.enable, latex.enable | modules/nixos/** | Feature toggles for individual services |
NAS behaviour follows the nas tag, and the TPM2 stack is driven by the
upstream security.tpm2.enable (this flake just layers PKCS11/tooling on top).
New custom options should be namespaced (or documented here) so they stay distinguishable from upstream options.
Getting Started
Clone
git clone https://github.com/arunoruto/flake ~/.config/flake
The flake sets NH_FLAKE (used by nh) to ~/.config/flake
automatically. If you clone elsewhere, override it:
export NH_FLAKE=/path/to/flake
Most day-to-day commands are wrapped in the justfile — run
just --list from the repo root to see them.
NixOS
First-time install:
sudo nixos-rebuild switch --flake ~/.config/flake#<device-name> --accept-flake-config
After initial setup, use nh for convenience (see Daily Usage):
nh os switch ~/.config/flake#<device-name>
Darwin (macOS)
sudo nix run nix-darwin/nix-darwin-<version>#darwin-rebuild -- switch
Replace <version> with the nix-darwin release (e.g., 25.11).
Home Manager
On NixOS
home-manager switch --flake ~/.config/flake#<username> --accept-flake-config
Standalone (non-NixOS)
nix --experimental-features 'nix-command flakes' --accept-flake-config run nixpkgs#home-manager -- switch --flake ~/.config/flake#<username>
Shells like zsh require quoting:
--flake './#<username>'
Directory layout
Your host config lives in systems/<arch>/<host>/. Home Manager profiles are in homes/<username>/. Add new ones following the existing patterns.
Building ISOs for NixOS Hosts
This flake supports building bootable ISOs that embed the flake source, so nixos-install --flake works against /etc/nixos/flake without network access to the flake repo.
ISOs are generated from a single generic module (systems/iso/installer.nix) parameterized by hostname.
Quick start
# Build ISO + .sha256 checksum sidecar (Ventoy-ready)
nix build .#iso-<hostname>
# Copy to USB (ISO + sidecar file)
cp -r result/iso/* /mnt/ventoy/
# Or write directly with dd
sudo dd if=result/iso/*.iso of=/dev/sdX bs=4M status=progress conv=fsync
## Adding a new ISO target
Append the hostname to the `isoHosts` list in `flake.nix`:
```nix
isoHosts = [ "shinji" "kenpachi" "zangetsu" ];
The generic installer.nix module:
- Sets
isoImage.edition= hostname (distinguishable filenames likenixos-shinji-25.11-x86_64-linux.iso) - Embeds the flake source at
/nixos-flake→ copied to/etc/nixos/flakeat boot - Includes
diskoin the live Nix store - Prints MOTD instructions referencing the hostname
- Provides an
autoinstallsystemd oneshot (triggers whenautoinstallis in/proc/cmdline)
Install workflow
Manual (default)
- Boot from USB
- Login (root, no password)
- Follow the on-screen instructions:
sudo disko --mode disko --flake /etc/nixos/flake#<hostname>
sudo nixos-install --flake /etc/nixos/flake#<hostname> --root /mnt
sudo reboot
Autoinstall
Add autoinstall to the kernel command line at boot:
- In the GRUB menu, highlight the entry and press
e - Append
autoinstallto thelinuxline - Press
Ctrl+xorF10to boot
The system will automatically partition the disk (via disko), install NixOS, and reboot.
Example: shinji ISO
# Build ISO only
nix build .#nixosConfigurations.iso-shinji.config.system.build.isoImage
# Build ISO + checksums (recommended)
nix build .#nixosConfigurations.iso-shinji.config.system.build.isoChecksums
# Write to USB
sudo dd if=result/iso/*.iso of=/dev/sdX bs=4M status=progress conv=fsync
For Ventoy, copy result/iso/* directly — the .sha256 file will be auto-detected.
Prerequisites for a host to be ISO-installable
The target host must:
- Use disko for disk partitioning (
disk.niximportinginputs.disko.nixosModules.disko) - Have
fileSystemsinhardware-configuration.nixcommented out — disko generates them declaratively - Include the block device kernel module in
boot.initrd.availableKernelModules(e.g."nvme","ahci","sd_mod")
How it works
systems/iso/installer.nix receives hostname via specialArgs and uses it to:
- Set the ISO edition/volume label
- Populate MOTD instructions and autoinstall commands dynamically
- The flake source (
self) is embedded as a raw copy on the ISO at/nixos-flake - At boot,
postBootCommandscopies it to/etc/nixos/flakewherenixos-install --flakecan find it
Verification
Build the ISO with an accompanying .sha256 checksum file for verification:
nix build .#nixosConfigurations.iso-<hostname>.config.system.build.isoChecksums
Output:
result/
├── iso/
│ ├── nixos-<hostname>-...iso
│ └── nixos-<hostname>-...iso.sha256
└── SHA256SUMS
The .sha256 file uses sha256sum format (hash filename). Copy result/iso/* to your USB stick.
Ventoy
Ventoy auto-detects .sha256 files placed next to the ISO. Select the ISO in the Ventoy boot menu, press m, choose SHA256 — it calculates and compares against the file, confirming the copy was not corrupted.
Manual verification
cd result/
sha256sum -c SHA256SUMS
Size considerations
- The ISO includes the flake source closure, the live Nix store (squashfs), and disko
- Typical size: ~1.4 GB depending on extra
storeContents - Use
isoImage.squashfsCompression = "zstd -Xcompression-level 19"(default) for best compression - Add
isoImage.compressImage = truefor an extra.zstlayer (slower build, smaller file)
Generic minimal ISO
The flake also has a barebones ISO at systems/iso/default.nix that imports the standard installation-cd-minimal with helix added. Not registered as a nixosConfiguration — build it directly if needed.
Secrets Management (sops-nix)
All secrets in this flake are encrypted with sops using age as the backing encryption layer. sops-nix handles decryption at boot and materializes secrets into /run/secrets/ (NixOS) or ~/.config/sops/ (Home Manager).
How it works
secrets/secrets.yaml ──encrypted to──► 15 age public keys (.sops.yaml)
│
▼ sops-nix (at boot)
/run/secrets/<name> (NixOS hosts)
~/.config/sops/<name> (Home Manager / macOS)
Each host derives its age identity from its SSH host ed25519 key. The age public key is listed in .sops.yaml. At boot, sops-nix converts the host’s SSH private key to an age identity and decrypts the secrets.
NixOS hosts
Key generation at first boot
SSH host keys are generated by services.openssh.hostKeys (configured in modules/nixos/services/ssh.nix). The ed25519 key at /etc/ssh/ssh_host_ed25519_key is the one used by sops-nix.
sops-nix auto-converts this key to an age identity via generateKey = true and stores it at /var/lib/sops-nix/keys.txt. No manual key provisioning needed on the host — it “just works” after first boot.
Configuration (per host, implicit)
# modules/nixos/security/secrets.nix
sops = {
defaultSopsFile = "${secretspath}/secrets.yaml";
age = {
sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
keyFile = "/var/lib/sops-nix/keys.txt";
generateKey = true;
};
};
Using a secret in a host config
sops.secrets."tokens/beszel-marvin" = {
mode = "0444";
};
Then reference it:
environment.KEY_FILE = config.sops.secrets."tokens/beszel-marvin".path;
Adding a new NixOS host to secrets
- Generate the age public key from the host’s SSH key:
ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub
# Output: age1...
If ssh-to-age isn’t available:
nix shell nixpkgs#ssh-to-age --command ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub
- Add the age public key to
secrets/.sops.yamlunder the host’s entry:
creation_rules:
- path_regex: secrets.yaml$
key_groups:
- age:
- age1... # shinji
- age1... # kenpachi
- age1... # new-host ← add here
- Re-encrypt the secrets file so the new host can decrypt it:
sops updatekeys secrets/secrets.yaml
- Commit the updated
.sops.yamlandsecrets.yaml. The new host can now decrypt secrets on its next boot.
Home Manager / macOS
User age key
Home Manager users decrypt secrets using their own age identity at ~/.config/sops/age/keys.txt (not an SSH host key). This file must be placed manually:
mkdir -p ~/.config/sops/age
# Copy your age identity (private key) here:
echo "AGE-SECRET-KEY-..." > ~/.config/sops/age/keys.txt
chmod 600 ~/.config/sops/age/keys.txt
Bootstrap: user SSH key from secrets
The user’s SSH private key (~/.ssh/id_ed25519) is itself stored in secrets.yaml and decrypted at Home Manager activation. This works because the user’s age key (e.g. mirza) is separately listed in .sops.yaml and can decrypt the SSH key secret.
# modules/home-manager/background/secrets.nix
sops.secrets."ssh_keys/${config.user}" = {
path = "${config.home.homeDirectory}/.ssh/id_ed25519";
};
The user’s age key (for sops CLI use) is separate from the SSH key (for git/SSH access).
Editing secrets
# Decrypt, edit in $EDITOR, re-encrypt
sops secrets/secrets.yaml
To add a new secret entry:
# secrets/secrets.yaml
my-new-token: "sk-abc123..."
Then reference it in your Nix config:
sops.secrets."my-new-token".path = "/run/secrets/my-token";
Key hierarchy
| Key | Source | Used by |
|---|---|---|
| Host age keys (shinji, kenpachi, …) | /etc/ssh/ssh_host_ed25519_key → age | sops-nix decryption on that host |
| User age keys (mirza, mar) | Manually generated with age-keygen | sops CLI for editing secrets |
| User SSH keys | Decrypted from secrets.yaml by Home Manager | Git push, SSH access, etc. |
| SSH host keys | Generated at first boot by services.openssh.hostKeys | SSH server identity + age identity source |
Common commands
# Edit secrets
sops secrets/secrets.yaml
# Re-encrypt after adding/removing recipients
sops updatekeys secrets/secrets.yaml
# Check which keys can decrypt
sops --decrypt secrets/secrets.yaml > /dev/null && echo "OK"
# List recipients
grep age secrets/.sops.yaml
# Convert SSH key to age
ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub
# Generate a new age key (for users)
age-keygen -o ~/.config/sops/age/keys.txt
Determinate Nix
Determinate Nix is Determinate Systems’
downstream distribution of Nix (flakes enabled by default, lazy-trees, its own
determinate-nixd daemon). This flake used to run it on every host; it has
since been removed in favour of the stock Nix that ships with nixpkgs.
This page documents how it was wired in so it can be re-enabled if ever needed — and the one gotcha (source rebuilds) that made it painful.
Why it was removed
The determinate module pins a specific determinate-nix-<version> build. That
build is published as a binary on the FlakeHub cache, but this flake never
listed https://cache.flakehub.com as a substituter — only its public key was
present (see the gotcha below). So every version bump rebuilt Determinate Nix
(and its boost etc. dependencies) from source during nh os switch, which
was slow enough to not be worth it. Stock Nix already covers everything this
config needs (flakes, nix-command, pipe-operators are enabled in
modules/nixos/system/nix-utils.nix and modules/darwin/system/nix.nix,
independent of Determinate).
How to re-enable it
Three edits, then rebuild:
-
Add the flake input (
flake.nix):determinate.url = "https://flakehub.com/f/DeterminateSystems/determinate/*"; -
Import the module for the relevant platform (
systems/default.nix) — it lives in thenixosModules/darwinModuleslist:# in nixosModules determinate.nixosModules.default # in darwinModules determinate.darwinModules.default -
Add the FlakeHub cache substituter (
flake.nix→nixConfig). This is the important one — without it, Determinate Nix builds from source:extra-substituters = [ # … "https://cache.flakehub.com" ]; extra-trusted-public-keys = [ # … "cache.flakehub.com-3:hJuILl5sVK4iKm86JzgdXW12Y2Hwd5G07qKtHTOcDCM=" ];
Then just switch (NixOS) / just switch on the darwin host.
The gotcha:
install.determinate.systemsis the installer host, not a binary cache. The Determinate Nix binaries live oncache.flakehub.com. If you re-add the module but forget the substituter, you’ll compile Nix from source on every lockfile bump — the exact reason it was removed.
Platform notes
NixOS
The determinate.nixosModules.default module sets nix.package to the
Determinate build and hands daemon management to determinate-nixd. Nothing else
in the config depends on it — nix-command/flakes/pipe-operators come from
modules/nixos/system/nix-utils.nix, so removing Determinate does not disable
flakes.
darwin (tensa)
On darwin the module sets nix.enable = false, telling nix-darwin not to
manage Nix (Determinate manages it out-of-band via determinate-nixd). It also
consumes a determinateNix.customSettings option to push the shared settings
into Determinate’s config.
If you run darwin on stock Nix instead, nix.enable must be true so
nix-darwin manages the daemon — otherwise nothing configures Nix on that host.
modules/darwin/system/nix.nix is where both live.
Storage
Filesystems at a glance
| FS | Use in this flake | Best for |
|---|---|---|
| ext4 | the default root filesystem for most hosts | Simplicity, zero maintenance |
| btrfs | root on shinji, yhwach, kyuubi | Compression, snapshots |
| zfs | data pools only (sado, kuchiki) | Large storage, checksums |
| vfat | Every host with /boot (EFI) | EFI system partition |
Host → filesystem mapping
| Host | Root | Data | Managed by |
|---|---|---|---|
| shinji | btrfs (@root + @nix) | — | disko |
| yhwach | btrfs (subvol=@) | — | hardware-config |
| kyuubi | btrfs (subvol=@) | — | hardware-config |
| sado | ext4 | zfs (/mnt/flash) | hardware-config |
| kuchiki | ext4 | zfs (/mnt/storage) | hardware-config |
| kenpachi | ext4 (LVM) | — | disko |
| aizen | ext4 (LVM) | — | disko |
| all others | ext4 | — | hardware-config |
Choosing a filesystem for a new host
- ext4 — the default. Simple, proven, zero configuration. Used on most hosts.
- btrfs — when you want transparent zstd compression (saves ~25% on
/nix/store) or snapshot support. Use the@root+@nixsubvolume layout to keep snapshots lightweight. See btrfs.md for details. - zfs — only for dedicated data pools that benefit from checksums, dedup, or RAID-Z. Do not use for root: the out-of-tree kernel module frequently breaks on kernel updates.
Disko
Disko provides declarative disk partitioning. Instead of manually running fdisk, mkfs, and recording UUIDs in hardware-configuration.nix, you define everything in a disk.nix:
- Partition layout (BIOS boot, ESP, root)
- Filesystem type (ext4, btrfs, zfs)
- Subvolume layout for btrfs
- LVM volumes if needed
Disko is imported per-host via inputs.disko.nixosModules.disko in the host’s disk.nix. A few hosts use it (shinji, kenpachi, aizen); the rest use a manually-generated hardware-configuration.nix with explicit fileSystems entries.
When disko manages the filesystems, the fileSystems entries in hardware-configuration.nix should be commented out — disko generates them declaratively at build time.
Example: shinji
# systems/x86_64-linux/shinji/default.nix
{ inputs, ... }: {
imports = [
./configuration.nix
./disk.nix # disko: btrfs on NVMe with @root + @nix subvolumes
./hardware-configuration.nix # fileSystems commented out, disko handles them
];
}
See btrfs.md for a complete disko + btrfs walkthrough.
Btrfs on NixOS
Why btrfs
| Feature | Benefit on NixOS |
|---|---|
| zstd compression | 20-30% space saved on /nix/store transparently |
| Subvolumes | Separate / and /nix — snapshot root without bloating snapshots with store data |
| Snapshots | Roll back a bad nixos-rebuild in seconds |
| Native kernel | No out-of-tree module — works on every kernel update (unlike ZFS) |
Compared to ext4: same simplicity, more features. Compared to ZFS: fewer features but zero maintenance overhead.
Layout used in this flake
Two subvolumes on a single btrfs partition:
/dev/<disk> (btrfs)
├── @root → / compress=zstd
└── @nix → /nix compress=zstd,noatime
@root: the OS, your home,/etc— everything except the Nix store.@nix: the Nix store only. Mounted withnoatimeto reduce metadata writes during package operations.- Why separate? Snapshots of
/don’t capture/nix/store, keeping them small. Restoring/from a snapshot doesn’t touch the store.
Disko configuration
The flake uses disko for declarative partitioning. Here’s a minimal btrfs layout:
{ inputs, lib, ... }:
{
imports = [ inputs.disko.nixosModules.disko ];
disko.devices = {
disk.main = {
device = lib.mkDefault "/dev/nvme0n1";
type = "disk";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02";
};
esp = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
root = {
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ];
subvolumes = {
"@root" = {
mountpoint = "/";
mountOptions = [ "compress=zstd" ];
};
"@nix" = {
mountpoint = "/nix";
mountOptions = [
"compress=zstd"
"noatime"
];
};
};
};
};
};
};
};
};
}
See systems/x86_64-linux/shinji/disk.nix for a real example.
Maintenance
Check filesystem usage
btrfs filesystem usage /
btrfs filesystem df /
Scrub (data integrity check)
Run occasionally to detect bit rot or disk errors:
sudo btrfs scrub start /
sudo btrfs scrub status /
Balance
Only needed when adding/removing drives or after heavy usage patterns. Not routine:
sudo btrfs balance start -dusage=50 /
Compression stats
See how much space compression saves:
sudo compsize /nix/store
Snapshots
Manual snapshot before a risky rebuild
sudo btrfs subvolume snapshot -r / /snapshots/root-$(date -I)
Roll back to a snapshot
# Boot from a live USB, mount the btrfs volume
sudo mount -o subvol=/ /dev/<disk> /mnt
sudo mv /mnt/@root /mnt/@root-broken
sudo btrfs subvolume snapshot /mnt/snapshots/root-2026-01-01 /mnt/@root
reboot
Delete old snapshots
sudo btrfs subvolume delete /snapshots/root-2025-12-01
Recovery
Mount subvolumes from a live USB
sudo mount /dev/<device> /mnt
# The default subvolume mounts automatically
# Access subvolumes:
ls /mnt/@root
ls /mnt/@nix
Mount a specific subvolume for chroot repair
sudo mount -o subvol=@root,compress=zstd /dev/<device> /mnt
sudo mount -o subvol=@nix,compress=zstd /dev/<device> /mnt/nix
sudo mount /dev/<esp> /mnt/boot
sudo nixos-enter
Filesystem creation (manual, without disko)
If you need to set up btrfs manually instead of using disko:
mkfs.btrfs -f /dev/<partition>
mount /dev/<partition> /mnt
btrfs subvolume create /mnt/@root
btrfs subvolume create /mnt/@nix
umount /mnt
mount -o subvol=@root,compress=zstd /dev/<partition> /mnt
mkdir -p /mnt/nix
mount -o subvol=@nix,compress=zstd,noatime /dev/<partition> /mnt/nix
ZFS on NixOS
Why ZFS for data pools
| Feature | Benefit |
|---|---|
| Checksums | Detects and repairs bit rot on every read |
| Snapshots | Instant, zero-overhead point-in-time copies |
| Dataset properties | Compression, recordsize, etc. per dataset — no chattr hacks |
| RAID-Z | Redundancy without a hardware RAID controller |
| Send/Receive | Efficient backup and replication |
Why NOT for root
- Out-of-tree kernel module — ZFS frequently breaks when the kernel updates. On NixOS with
boot.kernelPackages = linuxPackages_latest, this is a recurring headache. - Boot complexity — requires ZFSBootMenu or a separate
/boot+ manual pool import in initrd. - This flake’s convention — ext4 or btrfs for root, ZFS exclusively for data pools (
/mnt/flash,/mnt/storage).
Pool structure in this flake
sado — flash pool
flash
├── flash/appdata → /mnt/flash/appdata (immich, paperless, komga configs)
├── flash/photos → /mnt/flash/photos
├── flash/documents → /mnt/flash/documents
└── flash/books → /mnt/flash/books
Source: systems/x86_64-linux/sado/hardware-configuration.nix
kuchiki — storage pool
storage
├── storage/appdata → /mnt/storage/appdata (media service configs)
├── storage/downloads → /mnt/storage/downloads
└── storage/media → /mnt/storage/media
Source: systems/x86_64-linux/kuchiki/hardware-configuration.nix
Both hosts use hosts.zfs.enable = true (shared module at modules/nixos/system/zfs.nix) and systemd.services.zfs-mount.enable = false (mount via NixOS fileSystems declarations instead).
Creating a pool
Single disk — no redundancy
zpool create tank /dev/sda
Mirror — survives 1 disk failure
zpool create tank mirror /dev/sda /dev/sdb
Good for: root filesystem (if you must), appdata where uptime matters.
RAID-Z — 1 disk parity, minimum 3 disks
zpool create tank raidz /dev/sda /dev/sdb /dev/sdc
Good for: media storage where capacity > performance.
RAID-Z2 — 2 disk parity, minimum 4 disks
zpool create tank raidz2 /dev/sda /dev/sdb /dev/sdc /dev/sdd
Good for: large arrays where rebuild time is a concern.
Ashift — always set for modern drives
4K sector drives (all SSDs and most HDDs since ~2011) need ashift=12:
zpool create -o ashift=12 tank mirror /dev/sda /dev/sdb
Without it: write amplification and performance degradation on 4K-native drives.
Adding to NixOS config
After creating pools/datasets imperatively, declare them in hardware-configuration.nix so NixOS mounts them at boot:
fileSystems."/mnt/tank" = {
device = "tank";
fsType = "zfs";
};
fileSystems."/mnt/tank/media" = {
device = "tank/media";
fsType = "zfs";
};
Datasets
Creating
zfs create tank/appdata
zfs create tank/media
# Custom mountpoint
zfs create -o mountpoint=/custom/path tank/custom
Recommended properties
| Property | Value | Why |
|---|---|---|
compression | lz4 or zstd | Free space savings, negligible CPU cost. lz4 for speed, zstd for higher compression. |
recordsize | 1M (media) / 128K (general) / 16K (databases) | Match workload I/O patterns. |
atime | off | Eliminates metadata writes on every read access. |
xattr | sa | Store extended attributes in dnodes instead of hidden files — big speedup. |
acltype | posix | POSIX ACLs; enables nfs4 if you need that. |
aclinherit | passthrough | Inherit ACLs from parent if using ACLs. |
dedup | off | Keep it off — RAM consumption is enormous. |
Media dataset — large sequential files
zfs set compression=zstd recordsize=1M atime=off tank/media
Appdata dataset — small random I/O (databases, configs)
zfs set compression=lz4 recordsize=128K atime=off xattr=sa tank/appdata
Downloads dataset — mixed, discard-friendly
zfs set compression=zstd recordsize=1M atime=off tank/downloads
Snapshots
Manual
zfs snapshot tank/media@backup-$(date -I)
zfs list -t snapshot
Automatic (zfs-auto-snapshot)
services.zfs.autoSnapshot = {
enable = true;
frequent = 4; # keep 4 quarter-hourly snapshots
hourly = 24;
daily = 7;
weekly = 4;
monthly = 6;
};
Rollback
zfs rollback tank/media@backup-2026-01-01
# For older snapshots (destroys intermediate snapshots):
zfs rollback -r tank/media@backup-2025-12-01
Access files from a snapshot without rollback
Snapshots are mounted read-only under /.zfs/snapshot/<name>/:
ls /mnt/tank/media/.zfs/snapshot/backup-2026-01-01/
cp /mnt/tank/media/.zfs/snapshot/backup-2026-01-01/deleted-file.txt .
Maintenance
Scrubbing — data integrity check
zpool scrub tank
zpool status tank # watch progress
This flake enables auto-scrub in modules/nixos/system/zfs.nix:
services.zfs.autoScrub = {
enable = true;
interval = "*-*-1,15 02:30"; # 1st and 15th of every month at 2:30 AM
};
Pool and dataset status
zpool status # health, errors, scrub progress
zpool list # space per pool
zfs list # space per dataset
zfs list -t snapshot # list all snapshots
zfs get all tank/appdata # all properties of a dataset
Replacing a failed disk
# After physically swapping the disk:
zpool replace tank /dev/sdb /dev/sdc
zpool status tank # wait for resilver
Destroying a pool
zpool destroy tank
# If pool claims to be busy:
zpool destroy -f tank
Adding ZFS to a new host
- Enable the shared ZFS module:
hosts.zfs.enable = true;
- Set a host ID (required — pick a random hex string):
networking.hostId = "a1b2c3d4";
- Disable automatic zfs-mount service (NixOS handles mounting via
fileSystems):
systemd.services.zfs-mount.enable = false;
- Declare datasets in
hardware-configuration.nix:
fileSystems."/mnt/tank" = {
device = "tank";
fsType = "zfs";
};
fileSystems."/mnt/tank/appdata" = {
device = "tank/appdata";
fsType = "zfs";
};
See systems/x86_64-linux/sado/ and systems/x86_64-linux/kuchiki/ for working examples.
devix
Stylix, but for development environments.
Stylix lets you declare a colour scheme once and have every program that understands theming configure itself from it. devix does the same thing for development tooling: you describe a language once — its language servers, its formatters, how it should be indented — and every editor that understands the description configures itself from it.
The problem it solves is duplication. Setting up Rust in Helix means writing a
languages.toml stanza. Setting up the same Rust in Zed means writing a
different stanza in settings.json. Doing it for an AI harness like OpenCode
means a third format. Three files, three syntaxes, one fact — which is exactly
the kind of thing Nix is supposed to abolish.
devix = {
enable = true;
languages.rust.enable = true;
};
programs.helix.enable = true;
programs.zed-editor.enable = true;
That is the whole configuration. Both editors get rust-analyzer wired up with
rustfmt on save and the right indentation, because both of them consume the
same description.
Why editors are not the only consumers
Language servers used to be an editor concern. They are not anymore — coding agents such as OpenCode and Claude Code benefit from exactly the same information, because “which LSP understands this file, and what formats it” is a fact about the project, not about the program looking at it.
devix calls anything that consumes the description a consumer. An editor is a consumer. An AI harness is a consumer. Adding a new one does not require touching a single language definition.
Where to go next
- Concepts — the four pieces devix is built from, and the two rules that decide what each consumer sees.
- Usage — turning things on, overriding servers, opting out.
- Adding a language and adding an editor.
- Support matrix — which languages each consumer covers, generated from the definitions themselves.
- Option reference: core,
languages, addons,
registries — generated from the option
descriptions in
modules/devix.
Status
devix currently lives inside this flake
as modules/devix, and is exported as homeModules.devix and
devenvModules.*. It has no dependency on the rest of the configuration: the
language definitions use plain nixpkgs, so the module works against a stock
home-manager setup. If it outgrows this repository it can move out as-is.
Concepts
devix is built from four pieces. Three of them are things you write; the fourth is the machinery that connects them.
languages/<lang>.nix pure data: lsps, formatters, indentation, consumerMeta
addons/<addon>.nix server groups that attach to languages (grammar, ai)
│
▼
devix.lsps / devix.formatters shared registries
devix.languages.<lang> / .addons.<addon> what is on
│
▼
consumers/registry.nix applies exposure + capability, resolves per consumer
│
├─ consumers/helix/ → programs.helix.languages
├─ consumers/zed/ → programs.zed-editor.userSettings
└─ consumers/opencode/ → programs.opencode.settings
Languages
A language is one file in modules/devix/languages/, and it is pure data — no
conditionals, no references to your configuration:
{ lib, pkgs }:
{
lsps.tinymist = {
enable = true;
package = pkgs.tinymist;
};
formatters.typstyle = {
enable = true;
package = pkgs.typstyle;
};
language = {
lspServers = [ "tinymist" ];
formatters = [ "typstyle" ];
tabWidth = 2;
insertSpaces = true;
};
consumerMeta.zed = {
name = "Typst";
extensions = [ "typst" ];
languageServers = [ "tinymist" "..." ];
};
consumerMeta.opencode.extensions = [ ".typ" ];
}
Adding a file to that directory is all it takes — the directory is scanned, and
each file becomes a devix.languages.<name> option. There is no list to update.
Registries
Notice that the language above refers to its server as the string
"tinymist", not as the definition directly. Server and formatter definitions
go into two shared registries, devix.lsps and devix.formatters, and
languages reference them by name.
That indirection is what makes overrides work in one place:
devix.lsps.pyright.config.python.analysis.typeCheckingMode = "basic";
Every consumer that uses pyright now uses that setting. If each language embedded its servers directly, you would be overriding the same thing once per editor — the duplication devix exists to remove.
A server’s command is derived from its package, so swapping a build is one
line and every consumer follows:
devix.lsps.nixd.package = pkgs.unstable.nixd;
Addons
Some servers are not tied to a language. Spell and grammar checking applies to prose formats; AI completion applies to everything. Making them languages would be a lie — and would leak a phantom “grammar” entry into your editor’s language list.
An addon is a group of servers plus the languages it attaches to:
{
description = "spelling, grammar and style checking";
lsps = { ltex = { ... }; codebook = { ... }; harper = { ... }; };
lspServers = [ "ltex" "codebook" ]; # attached
languages = [ "markdown" "latex" "typst" ]; # "*" for everything
}
Enabling it appends those servers to the languages named, wherever they are enabled. Nothing appears in an editor as a language of its own.
Consumers
A consumer is anything that reads the description and configures itself: an
editor, an AI harness. Each is one directory under modules/devix/consumers/,
containing what it is (default.nix), a pure transform into its config format
(transform.nix), and one adapter per target (home.nix, optionally
devenv.nix).
Consumers attach themselves the way Stylix targets do — enabling the program is enough:
devix.consumers.<name>.enable # defaults to
devix.autoEnable && programs.<editor>.enable
The two rules
Exactly two things decide what a given consumer sees, and both are applied in
one place (consumers/registry.nix) rather than by each adapter.
Exposure — every language, addon, server and formatter carries a
consumers.<name>.enable toggle, defaulting to true. This is how you say
“markdown’s grammar servers everywhere, except in Zed”.
Capability — what a consumer can handle at all. Each consumer declares one of two models:
capability | Meaning | Example |
|---|---|---|
"all" | Configures languages generically; handles anything devix defines | Helix |
"meta" | Only covers languages carrying consumerMeta.<name> | Zed, OpenCode |
Zed addresses languages by its own display name and curates its own server list, so it cannot configure a language that has not told it how. OpenCode attaches servers by file extension, so it needs to know the extensions. Helix needs neither, so it takes everything.
A "meta" consumer also declares metaOptions, which types its slice of
consumerMeta. A typo or a wrong type in a language’s metadata is a build
error rather than something silently ignored.
The support matrix shows the result of these rules across every language currently defined.
Mechanism and policy
devix itself enables nothing. It defines what a language is and how each
consumer should be configured — the mechanism. Deciding that Rust should be on
for development machines is policy, and in this flake it lives in
modules/home-manager/development/. Keeping the two apart is what lets devix
be lifted out of this repository unchanged.
Usage
Getting started
devix is a Home Manager module. Import it, turn it on, and enable the languages you want:
{
imports = [ inputs.flake.homeModules.devix ];
devix = {
enable = true;
defaultEditor = "helix";
languages = {
nix.enable = true;
python.enable = true;
rust.enable = true;
};
};
programs.helix.enable = true;
}
Enabling the editor is all that is needed for devix to configure it —
devix.consumers.helix.enable defaults to
devix.autoEnable && programs.helix.enable. Set devix.autoEnable = false if
you would rather opt each consumer in by hand.
defaultEditor sets EDITOR and VISUAL. Only consumers that are actual
editors are candidates; an AI harness like OpenCode is not offered.
The language servers and formatters a language references are installed for
you — there is no separate home.packages entry to maintain.
Adding an addon
devix.addons.grammar.enable = true;
This attaches ltex and codebook to markdown, LaTeX and Typst wherever those
are enabled. To change what it attaches or where:
devix.addons.grammar = {
enable = true;
lspServers = [ "codebook" ]; # drop ltex, it is heavy
languages = [ "markdown" "typst" ]; # leave LaTeX alone
};
Overriding a server
Servers live in a shared registry, so an override applies to every consumer at once. Change its settings:
devix.lsps.pyright.config.python.analysis.typeCheckingMode = "basic";
Change which build is used — command follows package, so this is enough:
devix.lsps.nixd.package = pkgs.unstable.nixd;
Turn one off everywhere without touching the language:
devix.lsps.markdown-oxide.enable = false;
Turning things off per editor
Every language, addon, server and formatter has a per-consumer toggle. They all default to enabled, so you only write the exceptions.
# Zed has good built-in Markdown support; keep devix out of it
devix.languages.markdown.consumers.zed.enable = false;
# Grammar servers are useful in the editor, noise in the AI harness
devix.addons.grammar.consumers.opencode.enable = false;
# One server, one editor
devix.lsps.ltex.consumers.zed.enable = false;
Note the difference between this and devix.lsps.<name>.enable = false: the
latter switches the server off everywhere, the former only hides it from one
consumer.
Changing a language’s servers
lspServers and formatters are lists of registry keys, so you can reorder,
extend or replace them:
devix.languages.python = {
enable = true;
lspServers = [ "pyright" "ruff" ]; # add a second server
formatters = [ "ruff-format" ]; # drop the import-sorting pass
};
Referencing a name that no registry entry defines is caught with a clear message rather than an obscure evaluation failure:
devix.languages / devix.addons reference unknown LSPs: ruff
Migrating from development.*
Options used to live under development.*, and autoEnable used to be called
autoConfigureEditors. The old names still work and warn:
The option `development.languages.rust' has been renamed to `devix.languages.rust'.
The aliases live in modules/devix/core/renames.nix and can be deleted once
nothing refers to the old paths.
Adding a language
Create one file in modules/devix/languages/. The directory is scanned, so
there is no list to update and no module to write — the file becomes a
devix.languages.<name> option automatically, named after the file.
The shape
# modules/devix/languages/zig.nix
{ lib, pkgs }:
{
description = "Zig development environment";
lsps.zls = {
enable = true;
package = pkgs.zls;
};
formatters.zig-fmt = {
enable = true;
package = pkgs.zig;
command = lib.getExe' pkgs.zig "zig";
args = [ "fmt" "--stdin" ];
};
language = {
lspServers = [ "zls" ];
formatters = [ "zig-fmt" ];
tabWidth = 4;
insertSpaces = true;
roots = [ "build.zig" ];
};
consumerMeta.zed = {
name = "Zig";
extensions = [ "zig" ];
languageServers = [ "zls" "..." ];
};
consumerMeta.opencode.extensions = [ ".zig" ];
}
That is the entire change. devix.languages.zig now exists, defaulted from
this file, and every consumer picks it up.
Field by field
description — optional; shown in the generated option reference.
lsps / formatters — definitions that go into the shared registries.
command is derived from package, so you normally omit it. Set it explicitly
only when the binary is not the package’s main program, as with the
lib.getExe' call above.
language.lspServers / language.formatters — which registry entries this
language uses, by name. A language may define more than it uses; the extras stay
available for users to opt into. Multiple formatters are piped in order.
language.roots — files that mark a project root, for consumers that
support the notion.
consumerMeta.<consumer> — how a "meta" consumer should handle this
language. Omit it and that consumer simply skips the language. The shape is
typed by each consumer’s metaOptions, so mistakes fail the build:
zed—nameis Zed’s display name and becomes the settings key.languageServersis a curated, ordered list; the literal"..."means “then Zed’s own defaults”. Omitting a devix server here is how you let Zed use its built-in support instead.opencode—extensionsare the file extensions, with leading dots.
Helix needs no metadata: it declares capability = "all" and configures
languages generically.
Rules
Use plain pkgs. Language files must not reference this flake’s overlays —
no pkgs.unstable, no pkgs.custom. devix is exported as a standalone module
and has to evaluate against stock nixpkgs. “Use the unstable build” is a policy
decision and belongs in modules/home-manager/development/:
devix.lsps.zls.package = pkgs.unstable.zls;
Do not enable anything. A language file describes; it never decides. Turning
zig on for development machines is policy.
Keep it data. No config, no lib.mkIf, no reading the user’s settings.
The file receives lib and pkgs, and returns an attribute set.
Checking your work
# does everything still evaluate?
nix eval .#homeConfigurations.<user>.activationPackage.drvPath
# what did the consumers make of it?
nix eval .#homeConfigurations.<user>.config.programs.helix.languages --json | jq
# rebuild the generated reference and matrix
nix build .#docs-devix-reference && cat result/support-matrix.md
Your language should appear in the support matrix with a mark under each consumer that covers it.
Adding an editor
A consumer is one directory under modules/devix/consumers/. The directory is
discovered automatically, and everything derived from it — the consumer list,
the per-item exposure toggles, the defaultEditor candidates, the
EDITOR/VISUAL map, the adapter lists both targets assemble from — follows
without you editing anything else.
consumers/<name>/
default.nix what this consumer is (the registry entry)
transform.nix pure: resolved languages -> that tool's config format
home.nix Home Manager adapter
devenv.nix devenv adapter (optional)
1. The registry entry
# consumers/kakoune/default.nix
{
description = "Kakoune text editor";
# "all" — configures languages generically, needs no per-language metadata
# "meta" — only covers languages carrying consumerMeta.kakoune
capability = "meta";
# Types the `consumerMeta.kakoune` block in each language file. Required for
# "meta" consumers; null for "all".
metaOptions = lib: {
filetypes = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Kakoune filetype names for this language.";
};
};
# When should this consumer switch itself on?
activeWhen = config: config.programs.kakoune.enable or false;
# Command for EDITOR/VISUAL, or null if this is not an interactive editor.
editorCommand = "kak";
home = ./home.nix;
devenv = null;
}
Declaring capability = "meta" without metaOptions is rejected at evaluation
time, so the two cannot drift apart.
2. The transform
Keep this pure — it takes resolved languages and returns the tool’s config.
Being a plain function of lib makes it easy to reason about and to reuse
across targets.
# consumers/kakoune/transform.nix
{ lib }:
let
toKakoune = name: language: { ... };
in
{
toKakouneConfig = languages: lib.mapAttrs toKakoune languages;
}
Languages arrive already filtered and resolved: only those enabled, exposed
to you, and supported by your capability model, with their server and formatter
lists already narrowed and the registry entries attached as lsps and
formatterConfigs. Do not re-implement that filtering — it lives in
consumers/registry.nix precisely so every consumer agrees.
3. The adapter
# consumers/kakoune/home.nix
{ config, lib, ... }:
let
consumers = import ../registry.nix { inherit lib; };
kakouneLib = import ./transform.nix { inherit lib; };
cfg = config.devix;
languages = consumers.languagesFor "kakoune" cfg.languages;
resolved = consumers.resolveForConsumer "kakoune" cfg languages;
in
{
config = lib.mkIf (cfg.enable && cfg.consumers.kakoune.enable && languages != { }) {
programs.kakoune.settings = kakouneLib.toKakouneConfig resolved;
};
}
The adapter does not need to enable itself — targets/home/auto-enable.nix
derives that from your activeWhen for every consumer at once.
4. Teach the languages about it
For a "meta" consumer, add a consumerMeta.kakoune block to each language it
should cover. Languages without one are skipped, which is a perfectly good
resting state — support can grow language by language, and the
support matrix shows the coverage at a glance.
Checking your work
nix eval --impure --expr '
let lib = (import <nixpkgs> {}).lib;
in (import ./modules/devix/consumers/registry.nix { inherit lib; }).names'
nix eval .#homeConfigurations.<user>.config.programs.kakoune.settings --json | jq
Your consumer should appear in names, gain a column in the support matrix, and
show up as a toggle on every language, addon, server and formatter.
Support matrix
Generated from the language and addon definitions in modules/devix, so it
cannot drift from the code.
Languages
A consumer whose capability is "all" configures every language
generically. One with capability = "meta" only covers languages that carry
metadata for it — the marks below show the Zed language name and the
OpenCode file extensions that metadata provides.
| Language | Language servers | Formatters | helix | opencode | zed |
|---|---|---|---|---|---|
bash | — | shfmt | ✅ | ✅ .sh .bash | ✅ Shell Script |
fish | fish-lsp | fish-indent | ✅ | ✅ .fish | ✅ Fish |
fortran | fortls | fprettify | ✅ | ✅ .f90 .f95 .f03 .f .for | — |
go | gopls, golangci-lint-langserver | gofmt | ✅ | ✅ .go | ✅ Go |
json | vscode-json-languageserver | prettier-json | ✅ | ✅ .json .jsonc | ✅ JSON |
julia | julia-ls | — | ✅ | ✅ .jl | — |
latex | texlab | — | ✅ | ✅ .tex .sty .cls | ✅ LaTeX |
markdown | marksman, markdown-oxide, iwe | prettier-markdown | ✅ | ✅ .md .markdown | ✅ Markdown |
matlab | matlab-ls | — | ✅ | ✅ .m | — |
nix | nixd, nil | nixfmt | ✅ | ✅ .nix | ✅ Nix |
nu | — | nufmt | ✅ | ✅ .nu | ✅ Nu |
python | pyright | ruff-check, ruff-format | ✅ | ✅ .py .pyi | ✅ Python |
rust | rust-analyzer | rustfmt | ✅ | ✅ .rs | ✅ Rust |
toml | taplo | taplo-fmt | ✅ | ✅ .toml | ✅ TOML |
typst | tinymist | typstyle | ✅ | ✅ .typ | ✅ Typst |
xml | lemminx | — | ✅ | ✅ .xml | ✅ XML |
yaml | yaml-lsp | yamlfmt | ✅ | ✅ .yaml .yml | ✅ YAML |
Consumer capabilities: helix all, opencode meta, zed meta.
Addons
| Addon | Attached by default | Available, not attached | Attaches to |
|---|---|---|---|
ai | copilot, lsp-ai | — | every enabled language |
grammar | ltex, codebook | harper | markdown, latex, typst |
A server in the Available, not attached column is defined in the registry
but stays inactive until you add it to that addon’s lspServers.
Core options
The top-level switches: whether devix is active, how consumers attach themselves, and which editor becomes EDITOR.
devix.enable
Whether to let devix configure development tooling. When off, the language and addon definitions are still available as options but no editor is configured and no language server is installed.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.autoEnable
Master auto-enable for consumers, analogous to Stylix’s
stylix.autoEnable. When true, each consumer defaults to active
whenever its program is enabled (devix.consumers.<name>.enable
defaults to programs.<editor>.enable). Set to false to opt out of
automatic per-consumer enabling; you can still force an individual
consumer on via devix.consumers.<name>.enable = true.
Type: boolean
Default:
true
Declared by:
devix.consumers
Active editor/program consumers of the development configuration.
Type: submodule
Default:
{ }
Declared by:
devix.consumers.helix
Activation state for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.consumers.helix.enable
Whether the helix consumer (Helix text editor) is active.
This defaults to the consumer’s own programs.*.enable; set it
explicitly to force a consumer on or off regardless of the
program.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.consumers.opencode
Activation state for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.consumers.opencode.enable
Whether the opencode consumer (OpenCode AI coding agent) is active.
This defaults to the consumer’s own programs.*.enable; set it
explicitly to force a consumer on or off regardless of the
program.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.consumers.zed
Activation state for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.consumers.zed.enable
Whether the zed consumer (Zed editor) is active.
This defaults to the consumer’s own programs.*.enable; set it
explicitly to force a consumer on or off regardless of the
program.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.defaultEditor
Preferred editor for EDITOR/VISUAL and future default-app integrations.
The candidates are the consumers that declare an editorCommand.
Generated editor configuration is controlled by programs.<editor>.enable.
Type: null or one of “helix”, “zed”
Default:
null
Declared by:
Language options
One entry per file in modules/devix/languages/. Everything except enable is defaulted from that file, so you only need to override what you want to change.
devix.languages.bash
Settings for bash (bash development environment).
Every value below is defaulted from modules/devix/languages/bash.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.bash.enable
Whether to enable bash development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.bash.consumerMeta
Per-consumer metadata for bash, defaulted from
languages/bash.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.bash.consumerMeta.opencode
How the opencode consumer handles bash. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".sh"
".bash"
];
}
Declared by:
devix.languages.bash.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.bash.consumerMeta.zed
How the zed consumer handles bash. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [ ];
languageServers = [
"..."
];
name = "Shell Script";
}
Declared by:
devix.languages.bash.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.bash.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.bash.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.bash.consumers
Per-consumer exposure for the bash language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip bash in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.bash.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.bash.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.bash.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.bash.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.bash.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.bash.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.bash.formatters
Formatters (keys into devix.formatters) used for bash.
Type: list of string
Default:
[
"shfmt"
]
Declared by:
devix.languages.bash.lspServers
Language servers (keys into devix.lsps) used for bash.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.fish
Settings for fish (fish development environment).
Every value below is defaulted from modules/devix/languages/fish.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.fish.enable
Whether to enable fish development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.fish.consumerMeta
Per-consumer metadata for fish, defaulted from
languages/fish.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.fish.consumerMeta.opencode
How the opencode consumer handles fish. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".fish"
];
}
Declared by:
devix.languages.fish.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.fish.consumerMeta.zed
How the zed consumer handles fish. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
"fish"
];
languageServers = [
"fish-lsp"
"..."
];
name = "Fish";
}
Declared by:
devix.languages.fish.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.fish.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.fish.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.fish.consumers
Per-consumer exposure for the fish language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip fish in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.fish.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.fish.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.fish.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.fish.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.fish.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.fish.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.fish.formatters
Formatters (keys into devix.formatters) used for fish.
Type: list of string
Default:
[
"fish-indent"
]
Declared by:
devix.languages.fish.lspServers
Language servers (keys into devix.lsps) used for fish.
Type: list of string
Default:
[
"fish-lsp"
]
Declared by:
devix.languages.fortran
Settings for fortran (fortran development environment).
Every value below is defaulted from modules/devix/languages/fortran.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.fortran.enable
Whether to enable fortran development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.fortran.consumerMeta
Per-consumer metadata for fortran, defaulted from
languages/fortran.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.fortran.consumerMeta.opencode
How the opencode consumer handles fortran. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".f90"
".f95"
".f03"
".f"
".for"
];
}
Declared by:
devix.languages.fortran.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.fortran.consumerMeta.zed
How the zed consumer handles fortran. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
null
Declared by:
devix.languages.fortran.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.fortran.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.fortran.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.fortran.consumers
Per-consumer exposure for the fortran language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip fortran in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.fortran.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.fortran.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.fortran.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.fortran.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.fortran.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.fortran.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.fortran.formatters
Formatters (keys into devix.formatters) used for fortran.
Type: list of string
Default:
[
"fprettify"
]
Declared by:
devix.languages.fortran.lspServers
Language servers (keys into devix.lsps) used for fortran.
Type: list of string
Default:
[
"fortls"
]
Declared by:
devix.languages.go
Settings for go (go development environment).
Every value below is defaulted from modules/devix/languages/go.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.go.enable
Whether to enable go development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.go.consumerMeta
Per-consumer metadata for go, defaulted from
languages/go.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.go.consumerMeta.opencode
How the opencode consumer handles go. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".go"
];
}
Declared by:
devix.languages.go.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.go.consumerMeta.zed
How the zed consumer handles go. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [ ];
languageServers = [
"gopls"
"..."
];
name = "Go";
}
Declared by:
devix.languages.go.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.go.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.go.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.go.consumers
Per-consumer exposure for the go language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip go in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.go.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.go.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.go.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.go.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.go.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.go.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.go.formatters
Formatters (keys into devix.formatters) used for go.
Type: list of string
Default:
[
"gofmt"
]
Declared by:
devix.languages.go.lspServers
Language servers (keys into devix.lsps) used for go.
Type: list of string
Default:
[
"gopls"
"golangci-lint-langserver"
]
Declared by:
devix.languages.json
Settings for json (json development environment).
Every value below is defaulted from modules/devix/languages/json.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.json.enable
Whether to enable json development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.json.consumerMeta
Per-consumer metadata for json, defaulted from
languages/json.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.json.consumerMeta.opencode
How the opencode consumer handles json. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".json"
".jsonc"
];
}
Declared by:
devix.languages.json.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.json.consumerMeta.zed
How the zed consumer handles json. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [ ];
languageServers = [
"..."
];
name = "JSON";
}
Declared by:
devix.languages.json.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.json.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.json.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.json.consumers
Per-consumer exposure for the json language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip json in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.json.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.json.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.json.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.json.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.json.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.json.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.json.formatters
Formatters (keys into devix.formatters) used for json.
Type: list of string
Default:
[
"prettier-json"
]
Declared by:
devix.languages.json.lspServers
Language servers (keys into devix.lsps) used for json.
Type: list of string
Default:
[
"vscode-json-languageserver"
]
Declared by:
devix.languages.julia
Settings for julia (julia development environment).
Every value below is defaulted from modules/devix/languages/julia.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.julia.enable
Whether to enable julia development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.julia.consumerMeta
Per-consumer metadata for julia, defaulted from
languages/julia.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.julia.consumerMeta.opencode
How the opencode consumer handles julia. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".jl"
];
}
Declared by:
devix.languages.julia.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.julia.consumerMeta.zed
How the zed consumer handles julia. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
null
Declared by:
devix.languages.julia.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.julia.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.julia.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.julia.consumers
Per-consumer exposure for the julia language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip julia in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.julia.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.julia.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.julia.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.julia.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.julia.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.julia.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.julia.formatters
Formatters (keys into devix.formatters) used for julia.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.julia.lspServers
Language servers (keys into devix.lsps) used for julia.
Type: list of string
Default:
[
"julia-ls"
]
Declared by:
devix.languages.latex
Settings for latex (latex development environment).
Every value below is defaulted from modules/devix/languages/latex.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.latex.enable
Whether to enable latex development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.latex.consumerMeta
Per-consumer metadata for latex, defaulted from
languages/latex.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.latex.consumerMeta.opencode
How the opencode consumer handles latex. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".tex"
".sty"
".cls"
];
}
Declared by:
devix.languages.latex.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.latex.consumerMeta.zed
How the zed consumer handles latex. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
"latex"
];
languageServers = [
"texlab"
"..."
];
name = "LaTeX";
}
Declared by:
devix.languages.latex.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.latex.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.latex.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.latex.consumers
Per-consumer exposure for the latex language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip latex in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.latex.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.latex.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.latex.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.latex.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.latex.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.latex.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.latex.formatters
Formatters (keys into devix.formatters) used for latex.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.latex.lspServers
Language servers (keys into devix.lsps) used for latex.
Type: list of string
Default:
[
"texlab"
]
Declared by:
devix.languages.markdown
Settings for markdown (markdown development environment).
Every value below is defaulted from modules/devix/languages/markdown.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.markdown.enable
Whether to enable markdown development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.markdown.consumerMeta
Per-consumer metadata for markdown, defaulted from
languages/markdown.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.markdown.consumerMeta.opencode
How the opencode consumer handles markdown. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".md"
".markdown"
];
}
Declared by:
devix.languages.markdown.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.markdown.consumerMeta.zed
How the zed consumer handles markdown. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [ ];
languageServers = [
"..."
];
name = "Markdown";
}
Declared by:
devix.languages.markdown.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.markdown.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.markdown.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.markdown.consumers
Per-consumer exposure for the markdown language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip markdown in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.markdown.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.markdown.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.markdown.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.markdown.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.markdown.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.markdown.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.markdown.formatters
Formatters (keys into devix.formatters) used for markdown.
Type: list of string
Default:
[
"prettier-markdown"
]
Declared by:
devix.languages.markdown.lspServers
Language servers (keys into devix.lsps) used for markdown.
Type: list of string
Default:
[
"marksman"
"markdown-oxide"
"iwe"
]
Declared by:
devix.languages.matlab
Settings for matlab (matlab development environment).
Every value below is defaulted from modules/devix/languages/matlab.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.matlab.enable
Whether to enable matlab development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.matlab.consumerMeta
Per-consumer metadata for matlab, defaulted from
languages/matlab.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.matlab.consumerMeta.opencode
How the opencode consumer handles matlab. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".m"
];
}
Declared by:
devix.languages.matlab.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.matlab.consumerMeta.zed
How the zed consumer handles matlab. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
null
Declared by:
devix.languages.matlab.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.matlab.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.matlab.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.matlab.consumers
Per-consumer exposure for the matlab language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip matlab in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.matlab.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.matlab.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.matlab.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.matlab.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.matlab.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.matlab.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.matlab.formatters
Formatters (keys into devix.formatters) used for matlab.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.matlab.lspServers
Language servers (keys into devix.lsps) used for matlab.
Type: list of string
Default:
[
"matlab-ls"
]
Declared by:
devix.languages.nix
Settings for nix (nix development environment).
Every value below is defaulted from modules/devix/languages/nix.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.nix.enable
Whether to enable nix development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.nix.consumerMeta
Per-consumer metadata for nix, defaulted from
languages/nix.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.nix.consumerMeta.opencode
How the opencode consumer handles nix. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".nix"
];
}
Declared by:
devix.languages.nix.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.nix.consumerMeta.zed
How the zed consumer handles nix. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
"nix"
];
languageServers = [
"nil"
"nixd"
"..."
];
name = "Nix";
}
Declared by:
devix.languages.nix.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.nix.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.nix.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.nix.consumers
Per-consumer exposure for the nix language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip nix in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.nix.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.nix.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.nix.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.nix.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.nix.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.nix.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.nix.formatters
Formatters (keys into devix.formatters) used for nix.
Type: list of string
Default:
[
"nixfmt"
]
Declared by:
devix.languages.nix.lspServers
Language servers (keys into devix.lsps) used for nix.
Type: list of string
Default:
[
"nixd"
"nil"
]
Declared by:
devix.languages.nu
Settings for nu (nu development environment).
Every value below is defaulted from modules/devix/languages/nu.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.nu.enable
Whether to enable nu development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.nu.consumerMeta
Per-consumer metadata for nu, defaulted from
languages/nu.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.nu.consumerMeta.opencode
How the opencode consumer handles nu. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".nu"
];
}
Declared by:
devix.languages.nu.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.nu.consumerMeta.zed
How the zed consumer handles nu. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
"nu"
];
languageServers = [
"..."
];
name = "Nu";
}
Declared by:
devix.languages.nu.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.nu.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.nu.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.nu.consumers
Per-consumer exposure for the nu language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip nu in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.nu.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.nu.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.nu.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.nu.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.nu.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.nu.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.nu.formatters
Formatters (keys into devix.formatters) used for nu.
Type: list of string
Default:
[
"nufmt"
]
Declared by:
devix.languages.nu.lspServers
Language servers (keys into devix.lsps) used for nu.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.python
Settings for python (python development environment).
Every value below is defaulted from modules/devix/languages/python.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.python.enable
Whether to enable python development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.python.consumerMeta
Per-consumer metadata for python, defaulted from
languages/python.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.python.consumerMeta.opencode
How the opencode consumer handles python. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".py"
".pyi"
];
}
Declared by:
devix.languages.python.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.python.consumerMeta.zed
How the zed consumer handles python. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [ ];
languageServers = [
"pyright"
"..."
];
name = "Python";
}
Declared by:
devix.languages.python.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.python.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.python.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.python.consumers
Per-consumer exposure for the python language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip python in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.python.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.python.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.python.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.python.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.python.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.python.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.python.formatters
Formatters (keys into devix.formatters) used for python.
Type: list of string
Default:
[
"ruff-check"
"ruff-format"
]
Declared by:
devix.languages.python.lspServers
Language servers (keys into devix.lsps) used for python.
Type: list of string
Default:
[
"pyright"
]
Declared by:
devix.languages.rust
Settings for rust (rust development environment).
Every value below is defaulted from modules/devix/languages/rust.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.rust.enable
Whether to enable rust development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.rust.consumerMeta
Per-consumer metadata for rust, defaulted from
languages/rust.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.rust.consumerMeta.opencode
How the opencode consumer handles rust. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".rs"
];
}
Declared by:
devix.languages.rust.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.rust.consumerMeta.zed
How the zed consumer handles rust. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [ ];
languageServers = [
"rust-analyzer"
"..."
];
name = "Rust";
}
Declared by:
devix.languages.rust.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.rust.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.rust.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.rust.consumers
Per-consumer exposure for the rust language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip rust in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.rust.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.rust.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.rust.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.rust.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.rust.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.rust.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.rust.formatters
Formatters (keys into devix.formatters) used for rust.
Type: list of string
Default:
[
"rustfmt"
]
Declared by:
devix.languages.rust.lspServers
Language servers (keys into devix.lsps) used for rust.
Type: list of string
Default:
[
"rust-analyzer"
]
Declared by:
devix.languages.toml
Settings for toml (toml development environment).
Every value below is defaulted from modules/devix/languages/toml.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.toml.enable
Whether to enable toml development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.toml.consumerMeta
Per-consumer metadata for toml, defaulted from
languages/toml.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.toml.consumerMeta.opencode
How the opencode consumer handles toml. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".toml"
];
}
Declared by:
devix.languages.toml.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.toml.consumerMeta.zed
How the zed consumer handles toml. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [ ];
languageServers = [
"taplo"
"..."
];
name = "TOML";
}
Declared by:
devix.languages.toml.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.toml.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.toml.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.toml.consumers
Per-consumer exposure for the toml language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip toml in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.toml.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.toml.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.toml.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.toml.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.toml.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.toml.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.toml.formatters
Formatters (keys into devix.formatters) used for toml.
Type: list of string
Default:
[
"taplo-fmt"
]
Declared by:
devix.languages.toml.lspServers
Language servers (keys into devix.lsps) used for toml.
Type: list of string
Default:
[
"taplo"
]
Declared by:
devix.languages.typst
Settings for typst (typst development environment).
Every value below is defaulted from modules/devix/languages/typst.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.typst.enable
Whether to enable typst development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.typst.consumerMeta
Per-consumer metadata for typst, defaulted from
languages/typst.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.typst.consumerMeta.opencode
How the opencode consumer handles typst. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".typ"
];
}
Declared by:
devix.languages.typst.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.typst.consumerMeta.zed
How the zed consumer handles typst. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
"typst"
];
languageServers = [
"tinymist"
"..."
];
name = "Typst";
}
Declared by:
devix.languages.typst.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.typst.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.typst.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.typst.consumers
Per-consumer exposure for the typst language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip typst in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.typst.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.typst.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.typst.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.typst.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.typst.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.typst.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.typst.formatters
Formatters (keys into devix.formatters) used for typst.
Type: list of string
Default:
[
"typstyle"
]
Declared by:
devix.languages.typst.lspServers
Language servers (keys into devix.lsps) used for typst.
Type: list of string
Default:
[
"tinymist"
]
Declared by:
devix.languages.xml
Settings for xml (xml development environment).
Every value below is defaulted from modules/devix/languages/xml.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.xml.enable
Whether to enable xml development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.xml.consumerMeta
Per-consumer metadata for xml, defaulted from
languages/xml.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.xml.consumerMeta.opencode
How the opencode consumer handles xml. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".xml"
];
}
Declared by:
devix.languages.xml.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.xml.consumerMeta.zed
How the zed consumer handles xml. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
"xml"
];
languageServers = [
"lemminx"
"..."
];
name = "XML";
}
Declared by:
devix.languages.xml.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.xml.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.xml.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.xml.consumers
Per-consumer exposure for the xml language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip xml in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.xml.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.xml.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.xml.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.xml.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.xml.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.xml.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.xml.formatters
Formatters (keys into devix.formatters) used for xml.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.xml.lspServers
Language servers (keys into devix.lsps) used for xml.
Type: list of string
Default:
[
"lemminx"
]
Declared by:
devix.languages.yaml
Settings for yaml (yaml development environment).
Every value below is defaulted from modules/devix/languages/yaml.nix,
so you only need to set what you want to change.
Type: submodule
Default:
{ }
Declared by:
devix.languages.yaml.enable
Whether to enable yaml development environment.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.languages.yaml.consumerMeta
Per-consumer metadata for yaml, defaulted from
languages/yaml.nix. Only consumers that need metadata appear
here; each one types its own shape (see the consumer’s
metaOptions in consumers/<name>/default.nix).
Type: submodule
Default:
{ }
Declared by:
devix.languages.yaml.consumerMeta.opencode
How the opencode consumer handles yaml. null means
opencode cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [
".yaml"
".yml"
];
}
Declared by:
devix.languages.yaml.consumerMeta.opencode.extensions
File extensions (leading dot) OpenCode should treat as this language.
Type: list of string
Default:
[ ]
Example:
[
".py"
".pyi"
]
Declared by:
devix.languages.yaml.consumerMeta.zed
How the zed consumer handles yaml. null means
zed cannot configure this language and skips it.
Type: null or (submodule)
Default:
{
extensions = [ ];
languageServers = [
"yaml-lsp"
"..."
];
name = "YAML";
}
Declared by:
devix.languages.yaml.consumerMeta.zed.extensions
Zed extensions to install for this language.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.yaml.consumerMeta.zed.languageServers
The curated, ordered language_servers list Zed should use. May contain
the literal “…” token (Zed’s “then the defaults” marker) and may
deliberately omit devix servers that Zed handles better with its own
built-ins. Only the servers named here get a lsp.<id>.binary override.
Type: list of string
Default:
[ ]
Declared by:
devix.languages.yaml.consumerMeta.zed.name
Zed’s display name for the language; the key under languages in its settings.
Type: string
Declared by:
devix.languages.yaml.consumers
Per-consumer exposure for the yaml language. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to skip yaml in
Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.languages.yaml.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.yaml.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.yaml.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.yaml.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.yaml.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.languages.yaml.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.languages.yaml.formatters
Formatters (keys into devix.formatters) used for yaml.
Type: list of string
Default:
[
"yamlfmt"
]
Declared by:
devix.languages.yaml.lspServers
Language servers (keys into devix.lsps) used for yaml.
Type: list of string
Default:
[
"yaml-lsp"
]
Declared by:
Addon options
One entry per file in modules/devix/addons/. An addon contributes language servers to the languages it names rather than being a language itself.
devix.addons.ai
Settings for the ai addon (AI completion language servers),
defaulted from modules/devix/addons/ai.nix. An addon contributes its
language servers to the languages it names instead of being a language.
Type: submodule
Default:
{ }
Declared by:
devix.addons.ai.enable
Whether to enable AI completion language servers.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.addons.ai.consumers
Per-consumer exposure for the ai addon. Each consumer defaults to
enabled; set e.g. consumers.zed.enable = false to keep this addon’s
servers out of Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.addons.ai.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.addons.ai.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.addons.ai.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.addons.ai.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.addons.ai.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.addons.ai.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.addons.ai.languages
Languages these servers attach to, keyed by
devix.languages.<name>. The single entry “*” attaches to
every enabled language.
Type: list of string
Default:
[
"*"
]
Example:
[
"markdown"
"latex"
]
Declared by:
devix.addons.ai.lspServers
Language servers (keys into devix.lsps) this addon attaches. The addon may define more servers than it attaches — the extras stay available in the registry for you to opt into.
Type: list of string
Default:
[
"copilot"
"lsp-ai"
]
Declared by:
devix.addons.grammar
Settings for the grammar addon (spelling, grammar and style checking),
defaulted from modules/devix/addons/grammar.nix. An addon contributes its
language servers to the languages it names instead of being a language.
Type: submodule
Default:
{ }
Declared by:
devix.addons.grammar.enable
Whether to enable spelling, grammar and style checking.
Type: boolean
Default:
false
Example:
true
Declared by:
devix.addons.grammar.consumers
Per-consumer exposure for the grammar addon. Each consumer defaults to
enabled; set e.g. consumers.zed.enable = false to keep this addon’s
servers out of Zed only.
Type: submodule
Default:
{ }
Declared by:
devix.addons.grammar.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.addons.grammar.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.addons.grammar.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.addons.grammar.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.addons.grammar.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.addons.grammar.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.addons.grammar.languages
Languages these servers attach to, keyed by
devix.languages.<name>. The single entry “*” attaches to
every enabled language.
Type: list of string
Default:
[
"markdown"
"latex"
"typst"
]
Example:
[
"markdown"
"latex"
]
Declared by:
devix.addons.grammar.lspServers
Language servers (keys into devix.lsps) this addon attaches. The addon may define more servers than it attaches — the extras stay available in the registry for you to opt into.
Type: list of string
Default:
[
"ltex"
"codebook"
]
Declared by:
Registry options
The shared language-server and formatter registries. Languages and addons refer to entries here by name, so overriding one entry changes it for every editor at once.
devix.formatters
Reusable formatter registry.
Type: attribute set of (submodule)
Default:
{ }
Declared by:
devix.formatters.<name>.enable
Enable this formatter.
Type: boolean
Default:
true
Declared by:
devix.formatters.<name>.package
Package providing this formatter.
Type: null or package
Default:
null
Declared by:
devix.formatters.<name>.args
Arguments passed to this formatter.
Type: list of string
Default:
[ ]
Declared by:
devix.formatters.<name>.command
Command used to run this formatter. Derived from package; set it
explicitly when the binary is not the package’s main program.
Type: string
Default:
lib.getExe package, or the registry key when there is no package
Declared by:
devix.formatters.<name>.consumers
Per-consumer exposure for this formatter (see lsps.<name>.consumers).
Type: submodule
Default:
{ }
Declared by:
devix.formatters.<name>.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.formatters.<name>.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.formatters.<name>.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.formatters.<name>.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.formatters.<name>.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.formatters.<name>.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.lsps
Reusable language server registry.
Type: attribute set of (submodule)
Default:
{ }
Declared by:
devix.lsps.<name>.enable
Enable this language server.
Type: boolean
Default:
true
Declared by:
devix.lsps.<name>.package
Package providing this language server.
Type: null or package
Default:
null
Declared by:
devix.lsps.<name>.args
Arguments passed to this language server.
Type: list of string
Default:
[ ]
Declared by:
devix.lsps.<name>.command
Command used to start this language server. Derived from package,
so pointing package at a different build (an unstable one, say) is
normally all you need. Set this explicitly when the binary is not the
package’s main program.
Type: string
Default:
lib.getExe package, or the registry key when there is no package
Declared by:
devix.lsps.<name>.config
Editor-specific configuration passed to this language server.
Type: attribute set of anything
Default:
{ }
Declared by:
devix.lsps.<name>.consumers
Per-consumer exposure for this language server. Each consumer defaults
to enabled; set e.g. consumers.zed.enable = false to keep this server
out of Zed while leaving it available to Helix/OpenCode.
Type: submodule
Default:
{ }
Declared by:
devix.lsps.<name>.consumers.helix
Exposure toggle for the helix consumer.
Type: submodule
Default:
{ }
Declared by:
devix.lsps.<name>.consumers.helix.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.lsps.<name>.consumers.opencode
Exposure toggle for the opencode consumer.
Type: submodule
Default:
{ }
Declared by:
devix.lsps.<name>.consumers.opencode.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.lsps.<name>.consumers.zed
Exposure toggle for the zed consumer.
Type: submodule
Default:
{ }
Declared by:
devix.lsps.<name>.consumers.zed.enable
Whether this is exposed to / configured for this consumer.
Type: boolean
Default:
true
Declared by:
devix.lsps.<name>.environment
Literal environment variables to set when launching this server. Consumers that support it wrap the command in a shell launcher.
Type: attribute set of string
Default:
{ }
Declared by:
devix.lsps.<name>.environmentScript
Shell snippet prepended to the server launcher (e.g. to read a secret into an env var). Used for AI servers; see lib/opencode.nix.
Type: strings concatenated with “\n”
Default:
""
Declared by:
devix.lsps.<name>.kind
Classification of this server, used by consumers to decide how to attach it (e.g. AI/grammar servers may be handled specially).
Type: one of “language”, “grammar”, “tool”, “ai”
Default:
"language"
Declared by:
Daily Usage
Task runner (just)
Common commands live in the repo’s justfile. Run just --list to see them all
(just is available in the nix develop .#nix shell). The essentials:
just switch # build + activate this host (NixOS or macOS)
just home # build + activate your home-manager config (standalone)
just update # update all flake inputs
just fmt # format all nix files
just check # nix flake check
just docs # serve these docs locally
just iso <host> # build an installer ISO (Linux)
just deploy-tag nas # colmena-deploy every host tagged `nas`
just bump packages/top-level/<pkg> # bump a custom package with nix-update
The recipes wrap nh, nix, colmena, and sops — read them in the justfile
to see the exact commands.
Nix Helper (nh)
nh is a convenience wrapper around common Nix
operations. The flake sets NH_FLAKE to ~/.config/flake, so these work from
anywhere:
nh os switch # Update NixOS
nh home switch # Update Home Manager
nh clean all # Garbage collection
To point at a different checkout, override NH_FLAKE or pass the flake path
explicitly:
nh os switch ~/.config/flake#<device-name>
nh home switch ~/.config/flake#<username>
Clean-up
nix-collect-garbage --delete-older-than 30d
nh clean all
Git Fetchers
When adding a package from a git source, you need the commit and the Nix hash.
nix run nixpkgs#nix-prefetch-git https://github.com/EliverLara/candy-icons
Alternatively, leave the hash field empty in your derivation, attempt a build, and copy the hash from the error message.
Remote Builds
Use a more powerful machine (like kuchiki) to build heavy packages (e.g., Rust projects):
nix build --builders "ssh://mirza@kuchiki.sparrow-yo.ts.net x86_64-linux" \
-L .#nixosConfigurations.sado.config.services.stump.package
Prerequisites
The Nix daemon runs as root, so root needs SSH access to the remote machine:
# Run once to add kuchiki's host key to root's known_hosts
sudo ssh mirza@kuchiki.sparrow-yo.ts.net
After that, remote builds will work without further configuration.
Options
--max-jobs 0— disable local builds entirely, force everything to remote- Multiple builders:
--builders "ssh://host1 ... ; ssh://host2 ..."
Dev Shells
Available shells (see shells/):
nix develop .#go # Go toolchain
nix develop .#website # Hugo, for the website
nix develop .#nix # just + statix, deadnix, nixfmt (the repo dev shell)
Workflows & Automation
GitHub Workflow Token
If you edit the CI workflows, your token needs the workflow scope:
gh auth status # Check current scopes
gh auth login --scopes workflow
Formatting
nix fmt runs whatever package the flake’s formatter output points at
(flake.nix’s formatter.${system}), currently
nixfmt-tree.
Three different tools are involved and it’s easy to mix them up:
nixfmt— the official NixOS Foundation formatter. It formats a single file (or stdin), which makes it the right thing to point your editor’s format-on-save at.treefmt— a multi-language formatter orchestrator by numtide. It walks the whole repo tree and dispatches each file to the formatter configured for its type.nixfmt-tree— a small nixpkgs package that is not the upstream version of Numtide’s treefmt-nix (that’s a separate flake module numtide also publishes for configuring treefmt from Nix; unused here). It’s justtreefmtpre-wired to runnixfmton every*.nixfile, i.e. “zero-setupnix fmtfor a Nix repo”.
So nix fmt == treefmt-wide formatting using nixfmt under the hood, while
nixfmt on its own is the single-file tool for IDE integration.
The pre-commit hook (.pre-commit-config.yaml, run via
prek) invokes
nix fmt -- --fail-on-change --no-cache: it reformats any unformatted
files in place and fails the commit if anything changed, so re-stage and
commit again. treefmt has no dry-run/check flag that skips writing.
Facter
Generate a hardware report for a new system:
sudo nix run \
--option experimental-features "nix-command flakes" \
--option extra-substituters https://numtide.cachix.org \
--option extra-trusted-public-keys numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ik6cQjb41X0iXVXeHigGmycPPE= \
github:numtide/nixos-facter -- -o facter.json
Place the output in systems/<arch>/<host>/facter.json.
Networking
Networking in this flake is split into small, focused modules under modules/nixos/services/network/.
Use this section for host ingress, tunnels, and service exposure patterns.
Cloudflared
This flake uses Cloudflare Tunnel (cloudflared) to expose selected services without opening direct inbound ports on the origin hosts.
Module Location
- Base wrapper module:
modules/nixos/services/network/cloudflared/default.nix - Cloudflared service module:
modules/nixos/services/network/cloudflared/new.nix
One-Time Tunnel Bootstrap
Run these commands on the host where you manage tunnels:
cloudflared login
cloudflared tunnel create <name>
sudo mkdir -p /etc/cloudflared
sudo cp /home/mirza/.cloudflared/cert.pem /etc/cloudflared/cert.pem
Notes:
cloudflared logincreates~/.cloudflared/cert.pem.cloudflared tunnel create <name>creates tunnel credentials JSON under~/.cloudflared/.- The cert file is needed for declarative tunnel management workflows.
Secrets Wiring
Store tunnel credentials in secrets/secrets.yaml under:
config.cloudflared.<host>
Example existing keys:
config.cloudflared.sadoconfig.cloudflared.kuchikiconfig.cloudflared.madara
For a new ingress host (for example aizen), add:
config.cloudflared.aizen
The module reads from config/cloudflared/${config.networking.hostName}.
Host Configuration Pattern
Enable cloudflared on the host and define ordered ingress rules:
services.cloudflared = {
enable = true;
defaultDomain = "arnaut.me";
tunnels."${config.networking.hostName}".ingress = [
{
hostname = "arr.${config.services.cloudflared.defaultDomain}";
path = "/radarr.*";
service = "http://kuchiki.${config.services.tailscale.tailnet}.ts.net:${builtins.toString config.services.radarr.settings.server.port}";
}
{
hostname = "arr.${config.services.cloudflared.defaultDomain}";
path = "/sonarr.*";
service = "http://kuchiki.${config.services.tailscale.tailnet}.ts.net:${builtins.toString config.services.sonarr.settings.server.port}";
}
{
hostname = "arr.${config.services.cloudflared.defaultDomain}";
path = "/lidarr.*";
service = "http://sado.${config.services.tailscale.tailnet}.ts.net:${builtins.toString config.services.lidarr.settings.server.port}";
}
{
hostname = "arr.${config.services.cloudflared.defaultDomain}";
path = "/prowlarr.*";
service = "http://shinji.${config.services.tailscale.tailnet}.ts.net:${builtins.toString config.services.prowlarr.settings.server.port}";
}
];
};
Important details:
- Rules are matched in order.
pathsupports regex matching (for example/radarr.*).- A default catch-all service (
http_status:404) is configured by module defaults.
Deploy and Verify
After secrets + host config are in place:
sudo nixos-rebuild switch --flake ~/.config/flake#<host>
Check service status:
systemctl status cloudflared-tunnel-<host>
Verify external routes:
https://arr.arnaut.me/radarrhttps://arr.arnaut.me/sonarrhttps://arr.arnaut.me/lidarrhttps://arr.arnaut.me/prowlarr
Troubleshooting
- Missing or wrong secret path: verify
config.cloudflared.<host>exists insecrets/secrets.yaml. - Service unreachable: verify Tailnet DNS/host reachability from ingress host.
- Path not matching: confirm
hostnameand regexpathvalues in ingress rules. - Auth behavior unexpected: check Cloudflare Access app/policy scope for the hostname and path.
Tips & Resources
Tutorials & Guides
- NixOS & Flakes Book — comprehensive intro
- A Gentle Introduction to Nix Flakes — flake anatomy
- Why you don’t need flake-utils — the case for plain Nix
Vimjoyer (YouTube)
- Nix explained from the ground up
- NixOS: Everything Everywhere All At Once
- Ultimate NixOS Guide
- Modularize NixOS and Home Manager
- Nixvim: Neovim Distro Powered By Nix
- Is NixOS The Best Gaming Distro
Other
Nix Language
- explainix — hover over Nix syntax to see what it means
- inherit keyword
High-Level Libraries
- flake-utils
- flake-parts
- snowfall lib — this flake’s directory structure is inspired by snowfall
Updating Custom Packages
Use the bump recipe, which wraps nix-update
and figures out the attribute path and platform for you:
just bump packages/top-level/<pkg> # or packages/custom/<pkg>
Under the hood it runs scripts/bump-package.sh; extra flags are passed straight
through to nix-update, e.g. just bump packages/top-level/<pkg> --version=1.2.3.
ZFS
Dev Tools
Handy terminal tools available via nix run nixpkgs#<name>.
| Package | Description | Repo |
|---|---|---|
broot | Interactive directory tree navigator | https://github.com/Canop/broot |
glow | Markdown viewer for the terminal | https://github.com/charmbracelet/glow |
hexyl | Colored hex dump viewer | https://github.com/sharkdp/hexyl |
miller | CSV, JSON, TSV processor (mlr) | https://github.com/johnkerl/miller |
ncdu | NCurses disk usage analyzer | https://dev.yorhel.nl/ncdu |
ouch | Multi-format archiver (zip, tar, 7z, …) | https://github.com/ouch-org/ouch |
slides | Terminal-based presentation tool | https://github.com/maaslalani/slides |
poppler-utils | PDF rendering tools (pdftotext, pdftoppm, …) | https://gitlab.freedesktop.org/poppler/poppler |
q | SQL-like queries on CSV/TSV files | https://github.com/harelba/q |
up | Ultimate Plumber — interactive pipe viewer | https://github.com/akavel/up |
Also gone (no longer enabled by development tag): browsh, firefox.
Maintainer Notes
TODO
- Integrate disko for each host
- Manage host types via Colmena host tags for finer-grained control
Fixes
Thick black borders in GNOME apps
Set GSK_RENDERER=gl. Tracked at GTK#6890.
Credits
- use-the-fork — help moving from standalone Home Manager to module-based setup
- u/paulgdp — advice on detecting
nixosConfigin module context olmokramer— example usinglib.genAttrsfor host generation