Kieran's opinionated (and probably slightly dumb) nix config
1{
2 inputs,
3 lib,
4 config,
5 pkgs,
6 ...
7}:
8{
9 imports = [
10 ./disk-config.nix
11 ./home-manager.nix
12
13 (inputs.import-tree ../../modules/nixos)
14 inputs.tangled.nixosModules.knot
15 inputs.tangled.nixosModules.spindle
16 ];
17
18 nixpkgs = {
19 hostPlatform = "aarch64-linux";
20 config = {
21 allowUnfree = true;
22 };
23 };
24
25 nix =
26 let
27 flakeInputs = lib.filterAttrs (_: lib.isType "flake") inputs;
28 in
29 {
30 settings = {
31 experimental-features = "nix-command flakes";
32 flake-registry = "";
33 nix-path = config.nix.nixPath;
34 trusted-users = [
35 "kierank"
36 ];
37 };
38 channel.enable = false;
39 optimise.automatic = true;
40 registry = lib.mapAttrs (_: flake: { inherit flake; }) flakeInputs;
41 nixPath = lib.mapAttrsToList (n: _: "${n}=flake:${n}") flakeInputs;
42 };
43
44 time.timeZone = "America/New_York";
45
46 environment.systemPackages = with pkgs; [
47 # core
48 coreutils
49 screen
50 bc
51 jq
52 psmisc
53 # cli_utils
54 direnv
55 zsh
56 gum
57 vim
58 # networking
59 xh
60 curl
61 wget
62 dogdns
63 inetutils
64 mosh
65 # nix_tools
66 inputs.nixvim.packages.aarch64-linux.default
67 nixd
68 nil
69 nixfmt-rfc-style
70 inputs.agenix.packages.aarch64-linux.default
71 # security
72 openssl
73 gpgme
74 gnupg
75 # dev_langs
76 nodejs_22
77 unstable.bun
78 python3
79 go
80 gopls
81 gotools
82 go-tools
83 gcc
84 # misc
85 neofetch
86 git
87 ];
88
89 programs.nh = {
90 enable = true;
91 clean.enable = true;
92 clean.extraArgs = "--keep-since 4d --keep 3";
93 flake = "/home/kierank/dots";
94 };
95
96 age.identityPaths = [
97 "/home/kierank/.ssh/id_rsa"
98 "/etc/ssh/id_rsa"
99 ];
100 age.secrets = {
101 wakatime = {
102 file = ../../secrets/wakatime.age;
103 path = "/home/kierank/.wakatime.cfg";
104 owner = "kierank";
105 };
106 cachet = {
107 file = ../../secrets/cachet.age;
108 owner = "cachet";
109 };
110 hn-alerts = {
111 file = ../../secrets/hn-alerts.age;
112 owner = "hn-alerts";
113 };
114 emojibot = {
115 file = ../../secrets/emojibot.age;
116 owner = "emojibot";
117 };
118 cloudflare = {
119 file = ../../secrets/cloudflare.age;
120 owner = "caddy";
121 };
122 github-knot-sync = {
123 file = ../../secrets/github-knot-sync.age;
124 owner = "git";
125 };
126 };
127
128 environment.sessionVariables = {
129 XDG_CACHE_HOME = "$HOME/.cache";
130 XDG_CONFIG_HOME = "$HOME/.config";
131 XDG_DATA_HOME = "$HOME/.local/share";
132 XDG_STATE_HOME = "$HOME/.local/state";
133 EDITOR = "nvim";
134 SYSTEMD_EDITOR = "nvim";
135 VISUAL = "nvim";
136 };
137
138 atelier = {
139 authentication.enable = true;
140 };
141
142 networking = {
143 hostName = "terebithia";
144 networkmanager.enable = true;
145 };
146
147 programs.zsh.enable = true;
148 programs.direnv.enable = true;
149
150 users.users = {
151 kierank = {
152 initialPassword = "changeme";
153 isNormalUser = true;
154 shell = pkgs.zsh;
155 openssh.authorizedKeys.keys = [
156 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCzEEjvbL/ttqmYoDjxYQmDIq36BabROJoXgQKeh9liBxApwp+2PmgxROzTg42UrRc9pyrkq5kVfxG5hvkqCinhL1fMiowCSEs2L2/Cwi40g5ZU+QwdcwI8a4969kkI46PyB19RHkxg54OUORiIiso/WHGmqQsP+5wbV0+4riSnxwn/JXN4pmnE//stnyAyoiEZkPvBtwJjKb3Ni9n3eNLNs6gnaXrCtaygEZdebikr9kS2g9mM696HvIFgM6cdR/wZ7DcLbG3IdTXuHN7PC3xxL+Y4ek5iMreQIPmuvs4qslbthPGYoYbYLUQiRa9XO5s/ksIj5Z14f7anHE6cuTQVpvNWdGDOigyIVS5qU+4ZF7j+rifzOXVL48gmcAvw/uV68m5Wl/p0qsC/d8vI3GYwEsWG/EzpAlc07l8BU2LxWgN+d7uwBFaJV9VtmUDs5dcslsh8IbzmtC9gq3OLGjklxTfIl6qPiL8U33oc/UwqzvZUrI2BlbagvIZYy6rP+q0= kierank@mockingjay"
157 ];
158 extraGroups = [
159 "wheel"
160 "networkmanager"
161 "services"
162 ];
163 };
164 root.openssh.authorizedKeys.keys = [
165 "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCzEEjvbL/ttqmYoDjxYQmDIq36BabROJoXgQKeh9liBxApwp+2PmgxROzTg42UrRc9pyrkq5kVfxG5hvkqCinhL1fMiowCSEs2L2/Cwi40g5ZU+QwdcwI8a4969kkI46PyB19RHkxg54OUORiIiso/WHGmqQsP+5wbV0+4riSnxwn/JXN4pmnE//stnyAyoiEZkPvBtwJjKb3Ni9n3eNLNs6gnaXrCtaygEZdebikr9kS2g9mM696HvIFgM6cdR/wZ7DcLbG3IdTXuHN7PC3xxL+Y4ek5iMreQIPmuvs4qslbthPGYoYbYLUQiRa9XO5s/ksIj5Z14f7anHE6cuTQVpvNWdGDOigyIVS5qU+4ZF7j+rifzOXVL48gmcAvw/uV68m5Wl/p0qsC/d8vI3GYwEsWG/EzpAlc07l8BU2LxWgN+d7uwBFaJV9VtmUDs5dcslsh8IbzmtC9gq3OLGjklxTfIl6qPiL8U33oc/UwqzvZUrI2BlbagvIZYy6rP+q0= kierank@mockingjay"
166 ];
167 };
168
169 # Allow passwordless sudo for wheel group (needed for deploy-rs)
170 security.sudo.wheelNeedsPassword = false;
171
172 services.openssh = {
173 enable = true;
174 openFirewall = true;
175 settings = {
176 PermitRootLogin = "no";
177 PasswordAuthentication = false;
178 };
179 };
180
181 networking.firewall = {
182 enable = true;
183 allowedTCPPorts = [ 22 80 443 ];
184 logRefusedConnections = false;
185 rejectPackets = true;
186 };
187
188 services.tailscale = {
189 enable = true;
190 useRoutingFeatures = "client";
191 };
192
193 services.caddy = {
194 enable = true;
195 package = pkgs.caddy.withPlugins {
196 plugins = [ "github.com/caddy-dns/cloudflare@v0.2.2" ];
197 hash = "sha256-Z8nPh4OI3/R1nn667ZC5VgE+Q9vDenaQ3QPKxmqPNkc=";
198 };
199 email = "me@dunkirk.sh";
200 globalConfig = ''
201 acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN}
202 '';
203 virtualHosts."knot.dunkirk.sh" = {
204 extraConfig = ''
205 tls {
206 dns cloudflare {env.CLOUDFLARE_API_TOKEN}
207 }
208 header {
209 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
210 }
211 reverse_proxy localhost:5555 {
212 header_up X-Forwarded-Proto {scheme}
213 header_up X-Forwarded-For {remote}
214 }
215 '';
216 };
217 virtualHosts."spindle.dunkirk.sh" = {
218 extraConfig = ''
219 tls {
220 dns cloudflare {env.CLOUDFLARE_API_TOKEN}
221 }
222 header {
223 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
224 }
225 reverse_proxy localhost:6555 {
226 header_up X-Forwarded-Proto {scheme}
227 header_up X-Forwarded-For {remote}
228 }
229 '';
230 };
231 virtualHosts."emojibot.dunkirk.sh" = {
232 extraConfig = ''
233 tls {
234 dns cloudflare {env.CLOUDFLARE_API_TOKEN}
235 }
236 header {
237 Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
238 }
239 reverse_proxy localhost:3002 {
240 header_up X-Forwarded-Proto {scheme}
241 header_up X-Forwarded-For {remote}
242 }
243 '';
244 };
245 extraConfig = ''
246 # Default response for unhandled domains
247 :80 {
248 respond "404 - Looks like this bridge doesn't have an end" 404
249 }
250 :443 {
251 respond "404 - Looks like this bridge doesn't have an end" 404
252 }
253 '';
254 };
255
256 systemd.services.caddy.serviceConfig = {
257 EnvironmentFile = config.age.secrets.cloudflare.path;
258 };
259
260 atelier.services.cachet = {
261 enable = true;
262 domain = "cachet.dunkirk.sh";
263 secretsFile = config.age.secrets.cachet.path;
264 };
265
266 atelier.services.hn-alerts = {
267 enable = true;
268 domain = "hn.dunkirk.sh";
269 secretsFile = config.age.secrets.hn-alerts.path;
270 };
271
272 atelier.services.emojibot = {
273 enable = true;
274 domain = "emojibot.dunkirk.sh";
275 secretsFile = config.age.secrets.emojibot.path;
276 };
277
278 services.tangled.knot = {
279 enable = true;
280 package = inputs.tangled.packages.aarch64-linux.knot;
281 appviewEndpoint = "https://tangled.org";
282 server = {
283 owner = "did:plc:krxbvxvis5skq7jj6eot23ul";
284 hostname = "knot.dunkirk.sh";
285 listenAddr = "127.0.0.1:5555";
286 };
287 };
288
289 services.tangled.spindle = {
290 enable = true;
291 package = inputs.tangled.packages.aarch64-linux.spindle;
292 server = {
293 owner = "did:plc:krxbvxvis5skq7jj6eot23ul";
294 hostname = "spindle.dunkirk.sh";
295 listenAddr = "127.0.0.1:6555";
296 };
297 };
298
299 atelier.services.knot-sync = {
300 enable = true;
301 secretsFile = config.age.secrets.github-knot-sync.path;
302 };
303
304 boot.loader.systemd-boot.enable = true;
305 boot.loader.efi.canTouchEfiVariables = true;
306 boot.kernelParams = [ "console=ttyS0" ];
307
308 system.stateVersion = "23.05";
309}