everything she knows

A quiet reference for a cozy tool. Find what you need, take your time.

getting started

what is nina?

intro

Nina is a NixOS Intuitive Navigation Assistant — a gentle command-line tool that wraps the full NixOS workflow in one quiet, consistent voice.

She handles package search, rebuilds, flakes, services, generation history, and remote machines — all without dropping you into a different screen, switching tools, or making you memorise long nixos-rebuild incantations.

Nina is a ground-up rewrite in Zig. Zero runtime dependencies. A single binary. Runs the same on any NixOS machine.

The goal is simple: NixOS should feel like home. Nina is one step toward that.

(っ˘ω˘ς )

Nina stays inline at your prompt. Search results, install confirmations, diff previews — everything comes to you. You never leave the shell you're already in.

installation

setup

Nina is a Nix flake. Two ways to install:

prerequisites

Both methods require nix-command and flakes to be enabled. Add this to ~/.config/nix/nix.conf (or /etc/nix/nix.conf on NixOS):

experimental-features = nix-command flakes

Or run nina setup after installing — she'll add the line for you.

direct url (simplest, no setup needed)
$ nix profile add 'https://kepr.uk/nina/archive/HEAD.tar.gz#nina'
persistent registry (add once to ~/.config/nix/nix.conf)
extra-flake-registry = https://kepr.uk/nix-registry.json

Then:

$ nix profile add nina
steal the source & build
$ koh steal kepr.uk/nina && koh build
→ snapshots the full source, then builds and installs nina for you
(•̀ᴗ•́)و

After installing, run nina setup — it makes sure the Nix features Nina needs are turned on, and on NixOS walks you through the one config line.

nina setup

onboarding

nina setup is your onboarding step. It makes sure Nix's experimental features — nix-command and flakes — are enabled, which is all Nina needs to search and install packages. If they're already on, it prints all set and exits.

$ nina setup
on NixOS

Nina shows you the exact line and offers to add it to /etc/nixos/configuration.nix — the declarative, permanent home for these features:

# /etc/nixos/configuration.nix
nix.settings.experimental-features = [ "nix-command" "flakes" ];

Press y and Nina appends it for you (keeping a .nina-bak backup). Prefer to do it yourself? Press n and Nina shows you the line to paste and where it goes. Either way it ends with one step:

$ sudo nixos-rebuild switch

…and then you're ready to use Nina.

everywhere else

Nina writes experimental-features = nix-command flakes to ~/.config/nix/nix.conf (creating the file if needed). No sudo, and it takes effect in your next shell.

first run

setup

Just type nina help at the prompt. She'll show you every command she knows. (っ˘ω˘ς )

$ nina help
:: nina NixOS Intuitive Navigation Assistant by Asha Software
 
apply rebuild and switch
back roll back one generation
go <n> switch to generation n
history list all generations
clean remove old generations
search <q> find packages
install <pkg> add to profile or system
remove <pkg> remove from profile or system
try <pkg> test without installing
list show installed packages
edit open configuration.nix
check validate config
diff changes between generations
status machine health
update refresh channels
upgrade update and rebuild
log operation history
doctor diagnose issues
mood plain-language health
develop enter dev shell
flake flake management
build build a package
run run without installing
service manage services
store nix store tools
channel manage channels
profile user profile packages
home home manager commands (init/apply/back/history/edit/check/diff/packages)
home init set up home manager for the first time
home init --switch set up and activate immediately
home apply apply home config
home apply --dry preview changes without activating
home back roll back home config
home history list home generations
home edit open home.nix
home check validate home config
home diff compare home generations
home packages list managed packages
info system information
boot boot entries
fmt format configuration.nix
repl nix repl with nixpkgs
option <q> search nixos options
pkg inspect packages
why <pkg> explain why a package is installed
pin pin flake inputs
hash compute nix hash
fetch fetch and hash a url
goodbye uninstall nina
hello list machines
man [topic] open the built-in manual pager
help this message
 
flags: --on <machine> --dry --check --no-apply --last <n> --all --json --version

Then try nina hello to see the machines Nina has configured, or jump straight to nina search to find a package.

From here, search for a package, apply a change, or just wander through the tips section to see what's possible.

commands

packages

install · remove · profile

Install packages into your profile with a single command. Nina searches nixpkgs, shows you what it found, and asks how you want it.

Running nina pkg or nina profile or nina channel with no subcommand shows a subcommand list. nina pkg help does the same.

nina install
nina install <pkg>

Search nixpkgs for the package, then prompt for install method. Σ(°∀° )

$ nina install neovim
:: neovim 0.10.4
 
