me like nix
1{
2 pkgs,
3 inputs,
4 config,
5 ...
6}:
7
8let
9 zjctl = import ../../packages/zjctl.nix { inherit pkgs; };
10 zrpc-wasm = import ../../packages/zrpc-wasm.nix { inherit pkgs; };
11in
12{
13 # Import the home-manager modules you want to use
14 imports = [
15 inputs.catppuccin.homeModules.catppuccin
16 inputs.niri.homeModules.niri
17 inputs.zen-browser.homeModules.beta
18 inputs.agenix.homeManagerModules.default
19 ];
20
21 # All your user-specific packages
22 home.packages = with pkgs; [
23 helix
24 git
25 jujutsu # jj-cli
26 htop
27 iotop
28 ncdu
29 youtube-tui
30 yt-dlp # youtube-tui and mpv need this to resolve YouTube URLs
31 zellij # terminal multiplexer
32 alacritty
33 inputs.fsel.packages.${pkgs.system}.default # App launcher / fuzzy finder
34 bemoji # emoji picker
35 networkmanager_dmenu # network picker for fuzzel
36 quickshell # Status bar (QML-based)
37 inputs.kaleidux.packages.${pkgs.system}.default # Dynamic wallpaper daemon
38 (import ../../packages/cclip.nix { inherit pkgs; }) # Clipboard history manager
39 pavucontrol # GUI for PulseAudio/PipeWire volume control
40 playerctl # MPRIS media player control
41 (element-desktop.override {
42 commandLineArgs = "--password-store=gnome-libsecret";
43 })
44 fd
45 ripgrep
46 yazi # tui file browser
47 gh # github cli
48 gh-dash # github dashboard TUI
49 diffnav # git diff viewer
50 signal-desktop
51 xwayland-satellite # for running x11 apps
52 nixfmt # nix formatter
53 nil # nix language server
54 atac # postman-like TUI
55 trippy # network analyzer
56 rsync # file sync utility
57 udiskie # for mounting external drives
58 darktable # photo editing
59 zoxide
60 chromium
61 claude-code
62 libnotify # for notify-send (claude-notify)
63 zjctl # Programmatic Zellij control
64 (pkgs.writeShellScriptBin "claude-notify" ''
65 PANE_ID="$ZELLIJ_PANE_ID"
66 SESSION="$ZELLIJ_SESSION_NAME"
67 (
68 ACTION=$(${pkgs.libnotify}/bin/notify-send "Claude Code" "Waiting for your approval" \
69 --app-name=claude-code \
70 --action=default=Open \
71 --wait)
72 if [ "$ACTION" = "default" ]; then
73 WIN_ID=$(niri msg windows | ${pkgs.gnugrep}/bin/grep -B1 "$SESSION" | ${pkgs.gnugrep}/bin/grep -oP '(?<=Window ID )\d+')
74 if [ -n "$WIN_ID" ]; then
75 niri msg action focus-window --id "$WIN_ID"
76 fi
77 zjctl pane focus --pane "id:terminal:$PANE_ID"
78 fi
79 ) &
80 '')
81 nautilus # file browser
82 sqlitebrowser
83 gnome-characters # symbol picker
84 sendme # file transfer
85 desktop-file-utils # for managing .desktop files
86 flyctl # fly.io cli
87 vscode-json-languageserver
88 gnome-network-displays
89 rainfrog # db tui
90 loupe # image viewer
91 glycin-loaders # various format loaders for loupe
92 docker-compose
93 discord
94 mangohud
95 prismlauncher # minecraft launcher
96 fastfetch
97 inputs.agenix.packages.${pkgs.system}.default # agenix CLI
98 age-plugin-yubikey # Yubikey support for agenix
99 # --- FONTS ARE IMPORTANT ---
100 # Berkeley Mono is the main system font, keeping JetBrains and Font Awesome for icons
101 font-awesome
102 noto-fonts
103 noto-fonts-cjk-sans
104 noto-fonts-color-emoji
105 nerd-fonts.jetbrains-mono
106 nerd-fonts.symbols-only
107 # --- POLKIT AGENT (for 1Password GUI, etc.) ---
108 lxqt.lxqt-policykit # Lightweight polkit agent
109 ];
110
111 programs.niri = {
112 enable = true;
113 settings = {
114 window-rules = [
115 {
116 geometry-corner-radius = {
117 top-left = 5.0;
118 top-right = 5.0;
119 bottom-left = 5.0;
120 bottom-right = 5.0;
121 };
122 clip-to-geometry = true;
123 draw-border-with-background = false;
124 }
125 ];
126 debug = {
127 honor-xdg-activation-with-invalid-serial = { };
128 };
129 layout = {
130 focus-ring = {
131 width = 2;
132 active.color = "#8caaee";
133 inactive.color = "#414559";
134 };
135 struts = {
136 top = -6;
137 bottom = -6;
138 left = 0;
139 right = 0;
140 };
141 gaps = 8;
142 };
143 gestures = {
144 hot-corners = {
145 enable = false;
146 };
147 };
148 binds = {
149 "Mod+d".action.spawn = [
150 "alacritty"
151 "-e"
152 "fsel"
153 "--detach"
154 ];
155 "Mod+c".action.spawn = [
156 "alacritty"
157 "-e"
158 "fsel"
159 "--cclip"
160 ];
161 "Mod+e".action.spawn = "bemoji";
162 "Mod+n".action.spawn = "networkmanager_dmenu";
163 "Mod+a".action.spawn = "alacritty";
164 "Mod+h".action = {
165 focus-column-left = { };
166 };
167 "Mod+j".action = {
168 focus-workspace-down = { };
169 };
170 "Mod+k".action = {
171 focus-workspace-up = { };
172 };
173 "Mod+l".action = {
174 focus-column-right = { };
175 };
176 "Mod+Shift+h".action = {
177 move-column-left = { };
178 };
179 "Mod+Shift+j".action = {
180 move-window-down-or-to-workspace-down = { };
181 };
182 "Mod+Shift+k".action = {
183 move-window-up-or-to-workspace-up = { };
184 };
185 "Mod+Shift+l".action = {
186 move-column-right = { };
187 };
188 "Mod+Down".action = {
189 move-workspace-down = { };
190 };
191 "Mod+Up".action = {
192 move-workspace-up = { };
193 };
194 "Mod+p".action = {
195 show-hotkey-overlay = { };
196 };
197 "Mod+o".action = {
198 toggle-overview = { };
199 };
200 "Mod+q".action = {
201 close-window = { };
202 };
203 "Mod+f".action = {
204 toggle-window-floating = { };
205 };
206 "Mod+Shift+f".action = {
207 switch-focus-between-floating-and-tiling = { };
208 };
209 "Mod+m".action = {
210 fullscreen-window = { };
211 };
212 "Mod+s".action = {
213 screenshot = {
214 show-pointer = true;
215 };
216 };
217 "Mod+1".action = {
218 set-column-width = "100%";
219 };
220 "Mod+2".action = {
221 set-column-width = "50%";
222 };
223 "Mod+Minus".action = {
224 set-column-width = "-10%";
225 };
226 "Mod+Equal".action = {
227 set-column-width = "+10%";
228 };
229 "Mod+Shift+q".action = {
230 quit = { };
231 };
232 "Mod+Shift+r".action.spawn = [
233 "systemctl"
234 "--user"
235 "restart"
236 "quickshell.service"
237 ];
238 "XF86AudioPlay".action.spawn = [
239 "playerctl"
240 "play-pause"
241 ];
242 "XF86AudioStop".action.spawn = [
243 "playerctl"
244 "stop"
245 ];
246 "XF86AudioNext".action.spawn = [
247 "playerctl"
248 "next"
249 ];
250 "XF86AudioPrev".action.spawn = [
251 "playerctl"
252 "previous"
253 ];
254 "XF86MonBrightnessDown".action.spawn = [
255 "brightnessctl"
256 "set"
257 "5%-"
258 ];
259 "XF86MonBrightnessUp".action.spawn = [
260 "brightnessctl"
261 "set"
262 "+5%"
263 ];
264 };
265 outputs = {
266 # External monitor - primary display at position (0, 0)
267 "DP-5" = {
268 scale = 2.0;
269 mode = {
270 width = 5120;
271 height = 2160;
272 refresh = 120.0;
273 };
274 position = {
275 x = 0;
276 y = 0;
277 };
278 };
279 "DP-1" = {
280 scale = 2.0;
281 mode = {
282 width = 5120;
283 height = 2160;
284 refresh = 120.0;
285 };
286 position = {
287 x = 0;
288 y = 0;
289 };
290 };
291 "DP-2" = {
292 scale = 1.0;
293 mode = {
294 width = 5120;
295 height = 2160;
296 refresh = 120.0;
297 };
298 position = {
299 x = 0;
300 y = 0;
301 };
302 };
303 "DP-6" = {
304 scale = 2.0;
305 mode = {
306 width = 5120;
307 height = 2160;
308 refresh = 120.0;
309 };
310 position = {
311 x = 0;
312 y = 0;
313 };
314 };
315 "DP-7" = {
316 scale = 2.0;
317 mode = {
318 width = 5120;
319 height = 2160;
320 refresh = 120.0;
321 };
322 position = {
323 x = 0;
324 y = 0;
325 };
326 };
327 # Laptop display - secondary display positioned underneath
328 "eDP-1" = {
329 scale = 1.5;
330 mode = {
331 width = 2560;
332 height = 1600;
333 refresh = 165.0;
334 };
335 position = {
336 x = 0;
337 y = 1080; # Position underneath the external monitor (2160 / 2 scale = 1080 logical height)
338 };
339 };
340 };
341 spawn-at-startup = [
342 { command = [ "xwayland-satellite" ]; }
343 { command = [ "cclipd" ]; }
344 ];
345 environment = {
346 DISPLAY = ":0";
347 };
348 };
349 };
350
351 # Allow unfree packages
352 nixpkgs.config.allowUnfree = true;
353
354 nixpkgs.config.permittedInsecurePackages = [
355 "libsoup-2.74.3"
356 ];
357
358 # Download wallpapers at activation time (skips dead URLs gracefully)
359 home.activation.downloadWallpapers =
360 let
361 wallpapers = import ./wallpapers.nix;
362 downloads = builtins.concatStringsSep "\n" (
363 map (wp: ''
364 if [ ! -f "$DIR/${wp.filename}" ]; then
365 echo "Downloading ${wp.filename}..."
366 ${pkgs.curl}/bin/curl -fsSL -o "$DIR/${wp.filename}" ${
367 builtins.replaceStrings [ "\"" ] [ "\\\"" ] wp.url
368 } || echo "WARNING: Failed to download ${wp.filename}, skipping"
369 fi
370 '') wallpapers
371 );
372 in
373 config.lib.dag.entryAfter [ "writeBoundary" ] ''
374 DIR="${config.home.homeDirectory}/Pictures/Wallpapers"
375 mkdir -p "$DIR"
376 ${downloads}
377 '';
378
379 # Kaleidux wallpaper daemon config
380 xdg.configFile."kaleidux/config.toml".text = ''
381 [global]
382 monitor-behavior = "independent"
383 video-ratio = 50
384 sorting = "loveit"
385 transition-time = 1000
386
387 [any]
388 path = "${config.home.homeDirectory}/Pictures/Wallpapers"
389 duration = "15m"
390 transition = { type = "fade" }
391 '';
392
393 # Quickshell status bar
394 xdg.configFile."quickshell" = {
395 source = ./quickshell;
396 recursive = true;
397 };
398
399 systemd.user.services.kaleidux = {
400 Unit = {
401 Description = "Kaleidux dynamic wallpaper daemon";
402 After = [ "graphical-session.target" ];
403 PartOf = [ "graphical-session.target" ];
404 };
405 Service = {
406 ExecStart = "${inputs.kaleidux.packages.${pkgs.system}.default}/bin/kaleidux-daemon";
407 Restart = "on-failure";
408 RestartSec = 2;
409 };
410 Install = {
411 WantedBy = [ "graphical-session.target" ];
412 };
413 };
414
415 systemd.user.services.quickshell = {
416 Unit = {
417 Description = "QuickShell status bar";
418 After = [ "graphical-session.target" ];
419 PartOf = [ "graphical-session.target" ];
420 };
421 Service = {
422 ExecStart = "${pkgs.quickshell}/bin/quickshell";
423 Restart = "on-failure";
424 RestartSec = 2;
425 };
426 Install = {
427 WantedBy = [ "graphical-session.target" ];
428 };
429 };
430
431 systemd.user.services.quickshell-reload = {
432 Unit = {
433 Description = "Reload QuickShell on wake or display change";
434 After = [
435 "quickshell.service"
436 "graphical-session.target"
437 ];
438 PartOf = [ "graphical-session.target" ];
439 };
440 Service = {
441 Type = "simple";
442 ExecStart = "${pkgs.writeShellScript "quickshell-reload" ''
443 LOCKFILE="/tmp/quickshell-reload.lock"
444
445 do_restart() {
446 (
447 ${pkgs.util-linux}/bin/flock -xn 200 || exit 0
448 sleep 2
449 ${pkgs.systemd}/bin/systemctl --user restart quickshell.service
450 sleep 3
451 ) 200>"$LOCKFILE"
452 }
453
454 # Sleep/wake monitor
455 ${pkgs.dbus}/bin/dbus-monitor --system \
456 "type='signal',interface='org.freedesktop.login1.Manager',member='PrepareForSleep'" 2>/dev/null | \
457 while IFS= read -r line; do
458 if [[ "$line" == *"boolean false"* ]]; then
459 do_restart
460 fi
461 done &
462
463 # Display hotplug monitor
464 ${pkgs.systemd}/bin/udevadm monitor --property --subsystem-match=drm 2>/dev/null | \
465 while IFS= read -r line; do
466 if [[ "$line" == *"HOTPLUG=1"* ]]; then
467 do_restart
468 fi
469 done &
470
471 wait
472 ''}";
473 Restart = "on-failure";
474 RestartSec = 5;
475 };
476 Install = {
477 WantedBy = [ "graphical-session.target" ];
478 };
479 };
480
481 programs.ssh = {
482 enable = true;
483 enableDefaultConfig = false;
484 matchBlocks = {
485 "*" = {
486 identityFile = [
487 "${config.home.homeDirectory}/.ssh/id_ed25519_sk_rk"
488 "${config.home.homeDirectory}/.ssh/id_rsa.pub"
489 ];
490 };
491 };
492 };
493
494 programs.awscli = {
495 enable = true;
496 settings = {
497 "default" = {
498 region = "us-east-1";
499 };
500 };
501 };
502
503 services.udiskie = {
504 enable = true;
505 tray = "never";
506 automount = true;
507 };
508
509 services.mako = {
510 enable = true;
511 settings = {
512 border-radius = 8;
513 border-size = 2;
514 padding = "12";
515 margin = "12";
516 font = "BerkeleyMono Nerd Font 11";
517 on-button-left = "invoke-default-action";
518 on-button-right = "dismiss";
519 };
520 };
521
522 catppuccin = {
523 enable = true;
524 flavor = "frappe";
525 };
526
527 programs.direnv.enable = true;
528
529 programs.atuin = {
530 enable = true;
531 enableFishIntegration = true;
532 daemon.enable = true;
533 settings = {
534 filter_mode_shell_up_key_binding = "session";
535 };
536 };
537
538 programs.zellij = {
539 enable = true;
540 settings = {
541 keybinds = {
542 unbind = [
543 "Ctrl q"
544 "Ctrl o"
545 ];
546 normal = {
547 "bind \"Ctrl m\"" = {
548 SwitchToMode = "Session";
549 };
550 };
551 };
552 load_plugins = {
553 "\"file:~/.config/zellij/plugins/zrpc.wasm\"" = {};
554 };
555 pane_frames = false;
556 show_startup_tips = false;
557 ui = {
558 pane_frames.hide_session_name = true;
559 };
560 };
561 };
562
563 xdg.configFile."zellij/plugins/zrpc.wasm".source = "${zrpc-wasm}/zrpc.wasm";
564
565 home.file.".claude/settings.json".text = builtins.toJSON {
566 enabledPlugins = {
567 "rust-analyzer-lsp@claude-plugins-official" = true;
568 "typescript-lsp@claude-plugins-official" = true;
569 };
570 hooks = {
571 Notification = [
572 {
573 matcher = "permission_prompt";
574 hooks = [{ type = "command"; command = "claude-notify"; }];
575 }
576 ];
577 };
578 };
579
580 xdg.configFile."zellij/layouts/split.kdl".text = ''
581 layout {
582 tab {
583 pane size="50%"
584 pane split_direction="vertical" size="50%" {
585 pane
586 pane
587 }
588 }
589 }
590 '';
591
592 xdg.configFile."gh-dash/config.yml".text = ''
593 prSections:
594 - title: My Pull Requests
595 filters: is:open author:@me
596 - title: Review Requested
597 filters: is:open review-requested:@me
598 issuesSections:
599 - title: My Issues
600 filters: is:open author:@me
601 pager:
602 diff: diffnav
603 keybindings:
604 prs:
605 - key: T
606 name: enhance
607 command: >-
608 zellij run -- gh enhance -R {{.RepoName}} {{.PrNumber}}
609 '';
610
611 programs.zen-browser.enable = true;
612 # programs.swww.enable = true;
613 programs.zoxide = {
614 enable = true;
615 enableFishIntegration = true;
616 };
617
618 programs.obs-studio = {
619 enable = true;
620 plugins = with pkgs.obs-studio-plugins; [
621 obs-backgroundremoval
622 ];
623 };
624
625 # Program configurations
626 programs.mpv = {
627 enable = true;
628 scripts = [ pkgs.mpvScripts.mpris ];
629 config.af = "loudnorm=I=-16:TP=-1.5:LRA=11";
630 };
631 xdg.configFile."youtube-tui/commands.yml".source = ./youtube-tui/commands.yml;
632 programs.git = {
633 enable = true;
634 settings = {
635 user = {
636 name = "seanaye";
637 email = "hello@seanaye.ca";
638 };
639 init.defaultBranch = "main";
640 commit.gpgSign = true;
641 gpg.format = "ssh";
642 user.signingKey = "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIOIgEteUEW06dnBHe2z8vNLwz2iMKe8bba6JgMmOUpcBAAAABHNzaDo= sean@framework16";
643 gpg.ssh.allowedSignersFile = "${config.home.homeDirectory}/.ssh/allowed_signers";
644 diff.tool = "diffnav";
645 difftool.prompt = false;
646 "difftool \"diffnav\"".cmd = "diffnav \"$LOCAL\" \"$REMOTE\"";
647 };
648 };
649 programs.jujutsu = {
650 enable = true;
651 settings = {
652 user = {
653 email = "hello@seanaye.ca";
654 name = "Sean Aye";
655 };
656 ui."diff-formatter" = ":git";
657 signing = {
658 sign-all = true;
659 behavior = "own";
660 backend = "ssh";
661 key = "${config.home.homeDirectory}/.ssh/id_ed25519_sk_rk";
662 backends.ssh.allowed-signers = "${config.home.homeDirectory}/.ssh/allowed_signers";
663 };
664 };
665 };
666 xdg.configFile."jj/conf.d/diffnav.toml".text = ''
667 [[--scope]]
668 --when.commands = ["diff", "show"]
669 [--scope.ui]
670 pager = "diffnav"
671 '';
672
673 programs.home-manager.enable = true;
674
675 programs.fish = {
676 enable = true;
677 shellAliases = {
678 agenix = "agenix -i ~/.config/agenix/yubikey-identity.txt";
679 };
680 interactiveShellInit = ''
681 set fish_greeting
682 # Set 1Password SSH agent socket
683 set -gx SSH_AUTH_SOCK ${config.home.homeDirectory}/.1password/agent.sock
684 # Load 1Password CLI plugins
685 if test -f ~/.config/op/plugins.sh
686 source ~/.config/op/plugins.sh
687 end
688 # Auto-launch zellij if not already inside a session
689 if not set -q ZELLIJ
690 zellij
691 else
692 fastfetch --logo small
693 end
694
695 function y
696 set tmp (mktemp -t "yazi-cwd.XXXXXX")
697 yazi $argv --cwd-file="$tmp"
698 if read -z cwd < "$tmp"; and [ -n "$cwd" ]; and [ "$cwd" != "$PWD" ]
699 builtin cd -- "$cwd"
700 end
701 rm -f -- "$tmp"
702 end
703 '';
704 functions = {
705 s3edit = ''
706 set file (basename $argv[1])
707 set tmpfile /tmp/$file
708 aws s3 cp $argv[1] $tmpfile
709 and $EDITOR $tmpfile
710 and aws s3 cp $tmpfile $argv[1]
711 '';
712 };
713 };
714
715 programs.starship = {
716 enable = true;
717 enableFishIntegration = true;
718 };
719
720 programs.alacritty = {
721 enable = true;
722 settings = {
723 terminal.shell.program = "fish";
724 window = {
725 decorations = "none";
726 opacity = 0.9;
727 };
728 font = {
729 normal = {
730 family = "BerkeleyMono Nerd Font";
731 style = "Regular";
732 };
733 size = 12.0;
734 };
735 };
736
737 };
738
739 programs.helix = {
740 enable = true;
741 settings = {
742 editor = {
743 bufferline = "multiple";
744 file-picker = {
745 hidden = false;
746 git-ignore = true;
747 };
748 cursor-shape = {
749 insert = "bar";
750 normal = "block";
751 select = "underline";
752 };
753 line-number = "relative";
754 cursorline = true;
755 auto-format = true;
756 end-of-line-diagnostics = "hint";
757 soft-wrap = {
758 enable = true;
759 };
760 lsp = {
761 display-inlay-hints = true;
762 display-messages = true;
763 display-progress-messages = true;
764 };
765 inline-diagnostics = {
766 cursor-line = "hint";
767 };
768 };
769 keys = {
770 normal = {
771 esc = [
772 "keep_primary_selection"
773 "collapse_selection"
774 ];
775 };
776
777 };
778 };
779 languages = {
780
781 language-server.rust-analyzer = {
782 config = {
783 check = {
784 command = "clippy";
785 };
786 checkOnSave = true;
787 cargo = {
788 allFeatures = true;
789 };
790 };
791 };
792 language-server.deno-lsp = {
793 command = "deno";
794 args = [ "lsp" ];
795 config.deno.enable = true;
796 };
797
798 language = [
799 {
800 name = "html";
801 formatter = {
802 command = "prettier";
803 args = [
804 "--parser"
805 "html"
806 ];
807 };
808 }
809 {
810 name = "nix";
811 auto-format = true;
812 formatter = {
813 command = "${pkgs.nixfmt}/bin/nixfmt";
814 };
815 }
816 {
817 name = "kotlin";
818 auto-format = true;
819 }
820 {
821 name = "rust";
822 auto-format = true;
823 formatter = {
824 command = "rustfmt";
825 args = [
826 "--edition"
827 "2024"
828 ];
829 };
830 indent = {
831 tab-width = 4;
832 unit = "t";
833 };
834 }
835 {
836 name = "astro";
837 auto-format = true;
838 formatter = {
839 command = "npx";
840 args = [
841 "prettier"
842 "--plugin"
843 "prettier-plugin-astro"
844 "--parser"
845 "astro"
846 ];
847 };
848 }
849 {
850 name = "json";
851 auto-format = true;
852 }
853 {
854 name = "just";
855 auto-format = true;
856 formatter = {
857 command = "just";
858 args = [
859 "--justfile"
860 "/dev/stdin"
861 "--dump"
862 ];
863 };
864 }
865 {
866 name = "toml";
867 auto-format = true;
868 formatter = {
869 command = "taplo";
870 args = [
871 "format"
872 "-"
873 ];
874 };
875 }
876 # {
877 # name = "typescript";
878 # roots = [
879 # "deno.json"
880 # "deno.jsonc"
881 # ];
882 # file-types = [
883 # "ts"
884 # "tsx"
885 # ];
886 # auto-format = true;
887 # language-servers = [ "deno-lsp" ];
888 # }
889 ];
890 };
891 };
892
893 dconf.settings = {
894 "org/gnome/desktop/interface" = {
895 color-scheme = "prefer-dark";
896 enable-hot-corners = false;
897 };
898 };
899
900 # Font rendering configuration
901 fonts.fontconfig = {
902 enable = true;
903 defaultFonts = {
904 monospace = [ "BerkeleyMono Nerd Font" ];
905 sansSerif = [ "Noto Sans" ];
906 serif = [ "Noto Serif" ];
907 };
908 };
909
910 # Cursor configuration
911 home.pointerCursor = {
912 name = "Adwaita";
913 package = pkgs.adwaita-icon-theme;
914 size = 16;
915 x11.enable = true;
916 gtk.enable = true;
917 };
918
919 # Session variables
920 home.sessionVariables = {
921 EDITOR = "hx";
922 VISUAL = "hx";
923 SUDO_EDITOR = "hx";
924 SSH_AUTH_SOCK = "${config.home.homeDirectory}/.1password/agent.sock";
925 SSH_ASKPASS = "${pkgs.openssh-askpass}/bin/gnome-ssh-askpass3";
926 SSH_ASKPASS_REQUIRE = "prefer";
927 };
928
929 # SSH allowed signers for commit signature verification
930 home.file.".ssh/allowed_signers".text = ''
931 hello@seanaye.ca ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCIqgZ7kedxo+mOW7YG73Vp3zel3h180y3GKvHtRsXfGlpIIvRDy7pgCBQ4AGXYD4y78URQmFohYSAPqCPOPaWcU2un3XG9KvCzEsHmsbskPonitUmCiKvrKkb6oW4jCBtd7AEtBn+AiajAQFtPZ7NN2Df3AmTypvR6Irg7R+nxnfc9NTIHmGvxSDyWcbb4pguL20sctUSqGL6xGh8q/bqhdOThSimM+z9bEUNxK/5rPhwkNniMrp4pJcUrUiAh5/4DiRFG6KT+oeg+/myoz/Z1sPvAs7u/8JDQI4RshRD8Hu0oTkRBN6Hxj478q2SXbeBUZlD6IdjP3RhGpmSecoDdtWqKbpuV3eVRtQtba3KL86GBeV/bugaOdJ1Aud+1SOFJreAAuvxzMMKT+cdQZk6oOPP148DA/No+mDm/2S43lcdCXh79wA6YRAmKQ8jmZxTCtPutrvuZK1rguvvUlEoG/vhdNHh7eDa4Td07V6bjCRPUl8qk/e4M0E3pwsTlZc=
932 hello@seanaye.ca sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAILdilHXHdAP/V8Zq28EzHKtLAMMaFPu4+1det2N50QfhAAAABHNzaDo= sean@framework16
933 '';
934
935 # Yubikey identity for agenix (not secret - just a reference to the hardware key)
936 home.file.".config/agenix/yubikey-identity.txt".text = ''
937 # Serial: 26930059, Slot: 1
938 # Name: agenix
939 # Recipient: age1yubikey1qw64ag5lzvn9ekrflu5ruj4a6ucycscl6ctk39fjzf76jptsay39z442pxv
940 AGE-PLUGIN-YUBIKEY-1304E5QVZZD74FKSP8FMCT
941 '';
942
943 # Same identity for sops (expects keys at this path by default)
944 home.file.".config/sops/age/keys.txt".text = ''
945 # Serial: 26930059, Slot: 1
946 # Name: agenix
947 # Recipient: age1yubikey1qw64ag5lzvn9ekrflu5ruj4a6ucycscl6ctk39fjzf76jptsay39z442pxv
948 AGE-PLUGIN-YUBIKEY-1304E5QVZZD74FKSP8FMCT
949 '';
950
951 # yubikey sudo access
952 home.file.".config/Yubico/u2f_keys".text = ''
953 sean:2HY//CedY0ZSrKf57lT7abxG8+8bkPyxCfp/0HMlk/il/5W8pn4R5xLiZDcJtvL85U24h9IEIxa4CS22mpaDSA==,gcD/dpLdwvUFcGGPHS4qNsarH4lOEy1AJAT7zoC6BPlFRUYEa8DpVVKFTcvT6PotjnSHSrWWGb/f3U2k2jIOIw==,es256,+presence
954 '';
955
956 # Set the state version for Home Manager
957 home.stateVersion = "25.05";
958}