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