[i] profile install instant no rebuild
[s] system install opens editor requires apply
[t] try now exits when done
 
> i
:: installing neovim profile
-> nix profile install nixpkgs#neovim
:: neovim installed available now

[i] profile install — runs nix profile install nixpkgs#attr. Available immediately, no rebuild needed.

[s] system install — opens configuration.nix in your editor at the systemPackages line. Add the package, save, and Nina asks whether to apply.

[t] try now — drops you into a nix shell with the package available. Gone when you exit.

nina remove
nina remove <pkg>

Remove a package from your active Nix profile by name.

nina try
nina try <pkg>

Drop into a temporary nix shell with the package available. Exits cleanly — nothing is written to your profile or config.

nina list
nina list

Show packages in your active Nix profile.

nina profile
nina profile [list | install <attr> | remove <attr> | upgrade]

Low-level profile management. profile list shows all profile packages. profile upgrade upgrades all profile packages at once.

nina why
nina why <pkg>

Explain why a package is installed by showing its dependency chain. An alias for nina pkg why <pkg>.

$ nina why ripgrep
:: why ripgrep kyoshi
 
ripgrep is a direct profile package
nina pin / nina unpin
nina pin <input> <commit> · nina unpin <input>

Pin or unpin a flake input to a specific commit. nina unpin runs nix flake update on that input to release the pin.

system changes

diff · apply · back

Preview, apply, and roll back changes to your NixOS system. Nina shows you exactly what will change before it happens, and arms a rollback the moment things go sideways.

nina diff
nina diff [<from> <to>]

Compare two generations using nix store diff-closures. Without arguments, compares the previous generation to the current one. Pass two generation numbers to compare any two.

$ nina diff
:: gen 348 -> gen 349 kyoshi
 
+ neovim 0.10.4
^ ripgrep 14.0.3 -> 14.1.1
nina check
nina check

Validate your configuration with nixos-rebuild build. Catches syntax errors and missing references before they reach a running system. No activation — nothing changes.

nina apply
nina apply [--on <machine>] [--dry]

Rebuild and switch to the new generation with nixos-rebuild switch. If confirm = true in your config, Nina asks before proceeding.

On flake-based systems where your config directory contains a flake.nix, Nina automatically passes --flake <config> to nixos-rebuild. No configuration needed.

$ nina apply
:: rebuilding kyoshi...
:: generation 349 [4.1s]
--on <machine> rebuild a remote machine over SSH
--dry dry-activate only — build without switching
--check validate config only, same as nina check
nina back
nina back

Roll back one generation using nixos-rebuild switch --rollback. To jump to a specific generation, use nina go <n>.

