├── .github ├── dependabot.yml ├── images │ ├── flake_logo.png │ ├── hypr_uw_clean.png │ └── hypr_uw_dirty.png └── workflows │ ├── cachix.yaml │ ├── flake-check.yaml │ └── flake-inputs.yaml ├── .gitignore ├── LICENSE ├── README.md ├── flake.lock ├── flake.nix ├── home ├── common │ ├── desktop │ │ ├── default.nix │ │ ├── file-associations.nix │ │ ├── ghostty.nix │ │ ├── gtk.nix │ │ ├── hyprland │ │ │ ├── config │ │ │ │ ├── displays.nix │ │ │ │ ├── keybinds.conf │ │ │ │ └── window-rules.nix │ │ │ ├── default.nix │ │ │ ├── hyprlock.nix │ │ │ └── hyprshot.nix │ │ ├── mako.nix │ │ ├── qt.nix │ │ ├── rofi.nix │ │ ├── sway │ │ │ ├── config │ │ │ │ ├── displays.nix │ │ │ │ ├── keybindings.nix │ │ │ │ └── window-rules.nix │ │ │ ├── default.nix │ │ │ ├── packages.nix │ │ │ ├── swappy.nix │ │ │ └── swaylock.nix │ │ ├── waybar │ │ │ ├── default.nix │ │ │ ├── lib.nix │ │ │ └── theme.nix │ │ ├── wl-common.nix │ │ └── xdg.nix │ ├── dev │ │ ├── base.nix │ │ ├── charm-tools.nix │ │ ├── default.nix │ │ └── desktop.nix │ ├── shell │ │ ├── atuin.nix │ │ ├── bat.nix │ │ ├── bottom.nix │ │ ├── default.nix │ │ ├── fastfetch.jsonc │ │ ├── fastfetch.nix │ │ ├── fish.nix │ │ ├── fzf.nix │ │ ├── git.nix │ │ ├── helix.nix │ │ ├── starship.nix │ │ ├── xdg.nix │ │ └── zellij.nix │ └── users │ │ ├── jon │ │ ├── default.nix │ │ ├── freyja.nix │ │ ├── kara.nix │ │ └── thor.nix │ │ └── ubuntu │ │ └── default.nix └── default.nix ├── host ├── common │ ├── base │ │ ├── boot.nix │ │ ├── console.nix │ │ ├── default.nix │ │ ├── hardware.nix │ │ ├── locale.nix │ │ ├── nh.nix │ │ ├── packages.nix │ │ └── zramswap.nix │ ├── desktop │ │ ├── default.nix │ │ ├── hyprland.nix │ │ ├── sway.nix │ │ └── tiling-common.nix │ ├── hardware │ │ ├── bluetooth.nix │ │ ├── ledger.nix │ │ ├── yubihsm.nix │ │ └── yubikey.nix │ ├── services │ │ ├── avahi.nix │ │ ├── backup │ │ │ ├── default.nix │ │ │ ├── kara.nix │ │ │ └── thor.nix │ │ ├── files.nix │ │ ├── firewall.nix │ │ ├── home-assistant.nix │ │ ├── immich.nix │ │ ├── libations.nix │ │ ├── networkmanager.nix │ │ ├── nfs │ │ │ ├── default.nix │ │ │ └── thor.nix │ │ ├── openssh.nix │ │ ├── photo-backup │ │ │ ├── default.nix │ │ │ └── photo-backup.sh │ │ ├── pipewire.nix │ │ ├── reverse-proxy │ │ │ ├── default.nix │ │ │ └── thor.nix │ │ └── tailscale.nix │ ├── users │ │ └── jon │ │ │ └── default.nix │ └── virt │ │ ├── default.nix │ │ ├── docker.nix │ │ ├── lxd.nix │ │ └── multipass.nix ├── default.nix ├── kara │ ├── boot.nix │ ├── disks.nix │ ├── extra.nix │ └── hardware.nix ├── thor │ ├── boot.nix │ ├── disks.nix │ ├── extra.nix │ └── hardware.nix └── volnir │ ├── boot.nix │ ├── disks.nix │ ├── extra.nix │ └── hardware.nix ├── lib ├── default.nix ├── helpers.nix └── theme │ ├── colours.nix │ ├── default.nix │ ├── lib.nix │ └── wallpapers │ ├── elk-colors.jpg │ ├── jokulsarlon.png │ ├── mountains.png │ └── space-clouds.png ├── nixpkgs.nix ├── overlays ├── custom-caddy.nix └── default.nix ├── pkgs ├── default.nix ├── ght │ ├── default.nix │ └── ght-graders-loc.patch ├── juju4.nix └── nixfmt-plus.nix ├── scripts ├── install-with-disko ├── partition-btrfs-luks └── setup-hyprland-ubuntu ├── secrets ├── kara-borgbase-passphrase.age ├── kara-borgbase-ssh.age ├── secrets.nix ├── thor-backup-env.age ├── thor-borgbase-passphrase.age ├── thor-borgbase-ssh.age ├── thor-digitalocean.age ├── thor-libations-recipes.age ├── thor-libations-tskey.age └── volnir-planefinder-config.age └── shell.nix /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | # Check for updates to GitHub Actions every week 7 | interval: "weekly" 8 | -------------------------------------------------------------------------------- /.github/images/flake_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/.github/images/flake_logo.png -------------------------------------------------------------------------------- /.github/images/hypr_uw_clean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/.github/images/hypr_uw_clean.png -------------------------------------------------------------------------------- /.github/images/hypr_uw_dirty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/.github/images/hypr_uw_dirty.png -------------------------------------------------------------------------------- /.github/workflows/cachix.yaml: -------------------------------------------------------------------------------- 1 | name: "Cachix" 2 | on: 3 | push: 4 | jobs: 5 | cachix: 6 | runs-on: ubuntu-latest 7 | strategy: 8 | matrix: 9 | package: ["ght"] 10 | steps: 11 | - name: Checkout flake 12 | uses: actions/checkout@v4 13 | 14 | - name: Install nix 15 | uses: DeterminateSystems/nix-installer-action@v17 16 | 17 | - name: Build ${{ matrix.package }} 18 | run: nix build .#${{ matrix.package }} 19 | -------------------------------------------------------------------------------- /.github/workflows/flake-check.yaml: -------------------------------------------------------------------------------- 1 | name: "Flake Checks" 2 | on: 3 | push: 4 | jobs: 5 | checks: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout flake 9 | uses: actions/checkout@v4 10 | 11 | - name: Install nix 12 | uses: DeterminateSystems/nix-installer-action@v17 13 | 14 | - name: nix flake check 15 | run: nix flake check 16 | -------------------------------------------------------------------------------- /.github/workflows/flake-inputs.yaml: -------------------------------------------------------------------------------- 1 | name: Flake ❄️ Checker ✅ 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | schedule: 8 | - cron: "37 13 * * *" 9 | workflow_dispatch: 10 | 11 | jobs: 12 | flake-checker: 13 | runs-on: ubuntu-22.04 14 | steps: 15 | - name: Checkout flake 16 | uses: actions/checkout@v4 17 | 18 | - name: Install nix 19 | uses: DeterminateSystems/nix-installer-action@v17 20 | 21 | - name: Check flake inputs 22 | uses: DeterminateSystems/flake-checker-action@v10 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | result* 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright jnsgruk 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | jnsgruk flake logo 3 |

4 | 5 |

jnsgruk's NixOS & Home Manager Configurations

