main
  1_: {
  2  ssh = {
  3    # Keyed SSH registry. Each entry is `{ key; access; }` where
  4    # `access.<account>.<host|default>` resolves to one of:
  5    #   "trusted"        -> full unrestricted login
  6    #   { gated = "a"; } -> command="praetorian run a" restricted (see carthage)
  7    #   absent           -> no access on that host/account
  8    # Rendered via libx.authorizedKeysFor. Every entry MUST declare a non-empty
  9    # `access` (eval-time throw otherwise) so a key can never be silently
 10    # over-provisioned. Greppable unrestricted surface: `grep '"trusted"'`.
 11    vincent = {
 12      # Yubikeys (PIV - legacy, keep during transition)
 13      # TODO: phase these out once FIDO2/TPM coverage is complete.
 14      yubikey-piv-1 = {
 15        key = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFT5Rx+4Wuvd8lMBkcHxb4oHdRhm/OTg+p5tvPzoIN9enSmgRw5Inm/SlS8ZzV87G1NESTgzDRi6hREvqDlKvxs=";
 16        access = {
 17          vincent.default = "trusted";
 18          root.default = "trusted";
 19        };
 20      };
 21      yubikey-piv-2 = {
 22        key = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGHMa4rHuBbQQYv+8jvlkFCD2VYRGA4+5fnZAhLx8iDirzfEPqHB60UJWcDeixnJCUlpJjzFbS4crNOXhfCTCTE=";
 23        access = {
 24          vincent.default = "trusted";
 25          root.default = "trusted";
 26        };
 27      };
 28      yubikey-piv-3 = {
 29        key = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBFzxC16VqwTgWDQfw2YCiOw2JzpH3z9XgHtKoHhBdHi2i9m9XUc7fIUeEIIf7P8ARRNd8q5bjvl8JY7LtPkNCU=";
 30        access = {
 31          vincent.default = "trusted";
 32          root.default = "trusted";
 33        };
 34      };
 35      # FIDO2 resident keys (touch is the gate -> unrestricted)
 36      homelab-servers = {
 37        key = "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIODTc5Exm59skgJdu6/rA3CpX4k4P1CFBqCFtelWGGmEAAAAC3NzaDpob21lbGFi homelab-servers";
 38        access = {
 39          vincent.default = "trusted";
 40          root.default = "trusted";
 41        };
 42      };
 43      critical-infra = {
 44        key = "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIAGh5p44LvQrWjAMyC/5LjUnViqFl3ddVfiFnoiLgJb7AAAAEnNzaDpjcml0aWNhbC1pbmZyYQ== infra-touch-required";
 45        access = {
 46          vincent.default = "trusted";
 47          root.default = "trusted";
 48        };
 49      };
 50      okinawa-fido = {
 51        key = "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEefW7gStvkrO98v6UUawwa3yOu896Ei8USE/Sh2DjaUAAAABHNzaDo= vincent@okinawa";
 52        access = {
 53          vincent.default = "trusted";
 54          root.default = "trusted";
 55        };
 56      };
 57      # Passage sync keys (non-SK, automated git pull only)
 58      # TODO: replace with the per-machine TPM approach.
 59
 60      # okinawa user key: TPM-sealed ecdsa via ssh-tpm-agent (machine-bound, no touch)
 61      okinawa-tpm = {
 62        key = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBODwU2/sE+S51ShfL8t+QA9IMcVuQL+VLYuENUc5VDVD4AVoVj97BggowbAhtqDW6hdc0S3XSVrZs+aOSd9ukbg= vincent@okinawa-tpm";
 63        access = {
 64          vincent.default = "trusted";
 65          root.default = "trusted";
 66        };
 67      };
 68      # aomi user key: TPM-sealed ecdsa via ssh-tpm-agent (machine-bound, no touch)
 69      # Gated to git push+pull on carthage only.
 70      aomi-tpm = {
 71        key = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGEL1A++UUsFnGmnrIR8S6A8IUFkR5WyxDPol2bs02TY0O+sWOPJFJZIAKqcWVSUIkxRoktUBMRe0mMZyiHa3jw= vdemeest@aomi-tpm";
 72        access.vincent = {
 73          carthage = {
 74            gated = "aomi-git";
 75          };
 76          kyushu = "trusted";
 77        };
 78      };
 79      aomi-nagoya-builder = {
 80        key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIuiAFmNPzh+hpnLfjCauNQWIuQ4JRBT7cD/y/5zjmZK aomi-nagoya-builder";
 81        access.vincent.nagoya = "trusted";
 82      };
 83      kyushu-nagoya-builder = {
 84        key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINkw0Oc5ZQWIOSAykMb37B1xqViH1Nt8Ol+bMhE/qzK8 kyushu-nagoya-builder";
 85        access.vincent.nagoya = "trusted";
 86      };
 87      boox-moshi = {
 88        key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICuaKzLeP5TasvkLucaqM/SDxP07B3zqVetuFz5+ZXQS moshi";
 89        access.vincent.aomi = "trusted";
 90      };
 91      kyushu-tpm = {
 92        key = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBc8oKSsUPsgbOB+yrqLpxdI3lt0ONhlHhTwbnrobSgDat5J/w0ZOsHJUPNU/O2Hyqd9l0UXz3d7rnwfN1sO0tQ= vdemeest@kyushu-tpm";
 93        access.vincent = {
 94          carthage = {
 95            gated = "kyushu-git";
 96          };
 97          aomi = "trusted";
 98        };
 99      };
100      # aion host key: has automation (restic backup to aix).
101      # TODO: scope to aix + gate restic instead of full trust everywhere.
102      aion-hostkey = {
103        key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE3iD9Eaf5xglTyP+kIO9t8qQpF2H42rQ2AuUibs2hn7 vincent@aion";
104        access = {
105          vincent.default = "trusted";
106          root.default = "trusted";
107        };
108      };
109      # kyushu host key.
110      # TODO: work on removing this key entirely.
111      kyushu-hostkey = {
112        key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAJOJ/Kd3KGVbd2QoOC+FqKxmNHLmrAEkP6lH0yqx1nO Kyushu's ssh key";
113        access = {
114          vincent.default = "trusted";
115          root.default = "trusted";
116        };
117      };
118      # Termux phones: git pull only (src/home, passage, ... under ~/git).
119      osaka-termux = {
120        key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICa0SyAspL7PBPudCjb7oCBG17WRmYnDQF7/BYkFwqDi oksaka-termux";
121        access.vincent.carthage = {
122          gated = "git-pull";
123        };
124      };
125      suzu-termux = {
126        key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0Q1oXzMMJG03n2VoxKKOGruyGBy8V8yanqgeeUYm+N suzu-termux";
127        access.vincent.carthage = {
128          gated = "git-pull";
129        };
130      };
131    };
132  };
133  syncthingFolders = {
134    sync = {
135      id = "7dshg-r8zr6";
136      path = "/home/vincent/sync";
137    };
138    documents = {
139      id = "oftdb-t5anv";
140      path = "/home/vincent/desktop/documents";
141    };
142    org = {
143      id = "sjpsr-xfwdu";
144      path = "/home/vincent/desktop/org";
145      versioning = {
146        type = "staggered";
147        params = {
148          cleanInterval = "3600"; # cleanup every hour
149          maxAge = "15768000"; # keep for ~6 months (182 days in seconds)
150        };
151      };
152    };
153    screenshots = {
154      id = "prpsz-azlz9";
155      path = "/home/vincent/desktop/pictures/screenshots";
156    };
157    wallpapers = {
158      id = "wpiah-ydwwx";
159      path = "/home/vincent/desktop/pictures/wallpapers";
160    };
161    claude-sync = {
162      id = "claude-sync"; # new consolidated folder
163      path = "/home/vincent/.local/share/claude-sync";
164    };
165    ai-sync = {
166      id = "ai-sync"; # unified AI agent storage (sessions, plans, learnings, research)
167      path = "/home/vincent/.local/share/ai-sync";
168    };
169    usage-metrics = {
170      id = "usage-metrics";
171      path = "/home/vincent/.local/share/usage-metrics";
172    };
173    paperless-media = {
174      id = "paperless-media";
175      path = "/neo/paperless/media";
176      versioning = {
177        type = "staggered";
178        params = {
179          cleanInterval = "3600"; # cleanup every hour
180          maxAge = "15768000"; # keep for ~6 months (182 days in seconds)
181        };
182      };
183    };
184    paperless-inbox = {
185      id = "paperless-inbox";
186      path = "/neo/paperless/consume";
187      # No versioning - temporary files get processed and deleted
188    };
189  };
190  net = {
191    dns = {
192      cacheNetworks = [
193        "192.168.1.0/24"
194        "10.100.0.0/24"
195      ];
196    };
197    vpn = {
198      endpoint = "46.224.100.116";
199    };
200    # Travel tunnel: lutece (Paris) as a full-tunnel exit node, used to get a
201    # French IP from abroad. Separate subnet and separate interface (wg1) from
202    # the 10.100.0.0/24 homelab mesh.
203    #
204    # Traffic from these peers is masqueraded on lutece, so the rest of the
205    # mesh sees 10.100.0.3 as the source and needs no route for 10.101.0.0/24.
206    travel = {
207      subnet = "10.101.0.0/24";
208      server = "lutece";
209      # Public key of /etc/wireguard/travel.key on lutece, for client configs.
210      pubkey = "d28ha+85FlP188kDkCDMX6I8iM20PtbTy6kj0sHvHwo=";
211      # name -> travel IP. Public keys are reused from the machine's mesh
212      # identity (globals.machines.<name>.net.vpn.pubkey); last octet mirrors
213      # the machine's VPN address for readability.
214      peers = {
215        hokkaido = "10.101.0.5";
216        osaka = "10.101.0.64";
217        suzu = "10.101.0.65";
218        houbeb-makcbook-pro = "10.101.0.71";
219      };
220    };
221    nfs = {
222      defaultHosts = {
223        rhea = {
224          server = "rhea.sbr.pm";
225          folders = [
226            "downloads"
227            "pictures"
228            "videos"
229          ];
230        };
231        zion = {
232          server = "rhea.sbr.pm";
233          rootPath = "/zion";
234          folders = [
235            "audiobooks"
236            "documents"
237            "ebooks"
238            "music"
239          ];
240        };
241        aion = {
242          server = "aion.sbr.pm";
243          folders = [
244            "audiobooks"
245            "music"
246          ];
247        };
248      };
249    };
250  };
251  machines = {
252    athena = {
253      net = {
254        ips = [ "192.168.1.183" ];
255        vpn = {
256          pubkey = "RWqH7RdIXg+YE9U1nlsNiOC7jH8eWjWQmikqBVDGSXU=";
257          ips = [ "10.100.0.83" ];
258        };
259        names = [
260          "athena.home"
261          "athena.vpn"
262          "athena.sbr.pm"
263        ];
264      };
265      ssh = {
266        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM/4KRP1rzOwyA2zP1Nf1WlLRHqAGutLtOHYWfH732xh";
267        # root = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFQVlSrUKU0xlM9E+sJ8qgdgqCW6ePctEBD2Yf+OnyME root@aomiy";
268        # vincent = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILJmTdMKYdgqpbQWBif58VBuwX+GqMGsMfB1ey1TKrM3 vincent@aomi";
269      };
270      # syncthing = {
271      #   id = "N3AMUVI-FM2BAOD-U3OMZDJ-UHMQE6J-ACMM5B7-S7BTK6P-PSM36NR-DVZHLQF";
272      #   folders = {
273      #     sync = {
274      #       type = "receiveonly";
275      #     };
276      #   };
277      # };
278    };
279    demeter = {
280      net = {
281        ips = [ "192.168.1.182" ];
282        vpn = {
283          pubkey = "/bBh4gvDty/AA2qIiHc7K0OHoOXWmj2SFFXdDq8nsUU=";
284          ips = [ "10.100.0.82" ];
285        };
286        names = [
287          "demeter.home"
288          "demeter.vpn"
289          "demeter.sbr.pm"
290        ];
291      };
292      ssh = {
293        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGqQfEyHyjIGglayB9FtCqL7bnYfNSQlBXks2IuyCPmd";
294      };
295      # syncthing = {
296      #   id = "TXCV3TS-TUEOTH6-ETB3LBV-KCIHT4L-RCCOIE3-VPBCNJB-VHQEAYI-WOXK5A5";
297      #   folders = {
298      #     sync = {
299      #       type = "receiveonly";
300      #     };
301      #   };
302      # };
303    };
304    nagoya = {
305      net = {
306        ips = [ "192.168.1.80" ];
307        vpn = {
308          pubkey = "NCj5pwShre/xyRCK800ybjso1zIYUZ08YvFA2qGzhAI=";
309          ips = [ "10.100.0.80" ];
310        };
311        names = [
312          "nagoya.home"
313          "nagoya.vpn"
314          "nagoya.sbr.pm"
315        ];
316      };
317      ssh = {
318        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIfep1SkMsAPHggXFLfEJNzZb7eoihtkqDeQruG+TbhF";
319      };
320      # syncthing = {
321      #   id = "HZDLS5A-LKCEIYQ-DDMDYDF-DBTSRYH-HUNQSII-TVCDACT-DIYIO7V-G4K2EQV";
322      #   folders = {
323      #     sync = {
324      #       type = "receiveonly";
325      #     };
326      #   };
327      # };
328    };
329    aix = {
330      net = {
331        vpn = {
332          pubkey = "D1HoBqrqBchHOOi8mjKpVg5vZtt+iFy8wj4o3kGYwkc=";
333          ips = [ "10.100.0.89" ];
334        };
335        names = [
336          "aix.vpn"
337          "aix.sbr.pm"
338        ];
339      };
340      syncthing = {
341        id = "GHE6XF4-YCKEMZS-JEZYXA6-ETJI3SS-BQFFOCS-ZJAWN4D-Q33IQ46-OYL7BQM";
342        folders = {
343          sync = {
344            type = "receiveonly";
345          };
346        };
347      };
348      ssh = {
349        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEoUicDySCGETPAgmI0P3UrgZEXXw3zNsyCIylUP0bML";
350      };
351    };
352    kyushu = {
353      user = "vdemeest";
354      net = {
355        ips = [
356          "192.168.1.36"
357          "192.168.1.68"
358        ];
359        vpn = {
360          pubkey = "KVRzoPUw8UTQblYtbs/NLYLIVmtQehrc4Hacbpf5Ugs=";
361          ips = [ "10.100.0.19" ];
362        };
363        names = [
364          "kyushu.home"
365          "kyushu.vpn"
366          "kyushu.sbr.pm"
367        ];
368      };
369      ssh = {
370        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAJOJ/Kd3KGVbd2QoOC+FqKxmNHLmrAEkP6lH0yqx1nO";
371      };
372      syncthing = {
373        id = "G6EVI7A-WFXEAI2-ZK3BI5C-7FZJNQH-PIPBHSC-57VZDKU-HN3WBCX-A757UQJ";
374        folders = {
375          org = { };
376          documents = { };
377          sync = { };
378          screenshots = { };
379          wallpapers = { };
380          claude-sync = { };
381          ai-sync = { };
382          usage-metrics = { };
383          paperless-media = {
384            type = "receiveonly";
385            path = "/home/vincent/desktop/paperless-media";
386          };
387          paperless-inbox = {
388            path = "/home/vincent/desktop/paperless-inbox";
389          };
390          # TODO: implement paused or filter theses
391          # photos = {
392          #   type = "receiveonly";
393          #   paused = true; # TODO: implement this, start as paused
394          # };
395          # music = {
396          #   type = "receiveonly";
397          #   paused = true; # TODO: implement this, start as paused
398          # };
399        };
400      };
401    };
402    aomi = {
403      user = "vdemeest";
404      net = {
405        ips = [ "192.168.1.23" ];
406        vpn = {
407          pubkey = "Pmu/0CjRaClmz+v25OV3SLd/V6Il8YLLavbtu8cYSGY=";
408          ips = [ "10.100.0.17" ];
409        };
410        names = [
411          "aomi.home"
412          "aomi.vpn"
413          "aomi.sbr.pm"
414        ];
415      };
416      ssh = {
417        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFL4OaoBtd/BzNeQvjB9AdYUiADk2PS0cKNmXu+PgkYY";
418      };
419      syncthing = {
420        id = "S6UM56N-TI2Z73H-IKO6F5Y-652ENJL-5BICIVD-JGFXED3-7JEJLZ2-XEIEXAL";
421        folders = {
422          sync = { };
423          org = { };
424          screenshots = { };
425          wallpapers = { };
426          claude-sync = { };
427          ai-sync = { };
428          usage-metrics = { };
429        };
430      };
431    };
432    shikoku = {
433      net = {
434        ips = [ "192.168.1.24" ];
435        vpn = {
436          pubkey = "foUoAvJXGyFV4pfEE6ISwivAgXpmYmHwpGq6X+HN+yA=";
437          ips = [ "10.100.0.2" ];
438        };
439        names = [
440          "shikoku.home"
441          "shikoku.vpn"
442          "shikoku.sbr.pm"
443        ];
444      };
445      ssh = {
446        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH18c6kcorVbK2TwCgdewL6nQf29Cd5BVTeq8nRYUigm";
447        vincent = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGxstR3xEf87leVVDS3GVPx8Ap9+eP+OfkSvM26V54XP vincent@shikoku";
448      };
449    };
450    # Scaleway Dedibox VPS (VPS-START-2-S, Paris) — see infra/lutece
451    # Mesh client on wg0 (10.100.0.3) and travel exit server on wg1
452    # (see globals.net.travel).
453    lutece = {
454      net = {
455        vpn = {
456          pubkey = "H7q7Qz7JqAKXlEbNMBW3quac2GDASAZ0SYvCvJ96NTo=";
457          ips = [ "10.100.0.3" ];
458        };
459        names = [
460          "lutece.vpn"
461          "lutece.sbr.pm"
462        ];
463      };
464      ssh = {
465        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII0QxlxWPQfd4t8IxqhLKFRXPVfrRSJBpUSXipIcKOVm"; # ssh-keyscan -q -t ed25519 64.31.63.130
466      };
467    };
468    # Hetzner Cloud VPS
469    # TODO: Update pubkey, hostKey, and syncthing id after provisioning
470    carthage = {
471      net = {
472        vpn = {
473          pubkey = "PQD1dtxhy9NMbmfy2OQPeLiQg0Alcfa1Mo4HVN5WqgA=";
474          ips = [ "10.100.0.1" ]; # VPN server
475        };
476        names = [
477          "carthage.vpn"
478          "carthage.sbr.pm"
479        ];
480      };
481      ssh = {
482        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHDurbEy1PiidOirbiPXz84ySdv3rwosPTAlCqacc73a";
483      };
484      # syncthing = {
485      #   id = "PLACEHOLDER_UNTIL_INSTALL";
486      #   folders = {
487      #     sync = { };
488      #   };
489      # };
490    };
491    sakhalin = {
492      net = {
493        ips = [ "192.168.1.70" ];
494        vpn = {
495          pubkey = "OAjw1l0z56F8kj++tqoasNHEMIWBEwis6iaWNAh1jlk=";
496          ips = [ "10.100.0.16" ];
497        };
498        names = [
499          "sakhalin.home"
500          "sakhalin.vpn"
501          "sakhalin.sbr.pm"
502        ];
503      };
504      ssh = {
505        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN/PMBThi4DhgZR8VywbRDzzMVh2Qp3T6NJAcPubfXz6";
506      };
507      syncthing = {
508        id = "3L2KCXM-D75XCVU-5JLMV6V-FKQID2K-LJA6GFB-R2G77LD-5WXFHJT-BB4B7Q5";
509        folders = {
510          org = { };
511          documents = { };
512          sync = { };
513          screenshots = { };
514          wallpapers = { };
515          # photos = {
516          #   type = "receiveonly";
517          #   paused = true; # TODO: implement this, start as paused
518          # };
519        };
520      };
521    };
522    rhea = {
523      net = {
524        ips = [ "192.168.1.50" ];
525        vpn = {
526          pubkey = "QBGdlPgtaLIh+WDLbuIWPL+Nr08mtfIqs6RwgVDAGjA=";
527          ips = [ "10.100.0.50" ];
528        };
529        names = [
530          "rhea.home"
531          "rhea.vpn"
532          "rhea.sbr.pm"
533        ];
534      };
535      ssh = {
536        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKFH3Lk4bRgNyFRK/Hzg1PvVbL/dpyI1SmLJFkb6VQDw";
537      };
538      syncthing = {
539        id = "73YP3XV-X3YLVBD-4KL77TS-DCFFXJ7-AYJDF3R-CJDRZNP-7YFPQNT-FKHJ7QW";
540        folders = {
541          org = { };
542          documents = { };
543          sync = { };
544          screenshots = {
545            path = "/neo/pictures/vincent/screenshots";
546          };
547          wallpapers = {
548            path = "/neo/pictures/vincent/wallpapers";
549          };
550          paperless-media = {
551            type = "receiveonly";
552            path = "/neo/paperless/media";
553          };
554          paperless-inbox = {
555            path = "/neo/paperless/consume";
556          };
557        };
558      };
559    };
560    aion = {
561      net = {
562        ips = [ "192.168.1.49" ];
563        vpn = {
564          pubkey = "Z145TsRbQTASEeAXsGkQR/PGvdI5rhHAXA5wllCKMGI=";
565          ips = [ "10.100.0.49" ];
566        };
567        names = [
568          "aion.home"
569          "aion.vpn"
570          "aion.sbr.pm"
571        ];
572      };
573      ssh = {
574        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAXDNi2KtoRU83y/V5OWnMbFWmxwBknPmrNWV4RChE7R";
575      };
576      syncthing = {
577        id = "YBQ6U6T-TMCZVUG-JNHGLRH-CZNZMHG-GQICVTK-LUXJR3H-KR2SXS7-CTNQCAU";
578        folders = {
579          org = { };
580          sync = { };
581          paperless-media = {
582            path = "/neo/paperless/media";
583          };
584          paperless-inbox = {
585            path = "/neo/paperless/consume";
586          };
587        };
588      };
589    };
590    synodine = {
591      net = {
592        ips = [ "192.168.1.20" ];
593        names = [
594          "synodine.home"
595          "synodine.sbr.pm"
596        ];
597      };
598      ssh = {
599        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDWdnPJg0Y4kd4lHPAGE4xgMAK2qvMg3oBxh0t+xO+7O";
600      };
601    };
602    wakasu = {
603      net = {
604        vpn = {
605          ips = [ "10.100.0.8" ];
606        };
607        names = [
608          "wakasu.vpn"
609          "wakasu.sbr.pm"
610        ];
611      };
612      # syncthing = {
613      #   id = "WM23THJ-ECXRLXA-HE5TIKO-VPLSMRY-Y2EWZI7-Q7JMLPX-5Q5UNEN-QMB7ZQJ";
614      #   folders = {
615      #     org = { };
616      #     documents = { };
617      #     sync = { };
618      #     screenshots = { };
619      #     wallpapers = { };
620      #   };
621      # };
622    };
623    # Home Assistant
624    hass = {
625      net = {
626        ips = [ "192.168.1.181" ];
627        vpn = {
628          ips = [ "10.100.0.81" ];
629        };
630        names = [
631          "hass.home"
632          "hass.vpn"
633          "hass.sbr.pm"
634        ];
635      };
636    };
637    okinawa = {
638      net = {
639        ips = [ "192.168.1.42" ];
640        vpn = {
641          pubkey = "SxH2pDYtAlK79s5K3YwplFm4hRn8lxnl4638IPRZnBk="; # From: sudo wg show wg0 public-key
642          ips = [ "10.100.0.14" ];
643        };
644        names = [
645          "okinawa.home"
646          "okinawa.vpn"
647          "okinawa.sbr.pm"
648        ];
649      };
650      ssh = {
651        hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM8vCZ0h6geJZt6i5k6chEDZBggoyq91Z+oNSjvVeSfW root@okinawa"; # From: cat /etc/ssh/ssh_host_ed25519_key.pub
652      };
653      syncthing = {
654        id = "BTDYRYQ-IOUC6IE-2BRMDDG-4CX6OUU-XCEUMSG-QF5O463-YFTNHTS-3NGRUQX"; # From Syncthing UI (http://localhost:8384)
655        folders = {
656          sync = { };
657          org = { };
658          documents = { };
659          screenshots = { };
660          wallpapers = { };
661          claude-sync = { };
662          ai-sync = { };
663          usage-metrics = { };
664          paperless-media = {
665            type = "receiveonly";
666            path = "/home/vincent/desktop/paperless-media";
667          };
668          paperless-inbox = {
669            path = "/home/vincent/desktop/paperless-inbox";
670          };
671        };
672      };
673    };
674    # iPhone
675    hokkaido = {
676      net = {
677        ips = [ "192.168.1.115" ];
678        vpn = {
679          pubkey = "rN5ekYI/k3Ymd76vudCZtcaNNu6HmMLzB61uRXakV0M=";
680          ips = [ "10.100.0.5" ];
681        };
682        names = [
683          "hokkaido.home"
684          "hokkaido.vpn"
685          "hokkaido.sbr.pm"
686        ];
687      };
688      syncthing = {
689        id = "XD4XYNZ-DT3PJEY-UJYBHWX-6OQPPUI-HTW752L-FYTX3TW-GVHDTKW-PT336QV";
690        folders = {
691          documents = { };
692          sync = { };
693          org = { };
694        };
695      };
696    };
697    # Light Phone
698    suzu = {
699      net = {
700        vpn = {
701          ips = [ "10.100.0.65" ];
702          pubkey = "ufKLXzLkmYx1z7/VZJs9Ix6aXL3rYzP5B73QQP2WNx8=";
703        };
704        names = [
705          # "suzu.home"
706          "suzu.vpn"
707          "suzu.sbr.pm"
708        ];
709      };
710    };
711    # Boox tablet
712    osaka = {
713      net = {
714        vpn = {
715          ips = [ "10.100.0.64" ];
716          pubkey = "IJkiyMdHnhwxN9hFpDNVFKzByyqEQKIyIFBpYst65x8=";
717        };
718        names = [
719          # "oksaka.home"
720          "osaka.vpn"
721          "osaka.sbr.pm"
722        ];
723      };
724    };
725    # Houbeb's iPhone
726    houbeb-iphone = {
727      net = {
728        vpn = {
729          ips = [ "10.100.0.44" ];
730          pubkey = "UdgP0UBPfIrzdUe8JNeom4qahtoBxSADI5gKwpoNYEs=";
731        };
732        names = [
733          "houbeb-iphone.vpn"
734          "houbeb-iphone.sbr.pm"
735        ];
736      };
737    };
738    # Houbeb's macbook pro 16
739    houbeb-makcbook-pro = {
740      net = {
741        vpn = {
742          ips = [ "10.100.0.71" ];
743          pubkey = "RANYR3ouf+3Bh+6kJVF1I0s5iglBGs7ucW6iRyv2cGc=";
744        };
745        names = [ ];
746      };
747    };
748  };
749  services = {
750    # Media services on rhea
751    immich = {
752      host = "rhea";
753      aliases = [ "photos" ];
754    };
755    jellyfin.host = "rhea";
756    jellyseerr.host = "rhea";
757    sonarr.host = "rhea";
758    radarr.host = "rhea";
759    lidarr.host = "rhea";
760    bazarr.host = "rhea";
761    prowlarr.host = "rhea";
762    transmission = {
763      host = "rhea";
764      aliases = [ "t" ];
765    };
766    transmission-music = {
767      host = "rhea";
768      aliases = [ "tm" ];
769    };
770    syncthing = {
771      host = "rhea";
772      aliases = [ "s" ];
773    };
774    homepage.host = "rhea";
775    # Wallabag read-it-later (runs on aion, proxied via rhea/Traefik)
776    wallabag = {
777      host = "rhea";
778      aliases = [ "read" ];
779    };
780    # Miniflux RSS reader (runs on aion, proxied via rhea/Traefik)
781    miniflux = {
782      host = "rhea";
783      aliases = [ "rss" ];
784    };
785    # Traefik dashboard
786    traefik.host = "rhea";
787    # Music streaming on aion (routed through rhea/traefik)
788    music.host = "rhea";
789    navidrome.host = "rhea";
790    # Podcast and audiobook management on aion (routed through rhea/traefik)
791    audiobookshelf.host = "rhea";
792    podcasts.host = "rhea";
793    # Ebook library and reading-position synchronization on rhea
794    liseur = {
795      host = "rhea";
796      aliases = [ "books" ];
797    };
798    # WebDAV on rhea
799    dav.host = "rhea";
800    # MQTT on demeter (routed through rhea/traefik)
801    mqtt.host = "rhea";
802    # Home Assistant on hass (routed through rhea/traefik)
803    home.host = "rhea";
804    # Services on sakhalin (routed through rhea/traefik)
805    paperless.host = "rhea";
806    grafana.host = "rhea";
807    # OpenCode web interface on okinawa (routed through rhea/traefik)
808    opencode.host = "rhea";
809    # Readwise Reader triage report on okinawa (routed through rhea/traefik)
810    reading.host = "rhea";
811    # XMPP messaging server on aion (VPN-only, direct access)
812    xmpp.host = "aion";
813  };
814}