$ nina back
:: rolling back kyoshi gen 349 -> gen 348
:: done gen 348
ε-(´∀`; )

Generations are immutable snapshots. nina back always has somewhere to go, and nina go <n> can reach any point in your history.

flakes & dev shells

flake · develop · build

Inspect flake outputs, step into dev shells, build specific outputs, and manage the flake lifecycle — all with the same quiet vocabulary.

Running nina flake with no subcommand shows a subcommand list. nina flake help does the same.

nina flake show
nina flake show

Show what the current directory's flake exposes — packages, apps, dev shells, NixOS configurations. Parses nix flake show --json output.

$ nina flake show
:: .
 
packages x86_64-linux nina
apps x86_64-linux nina
devShells x86_64-linux default
nina flake update
nina flake update [<input>]

Run nix flake update. Pass a specific input name to update only that one. Prints a confirmation when flake.lock is written.

(・_・;)

Unpinned inputs — including https://kepr.uk/nina/archive/HEAD.tar.gz, if you've pinned Nina as a flake input rather than installing via nix profile — are cached locally by Nix for tarball-ttl seconds (an hour, by default) no matter what's actually changed upstream. If an update looks like a no-op and you know there's something newer, run nix flake update <input> --refresh directly — nina flake update doesn't forward flags yet.

nina flake check
nina flake check

Run nix flake check. Reports valid or prints the error.

nina develop
nina develop [run <cmd> | show]

Enter the flake's dev shell with nix develop. Type exit to return. develop run <cmd> runs a single command without an interactive shell. develop show builds without entering.

$ nina develop
:: entering dev shell
 
exit to return
nina build
nina build [<attr>]

Build a flake output with nix build. Defaults to the default attribute. Result is linked as ./result.

nina run
nina run <pkg>

Run a package without installing it. Bare names are resolved as nixpkgs#<pkg>.

inspection & care

status · service · clean

Check on the health of your system, follow service logs, inspect the store, and reclaim disk space — without jumping between different tools.

Running nina service or nina store with no subcommand shows a subcommand list. nina service help does the same.

nina status
nina status [--on <machine>]

Show the current generation and reachability of the target machine.

$ nina status
:: status
 
kyoshi gen 349 1h ago 10.2 GB
hm gen 2 2h ago

Home Manager generation is shown when Nina detects a standalone Home Manager installation.

nina doctor
nina doctor [--on <machine>]

Run a health diagnostic. Checks the nix daemon, config syntax, and channels. Prints ok / warn / fail per check.

$ nina doctor
:: diagnosing kyoshi
 
nix daemon ok
config syntax ok
channel ok
home manager standalone gen 2
 
:: all good
nina service
nina service [list | status <name> | logs <name> | start <name> | stop <name> | restart <name> | enable <name> | disable <name>] [--user]

Manage systemd services. service logs <name> -f follows the journal live. stop and disable prompt for confirmation when confirm = true.

All service commands accept --user to target user-managed systemd services instead of system services.

$ nina service logs ollama -f
:: ollama last 50 lines kyoshi
 
$ nina service list --user
:: services kyoshi user
$ nina service start --user syncthing
nina store
nina store [info | gc | verify | repair | path <attr>]

Inspect and maintain the Nix store. store info (default) shows total size, live paths, and reclaimable space. store gc runs garbage collection. store path <attr> evaluates a store path for a nixpkgs attribute.

nina clean
nina clean [--all]

Remove old generations and run garbage collection. Keeps the number of generations set by generations in your config (default: 5). --all removes all old generations regardless of that setting. ( ˘ω˘ )

$ nina clean
:: cleaning kyoshi keeping 5 of 14
continue? [Y/n] y
:: freed ?

generation history

history · go · gen · log

Your system's whole story is saved in generations. Browse them, compare them, travel back to any of them. Nothing is gone until you clean it up.

Running nina gen with no subcommand shows a subcommand list. nina gen help does the same.

nina history
nina history

List all system generations with their timestamps. The current generation is highlighted.

$ nina history
:: generations kyoshi
 
349 2026-05-30 09:32:01 current
348 2026-05-29 14:22:44
347 2026-05-25 11:05:33
nina go
nina go <n>

Switch to any specific generation by number using nix-env --switch-generation. Use this when nina back isn't far enough.

nina gen
nina gen [list | current | delete <n | old>]

gen list is an alias for history. gen current prints the current generation number and nothing else. gen delete <n> removes a specific generation; gen delete old removes all non-current generations.

nina log
nina log [--last <n>]

Show Nina's operation log from ~/.local/share/nina/nina.log. Displays the last 50 entries by default. Use --last N to show more or fewer.

(´▽`)

Generations are your safety net. Apply freely, knowing you can always go back. nina history shows the whole story. nina go takes you there.

home manager

home · init · apply · back · history

Nina manages your Home Manager configuration with the same vocabulary as your system. All nina home commands work with standalone Home Manager and NixOS module setups. Nina detects which mode you're using automatically.

nina home init
nina home init [--switch] [<dir>]

Bootstrap Home Manager for the first time. Runs nix run home-manager/<branch> -- init, which creates ~/.config/home-manager/home.nix with a starter configuration. After running, edit home.nix and apply with nina home apply.

Passing --switch activates the configuration immediately after creating it — no separate nina home apply needed. Pass a directory path to initialise into a custom location instead of ~/.config/home-manager.

If ~/.config/home-manager/home.nix already exists, Nina refuses to overwrite it and points you at nina home apply instead.

$ nina home init
:: initialising home manager ~/.config/home-manager
-> edit home.nix then run nina home apply
 
:: done edit home.nix and run nina home apply when ready
$ nina home init --switch
(っ˘ω˘ς) :: setting up home manager ~/.config/home-manager
-> will activate after init
 
(˘ᵕ˘) :: home manager ready
nina home apply
nina home apply [--dry]

Apply your Home Manager configuration. Runs home-manager switch under the hood. --dry builds without activating.

$ nina home apply
 
(っ˘ω˘ς) :: applying home config kyoshi
 
-> home-manager switch
 
:: generation 2 [63.9s]

On NixOS module setups (where home-manager is managed by nixos-rebuild), Nina redirects you to nina apply which rebuilds both system and home config together.

nina home back
nina home back

Roll back to the previous Home Manager generation using home-manager switch --rollback.

$ nina home back
 
:: rolling back home config kyoshi gen 2 -> gen 1
 
continue? [Y/n] y
 
:: done gen 1
nina home history
nina home history

List all Home Manager generations with timestamps. The current generation is marked.

$ nina home history
 