6 | 7 | This repository contains a [Nix Flake](https://nixos.wiki/wiki/Flakes) for configuring my machines. As a general rule, my "server" machines track the latest stable release of NixOS, and my workstations/laptops track `unstable`. 8 | 9 | I use [Hyprland](https://hyprland.org/) as my tiling window manager, [Ghostty](https://ghostty.org/) as my terminal emulator, and I move between [Helix](https://helix-editor.com/) and [Visual Studio Code](https://code.visualstudio.com/) for editing. All of my apps/system components are themed with the excellent [Catppuccin](https://catppuccin.com/) theme where possible. I also rely on [1Password](https://1password.com/), [Obsidian](https://obsidian.md/) and [Todoist](https://todoist.com/) in my daily work. At work, we use Google Workspace, so I use [Google Chrome](https://www.google.com/intl/en_uk/chrome/) for work browsing and [Firefox](https://www.mozilla.org/en-GB/firefox/new/) for home browsing. I use (and **love**) [Tailscale](https://tailscale.com/) to network my computers, phones and tablets. 10 | 11 | ## 🖥️ Machines 12 | 13 | My machines are partitioned with [disko], and most are encrypted using the TPM to unlock the disks automatically on boot (I wrote about this [on my blog](https://jnsgr.uk/2024/04/nixos-secure-boot-tpm-fde/)). I tend toward [btrfs] for my machines - I've been using it for many years without issue. Machines are mostly named after characters or places in [Norse mythology](https://en.wikipedia.org/wiki/Norse_mythology). 14 | 15 | | Hostname | Board | CPU | RAM | GPU | OS | Role | Desktop | 16 | | :------: | :-------------------: | :---------------------: | :--: | :-------------------------: | :----: | :-----: | :----------: | 17 | | `dev` | [Multipass] VM | - | - | - | Ubuntu | Server | - | 18 | | `freyja` | [Thinkpad Z13] | [AMD Ryzen 7 Pro 6860Z] | 32GB | AMD Radeon 680M | Ubuntu | Laptop | Hyprland | 19 | | `kara` | [MSI MPG X670 Carbon] | [AMD Ryzen 9 7950X] | 64GB | [AMD Radeon RX 7900 XT] | NixOS | Desktop | Hyprland | 20 | | `thor` | [Intel NUC6i7KYK] | [Intel Core i7-6770HQ] | 16GB | Intel Iris Pro Graphics 580 | NixOS | Server | - | 21 | | `volnir` | [Raspberry Pi 4] | BCM2711 / Cortex A72 | 4GB | - | NixOS | Kiosk | Cage/Firefox | 22 | 23 | [btrfs]: https://btrfs.readthedocs.io/en/latest/index.html 24 | [disko]: https://github.com/nix-community/disko 25 | [Multipass]: https://multipass.run 26 | [Thinkpad Z13]: https://www.lenovo.com/gb/en/p/laptops/thinkpad/thinkpadz/thinkpad-z13-(13-inch-amd)/21d20012uk 27 | [Thinkcentre M93p]: https://psref.lenovo.com/Product/ThinkCentre/ThinkCentre_M93_M93p_Tiny 28 | [MSI MPG X670 Carbon]: https://www.msi.com/Motherboard/MPG-X670E-CARBON-WIFI 29 | [Intel NUC6i7KYK]: https://ark.intel.com/content/www/us/en/ark/products/89187/intel-nuc-kit-nuc6i7kyk.html 30 | [AMD Ryzen 7 Pro 6860Z]: https://www.cpubenchmark.net/cpu.php?id=4921&cpu=AMD+Ryzen+7+PRO+6860Z 31 | [Intel Core i5-4690]: https://www.cpubenchmark.net/cpu.php?cpu=Intel+Core+i5-4690+%40+3.50GHz&id=2236 32 | [AMD Ryzen 9 7950X]: https://www.cpubenchmark.net/cpu.php?cpu=AMD+Ryzen+9+7950X&id=5031 33 | [Intel Core i7-6770HQ]: https://www.cpubenchmark.net/cpu.php?cpu=Intel+Core+i7-6770HQ+%40+2.60GHz&id=2759 34 | [AMD Radeon RX 7900 XT]: https://www.xfxforce.com/shop/xfx-speedster-merc310-7900xt 35 | [Raspberry Pi 4]: https://www.raspberrypi.com/products/raspberry-pi-4-model-b/ 36 | 37 | ## 🚧 Structure 38 | 39 | - [.github]: ci/cd workflows for flake checks and version bumps 40 | - [home]: my home-manager configurations 41 | - [host]: host-specific configurations 42 | - [lib]: custom libraries for host/home construction and theming 43 | - [overlays]: package/configuration overlays 44 | - [pkgs]: my custom package definitions 45 | - [scripts]: helper scripts for machine setup 46 | - [secrets]: secrets encrypted with [age] and [agenix] 47 | 48 | Both [host] and [home] contain `common` directories, which contain reusable piece of configuration 49 | used across multiple machines. 50 | 51 | [.github]: ./github 52 | [age]: https://github.com/FiloSottile/age 53 | [agenix]: https://github.com/ryantm/agenix 54 | [home]: ./home 55 | [host]: ./host 56 | [lib]: ./lib 57 | [overlays]: ./overlays 58 | [pkgs]: ./pkgs 59 | [scripts]: ./scripts 60 | [secrets]: ./secrets 61 | 62 | ## 📦 Applications / Packages 63 | 64 | The following is a list of the key elements of my setup, with links to their config: 65 | 66 | | Type | Details | 67 | | :------: | :--------------------------------------------------------------- | 68 | | Shell | [fish], [starship], [helix], [zellij], [fastfetch], [bat], [fzf] | 69 | | WM | [hyprland], [waybar], [hyprlock], [mako], [gtk], [rofi] | 70 | | Apps | [vscode], [ghostty] | 71 | | Services | [home-assistant] | 72 | 73 | ## 🖼️ Screenshots 74 | 75 | ![clean](.github/images/hypr_uw_clean.png) 76 | ![dirty](.github/images/hypr_uw_dirty.png) 77 | 78 | 79 | 80 | [bat]: ./home/common/shell/bat.nix 81 | [fastfetch]: ./home/common/shell/fastfetch.nix 82 | [fish]: ./home/common/shell/fish.nix 83 | [fzf]: ./home/common/shell/fzf.nix 84 | [ghostty]: ./home/common/desktop/ghostty.nix 85 | [gtk]: ./home/common/desktop/gtk.nix 86 | [helix]: ./home/common/shell/helix.nix 87 | [home-assistant]: ./host/common/services/home-assistant.nix 88 | [hyprland]: ./home/common/desktop/hyprland/default.nix 89 | [hyprlock]: ./home/common/desktop/hyprland/hyprlock.nix 90 | [mako]: ./home/common/desktop/mako.nix 91 | [rofi]: ./home/common/desktop/rofi.nix 92 | [starship]: ./home/common/shell/starship.nix 93 | [vscode]: ./home/common/dev/desktop.nix 94 | [waybar]: ./home/common/desktop/waybar/default.nix 95 | [zellij]: ./home/common/shell/zellij.nix 96 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "agenix": { 4 | "inputs": { 5 | "darwin": "darwin", 6 | "home-manager": "home-manager", 7 | "nixpkgs": [ 8 | "nixpkgs" 9 | ], 10 | "systems": "systems" 11 | }, 12 | "locked": { 13 | "lastModified": 1736955230, 14 | "narHash": "sha256-uenf8fv2eG5bKM8C/UvFaiJMZ4IpUFaQxk9OH5t/1gA=", 15 | "owner": "ryantm", 16 | "repo": "agenix", 17 | "rev": "e600439ec4c273cf11e06fe4d9d906fb98fa097c", 18 | "type": "github" 19 | }, 20 | "original": { 21 | "owner": "ryantm", 22 | "repo": "agenix", 23 | "type": "github" 24 | } 25 | }, 26 | "catppuccin": { 27 | "inputs": { 28 | "nixpkgs": "nixpkgs" 29 | }, 30 | "locked": { 31 | "lastModified": 1739822884, 32 | "narHash": "sha256-g17zPoBFOrQfmgwZlBLnk4vfYDyfzllPQl02RWh8r4w=", 33 | "owner": "catppuccin", 34 | "repo": "nix", 35 | "rev": "24dac16e6babd41961d985eef3dce6a30dab2e91", 36 | "type": "github" 37 | }, 38 | "original": { 39 | "owner": "catppuccin", 40 | "repo": "nix", 41 | "type": "github" 42 | } 43 | }, 44 | "crane": { 45 | "locked": { 46 | "lastModified": 1731098351, 47 | "narHash": "sha256-HQkYvKvaLQqNa10KEFGgWHfMAbWBfFp+4cAgkut+NNE=", 48 | "owner": "ipetkov", 49 | "repo": "crane", 50 | "rev": "ef80ead953c1b28316cc3f8613904edc2eb90c28", 51 | "type": "github" 52 | }, 53 | "original": { 54 | "owner": "ipetkov", 55 | "repo": "crane", 56 | "type": "github" 57 | } 58 | }, 59 | "darwin": { 60 | "inputs": { 61 | "nixpkgs": [ 62 | "agenix", 63 | "nixpkgs" 64 | ] 65 | }, 66 | "locked": { 67 | "lastModified": 1700795494, 68 | "narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=", 69 | "owner": "lnl7", 70 | "repo": "nix-darwin", 71 | "rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d", 72 | "type": "github" 73 | }, 74 | "original": { 75 | "owner": "lnl7", 76 | "ref": "master", 77 | "repo": "nix-darwin", 78 | "type": "github" 79 | } 80 | }, 81 | "disko": { 82 | "inputs": { 83 | "nixpkgs": [ 84 | "nixpkgs" 85 | ] 86 | }, 87 | "locked": { 88 | "lastModified": 1739841949, 89 | "narHash": "sha256-lSOXdgW/1zi/SSu7xp71v+55D5Egz8ACv0STkj7fhbs=", 90 | "owner": "nix-community", 91 | "repo": "disko", 92 | "rev": "15dbf8cebd8e2655a883b74547108e089f051bf0", 93 | "type": "github" 94 | }, 95 | "original": { 96 | "owner": "nix-community", 97 | "repo": "disko", 98 | "type": "github" 99 | } 100 | }, 101 | "flake-compat": { 102 | "flake": false, 103 | "locked": { 104 | "lastModified": 1696426674, 105 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 106 | "owner": "edolstra", 107 | "repo": "flake-compat", 108 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 109 | "type": "github" 110 | }, 111 | "original": { 112 | "owner": "edolstra", 113 | "repo": "flake-compat", 114 | "type": "github" 115 | } 116 | }, 117 | "flake-parts": { 118 | "inputs": { 119 | "nixpkgs-lib": [ 120 | "lanzaboote", 121 | "nixpkgs" 122 | ] 123 | }, 124 | "locked": { 125 | "lastModified": 1730504689, 126 | "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", 127 | "owner": "hercules-ci", 128 | "repo": "flake-parts", 129 | "rev": "506278e768c2a08bec68eb62932193e341f55c90", 130 | "type": "github" 131 | }, 132 | "original": { 133 | "owner": "hercules-ci", 134 | "repo": "flake-parts", 135 | "type": "github" 136 | } 137 | }, 138 | "flake-utils": { 139 | "inputs": { 140 | "systems": "systems_2" 141 | }, 142 | "locked": { 143 | "lastModified": 1681202837, 144 | "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", 145 | "owner": "numtide", 146 | "repo": "flake-utils", 147 | "rev": "cfacdce06f30d2b68473a46042957675eebb3401", 148 | "type": "github" 149 | }, 150 | "original": { 151 | "owner": "numtide", 152 | "repo": "flake-utils", 153 | "type": "github" 154 | } 155 | }, 156 | "flypi": { 157 | "inputs": { 158 | "nixpkgs": "nixpkgs_2" 159 | }, 160 | "locked": { 161 | "lastModified": 1738941324, 162 | "narHash": "sha256-3nJgDAbgjMpiMzPgafFI8SAXOGnPrlV3VOwr1b/J6Jo=", 163 | "owner": "jnsgruk", 164 | "repo": "flypi", 165 | "rev": "0e50967176e4983b43e27a6fa462418f641176c7", 166 | "type": "github" 167 | }, 168 | "original": { 169 | "owner": "jnsgruk", 170 | "repo": "flypi", 171 | "type": "github" 172 | } 173 | }, 174 | "gitignore": { 175 | "inputs": { 176 | "nixpkgs": [ 177 | "lanzaboote", 178 | "pre-commit-hooks-nix", 179 | "nixpkgs" 180 | ] 181 | }, 182 | "locked": { 183 | "lastModified": 1709087332, 184 | "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", 185 | "owner": "hercules-ci", 186 | "repo": "gitignore.nix", 187 | "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", 188 | "type": "github" 189 | }, 190 | "original": { 191 | "owner": "hercules-ci", 192 | "repo": "gitignore.nix", 193 | "type": "github" 194 | } 195 | }, 196 | "home-manager": { 197 | "inputs": { 198 | "nixpkgs": [ 199 | "agenix", 200 | "nixpkgs" 201 | ] 202 | }, 203 | "locked": { 204 | "lastModified": 1703113217, 205 | "narHash": "sha256-7ulcXOk63TIT2lVDSExj7XzFx09LpdSAPtvgtM7yQPE=", 206 | "owner": "nix-community", 207 | "repo": "home-manager", 208 | "rev": "3bfaacf46133c037bb356193bd2f1765d9dc82c1", 209 | "type": "github" 210 | }, 211 | "original": { 212 | "owner": "nix-community", 213 | "repo": "home-manager", 214 | "type": "github" 215 | } 216 | }, 217 | "home-manager_2": { 218 | "inputs": { 219 | "nixpkgs": [ 220 | "nixpkgs" 221 | ] 222 | }, 223 | "locked": { 224 | "lastModified": 1739757849, 225 | "narHash": "sha256-Gs076ot1YuAAsYVcyidLKUMIc4ooOaRGO0PqTY7sBzA=", 226 | "owner": "nix-community", 227 | "repo": "home-manager", 228 | "rev": "9d3d080aec2a35e05a15cedd281c2384767c2cfe", 229 | "type": "github" 230 | }, 231 | "original": { 232 | "owner": "nix-community", 233 | "ref": "release-24.11", 234 | "repo": "home-manager", 235 | "type": "github" 236 | } 237 | }, 238 | "lanzaboote": { 239 | "inputs": { 240 | "crane": "crane", 241 | "flake-compat": "flake-compat", 242 | "flake-parts": "flake-parts", 243 | "nixpkgs": [ 244 | "nixpkgs" 245 | ], 246 | "pre-commit-hooks-nix": "pre-commit-hooks-nix", 247 | "rust-overlay": "rust-overlay" 248 | }, 249 | "locked": { 250 | "lastModified": 1739186342, 251 | "narHash": "sha256-2j+sln9RwQn+g7J4GmdFFgvqXnLkvWBNMaUzONlkzUE=", 252 | "owner": "nix-community", 253 | "repo": "lanzaboote", 254 | "rev": "3bdeebbc484a09391c4f0ec8a37bb77809426660", 255 | "type": "github" 256 | }, 257 | "original": { 258 | "owner": "nix-community", 259 | "repo": "lanzaboote", 260 | "type": "github" 261 | } 262 | }, 263 | "libations": { 264 | "inputs": { 265 | "nixpkgs": [ 266 | "nixpkgs" 267 | ] 268 | }, 269 | "locked": { 270 | "lastModified": 1738941367, 271 | "narHash": "sha256-lOxliNwviqF5JDVDt7iitmTkdhjV3ugFD2tADco7bqY=", 272 | "owner": "jnsgruk", 273 | "repo": "libations", 274 | "rev": "34eaf7e0d411308c37f8bcea6cf2c799c98fec70", 275 | "type": "github" 276 | }, 277 | "original": { 278 | "owner": "jnsgruk", 279 | "repo": "libations", 280 | "type": "github" 281 | } 282 | }, 283 | "master": { 284 | "locked": { 285 | "lastModified": 1739871119, 286 | "narHash": "sha256-ljb/20W3Du8kGcn6c9IqDgGd1sbUlYMU3SKHfkuR69I=", 287 | "owner": "nixos", 288 | "repo": "nixpkgs", 289 | "rev": "0add023131d28c3db0fc86889d343b2295f77bef", 290 | "type": "github" 291 | }, 292 | "original": { 293 | "owner": "nixos", 294 | "ref": "master", 295 | "repo": "nixpkgs", 296 | "type": "github" 297 | } 298 | }, 299 | "nix-system-graphics": { 300 | "inputs": { 301 | "nixpkgs": [ 302 | "nixpkgs" 303 | ] 304 | }, 305 | "locked": { 306 | "lastModified": 1737457219, 307 | "narHash": "sha256-nX9dxoATDCSQgWw/iv6BngXDJEyHVYYEvHEVQ7Ig3fI=", 308 | "owner": "soupglasses", 309 | "repo": "nix-system-graphics", 310 | "rev": "9c875e0c56cf2eb272b9102a4f3e24e4e31629fd", 311 | "type": "github" 312 | }, 313 | "original": { 314 | "owner": "soupglasses", 315 | "repo": "nix-system-graphics", 316 | "type": "github" 317 | } 318 | }, 319 | "nixos-hardware": { 320 | "locked": { 321 | "lastModified": 1739798439, 322 | "narHash": "sha256-GyipmjbbQEaosel/+wq1xihCKbv0/e1LU00x/8b/fP4=", 323 | "owner": "NixOS", 324 | "repo": "nixos-hardware", 325 | "rev": "3e2ea8a49d4d76276b0f4e2041df8ca5c0771371", 326 | "type": "github" 327 | }, 328 | "original": { 329 | "owner": "NixOS", 330 | "ref": "master", 331 | "repo": "nixos-hardware", 332 | "type": "github" 333 | } 334 | }, 335 | "nixpkgs": { 336 | "locked": { 337 | "lastModified": 1736012469, 338 | "narHash": "sha256-/qlNWm/IEVVH7GfgAIyP6EsVZI6zjAx1cV5zNyrs+rI=", 339 | "owner": "NixOS", 340 | "repo": "nixpkgs", 341 | "rev": "8f3e1f807051e32d8c95cd12b9b421623850a34d", 342 | "type": "github" 343 | }, 344 | "original": { 345 | "owner": "NixOS", 346 | "ref": "nixos-unstable", 347 | "repo": "nixpkgs", 348 | "type": "github" 349 | } 350 | }, 351 | "nixpkgs-stable": { 352 | "locked": { 353 | "lastModified": 1730741070, 354 | "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", 355 | "owner": "NixOS", 356 | "repo": "nixpkgs", 357 | "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", 358 | "type": "github" 359 | }, 360 | "original": { 361 | "owner": "NixOS", 362 | "ref": "nixos-24.05", 363 | "repo": "nixpkgs", 364 | "type": "github" 365 | } 366 | }, 367 | "nixpkgs_2": { 368 | "locked": { 369 | "lastModified": 1738142207, 370 | "narHash": "sha256-NGqpVVxNAHwIicXpgaVqJEJWeyqzoQJ9oc8lnK9+WC4=", 371 | "owner": "nixos", 372 | "repo": "nixpkgs", 373 | "rev": "9d3ae807ebd2981d593cddd0080856873139aa40", 374 | "type": "github" 375 | }, 376 | "original": { 377 | "owner": "nixos", 378 | "ref": "nixos-unstable", 379 | "repo": "nixpkgs", 380 | "type": "github" 381 | } 382 | }, 383 | "nixpkgs_3": { 384 | "locked": { 385 | "lastModified": 1739758141, 386 | "narHash": "sha256-uq6A2L7o1/tR6VfmYhZWoVAwb3gTy7j4Jx30MIrH0rE=", 387 | "owner": "nixos", 388 | "repo": "nixpkgs", 389 | "rev": "c618e28f70257593de75a7044438efc1c1fc0791", 390 | "type": "github" 391 | }, 392 | "original": { 393 | "owner": "nixos", 394 | "ref": "nixos-24.11", 395 | "repo": "nixpkgs", 396 | "type": "github" 397 | } 398 | }, 399 | "pre-commit-hooks-nix": { 400 | "inputs": { 401 | "flake-compat": [ 402 | "lanzaboote", 403 | "flake-compat" 404 | ], 405 | "gitignore": "gitignore", 406 | "nixpkgs": [ 407 | "lanzaboote", 408 | "nixpkgs" 409 | ], 410 | "nixpkgs-stable": "nixpkgs-stable" 411 | }, 412 | "locked": { 413 | "lastModified": 1731363552, 414 | "narHash": "sha256-vFta1uHnD29VUY4HJOO/D6p6rxyObnf+InnSMT4jlMU=", 415 | "owner": "cachix", 416 | "repo": "pre-commit-hooks.nix", 417 | "rev": "cd1af27aa85026ac759d5d3fccf650abe7e1bbf0", 418 | "type": "github" 419 | }, 420 | "original": { 421 | "owner": "cachix", 422 | "repo": "pre-commit-hooks.nix", 423 | "type": "github" 424 | } 425 | }, 426 | "root": { 427 | "inputs": { 428 | "agenix": "agenix", 429 | "catppuccin": "catppuccin", 430 | "disko": "disko", 431 | "flypi": "flypi", 432 | "home-manager": "home-manager_2", 433 | "lanzaboote": "lanzaboote", 434 | "libations": "libations", 435 | "master": "master", 436 | "nix-system-graphics": "nix-system-graphics", 437 | "nixos-hardware": "nixos-hardware", 438 | "nixpkgs": "nixpkgs_3", 439 | "system-manager": "system-manager", 440 | "unstable": "unstable", 441 | "vscode-server": "vscode-server" 442 | } 443 | }, 444 | "rust-overlay": { 445 | "inputs": { 446 | "nixpkgs": [ 447 | "lanzaboote", 448 | "nixpkgs" 449 | ] 450 | }, 451 | "locked": { 452 | "lastModified": 1731897198, 453 | "narHash": "sha256-Ou7vLETSKwmE/HRQz4cImXXJBr/k9gp4J4z/PF8LzTE=", 454 | "owner": "oxalica", 455 | "repo": "rust-overlay", 456 | "rev": "0be641045af6d8666c11c2c40e45ffc9667839b5", 457 | "type": "github" 458 | }, 459 | "original": { 460 | "owner": "oxalica", 461 | "repo": "rust-overlay", 462 | "type": "github" 463 | } 464 | }, 465 | "system-manager": { 466 | "inputs": { 467 | "nixpkgs": [ 468 | "nixpkgs" 469 | ] 470 | }, 471 | "locked": { 472 | "lastModified": 1739316420, 473 | "narHash": "sha256-FZBKtR8mqbcEazdpI1SoID43FeldQPhjnvluUO9HAaI=", 474 | "owner": "numtide", 475 | "repo": "system-manager", 476 | "rev": "82d5a9ecd15ec48bcbfbacf5462066ee267d6aae", 477 | "type": "github" 478 | }, 479 | "original": { 480 | "owner": "numtide", 481 | "repo": "system-manager", 482 | "type": "github" 483 | } 484 | }, 485 | "systems": { 486 | "locked": { 487 | "lastModified": 1681028828, 488 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 489 | "owner": "nix-systems", 490 | "repo": "default", 491 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 492 | "type": "github" 493 | }, 494 | "original": { 495 | "owner": "nix-systems", 496 | "repo": "default", 497 | "type": "github" 498 | } 499 | }, 500 | "systems_2": { 501 | "locked": { 502 | "lastModified": 1681028828, 503 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 504 | "owner": "nix-systems", 505 | "repo": "default", 506 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 507 | "type": "github" 508 | }, 509 | "original": { 510 | "owner": "nix-systems", 511 | "repo": "default", 512 | "type": "github" 513 | } 514 | }, 515 | "unstable": { 516 | "locked": { 517 | "lastModified": 1739736696, 518 | "narHash": "sha256-zON2GNBkzsIyALlOCFiEBcIjI4w38GYOb+P+R4S8Jsw=", 519 | "owner": "nixos", 520 | "repo": "nixpkgs", 521 | "rev": "d74a2335ac9c133d6bbec9fc98d91a77f1604c1f", 522 | "type": "github" 523 | }, 524 | "original": { 525 | "owner": "nixos", 526 | "ref": "nixos-unstable", 527 | "repo": "nixpkgs", 528 | "type": "github" 529 | } 530 | }, 531 | "vscode-server": { 532 | "inputs": { 533 | "flake-utils": "flake-utils", 534 | "nixpkgs": [ 535 | "nixpkgs" 536 | ] 537 | }, 538 | "locked": { 539 | "lastModified": 1729422940, 540 | "narHash": "sha256-DlvJv33ml5UTKgu4b0HauOfFIoDx6QXtbqUF3vWeRCY=", 541 | "owner": "nix-community", 542 | "repo": "nixos-vscode-server", 543 | "rev": "8b6db451de46ecf9b4ab3d01ef76e59957ff549f", 544 | "type": "github" 545 | }, 546 | "original": { 547 | "owner": "nix-community", 548 | "repo": "nixos-vscode-server", 549 | "type": "github" 550 | } 551 | } 552 | }, 553 | "root": "root", 554 | "version": 7 555 | } 556 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "jnsgruk's nixos configuration"; 3 | inputs = { 4 | nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; 5 | unstable.url = "github:nixos/nixpkgs/nixos-unstable"; 6 | master.url = "github:nixos/nixpkgs/master"; 7 | nixos-hardware.url = "github:NixOS/nixos-hardware/master"; 8 | 9 | system-manager.url = "github:numtide/system-manager"; 10 | system-manager.inputs.nixpkgs.follows = "nixpkgs"; 11 | 12 | nix-system-graphics.url = "github:soupglasses/nix-system-graphics"; 13 | nix-system-graphics.inputs.nixpkgs.follows = "nixpkgs"; 14 | 15 | agenix.url = "github:ryantm/agenix"; 16 | agenix.inputs.nixpkgs.follows = "nixpkgs"; 17 | 18 | catppuccin.url = "github:catppuccin/nix"; 19 | 20 | disko.url = "github:nix-community/disko"; 21 | disko.inputs.nixpkgs.follows = "nixpkgs"; 22 | 23 | flypi.url = "github:jnsgruk/flypi"; 24 | 25 | home-manager.url = "github:nix-community/home-manager/release-24.11"; 26 | home-manager.inputs.nixpkgs.follows = "nixpkgs"; 27 | 28 | lanzaboote.url = "github:nix-community/lanzaboote"; 29 | lanzaboote.inputs.nixpkgs.follows = "nixpkgs"; 30 | 31 | libations.url = "github:jnsgruk/libations"; 32 | libations.inputs.nixpkgs.follows = "nixpkgs"; 33 | 34 | vscode-server.url = "github:nix-community/nixos-vscode-server"; 35 | vscode-server.inputs.nixpkgs.follows = "nixpkgs"; 36 | }; 37 | 38 | outputs = 39 | { 40 | self, 41 | unstable, 42 | ... 43 | }@inputs: 44 | let 45 | inherit (self) outputs; 46 | stateVersion = "24.11"; 47 | username = "jon"; 48 | 49 | libx = import ./lib { 50 | inherit 51 | self 52 | inputs 53 | outputs 54 | stateVersion 55 | username 56 | ; 57 | }; 58 | in 59 | { 60 | # nix build .#homeConfigurations."jon@freyja".activationPackage 61 | homeConfigurations = { 62 | # Desktop machines 63 | "${username}@freyja" = libx.mkHome { 64 | hostname = "freyja"; 65 | desktop = "hyprland"; 66 | }; 67 | "${username}@kara" = libx.mkHome { 68 | hostname = "kara"; 69 | desktop = "hyprland"; 70 | }; 71 | # Headless machines 72 | "${username}@thor" = libx.mkHome { hostname = "thor"; }; 73 | "${username}@volnir" = libx.mkHome { 74 | hostname = "volnir"; 75 | system = "aarch64-linux"; 76 | }; 77 | "ubuntu@dev" = libx.mkHome { 78 | hostname = "dev"; 79 | user = "ubuntu"; 80 | }; 81 | }; 82 | 83 | # nix build .#nixosConfigurations.freyja.config.system.build.toplevel 84 | nixosConfigurations = { 85 | # Desktop machines 86 | kara = libx.mkHost { 87 | hostname = "kara"; 88 | desktop = "hyprland"; 89 | }; 90 | # Headless machines 91 | thor = libx.mkHost { 92 | hostname = "thor"; 93 | }; 94 | volnir = libx.mkHost { 95 | hostname = "volnir"; 96 | }; 97 | }; 98 | 99 | # system-manager configurations 100 | systemConfigs = { 101 | freyja = libx.mkSystemManager { }; 102 | }; 103 | 104 | # Custom packages; acessible via 'nix build', 'nix shell', etc 105 | packages = libx.forAllSystems ( 106 | system: 107 | let 108 | pkgs = unstable.legacyPackages.${system}; 109 | in 110 | import ./pkgs { inherit pkgs; } 111 | ); 112 | 113 | # Custom overlays 114 | overlays = import ./overlays { inherit inputs; }; 115 | 116 | # Devshell for bootstrapping 117 | # Accessible via 'nix develop' or 'nix-shell' (legacy) 118 | devShells = libx.forAllSystems ( 119 | system: 120 | let 121 | pkgs = unstable.legacyPackages.${system}; 122 | in 123 | import ./shell.nix { inherit pkgs; } 124 | ); 125 | 126 | formatter = libx.forAllSystems (system: self.packages.${system}.nixfmt-plus); 127 | }; 128 | } 129 | -------------------------------------------------------------------------------- /home/common/desktop/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, desktop, ... }: 2 | { 3 | imports = [ 4 | (./. + "/${desktop}") 5 | 6 | ../dev 7 | 8 | ./ghostty.nix 9 | ./gtk.nix 10 | ./qt.nix 11 | ./xdg.nix 12 | ]; 13 | 14 | programs = { 15 | firefox.enable = true; 16 | mpv.enable = true; 17 | }; 18 | 19 | home.packages = with pkgs; [ 20 | bambu-studio 21 | catppuccin-gtk 22 | desktop-file-utils 23 | ght 24 | google-chrome 25 | # (google-chrome.override { 26 | # commandLineArgs = [ 27 | # "--enable-features=VaapiVideoDecoder,VaapiIgnoreDriverChecks,Vulkan,DefaultANGLEVulkan,VulkanFromANGLE" 28 | # "--ignore-gpu-blocklist" 29 | # "--enable-zero-copy" 30 | # ]; 31 | # }) 32 | libnotify 33 | loupe 34 | papers 35 | pwvucontrol 36 | thunderbird-latest 37 | todoist-electron 38 | xdg-utils 39 | 40 | unstable.obsidian 41 | unstable.rambox 42 | unstable.signal-desktop 43 | ]; 44 | 45 | fonts.fontconfig.enable = true; 46 | } 47 | -------------------------------------------------------------------------------- /home/common/desktop/file-associations.nix: -------------------------------------------------------------------------------- 1 | let 2 | browser = [ "google-chrome.desktop" ]; 3 | archiveManager = [ "org.gnome.FileRoller.desktop" ]; 4 | imageViewer = [ "org.gnome.Loupe.desktop" ]; 5 | videoPlayer = [ "mpv.desktop" ]; 6 | in 7 | { 8 | # XDG MIME types 9 | associations = { 10 | "application/x-extension-htm" = browser; 11 | "application/x-extension-html" = browser; 12 | "application/x-extension-shtml" = browser; 13 | "application/x-extension-xht" = browser; 14 | "application/x-extension-xhtml" = browser; 15 | "application/xhtml+xml" = browser; 16 | "text/html" = browser; 17 | "x-scheme-handler/about" = browser; 18 | "x-scheme-handler/chrome" = browser; 19 | "x-scheme-handler/ftp" = browser; 20 | "x-scheme-handler/http" = browser; 21 | "x-scheme-handler/https" = browser; 22 | "x-scheme-handler/unknown" = browser; 23 | 24 | "audio/*" = videoPlayer; 25 | "video/*" = videoPlayer; 26 | "image/*" = imageViewer; 27 | 28 | "application/json" = browser; 29 | 30 | "application/pdf" = [ "org.gnome.Papers.desktop" ]; 31 | 32 | # Archives / compressed files 33 | "application/x-7z-compressed" = archiveManager; 34 | "application/x-7z-compressed-tar" = archiveManager; 35 | "application/x-bzip" = archiveManager; 36 | "application/x-bzip-compressed-tar" = archiveManager; 37 | "application/x-compress" = archiveManager; 38 | "application/x-compressed-tar" = archiveManager; 39 | "application/x-cpio" = archiveManager; 40 | "application/x-gzip" = archiveManager; 41 | "application/x-lha" = archiveManager; 42 | "application/x-lzip" = archiveManager; 43 | "application/x-lzip-compressed-tar" = archiveManager; 44 | "application/x-lzma" = archiveManager; 45 | "application/x-lzma-compressed-tar" = archiveManager; 46 | "application/x-tar" = archiveManager; 47 | "application/x-tarz" = archiveManager; 48 | "application/x-xar" = archiveManager; 49 | "application/x-xz" = archiveManager; 50 | "application/x-xz-compressed-tar" = archiveManager; 51 | "application/zip" = archiveManager; 52 | "application/gzip" = archiveManager; 53 | "application/bzip2" = archiveManager; 54 | "application/vnd.rar" = archiveManager; 55 | 56 | # Images 57 | "image/jpeg" = imageViewer; 58 | "image/png" = imageViewer; 59 | "image/gif" = imageViewer; 60 | "image/webp" = imageViewer; 61 | "image/tiff" = imageViewer; 62 | "image/x-tga" = imageViewer; 63 | "image/vnd-ms.dds" = imageViewer; 64 | "image/x-dds" = imageViewer; 65 | "image/bmp" = imageViewer; 66 | "image/vnd.microsoft.icon" = imageViewer; 67 | "image/vnd.radiance" = imageViewer; 68 | "image/x-exr" = imageViewer; 69 | "image/x-portable-bitmap" = imageViewer; 70 | "image/x-portable-graymap" = imageViewer; 71 | "image/x-portable-pixmap" = imageViewer; 72 | "image/x-portable-anymap" = imageViewer; 73 | "image/x-qoi" = imageViewer; 74 | "image/svg+xml" = imageViewer; 75 | "image/svg+xml-compressed" = imageViewer; 76 | "image/avif" = imageViewer; 77 | "image/heic" = imageViewer; 78 | "image/jxl" = imageViewer; 79 | }; 80 | } 81 | -------------------------------------------------------------------------------- /home/common/desktop/ghostty.nix: -------------------------------------------------------------------------------- 1 | { pkgs, self, ... }: 2 | let 3 | theme = import "${self}/lib/theme" { inherit pkgs; }; 4 | in 5 | { 6 | programs.ghostty = { 7 | enable = true; 8 | settings = { 9 | theme = "catppucin-macchiato"; 10 | font-family = "${theme.fonts.monospace.name}"; 11 | font-size = 14; 12 | window-padding-x = 10; 13 | window-padding-y = 10; 14 | }; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /home/common/desktop/gtk.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | self, 4 | config, 5 | ... 6 | }: 7 | let 8 | theme = import "${self}/lib/theme" { inherit pkgs; }; 9 | in 10 | { 11 | home.pointerCursor = { 12 | inherit (theme.cursorTheme) package size name; 13 | gtk.enable = true; 14 | x11.enable = true; 15 | }; 16 | 17 | gtk = { 18 | enable = true; 19 | 20 | theme = theme.gtkTheme; 21 | 22 | font = { 23 | inherit (theme.fonts.default) package; 24 | name = "${theme.fonts.default.name}, ${theme.fonts.default.size}"; 25 | }; 26 | 27 | gtk2 = { 28 | configLocation = "${config.xdg.configHome}/gtk-2.0/gtkrc"; 29 | extraConfig = '' 30 | gtk-application-prefer-dark-theme=1 31 | ''; 32 | }; 33 | 34 | gtk3.extraConfig = { 35 | gtk-button-images = 1; 36 | gtk-application-prefer-dark-theme = true; 37 | }; 38 | 39 | gtk4.extraConfig = { 40 | gtk-application-prefer-dark-theme = true; 41 | }; 42 | 43 | iconTheme = { 44 | inherit (theme.iconTheme) name package; 45 | }; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /home/common/desktop/hyprland/config/displays.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | kara = { 3 | workspace = [ ]; 4 | monitor = [ "DP-1, 7680x2160@120Hz, auto, 1" ]; 5 | }; 6 | 7 | freyja = { 8 | workspace = [ ]; 9 | monitor = [ "eDP-1, preferred, auto, 1.5" ]; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /home/common/desktop/hyprland/config/keybinds.conf: -------------------------------------------------------------------------------- 1 | # terminal, screen locking, launcher 2 | bind = $mod, RETURN, exec, ghostty 3 | bind = $mod, L, exec, hyprlock 4 | bind = $mod, SPACE, exec, rofi -show drun 5 | bind = ALT, Q, killactive, 6 | 7 | # media controls 8 | bindl = , XF86AudioPlay, exec, playerctl play-pause 9 | bindl = , XF86AudioPrev, exec, playerctl previous 10 | bindl = , XF86AudioNext, exec, playerctl next 11 | 12 | # volume 13 | bindle = , XF86AudioRaiseVolume, exec, volumectl -u up 14 | bindle = , XF86AudioLowerVolume, exec, volumectl -u down 15 | bindl = , XF86AudioMute, exec, volumectl -u toggle-mute 16 | bindl = , XF86AudioMicMute, exec, volumectl -m toggle-mute 17 | bindl = , Pause, exec, volumectl -m toggle-mute 18 | 19 | # backlight 20 | bindle = , XF86MonBrightnessUp, exec, lightctl up 21 | bindle = , XF86MonBrightnessDown, exec, lightctl down 22 | 23 | # clipboard 24 | bind = $mod, C, exec, bash -c "export XDG_CACHE_HOME=/home/$USER/.local/cache; cliphist list | rofi -dmenu -display-columns 2 -window-title "📋" | cliphist decode | wl-copy" 25 | 26 | # apps 27 | bind = $mod, grave, exec, 1password --quick-access 28 | bind = $mod, E, exec, bemoji -c -n 29 | 30 | # window controls 31 | bind = $mod, F, fullscreen, 32 | bind = $mod SHIFT, Space, togglefloating, 33 | bind = $mod, A, togglesplit, 34 | 35 | # override the split direction for the next window to be opened 36 | bind = $mod, V, layoutmsg, preselect d 37 | bind = $mod, H, layoutmsg, preselect r 38 | 39 | # group management 40 | bind = $mod, G, togglegroup, 41 | bind = $mod SHIFT, G, moveoutofgroup, 42 | bind = ALT, left, changegroupactive, b 43 | bind = ALT, right, changegroupactive, f 44 | 45 | # move focus 46 | bind = $mod, left, movefocus, l 47 | bind = $mod, right, movefocus, r 48 | bind = $mod, up, movefocus, u 49 | bind = $mod, down, movefocus, d 50 | 51 | # move window 52 | bind = $mod SHIFT, left, movewindoworgroup, l 53 | bind = $mod SHIFT, right, movewindoworgroup, r 54 | bind = $mod SHIFT, up, movewindoworgroup, u 55 | bind = $mod SHIFT, down, movewindoworgroup, d 56 | 57 | # window resize 58 | bind = $mod, R, submap, resize 59 | submap = resize 60 | binde = , right, resizeactive, 10 0 61 | binde = , left, resizeactive, -10 0 62 | binde = , up, resizeactive, 0 -10 63 | binde = , down, resizeactive, 0 10 64 | bind = , escape, submap, reset 65 | submap = reset 66 | 67 | # mouse bindings 68 | bindm = SUPER, mouse:272, movewindow 69 | bindm = SUPER, mouse:273, resizewindow 70 | 71 | # navigate workspaces 72 | bind = $mod, 1, workspace, 1 73 | bind = $mod, 2, workspace, 2 74 | bind = $mod, 3, workspace, 3 75 | bind = $mod, 4, workspace, 4 76 | bind = $mod, 5, workspace, 5 77 | bind = $mod, 6, workspace, 6 78 | bind = $mod, 7, workspace, 7 79 | bind = $mod, 8, workspace, 8 80 | bind = $mod, 9, workspace, 9 81 | 82 | # move window to workspace 83 | bind = $mod SHIFT, 1, movetoworkspace, 1 84 | bind = $mod SHIFT, 2, movetoworkspace, 2 85 | bind = $mod SHIFT, 3, movetoworkspace, 3 86 | bind = $mod SHIFT, 4, movetoworkspace, 4 87 | bind = $mod SHIFT, 5, movetoworkspace, 5 88 | bind = $mod SHIFT, 6, movetoworkspace, 6 89 | bind = $mod SHIFT, 7, movetoworkspace, 7 90 | bind = $mod SHIFT, 8, movetoworkspace, 8 91 | bind = $mod SHIFT, 9, movetoworkspace, 9 92 | -------------------------------------------------------------------------------- /home/common/desktop/hyprland/config/window-rules.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | windowrulev2 = [ 3 | # only allow shadows for floating windows 4 | "noshadow, floating:0" 5 | 6 | # idle inhibit while watching videos 7 | "idleinhibit focus, class:^(mpv|.+exe)$" 8 | "idleinhibit fullscreen, class:.*" 9 | 10 | # make Firefox PiP window floating and sticky 11 | "float, title:^(Picture-in-Picture)$" 12 | "pin, title:^(Picture-in-Picture)$" 13 | 14 | "float, class:^(1Password)$" 15 | "stayfocused,title:^(Quick Access — 1Password)$" 16 | "dimaround,title:^(Quick Access — 1Password)$" 17 | "noanim,title:^(Quick Access — 1Password)$" 18 | 19 | "float, class:^(org.gnome.*)$" 20 | "float, class:^(pavucontrol|com.saivert.pwvucontrol)$" 21 | "float, class:(blueberry\.py)" 22 | 23 | # make pop-up file dialogs floating, centred, and pinned 24 | "float, title:(Open|Progress|Save File)" 25 | "center, title:(Open|Progress|Save File)" 26 | "pin, title:(Open|Progress|Save File)" 27 | "float, class:^(code)$, initialTitle:^((?!Visual Studio Code).)*$" 28 | "center, class:^(code)$, initialTitle:^((?!Visual Studio Code).)*$" 29 | "pin, class:^(code)$, initialTitle:^((?!Visual Studio Code).)*$" 30 | 31 | # assign windows to workspaces 32 | # "workspace 1 silent, class:[Ff]irefox" 33 | # "workspace 2 silent, class:[Oo]bsidian" 34 | # "workspace 2 silent, class:google-chrome" 35 | # "workspace 2 silent, class:[Rr]ambox" 36 | # "workspace 1 silent, class:[Ss]ignal" 37 | # "workspace 5 silent, class:code-url-handler" 38 | 39 | # throw sharing indicators away 40 | "workspace special silent, title:^(Firefox — Sharing Indicator)$" 41 | "workspace special silent, title:^(.*is sharing (your screen|a window)\.)$" 42 | ]; 43 | } 44 | -------------------------------------------------------------------------------- /home/common/desktop/hyprland/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | hostname, 4 | lib, 5 | pkgs, 6 | ... 7 | }: 8 | let 9 | theme = import "${self}/lib/theme" { inherit pkgs hostname; }; 10 | inherit (theme) colours fonts; 11 | keybinds = builtins.readFile ./config/keybinds.conf; 12 | outputs = (import ./config/displays.nix { }).${hostname}; 13 | windowRules = import ./config/window-rules.nix { }; 14 | in 15 | { 16 | imports = [ 17 | ../wl-common.nix 18 | ./hyprlock.nix 19 | ./hyprshot.nix 20 | ]; 21 | 22 | wayland.windowManager.hyprland = { 23 | enable = true; 24 | xwayland.enable = true; 25 | 26 | settings = { 27 | inherit (outputs) monitor workspace; 28 | inherit (windowRules) windowrulev2; 29 | 30 | "$mod" = "SUPER"; 31 | 32 | general = { 33 | gaps_in = 4; 34 | gaps_out = 8; 35 | border_size = 0; 36 | }; 37 | 38 | dwindle = { 39 | preserve_split = true; 40 | force_split = 2; 41 | }; 42 | 43 | gestures = { 44 | workspace_swipe = true; 45 | workspace_swipe_forever = true; 46 | workspace_swipe_invert = false; 47 | }; 48 | 49 | input = { 50 | kb_layout = "gb"; 51 | follow_mouse = 2; 52 | repeat_rate = 50; 53 | repeat_delay = 300; 54 | }; 55 | 56 | decoration = { 57 | rounding = 8; 58 | shadow = { 59 | enabled = true; 60 | ignore_window = true; 61 | offset = "0 5"; 62 | range = 50; 63 | render_power = 3; 64 | color = "rgba(00000099)"; 65 | }; 66 | }; 67 | 68 | animation = [ 69 | "border, 1, 2, default" 70 | "fade, 1, 4, default" 71 | "windows, 1, 3, default, popin 80%" 72 | "workspaces, 1, 2, default, slide" 73 | ]; 74 | 75 | misc = { 76 | background_color = "rgb(${__substring 1 7 colours.surface1})"; 77 | disable_hyprland_logo = true; 78 | disable_splash_rendering = true; 79 | animate_manual_resizes = true; 80 | }; 81 | 82 | group = { 83 | groupbar = { 84 | font_family = "${fonts.default.name}"; 85 | font_size = 12; 86 | gradients = true; 87 | }; 88 | }; 89 | 90 | xwayland = { 91 | force_zero_scaling = true; 92 | }; 93 | }; 94 | 95 | extraConfig = '' 96 | ${keybinds} 97 | ''; 98 | }; 99 | 100 | systemd.user.services.swaybg = { 101 | Unit.Description = "swaybg"; 102 | Install.WantedBy = [ "hyprland-session.target" ]; 103 | Service = { 104 | Type = "simple"; 105 | ExecStart = "${lib.getExe pkgs.swaybg} -m fill -i ${theme.wallpaper}"; 106 | Restart = "on-failure"; 107 | }; 108 | }; 109 | } 110 | -------------------------------------------------------------------------------- /home/common/desktop/hyprland/hyprlock.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | lib, 4 | pkgs, 5 | hostname, 6 | ... 7 | }: 8 | let 9 | theme = import "${self}/lib/theme" { inherit pkgs hostname; }; 10 | inherit (theme) hexToRgb colours; 11 | in 12 | { 13 | programs.hyprlock = { 14 | enable = true; 15 | settings = { 16 | general = { 17 | grace = 5; 18 | hide_cursor = true; 19 | }; 20 | 21 | background = [ 22 | { 23 | path = "${theme.wallpaper}"; 24 | blur_passes = 2; 25 | blur_size = 6; 26 | } 27 | ]; 28 | 29 | input-field = [ 30 | { 31 | size = "250, 60"; 32 | outer_color = "rgb(${hexToRgb colours.black})"; 33 | inner_color = "rgb(${hexToRgb colours.bgDark})"; 34 | font_color = "rgb(${hexToRgb colours.purple})"; 35 | placeholder_text = ""; 36 | } 37 | ]; 38 | 39 | label = [ 40 | { 41 | text = "Hello"; 42 | color = "rgba(${hexToRgb colours.text}, 1.0)"; 43 | font_family = theme.fonts.default.name; 44 | font_size = 64; 45 | text_align = "center"; 46 | halign = "center"; 47 | valign = "center"; 48 | position = "0, 160"; 49 | } 50 | { 51 | text = "$TIME"; 52 | color = "rgba(${hexToRgb colours.subtext1}, 1.0)"; 53 | font_family = theme.fonts.default.name; 54 | font_size = 32; 55 | text_align = "center"; 56 | halign = "center"; 57 | valign = "center"; 58 | position = "0, 75"; 59 | } 60 | ]; 61 | }; 62 | }; 63 | 64 | services.hypridle = { 65 | enable = true; 66 | settings = { 67 | general = { 68 | lock_cmd = "${lib.getExe pkgs.hyprlock}"; 69 | before_sleep_cmd = "${lib.getExe pkgs.hyprlock}"; 70 | }; 71 | 72 | listener = [ 73 | { 74 | timeout = 300; 75 | on-timeout = "${lib.getExe pkgs.hyprlock}"; 76 | } 77 | { 78 | timeout = 305; 79 | on-timeout = "${pkgs.hyprland}/bin/hyprctl dispatch dpms off"; 80 | on-resume = "${pkgs.hyprland}/bin/hyprctl dispatch dpms on"; 81 | } 82 | ]; 83 | }; 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /home/common/desktop/hyprland/hyprshot.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | pkgs, 5 | ... 6 | }: 7 | { 8 | home = { 9 | file = { 10 | "${config.xdg.configHome}/satty/config.toml".text = '' 11 | [general] 12 | # Start Satty in fullscreen mode 13 | fullscreen = false 14 | # Exit directly after copy/save action 15 | early-exit = false 16 | # Select the tool on startup [possible values: pointer, crop, line, arrow, rectangle, text, marker, blur, brush] 17 | initial-tool = "pointer" 18 | # Configure the command to be called on copy, for example `wl-copy` 19 | copy-command = "${pkgs.wl-clipboard}/bin/wl-copy" 20 | # Increase or decrease the size of the annotations 21 | annotation-size-factor = 1 22 | # Filename to use for saving action: https://docs.rs/chrono/latest/chrono/format/strftime/index.html 23 | output-filename = "${config.home.homeDirectory}/pictures/screenshots/screenshot-%Y%m%d-%H%M%S.png" 24 | save-after-copy = false 25 | default-hide-toolbars = false 26 | # The primary highlighter: block, freehand 27 | primary-highlighter = "block" 28 | disable-notifications = false 29 | 30 | # Font to use for text annotations 31 | [font] 32 | family = "Work Sans" 33 | style = "Bold" 34 | ''; 35 | }; 36 | packages = with pkgs; [ 37 | hyprshot # screenshot grabber 38 | satty # screenshot editor 39 | ]; 40 | }; 41 | wayland.windowManager.hyprland = { 42 | settings = { 43 | bind = [ 44 | ", Print, exec, ${lib.getExe pkgs.hyprshot} --mode output --raw | ${lib.getExe pkgs.satty} --filename -" 45 | "SHIFT, Print, exec, ${lib.getExe pkgs.hyprshot} --mode window --raw | ${lib.getExe pkgs.satty} --filename -" 46 | "ALT, Print, exec, ${lib.getExe pkgs.hyprshot} --mode region --raw | ${lib.getExe pkgs.satty} --filename -" 47 | ]; 48 | }; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /home/common/desktop/mako.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | self, 4 | hostname, 5 | ... 6 | }: 7 | let 8 | theme = import "${self}/lib/theme" { inherit pkgs; }; 9 | in 10 | { 11 | catppuccin.mako.enable = true; 12 | 13 | services = { 14 | mako = { 15 | enable = true; 16 | actions = true; 17 | anchor = if hostname == "kara" then "top-center" else "top-right"; 18 | borderRadius = 8; 19 | borderSize = 1; 20 | defaultTimeout = 10000; 21 | font = "${theme.fonts.default.name}"; 22 | iconPath = "${theme.iconTheme.iconPath}"; 23 | icons = true; 24 | layer = "overlay"; 25 | maxVisible = 3; 26 | padding = "10"; 27 | width = 300; 28 | }; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /home/common/desktop/qt.nix: -------------------------------------------------------------------------------- 1 | { pkgs, self, ... }: 2 | let 3 | theme = import "${self}/lib/theme" { inherit pkgs; }; 4 | in 5 | { 6 | qt = { 7 | enable = true; 8 | platformTheme.name = "gtk"; 9 | }; 10 | 11 | home = { 12 | packages = with pkgs; [ 13 | theme.qtTheme.package 14 | libsForQt5.qtstyleplugin-kvantum 15 | ]; 16 | sessionVariables = { 17 | "QT_STYLE_OVERRIDE" = "kvantum"; 18 | }; 19 | }; 20 | 21 | xdg.configFile = { 22 | "Kvantum/kvantum.kvconfig".text = '' 23 | [General] 24 | theme=${theme.qtTheme.name} 25 | ''; 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /home/common/desktop/rofi.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | catppuccin.rofi = { 4 | enable = true; 5 | flavor = "mocha"; 6 | }; 7 | 8 | programs.rofi = { 9 | enable = true; 10 | package = pkgs.rofi-wayland; 11 | terminal = "${pkgs.ghostty}/bin/ghostty"; 12 | 13 | extraConfig = { 14 | modi = "drun"; 15 | show-icons = true; 16 | drun-display-format = "{icon} {name}"; 17 | disable-history = false; 18 | hide-scrollbar = true; 19 | display-drun = "  Apps "; 20 | sidebar-mode = true; 21 | }; 22 | }; 23 | 24 | home.packages = [ pkgs.bemoji ]; 25 | } 26 | -------------------------------------------------------------------------------- /home/common/desktop/sway/config/displays.nix: -------------------------------------------------------------------------------- 1 | { wallpaper, ... }: 2 | { 3 | kara = { 4 | "DP-1" = { 5 | mode = "7680x2160@119.997Hz"; 6 | bg = "${wallpaper} fill"; 7 | }; 8 | }; 9 | 10 | freyja = { 11 | "eDP-1" = { 12 | mode = "2880x1800@60Hz"; 13 | scale = 1.5; 14 | bg = "${wallpaper} fill"; 15 | }; 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /home/common/desktop/sway/config/keybindings.nix: -------------------------------------------------------------------------------- 1 | { 2 | modifier, 3 | terminal, 4 | menu, 5 | pkgs, 6 | ... 7 | }: 8 | { 9 | main = { 10 | # Open a terminal 11 | "${modifier}+Return" = "exec ${terminal}"; 12 | # Toggle the launcher 13 | "${modifier}+Space" = "exec ${menu} | xargs swaymsg exec --"; 14 | # Reload the config 15 | "${modifier}+Shift+c" = "reload"; 16 | # Lock the screen 17 | "${modifier}+l" = "exec ${pkgs.swaylock-effects}/bin/swaylock -f"; 18 | # Kill application 19 | "Mod1+q" = "kill"; 20 | 21 | # Screenshots 22 | "Print" = "exec sway-screenshot screen"; 23 | "Shift+Print" = "exec sway-screenshot window"; 24 | "Alt+Print" = "exec sway-screenshot region"; 25 | 26 | # Move focus 27 | "${modifier}+Left" = "focus left"; 28 | "${modifier}+Right" = "focus right"; 29 | "${modifier}+Up" = "focus up"; 30 | "${modifier}+Down" = "focus down"; 31 | 32 | # Move windows 33 | "${modifier}+Shift+Left" = "move left"; 34 | "${modifier}+Shift+Down" = "move down"; 35 | "${modifier}+Shift+Up" = "move up"; 36 | "${modifier}+Shift+Right" = "move right"; 37 | 38 | # Workspaces 39 | "${modifier}+1" = "workspace number 1"; 40 | "${modifier}+2" = "workspace number 2"; 41 | "${modifier}+3" = "workspace number 3"; 42 | "${modifier}+4" = "workspace number 4"; 43 | "${modifier}+5" = "workspace number 5"; 44 | "${modifier}+6" = "workspace number 6"; 45 | "${modifier}+7" = "workspace number 7"; 46 | "${modifier}+8" = "workspace number 8"; 47 | "${modifier}+9" = "workspace number 9"; 48 | 49 | # Move focused container to workspace 50 | "${modifier}+Shift+1" = "move container to workspace number 1"; 51 | "${modifier}+Shift+2" = "move container to workspace number 2"; 52 | "${modifier}+Shift+3" = "move container to workspace number 3"; 53 | "${modifier}+Shift+4" = "move container to workspace number 4"; 54 | "${modifier}+Shift+5" = "move container to workspace number 5"; 55 | "${modifier}+Shift+6" = "move container to workspace number 6"; 56 | "${modifier}+Shift+7" = "move container to workspace number 7"; 57 | "${modifier}+Shift+8" = "move container to workspace number 8"; 58 | "${modifier}+Shift+9" = "move container to workspace number 9"; 59 | 60 | # Split current object of focus 61 | "${modifier}+h" = "splith"; 62 | "${modifier}+v" = "splitv"; 63 | 64 | # Switch layout style for current container 65 | "${modifier}+s" = "layout stacking"; 66 | "${modifier}+w" = "layout tabbed"; 67 | "${modifier}+a" = "layout toggle split"; 68 | 69 | # Make current container fullscreen 70 | "${modifier}+f" = "fullscreen"; 71 | # Toggle current container between tiling/floating 72 | "${modifier}+Shift+space" = "floating toggle"; 73 | # Swap focus between tiled windows and floating window 74 | "Mod1+space" = "focus mode_toggle"; 75 | # Focus parent container 76 | # "${modifier}+a" = "focus parent"; 77 | # Move focused window to scratch pad 78 | "${modifier}+Shift+minus" = "move scratchpad"; 79 | "${modifier}+minus" = "scratchpad show"; 80 | 81 | "${modifier}+r" = "mode 'resize'"; 82 | 83 | # Audio/Media Keys 84 | "--locked XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play-pause"; 85 | "--locked XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next"; 86 | "--locked XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl prev"; 87 | 88 | # Volume Keys 89 | "--locked XF86AudioRaiseVolume" = "exec ${pkgs.avizo}/bin/volumectl -u up"; 90 | "--locked XF86AudioLowerVolume" = "exec ${pkgs.avizo}/bin/volumectl -u down"; 91 | "--locked XF86AudioMute" = "exec ${pkgs.avizo}/bin/volumectl toggle-mute"; 92 | "--locked Pause" = "exec ${pkgs.avizo}/bin/volumectl -m toggle-mute"; 93 | 94 | # Brightness Controls 95 | "--locked XF86MonBrightnessUp" = "exec ${pkgs.avizo}/bin/lightctl up"; 96 | "--locked XF86MonBrightnessDown" = "exec ${pkgs.avizo}/bin/lightctl down"; 97 | 98 | # Applications 99 | "Mod1+grave" = "exec ${pkgs._1password-gui}/bin/1password --quick-access"; 100 | "${modifier}+c" = "exec sway-clip"; 101 | "${modifier}+e" = "exec bemoji -c -n"; 102 | }; 103 | 104 | resize = { 105 | "Left" = "resize shrink width 10px"; 106 | "Right" = "resize shrink width 10px"; 107 | "Up" = "resize shrink height 10px"; 108 | "Down" = "resize grow height 10px"; 109 | "Return" = "mode 'default'"; 110 | "Escape" = "mode 'default'"; 111 | }; 112 | } 113 | -------------------------------------------------------------------------------- /home/common/desktop/sway/config/window-rules.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | commands = [ 3 | # Floating window config 4 | { 5 | command = "floating enable"; 6 | criteria = { 7 | window_role = "pop up|bubble|task_dialog|Preferences|page-info|Saves As|dialog|menu"; 8 | }; 9 | } 10 | { 11 | command = "floating enable"; 12 | criteria = { 13 | app_id = "org.gnome.*|nm-connection-editor|pavucontrol|com.saivert.pwvucontrol|pinentry-qt|^code$"; 14 | }; 15 | } 16 | { 17 | command = "floating enable"; 18 | criteria = { 19 | title = "Picture-in-picture|1Password"; 20 | }; 21 | } 22 | { 23 | command = "floating enable"; 24 | criteria = { 25 | class = "1Password"; 26 | }; 27 | } 28 | # Floating & Sticky windows 29 | { 30 | command = "floating enable sticky"; 31 | criteria = { 32 | window_role = "Open Files|Open Folder|File Operation Progress"; 33 | }; 34 | } 35 | # Sticky windows 36 | { 37 | command = "sticky enable"; 38 | criteria = { 39 | app_id = "^code$"; 40 | }; 41 | } 42 | { 43 | command = "sticky enable"; 44 | criteria = { 45 | instance = "file_progress"; 46 | }; 47 | } 48 | { 49 | command = "sticky enable"; 50 | criteria = { 51 | class = "info|Mate-color-select|gcolor2|timesup|QtPass|GtkFileChooserDialog"; 52 | }; 53 | } 54 | # Inhibit idle when there is a fullscreen app 55 | { 56 | command = "inhibit_idle fullscreen"; 57 | criteria = { 58 | class = ".*"; 59 | }; 60 | } 61 | ]; 62 | } 63 | -------------------------------------------------------------------------------- /home/common/desktop/sway/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | self, 4 | hostname, 5 | ... 6 | }: 7 | { 8 | imports = [ 9 | ../wl-common.nix 10 | 11 | ./packages.nix 12 | ./swappy.nix 13 | ./swaylock.nix 14 | ]; 15 | 16 | wayland.windowManager.sway = 17 | let 18 | theme = import "${self}/lib/theme" { inherit pkgs hostname; }; 19 | modifier = "Mod4"; 20 | terminal = "ghostty"; 21 | menu = "rofi -show drun"; 22 | in 23 | { 24 | enable = true; 25 | 26 | systemd.enable = true; 27 | wrapperFeatures = { 28 | gtk = true; 29 | base = true; 30 | }; 31 | 32 | config = { 33 | inherit menu terminal modifier; 34 | bars = [ ]; 35 | gaps = { 36 | inner = 8; 37 | }; 38 | 39 | input = { 40 | "*" = { 41 | xkb_layout = "gb"; 42 | }; 43 | "type:keyboard" = { 44 | repeat_rate = "100"; 45 | repeat_delay = "250"; 46 | }; 47 | "type:touchpad" = { 48 | tap = "enabled"; 49 | }; 50 | }; 51 | 52 | output = (import ./config/displays.nix { inherit (theme) wallpaper; })."${hostname}"; 53 | 54 | fonts = { 55 | names = [ "${theme.fonts.iconFont.name}" ]; 56 | size = 8.0; 57 | }; 58 | 59 | window = { 60 | border = 0; 61 | hideEdgeBorders = "none"; 62 | titlebar = false; 63 | inherit ((import ./config/window-rules.nix { inherit theme; })) commands; 64 | }; 65 | 66 | floating = { 67 | border = 1; 68 | titlebar = false; 69 | }; 70 | 71 | focus = { 72 | newWindow = "focus"; 73 | followMouse = "no"; 74 | }; 75 | 76 | startup = [ ]; 77 | 78 | keybindings = 79 | (import ./config/keybindings.nix { 80 | inherit 81 | terminal 82 | menu 83 | modifier 84 | pkgs 85 | ; 86 | }).main; 87 | modes.resize = 88 | (import ./config/keybindings.nix { 89 | inherit 90 | terminal 91 | menu 92 | modifier 93 | pkgs 94 | ; 95 | }).resize; 96 | }; 97 | }; 98 | } 99 | -------------------------------------------------------------------------------- /home/common/desktop/sway/packages.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | let 3 | sway-screenshot = pkgs.writeShellScriptBin "sway-screenshot" '' 4 | if [[ "$1" == "screen" ]]; then 5 | grim -o $(swaymsg -t get_outputs | jq -r '.[] | select(.focused) | .name') - | swappy -f - 6 | elif [[ "$1" == "window" ]]; then 7 | grim -g "$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp)" - | swappy -f - 8 | elif [[ "$1" == "region" ]]; then 9 | grim -g "$(slurp)" - | swappy -f - 10 | fi 11 | ''; 12 | sway-clip = pkgs.writeShellScriptBin "sway-clip" '' 13 | export XDG_CACHE_HOME=/home/$USER/.local/cache 14 | cliphist list | rofi -dmenu -display-columns 2 -window-title '📋' | cliphist decode | wl-copy 15 | ''; 16 | in 17 | { 18 | home.packages = with pkgs; [ 19 | grim 20 | slurp 21 | sway-clip 22 | sway-screenshot 23 | ]; 24 | } 25 | -------------------------------------------------------------------------------- /home/common/desktop/sway/swappy.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | home.packages = with pkgs; [ swappy ]; 4 | 5 | home.file = { 6 | ".config/swappy/config".text = '' 7 | [Default] 8 | save_dir=$HOME/pictures/screenshots 9 | save_filename_format=screenshot-%Y%m%d-%H%M%S.png 10 | early_exit=true 11 | ''; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /home/common/desktop/sway/swaylock.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | pkgs, 4 | self, 5 | hostname, 6 | ... 7 | }: 8 | let 9 | theme = import "${self}/lib/theme" { inherit pkgs hostname; }; 10 | in 11 | { 12 | programs.swaylock = { 13 | enable = true; 14 | package = pkgs.swaylock-effects; 15 | 16 | settings = { 17 | 18 | font = "${theme.fonts.default.name}"; 19 | clock = true; 20 | 21 | indicator = true; 22 | ignore-empty-password = true; 23 | indicator-idle-visible = true; 24 | disable-caps-lock-text = true; 25 | 26 | timestr = "%R"; 27 | datestr = "%a, %e of %B"; 28 | image = "${theme.wallpaper}"; 29 | # effect-blur = "30x3"; 30 | 31 | key-hl-color = "${theme.colours.green}"; 32 | separator-color = "${theme.colours.black}00"; 33 | 34 | inside-color = "${theme.colours.bg}99"; 35 | inside-clear-color = "${theme.colours.yellow}00"; 36 | inside-caps-lock-color = "${theme.colours.lightBlue}00"; 37 | inside-ver-color = "${theme.colours.overlay0}00"; 38 | inside-wrong-color = "${theme.colours.red}00"; 39 | 40 | ring-color = "${theme.colours.bgDark}D9"; 41 | ring-clear-color = "${theme.colours.bgDark}D9"; 42 | ring-caps-lock-color = "${theme.colours.bgDark}D9"; 43 | ring-ver-color = "${theme.colours.bgDark}D9"; 44 | ring-wrong-color = "${theme.colours.bgDark}D9"; 45 | 46 | line-color = "${theme.colours.black}00"; 47 | line-clear-color = "${theme.colours.yellow}FF"; 48 | line-caps-lock-color = "${theme.colours.lightBlue}FF"; 49 | line-ver-color = "${theme.colours.overlay0}FF"; 50 | line-wrong-color = "${theme.colours.red}FF"; 51 | 52 | text-clear-color = "${theme.colours.yellow}00"; 53 | text-ver-color = "${theme.colours.overlay0}00"; 54 | text-wrong-color = "${theme.colours.red}00"; 55 | 56 | bs-hl-color = "${theme.colours.red}FF"; 57 | caps-lock-key-hl-color = "${theme.colours.yellow}FF"; 58 | caps-lock-bs-hl-color = "${theme.colours.red}FF"; 59 | text-caps-lock-color = "${theme.colours.lightBlue}"; 60 | }; 61 | }; 62 | 63 | services.swayidle = { 64 | enable = true; 65 | extraArgs = [ "-w" ]; 66 | events = [ 67 | { 68 | event = "before-sleep"; 69 | command = "${lib.getExe pkgs.swaylock-effects} -f"; 70 | } 71 | { 72 | event = "lock"; 73 | command = "${lib.getExe pkgs.swaylock-effects} -f"; 74 | } 75 | ]; 76 | timeouts = [ 77 | { 78 | timeout = 300; 79 | command = "${lib.getExe pkgs.swaylock-effects} -f"; 80 | } 81 | { 82 | timeout = 305; 83 | command = ''${pkgs.sway}/bin/swaymsg "output * dpms off"''; 84 | resumeCommand = ''${pkgs.sway}/bin/swaymsg "output * dpms on"''; 85 | } 86 | ]; 87 | }; 88 | } 89 | -------------------------------------------------------------------------------- /home/common/desktop/waybar/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | config, 4 | pkgs, 5 | lib, 6 | hostname, 7 | desktop, 8 | ... 9 | }: 10 | let 11 | # If this is a laptop, then include network/battery controls 12 | modules = 13 | if hostname == "freyja" then 14 | [ 15 | "network" 16 | "battery" 17 | "custom/vpn" 18 | "wireplumber" 19 | "pulseaudio#source" 20 | "bluetooth" 21 | "group/group-power" 22 | ] 23 | else 24 | [ 25 | "wireplumber" 26 | "pulseaudio#source" 27 | "bluetooth" 28 | "group/group-power" 29 | ]; 30 | 31 | workspaceConfig = { 32 | format = "{icon}"; 33 | format-icons = { 34 | "1" = ""; 35 | "2" = ""; 36 | "3" = "󰙀"; 37 | "4" = ""; 38 | "5" = ""; 39 | "6" = ""; 40 | "7" = ""; 41 | }; 42 | on-click = "activate"; 43 | }; 44 | 45 | bluetoothToggle = pkgs.writeShellApplication { 46 | name = "bluetooth-toggle"; 47 | runtimeInputs = with pkgs; [ 48 | gnugrep 49 | bluez 50 | ]; 51 | text = '' 52 | if [[ "$(bluetoothctl show | grep -Po "Powered: \K(.+)$")" =~ no ]]; then 53 | bluetoothctl power on 54 | bluetoothctl discoverable on 55 | else 56 | bluetoothctl power off 57 | fi 58 | ''; 59 | }; 60 | 61 | tsCheck = pkgs.writeShellApplication { 62 | name = "tscheck"; 63 | runtimeInputs = with pkgs; [ 64 | tailscale 65 | jq 66 | ]; 67 | text = '' 68 | if [[ "$1" == "toggle" ]]; then 69 | if [[ "$(tailscale status --json | jq -r '.BackendState')" == "Stopped" ]]; then 70 | tailscale up --operator=jon --ssh --reset 71 | else 72 | tailscale down 73 | fi 74 | elif [[ "$1" == "exit" ]]; then 75 | if [[ "$(tailscale status --json | jq -r '.ExitNodeStatus.Online')" == "true" ]]; then 76 | tailscale set --exit-node= 77 | else 78 | tailscale set --exit-node=gb-lon-wg-001.mullvad.ts.net &>/dev/null 79 | fi 80 | fi 81 | 82 | if tailscale status &>/dev/null; then 83 | if [[ "$(tailscale status --json | jq -r '.ExitNodeStatus.Online')" == "true" ]]; then 84 | ip="$(tailscale status --json | jq -r '.ExitNodeStatus.TailscaleIPs[0]')" 85 | echo "{\"text\": \"󰖂\", \"tooltip\": \"Connected to exit node ($ip)\", \"class\": \"exitNode\"}" | jq --unbuffered --compact-output 86 | else 87 | echo "{\"text\": \"󰖂\", \"tooltip\": \"Connected to tailnet\", \"class\": \"tailnet\"}" | jq --unbuffered --compact-output 88 | fi 89 | else 90 | echo '{"text": "󰖂", "tooltip": "Disconnected", "class": "disconnected"}' | jq --unbuffered --compact-output 91 | fi 92 | ''; 93 | }; 94 | 95 | theme = import "${self}/lib/theme" { inherit pkgs; }; 96 | inherit ((import ./lib.nix { inherit lib; })) toRasi; 97 | in 98 | { 99 | programs.waybar = { 100 | enable = true; 101 | 102 | systemd = { 103 | enable = true; 104 | }; 105 | 106 | settings = [ 107 | { 108 | exclusive = true; 109 | position = "top"; 110 | layer = "top"; 111 | height = 18; 112 | width = if hostname == "kara" then 2560 else null; 113 | passthrough = false; 114 | gtk-layer-shell = true; 115 | 116 | modules-left = [ (if desktop == "hyprland" then "hyprland/workspaces" else "sway/workspaces") ]; 117 | modules-center = [ 118 | "clock" 119 | "idle_inhibitor" 120 | ]; 121 | modules-right = modules; 122 | 123 | "hyprland/workspaces" = workspaceConfig; 124 | "sway/workspaces" = workspaceConfig; 125 | 126 | "network" = { 127 | format-wifi = "{essid} "; 128 | format-ethernet = "{ifname} "; 129 | format-disconnected = ""; 130 | tooltip-format = "{ifname} / {essid} ({signalStrength}%) / {ipaddr}"; 131 | max-length = 15; 132 | on-click = "${pkgs.ghostty}/bin/ghostty -e ${pkgs.networkmanager}/bin/nmtui"; 133 | }; 134 | 135 | "idle_inhibitor" = { 136 | format = "{icon}"; 137 | format-icons = { 138 | activated = ""; 139 | deactivated = ""; 140 | }; 141 | }; 142 | 143 | "battery" = { 144 | states = { 145 | good = 95; 146 | warning = 20; 147 | critical = 10; 148 | }; 149 | format = "{capacity}% {icon}"; 150 | format-charging = "{capacity}% "; 151 | format-plugged = ""; 152 | tooltip-format = "{time} ({capacity}%)"; 153 | format-alt = "{time} {icon}"; 154 | format-full = ""; 155 | format-icons = [ 156 | "" 157 | "" 158 | "" 159 | "" 160 | "" 161 | ]; 162 | }; 163 | 164 | "custom/vpn" = { 165 | format = "{}"; 166 | exec = "${lib.getExe tsCheck} status"; 167 | on-click = "${lib.getExe tsCheck} toggle"; 168 | on-click-right = "${lib.getExe tsCheck} exit"; 169 | return-type = "json"; 170 | interval = 1; 171 | }; 172 | 173 | "group/group-power" = { 174 | orientation = "inherit"; 175 | drawer = { 176 | transition-duration = 500; 177 | transition-left-to-right = false; 178 | }; 179 | modules = [ 180 | "custom/power" 181 | "custom/quit" 182 | "custom/lock" 183 | "custom/reboot" 184 | ]; 185 | }; 186 | 187 | "custom/quit" = { 188 | format = "󰗼"; 189 | on-click = 190 | if desktop == "hyprland" then 191 | "${pkgs.hyprland}/bin/hyprctl dispatch exit" 192 | else 193 | "${pkgs.sway}/bin/swaymsg exit"; 194 | tooltip = false; 195 | }; 196 | 197 | "custom/lock" = { 198 | format = "󰍁"; 199 | on-click = 200 | if desktop == "hyprland" then 201 | "${pkgs.hyprlock}/bin/hyprlock" 202 | else 203 | "${pkgs.swaylock-effects}/bin/swaylock -f"; 204 | tooltip = false; 205 | }; 206 | 207 | "custom/reboot" = { 208 | format = "󰜉"; 209 | on-click = "${pkgs.systemd}/bin/systemctl reboot"; 210 | tooltip = false; 211 | }; 212 | 213 | "custom/power" = { 214 | format = ""; 215 | on-click = "${pkgs.systemd}/bin/systemctl poweroff"; 216 | tooltip = false; 217 | }; 218 | 219 | "clock" = { 220 | format = "{:%d %b %H:%M}"; 221 | }; 222 | 223 | "wireplumber" = { 224 | format = "{volume}% {icon}"; 225 | format-muted = ""; 226 | on-click = "${lib.getExe pkgs.pwvucontrol}"; 227 | format-icons = [ 228 | "" 229 | "" 230 | "" 231 | ]; 232 | tooltip-format = "{volume}% / {node_name}"; 233 | }; 234 | 235 | "pulseaudio#source" = { 236 | format = "{format_source}"; 237 | format-source = ""; 238 | format-source-muted = ""; 239 | on-click = "${lib.getExe pkgs.pwvucontrol}"; 240 | tooltip-format = "{source_volume}% / {desc}"; 241 | }; 242 | 243 | "bluetooth" = { 244 | format-on = ""; 245 | format-connected = "{device_alias} "; 246 | format-off = ""; 247 | format-disabled = ""; 248 | on-click-right = "${lib.getExe' pkgs.blueberry "blueberry"}"; 249 | on-click = "${lib.getExe bluetoothToggle}"; 250 | }; 251 | } 252 | ]; 253 | 254 | # This is a bit of a hack. Rasi turns out to be basically CSS, and there is 255 | # a handy helper to convert nix -> rasi in the home-manager module for rofi, 256 | # so I'm using that here to render the stylesheet for waybar 257 | style = 258 | toRasi 259 | (import ./theme.nix { 260 | inherit 261 | config 262 | desktop 263 | lib 264 | pkgs 265 | theme 266 | ; 267 | }).theme; 268 | }; 269 | 270 | # This is a hack to ensure that hyprctl ends up in the PATH for the waybar service on hyprland 271 | systemd.user.services.waybar.Service.Environment = lib.optional (desktop == "hyprland") ( 272 | lib.mkForce "PATH=${lib.makeBinPath [ pkgs."${desktop}" ]}" 273 | ); 274 | } 275 | -------------------------------------------------------------------------------- /home/common/desktop/waybar/lib.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | let 3 | inherit (lib) isBool isInt; 4 | inherit (builtins) 5 | toString 6 | isList 7 | isString 8 | concatStringsSep 9 | concatMap 10 | removeAttrs 11 | isAttrs 12 | ; 13 | inherit (lib.attrsets) filterAttrs; 14 | 15 | # Copied from https://github.com/nix-community/home-manager/blob/master/modules/programs/rofi.nix 16 | mkValueString = 17 | value: 18 | if isBool value then 19 | if value then "true" else "false" 20 | else if isInt value then 21 | toString value 22 | else if (value._type or "") == "literal" then 23 | value.value 24 | else if isString value then 25 | ''"${value}"'' 26 | else if isList value then 27 | "[ ${concatStringsSep "," (map mkValueString value)} ]" 28 | else 29 | abort "Unhandled value type ${builtins.typeOf value}"; 30 | 31 | mkKeyValue = 32 | { 33 | sep ? ": ", 34 | end ? ";", 35 | }: 36 | name: value: "${name}${sep}${mkValueString value}${end}"; 37 | 38 | mkRasiSection = 39 | name: value: 40 | if isAttrs value then 41 | let 42 | toRasiKeyValue = lib.generators.toKeyValue { mkKeyValue = mkKeyValue { }; }; 43 | # Remove null values so the resulting config does not have empty lines 44 | configStr = toRasiKeyValue (filterAttrs (_: v: v != null) value); 45 | in 46 | '' 47 | ${name} { 48 | ${configStr}} 49 | '' 50 | else 51 | (mkKeyValue { 52 | sep = " "; 53 | end = ""; 54 | } name value) 55 | + "\n"; 56 | in 57 | { 58 | toRasi = 59 | attrs: 60 | concatStringsSep "\n" ( 61 | concatMap (lib.attrsets.mapAttrsToList mkRasiSection) [ 62 | (filterAttrs (n: _: n == "@theme") attrs) 63 | (filterAttrs (n: _: n == "@import") attrs) 64 | (removeAttrs attrs [ 65 | "@theme" 66 | "@import" 67 | ]) 68 | ] 69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /home/common/desktop/waybar/theme.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | lib, 4 | theme, 5 | desktop, 6 | ... 7 | }: 8 | { 9 | theme = 10 | let 11 | inherit (config.lib.formats.rasi) mkLiteral; 12 | in 13 | { 14 | "*" = { 15 | border = mkLiteral "none"; 16 | padding = mkLiteral "0px"; 17 | font-family = "${theme.fonts.iconFont.name}"; 18 | font-size = mkLiteral "15px"; 19 | }; 20 | 21 | "window#waybar" = { 22 | background-color = mkLiteral "transparent"; 23 | }; 24 | 25 | "window>box" = { 26 | margin = mkLiteral "8px 8px 0px 8px"; 27 | background = mkLiteral "${theme.colours.bg}"; 28 | opacity = mkLiteral "0.8"; 29 | border-radius = mkLiteral "8px"; 30 | }; 31 | 32 | ".modules-right" = { 33 | margin-right = mkLiteral "10px"; 34 | padding = mkLiteral "5px 10px"; 35 | }; 36 | 37 | ".modules-center" = { 38 | margin = mkLiteral "0px"; 39 | padding = mkLiteral "5px 10px"; 40 | }; 41 | 42 | ".modules-left" = { 43 | margin-left = mkLiteral "10px"; 44 | padding = mkLiteral "5px 0px"; 45 | }; 46 | 47 | "#workspaces button" = { 48 | padding = if desktop == "hyprland" then mkLiteral "0px 10px" else mkLiteral "0px"; 49 | background-color = mkLiteral "transparent"; 50 | font-weight = mkLiteral "lighter"; 51 | color = mkLiteral "${theme.colours.text}"; 52 | }; 53 | 54 | "#workspaces button:hover" = { 55 | color = mkLiteral "${theme.colours.accent}"; 56 | background-color = mkLiteral "transparent"; 57 | }; 58 | 59 | "#workspaces button.focused, #workspaces button.active" = { 60 | color = mkLiteral "${theme.colours.accent}"; 61 | font-weight = mkLiteral "normal"; 62 | background-color = mkLiteral "transparent"; 63 | }; 64 | 65 | "#battery, 66 | #bluetooth, 67 | #clock, 68 | #cpu, 69 | #custom-lock, 70 | #custom-power, 71 | #custom-quit, 72 | #custom-reboot, 73 | #custom-vpn, 74 | #group-group-power, 75 | #idle_inhibitor, 76 | #memory, 77 | #network, 78 | #pulseaudio, 79 | #wireplumber" = 80 | { 81 | padding = mkLiteral "0px 10px"; 82 | color = mkLiteral "${theme.colours.text}"; 83 | }; 84 | 85 | "#custom-vpn.tailnet" = { 86 | color = mkLiteral "${theme.colours.blue}"; 87 | background-color = mkLiteral "transparent"; 88 | }; 89 | "#custom-vpn.exitNode" = { 90 | color = mkLiteral "${theme.colours.green}"; 91 | background-color = mkLiteral "transparent"; 92 | }; 93 | "#custom-vpn.disconnected" = { 94 | color = mkLiteral "${theme.colours.red}"; 95 | background-color = mkLiteral "transparent"; 96 | }; 97 | 98 | "#custom-power" = { 99 | color = mkLiteral "${theme.colours.accent}"; 100 | background-color = mkLiteral "transparent"; 101 | }; 102 | 103 | "#custom-quit, #custom-lock, #custom-reboot" = { 104 | color = mkLiteral "${theme.colours.red}"; 105 | background-color = mkLiteral "transparent"; 106 | }; 107 | 108 | # -----Indicators---- 109 | "#idle_inhibitor.activated" = { 110 | color = mkLiteral "${theme.colours.accent}"; 111 | }; 112 | 113 | "#battery.charging" = { 114 | color = mkLiteral "${theme.colours.green}"; 115 | }; 116 | 117 | "#battery.warning:not(.charging)" = { 118 | color = mkLiteral "${theme.colours.orange}"; 119 | }; 120 | 121 | "#battery.critical:not(.charging)" = { 122 | color = mkLiteral "${theme.colours.red}"; 123 | }; 124 | 125 | "#temperature.critical" = { 126 | color = mkLiteral "${theme.colours.red}"; 127 | }; 128 | 129 | "#bluetooth.off" = { 130 | color = mkLiteral "${theme.colours.orange}"; 131 | }; 132 | 133 | "#wireplumber.muted" = { 134 | color = mkLiteral "${theme.colours.orange}"; 135 | }; 136 | 137 | "#pulseaudio.source-muted" = { 138 | color = mkLiteral "${theme.colours.orange}"; 139 | }; 140 | }; 141 | } 142 | -------------------------------------------------------------------------------- /home/common/desktop/wl-common.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | imports = [ 4 | ./waybar 5 | ./mako.nix 6 | ./rofi.nix 7 | ]; 8 | 9 | services = { 10 | avizo.enable = true; 11 | 12 | cliphist = { 13 | enable = true; 14 | package = pkgs.cliphist; 15 | }; 16 | 17 | gnome-keyring.enable = true; 18 | 19 | wlsunset = { 20 | enable = true; 21 | latitude = "51.51"; 22 | longitude = "-2.53"; 23 | }; 24 | }; 25 | 26 | systemd.user.services.polkit-gnome-authentication-agent-1 = { 27 | Unit.Description = "polkit-gnome-authentication-agent-1"; 28 | Install.WantedBy = [ "graphical-session.target" ]; 29 | Service = { 30 | Type = "simple"; 31 | ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; 32 | Restart = "on-failure"; 33 | RestartSec = 1; 34 | TimeoutStopSec = 10; 35 | }; 36 | }; 37 | 38 | home.packages = with pkgs; [ 39 | playerctl 40 | wl-clipboard 41 | wdisplays 42 | ]; 43 | 44 | home.sessionVariables = { 45 | _JAVA_AWT_WM_NONREPARENTING = "1"; 46 | CLUTTER_BACKEND = "wayland"; 47 | GDK_BACKEND = "wayland"; 48 | MOZ_ENABLE_WAYLAND = "1"; 49 | QT_QPA_PLATFORM = "wayland"; 50 | # QT_QPA_PLATFORMTHEME = "qt5ct"; 51 | QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; 52 | SDL_VIDEODRIVER = "wayland"; 53 | XDG_SESSION_TYPE = "wayland"; 54 | NIXOS_OZONE_WL = "1"; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /home/common/desktop/xdg.nix: -------------------------------------------------------------------------------- 1 | _: 2 | let 3 | inherit ((import ./file-associations.nix)) associations; 4 | in 5 | { 6 | xdg = { 7 | enable = true; 8 | mimeApps = { 9 | enable = true; 10 | defaultApplications = associations; 11 | }; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /home/common/dev/base.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | home.packages = with pkgs; [ 4 | ijq 5 | 6 | # Rust tooling 7 | cargo 8 | rust-analyzer 9 | rustfmt 10 | rustc 11 | 12 | gnumake 13 | gcc 14 | 15 | nodejs 16 | nodePackages_latest.prettier 17 | 18 | spread 19 | 20 | # Container tooling 21 | dive 22 | kubectl 23 | skopeo 24 | 25 | # Go tooling 26 | go 27 | go-tools 28 | gofumpt 29 | gopls 30 | 31 | # Nix tooling 32 | deadnix 33 | nil 34 | nix-init 35 | # My custom wrapper that does nixfmt + deadnix + statix 36 | nixfmt-plus 37 | nixfmt-rfc-style 38 | nurl 39 | statix 40 | 41 | # Python tooling 42 | ruff 43 | uv 44 | (pkgs.python3.withPackages ( 45 | p: with p; [ 46 | tox 47 | virtualenv 48 | ] 49 | )) 50 | 51 | # Shell tooling 52 | shellcheck 53 | shfmt 54 | ]; 55 | } 56 | -------------------------------------------------------------------------------- /home/common/dev/charm-tools.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | home.packages = with pkgs; [ 4 | charmcraft 5 | juju 6 | juju4 7 | rockcraft 8 | snapcraft 9 | ]; 10 | } 11 | -------------------------------------------------------------------------------- /home/common/dev/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | desktop, 3 | lib, 4 | ... 5 | }: 6 | { 7 | imports = [ 8 | ./base.nix 9 | ] ++ lib.optional (builtins.isString desktop) ./desktop.nix; 10 | } 11 | -------------------------------------------------------------------------------- /home/common/dev/desktop.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | programs.vscode = { 4 | enable = true; 5 | }; 6 | 7 | home.packages = with pkgs; [ sublime-merge ]; 8 | } 9 | -------------------------------------------------------------------------------- /home/common/shell/atuin.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | programs.atuin = { 4 | enable = true; 5 | enableFishIntegration = true; 6 | package = pkgs.atuin; 7 | flags = [ "--disable-up-arrow" ]; 8 | settings = { 9 | enter_accept = false; 10 | dialect = "uk"; 11 | dotfiles = { 12 | enabled = false; 13 | }; 14 | sync = { 15 | records = true; 16 | }; 17 | }; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /home/common/shell/bat.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | catppuccin.bat.enable = true; 3 | 4 | programs = { 5 | bat = { 6 | enable = true; 7 | }; 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /home/common/shell/bottom.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | catppuccin.bottom.enable = true; 3 | 4 | programs.bottom = { 5 | enable = true; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /home/common/shell/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, self, ... }: 2 | let 3 | theme = import "${self}/lib/theme" { inherit pkgs; }; 4 | in 5 | { 6 | imports = [ 7 | ./atuin.nix 8 | ./bat.nix 9 | ./bottom.nix 10 | ./fastfetch.nix 11 | ./fzf.nix 12 | ./git.nix 13 | ./helix.nix 14 | ./starship.nix 15 | ./xdg.nix 16 | ./zellij.nix 17 | ./fish.nix 18 | ]; 19 | 20 | catppuccin = { 21 | inherit (theme.catppuccin) flavor; 22 | inherit (theme.catppuccin) accent; 23 | }; 24 | 25 | programs = { 26 | eza.enable = true; 27 | git.enable = true; 28 | gpg.enable = true; 29 | home-manager.enable = true; 30 | jq.enable = true; 31 | }; 32 | 33 | services.gpg-agent = { 34 | enable = true; 35 | pinentryPackage = pkgs.pinentry-curses; 36 | }; 37 | 38 | home.packages = with pkgs; [ 39 | age 40 | sops 41 | ]; 42 | } 43 | -------------------------------------------------------------------------------- /home/common/shell/fastfetch.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://github.com/fastfetch-cli/fastfetch/raw/dev/doc/json_schema.json", 3 | "logo": { 4 | "padding": { 5 | "top": 0, 6 | "left": 5, 7 | "right": 5 8 | } 9 | }, 10 | "display": { 11 | "separator": "", 12 | "binaryPrefix": "si", 13 | "size": { 14 | "ndigits": 0 15 | }, 16 | "keyWidth": 4 17 | }, 18 | "modules": [ 19 | "break", 20 | "break", 21 | { 22 | "type": "title", 23 | "key": "", 24 | "color": { 25 | "user": "34", 26 | "host": "34" 27 | }, 28 | "keyColor": "32" 29 | }, 30 | { 31 | "type": "os", 32 | "key": "", 33 | "format": "{3} ({12})", 34 | "keyColor": "32" 35 | }, 36 | { 37 | "type": "kernel", 38 | "key": "", 39 | "format": "{1} {2}", 40 | "keyColor": "32" 41 | }, 42 | "break", 43 | { 44 | "type": "cpu", 45 | "key": "", 46 | "keyColor": "34", 47 | "freqNdigits": 1 48 | }, 49 | { 50 | "type": "board", 51 | "key": "󱤓", 52 | "keyColor": "34" 53 | }, 54 | { 55 | "type": "gpu", 56 | "key": "󰢮", 57 | "keyColor": "34", 58 | "format": "{2}", 59 | "forceVulkan": true, 60 | "hideType": "integrated" 61 | }, 62 | { 63 | "type": "memory", 64 | "key": "", 65 | "keyColor": "34", 66 | "format": "{1} / {2}" 67 | }, 68 | "break", 69 | { 70 | "type": "wm", 71 | "key": "", 72 | "keyColor": "33" 73 | }, 74 | { 75 | "type": "theme", 76 | "key": "󰉼", 77 | "keyColor": "33" 78 | }, 79 | { 80 | "type": "terminal", 81 | "key": "", 82 | "format": "{3}", 83 | "keyColor": "33" 84 | }, 85 | { 86 | "type": "shell", 87 | "key": "$", 88 | "format": "{1} {4}", 89 | "keyColor": "33" 90 | }, 91 | { 92 | "type": "display", 93 | "key": "󰹑", 94 | "keyColor": "33", 95 | "compactType": "original" 96 | }, 97 | "break", 98 | { 99 | "type": "colors", 100 | "symbol": "circle" 101 | }, 102 | "break" 103 | ] 104 | } 105 | -------------------------------------------------------------------------------- /home/common/shell/fastfetch.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | home = { 4 | packages = with pkgs; [ fastfetch ]; 5 | 6 | file = { 7 | ".config/fastfetch/config.jsonc".text = builtins.readFile ./fastfetch.jsonc; 8 | }; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /home/common/shell/fish.nix: -------------------------------------------------------------------------------- 1 | { 2 | catppuccin.fish.enable = true; 3 | 4 | programs = { 5 | fish = { 6 | enable = true; 7 | 8 | shellInit = '' 9 | set fish_greeting "" 10 | set -gx EDITOR hx 11 | set -gx SUDO_EDITOR hx 12 | ''; 13 | 14 | shellAliases = { 15 | ls = "eza -gl --git --color=automatic"; 16 | tree = "eza --tree"; 17 | cat = "bat"; 18 | 19 | lsusb = "cyme --headings"; 20 | 21 | ip = "ip --color"; 22 | ipb = "ip --color --brief"; 23 | 24 | gac = "git add -A && git commit -a"; 25 | gp = "git push"; 26 | gst = "git status -sb"; 27 | 28 | htop = "btm -b"; 29 | neofetch = "fastfetch"; 30 | 31 | tf = "terraform"; 32 | tfi = "terraform init"; 33 | tfp = "terraform plan"; 34 | tfa = "terraform apply -auto-approve"; 35 | tfd = "terraform destroy -auto-approve"; 36 | tfo = "terraform output -json"; 37 | 38 | wgu = "sudo wg-quick up"; 39 | wgd = "sudo wg-quick down"; 40 | 41 | ts = "tailscale"; 42 | tst = "tailscale status"; 43 | tsu = "tailscale up --ssh --operator=$USER"; 44 | tsd = "tailscale down"; 45 | 46 | js = "juju status"; 47 | jsw = "juju status --watch 1s --color"; 48 | jsrw = "juju status --watch 1s --color --relations"; 49 | jdl = "juju debug-log"; 50 | 51 | open = "xdg-open"; 52 | k = "kubectl"; 53 | 54 | opget = "op item get \"$(op item list --format=json | jq -r '.[].title' | fzf)\""; 55 | 56 | speedtest = "curl -s https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py | python -"; 57 | 58 | cleanup-nix = "nh clean all --keep-since 10d --keep 3"; 59 | rln = "nh os switch /home/jon/nixos-config"; 60 | rlh = "nh home switch /home/jon/nixos-config"; 61 | rlb = "rln && rlh"; 62 | }; 63 | }; 64 | }; 65 | } 66 | -------------------------------------------------------------------------------- /home/common/shell/fzf.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | catppuccin.fzf.enable = true; 3 | 4 | programs.fzf = { 5 | enable = true; 6 | enableFishIntegration = true; 7 | 8 | changeDirWidgetOptions = [ "--preview 'tree -C {} | head -200'" ]; 9 | 10 | defaultCommand = "rg --files"; 11 | defaultOptions = [ 12 | "--height 90%" 13 | "--border" 14 | ]; 15 | 16 | fileWidgetCommand = "rg --files"; 17 | fileWidgetOptions = [ 18 | "--preview 'bat -n --color=always {}'" 19 | "--bind 'ctrl-/:change-preview-window(down|hidden|)'" 20 | ]; 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /home/common/shell/git.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | 4 | home.file.".config/git/allowed_signers".text = '' 5 | jon@sgrs.uk sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIB9bIEMgZVBCDxBWQ4m4hQP6ZZp0P3TfzjzcgUOdbYDLAAAABHNzaDo= YK5C 6 | jon@sgrs.uk sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIBC8cs1B64XqEswY5pART6yERbjUMB7RdQdT38dgkZT6AAAABHNzaDo= YK5 7 | jon.seager@canonical.com sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIB9bIEMgZVBCDxBWQ4m4hQP6ZZp0P3TfzjzcgUOdbYDLAAAABHNzaDo= YK5C 8 | jon.seager@canonical.com sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIBC8cs1B64XqEswY5pART6yERbjUMB7RdQdT38dgkZT6AAAABHNzaDo= YK5 9 | jnsgruk@ubuntu.com sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIB9bIEMgZVBCDxBWQ4m4hQP6ZZp0P3TfzjzcgUOdbYDLAAAABHNzaDo= YK5C 10 | jnsgruk@ubuntu.com sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIBC8cs1B64XqEswY5pART6yERbjUMB7RdQdT38dgkZT6AAAABHNzaDo= YK5 11 | ''; 12 | 13 | home.packages = with pkgs; [ gh ]; 14 | 15 | programs = { 16 | git = { 17 | enable = true; 18 | 19 | userEmail = "jon@sgrs.uk"; 20 | userName = "Jon Seager"; 21 | 22 | includes = [ 23 | # When the working directory is under ~/code/canonical then sign-off commits 24 | # with Canonical email address. 25 | { 26 | condition = "gitdir:~/code/canonical/"; 27 | contents.user.email = "jon.seager@canonical.com"; 28 | } 29 | # When the working directory is under ~/code/snapcrafters then sign-off commits 30 | # with Ubuntu email address. 31 | { 32 | condition = "gitdir:~/code/snapcrafters/"; 33 | contents.user.email = "jnsgruk@ubuntu.com"; 34 | } 35 | ]; 36 | 37 | aliases = { 38 | lg = "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"; 39 | }; 40 | 41 | extraConfig = { 42 | branch = { 43 | sort = "-committerdate"; 44 | }; 45 | push = { 46 | default = "matching"; 47 | }; 48 | pull = { 49 | rebase = true; 50 | }; 51 | init = { 52 | defaultBranch = "main"; 53 | }; 54 | gpg = { 55 | format = "ssh"; 56 | ssh = { 57 | defaultKeyCommand = "sh -c 'echo key::$(ssh-add -L | head -n1)'"; 58 | allowedSignersFile = "~/.config/git/allowed_signers"; 59 | }; 60 | }; 61 | commit = { 62 | gpgSign = true; 63 | }; 64 | tag = { 65 | gpgSign = true; 66 | }; 67 | }; 68 | }; 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /home/common/shell/helix.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | programs.helix = { 3 | enable = true; 4 | settings = { 5 | theme = "catppuccin_macchiato"; 6 | editor = { 7 | text-width = 100; 8 | rulers = [ 100 ]; 9 | lsp = { 10 | display-inlay-hints = true; 11 | }; 12 | }; 13 | }; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /home/common/shell/starship.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | { 3 | programs = { 4 | starship = { 5 | enable = true; 6 | enableFishIntegration = true; 7 | settings = lib.mkDefault { 8 | format = lib.concatStrings [ 9 | "$username" 10 | "$hostname" 11 | "$directory" 12 | "$git_branch" 13 | "$git_state" 14 | "$git_status" 15 | "$cmd_duration" 16 | "$line_break" 17 | "$nix_shell" 18 | "$python" 19 | "$character" 20 | ]; 21 | 22 | directory.style = "blue"; 23 | 24 | character = { 25 | success_symbol = "[❯](purple)"; 26 | error_symbol = "[❯](red)"; 27 | vimcmd_symbol = "[❮](green)"; 28 | }; 29 | 30 | git_branch = { 31 | format = "[$branch]($style)"; 32 | style = "bright-black"; 33 | }; 34 | 35 | git_status = { 36 | format = "[[(*$conflicted$untracked$modified$staged$renamed$deleted)](218) ($ahead_behind$stashed)]($style)"; 37 | style = "cyan"; 38 | conflicted = ""; 39 | untracked = ""; 40 | modified = ""; 41 | staged = ""; 42 | renamed = ""; 43 | deleted = ""; 44 | stashed = "≡"; 45 | }; 46 | 47 | git_state = { 48 | format = "\([$state( $progress_current/$progress_total)]($style)\)"; 49 | style = "bright-black"; 50 | }; 51 | 52 | cmd_duration = { 53 | format = "[$duration]($style)"; 54 | style = "yellow"; 55 | }; 56 | 57 | python = { 58 | format = "[$virtualenv]($style) "; 59 | style = "bright-black"; 60 | }; 61 | 62 | nix_shell = { 63 | format = "[$name]($style)"; 64 | style = "bright-black"; 65 | }; 66 | }; 67 | }; 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /home/common/shell/xdg.nix: -------------------------------------------------------------------------------- 1 | { config, lib, ... }: 2 | { 3 | xdg = { 4 | enable = true; 5 | 6 | configHome = config.home.homeDirectory + "/.config"; 7 | cacheHome = config.home.homeDirectory + "/.local/cache"; 8 | dataHome = config.home.homeDirectory + "/.local/share"; 9 | stateHome = config.home.homeDirectory + "/.local/state"; 10 | 11 | userDirs = { 12 | enable = true; 13 | createDirectories = lib.mkDefault true; 14 | 15 | download = config.home.homeDirectory + "/downloads"; 16 | pictures = config.home.homeDirectory + "/pictures"; 17 | 18 | desktop = config.home.homeDirectory; 19 | documents = config.home.homeDirectory; 20 | music = config.home.homeDirectory; 21 | publicShare = config.home.homeDirectory; 22 | templates = config.home.homeDirectory; 23 | videos = config.home.homeDirectory; 24 | 25 | extraConfig = { 26 | XDG_SCREENSHOTS_DIR = "${config.home.homeDirectory}/pictures/screenshots"; 27 | }; 28 | }; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /home/common/shell/zellij.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | programs.zellij = { 3 | enable = true; 4 | settings = { 5 | theme = "catppuccin-macchiato"; 6 | }; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /home/common/users/jon/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs, 3 | lib, 4 | hostname, 5 | ... 6 | }: 7 | { 8 | imports = [ 9 | inputs.vscode-server.nixosModules.home 10 | ] ++ lib.optional (builtins.pathExists (./. + "/${hostname}.nix")) ./${hostname}.nix; 11 | 12 | services = { 13 | # Following install, you may need to run: 14 | # systemctl --user enable auto-fix-vscode-server.service 15 | # systemctl --user start auto-fix-vscode-server.service 16 | vscode-server.enable = true; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /home/common/users/jon/freyja.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | self, 4 | ... 5 | }: 6 | let 7 | # This isn't a NixOS machine, so the normal host-level packages aren't 8 | # automatically available. 9 | inherit ((import "${self}/host/common/base/packages.nix" { inherit pkgs; })) basePackages; 10 | in 11 | { 12 | targets.genericLinux.enable = true; 13 | 14 | home.packages = 15 | basePackages 16 | # Add additional packages which would usually be added at the NixOS 17 | # host level. 18 | ++ (with pkgs; [ 19 | _1password-gui 20 | nh 21 | yubikey-manager 22 | 23 | (nerdfonts.override { fonts = [ "Meslo" ]; }) 24 | inter 25 | ]); 26 | 27 | # Work around default Ubuntu config in /etc/X11/Xsession.d/90gpg-agent. 28 | # programs.zsh.envExtra = '' 29 | # SSH_AUTH_SOCK=$XDG_RUNTIME_DIR/ssh-agent 30 | # ''; 31 | 32 | services = { 33 | ssh-agent.enable = true; 34 | syncthing = { 35 | enable = true; 36 | extraOptions = [ 37 | "-gui-address=freyja.tailnet-d5da.ts.net:8384" 38 | "-home=/home/jon/data/.syncthing" 39 | ]; 40 | }; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /home/common/users/jon/kara.nix: -------------------------------------------------------------------------------- 1 | { self, ... }: 2 | { 3 | imports = [ 4 | "${self}/home/common/dev/charm-tools.nix" 5 | ]; 6 | 7 | services.syncthing = { 8 | enable = true; 9 | extraOptions = [ 10 | "-gui-address=kara.tailnet-d5da.ts.net:8384" 11 | "-home=/home/jon/data/.syncthing" 12 | ]; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /home/common/users/jon/thor.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | services.syncthing = { 3 | enable = true; 4 | extraOptions = [ 5 | "-gui-address=thor.tailnet-d5da.ts.net:8384" 6 | "-home=/data/apps/syncthing" 7 | ]; 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /home/common/users/ubuntu/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, self, ... }: 2 | let 3 | inherit ((import "${self}/host/common/base/packages.nix" { inherit pkgs; })) basePackages; 4 | in 5 | { 6 | imports = [ "${self}/home/common/dev/base.nix" ]; 7 | home.packages = basePackages; 8 | } 9 | -------------------------------------------------------------------------------- /home/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | desktop, 4 | lib, 5 | outputs, 6 | stateVersion, 7 | username, 8 | inputs, 9 | ... 10 | }: 11 | { 12 | # Only import desktop configuration if the host is desktop enabled 13 | # Only import user specific configuration if they have bespoke settings 14 | imports = 15 | [ 16 | # If you want to use modules your own flake exports (from modules/home-manager): 17 | # outputs.homeManagerModules.example 18 | 19 | # Or modules exported from other flakes (such as nix-colors): 20 | # inputs.nix-colors.homeManagerModules.default 21 | 22 | ./common/shell 23 | ] 24 | ++ lib.optional (builtins.isString desktop) ./common/desktop 25 | ++ lib.optional (builtins.pathExists ( 26 | ./. + "/common/users/${username}" 27 | )) ./common/users/${username}; 28 | 29 | home = { 30 | inherit username stateVersion; 31 | homeDirectory = "/home/${username}"; 32 | }; 33 | 34 | nixpkgs = { 35 | overlays = [ 36 | # Add overlays your own flake exports (from overlays and pkgs dir): 37 | outputs.overlays.additions 38 | outputs.overlays.modifications 39 | outputs.overlays.unstable-packages 40 | 41 | # You can also add overlays exported from other flakes: 42 | inputs.agenix.overlays.default 43 | ]; 44 | 45 | config = { 46 | # Disable if you don't want unfree packages 47 | allowUnfree = true; 48 | # Workaround for https://github.com/nix-community/home-manager/issues/2942 49 | allowUnfreePredicate = _: true; 50 | permittedInsecurePackages = [ "electron-25.9.0" ]; 51 | }; 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /host/common/base/boot.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | boot = { 3 | initrd.systemd.enable = true; 4 | 5 | kernel.sysctl = { 6 | "net.ipv4.ip_forward" = 1; 7 | "net.ipv6.conf.all.forwarding" = 1; 8 | }; 9 | 10 | loader = { 11 | systemd-boot.enable = true; 12 | efi.canTouchEfiVariables = true; 13 | }; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /host/common/base/console.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | ... 4 | }: 5 | { 6 | boot = { 7 | # Catppuccin theme 8 | kernelParams = [ 9 | "vt.default_red=30,243,166,249,137,245,148,186,88,243,166,249,137,245,148,166" 10 | "vt.default_grn=30,139,227,226,180,194,226,194,91,139,227,226,180,194,226,173" 11 | "vt.default_blu=46,168,161,175,250,231,213,222,112,168,161,175,250,231,213,200" 12 | ]; 13 | }; 14 | 15 | console = { 16 | earlySetup = true; 17 | packages = with pkgs; [ 18 | terminus_font 19 | powerline-fonts 20 | ]; 21 | font = "ter-powerline-v32n"; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /host/common/base/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | hostname, 3 | pkgs, 4 | lib, 5 | username, 6 | ... 7 | }: 8 | { 9 | imports = [ 10 | ./boot.nix 11 | ./console.nix 12 | ./hardware.nix 13 | ./locale.nix 14 | ./nh.nix 15 | ./zramswap.nix 16 | 17 | ../services/avahi.nix 18 | ../services/firewall.nix 19 | ../services/openssh.nix 20 | ../services/tailscale.nix 21 | ]; 22 | 23 | networking = { 24 | hostName = hostname; 25 | useDHCP = lib.mkDefault true; 26 | }; 27 | 28 | environment.systemPackages = (import ./packages.nix { inherit pkgs; }).basePackages; 29 | 30 | programs = { 31 | fish.enable = true; 32 | _1password.enable = true; 33 | }; 34 | 35 | services = { 36 | chrony.enable = true; 37 | journald.extraConfig = "SystemMaxUse=250M"; 38 | }; 39 | 40 | security = { 41 | polkit.enable = true; 42 | rtkit.enable = true; 43 | }; 44 | 45 | # Create dirs for home-manager 46 | systemd.tmpfiles.rules = [ "d /nix/var/nix/profiles/per-user/${username} 0755 ${username} root" ]; 47 | } 48 | -------------------------------------------------------------------------------- /host/common/base/hardware.nix: -------------------------------------------------------------------------------- 1 | _: { hardware.enableRedistributableFirmware = true; } 2 | -------------------------------------------------------------------------------- /host/common/base/locale.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | time.timeZone = "Europe/London"; 3 | 4 | console.keyMap = "uk"; 5 | services.xserver.xkb.layout = "gb"; 6 | 7 | i18n = { 8 | defaultLocale = "en_GB.utf8"; 9 | extraLocaleSettings = { 10 | LC_ADDRESS = "en_GB.utf8"; 11 | LC_IDENTIFICATION = "en_GB.utf8"; 12 | LC_MEASUREMENT = "en_GB.utf8"; 13 | LC_MONETARY = "en_GB.utf8"; 14 | LC_NAME = "en_GB.utf8"; 15 | LC_NUMERIC = "en_GB.utf8"; 16 | LC_PAPER = "en_GB.utf8"; 17 | LC_TELEPHONE = "en_GB.utf8"; 18 | LC_TIME = "en_GB.utf8"; 19 | }; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /host/common/base/nh.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | programs.nh = { 4 | enable = true; 5 | package = pkgs.nh; 6 | flake = "/home/jon/nixos-config"; 7 | clean = { 8 | enable = true; 9 | extraArgs = "--keep-since 10d --keep 3"; 10 | }; 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /host/common/base/packages.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | basePackages = with pkgs; [ 4 | _1password-cli 5 | agenix 6 | bat 7 | binutils 8 | curl 9 | cyme 10 | dig 11 | dua 12 | duf 13 | eza 14 | fd 15 | file 16 | git 17 | jq 18 | killall 19 | nfs-utils 20 | ntfs3g 21 | pciutils 22 | ripgrep 23 | rsync 24 | tpm2-tss 25 | traceroute 26 | tree 27 | unzip 28 | usbutils 29 | vim 30 | wget 31 | yq-go 32 | ]; 33 | } 34 | -------------------------------------------------------------------------------- /host/common/base/zramswap.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | # Balance lz4 latency/throughput 3 | boot.kernel.sysctl."vm.page-cluster" = 1; 4 | 5 | # Hibernate and hybrid-sleep won't work correctly without 6 | # an on-disk swap. 7 | systemd.targets.hibernate.enable = false; 8 | systemd.targets.hybrid-sleep.enable = false; 9 | 10 | # Enable zramSwap 11 | zramSwap = { 12 | algorithm = "lz4"; 13 | enable = true; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /host/common/desktop/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | desktop, 3 | pkgs, 4 | self, 5 | ... 6 | }: 7 | let 8 | theme = import "${self}/lib/theme" { inherit pkgs; }; 9 | in 10 | { 11 | imports = [ 12 | (./. + "/${desktop}.nix") 13 | ../hardware/yubikey.nix 14 | ../services/pipewire.nix 15 | ../virt 16 | ]; 17 | 18 | # Enable Plymouth and surpress some logs by default. 19 | boot.plymouth.enable = true; 20 | boot.kernelParams = [ 21 | # The 'splash' arg is included by the plymouth option 22 | "quiet" 23 | "loglevel=3" 24 | "rd.udev.log_priority=3" 25 | "vt.global_cursor_default=0" 26 | ]; 27 | 28 | hardware.graphics.enable = true; 29 | 30 | # Enable location services 31 | location.provider = "geoclue2"; 32 | 33 | programs = { 34 | _1password-gui = { 35 | enable = true; 36 | polkitPolicyOwners = [ "jon" ]; 37 | }; 38 | }; 39 | 40 | fonts = { 41 | packages = with pkgs; [ 42 | liberation_ttf 43 | ubuntu_font_family 44 | 45 | theme.fonts.default.package 46 | theme.fonts.emoji.package 47 | theme.fonts.iconFont.package 48 | theme.fonts.monospace.package 49 | ]; 50 | 51 | # Use fonts specified by user rather than default ones 52 | enableDefaultPackages = false; 53 | 54 | fontconfig = { 55 | enable = true; 56 | defaultFonts = { 57 | serif = [ 58 | "${theme.fonts.default.name}" 59 | "${theme.fonts.emoji.name}" 60 | ]; 61 | sansSerif = [ 62 | "${theme.fonts.default.name}" 63 | "${theme.fonts.emoji.name}" 64 | ]; 65 | monospace = [ "${theme.fonts.monospace.name}" ]; 66 | emoji = [ "${theme.fonts.emoji.name}" ]; 67 | }; 68 | }; 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /host/common/desktop/hyprland.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | lib, 4 | self, 5 | ... 6 | }: 7 | let 8 | theme = import "${self}/lib/theme" { inherit pkgs; }; 9 | hypr-run = pkgs.writeShellScriptBin "hypr-run" '' 10 | export XDG_SESSION_TYPE="wayland" 11 | export XDG_SESSION_DESKTOP="Hyprland" 12 | export XDG_CURRENT_DESKTOP="Hyprland" 13 | export GTK_THEME=${theme.gtkTheme.name} 14 | 15 | systemd-run --user --scope --collect --quiet --unit="hyprland" \ 16 | systemd-cat --identifier="hyprland" ${pkgs.hyprland}/bin/Hyprland $@ 17 | 18 | ${pkgs.hyprland}/bin/hyprctl dispatch exit 19 | ''; 20 | in 21 | { 22 | imports = [ ./tiling-common.nix ]; 23 | 24 | programs.hyprland.enable = true; 25 | 26 | xdg.portal.extraPortals = with pkgs; [ xdg-desktop-portal-gtk ]; 27 | 28 | services.greetd.settings.default_session.command = '' 29 | ${lib.makeBinPath [ pkgs.greetd.tuigreet ]}/tuigreet -r --asterisks --time \ 30 | --cmd ${lib.getExe hypr-run} 31 | ''; 32 | } 33 | -------------------------------------------------------------------------------- /host/common/desktop/sway.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | lib, 4 | self, 5 | ... 6 | }: 7 | let 8 | theme = import "${self}/lib/theme" { inherit pkgs; }; 9 | sway-run = pkgs.writeShellScriptBin "sway-run" '' 10 | export XDG_SESSION_TYPE="wayland" 11 | export XDG_SESSION_DESKTOP="sway" 12 | export XDG_CURRENT_DESKTOP="sway" 13 | export GTK_THEME=${theme.gtkTheme.name} 14 | 15 | systemd-run --user --scope --collect --quiet --unit="sway" \ 16 | systemd-cat --identifier="sway" ${pkgs.sway}/bin/sway $@ 17 | 18 | ${pkgs.sway}/bin/swaymsg exit 19 | ''; 20 | in 21 | { 22 | imports = [ ./tiling-common.nix ]; 23 | 24 | programs.sway = { 25 | enable = true; 26 | wrapperFeatures.gtk = true; 27 | }; 28 | 29 | services.greetd.settings.default_session.command = '' 30 | ${lib.makeBinPath [ pkgs.greetd.tuigreet ]}/tuigreet -r --asterisks --time \ 31 | --cmd ${lib.getExe sway-run} 32 | ''; 33 | } 34 | -------------------------------------------------------------------------------- /host/common/desktop/tiling-common.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | programs = { 4 | dconf.enable = true; 5 | file-roller.enable = true; 6 | }; 7 | 8 | environment = { 9 | variables.NIXOS_OZONE_WL = "1"; 10 | 11 | systemPackages = with pkgs; [ 12 | nautilus 13 | zenity 14 | # Enable HEIC image previews in Nautilus 15 | libheif 16 | libheif.out 17 | polkit_gnome 18 | ]; 19 | 20 | # Enable HEIC image previews in Nautilus 21 | pathsToLink = [ "share/thumbnailers" ]; 22 | }; 23 | 24 | services = { 25 | dbus = { 26 | enable = true; 27 | implementation = "broker"; 28 | }; 29 | 30 | gnome = { 31 | gnome-keyring.enable = true; 32 | sushi.enable = true; 33 | }; 34 | 35 | greetd = { 36 | enable = true; 37 | restart = false; 38 | }; 39 | 40 | gvfs.enable = true; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /host/common/hardware/bluetooth.nix: -------------------------------------------------------------------------------- 1 | { pkgs, desktop, ... }: 2 | { 3 | hardware.bluetooth = { 4 | enable = true; 5 | package = pkgs.bluez; 6 | settings = { 7 | General = { 8 | Experimental = true; 9 | KernelExperimental = true; 10 | }; 11 | }; 12 | }; 13 | 14 | environment.systemPackages = if (builtins.isString desktop) then [ pkgs.blueberry ] else [ ]; 15 | } 16 | -------------------------------------------------------------------------------- /host/common/hardware/ledger.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | # Enable the udev rules for Ledger devices 4 | hardware.ledger.enable = true; 5 | 6 | # Install the ledger live desktop app globally 7 | environment.systemPackages = with pkgs; [ ledger-live-desktop ]; 8 | 9 | # Needed for Ledger Live app 10 | users.groups.plugdev = { }; 11 | } 12 | -------------------------------------------------------------------------------- /host/common/hardware/yubihsm.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | environment.systemPackages = with pkgs; [ 4 | yubihsm-connector 5 | yubihsm-shell 6 | ]; 7 | } 8 | -------------------------------------------------------------------------------- /host/common/hardware/yubikey.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | environment.systemPackages = with pkgs; [ yubikey-manager ]; 4 | 5 | services = { 6 | pcscd.enable = true; 7 | udev.packages = [ pkgs.yubikey-personalization ]; 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /host/common/services/avahi.nix: -------------------------------------------------------------------------------- 1 | { desktop, ... }: 2 | { 3 | services = { 4 | avahi = { 5 | enable = true; 6 | openFirewall = true; 7 | nssmdns4 = true; 8 | publish = { 9 | enable = true; 10 | addresses = true; 11 | workstation = if (builtins.isString desktop) then true else false; 12 | }; 13 | }; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /host/common/services/backup/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | hostname, 4 | lib, 5 | self, 6 | ... 7 | }: 8 | { 9 | imports = lib.optional (builtins.pathExists (./. + "/${hostname}.nix")) ./${hostname}.nix; 10 | 11 | age.secrets = { 12 | borgbase-ssh = { 13 | file = "${self}/secrets/${hostname}-borgbase-ssh.age"; 14 | owner = "root"; 15 | group = "root"; 16 | mode = "400"; 17 | }; 18 | borgbase-passphrase = { 19 | file = "${self}/secrets/${hostname}-borgbase-passphrase.age"; 20 | owner = "root"; 21 | group = "root"; 22 | mode = "400"; 23 | }; 24 | }; 25 | 26 | # See individual host config files for backup/restore instructions. 27 | services.borgbackup.jobs."borgbase" = { 28 | encryption = { 29 | mode = "repokey-blake2"; 30 | passCommand = "cat ${config.age.secrets."borgbase-passphrase".path}"; 31 | }; 32 | environment.BORG_RSH = "ssh -i ${config.age.secrets."borgbase-ssh".path}"; 33 | compression = "auto,lzma"; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /host/common/services/backup/kara.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | 3 | # In order to mount the backup to restore files, perform the following: 4 | # 5 | # mkdir backup 6 | # sudo borg-job-borgbase mount z2sqv4mw@z2sqv4mw.repo.borgbase.com:repo ./backup 7 | # 8 | # Then copy out the files you need using normal Linux commands. Once complete, unmount 9 | # with: 10 | # 11 | # borg-job-borgbase umount backup 12 | services.borgbackup.jobs."borgbase" = { 13 | paths = [ "/home/jon/data" ]; 14 | exclude = [ 15 | "**/node_modules" 16 | "**/build" 17 | "**/dist" 18 | "**/.tox" 19 | "**/.venv" 20 | "**/venv" 21 | "**/target" 22 | "/home/jon/downloads" 23 | "/home/jon/data/downloads" 24 | "/home/jon/data/temp" 25 | "/home/jon/go" 26 | "/home/jon/sdk" 27 | ]; 28 | repo = "z2sqv4mw@z2sqv4mw.repo.borgbase.com:repo"; 29 | startAt = "*-*-* 12:00:00"; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /host/common/services/backup/thor.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | # In order to mount the backup to restore files, perform the following: 3 | # 4 | # mkdir backup 5 | # sudo borg-job-borgbase mount ike67cye@ike67cye.repo.borgbase.com:repo ./backup 6 | # 7 | # Then copy out the files you need using normal Linux commands. Once complete, unmount 8 | # with: 9 | # 10 | # borg-job-borgbase umount backup 11 | services.borgbackup.jobs."borgbase" = { 12 | paths = [ "/data" ]; 13 | exclude = [ 14 | "/data/apps/files/_files/cache" 15 | "/data/lost+found" 16 | "/data/media" 17 | ]; 18 | repo = "ike67cye@ike67cye.repo.borgbase.com:repo"; 19 | startAt = "daily"; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /host/common/services/files.nix: -------------------------------------------------------------------------------- 1 | { pkgs, config, ... }: 2 | { 3 | # Set up for https://www.files.gallery 4 | services = { 5 | nginx = { 6 | enable = true; 7 | group = "users"; 8 | defaultHTTPListenPort = 8081; 9 | virtualHosts."_" = { 10 | root = "/data/apps/files"; 11 | locations."/".extraConfig = '' 12 | fastcgi_pass unix:${config.services.phpfpm.pools.nginx-pool.socket}; 13 | fastcgi_index index.php; 14 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 15 | include ${pkgs.nginx}/conf/fastcgi_params; 16 | include ${pkgs.nginx}/conf/fastcgi.conf; 17 | ''; 18 | }; 19 | }; 20 | phpfpm.pools.nginx-pool = { 21 | inherit (config.services.nginx) user; 22 | group = "users"; 23 | settings = { 24 | pm = "dynamic"; 25 | "listen.owner" = config.services.nginx.user; 26 | "pm.max_children" = 5; 27 | "pm.start_servers" = 2; 28 | "pm.min_spare_servers" = 1; 29 | "pm.max_spare_servers" = 3; 30 | "pm.max_requests" = 500; 31 | }; 32 | phpOptions = '' 33 | memory_limit=512M 34 | ''; 35 | }; 36 | }; 37 | 38 | environment.systemPackages = with pkgs; [ ffmpeg ]; 39 | } 40 | -------------------------------------------------------------------------------- /host/common/services/firewall.nix: -------------------------------------------------------------------------------- 1 | { lib, hostname, ... }: 2 | let 3 | # Firewall configuration variable for syncthing 4 | syncthing = { 5 | hosts = [ 6 | "freyja" 7 | "kara" 8 | "thor" 9 | ]; 10 | tcpPorts = [ 22000 ]; 11 | udpPorts = [ 12 | 22000 13 | 21027 14 | ]; 15 | }; 16 | in 17 | { 18 | networking = { 19 | firewall = { 20 | enable = true; 21 | allowedTCPPorts = lib.optionals (builtins.elem hostname syncthing.hosts) syncthing.tcpPorts; 22 | allowedUDPPorts = lib.optionals (builtins.elem hostname syncthing.hosts) syncthing.udpPorts; 23 | }; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /host/common/services/home-assistant.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | services.home-assistant = { 4 | enable = true; 5 | openFirewall = true; 6 | package = pkgs.unstable.home-assistant; 7 | extraComponents = [ 8 | "apple_tv" 9 | "brother" 10 | "default_config" 11 | "esphome" 12 | "ipp" 13 | "met" 14 | "otbr" 15 | "ring" 16 | "roomba" 17 | "snmp" 18 | "sonos" 19 | "touchline_sl" 20 | "tplink" 21 | "tplink_tapo" 22 | "unifi" 23 | "unifiprotect" 24 | "upnp" 25 | "webostv" 26 | ]; 27 | customComponents = [ 28 | pkgs.unstable.home-assistant-custom-components.solis-sensor 29 | ]; 30 | config = { 31 | default_config = { }; 32 | http = { 33 | trusted_proxies = [ 34 | "::1" 35 | "127.0.0.1" 36 | ]; 37 | use_x_forwarded_for = true; 38 | }; 39 | }; 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /host/common/services/immich.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | services.immich = { 4 | enable = true; 5 | package = pkgs.immich; 6 | openFirewall = true; 7 | port = 3001; 8 | 9 | redis = { 10 | enable = true; 11 | host = "127.0.0.1"; 12 | port = 6379; 13 | }; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /host/common/services/libations.nix: -------------------------------------------------------------------------------- 1 | { config, self, ... }: 2 | { 3 | age.secrets = { 4 | libations-auth-key = { 5 | file = "${self}/secrets/thor-libations-tskey.age"; 6 | owner = "root"; 7 | group = "root"; 8 | mode = "400"; 9 | }; 10 | 11 | libations-recipes = { 12 | file = "${self}/secrets/thor-libations-recipes.age"; 13 | owner = "root"; 14 | group = "root"; 15 | mode = "444"; 16 | }; 17 | }; 18 | 19 | services.libations = { 20 | enable = true; 21 | recipesFile = config.age.secrets.libations-recipes.path; 22 | tailscaleKeyFile = config.age.secrets.libations-auth-key.path; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /host/common/services/networkmanager.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | networking = { 3 | networkmanager = { 4 | enable = true; 5 | wifi = { 6 | backend = "iwd"; 7 | }; 8 | }; 9 | }; 10 | 11 | # Workaround https://github.com/NixOS/nixpkgs/issues/180175 12 | systemd.services.NetworkManager-wait-online.enable = false; 13 | } 14 | -------------------------------------------------------------------------------- /host/common/services/nfs/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | hostname, 3 | lib, 4 | ... 5 | }: 6 | { 7 | imports = lib.optional (builtins.pathExists (./. + "/${hostname}.nix")) ./${hostname}.nix; 8 | 9 | services.nfs.server.enable = true; 10 | 11 | networking = { 12 | firewall = { 13 | allowedTCPPorts = [ 14 | 111 15 | 2049 16 | 4000 17 | 4001 18 | 4002 19 | 20048 20 | ]; 21 | allowedUDPPorts = [ 22 | 111 23 | 2049 24 | 4000 25 | 4001 26 | 4002 27 | 20048 28 | ]; 29 | }; 30 | 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /host/common/services/nfs/thor.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | services.nfs.server.exports = '' 3 | /data 100.64.0.0/10 (rw,fsid=0,no_subtree_check) 4 | ''; 5 | } 6 | -------------------------------------------------------------------------------- /host/common/services/openssh.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | services.openssh = { 3 | enable = true; 4 | openFirewall = true; 5 | settings = { 6 | PasswordAuthentication = false; 7 | PermitRootLogin = "no"; 8 | }; 9 | }; 10 | 11 | programs.ssh.startAgent = true; 12 | } 13 | -------------------------------------------------------------------------------- /host/common/services/photo-backup/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | lib, 4 | config, 5 | self, 6 | ... 7 | }: 8 | let 9 | photo-backup = pkgs.writeShellApplication { 10 | name = "photo-backup"; 11 | runtimeInputs = with pkgs; [ 12 | bash 13 | curl 14 | gphotos-sync 15 | unstable.icloudpd 16 | urlencode 17 | ]; 18 | text = builtins.readFile ./photo-backup.sh; 19 | }; 20 | in 21 | { 22 | age.secrets = { 23 | backup-env = { 24 | file = "${self}/secrets/thor-backup-env.age"; 25 | owner = "root"; 26 | group = "root"; 27 | mode = "400"; 28 | }; 29 | }; 30 | 31 | environment.systemPackages = [ photo-backup ]; 32 | 33 | systemd = { 34 | services.photo-backup = { 35 | description = "Photo Backup Service"; 36 | after = [ "network.target" ]; 37 | serviceConfig = { 38 | Type = "oneshot"; 39 | ExecStart = "${lib.getExe photo-backup}"; 40 | EnvironmentFile = config.age.secrets."backup-env".path; 41 | }; 42 | startAt = "*-*-* 21:00:00"; 43 | }; 44 | 45 | timers.photo-backup.timerConfig.Persistent = true; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /host/common/services/photo-backup/photo-backup.sh: -------------------------------------------------------------------------------- 1 | PHOTOS_ROOT="/data/photos" 2 | 3 | handle_failure() { 4 | local name; name="$1" 5 | 6 | echo "Failed to backup photos for $name; See '${PHOTOS_ROOT}/${name}/backup.log'" 7 | 8 | msg="$(mktemp)" 9 | echo "Failed to backup photos for $name:" > "$msg" 10 | echo "" >> "$msg" 11 | cat "${PHOTOS_ROOT}/${name}/backup.log" >> "$msg" 12 | 13 | curl -s -X POST \ 14 | -H 'Content-Type: application/json' \ 15 | -d "{\"chat_id\": \"${TG_CHAT}\", \"text\": \"$(cat "$msg")\"}" \ 16 | "https://api.telegram.org/${TG_TOKEN}/sendMessage" >/dev/null 17 | 18 | rm "$msg" 19 | } 20 | 21 | backup_gphotos() { 22 | local name; name="$1" 23 | 24 | echo "Backing up Google Photos for $name" 25 | 26 | gphotos-sync \ 27 | --secret "${PHOTOS_ROOT}/${name}/client_secret.json" \ 28 | "${PHOTOS_ROOT}/${name}" \ 29 | > "${PHOTOS_ROOT}/${name}/backup.log" \ 30 | || handle_failure "$name" 31 | } 32 | 33 | backup_icloud() { 34 | local name; name="$1" 35 | local user; user="$2" 36 | local pass; pass="$3" 37 | 38 | echo "Backing up iCloud Photos for $name" 39 | 40 | icloudpd \ 41 | --log-level info \ 42 | --directory "${PHOTOS_ROOT}/${name}" \ 43 | --cookie-directory "${PHOTOS_ROOT}/.icloudpd"\ 44 | --folder-structure "{:%Y/%m-%b}" \ 45 | --username "$user" --password "$pass" \ 46 | --size original \ 47 | > "${PHOTOS_ROOT}/${name}/backup.log" \ 48 | || handle_failure "$name" 49 | 50 | icloudpd \ 51 | --log-level info \ 52 | --directory "${PHOTOS_ROOT}/${name}" \ 53 | --cookie-directory "${PHOTOS_ROOT}/.icloudpd"\ 54 | --folder-structure "{:%Y/%m-%b}" \ 55 | --username "$user" --password "$pass" \ 56 | --size original \ 57 | --album "Hidden" \ 58 | >> "${PHOTOS_ROOT}/${name}/backup.log" \ 59 | || handle_failure "$name" 60 | 61 | icloudpd \ 62 | --log-level info \ 63 | --directory "${PHOTOS_ROOT}/${name}" \ 64 | --cookie-directory "${PHOTOS_ROOT}/.icloudpd"\ 65 | --folder-structure "{:%Y/%m-%b}" \ 66 | --username "$user" --password "$pass" \ 67 | --size original \ 68 | --album "Recently Deleted" \ 69 | >> "${PHOTOS_ROOT}/${name}/backup.log" \ 70 | || handle_failure "$name" 71 | } 72 | 73 | auth_icloud() { 74 | local user; user="$1" 75 | local pass; pass="$2" 76 | 77 | icloudpd --auth-only \ 78 | --cookie-directory "${PHOTOS_ROOT}/.icloudpd" \ 79 | --username "$user" --password "$pass" 80 | } 81 | 82 | if [[ "${1:-}" == "auth" ]]; then 83 | echo "Authenticating Nina" 84 | auth_icloud "$NINA_EMAIL" "$NINA_PASSWORD" 85 | echo "Authenticating Rich" 86 | auth_icloud "$RICH_EMAIL" "$RICH_PASSWORD" 87 | echo "Authenticating Laura" 88 | auth_icloud "$LAURA_EMAIL" "$LAURA_PASSWORD" 89 | echo "Authenticating Jon" 90 | auth_icloud "$JON_EMAIL" "$JON_PASSWORD" 91 | else 92 | backup_gphotos hattie 93 | backup_gphotos tom 94 | backup_icloud nina "$NINA_EMAIL" "$NINA_PASSWORD" 95 | backup_icloud rich "$RICH_EMAIL" "$RICH_PASSWORD" 96 | backup_icloud laura "$LAURA_EMAIL" "$LAURA_PASSWORD" 97 | backup_icloud jon "$JON_EMAIL" "$JON_PASSWORD" 98 | fi 99 | 100 | chown -R jon:users /data/photos -------------------------------------------------------------------------------- /host/common/services/pipewire.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | pkgs, 4 | desktop, 5 | ... 6 | }: 7 | { 8 | hardware.pulseaudio.enable = lib.mkForce false; 9 | 10 | services.pipewire = { 11 | enable = true; 12 | alsa.enable = true; 13 | pulse.enable = true; 14 | wireplumber.enable = true; 15 | }; 16 | 17 | environment.systemPackages = if (builtins.isString desktop) then [ pkgs.pwvucontrol ] else [ ]; 18 | } 19 | -------------------------------------------------------------------------------- /host/common/services/reverse-proxy/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | hostname, 4 | lib, 5 | ... 6 | }: 7 | { 8 | imports = lib.optional (builtins.pathExists (./. + "/${hostname}.nix")) ./${hostname}.nix; 9 | 10 | services.caddy = { 11 | enable = true; 12 | package = pkgs.custom-caddy; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /host/common/services/reverse-proxy/thor.nix: -------------------------------------------------------------------------------- 1 | { config, self, ... }: 2 | let 3 | tailnet = "tailnet-d5da.ts.net"; 4 | domain = "jnsgr.uk"; 5 | 6 | mkVHost = backend: '' 7 | tls { 8 | dns digitalocean {$DO_AUTH_TOKEN} 9 | } 10 | reverse_proxy ${backend} 11 | ''; 12 | in 13 | { 14 | age.secrets.digitalocean = { 15 | file = "${self}/secrets/thor-digitalocean.age"; 16 | owner = "caddy"; 17 | group = "caddy"; 18 | mode = "600"; 19 | }; 20 | 21 | # Ensure DigitalOcean token is in Caddy's environment 22 | systemd.services.caddy.serviceConfig.EnvironmentFile = config.age.secrets.digitalocean.path; 23 | 24 | services = { 25 | # Enable caddy to talk to the tailscale daemon for certs 26 | tailscale.permitCertUid = "caddy"; 27 | 28 | caddy.virtualHosts = { 29 | "files.${domain}".extraConfig = mkVHost "http://localhost:8081"; 30 | "ha.${domain}".extraConfig = mkVHost "http://localhost:8123"; 31 | "photos.${domain}".extraConfig = mkVHost "http://localhost:3001"; 32 | "freyja.sync.${domain}".extraConfig = mkVHost "http://freyja.${tailnet}:8384"; 33 | "kara.sync.${domain}".extraConfig = mkVHost "http://kara.${tailnet}:8384"; 34 | "thor.sync.${domain}".extraConfig = mkVHost "http://thor.${tailnet}:8384"; 35 | }; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /host/common/services/tailscale.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | services.tailscale = { 3 | enable = true; 4 | useRoutingFeatures = "both"; 5 | openFirewall = true; 6 | }; 7 | 8 | networking.firewall.trustedInterfaces = [ "tailscale0" ]; 9 | } 10 | -------------------------------------------------------------------------------- /host/common/users/jon/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs, config, ... }: 2 | let 3 | ifExists = groups: builtins.filter (group: builtins.hasAttr group config.users.groups) groups; 4 | in 5 | { 6 | users.users.jon = { 7 | isNormalUser = true; 8 | shell = pkgs.fish; 9 | extraGroups = 10 | [ 11 | "audio" 12 | "networkmanager" 13 | "users" 14 | "video" 15 | "wheel" 16 | ] 17 | ++ ifExists [ 18 | "docker" 19 | "plugdev" 20 | "render" 21 | "lxd" 22 | ]; 23 | 24 | openssh.authorizedKeys.keys = [ 25 | "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIB9bIEMgZVBCDxBWQ4m4hQP6ZZp0P3TfzjzcgUOdbYDLAAAABHNzaDo= YK5C" 26 | "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIBC8cs1B64XqEswY5pART6yERbjUMB7RdQdT38dgkZT6AAAABHNzaDo= YK5" 27 | ]; 28 | 29 | packages = [ pkgs.home-manager ]; 30 | }; 31 | 32 | # This is a workaround for not seemingly being able to set $EDITOR in home-manager 33 | environment.sessionVariables = { 34 | EDITOR = "hx"; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /host/common/virt/default.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | imports = [ 3 | ./docker.nix 4 | ./lxd.nix 5 | ./multipass.nix 6 | ]; 7 | } 8 | -------------------------------------------------------------------------------- /host/common/virt/docker.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | virtualisation = { 3 | docker = { 4 | enable = true; 5 | storageDriver = "btrfs"; 6 | }; 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /host/common/virt/lxd.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | virtualisation = { 4 | lxd = { 5 | enable = true; 6 | zfsSupport = true; 7 | ui = { 8 | enable = true; 9 | package = pkgs.lxd-ui; 10 | }; 11 | }; 12 | }; 13 | 14 | networking = { 15 | firewall = { 16 | trustedInterfaces = [ "lxdbr0" ]; 17 | }; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /host/common/virt/multipass.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | virtualisation.multipass = { 3 | enable = true; 4 | }; 5 | } 6 | -------------------------------------------------------------------------------- /host/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | config, 3 | desktop, 4 | hostname, 5 | inputs, 6 | lib, 7 | modulesPath, 8 | outputs, 9 | stateVersion, 10 | username, 11 | ... 12 | }: 13 | { 14 | 15 | imports = 16 | [ 17 | (modulesPath + "/installer/scan/not-detected.nix") 18 | (./. + "/${hostname}/boot.nix") 19 | (./. + "/${hostname}/hardware.nix") 20 | 21 | ./common/base 22 | ./common/users/${username} 23 | ] 24 | ++ lib.optional (builtins.pathExists (./. + "/${hostname}/extra.nix")) ./${hostname}/extra.nix 25 | # Include desktop config if a desktop is defined 26 | ++ lib.optional (builtins.isString desktop) ./common/desktop; 27 | 28 | nixpkgs = { 29 | overlays = [ 30 | # Add overlays your own flake exports (from overlays and pkgs dir): 31 | outputs.overlays.additions 32 | outputs.overlays.modifications 33 | outputs.overlays.unstable-packages 34 | 35 | # You can also add overlays exported from other flakes: 36 | # neovim-nightly-overlay.overlays.default 37 | inputs.agenix.overlays.default 38 | inputs.flypi.overlay 39 | inputs.libations.overlays.default 40 | 41 | # Or just specify overlays directly here, for example: 42 | # (_: _: { embr = inputs.embr.packages."${pkgs.system}".embr; }) 43 | ]; 44 | 45 | config = { 46 | allowUnfree = true; 47 | joypixels.acceptLicense = true; 48 | }; 49 | }; 50 | 51 | nix = { 52 | # This will add each flake input as a registry 53 | # To make nix3 commands consistent with your flake 54 | registry = lib.mkForce (lib.mapAttrs (_: value: { flake = value; }) inputs); 55 | 56 | # This will additionally add your inputs to the system's legacy channels 57 | # Making legacy nix commands consistent as well, awesome! 58 | nixPath = lib.mkForce ( 59 | lib.mapAttrsToList (key: value: "${key}=${value.to.path}") config.nix.registry 60 | ); 61 | 62 | optimise.automatic = true; 63 | settings = { 64 | auto-optimise-store = true; 65 | experimental-features = [ 66 | "nix-command" 67 | "flakes" 68 | ]; 69 | trusted-users = [ 70 | "root" 71 | "@wheel" 72 | ]; 73 | }; 74 | }; 75 | 76 | system = { 77 | inherit stateVersion; 78 | }; 79 | } 80 | -------------------------------------------------------------------------------- /host/kara/boot.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | { 3 | boot = { 4 | # Secure boot configuration 5 | bootspec.enable = true; 6 | loader.systemd-boot.enable = lib.mkForce false; 7 | lanzaboote = { 8 | enable = true; 9 | pkiBundle = "/etc/secureboot"; 10 | }; 11 | 12 | initrd = { 13 | availableKernelModules = [ 14 | "ahci" 15 | "nvme" 16 | "sd_mod" 17 | "usb_storage" 18 | "usbhid" 19 | "xhci_pci" 20 | ]; 21 | }; 22 | 23 | kernelModules = [ 24 | "kvm_amd" 25 | "vhost_vsock" 26 | ]; 27 | 28 | # Use the latest Linux kernel, rather than the default LTS 29 | # kernelPackages = pkgs.linuxPackages_latest; 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /host/kara/disks.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | disks ? [ 4 | "/dev/nvme0n1" 5 | "/dev/nvme1n1" 6 | ], 7 | ... 8 | }: 9 | let 10 | cryptroot = "cryptroot"; 11 | defaultBtrfsOpts = [ 12 | "defaults" 13 | "compress=zstd:1" 14 | "ssd" 15 | "noatime" 16 | "nodiratime" 17 | ]; 18 | in 19 | { 20 | boot.initrd.luks.devices.${cryptroot} = { 21 | # TODO: Remove this "device" attr if/when machine is reinstalled. 22 | # This is a workaround for the legacy -> gpt tables disko format. 23 | device = lib.mkForce "/dev/disk/by-uuid/69f73515-2d29-446c-a6e4-ab9deea44712"; 24 | allowDiscards = true; 25 | preLVM = true; 26 | }; 27 | 28 | environment.etc = { 29 | "crypttab".text = '' 30 | data /dev/disk/by-partlabel/data /etc/data.keyfile 31 | ''; 32 | }; 33 | 34 | # TODO: Remove this if/when machine is reinstalled. 35 | # This is a workaround for the legacy -> gpt tables disko format. 36 | fileSystems."/boot".device = lib.mkForce "/dev/disk/by-partlabel/ESP"; 37 | 38 | disko.devices = { 39 | disk = { 40 | # 1TB root/boot drive. Configured with: 41 | # - A FAT32 ESP partition for systemd-boot 42 | # - A LUKS container which containers multiple btrfs subvolumes for nixos install 43 | nvme0 = { 44 | device = builtins.elemAt disks 0; 45 | type = "disk"; 46 | content = { 47 | type = "gpt"; 48 | partitions = { 49 | ESP = { 50 | start = "0%"; 51 | end = "512MiB"; 52 | type = "EF00"; 53 | content = { 54 | type = "filesystem"; 55 | format = "vfat"; 56 | mountpoint = "/boot"; 57 | }; 58 | }; 59 | luks = { 60 | start = "512MiB"; 61 | end = "100%"; 62 | content = { 63 | type = "luks"; 64 | name = "${cryptroot}"; 65 | 66 | settings = { 67 | allowDiscards = true; 68 | }; 69 | 70 | content = { 71 | type = "btrfs"; 72 | # Override existing partition 73 | extraArgs = [ "-f" ]; 74 | subvolumes = { 75 | "@" = { 76 | mountpoint = "/"; 77 | mountOptions = defaultBtrfsOpts; 78 | }; 79 | "@nix" = { 80 | mountpoint = "/nix"; 81 | mountOptions = defaultBtrfsOpts; 82 | }; 83 | "@home" = { 84 | mountpoint = "/home"; 85 | mountOptions = defaultBtrfsOpts; 86 | }; 87 | "@var" = { 88 | mountpoint = "/var"; 89 | mountOptions = defaultBtrfsOpts; 90 | }; 91 | "@snapshots" = { 92 | mountpoint = "/.snapshots"; 93 | mountOptions = defaultBtrfsOpts; 94 | }; 95 | }; 96 | }; 97 | }; 98 | }; 99 | }; 100 | }; 101 | }; 102 | 103 | # 2TB data drive. LUKS encrypted with single btrfs subvolume. 104 | nvme1 = { 105 | device = builtins.elemAt disks 1; 106 | type = "disk"; 107 | content = { 108 | type = "gpt"; 109 | partitions = { 110 | data = { 111 | start = "0%"; 112 | end = "100%"; 113 | content = { 114 | type = "luks"; 115 | name = "data"; 116 | 117 | settings = { 118 | # Make sure there is no trailing newline in keyfile if used for interactive unlock. 119 | # Use `echo -n "password" > /tmp/secret.key` 120 | keyFile = "/tmp/data.keyfile"; 121 | allowDiscards = true; 122 | }; 123 | 124 | # Don't try to unlock this drive early in the boot. 125 | initrdUnlock = false; 126 | 127 | content = { 128 | type = "btrfs"; 129 | # Override existing partition 130 | extraArgs = [ "-f" ]; 131 | subvolumes = { 132 | "@data" = { 133 | mountpoint = "/home/jon/data"; 134 | mountOptions = defaultBtrfsOpts; 135 | }; 136 | }; 137 | }; 138 | }; 139 | }; 140 | }; 141 | }; 142 | }; 143 | }; 144 | }; 145 | } 146 | -------------------------------------------------------------------------------- /host/kara/extra.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | imports = [ 4 | ../common/services/backup 5 | ]; 6 | 7 | services.pipewire.wireplumber.configPackages = [ 8 | (pkgs.writeTextDir "share/wireplumber/wireplumber.conf.d/99-disable-idle-timeout.conf" '' 9 | monitor.alsa.rules = [ 10 | { 11 | matches = [ 12 | { node.name = "~alsa_input.*" } 13 | { node.name = "~alsa_output.*" } 14 | ] 15 | actions = { 16 | update-props = { 17 | session.suspend-timeout-seconds = 0, 18 | } 19 | } 20 | } 21 | ] 22 | '') 23 | ]; 24 | } 25 | -------------------------------------------------------------------------------- /host/kara/hardware.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, ... }: 2 | 3 | { 4 | imports = [ 5 | inputs.disko.nixosModules.disko 6 | (import ./disks.nix { inherit lib; }) 7 | 8 | inputs.nixos-hardware.nixosModules.common-cpu-amd 9 | inputs.nixos-hardware.nixosModules.common-cpu-amd-pstate 10 | inputs.nixos-hardware.nixosModules.common-gpu-amd 11 | inputs.nixos-hardware.nixosModules.common-hidpi 12 | inputs.nixos-hardware.nixosModules.common-pc 13 | inputs.nixos-hardware.nixosModules.common-pc-ssd 14 | 15 | ../common/hardware/bluetooth.nix 16 | ../common/hardware/yubihsm.nix 17 | ]; 18 | 19 | hardware.amdgpu.amdvlk.enable = true; 20 | hardware.amdgpu.opencl.enable = true; 21 | 22 | nixpkgs.hostPlatform = "x86_64-linux"; 23 | } 24 | -------------------------------------------------------------------------------- /host/thor/boot.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | boot = { 3 | initrd = { 4 | availableKernelModules = [ 5 | "nvme" 6 | "sd_mod" 7 | "sdhci_pci" 8 | "usb_storage" 9 | "usbhid" 10 | "xhci_pci" 11 | ]; 12 | kernelModules = [ ]; 13 | }; 14 | 15 | kernelModules = [ 16 | "kvm-intel" 17 | "vhost_vsock" 18 | ]; 19 | 20 | kernel = { 21 | sysctl = { 22 | "fs.inotify.max_user_watches" = 524288; 23 | }; 24 | }; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /host/thor/disks.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | let 3 | defaultBtrfsOpts = [ 4 | "defaults" 5 | "compress=zstd:1" 6 | "ssd" 7 | "noatime" 8 | "nodiratime" 9 | ]; 10 | in 11 | { 12 | environment.etc = { 13 | "crypttab".text = '' 14 | data /dev/disk/by-partlabel/data /etc/data.keyfile 15 | ''; 16 | }; 17 | 18 | # TODO: Remove this if/when machine is reinstalled. 19 | # This is a workaround for the legacy -> gpt tables disko format. 20 | fileSystems = { 21 | "/".device = lib.mkForce "/dev/disk/by-partlabel/root"; 22 | "/boot".device = lib.mkForce "/dev/disk/by-partlabel/ESP"; 23 | "/.snapshots".device = lib.mkForce "/dev/disk/by-partlabel/root"; 24 | "/home".device = lib.mkForce "/dev/disk/by-partlabel/root"; 25 | "/nix".device = lib.mkForce "/dev/disk/by-partlabel/root"; 26 | "/var".device = lib.mkForce "/dev/disk/by-partlabel/root"; 27 | }; 28 | 29 | disko.devices = { 30 | disk = { 31 | # 512GB root/boot drive. Configured with: 32 | # - A FAT32 ESP partition for systemd-boot 33 | # - Multiple btrfs subvolumes for the installation of nixos 34 | nvme0 = { 35 | device = "/dev/nvme0n1"; 36 | type = "disk"; 37 | content = { 38 | type = "gpt"; 39 | partitions = { 40 | ESP = { 41 | start = "0%"; 42 | end = "512MiB"; 43 | type = "EF00"; 44 | content = { 45 | type = "filesystem"; 46 | format = "vfat"; 47 | mountpoint = "/boot"; 48 | }; 49 | }; 50 | root = { 51 | start = "512MiB"; 52 | end = "100%"; 53 | content = { 54 | type = "btrfs"; 55 | # Override existing partition 56 | extraArgs = [ "-f" ]; 57 | subvolumes = { 58 | "@" = { 59 | mountpoint = "/"; 60 | mountOptions = defaultBtrfsOpts; 61 | }; 62 | "@nix" = { 63 | mountpoint = "/nix"; 64 | mountOptions = defaultBtrfsOpts; 65 | }; 66 | "@home" = { 67 | mountpoint = "/home"; 68 | mountOptions = defaultBtrfsOpts; 69 | }; 70 | "@var" = { 71 | mountpoint = "/var"; 72 | mountOptions = defaultBtrfsOpts; 73 | }; 74 | "@snapshots" = { 75 | mountpoint = "/.snapshots"; 76 | mountOptions = defaultBtrfsOpts; 77 | }; 78 | }; 79 | }; 80 | }; 81 | }; 82 | }; 83 | }; 84 | 85 | # 4TB data drive. LUKS encrypted with single btrfs subvolume. 86 | sda = { 87 | device = "/dev/sda"; 88 | type = "disk"; 89 | content = { 90 | type = "gpt"; 91 | partitions = { 92 | data = { 93 | start = "0%"; 94 | end = "100%"; 95 | content = { 96 | type = "luks"; 97 | name = "data"; 98 | 99 | settings = { 100 | # Make sure there is no trailing newline in keyfile if used for interactive unlock. 101 | # Use `echo -n "password" > /tmp/secret.key` 102 | keyFile = "/tmp/data.keyfile"; 103 | allowDiscards = true; 104 | }; 105 | 106 | # Don't try to unlock this drive early in the boot. 107 | initrdUnlock = false; 108 | 109 | content = { 110 | type = "btrfs"; 111 | # Override existing partition 112 | extraArgs = [ "-f" ]; 113 | subvolumes = { 114 | "@data" = { 115 | mountpoint = "/data"; 116 | mountOptions = defaultBtrfsOpts; 117 | }; 118 | }; 119 | }; 120 | }; 121 | }; 122 | }; 123 | }; 124 | }; 125 | }; 126 | }; 127 | } 128 | -------------------------------------------------------------------------------- /host/thor/extra.nix: -------------------------------------------------------------------------------- 1 | _: { 2 | imports = [ 3 | ../common/services/files.nix 4 | ../common/services/home-assistant.nix 5 | ../common/services/libations.nix 6 | ../common/services/immich.nix 7 | ../common/services/backup 8 | ../common/services/nfs 9 | ../common/services/photo-backup 10 | ../common/services/reverse-proxy 11 | ]; 12 | } 13 | -------------------------------------------------------------------------------- /host/thor/hardware.nix: -------------------------------------------------------------------------------- 1 | { inputs, lib, ... }: 2 | { 3 | imports = [ 4 | inputs.disko.nixosModules.disko 5 | inputs.nixos-hardware.nixosModules.common-cpu-intel 6 | inputs.nixos-hardware.nixosModules.common-hidpi 7 | inputs.nixos-hardware.nixosModules.common-pc 8 | inputs.nixos-hardware.nixosModules.common-pc-ssd 9 | 10 | (import ./disks.nix { inherit lib; }) 11 | ]; 12 | 13 | nixpkgs.hostPlatform = "x86_64-linux"; 14 | } 15 | -------------------------------------------------------------------------------- /host/volnir/boot.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | { 3 | boot = { 4 | initrd = { 5 | availableKernelModules = [ ]; 6 | kernelModules = [ ]; 7 | }; 8 | 9 | loader = { 10 | generic-extlinux-compatible.enable = true; 11 | grub.enable = false; 12 | systemd-boot.enable = lib.mkForce false; 13 | }; 14 | 15 | extraModulePackages = [ ]; 16 | kernelModules = [ ]; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /host/volnir/disks.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | { 3 | fileSystems."/" = { 4 | device = "/dev/disk/by-label/NIXOS_SD"; 5 | fsType = "ext4"; 6 | }; 7 | 8 | swapDevices = [ 9 | { 10 | device = "/.swapfile"; 11 | size = 2048; 12 | } 13 | ]; 14 | 15 | zramSwap.enable = lib.mkForce false; 16 | } 17 | -------------------------------------------------------------------------------- /host/volnir/extra.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs, 3 | lib, 4 | pkgs, 5 | self, 6 | config, 7 | ... 8 | }: 9 | { 10 | imports = [ 11 | "${self}/host/common/services/networkmanager.nix" 12 | inputs.flypi.nixosModules.dump1090 13 | inputs.flypi.nixosModules.planefinder 14 | ]; 15 | 16 | age.secrets = { 17 | pfconfig = { 18 | file = "${self}/secrets/volnir-planefinder-config.age"; 19 | owner = "root"; 20 | group = "root"; 21 | mode = "444"; 22 | }; 23 | }; 24 | 25 | nixpkgs.overlays = [ 26 | (_final: super: { 27 | makeModulesClosure = x: super.makeModulesClosure (x // { allowMissing = true; }); 28 | }) 29 | ]; 30 | 31 | programs.firefox.enable = true; 32 | 33 | services = { 34 | cage = { 35 | enable = true; 36 | extraArguments = [ "-s" ]; 37 | user = "jon"; 38 | program = "${lib.getExe pkgs.firefox} -kiosk http://localhost:30053"; 39 | environment = { 40 | WLR_LIBINPUT_NO_DEVICES = "1"; 41 | }; 42 | }; 43 | 44 | chrony.extraConfig = '' 45 | makestep 1 -1 46 | ''; 47 | 48 | dump1090.enable = true; 49 | 50 | planefinder = { 51 | enable = true; 52 | openFirewall = true; 53 | configFile = config.age.secrets.pfconfig.path; 54 | }; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /host/volnir/hardware.nix: -------------------------------------------------------------------------------- 1 | { lib, inputs, ... }: 2 | { 3 | imports = [ 4 | inputs.nixos-hardware.nixosModules.raspberry-pi-4 5 | (import ./disks.nix { inherit lib; }) 6 | ]; 7 | 8 | hardware.raspberry-pi."4".fkms-3d.enable = true; 9 | 10 | nixpkgs.hostPlatform = "aarch64-linux"; 11 | } 12 | -------------------------------------------------------------------------------- /lib/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | inputs, 4 | outputs, 5 | stateVersion, 6 | username, 7 | ... 8 | }: 9 | let 10 | helpers = import ./helpers.nix { 11 | inherit 12 | self 13 | inputs 14 | outputs 15 | stateVersion 16 | username 17 | ; 18 | }; 19 | in 20 | { 21 | inherit (helpers) mkHome mkHost forAllSystems; 22 | } 23 | -------------------------------------------------------------------------------- /lib/helpers.nix: -------------------------------------------------------------------------------- 1 | { 2 | self, 3 | inputs, 4 | outputs, 5 | stateVersion, 6 | username, 7 | ... 8 | }: 9 | { 10 | # Helper function for generating home-manager configs 11 | mkHome = 12 | { 13 | hostname, 14 | user ? username, 15 | desktop ? null, 16 | system ? "x86_64-linux", 17 | }: 18 | inputs.home-manager.lib.homeManagerConfiguration { 19 | pkgs = inputs.nixpkgs.legacyPackages.${system}; 20 | extraSpecialArgs = { 21 | inherit 22 | self 23 | inputs 24 | outputs 25 | stateVersion 26 | hostname 27 | desktop 28 | ; 29 | username = user; 30 | }; 31 | modules = [ 32 | inputs.catppuccin.homeManagerModules.catppuccin 33 | ../home 34 | ]; 35 | }; 36 | 37 | # Helper function for generating host configs 38 | mkHost = 39 | { 40 | hostname, 41 | desktop ? null, 42 | pkgsInput ? inputs.nixpkgs, 43 | }: 44 | pkgsInput.lib.nixosSystem { 45 | specialArgs = { 46 | inherit 47 | self 48 | inputs 49 | outputs 50 | stateVersion 51 | username 52 | hostname 53 | desktop 54 | ; 55 | }; 56 | modules = [ 57 | inputs.agenix.nixosModules.default 58 | inputs.lanzaboote.nixosModules.lanzaboote 59 | inputs.libations.nixosModules.libations 60 | ../host 61 | ]; 62 | }; 63 | 64 | mkSystemManager = 65 | { 66 | system ? "x86_64-linux", 67 | }: 68 | inputs.system-manager.lib.makeSystemConfig { 69 | modules = [ 70 | inputs.nix-system-graphics.systemModules.default 71 | { 72 | config = { 73 | nixpkgs.hostPlatform = system; 74 | system-manager.allowAnyDistro = true; 75 | system-graphics.enable = true; 76 | }; 77 | } 78 | ]; 79 | }; 80 | 81 | forAllSystems = inputs.nixpkgs.lib.genAttrs [ 82 | "aarch64-linux" 83 | "i686-linux" 84 | "x86_64-linux" 85 | "aarch64-darwin" 86 | "x86_64-darwin" 87 | ]; 88 | } 89 | -------------------------------------------------------------------------------- /lib/theme/colours.nix: -------------------------------------------------------------------------------- 1 | rec { 2 | colours = rec { 3 | inherit (catppuccin-macchiato) 4 | pink 5 | red 6 | yellow 7 | green 8 | ; 9 | inherit (catppuccin-macchiato) subtext0 subtext1 text; 10 | inherit (catppuccin-macchiato) overlay0 overlay1 overlay2; 11 | inherit (catppuccin-macchiato) surface0 surface1 surface2; 12 | 13 | accent = darkBlue; 14 | black = catppuccin-macchiato.crust; 15 | white = catppuccin-macchiato.rosewater; 16 | lightPink = catppuccin-macchiato.flamingo; 17 | lightRed = catppuccin-macchiato.maroon; 18 | orange = catppuccin-macchiato.peach; 19 | cyan = catppuccin-macchiato.teal; 20 | blue = catppuccin-macchiato.sapphire; 21 | darkBlue = catppuccin-macchiato.blue; 22 | lightBlue = catppuccin-macchiato.sky; 23 | purple = catppuccin-macchiato.mauve; 24 | lightPurple = catppuccin-macchiato.lavender; 25 | bg = catppuccin-macchiato.base; 26 | bgDark = catppuccin-macchiato.mantle; 27 | }; 28 | 29 | catppuccin-macchiato = { 30 | rosewater = "#f4dbd6"; 31 | flamingo = "#f0c6c6"; 32 | pink = "#f5bde6"; 33 | mauve = "#c6a0f6"; 34 | red = "#ed8796"; 35 | maroon = "#ee99a0"; 36 | peach = "#f5a97f"; 37 | yellow = "#eed49f"; 38 | green = "#a6da95"; 39 | teal = "#8bd5ca"; 40 | sky = "#91d7e3"; 41 | sapphire = "#7dc4e4"; 42 | blue = "#8aadf4"; 43 | lavender = "#b7bdf8"; 44 | 45 | subtext0 = "#a5adcb"; 46 | subtext1 = "#b8c0e0"; 47 | text = "#cad3f5"; 48 | 49 | overlay0 = "#6e738d"; 50 | overlay1 = "#8087a2"; 51 | overlay2 = "#939ab7"; 52 | 53 | surface0 = "#363a4f"; 54 | surface1 = "#494d64"; 55 | surface2 = "#5b6078"; 56 | 57 | base = "#24273a"; 58 | crust = "#181926"; 59 | mantle = "#1e2030"; 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /lib/theme/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs, 3 | hostname ? "", 4 | ... 5 | }: 6 | let 7 | inherit ((import ./colours.nix)) colours; 8 | libx = import ./lib.nix { inherit (pkgs) lib; }; 9 | in 10 | rec { 11 | inherit (libx) hexToRgb; 12 | inherit colours; 13 | 14 | catppuccin = { 15 | flavor = "macchiato"; 16 | accent = "blue"; 17 | size = "standard"; 18 | }; 19 | 20 | wallpaper = if hostname == "kara" then ./wallpapers/mountains.png else ./wallpapers/jokulsarlon.png; 21 | 22 | gtkTheme = { 23 | name = "catppuccin-macchiato-blue-standard"; 24 | package = pkgs.catppuccin-gtk.override { 25 | inherit (catppuccin) size; 26 | variant = catppuccin.flavor; 27 | accents = [ catppuccin.accent ]; 28 | }; 29 | }; 30 | 31 | qtTheme = { 32 | name = "Catppuccin-Macchiato-Blue"; 33 | package = pkgs.catppuccin-kvantum.override { 34 | variant = catppuccin.flavor; 35 | inherit (catppuccin) accent; 36 | }; 37 | }; 38 | 39 | iconTheme = rec { 40 | name = "Papirus-Dark"; 41 | package = pkgs.papirus-icon-theme; 42 | iconPath = "${package}/share/icons/${name}"; 43 | }; 44 | 45 | cursorTheme = { 46 | name = "Adwaita"; 47 | package = pkgs.adwaita-icon-theme; 48 | size = 24; 49 | }; 50 | 51 | fonts = { 52 | default = { 53 | name = "Inter"; 54 | package = pkgs.inter; 55 | size = "11"; 56 | }; 57 | iconFont = { 58 | name = "Inter"; 59 | package = pkgs.inter; 60 | }; 61 | monospace = { 62 | name = "MesloLGSDZ Nerd Font Mono"; 63 | package = pkgs.nerdfonts.override { fonts = [ "Meslo" ]; }; 64 | }; 65 | emoji = { 66 | name = "Joypixels"; 67 | package = pkgs.joypixels; 68 | }; 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /lib/theme/lib.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | with lib; 3 | rec { 4 | # color-related functions 5 | 6 | # convert rrggbb hex to #rrggbb 7 | x = c: "#${c}"; 8 | 9 | # convert #rrggbb -> r, g, b 10 | hexToRgb = 11 | c: 12 | let 13 | r = toString (hexToDec (__substring 1 2 c)); 14 | g = toString (hexToDec (__substring 3 2 c)); 15 | b = toString (hexToDec (__substring 5 2 c)); 16 | res = "${r}, ${g}, ${b}"; 17 | in 18 | res; 19 | 20 | # functions copied from https://gist.github.com/corpix/f761c82c9d6fdbc1b3846b37e1020e11 21 | # convert a hex value to an integer 22 | hexToDec = 23 | v: 24 | let 25 | hexToInt = { 26 | "0" = 0; 27 | "1" = 1; 28 | "2" = 2; 29 | "3" = 3; 30 | "4" = 4; 31 | "5" = 5; 32 | "6" = 6; 33 | "7" = 7; 34 | "8" = 8; 35 | "9" = 9; 36 | "a" = 10; 37 | "b" = 11; 38 | "c" = 12; 39 | "d" = 13; 40 | "e" = 14; 41 | "f" = 15; 42 | "A" = 10; 43 | "B" = 11; 44 | "C" = 12; 45 | "D" = 13; 46 | "E" = 14; 47 | "F" = 15; 48 | }; 49 | chars = stringToCharacters v; 50 | charsLen = length chars; 51 | in 52 | foldl (a: v: a + v) 0 (imap0 (k: v: hexToInt."${v}" * (pow 16 (charsLen - k - 1))) chars); 53 | 54 | pow = 55 | let 56 | pow' = 57 | base: exponent: value: 58 | # FIXME: It will silently overflow on values > 2**62 :( 59 | # The value will become negative or zero in this case 60 | if exponent == 0 then 61 | 1 62 | else if exponent <= 1 then 63 | value 64 | else 65 | (pow' base (exponent - 1) (value * base)); 66 | in 67 | base: exponent: pow' base exponent base; 68 | } 69 | -------------------------------------------------------------------------------- /lib/theme/wallpapers/elk-colors.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/lib/theme/wallpapers/elk-colors.jpg -------------------------------------------------------------------------------- /lib/theme/wallpapers/jokulsarlon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/lib/theme/wallpapers/jokulsarlon.png -------------------------------------------------------------------------------- /lib/theme/wallpapers/mountains.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/lib/theme/wallpapers/mountains.png -------------------------------------------------------------------------------- /lib/theme/wallpapers/space-clouds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/lib/theme/wallpapers/space-clouds.png -------------------------------------------------------------------------------- /nixpkgs.nix: -------------------------------------------------------------------------------- 1 | # A nixpkgs instance that is grabbed from the pinned nixpkgs commit in the lock file 2 | # Useful to avoid using channels when using legacy nix commands 3 | let 4 | lock = (builtins.fromJSON (builtins.readFile ./flake.lock)).nodes.nixpkgs.locked; 5 | in 6 | import (fetchTarball { 7 | url = "https://github.com/nixos/nixpkgs/archive/${lock.rev}.tar.gz"; 8 | sha256 = lock.narHash; 9 | }) 10 | -------------------------------------------------------------------------------- /overlays/custom-caddy.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | let 3 | inherit (pkgs) 4 | buildGoModule 5 | cacert 6 | caddy 7 | go 8 | lib 9 | stdenv 10 | xcaddy 11 | ; 12 | in 13 | caddy.override { 14 | buildGoModule = 15 | args: 16 | buildGoModule ( 17 | args 18 | // { 19 | src = stdenv.mkDerivation rec { 20 | pname = "caddy-using-xcaddy-${xcaddy.version}"; 21 | inherit (caddy) version; 22 | 23 | dontUnpack = true; 24 | dontFixup = true; 25 | 26 | nativeBuildInputs = [ 27 | cacert 28 | go 29 | ]; 30 | 31 | plugins = [ "github.com/caddy-dns/digitalocean" ]; 32 | 33 | configurePhase = '' 34 | export GOCACHE=$TMPDIR/go-cache 35 | export GOPATH="$TMPDIR/go" 36 | export XCADDY_SKIP_BUILD=1 37 | ''; 38 | 39 | buildPhase = '' 40 | ${xcaddy}/bin/xcaddy build "${caddy.src.rev}" ${ 41 | lib.concatMapStringsSep " " (plugin: "--with ${plugin}") plugins 42 | } 43 | cd buildenv* 44 | go mod vendor 45 | ''; 46 | 47 | installPhase = '' 48 | cp -r --reflink=auto . $out 49 | ''; 50 | 51 | outputHash = "sha256-k4Lu9QKShVEhVuvGEPv3KspcsmjQmPUo0jWHhXOlofQ="; 52 | outputHashMode = "recursive"; 53 | }; 54 | 55 | subPackages = [ "." ]; 56 | ldflags = [ 57 | "-s" 58 | "-w" 59 | ]; # # don't include version info twice 60 | vendorHash = null; 61 | } 62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /overlays/default.nix: -------------------------------------------------------------------------------- 1 | { inputs, ... }: 2 | { 3 | # This one brings our custom packages from the 'pkgs' directory 4 | additions = final: _prev: import ../pkgs { pkgs = final; }; 5 | 6 | modifications = _final: prev: { 7 | # example = prev.example.overrideAttrs (oldAttrs: rec { 8 | # ... 9 | # }); 10 | custom-caddy = import ./custom-caddy.nix { pkgs = prev; }; 11 | }; 12 | 13 | # When applied, the unstable nixpkgs set (declared in the flake inputs) will 14 | # be accessible through 'pkgs.unstable' 15 | unstable-packages = final: _prev: { 16 | master = import inputs.master { 17 | inherit (final) system; 18 | config.allowUnfree = true; 19 | overlays = [ 20 | (_final: _prev: { 21 | # example = prev.example.overrideAttrs (oldAttrs: rec { 22 | # ... 23 | # }); 24 | }) 25 | ]; 26 | }; 27 | unstable = import inputs.unstable { 28 | inherit (final) system; 29 | config.allowUnfree = true; 30 | overlays = [ 31 | (_final: _prev: { 32 | # example = prev.example.overrideAttrs (oldAttrs: rec { 33 | # ... 34 | # }); 35 | }) 36 | ]; 37 | }; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /pkgs/default.nix: -------------------------------------------------------------------------------- 1 | # Custom packages, that can be defined similarly to ones from nixpkgs 2 | # Build them using 'nix build .#example' or (legacy) 'nix-build -A example' 3 | 4 | { 5 | pkgs ? (import ../nixpkgs.nix) { }, 6 | }: 7 | { 8 | ght = pkgs.callPackage ./ght { }; 9 | juju4 = pkgs.callPackage ./juju4.nix { }; 10 | nixfmt-plus = pkgs.callPackage ./nixfmt-plus.nix { }; 11 | } 12 | -------------------------------------------------------------------------------- /pkgs/ght/default.nix: -------------------------------------------------------------------------------- 1 | { lib, pkgs, ... }: 2 | pkgs.buildNpmPackage rec { 3 | pname = "ght"; 4 | version = "1.11.3-unstable-2025-01-21"; 5 | 6 | src = pkgs.fetchFromGitHub { 7 | owner = "canonical"; 8 | repo = "ght"; 9 | # rev = "refs/tags/v${version}"; 10 | rev = "306565a0fb39e9d24e36de66cf7881cdb4f30d2e"; 11 | hash = "sha256-1cBuMJKDM3zPkfCFOH9Q5CoQLP7A8b8Ejvn/Pbrox8U="; 12 | }; 13 | 14 | npmDeps = pkgs.fetchNpmDeps { 15 | inherit src; 16 | hash = "sha256-9SrfnaDpdLR1KL8WQjFSM0Pza1yRm7/YgQ/TimTJm8o="; 17 | }; 18 | 19 | patches = [ ./ght-graders-loc.patch ]; 20 | 21 | postPatch = '' 22 | substituteInPlace ght \ 23 | --replace-fail "./index.js" "$out/opt/ght/index.js" \ 24 | --replace-fail "./package.json" "$out/opt/ght/package.json" 25 | ''; 26 | 27 | env.PUPPETEER_SKIP_DOWNLOAD = true; 28 | 29 | installPhase = '' 30 | mkdir -p $out/bin $out/opt 31 | 32 | # Copy the built tool & node_modules into the output 33 | cp -r dist $out/opt/ght 34 | cp -r node_modules $out/opt/ght/node_modules 35 | 36 | # Copy the 'binary' into place 37 | cp ght $out/bin/ght 38 | 39 | # Make sure that chromium is available and puppeteer knows to use it 40 | wrapProgram $out/bin/ght \ 41 | --set PUPPETEER_EXECUTABLE_PATH ${pkgs.chromium.outPath}/bin/chromium 42 | ''; 43 | 44 | meta = { 45 | mainProgram = "ght"; 46 | description = "Perform actions in Greenhouse from you terminal"; 47 | homepage = "https://github.com/canonical/ght"; 48 | changelog = "https://github.com/canonical/ght/releases/tag/v${version}"; 49 | maintainers = with lib.maintainers; [ jnsgruk ]; 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /pkgs/ght/ght-graders-loc.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/controllers/AssignGradersController.ts b/src/controllers/AssignGradersController.ts 2 | index 422d5b9..ab3fd67 100644 3 | --- a/src/controllers/AssignGradersController.ts 4 | +++ b/src/controllers/AssignGradersController.ts 5 | @@ -92,8 +92,7 @@ export class AssignGradersController extends BaseController { 6 | }); 7 | 8 | const gradersConfigPath = join( 9 | - process.env["SNAP_REAL_HOME"] || homedir(), 10 | - "ght-graders.yml", 11 | + homedir(), ".config/ght/ght-graders.yml" 12 | ); 13 | const gradersConfig = loadConfigFile(gradersConfigPath); 14 | if (!config) { 15 | -------------------------------------------------------------------------------- /pkgs/juju4.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | let 3 | inherit (pkgs) buildGo123Module fetchFromGitHub installShellFiles; 4 | in 5 | buildGo123Module { 6 | pname = "juju4"; 7 | version = "4.0-unstable-2024-09-08"; 8 | 9 | src = fetchFromGitHub { 10 | owner = "juju"; 11 | repo = "juju"; 12 | rev = "e51606a12490923092eb8f99882b2c4de7f4af7a"; 13 | hash = "sha256-UZUQqlUzsjf52BJ8lGMWqUM+pKDHplMZLlcHFQY4c1c="; 14 | }; 15 | 16 | vendorHash = "sha256-KzR7JCjmfGe5nNuXAk4SsxGdpXjzV1+Dc7YOQc1TwcQ="; 17 | 18 | subPackages = [ "cmd/juju" ]; 19 | 20 | nativeBuildInputs = [ installShellFiles ]; 21 | 22 | doCheck = false; 23 | 24 | postInstall = '' 25 | mv $out/bin/juju $out/bin/juju4 26 | ''; 27 | } 28 | -------------------------------------------------------------------------------- /pkgs/nixfmt-plus.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | pkgs.writeShellApplication { 4 | name = "nixfmt-plus"; 5 | runtimeInputs = with pkgs; [ 6 | deadnix 7 | nixfmt-rfc-style 8 | statix 9 | ]; 10 | text = '' 11 | set -x 12 | deadnix --edit 13 | statix fix 14 | nixfmt . 15 | ''; 16 | } 17 | -------------------------------------------------------------------------------- /scripts/install-with-disko: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 5 | 6 | TARGET_HOST="${1:-}" 7 | TARGET_USER="${2:-jon}" 8 | 9 | if [ "$(id -u)" -eq 0 ]; then 10 | echo "ERROR! $(basename "${0}") should be run as a regular user" 11 | exit 1 12 | fi 13 | 14 | if [[ -z "$TARGET_HOST" ]]; then 15 | echo "ERROR! $(basename "${0}") requires a hostname as the first argument" 16 | exit 1 17 | fi 18 | 19 | if [ ! -e "host/${TARGET_HOST}/disks.nix" ]; then 20 | echo "ERROR! $(basename "${0}") could not find the required host/${TARGET_HOST}/disks.nix" 21 | exit 1 22 | fi 23 | 24 | # Check if the machine we're provisioning expects a keyfile to unlock a disk. 25 | # If it does, generate a new key, and write to a known location. 26 | if grep -q "data.keyfile" "host/${TARGET_HOST}/disks.nix"; then 27 | echo -n "$(head -c32 /dev/random | base64)" > /tmp/data.keyfile 28 | fi 29 | 30 | echo "WARNING! The disks in ${TARGET_HOST} are about to get wiped" 31 | echo " NixOS will be re-installed" 32 | echo " This is a destructive operation" 33 | echo 34 | read -p "Are you sure? [y/N]" -n 1 -r 35 | echo 36 | if [[ $REPLY =~ ^[Yy]$ ]]; then 37 | sudo true 38 | 39 | sudo nix run github:nix-community/disko \ 40 | --extra-experimental-features "nix-command flakes" \ 41 | --no-write-lock-file \ 42 | -- \ 43 | --mode zap_create_mount \ 44 | "host/${TARGET_HOST}/disks.nix" 45 | 46 | sudo nixos-install --flake ".#${TARGET_HOST}" 47 | 48 | # Rsync my nix-config to the target install 49 | mkdir -p "/mnt/home/${TARGET_USER}/nixos-config" 50 | rsync -a --delete "${DIR}/.." "/mnt/home/${TARGET_USER}/nixos-config" 51 | 52 | # If there is a keyfile for a data disk, put copy it to the root partition and 53 | # ensure the permissions are set appropriately. 54 | if [[ -f "/tmp/data.keyfile" ]]; then 55 | sudo cp /tmp/data.keyfile /mnt/etc/data.keyfile 56 | sudo chmod 0400 /mnt/etc/data.keyfile 57 | fi 58 | fi 59 | 60 | 61 | -------------------------------------------------------------------------------- /scripts/partition-btrfs-luks: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | # Output green message prefixed with [+] 4 | _info() { echo -e "\e[92m[+] ${1:-}\e[0m"; } 5 | # Output orange message prefixed with [-] 6 | _warn() { echo -e "\e[33m[-] ${1:-}\e[0m"; } 7 | # Output red message prefixed with [!] and exit 8 | _error() { echo -e >&2 "\e[31m[!] ${1:-}\e[0m"; exit 1; } 9 | 10 | DISK="${DISK:-}" 11 | if [[ -z "$DISK" ]]; then 12 | _error "Set the DISK environment variable to continue" 13 | fi 14 | 15 | # Create a new partition table on the disk 16 | parted "${DISK}" -s mklabel gpt 17 | # Create a 500MiB FAT32 Boot Partition 18 | parted "${DISK}" -s mkpart boot fat32 0% 500MiB 19 | # Set the boot/esp flags on the boot partition 20 | parted "${DISK}" set 1 boot on 21 | # Create a single root partition 22 | parted "${DISK}" -s mkpart root 500MiB 100% 23 | 24 | _warn "Setting up disk encryption. Confirmation and password entry required" 25 | root_part="p2" 26 | 27 | # luksFormat the root partition 28 | cryptsetup luksFormat "${DISK}${root_part}" 29 | _warn "Decrypting disk, password entry required" 30 | # Open the encrypted container 31 | cryptsetup open "${DISK}${root_part}" cryptlvm 32 | # Setup LVM physical volumes, volume groups and logical volumes 33 | _info "Setting up LVM" 34 | # Create a physical volume 35 | pvcreate /dev/mapper/cryptlvm 36 | vgcreate vg /dev/mapper/cryptlvm 37 | lvcreate -l 100%FREE vg -n root 38 | 39 | # Setup the filesystems 40 | root_part="/dev/vg/root" 41 | 42 | # Format the root partition 43 | mkfs.btrfs --force "${root_part}" 44 | # Mount the root partition to /mnt 45 | mount "${root_part}" /mnt 46 | # Create btrfs subvolumes 47 | btrfs subvolume create /mnt/@ 48 | btrfs subvolume create /mnt/@home 49 | btrfs subvolume create /mnt/@snapshots 50 | btrfs subvolume create /mnt/@var 51 | # Remount with btrfs options 52 | umount -R /mnt 53 | btrfs_opts="defaults,x-mount.mkdir,compress=lzo,ssd,noatime,nodiratime" 54 | mount -t btrfs -o subvol=@,"${btrfs_opts}" "${root_part}" /mnt 55 | mount -t btrfs -o subvol=@home,"${btrfs_opts}" "${root_part}" /mnt/home 56 | mount -t btrfs -o subvol=@var,"${btrfs_opts}" "${root_part}" /mnt/var 57 | mount -t btrfs -o subvol=@snapshots,"${btrfs_opts}" "${root_part}" /mnt/.snapshots 58 | 59 | boot_part="${DISK}p1" 60 | # Format the boot partition 61 | mkfs.fat -F32 "${boot_part}" 62 | # Mount the boot partition 63 | mount -o "defaults,x-mount.mkdir" "${boot_part}" /mnt/boot 64 | -------------------------------------------------------------------------------- /scripts/setup-hyprland-ubuntu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script is used to make some configuration changes to Ubuntu 4 | # when running Hyprland using home-manager, system-manager and nix-system-graphics. 5 | 6 | # First, create a script that can execute Hyprland in the right context 7 | cat <<-EOF | sudo tee /usr/local/bin/hypr-run 8 | #!/home/jon/.nix-profile/bin/fish 9 | export GTK_THEME="catppuccin-macchiato-blue-standard" 10 | source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh 11 | systemd-run --user --scope --collect --quiet --unit="Hyprland" systemd-cat --identifier="hyprland" /home/jon/.nix-profile/bin/Hyprland $@ 12 | EOF 13 | 14 | # Ensure the script is executable 15 | sudo chmod 755 /usr/local/bin/hypr-run 16 | 17 | # Next add a desktop configuration so that Hyprland comes up in the GDM session menu 18 | cat <<-EOF | sudo tee /usr/share/wayland-sessions/hyprland.desktop 19 | [Desktop Entry] 20 | Name=Hyprland 21 | Comment=Hyprland Tiling Window Manager 22 | Exec=/usr/local/bin/hypr-run 23 | Type=application 24 | EOF 25 | 26 | # Prevent errors occurring when launching Chromium/Electron apps. 27 | echo "kernel.apparmor_restrict_unprivileged_userns=0" | sudo tee /etc/sysctl.d/60-apparmor-namespace.conf 28 | -------------------------------------------------------------------------------- /secrets/kara-borgbase-passphrase.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/secrets/kara-borgbase-passphrase.age -------------------------------------------------------------------------------- /secrets/kara-borgbase-ssh.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/secrets/kara-borgbase-ssh.age -------------------------------------------------------------------------------- /secrets/secrets.nix: -------------------------------------------------------------------------------- 1 | let 2 | jon = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL3bnlKpGO7eqZFafiLxJVG0TYyleVfuO1C9Q2q0QHJg"; 3 | kara = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFhVkbNk0hYPZOu4SlBwD1RXd78PmiD21Yen2j8JgT6Z"; 4 | thor = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJqnwVNoWVkTJxUJ1yZh64wKBYaij1IwXUA3PRE3/lIx"; 5 | volnir = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIASBzlLkAv8pbvg3/ffDko/K7s5YNWutxclPOLPxNmcu"; 6 | users = [ jon ]; 7 | in 8 | { 9 | "kara-borgbase-ssh.age".publicKeys = users ++ [ kara ]; 10 | "kara-borgbase-passphrase.age".publicKeys = users ++ [ kara ]; 11 | 12 | "thor-digitalocean.age".publicKeys = users ++ [ thor ]; 13 | "thor-borgbase-ssh.age".publicKeys = users ++ [ thor ]; 14 | "thor-borgbase-passphrase.age".publicKeys = users ++ [ thor ]; 15 | "thor-libations-tskey.age".publicKeys = users ++ [ thor ]; 16 | "thor-libations-recipes.age".publicKeys = users ++ [ thor ]; 17 | "thor-backup-env.age".publicKeys = users ++ [ thor ]; 18 | 19 | "volnir-planefinder-config.age".publicKeys = users ++ [ volnir ]; 20 | } 21 | -------------------------------------------------------------------------------- /secrets/thor-backup-env.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/secrets/thor-backup-env.age -------------------------------------------------------------------------------- /secrets/thor-borgbase-passphrase.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/secrets/thor-borgbase-passphrase.age -------------------------------------------------------------------------------- /secrets/thor-borgbase-ssh.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/secrets/thor-borgbase-ssh.age -------------------------------------------------------------------------------- /secrets/thor-digitalocean.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/secrets/thor-digitalocean.age -------------------------------------------------------------------------------- /secrets/thor-libations-recipes.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/secrets/thor-libations-recipes.age -------------------------------------------------------------------------------- /secrets/thor-libations-tskey.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/secrets/thor-libations-tskey.age -------------------------------------------------------------------------------- /secrets/volnir-planefinder-config.age: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnsgruk/nixos-config/d88511d5007f71f23acf07d5b7b8d1c4a45dcaae/secrets/volnir-planefinder-config.age -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | # Shell for bootstrapping flake-enabled nix and home-manager 2 | # Access development shell with 'nix develop' or (legacy) 'nix-shell' 3 | { 4 | pkgs ? (import ./nixpkgs.nix) { }, 5 | }: 6 | { 7 | default = pkgs.mkShell { 8 | name = "jnsgruk-flake"; 9 | # Enable experimental features without having to specify the argument 10 | NIX_CONFIG = "experimental-features = nix-command flakes"; 11 | nativeBuildInputs = with pkgs; [ 12 | nix 13 | home-manager 14 | git 15 | ]; 16 | shellHook = '' 17 | exec fish 18 | ''; 19 | }; 20 | } 21 | --------------------------------------------------------------------------------