├── .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 | --------------------------------------------------------------------------------