:: home generations kyoshi
 
2 2026-06-09 14:22 current
1 2026-06-09 11:05
nina home diff
nina home diff

Show what changed between the last two Home Manager generations using nix store diff-closures.

nina home check
nina home check

Validate your Home Manager configuration with home-manager build without activating.

nina home edit
nina home edit

Open ~/.config/home-manager/home.nix in your configured editor.

nina home packages
nina home packages

List packages managed by Home Manager in your current generation.

$ nina home packages
 
:: home packages kyoshi
 
hello 2.12.1
jq 1.7.1
(っ˘ω˘ς )

The same apply → diff → back pattern works for your home config, just as it does for your system. Nina uses the same verbs so you don't have to learn a second vocabulary.

machine config

edit · info · channel · boot

Edit your configuration safely, inspect the running system, manage channels, and set boot defaults — all without needing to remember the right file path or nixos-rebuild flag.

nina edit
nina edit [hardware]

Open configuration.nix in your configured editor (default: vim). Pass hardware to open hardware-configuration.nix instead. After saving, run nina check to validate before applying.

$ nina edit
:: opening /etc/nixos/configuration.nix
-> vim /etc/nixos/configuration.nix
 
run nina check to validate before applying
nina fmt
nina fmt [--check]

Format configuration.nix with nixpkgs-fmt. --check reports whether formatting is needed without writing.

nina info
nina info

Show the NixOS version, kernel version, and uptime of the target machine.

nina channel
nina channel [list | add <url> [<name>] | remove <name> | update]

Manage Nix channels. channel list is the default. channel update runs nix-channel --update.

On flake-based systems, nina upgrade updates flake inputs and rebuilds instead of running nix-channel --update.

nina boot
nina boot

List boot entries from the bootloader. Shows the current entry and all available generations.

nina repl
nina repl

Launch nix repl with nixpkgs loaded. Type :q to exit.

(ˊᗜˋ)

nina edit uses the editor value from ~/.config/nina/config (default: vim). Set it to your editor of choice and Nina will use it everywhere.

remote machines

--on · hello · doctor

The same voice, even over SSH. Every Nina command accepts --on <machine> and behaves identically on a remote host. No separate tooling, no context switching.

the --on flag
nina <command> --on <machine>

Any Nina command runs on a configured remote machine. The machine name matches an entry in your Nina config file, or falls back to your SSH config.

$ nina apply --on azula
✦ rebuilding azula over ssh
· building generation 193
generation 193.
 
$ nina service logs ollama -f --on azula
✦ following logs for ollama on azula
[info] serving on 0.0.0.0:11434
configuring machines

Define remote machines in ~/.config/nina/config:

# ~/.config/nina/config
 
[machine]
name = azula
host = june@azula
ssh_key = ~/.ssh/id_ed25519
fleet commands
nina hello list all configured machines and their kind (local / ssh)
nina status --on <machine> show generation and uptime for a remote machine
nina doctor --on <machine> run a full health diagnostic on any machine
nina apply --on <machine> rebuild and switch a remote machine over SSH
(˶ᵔ ᵕ ᵔ˶)

Nina keeps remote work from turning into a whole new personality. Same verbs, same little reassurances, same sense of where you are.

reference

hooks

pre · post

Nina can run optional executable scripts before and after state-changing commands. Hooks live in ~/.config/nina/hooks/. Missing hooks are ignored, and files that are not executable are treated as absent.

available hook files
pre-apply post-apply
pre-back post-back
pre-home post-home
pre-upgrade post-upgrade

pre-apply runs before nina apply, pre-back before nina back, pre-home before nina home apply, and pre-upgrade before nina upgrade. The matching post-* hooks run only after the command succeeds.

pre-hook failures

If a pre-hook exits non-zero, Nina shows the exit code and the last five lines of hook output, then asks whether to continue. That prompt defaults to no, even if confirm = false, because a hook failure usually means something the user explicitly cares about.

:: pre-apply hook exited with code 1
 
uncommitted changes - commit before applying
 
continue anyway? [y/N] n
:: aborted
post-hook failures

If a post-hook fails, Nina prints a warning after the success line. The original command already succeeded, so the hook warning does not turn the operation into a failure.

:: generation 137 [42.3s]
warning: post-apply hook exited with code 1
example: require a clean config tree

This hook stops nina apply when your NixOS config has uncommitted changes:

#!/usr/bin/env sh
# ~/.config/nina/hooks/pre-apply
if git -C ~/nixos-config status --porcelain | grep -q .; then
echo "uncommitted changes - commit before applying"
exit 1
fi

