r/o

README.md

vyx

Note

This is my live, primary repository for deploying all my infrastructure, and laptop! I don’t update the documentation nearly as regularly as I update the repository, but queries are welcome.

For bootstrapping reasons, pulls are made from https://gitlab.com/akhya/vyx, but this repository is updated at the same time as that one and is the canonical one.

You may use any of the contents per the (very permissive) license, noted at the end of this document.

Summary

This is a couple things at once!

  • NixOS/nix-darwin configurations for my laptops and VPS.
  • Standalone Home Manager configurations for my user on each of those.
  • Flux CD repository for a single-node k3s cluster.

A description of the basic layout follows.

  • flake.nix — entrypoint. I use 25.11 on both NixOS and Darwin. Load common config, our modules, host-specific config, Home Manager, and HEAD jj/Helix builds.
  • modules — root of vyx modules. Host data is loaded from hosts.toml, which supplies values used all over.
  • hosts — host-specific config.
    • hosts/seraphim — configures my Macbook (on nix-darwin).
      • Homebrew is configured declaratively, which is really nice — I’ll often brew install something manually just to try it out, and let my next rebuild GC it, or add it to the config if I decide to keep it.
      • Comenzar is enabled.
    • hosts/rebane — configures my work Macbook (on nix-darwin).
    • hosts/akhya — configures my Framework laptop on NixOS.
    • hosts/coer — unused Raspberry Pi configuration.
    • hosts/kala — configures kala, my VPS on NixOS and k3s server.
      • Hardware config is from hosts/ovh-vps.nix, originally pulled from nixos-infect-generated hardware-configuration.nix.
      • A baseline vhost for the server’s FQDN itself is set which just returns a 502. (This is important.)
      • Tailscale is enabled.
      • k3s is started.
  • home/ — Home Manager config, instantiated in the flake’s homeConfigurations. Loads submodules and installs baseline packages.

Git aliases

[!NOTE] I’m almost exclusively using jj now, so while the below still represents how I use git, I don’t tend to use it directly all that much any more! Here’s a post about jj. home/fish.nix includes jj aliases similar to the below, but they’re undergoing active refinement as my use increases!

Firstly, for all our git aliases, we create a shell alias with a g prefix; i.e. if you have git c defined for commit, then gc at the shell invokes it. (The bare letters are used by jj aliases.)

This isn’t overkill — I talk a bit about this early in Nix revisited, but I’d like to elaborate now: Git is a Swiss Army knife. It’s way better than what came before it in lots of ways, and I’m sure (and glad) folks are improving on it, but I’m very comfy with it, and not because I’ve abstracted it all away from me so I don’t have to think about it, but because I brought it close enough that all its little implements (continuing with the SAK analogy) are like my own fingers. Err. Or something.

