├── .envrc
├── .git-blame-ignore-revs
├── .github
├── dependabot.yml
└── workflows
│ ├── test.yml
│ └── update-flake.yml
├── .gitignore
├── README.md
├── UNLICENSE
├── config
├── alacritty
│ └── alacritty.toml
├── fontconfig
│ └── fonts.conf
├── gtk-3.0
│ ├── import-gsettings.sh
│ └── settings.ini
├── mako
│ └── config
├── nvim
│ └── init.vim
├── sway
│ └── config
├── waybar
│ └── config
└── wofi
│ ├── config
│ └── style.css
├── default.nix
├── docs
└── machine_init.md
├── flake-compat.nix
├── flake.lock
├── flake.nix
├── homeManagerModules
├── alacritty.nix
├── default.nix
├── direnv.nix
├── fish.nix
├── fonts.nix
├── fzf.nix
├── git.nix
├── gtk.nix
├── jj.nix
├── nvim.nix
├── rust.nix
├── sway.nix
└── taskwarrior.nix
├── lib
├── default.nix
├── mkAppendConfig.nix
└── mkNixosSystem.nix
├── nixosConfigurations
├── asphodel
│ ├── default.nix
│ ├── hardware-configuration.nix
│ └── persist.nix
├── elysium
│ ├── default.nix
│ ├── hardware-configuration.nix
│ └── persist.nix
├── rpi
│ └── default.nix
└── tartarus
│ ├── default.nix
│ └── hardware-configuration.nix
├── nixosModules
├── _1password.nix
├── default.nix
├── knownHosts.nix
├── nixConfig.nix
├── pihole.nix
├── tailscale.nix
└── zfs-send.nix
├── pkgs
├── default.nix
└── swaynagmode.nix
└── users
└── ivan
├── default.nix
└── home.nix
/.envrc:
--------------------------------------------------------------------------------
1 | use flake
2 |
--------------------------------------------------------------------------------
/.git-blame-ignore-revs:
--------------------------------------------------------------------------------
1 | # Add nixfmt-tree
2 | a855d41dce23d012fdf5e72c31789455111be182
3 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 |
4 | - package-ecosystem: github-actions
5 | directory: "/"
6 | schedule:
7 | interval: weekly
8 | time: '00:00'
9 | timezone: UTC
10 | open-pull-requests-limit: 10
11 | commit-message:
12 | prefix: "chore"
13 | include: "scope"
14 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: "Test"
2 | on:
3 | pull_request:
4 | push:
5 | branches-ignore:
6 | # Don't run twice on automation updates, rely on pull_request hook
7 | - 'automation/**'
8 | - 'dependabot/**'
9 | jobs:
10 | tests-pass:
11 | name: all systems go
12 | runs-on: ubuntu-latest
13 | if: ${{ always() && contains(needs.*.result, 'failure') }}
14 | steps:
15 | - run: exit 1
16 | needs:
17 | - tests
18 |
19 | tests:
20 | strategy:
21 | # Allow other jobs to finish building and cache properly before bailing
22 | fail-fast: false
23 | matrix:
24 | include:
25 | #- os: macos-12
26 | - os: ubuntu-latest
27 | system: "x86_64-linux"
28 | - os: ubuntu-latest
29 | qemuPlatforms: "arm64"
30 | system: "aarch64-linux"
31 | extraPlatforms: "aarch64-linux"
32 |
33 | runs-on: ${{ matrix.os }}
34 | steps:
35 | - uses: actions/checkout@v4
36 | - uses: cachix/install-nix-action@v31
37 | with:
38 | extra_nix_config: |
39 | extra-platforms = ${{ matrix.extraPlatforms }}
40 |
41 | - uses: cachix/cachix-action@v16
42 | with:
43 | name: ipetkov
44 | signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
45 |
46 | - name: install QEMU/binfmt
47 | if: ${{ matrix.qemuPlatforms != '' }}
48 | uses: docker/setup-qemu-action@v3
49 | with:
50 | image: tonistiigi/binfmt:latest
51 | platforms: ${{ matrix.qemuPlatforms }}
52 |
53 | - name: build all definitions
54 | run: nix run github:Mic92/nix-fast-build -L -- --skip-cached --no-download --no-nom --eval-workers 1 --eval-max-memory-size 2048 --flake .#checks.${{ matrix.system }}
55 |
--------------------------------------------------------------------------------
/.github/workflows/update-flake.yml:
--------------------------------------------------------------------------------
1 | name: Update flake dependencies
2 |
3 | on:
4 | schedule:
5 | - cron: '0 16 * * 5'
6 | workflow_dispatch: # for allowing manual triggers of the workflow
7 |
8 | permissions:
9 | contents: read
10 |
11 | jobs:
12 | update-and-push-deps:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | - uses: cachix/install-nix-action@v31
17 | - uses: cachix/cachix-action@v16
18 | with:
19 | name: ipetkov
20 | signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
21 | - name: add nix-community cache
22 | run: cachix use nix-community
23 | - name: Update flake.lock
24 | id: flakeupdate
25 | uses: DeterminateSystems/update-flake-lock@v25
26 | with:
27 | token: ${{ secrets.PAT_FLAKE_UPDATE }}
28 | branch: "automation/flake-update"
29 | pr-title: "Update flake.lock"
30 | pr-labels: |
31 | automated
32 | dependencies
33 | flake update
34 | - name: Enable Pull Request Automerge
35 | run: gh pr merge --squash --delete-branch --auto ${{ steps.flakeupdate.outputs.pull-request-number }}
36 | env:
37 | GH_TOKEN: ${{ secrets.PAT_FLAKE_UPDATE }}
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .direnv
2 | result*
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ipetkov's dotfiles
2 |
3 | These are my personal dotfiles and NixOS/home-manager configurations for
4 | setting everything up. I doubt anyone would even want to use this directly,
5 | but feel free to use it for your own inspiration (or apply any workarounds for
6 | issues I've hit...).
7 |
8 | ## Layout Structure
9 |
10 | I've tried to organize things in a logical way, so for the time being,
11 | things are structured as follows:
12 |
13 | * `config`: contents which might represent the `~/.config` directory. Basically
14 | "literal" configs and definitions which get linked/incorporated through home-manager
15 | * `docs`: additional documentation files, mostly for myself, but may be useful to others
16 | * `homeConfigurations`: top-level fully instantiated home-manager
17 | configurations. Useful for having the CI cache the artifacts and make sure
18 | that everything builds. Also includes a `module` attribute which allows for
19 | importing/overriding the definition as needed.
20 | * `homeManagerModules`: a collection of "common" home-manager modules which could be used
21 | in other dependent flakes. They're definitely tailored to my own preferences though, and lack
22 | robust options (like how home-manager might lay things out)!
23 | * `lib`: collection of helper functions for doing things like crawling
24 | directories and automatically making flake outputs of things
25 | * `nixosConfigurations`: top level system configurations, named by machine/host name
26 | * `nixosModules`: same as `homeManagerModules` but related to system configuration modules
27 | * `pkgs`: my own package definitions which aren't available upstream
28 | * `users`: common user definitions that can be reused across system configurations.
29 | Normally this directory would have a `default.nix` module which defines user groups,
30 | home directories, etc., and a `home.nix` module which contains the root of the
31 | home-manager configurations for this user.
32 |
33 | ## Additional Documentation
34 | * [Machine initialization](./docs/machine_init.md) - How to prepare a machine for NixOS installation
35 |
36 | ## Applying configuration changes
37 |
38 | Applying configuration changes on a local machine can be done as follows:
39 |
40 | ```sh
41 | cd ~/dotfiles
42 | sudo nixos-rebuild switch --flake .
43 | # This will automatically pick the configuration name based on the hostname
44 | ```
45 |
46 | Applying configuration changes to a remote machine can be done as follows:
47 |
48 | ```sh
49 | cd ~/dotfiles
50 | nixos-rebuild switch --flake .#nameOfMachine --target-host machineToSshInto --use-remote-sudo
51 | ```
52 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
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/alacritty/alacritty.toml:
--------------------------------------------------------------------------------
1 | [keyboard]
2 | bindings = [
3 | { key = "Return", mods = "Super|Shift", action = "SpawnNewInstance" }
4 | ]
5 |
6 | [colors.primary]
7 | background = '#2c2b2a'
8 | foreground = '#f9e7c4'
9 |
10 | dim_foreground = '#685E4A'
11 | bright_foreground = '#1C1508'
12 |
13 | [colors.normal]
14 | black = '#282a2e'
15 | red = '#a54242'
16 | green = '#8c9440'
17 | yellow = '#de935f'
18 | blue = '#5f819d'
19 | magenta = '#85678f'
20 | cyan = '#5e8d87'
21 | white = '#707880'
22 |
23 | [colors.bright]
24 | black = '#373b41'
25 | red = '#cc6666'
26 | green = '#b5bd68'
27 | yellow = '#f0c674'
28 | blue = '#81a2be'
29 | magenta = '#b294bb'
30 | cyan = '#8abeb7'
31 | white = '#c5c8c6'
32 |
33 | [font]
34 | size = 10.5
35 |
36 | [window]
37 | opacity = 0.9
38 |
--------------------------------------------------------------------------------
/config/fontconfig/fonts.conf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | sans-serif
7 | Noto Color Emoji
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/config/gtk-3.0/import-gsettings.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # usage: import-gsettings.sh
4 | config="${XDG_CONFIG_HOME:-$HOME/.config}/gtk-3.0/settings.ini"
5 | if [ ! -f "$config" ]; then exit 1; fi
6 |
7 | gnome_schema="org.gnome.desktop.interface"
8 |
9 | function get_setting() {
10 | grep "$1" "$config" | cut -d '=' -f 2
11 | }
12 |
13 | function apply_setting() {
14 | value="$(get_setting "$1")"
15 | if ! [ -z "$value" ]; then
16 | echo gsettings set "$gnome_schema" "$1" "$value"
17 | fi
18 | }
19 |
20 | apply_setting gtk-theme
21 | apply_setting icon-theme
22 | apply_setting cursor-theme
23 | apply_setting font-name
24 |
--------------------------------------------------------------------------------
/config/gtk-3.0/settings.ini:
--------------------------------------------------------------------------------
1 | [Settings]
2 | gtk-application-prefer-dark-theme=true
3 | gtk-icon-theme-name=Adwaita
4 | gtk-theme-name=Nordic
5 |
--------------------------------------------------------------------------------
/config/mako/config:
--------------------------------------------------------------------------------
1 | #
2 | # Display options
3 | #
4 | anchor=bottom-right
5 | default-timeout=30000
6 | # Let notifications decay over time
7 | max-visible=-1
8 | # Newest notifications on bottom
9 | sort=+time
10 |
11 | #
12 | # Style
13 | #
14 | border-color=#00000000
15 | height=300
16 | width=450
17 |
18 | [urgency=high]
19 | background-color=#e21b1b
20 | default-timeout=0
21 |
--------------------------------------------------------------------------------
/config/nvim/init.vim:
--------------------------------------------------------------------------------
1 | filetype plugin indent on
2 |
3 | syntax on " enable syntax processing
4 | set backspace=indent,eol,start " get around backspace defaults, behave as expected in other apps
5 | set completeopt=menuone,noinsert,noselect " Set completeopt to have a better completion experience
6 | set ignorecase " when smartcase and ignore case are both on, search will be case
7 | set incsearch " start search while typing
8 | set laststatus=2 " always display the statusline
9 | set lazyredraw " redraw only when needed, get speedup from not redrawing during macros
10 | set mouse="" " Set the old mouse behavior I'm used to
11 | set sessionoptions-=options " Don't save options, see if this fixes problems with session restoration
12 | set shortmess+=c " Avoid showing extra messages when using completion
13 | set showcmd " show the (currently pending) command at bottom right
14 | set smartcase " sensitive if pattern contains uppercase letter, and insensitive otherwise
15 | set statusline=%f\ %l:%c\ %m " show: :
16 | set undofile " save undo history persistently to disk
17 | set updatetime=500 " how long before triggering CursorHold/swap write
18 | set wildmenu " visual autocomplete for command menu
19 | set wildignore+=*/.git/*,*/.hg/*,*/.svn/* " Ignore version control directories
20 |
21 | " Default tab settings
22 | set tabstop=2
23 | set softtabstop=2
24 | set shiftwidth=2
25 | set expandtab
26 |
27 | set textwidth=100
28 |
29 | " Spell settings
30 | execute 'set spellfile=' . stdpath("config") . '/spell.utf8.add'
31 |
32 | " Even though alacritty sets $COLORINFO = 'truecolor' neovim doesn't
33 | " seem to turn on gui colors, so we do it manually here
34 | if $TERM == 'alacritty'
35 | set termguicolors
36 | endif
37 |
38 | highlight clear SignColumn " NB: enforce this *after* color scheme
39 |
40 | let mapleader="\"
41 | nnoremap v :vsplit
42 | nnoremap t :tabe
43 | nnoremap s :vert Git
44 | nnoremap n :GitGutterNextHunk
45 | nnoremap p :GitGutterPrevHunk
46 | nnoremap h :set hlsearch!
47 | nnoremap rc :vsplit $MYVIMRC
48 | nnoremap l :lclose:cclose
49 |
50 | " Bring back (old) Y -> yy behavior
51 | " old habits die hard...
52 | nnoremap Y yy
53 |
54 | " Make work in terminal mode
55 | tnoremap
56 |
57 | " Ctrl-p with fzf
58 | nnoremap :Files
59 |
60 | " Keep accidentally hitting K (to move up) during visual selection
61 | " after hitting V (for visual line) without letting go of
62 | " which results in trying to run `man` on the word under the cursor
63 | " nnoremap K
64 | vnoremap K
65 |
66 | " Put searches in the center of the screen
67 | nnoremap n nzz
68 | nnoremap N Nzz
69 |
70 | " Start interactive EasyAlign in visual mode (e.g. vip)
71 | vmap (EasyAlign)
72 | " Start interactive EasyAlign for a motion/text object (e.g. gaip)
73 | nmap ga (EasyAlign)
74 |
75 | " Goto previous/next diagnostic warning/error
76 | nnoremap g[ lua vim.diagnostic.goto_prev()
77 | nnoremap g] lua vim.diagnostic.goto_next()
78 |
79 | nnoremap q lua vim.diagnostic.setqflist()
80 | nnoremap f lua vim.lsp.buf.format()
81 |
82 | nnoremap ] lua vim.lsp.buf.definition()
83 | nnoremap K lua vim.lsp.buf.hover()
84 | nnoremap gD lua vim.lsp.buf.implementation()
85 | nnoremap lua vim.lsp.buf.signature_help()
86 | nnoremap 1gD lua vim.lsp.buf.type_definition()
87 | nnoremap gr lua vim.lsp.buf.references()
88 | nnoremap g0 lua vim.lsp.buf.document_symbol()
89 | nnoremap gW lua vim.lsp.buf.workspace_symbol()
90 | nnoremap gd lua vim.lsp.buf.declaration()
91 | nnoremap gn lua vim.lsp.buf.rename()
92 | nnoremap lua vim.lsp.buf.code_action()
93 |
94 | " Trouble keybinds
95 | nnoremap xx Trouble diagnostics toggle
96 | nnoremap xw Trouble diagnostics focus=true
97 | nnoremap gR Trouble lsp focus=true
98 |
99 | function! s:check_back_space() abort
100 | let col = col('.') - 1
101 | return !col || getline('.')[col - 1] =~ '\s'
102 | endfunction
103 |
104 | " Treat :W as :w for when typos happen
105 | command! W w
106 | command! Wa wa
107 |
108 | augroup auto_cmds
109 | autocmd!
110 | " NB: hooks run in the order they were defined
111 | " NB: need to split the option changes like this or else it doesn't seem to work correctly
112 | autocmd Filetype * setlocal formatoptions-=t formatoptions-=r formatoptions-=o
113 |
114 | " Crontabs must usually be edited in place
115 | autocmd BufEnter crontab* setlocal backupcopy=yes
116 |
117 | autocmd Filetype help wincmd H
118 |
119 | " Turn on spell checking and auto wrap text
120 | autocmd Filetype markdown setlocal spell textwidth=80 formatoptions+=t
121 | autocmd Filetype gitcommit setlocal spell textwidth=72 formatoptions+=t
122 | autocmd Filetype jj setlocal spell textwidth=72 formatoptions+=t
123 | autocmd Filetype jjdescription setlocal spell textwidth=72 formatoptions+=t
124 |
125 | " Show diagnostic popup on cursor hold
126 | autocmd CursorHold * lua vim.diagnostic.open_float({focusable = false})
127 | augroup END
128 |
129 | " https://sharksforarms.dev/posts/neovim-rust/
130 | lua<'] = cmp.mapping.select_prev_item(),
300 | [''] = cmp.mapping.select_next_item(),
301 | -- Add tab support
302 | [''] = cmp.mapping.select_prev_item(),
303 | [''] = cmp.mapping.select_next_item(),
304 | [''] = cmp.mapping.scroll_docs(-4),
305 | [''] = cmp.mapping.scroll_docs(4),
306 | [''] = cmp.mapping.complete(),
307 | [''] = cmp.mapping.close(),
308 | [''] = cmp.mapping.confirm({
309 | behavior = cmp.ConfirmBehavior.Insert,
310 | select = true,
311 | })
312 | },
313 |
314 | -- Installed sources
315 | sources = {
316 | --{ name = 'buffer' },
317 | { name = 'nvim_lsp' },
318 | { name = 'path' },
319 | { name = 'vsnip' },
320 | },
321 | })
322 |
323 | require('dressing').setup({
324 | input = {
325 | enabled = true,
326 | },
327 | select = {
328 | enabled = true,
329 | },
330 | })
331 |
332 | require('fidget').setup({
333 | progress = {
334 | display = {
335 | progress_icon = {"moon"},
336 | },
337 | }
338 | })
339 |
340 | require('trouble').setup({
341 | auto_close = true,
342 | modes = {
343 | diagnostics = {
344 | auto_open = true,
345 | },
346 | },
347 | })
348 |
349 | require('kanagawa').setup({
350 | undercurl = true, -- enable undercurls
351 | commentStyle = { italic = false },
352 | functionStyle = {},
353 | keywordStyle = { italic = false },
354 | statementStyle = { bold = false },
355 | typeStyle = {},
356 | variablebuiltinStyle = { italic = false },
357 | specialReturn = true, -- special highlight for the return keyword
358 | specialException = true, -- special highlight for exception handling keywords
359 | transparent = false, -- do not set background color
360 | dimInactive = false, -- dim inactive window `:h hl-NormalNC`
361 | globalStatus = false, -- adjust window separators highlight for laststatus=3
362 | terminalColors = true, -- define vim.g.terminal_color_{0,17}
363 | colors = {},
364 | overrides = function(colors)
365 | return {
366 | }
367 | end,
368 | theme = "default" -- Load "default" theme or the experimental "light" theme
369 | })
370 | -- setup must be called before loading
371 | vim.cmd("colorscheme kanagawa")
372 |
373 | vim.lsp.inlay_hint.enable(true)
374 | EOF
375 |
--------------------------------------------------------------------------------
/config/sway/config:
--------------------------------------------------------------------------------
1 | # vim: ft=conf
2 | # Read `man 5 sway` for a complete reference.
3 |
4 | font pango:monospace 8
5 |
6 | default_border pixel 2
7 | default_floating_border pixel 2
8 | gaps inner 10
9 | smart_borders on
10 | smart_gaps on
11 |
12 | focus_wrapping no
13 | focus_follows_mouse yes
14 | workspace_layout default
15 | workspace_auto_back_and_forth no
16 |
17 | ### Variables
18 | #
19 | # Logo key. Use Mod1 for Alt.
20 | set $mod Mod4
21 | # Home row direction keys, like vim
22 | set $left h
23 | set $down j
24 | set $up k
25 | set $right l
26 |
27 | set $bgfile ~/Pictures/road.jpg
28 | set $term alacritty
29 | set $lockcmd 'swaylock -f -i $bgfile --indicator-idle-visible'
30 |
31 | set $nag exec swaynagmode
32 | set $nag_cancel $nag --exit
33 | set $nag_confirm $nag --confirm
34 | set $nag_select $nag --select
35 |
36 | set $exit $nag -t warning -m 'Exit sway?' -b 'Exit' 'swaymsg exit'
37 | set $suspend $nag -t warning -m 'Suspend?' -b 'Suspend' 'systemctl suspend'
38 |
39 | # -R is recommended for swaynag_command so that, upon a syntax error in your sway config, the
40 | # 'Reload Sway' option will be initially selected instead of the 'Exit Sway' option
41 | swaynag_command $nag -R
42 |
43 | # Using drun here to limit launcher to desktop applications since
44 | # other aribtrary commands should probably be launched in a terminal
45 | set $menu wofi --show drun
46 |
47 | ### Input configuration
48 | #
49 | # Example configuration:
50 | # input "2:14:SynPS/2_Synaptics_TouchPad" {
51 | # dwt enabled
52 | # tap enabled
53 | # natural_scroll enabled
54 | # middle_emulation enabled
55 | # }
56 |
57 | ### Output configuration
58 | output "Acer Technologies XF270HU T78AA0038543" {
59 | bg $bgfile center
60 | resolution 2560x1440@144Hz
61 | adaptive_sync on
62 | }
63 |
64 | ### Idle configuration
65 | exec swayidle -w \
66 | timeout 300 $lockcmd \
67 | timeout 600 'swaymsg "output * dpms off"' resume 'swaymsg "output * dpms on"' \
68 | timeout 1800 'systemctl suspend' \
69 | before-sleep $lockcmd
70 |
71 | ### Key bindings
72 | #
73 | # Basics:
74 | #
75 | # Start a terminal
76 | bindsym $mod+Return exec $term
77 |
78 | # Kill focused window
79 | bindsym $mod+Shift+q mode "kill"
80 |
81 | # Start your launcher
82 | bindsym $mod+d exec $menu
83 |
84 | # Drag floating windows by holding down $mod and left mouse button.
85 | # Resize them with right mouse button + $mod.
86 | # Despite the name, also works for non-floating windows.
87 | # Change normal to inverse to use left mouse button for resizing and right
88 | # mouse button for dragging.
89 | floating_modifier $mod normal
90 |
91 | # Reload the configuration file
92 | bindsym $mod+Shift+c reload
93 |
94 | # Exit sway (logs you out of your Wayland session)
95 | bindsym $mod+Shift+e exec $exit
96 |
97 | bindsym $mod+Shift+s exec $suspend
98 |
99 | #
100 | # Moving around:
101 | #
102 | # Move your focus around
103 | bindsym $mod+$left focus left
104 | bindsym $mod+$down focus down
105 | bindsym $mod+$up focus up
106 | bindsym $mod+$right focus right
107 | # Or use $mod+[up|down|left|right]
108 | bindsym $mod+Left focus left
109 | bindsym $mod+Down focus down
110 | bindsym $mod+Up focus up
111 | bindsym $mod+Right focus right
112 |
113 | # Move the focused window with the same, but add Shift
114 | bindsym $mod+Shift+$left move left
115 | bindsym $mod+Shift+$down move down
116 | bindsym $mod+Shift+$up move up
117 | bindsym $mod+Shift+$right move right
118 | # Ditto, with arrow keys
119 | bindsym $mod+Shift+Left move left
120 | bindsym $mod+Shift+Down move down
121 | bindsym $mod+Shift+Up move up
122 | bindsym $mod+Shift+Right move right
123 |
124 | #
125 | # Workspaces:
126 | #
127 | # Note: workspaces can have any name you want, not just numbers.
128 | # We just use 1-10 as the default.
129 |
130 | # Switch to workspace
131 | bindsym $mod+1 workspace number 1
132 | bindsym $mod+2 workspace number 2
133 | bindsym $mod+3 workspace number 3
134 | bindsym $mod+4 workspace number 4
135 | bindsym $mod+5 workspace number 5
136 | bindsym $mod+6 workspace number 6
137 | bindsym $mod+7 workspace number 7
138 | bindsym $mod+8 workspace number 8
139 | bindsym $mod+9 workspace number 9
140 | bindsym $mod+0 workspace number 10
141 | # Move focused container to workspace
142 | bindsym $mod+Shift+1 move container to workspace number 1
143 | bindsym $mod+Shift+2 move container to workspace number 2
144 | bindsym $mod+Shift+3 move container to workspace number 3
145 | bindsym $mod+Shift+4 move container to workspace number 4
146 | bindsym $mod+Shift+5 move container to workspace number 5
147 | bindsym $mod+Shift+6 move container to workspace number 6
148 | bindsym $mod+Shift+7 move container to workspace number 7
149 | bindsym $mod+Shift+8 move container to workspace number 8
150 | bindsym $mod+Shift+9 move container to workspace number 9
151 | bindsym $mod+Shift+0 move container to workspace number 10
152 |
153 | #
154 | # Layout stuff:
155 | #
156 | # You can "split" the current object of your focus with
157 | # NB: these are "inverted" if your mental model is coming from vim:
158 | # for example, a horizontal split makes two windows side by side
159 | # with each other (even though the division between them goes vertically)
160 | bindsym $mod+b splitv
161 | bindsym $mod+v splith
162 | bindsym $mod+z split none
163 |
164 | # Switch the current container between different layout styles
165 | bindsym $mod+s layout stacking
166 | bindsym $mod+w layout tabbed
167 | bindsym $mod+e layout toggle split
168 |
169 | # Make the current focus fullscreen
170 | bindsym $mod+f fullscreen
171 |
172 | # Toggle the current focus between tiling and floating mode
173 | bindsym $mod+Shift+space floating toggle
174 |
175 | # Swap focus between the tiling area and the floating area
176 | bindsym $mod+space focus mode_toggle
177 |
178 | # Move focus to the parent container
179 | bindsym $mod+a focus parent
180 |
181 | #
182 | # Scratchpad:
183 | #
184 | # Sway has a "scratchpad", which is a bag of holding for windows.
185 | # You can send windows there and get them back later.
186 |
187 | # Move the currently focused window to the scratchpad
188 | bindsym $mod+Shift+minus move scratchpad
189 |
190 | # Show the next scratchpad window or hide the focused scratchpad window.
191 | # If there are multiple scratchpad windows, this command cycles through them.
192 | bindsym $mod+minus scratchpad show
193 |
194 | #
195 | # Resizing containers:
196 | #
197 | mode "resize" {
198 | # left will shrink the containers width
199 | # right will grow the containers width
200 | # up will shrink the containers height
201 | # down will grow the containers height
202 | bindsym $left resize shrink width 10px
203 | bindsym $down resize grow height 10px
204 | bindsym $up resize shrink height 10px
205 | bindsym $right resize grow width 10px
206 |
207 | # Ditto, with arrow keys
208 | bindsym Left resize shrink width 10px
209 | bindsym Down resize grow height 10px
210 | bindsym Up resize shrink height 10px
211 | bindsym Right resize grow width 10px
212 |
213 | # Return to default mode
214 | bindsym Return mode "default"
215 | bindsym Escape mode "default"
216 | }
217 | bindsym $mod+r mode "resize"
218 |
219 | mode "nag" {
220 | bindsym Ctrl+d mode "default"
221 |
222 | bindsym Ctrl+c $nag_cancel
223 | bindsym q $nag_cancel
224 | bindsym Escape $nag_cancel
225 |
226 | bindsym Return $nag_confirm
227 |
228 | bindsym Tab $nag_select prev
229 | bindsym Right $nag_select prev
230 | bindsym $right $nag_select prev
231 |
232 | bindsym Shift+Tab $nag_select next
233 | bindsym Left $nag_select next
234 | bindsym $left $nag_select next
235 | }
236 |
237 | # A mode for "killing" apps, basically acting as a confirmation
238 | # before executing the kill
239 | mode "kill" {
240 | bindsym Return kill, mode "default"
241 |
242 | # Return to default mode
243 | bindsym Escape mode "default"
244 | bindsym Ctrl+c mode "default"
245 | bindsym q mode "default"
246 | }
247 |
248 | #
249 | # Volume
250 | #
251 | bindsym XF86AudioRaiseVolume exec 'pactl set-sink-volume @DEFAULT_SINK@ +5%'
252 | bindsym XF86AudioLowerVolume exec 'pactl set-sink-volume @DEFAULT_SINK@ -5%'
253 | bindsym XF86AudioMute exec 'pactl set-sink-mute @DEFAULT_SINK@ toggle'
254 |
255 | #
256 | # Brightness
257 | #
258 | bindsym XF86MonBrightnessDown exec "brightnessctl set 2%-"
259 | bindsym XF86MonBrightnessUp exec "brightnessctl set +2%"
260 |
261 | #
262 | # GTK settings
263 | #
264 | exec_always ~/.config/gtk-3.0/import-gsettings.sh
265 |
266 | #
267 | # Status Bar:
268 | #
269 | # Read `man 5 sway-bar` for more information about this section.
270 | bar {
271 | swaybar_command waybar
272 | }
273 |
274 | #
275 | # Assignments
276 | #
277 | assign [app_id="firefox"] workspace 2
278 | assign [class="discord"] workspace 3
279 |
280 | # It appears that windows created by discord (or probably electron)'
281 | # get their class filled in after creation, so a regular assignment
282 | # doesn't work, but having this workaround appears to work
283 | for_window [class="discord"] move container to workspace 3
284 |
285 | # Prevent idle lock/sleep from triggering if firefox/discord are fullscreen
286 | for_window [app_id="firefox"] inhibit_idle fullscreen
287 | for_window [class="discord"] inhibit_idle fullscreen
288 |
289 | #
290 | # Systemd hook
291 | #
292 | exec "systemctl --user import-environment DISPLAY I3SOCK SWAYSOCK WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XDG_SESSION_TYPE; systemctl --user start sway-session.target"
293 |
294 | #
295 | # Start up programs
296 | #
297 | exec "firefox"
298 | exec "Discord"
299 | exec "1password"
300 |
--------------------------------------------------------------------------------
/config/waybar/config:
--------------------------------------------------------------------------------
1 | {
2 | "position": "bottom",
3 | "modules-left": [
4 | "sway/workspaces",
5 | "sway/mode"
6 | ],
7 | "modules-center": [
8 | "sway/window"
9 | ],
10 | "modules-right": [
11 | "tray",
12 | "idle_inhibitor",
13 | "pulseaudio",
14 | "cpu",
15 | "memory",
16 | "temperature",
17 | "clock"
18 | ],
19 | "clock": {
20 | "interval": 1,
21 | "tooltip-format": "{:%Y %B}\n{calendar}",
22 | "format": "{:%I:%M:%S %p}",
23 | "format-alt": "{:%Y-%m-%d}"
24 | },
25 | "cpu": {
26 | "format": "{usage}% ",
27 | "tooltip": true
28 | },
29 | "idle_inhibitor": {
30 | "format": "{icon}",
31 | "format-icons": {
32 | "activated": "",
33 | "deactivated": ""
34 | }
35 | },
36 | "memory": {
37 | "max-length": "2",
38 | "format": "{avail:0.1f}G "
39 | },
40 | "network": {
41 | "format-wifi": "{signalStrength}% ",
42 | "format-ethernet": "{ifname}: {ipaddr}/{cidr} ",
43 | "format-linked": "{ifname} (No IP) ",
44 | "format-disconnected": "Disconnected ⚠",
45 | "format-alt": "{ifname}: {ipaddr}/{cidr}"
46 | },
47 | "pulseaudio": {
48 | "scroll-step": 0,
49 | "format": "{volume}% {icon} {format_source}",
50 | "format-bluetooth": "{volume}% {icon} {format_source}",
51 | "format-bluetooth-muted": " {icon} {format_source}",
52 | "format-muted": " {format_source}",
53 | "format-source": "{volume}% ",
54 | "format-source-muted": "",
55 | "format-icons": {
56 | "headphone": "",
57 | "hands-free": "",
58 | "headset": "",
59 | "phone": "",
60 | "portable": "",
61 | "car": "",
62 | "default": [
63 | "",
64 | "",
65 | ""
66 | ]
67 | },
68 | "on-click": "pavucontrol"
69 | },
70 | "tray": {
71 | "spacing": 10
72 | },
73 | "sway/mode": {
74 | "format": "{}"
75 | },
76 | "sway/workspaces": {
77 | "disable-scroll": true,
78 | "format": "{name}{icon}",
79 | "format-icons": {
80 | "urgent": " ",
81 | "default": ""
82 | }
83 | },
84 | "temperature": {
85 | "critical-threshold": 80,
86 | "format": "{temperatureC}°C {icon}",
87 | "format-icons": [
88 | "",
89 | "",
90 | ""
91 | ]
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/config/wofi/config:
--------------------------------------------------------------------------------
1 | allow_images=true
2 | allow_markup=true
3 | insensitive=true
4 |
--------------------------------------------------------------------------------
/config/wofi/style.css:
--------------------------------------------------------------------------------
1 | #entry:selected {
2 | background-color: #4c566a;
3 | color: #2e3440;
4 | }
5 |
6 | #input {
7 | border: none;
8 | border-radius: 0px;
9 | }
10 |
11 | #text {
12 | /* Give the text some breathing room from the icons */
13 | margin-left: 0.5em;
14 | }
15 |
16 | window {
17 | background-color: #3b4252;
18 | border: 2px solid #2e3440;
19 | border-radius: 5px;
20 | color: #eceff4;
21 | opacity: 0.9;
22 | }
23 |
--------------------------------------------------------------------------------
/default.nix:
--------------------------------------------------------------------------------
1 | let
2 | flake-compat = import ./flake-compat.nix;
3 | in
4 | flake-compat.defaultNix
5 |
--------------------------------------------------------------------------------
/docs/machine_init.md:
--------------------------------------------------------------------------------
1 | # Machine initialization
2 |
3 | NixOS is a great tool for declaratively managing system configurations (namely
4 | what packages and other config files are available), but there are still a
5 | number of imperative steps to run when setting up a brand new machine before
6 | installation. (Okay, there *are* some projects which seek to automate this
7 | process as well, but unless you are constantly (re-)provisioning new machines,
8 | it is probably overkill...). There's a number of available tutorials and guides
9 | on how to prepare a machine for NixOS installation, but I found all of them to
10 | be incredibly basic (e.g. set up single ext4 partition and move on), or didn't
11 | quite fit the requirements I was imposing.
12 |
13 | Since this was also my first "real" Linux install, there were a number of things
14 | I was unsure of and had to research on my own to figure out whether it was
15 | relevant or what configurations to choose. Here I document all the steps I went
16 | through, along with (an attempt!) to capture my assumptions and deliberate
17 | decisions so that whether I got something wrong, it stops being correct in the
18 | future, or you simply disagree with my choices, it becomes easy to spot where to
19 | stray and where to follow this guide.
20 |
21 | Hope this is useful.
22 |
23 | ## Acknowledgements
24 |
25 | Huge thanks to [Graham Christensen](https://grahamc.com) whose blog posts were
26 | my main inspiration and guide for installation:
27 | * [NixOS on a Dell 9560](https://web.archive.org/web/20190831102758/https://grahamc.com/blog/nixos-on-dell-9560)
28 | * [ZFS Datasets for NixOS](https://web.archive.org/web/20200621003223/https://grahamc.com/blog/nixos-on-zfs)
29 | * [Erase your darlings](https://web.archive.org/web/20201124122057/https://grahamc.com/blog/erase-your-darlings)
30 |
31 | ## Assumptions and Requirements
32 |
33 | Below is a quick summary of the assumptions and requirements I had for the
34 | system to provide some historical context and keep the rest of the guide
35 | focused on running each step.
36 |
37 | The installation was done in early December of 2020 on a brand new 1 TiB SSD.
38 | The disk has four partitions, and here is the final result:
39 | 1. A 1 GiB unencrypted boot partition (more on this later). I chose 1 GiB
40 | because I've been bitten by having too small of a boot partition in the
41 | past, and it will be a headache to try to resize the partition if it turns
42 | out too small. Most people recommend using 300-500 MiB, but since I have
43 | plenty of storage to spare I decided to use 1 GiB and forget about it (note
44 | that every NixOS generation adds links in this partition, so having too
45 | many generations laying around can fill it up as well).
46 | 1. A 32 MiB LUKS encrypted partition which contains the key for the remaining
47 | partitions. I forget exactly why I went with 32 MiB exactly, but I wanted
48 | to make this partition large enough to handle any LUKS upgrades or extra
49 | key configurations, and I have space to spare (I think the LUKS2 max header
50 | size is 4 MiB, and most people recommend having a partition about this big
51 | plus some space for the actual key).
52 | 1. A 32 GiB swap partition because this machine has 16 GiB of RAM and I left
53 | some headroom in case I upgrade it. I don't plan on enabling swap (to avoid
54 | wearing out my SSD), but I made the partition anyway in case I change my
55 | mind.
56 | 1. The remainder of the disk is managed by ZFS split into the following
57 | datasets:
58 | * `/local` - dataset for mounting `/nix/store`. It is not snapshotted since
59 | the nix store can be trivially repopulated/rebuilt if the data is lost or
60 | corrupted
61 | * `/reserved` - A 100 GiB reserved partition to act as an over-provisioning
62 | guard and preserve the SSD performance (SSDs avoid wearing out individual
63 | blocks by moving writes around. But if the drive fills up, the speed and
64 | health of the drive will decrease. By never mounting this dataset, and
65 | asking ZFS to ensure there is always 100 GiB available for it, I'm
66 | effectively capping the disk at 90%).
67 | * `/system` - dataset for mounting `/root` and `/var`. This dataset is
68 | regularly snapshotted so I can rollback in case something catastrophic
69 | happens. I have not yet decided to [erase my
70 | darlings](https://grahamc.com/blog/erase-your-darlings) but if I do I
71 | would move my `/root` mount...
72 | * `/user` - dataset for storing user home directories, regularly
73 | snapshotted
74 |
75 | ### Why ZFS?
76 |
77 | I had generally heard good things about it, namely that it's a stable file
78 | system implementation which supports efficient snapshots, rollbacks, and data
79 | exports. I briefly looked into btrfs which also supports very similar
80 | features, but the NixOS support (at the time) was lacking, and since I had
81 | not previously used neither btrfs nor zfs, I went with the latter since I
82 | expected the experience to be smoother.
83 |
84 | ### What about SSD over-provisioning?
85 |
86 | SSDs are made up of flash storage which supports a (large, but) finite number
87 | of write operations before the medium begins to degrade. The SSD controller
88 | performs wear leveling by effectively writing new data in a new location by
89 | transparently remapping the block identifier to the new location. This
90 | requires having some free space on the disk to "move" the blocks around. If
91 | the disk fills up, the controller will be forced to do subsequent writes in
92 | the same spot.
93 |
94 | Over-provisioning is a name that storage vendors give to the concept of
95 | reserving some storage capacity to avoid accidentally filling it up and
96 | degrading performance. Some vendors state that their modern products
97 | automatically achieve this in their firmware without any manual intervention
98 | (maybe the drive itself has more storage than advertised?). Other vendors
99 | peddle special tools like Samsung Data Magician (which simply creates an empty
100 | partition) to achieve the task.
101 |
102 | Since I have lots of storage to spare on my 1 TiB drive, I decided to
103 | over-provision 10% of its capacity by creating a reserved ZFS pool which I
104 | will never mount. I can easily remove or shrink that reservation if needed, so
105 | this seemed like a sensible choice.
106 |
107 | ### Why an unencrypted boot partition?
108 |
109 | Ultimately, you have to trust some piece of software somewhere to take your
110 | keyboard input and unlock the disk without leaking the key somehow. An
111 | unencrypted boot partition means someone who gets access to the disk can put a
112 | compromised boot loader that can steal the key. Using an encrypted boot
113 | partition avoids this risk, but that means that the UEFI implementation needs
114 | to do the decryption, and someone who can access it could flash a compromised
115 | implementation which also steals the key. A solution to that can be to use
116 | Secure boot/Trusted boot but now we have to trust that the hardware itself
117 | isn't compromised with some other back-door... It's turtles all the way down.
118 |
119 | My threat model does not include someone physically accessing my machine, so
120 | an unencrypted boot partition works fine for me. Setting up a trusted boot
121 | sequence sounds interesting, but it's a project for another time.
122 |
123 | ### Why LUKS and not native-ZFS encryption?
124 |
125 | I chose to go with using LUKS to encrypt the entire disk and run ZFS from
126 | within it. LUKS has been around for a while and there is plenty of
127 | tooling/documentation/guides around it, so it seemed like a safe approach.
128 |
129 | ZFS apparently supports natively encrypting the disk, which avoids some double
130 | indirection when trimming SSD blocks (and then having the decryption mapper
131 | propagate those to the device). There are some potential security concerns
132 | (like leaking dataset names/sizes and dedup tables), but none of them are
133 | within my threat model. What really convinced me against using native-ZFS
134 | encryption was the impression that the feature was somewhat newer, and I
135 | didn't want to risk having it eat my laundry...
136 |
137 | ### Why use the `allowDiscards` flag with LUKS?
138 |
139 | The `allowDiscards` option instructs the mapper to propagate trim commands
140 | issued by the underlying filesystem, which allows the SSD to better perform
141 | wear leveling. This option is disabled by default since there are some
142 | theoretical attack vectors from having it enabled (namely leaking which blocks
143 | are trimmed, an some potential oracle attacks if the attacker can influence
144 | what data is written to the disk).
145 |
146 | Since this doesn't fit my threat model (namely someone gaining physical access
147 | to my disk) and since I am more worried about maintaining my SSD performance,
148 | I decided to enable this option.
149 |
150 | ## Installation
151 |
152 | On to the good stuff, actual installation steps start here! Note all commands
153 | should be run as root.
154 |
155 | ### Installer preparation
156 |
157 | 1. [Download an installer](https://nixos.org/download.html) and burn it to a
158 | bootable USB drive. Worth noting that if you already have an existing nix
159 | install you can create your own custom installer (e.g. if you're a power
160 | user or you need specific tools available during installation), but the
161 | base installer should cover all the bases.
162 | 1. If there is an existing Windows installation on this machine (even if it is
163 | on an entirely separate drive), consider [turning off the "Enable fast
164 | startup" option](https://askubuntu.com/questions/1291758/ubuntu-20-04-and-fenvi-ax200-wifi-bluetooth-card-drivers-or-soolution-to-wifi)
165 | and rebooting before continuing. I had to do this to get my bluetooth/wifi
166 | (AX200) adapter to work (yay Windows hacks to gain speedup!).
167 | 1. (Optional) if you have other drives in the machine, consider unplugging
168 | them to avoid accidentally overwriting the wrong disk due to a typo...
169 | 1. Plug in the USB, reboot the computer, hit the appropriate keys during the
170 | BIOS, and boot into the USB
171 |
172 | ### Partitioning the Disk
173 |
174 | 1. If a graphical installer image was used, it should drop us in a desktop
175 | environment which should set up some basic stuff like networking. The rest
176 | of the commands all need root privileges, so open a terminal and switch to
177 | root to avoid having to prefix everything with `sudo`.
178 |
179 | ```sh
180 | sudo su
181 | ```
182 | 1. Next, we need to figure out which disk we want to use.
183 |
184 | ```sh
185 | ls /dev
186 | ```
187 | 1. In my case this is my second NVMe in this machine so I will be using
188 | `nvme1n1`, but you may see a different number based on what is connected.
189 | We'll store this in a variable to make it easier to copy-paste other
190 | commands, so **make sure to replace the `...` with your selected drive**:
191 |
192 | ```sh
193 | DISK=/dev/...
194 | ```
195 | 1. Next, it's time to partition the actual disk. I'm going to be creating the
196 | following partitions:
197 | * 1 GiB (unencrypted) boot partition - for storing the initial boot files
198 | * 32 MiB LUKS key partition - the key for the rest of the disk. This will be
199 | encrypted with a password that we remember (and type in during boot)
200 | * 32 GiB swap partition - for enabling system swap
201 | * The remainder of the drive will be our actual, usable, partition
202 |
203 | We're going to be using `gdisk` below, but if you know how to use another
204 | disk partition program, feel free to use it instead.
205 |
206 | ```sh
207 | gdisk "${DISK}"
208 | ```
209 |
210 |
211 | Click to expand!
212 |
213 | ```
214 | GPT fdisk (gdisk) version 1.0.5
215 |
216 | Partition table scan:
217 | MBR: not present
218 | BSD: not present
219 | APM: not present
220 | GPT: not present
221 |
222 | Creating new GPT entries in memory.
223 |
224 | Command (? for help): o
225 | This option deletes all partitions and creates a new protective MBR.
226 | Proceed? (Y/N): Y
227 |
228 | Command (? for help): n
229 | Partition number (1-128, default 1):
230 | First sector (34-1953525134, default = 2048) or {+-}size{KMGTP}:
231 | Last sector (2048-1953525134, default = 1953525134) or {+-}size{KMGTP}: +1G
232 | Current type is 8300 (Linux filesystem)
233 | Hex code or GUID (L to show codes, Enter = 8300): EF00
234 | Changed type of partition to 'EFI system partition'
235 |
236 | Command (? for help): n
237 | Partition number (2-128, default 2): 2
238 | First sector (34-1953525134, default = 2099200) or {+-}size{KMGTP}:
239 | Last sector (2099200-1953525134, default = 1953525134) or {+-}size{KMGTP}: +32M
240 | Current type is 8300 (Linux filesystem)
241 | Hex code or GUID (L to show codes, Enter = 8300):
242 | Changed type of partition to 'Linux filesystem'
243 |
244 | Command (? for help): c
245 | Partition number (1-2): 2
246 | Enter name: luks key
247 |
248 | Command (? for help): n
249 | Partition number (3-128, default 3):
250 | First sector (34-1953525134, default = 2164736) or {+-}size{KMGTP}:
251 | Last sector (2164736-1953525134, default = 1953525134) or {+-}size{KMGTP}: +32G
252 | Current type is 8300 (Linux filesystem)
253 | Hex code or GUID (L to show codes, Enter = 8300):
254 | Changed type of partition to 'Linux filesystem'
255 |
256 | Command (? for help): c
257 | Partition number (1-3): 3
258 | Enter name: swap
259 |
260 | Command (? for help): n
261 | Partition number (4-128, default 4):
262 | First sector (34-1953525134, default = 69273600) or {+-}size{KMGTP}:
263 | Last sector (69273600-1953525134, default = 1953525134) or {+-}size{KMGTP}:
264 | Current type is 8300 (Linux filesystem)
265 | Hex code or GUID (L to show codes, Enter = 8300):
266 | Changed type of partition to 'Linux filesystem'
267 |
268 | Command (? for help): c
269 | Partition number (1-4): 4
270 | Enter name: root
271 |
272 | Command (? for help): p
273 | Disk /dev/nvme1n1: 1953525168 sectors, 931.5 GiB
274 | Model: Samsung SSD 970 EVO Plus 1TB
275 | Sector size (logical/physical): 512/512 bytes
276 | Disk identifier (GUID): 22610B10-DB5F-467D-8B9E-ECD88878ABA5
277 | Partition table holds up to 128 entries
278 | Main partition table begins at sector 2 and ends at sector 33
279 | First usable sector is 34, last usable sector is 1953525134
280 | Partitions will be aligned on 2048-sector boundaries
281 | Total free space is 2014 sectors (1007.0 KiB)
282 |
283 | Number Start (sector) End (sector) Size Code Name
284 | 1 2048 2099199 1024.0 MiB EF00 EFI system partition
285 | 2 2099200 2164735 32.0 MiB 8300 luks key
286 | 3 2164736 69273599 32.0 GiB 8300 swap
287 | 4 69273600 1953525134 898.5 GiB 8300 root
288 |
289 | Command (? for help): w
290 |
291 | Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
292 | PARTITIONS!!
293 |
294 | Do you want to proceed? (Y/N): Y
295 | OK; writing new GUID partition table (GPT) to /dev/nvme1n1.
296 | The operation has completed successfully.
297 | ```
298 |
299 |
300 | ### LUKS Setup
301 |
302 | 1. Now that the disk is partitioned, it's time to turn on encryption! First
303 | we'll initialize our `cryptkey` partition and fill it with random data.
304 | This will eventually become the key to decrypt our actual drive. Note that
305 | **this is the day-to-day password used to unlock the computer at boot**.
306 |
307 | ```sh
308 | cryptsetup luksFormat --type luks1 "${DISK}p2"
309 | cryptsetup luksOpen "${DISK}p2" cryptkey
310 | dd if=/dev/urandom of=/dev/mapper/cryptkey bs=1024 status=progress
311 | ```
312 | 1. Next, we initialize the swap partition (which will share the same key
313 | written to our `cryptkey` partition along with the rest of the drive). Note
314 | that there is no backup key set for this partition, but there should never
315 | be any reason to try to recover any data written in the swap in case the
316 | `cryptkey` partition is damaged.
317 |
318 | ```sh
319 | cryptsetup luksFormat --type luks1 --keyfile-size 8192 --key-file /dev/mapper/cryptkey "${DISK}p3"
320 | # Mount the partition after creation
321 | cryptsetup luksOpen --keyfile-size 8192 --key-file /dev/mapper/cryptkey "${DISK}p3" cryptswap
322 | ```
323 | 1. Now it's time to encrypt the rest of the drive. Note that first we'll
324 | initialize the drive with a *backup passphrase*. Make this a strong
325 | password (e.g. diceware) **and write it down and store is someplace safe**.
326 | If the `cryptkey` partition becomes damaged, this will be the only way to
327 | recover the data on the drive!
328 |
329 | ```sh
330 | # Initialize with a passphrase
331 | cryptsetup luksFormat --type luks1 "${DISK}p4"
332 | # Add the cryptkey partition as a keyfile for unlocking during boot
333 | cryptsetup luksAddKey --new-keyfile-size 8192 "${DISK}p4" /dev/mapper/cryptkey
334 | ```
335 | 1. Finally, mount the root partition. **Note the use of `--allow-discards`
336 | which may be a security risk**. Read about the choices and assumptions
337 | above as to why I have chosen to use this flag, but feel free to omit it if
338 | desired.
339 | ```sh
340 | cryptsetup luksOpen --keyfile-size 8192 --key-file /dev/mapper/cryptkey --allow-discards "${DISK}p4" cryptroot
341 | ```
342 | 1. Note that some guides recommend filling the drive with random data before
343 | doing the encryption to avoid leaking information about how big the drive
344 | is and which blocks are encrypted, etc. I am going to omit this step since
345 | I want to avoid wearing out my SSD,
346 |
347 | ### Filesystem setup
348 |
349 | 1. Initialize the boot partition as vfat
350 | ```sh
351 | mkfs.vfat "${DISK}p1"
352 | ```
353 | 1. Initialize the swap partition
354 | ```sh
355 | mkswap /dev/mapper/cryptswap
356 | ```
357 | 1. Next, it's time to initialize zfs. Feel free to pick any pool name you want,
358 | but consider keeping it unique if you manage other zfs pools elsewhere
359 | ```sh
360 | POOL=nvme-pool # change as desired
361 | zpool create "${POOL}" /dev/mapper/cryptroot
362 | # autotrim enabled to maintain SSD performance
363 | zpool set autotrim=on "${POOL}"
364 | ```
365 | 1. Create the desired root datasets (or mounts) in the pool. Note that at the
366 | time of writing, using `mountpoint=legacy` is required for correct NixOS
367 | interoperation.
368 |
369 | ```
370 | zfs create -o compression=on -o mountpoint=legacy "${POOL}/local"
371 | zfs create -o compression=on -o mountpoint=legacy "${POOL}/system"
372 | zfs create -o compression=on -o mountpoint=legacy "${POOL}/user"
373 | zfs create -o compression=on -o mountpoint=legacy "${POOL}/reserved"
374 | ```
375 | 1. Set a variable with the default username you wish to use which we'll use
376 | for creating a home directory later
377 | ```sh
378 | MY_USER=...
379 | ```
380 | 1. Next, we create any child datasets. Note that the `acltype=posixacl` flag
381 | is required wherever `/var` will be mounted, so that users can access their
382 | own journal logs
383 |
384 | ```sh
385 | zfs create -o xattr=sa -o acltype=posixacl "${POOL}/system/var"
386 |
387 | zfs create "${POOL}/system/root"
388 | zfs create "${POOL}/user/home"
389 | zfs create "${POOL}/user/home/${MY_USER}"
390 | ```
391 | 1. Set a quota and reservation on the `reserved` data set. This will ensure
392 | that the disk always has the specified amount of space available, and since
393 | we will never mount this partition, we're effectively saving some space
394 | from never being written (i.e. over-provisioning the SSD to maintain its
395 | performance)
396 |
397 |
398 | ```sh
399 | zfs set reservation=100G "${POOL}/reserved"
400 | zfs set quota=100G "${POOL}/reserved" # ensure we can't accidentally write more than 100G to this partition
401 | ```
402 | 1. Next, we enable local snapshotting so that we can quickly recover past
403 | state if something goes wrong. Note that we only need to snapshot user data
404 | and the system root. Other easily-rebuilt partitions (like `local`) don't
405 | need snapshotting enabled. Also note that the actual snapshot frequency
406 | will be managed by our NixOS configuration
407 |
408 | ```sh
409 | zfs set com.sun:auto-snapshot=true "${POOL}/system"
410 | zfs set com.sun:auto-snapshot=true "${POOL}/user"
411 | ```
412 |
413 | 1. Lastly, *mount everything*! If you forget to mount a zfs dataset to the
414 | right place, then data may get written in the wrong place and fail during
415 | boot!
416 | * I made the mistake of forgetting to mount the `/nix/store` path on the
417 | new drive. The installer happily filled the root partition with the data,
418 | but when my configuration correctly mounted the right dataset during
419 | boot, suddenly all the packages were missing!
420 |
421 | ```sh
422 | # Mount the root partition itself
423 | mount -t zfs "${POOL}/system/root" /mnt
424 |
425 | # Make directory entries for the subsequent mounts
426 | mkdir -p /mnt/boot
427 | mkdir -p /mnt/nix
428 | mkdir -p /mnt/var
429 | mkdir -p "/mnt/home/${MY_USER}"
430 |
431 | # Mount the boot partition
432 | mount "${DISK}p1" /mnt/boot
433 |
434 | # Mount the rest of our zfs datasets
435 | mount -t zfs "${POOL}/local/nix" /mnt/nix
436 | mount -t zfs "${POOL}/system/var" /mnt/var
437 | mount -t zfs "${POOL}/user/home/${MY_USER}" "/mnt/home/${MY_USER}"
438 | ```
439 |
440 | ### NixOS installation
441 |
442 | 1. Finally it's time to get nix involved! Run the generation command below
443 | and it should do a good job at auto-detecting any hardware and filesystem
444 | configurations
445 |
446 | ```sh
447 | nixos-generate-config --root /mnt
448 | ```
449 | 1. Edit the generated config in `/mnt/etc/nixos/configuration.nix`. If you're
450 | new to NixOS, or missing your favorite editor/environment setup, consider
451 | lightly tweaking the default config (e.g. turning on ssh, setting up
452 | networking, etc.) to get things going and come back to flesh it out later.
453 | But before we continue there are a few more things to double check:
454 | 1. Make sure that the `initrd.luks.devices` are correctly configured. If
455 | anything is missing, or the disk uuid is incorrect, **carefully** update
456 | the config and double check everything
457 | 1. Also carefully note that the `cryptkey` declaration shows up before any
458 | other partions which are unlocked by it!
459 | 1. Make sure to update the `keyFileSize` parameter to whatever was used
460 | during initialization
461 | 1. Also make sure to set the `allowDiscards` flag if used above
462 | (**noting the security caveats from before**)
463 | 1. Make sure that all filesystems are correctly mapped to their zfs data
464 | sets.
465 | 1. Add any missing `boot.initrd.availableKernelModules`. For example, I
466 | had to add `"amdgpu"` to fix some screen resolution issues during early
467 | boot.
468 | 1. Add your default user and set their home directory
469 | 1. Time to actually install NixOS now! After the initial install is done,
470 | reboot and hope everything went well...
471 |
472 | ```sh
473 | nixos-install
474 | reboot
475 | ```
476 | 1. If you got this far and were able to log in, congrats, you did it! A few
477 | more things to consider doing:
478 | * Change the password for your default user
479 | * Change the root password, or even better, lock the root account so no one
480 | can log into it directly
481 |
--------------------------------------------------------------------------------
/flake-compat.nix:
--------------------------------------------------------------------------------
1 | let
2 | lock = builtins.fromJSON (builtins.readFile ./flake.lock);
3 | compat = fetchTarball {
4 | url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
5 | sha256 = lock.nodes.flake-compat.locked.narHash;
6 | };
7 | in
8 | import compat { src = ./.; }
9 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-compat": {
4 | "flake": false,
5 | "locked": {
6 | "lastModified": 1747046372,
7 | "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
8 | "owner": "edolstra",
9 | "repo": "flake-compat",
10 | "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
11 | "type": "github"
12 | },
13 | "original": {
14 | "owner": "edolstra",
15 | "repo": "flake-compat",
16 | "type": "github"
17 | }
18 | },
19 | "flake-utils": {
20 | "inputs": {
21 | "systems": "systems"
22 | },
23 | "locked": {
24 | "lastModified": 1731533236,
25 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
26 | "owner": "numtide",
27 | "repo": "flake-utils",
28 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
29 | "type": "github"
30 | },
31 | "original": {
32 | "owner": "numtide",
33 | "repo": "flake-utils",
34 | "type": "github"
35 | }
36 | },
37 | "home-manager": {
38 | "inputs": {
39 | "nixpkgs": [
40 | "nixpkgs"
41 | ]
42 | },
43 | "locked": {
44 | "lastModified": 1748618795,
45 | "narHash": "sha256-XrNoXAbUenzde4NKMsuCYdmW8t+2/Ks+vcFrlwRh4K4=",
46 | "owner": "nix-community",
47 | "repo": "home-manager",
48 | "rev": "214f9bd3a693bbc8cc6d705d01421787e04eaacd",
49 | "type": "github"
50 | },
51 | "original": {
52 | "owner": "nix-community",
53 | "repo": "home-manager",
54 | "type": "github"
55 | }
56 | },
57 | "nixos-hardware": {
58 | "locked": {
59 | "lastModified": 1748613622,
60 | "narHash": "sha256-SLB2MV138ujdjw0ETEakNt/o2O+d/QtvNLlwaBZSWKg=",
61 | "owner": "NixOS",
62 | "repo": "nixos-hardware",
63 | "rev": "b9d69212b5e65620e7d5b08df818db656f7fefb3",
64 | "type": "github"
65 | },
66 | "original": {
67 | "owner": "NixOS",
68 | "repo": "nixos-hardware",
69 | "type": "github"
70 | }
71 | },
72 | "nixos-pibox": {
73 | "inputs": {
74 | "flake-compat": [
75 | "flake-compat"
76 | ],
77 | "flake-utils": [
78 | "flake-utils"
79 | ],
80 | "nixpkgs": [
81 | "nixpkgs"
82 | ],
83 | "pibox-framebuffer": "pibox-framebuffer",
84 | "pibox-os": "pibox-os"
85 | },
86 | "locked": {
87 | "lastModified": 1746291289,
88 | "narHash": "sha256-MEKMG3tOffcYf5NJ4JwDgPx4ZGo8wRtPRO8XZaE3FY4=",
89 | "owner": "ipetkov",
90 | "repo": "nixos-pibox",
91 | "rev": "db48252606e1d36b637dfaabae7f965fcc44fad3",
92 | "type": "github"
93 | },
94 | "original": {
95 | "owner": "ipetkov",
96 | "repo": "nixos-pibox",
97 | "type": "github"
98 | }
99 | },
100 | "nixpkgs": {
101 | "locked": {
102 | "lastModified": 1748460289,
103 | "narHash": "sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg=",
104 | "owner": "NixOS",
105 | "repo": "nixpkgs",
106 | "rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102",
107 | "type": "github"
108 | },
109 | "original": {
110 | "owner": "NixOS",
111 | "ref": "nixos-unstable",
112 | "repo": "nixpkgs",
113 | "type": "github"
114 | }
115 | },
116 | "nixpkgs-for-rpi": {
117 | "locked": {
118 | "lastModified": 1748460289,
119 | "narHash": "sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg=",
120 | "owner": "NixOS",
121 | "repo": "nixpkgs",
122 | "rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102",
123 | "type": "github"
124 | },
125 | "original": {
126 | "owner": "NixOS",
127 | "repo": "nixpkgs",
128 | "rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102",
129 | "type": "github"
130 | }
131 | },
132 | "pibox-framebuffer": {
133 | "flake": false,
134 | "locked": {
135 | "lastModified": 1707191842,
136 | "narHash": "sha256-xxHs2lseT1111EF34ABluOH/GUTXCMdcDeoo5k13geU=",
137 | "owner": "kubesail",
138 | "repo": "pibox-framebuffer",
139 | "rev": "4295efeffa22369f3843bedc989d881dcec26bfa",
140 | "type": "github"
141 | },
142 | "original": {
143 | "owner": "kubesail",
144 | "repo": "pibox-framebuffer",
145 | "type": "github"
146 | }
147 | },
148 | "pibox-os": {
149 | "flake": false,
150 | "locked": {
151 | "lastModified": 1714956345,
152 | "narHash": "sha256-YuHCnBgqpgifWfybXDe6lTELtaKKznhCLS40hH/cLlA=",
153 | "owner": "kubesail",
154 | "repo": "pibox-os",
155 | "rev": "14a6e60814514e85876bf9125b80b39100ffadd0",
156 | "type": "github"
157 | },
158 | "original": {
159 | "owner": "kubesail",
160 | "repo": "pibox-os",
161 | "type": "github"
162 | }
163 | },
164 | "root": {
165 | "inputs": {
166 | "flake-compat": "flake-compat",
167 | "flake-utils": "flake-utils",
168 | "home-manager": "home-manager",
169 | "nixos-hardware": "nixos-hardware",
170 | "nixos-pibox": "nixos-pibox",
171 | "nixpkgs": "nixpkgs",
172 | "nixpkgs-for-rpi": "nixpkgs-for-rpi"
173 | }
174 | },
175 | "systems": {
176 | "locked": {
177 | "lastModified": 1681028828,
178 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
179 | "owner": "nix-systems",
180 | "repo": "default",
181 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
182 | "type": "github"
183 | },
184 | "original": {
185 | "owner": "nix-systems",
186 | "repo": "default",
187 | "type": "github"
188 | }
189 | }
190 | },
191 | "root": "root",
192 | "version": 7
193 | }
194 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "ipetkov's nixos configs";
3 |
4 | inputs = {
5 | # Use the nixos-unstable channel for all of our configurations, even on non-NixOS
6 | # systems. The nixpkgs-unstable branch tends to break a bit more often than
7 | # nixos-unstable, so trying this out to see if things are a bit smoother. Also, it is
8 | # nice having the exact same application versions across all machines rather than
9 | # mixing and matching branches.
10 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
11 | # Pinned because deploying to rpi is slow as molasses due to SD card I/O being crap
12 | nixpkgs-for-rpi.url = "github:NixOS/nixpkgs/96ec055edbe5ee227f28cdbc3f1ddf1df5965102";
13 | nixos-hardware.url = "github:NixOS/nixos-hardware";
14 |
15 | flake-compat = {
16 | url = "github:edolstra/flake-compat";
17 | flake = false;
18 | };
19 |
20 | flake-utils = {
21 | url = "github:numtide/flake-utils";
22 | };
23 |
24 | home-manager = {
25 | url = "github:nix-community/home-manager";
26 | inputs = {
27 | nixpkgs.follows = "nixpkgs";
28 | };
29 | };
30 |
31 | nixos-pibox = {
32 | url = "github:ipetkov/nixos-pibox";
33 | inputs = {
34 | nixpkgs.follows = "nixpkgs";
35 | flake-compat.follows = "flake-compat";
36 | flake-utils.follows = "flake-utils";
37 | };
38 | };
39 | };
40 |
41 | outputs =
42 | inputs@{ self, ... }:
43 | let
44 | inherit (inputs.nixpkgs) lib legacyPackages;
45 |
46 | myPkgs = self.packages;
47 |
48 | myLib = import ./lib {
49 | inherit inputs lib myPkgs;
50 | };
51 |
52 | inherit (myLib) mkHost;
53 |
54 | systemLinux = "x86_64-linux";
55 | systemLinuxArm = "aarch64-linux";
56 | in
57 | {
58 | homeManagerModules.default = import ./homeManagerModules/default.nix;
59 |
60 | homeConfigurations = { };
61 |
62 | nixosModules.default = import ./nixosModules/default.nix;
63 |
64 | nixosConfigurations = {
65 | asphodel = mkHost {
66 | system = systemLinuxArm;
67 | rootConfig = ./nixosConfigurations/asphodel;
68 | };
69 |
70 | elysium = mkHost {
71 | system = systemLinux;
72 | rootConfig = ./nixosConfigurations/elysium;
73 | };
74 |
75 | rpi = mkHost {
76 | system = systemLinuxArm;
77 | rootConfig = ./nixosConfigurations/rpi;
78 | nixpkgs = inputs.nixpkgs-for-rpi;
79 | };
80 |
81 | tartarus = mkHost {
82 | system = systemLinux;
83 | rootConfig = ./nixosConfigurations/tartarus;
84 | includeHomeManager = true;
85 | };
86 | };
87 | }
88 | // inputs.flake-utils.lib.eachDefaultSystem (
89 | system:
90 | let
91 | pkgs = legacyPackages.${system};
92 |
93 | packages = lib.filterAttrs (_: pkg: builtins.any (x: x == system) pkg.meta.platforms) (
94 | import ./pkgs { inherit pkgs; }
95 | );
96 |
97 | checksForConfigs =
98 | configs: extract:
99 | lib.attrsets.filterAttrs (_: p: p.system == system) (lib.attrsets.mapAttrs (_: extract) configs);
100 |
101 | formatter = pkgs.nixfmt-tree;
102 | in
103 | {
104 | inherit formatter packages;
105 |
106 | checks = lib.lists.foldl lib.attrsets.unionOfDisjoint packages [
107 | (checksForConfigs self.homeConfigurations (hm: hm.activationPackage))
108 | (checksForConfigs self.nixosConfigurations (c: c.config.system.build.toplevel))
109 | ];
110 |
111 | devShells.default = pkgs.mkShell {
112 | nativeBuildInputs = [
113 | formatter
114 | ];
115 | };
116 | }
117 | );
118 | }
119 |
--------------------------------------------------------------------------------
/homeManagerModules/alacritty.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | pkgs,
5 | ...
6 | }:
7 | let
8 | cfg = config.dotfiles.alacritty;
9 | in
10 | {
11 | options.dotfiles.alacritty = {
12 | enable = lib.mkEnableOption "alacritty";
13 | package = lib.mkPackageOption pkgs "alacritty" { };
14 | };
15 |
16 | config = lib.mkIf cfg.enable {
17 | xdg.configFile."alacritty/alacritty.toml".source = ../config/alacritty/alacritty.toml;
18 |
19 | home.packages = [
20 | cfg.package
21 | ];
22 | };
23 | }
24 |
--------------------------------------------------------------------------------
/homeManagerModules/default.nix:
--------------------------------------------------------------------------------
1 | { ... }:
2 | {
3 | imports = [
4 | ./alacritty.nix
5 | ./direnv.nix
6 | ./fish.nix
7 | ./fonts.nix
8 | ./fzf.nix
9 | ./git.nix
10 | ./gtk.nix
11 | ./jj.nix
12 | ./nvim.nix
13 | ./rust.nix
14 | ./sway.nix
15 | ./taskwarrior.nix
16 | ];
17 |
18 | # This value determines the Home Manager release that your
19 | # configuration is compatible with. This helps avoid breakage
20 | # when a new Home Manager release introduces backwards
21 | # incompatible changes.
22 | #
23 | # You can update Home Manager without changing this value. See
24 | # the Home Manager release notes for a list of state version
25 | # changes in each release.
26 | home.stateVersion = "21.03";
27 |
28 | xdg.enable = true;
29 |
30 | programs.jq.enable = true;
31 | }
32 |
--------------------------------------------------------------------------------
/homeManagerModules/direnv.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | nixosConfig,
5 | ...
6 | }:
7 | let
8 | cfg = config.dotfiles.direnv;
9 | cfgNix = nixosConfig.dotfiles.nix;
10 | useLix = nixosConfig != null && cfgNix.useLix;
11 | in
12 | {
13 | options.dotfiles.direnv.enable = lib.mkOption {
14 | default = true;
15 | description = "Whether to enable direnv";
16 | type = lib.types.bool;
17 | };
18 |
19 | config = lib.mkMerge [
20 | (lib.mkIf cfg.enable {
21 | programs.direnv = {
22 | enable = true;
23 | nix-direnv.enable = true;
24 | };
25 | })
26 |
27 | (lib.mkIf (cfg.enable && useLix) {
28 | programs.direnv.nix-direnv.package = cfgNix.lixPackageSet.nix-direnv;
29 | })
30 | ];
31 | }
32 |
--------------------------------------------------------------------------------
/homeManagerModules/fish.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | pkgs,
5 | ...
6 | }:
7 | let
8 | cfg = config.dotfiles.fish;
9 | in
10 | {
11 | options.dotfiles.fish.enable = lib.mkOption {
12 | default = true;
13 | description = "Whether to enable fish";
14 | type = lib.types.bool;
15 | };
16 |
17 | config = lib.mkIf cfg.enable {
18 | home.packages = [
19 | pkgs.nix-output-monitor
20 | pkgs.nix-tree
21 | ];
22 |
23 | programs = {
24 | bat.enable = true;
25 | eza.enable = lib.mkDefault true;
26 | fish = {
27 | enable = true;
28 |
29 | interactiveShellInit = ''
30 | if test -n "$IN_NIX_SHELL"
31 | set --global nix_shell_info " "
32 | else
33 | set --global nix_shell_info ""
34 | end
35 |
36 | functions --copy fish_prompt fish_prompt_default
37 | function fish_prompt
38 | echo -n -s "$nix_shell_info"
39 | fish_prompt_default
40 | end
41 | '';
42 |
43 | functions = {
44 | fish_greeting = lib.mkDefault "";
45 | nom = {
46 | wraps = "nix";
47 | body = ''
48 | if test x_flake_check_x = "x_$argv[1]_$argv[2]_x"
49 | nix --log-format internal-json -v $argv 2>| command nom --json
50 | else
51 | command nom $argv
52 | end
53 | '';
54 | };
55 | };
56 |
57 | shellAliases = lib.optionalAttrs config.programs.eza.enable {
58 | ll = "eza -la";
59 | };
60 | };
61 | };
62 | };
63 | }
64 |
--------------------------------------------------------------------------------
/homeManagerModules/fonts.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | pkgs,
5 | ...
6 | }:
7 | let
8 | cfg = config.dotfiles.fonts;
9 | in
10 | {
11 | options.dotfiles.fonts.enable = lib.mkEnableOption "fonts";
12 |
13 | config = lib.mkIf cfg.enable {
14 | xdg.configFile."fontconfig/fonts.conf".source = ../config/fontconfig/fonts.conf;
15 |
16 | fonts.fontconfig.enable = true;
17 |
18 | home.packages = [
19 | pkgs.font-awesome
20 | pkgs.hack-font
21 | pkgs.noto-fonts-emoji
22 | ];
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/homeManagerModules/fzf.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | pkgs,
5 | ...
6 | }:
7 | let
8 | cfg = config.dotfiles.fzf;
9 | in
10 | {
11 | options.dotfiles.fzf.enable = lib.mkOption {
12 | default = true;
13 | description = "Whether to enable fzf";
14 | type = lib.types.bool;
15 | };
16 |
17 | config = lib.mkIf cfg.enable {
18 | programs.fzf = {
19 | enable = true;
20 | defaultCommand = "rg --files --hidden --glob !.git";
21 | fileWidgetCommand = "rg --files --hidden --glob !.git";
22 | changeDirWidgetCommand = "fd --type d";
23 | };
24 |
25 | home.packages = [
26 | # Ensure we include ripgrep and fd for the commands above
27 | pkgs.ripgrep
28 | pkgs.fd
29 | ];
30 | };
31 | }
32 |
--------------------------------------------------------------------------------
/homeManagerModules/git.nix:
--------------------------------------------------------------------------------
1 | { pkgs, ... }:
2 | let
3 | inherit (pkgs) git gitFull stdenv;
4 | inherit (stdenv) isDarwin;
5 | in
6 | {
7 | programs.git = {
8 | enable = true;
9 | package = if isDarwin then git else gitFull;
10 |
11 | ignores = [
12 | "*~"
13 | ".DS_Store"
14 | "*.swp"
15 | "Session.vim"
16 | ];
17 |
18 | aliases = {
19 | lg = "log --graph --pretty=format:'%Cred%h%Creset - %G? -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative";
20 | };
21 |
22 | extraConfig = {
23 | core.autoctrlf = "input";
24 | fetch.fsckobjects = "true";
25 | init.defaultBranch = "main";
26 | merge.conflictstyle = "zdiff3";
27 | pull.ff = "only";
28 | push.default = "matching";
29 | rebase.autosquash = "true";
30 | receive.fsckObjects = "true";
31 | rerere.enabled = "true";
32 | transfer.fsckobjects = "true";
33 | };
34 |
35 | difftastic = {
36 | enable = true;
37 | display = "side-by-side";
38 | };
39 | };
40 | }
41 |
--------------------------------------------------------------------------------
/homeManagerModules/gtk.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | pkgs,
5 | ...
6 | }:
7 | let
8 | cfg = config.dotfiles.gtk;
9 | in
10 | {
11 | options.dotfiles.gtk.enable = lib.mkEnableOption "gtk";
12 |
13 | config = lib.mkIf cfg.enable {
14 | xdg.configFile."gtk-3.0/settings.ini".source = ../config/gtk-3.0/settings.ini;
15 | xdg.configFile."gtk-3.0/import-gsettings.sh".source = ../config/gtk-3.0/import-gsettings.sh;
16 |
17 | home.packages = [
18 | pkgs.gtk3 # for managing settings
19 |
20 | pkgs.hicolor-icon-theme # base icons
21 | pkgs.adwaita-icon-theme # standard default theme
22 | pkgs.nordic # popular dark theme
23 | ];
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/homeManagerModules/jj.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | pkgs,
5 | ...
6 | }:
7 | let
8 | cfgGit = config.programs.git;
9 | cfgJJ = config.programs.jujutsu;
10 | in
11 | {
12 | config = lib.mkMerge [
13 | ({
14 | programs.jujutsu = {
15 | enable = true;
16 | settings = {
17 | git.private-commits = lib.mkDefault "description(glob:'wip:*') | description(glob:'private:*')";
18 |
19 | revset-aliases = lib.mkDefault {
20 | # The `trunk().. &` bit is an optimization to scan for non-`mine()` commits
21 | # only among commits that are not in `trunk()`
22 | "immutable_heads()" = "builtin_immutable_heads() | (trunk().. & ~mine())";
23 | };
24 |
25 | templates.draft_commit_description = lib.mkDefault ''
26 | concat(
27 | coalesce(description, "wip: "),
28 | surround(
29 | "\nJJ: This commit contains the following changes:\n", "",
30 | indent("JJ: ", diff.summary()),
31 | ),
32 | "\nJJ: ignore-rest\n",
33 | diff.git(),
34 | )
35 | '';
36 |
37 | ui = {
38 | pager = "less -FRX";
39 | show-cryptographic-signatures = true;
40 | };
41 |
42 | user = {
43 | name = cfgGit.userName;
44 | email = cfgGit.userEmail;
45 | };
46 | };
47 | };
48 | })
49 | (lib.mkIf (cfgJJ.enable && cfgJJ.settings.ui.show-cryptographic-signatures) {
50 | home.packages = [
51 | pkgs.gnupg
52 | ];
53 | })
54 | ];
55 | }
56 |
--------------------------------------------------------------------------------
/homeManagerModules/nvim.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | pkgs,
4 | lib,
5 | ...
6 | }:
7 | let
8 | inherit (lib) mkMerge;
9 | inherit (pkgs) vimPlugins;
10 | cfgFzf = config.programs.fzf;
11 | cfgRust = config.dotfiles.rust;
12 | cfgTypescript = config.dotfiles.typescript;
13 | in
14 | {
15 | options.dotfiles.typescript.enable = lib.mkEnableOption "typescript";
16 |
17 | config = mkMerge [
18 | ({
19 | home.sessionVariables = {
20 | EDITOR = "vim";
21 | };
22 |
23 | programs.neovim = {
24 | enable = true;
25 | vimAlias = true;
26 | vimdiffAlias = true;
27 |
28 | withPython3 = false;
29 | withRuby = false;
30 |
31 | package = pkgs.neovim-unwrapped;
32 |
33 | extraPackages = [
34 | pkgs.bash-language-server
35 | pkgs.nil
36 | pkgs.nixfmt-rfc-style
37 | pkgs.shellcheck
38 | pkgs.shfmt
39 | pkgs.tree-sitter
40 | ] ++ lib.optional cfgTypescript.enable pkgs.nodePackages.typescript-language-server;
41 |
42 | plugins =
43 | with vimPlugins;
44 | [
45 | # Git
46 | vim-gitgutter
47 | vim-fugitive
48 |
49 | # Color themes/syntax highlighting
50 | kanagawa-nvim
51 | rust-vim # Also makes things work like formatting and cargo integration
52 | # NB: let treesitter manage its own grammars, there's something about the
53 | # ones in nixpkgs makes it break from time to time. Internally it uses a
54 | # lockfile for the grammars so this is still fully reproducible
55 | nvim-treesitter
56 | hmts-nvim # better language highlighting inside home-manager configs
57 |
58 | # LSP plugins
59 | nvim-lspconfig # Collection of common configurations for the Nvim LSP client
60 | rustaceanvim # To enable more of the features of rust-analyzer, such as inlay hints and more!
61 | nvim-cmp # Completion framework
62 | cmp-buffer # completion source for buffer words
63 | cmp-nvim-lsp # completion source for builtin lsp
64 | cmp-path # completion source for paths
65 | cmp-vsnip # completion source for snippets
66 | vim-vsnip # snippet engine (required...)
67 |
68 | # Diagnostics
69 | dressing-nvim
70 | trouble-nvim
71 | fidget-nvim
72 |
73 | # Misc
74 | neoconf-nvim
75 | vim-easy-align
76 | ]
77 | ++ lib.optional cfgFzf.enable vimPlugins.fzf-vim;
78 |
79 | extraConfig =
80 | let
81 | file = builtins.readFile ../config/nvim/init.vim;
82 | in
83 | if cfgRust.enable then
84 | builtins.replaceStrings [ "@rustAnalyzer@" ] [ "${pkgs.rust-analyzer}" ] (
85 | builtins.readFile ../config/nvim/init.vim
86 | )
87 | else
88 | file;
89 | };
90 | })
91 | ];
92 | }
93 |
--------------------------------------------------------------------------------
/homeManagerModules/rust.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | pkgs,
4 | lib,
5 | ...
6 | }:
7 | let
8 | cfg = config.dotfiles.rust;
9 | in
10 | {
11 | options.dotfiles.rust.enable = lib.mkEnableOption "rust";
12 |
13 | config = lib.mkIf cfg.enable {
14 | home.sessionVariables = {
15 | RUSTUP_HOME = "${config.xdg.dataHome}/rustup";
16 | CARGO_HOME = "${config.xdg.dataHome}/cargo";
17 | };
18 |
19 | home.packages =
20 | [
21 | pkgs.cargo-outdated
22 | pkgs.rustup
23 | ]
24 | ++ lib.lists.optionals pkgs.stdenv.isLinux [
25 | # binutils now conflicts with clang as well, turning this off for now...
26 | # binutils # For some reason conflicts on darwin
27 | pkgs.clang # Provides `cc` for any *-sys crates
28 | ];
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/homeManagerModules/sway.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | pkgs,
5 | ...
6 | }:
7 | let
8 | cfg = config.dotfiles.sway;
9 | fishcfg = config.programs.fish;
10 | in
11 | {
12 | options.dotfiles.sway.enable = lib.mkEnableOption "sway";
13 |
14 | config = lib.mkIf cfg.enable {
15 | dotfiles = {
16 | # Alacritty is the default terminal in the config,
17 | # so ensure our config is pulled in
18 | alacritty.enable = true;
19 | # Pull in GTK themes for wofi and just about everything else.
20 | gtk.enable = true;
21 | };
22 |
23 | xdg.configFile."mako/config".source = ../config/mako/config;
24 | xdg.configFile."sway/config".source = ../config/sway/config;
25 | xdg.configFile."waybar/config".source = ../config/waybar/config;
26 |
27 | nixpkgs.config.allowUnfreePredicate =
28 | pkg:
29 | builtins.elem (lib.getName pkg) [
30 | "discord"
31 | ];
32 |
33 | home.packages = [
34 | pkgs.blueberry # bluetooth configuration
35 | pkgs.discord
36 | pkgs.firefox-wayland
37 | pkgs.mako # notification daemon
38 | (pkgs.callPackage ../pkgs/swaynagmode.nix { })
39 | pkgs.waybar # status bar
40 | pkgs.wofi # launcher
41 | pkgs.xdg-utils # for xdg-open, make links clickable from outside firefox
42 | ];
43 |
44 | home.sessionVariables = {
45 | MOZ_ENABLE_WAYLAND = 1;
46 | XDG_CURRENT_DESKTOP = "sway";
47 | };
48 |
49 | # Allow starting up sway (which should exec a systemd call that
50 | # sway-session.target has started) to then kick off other systemd
51 | # units (e.g. redshift, etc.)
52 | systemd.user.targets.sway-session = {
53 | Unit = {
54 | Description = "sway compositor session";
55 | Documentation = [ "man:systemd.special(7)" ];
56 | BindsTo = [ "graphical-session.target" ];
57 | Wants = [ "graphical-session-pre.target" ];
58 | After = [ "graphical-session-pre.target" ];
59 | };
60 | };
61 |
62 | programs.fish.loginShellInit = lib.mkIf fishcfg.enable ''
63 | if test -z "$DISPLAY"; and test (tty) = "/dev/tty1"
64 | # Use systemd-cat here to capture sway logs
65 | exec systemd-cat --identifier=sway sway
66 | end
67 | '';
68 | };
69 | }
70 |
--------------------------------------------------------------------------------
/homeManagerModules/taskwarrior.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | pkgs,
5 | ...
6 | }:
7 | let
8 | cfg = config.dotfiles.taskwarrior;
9 | in
10 | {
11 | options.dotfiles.taskwarrior.enable = lib.mkEnableOption "taskwarrior";
12 |
13 | config = lib.mkIf cfg.enable {
14 | home.packages = [
15 | pkgs.tasksh
16 | # NB: do not use the home-manager version of task warrior
17 | # since it insists on placing the .taskrc file in $HOME
18 | pkgs.taskwarrior3
19 | ];
20 |
21 | programs.fish.functions.fish_greeting = "task";
22 | };
23 | }
24 |
--------------------------------------------------------------------------------
/lib/default.nix:
--------------------------------------------------------------------------------
1 | # Helper methods and other extensions
2 | {
3 | lib,
4 | inputs,
5 | myPkgs,
6 | }:
7 |
8 | let
9 | mkNixosSystem = import ./mkNixosSystem.nix {
10 | inherit lib myPkgs;
11 | };
12 |
13 | mkAppendConfig = import ./mkAppendConfig.nix {
14 | inherit mkNixosSystem;
15 | };
16 | in
17 | {
18 | mkHost = args: mkAppendConfig ({ inherit inputs; } // args);
19 | }
20 |
--------------------------------------------------------------------------------
/lib/mkAppendConfig.nix:
--------------------------------------------------------------------------------
1 | { mkNixosSystem }:
2 |
3 | # Make a nixosSystem and add a `appendConfig` attribute, which, when
4 | # invoked with the same parameters that `mkNixosSystem` accepts,
5 | # will produce a new nixosSystem with the new parameters merged in.
6 | #
7 | # Specifically:
8 | # - specifying a `system` parameter will replace the old definition.
9 | # Omitting it will retain the old definition.
10 | # - specifying an `inputs` attrSet will merge it with the old definition,
11 | # overwriting any collisions (be careful with overwriting inputs this way).
12 | # - specifying a `rootConfig` module will include it within the system
13 | # definition (just like manually specifying an `import`).
14 | let
15 | mkAppendConfig =
16 | origArgs:
17 | let
18 | origRes = mkNixosSystem origArgs;
19 |
20 | mergeArgs =
21 | newArgs@{
22 | inputs ? { },
23 | rootConfig,
24 | ...
25 | }:
26 | origArgs
27 | // newArgs
28 | // {
29 | # Ensure we don't "lose" any previous inputs
30 | inputs = origArgs.inputs // inputs;
31 |
32 | # Ensure we keep the old rootConfig around
33 | rootConfig = {
34 | imports = [
35 | origArgs.rootConfig
36 | rootConfig
37 | ];
38 | };
39 | };
40 | in
41 | origRes // { appendConfig = newArgs: mkAppendConfig (mergeArgs newArgs); };
42 | in
43 | mkAppendConfig
44 |
--------------------------------------------------------------------------------
/lib/mkNixosSystem.nix:
--------------------------------------------------------------------------------
1 | { lib, myPkgs }:
2 |
3 | # Make a new nixosSystem configuration with the provided arguments.
4 | {
5 | system,
6 | inputs,
7 | rootConfig,
8 | # The specific version of nixpkgs we should use for instantiating the system,
9 | # allowing downstream consumers to change it if necessary.
10 | nixpkgs ? inputs.nixpkgs,
11 | includeHomeManager ? false,
12 | }:
13 |
14 | let
15 | # Allows our flake inputs to appear as an argument in all of our modules.
16 | specialArgs = {
17 | inherit inputs;
18 | myPkgs = myPkgs."${system}";
19 | };
20 |
21 | homeManagerModule = {
22 | imports = [ inputs.home-manager.nixosModules.home-manager ];
23 |
24 | options = {
25 | # Submodules have merge semantics, making it possible to amend
26 | # the `home-manager.users` submodule for additional functionality.
27 | home-manager.users = lib.mkOption {
28 | type = lib.types.attrsOf (
29 | lib.types.submoduleWith {
30 | # make our flake inputs accessible from home-manager modules as well
31 | inherit specialArgs;
32 | modules = [
33 | ../homeManagerModules/default.nix
34 | ];
35 | }
36 | );
37 | };
38 | };
39 |
40 | config = {
41 | # Put home-manager results in /etc/profiles instead of ~/.nix-profile
42 | # keeps a clean $HOME (plus it works with nixos-build-vms)
43 | home-manager.useUserPackages = true;
44 | };
45 | };
46 | in
47 | nixpkgs.lib.nixosSystem {
48 | inherit system specialArgs;
49 |
50 | modules = [
51 | ../nixosModules/default.nix
52 | rootConfig
53 | ] ++ lib.lists.optional includeHomeManager homeManagerModule;
54 | }
55 |
--------------------------------------------------------------------------------
/nixosConfigurations/asphodel/default.nix:
--------------------------------------------------------------------------------
1 | { pkgs, inputs, ... }:
2 |
3 | {
4 | imports = [
5 | ./hardware-configuration.nix
6 | ./persist.nix
7 | inputs.nixos-pibox.nixosModules.default
8 | ];
9 |
10 | nixpkgs = {
11 | overlays = [
12 | inputs.nixos-pibox.overlays.default
13 | (final: prev: {
14 | # https://github.com/NixOS/nixpkgs/pull/239658#issuecomment-1622748163
15 | compressFirmwareXz = firmware: prev.compressFirmwareXz (builtins.removeAttrs firmware [ "meta" ]);
16 | })
17 | ];
18 | };
19 |
20 | networking = {
21 | hostName = "asphodel";
22 | hostId = "feeddead";
23 |
24 | networkmanager.enable = true;
25 |
26 | useDHCP = false;
27 | interfaces = {
28 | eth0.useDHCP = true;
29 | wlan0.useDHCP = true;
30 | };
31 | };
32 |
33 | time.timeZone = "America/Los_Angeles";
34 | i18n.defaultLocale = "en_US.UTF-8";
35 |
36 | environment.systemPackages = with pkgs; [
37 | bash
38 | dnsutils
39 | fish
40 | git
41 | htop
42 | vim
43 | ];
44 |
45 | dotfiles = {
46 | nix.distributedBuilds.enable = true;
47 | zfs-send = {
48 | enable = true;
49 | rootPool = "phlegethon";
50 | };
51 | };
52 |
53 | services = {
54 | nginx = {
55 | enable = true;
56 | recommendedGzipSettings = true;
57 | recommendedOptimisation = true;
58 | recommendedProxySettings = true;
59 | recommendedTlsSettings = true;
60 | };
61 |
62 | openssh.enable = true;
63 |
64 | piboxPwmFan.enable = true;
65 | # Broken on Linux 6.1 the sitronix driver doesn't seem to work
66 | #piboxFramebuffer.enable = true;
67 |
68 | tailscale.enable = true;
69 | zfs = {
70 | autoScrub = {
71 | enable = true;
72 | interval = "monthly";
73 | };
74 | autoSnapshot.enable = true;
75 | trim.enable = true;
76 | };
77 | };
78 |
79 | users.mutableUsers = false;
80 | users.users.ivan = {
81 | uid = 1000;
82 | isNormalUser = true;
83 | home = "/home/ivan";
84 | extraGroups = [
85 | "wheel" # Enable sudo
86 | "disk"
87 | "systemd-journal"
88 | ];
89 |
90 | openssh.authorizedKeys.keys = [
91 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKRVRlSZLcDEdJ13GjfJigN/KT3/Q1odIS4pf+hbmz+Z"
92 | ];
93 | };
94 |
95 | # This value determines the NixOS release from which the default
96 | # settings for stateful data, like file locations and database versions
97 | # on your system were taken. It‘s perfectly fine and recommended to leave
98 | # this value at the release version of the first install of this system.
99 | # Before changing this value read the documentation for this option
100 | # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
101 | system.stateVersion = "20.09"; # Did you read the comment?
102 |
103 | programs.command-not-found.enable = false;
104 |
105 | security.sudo.execWheelOnly = true;
106 | security.sudo.wheelNeedsPassword = false;
107 | }
108 |
--------------------------------------------------------------------------------
/nixosConfigurations/asphodel/hardware-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | inputs,
5 | ...
6 | }:
7 |
8 | {
9 | imports = [
10 | inputs.nixos-hardware.nixosModules.raspberry-pi-4
11 | ];
12 |
13 | boot = {
14 | extraModulePackages = [ ];
15 |
16 | # !!! cryptkey must be done first, and the list seems to be
17 | # alphabetically sorted, so take care that cryptroot / cryptswap,
18 | # whatever you name them, come after cryptkey.
19 | initrd = {
20 | luks.devices = {
21 | cryptkey = {
22 | device = "/dev/disk/by-uuid/65c4794c-dbbd-47d4-9611-716c68fab36a";
23 | };
24 |
25 | cryptroot = {
26 | allowDiscards = true;
27 | device = "/dev/disk/by-uuid/396901c7-225a-4c50-b723-b7e6f2a7f772";
28 | keyFile = "/dev/mapper/cryptkey";
29 | keyFileSize = 8192;
30 | };
31 |
32 | cryptroot2 = {
33 | allowDiscards = true;
34 | device = "/dev/disk/by-uuid/00eafab2-09b4-4fab-acf6-3a10b9aa6cb6";
35 | keyFile = "/dev/mapper/cryptkey";
36 | keyFileSize = 8192;
37 | };
38 |
39 | cryptswap = {
40 | allowDiscards = true;
41 | device = "/dev/disk/by-uuid/b6e946d7-27b9-48f6-99ff-6ab9d355b644";
42 | keyFile = "/dev/mapper/cryptkey";
43 | keyFileSize = 8192;
44 | };
45 | };
46 |
47 | postDeviceCommands = lib.mkAfter ''
48 | cryptsetup close cryptkey
49 | zfs rollback -r phlegethon/local/root@blank && echo blanked out root
50 | '';
51 |
52 | # Support remote unlock. Run `cryptsetup-askpass` to unlock
53 | network = {
54 | enable = true;
55 | ssh = {
56 | enable = true;
57 | authorizedKeys = config.users.users.ivan.openssh.authorizedKeys.keys;
58 | hostKeys = [
59 | # Note this file lives on the host itself, and isn't passed in by the deployer
60 | "/persist/etc/ssh/initrd_ssh_host_ed25519_key"
61 | ];
62 | };
63 | };
64 | };
65 |
66 | loader = {
67 | efi.canTouchEfiVariables = true;
68 | generic-extlinux-compatible.enable = false;
69 | systemd-boot.enable = true;
70 | timeout = 3; # seconds
71 | };
72 |
73 | kernelParams = [
74 | "8250.nr_uarts=1"
75 | "console=ttyAMA0,115200"
76 | "console=tty1"
77 | ];
78 |
79 | supportedFilesystems = [ "zfs" ];
80 | };
81 |
82 | fileSystems = {
83 | "/" = {
84 | device = "phlegethon/local/root";
85 | fsType = "zfs";
86 | options = [ "zfsutil" ];
87 | };
88 |
89 | "/boot" = {
90 | device = "/dev/disk/by-uuid/0F92-BECC";
91 | fsType = "vfat";
92 | options = [
93 | "noatime"
94 | "umask=0077"
95 | ];
96 | };
97 |
98 | "/empty/phlegethon-persist" = {
99 | device = "phlegethon/persist";
100 | fsType = "zfs";
101 | };
102 | "/empty/phlegethon-persist-user" = {
103 | device = "phlegethon/persist/user";
104 | fsType = "zfs";
105 | };
106 |
107 | "/home/ivan" = {
108 | device = "phlegethon/persist/user/ivan";
109 | fsType = "zfs";
110 | };
111 |
112 | "/nix" = {
113 | device = "phlegethon/local/nix";
114 | fsType = "zfs";
115 | };
116 |
117 | "/persist" = {
118 | device = "phlegethon/persist/system";
119 | fsType = "zfs";
120 | neededForBoot = true;
121 | };
122 |
123 | "/var/lib" = {
124 | device = "phlegethon/persist/lib";
125 | fsType = "zfs";
126 | };
127 |
128 | "/var/log/journal" = {
129 | device = "phlegethon/local/journal";
130 | fsType = "zfs";
131 | neededForBoot = true;
132 | };
133 | };
134 |
135 | swapDevices = [
136 | {
137 | device = "/dev/mapper/cryptswap";
138 | }
139 | ];
140 |
141 | powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
142 | }
143 |
--------------------------------------------------------------------------------
/nixosConfigurations/asphodel/persist.nix:
--------------------------------------------------------------------------------
1 | { ... }:
2 | {
3 | environment.etc = {
4 | "machine-id".source = "/persist/etc/machine-id";
5 | "NetworkManager/system-connections".source = "/persist/etc/NetworkManager/system-connections/";
6 | };
7 |
8 | users.users.root.hashedPasswordFile = "/persist/root/passwordfile";
9 |
10 | services.openssh.hostKeys = [
11 | {
12 | path = "/persist/etc/ssh/ssh_host_ed25519_key";
13 | type = "ed25519";
14 | }
15 | ];
16 | }
17 |
--------------------------------------------------------------------------------
/nixosConfigurations/elysium/default.nix:
--------------------------------------------------------------------------------
1 | { pkgs, lib, ... }:
2 |
3 | {
4 | imports = [
5 | ./hardware-configuration.nix
6 | ./persist.nix
7 | ];
8 |
9 | networking = {
10 | hostName = "elysium";
11 | hostId = "deadcafe";
12 |
13 | networkmanager.enable = true;
14 |
15 | useDHCP = false;
16 | interfaces = {
17 | eno1.useDHCP = true;
18 | # FIXME: enable once kernel is 6.4 or higher
19 | #wlp8s0.useDHCP = true;
20 | };
21 | };
22 |
23 | time.timeZone = "America/Los_Angeles";
24 | i18n.defaultLocale = "en_US.UTF-8";
25 |
26 | environment.systemPackages = with pkgs; [
27 | bash
28 | dnsutils
29 | fish
30 | git
31 | htop
32 | rsync
33 | vim
34 |
35 | # For syncoid
36 | procps
37 | pv
38 | mbuffer
39 | lzop
40 | ];
41 |
42 | services = {
43 | openssh.enable = true;
44 |
45 | syncoid = {
46 | enable = true;
47 |
48 | # Ten mins after the top of the hour, give snapshots a chance to settle before we pull
49 | interval = "*:10:00";
50 | commonArgs = [
51 | "--create-bookmark"
52 | "--no-clone-handling"
53 | "--no-sync-snap"
54 | "--use-hold"
55 | "--skip-parent"
56 | "--preserve-recordsize"
57 | ];
58 | commands = {
59 | # Local
60 | "acheron/persist" = {
61 | recursive = true;
62 | target = "lethe/backups/acheron";
63 | };
64 |
65 | # Remote
66 | "syncoid@asphodel:phlegethon/persist" = {
67 | recursive = true;
68 | target = "lethe/backups/phlegethon";
69 | };
70 | };
71 |
72 | localSourceAllow = [
73 | "bookmark"
74 | "hold"
75 | "send"
76 | "release"
77 | ];
78 |
79 | localTargetAllow = [
80 | "bookmark"
81 | "compression"
82 | "create"
83 | "destroy" # For aborting partial/interrupted receives
84 | "hold"
85 | "mount"
86 | "mountpoint"
87 | "receive"
88 | "release"
89 | "rollback"
90 | ];
91 |
92 | # https://github.com/NixOS/nixpkgs/issues/264071
93 | service.serviceConfig.PrivateUsers = lib.mkForce false;
94 | };
95 |
96 | tailscale.enable = true;
97 | zfs = {
98 | autoScrub = {
99 | enable = true;
100 | interval = "Mon *-*-* 03:00:00";
101 | };
102 | autoSnapshot.enable = true;
103 | trim.enable = true;
104 | };
105 | };
106 |
107 | users.mutableUsers = false;
108 | users.groups.syncoid-tartarus = { };
109 | users.users = {
110 | ivan = {
111 | # Unfortunate that this one ended up being different but
112 | # probably not worth the hassle to fix now
113 | uid = lib.mkForce 1002;
114 | isNormalUser = true;
115 | home = "/home/ivan";
116 | extraGroups = [
117 | "wheel" # Enable sudo
118 | "disk"
119 | "systemd-journal"
120 | ];
121 |
122 | openssh.authorizedKeys.keys = [
123 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKRVRlSZLcDEdJ13GjfJigN/KT3/Q1odIS4pf+hbmz+Z"
124 | ];
125 | };
126 |
127 | syncoid-tartarus = {
128 | group = "syncoid-tartarus";
129 | isSystemUser = true;
130 | useDefaultShell = true; # Do permit login
131 | };
132 | };
133 |
134 | # This value determines the NixOS release from which the default
135 | # settings for stateful data, like file locations and database versions
136 | # on your system were taken. It‘s perfectly fine and recommended to leave
137 | # this value at the release version of the first install of this system.
138 | # Before changing this value read the documentation for this option
139 | # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
140 | system.stateVersion = "20.09"; # Did you read the comment?
141 |
142 | programs.command-not-found.enable = false;
143 |
144 | security.sudo.execWheelOnly = true;
145 | security.sudo.wheelNeedsPassword = false;
146 | }
147 |
--------------------------------------------------------------------------------
/nixosConfigurations/elysium/hardware-configuration.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | modulesPath,
5 | ...
6 | }:
7 |
8 | {
9 | imports = [
10 | (modulesPath + "/installer/scan/not-detected.nix")
11 | ];
12 |
13 | boot = {
14 | binfmt.emulatedSystems = [ "aarch64-linux" ];
15 |
16 | extraModulePackages = [ ];
17 |
18 | # !!! cryptkey must be done first, and the list seems to be
19 | # alphabetically sorted, so take care that cryptroot / cryptswap,
20 | # whatever you name them, come after cryptkey.
21 | initrd = {
22 | availableKernelModules = [
23 | "ahci"
24 | "amdgpu"
25 | "ccp"
26 | "cryptd"
27 | "nvme"
28 | "r8169"
29 | # FIXME: enable once kernel is 6.4 or higher
30 | #"rtw89_8852be"
31 | "sd_mod"
32 | "usbhid"
33 | "usb_storage"
34 | "xhci_pci"
35 | ];
36 |
37 | luks.devices = {
38 | cryptkey = {
39 | device = "/dev/disk/by-uuid/2896616e-f1d0-48ad-a980-681db105ad1c";
40 | };
41 |
42 | cryptroot = {
43 | allowDiscards = true;
44 | device = "/dev/disk/by-uuid/27cbbb09-665b-4a12-bf9e-5f43064839d5";
45 | keyFile = "/dev/mapper/cryptkey";
46 | keyFileSize = 8192;
47 | };
48 |
49 | cryptswap = {
50 | allowDiscards = true;
51 | device = "/dev/disk/by-uuid/1eb719ba-5599-4a8c-bc23-c1b7bf43d46b";
52 | keyFile = "/dev/mapper/cryptkey";
53 | keyFileSize = 8192;
54 | };
55 | };
56 |
57 | postDeviceCommands = lib.mkAfter ''
58 | cryptsetup close cryptkey
59 | zfs rollback -r acheron/local/root@blank && echo blanked out root
60 | '';
61 |
62 | # Support remote unlock. Run `cryptsetup-askpass` to unlock
63 | network = {
64 | enable = true;
65 | ssh = {
66 | enable = true;
67 | authorizedKeys = config.users.users.ivan.openssh.authorizedKeys.keys;
68 | hostKeys = [
69 | # Note this file lives on the host itself, and isn't passed in by the deployer
70 | "/persist/etc/ssh/initrd_ssh_host_ed25519_key"
71 | ];
72 | };
73 | };
74 | };
75 |
76 | kernelModules = [ "kvm-amd" ];
77 |
78 | loader.systemd-boot.enable = true;
79 |
80 | supportedFilesystems = [ "zfs" ];
81 | zfs.extraPools = [ "lethe" ];
82 | };
83 |
84 | fileSystems = {
85 | "/" = {
86 | device = "acheron/local/root";
87 | fsType = "zfs";
88 | };
89 |
90 | "/boot" = {
91 | device = "/dev/disk/by-uuid/87D1-ADFE";
92 | fsType = "vfat";
93 | options = [
94 | "noatime"
95 | "umask=0077"
96 | ];
97 | };
98 |
99 | "/empty/acheron-persist-user" = {
100 | device = "acheron/persist/user";
101 | fsType = "zfs";
102 | };
103 |
104 | "/home/ivan" = {
105 | device = "acheron/persist/user/ivan";
106 | fsType = "zfs";
107 | };
108 |
109 | "/nix" = {
110 | device = "acheron/local/nix";
111 | fsType = "zfs";
112 | };
113 |
114 | "/persist" = {
115 | device = "acheron/persist/system";
116 | fsType = "zfs";
117 | neededForBoot = true;
118 | };
119 |
120 | "/var/lib" = {
121 | device = "acheron/persist/lib";
122 | fsType = "zfs";
123 | };
124 |
125 | "/var/log/journal" = {
126 | device = "acheron/local/journal";
127 | fsType = "zfs";
128 | neededForBoot = true;
129 | };
130 | };
131 |
132 | hardware = {
133 | enableRedistributableFirmware = true;
134 | cpu.amd.updateMicrocode = true;
135 | };
136 |
137 | powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
138 |
139 | swapDevices = [
140 | ];
141 | }
142 |
--------------------------------------------------------------------------------
/nixosConfigurations/elysium/persist.nix:
--------------------------------------------------------------------------------
1 | { ... }:
2 | {
3 | environment.etc = {
4 | "machine-id".source = "/persist/etc/machine-id";
5 | "NetworkManager/system-connections".source = "/persist/etc/NetworkManager/system-connections/";
6 | };
7 |
8 | users.users.root.hashedPasswordFile = "/persist/root/passwordfile";
9 |
10 | services.openssh.hostKeys = [
11 | {
12 | path = "/persist/etc/ssh/ssh_host_ed25519_key";
13 | type = "ed25519";
14 | }
15 | ];
16 | }
17 |
--------------------------------------------------------------------------------
/nixosConfigurations/rpi/default.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | pkgs,
4 | lib,
5 | ...
6 | }:
7 |
8 | {
9 | imports = [
10 | ];
11 |
12 | # NixOS wants to enable GRUB by default
13 | boot.loader.grub.enable = false;
14 | boot.loader.generic-extlinux-compatible.enable = true;
15 |
16 | boot.kernelPackages = pkgs.linuxPackages; # Use LTS kernel
17 | boot.kernelParams = [
18 | # Needed for the virtual console to work on the RPi 3, as the default of 16M
19 | # doesn't seem to be enough. If X.org behaves weirdly (I only saw the cursor)
20 | # then try increasing this to 256M.
21 | "cma=32M"
22 |
23 | # Enable serial console
24 | "console=ttyS1,115200n8"
25 | ];
26 |
27 | boot.enableContainers = false;
28 | boot.tmp.cleanOnBoot = true;
29 |
30 | # Trim fat
31 | boot.bcache.enable = false;
32 | boot.swraid.enable = false;
33 |
34 | hardware.enableRedistributableFirmware = true;
35 |
36 | fileSystems = {
37 | "/" = {
38 | device = "/dev/disk/by-label/NIXOS_SD";
39 | fsType = "ext4";
40 | };
41 | };
42 |
43 | swapDevices = [
44 | {
45 | device = "/swapfile";
46 | size = 1024; # MB
47 | }
48 | ];
49 |
50 | # Allows "wheel" users to not need to type a password to get sudo.
51 | # Useful for doing remote deployments without having an ssh key for root.
52 | security.sudo.wheelNeedsPassword = false;
53 | security.polkit.enable = false; # Unused, trim some fat
54 |
55 | # Copying nixpkgs-source causes a big I/O penalty on SD card writes, so skip it
56 | dotfiles = {
57 | nix = {
58 | distributedBuilds.enable = true;
59 | enableSetNixPathAndFlakeRegistry = false;
60 | };
61 | services.pihole.enable = true;
62 | };
63 |
64 | nix = {
65 | optimise.automatic = true;
66 | };
67 |
68 | nixpkgs.overlays = [
69 | (self: super: {
70 | # Needed to get wifi drivers working, may need to revisit/remove once drivers are upstreamed?
71 | firmwareLinuxNonfree = super.firmwareLinuxNonfree.overrideAttrs (old: {
72 | version = "2020-12-18";
73 | src = pkgs.fetchgit {
74 | url = "https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git";
75 | rev = "b79d2396bc630bfd9b4058459d3e82d7c3428599";
76 | sha256 = "1rb5b3fzxk5bi6kfqp76q1qszivi0v1kdz1cwj2llp5sd9ns03b5";
77 | };
78 | outputHash = "1p7vn2hfwca6w69jhw5zq70w44ji8mdnibm1z959aalax6ndy146";
79 | });
80 | })
81 | ];
82 |
83 | networking = {
84 | hostName = "rpi";
85 | hostId = "cafeb0ba";
86 | wireless.enable = true; # Enables wireless support via wpa_supplicant.
87 |
88 | # The global useDHCP flag is deprecated, therefore explicitly set to false here.
89 | # Per-interface useDHCP will be mandatory in the future, so this generated config
90 | # replicates the default behaviour.
91 | useDHCP = false;
92 | interfaces.wlan0.useDHCP = true;
93 | };
94 |
95 | environment.stub-ld.enable = lib.mkForce false; # trim fat
96 | environment.systemPackages = with pkgs; [
97 | dnsutils
98 | git
99 | htop
100 | vim
101 | ];
102 |
103 | # This value determines the NixOS release from which the default
104 | # settings for stateful data, like file locations and database versions
105 | # on your system were taken. It‘s perfectly fine and recommended to leave
106 | # this value at the release version of the first install of this system.
107 | # Before changing this value read the documentation for this option
108 | # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
109 | system.stateVersion = "20.09"; # Did you read the comment?
110 |
111 | users.users.ivan = {
112 | uid = 1000;
113 | isNormalUser = true;
114 | home = "/home/ivan";
115 | extraGroups = [
116 | "wheel" # Enable sudo
117 | "disk"
118 | "systemd-journal"
119 | ];
120 |
121 | openssh.authorizedKeys.keys = [
122 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKRVRlSZLcDEdJ13GjfJigN/KT3/Q1odIS4pf+hbmz+Z"
123 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPKGnvJIfS1FFpLzfa+OlKU/YEC1p29HPzSKCNTsIUMZ"
124 | ];
125 | };
126 |
127 | # Stop additional documentation, etc. from being generated
128 | documentation.enable = false; # Man cache takes forever to build
129 | documentation.man.enable = false; # Man cache takes forever to build
130 | documentation.doc.enable = false;
131 | documentation.info.enable = false;
132 | documentation.nixos.enable = false;
133 |
134 | # Locales
135 | time.timeZone = "America/Los_Angeles";
136 | i18n.supportedLocales = [ (config.i18n.defaultLocale + "/UTF-8") ];
137 | i18n.defaultLocale = "en_US.UTF-8";
138 |
139 | programs = {
140 | command-not-found.enable = false;
141 | nano.enable = false;
142 | };
143 |
144 | virtualisation.docker.autoPrune = {
145 | enable = true;
146 | dates = "monthly";
147 | };
148 | systemd.timers.docker-prune.timerConfig.Persistent = true;
149 |
150 | services = {
151 | logrotate.enable = false; # trim fat
152 | openssh.enable = true;
153 | tailscale.enable = true;
154 | udisks2.enable = false; # Unused, trim some fat
155 |
156 | # Limit the journal size to X MB or last Y days of logs
157 | journald.extraConfig = ''
158 | SystemMaxUse=1536M
159 | MaxFileSec=60day
160 | '';
161 | };
162 |
163 | # Trim more fat
164 | appstream.enable = false;
165 | fonts.fontconfig.enable = false;
166 | powerManagement.enable = false;
167 | xdg = {
168 | autostart.enable = false;
169 | icons.enable = false;
170 | menus.enable = false;
171 | mime.enable = false;
172 | sounds.enable = false;
173 | };
174 | }
175 |
--------------------------------------------------------------------------------
/nixosConfigurations/tartarus/default.nix:
--------------------------------------------------------------------------------
1 | { pkgs, lib, ... }:
2 |
3 | {
4 | imports = [
5 | ./hardware-configuration.nix
6 | ../../users/ivan/default.nix
7 | ];
8 |
9 | boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
10 |
11 | # Use the systemd-boot EFI boot loader.
12 | boot.loader.systemd-boot.enable = true;
13 | boot.loader.systemd-boot.memtest86.enable = true;
14 | boot.loader.efi.canTouchEfiVariables = false;
15 | # https://nixos.wiki/wiki/NixOS_on_ZFS
16 | boot.loader.grub.copyKernels = true;
17 | boot.supportedFilesystems = [ "zfs" ];
18 | boot.kernelParams = [ "elevator=none" ]; # Because ZFS doesn't have the whole disk
19 |
20 | #boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
21 |
22 | networking = {
23 | hostName = "tartarus"; # Define your hostname.
24 | hostId = "feedbeef";
25 | };
26 |
27 | nix.extraOptions = ''
28 | secret-key-files = /persist/tartarus-nix-store-signing-secret-key
29 | '';
30 |
31 | # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
32 | networking.networkmanager.enable = true;
33 |
34 | # Set your time zone.
35 | time.timeZone = "America/Los_Angeles";
36 |
37 | # The global useDHCP flag is deprecated, therefore explicitly set to false here.
38 | # Per-interface useDHCP will be mandatory in the future, so this generated config
39 | # replicates the default behaviour.
40 | networking.useDHCP = false;
41 | networking.interfaces.enp6s0.useDHCP = true;
42 | networking.interfaces.wlp5s0.useDHCP = true;
43 |
44 | # Configure network proxy if necessary
45 | # networking.proxy.default = "http://user:password@proxy:port/";
46 | # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
47 |
48 | # Select internationalisation properties.
49 | i18n.defaultLocale = "en_US.UTF-8";
50 | # console = {
51 | # font = "Lat2-Terminus16";
52 | # keyMap = "us";
53 | # };
54 |
55 | programs.dconf.enable = true;
56 | # virtualisation.libvirtd = {
57 | # enable = true;
58 | # onShutdown = "shutdown";
59 | # };
60 |
61 | location = {
62 | provider = "manual";
63 | latitude = 47.6;
64 | longitude = -122.3;
65 | };
66 |
67 | services.redshift = {
68 | enable = true;
69 | brightness = {
70 | # Note the string values below.
71 | day = "1";
72 | night = "1";
73 | };
74 | temperature = {
75 | day = 4200;
76 | night = 2900;
77 | };
78 |
79 | package = pkgs.gammastep;
80 | executable = "/bin/gammastep";
81 | };
82 |
83 | # The sway module will unconditionally import wayland-session.nix
84 | # which defaults to enabling xdg.portal. That in turn adds xdg-desktop-portal-wlr
85 | # which fails to startup (with pipewire and xdpw failures). Eventually things timeout
86 | # and the system moves on, but it messes with the rest of my configs starting (like waybar)
87 | # in a timely manner. Rather than pull a bunch of extra things I don't use, I'd rather turn
88 | # desktop portals off and revisit it if I ever need them in the future.
89 | xdg.portal.enable = lib.mkForce false;
90 |
91 | # Desktop/window management
92 | programs.sway = {
93 | enable = true;
94 |
95 | wrapperFeatures = {
96 | base = true; # Setup dbus stuff...
97 | gtk = true; # Allow GTK apps to run
98 | };
99 |
100 | # Tweak the extra packages and make sure they're available
101 | # *just* in case something goes wrong with a user sway config
102 | extraPackages = with pkgs; [
103 | alacritty
104 | #calibre # Build seems broken atm
105 | dmenu
106 | swayidle
107 | swaylock
108 | wl-clipboard
109 | xwayland
110 | ];
111 | };
112 |
113 | # Enable sound.
114 | services.pipewire.enable = false; # pipewire is enabled by default for new installs, keep the old behavior here
115 | hardware.bluetooth.enable = true;
116 | services.pulseaudio = {
117 | enable = true;
118 | package = pkgs.pulseaudioFull; # For bluetooth support
119 | };
120 |
121 | environment.systemPackages = with pkgs; [
122 | attic-client
123 | bash
124 | dnsutils
125 | element-desktop
126 | fish
127 | git
128 | #handbrake
129 | htop
130 | lsof
131 | pavucontrol
132 | vim
133 | vlc
134 | ];
135 |
136 | # So smartctl can read the disks
137 | services.udev.extraRules = ''
138 | SUBSYSTEM=="nvme", KERNEL=="nvme[0-9]*", GROUP="disk"
139 | '';
140 |
141 | services.prometheus.exporters = {
142 | node = {
143 | enable = true;
144 | port = 9100;
145 | enabledCollectors = [ "systemd" ];
146 | disabledCollectors = [
147 | "bonding"
148 | "fibrechannel"
149 | "infiniband"
150 | "ipvs"
151 | "mdadm"
152 | "nfs"
153 | "nfsd"
154 | "rapl"
155 | "tapestats"
156 | ];
157 | };
158 | smartctl = {
159 | enable = true;
160 | port = 9633;
161 | };
162 | zfs = {
163 | enable = true;
164 | port = 9134;
165 | };
166 | };
167 |
168 | services.syncoid = {
169 | enable = true;
170 |
171 | interval = "*:50:00";
172 | commonArgs = [
173 | "--sshkey"
174 | "%d/sshKey"
175 | "--create-bookmark"
176 | "--no-clone-handling"
177 | "--no-sync-snap"
178 | "--use-hold"
179 | "--skip-parent"
180 | ];
181 |
182 | commands = {
183 | "nvme-pool/persist" = {
184 | recursive = true;
185 | target = "syncoid-tartarus@elysium:lethe/backups/nvme-pool";
186 | };
187 | };
188 |
189 | localSourceAllow = [
190 | "bookmark"
191 | "hold"
192 | "send"
193 | "release"
194 | ];
195 |
196 | # NB: remember to run the following on elysium:
197 | # zfs allow -u syncoid-tartarus \
198 | # bookmark,compression,create,destroy,hold,mount,mountpoint,receive,release,rollback \
199 | # lethe/backups/nvme-pool
200 |
201 | service = {
202 | wants = [ "network-online.target" ];
203 | after = [ "network-online.target" ];
204 | serviceConfig = {
205 | LoadCredential = "sshKey:/persist/syncoid-zfs-send-id_ed25519";
206 | };
207 | };
208 | };
209 | systemd.timers."syncoid-nvme-pool-persist".timerConfig.Persistent = true;
210 |
211 | # Enable the OpenSSH daemon.
212 | # services.openssh.enable = true;
213 | services.zfs = {
214 | autoScrub = {
215 | enable = true;
216 | interval = "monthly";
217 | };
218 | autoSnapshot.enable = true;
219 | trim.enable = true;
220 | };
221 |
222 | services.tailscale.enable = true;
223 |
224 | # This value determines the NixOS release from which the default
225 | # settings for stateful data, like file locations and database versions
226 | # on your system were taken. It‘s perfectly fine and recommended to leave
227 | # this value at the release version of the first install of this system.
228 | # Before changing this value read the documentation for this option
229 | # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
230 | system.stateVersion = "20.09"; # Did you read the comment?
231 |
232 | programs.command-not-found.enable = false;
233 |
234 | dotfiles = {
235 | _1password.enable = true;
236 | nix.distributedBuilds = {
237 | enable = true;
238 | sshKey = "/persist/elysium-nixuser-id_ed25519";
239 | };
240 | };
241 |
242 | home-manager.users.ivan =
243 | { ... }:
244 | {
245 | imports = [ ../../users/ivan/home.nix ];
246 |
247 | dotfiles = {
248 | rust.enable = true;
249 | sway.enable = true;
250 | taskwarrior.enable = true;
251 | };
252 | };
253 |
254 | systemd.user.services.polkit-gnome-authentication-agent-1 = {
255 | description = "polkit-gnome-authentication-agent-1";
256 | wantedBy = [ "graphical-session.target" ];
257 | wants = [ "graphical-session.target" ];
258 | after = [ "graphical-session.target" ];
259 | serviceConfig = {
260 | Type = "simple";
261 | ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1";
262 | Restart = "on-failure";
263 | RestartSec = 1;
264 | TimeoutStopSec = 10;
265 | };
266 | };
267 | }
268 |
--------------------------------------------------------------------------------
/nixosConfigurations/tartarus/hardware-configuration.nix:
--------------------------------------------------------------------------------
1 | # Do not modify this file! It was generated by ‘nixos-generate-config’
2 | # and may be overwritten by future invocations. Please make changes
3 | # to /etc/nixos/configuration.nix instead.
4 | {
5 | lib,
6 | pkgs,
7 | modulesPath,
8 | ...
9 | }:
10 |
11 | {
12 | imports = [
13 | (modulesPath + "/installer/scan/not-detected.nix")
14 | ];
15 |
16 | boot.initrd.availableKernelModules = [
17 | "nvme"
18 | "xhci_pci"
19 | "ahci"
20 | "usbhid"
21 | "usb_storage"
22 | "sd_mod"
23 | "cryptd"
24 | "amdgpu"
25 | ];
26 | boot.initrd.kernelModules = [ ];
27 | boot.kernelModules = [ "kvm-amd" ];
28 | boot.extraModulePackages = [ ];
29 |
30 | # !!! cryptkey must be done first, and the list seems to be
31 | # alphabetically sorted, so take care that cryptroot / cryptswap,
32 | # whatever you name them, come after cryptkey.
33 | boot.initrd.luks.devices = {
34 | cryptkey = {
35 | device = "/dev/disk/by-uuid/e264facc-7943-4fd7-8be7-9fa555664af6";
36 | };
37 |
38 | cryptroot = {
39 | allowDiscards = true;
40 | device = "/dev/disk/by-uuid/50650a6a-003c-4928-82ae-eeeffc3a3fe1";
41 | keyFile = "/dev/mapper/cryptkey";
42 | keyFileSize = 8192;
43 | };
44 |
45 | cryptswap = {
46 | allowDiscards = true;
47 | device = "/dev/disk/by-uuid/db266c85-9dce-484a-8b32-00c410b33234";
48 | keyFile = "/dev/mapper/cryptkey";
49 | keyFileSize = 8192;
50 | };
51 | };
52 |
53 | boot.initrd.postDeviceCommands = lib.mkAfter ''
54 | cryptsetup close cryptkey
55 | '';
56 |
57 | fileSystems = {
58 | "/" = {
59 | device = "nvme-pool/local/root";
60 | fsType = "zfs";
61 | };
62 | "/boot" = {
63 | device = "/dev/disk/by-uuid/2100-01FF";
64 | fsType = "vfat";
65 | options = [
66 | "noatime"
67 | "umask=0077"
68 | ];
69 | };
70 | "/empty/user" = {
71 | device = "nvme-pool/persist/user";
72 | fsType = "zfs";
73 | };
74 | "/home/ivan" = {
75 | device = "nvme-pool/persist/user/ivan";
76 | fsType = "zfs";
77 | };
78 | "/home/ivan/books" = {
79 | device = "nvme-pool/persist/user/ivan/books";
80 | fsType = "zfs";
81 | };
82 | "/nix" = {
83 | device = "nvme-pool/local/nix";
84 | fsType = "zfs";
85 | };
86 | "/persist" = {
87 | device = "nvme-pool/persist/system";
88 | fsType = "zfs";
89 | neededForBoot = true;
90 | };
91 | "/scratch" = {
92 | device = "nvme-pool/local/scratch";
93 | fsType = "zfs";
94 | };
95 | "/var" = {
96 | device = "nvme-pool/persist/var";
97 | fsType = "zfs";
98 | };
99 | };
100 |
101 | swapDevices = [
102 | {
103 | device = "/dev/mapper/cryptswap";
104 | }
105 | ];
106 |
107 | # Bring back previous font look: https://github.com/NixOS/nixpkgs/issues/222805
108 | fonts.packages = [
109 | pkgs.xorg.fontmiscmisc
110 | ];
111 | }
112 |
--------------------------------------------------------------------------------
/nixosModules/_1password.nix:
--------------------------------------------------------------------------------
1 | { config, lib, ... }:
2 | let
3 | cfg = config.dotfiles._1password;
4 | in
5 | {
6 | options.dotfiles._1password.enable = lib.mkEnableOption "1Password";
7 |
8 | config = lib.mkIf cfg.enable {
9 | nixpkgs.config.allowUnfreePredicate =
10 | pkg:
11 | builtins.elem (lib.getName pkg) [
12 | "1password"
13 | ];
14 | security.polkit.enable = true;
15 | programs._1password-gui = {
16 | enable = true;
17 | polkitPolicyOwners = [
18 | config.users.users.ivan.name
19 | ];
20 | };
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/nixosModules/default.nix:
--------------------------------------------------------------------------------
1 | { lib, ... }:
2 |
3 | {
4 | imports = [
5 | ./_1password.nix
6 | ./knownHosts.nix
7 | ./nixConfig.nix
8 | ./pihole.nix
9 | ./tailscale.nix
10 | ./zfs-send.nix
11 | ];
12 |
13 | # https://github.com/NixOS/nixpkgs/issues/180175
14 | systemd.services.NetworkManager-wait-online.enable = lib.mkForce false;
15 | }
16 |
--------------------------------------------------------------------------------
/nixosModules/knownHosts.nix:
--------------------------------------------------------------------------------
1 | { ... }:
2 | {
3 | programs.ssh = {
4 | knownHosts = {
5 | "elysium" = {
6 | publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOWd8Xzy1H1PwwCYzAypTsnAnybhEXwX0RtWWI8LqcxL";
7 | };
8 | };
9 | };
10 | }
11 |
--------------------------------------------------------------------------------
/nixosModules/nixConfig.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | pkgs,
4 | lib,
5 | ...
6 | }:
7 | let
8 | inherit (lib) mkEnableOption mkOption types;
9 | cfg = config.dotfiles.nix;
10 | chosenNixpkgs = config.nixpkgs.flake.source;
11 | in
12 | {
13 | # The nixpkgs source is kinda large (87MB at this time, but constantly growing)
14 | # so allow machines (i.e. non-desktops) to opt out of adding it to the NIX_PATH/flake registry
15 | # to avoid having to copy it over with all deployments
16 | options.dotfiles.nix = {
17 | enableSetNixPathAndFlakeRegistry = mkOption {
18 | type = lib.types.bool;
19 | default = true;
20 | example = true;
21 | description = "Whether to add nixpkgs to nixPath and flake registry";
22 | };
23 |
24 | distributedBuilds = mkOption {
25 | default = { };
26 | type = types.submodule {
27 | options = {
28 | enable = mkEnableOption "distributed builds";
29 | sshKey = mkOption {
30 | type = types.nullOr types.str;
31 | default = null;
32 | };
33 | };
34 | };
35 | };
36 |
37 | useLix = lib.mkOption {
38 | default = true;
39 | description = "Whether to use Lix";
40 | type = lib.types.bool;
41 | };
42 |
43 | lixPackageSet = mkOption {
44 | type = lib.types.attrs;
45 | default = pkgs.lixPackageSets.stable;
46 | description = "Which Lix packages to use";
47 | };
48 | };
49 |
50 | config = lib.mkMerge [
51 | ({
52 | nix = {
53 | extraOptions = ''
54 | experimental-features = nix-command flakes
55 | keep-outputs = true
56 | keep-derivations = true
57 | '';
58 |
59 | settings = {
60 | sandbox = true;
61 |
62 | trusted-substituters = [
63 | "https://cache.ipetkov.dev/isc"
64 | "https://crane.cachix.org"
65 | "https://ipetkov.cachix.org"
66 | ];
67 |
68 | trusted-public-keys = [
69 | "crane.cachix.org-1:8Scfpmn9w+hGdXH/Q9tTLiYAE/2dnJYRJP7kl80GuRk="
70 | "ipetkov.cachix.org-1:xK9taxnomX0ZVyDmobpZB5AQvuZ+L3q4u7IlRvEtomg="
71 | "isc:b6qs2oRmB0HiJ0KCePMrv40lalsp6+e8eZRQRkXrMIc="
72 | "elysium.ipetkov.dev-1:H0okpsNoJPtsge8uMtBiN6tLavSyi+l3ziMTA31CRfc="
73 | ];
74 | };
75 |
76 | gc = {
77 | automatic = true;
78 | # Run on the 7th of the month: could take up a lot of I/O, so avoid
79 | # running at the same time as other "monthly" jobs
80 | dates = "*-7";
81 | options = "--delete-older-than 30d";
82 | persistent = true;
83 | };
84 |
85 | optimise = {
86 | automatic = true;
87 | # Run on the 21st of the month: offset this a bit from the `gc` job
88 | # so they don't try to run at the same time
89 | dates = [ "*-21" ];
90 | };
91 | };
92 | })
93 |
94 | (lib.mkIf cfg.useLix {
95 | nix.package = cfg.lixPackageSet.lix;
96 | # This pulls in vanilla nix, plus I don't use it anyway
97 | system.tools.nixos-option.enable = false;
98 | })
99 |
100 | (lib.mkIf cfg.enableSetNixPathAndFlakeRegistry {
101 | # Use our inputs as defaults for nixpkgs/nixos so everything (like nix-env)
102 | # moves in lockstep. (Note adding a channel will take precedence over this).
103 | nix = {
104 | nixPath = [
105 | "nixpkgs=${chosenNixpkgs}"
106 | ];
107 | registry = {
108 | nixpkgs.flake.outPath = chosenNixpkgs;
109 | };
110 | };
111 | })
112 |
113 | (lib.mkIf cfg.distributedBuilds.enable {
114 | nix = {
115 | buildMachines = [
116 | {
117 | inherit (cfg.distributedBuilds) sshKey;
118 |
119 | hostName = "elysium";
120 | maxJobs = 4;
121 | protocol = "ssh-ng";
122 | publicHostKey = "c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSU9XZDhYenkxSDFQd3dDWXpBeXBUc25BbnliaEVYd1gwUnRXV0k4THFjeEwgcm9vdEBlbHlzaXVtCg==";
123 | speedFactor = 1;
124 | sshUser = "nixuser";
125 | supportedFeatures = [
126 | "nixos-test"
127 | "benchmark"
128 | "big-parallel"
129 | "kvm"
130 | ];
131 | systems = [
132 | "x86_64-linux"
133 | "aarch64-linux"
134 | ];
135 | }
136 | ];
137 | distributedBuilds = true;
138 | settings.builders-use-substitutes = true;
139 | };
140 | })
141 | ];
142 | }
143 |
--------------------------------------------------------------------------------
/nixosModules/pihole.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | pkgs,
5 | ...
6 | }:
7 |
8 | let
9 | defaultDnsPort = 53;
10 | httpDefaultPort = 80;
11 | cfg = config.dotfiles.services.pihole;
12 |
13 | # NET_ADMIN allows for full network control (including making network changes
14 | # for DHCP broadcasting stuff). NET_BIND_SERVICE simply allows grabbing
15 | # privileged ports (like 53 or 80).
16 | networkCapability = if cfg.enableDHCPCap then "NET_ADMIN" else "NET_BIND_SERVICE";
17 | in
18 | {
19 | options.dotfiles.services.pihole = {
20 | enable = lib.mkEnableOption "pihole service";
21 |
22 | niceness = lib.mkOption {
23 | type = lib.types.ints.between (-20) 19;
24 | default = -15;
25 | description = "the niceness level of the process: https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Nice=";
26 | };
27 |
28 | enableDHCPCap = lib.mkEnableOption "grant network capabilities for DHCP changes";
29 |
30 | containerBackupDns = lib.mkOption {
31 | type = lib.types.str;
32 | default = "9.9.9.9";
33 | description = "a backup DNS server for the container in case DNSMasq has problems starting";
34 | };
35 |
36 | hostDnsPort = lib.mkOption {
37 | type = lib.types.port;
38 | default = defaultDnsPort;
39 | defaultText = toString defaultDnsPort;
40 | description = ''
41 | the (host) port that will be mapped to the docker image's DNS port
42 | '';
43 | };
44 |
45 | pullAt = lib.mkOption {
46 | type = lib.types.str;
47 | # Pihole images are updated monthly, so run half way through the month
48 | default = "*-*-15 00:00:00";
49 | description = "a systemd.time(7) compatible option for how often the image should be pulled";
50 | };
51 |
52 | webPort = lib.mkOption {
53 | type = lib.types.port;
54 | default = httpDefaultPort;
55 | defaultText = toString httpDefaultPort;
56 | description = ''
57 | the port that lighttpd should listen to. Change it to another value
58 | if this host will have more than one HTTP server/virtual host.
59 | '';
60 | };
61 | };
62 |
63 | config = lib.mkIf cfg.enable {
64 | systemd.services."docker-pihole".serviceConfig.Nice = cfg.niceness;
65 | virtualisation.oci-containers.containers.pihole = {
66 | image = "pihole/pihole:latest";
67 | ports = [
68 | "${toString cfg.hostDnsPort}:${toString defaultDnsPort}/udp"
69 | "${toString cfg.hostDnsPort}:${toString defaultDnsPort}/tcp"
70 | "${toString cfg.webPort}:${toString cfg.webPort}/tcp"
71 | ];
72 | volumes = [
73 | "/var/lib/pihole/:/etc/pihole/"
74 | ];
75 | environment = {
76 | TZ = config.time.timeZone;
77 | FTLCONF_webserver_port = "${toString cfg.webPort}";
78 | };
79 | extraOptions =
80 | [
81 | "--cap-add=${networkCapability}"
82 | "--dns=${cfg.containerBackupDns}"
83 | ]
84 | # Allocate a port in the host's network space so ports opened in the firewall work
85 | ++ (lib.lists.optional (cfg.hostDnsPort == defaultDnsPort) "--network=host");
86 | };
87 |
88 | systemd.services.pihole-update-image = {
89 | script = ''
90 | set -e
91 | ${pkgs.docker}/bin/docker pull pihole/pihole:latest
92 | systemctl restart docker-pihole.service
93 | '';
94 |
95 | serviceConfig = {
96 | Type = "oneshot";
97 | };
98 | };
99 |
100 | systemd.timers.pihole-update-image = {
101 | wantedBy = [ "timers.target" ];
102 | partOf = [ "pihole-update-image.service" ];
103 | timerConfig = {
104 | Persistent = true;
105 | OnCalendar = cfg.pullAt;
106 | };
107 | };
108 |
109 | # Only open up the hostDnsPort if it's the default DNS port
110 | # otherwise let the caller sort it out
111 | networking.firewall.allowedUDPPorts = lib.lists.optional (
112 | cfg.hostDnsPort == defaultDnsPort
113 | ) cfg.hostDnsPort;
114 | networking.firewall.allowedTCPPorts =
115 | (lib.lists.optional (cfg.hostDnsPort == defaultDnsPort) cfg.hostDnsPort)
116 |
117 | # Only open up the web port if its the default HTTP port. Otherwise
118 | # if a different port has been configured, leave it up to the caller if
119 | # they want to expose the port directly (or use a reverse proxy, etc.).
120 | ++ lib.lists.optional (cfg.webPort == httpDefaultPort) cfg.webPort;
121 | };
122 | }
123 |
--------------------------------------------------------------------------------
/nixosModules/tailscale.nix:
--------------------------------------------------------------------------------
1 | { config, lib, ... }:
2 | let
3 | inherit (lib) mkIf;
4 | cfg = config.services.tailscale;
5 | in
6 | {
7 | config = mkIf cfg.enable {
8 | networking.firewall = {
9 | allowedUDPPorts = [ cfg.port ];
10 | # Needed for taildrop to work, may need to be revisited in the future
11 | # https://forum.tailscale.com/t/taildrop-not-working-when-sending-a-file-from-iphone-to-a-nixos-machine/633/7
12 | trustedInterfaces = [ cfg.interfaceName ];
13 | checkReversePath = "loose";
14 | };
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/nixosModules/zfs-send.nix:
--------------------------------------------------------------------------------
1 | {
2 | config,
3 | lib,
4 | pkgs,
5 | ...
6 | }:
7 |
8 | let
9 | inherit (lib) escapeShellArg;
10 | cfg = config.dotfiles.zfs-send;
11 | hasZfs = builtins.any (e: e == "zfs") config.boot.initrd.supportedFilesystems;
12 | in
13 | {
14 | options.dotfiles.zfs-send = {
15 | enable = lib.mkEnableOption "zfs-send";
16 | rootPool = lib.mkOption {
17 | type = lib.types.str;
18 | example = "tank";
19 | description = ''
20 | the root pool of the host. zfs send permissions will be delegated to $POOL/persist
21 | '';
22 | };
23 | };
24 |
25 | config = lib.mkMerge [
26 | ({
27 | # NB: the syncoid user is _always_ present to avoid a recycled UID
28 | # suddenly gaining the existing permissions when we cannot remove them
29 | users = {
30 | groups.syncoid = { };
31 | users.syncoid = {
32 | group = config.users.groups.syncoid.name;
33 | isSystemUser = true;
34 | };
35 | };
36 | })
37 |
38 | # When disabled, strip any previous permissions we may have granted
39 | (lib.mkIf (!cfg.enable) {
40 | users.users.syncoid.useDefaultShell = false; # Do NOT permit login
41 | #system.activationScripts.zfs-unallow-syncoid = {
42 | # deps = [ "users" ];
43 | # text = lib.optionalString hasZfs ''
44 | # echo 'removing delegated zfs permissions for sending snapshots'
45 | # /run/booted-system/sw/bin/zfs list -H -o name -d 0 | \
46 | # xargs -n1 /run/booted-system/sw/bin/zfs unallow -r -u ${escapeShellArg config.users.users.syncoid.name}
47 | # '';
48 | #};
49 | })
50 |
51 | (lib.mkIf cfg.enable {
52 | # Packages used by syncoid, make them available on the whole system
53 | environment.systemPackages = with pkgs; [
54 | procps
55 | pv
56 | mbuffer
57 | lzop
58 | ];
59 |
60 | users.users.syncoid.useDefaultShell = true; # Do permit login
61 |
62 | system.activationScripts.zfs-allow-syncoid = {
63 | deps = [ "users" ];
64 | text = ''
65 | echo 'delegating zfs permissions for sending snapshots'
66 | /run/booted-system/sw/bin/zfs allow \
67 | -u ${escapeShellArg config.users.users.syncoid.name} \
68 | bookmark,hold,send,release \
69 | ${escapeShellArg cfg.rootPool}/persist
70 | '';
71 | };
72 | })
73 | ];
74 | }
75 |
--------------------------------------------------------------------------------
/pkgs/default.nix:
--------------------------------------------------------------------------------
1 | { pkgs }:
2 |
3 | with pkgs;
4 | {
5 | swaynagmode = callPackage ./swaynagmode.nix { };
6 | }
7 |
--------------------------------------------------------------------------------
/pkgs/swaynagmode.nix:
--------------------------------------------------------------------------------
1 | {
2 | fetchFromGitHub,
3 | stdenv,
4 | lib,
5 | }:
6 |
7 | stdenv.mkDerivation rec {
8 | pname = "swaynagmode";
9 | version = "0.2.1";
10 |
11 | src = fetchFromGitHub {
12 | owner = "b0o";
13 | repo = "swaynagmode";
14 | rev = "v${version}";
15 | sha256 = "BuPnP9PerPpxi0DJgp0Cfkaddi8QAYzcvbDTiMehkJw=";
16 | };
17 |
18 | installPhase = ''
19 | mkdir -p $out/bin
20 | cp swaynagmode $out/bin
21 | '';
22 |
23 | meta = with lib; {
24 | description = "A wrapper script which provides programmatic control over swaynag, intended for use with keyboard bindings.";
25 | homepage = "https://github.com/b0o/swaynagmode";
26 | license = licenses.gpl3;
27 | maintainers = [ ];
28 | platforms = platforms.linux;
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/users/ivan/default.nix:
--------------------------------------------------------------------------------
1 | { lib, pkgs, ... }:
2 |
3 | let
4 | userName = "ivan";
5 | in
6 | {
7 | programs.fish.enable = true;
8 |
9 | users.users."${userName}" = {
10 | uid = lib.mkForce 1000;
11 | isNormalUser = true;
12 | home = "/home/${userName}";
13 | shell = pkgs.fish;
14 | extraGroups = [
15 | "wheel" # Enable sudo
16 | "disk"
17 | "audio"
18 | "video"
19 | "networkmanager"
20 | "systemd-journal"
21 | "libvirtd"
22 | ];
23 | };
24 | }
25 |
--------------------------------------------------------------------------------
/users/ivan/home.nix:
--------------------------------------------------------------------------------
1 | { config, pkgs, ... }:
2 |
3 | let
4 | gitExtraConfig = config.programs.git.extraConfig;
5 | in
6 | {
7 | dotfiles.fonts.enable = true;
8 |
9 | programs.git = {
10 | userName = "Ivan Petkov";
11 | userEmail = "ivanppetkov@gmail.com";
12 | extraConfig = {
13 | github.user = "ipetkov";
14 | commit.gpgsign = true;
15 | gpg = {
16 | format = "ssh";
17 | ssh = {
18 | program = "/run/current-system/sw/bin/op-ssh-sign";
19 | allowedSignersFile = builtins.toString (
20 | pkgs.writeText "allowedSignersFile" ''
21 | ivanppetkov@gmail.com namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKFl+lzHHWKk9dgl6XkfSbKCFAkAZEEC3t+WXszgJuXX
22 | ''
23 | );
24 | };
25 | };
26 | user.signingKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKFl+lzHHWKk9dgl6XkfSbKCFAkAZEEC3t+WXszgJuXX";
27 | };
28 | };
29 |
30 | programs.jujutsu.settings.signing = {
31 | key = config.programs.git.extraConfig.user.signingKey;
32 | behavior = "own";
33 | backend = "ssh";
34 | backends.ssh = {
35 | inherit (gitExtraConfig.gpg.ssh) program;
36 | allowed-signers = gitExtraConfig.gpg.ssh.allowedSignersFile;
37 | };
38 | };
39 | }
40 |
--------------------------------------------------------------------------------