Make hook scripts executable with chmod +x ~/.config/nina/hooks/pre-apply. Nina creates the hooks directory automatically the first time it checks for hooks.

configuration

~/.config/nina/config

Nina's config file lives at ~/.config/nina/config (XDG). The format is plain key=value, one per line. Comments start with #. All settings are optional — Nina works out of the box with no config file. (´ω`)

(っ˘ω˘ς )

If you have an existing ~/.nina.conf from an earlier version, Nina migrates it to ~/.config/nina/config automatically on first run.

full example
# ~/.config/nina/config
 
editor = nvim
generations = 5
confirm = true
teach = false
color = true
 
# local machine (default)
[machine]
name = kyoshi
config = /etc/nixos
local = true
default = true
 
# remote machine over SSH
[machine]
name = azula
config = /etc/nixos
host = june@azula
ssh_key = ~/.ssh/id_ed25519
all options
option type default description
editor string vim editor command Nina opens for nina edit
generations integer 5 how many generations to keep when running nina clean
confirm bool true prompt before destructive operations. empty input or non-'n' = yes
teach bool false print the underlying nix commands Nina runs (prefixed with =)
color bool true enable ANSI colors. also respects the NO_COLOR environment variable
flake bool auto-detected set to true if your config directory contains a flake.nix. Nina detects this automatically
[machine] name string local name used with --on <name>
[machine] config string /etc/nixos path to the directory containing configuration.nix
[machine] local bool true run commands locally. set automatically to false when host is given
[machine] default bool false use this machine when no --on flag is given
[machine] host string SSH destination (user@host or just host)
[machine] user string SSH user, if not included in host
[machine] ssh_key string path to SSH private key. ~ expands to $HOME

expressions ♡

kaomoji · moods

Nina uses a few small kaomoji expressions instead of a mascot. They stay tucked into the UI, show a little feeling, and leave the work itself readable.

The rule is simple: calm most of the time, brighter when something lands, softer when something goes wrong.

(˶ᵔ ᵕ ᵔ˶)
hello

A warm, quiet welcome. Nina is ready to help and not making a fuss about it.

shows when: setup wizard, first-run greeting
(っ˘ω˘ς)
starting

Settling in and getting to work. Something meaningful is beginning.

shows when: apply start, home apply start, entering a dev shell
╰(*°▽°*)╯
relief

Something that was broken is working again. A small celebration for persistence.

shows when: first success after a previously failed build
( ˘ᵕ˘ )
done

Calm satisfaction. A rollback landed cleanly and the system is in good hands.

shows when: back success
( ´ ∀ ` )
clean

Light and tidy. The work is done and things look better than before.

shows when: clean done
( ´ ▽ ` )
all good

Everything checked out. A quiet joy that the system is healthy.

shows when: doctor all clear, mood all good
(˘ω˘ )
exiting

A gentle close. Nothing went wrong — it's just time to step out.

shows when: try exit, leaving a dev shell
(;ω;)
sorry

Something went sideways, and Nina is honest about it. Details are right there with her.

shows when: network or SSH errors, search timeout
( ´•̥̥̥ω•̥̥̥` )

If you prefer a quieter experience, set color = false in your config. Nina will still show everything — just without the ANSI palette.

tips & recipes

patterns · workflow

A few patterns that make daily NixOS life a little smoother. (*˘︶˘*)

daily driver routine

A quiet morning with Nina:

# update flake inputs and preview changes
$ nina flake update
$ nina diff
 
# apply if the diff looks good
$ nina apply
safe exploration

Try a package without committing to it:

$ nina try bat
:: trying bat exit when done
→ bat is available in this shell only, gone when you exit
fleet rollout

Push the same config change to multiple machines:

$ nina apply --on kyoshi
:: generation 349 [4.1s]
$ nina apply --on azula
:: generation 193 [6.3s]
generation archaeology

Something broke and you want to find when:

$ nina history
→ find the last known-good generation number
$ nina diff 344 349
→ see what changed across those generations
$ nina go 344
:: done gen 344
home manager workflow

Nina manages Home Manager directly with nina home. Apply your home config, roll it back, browse generations, and inspect packages — all with the same verbs as the rest of Nina.

# apply your home config
$ nina home apply
 
# check what changed
$ nina home diff
 
# roll back if needed
$ nina home back
useful aliases
# in your shell config
alias ns="nina search"
alias no="nina option"
alias na="nina apply"
alias nd="nina diff"
alias nb="nina back"
(≧◡≦)

When something goes wrong, nina back is always there. When you're not sure what to try next, nina diff shows you the picture. And when things go right, Nina keeps the terminal feeling light.