Have a look at home/git-aliases.nix. Everything’s split into groups of related aliases. Remember that these are all promoted to shell aliases in their own right, so (as documented in Nix revisited) where necessary I adjust the alias to move out of the way of a common shell utility (like cp ~> pc). There are some utilities I don’t care about and happily displace; that’s what command and builtin are for.

  • cl: clone. I pull down a lot of new code! (Don’t use this for multiple working trees, or multiple remotes.)
  • co: checkout. Something I find myself doing a lot is co BLAH^ -- x/y/z if I found I removed x/y/z in commit BLAH and want to reintroduce it (but I don’t want to revert all of BLAH). Of course, co jkl or co - to change branches are the main use.
    • cb: checkout -b. Branches are extremely lightweight, use them constantly! Use them to slap temporary names on things while you shift things around (so you don’t have to hunt in the reflog, but you always can do that too), to track remotes, all over.
    • pc: checkout -p. Interactively checkout hunks. I’ll do this when I want to get rid of some debugging stuff I don’t need any more, often cycling between pc and a to remove and stage alternately. You can give a path to restrict the scope if you know what you want to revert parts of.
  • s: status -sb. Default to short output, with branch/tracking info. Making the output short is key to ensuring you can call it as freely as ls (which I alias to .!); if it’s long, the feeling of loss of context will act as counterpressure, which makes it easy to feel disconnected from the state of your repo/working tree/index.
  • b: branch. Branch ops, but also just to list local branches by itself.
    • ba: branch -a. Include remotes.
    • bd: branch -d. Delete a branch.
    • bdd: branch -D. Really delete a branch. Part of branches being lightweight is not just “easy to create” but “easy to delete” (and recreate again if you need to!). Otherwise they accumulate and become the very opposite.
  • h: show. (s was taken, so we go to the second letter.)
    • h1: show HEAD^.
    • h2: show HEAD^^.
    • h5: show HEAD^^^^^. If I’ve gotten to this stage I need to find a better point of reference, but they do come in handy for quickly retracing steps.
  • d: diff. Use it often. Consider also d -w to ignore whitespace.
    • dch: diff --cached. Shows the diff between the index (staging area) and HEAD; i.e. what you would commit if you hit c.
  • l: log --show-notes='*' --abbrev-commit --pretty=format:'%Cred%h %Cgreen(%aD)%Creset -%C(bold red)%d%Creset %s %C(bold blue)<%an>% cm%Creset' --graph.
    • Yes, really.
    • This is probably one of the most important ones. For the same reason as “short status” above, actually condensing log info into short lines with the tree structure visible is super important for having a feel about the shape of your repo and where exactly you’re peeking into it from.
    • la is the same but with --all added; this means you can see remote tracking branches, branches you’re not on, etc., which gives an even wider overview.
    • lp adds --patch, which includes the full diff of each commit between the lines. I’ll use this pretty often when looking at where references to certain items have been added and removed over time across a repository; if it’s all in one file or subtree, lp x/y/z works great too.
  • a: add -p. The default is always to use the patch interface to stage hunks incrementally. You want this to be your default. Know what you’re committing.
    • Seriously, in a work situation, in a hobby project situation, in a tiny throwaway whatever, know what you’re committing. You can’t accidentally commit a TODO if you look at every line before you put it in a commit.
    • Short on time or motivation? Sure, ad . && cm blah and call it a day. Tomorrow you r HEAD^, a and split it out properly.
    • ad: add. For when you want to add whole files or directories. a is my default, but I still need ad because this is a Swiss Army knife, not a nanny.
  • mrm: rm. Because rm was taken.
    • mrc: rm --cached. Remove a file from the index, without touching the working tree; “unstage” a file. You’ll need mrc -f a fair bit of the time.
  • c: commit. Use this often, since you’ll have interactively staged with a and want to write a long commit message in $EDITOR.
    • cm: commit -m. More often for quick shots: cm wip, cm fixup, cm 'v0.26.0.', etc.
    • cx: commit --amend. You forgot to stage one little hunk of a file? a && cx, no wuckas.
    • pcp: cherry-pick. This is conceptually similar to committing, so I include it here! I use this when I’m doing a drawn-out “manual” rebase, or when I simply have one commit to pull from elsewhere. It’s handy to have at hand so you can easily pcp --abort if you don’t like a conflict.
  • m: merge.
    • mnf: merge --no-ff. When you’re merging something that could be a fast-forward and you don’t want it to be; this results in a merge commit like a PR merge on GitHub.
  • r: reset. Use this a lot. By default with no arguments, this “unstages” everything by resetting the index to match HEAD, leaving your working tree untouched. With a commit argument, it resets both the index and the current branch HEAD to the commit given (which can be a branch name or tag), still leaving the working tree untouched. This means you can essentially rewind a branch to a different point in time, without disrupting your work.
    • In other words, a poor man’s “squash the last 5 commits” looks like r HEAD~5 && ad . && c. (This also includes your working tree at the time of starting, note.)
    • rh: reset --hard. This is as the above, but also resets the working tree.
    • You like what you did in subdirectory plant/ as of the current commit, but otherwise you want to drop the last 3 commits, and condense the plant changes? cb yummy-plant && co - && rh HEAD^^^ && co yummy-plant -- plant/ && bdd yummy-plant && c. It’s free real estate. (You can skip the branch creation and just plonk HEAD’s SHA into your clipboard before starting, or even use HEAD@{n}!)
  • en: revert. For introducing a revert commit.
  • rb: rebase. Bringing some changes up to date.
    • ri: rebase -i. For carefully bringing changes up to date, or moreover for rewriting history. Use this to condense commits, drop commits, pull commits apart, anything you so desire.
    • rbc: rebase --continue. Continue a rebase after editing a commit or addressing conflicts. You might also need rb --skip if a commit doesn’t result in any change on the new base.
    • rba: rebase --abort. Rewind all changes made to before you started your last rebase.
  • w: push. The mnemonic is “write”.
    • wf: push -f. If you enjoy revising history as much as I do, you will find this helpful. All the usual nonsense applies: don’t rewrite history of branches others are working on unless you have a really good thing going.
    • wo: push origin. Particularly useful with wo :xyz to delete branches and tags on the remote named origin.
    • wu: push -u. Set the local branch to track the remote.
  • v: pull. The mnemonic is “v is next to w on Dvorak” (:
  • f: fetch.
    • fa: fetch --all. Fetch all remotes.
  • rv: remote -v. Lists remotes.
    • ra: remote add. Add a new remote. I do this when fetching a new long-term collaborator’s changes, or adding my own fork (or an upstream to my existing fork).
    • rp: remote prune. Prune a remote; i.e. delete tracking branches that don’t exist on it.
    • rpo: remote prune origin. Prune the origin remote.
    • rpa: fetch --all --prune. Fetches all remotes, pruning them in the process. (The fact that this also fetches is Fine. Fetching is not dangergoose.)
  • st: stash. Stashes all local changes. You can also just make a temporary commit and use reset shenanigans, but stash is often faster, and by default preserves your index and working tree separately. (Try la after a stash to see refs/stash.)
    • st -p is helpful if you want to partially stash some stuff, before committing (and perhaps reverting) some others.
    • sth: stash show -p. We add -p as, by default, stash show doesn’t actually show you the diff of the stashed change. (The h mnemonic is preserved from our top-level h alias.)
    • stl: stash list.
    • std: stash drop. Drops the latest stash (or stash reference). Note that the commit reference it gives you can be given to sta until it’s garbage collected, so it’s no big deal if you do this by accident.
    • stp: stash pop. Applies the latest stash (or stash reference), and then drops the stash if it applied without conflict.
    • sta: stash apply. Applies the latest stash (or argument), but the argument doesn’t have to be an explicit stash reference (since it doesn’t need to be a valid stash to drop, unlike with pop).
  • sms: submodule sync --recursive. Sync submodule remotes with .gitmodules.
    • smu: submodule update --init --recursive. Use this to initially get and update submodules over time.
  • bl: blame.

SSH aliases

Similar to above, a shell alias is created for every host defined in hosts.toml; i.e. typing kala is enough to ssh kala.

vyxnix

Similar to git, I need the Nix tools closer to hand. vyxnix is the launcher that makes that happen; combine it with a range of aliases and baby you’ve got yourself a stew.

It mostly documents itself, so I’m going to just repeat the header comments:

# Principal nix3 launcher.
#
# "develop" and "shell" are treated specially:
# vyxnix develop -> nix develop --command fish
# vyxnix develop x y z -> nix develop x y z --command fish
# vyxnix develop x -- y z -> nix develop x --command fish -c "y z"
# vyxnix develop x --command y z -> nix develop x --command y z
# "d/s" below refers to these cases.
#
# "run" and "shell" are also treated specially:
# vyxnix run xyz -> nix run nixpkgs#xyz
# vyxnix run abc#def -> nix run abc#def
# vyxnix shell uvw -- nyonk -> nix shell nixpkgs#uvw -- nyonk
#
# !x=y translates to --override-input x y.
# !o -> --offline.
# !n -> don't use `nom` for build/shell/develop.
# $defaultargs: specify --vyx-no-defaults to omit.
set -f defaultargs -L --keep-going
# $dryrun: echo the command that would be executed.

In practice this means (modulo the defaultargs mentioned above):

  • nd -- make is equivalent to nix develop --command fish -c "make".
  • nb !nixpkgs=~/g/nixpkgs is equivalent to nix build --override-input nixpkgs ~/g/nixpkgs.
  • nr htop is equivalent to nix run nixpkgs#htop.

The default args are also really helpful — I almost never remember to include -L or --keep-going until a long build has failed and I wish I already had.

The really nice thing about using Fish everywhere, too, is that vyxnix and aliases using it all support tab-completion correctly, including the --vyx options. If they didn’t, I’d want to use them a lot less, and keep forgetting the exact --vyx options since they’re so rarely wanted.

j

This is like vyxnix, but for jj! j is a simple launcher which makes a few options nearer to hand:

# !i -> --ignore-immutable
# !b -> --allow-backwards
# !p -> --no-pager
# !s -> --stat
# !ua -> --update-author
# ^XX -> --remote XX (may be specified more than once)
# %XX -> -r XX
# !U -> sets DELTA_FEATURES=+

This allows for scenarios like the following:

$ Aq
Error: Commit 063ff6e65ac0 is immutable
Hint: Pass `--ignore-immutable` or configure the set of immutable commits via `revset-aliases.immutable_heads()`.
$ Aq !i
Working copy now at: vwsmmryz dde83cf9 (empty) (no description set)
Parent commit : qoqluvpo 4921e89b main* | README: update for jj.

k

This is like vyxnix, but for Kubernetes! Oh my goodness! k abbreviates calls to kubectl, namespace specification, and common tasks.

$ K kv gp
NAME READY STATUS RESTARTS AGE
kv-deploy-d9bb5f75-4glh8 2/2 Running 0 8m21s
kv-deploy-d9bb5f75-gzqmn 2/2 Running 0 8m7s
kv-deploy-d9bb5f75-ksc9k 2/2 Running 0 8m39s

License

Copyright © 2025–2026 Asherah Connor <ashe@kivikakk.ee>

This work is free. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2, as published by Sam Hocevar. See the COPYING file for more details.