├── config
├── helix
│ ├── helix.scm
│ ├── .gitignore
│ ├── languages.toml
│ └── config.toml
├── niri
│ ├── .gitignore
│ ├── private.kdl
│ ├── config.kdl
│ ├── common
│ │ ├── config.kdl
│ │ ├── input.kdl
│ │ ├── shell.kdl
│ │ ├── visuals.kdl
│ │ ├── window-rule.kdl
│ │ └── binds.kdl
│ └── hosts
│ │ └── pc3
│ │ └── config.kdl
├── kitty
│ ├── .gitignore
│ ├── kitty.conf
│ └── current-theme.conf
├── nvim
│ ├── lazy-lock.json
│ ├── plugins.nix
│ └── init.lua
├── zed
│ └── hosts
│ │ ├── pc3
│ │ ├── keymap.json
│ │ └── settings.json
│ │ └── work-macbookpro
│ │ ├── keymap.json
│ │ └── settings.json
├── npm
│ └── npmrc
├── zsh
│ ├── .gitignore
│ ├── .zshenv
│ ├── home_zshenv
│ └── .zshrc
├── fish
│ ├── functions
│ │ ├── ipv4.fish
│ │ ├── ipv6.fish
│ │ ├── shell.fish
│ │ ├── pathify.fish
│ │ ├── poke.fish
│ │ ├── clean-store.fish
│ │ ├── mvcd.fish
│ │ ├── mkblueprint.fish
│ │ ├── paths.fish
│ │ ├── announce.fish
│ │ ├── sayresult.fish
│ │ ├── ls.fish
│ │ ├── abbred.fish
│ │ ├── fish_greeting.fish
│ │ ├── nix.fish
│ │ ├── fish_job_summary.fish
│ │ ├── trash.fish
│ │ ├── fish_user_key_bindings.fish
│ │ └── run.fish
│ ├── .gitignore
│ ├── completions
│ │ └── run.fish
│ ├── conf.d
│ │ ├── init.fish
│ │ ├── ghostty-shell-integration.fish
│ │ ├── man.fish
│ │ ├── options.fish
│ │ ├── 999_fix_path.fish
│ │ ├── eza.fish
│ │ ├── abbreviations.fish
│ │ └── environment.fish
│ └── config.fish
├── jj
│ └── config.toml
├── direnv
│ └── direnv.toml
├── git
│ ├── ignore
│ └── config
├── tmux
│ └── tmux.conf
└── ghostty
│ ├── config
│ ├── os-config-linux
│ └── os-config-darwin
├── .envrc
├── .gitignore
├── hosts
├── macmini
│ ├── id_ed25519.pub
│ ├── users
│ │ └── robert
│ │ │ ├── id_ed25519.pub
│ │ │ └── home-configuration.nix
│ └── darwin-configuration.nix
├── homeserver1
│ ├── id_ed25519.pub
│ ├── clouddns-config.json.age
│ ├── tailscale-homeserver1.age
│ ├── minecraft
│ │ ├── default.nix
│ │ └── servers
│ │ │ └── family.nix
│ ├── clouddns.nix
│ ├── vrising.nix
│ ├── disko.nix
│ └── configuration.nix
├── macbook-air
│ ├── id_ed25519.pub
│ └── users
│ │ └── robert
│ │ ├── id_ed25519.pub
│ │ └── home-configuration.nix
├── pc3
│ └── users
│ │ ├── work
│ │ ├── gitconfig.age
│ │ ├── id_ed25519.pub
│ │ └── home-configuration.nix
│ │ └── robert
│ │ ├── id_ed25519.pub
│ │ └── home-configuration.nix
├── work-macbookpro
│ ├── users
│ │ └── robert
│ │ │ ├── id_ed25519.pub
│ │ │ ├── work-gitconfig.age
│ │ │ └── home-configuration.nix
│ └── darwin-configuration.nix
└── legacy
│ └── users
│ └── robert
│ └── home-configuration.nix
├── modules
├── common
│ └── nixpkgs-unstable.nix
├── darwin
│ ├── system-defaults.nix
│ └── fish-environment.nix
└── home
│ ├── my-config.nix
│ ├── my-programs-neovim.nix
│ └── my-programs-fish.nix
├── packages
├── homeserver1-install.nix
├── rcon-cli.nix
├── helix-clo4.nix
├── ccase.nix
├── mrpack-install.nix
├── run.nix
├── helix.nix
└── schemat.nix
├── users
└── robert
│ ├── darwin.nix
│ ├── work-configuration.nix
│ └── home-configuration.nix
├── .github
└── workflows
│ └── update-flake.yml
├── devshell.nix
├── secrets.nix
├── LICENSE
├── flake.nix
├── README.md
├── run.fish
└── flake.lock
/config/helix/helix.scm:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/config/helix/.gitignore:
--------------------------------------------------------------------------------
1 | lib
2 |
--------------------------------------------------------------------------------
/config/niri/.gitignore:
--------------------------------------------------------------------------------
1 | !*.kdl
2 |
--------------------------------------------------------------------------------
/config/kitty/.gitignore:
--------------------------------------------------------------------------------
1 | dank-*.conf
2 |
--------------------------------------------------------------------------------
/config/nvim/lazy-lock.json:
--------------------------------------------------------------------------------
1 | {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/config/zed/hosts/pc3/keymap.json:
--------------------------------------------------------------------------------
1 | []
2 |
--------------------------------------------------------------------------------
/.envrc:
--------------------------------------------------------------------------------
1 | watch_file devshell.nix
2 | use flake
3 |
--------------------------------------------------------------------------------
/config/npm/npmrc:
--------------------------------------------------------------------------------
1 | prefix = ${HOME}/.local
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | result
2 | result-*
3 | .direnv
4 | _*
5 |
--------------------------------------------------------------------------------
/config/zed/hosts/work-macbookpro/keymap.json:
--------------------------------------------------------------------------------
1 | []
2 |
--------------------------------------------------------------------------------
/config/zsh/.gitignore:
--------------------------------------------------------------------------------
1 | .zcompdump*
2 | .zsh_history
3 |
--------------------------------------------------------------------------------
/config/fish/functions/ipv4.fish:
--------------------------------------------------------------------------------
1 | function ipv4
2 | curl -4 https://api.ipify.org
3 | end
4 |
--------------------------------------------------------------------------------
/config/fish/functions/ipv6.fish:
--------------------------------------------------------------------------------
1 | function ipv6
2 | curl -6 https://api6.ipify.org
3 | end
4 |
--------------------------------------------------------------------------------
/config/niri/private.kdl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clo4/nix-dotfiles/HEAD/config/niri/private.kdl
--------------------------------------------------------------------------------
/config/niri/config.kdl:
--------------------------------------------------------------------------------
1 | include "common/config.kdl"
2 | include "host/config.kdl"
3 | include "private.kdl"
4 |
--------------------------------------------------------------------------------
/hosts/macmini/id_ed25519.pub:
--------------------------------------------------------------------------------
1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL7jCIKZyfKrAYIDzsiBOLf5/JgSRtNtXeBOjIG3CVNi
2 |
--------------------------------------------------------------------------------
/hosts/homeserver1/id_ed25519.pub:
--------------------------------------------------------------------------------
1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL3hbWmccdfoYplE/PZ251CMUrCiTJJd9ON37/RR2JkP
2 |
--------------------------------------------------------------------------------
/hosts/macbook-air/id_ed25519.pub:
--------------------------------------------------------------------------------
1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAcpkHfo1d9l7WDahmib2sBuhdsSpllCGPDiBjcq757d
2 |
--------------------------------------------------------------------------------
/config/fish/functions/shell.fish:
--------------------------------------------------------------------------------
1 | function shell
2 | NIXPKGS_ALLOW_UNFREE=1 nix shell --impure nixpkgs#$argv
3 | end
4 |
--------------------------------------------------------------------------------
/hosts/pc3/users/work/gitconfig.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clo4/nix-dotfiles/HEAD/hosts/pc3/users/work/gitconfig.age
--------------------------------------------------------------------------------
/config/fish/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything except for directories and .fish files
2 | *
3 | !*/
4 | !*.fish
5 | !.gitignore
6 |
--------------------------------------------------------------------------------
/config/fish/functions/pathify.fish:
--------------------------------------------------------------------------------
1 | function pathify
2 | for p in $argv
3 | set --path $p $$p
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/hosts/macmini/users/robert/id_ed25519.pub:
--------------------------------------------------------------------------------
1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILAWNw2z4swcjAkPPwO1evXclnlIYta1jaJKPKWsrOoo
2 |
--------------------------------------------------------------------------------
/hosts/macbook-air/users/robert/id_ed25519.pub:
--------------------------------------------------------------------------------
1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO4GO2AYXejanlgjDDg3C9K2IG8WhB+Bp8up785b3IP5
2 |
--------------------------------------------------------------------------------
/hosts/pc3/users/robert/id_ed25519.pub:
--------------------------------------------------------------------------------
1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFQZ33imCWLzYj9ZsMOs+SqnNNQVLhsrs1laY5tImq8D robert@pc3
2 |
--------------------------------------------------------------------------------
/hosts/pc3/users/work/id_ed25519.pub:
--------------------------------------------------------------------------------
1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBpv/AINUV6zJZDtJAn13pbNJEz7ImAs52nTOX0+JgBQ work@pc3
2 |
--------------------------------------------------------------------------------
/hosts/homeserver1/clouddns-config.json.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clo4/nix-dotfiles/HEAD/hosts/homeserver1/clouddns-config.json.age
--------------------------------------------------------------------------------
/hosts/homeserver1/tailscale-homeserver1.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clo4/nix-dotfiles/HEAD/hosts/homeserver1/tailscale-homeserver1.age
--------------------------------------------------------------------------------
/config/fish/functions/poke.fish:
--------------------------------------------------------------------------------
1 | function poke
2 | for arg in $argv
3 | mkdir -p (path dirname $arg); and touch $arg
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/config/jj/config.toml:
--------------------------------------------------------------------------------
1 | "$schema" = "https://jj-vcs.github.io/jj/latest/config-schema.json"
2 |
3 | [user]
4 | name = "clo4"
5 | email = "robert@clo4.net"
6 |
--------------------------------------------------------------------------------
/config/niri/common/config.kdl:
--------------------------------------------------------------------------------
1 | include "binds.kdl"
2 | include "input.kdl"
3 | include "visuals.kdl"
4 | include "window-rule.kdl"
5 | include "shell.kdl"
6 |
--------------------------------------------------------------------------------
/hosts/work-macbookpro/users/robert/id_ed25519.pub:
--------------------------------------------------------------------------------
1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHw37HYeSngFDNoNaiLo0rZcwA/BjHNebH8nzOPQ3LTs robert@work-macbookpro
2 |
--------------------------------------------------------------------------------
/hosts/work-macbookpro/users/robert/work-gitconfig.age:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clo4/nix-dotfiles/HEAD/hosts/work-macbookpro/users/robert/work-gitconfig.age
--------------------------------------------------------------------------------
/config/fish/functions/clean-store.fish:
--------------------------------------------------------------------------------
1 | function clean-store
2 | announce nix store gc --verbose
3 | echo
4 | announce nix store optimise --verbose
5 | end
6 |
--------------------------------------------------------------------------------
/config/fish/functions/mvcd.fish:
--------------------------------------------------------------------------------
1 | function mvcd
2 | set cwd $PWD
3 | set newcwd $argv[1]
4 | cd ..
5 | mv $cwd $newcwd
6 | cd $newcwd
7 | pwd
8 | end
9 |
--------------------------------------------------------------------------------
/config/fish/functions/mkblueprint.fish:
--------------------------------------------------------------------------------
1 | function mkblueprint
2 | nix flake init -t blueprint
3 | nix flake update
4 | git init
5 | git add .
6 | direnv allow
7 | end
8 |
--------------------------------------------------------------------------------
/config/niri/common/input.kdl:
--------------------------------------------------------------------------------
1 | input {
2 | keyboard {
3 | xkb {
4 | layout "us,us(colemak_dh)"
5 | }
6 |
7 | numlock
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/config/direnv/direnv.toml:
--------------------------------------------------------------------------------
1 | [global]
2 | load_dotenv = true
3 | strict_env = true
4 | warn_timeout = 0
5 |
6 | [whitelist]
7 | prefix = ["~/Developer/clo4", "~/Developer/Work", "~/Repos"]
8 |
--------------------------------------------------------------------------------
/config/git/ignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.swp
3 | result
4 | result-*
5 | .DS_Store
6 | .direnv
7 | /.helix
8 | .flake
9 | .pkgs
10 | .jj
11 | /.envrc
12 |
13 | **/.claude/settings.local.json
14 |
--------------------------------------------------------------------------------
/config/fish/functions/paths.fish:
--------------------------------------------------------------------------------
1 | function paths
2 | set --append --local argv PATH
3 | set --local --path paths $$argv[1]
4 | for p in $paths
5 | echo $p
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/config/fish/completions/run.fish:
--------------------------------------------------------------------------------
1 | set -l commands (run 2>/dev/null)
2 | complete -c run -s h -l help -fx
3 | complete -c run -n "not __fish_seen_subcommand_from $commands" -fa "(__COMPLETE_RUN_DESCRIPTIONS=1 run 2>/dev/null)"
4 |
--------------------------------------------------------------------------------
/modules/common/nixpkgs-unstable.nix:
--------------------------------------------------------------------------------
1 | { inputs, pkgs, ... }:
2 | {
3 | _module.args.pkgs' = import inputs.nixpkgs-unstable {
4 | system = pkgs.stdenv.hostPlatform.system;
5 | config.allowUnfree = true;
6 | };
7 | }
8 |
--------------------------------------------------------------------------------
/config/fish/functions/announce.fish:
--------------------------------------------------------------------------------
1 | function announce
2 | set colored_command (string escape -- $argv | string join ' ' | fish_indent --ansi)
3 | echo "$(set_color magenta)~~>$(set_color normal) $colored_command"
4 | $argv
5 | end
6 |
--------------------------------------------------------------------------------
/config/zsh/.zshenv:
--------------------------------------------------------------------------------
1 | # Executing hm-session-vars is designed to be idempotent, safe to execute
2 | # multiple times
3 | local session_vars="$HOME/.local/share/zsh/hm-session-vars.sh"
4 | if [[ -f $session_vars && -r $session_vars ]]; then
5 | source $session_vars
6 | fi
7 |
--------------------------------------------------------------------------------
/config/kitty/kitty.conf:
--------------------------------------------------------------------------------
1 | # clear_all_shortcuts
2 |
3 | # map command+t new_tab_with_cwd
4 | # map command+n new_os_window
5 |
6 | # map control+t next_tab
7 | # map control+shift+t previous_tab
8 |
9 | # map command+d new_window_with_cwd
10 |
11 | include current-theme.conf
12 |
--------------------------------------------------------------------------------
/config/fish/conf.d/init.fish:
--------------------------------------------------------------------------------
1 | function __source
2 | # Using type instead of command allows for functions too
3 | if type -q $argv[1]
4 | $argv | source
5 | end
6 | end
7 |
8 | __source zoxide init fish
9 | __source direnv hook fish
10 | __source fzf --fish
11 |
12 | functions -e __source
13 |
--------------------------------------------------------------------------------
/packages/homeserver1-install.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | pname,
4 | perSystem,
5 | flake,
6 | }:
7 | if pkgs.stdenv.hostPlatform.system != "x86_64-linux" then
8 | pkgs.emptyFile
9 | else
10 | pkgs.writeShellScriptBin pname ''
11 | ${perSystem.disko.disko-install}/bin/disko-install --flake path:${flake}#homeserver1 --disk main /dev/nvme0n1
12 | ''
13 |
--------------------------------------------------------------------------------
/hosts/legacy/users/robert/home-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | flake,
4 | config,
5 | ...
6 | }:
7 | {
8 | imports = [ "${flake}/users/robert/home-configuration.nix" ];
9 |
10 | home.stateVersion = "25.05";
11 |
12 | # Config fails to build without this.
13 | nix.package = pkgs.nix;
14 |
15 | my.config.directory = "${config.home.homeDirectory}/Developer/clo4/nix-dotfiles";
16 | }
17 |
--------------------------------------------------------------------------------
/hosts/homeserver1/minecraft/default.nix:
--------------------------------------------------------------------------------
1 | { config, ... }:
2 | {
3 | imports = [
4 | ./servers/family.nix
5 | ];
6 | assertions = [
7 | {
8 | assertion = config.virtualisation.oci-containers.backend == "podman";
9 | message = ''
10 | You can't change to Docker without also updating minecraft/servers/*.nix as these
11 | files explicitly invoke podman.
12 | '';
13 | }
14 | ];
15 | }
16 |
--------------------------------------------------------------------------------
/config/tmux/tmux.conf:
--------------------------------------------------------------------------------
1 | set -g default-terminal "tmux-256color"
2 | set -g base-index 1
3 | setw -g pane-base-index 1
4 | set -g status-keys emacs
5 | set -g mode-keys emacs
6 | set -g mouse on
7 | set -g focus-events off
8 | setw -g aggressive-resize off
9 | setw -g clock-mode-style 12
10 | set -s escape-time 50
11 | set -g history-limit 10000
12 |
13 | set-option -sa terminal-overrides ",tmux-256color:RGB"
14 |
--------------------------------------------------------------------------------
/users/robert/darwin.nix:
--------------------------------------------------------------------------------
1 | { pkgs, lib, ... }:
2 | {
3 | config = lib.mkIf pkgs.stdenv.isDarwin {
4 | # Notably, in my configuration, the *value* of this variable is never checked.
5 | # The only important thing is whether or not it's set at all.
6 | home.sessionVariables.IS_DARWIN = "";
7 |
8 | # On macOS, this is intended to suppress the login welcome message.
9 | home.file.".hushlogin".source = pkgs.emptyFile;
10 | };
11 | }
12 |
--------------------------------------------------------------------------------
/config/fish/functions/sayresult.fish:
--------------------------------------------------------------------------------
1 | function sayresult
2 | announce $argv
3 | set -l cmd_status $status
4 |
5 | if not command -q say
6 | return $cmd_status
7 | end
8 |
9 | if test $CMD_DURATION -lt 3000
10 | return $cmd_status
11 | end
12 |
13 | if test $cmd_status -eq 0
14 | say "Command succeeded"
15 | else
16 | say "Command failed"
17 | end
18 |
19 | return $cmd_status
20 | end
21 |
--------------------------------------------------------------------------------
/packages/rcon-cli.nix:
--------------------------------------------------------------------------------
1 | { pkgs }:
2 | pkgs.buildGoModule rec {
3 | pname = "rcon-cli";
4 | # current as of 2025-02-20
5 | version = "1.6.11";
6 |
7 | src = pkgs.fetchFromGitHub {
8 | owner = "itzg";
9 | repo = "rcon-cli";
10 | rev = "${version}";
11 | hash = "sha256-RfcmAF2lj/huQNxxQFS1GUsqCS1eVfF5jTpXVGvykFE=";
12 | };
13 |
14 | vendorHash = "sha256-b9mWhrsHyXPhUm/9v9Oj72O4VEnlYMnieJiahE/9k1k=";
15 |
16 | doCheck = false;
17 | }
18 |
--------------------------------------------------------------------------------
/config/ghostty/config:
--------------------------------------------------------------------------------
1 | scrollback-limit = 100000000
2 | window-padding-color = extend
3 | font-family = RobotoMono Nerd Font
4 | font-size = 12
5 | window-height = 60
6 | window-width = 170
7 | window-theme = auto
8 | theme = Gruvbox Dark
9 | auto-update-channel = tip
10 |
11 | # This file will be linked to the appropriate os-config file depending on the current platform
12 | config-file = os-config
13 |
14 | # Keybind for Claude Code
15 | keybind = shift+enter=text:\n
16 |
--------------------------------------------------------------------------------
/config/zed/hosts/work-macbookpro/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ui_font_size": 14.0,
3 | "buffer_font_size": 14.0,
4 | "buffer_font_weight": 400.0,
5 | "ui_font_weight": 390.0,
6 | "icon_theme": "JetBrains New UI Icons (Dark)",
7 | "features": {
8 | "edit_prediction_provider": "zed"
9 | },
10 | "theme": "Fleet Dark",
11 | "vim_mode": true,
12 | "buffer_font_family": "RobotoMono Nerd Font",
13 | "ui_font_family": "RobotoMono Nerd Font",
14 | "format_on_save": "off"
15 | }
16 |
--------------------------------------------------------------------------------
/config/fish/conf.d/ghostty-shell-integration.fish:
--------------------------------------------------------------------------------
1 | # If using distrobox, it's quite possible that the variables could be set
2 | # but inaccessible.
3 | if set -q GHOSTTY_RESOURCES_DIR GHOSTTY_BIN_DIR
4 | and test -d $GHOSTTY_RESOURCES_DIR -a -d $GHOSTTY_BIN_DIR
5 | source "$GHOSTTY_RESOURCES_DIR/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish"
6 | set --append fish_complete_path "$GHOSTTY_RESOURCES_DIR/shell-integration/fish/vendor_completions.d"
7 | set --append PATH $GHOSTTY_BIN_DIR
8 | end
9 |
--------------------------------------------------------------------------------
/config/fish/conf.d/man.fish:
--------------------------------------------------------------------------------
1 | # This function can't be lazy loaded because it needs to wrap another fish function,
2 | # not a builtin or command. In a normal fish distribution, `man` is guaranteed to be
3 | # a function.
4 |
5 | functions --copy man __fish_man
6 | functions --erase man
7 |
8 | function man --wraps man
9 | if not set -q MANWIDTH
10 | set --function MANWIDTH 80
11 | end
12 | set --function --export MANWIDTH (math "min($MANWIDTH, $(tput cols))")
13 | __fish_man --no-justification $argv
14 | end
15 |
--------------------------------------------------------------------------------
/packages/helix-clo4.nix:
--------------------------------------------------------------------------------
1 | # This is a helix setup on either the latest (or close to the latest) version of
2 | # the plugin system fork, which is usually quite stable and only a couple of weeks
3 | # behind upstream. This wraps my existing Helix configuration, allowing me to use
4 | # it on all systems.
5 | {
6 | perSystem,
7 | pkgs,
8 | flake,
9 | }:
10 | pkgs.writeShellScriptBin "hx" ''
11 | export PATH=${perSystem.self.ccase}/bin:$PATH
12 | exec ${perSystem.self.helix}/bin/hx -c ${flake}/config/helix/config.toml "$@"
13 | ''
14 |
--------------------------------------------------------------------------------
/config/zsh/home_zshenv:
--------------------------------------------------------------------------------
1 | local session_vars="$HOME/.local/share/zsh/hm-session-vars.sh"
2 | if [[ -f $session_vars && -r $session_vars ]]; then
3 | source $session_vars
4 | fi
5 |
6 | # ~/.zshenv is always sourced first, so when this file sets the ZDOTDIR,
7 | # it finishes executing and ZSH moves on to the next item in the source order.
8 | # We need to manually source the appropriate file in the correct directory, if
9 | # it exists and is readable.
10 | if [[ -n $ZDOTDIR && -f $ZDOTDIR/.zshenv && -r $ZDOTDIR/.zshenv ]]; then
11 | source $ZDOTDIR/.zshenv
12 | fi
13 |
--------------------------------------------------------------------------------
/hosts/macmini/users/robert/home-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | flake,
3 | config,
4 | ...
5 | }:
6 | {
7 | home.stateVersion = "24.11";
8 |
9 | imports = [ "${flake}/users/robert/home-configuration.nix" ];
10 |
11 | home.sessionVariables = {
12 | # My fish configuration uses this to check whether it should check if
13 | # the Touch ID PAM module is enabled. See: config/fish/functions/fish_greeting.fish
14 | FISH_GREETING_CHECK_SUDO_TOUCHID = "1";
15 | };
16 |
17 | my.config.directory = "${config.home.homeDirectory}/Developer/clo4/nix-dotfiles";
18 | }
19 |
--------------------------------------------------------------------------------
/config/git/config:
--------------------------------------------------------------------------------
1 | [init]
2 | defaultBranch = "main"
3 |
4 | [push]
5 | autoSetupRemote = true
6 |
7 | [user]
8 | email = robert@clo4.net
9 | name = "clo4"
10 |
11 | [alias]
12 | root = rev-parse --show-toplevel
13 |
14 | [includeIf "gitdir:~/Developer/Work/**"]
15 | path = ~/Developer/Work/.gitconfig
16 |
17 | [includeIf "gitdir:~/Repos/Work/**"]
18 | path = ~/Repos/Work/.gitconfig
19 |
20 | [core]
21 | pager = delta
22 |
23 | [interactive]
24 | diffFilter = delta --color-only
25 |
26 | [delta]
27 | navigate = true
28 |
29 | [merge]
30 | conflictStyle = zdiff3
31 |
--------------------------------------------------------------------------------
/config/fish/functions/ls.fish:
--------------------------------------------------------------------------------
1 | # When used interactively, wraps eza with some nice coloring and good default options.
2 | # When given options or used in a command substitution, delegates to system ls.
3 |
4 | function ls --wraps ls
5 | if string match --quiet -- '-*' $argv; or not isatty stdout
6 | command ls $argv
7 | return
8 | end
9 |
10 | set cmd eza --long --group-directories-first --sort=Name --follow-symlinks --git --almost-all
11 | if path is -d .git
12 | and not path is -d node_modules
13 | $cmd --total-size $argv
14 | else
15 | $cmd $argv
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/config/zed/hosts/pc3/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ui_font_size": 14.0,
3 | "buffer_font_size": 14.0,
4 | "buffer_font_weight": 400.0,
5 | "ui_font_weight": 390.0,
6 | "icon_theme": "JetBrains New UI Icons (Dark)",
7 | "features": {
8 | "edit_prediction_provider": "zed"
9 | },
10 | "theme": "Fleet Dark",
11 | "vim_mode": true,
12 | "buffer_font_family": "RobotoMono Nerd Font",
13 | "ui_font_family": "RobotoMono Nerd Font",
14 | "format_on_save": "off",
15 | "languages": {
16 | "Go": {
17 | "format_on_save": "on"
18 | },
19 | "Go Mod": {
20 | "format_on_save": "on"
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/config/niri/hosts/pc3/config.kdl:
--------------------------------------------------------------------------------
1 | output "Samsung Electric Company S90D 0x01000E00" {
2 | mode "3840x2160@120.000"
3 | scale 2
4 | }
5 |
6 | input {
7 | touchpad {
8 | natural-scroll
9 |
10 | // I'm using a Magic Trackpad, which seems to scroll exactly twice as fast as is reasonable by default.
11 | scroll-factor 0.5
12 | }
13 |
14 | mouse {
15 | accel-profile "flat"
16 | accel-speed -0.2
17 | }
18 | }
19 |
20 | // Testing if this will help with the occasional flicker I get
21 | debug {
22 | wait-for-frame-completion-before-queueing
23 | keep-max-bpc-unchanged
24 | }
25 |
--------------------------------------------------------------------------------
/hosts/macbook-air/users/robert/home-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | flake,
4 | config,
5 | ...
6 | }:
7 | {
8 | imports = [ "${flake}/users/robert/home-configuration.nix" ];
9 |
10 | home.stateVersion = "24.05";
11 |
12 | my.config.directory = "${config.home.homeDirectory}/.config/nix-dotfiles";
13 |
14 | # Config fails to build without this.
15 | nix.package = pkgs.nix;
16 |
17 | # FIXME: This isn't working, need to figure out why
18 | targets.darwin.currentHostDefaults = {
19 | NSGlobalDomain = {
20 | NSUserKeyEquivalents = {
21 | "\\033Window\\033Zoom" = "@$\\\\U21a9";
22 | };
23 | };
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/packages/ccase.nix:
--------------------------------------------------------------------------------
1 | { pname, pkgs, ... }:
2 | pkgs.rustPlatform.buildRustPackage {
3 | inherit pname;
4 | version = "0.4.1";
5 | src = pkgs.fetchFromGitHub {
6 | owner = "rutrum";
7 | repo = pname;
8 | rev = "7ca56557d0cc69641e0d0c5ae9370c48f4cce09d";
9 | hash = "sha256-TQJkvANms/5Mzh1J4qsEYOrlML17dVv7MYEoN4Z/gm0=";
10 | };
11 | cargoHash = "sha256-RLjwLr1IF1T3QR5t8i2dGEWs72YY49Ib1l8QlaFkcqg=";
12 |
13 | meta = {
14 | description = "Command line interface to convert strings into any case";
15 | homepage = "https://github.com/rutrum/ccase";
16 | license = pkgs.lib.licenses.mit;
17 | maintainers = [ ];
18 | };
19 | }
20 |
--------------------------------------------------------------------------------
/packages/mrpack-install.nix:
--------------------------------------------------------------------------------
1 | { pkgs }:
2 | # Requires specifying the go version because it uses the 'tool' directive.
3 | # At the time of writing, the version used by buildGoModule is 1.23.5, but the
4 | # latest available is 1.24
5 | pkgs.buildGo124Module rec {
6 | pname = "mrpack-install";
7 | # current as of 2025-02-20
8 | version = "v0.20.0-beta";
9 |
10 | src = pkgs.fetchFromGitHub {
11 | owner = "nothub";
12 | repo = "mrpack-install";
13 | rev = "${version}";
14 | hash = "sha256-vMueeK9iLr4W7LFJ+FQxATpB4s7QXazFVOmZ4SQ9B+M=";
15 | };
16 |
17 | vendorHash = "sha256-GA3dbl+Rld6xlW5is3SINEhYIjfm00Sy8B51hgOcfCw=";
18 |
19 | doCheck = false;
20 | }
21 |
--------------------------------------------------------------------------------
/config/ghostty/os-config-linux:
--------------------------------------------------------------------------------
1 | window-padding-x = 6
2 | window-padding-y = 4
3 |
4 | font-size = 8
5 | gtk-toolbar-style = raised-border
6 | gtk-titlebar-style = tabs
7 | gtk-wide-tabs = false
8 |
9 |
10 | # I'm trying to reserve ctrl+shift for window or tab-level stuff,
11 | # and use ctrl+alt for surfaces.
12 |
13 | keybind = ctrl+shift+r=prompt_surface_title
14 | keybind = ctrl+shift+comma=reload_config
15 |
16 | keybind = ctrl+alt+d=new_split:right
17 | keybind = ctrl+shift+d=new_split:down
18 |
19 | keybind = ctrl+alt+w=close_surface
20 |
21 | keybind = page_up=scroll_page_fractional:-0.5
22 | keybind = page_down=scroll_page_fractional:0.5
23 |
24 | keybind = ctrl+alt+s=write_scrollback_file:paste
25 |
--------------------------------------------------------------------------------
/.github/workflows/update-flake.yml:
--------------------------------------------------------------------------------
1 | name: "Update nightly"
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: "0 18 * * *"
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4
13 | - uses: cachix/install-nix-action@v27
14 | with:
15 | github_access_token: ${{ secrets.GITHUB_TOKEN }}
16 |
17 | - name: Update flake.lock
18 | run: |
19 | git config --global user.name 'github-actions[bot]'
20 | git config --global user.email 'github-actions[bot]@users.noreply.github.com'
21 | nix flake update nixpkgs-unstable --commit-lock-file
22 |
23 | - name: Push to main
24 | run: git push
25 |
--------------------------------------------------------------------------------
/packages/run.nix:
--------------------------------------------------------------------------------
1 | { pkgs, flake }:
2 | pkgs.stdenv.mkDerivation {
3 | name = "run";
4 |
5 | src = flake;
6 |
7 | buildInputs = [ pkgs.fish ];
8 |
9 | installPhase = ''
10 | mkdir -p $out/bin
11 | echo "#!${pkgs.fish}/bin/fish" > $out/bin/run
12 | echo "" >> $out/bin/run
13 | cat config/fish/functions/run.fish >> $out/bin/run
14 | echo "" >> $out/bin/run
15 | echo "run \$argv" >> $out/bin/run
16 | chmod +x $out/bin/run
17 |
18 | mkdir -p $out/share/fish/vendor_completions.d
19 | cp config/fish/completions/run.fish $out/share/fish/vendor_completions.d/
20 |
21 | mkdir -p $out/share/fish/vendor_functions.d
22 | cp config/fish/functions/run.fish $out/share/fish/vendor_functions.d/
23 | '';
24 | }
25 |
--------------------------------------------------------------------------------
/packages/helix.nix:
--------------------------------------------------------------------------------
1 | # This is a helix setup on either the latest (or close to the latest) version of
2 | # the plugin system fork, which is usually quite stable and only a couple of weeks
3 | # behind upstream. This wraps my existing Helix configuration, allowing me to use
4 | # it on all systems.
5 | {
6 | perSystem,
7 | pkgs,
8 | flake,
9 | }:
10 | let
11 | steelWithLsp = perSystem.steel.default.overrideAttrs (oldAttrs: {
12 | cargoBuildFlags = "-p cargo-steel-lib -p steel-interpreter -p steel-language-server";
13 | });
14 | in
15 | pkgs.writeShellScriptBin "hx" ''
16 | export PATH=${steelWithLsp}/bin:$PATH
17 | export STEEL_HOME=${perSystem.helix.helix-cogs}
18 | export STEEL_LSP_HOME=${perSystem.helix.helix-cogs}/steel-language-server
19 | exec ${perSystem.helix.default}/bin/hx -c ${flake}/config/helix/config.toml "$@"
20 | ''
21 |
--------------------------------------------------------------------------------
/packages/schemat.nix:
--------------------------------------------------------------------------------
1 | { pname, pkgs, ... }:
2 | pkgs.rustPlatform.buildRustPackage {
3 | inherit pname;
4 | version = "0.2.14";
5 |
6 | src = pkgs.fetchFromGitHub {
7 | owner = "raviqqe";
8 | repo = pname;
9 | rev = "ea9bcb6545214a70ce93a5c49c229f430ab58c2e"; # as of 2025-01-22
10 | hash = "sha256-h8JjT1cspfpuLII2hBMVRiPMDoC2hV8e8SVykWVkkys=";
11 | };
12 |
13 | # FIXME: This should really be removed in favor of using a proper nightly
14 | # toolchain, but it works for now. Might need to use rust-overlay?
15 | RUSTC_BOOTSTRAP = true;
16 |
17 | cargoHash = "sha256-ZKy+voOLROK1S5YD8b8i5/pXZXnQn2ZBarFsUjYThPY=";
18 |
19 | meta = {
20 | description = "Code formatter for Scheme, Lisp, and any S-expressions";
21 | homepage = "github.com/raviqqe/schemat";
22 | license = pkgs.lib.licenses.unlicense;
23 | maintainers = [ ];
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/devshell.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | inputs,
4 | perSystem,
5 | }:
6 | pkgs.mkShellNoCC {
7 | packages = [
8 | perSystem.home-manager.default
9 | perSystem.agenix.default
10 | # In my normal shell, run is a function. In any other shell,
11 | # or on a system without my configuration, it will instead
12 | # be the "packaged" version of that function.
13 | perSystem.self.run
14 | pkgs.nixos-rebuild
15 | pkgs.nixos-anywhere
16 | pkgs.nixd
17 | pkgs.taplo
18 | pkgs.age
19 | pkgs.deno
20 | ]
21 | ++ pkgs.lib.optional pkgs.stdenv.isDarwin [
22 | perSystem.nix-darwin.default
23 | ];
24 | # buildInputs =
25 | # [ ]
26 | # ++ pkgs.lib.optional pkgs.stdenv.isDarwin [
27 | # pkgs.clang
28 | # pkgs.darwin.cctools # I've been burned in the past by not having this
29 | # ];
30 | shellHook = ''
31 | export IN_NIX_CONFIG_DEVSHELL=1
32 | '';
33 | }
34 |
--------------------------------------------------------------------------------
/modules/darwin/system-defaults.nix:
--------------------------------------------------------------------------------
1 | {
2 | system.defaults.NSGlobalDomain = {
3 | ApplePressAndHoldEnabled = false;
4 | AppleShowAllExtensions = true;
5 | NSAutomaticCapitalizationEnabled = false;
6 | NSAutomaticPeriodSubstitutionEnabled = false;
7 | NSAutomaticSpellingCorrectionEnabled = false;
8 | NSWindowShouldDragOnGesture = true;
9 | InitialKeyRepeat = 15;
10 | KeyRepeat = 2;
11 | # Explicitly enabling media keys because the media keycodes themselves are
12 | # used for some shortcuts
13 | "com.apple.keyboard.fnState" = false;
14 | };
15 |
16 | system.defaults.dock.autohide = true;
17 |
18 | system.defaults.finder = {
19 | ShowPathbar = true;
20 | # This magic string makes it search the current folder by default
21 | FXDefaultSearchScope = "SCcf";
22 | # Use the column view by default (the obviously correct and best view)
23 | FXPreferredViewStyle = "clmv";
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/hosts/pc3/users/robert/home-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | flake,
4 | config,
5 | inputs,
6 | ...
7 | }:
8 | {
9 | imports = [
10 | "${flake}/users/robert/home-configuration.nix"
11 | inputs.agenix.homeManagerModules.default
12 | ];
13 |
14 | home.stateVersion = "25.05";
15 |
16 | # Config fails to build without this.
17 | nix.package = pkgs.nix;
18 |
19 | my.config.directory = "${config.home.homeDirectory}/Developer/clo4/nix-dotfiles";
20 | my.config.source = {
21 | ".config/zed" = "config/zed/hosts/pc3";
22 |
23 | ".config/niri/config.kdl" = "config/niri/config.kdl";
24 | ".config/niri/common" = "config/niri/common";
25 | ".config/niri/host" = "config/niri/hosts/pc3";
26 | };
27 |
28 | age.secrets.niri-private = {
29 | file = "${flake}/config/niri/private.kdl";
30 | path = "$HOME/.config/niri/private.kdl";
31 | };
32 |
33 | home.packages = [
34 | pkgs.systemd-lsp
35 | ];
36 | }
37 |
--------------------------------------------------------------------------------
/hosts/work-macbookpro/users/robert/home-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | flake,
3 | config,
4 | pkgs,
5 | inputs,
6 | ...
7 | }:
8 | {
9 | home.stateVersion = "24.11";
10 |
11 | imports = [
12 | "${flake}/users/robert/work-configuration.nix"
13 | inputs.agenix.homeManagerModules.default
14 | ];
15 |
16 | my.config.directory = "${config.home.homeDirectory}/Developer/clo4/nix-dotfiles";
17 | my.config.source = {
18 | ".config/zed" = "config/zed/hosts/work-macbookpro";
19 | };
20 |
21 | age.secrets.work-gitconfig = {
22 | file = ./work-gitconfig.age;
23 | # This can't be inside my ~/.config/git directory because the entire
24 | # directory is symlinked non-recursively, and we need to symlink the
25 | # decrypted copy of this file to this path at login-time.
26 | path = "$HOME/Developer/Work/.gitconfig";
27 | };
28 |
29 | home.sessionVariables = {
30 | FISH_GREETING_CHECK_SUDO_TOUCHID = "1";
31 | };
32 | }
33 |
--------------------------------------------------------------------------------
/config/fish/functions/abbred.fish:
--------------------------------------------------------------------------------
1 | function abbred
2 | set abbrfile ~/.config/fish/conf.d/abbreviations.fish
3 | pushd ~/.config/fish
4 | # If $EDITOR is set by basically anything other than fish,
5 | # it will be a single string that could contain multiple space-separated
6 | # arguments. Using 'read -at' will tokenize it like the shell would, and
7 | # and store that in a list.
8 | set -l editor vim
9 | if set -q EDITOR
10 | echo -- $EDITOR | read -at editor
11 | end
12 | $editor $abbrfile
13 | set editor_status $status
14 | popd
15 | if test $editor_status -gt 0
16 | echo "$EDITOR exited with a non-zero status code, not executing abbreviations"
17 | return $editor_status
18 | end
19 | echo "removing and reapplying abbreviations"
20 | abbr --erase (abbr --list)
21 | # This file can be sourced unconditionally because it only contains abbreviations,
22 | # which are idempotent if executed with the same name.
23 | source $abbrfile
24 | end
25 |
--------------------------------------------------------------------------------
/modules/darwin/fish-environment.nix:
--------------------------------------------------------------------------------
1 | # This module is loosely translated from
2 | # https://github.com/LnL7/nix-darwin/blob/6a1fdb2a1204c0de038847b601cff5012e162b5e/modules/programs/fish.nix
3 | # to resolve some issues with the environment not being set correctly.
4 | { pkgs, config, ... }:
5 | let
6 | babelfishTranslate =
7 | path: name:
8 | pkgs.runCommand "${name}.fish" { } ''
9 | ${pkgs.babelfish}/bin/babelfish < ${path} > $out
10 | '';
11 | in
12 | {
13 | # Fish from Nix has a hook to setup the environment correctly, which
14 | # requires this exact path: /etc/fish/nixos-env-preinit.fish
15 | # The fish module sets this up correctly, but since we're not using
16 | # that, we have to do it manually.
17 | environment.etc."fish/nixos-env-preinit.fish".text = ''
18 | if [ -z "$__NIX_DARWIN_SET_ENVIRONMENT_DONE" ]
19 | source /etc/fish/setEnvironment.fish
20 | end
21 | '';
22 | environment.etc."fish/setEnvironment.fish".source =
23 | babelfishTranslate config.system.build.setEnvironment "setEnvironment";
24 | }
25 |
--------------------------------------------------------------------------------
/config/fish/conf.d/options.fish:
--------------------------------------------------------------------------------
1 | # All of these options need to be exported for fish_indent to colorize correctly.
2 |
3 | set -gx fish_color_normal normal
4 | set -gx fish_color_command brcyan
5 | set -gx fish_color_keyword magenta
6 | set -gx fish_color_quote normal
7 | set -gx fish_color_redirection yellow --bold
8 | set -gx fish_color_end white
9 | set -gx fish_color_error red
10 | set -gx fish_color_param normal
11 | set -gx fish_color_valid_path cyan
12 | set -gx fish_color_option brblue
13 | set -gx fish_color_comment brblack
14 | set -gx fish_color_selection --background=brblack
15 | set -gx fish_color_operator yellow
16 | set -gx fish_color_escape cyan
17 | set -gx fish_color_autosuggestion brblack
18 |
19 | set -gx fish_color_cwd blue
20 | set -gx fish_color_cwd_root red
21 | set -gx fish_color_user green
22 | set -gx fish_color_host blue
23 | set -gx fish_color_host_remote yellow
24 | set -gx fish_color_status red
25 | set -gx fish_color_cancel red
26 |
27 | set -gx fish_color_search_match --background=brblack
28 | set -gx fish_color_history_current blue
29 |
--------------------------------------------------------------------------------
/config/kitty/current-theme.conf:
--------------------------------------------------------------------------------
1 | ## name: Gruvbox Dark
2 | ## author: Pavel Pertsev
3 | ## license: MIT/X11
4 | ## upstream: https://raw.githubusercontent.com/gruvbox-community/gruvbox-contrib/master/kitty/gruvbox-dark.conf
5 |
6 | selection_foreground #ebdbb2
7 | selection_background #d65d0e
8 |
9 | background #282828
10 | foreground #ebdbb2
11 |
12 | color0 #3c3836
13 | color1 #cc241d
14 | color2 #98971a
15 | color3 #d79921
16 | color4 #458588
17 | color5 #b16286
18 | color6 #689d6a
19 | color7 #a89984
20 | color8 #928374
21 | color9 #fb4934
22 | color10 #b8bb26
23 | color11 #fabd2f
24 | color12 #83a598
25 | color13 #d3869b
26 | color14 #8ec07c
27 | color15 #fbf1c7
28 |
29 | cursor #bdae93
30 | cursor_text_color #665c54
31 |
32 | url_color #458588
33 |
--------------------------------------------------------------------------------
/config/fish/functions/fish_greeting.fish:
--------------------------------------------------------------------------------
1 | function fish_greeting
2 | if test -n "$FISH_GREETING_CHECK_SUDO_TOUCHID"
3 | and not grep -qE '^auth\\s+sufficient\\s+pam_tid\\.so' /etc/pam.d/sudo_local
4 | gum style \
5 | --foreground 3 \
6 | --border-foreground 1 \
7 | --bold \
8 | --border rounded \
9 | --align center \
10 | --width 50 \
11 | --margin "1 3" \
12 | --padding "1 4" \
13 | 'Touch ID will not work with sudo until the system configuration has been reapplied.'
14 | echo
15 | end
16 |
17 | if string match -q "/nix/store/*" "$NIX_CONFIG_DIR"
18 | gum style \
19 | --foreground 3 \
20 | --border-foreground 1 \
21 | --bold \
22 | --border rounded \
23 | --align center \
24 | --width 50 \
25 | --margin "1 3" \
26 | --padding "1 4" \
27 | 'Configuration symlinked to /nix/store.
28 | You should set a config directory.'
29 | echo
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/hosts/pc3/users/work/home-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | perSystem,
4 | flake,
5 | config,
6 | inputs,
7 | ...
8 | }:
9 | {
10 | imports = [
11 | "${flake}/users/robert/work-configuration.nix"
12 | inputs.agenix.homeManagerModules.default
13 | ];
14 |
15 | home.stateVersion = "25.05";
16 |
17 | # Config fails to build without this.
18 | nix.package = pkgs.nix;
19 |
20 | my.config.directory = "${config.home.homeDirectory}/Repos/nix-dotfiles";
21 |
22 | age.secrets.work-gitconfig = {
23 | file = ./gitconfig.age;
24 | path = "$HOME/Repos/Work/.gitconfig";
25 | };
26 |
27 | my.config.source = {
28 | ".config/zed" = "config/zed/hosts/pc3";
29 | ".config/niri/config.kdl" = "config/niri/config.kdl";
30 | ".config/niri/common" = "config/niri/common";
31 | ".config/niri/host" = "config/niri/hosts/pc3";
32 | };
33 |
34 | age.secrets.niri-private = {
35 | file = "${flake}/config/niri/private.kdl";
36 | path = "$HOME/.config/niri/private.kdl";
37 | };
38 |
39 | home.packages = [
40 | perSystem.winapps.winapps
41 | pkgs.systemd-lsp
42 | ];
43 | }
44 |
--------------------------------------------------------------------------------
/config/fish/functions/nix.fish:
--------------------------------------------------------------------------------
1 | function nix --wraps nix
2 | if status is-interactive
3 | and test (count $argv) -gt 0
4 | and not contains -- --command $argv
5 | and not contains -- -c $argv
6 | and not contains -- --help $argv
7 | switch $argv[1]
8 | case develop
9 | announce nix develop $argv[2..] --command (status fish-path)
10 | case shell
11 | # eelco has stated that IN_NIX_SHELL will not be added to 'nix shell'
12 | # because its behavior differs from 'nix-shell' and 'nix develop', but
13 | # from a user's perspective, I'm still entering a subshell launched by
14 | # Nix, so I want to know about that.
15 | # Nix sets IN_NIX_SHELL to either 'pure' or 'impure'. I'm using an
16 | # alternative that differentiates it while still setting it.
17 | IN_NIX_SHELL=shell announce nix shell $argv[2..] --command (status fish-path)
18 | case '*'
19 | command nix $argv
20 | end
21 | else
22 | command nix $argv
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/config/niri/common/shell.kdl:
--------------------------------------------------------------------------------
1 | // dms is being managed by systemd:
2 | // systemctl --user add-wants niri.service dms
3 | // So, the following line is no longer necessary:
4 | // spawn-at-startup "dms" "run"
5 |
6 | // spawn-sh-at-startup "/usr/lib/polkit-kde-authentication-agent-1"
7 |
8 | spawn-at-startup "vicinae" "server"
9 |
10 | layer-rule {
11 | match namespace="^wallpaper$"
12 | place-within-backdrop true
13 | }
14 |
15 | layer-rule {
16 | match namespace="^notifications$"
17 | block-out-from "screencast"
18 | }
19 |
20 | hotkey-overlay {
21 | skip-at-startup
22 | }
23 |
24 | cursor {
25 | hide-when-typing
26 | xcursor-size 24
27 | xcursor-theme "capitaine-cursors"
28 | }
29 |
30 | screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"
31 |
32 | environment {
33 | XDG_SESSION_TYPE "wayland"
34 | XDG_CURRENT_DESKTOP "niri"
35 |
36 | ELECTRON_OZONE_PLATFORM_HINT "auto"
37 |
38 | QT_QPA_PLATFORM "wayland"
39 | QT_WAYLAND_DISABLE_WINDOWDECORATION "1"
40 | QT_QPA_PLATFORMTHEME "gtk3"
41 | QT_QPA_PLATFORMTHEME_QT6 "gtk3"
42 |
43 | _JAVA_AWT_WM_NONREPARENTING "1"
44 | }
45 |
--------------------------------------------------------------------------------
/config/fish/conf.d/999_fix_path.fish:
--------------------------------------------------------------------------------
1 | # Standalone Home Manager leaves Fish to reconstruct its own PATH, which
2 | # takes the existing path from ZSH, puts the contents of /etc/paths and
3 | # /etc/paths.d first, then appends the existing non-duplicate entries to
4 | # the end. This results in the Nix entries being at the tail of the PATH,
5 | # which isn't ideal. The solution is to add all profile bin directories
6 | # to the front and deduplicate them.
7 | #
8 | # Without the check before executing, the NIX_PROFILES directories will
9 | # always be moved to the front, even when entering a nested `nix develop`
10 | # shell, which could result in the wrong program being executed if specified
11 | # by both the user environment and dev shell.
12 | if set -q IS_DARWIN; and not set -q __NIX_PROFILES_HAVE_BEEN_MOVED
13 | for path in (string split ' ' $NIX_PROFILES)
14 | set -g PATH $path/bin $PATH
15 | end
16 | set -gx __NIX_PROFILES_HAVE_BEEN_MOVED
17 | end
18 |
19 | # Deduplicate PATH because it might contain dupes after the handoff from ZSH.
20 | set -l new_path
21 | for path in $PATH
22 | if not contains -- $path $new_path
23 | set new_path $new_path $path
24 | end
25 | end
26 | set -gx PATH $new_path
27 |
--------------------------------------------------------------------------------
/config/fish/config.fish:
--------------------------------------------------------------------------------
1 | if status is-interactive
2 | # Tide hasn't been set up yet but it is installed, so this must be a first-time setup.
3 | # This isn't critical to be in config.fish, but it's good to know that all other configuration
4 | # has already been applied before any setup is done.
5 | if not set -q tide_left_prompt_items; and type -q tide
6 | tide configure --auto --style='Lean' --prompt_colors='16 colors' --show_time='No' --lean_prompt_height='One line' --prompt_spacing='Compact' --icons='Few icons' --transient='No'
7 |
8 | set -g __setup__clear_screen_prompt_count 0
9 | function __setup__clear_screen --on-event fish_prompt
10 | if test "$__setup__clear_screen_prompt_count" = 0
11 | # Clearing the screen here gets rid of the greeting if it was displayed
12 | clear
13 | echo " Tide has been set up."
14 | echo " Press enter to begin."
15 | set -e __setup__clear_screen_prompt_count
16 | else
17 | clear
18 | functions -e __setup__clear_screen
19 | fish_greeting
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/secrets.nix:
--------------------------------------------------------------------------------
1 | let
2 | system-homeserver1 = builtins.readFile ./hosts/homeserver1/id_ed25519.pub;
3 |
4 | robert-macmini = builtins.readFile ./hosts/macmini/users/robert/id_ed25519.pub;
5 | robert-macbook-air = builtins.readFile ./hosts/macbook-air/users/robert/id_ed25519.pub;
6 | robert-pc3 = builtins.readFile ./hosts/pc3/users/robert/id_ed25519.pub;
7 | robert-personal = [
8 | robert-pc3
9 | robert-macmini
10 | robert-macbook-air
11 | ];
12 |
13 | robert-work-macbookpro = builtins.readFile ./hosts/work-macbookpro/users/robert/id_ed25519.pub;
14 | work-pc3 = builtins.readFile ./hosts/pc3/users/work/id_ed25519.pub;
15 | robert-work = [
16 | robert-work-macbookpro
17 | work-pc3
18 | ];
19 |
20 | robert = robert-personal ++ robert-work;
21 | in
22 | {
23 | "hosts/homeserver1/tailscale-homeserver1.age".publicKeys = [ system-homeserver1 ] ++ robert;
24 | "hosts/homeserver1/clouddns-config.json.age".publicKeys = [ system-homeserver1 ] ++ robert;
25 | "hosts/work-macbookpro/users/robert/work-gitconfig.age".publicKeys = [
26 | robert-work-macbookpro
27 | ]
28 | ++ robert;
29 | "hosts/pc3/users/work/gitconfig.age".publicKeys = robert;
30 | "config/niri/private.kdl".publicKeys = robert;
31 | }
32 |
--------------------------------------------------------------------------------
/users/robert/work-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | flake,
3 | config,
4 | pkgs,
5 | inputs,
6 | ...
7 | }:
8 | {
9 | imports = [
10 | ./home-configuration.nix
11 | ];
12 |
13 | # The work setups are managed much more imperatively than any of my personal
14 | # machines, mainly because it isn't worth my time while I'm working to set
15 | # up Nix correctly (without buy-in from anyone else on the team), and it
16 | # isn't worth my time when I'm not working because there are other things I'd
17 | # rather be doing. Instead, I can do as much as is reasonable in Nix, but
18 | # fall back to plugins and homebrew when stuff doesn't work right.
19 |
20 | my.programs.fish.plugins = [
21 | (pkgs.fetchFromGitHub {
22 | owner = "jorgebucaran";
23 | repo = "nvm.fish";
24 | rev = "846f1f20b2d1d0a99e344f250493c41a450f9448"; # current as of 2025-11-18
25 | hash = "sha256-u3qhoYBDZ0zBHbD+arDxLMM8XoLQlNI+S84wnM3nDzg=";
26 | })
27 | ];
28 |
29 | home.packages = [
30 | pkgs.awscli2
31 | pkgs.python3
32 | pkgs.nss_latest
33 | pkgs.ngrok
34 | pkgs.vtsls
35 | pkgs.mongosh
36 | pkgs.typos
37 | pkgs.typos-lsp
38 | pkgs.glow
39 | pkgs.deno
40 | pkgs._1password-cli
41 | pkgs.mongodb-tools
42 | ];
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/config/fish/functions/fish_job_summary.fish:
--------------------------------------------------------------------------------
1 | function fish_job_summary -a job_id is_foreground cmd_line signal_or_end_name signal_desc proc_pid proc_name
2 | if test "$signal_or_end_name" = SIGINT; and test $is_foreground -eq 1
3 | return
4 | end
5 |
6 | set -l max_cmd_len 32
7 | set cmd_line (string shorten -m$max_cmd_len -- $cmd_line)
8 |
9 | set -l message
10 | switch $signal_or_end_name
11 | # case STOPPED
12 | # set message (printf ( _ "fish: Job %s, '%s' has stopped\n" ) $job_id $cmd_line)
13 | case ENDED
14 | set message (printf ( _ "fish: Job %s, '%s' has ended\n" ) $job_id $cmd_line)
15 | case 'SIG*'
16 | if test -n "$proc_pid"
17 | set message (printf ( _ "fish: Process %s, '%s' from job %s, '%s' terminated by signal %s (%s)\n" ) \
18 | $proc_pid $proc_name $job_id $cmd_line $signal_or_end_name $signal_desc)
19 | else
20 | set message (printf ( _ "fish: Job %s, '%s' terminated by signal %s (%s)\n" ) \
21 | $job_id $cmd_line $signal_or_end_name $signal_desc)
22 | end
23 | end
24 |
25 | if test $is_foreground -eq 0; and test $signal_or_end_name != STOPPED
26 | __fish_echo string join \n -- $message
27 | else
28 | string join >&2 \n -- $message
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/hosts/homeserver1/clouddns.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | perSystem,
4 | ...
5 | }:
6 | {
7 | age.secrets.clouddns-config = {
8 | file = ./clouddns-config.json.age;
9 | owner = "clouddns";
10 | group = "clouddns";
11 | mode = "400";
12 | };
13 |
14 | users.users.clouddns = {
15 | description = "System user for clouddns";
16 | isSystemUser = true;
17 | group = "clouddns";
18 | };
19 |
20 | users.groups.clouddns = { };
21 |
22 | systemd.services.clouddns = {
23 | description = "Update Cloudflare DNS records with the current IP address";
24 | after = [ "network-online.target" ];
25 | wants = [ "network-online.target" ];
26 |
27 | serviceConfig = {
28 | Type = "oneshot";
29 | NoNewPrivileges = true;
30 | PrivateDevices = true;
31 | MemoryDenyWriteExecute = true;
32 | User = "clouddns";
33 | Group = "clouddns";
34 | Environment = [
35 | "DDNS_CONFIG_PATH=${config.age.secrets.clouddns-config.path}"
36 | "DDNS_CACHE_PATH=/var/tmp"
37 | ];
38 | ExecStart = "${perSystem.clouddns.default}/bin/clouddns";
39 | };
40 | };
41 |
42 | systemd.timers.clouddns = {
43 | description = "Timer for clouddns";
44 | wantedBy = [ "timers.target" ];
45 |
46 | timerConfig = {
47 | OnBootSec = "1m";
48 | OnCalendar = "*:0/20";
49 | };
50 | };
51 | }
52 |
--------------------------------------------------------------------------------
/config/fish/functions/trash.fish:
--------------------------------------------------------------------------------
1 | function trash
2 | set -l rm_rf_targets
3 | set -l trash_targets
4 |
5 | for target in $argv
6 | set name (path basename -- $target)
7 | # Well, this is horrifying. Fish doesn't really have sane grouping,
8 | # so we need this utterly deranged nested if statement with two grouped
9 | # conditions in `begin; ...; end` blocks.
10 | # The idea is that all symlinks should be rm'd, and any files/dirs
11 | # in their respective kill-lists.
12 | if path is -l -- $target
13 | or begin
14 | path is -f -- $target
15 | and contains -- $name $skip_trash_files
16 | end
17 | or begin
18 | path is -d -- $target
19 | and begin
20 | contains -- $name $skip_trash_directories
21 | or test (count (command ls -A $target)) -eq 0
22 | end
23 | end
24 | or begin
25 | path is -x -- $target
26 | and test (head -c 2 $target) != "#!"
27 | end
28 | set -a rm_rf_targets $target
29 | else
30 | set -a trash_targets $target
31 | end
32 | end
33 |
34 | set total_rm (count $rm_rf_targets)
35 | if test "$total_rm" -gt 0
36 | echo "Permanently deleting $total_rm item(s)"
37 | command rm -rf $rm_rf_targets
38 | end
39 |
40 | set total_trash (count $trash_targets)
41 | if test "$total_trash" -gt 0
42 | echo "Trashing $total_trash item(s)"
43 | command trash $trash_targets
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/hosts/homeserver1/vrising.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | config,
4 | ...
5 | }:
6 | {
7 | systemd.tmpfiles.rules = [
8 | "d /srv/vrising 0700 root root -"
9 | "d /srv/vrising/server 0700 root root -"
10 | "d /srv/vrising/data 0700 root root -"
11 | ];
12 |
13 | virtualisation.oci-containers.containers.vrising = {
14 | image = "docker.io/trueosiris/vrising";
15 | autoStart = true;
16 | ports = [
17 | "9876:9876/udp"
18 | "9877:9877/udp"
19 | ];
20 | volumes = [
21 | "/srv/vrising/server:/mnt/vrising/server"
22 | "/srv/vrising/data:/mnt/vrising/persistentdata"
23 | ];
24 | environment = {
25 | TZ = config.time.timeZone;
26 | SERVERNAME = "vrising-clo4";
27 | };
28 | extraOptions = [
29 | "--network=bridge"
30 | ];
31 | };
32 |
33 | systemd.services.podman-vrising = {
34 | after = [ "network.target" ];
35 | requires = [ "network.target" ];
36 | };
37 |
38 | # systemd.services.vrising-restart = {
39 | # description = "Restart V-Rising Server";
40 | # requires = [ "podman-vrising.service" ];
41 | # after = [ "podman-vrising.service" ];
42 | # script = ''
43 | # ${pkgs.systemd}/bin/systemctl restart podman-vrising.service
44 | # '';
45 | # serviceConfig = {
46 | # Type = "oneshot";
47 | # User = "root";
48 | # };
49 | # };
50 |
51 | # systemd.timers.vrising-restart = {
52 | # description = "Timer for daily Minecraft server restart";
53 | # wantedBy = [ "timers.target" ];
54 | # timerConfig = {
55 | # OnCalendar = "04:00:00";
56 | # Unit = "vrising-restart.service";
57 | # };
58 | # };
59 | }
60 |
--------------------------------------------------------------------------------
/hosts/homeserver1/disko.nix:
--------------------------------------------------------------------------------
1 | {
2 | disko.devices.disk = {
3 | main = {
4 | type = "disk";
5 | device = "/dev/nvme0n1";
6 | content = {
7 | type = "gpt";
8 | partitions = {
9 | ESP = {
10 | priority = 1;
11 | name = "ESP";
12 | start = "1M";
13 | end = "512M";
14 | type = "EF00";
15 | content = {
16 | type = "filesystem";
17 | format = "vfat";
18 | mountpoint = "/boot";
19 | mountOptions = [
20 | "defaults"
21 | "umask=0077"
22 | ];
23 | };
24 | };
25 | swap = {
26 | size = "4G";
27 | content = {
28 | type = "swap";
29 | };
30 | };
31 | root = {
32 | size = "100%";
33 | content = {
34 | type = "btrfs";
35 | extraArgs = [ "-f" ];
36 | subvolumes = {
37 | root = {
38 | mountOptions = [ "compress=zstd" ];
39 | mountpoint = "/";
40 | };
41 | home = {
42 | mountOptions = [ "compress=zstd" ];
43 | mountpoint = "/home";
44 | };
45 | nix = {
46 | mountOptions = [
47 | "compress=zstd"
48 | "noatime"
49 | ];
50 | mountpoint = "/nix";
51 | };
52 | };
53 | };
54 | };
55 | };
56 | };
57 | };
58 | };
59 | }
60 |
--------------------------------------------------------------------------------
/config/fish/functions/fish_user_key_bindings.fish:
--------------------------------------------------------------------------------
1 | function fish_user_key_bindings
2 | fish_default_key_bindings
3 | bind ctrl-z 'fg 2>/dev/null; commandline -f repaint'
4 | bind alt-z 'zi; commandline -f repaint'
5 |
6 | # c-g for git status
7 | bind ctrl-g _fzf_git_status_modified
8 |
9 | # Not sure why but the order of these is broken by default.
10 | # expand-abbr needs to happen first so the cursor is
11 | # still over the abbreviation when it tries to expand.
12 | bind ' ' expand-abbr self-insert
13 | bind ';' expand-abbr self-insert
14 | bind '|' expand-abbr self-insert
15 | bind '&' expand-abbr self-insert
16 | bind '>' expand-abbr self-insert
17 | bind '<' expand-abbr self-insert
18 | bind ')' expand-abbr self-insert
19 |
20 | # This isn't bound by default because of the fzf keybindings.
21 | bind ctrl-r history-pager
22 |
23 | command -q trash && bind enter _execute_no_rm
24 | end
25 |
26 | function _fzf_git_status_modified
27 | set -l selected (git status --porcelain | sed 's/^.. //' | fzf --query (commandline --current-token) --multi --layout=reverse --height=40% --cycle)
28 | commandline --current-token --replace -- (string join ' ' -- $selected)
29 | commandline -f repaint
30 | end
31 |
32 | function _execute_no_rm
33 | if string match --quiet "rm *" -- (commandline)
34 | echo
35 | echo " $(set_color --background red --bold) ERROR $(set_color normal) Interactive usage of 'rm' is disabled. Use 'trash' to prevent data loss."
36 | echo " (if you $(set_color --italics)need$(set_color normal) to run 'rm', use 'command rm' instead)"
37 | commandline ""
38 | commandline -f repaint
39 | else
40 | commandline -f execute
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/modules/home/my-config.nix:
--------------------------------------------------------------------------------
1 | # A very simple Home Manager module that allows me to easily create out-of-store symlinks.
2 | {
3 | pkgs,
4 | config,
5 | lib,
6 | flake,
7 | ...
8 | }:
9 | let
10 | inherit (lib) types;
11 | cfg = config.my.config;
12 | mkConfigSymlink =
13 | relativePath: config.lib.file.mkOutOfStoreSymlink "${cfg.directory}/${relativePath}";
14 | in
15 | {
16 | options.my.config = {
17 | directory = lib.mkOption {
18 | default = flake;
19 | type = types.path;
20 | description = ''
21 | Path to the directory that the configuration will be linked to.
22 | This is an absolute path on the system, which will be the flake's
23 | path in the store if not specified.
24 | '';
25 | example = lib.literalExpression ''
26 | ''${config.home.homeDirectory}/.config/system-configuration
27 | '';
28 | };
29 |
30 | source = lib.mkOption {
31 | default = { };
32 | type = with types; attrsOf (nullOr (either str path));
33 | description = ''
34 | Mapping from system path to path relative to the source directory.
35 | The value can be either a directory or a file.
36 | To reference your XDG_CONFIG_HOME, use Home Manager's `xdg.configHome`
37 | value.
38 | '';
39 | };
40 |
41 | force = lib.mkOption {
42 | default = true;
43 | type = types.bool;
44 | description = ''
45 | Whether the configuration links should override whatever exists already.
46 | '';
47 | };
48 | };
49 |
50 | config = lib.mkIf (cfg.source != { }) {
51 | home.file =
52 | let
53 | nonNull = lib.filterAttrs (n: v: v != null) cfg.source;
54 | in
55 | builtins.mapAttrs (_: v: {
56 | source = mkConfigSymlink v;
57 | force = cfg.force;
58 | }) nonNull;
59 | };
60 | }
61 |
--------------------------------------------------------------------------------
/modules/home/my-programs-neovim.nix:
--------------------------------------------------------------------------------
1 | {
2 | lib,
3 | pkgs,
4 | config,
5 | ...
6 | }:
7 |
8 | let
9 | cfg = config.my.programs.neovim;
10 |
11 | mkPluginLink =
12 | group: p:
13 | let
14 | subdir = if p.start or false then "start" else "opt";
15 | in
16 | {
17 | "${config.xdg.dataHome}/nvim/site/pack/${group}/${subdir}/${p.name}" = {
18 | source = p.src;
19 | recursive = p.recursive;
20 | };
21 | };
22 |
23 | links = lib.foldlAttrs (
24 | acc: group: plugins:
25 | lib.foldl' (acc2: pl: acc2 // mkPluginLink group pl) acc plugins
26 | ) { } cfg.packages;
27 |
28 | pluginType = lib.types.submodule (
29 | { ... }:
30 | {
31 | options = {
32 | name = lib.mkOption {
33 | type = lib.types.str;
34 | description = "Directory name used with :packadd.";
35 | };
36 | src = lib.mkOption {
37 | type = lib.types.path;
38 | description = "Plugin source derivation.";
39 | };
40 | start = lib.mkOption {
41 | type = lib.types.bool;
42 | default = false;
43 | description = "Install under start/ when true, opt/ when false.";
44 | };
45 | recursive = lib.mkOption {
46 | type = lib.types.bool;
47 | default = false;
48 | description = "Symlink the plugin files recursively when true, or the entire directory when false.";
49 | };
50 | };
51 | }
52 | );
53 |
54 | in
55 | {
56 | options.my.programs.neovim = with lib; {
57 | enable = mkEnableOption "Install NeoVim plugins via packpath" // {
58 | default = true;
59 | };
60 | packages = mkOption {
61 | type = types.attrsOf (types.listOf pluginType);
62 | default = { };
63 | description = "Mapping from pack group to plugin list.";
64 | };
65 | };
66 |
67 | config = lib.mkIf cfg.enable {
68 | home.file = links;
69 | };
70 | }
71 |
--------------------------------------------------------------------------------
/config/helix/languages.toml:
--------------------------------------------------------------------------------
1 | [[language]]
2 | name = "go"
3 | auto-format = true
4 | formatter.command = "goimports"
5 |
6 |
7 | [[language]]
8 | name = "typescript"
9 | language-servers = ["vtsls"]
10 |
11 | [language-server.vtsls]
12 | command = "vtsls"
13 | args = ["--stdio"]
14 |
15 |
16 | [[language]]
17 | name = "nix"
18 | auto-format = true
19 | language-servers = ["nil"]
20 | formatter.command = "nixfmt"
21 | formatter.args = ["-"]
22 |
23 |
24 | [[language]]
25 | name = "fish"
26 | auto-format = true
27 | formatter.command = "fish_indent"
28 | language-servers = ["fish-lsp"]
29 |
30 | [language-server.fish-lsp]
31 | command = "fish-lsp"
32 | # Helix doesn't support popups at the moment, which
33 | # causes the language server to crash
34 | args = ["start"]
35 | environment = { "fish_lsp_show_client_popups" = "false" }
36 |
37 |
38 | [[language]]
39 | name = "markdown"
40 | auto-format = false
41 | language-servers = ["ltex-ls"]
42 | formatter.command = "deno"
43 | formatter.args = ["--ext=md", "-"]
44 |
45 |
46 | [[language]]
47 | name = "git-commit"
48 | language-servers = ["ltex-ls"]
49 |
50 |
51 | [[language]]
52 | name = "scheme"
53 | formatter = { command = "schemat", args = ["/dev/stdin"] }
54 | # auto-format = true
55 | language-servers = ["steel-language-server"]
56 |
57 | [language-server.steel-language-server]
58 | command = "steel-language-server"
59 |
60 |
61 | [language-server.deno]
62 | command = "deno"
63 | args = ["lsp"]
64 | config.enable = true
65 | config.lint = true
66 | config.unstable = true
67 |
68 |
69 | [language-server.ltex-ls]
70 | command = "ltex-ls"
71 |
72 |
73 | [language-server.rust-analyzer]
74 | command = "rust-analyzer"
75 |
76 |
77 | [language-server.rust-analyzer.config.check]
78 | command = "clippy"
79 |
80 |
81 | [language-server.svelteserver]
82 | command = "svelteserver"
83 |
84 |
85 | [language-server.tailwindcss]
86 | command = "tailwindcss-language-server"
87 | args = ["--stdio"]
88 | language-id = "tailwindcss"
89 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
4 | nixpkgs-unstable.url = "github:nixos/nixpkgs/nixpkgs-unstable";
5 |
6 | # blueprint.url = "path:/Users/robert/Developer/blueprint";
7 | # blueprint.url = "github:numtide/blueprint";
8 | blueprint.url = "github:clo4/blueprint/generic-users";
9 | blueprint.inputs.nixpkgs.follows = "nixpkgs";
10 |
11 | nix-darwin.url = "github:LnL7/nix-darwin/master";
12 | nix-darwin.inputs.nixpkgs.follows = "nixpkgs";
13 |
14 | # helix.url = "github:clo4/helix/helix-cogs-steel-language-server";
15 | # helix.inputs.nixpkgs.follows = "nixpkgs";
16 | # helix.inputs.crane.follows = "crane";
17 | # crane.url = "github:ipetkov/crane";
18 | # steel.url = "github:mattwparas/steel";
19 | # steel.inputs.nixpkgs.follows = "nixpkgs";
20 |
21 | helix.url = "github:helix-editor/helix";
22 | helix.inputs.nixpkgs.follows = "nixpkgs";
23 |
24 | home-manager.url = "github:nix-community/home-manager/master";
25 | home-manager.inputs.nixpkgs.follows = "nixpkgs";
26 |
27 | nix-homebrew.url = "github:zhaofengli/nix-homebrew";
28 |
29 | disko.url = "github:nix-community/disko";
30 | disko.inputs.nixpkgs.follows = "nixpkgs";
31 |
32 | srvos.url = "github:nix-community/srvos";
33 | srvos.inputs.nixpkgs.follows = "nixpkgs";
34 |
35 | agenix.url = "github:ryantm/agenix";
36 | agenix.inputs.nixpkgs.follows = "nixpkgs";
37 | agenix.inputs.darwin.follows = "nix-darwin";
38 | agenix.inputs.home-manager.follows = "home-manager";
39 |
40 | clouddns.url = "github:clo4/clouddns";
41 | clouddns.inputs.nixpkgs.follows = "nixpkgs";
42 | clouddns.inputs.blueprint.follows = "blueprint";
43 |
44 | winapps.url = "github:winapps-org/winapps";
45 | winapps.inputs.nixpkgs.follows = "nixpkgs";
46 | };
47 |
48 | outputs =
49 | inputs:
50 | inputs.blueprint {
51 | inherit inputs;
52 | nixpkgs.config.allowUnfree = true;
53 | };
54 | }
55 |
--------------------------------------------------------------------------------
/hosts/work-macbookpro/darwin-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs,
3 | pkgs,
4 | lib,
5 | flake,
6 | ...
7 | }:
8 | {
9 | imports = [
10 | inputs.self.darwinModules.system-defaults
11 | inputs.self.darwinModules.fish-environment
12 |
13 | inputs.nix-homebrew.darwinModules.nix-homebrew
14 | ];
15 |
16 | users.users.robert = {
17 | description = "Robert";
18 | home = "/Users/robert";
19 | openssh.authorizedKeys.keyFiles = [
20 | "${flake}/hosts/pc3/users/work/id_ed25519.pub"
21 | ];
22 | };
23 |
24 | services.openssh.enable = true;
25 | environment.etc."ssh/sshd_config.d/999-disable-password-auth.conf".text = ''
26 | PermitRootLogin no
27 | PasswordAuthentication no
28 | KbdInteractiveAuthentication no
29 | UsePAM no
30 | '';
31 |
32 | # This needs to be reapplied after each system update. My Fish configuration
33 | # will warn about this if it detects the line it adds to sudo_local is absent.
34 | security.pam.services.sudo_local.touchIdAuth = true;
35 |
36 | networking.hostName = "work-macbookpro";
37 |
38 | system.stateVersion = 6;
39 | system.primaryUser = "robert";
40 |
41 | home-manager.backupFileExtension = "hm-backup";
42 |
43 | nix-homebrew.enable = true;
44 | # A user needs to own the prefix, so we'll make it my account
45 | nix-homebrew.user = "robert";
46 |
47 | environment.systemPackages = [
48 | pkgs.fish
49 | ];
50 |
51 | # Not sure about adding in vendor_conf.d because tools can just dump their init into it,
52 | # and because it will be included in a directory in $NIX_PROFILES, therefore
53 | # also $XDG_DATA_DIRS, it will be sourced during startup. This is probably fine, but
54 | # I want total control over what runs in my fish config.
55 | environment.pathsToLink = [
56 | # "/share/fish/vendor_conf.d"
57 | "/share/fish/vendor_completions.d"
58 | "/share/fish/vendor_functions.d"
59 | ];
60 |
61 | nixpkgs.hostPlatform = "aarch64-darwin";
62 |
63 | nix.enable = true;
64 | nix.nixPath = lib.mkForce [
65 | "nixpkgs=${inputs.nixpkgs}"
66 | "home-manager=${inputs.home-manager}"
67 | ];
68 | nix.settings.experimental-features = [
69 | "nix-command"
70 | "flakes"
71 | ];
72 | nix.settings.trusted-users = [ "@admin" ];
73 | nix.channel.enable = false;
74 | }
75 |
--------------------------------------------------------------------------------
/config/niri/common/visuals.kdl:
--------------------------------------------------------------------------------
1 | window-rule {
2 | geometry-corner-radius 12
3 | clip-to-geometry true
4 | tiled-state true
5 | draw-border-with-background false
6 | }
7 |
8 | window-rule {
9 | match is-floating=true
10 |
11 | geometry-corner-radius 16
12 |
13 | shadow {
14 | on
15 | spread -6
16 | softness 48
17 | offset x=0 y=8
18 | color "#00000090"
19 | inactive-color "#00000040"
20 | }
21 |
22 | border {
23 | on
24 | width 0.5
25 | active-color "#ffffffa0"
26 | inactive-color "#ffffff60"
27 | }
28 | }
29 |
30 | window-rule {
31 | match is-floating=true is-active=true
32 |
33 | shadow {
34 | spread -8
35 | softness 32
36 | }
37 | }
38 |
39 | layout {
40 | gaps 6
41 |
42 | empty-workspace-above-first
43 |
44 | always-center-single-column
45 |
46 | default-column-display "tabbed"
47 | tab-indicator {
48 | hide-when-single-tab
49 | // place-within-column
50 | length total-proportion=0.5
51 | gap -6
52 | width 3
53 | corner-radius 6
54 | active-color "#ffffffc0"
55 | inactive-color "#ffffff80"
56 | gaps-between-tabs 12
57 | }
58 |
59 | default-column-width {
60 | proportion 0.6
61 | }
62 | preset-column-widths {
63 | proportion 0.4
64 | proportion 0.6
65 | }
66 |
67 | focus-ring {
68 | off
69 | }
70 | border {
71 | on
72 | width 1
73 | active-color "#5da8dc"
74 | inactive-color "#77777b"
75 | }
76 | shadow {
77 | on
78 | softness 0
79 | spread 0.5
80 | offset x=0 y=0
81 | color "000000c0"
82 | inactive-color "000000a0"
83 | }
84 |
85 | background-color "#000000"
86 | }
87 |
88 | overview {
89 | backdrop-color "#000000"
90 |
91 | // since the background is black, no point rendering the shadow
92 | workspace-shadow {
93 | off
94 | }
95 | }
96 |
97 | prefer-no-csd
98 |
99 | animations {
100 | slowdown 0.3
101 | }
102 |
103 | recent-windows {
104 | highlight {
105 | corner-radius 6
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/config/zsh/.zshrc:
--------------------------------------------------------------------------------
1 | # In order to keep ZSH as the login shell but still use
2 | # Fish interactively, this snippet will execute Fish if
3 | # none of the parent processes were Fish. Unless I run into
4 | # some serious issues, I've consolidated on all shells
5 | # launching ZSH, which does some amount of generic init,
6 | # then puts me into Fish if the session is interactive.
7 | #
8 | # zshenv is the first file that ZSH executes, but a standalone
9 | # Home Manager installation adds the Nix setup to zshrc,
10 | # so the necessary setup would be skipped in that system.
11 | # Everything that zshenv does can also be done in zshrc,
12 | # unless it has to be done unconditionally for non-interactive
13 | # shells - in which case that's also the best spot for it,
14 | # since the spawned and exec'd fish shell will inherit
15 | # the variables.
16 | #
17 | # NOTE: this doesn't handle `nix shell` at all because
18 | # it's difficult to determine if the current shell was
19 | # directly invoked by it. Instead, I use a wrapper function
20 | # in Fish that appends `--command $(status fish-path)`.
21 |
22 | if [[ -o interactive && ! ( "$TERM_PROGRAM" = "WarpTerminal" ) ]]; then
23 | found_fish=0
24 | if type fish >/dev/null; then
25 | ppid=$$
26 | while [[ $ppid -gt 1 ]]; do
27 | if [[ $(ps -o comm= -p $ppid) =~ fish$ ]]; then
28 | found_fish=1
29 | break
30 | fi
31 | ppid=$(ps -o ppid= -p $ppid)
32 | ppid=${ppid##*[[:space:]]} # Strip leading whitespace
33 | done
34 | if [[ $found_fish -eq 0 ]]; then
35 | local shell=$(command -v fish)
36 | exec -l "$shell" -l
37 | fi
38 | else
39 | echo
40 | echo " WARNING: fish not found in PATH - falling back to zsh"
41 | echo
42 | fi
43 | fi
44 |
45 | # Beyond this point, we're setting up ZSH. If Fish doesn't exist on PATH
46 | # then I'll know that by now, so just set things up nicely.
47 |
48 | if [[ -n $GHOSTTY_RESOURCES_DIR ]]; then
49 | source "$GHOSTTY_RESOURCES_DIR"/shell-integration/zsh/ghostty-integration
50 | fi
51 |
52 | # Removes duplicates by keeping the first occurrence of each item, left to right.
53 | typeset -U path cdpath fpath manpath
54 |
55 | for profile in ${(z)NIX_PROFILES}; do
56 | fpath+=($profile/share/zsh/site-functions $profile/share/zsh/$ZSH_VERSION/functions $profile/share/zsh/vendor-completions)
57 | done
58 |
59 | HISTFILE=$HOME/.local/share/zsh/.zsh_history
60 |
--------------------------------------------------------------------------------
/config/ghostty/os-config-darwin:
--------------------------------------------------------------------------------
1 | command = /bin/sh -c "/bin/wait4path /nix/store && exec -l $SHELL"
2 |
3 | auto-update-channel = tip
4 |
5 | macos-option-as-alt = true
6 | macos-titlebar-style = tabs
7 |
8 | window-padding-x = 6
9 | window-padding-y = 4
10 |
11 | keybind = clear
12 |
13 | # On my Mac, I use native 4k (no scaling) which results in almost unreadable
14 | # text at the default size.
15 | font-size = 16
16 |
17 | # All keybinds are "command-first" - if it's possible to bind just using
18 | # command, that should be the preference. Nothing in the terminal will use
19 | # command, so it's safe, and simpler is easier to remember + use.
20 |
21 | keybind = command+shift+comma=reload_config
22 |
23 | # Splits are controlled by D.
24 | # preference for vertical, shift becomes horizontal.
25 | # ---
26 | keybind = command+d=new_split:right
27 | keybind = command+shift+d=new_split:down
28 |
29 | # Moving between splits is an arrow key.
30 | # Enter will "fullscreen" a split, for full focus.
31 | # ---
32 | keybind = command+enter=toggle_split_zoom
33 | keybind = command+left=goto_split:left
34 | keybind = command+right=goto_split:right
35 | keybind = command+up=goto_split:top
36 | keybind = command+down=goto_split:bottom
37 |
38 | # Resizing is the same as moving, but you add shift.
39 | # ---
40 | keybind = command+shift+left=resize_split:left,40
41 | keybind = command+shift+right=resize_split:right,40
42 | keybind = command+shift+up=resize_split:up,40
43 | keybind = command+shift+down=resize_split:down,40
44 | keybind = command+equal=equalize_splits
45 |
46 | # macOS controls.
47 | # Some things are implemented by the GUI toolkit, e.g. control+tab
48 | # ---
49 | keybind = command+t=new_tab
50 | keybind = command+n=new_window
51 | keybind = command+w=close_surface
52 | keybind = command+shift+w=close_window
53 | keybind = command+q=quit
54 | keybind = command+c=copy_to_clipboard
55 | keybind = command+v=paste_from_clipboard
56 | keybind = command+shift+v=paste_from_selection
57 | keybind = command+p=toggle_command_palette
58 |
59 |
60 | # Misc functionality
61 | # ---
62 | keybind = page_up=scroll_page_fractional:-0.5
63 | keybind = page_down=scroll_page_fractional:0.5
64 | # The s can be a mnemonic for scrollback or search
65 | keybind = command+shift+s=write_scrollback_file:paste
66 |
67 |
68 | # Quick terminal settings
69 | quick-terminal-animation-duration = 0
70 | quick-terminal-position = right
71 | quick-terminal-size = 50%
72 | keybind = global:command+shift+option+control+grave_accent=toggle_quick_terminal
73 |
--------------------------------------------------------------------------------
/config/fish/conf.d/eza.fish:
--------------------------------------------------------------------------------
1 | set -gx EZA_COLORS "reset:fi=0:di=1;34:ln=36:or=31:ex=32:pi=90:so=90:bd=90:cd=90:oc=2:ur=2:uw=2:ux=2:ue=2:gr=2:gw=2:gx=2:tr=2:tw=2:tx=2:su=1;37:sf=1;37:xa=90:sn=2:sb=2;90:uu=2:un=2:uR=2;37:gu=2:gn=2:gR=2;37:xx=90:da=2:in=2:bl=2:hd=1;37:lp=36:mp=34"
2 |
3 | # ----------------------------------------------------
4 | # The rest of this file is to make it easy to generate
5 | # ----------------------------------------------------
6 | #
7 |
8 | # set -gx --path EZA_COLORS reset
9 |
10 | # # Basic file types
11 | # set -a EZA_COLORS "fi=0" # Regular files (normal)
12 | # set -a EZA_COLORS "di=1;34" # Directories (bold blue)
13 | # set -a EZA_COLORS "ln=36" # Symlinks (cyan)
14 | # set -a EZA_COLORS "or=31" # Broken symlinks (red)
15 | # set -a EZA_COLORS "ex=32" # Executable files (green)
16 |
17 | # # Special files
18 | # set -a EZA_COLORS "pi=90" # Named pipes (dark gray)
19 | # set -a EZA_COLORS "so=90" # Sockets (dark gray)
20 | # set -a EZA_COLORS "bd=90" # Block devices (dark gray)
21 | # set -a EZA_COLORS "cd=90" # Character devices (dark gray)
22 |
23 | # # Permissions (dimmed)
24 | # set -a EZA_COLORS "oc=2" # Octal permissions
25 | # set -a EZA_COLORS "ur=2" # User read
26 | # set -a EZA_COLORS "uw=2" # User write
27 | # set -a EZA_COLORS "ux=2" # User execute
28 | # set -a EZA_COLORS "ue=2" # User execute (other file types)
29 | # set -a EZA_COLORS "gr=2" # Group read
30 | # set -a EZA_COLORS "gw=2" # Group write
31 | # set -a EZA_COLORS "gx=2" # Group execute
32 | # set -a EZA_COLORS "tr=2" # Others read
33 | # set -a EZA_COLORS "tw=2" # Others write
34 | # set -a EZA_COLORS "tx=2" # Others execute
35 | # set -a EZA_COLORS "su=1;37" # Setuid/setgid (bold white)
36 | # set -a EZA_COLORS "sf=1;37" # Setuid/setgid/sticky (other file types)
37 | # set -a EZA_COLORS "xa=90" # Extended attributes (dark gray)
38 |
39 | # # File size (dimmed)
40 | # set -a EZA_COLORS "sn=2" # File size numbers
41 | # set -a EZA_COLORS "sb=2;90" # File size units (dimmed dark gray)
42 |
43 | # # User/Group (dimmed)
44 | # set -a EZA_COLORS "uu=2" # Current user
45 | # set -a EZA_COLORS "un=2" # Other users
46 | # set -a EZA_COLORS "uR=2;37" # Root user (dimmed white)
47 | # set -a EZA_COLORS "gu=2" # Your group
48 | # set -a EZA_COLORS "gn=2" # Other groups
49 | # set -a EZA_COLORS "gR=2;37" # Root group (dimmed white)
50 |
51 | # # UI elements
52 | # set -a EZA_COLORS "xx=90" # UI punctuation (dark gray)
53 | # set -a EZA_COLORS "da=2" # Date (dimmed)
54 | # set -a EZA_COLORS "in=2" # Inode (dimmed)
55 | # set -a EZA_COLORS "bl=2" # Blocks (dimmed)
56 | # set -a EZA_COLORS "hd=1;37" # Table header (bold white)
57 | # set -a EZA_COLORS "lp=36" # Symlink path (cyan)
58 |
59 | # # Mount points
60 | # set -a EZA_COLORS "mp=34" # Mount points (blue)
61 |
--------------------------------------------------------------------------------
/config/nvim/plugins.nix:
--------------------------------------------------------------------------------
1 | { pkgs, ... }:
2 | let
3 | fromGitHub =
4 | {
5 | owner,
6 | repo,
7 | name ? repo,
8 | rev,
9 | hash,
10 | opt ? false,
11 | recursive ? false,
12 | }:
13 | {
14 | inherit name recursive;
15 | start = !opt;
16 | src = pkgs.fetchFromGitHub {
17 | inherit
18 | owner
19 | repo
20 | rev
21 | hash
22 | ;
23 | };
24 | };
25 | in
26 | {
27 | my.programs.neovim.packages.nix-managed = [
28 | (fromGitHub {
29 | owner = "echasnovski";
30 | repo = "mini.surround";
31 | rev = "1a2b59c77a0c4713a5bd8972da322f842f4821b1";
32 | hash = "sha256-khhvGI4aWVgdTeBabxncVNWPI5vouSTpHAUYfEhgISs=";
33 | })
34 |
35 | (fromGitHub {
36 | owner = "sainnhe";
37 | repo = "gruvbox-material";
38 | rev = "66cfeb7050e081a746a62dd0400446433e802368";
39 | hash = "sha256-EPz9jIbyext4WEjzh5V8JKMeMBVgUzmgeBPqiWf0dc4=";
40 | })
41 |
42 | (fromGitHub {
43 | owner = "nvim-treesitter";
44 | repo = "nvim-treesitter";
45 | rev = "1f069f1bc610493d38276023165075f7424ca7b4";
46 | hash = "sha256-PABUBcr0QFROpO3WHi1BwHpXk4Pe+cm4vr3otiyEK/4=";
47 | })
48 |
49 | # Oil depends on at least one library to do icons
50 | (fromGitHub {
51 | owner = "stevearc";
52 | repo = "oil.nvim";
53 | rev = "08c2bce8b00fd780fb7999dbffdf7cd174e896fb";
54 | hash = "sha256-fbRbRT9VJdppOs4hML1J113qLHdj7YRuSDQgZkt34cM=";
55 | })
56 | (fromGitHub {
57 | owner = "echasnovski";
58 | repo = "mini.icons";
59 | rev = "94848dad1589a199f876539bd79befb0c5e3abf0";
60 | hash = "sha256-2S9w8OGfV0QFs814cYMOzYiZwCZmyDl6n0TMsNWuIKA=";
61 | })
62 |
63 | # Telescope has a few dependencies.
64 | (fromGitHub {
65 | owner = "nvim-telescope";
66 | repo = "telescope.nvim";
67 | rev = "b4da76be54691e854d3e0e02c36b0245f945c2c7";
68 | hash = "sha256-JpW0ehsX81yVbKNzrYOe1hdgVMs6oaaxMLH6lECnOJg=";
69 | })
70 | (fromGitHub {
71 | owner = "nvim-lua";
72 | repo = "plenary.nvim";
73 | rev = "857c5ac632080dba10aae49dba902ce3abf91b35";
74 | hash = "sha256-8FV5RjF7QbDmQOQynpK7uRKONKbPRYbOPugf9ZxNvUs=";
75 | })
76 |
77 | {
78 | name = "telescope-fzf-native.nvim";
79 | src = pkgs.stdenv.mkDerivation {
80 | name = "telescope-fzf-native";
81 | src = pkgs.fetchFromGitHub {
82 | owner = "nvim-telescope";
83 | repo = "telescope-fzf-native.nvim";
84 | rev = "1f08ed60cafc8f6168b72b80be2b2ea149813e55";
85 | hash = "sha256-Zyv8ikxdwoUiDD0zsqLzfhBVOm/nKyJdZpndxXEB6ow=";
86 | };
87 | installPhase = ''
88 | runHook preInstall
89 | all_files=$(ls)
90 | mkdir -p $out
91 | cp -R $all_files $out
92 | runHook postInstall
93 | '';
94 | };
95 | }
96 | ];
97 | }
98 |
--------------------------------------------------------------------------------
/config/niri/common/window-rule.kdl:
--------------------------------------------------------------------------------
1 | workspace "games"
2 |
3 | window-rule {
4 | // These display media, the media should dictate how wide the column should be
5 | match app-id=r#"^org\.gnome\.(Loupe|Showtime)$"#
6 | default-column-display "normal"
7 | default-column-width {}
8 | }
9 |
10 | window-rule {
11 | match app-id=r#"^org\.gnome\.Calculator$"#
12 | open-floating true
13 | default-floating-position x=100 y=0 relative-to="right"
14 | }
15 |
16 | window-rule {
17 | match app-id=r#"^XEyes$"#
18 |
19 | default-column-width { fixed 340; }
20 | default-window-height { fixed 240; }
21 | open-floating true
22 | default-floating-position x=30 y=30 relative-to="top-right"
23 | }
24 |
25 | // Indicate screencasted windows with red colors.
26 | window-rule {
27 | match is-window-cast-target=true
28 |
29 | focus-ring {
30 | off
31 | }
32 |
33 | border {
34 | width 4
35 | active-color "#7bff44aa"
36 | inactive-color "#4f9b05aa"
37 | }
38 |
39 | tab-indicator {
40 | active-color "#4f9b05"
41 | inactive-color "#4f9b05"
42 | }
43 | }
44 |
45 | // I use the following apps in full-width, pretty much exclusively
46 | window-rule {
47 | match app-id=r#"^firefox$"#
48 | match app-id=r#"^code$"#
49 | match app-id=r#"^spotify$"#
50 |
51 | default-column-width {
52 | proportion 1.0
53 | }
54 | }
55 |
56 | window-rule {
57 | match app-id=r#"^firefox$"# title=r#"^Picture-in-Picture$"#
58 |
59 | open-floating true
60 | geometry-corner-radius 8
61 | default-floating-position x=12 y=12 relative-to="bottom-right"
62 | default-column-width { fixed 480; }
63 | default-window-height { fixed 270; }
64 | border {
65 | inactive-color "#ffffff40"
66 | }
67 | }
68 |
69 | window-rule {
70 | match app-id=r#"^org\.keepassxc\.KeePassXC$"#
71 | match app-id=r#"^org\.gnome\.World\.Secrets$"#
72 | match app-id=r#"^1password$"#
73 |
74 | block-out-from "screen-capture"
75 |
76 | // Use this instead if you want them visible on third-party screenshot tools.
77 | // block-out-from "screencast"
78 | }
79 |
80 | window-rule {
81 | match app-id=r#"^com\.discordapp\.Discord$"#
82 | open-on-workspace "games"
83 | default-column-width {
84 | proportion 0.8
85 | }
86 | }
87 |
88 | window-rule {
89 | match app-id=r#"^steam$"#
90 | exclude title=r#"^notificationtoasts_\d+_desktop$"#
91 | open-on-workspace "games"
92 | default-column-width {
93 | proportion 0.8
94 | }
95 | shadow { off; }
96 | border { off; }
97 | focus-ring { off; }
98 | clip-to-geometry false
99 | }
100 |
101 | window-rule {
102 | match app-id=r#"^steam$"# title=r#"^notificationtoasts_\d+_desktop$"#
103 | shadow { off; }
104 | border { off; }
105 | focus-ring { off; }
106 | clip-to-geometry false
107 | default-floating-position x=0 y=0 relative-to="bottom-right"
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/config/fish/conf.d/abbreviations.fish:
--------------------------------------------------------------------------------
1 | # General shell helpers.
2 | # These are commands that I type enough that I need them
3 | # to be faster to type.
4 | abbr -a - "cd -"
5 | abbr -a .. "cd .."
6 | abbr -a cpr "cp -r"
7 | abbr -a % "xargs -I % --"
8 | abbr -a f "fzf |"
9 | abbr -a fm "fzf --multi |"
10 | abbr -a rmf "rm -rf"
11 | abbr -a md "mkdir -p"
12 | abbr -a c cp
13 | abbr -a cmv "command -v"
14 | command -q trash; and abbr -a rm trash
15 | abbr -a j jj
16 | abbr -a fns functions
17 | abbr -a p pnpm
18 | abbr -a pr "pnpm run"
19 | abbr -a pa "pnpm add"
20 | abbr -a pi "pnpm install"
21 | abbr -a px pnpx
22 | abbr -a nx "npx nx"
23 | abbr -a nxr "npx nx run"
24 | abbr -a nrsl "npm run start-local"
25 |
26 | abbr -a r run
27 |
28 | if set -q IS_DARWIN
29 | abbr -a p pbcopy
30 | abbr -a pp pbpaste
31 | abbr -a b brew
32 | end
33 |
34 | abbr -a gb "go build"
35 | abbr -a gb. "go build ./..."
36 | abbr -a gf "go fmt"
37 | abbr -a gf. "go fmt ./..."
38 | abbr -a gt "go test"
39 | abbr -a gt. "go test ./..."
40 | abbr -a glint "golangci-lint run"
41 |
42 | abbr -a n nix
43 | abbr -a nxi nix
44 | abbr -a nd "nix develop"
45 | abbr -a nb "nix build"
46 | abbr -a nr "nix run"
47 | abbr -a ns "nix shell"
48 | abbr -a nre "nix repl"
49 | abbr -a nf "nix fmt"
50 | abbr -a nfs "nix flake show"
51 | abbr -a nfl "nix flake lock"
52 | abbr -a nfu "nix flake update"
53 | abbr -a nfuc "nix flake update --commit-lock-file"
54 | abbr -a nfc "nix flake check"
55 | abbr -a nfi "nix flake init"
56 | abbr -a nfit "nix flake init --template"
57 |
58 | # Random abbreviations that are easier to type on some layouts, because I hop
59 | # around a lot.
60 | abbr -a nv nvim
61 | abbr -a he hx
62 |
63 | abbr -a t tmux
64 | abbr -a ta "tmux attach; or tmux"
65 | abbr -a tk "tmux kill-session"
66 | abbr -a tl "tmux list-sessions"
67 |
68 | abbr -a ts tailscale
69 | abbr -a tsd tailscaled
70 | abbr -a tss "tailscale status"
71 | abbr -a tf terraform # not installed globally, used in projects
72 |
73 | abbr -a co cargo
74 | abbr -a cob "cargo build"
75 | abbr -a cor "cargo run"
76 | abbr -a corr "cargo run --release"
77 | abbr -a cot "cargo test"
78 | abbr -a coa "cargo add"
79 | abbr -a coc "cargo check"
80 |
81 | abbr -a d docker
82 | abbr -a dp "docker ps"
83 | abbr -a dc "docker compose"
84 | abbr -a dcu "docker compose up"
85 | abbr -a dcud "docker compose up --detach"
86 | abbr -a dcs "docker compose stop"
87 |
88 | abbr -a g lazygit
89 | abbr -a ",a" "git add"
90 | abbr -a ",ap" "git add --patch"
91 | abbr -a ",aa" "git add -A"
92 | abbr -a ",r" "git restore"
93 | abbr -a ",rs" "git restore --staged"
94 | abbr -a ",re" "git reset"
95 | abbr -a ",rv" "git remote -v"
96 | abbr -a ",ra" "git rebase --abort"
97 | abbr -a ",c" "git commit"
98 | abbr -a ",ca" "git commit --amend"
99 | abbr -a ",d" "git diff"
100 | abbr -a ",dc" "git diff --cached"
101 | abbr -a ",m" "git merge"
102 | abbr -a ",s" "git status"
103 | abbr -a ",p" "git push"
104 | abbr -a ",pf" "git push --force-with-lease"
105 | abbr -a ",pu" "git pull"
106 | abbr -a ",f" "git fetch"
107 | abbr -a ",fu" "git fetch upstream"
108 | abbr -a ",sw" "git switch"
109 | abbr -a ",sc" "git switch -c"
110 | abbr -a ",b" "git branch"
111 | abbr -a ",l" "git log"
112 |
113 | abbr -a yay paru
114 |
--------------------------------------------------------------------------------
/hosts/homeserver1/configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | flake,
3 | inputs,
4 | config,
5 | pkgs,
6 | ...
7 | }:
8 | {
9 | imports = [
10 | inputs.disko.nixosModules.default
11 | inputs.agenix.nixosModules.default
12 |
13 | inputs.srvos.nixosModules.server
14 | inputs.srvos.nixosModules.mixins-systemd-boot
15 |
16 | ./disko.nix
17 | ./clouddns.nix
18 | # ./minecraft
19 | # ./vrising.nix
20 | ];
21 |
22 | system.stateVersion = "24.11";
23 | nixpkgs.hostPlatform = "x86_64-linux";
24 |
25 | boot.loader.efi.canTouchEfiVariables = true;
26 | boot.initrd.enable = true;
27 | boot.initrd.availableKernelModules = [
28 | "xhci_pci"
29 | "ahci"
30 | "nvme"
31 | "usbhid"
32 | "usb_storage"
33 | "sd_mod"
34 | ];
35 | boot.initrd.kernelModules = [ ];
36 | boot.kernelModules = [ "kvm-intel" ];
37 | boot.kernelPackages = pkgs.linuxPackages_latest;
38 | boot.extraModulePackages = [ ];
39 | boot.supportedFilesystems = [ "btrfs" ];
40 | boot.initrd.supportedFilesystems = [ "btrfs" ];
41 |
42 | hardware.cpu.intel.updateMicrocode = true;
43 | hardware.enableRedistributableFirmware = true;
44 |
45 | networking.hostName = "homeserver1";
46 | networking.hostId = "027fb931";
47 | networking.useDHCP = true;
48 |
49 | nix.registry.nixpkgs.flake = inputs.nixpkgs;
50 | nix.registry.self.flake = inputs.self;
51 |
52 | # The srvos server profile disables documentation by default, but it's
53 | # useful to have it enabled so I can reference things quickly in an
54 | # SSH session instead of trying to find the documentation online.
55 | srvos.server.docs.enable = true;
56 |
57 | # Srvos also sets the timezone to UTC automatically. I want this to be
58 | # my current location because it will also handle DST changes smoothly.
59 | # A timer set for "04:00" should go off when my wall clock says it's 4.
60 | time.timeZone = "Australia/Sydney";
61 |
62 | age.secrets.tailscale-homeserver1.file = ./tailscale-homeserver1.age;
63 | services.tailscale = {
64 | enable = true;
65 | authKeyFile = config.age.secrets.tailscale-homeserver1.path;
66 | openFirewall = true;
67 | extraUpFlags = [
68 | "--advertise-exit-node"
69 | "--exit-node-allow-lan-access"
70 | ];
71 | };
72 |
73 | virtualisation.podman.enable = true;
74 | virtualisation.oci-containers.backend = "podman";
75 |
76 | environment.etc."configuration-revision".text = flake.rev or flake.dirtyRev;
77 |
78 | users.users.robert = {
79 | isNormalUser = true;
80 | extraGroups = [
81 | "wheel"
82 | "podman"
83 | "systemd-journal"
84 | ];
85 | hashedPassword = "!";
86 |
87 | # TODO: Think of a better place to store these keys. Maybe in users/robert/?
88 | openssh.authorizedKeys.keys = [
89 | # My iPhone, blink terminal
90 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFkVAe4iwrprDibMgY1m0BeUPgrKBRErKRfLfxjVl+lu"
91 | # My iPad, blink terminal
92 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEGcz3Qiqix5lJPsDeE+RY2q64Bpl+jY0tLO/fUM5TNr"
93 | ];
94 |
95 | # Allows each system's root and user to connect to the server, which
96 | # is necessary to use it as a builder.
97 | openssh.authorizedKeys.keyFiles = [
98 | "${flake}/hosts/macbook-air/id_ed25519.pub"
99 | "${flake}/hosts/macbook-air/users/robert/id_ed25519.pub"
100 |
101 | "${flake}/hosts/macmini/id_ed25519.pub"
102 | "${flake}/hosts/macmini/users/robert/id_ed25519.pub"
103 | ];
104 | };
105 | }
106 |
--------------------------------------------------------------------------------
/hosts/macmini/darwin-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs,
3 | pkgs,
4 | lib,
5 | flake,
6 | ...
7 | }:
8 | {
9 | imports = [
10 | inputs.self.darwinModules.system-defaults
11 | inputs.self.darwinModules.fish-environment
12 |
13 | inputs.nix-homebrew.darwinModules.nix-homebrew
14 | ];
15 |
16 | users.users.robert = {
17 | description = "Robert";
18 | home = "/Users/robert";
19 | openssh.authorizedKeys.keys = [
20 | # My iPhone, blink terminal
21 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFkVAe4iwrprDibMgY1m0BeUPgrKBRErKRfLfxjVl+lu"
22 |
23 | # My iPad, blink terminal
24 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEGcz3Qiqix5lJPsDeE+RY2q64Bpl+jY0tLO/fUM5TNr"
25 | ];
26 | openssh.authorizedKeys.keyFiles = [
27 | # Allows both my user account and system root to SSH into the robert account.
28 | # This is so I can use macmini as a remote builder to offload some work from
29 | # the more thermally constrained laptop, but unfortunately because the laptop
30 | # isn't managed with nix-darwin, I have to manually configure the builder :(
31 | "${flake}/hosts/macbook-air/id_ed25519.pub"
32 | "${flake}/hosts/macbook-air/users/robert/id_ed25519.pub"
33 | # Allows me to SSH into myself, which is useful sometimes
34 | "${flake}/hosts/macmini/users/robert/id_ed25519.pub"
35 | ];
36 | };
37 |
38 | # This needs to be reapplied after each system update. My Fish configuration
39 | # will warn about this if it detects the line it adds to sudo_local is absent.
40 | security.pam.services.sudo_local.touchIdAuth = true;
41 |
42 | networking.hostName = "macmini";
43 |
44 | system.stateVersion = 6;
45 | system.primaryUser = "robert";
46 |
47 | home-manager.backupFileExtension = "hm-backup";
48 |
49 | nix-homebrew.enable = true;
50 | # A user needs to own the prefix, so we'll make it my account
51 | nix-homebrew.user = "robert";
52 |
53 | environment.systemPackages = [
54 | pkgs.fish
55 | pkgs.mosh
56 | ];
57 |
58 | # Not sure about adding in vendor_conf.d because tools can just dump their init into it,
59 | # and because it will be included in a directory in $NIX_PROFILES, therefore
60 | # also $XDG_DATA_DIRS, it will be sourced during startup. This is probably fine, but
61 | # I want total control over what runs in my fish config.
62 | environment.pathsToLink = [
63 | # "/share/fish/vendor_conf.d"
64 | "/share/fish/vendor_completions.d"
65 | "/share/fish/vendor_functions.d"
66 | ];
67 |
68 | nixpkgs.hostPlatform = "aarch64-darwin";
69 |
70 | nix.enable = true;
71 | nix.nixPath = lib.mkForce [
72 | "nixpkgs=${inputs.nixpkgs}"
73 | "home-manager=${inputs.home-manager}"
74 | ];
75 | nix.settings.experimental-features = [
76 | "nix-command"
77 | "flakes"
78 | ];
79 | nix.settings.trusted-users = [ "@admin" ];
80 | nix.channel.enable = false;
81 |
82 | nix.distributedBuilds = true;
83 | nix.buildMachines = [
84 | {
85 | hostName = "homeserver1";
86 | sshUser = "robert";
87 | system = "x86_64-linux";
88 | maxJobs = 8;
89 | supportedFeatures = [
90 | "kvm"
91 | "benchmark"
92 | "big-parallel"
93 | ];
94 | }
95 | ];
96 |
97 | services.tailscale.enable = true;
98 | services.openssh.enable = true;
99 |
100 | # Good example for how to disable SSH password authentication with nix-darwin.
101 | # I want to use the builtin macOS SSH server, but with declarative config.
102 | environment.etc."ssh/sshd_config.d/999-disable-password-auth.conf".text = ''
103 | PermitRootLogin no
104 | PasswordAuthentication no
105 | KbdInteractiveAuthentication no
106 | UsePAM no
107 | '';
108 | }
109 |
--------------------------------------------------------------------------------
/config/fish/conf.d/environment.fish:
--------------------------------------------------------------------------------
1 | # These variables will not be pathified by Fish by default, but
2 | # they behave like path variables, so it makes sense to treat them
3 | # as such.
4 | for p in XDG_DATA_DIRS XDG_CONFIG_DIRS TERMINFO_DIRS
5 | # @fish-lsp-disable-next-line 3003
6 | set --path $p $$p
7 | end
8 |
9 | # It's possible that `hx` isn't available if I totally bungle my $PATH
10 | if command -q hx
11 | set -x EDITOR hx
12 | else
13 | set -x EDITOR vim
14 | end
15 |
16 | # Setting this to an empty string makes direnv silent
17 | set -x DIRENV_LOG_FORMAT
18 |
19 | # SQLite and Postgres both store their history files in a stupid location
20 | # by default, so this moves the histories to the data dir where they belong.
21 | set -x PSQL_HISTORY $HOME/.local/share/psql/psql_history
22 | set -l psql_history_dir (path dirname $PSQL_HISTORY)
23 | test -d $psql_history_dir; or mkdir -p $psql_history_dir
24 |
25 | set -x SQLITE_HISTORY $HOME/.local/share/sqlite3/sqlite_history
26 | set -l sqlite_history_dir (path dirname $SQLITE_HISTORY)
27 | test -d $sqlite_history_dir; or mkdir -p $sqlite_history_dir
28 |
29 | if test -d $HOME/.local/bin; and not contains -- $HOME/.local/bin $PATH
30 | set --append PATH $HOME/.local/bin
31 | end
32 |
33 | # Homebrew should be available, but I don't want anything installed by
34 | # it to take precendence over anything installed by Nix.
35 | if test -d /opt/homebrew/bin; and not contains -- /opt/homebrew/bin $PATH
36 | set --append PATH /opt/homebrew/bin
37 | end
38 |
39 | # Fish comes with some builtin aliases for ls, which we don't want.
40 | # Instead, `l` is an abbreviation defined in the abbreviations.fish file.
41 | functions -e la ll
42 |
43 | if set -q SSH_CLIENT; or set -q SSH_TTY
44 | set -x BROWSER echo
45 | end
46 |
47 | set -x FZF_CTRL_T_COMMAND "fd --type file --strip-cwd-prefix --follow"
48 | set -x FZF_ALT_C_COMMAND "fd --type directory --strip-cwd-prefix"
49 |
50 | set -g skip_trash_directories .git node_modules target .direnv .nx venv .venv dist result build
51 | set -g skip_trash_files .DS_Store
52 |
53 | # Something has gone wrong if the first item in these paths is not the
54 | # user configuration directories, but *if* it isn't, correct it so that
55 | # user functions will always win.
56 | # Shells are not pleasant to debug, so I want to give myself a head start
57 | # by making it hard to ignore errors.
58 | if test $fish_function_path[1] != $HOME/.config/fish/functions
59 | set --prepend fish_function_path $HOME/.config/fish/functions
60 | echo "$(set_color brred)WARNING:$(set_color normal) $(set_color --bold)fish_function_path[1]$(set_color normal) was not $(set_color --italics)'$HOME/.config/fish/functions'$(set_color normal), fixing for this shell."
61 | echo " You should take some time to figure out the root cause - something is misconfigured".
62 | echo
63 | for line in $fish_function_path
64 | if test $line = $HOME/.config/fish/functions
65 | set_color --bold
66 | echo -- " -> "$line
67 | set_color normal
68 | else
69 | echo -- " "$line
70 | end
71 | end
72 | echo
73 | end
74 | if test $fish_complete_path[1] != $HOME/.config/fish/completions
75 | set --prepend fish_complete_path $HOME/.config/fish/completions
76 | echo "$(set_color brred)WARNING:$(set_color normal) $(set_color --bold)fish_complete_path[1]$(set_color normal) was not $(set_color --italics)'$HOME/.config/fish/completions'$(set_color normal), fixing for this shell."
77 | echo " You should take some time to figure out the root cause - something is misconfigured".
78 | echo
79 | for line in $fish_complete_path
80 | if test $line = $HOME/.config/fish/completions
81 | set_color --bold
82 | echo -- " -> "$line
83 | set_color normal
84 | else
85 | echo -- " "$line
86 | end
87 | end
88 | echo
89 | end
90 |
--------------------------------------------------------------------------------
/config/helix/config.toml:
--------------------------------------------------------------------------------
1 | theme = "gruvbox"
2 |
3 | [editor]
4 | bufferline = "multiple"
5 | color-modes = true
6 | completion-trigger-len = 1
7 | idle-timeout = 0
8 | line-number = "relative"
9 | true-color = true
10 |
11 | [editor.cursor-shape]
12 | insert = "bar"
13 | normal = "block"
14 | select = "underline"
15 |
16 | [editor.indent-guides]
17 | character = "▏"
18 | render = true
19 | skip-levels = 1
20 |
21 | [editor.inline-diagnostics]
22 | cursor-line = "hint"
23 | other-lines = "hint"
24 |
25 | [editor.lsp]
26 | display-inlay-hints = true
27 | display-messages = true
28 |
29 | [editor.statusline]
30 | right = ["diagnostics", "selections", "position", "position-percentage", "file-encoding"]
31 |
32 | [editor.whitespace.characters]
33 | newline = "↵"
34 |
35 | [editor.whitespace.render]
36 | newline = "all"
37 |
38 | [keys.insert]
39 | C-h = ":toggle-option lsp.display-inlay-hints"
40 | C-space = "completion"
41 |
42 | [keys.normal]
43 | "*" = ["trim_selections", "search_selection", "select_mode"]
44 | C-R = ":reload-all"
45 | C-d = ["half_page_down", "goto_window_center"]
46 | C-h = ":toggle-option lsp.display-inlay-hints"
47 | C-q = ":quit-all"
48 | C-r = ":reload"
49 | C-u = ["half_page_up", "goto_window_center"]
50 | C-w = ":quit"
51 | D = "goto_word"
52 | S-down = "jump_view_down"
53 | S-left = "jump_view_left"
54 | S-right = "jump_view_right"
55 | S-up = "jump_view_up"
56 | a = ["append_mode", "collapse_selection"]
57 | i = ["insert_mode", "collapse_selection"]
58 | C-t = ["delete_selection", "paste_after"]
59 |
60 | [keys.normal.Z]
61 | C-d = ["half_page_down", "goto_window_center"]
62 | C-u = ["half_page_up", "goto_window_center"]
63 | D = ["scroll_down", "scroll_down", "scroll_down", "scroll_down", "scroll_down"]
64 | E = ["scroll_down", "scroll_down", "scroll_down", "scroll_down", "scroll_down"]
65 | J = ["scroll_down", "scroll_down", "scroll_down", "scroll_down", "scroll_down"]
66 | K = ["scroll_up", "scroll_up", "scroll_up", "scroll_up", "scroll_up"]
67 | U = ["scroll_up", "scroll_up", "scroll_up", "scroll_up", "scroll_up"]
68 | Y = ["scroll_up", "scroll_up", "scroll_up", "scroll_up", "scroll_up"]
69 | d = "scroll_down"
70 | e = "scroll_down"
71 | u = "scroll_up"
72 | y = "scroll_up"
73 |
74 | [keys.normal."`"]
75 | C = ["trim_selections", ":pipe ccase --to uppercamel"]
76 | K = ["trim_selections", ":pipe ccase --to upperkebab"]
77 | S = ["trim_selections", ":pipe ccase --to screamingsnake"]
78 | c = ["trim_selections", ":pipe ccase --to camel"]
79 | k = ["trim_selections", ":pipe ccase --to kebab"]
80 | r = ["trim_selections", ":pipe ccase --to pseudorandom"]
81 | s = ["trim_selections", ":pipe ccase --to snake"]
82 | t = ["trim_selections", ":pipe ccase --to title"]
83 |
84 | [keys.normal.g]
85 | down = "move_line_down"
86 | left = "goto_line_start"
87 | right = "goto_line_end"
88 | up = "move_line_up"
89 |
90 | [keys.normal.space]
91 | D = "hsplit"
92 | d = "vsplit"
93 | u = ":reset-diff-change"
94 | B = ":echo %sh{git blame -L %{cursor_line},+1 %{buffer_name}}"
95 |
96 | [keys.normal.space.w]
97 | S = ["hsplit_new", "file_picker"]
98 | V = ["vsplit_new", "file_picker"]
99 |
100 | [keys.select]
101 | ";" = ["collapse_selection", "normal_mode"]
102 | C-d = ["half_page_down", "goto_window_center"]
103 | C-h = ":toggle-option lsp.display-inlay-hints"
104 | C-u = ["half_page_up", "goto_window_center"]
105 | D = "extend_to_word"
106 | S-down = "jump_view_down"
107 | S-left = "jump_view_left"
108 | S-right = "jump_view_right"
109 | S-up = "jump_view_up"
110 | a = ["append_mode", "collapse_selection"]
111 | i = ["insert_mode", "collapse_selection"]
112 | C-t = ["delete_selection", "paste_after"]
113 |
114 | [keys.select."`"]
115 | C = ["trim_selections", ":pipe ccase --to uppercamel"]
116 | K = ["trim_selections", ":pipe ccase --to upperkebab"]
117 | S = ["trim_selections", ":pipe ccase --to screamingsnake"]
118 | c = ["trim_selections", ":pipe ccase --to camel"]
119 | k = ["trim_selections", ":pipe ccase --to kebab"]
120 | r = ["trim_selections", ":pipe ccase --to pseudorandom"]
121 | s = ["trim_selections", ":pipe ccase --to snake"]
122 | t = ["trim_selections", ":pipe ccase --to title"]
123 |
124 | [keys.select.g]
125 | down = "move_line_down"
126 | left = "goto_line_start"
127 | right = "goto_line_end"
128 | up = "move_line_up"
129 |
130 | [keys.select.space]
131 | D = "hsplit"
132 | d = "vsplit"
133 |
134 |
--------------------------------------------------------------------------------
/config/fish/functions/run.fish:
--------------------------------------------------------------------------------
1 | # There are an annoying amount of steps required for proper isolation
2 | # since this function is initially executed in our current environment.
3 | # When 'run.fish' is found, it needs to be executed in its own fish
4 | # (and you can't rely on fish being in $PATH because this function is
5 | # used for bootstrapping).
6 | # Constraints:
7 | # - Fish syntax is too complicated to reasonably grep function definitions
8 | # - Don't want to execute twice
9 | # Which means the script *has* to be sourced to bring its functions into
10 | # the local scope.
11 | # If it were just sourced normally, it would have access to local variables
12 | # such as pre_functions, which is undesirable, so it has to be sourced
13 | # from a different function with no locals.
14 | # All the functions defined will be in scope for 'run.fish' too, which
15 | # means a name collision would be possible. The functions names have to be
16 | # prefixed to avoid this.
17 | # Two extra requirements: 1) must preserve exit status, 2) executes in same
18 | # directory as the file.
19 |
20 | function run
21 | # 'test' is unable to test for '-h' because it sees it as a flag.
22 | switch $argv[1]
23 | case -h --help
24 | echo "Usage: run [function [args...]]
25 |
26 | Run fish functions defined in a single file. It's a command runner,
27 | similar to just, but you get use the best shell scripting language.
28 |
29 | Create a file named 'run.fish' and write some functions in it.
30 | Invoking 'run' will source the file in an isolated fish environment
31 | and invoke whichever function was named.
32 |
33 | Run 'run' without any arguments to get a list of public functions.
34 | Function names beginning with an underscore are private.
35 |
36 | The script itself, and all functions, will be invoked from the
37 | directory containing the file. All output from the top level of the
38 | script will be silenced, functions will not be."
39 | return
40 | end
41 |
42 | set -l dir (pwd)
43 | set -l file_path
44 | while test "$dir" != /
45 | if test -e "$dir/run.fish"
46 | set file_path "$dir/run.fish"
47 | break
48 | end
49 | set dir (dirname "$dir")
50 | end
51 |
52 | if not set -q file_path[1]
53 | echo (set_color brred)"error:"(set_color normal)" run.fish not found in current or parent directories"
54 | return 1
55 | end
56 |
57 | set -l fish_path (status fish-path)
58 |
59 | $fish_path --no-config -c "
60 | function __private_main
61 | set -l pre_functions (functions --names)
62 |
63 | cd \"$dir\"
64 | # Not all errors from sourcing can be detected. Instead, we're just
65 | # going to assume that everything went okay, because partial failure
66 | # won't actually impact the execution at all.
67 | __private_source \"$file_path\" &>/dev/null
68 |
69 | set -l post_functions (functions --names)
70 |
71 | set -l new_functions
72 | for function in \$post_functions
73 | if not contains -- \$function \$pre_functions; and not string match \"_*\" -- \$function
74 | set --append new_functions \$function
75 | end
76 | end
77 |
78 | if not set -q argv[1]
79 | if test \"\$__COMPLETE_RUN_DESCRIPTIONS\" != 1
80 | for function in \$new_functions
81 | echo -- \$function
82 | end
83 | else
84 | for function in \$new_functions
85 | set desc (functions -vD \$function | sed -n -e \"5{p;q}\")
86 | echo -- \$function\\t\$desc
87 | end
88 | end
89 | else if contains -- \$argv[1] \$new_functions
90 | # The directory could have changed when the script was sourced,
91 | # e.g. top-level 'cd' job
92 | cd \"$dir\"
93 | \$argv
94 | return \$status
95 | else
96 | echo (set_color brred)\"error:\"(set_color normal)\" did not match a function name: \$argv[1]\"
97 | end
98 | end
99 |
100 | function __private_source
101 | source \$argv
102 | end
103 |
104 | __private_main $argv
105 | " $argv
106 | set -l result_status $status
107 |
108 | return $result_status
109 | end
110 |
--------------------------------------------------------------------------------
/modules/home/my-programs-fish.nix:
--------------------------------------------------------------------------------
1 | # Declarative fish plugin installation
2 | {
3 | pkgs,
4 | config,
5 | lib,
6 | ...
7 | }:
8 | let
9 | cfg = config.my.programs.fish;
10 |
11 | fishIndent =
12 | name: text:
13 | pkgs.runCommand name {
14 | nativeBuildInputs = [ pkgs.fish ];
15 | inherit text;
16 | passAsFile = [ "text" ];
17 | } "env HOME=$(mktemp -d) fish_indent < $textPath > $out";
18 |
19 | babelfishTranslate =
20 | path: name:
21 | pkgs.runCommand "${name}.fish" { } ''
22 | ${pkgs.babelfish}/bin/babelfish < ${path} > $out
23 | '';
24 | in
25 | {
26 | options = {
27 | my.programs.fish.plugins = lib.mkOption {
28 | type = lib.types.listOf lib.types.path;
29 | description = ''
30 | Plugins that will be installed and activated.
31 | '';
32 | };
33 | };
34 |
35 | config = {
36 | # FIXME: This is no longer necessary now that ZSH handles sourcing it.
37 | # xdg.dataFile."fish/vendor_conf.d/00_hm-session-vars.fish".source =
38 | # let
39 | # sessionVars = "${config.home.sessionVariablesPackage}/etc/profile.d/hm-session-vars.sh";
40 | # in
41 | # babelfishTranslate sessionVars "hm-session-vars";
42 |
43 | xdg.dataFile."fish/vendor_conf.d/00_source_plugins.fish".source = lib.mkIf (cfg.plugins != [ ]) (
44 | fishIndent "source_plugins.fish" ''
45 | for plugin in ${lib.concatStringsSep " " cfg.plugins}
46 | if test -d $plugin/functions
47 | set fish_function_path $fish_function_path[1] $plugin/functions $fish_function_path[2..]
48 | end
49 |
50 | if test -d $plugin/completions
51 | set fish_complete_path $fish_complete_path[1] $plugin/completions $fish_complete_path[2..]
52 | end
53 |
54 | # Sourcing files in both places allows me to support OMF plugins too, just in case.
55 | for file in $plugin/conf.d/*.fish $plugin/*.fish
56 | test -f $file -a -r $file
57 | and source $file
58 | end
59 | end
60 | ''
61 | );
62 |
63 | # The following has been adapted from the Fish module of home-manager:
64 | # https://github.com/nix-community/home-manager/blob/0d7908bd09165db6699908b7e3970f137327cbf0/modules/programs/fish.nix#L4
65 | # This is temporary and will be cleaned up in the future.
66 |
67 | programs.man.generateCaches = true;
68 |
69 | xdg.dataFile."fish/generated_completions".source =
70 | let
71 | # paths later in the list will overwrite those already linked
72 | destructiveSymlinkJoin =
73 | args_@{
74 | name,
75 | paths,
76 | preferLocalBuild ? true,
77 | allowSubstitutes ? false,
78 | postBuild ? "",
79 | ...
80 | }:
81 | let
82 | args =
83 | removeAttrs args_ [
84 | "name"
85 | "postBuild"
86 | ]
87 | // {
88 | # pass the defaults
89 | inherit preferLocalBuild allowSubstitutes;
90 | };
91 | in
92 | pkgs.runCommand name args ''
93 | mkdir -p $out
94 | for i in $paths; do
95 | if [ -z "$(find $i -prune -empty)" ]; then
96 | cp -srf $i/* $out
97 | fi
98 | done
99 | ${postBuild}
100 | '';
101 |
102 | generateCompletions =
103 | let
104 | getName =
105 | attrs: attrs.name or "${attrs.pname or "«pname-missing»"}-${attrs.version or "«version-missing»"}";
106 | in
107 | package:
108 | pkgs.runCommand "${getName package}-fish-completions"
109 | {
110 | srcs = [
111 | package
112 | ]
113 | ++ lib.filter (p: p != null) (
114 | builtins.map (outName: package.${outName} or null) config.home.extraOutputsToInstall
115 | );
116 | nativeBuildInputs = [ pkgs.python3 ];
117 | buildInputs = [ pkgs.fish ];
118 | preferLocalBuild = true;
119 | }
120 | ''
121 | mkdir -p $out
122 | for src in $srcs; do
123 | if [ -d $src/share/man ]; then
124 | find -L $src/share/man -type f \
125 | | xargs python ${pkgs.fish}/share/fish/tools/create_manpage_completions.py --directory $out \
126 | > /dev/null
127 | fi
128 | done
129 | '';
130 | in
131 | destructiveSymlinkJoin {
132 | name = "${config.home.username}-fish-completions";
133 | paths =
134 | let
135 | cmp = (a: b: (a.meta.priority or 0) > (b.meta.priority or 0));
136 | in
137 | map generateCompletions (lib.sort cmp config.home.packages);
138 | };
139 |
140 | xdg.dataFile."fish/vendor_conf.d/99_generated_completions.fish".source =
141 | fishIndent "99_generated_completions.fish" ''
142 | set -l genpath ${config.xdg.dataHome}/fish/generated_completions
143 |
144 | if test -d $genpath; and not contains -- genpath $PATH
145 | set --append fish_complete_path $genpath
146 | end
147 | '';
148 | };
149 | }
150 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # clo4's configuration
2 |
3 | This is my dead-simple configuration. Maybe something to take inspiration from
4 | if you're getting stuck configuring your own Nix flake setup! I currently
5 | actively maintain my nix-darwin and standalone Home Manager configurations, but
6 | supporting NixOS or WSL would be as simple as adding another entry to `hosts`.
7 |
8 | Traditional Nix system configuration requires a rebuild to apply, but I found
9 | that this was slowing me down and disincentivising me from changing things on my
10 | system. This time around, everything about the way my config is structured is to
11 | allow me to iterate raplidly:
12 |
13 | - Home Manager symlinks my config to the right location. This results in the
14 | normal instant feedback loop of editing your dotfiles, but with the certainty
15 | that you can reapply it at any point exactly the same way it is now.
16 | - The flake uses [Blueprint](https://github.com/numtide/blueprint), which gets
17 | rid of all the glue code I would otherwise need. Creating hosts is as simple
18 | as creating a directory in `hosts`, or making a package, creating a file in
19 | `packages`.
20 |
21 | Program configuration is all stored in [config](/config).
22 |
23 | My shared Home Manager config applies this configuration:
24 | [users/robert/home-configuration.nix](/users/robert/home-configuration.nix)
25 |
26 | This configuration is applied per-host with tweaks on top of it: [hosts](/hosts)
27 |
28 | ## Custom stuff
29 |
30 | I keep finding yaks to shave.
31 |
32 | - Fish is my interactive shell, but all _initial_ environment setup is delegated
33 | to ZSH. This means the login shell is always guaranteed to be POSIX-enough to
34 | work, but I still get to benefit from using the _best_ shell. I'm particularly
35 | proud that the `$PATH` works flawlessly on my Darwin systems, which has been a
36 | known issue with Fish + Nix.
37 | - I implemented a Fish plugin manager in Nix for declarative plugins with
38 | imperative configuration. Plugins don't clutter up my config, nor can they
39 | accidentally clobber any files. Updates are done by updating the Nix plugin
40 | package definitions.
41 | - This config uses
42 | [mattwparas' fork of Helix](https://github.com/mattwparas/helix/tree/steel-event-system)
43 | with support for plugins, though I haven't set up or written any plugins yet.
44 | I had to fork it to get the Steel language server integration working. A build
45 | that uses this version and has working a working scheme language server for
46 | building plugins is available by running
47 | `nix run github:clo4/nix-dotfiles#helix`.
48 | - Homebrew is installed automatically and managed declaratively with
49 | [nix-homebrew](https://github.com/zhaofengli/nix-homebrew)
50 | - Since fish is the best language for shell scripting, I wrote a custom command
51 | runner for fish functions. It replaces tools like `just` and common abuse of
52 | `make`. You just write fish functions in `run.fish`, then run them with `run`.
53 | This is also usable as `nix run github:clo4/nix-dotfiles#run`.
54 | - My server hosts a Minecraft server using `virtualisation.oci-containers` with
55 | podman. The container is launched as root, but switched to a system
56 | user/group. Its data is stored in `/srv/minecraft/family`. It restarts every
57 | day at 4 am local-time. It has a DDNS client. I think this may be the best
58 | reference configuration for setting up a Minecraft server on NixOS.
59 | - I built a simple DDNS client for Cloudflare to keep my DNS record up to date
60 | with my home internet's IP address. It's a simple Go program, it compiles
61 | quickly, it caches IP to minimise useless updates. The credentials are stored
62 | encrypted with Agenix. It's been moved into its own repository:
63 | [clo4/clouddns](https://github.com/clo4/clouddns)
64 |
65 | More of my custom things will be documented in the future.
66 |
67 | ## Hosts
68 |
69 | - `macmini`
70 | - This is my main dev machine. Most configuration will be up-to-date for it.
71 | It's a nix-darwin system that also configures my user using Home Manager.
72 | - `macbook-air`
73 | - This is my secondary computer. It's owned by my partner, so I haven't
74 | installed nix-darwin. Instead, this is a standalone configuration.
75 | - `homeserver1`
76 | - This is my personal server. It's a Minisforum NAB6 Lite running an Intel
77 | Core i5 12600, so it uses <9w at idle.
78 | - Hopefully not the first of many, but knowing me, it's best to start adding
79 | versioning to the names.
80 |
81 | ### Building and switching
82 |
83 | As a way to make sure I always get the commands right, there's a command runner
84 | included in the developer environment named `run`. Run `run` with no arguments
85 | to print the available commands.
86 |
87 | Switching `macmini` and `macbook-air` will attempt to switch the currently
88 | active device, but switching `homeserver1` will cause the server to rebuild and
89 | switch remotely, allowing the command to be run from whatever device is being
90 | used without an SSH connection. The only requirement is that Tailscale is up and
91 | connected.
92 |
93 | `run` can be used from any shell, but is intended for use within Fish.
94 |
95 | ### Bootstrapping homeserver1
96 |
97 | This isn't included in `run.fish` because it has to be executed from the system
98 | itself or over SSH.
99 |
100 | ```bash
101 | sudo nix --extra-experimental-features 'nix-command flakes' run github:clo4/nix-dotfiles/vps#homeserver1-install
102 | ```
103 |
104 | ## Personal notes
105 |
106 | ### Moving configuration directories
107 |
108 | To migrate from one directory to another, create an exact copy of the
109 | configuration in the new destination **without removing the existing
110 | configuration.** Once created, edit the new configuration, changing
111 | `my.config.directory` to whatever the new path is. Now you can reapply the
112 | configuration from the new directory.
113 |
--------------------------------------------------------------------------------
/users/robert/home-configuration.nix:
--------------------------------------------------------------------------------
1 | # This is the shared user configuration applied and customised by each
2 | # host's `robert` user.
3 | {
4 | pkgs,
5 | pkgs',
6 | perSystem,
7 | inputs,
8 | config,
9 | flake,
10 | ...
11 | }@args:
12 | let
13 | steelWithLsp = perSystem.steel.default.overrideAttrs (oldAttrs: {
14 | cargoBuildFlags = "-p cargo-steel-lib -p steel-interpreter -p steel-language-server";
15 | });
16 |
17 | neovimWithDependencies = pkgs.symlinkJoin {
18 | name = "neovim-with-dependencies";
19 | paths = [ pkgs.neovim ];
20 | buildInputs = [ pkgs.makeWrapper ];
21 | postBuild = ''
22 | wrapProgram $out/bin/nvim \
23 | --prefix PATH : ${
24 | pkgs.lib.makeBinPath [
25 | pkgs.curl
26 | pkgs.tree-sitter
27 | pkgs.ripgrep
28 | ]
29 | }
30 | '';
31 | };
32 | in
33 | {
34 | imports = [
35 | inputs.self.homeModules.my-config
36 | inputs.self.homeModules.my-programs-fish
37 | inputs.self.homeModules.my-programs-neovim
38 | inputs.self.modules.common.nixpkgs-unstable
39 | ./darwin.nix
40 |
41 | "${flake}/config/nvim/plugins.nix"
42 | ];
43 |
44 | home.packages = [
45 | perSystem.helix.helix
46 | # perSystem.helix.helix-cogs
47 | # steelWithLsp
48 | perSystem.self.schemat
49 | perSystem.self.ccase # Case conversion used in my Helix keybindings (TODO: port to scheme plugin?)
50 | pkgs.curl
51 | pkgs'.stripe-cli
52 | pkgs.gh
53 | pkgs.delta
54 | pkgs.direnv
55 | pkgs.eza
56 | pkgs.fd
57 | pkgs.fish
58 | pkgs.fish-lsp
59 | pkgs.fzf
60 | pkgs.git
61 | pkgs.git-open
62 | pkgs.gum
63 | pkgs.home-manager
64 | pkgs.jq
65 | pkgs.jujutsu
66 | pkgs.just
67 | pkgs.lazygit
68 | neovimWithDependencies
69 | pkgs.nix-direnv
70 | pkgs.nix-output-monitor
71 | pkgs.nixfmt-rfc-style
72 | pkgs.nushell
73 | pkgs.ripgrep
74 | pkgs.tealdeer
75 | pkgs.tmux
76 | pkgs.tree
77 | pkgs.vim
78 | pkgs.wget
79 | pkgs.zoxide
80 |
81 | # Fonts
82 | pkgs.nerd-fonts.roboto-mono
83 | ];
84 |
85 | fonts.fontconfig.enable = !pkgs.stdenv.isDarwin;
86 |
87 | my.config.source =
88 | let
89 | # Some tools prefer to place their configuration in the correct directory
90 | # for the platform. On Linux, that's XDG_CONFIG_HOME, which defaults to
91 | # ~/.config if unset. On macOS, the configuration directory is
92 | # '~/Library/Application Support'.
93 | # Unfortunately, this isn't common - most tools simply use ~/.config
94 | # regardless of platform conventions.
95 | platformConfig = if pkgs.stdenv.isDarwin then "Library/Application Support" else ".config";
96 | in
97 | {
98 | ".config/ghostty/config" = "config/ghostty/config";
99 | ".config/ghostty/os-config" =
100 | if pkgs.stdenv.isDarwin then
101 | "config/ghostty/os-config-darwin"
102 | else
103 | "config/ghostty/os-config-linux";
104 |
105 | ".config/kitty" = "config/kitty";
106 |
107 | ".config/helix" = "config/helix";
108 |
109 | ".config/nvim" = "config/nvim";
110 |
111 | ".config/tmux" = "config/tmux";
112 |
113 | ".config/git" = "config/git";
114 | "${platformConfig}/jj" = "config/jj";
115 |
116 | ".config/direnv/direnv.toml" = "config/direnv/direnv.toml";
117 |
118 | # Fish can't just link the config directory because if the flake directory
119 | # is used as my.config.directory (which is only true on new home manager
120 | # systems during bootstrapping) then it will try to write to the fish_variables
121 | # file repeatedly and fail each time, spamming the terminal with errors.
122 | # It's better to link each of the directories individually to avoid this.
123 | ".config/fish/conf.d" = "config/fish/conf.d";
124 | ".config/fish/functions" = "config/fish/functions";
125 | ".config/fish/completions" = "config/fish/completions";
126 | ".config/fish/config.fish" = "config/fish/config.fish";
127 |
128 | # There needs to be two zshenv files because when the top-level
129 | # zshenv is executed, it would not normally execute the zshenv in the ZDOTDIR.
130 | ".zshenv" = "config/zsh/home_zshenv";
131 | ".config/zsh" = "config/zsh";
132 |
133 | # Allows imperative NPM package installation and management, low friction way
134 | # to install and manage things like Claude Code
135 | ".npmrc" = "config/npm/npmrc";
136 | };
137 |
138 | # This needs to be in a known location so it can be sourced regardless
139 | # of whether we're in standalone HM or as a system module.
140 | home.file.".local/share/zsh/hm-session-vars.sh".source =
141 | "${config.home.sessionVariablesPackage}/etc/profile.d/hm-session-vars.sh";
142 |
143 | # Since hm-session-vars is consistent and will be sourced, ZDOTDIR can
144 | # be set declaratively and will be used by ZSH during init.
145 | home.sessionVariables.ZDOTDIR = "$HOME/.config/zsh";
146 |
147 | # Ordinarily, the direnv module would set this automatically.
148 | home.file.".config/direnv/lib/nix-direnv.sh".source =
149 | "${pkgs.nix-direnv}/share/nix-direnv/direnvrc";
150 |
151 | my.programs.fish.plugins = [
152 | (pkgs.fetchFromGitHub {
153 | owner = "IlanCosman";
154 | repo = "tide";
155 | rev = "44c521ab292f0eb659a9e2e1b6f83f5f0595fcbd"; # as of 2025-01-01
156 | hash = "sha256-85iU1QzcZmZYGhK30/ZaKwJNLTsx+j3w6St8bFiQWxc=";
157 | })
158 | ];
159 |
160 | home.sessionVariables.NIX_CONFIG_REV = flake.rev or flake.dirtyRev;
161 | home.sessionVariables.NIX_CONFIG_DIR = config.my.config.directory;
162 | home.sessionVariables.NIX_CONFIG_LAST_MODIFIED = builtins.toString flake.lastModified;
163 |
164 | nix.registry = {
165 | nixpkgs.flake = inputs.nixpkgs;
166 | nixpkgs-unstable.flake = inputs.nixpkgs-unstable;
167 | blueprint.flake = inputs.blueprint;
168 | home-manager.flake = inputs.home-manager;
169 | nix-darwin.flake = inputs.nix-darwin;
170 | helix.flake = inputs.helix;
171 | };
172 | nix.settings.log-lines = 25;
173 | }
174 |
--------------------------------------------------------------------------------
/hosts/homeserver1/minecraft/servers/family.nix:
--------------------------------------------------------------------------------
1 | {
2 | pkgs,
3 | ...
4 | }:
5 | let
6 | uid = 399;
7 | gid = 398;
8 | in
9 | {
10 | users.users.minecraft-family = {
11 | inherit uid;
12 | isSystemUser = true;
13 | group = "minecraft-family";
14 | home = "/srv/minecraft/family";
15 |
16 | # I'm not certain if these are necessary any more, but they don't hurt.
17 | linger = true;
18 | autoSubUidGidRange = true;
19 | };
20 |
21 | # The group needs its own GID because the container references it directly.
22 | users.groups.minecraft-family = {
23 | inherit gid;
24 | };
25 |
26 | users.users.robert.extraGroups = [ "minecraft-family" ];
27 |
28 | # rwxrwx--- so the minecraft-family group can also make changes.
29 | systemd.tmpfiles.rules = [
30 | "d /srv/minecraft/family 0770 minecraft-family minecraft-family -"
31 | ];
32 |
33 | virtualisation.oci-containers.containers.minecraft-family = {
34 | image = "docker.io/itzg/minecraft-server";
35 | autoStart = true;
36 |
37 | # There are multiple ways to connect to a Minecraft server without entering
38 | # any port information. I'm choosing to use SRV records instead of A records
39 | # because I want to add more servers in the future and doing so means I'd
40 | # have to use a routing solution like mc-router to inspect incoming packets
41 | # and route them to the right port based on the address the client was
42 | # connected to. This way, the client connects right to the desired server
43 | # instance.
44 | ports = [
45 | # Minecraft server, using SRV record so clients connect to the right port.
46 | # This also has the advantage that the server scanners don't see the server,
47 | # since it's not hosted on the default port.
48 | "25580:25565"
49 | # UDP is used for server queries
50 | "25580:25565/udp"
51 | # Simple voice chat. It has to be configured in the mod settings to use
52 | # this port.
53 | "24480:24480/udp"
54 | ];
55 |
56 | # Launched by root, then changed to minecraft-family:minecraft-family.
57 | # Because the user has been set, it's also respected inside the container.
58 | user = "${toString uid}:${toString gid}";
59 |
60 | # All the actual server configuration is done manually in server.properties.
61 | environment = {
62 | EULA = "TRUE";
63 | TYPE = "FABRIC";
64 | VERSION = "1.21.4";
65 |
66 | # Security
67 | ENABLE_WHITELIST = "TRUE";
68 | WHITELIST = "clo4_";
69 | OPS = "clo4_";
70 |
71 | # Performance settings
72 | MEMORY = "6G";
73 | USE_AIKAR_FLAGS = "TRUE";
74 |
75 | # Auto-pause configuration
76 | ENABLE_AUTOPAUSE = "TRUE";
77 | # Wait 1 minute after server initialisation before pausing
78 | AUTOPAUSE_TIMEOUT_INIT = "60";
79 | # Wait 30 minutes after last player disconnection before pausing
80 | AUTOPAUSE_TIMEOUT_EST = "1800";
81 | # This fixes the "unable to start knockd" issue
82 | SKIP_SUDO = "TRUE";
83 |
84 | # Disable unnecessary watchdog timers
85 | MAX_TICK_TIME = "-1";
86 | WATCHDOG = "-1";
87 |
88 | MODRINTH_PROJECTS = ''
89 | almanac
90 | appleskin
91 | balm
92 | blossomlib
93 | blossomtpa
94 | blossomwarps
95 | c2me-fabric
96 | continents
97 | convenient-mobgriefing
98 | clumps
99 | datapack:afk-sleep
100 | datapack:detect-afk
101 | datapack:no-free-deaths
102 | datapack:pause-day-cycle
103 | distanthorizons:beta
104 | fabric-api
105 | ferrite-core
106 | forge-config-api-port
107 | geophilic
108 | ksyxis
109 | lithium
110 | leaves-be-gone
111 | lmd
112 | modernfix
113 | netherportalfix
114 | no-shield-delay
115 | noisium
116 | puzzles-lib
117 | scalablelux
118 | simple-voice-chat:beta
119 | sit!
120 | '';
121 | };
122 |
123 | volumes = [
124 | "/srv/minecraft/family:/data"
125 | ];
126 |
127 | extraOptions = [
128 | "--cap-add=CAP_NET_RAW" # Required for autopause
129 | "--no-healthcheck"
130 | "--tty"
131 | ];
132 | };
133 |
134 | systemd.services.podman-minecraft-family = {
135 | after = [ "network.target" ];
136 | requires = [ "network.target" ];
137 | };
138 |
139 | # Service to restart the Minecraft container
140 | systemd.services.minecraft-family-restart = {
141 | description = "Restart Minecraft Family Server";
142 | requires = [ "podman-minecraft-family.service" ];
143 | after = [ "podman-minecraft-family.service" ];
144 | script = ''
145 | # If the server is currently paused, the autopause daemon creates .paused
146 | # in the /data mount, so if this file is present then don't wake the server
147 | # up to send a message to nobody.
148 | function is_paused() {
149 | [ -f /srv/minecraft/family/.paused ]
150 | }
151 | is_paused || ${pkgs.podman}/bin/podman exec minecraft-family rcon-cli "say Server will restart in 5 minutes."
152 | sleep 240
153 | is_paused || ${pkgs.podman}/bin/podman exec minecraft-family rcon-cli "say Server will restart in 1 minute."
154 | sleep 50
155 | is_paused || ${pkgs.podman}/bin/podman exec minecraft-family rcon-cli "say Server will restart in 10 seconds."
156 | sleep 5
157 | is_paused || ${pkgs.podman}/bin/podman exec minecraft-family rcon-cli "say Server will restart in 5 seconds."
158 | sleep 5
159 |
160 | ${pkgs.systemd}/bin/systemctl restart podman-minecraft-family.service
161 | '';
162 | serviceConfig = {
163 | Type = "oneshot";
164 | User = "root";
165 | };
166 | };
167 |
168 | systemd.timers.minecraft-family-restart = {
169 | description = "Timer for daily Minecraft server restart";
170 | wantedBy = [ "timers.target" ];
171 | timerConfig = {
172 | # Since the script starts warning players at 5 minutes before restart,
173 | # it needs to be scheduled for 5 minutes before the intended time.
174 | OnCalendar = "03:55:00";
175 | Unit = "minecraft-family-restart.service";
176 | };
177 | };
178 | }
179 |
--------------------------------------------------------------------------------
/config/niri/common/binds.kdl:
--------------------------------------------------------------------------------
1 | binds {
2 | // Mod+Space hotkey-overlay-title="Application Launcher" {
3 | // spawn "dms" "ipc" "call" "spotlight" "toggle";
4 | // }
5 | Mod+Space hotkey-overlay-title="Application Launcher" {
6 | spawn "vicinae" "toggle"
7 | }
8 |
9 | Mod+Ctrl+Space hotkey-overlay-title="Emoji Picker" {
10 | spawn "vicinae" "vicinae://extensions/vicinae/vicinae/search-emojis"
11 | }
12 |
13 | Mod+V hotkey-overlay-title="Clipboard Manager" {
14 | spawn "vicinae" "vicinae://extensions/vicinae/clipboard/history"
15 | }
16 |
17 | Mod+N hotkey-overlay-title="Notification Center" {
18 | spawn "dms" "ipc" "call" "notifications" "toggle"
19 | }
20 |
21 | Mod+X hotkey-overlay-title="Power Menu" {
22 | spawn "dms" "ipc" "call" "powermenu" "open"
23 | }
24 |
25 | // Audio control
26 | Mod+WheelScrollUp cooldown-ms=20 {
27 | spawn "dms" "ipc" "call" "audio" "increment" "6"
28 | }
29 | XF86AudioRaiseVolume allow-when-locked=true {
30 | spawn "dms" "ipc" "call" "audio" "increment" "3"
31 | }
32 | Mod+WheelScrollDown cooldown-ms=20 {
33 | spawn "dms" "ipc" "call" "audio" "decrement" "6"
34 | }
35 | XF86AudioLowerVolume allow-when-locked=true {
36 | spawn "dms" "ipc" "call" "audio" "decrement" "3"
37 | }
38 | XF86AudioMute allow-when-locked=true {
39 | spawn "dms" "ipc" "call" "audio" "mute"
40 | }
41 | XF86AudioMicMute allow-when-locked=true {
42 | spawn "dms" "ipc" "call" "audio" "micmute"
43 | }
44 |
45 | // Media player
46 | XF86AudioNext allow-when-locked=true {
47 | spawn "dms" "ipc" "call" "mpris" "next"
48 | }
49 | XF86AudioPrev allow-when-locked=true {
50 | spawn "dms" "ipc" "call" "mpris" "previous"
51 | }
52 | XF86AudioPlay allow-when-locked=true {
53 | spawn "dms" "ipc" "call" "mpris" "playPause"
54 | }
55 | XF86AudioPause allow-when-locked=true {
56 | spawn "dms" "ipc" "call" "mpris" "pause"
57 | }
58 |
59 | Mod+Shift+Slash { show-hotkey-overlay; }
60 |
61 | Mod+B hotkey-overlay-title="Open Browser: Firefox" { spawn "firefox"; }
62 | Mod+E hotkey-overlay-title="File Manager: Nautilus" { spawn "nautilus"; }
63 | Mod+T hotkey-overlay-title="Open Terminal: Foot" { spawn "foot"; }
64 | Mod+Ctrl+Alt+Shift+Grave hotkey-overlay-title="Open Terminal: Foot" { spawn "foot"; }
65 |
66 | // Mod+T hotkey-overlay-title="Open Terminal: Ghostty" { spawn "ghostty" "+new-window"; }
67 | // Mod+Ctrl+Alt+Shift+Grave hotkey-overlay-title="Open Terminal: Ghostty" { spawn "ghostty" "+new-window"; }
68 |
69 | Mod+Return { maximize-column; }
70 | Mod+Ctrl+Return { maximize-window-to-edges; }
71 | Mod+Shift+Return { fullscreen-window; }
72 |
73 | Mod+Q repeat=false { close-window; }
74 |
75 | // Regular directional keybinds just move the focus around the current workspace.
76 | Mod+Left { focus-column-left; }
77 | Mod+Down { focus-window-down; }
78 | Mod+Up { focus-window-up; }
79 | Mod+Right { focus-column-right; }
80 |
81 | Mod+Next { focus-workspace-down; }
82 | Mod+Prior { focus-workspace-up; }
83 |
84 | // Control is the "window" modifier. Adding in control moves the window instead of the focus.
85 | Mod+Ctrl+Left { move-column-left; }
86 | Mod+Ctrl+Down { move-window-down; }
87 | Mod+Ctrl+Up { move-window-up; }
88 | Mod+Ctrl+Right { move-column-right; }
89 |
90 | Mod+Ctrl+Next { move-column-to-workspace-down; }
91 | Mod+Ctrl+Prior { move-column-to-workspace-up; }
92 |
93 |
94 | // Home and end naturally move focus or window to the start or end of the list.
95 | Mod+Home { focus-column-first; }
96 | Mod+End { focus-column-last; }
97 | Mod+Ctrl+Home { move-column-to-first; }
98 | Mod+Ctrl+End { move-column-to-last; }
99 |
100 | // Shift is the "monitor" modifier. Adding in shift changes the movement from the current
101 | // monitor to the other monitors.
102 | Mod+Shift+Left { focus-monitor-left; }
103 | Mod+Shift+Down { focus-monitor-down; }
104 | Mod+Shift+Up { focus-monitor-up; }
105 | Mod+Shift+Right { focus-monitor-right; }
106 |
107 | // Naturally, shift and control move the column to another monitor, instead
108 | // of moving the focus to another monitor.
109 | Mod+Shift+Ctrl+Left { move-column-to-monitor-left; }
110 | Mod+Shift+Ctrl+Down { move-column-to-monitor-down; }
111 | Mod+Shift+Ctrl+Next { move-column-to-monitor-down; }
112 | Mod+Shift+Ctrl+Up { move-column-to-monitor-up; }
113 | Mod+Shift+Ctrl+Prior { move-column-to-monitor-up; }
114 | Mod+Shift+Ctrl+Right { move-column-to-monitor-right; }
115 |
116 | Mod+1 { focus-workspace 1; }
117 | Mod+2 { focus-workspace 2; }
118 | Mod+3 { focus-workspace 3; }
119 | Mod+4 { focus-workspace 4; }
120 | Mod+5 { focus-workspace 5; }
121 | Mod+6 { focus-workspace 6; }
122 | Mod+7 { focus-workspace 7; }
123 | Mod+8 { focus-workspace 8; }
124 | Mod+9 { focus-workspace 9; }
125 | Mod+Ctrl+1 { move-column-to-workspace 1; }
126 | Mod+Ctrl+2 { move-column-to-workspace 2; }
127 | Mod+Ctrl+3 { move-column-to-workspace 3; }
128 | Mod+Ctrl+4 { move-column-to-workspace 4; }
129 | Mod+Ctrl+5 { move-column-to-workspace 5; }
130 | Mod+Ctrl+6 { move-column-to-workspace 6; }
131 | Mod+Ctrl+7 { move-column-to-workspace 7; }
132 | Mod+Ctrl+8 { move-column-to-workspace 8; }
133 | Mod+Ctrl+9 { move-column-to-workspace 9; }
134 |
135 | Mod+Alt+Ctrl+Left { consume-or-expel-window-left; }
136 | Mod+Alt+Ctrl+Right { consume-or-expel-window-right; }
137 |
138 | Mod+BracketLeft { consume-window-into-column; }
139 | Mod+BracketRight { expel-window-from-column; }
140 |
141 | Mod+R { switch-preset-column-width; }
142 | Mod+Shift+R { switch-preset-window-height; }
143 | Mod+Ctrl+R { reset-window-height; }
144 |
145 | Mod+C { center-column; }
146 | Mod+Ctrl+C { center-visible-columns; }
147 |
148 | Mod+Minus { set-column-width "-10%"; }
149 | Mod+Shift+Minus { set-window-height "-10%"; }
150 | Mod+Equal { set-column-width "+10%"; }
151 | Mod+Shift+Equal { set-window-height "+10%"; }
152 |
153 | Mod+Ctrl+V { toggle-window-floating; }
154 | Mod+Shift+V { switch-focus-between-floating-and-tiling; }
155 |
156 | Mod+W { toggle-column-tabbed-display; }
157 |
158 | Mod+S { screenshot; }
159 | Mod+Shift+S { screenshot-screen; }
160 | Mod+Ctrl+S { screenshot-window; }
161 |
162 | Mod+Escape { toggle-overview; }
163 |
164 | Mod+Shift+Ctrl+Alt+O { debug-toggle-opaque-regions; }
165 | Mod+Shift+Ctrl+Alt+T { toggle-debug-tint; }
166 | }
167 |
--------------------------------------------------------------------------------
/config/nvim/init.lua:
--------------------------------------------------------------------------------
1 | vim.cmd.colorscheme("gruvbox-material")
2 |
3 | vim.g.mapleader = " "
4 |
5 | -- Uses ripgrep for grep
6 | vim.opt.grepprg = "rg --vimgrep"
7 |
8 | -- How long before the swap file is written to?
9 | vim.opt.updatetime = 200
10 |
11 | -- Makes visual block mode able to select empty space
12 | vim.opt.virtualedit = "block"
13 |
14 | -- TODO: Find out more about this option
15 | vim.opt.wildmode = "longest:full,full"
16 |
17 | -- Absolute line number with relative numbers above/below
18 | vim.opt.number = true
19 | vim.opt.relativenumber = true
20 |
21 | -- Set highlight on search
22 | vim.opt.hlsearch = false
23 |
24 | -- Substitute always uses global flag
25 | vim.opt.gdefault = true
26 |
27 | -- Enable mouse mode
28 | vim.opt.mouse = "a"
29 |
30 | -- Enable break indent
31 | vim.opt.breakindent = true
32 |
33 | -- Save undo history
34 | vim.opt.undofile = true
35 | vim.opt.undolevels = 10000
36 |
37 | -- Case insensitive searching UNLESS /C or capital in search
38 | vim.opt.ignorecase = true
39 | vim.opt.smartcase = true
40 |
41 | -- Set colorscheme
42 | vim.opt.termguicolors = true
43 | vim.g.gruvbox_material_enable_italic = 0
44 | vim.g.gruvbox_material_disable_italic_comment = 1
45 | vim.g.gruvbox_material_sign_column_background = "none"
46 | vim.g.gruvbox_material_palette = "original"
47 | vim.cmd([[colorscheme gruvbox-material]])
48 |
49 | -- Set completeopt to have a better completion experience
50 | vim.opt.completeopt = { "menuone", "noselect" }
51 |
52 | -- Show line and col
53 | vim.opt.ruler = true
54 |
55 | -- Show certain hidden characters
56 | vim.opt.list = true
57 | vim.opt.listchars = { tab = "→ ", lead = "·", trail = "·", nbsp = "•" }
58 |
59 | -- Delete comment characters when joining lines
60 | vim.opt.formatoptions:append("j")
61 |
62 | -- Recognize numbered lists
63 | vim.opt.formatoptions:append("n")
64 |
65 | -- Read changes from disk
66 | vim.opt.autoread = true
67 |
68 | -- Highlight the line with the cursor on it
69 | vim.opt.cursorline = true
70 |
71 | -- Sane line wrapping settings
72 | vim.opt.wrap = false
73 | vim.opt.linebreak = true
74 | vim.opt.showbreak = "↪"
75 | vim.opt.breakindent = true
76 | vim.opt.breakindentopt = "list:-1"
77 |
78 | -- Sane tab settings
79 | vim.opt.expandtab = false
80 | vim.opt.smarttab = true
81 | vim.opt.tabstop = 2
82 | vim.opt.shiftwidth = 0
83 |
84 | -- Splits will open on the right/below instead of left/above
85 | vim.opt.splitright = true
86 | vim.opt.splitbelow = true
87 | vim.opt.splitkeep = "screen"
88 |
89 | -- Vertical diffs
90 | vim.opt.diffopt:append("vertical")
91 |
92 | -- 3 lines/cols buffer when scrolling
93 | vim.opt.scrolloff = 3
94 | vim.opt.sidescrolloff = 3
95 |
96 | -- Always draw the sign column
97 | vim.opt.signcolumn = "yes"
98 |
99 | -- Wait 300ms for a sequence to complete
100 | vim.opt.timeout = true
101 | vim.opt.timeoutlen = 300
102 |
103 | -- Ignore files matching these patterns while expanding a wildcard
104 | vim.opt.wildignore = { "*.o", "*.obj", "*.bak", "*.exe", "*.pyc", ".DS_Store" }
105 |
106 | -- Hide the welcome message
107 | vim.opt.shortmess = "I"
108 |
109 | -- Ask to write instead of failing when using :q
110 | vim.opt.confirm = true
111 |
112 | -- Use tree-sitter folds
113 | vim.opt.foldmethod = "expr"
114 | vim.opt.foldexpr = "nvim_treesitter#foldexpr()"
115 | vim.opt.foldlevelstart = 69
116 |
117 | -- Display a window title
118 | vim.opt.title = true
119 |
120 | -- Use the + register for operations that would normally use _
121 | vim.opt.clipboard = "unnamedplus"
122 |
123 | -- Don't display the ~ chars at the end of the buffer
124 | vim.opt.fillchars = { eob = " " }
125 |
126 | --
127 |
128 | -- Highlight the yanked region briefly
129 | local highlight_group = vim.api.nvim_create_augroup("YankHighlight", {
130 | clear = true,
131 | })
132 | vim.api.nvim_create_autocmd("TextYankPost", {
133 | callback = function()
134 | vim.highlight.on_yank()
135 | end,
136 | group = highlight_group,
137 | pattern = "*",
138 | })
139 |
140 | -- If the file's parent directory doesn't exist, create it first
141 | local mkdirp_on_write = vim.api.nvim_create_augroup("MkdirpOnWrite", {
142 | clear = true,
143 | })
144 | vim.api.nvim_create_autocmd("BufWritePre", {
145 | callback = function()
146 | local dir = vim.fn.expand(":p:h")
147 | if vim.fn.isdirectory(dir) == 0 then
148 | vim.fn.mkdir(dir, "p")
149 | end
150 | end,
151 | group = mkdirp_on_write,
152 | pattern = "*",
153 | })
154 |
155 |
156 | vim.api.nvim_create_user_command("Term", ":vsp | terminal", { desc = "Open a new terminal in a split" })
157 |
158 |
159 | -- Make Y behave consistently, like D and C
160 | vim.keymap.set("n", "Y", "y$", { silent = true })
161 |
162 | -- Paste without overwriting the unnamed register
163 | vim.keymap.set("v", "P", '"_dP', { silent = true })
164 |
165 | -- dD deletes all the characters in the line without removing the line itself
166 | vim.keymap.set("n", "dD", "0D", { desc = "Delete all the characters in the line" })
167 |
168 | -- Space does nothing in normal or visual mode
169 | vim.keymap.set({ "n", "v" }, "", "", { silent = true })
170 |
171 | -- Up and down navigate through wrapped lines sensibly
172 | vim.keymap.set("n", "", "v:count == 0 ? 'gk' : 'k'", { expr = true, silent = true })
173 | vim.keymap.set("n", "", "v:count == 0 ? 'gj' : 'j'", { expr = true, silent = true })
174 |
175 | -- Delete a buffer without deleting the split
176 | vim.keymap.set("n", "bd", "bp|bd #", { desc = "Delete buffer without closing pane" })
177 |
178 | -- Telescope mappings
179 | vim.keymap.set("n", "f", require("telescope.builtin").find_files, { desc = "Find files by name" })
180 | vim.keymap.set("n", "/", require("telescope.builtin").live_grep, { desc = "Find files with grep" })
181 | vim.keymap.set("n", "b", require("telescope.builtin").buffers, { desc = "Find buffers" })
182 | vim.keymap.set("n", "d", require("telescope.builtin").diagnostics, { desc = "Find diagnostics" })
183 | vim.keymap.set("n", "o", require("telescope.builtin").jumplist, { desc = "View jumplist" })
184 |
185 | -- Diagnostic pairs
186 | vim.keymap.set("n", "]d", vim.diagnostic.goto_next)
187 | vim.keymap.set("n", "[d", vim.diagnostic.goto_prev)
188 | vim.keymap.set("n", "dn", vim.diagnostic.goto_next)
189 | vim.keymap.set("n", "dp", vim.diagnostic.goto_prev)
190 |
191 | -- View diagnostic information
192 | vim.keymap.set("n", "ge", vim.diagnostic.open_float)
193 |
194 | -- Hit escape twice to exit the terminal
195 | vim.keymap.set("t", "", "")
196 |
197 | require("mini.surround").setup()
198 |
199 | require("telescope").setup({
200 | defaults = {
201 | mappings = {
202 | i = {
203 | [""] = false,
204 | [""] = false,
205 | },
206 | },
207 | },
208 | extensions = {
209 | ["ui-select"] = {
210 | require("telescope.themes").get_dropdown({}),
211 | },
212 | },
213 | })
214 | require("telescope").load_extension("fzf")
215 | --require("telescope").load_extension("ui-select")
216 |
--------------------------------------------------------------------------------
/run.fish:
--------------------------------------------------------------------------------
1 | # @fish-lsp-disable 2002 4004
2 |
3 | # If the devshell isn't active, the functiond defined in this file cannot be
4 | # used, so ensure that the dev environment is always active.
5 | if not set -q IN_NIX_CONFIG_DEVSHELL
6 | alias devshell "nix develop"
7 | return 1
8 | end
9 |
10 | #
11 | # --- Private utility functions
12 | #
13 |
14 | function _pretty_print
15 | set colored_command (string escape -- $argv | string join ' ' | fish_indent --ansi)
16 | echo "$(set_color brgreen --bold)~~>$(set_color normal) $colored_command"
17 | end
18 |
19 | function _run
20 | _pretty_print $argv
21 | $argv
22 | end
23 |
24 | function _require
25 | for program in $argv
26 | if not command -vq $program
27 | echo "Required program '$program' is missing. Ensure it is in your environment, then try again."
28 | end
29 | end
30 | end
31 |
32 | #
33 | # --- Commands
34 | #
35 |
36 | set -g this_host (hostname -s)
37 |
38 | function dry -d "Dry-run a function (replaces _run with _pretty_print)"
39 | functions --erase _run
40 | functions --copy _pretty_print _run
41 |
42 | # Mistakenly ran this function without any arguments, print functions.
43 | if not set -q argv[1]
44 | run
45 | return
46 | end
47 |
48 | $argv
49 | end
50 |
51 | function mc-rcon -d "Connect to homeserver1 and begin an interactive RCON session"
52 | echo (set_color --italics)"connecting to homeserver1 and executing rcon-cli..."(set_color normal)
53 | _run ssh robert@homeserver1 "sudo podman exec -i minecraft-family rcon-cli"
54 | end
55 |
56 | function mc-stop
57 | echo (set_color --italics)"connecting to homeserver1 and stopping server..."(set_color normal)
58 | _run ssh robert@homeserver1 "sudo systemctl stop podman-minecraft-family.service"
59 | end
60 |
61 | function mc-start
62 | echo (set_color --italics)"connecting to homeserver1 and stopping server..."(set_color normal)
63 | _run ssh robert@homeserver1 "sudo systemctl start podman-minecraft-family.service"
64 | end
65 |
66 | function mc-logs
67 | echo (set_color --italics)"connecting to homeserver1 and stopping server..."(set_color normal)
68 | _run ssh robert@homeserver1 "journalctl -xefu podman-minecraft-family.service"
69 | end
70 |
71 | function check-applied -d "Check if the currently applied configuration needs to be updated"
72 | set last_commit_timestamp (git log -1 --format=%at)
73 | set current_commit_pretty (set_color --dim --italics)$NIX_CONFIG_REV(set_color normal)
74 | if test $NIX_CONFIG_LAST_MODIFIED -lt $last_commit_timestamp
75 | echo "Configuration is out of date."
76 | echo $current_commit_pretty
77 | # TODO: Prompt to apply configuration if out of date?
78 | return 1
79 | else
80 | set commit_hash (git log -1 --format=%H)
81 | if test $NIX_CONFIG_REV = $commit_hash
82 | echo "Configuration is the most recent commit."
83 | echo $current_commit_pretty
84 | else
85 | echo "Current configuration is dirty (new)."
86 | echo $current_commit_pretty
87 | end
88 | end
89 | end
90 |
91 | function edit-age -d "Edit the encrypted files stored in this repository"
92 | set file (fd --type file --glob '*.age' | fzf --height=40% --layout=reverse)
93 | or return
94 | agenix -e $file $argv
95 | end
96 |
97 | #
98 | # --- Functions for building/switching hosts
99 | #
100 |
101 | function homeserver1 -a verb
102 | set rebuild_args
103 | set maybe_sudo
104 |
105 | if test $this_host != homeserver1
106 | # If not building on homeserver1, set homeserver1 to be the target.
107 | # Otherwise, it is definitely a mistake to switch on the local system.
108 | set --append rebuild_args --use-remote-sudo --target-host robert@homeserver1
109 | echo (set_color --dim --italics)"not on homeserver1, targeting remote host..."(set_color normal)
110 |
111 | # If the current system isn't x86-64 Linux, then homeserver1 needs to build
112 | # its own configuration. --fast (--no-build-nix) is also required because
113 | # the local system will attempt to execute a version of `nix` that it can't
114 | # run.
115 | if test (nix eval --impure --raw --expr 'builtins.currentSystem') != x86_64-linux
116 | set --append rebuild_args --build-host robert@homeserver1 --fast
117 | echo (set_color --dim --italics)"not on x86_64-linux, performing remote build..."(set_color normal)
118 | end
119 |
120 | else
121 | # If, for whatever reason, we *are* on homeserver1, then we need to use sudo.
122 | set maybe_sudo sudo
123 | echo (set_color --dim --italics)"on homeserver1, using sudo..."(set_color normal)
124 | end
125 |
126 | _run $maybe_sudo nixos-rebuild $verb --flake .#homeserver1 $rebuild_args $argv[2..]
127 | end
128 |
129 | function macmini -a verb
130 | set maybe_sudo
131 | if test $verb = switch
132 | set maybe_sudo sudo
133 | end
134 | _run $maybe_sudo darwin-rebuild $verb --flake .#macmini --max-jobs 8 $argv[2..]
135 | end
136 |
137 | function work-macbookpro -a verb
138 | set maybe_sudo
139 | if test $verb = switch
140 | set maybe_sudo sudo
141 | end
142 | _run $maybe_sudo darwin-rebuild $verb --flake .#work-macbookpro $argv[2..]
143 | end
144 |
145 | function macbook-air -a verb
146 | _require pmset timeout home-manager
147 |
148 | set jobs 8
149 |
150 | if test $this_host = macbook-air; and pmset -g batt | grep -q "Battery Power"
151 | echo (set_color --dim --italics)"on battery power, testing connection to macmini..."(set_color normal)
152 | if timeout 3 nix store info --store ssh-ng://robert@macmini &>/dev/null
153 | echo (set_color --dim --italics)"delegating to macmini..."(set_color normal)
154 | set jobs 0
155 | else
156 | echo (set_color --dim --italics)"running on this machine..."(set_color normal)
157 | end
158 | end
159 |
160 | _run home-manager $verb --flake ".#$USER@macbook-air" --max-jobs $jobs $argv[2..]
161 | end
162 |
163 | function legacy -a verb
164 | _require home-manager
165 | _run home-manager $verb --flake ".#$USER@legacy" --max-jobs 8 $argv[2..]
166 | end
167 |
168 | function pc3 -a verb
169 | _require home-manager
170 | set jobs 8
171 | if test $this_host = pc3
172 | set jobs 32
173 | end
174 | _run home-manager $verb --flake ".#$USER@pc3" --max-jobs $jobs $argv[2..]
175 | end
176 |
177 | # The logic below defines the commands used to build/switch configurations for
178 | # the hosts above. This requires some amount of metaprogramming, which Fish has
179 | # decent support for.
180 |
181 | set hosts (ls hosts)
182 | set verbs build switch
183 |
184 | for host in $hosts
185 | functions -q $host; or continue
186 | functions --copy $host _$host
187 | functions --erase $host
188 |
189 | for verb in $verbs
190 | echo "function $verb-$host -d '$verb the configuration for $host'; _$host $verb \$argv; end" | source
191 | # This line is visually confusing. Essentially, if there is a config
192 | # for the current $hostname, we create an alias for it so I can refer
193 | # to it as "host" rather than the actual hostname. It's easier to type
194 | # and remember, since most of the time I'll want to build the config
195 | # for the system I'm using at the time, regardless of what it is.
196 | test $this_host = $host; and alias $verb-host $verb-$host
197 | end
198 | end
199 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "agenix": {
4 | "inputs": {
5 | "darwin": [
6 | "nix-darwin"
7 | ],
8 | "home-manager": [
9 | "home-manager"
10 | ],
11 | "nixpkgs": [
12 | "nixpkgs"
13 | ],
14 | "systems": "systems"
15 | },
16 | "locked": {
17 | "lastModified": 1762618334,
18 | "narHash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ=",
19 | "owner": "ryantm",
20 | "repo": "agenix",
21 | "rev": "fcdea223397448d35d9b31f798479227e80183f6",
22 | "type": "github"
23 | },
24 | "original": {
25 | "owner": "ryantm",
26 | "repo": "agenix",
27 | "type": "github"
28 | }
29 | },
30 | "blueprint": {
31 | "inputs": {
32 | "nixpkgs": [
33 | "nixpkgs"
34 | ],
35 | "systems": "systems_2"
36 | },
37 | "locked": {
38 | "lastModified": 1740813264,
39 | "narHash": "sha256-LIO3+CMw9yLVLK+8TjcaQoLmEgCUkXfRonzFguoLZZk=",
40 | "owner": "clo4",
41 | "repo": "blueprint",
42 | "rev": "b033f96062fd91c024f6a04ee2cbf7c168a28a6e",
43 | "type": "github"
44 | },
45 | "original": {
46 | "owner": "clo4",
47 | "ref": "generic-users",
48 | "repo": "blueprint",
49 | "type": "github"
50 | }
51 | },
52 | "brew-src": {
53 | "flake": false,
54 | "locked": {
55 | "lastModified": 1763638478,
56 | "narHash": "sha256-n/IMowE9S23ovmTkKX7KhxXC2Yq41EAVFR2FBIXPcT8=",
57 | "owner": "Homebrew",
58 | "repo": "brew",
59 | "rev": "fbfdbaba008189499958a7aeb1e2c36ab10c067d",
60 | "type": "github"
61 | },
62 | "original": {
63 | "owner": "Homebrew",
64 | "ref": "5.0.3",
65 | "repo": "brew",
66 | "type": "github"
67 | }
68 | },
69 | "clouddns": {
70 | "inputs": {
71 | "blueprint": [
72 | "blueprint"
73 | ],
74 | "nixpkgs": [
75 | "nixpkgs"
76 | ]
77 | },
78 | "locked": {
79 | "lastModified": 1754186948,
80 | "narHash": "sha256-Iuu0ZTyZcPBGN1P8n8s70uNLONGm1XBLEEmUoouZfQM=",
81 | "owner": "clo4",
82 | "repo": "clouddns",
83 | "rev": "074c4096fbf842383190d10c4f47b44b34b366f4",
84 | "type": "github"
85 | },
86 | "original": {
87 | "owner": "clo4",
88 | "repo": "clouddns",
89 | "type": "github"
90 | }
91 | },
92 | "disko": {
93 | "inputs": {
94 | "nixpkgs": [
95 | "nixpkgs"
96 | ]
97 | },
98 | "locked": {
99 | "lastModified": 1765688338,
100 | "narHash": "sha256-MjrytR2kiHYUnzX11cXaD31tS7kKdhM1KFaac0+KAig=",
101 | "owner": "nix-community",
102 | "repo": "disko",
103 | "rev": "be1a6b8a05afdd5d5fa69fcaf3c4ead7014c9fd8",
104 | "type": "github"
105 | },
106 | "original": {
107 | "owner": "nix-community",
108 | "repo": "disko",
109 | "type": "github"
110 | }
111 | },
112 | "flake-compat": {
113 | "locked": {
114 | "lastModified": 1696426674,
115 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
116 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
117 | "revCount": 57,
118 | "type": "tarball",
119 | "url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
120 | },
121 | "original": {
122 | "type": "tarball",
123 | "url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
124 | }
125 | },
126 | "flake-utils": {
127 | "inputs": {
128 | "systems": "systems_3"
129 | },
130 | "locked": {
131 | "lastModified": 1710146030,
132 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
133 | "owner": "numtide",
134 | "repo": "flake-utils",
135 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
136 | "type": "github"
137 | },
138 | "original": {
139 | "owner": "numtide",
140 | "repo": "flake-utils",
141 | "type": "github"
142 | }
143 | },
144 | "helix": {
145 | "inputs": {
146 | "nixpkgs": [
147 | "nixpkgs"
148 | ],
149 | "rust-overlay": "rust-overlay"
150 | },
151 | "locked": {
152 | "lastModified": 1765575289,
153 | "narHash": "sha256-zvFdyJO+n+myu3t5DQ9gqoXyfWzvmUn1zsDElQtqWXU=",
154 | "owner": "helix-editor",
155 | "repo": "helix",
156 | "rev": "e709298cfb6db7a222c433b75e1a4f950d4a4a15",
157 | "type": "github"
158 | },
159 | "original": {
160 | "owner": "helix-editor",
161 | "repo": "helix",
162 | "type": "github"
163 | }
164 | },
165 | "home-manager": {
166 | "inputs": {
167 | "nixpkgs": [
168 | "nixpkgs"
169 | ]
170 | },
171 | "locked": {
172 | "lastModified": 1765682243,
173 | "narHash": "sha256-yeCxFV/905Wr91yKt5zrVvK6O2CVXWRMSrxqlAZnLp0=",
174 | "owner": "nix-community",
175 | "repo": "home-manager",
176 | "rev": "58bf3ecb2d0bba7bdf363fc8a6c4d49b4d509d03",
177 | "type": "github"
178 | },
179 | "original": {
180 | "owner": "nix-community",
181 | "ref": "master",
182 | "repo": "home-manager",
183 | "type": "github"
184 | }
185 | },
186 | "nix-darwin": {
187 | "inputs": {
188 | "nixpkgs": [
189 | "nixpkgs"
190 | ]
191 | },
192 | "locked": {
193 | "lastModified": 1765684049,
194 | "narHash": "sha256-svCS2r984qEowMT0y3kCrsD/m0J6zaF5I/UusS7QaH0=",
195 | "owner": "LnL7",
196 | "repo": "nix-darwin",
197 | "rev": "9b628e171bfaea1a3d1edf31eee46251e0fe4a33",
198 | "type": "github"
199 | },
200 | "original": {
201 | "owner": "LnL7",
202 | "ref": "master",
203 | "repo": "nix-darwin",
204 | "type": "github"
205 | }
206 | },
207 | "nix-filter": {
208 | "locked": {
209 | "lastModified": 1731533336,
210 | "narHash": "sha256-oRam5PS1vcrr5UPgALW0eo1m/5/pls27Z/pabHNy2Ms=",
211 | "owner": "numtide",
212 | "repo": "nix-filter",
213 | "rev": "f7653272fd234696ae94229839a99b73c9ab7de0",
214 | "type": "github"
215 | },
216 | "original": {
217 | "owner": "numtide",
218 | "repo": "nix-filter",
219 | "type": "github"
220 | }
221 | },
222 | "nix-homebrew": {
223 | "inputs": {
224 | "brew-src": "brew-src"
225 | },
226 | "locked": {
227 | "lastModified": 1764473698,
228 | "narHash": "sha256-C91gPgv6udN5WuIZWNehp8qdLqlrzX6iF/YyboOj6XI=",
229 | "owner": "zhaofengli",
230 | "repo": "nix-homebrew",
231 | "rev": "6a8ab60bfd66154feeaa1021fc3b32684814a62a",
232 | "type": "github"
233 | },
234 | "original": {
235 | "owner": "zhaofengli",
236 | "repo": "nix-homebrew",
237 | "type": "github"
238 | }
239 | },
240 | "nixpkgs": {
241 | "locked": {
242 | "lastModified": 1765472234,
243 | "narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=",
244 | "owner": "nixos",
245 | "repo": "nixpkgs",
246 | "rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b",
247 | "type": "github"
248 | },
249 | "original": {
250 | "owner": "nixos",
251 | "ref": "nixos-unstable",
252 | "repo": "nixpkgs",
253 | "type": "github"
254 | }
255 | },
256 | "nixpkgs-unstable": {
257 | "locked": {
258 | "lastModified": 1765644376,
259 | "narHash": "sha256-yqHBL2wYGwjGL2GUF2w3tofWl8qO9tZEuI4wSqbCrtE=",
260 | "owner": "nixos",
261 | "repo": "nixpkgs",
262 | "rev": "23735a82a828372c4ef92c660864e82fbe2f5fbe",
263 | "type": "github"
264 | },
265 | "original": {
266 | "owner": "nixos",
267 | "ref": "nixpkgs-unstable",
268 | "repo": "nixpkgs",
269 | "type": "github"
270 | }
271 | },
272 | "root": {
273 | "inputs": {
274 | "agenix": "agenix",
275 | "blueprint": "blueprint",
276 | "clouddns": "clouddns",
277 | "disko": "disko",
278 | "helix": "helix",
279 | "home-manager": "home-manager",
280 | "nix-darwin": "nix-darwin",
281 | "nix-homebrew": "nix-homebrew",
282 | "nixpkgs": "nixpkgs",
283 | "nixpkgs-unstable": "nixpkgs-unstable",
284 | "srvos": "srvos",
285 | "winapps": "winapps"
286 | }
287 | },
288 | "rust-overlay": {
289 | "inputs": {
290 | "nixpkgs": [
291 | "helix",
292 | "nixpkgs"
293 | ]
294 | },
295 | "locked": {
296 | "lastModified": 1759631821,
297 | "narHash": "sha256-V8A1L0FaU/aSXZ1QNJScxC12uP4hANeRBgI4YdhHeRM=",
298 | "owner": "oxalica",
299 | "repo": "rust-overlay",
300 | "rev": "1d7cbdaad90f8a5255a89a6eddd8af24dc89cafe",
301 | "type": "github"
302 | },
303 | "original": {
304 | "owner": "oxalica",
305 | "repo": "rust-overlay",
306 | "type": "github"
307 | }
308 | },
309 | "srvos": {
310 | "inputs": {
311 | "nixpkgs": [
312 | "nixpkgs"
313 | ]
314 | },
315 | "locked": {
316 | "lastModified": 1765415765,
317 | "narHash": "sha256-DNEUksb+s7DbwahAlIZ4v/BUFUacOqGklCbjgAHZb4k=",
318 | "owner": "nix-community",
319 | "repo": "srvos",
320 | "rev": "a9e46dc439591c67337a0caf0beebb5a73ed9a86",
321 | "type": "github"
322 | },
323 | "original": {
324 | "owner": "nix-community",
325 | "repo": "srvos",
326 | "type": "github"
327 | }
328 | },
329 | "systems": {
330 | "locked": {
331 | "lastModified": 1681028828,
332 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
333 | "owner": "nix-systems",
334 | "repo": "default",
335 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
336 | "type": "github"
337 | },
338 | "original": {
339 | "owner": "nix-systems",
340 | "repo": "default",
341 | "type": "github"
342 | }
343 | },
344 | "systems_2": {
345 | "locked": {
346 | "lastModified": 1681028828,
347 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
348 | "owner": "nix-systems",
349 | "repo": "default",
350 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
351 | "type": "github"
352 | },
353 | "original": {
354 | "owner": "nix-systems",
355 | "repo": "default",
356 | "type": "github"
357 | }
358 | },
359 | "systems_3": {
360 | "locked": {
361 | "lastModified": 1681028828,
362 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
363 | "owner": "nix-systems",
364 | "repo": "default",
365 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
366 | "type": "github"
367 | },
368 | "original": {
369 | "owner": "nix-systems",
370 | "repo": "default",
371 | "type": "github"
372 | }
373 | },
374 | "winapps": {
375 | "inputs": {
376 | "flake-compat": "flake-compat",
377 | "flake-utils": "flake-utils",
378 | "nix-filter": "nix-filter",
379 | "nixpkgs": [
380 | "nixpkgs"
381 | ]
382 | },
383 | "locked": {
384 | "lastModified": 1765228041,
385 | "narHash": "sha256-+h7yJqGfTbNy0xAUaGWtMxm8REcHG4SMucVSt3D6vZQ=",
386 | "owner": "winapps-org",
387 | "repo": "winapps",
388 | "rev": "44342c34b839547be0b2ea4f94ed00293fa7cc38",
389 | "type": "github"
390 | },
391 | "original": {
392 | "owner": "winapps-org",
393 | "repo": "winapps",
394 | "type": "github"
395 | }
396 | }
397 | },
398 | "root": "root",
399 | "version": 7
400 | }
401 |
--------------------------------------------------------------------------------