Compare commits

...

202 Commits

Author SHA1 Message Date
Jörg Thalheim
d74b9c06e1 mv out of place test_host_interface to localhost_test 2025-08-08 15:11:49 +02:00
Jörg Thalheim
f248cc91ad switch to flake-compat for private flake 2025-08-08 15:06:57 +02:00
lassulus
e2cb75784c Merge pull request 'Add default bootstrapNodes for data-mesher service' (#4555) from fix-4424 into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4555
Reviewed-by: lassulus <clanlol@lassul.us>
2025-08-08 12:24:51 +00:00
pinpox
a8d48b22f8 Add default bootstrapnodes for data-mesher service 2025-08-08 11:18:08 +02:00
hsjobeki
c0f2bcf751 Merge pull request 'API/Machine: refactor api returns readonly' (#4627) from readonly into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4627
2025-08-08 08:54:49 +00:00
Johannes Kirschbauer
20c23fa64b API/Machine: refactor api returns readonly 2025-08-08 10:44:32 +02:00
clan-bot
23573e16c4 Merge pull request 'Update flake-parts' (#4620) from update-flake-parts into main 2025-08-08 07:18:42 +00:00
gitea-actions[bot]
eaee4e8cad Update flake-parts 2025-08-08 17:08:03 +10:00
clan-bot
10e43a8884 Merge pull request 'Update nixpkgs' (#4443) from update-nixpkgs into main 2025-08-08 05:57:37 +00:00
Michael Hoang
dc1cd03717 Merge pull request 'cli: fix missing newline in error message' (#4634) from push-lnmsprtyuntw into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4634
2025-08-08 05:30:05 +00:00
Michael Hoang
a71a5880c1 treewide: reformat 2025-08-08 15:28:37 +10:00
gitea-actions[bot]
6b137f21de Update nixpkgs 2025-08-08 15:28:37 +10:00
Michael Hoang
fbc14bf20f Merge pull request 'docs: fix command in Secrets guide' (#4635) from push-mxkpvktxwypw into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4635
2025-08-08 05:25:38 +00:00
Michael Hoang
2f2f3b6898 cli: fix missing newline in error message 2025-08-08 15:19:19 +10:00
Michael Hoang
3ae0f37bcb docs: fix command in Secrets guide 2025-08-08 15:16:58 +10:00
clan-bot
e49d432542 Merge pull request 'Update Clan Core for Checks' (#4633) from update-clan-core-for-checks into main 2025-08-08 03:00:34 +00:00
clan-bot
76955533cf Update pinned clan-core for checks 2025-08-08 02:51:46 +00:00
hsjobeki
d0ebc75135 Merge pull request 'ui/install: hook up stepper store and api' (#4626) from install-ui into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4626
2025-08-07 14:21:21 +00:00
Johannes Kirschbauer
40503306d1 cli/flash: fixup types 2025-08-07 16:09:36 +02:00
hsjobeki
da99407e74 Merge pull request 'Vars: rename classmethod to get_machine_generators' (#4629) from vars-fixing into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4629
2025-08-07 13:50:38 +00:00
Johannes Kirschbauer
915178765b Vars: rename classmethod to get_machine_generators 2025-08-07 15:31:17 +02:00
Johannes Kirschbauer
518de45d41 ui/install: hook up stepper store and api 2025-08-07 13:46:07 +02:00
Johannes Kirschbauer
7d23189c1c ui/intall: extend stories to mock router and api 2025-08-07 13:46:07 +02:00
Johannes Kirschbauer
eec55f73a2 ui/stepper: add stepper store to hook 2025-08-07 13:46:07 +02:00
Johannes Kirschbauer
484d274c3c ui/queries: add required flash data queries 2025-08-07 13:46:07 +02:00
Johannes Kirschbauer
a4b20f9167 UI/queries: migrate existing queries to useApiClient 2025-08-07 13:46:07 +02:00
Johannes Kirschbauer
dc7291c62b UI/api: add api client provider
This allows to switch out the used api backend for testing purposes.
Or for different plattforms
2025-08-07 13:46:07 +02:00
Johannes Kirschbauer
a814a44bc6 UI/Select: add async option loading 2025-08-07 13:46:07 +02:00
Johannes Kirschbauer
86a6177126 UI/useClan: add error debugging 2025-08-07 13:46:07 +02:00
Johannes Kirschbauer
4536a5b4f5 clan/flash: provide defaults for verbose flash options 2025-08-07 13:46:07 +02:00
Johannes Kirschbauer
a9cfda9acb dirs: add local path to clan_core flake in dirs 2025-08-07 13:46:07 +02:00
Johannes Kirschbauer
b9f60218d7 UI/install: create installer improve wording 2025-08-07 13:46:07 +02:00
clan-bot
f69e28a133 Merge pull request 'Update Clan Core for Checks' (#4625) from update-clan-core-for-checks into main 2025-08-07 03:00:25 +00:00
clan-bot
1968230c28 Update pinned clan-core for checks 2025-08-07 02:51:46 +00:00
clan-bot
9cad074732 Merge pull request 'Update treefmt-nix' (#4621) from update-treefmt-nix into main 2025-08-06 15:20:13 +00:00
gitea-actions[bot]
4859a9ab7c Update treefmt-nix 2025-08-06 15:01:29 +00:00
hsjobeki
b53ecdc89d Merge pull request 'UI/install: add machine progress, minor stepper fixes' (#4619) from install-ui into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4619
2025-08-06 14:49:30 +00:00
Johannes Kirschbauer
19603e1a1c UI/install: add machine progress 2025-08-06 16:44:30 +02:00
Johannes Kirschbauer
7d20f3a33b UI/install: create installer improve wording 2025-08-06 16:43:48 +02:00
Johannes Kirschbauer
fa03c190f8 UI/install: split initial choice 2025-08-06 16:43:24 +02:00
Johannes Kirschbauer
65101ad55a UI/steps: make step footer next text customizable 2025-08-06 16:42:45 +02:00
Johannes Kirschbauer
e5db3e269b UI/stepper: hooks add helper to more typesafe define steps 2025-08-06 16:42:03 +02:00
Johannes Kirschbauer
073750e4c5 clanServices: update description of generators that can be left empty 2025-08-06 16:41:11 +02:00
DavHau
8bafbcb295 machines update: use 'localhost' for local build 2025-08-06 19:06:20 +07:00
hsjobeki
dbef6ced77 Merge pull request 'cubes and lighting: refinements on coloring of cubes and lighting to fit with design' (#4617) from ui/3d-cubes-refinement into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4617
2025-08-06 10:47:00 +00:00
Timo
65e7f9e6ca cubes and lighting: refinements on coloring of cubes and lighting to fit with design 2025-08-06 12:33:53 +02:00
Mic92
e1062ed97c Merge pull request 'docs/update: mention build-host local and uploading flake inputs' (#4614) from local-build into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4614
2025-08-06 10:18:28 +00:00
Jörg Thalheim
2eb1a56d8f update.md: mention build-host local and uploading flake inputs 2025-08-06 12:14:35 +02:00
clan-bot
0f499fc651 Merge pull request 'Update Clan Core for Checks' (#4608) from update-clan-core-for-checks into main 2025-08-06 03:00:32 +00:00
clan-bot
bcb7a1aa60 Update pinned clan-core for checks 2025-08-06 02:51:47 +00:00
Mic92
273c83ec27 Merge pull request 'update/flake-upload: use ssh-ng:// for nix copy' (#4597) from local-build into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4597
2025-08-05 20:47:23 +00:00
clan-bot
c74d7857da Merge pull request 'Update flake-parts' (#4607) from update-flake-parts into main 2025-08-05 20:12:05 +00:00
gitea-actions[bot]
11405966c6 Update flake-parts 2025-08-05 20:00:50 +00:00
hsjobeki
220839598d Merge pull request 'UI/install: bootstrap visuals for {createImage, Installer}' (#4605) from install-ui into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4605
2025-08-05 17:34:14 +00:00
Jörg Thalheim
44dcfa7844 rename --fetch-local to --upload-inputs 2025-08-05 19:31:29 +02:00
Jörg Thalheim
98f685f3ca update/flake-upload: set correct remote-program for macOS targets 2025-08-05 19:31:29 +02:00
Johannes Kirschbauer
9e43285ba8 UI/install: bootstrap steps for {DiskSchema, Vars, Summary} 2025-08-05 19:29:06 +02:00
Johannes Kirschbauer
c0bc0417a6 UI/install: fix metaHeader reactive 2025-08-05 19:29:06 +02:00
Johannes Kirschbauer
c90b69d499 UI/install: clean up create steps 2025-08-05 19:29:06 +02:00
Johannes Kirschbauer
0240acdf3e UI/modal: move common styling into meta header 2025-08-05 19:29:06 +02:00
Johannes Kirschbauer
92726ecebc UI/install: installer steps bootstrap visuals {TargetHost,hw_report} 2025-08-05 19:29:06 +02:00
Johannes Kirschbauer
b8e9546762 UI/install: bootstrap visuals for createInstaller 2025-08-05 19:29:06 +02:00
Johannes Kirschbauer
2039f034b1 UI/steps: minor layout fixes 2025-08-05 19:29:06 +02:00
Johannes Kirschbauer
0a329f43a8 UI/Modal: add 'disablePadding' 2025-08-05 19:29:06 +02:00
Johannes Kirschbauer
bde0a2845c UI/LoadingBar: allow injecting props 2025-08-05 19:29:06 +02:00
Johannes Kirschbauer
af3c6282c9 UI/Alert: make description optional 2025-08-05 19:29:06 +02:00
hsjobeki
73ab4d2a6e Merge pull request 'ui/install: add disk selection step to image create' (#4598) from install-ui into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4598
2025-08-05 16:16:54 +00:00
Johannes Kirschbauer
cc269c4f58 ui/install: add disk selection step to image create 2025-08-05 16:22:44 +02:00
Johannes Kirschbauer
20021a92ea ui/next-button: fix interface should extend button 2025-08-05 16:22:02 +02:00
Johannes Kirschbauer
7b54e9b033 ui/loading-bar: move into component 2025-08-05 16:20:56 +02:00
hsjobeki
7971eceb74 Merge pull request 'UI: extend components to prepare install workflows' (#4576) from install-ui into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4576
2025-08-05 13:36:31 +00:00
Johannes Kirschbauer
49a5763f69 Install: split steps into files 2025-08-05 15:32:43 +02:00
Johannes Kirschbauer
10694e58c8 install: use typed stepper 2025-08-05 15:10:38 +02:00
Johannes Kirschbauer
0d919c4fce hooks/stepper: add generic stepper hook 2025-08-05 15:09:29 +02:00
Johannes Kirschbauer
8cccf757a8 Fix: modal header slot was renamed to metaHeader 2025-08-05 13:52:03 +02:00
Johannes Kirschbauer
80c8cc8628 HostFileInput: allow overriding placeholder 2025-08-05 13:48:49 +02:00
Johannes Kirschbauer
ab63f0d7a4 divider: add extra class prop 2025-08-05 13:48:49 +02:00
Johannes Kirschbauer
06e0461ec9 Modal: add metaHeader slot, fix border styling 2025-08-05 13:48:49 +02:00
Johannes Kirschbauer
60ba00dd8f Select: add simple select dropdown for single select 2025-08-05 13:48:49 +02:00
Johannes Kirschbauer
90ef55f040 Label: add support for kobalte select 2025-08-05 13:48:49 +02:00
Johannes Kirschbauer
de81a5d810 Modal: prepare for install flow 2025-08-05 13:48:49 +02:00
Mic92
3fe65f1f12 Merge pull request 'machines update: support local build' (#4515) from local-build into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4515
2025-08-05 11:28:50 +00:00
Jörg Thalheim
6bb998f9dd update/flake-upload: use ssh-ng:// for nix copy
I had concurrency issues with `nix copy` and the ssh:// protocol when
using a machine both as the build host/target host (for different
machines), where it make the result path partially available to a
different update command thread.
2025-08-05 13:26:34 +02:00
DavHau
af7ce9b8ed machines update: support local build
Now the user can pass `--build-host local`, to select the local machine as a build host, in which case no ssh is used.

This means the admin machine does not necessarily have ssh set up to itself, which was confusing for many users.

Also this makes it easier to re-use a well configured nix remote build setup which is only available on the local machine. Eg if `--build-host local` nix' defaults for remote builds on that machine will be utilized.
2025-08-05 13:16:59 +02:00
DavHau
b74193514d ssh: refactor callers to use new Host interface 2025-08-05 13:16:59 +02:00
DavHau
c33fd4e504 ssh: Introduce LocalHost vs. Remote via Host interface
Motivation: local builds and deployments without ssh

Add a new interface `Host` which is implemented bei either `Remote` or `Localhost`

This simplifies all interactions with hosts. THe caller does ot need to know if the Host is remote or local in mot cases anymore
2025-08-05 13:16:59 +02:00
pinpox
65f3cb562a Merge pull request 'Reduce targetHost warning level' (#4594) from fix-target-warning into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4594
2025-08-05 09:55:30 +00:00
Mic92
355ff648d7 Merge pull request 'consistently use tarball urls in documentation' (#4589) from no-git-fix into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4589
Reviewed-by: pinpox <clan@pablo.tools>
2025-08-05 09:55:15 +00:00
pinpox
f314eb04d6 Reduce targetHost warning level
The documentation currently lists setting targetHost in the NixOS
configuration as a slower, but valid option. Especially for dynamic
values, this is the recommended way but it results in a lot of annyoing
warnings.

This lowers the warning level, so it will only get printed on --debug
2025-08-05 11:52:06 +02:00
clan-bot
ebe206cdc0 Merge pull request 'Update Clan Core for Checks' (#4593) from update-clan-core-for-checks into main 2025-08-05 09:02:50 +00:00
clan-bot
2a138d3248 Update pinned clan-core for checks 2025-08-05 08:53:28 +00:00
Kenji Berthold
77810b1d4f Merge pull request 'clanServices: migrate syncthing module to clanServices' (#4558) from ke-migrate-clan-module-syncthing into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4558
2025-08-05 08:25:07 +00:00
a-kenji
77c840c9ba services/syncthing: Add basic usage documentation 2025-08-05 10:00:30 +02:00
a-kenji
9df7e6df1e services/syncthing: Add eval-test 2025-08-05 10:00:30 +02:00
a-kenji
a5e51f658d clanServices: migrate syncthing module to clanServices
Migrate the syncthing module from `clanModules` to `clanServices`.
2025-08-05 10:00:01 +02:00
clan-bot
98d5b3651b Merge pull request 'Update sops-nix' (#4591) from update-sops-nix into main 2025-08-04 20:12:26 +00:00
gitea-actions[bot]
713a1a550e Update sops-nix 2025-08-04 20:01:21 +00:00
Jörg Thalheim
d51d656391 consistently use tarball urls in documentation
otherwise users not using our templates will find themselves missing
git.
2025-08-04 11:20:14 +02:00
lassulus
0f79af697e Merge pull request 'simplify select debug output logic, add better error messages' (#4582) from select-debug into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4582
2025-08-02 20:55:31 +00:00
lassulus
0119fc06ca clan-cli select: show if select is cached or not 2025-08-02 21:26:39 +02:00
lassulus
5361261bd5 clan select: better error 2025-08-02 17:59:12 +02:00
lassulus
86e7bcc389 clan select: simplify select logging 2025-08-02 17:19:35 +02:00
clan-bot
79281aba90 Merge pull request 'Update flake-parts' (#4581) from update-flake-parts into main 2025-08-02 00:13:08 +00:00
gitea-actions[bot]
dade91c292 Update flake-parts 2025-08-02 00:00:52 +00:00
clan-bot
d285a0e716 Merge pull request 'Update treefmt-nix' (#4579) from update-treefmt-nix into main 2025-08-01 20:13:21 +00:00
gitea-actions[bot]
a97128db17 Update treefmt-nix 2025-08-01 20:01:25 +00:00
brianmcgee
ff7b49be5f Merge pull request 'feat: ui/auto-resizing-textarea' (#4562) from ui/auto-resizing-textarea into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4562
2025-08-01 09:40:33 +00:00
Luis Hebendanz
0b816a2672 Merge pull request 'Fix getting-started guide' (#4545) from Qubasa/clan-core:migrate-dyndns into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4545
2025-08-01 08:30:09 +00:00
hsjobeki
e6ec331da0 Merge pull request 'vars: add display attribute submodule for customisable ux' (#4559) from vars-display into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4559
Reviewed-by: DavHau <d.hauer.it@gmail.com>
2025-08-01 07:55:07 +00:00
Qubasa
0b05b0b1ec docs: review fixups
docs: review fixups

docs: review fixups

docs: fixup links in cli

docs: fixup links in cli
2025-08-01 14:53:31 +07:00
Michael Hoang
efd9beba15 Merge pull request 'docs: macOS' (#4563) from push-xptxwrqwvymq into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4563
2025-08-01 05:49:35 +00:00
Michael Hoang
dc03a9183f docs: macOS 2025-08-01 15:45:41 +10:00
Johannes Kirschbauer
ab3158ca07 vars/decrypt_dependencies: simplify 2025-08-01 04:01:43 +00:00
Brian McGee
75a1f7b67f feat(ui): auto-resizing textarea 2025-07-31 18:50:39 +01:00
brianmcgee
d453720a57 Merge pull request 'feat(ui): add tooltips for general section in machine detail pane' (#4561) from feat/machine-detail-tooltips into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4561
2025-07-31 17:48:27 +00:00
Brian McGee
a4331cc109 feat(ui): add tooltips for general section in machine detail pane 2025-07-31 18:38:56 +01:00
hsjobeki
434ce1af49 Merge pull request 'vars/list: doogfood get_machines into cli' (#4549) from vars-dog into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4549
2025-07-31 17:19:42 +00:00
Johannes Kirschbauer
488ee1ae63 users/display: add display properties 2025-07-31 16:45:20 +02:00
Johannes Kirschbauer
fc2e619046 vars: add display attribute submodule for customizable ux 2025-07-31 16:35:15 +02:00
Johannes Kirschbauer
cf6c3604ca generators_from_flake: vars always bind to store 2025-07-31 16:16:36 +02:00
hsjobeki
a3ea62caba Merge pull request 'docs: add vars/gaph doc-strings' (#4554) from vars-docs-2 into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4554
2025-07-31 13:37:29 +00:00
Johannes Kirschbauer
e2e4837b29 docs: add vars/gaph doc-strings 2025-07-31 15:26:22 +02:00
Johannes Kirschbauer
96fc3d409a vars/list: untangle generators_from_flake and get_generators 2025-07-31 15:17:57 +02:00
Johannes Kirschbauer
392f244361 vars/list: doogfood get_machines into cli
This is important otherwise cli diverges from api
2025-07-31 14:02:50 +02:00
Qubasa
d2529704d5 docs: Split up getting-started guide in a Physical and Virtual installation, and properly document how to install on non-NixOS machines
docs: git add docs
2025-07-31 17:06:44 +07:00
Qubasa
62a3503987 clan-lib: Always set a static private key for nixos-anywhere, to make --phases work properly 2025-07-31 17:06:00 +07:00
Qubasa
c39aa89e29 docs: Add a nixos-anywhere debugging hint 2025-07-31 17:06:00 +07:00
Mic92
d19ac1b9f5 Merge pull request 'machines update: refactor - simplify' (#4506) from simplify-update into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4506
2025-07-30 18:42:02 +00:00
Jörg Thalheim
57eec8edb4 bump clan-core-for-checks 2025-07-30 20:15:44 +02:00
Jörg Thalheim
e99981cfaf flake: fix privateInputs loading in nix store contexts
When clan-core is fetched via fetchgit (e.g. in tests), the devFlake/private
directory exists but cannot be loaded as a flake. This causes errors when
building test machines.

Fix by:
1. Adding a .skip-private-inputs marker file in clan-core-for-checks to
   explicitly disable private inputs in test contexts
2. Checking for this marker file before attempting to load private inputs
3. Keeping the original tryEval approach as a fallback for compatibility

This ensures tests can run without errors while preserving the ability to
load private inputs in development environments.
2025-07-30 20:15:44 +02:00
Jörg Thalheim
ae0ea37437 add update test 2025-07-30 19:13:17 +02:00
Jörg Thalheim
15557cb532 test/installation: drop out-dated comment 2025-07-30 17:58:12 +02:00
Jörg Thalheim
8f3a0b59f3 update-hardware-configuration: remove unused CLAN_FLAKE 2025-07-30 17:49:28 +02:00
Jörg Thalheim
10f731c974 container-test-driver: fix rebuild and make container-test-driver importable 2025-07-30 17:49:28 +02:00
hsjobeki
0e5c8d1a33 Merge pull request 'ui/hostfile: convert to use css modules' (#4540) from ui-progress into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4540
2025-07-30 14:34:52 +00:00
Johannes Kirschbauer
e5f8c515cd ui/hostfile: convert to use css modules 2025-07-30 16:30:46 +02:00
hsjobeki
e856d4018a Merge pull request 'ui/button: hide loader when not loading' (#4539) from ui-progress into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4539
2025-07-30 13:50:35 +00:00
Johannes Kirschbauer
17b75500fb ui/button: hide loader when not loading 2025-07-30 15:46:43 +02:00
Kenji Berthold
cf8b7f63fc Merge pull request 'pkgs/clan/lib: Move get_clan_directories to dirs' (#4538) from kenji/ke-directory-move into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4538
2025-07-30 13:17:37 +00:00
a-kenji
62c4f735ed pkgs/clan/lib: Move get_clan_directories to dirs 2025-07-30 15:07:05 +02:00
brianmcgee
cba951b2c5 Merge pull request 'feat: ui/machine-detail-pane' (#4531) from ui/machine-detail-pane into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4531
2025-07-30 12:59:27 +00:00
clan-bot
ef6f652b92 Merge pull request 'Update treefmt-nix' (#4492) from update-treefmt-nix into main 2025-07-30 12:39:59 +00:00
Johannes Kirschbauer
3d51cee4bb ui/modal: autofocus first input 2025-07-30 12:31:10 +00:00
Kenji Berthold
1791743444 Merge pull request 'pkgs/clan/lib: Add clan api to get the relative clan directory' (#4534) from kenji/ke-add-computed-directory-function into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4534
Reviewed-by: hsjobeki <hsjobeki@gmail.com>
2025-07-30 12:30:30 +00:00
gitea-actions[bot]
6208a6e857 Update treefmt-nix 2025-07-30 12:28:55 +00:00
Johannes Kirschbauer
4759cce8a4 ui/modal: autofocus first input 2025-07-30 14:27:10 +02:00
a-kenji
c7ad875e7e pkgs/clan/lib: Add clan api to get the relative clan directory
This is a Continuation of: #4519
2025-07-30 14:22:23 +02:00
hsjobeki
2ef292942f Merge pull request 'ui/button: fix loader needs explizit styling, not stylable via css leakage anymore' (#4536) from ui-progress into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4536
2025-07-30 12:20:45 +00:00
Johannes Kirschbauer
b83f5d2ffc ui/button: fix loader needs explizit styling, not stylable via css leakage anymore 2025-07-30 14:16:23 +02:00
hsjobeki
567e8b57cd Merge pull request 'ui/modal: use css modules' (#4535) from ui-progress into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4535
2025-07-30 11:55:35 +00:00
Johannes Kirschbauer
7f1a7da5c7 ui/modal: use css modules 2025-07-30 13:51:47 +02:00
hsjobeki
bb92ffb898 Merge pull request 'ui/toolbarButton: fix selected state' (#4533) from ui-progress into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4533
2025-07-30 11:45:19 +00:00
Johannes Kirschbauer
7ed62c427c ui/toolbarButton: fix selected state 2025-07-30 13:40:08 +02:00
Johannes Kirschbauer
596458d809 fix: set fixed height for SidebarSectionForm controls 2025-07-30 12:32:59 +01:00
Brian McGee
f677d96acf feat(ui): add sidebar pane for machine detail 2025-07-30 12:32:58 +01:00
Brian McGee
2c3b0f3771 feat(ui): use keyed show to re-render Machine route when route changes 2025-07-30 12:32:57 +01:00
Brian McGee
ae20230a57 feat(ui): change machineID to machineName
And no longer base64 encode it in url params or cache keys.

The term used in the API is name, so this is aligning with that.
2025-07-30 12:32:57 +01:00
Kenji Berthold
549ba9bdc2 Merge pull request 'pkgs/cli/lib: Remove obsolete clan creation function' (#4532) from kenji/ke-add-computed-directory into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4532
2025-07-30 10:52:16 +00:00
a-kenji
e167137672 pkgs/cli/lib: Remove obsolete clan creation function 2025-07-30 12:41:42 +02:00
hsjobeki
e36735119c Merge pull request 'ui/loader: transform to use css modules' (#4530) from ui-css into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4530
2025-07-30 09:07:59 +00:00
Johannes Kirschbauer
f8cdac2a63 ui/debug: remove floating debug buttons 2025-07-30 11:04:22 +02:00
Johannes Kirschbauer
ea63b4411e ui/splashscreen: transform to use css modules 2025-07-30 11:03:56 +02:00
Johannes Kirschbauer
a070fc74c1 ui/loader: transform to use css modules 2025-07-30 11:03:56 +02:00
DavHau
b30686269b machines update: fix lacks a signature by a trusted key
Despite using `root` as the ssh user, `ssh-ng` still fails with:

`error: cannot add path '/nix/store/...' because it lacks a signature by a trusted key
`

This does not happen with `ssh` instead of `ssh-ng`
2025-07-30 15:15:57 +07:00
Jörg Thalheim
1626d179a0 run_machine_update: document missing flag 2025-07-30 09:54:50 +02:00
Jörg Thalheim
6ec38c33d7 container-test-driver: fixup /etc/passwd for unprivileged user
By default /etc/passwd in container build sandboxes have two users
(root,nixbld) mapped to root. This confuses nix especially it behaves
different if it runs as root. setuid/setgid() is not enough because ssh
will break if the current uid does not exist in /etc/passwd.
Along with this we now also only run the setup for setting up the
network bridge and cgroup filesystems once and not per container.
2025-07-30 09:54:50 +02:00
Jörg Thalheim
fdfbed1a3f nixos_test_lib/setup_ssh_connection: no forward in container tests 2025-07-30 09:54:50 +02:00
Jörg Thalheim
f44b8c63c2 nixos_test_lib/prepare_test_flake: return a Path instead of str 2025-07-30 09:54:50 +02:00
Jörg Thalheim
092ac21dcd git_clan_flake_toplevel: gracefully handle permission errors
since this code is used as a default in the cli parser, we should not
crash on OSError because we wouldn't be abe to perform basic tasks such
as --help.
2025-07-30 09:54:50 +02:00
Jörg Thalheim
bd6f7b03af inline find_git_repo_root 2025-07-30 09:54:50 +02:00
Jörg Thalheim
0908a2efb8 don't resolve absolute paths for flake uri
pathlib.Path("git+file:///foo").resolve() might resolve to urls like
PosixPath('/home/joerg/work/clan/clan-core/git+file:/foo'). If those
then actually exist, this can have weird behavior. We should in general
avoid changing directories for everything except for subprocess.run.
2025-07-30 09:54:50 +02:00
Jörg Thalheim
6c84b2e100 container-test-driver: also setup network if we start a single container 2025-07-30 09:54:50 +02:00
Jörg Thalheim
de65619442 update: explain why we use sudo rather than --use-remote-sudo 2025-07-30 09:54:50 +02:00
DavHau
85dda9e125 machines update: refactor - simplify
This is an attempt to reduce cognitive load when trying to understand the host related logic in run_machine_update.

The change should not affect behavior.

Done:
- make it very clear early on, that build_host == target_host if build_host is not set.
- rename some variables to make relations more clear
- remove `deploy_host` variable. unnecessary complexity
- remove `become_root` variable. After simplifying via boolean algebra, this boils down to `build_host == target_host`.
2025-07-30 09:54:50 +02:00
hsjobeki
7961a92d32 Merge pull request 'ui/toolbar: fix overly specifify css selectors' (#4525) from toolbar into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4525
2025-07-30 06:48:52 +00:00
hsjobeki
50ba21316e Merge pull request 'ui/sidebar: fix close animation' (#4524) from sidebar-fix into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4524
2025-07-29 17:14:33 +00:00
Johannes Kirschbauer
08342578f1 ui/toolbar: fix story types 2025-07-29 19:12:27 +02:00
Johannes Kirschbauer
9954653657 ui/toolbar: use css modules 2025-07-29 19:10:42 +02:00
Johannes Kirschbauer
6e71b541aa ui/toolbar: fix overly specifify css selectors 2025-07-29 18:50:34 +02:00
Johannes Kirschbauer
0f72f12461 ui/sidebar: fix close animation
Animation needs static layout as a starting point
2025-07-29 18:03:00 +02:00
Kenji Berthold
db579e169c Merge pull request 'pkgs/clan/lib: Fix directory functionality' (#4519) from kenji/ke-fix-directory-usage into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4519
2025-07-29 16:01:35 +00:00
a-kenji
31438d6781 pkgs/clan/lib: Fix directory functionality
Fix the directory functionality of clan (clan.directory).
The python API interface was not able to distinguish if the directory
was set to anything other than `self.src`.
Breaking every command that relied on the clan directory, for example:
- `clan machines update`
- `clan machines update-hardware-config`
See more in #2906

This is the first step in fixing all those commands.
Individual command support and implementation will be implemented in
follow ups.
2025-07-29 17:51:12 +02:00
Kenji Berthold
eac21c5176 Merge pull request 'pkgs/clan/lib: Fix documentation of from_ssh_uri in the Remote class' (#4523) from kenji/ke-fix-remote-documentation into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4523
2025-07-29 15:47:47 +00:00
Kenji Berthold
2bd432bdb7 Merge pull request 'pkgs/clan/cli: Fix typo in machines update' (#4522) from kenji/ke-machines-update-fix-typo into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4522
2025-07-29 15:46:18 +00:00
a-kenji
7ef09343ed pkgs/clan/lib: Fix documentation of from_ssh_uri in the Remote class 2025-07-29 17:37:50 +02:00
a-kenji
8c2cee0e44 pkgs/clan/cli: Fix typo in machines update 2025-07-29 17:32:45 +02:00
hsjobeki
b421698f70 Merge pull request 'templates: fix urls for relative file paths' (#4520) from fix-template-urls into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4520
2025-07-29 15:19:20 +00:00
hsjobeki
857b9d0260 Merge pull request 'docs/templates: add more docs for template urls' (#4521) from docs-templates into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4521
2025-07-29 15:11:15 +00:00
Johannes Kirschbauer
2776294de0 templates: url add support for home and abspath 2025-07-29 17:04:15 +02:00
Johannes Kirschbauer
c90b8d7401 templates/cli: add more help 2025-07-29 16:58:19 +02:00
Johannes Kirschbauer
5c746311c7 templates: init docs 2025-07-29 16:50:48 +02:00
Johannes Kirschbauer
7784df8180 templates: fix urls for relative file paths 2025-07-29 15:01:29 +02:00
Kenji Berthold
5d0ca5aff8 Merge pull request 'pkgs/clan/lib: Fix clan template creation when already in a flake' (#4501) from kenji/ke-clan-flakes-create-existing-flake-fix into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4501
2025-07-29 11:19:04 +00:00
a-kenji
3ef6b2f715 pkgs/clan/cli: Add test for builtin flakeref 2025-07-29 13:07:48 +02:00
Kenji Berthold
58053748b9 Merge pull request 'pkgs/clan/cli: Add clan flake validation to clan vars check' (#4513) from kenji/ke-vars-check-validation into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4513
2025-07-29 11:03:45 +00:00
DavHau
19a8101e98 Merge pull request 'pkgs/cli/create: Show less output by default' (#4499) from kenji/ke-create-show-less-output into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4499
2025-07-29 10:45:16 +00:00
pinpox
e5cb5afb4b Merge pull request 'Migrate postgresql to clan.core' (#4466) from postgres-core into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4466
2025-07-29 09:50:08 +00:00
pinpox
b75cf516f6 Merge branch 'main' into postgres-core 2025-07-29 09:41:50 +00:00
pinpox
3c58e2f04e Migrate postgresql to clan.core 2025-07-29 10:33:14 +02:00
a-kenji
35315d9596 pkgs/clan/cli: Add clan flake validation to clan vars check
This now gives a clearer error than:
```
Traceback (most recent call last):
  File "/nix/store/mznnb8il3njp6jxn5i57d0myjdh6cs0i-clan-cli/bin/.clan-wrapped", line 9, in <module>
    sys.exit(main())
             ~~~~^^
  File "/nix/store/mznnb8il3njp6jxn5i57d0myjdh6cs0i-clan-cli/lib/python3.13/site-packages/clan_cli/cli.py", line 516, in main
    args.func(args)
    ~~~~~~~~~^^^^^^
  File "/nix/store/mznnb8il3njp6jxn5i57d0myjdh6cs0i-clan-cli/lib/python3.13/site-packages/clan_cli/vars/check.py", line 113, in check_command
    ok = check_vars(args.machine, args.flake, generator_name=args.generator)
  File "/nix/store/mznnb8il3njp6jxn5i57d0myjdh6cs0i-clan-cli/lib/python3.13/site-packages/clan_cli/vars/check.py", line 103, in check_vars
    status = vars_status(machine_name, flake, generator_name=generator_name)
  File "/nix/store/mznnb8il3njp6jxn5i57d0myjdh6cs0i-clan-cli/lib/python3.13/site-packages/clan_cli/vars/check.py", line 41, in vars_status
    generators = Generator.generators_from_flake(machine.name, machine.flake)
  File "/nix/store/mznnb8il3njp6jxn5i57d0myjdh6cs0i-clan-cli/lib/python3.13/site-packages/clan_cli/vars/generate.py", line 67, in generators_from_flake
    generators_data = flake.select_machine(
                      ^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'select_machine'
```

When not being in a flake.
2025-07-29 10:25:28 +02:00
a-kenji
0a43721a45 pkgs/clan/lib: Fix clan template creation when already in a flake
Fix clan template creation when already in a flake.
Currently we already fail with very clear and descriptive error when
trying to evaluate the template of the flake we are in:
```
Failed to select template 'flake-parts' from flake '/tmp/superclan' (via attribute path: /tmp/superclan#clanInternals.templates.clan."flake-parts")
```

This is undesired behavior.
When we are trying to create a clan with `clan flakes create`.
We can't rely on the fact that the flake we are currently in exports flake templates.

Now we *try* to evaluate the flake we are in upon creation.
If there are no clan templates available, we now will fall back to
builtin templates.

Closes: #4472
2025-07-26 15:21:44 +02:00
a-kenji
51eb7bd0b5 pkgs/cli/create: Show less output by default
This masks the output of `nix flake update`, since it is quite verbose
and takes attention away to usually more interesting and pressing
information.

Example:
```
unpacking 'https://git.clan.lol/clan/clan-core/archive/main.tar.gz' into the Git cache...
warning: creating lock file "/tmp/hoowowo/clan/flake.lock":
• Added input 'clan-core':
    '1d8ac7b1b5.tar.gz?narHash=sha256-eBxi0ZMwaALfMsP70N0FRMlOSq0qePv%2BjebVBHXlOqk%3D' (2025-07-25)
• Added input 'clan-core/data-mesher':
    '18dfd42bdb.tar.gz?narHash=sha256-jyoEbaXa8/MwVQ%2BPajUdT63y3gYhgD9o7snO/SLaikw%3D' (2025-07-21)
• Added input 'clan-core/data-mesher/flake-parts':
    follows 'clan-core/flake-parts'
• Added input 'clan-core/data-mesher/nixpkgs':
    follows 'clan-core/nixpkgs'
• Added input 'clan-core/data-mesher/treefmt-nix':
    follows 'clan-core/treefmt-nix'
• Added input 'clan-core/disko':
    'github:nix-community/disko/545aba02960caa78a31bd9a8709a0ad4b6320a5c?narHash=sha256-7lrVrE0jSvZHrxEzvnfHFE/Wkk9DDqb%2BmYCodI5uuB8%3D' (2025-07-21)
• Added input 'clan-core/disko/nixpkgs':
    follows 'clan-core/nixpkgs'
• Added input 'clan-core/flake-parts':
    'github:hercules-ci/flake-parts/644e0fc48951a860279da645ba77fe4a6e814c5e?narHash=sha256-TVcTNvOeWWk1DXljFxVRp%2BE0tzG1LhrVjOGGoMHuXio%3D' (2025-07-21)
• Added input 'clan-core/flake-parts/nixpkgs-lib':
    follows 'clan-core/nixpkgs'
• Added input 'clan-core/nix-darwin':
    'github:nix-darwin/nix-darwin/e04a388232d9a6ba56967ce5b53a8a6f713cdfcf?narHash=sha256-HsJM3XLa43WpG%2B665aGEh8iS8AfEwOIQWk3Mke3e7nk%3D' (2025-06-30)
• Added input 'clan-core/nix-darwin/nixpkgs':
    follows 'clan-core/nixpkgs'
• Added input 'clan-core/nix-select':
    '69d8bf5961.tar.gz?narHash=sha256-IVaoOGDIvAa/8I0sdiiZuKptDldrkDWUNf/%2BezIRhyc%3D' (2025-04-18)
• Added input 'clan-core/nixos-facter-modules':
    'github:nix-community/nixos-facter-modules/14df13c84552a7d1f33c1cd18336128fbc43f920?narHash=sha256-uP9Xxw5XcFwjX9lNoYRpybOnIIe1BHfZu5vJnnPg3Jc%3D' (2025-06-20)
• Added input 'clan-core/nixpkgs':
    'https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre827262.be9e214982e2/nixexprs.tar.xz?narHash=sha256-lUi%2BsPH7Kuh9uP3PyfgbENcJGReUM8Ffk9GxGBFbSN8%3D' (1980-01-01)
• Added input 'clan-core/sops-nix':
    'github:Mic92/sops-nix/2c8def626f54708a9c38a5861866660395bb3461?narHash=sha256-GllP7cmQu7zLZTs9z0J2gIL42IZHa9CBEXwBY9szT0U%3D' (2025-07-15)
• Added input 'clan-core/sops-nix/nixpkgs':
    follows 'clan-core/nixpkgs'
• Added input 'clan-core/systems':
    'github:nix-systems/default/da67096a3b9bf56a91d16901293e51ba5b49a27e?narHash=sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768%3D' (2023-04-09)
• Added input 'clan-core/treefmt-nix':
    'github:numtide/treefmt-nix/421b56313c65a0815a52b424777f55acf0b56ddf?narHash=sha256-tzbhc4XttkyEhswByk5R38l%2BztN9UDbnj0cTcP6Hp9A%3D' (2025-07-20)
• Added input 'clan-core/treefmt-nix/nixpkgs':
    follows 'clan-core/nixpkgs'
• Added input 'flake-parts':
    'github:hercules-ci/flake-parts/644e0fc48951a860279da645ba77fe4a6e814c5e?narHash=sha256-TVcTNvOeWWk1DXljFxVRp%2BE0tzG1LhrVjOGGoMHuXio%3D' (2025-07-21)
• Added input 'flake-parts/nixpkgs-lib':
    follows 'clan-core/nixpkgs'
• Added input 'nixpkgs':
    follows 'clan-core/nixpkgs'
```

Those are 48 lines that seldom carry actual useful information.
This can be shown on running `clan flakes create` with the `--debug`
flag.

Closes: #4496
2025-07-26 14:11:56 +02:00
268 changed files with 7487 additions and 2837 deletions

View File

@@ -19,8 +19,7 @@
...
}:
let
dependencies =
[
dependencies = [
pkgs.stdenv.drvPath
]
++ builtins.map (i: i.outPath) (builtins.attrValues (builtins.removeAttrs self.inputs [ "self" ]));
@@ -154,8 +153,7 @@
nixos-test-backups = self.clanLib.test.containerTest {
name = "nixos-test-backups";
nodes.machine = {
imports =
[
imports = [
self.nixosModules.clanCore
# Some custom overrides for the backup tests
self.nixosModules.test-backup

View File

@@ -1,6 +1,6 @@
{ fetchgit }:
fetchgit {
url = "https://git.clan.lol/clan/clan-core.git";
rev = "eea93ea22c9818da67e148ba586277bab9e73cea";
sha256 = "sha256-PV0Z+97QuxQbkYSVuNIJwUNXMbHZG/vhsA9M4cDTCOE=";
rev = "d0ebc75135b125fd509558c7680fa2459af91195";
sha256 = "1k9wpy661dhwas7z05jkn8157pgmr408dn67zd9px9iphmrf7bry";
}

View File

@@ -19,14 +19,26 @@ let
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
in
{
imports = filter pathExists [
imports =
let
clanCoreModulesDir = ../nixosModules/clanCore;
getClanCoreTestModules =
let
moduleNames = attrNames (builtins.readDir clanCoreModulesDir);
testPaths = map (
moduleName: clanCoreModulesDir + "/${moduleName}/tests/flake-module.nix"
) moduleNames;
in
filter pathExists testPaths;
in
getClanCoreTestModules
++ filter pathExists [
./backups/flake-module.nix
../nixosModules/clanCore/machine-id/tests/flake-module.nix
../nixosModules/clanCore/state-version/tests/flake-module.nix
./devshell/flake-module.nix
./flash/flake-module.nix
./impure/flake-module.nix
./installation/flake-module.nix
./update/flake-module.nix
./morph/flake-module.nix
./nixos-documentation/flake-module.nix
./dont-depend-on-repo-root.nix
@@ -88,7 +100,6 @@ in
nixos-test-container = self.clanLib.test.containerTest ./container nixosTestArgs;
nixos-test-zt-tcp-relay = self.clanLib.test.containerTest ./zt-tcp-relay nixosTestArgs;
nixos-test-matrix-synapse = self.clanLib.test.containerTest ./matrix-synapse nixosTestArgs;
nixos-test-postgresql = self.clanLib.test.containerTest ./postgresql nixosTestArgs;
nixos-test-user-firewall-iptables = self.clanLib.test.containerTest ./user-firewall/iptables.nix nixosTestArgs;
nixos-test-user-firewall-nftables = self.clanLib.test.containerTest ./user-firewall/nftables.nix nixosTestArgs;
@@ -147,8 +158,11 @@ in
clan-core-for-checks = pkgs.runCommand "clan-core-for-checks" { } ''
cp -r ${pkgs.callPackage ./clan-core-for-checks.nix { }} $out
chmod +w $out/flake.lock
chmod -R +w $out
cp ${../flake.lock} $out/flake.lock
# Create marker file to disable private flake loading in tests
touch $out/.skip-private-inputs
'';
};
packages = lib.optionalAttrs (pkgs.stdenv.isLinux) {

View File

@@ -50,7 +50,8 @@
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.toplevel
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript.drvPath
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
in
{

View File

@@ -149,7 +149,6 @@
# vm-test-run-test-installation-> target: To debug, enter the VM and run 'systemctl status backdoor.service'.
checks =
let
# Custom Python package for port management utilities
closureInfo = pkgs.closureInfo {
rootPaths = [
self.checks.x86_64-linux.clan-core-for-checks
@@ -159,7 +158,8 @@
pkgs.stdenv.drvPath
pkgs.bash.drvPath
pkgs.buildPackages.xorg.lndir
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
};
in
pkgs.lib.mkIf (pkgs.stdenv.isLinux && !pkgs.stdenv.isAarch64) {
@@ -225,7 +225,7 @@
"install",
"--phases", "disko,install",
"--debug",
"--flake", flake_dir,
"--flake", str(flake_dir),
"--yes", "test-install-machine-without-system",
"--target-host", f"nonrootuser@localhost:{ssh_conn.host_port}",
"-i", ssh_conn.ssh_key,
@@ -289,9 +289,6 @@
assert not os.path.exists(hw_config_file), "hardware-configuration.nix should not exist initially"
assert not os.path.exists(facter_file), "facter.json should not exist initially"
# Set CLAN_FLAKE for the commands
os.environ["CLAN_FLAKE"] = flake_dir
# Test facter backend
clan_cmd = [
"${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",

View File

@@ -159,7 +159,8 @@ let
pkgs.stdenv.drvPath
pkgs.bash.drvPath
pkgs.buildPackages.xorg.lndir
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
};
in

View File

@@ -35,7 +35,8 @@
pkgs.stdenv.drvPath
pkgs.stdenvNoCC
self.nixosConfigurations.test-morph-machine.config.system.build.toplevel
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
in

View File

@@ -1,73 +0,0 @@
({
name = "postgresql";
nodes.machine =
{ self, config, ... }:
{
imports = [
self.nixosModules.clanCore
self.clanModules.postgresql
self.clanModules.localbackup
];
clan.postgresql.users.test = { };
clan.postgresql.databases.test.create.options.OWNER = "test";
clan.postgresql.databases.test.restore.stopOnRestore = [ "sample-service" ];
clan.localbackup.targets.hdd.directory = "/mnt/external-disk";
clan.core.settings.directory = ./.;
systemd.services.sample-service = {
wantedBy = [ "multi-user.target" ];
script = ''
while true; do
echo "Hello, world!"
sleep 5
done
'';
};
environment.systemPackages = [ config.services.postgresql.package ];
};
testScript =
{ nodes, ... }:
''
start_all()
machine.wait_for_unit("postgresql")
machine.wait_for_unit("sample-service")
# Create a test table
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -c 'CREATE TABLE test (id serial PRIMARY KEY);' test")
machine.succeed("/run/current-system/sw/bin/localbackup-create >&2")
timestamp_before = int(machine.succeed("systemctl show --property=ExecMainStartTimestampMonotonic sample-service | cut -d= -f2").strip())
machine.succeed("test -e /mnt/external-disk/snapshot.0/machine/var/backup/postgres/test/pg-dump || { echo 'pg-dump not found'; exit 1; }")
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c 'INSERT INTO test DEFAULT VALUES;'")
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c 'DROP TABLE test;'")
machine.succeed("test -e /var/backup/postgres/test/pg-dump || { echo 'pg-dump not found'; exit 1; }")
machine.succeed("rm -rf /var/backup/postgres")
machine.succeed("NAME=/mnt/external-disk/snapshot.0 FOLDERS=/var/backup/postgres/test /run/current-system/sw/bin/localbackup-restore >&2")
machine.succeed("test -e /var/backup/postgres/test/pg-dump || { echo 'pg-dump not found'; exit 1; }")
machine.succeed("""
set -x
${nodes.machine.clan.core.state.test.postRestoreCommand}
""")
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -l >&2")
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c '\dt' >&2")
timestamp_after = int(machine.succeed("systemctl show --property=ExecMainStartTimestampMonotonic sample-service | cut -d= -f2").strip())
assert timestamp_before < timestamp_after, f"{timestamp_before} >= {timestamp_after}: expected sample-service to be restarted after restore"
# Check that the table is still there
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c 'SELECT * FROM test;'")
output = machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql --csv -c \"SELECT datdba::regrole FROM pg_database WHERE datname = 'test'\"")
owner = output.split("\n")[1]
assert owner == "test", f"Expected database owner to be 'test', got '{owner}'"
# check if restore works if the database does not exist
machine.succeed("runuser -u postgres -- dropdb test")
machine.succeed("${nodes.machine.clan.core.state.test.postRestoreCommand}")
machine.succeed("runuser -u postgres -- /run/current-system/sw/bin/psql -d test -c '\dt' >&2")
'';
})

View File

@@ -29,18 +29,10 @@ nixosLib.runTest (
testScript =
{ nodes, ... }:
''
from nixos_test_lib.nix_setup import setup_nix_in_nix # type: ignore[import-untyped]
setup_nix_in_nix(None) # No closure info for this test
def run_clan(cmd: list[str], **kwargs) -> str:
import subprocess
clan = "${clan-core.packages.${hostPkgs.system}.clan-cli}/bin/clan"
clan_args = ["--flake", "${config.clan.test.flakeForSandbox}"]
return subprocess.run(
["${hostPkgs.util-linux}/bin/unshare", "--user", "--map-user", "1000", "--map-group", "1000", clan, *cmd, *clan_args],
**kwargs,
check=True,
).stdout
from nixos_test_lib.nix_setup import setup_nix_in_nix # type: ignore[import-untyped]
setup_nix_in_nix(None) # No closure info for this test
start_all()
admin1.wait_for_unit("multi-user.target")
@@ -60,7 +52,13 @@ nixosLib.runTest (
# Check that the file is in the '0644' mode
assert "-rw-r--r--" in ls_out, f"File is not in the '0644' mode: {ls_out}"
run_clan(["machines", "list"])
# Run clan command
result = subprocess.run(
["${
clan-core.packages.${hostPkgs.system}.clan-cli
}/bin/clan", "machines", "list", "--flake", "${config.clan.test.flakeForSandbox}"],
check=True
)
'';
}
)

View File

@@ -0,0 +1,307 @@
{ self, ... }:
{
# Machine for update test
clan.machines.test-update-machine = {
imports = [
self.nixosModules.test-update-machine
# Import the configuration file that will be created/updated during the test
./test-update-machine/configuration.nix
];
};
flake.nixosModules.test-update-machine =
{ lib, modulesPath, ... }:
{
imports = [
(modulesPath + "/testing/test-instrumentation.nix")
(modulesPath + "/profiles/qemu-guest.nix")
self.clanLib.test.minifyModule
../../lib/test/container-test-driver/nixos-module.nix
];
# Apply patch to fix x-initrd.mount filesystem handling in switch-to-configuration-ng
nixpkgs.overlays = [
(_final: prev: {
switch-to-configuration-ng = prev.switch-to-configuration-ng.overrideAttrs (old: {
patches = (old.patches or [ ]) ++ [ ./switch-to-configuration-initrd-mount-fix.patch ];
});
})
];
networking.hostName = "update-machine";
environment.etc."install-successful".text = "ok";
# Enable SSH and add authorized key for testing
services.openssh.enable = true;
services.openssh.settings.PasswordAuthentication = false;
users.users.root.openssh.authorizedKeys.keys = [ (builtins.readFile ../assets/ssh/pubkey) ];
services.openssh.knownHosts.localhost.publicKeyFile = ../assets/ssh/pubkey;
services.openssh.hostKeys = [
{
path = ../assets/ssh/privkey;
type = "ed25519";
}
];
security.sudo.wheelNeedsPassword = false;
boot.consoleLogLevel = lib.mkForce 100;
boot.kernelParams = [ "boot.shell_on_fail" ];
boot.isContainer = true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
# Preserve the IP addresses assigned by the test framework
# (based on virtualisation.vlans = [1] and node number 1)
networking.interfaces.eth1 = {
useDHCP = false;
ipv4.addresses = [
{
address = "192.168.1.1";
prefixLength = 24;
}
];
ipv6.addresses = [
{
address = "2001:db8:1::1";
prefixLength = 64;
}
];
};
# Define the mounts that exist in the container to prevent them from being stopped
fileSystems = {
"/" = {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
options = [ "x-initrd.mount" ];
};
"/nix/.rw-store" = {
device = "tmpfs";
fsType = "tmpfs";
options = [
"mode=0755"
];
};
"/nix/store" = {
device = "overlay";
fsType = "overlay";
options = [
"lowerdir=/nix/.ro-store"
"upperdir=/nix/.rw-store/upper"
"workdir=/nix/.rw-store/work"
];
};
};
};
perSystem =
{
pkgs,
...
}:
{
checks =
pkgs.lib.optionalAttrs (pkgs.stdenv.isLinux && pkgs.stdenv.hostPlatform.system == "x86_64-linux")
{
nixos-test-update =
let
closureInfo = pkgs.closureInfo {
rootPaths = [
self.packages.${pkgs.system}.clan-cli
self.checks.${pkgs.system}.clan-core-for-checks
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-update-machine.config.system.build.toplevel
pkgs.stdenv.drvPath
pkgs.bash.drvPath
pkgs.buildPackages.xorg.lndir
]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
};
in
self.clanLib.test.containerTest {
name = "update";
nodes.machine = {
imports = [ self.nixosModules.test-update-machine ];
};
extraPythonPackages = _p: [
self.legacyPackages.${pkgs.system}.nixosTestLib
];
testScript = ''
import tempfile
import os
import subprocess
from nixos_test_lib.ssh import setup_ssh_connection # type: ignore[import-untyped]
from nixos_test_lib.nix_setup import prepare_test_flake # type: ignore[import-untyped]
start_all()
machine.wait_for_unit("multi-user.target")
# Verify initial state
machine.succeed("test -f /etc/install-successful")
machine.fail("test -f /etc/update-successful")
# Set up test environment
with tempfile.TemporaryDirectory() as temp_dir:
# Prepare test flake and Nix store
flake_dir = prepare_test_flake(
temp_dir,
"${self.checks.x86_64-linux.clan-core-for-checks}",
"${closureInfo}"
)
(flake_dir / ".clan-flake").write_text("") # Ensure .clan-flake exists
# Set up SSH connection
ssh_conn = setup_ssh_connection(
machine,
temp_dir,
"${../assets/ssh/privkey}"
)
# Update the machine configuration to add a new file
machine_config_path = os.path.join(flake_dir, "machines", "test-update-machine", "configuration.nix")
os.makedirs(os.path.dirname(machine_config_path), exist_ok=True)
# Note: update command doesn't accept -i flag, SSH key must be in ssh-agent
# Start ssh-agent and add the key
agent_output = subprocess.check_output(["${pkgs.openssh}/bin/ssh-agent", "-s"], text=True)
for line in agent_output.splitlines():
if line.startswith("SSH_AUTH_SOCK="):
os.environ["SSH_AUTH_SOCK"] = line.split("=", 1)[1].split(";")[0]
elif line.startswith("SSH_AGENT_PID="):
os.environ["SSH_AGENT_PID"] = line.split("=", 1)[1].split(";")[0]
# Add the SSH key to the agent
subprocess.run(["${pkgs.openssh}/bin/ssh-add", ssh_conn.ssh_key], check=True)
##############
print("TEST: update with --build-host local")
with open(machine_config_path, "w") as f:
f.write("""
{
environment.etc."update-build-local-successful".text = "ok";
}
""")
# rsync the flake into the container
os.environ["PATH"] = f"{os.environ['PATH']}:${pkgs.openssh}/bin"
subprocess.run(
[
"${pkgs.rsync}/bin/rsync",
"-a",
"--delete",
"-e",
"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no",
f"{str(flake_dir)}/",
f"root@192.168.1.1:/flake",
],
check=True
)
# allow machine to ssh into itself
subprocess.run([
"ssh",
"-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no",
f"root@192.168.1.1",
"mkdir -p /root/.ssh && chmod 700 /root/.ssh && echo \"$(cat \"${../assets/ssh/privkey}\")\" > /root/.ssh/id_ed25519 && chmod 600 /root/.ssh/id_ed25519",
], check=True)
# install the clan-cli package into the container's Nix store
subprocess.run(
[
"${pkgs.nix}/bin/nix",
"copy",
"--to",
"ssh://root@192.168.1.1",
"--no-check-sigs",
f"${self.packages.${pkgs.system}.clan-cli}",
"--extra-experimental-features", "nix-command flakes",
"--from", f"{os.environ["TMPDIR"]}/store"
],
check=True,
env={
**os.environ,
"NIX_SSHOPTS": "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no",
},
)
# Run ssh on the host to run the clan update command via --build-host local
subprocess.run([
"ssh",
"-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no",
f"root@192.168.1.1",
"${self.packages.${pkgs.system}.clan-cli}/bin/clan",
"machines",
"update",
"--debug",
"--flake", "/flake",
"--host-key-check", "none",
"--upload-inputs", # Use local store instead of fetching from network
"--build-host", "localhost",
"test-update-machine",
"--target-host", f"root@localhost",
], check=True)
# Verify the update was successful
machine.succeed("test -f /etc/update-build-local-successful")
##############
print("TEST: update with --target-host")
with open(machine_config_path, "w") as f:
f.write("""
{
environment.etc."target-host-update-successful".text = "ok";
}
""")
# Run clan update command
subprocess.run([
"${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
"machines",
"update",
"--debug",
"--flake", flake_dir,
"--host-key-check", "none",
"--upload-inputs", # Use local store instead of fetching from network
"test-update-machine",
"--target-host", f"root@192.168.1.1:{ssh_conn.host_port}",
], check=True)
# Verify the update was successful
machine.succeed("test -f /etc/target-host-update-successful")
##############
print("TEST: update with --build-host")
# Update configuration again
with open(machine_config_path, "w") as f:
f.write("""
{
environment.etc."build-host-update-successful".text = "ok";
}
""")
# Run clan update command with --build-host
subprocess.run([
"${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
"machines",
"update",
"--debug",
"--flake", flake_dir,
"--host-key-check", "none",
"--upload-inputs", # Use local store instead of fetching from network
"--build-host", f"root@192.168.1.1:{ssh_conn.host_port}",
"test-update-machine",
"--target-host", f"root@192.168.1.1:{ssh_conn.host_port}",
], check=True)
# Verify the second update was successful
machine.succeed("test -f /etc/build-host-update-successful")
'';
} { inherit pkgs self; };
};
};
}

View File

@@ -0,0 +1,17 @@
diff --git a/src/main.rs b/src/main.rs
index 8baf5924a7db..1234567890ab 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1295,6 +1295,12 @@ won't take effect until you reboot the system.
for (mountpoint, current_filesystem) in current_filesystems {
// Use current version of systemctl binary before daemon is reexeced.
+
+ // Skip filesystem comparison if x-initrd.mount is present in options
+ if current_filesystem.options.contains("x-initrd.mount") {
+ continue;
+ }
+
let unit = path_to_unit_name(&current_system_bin, &mountpoint);
if let Some(new_filesystem) = new_filesystems.get(&mountpoint) {
if current_filesystem.fs_type != new_filesystem.fs_type

View File

@@ -0,0 +1,3 @@
{
# Initial empty configuration
}

View File

@@ -112,8 +112,7 @@ in
'';
in
lib.mkIf (cfg.targets != { }) {
environment.systemPackages =
[
environment.systemPackages = [
(pkgs.writeShellScriptBin "localbackup-create" ''
set -efu -o pipefail
export PATH=${

View File

@@ -61,7 +61,6 @@ in
};
};
imports = [
../postgresql
(lib.mkRemovedOptionModule [
"clan"
"matrix-synapse"
@@ -106,18 +105,18 @@ in
};
};
clan.postgresql.users.matrix-synapse = { };
clan.postgresql.databases.matrix-synapse.create.options = {
clan.core.postgresql.enable = true;
clan.core.postgresql.users.matrix-synapse = { };
clan.core.postgresql.databases.matrix-synapse.create.options = {
TEMPLATE = "template0";
LC_COLLATE = "C";
LC_CTYPE = "C";
ENCODING = "UTF8";
OWNER = "matrix-synapse";
};
clan.postgresql.databases.matrix-synapse.restore.stopOnRestore = [ "matrix-synapse" ];
clan.core.postgresql.databases.matrix-synapse.restore.stopOnRestore = [ "matrix-synapse" ];
clan.core.vars.generators =
{
clan.core.vars.generators = {
"matrix-synapse" = {
files."synapse-registration_shared_secret" = { };
runtimeInputs = with pkgs; [
@@ -144,8 +143,7 @@ in
systemd.services.matrix-synapse =
let
usersScript =
''
usersScript = ''
while ! ${pkgs.netcat}/bin/nc -z -v ::1 8008; do
if ! kill -0 "$MAINPID"; then exit 1; fi
sleep 1;

View File

@@ -1,224 +1,9 @@
{ lib, ... }:
{
pkgs,
lib,
config,
...
}:
let
createDatabaseState =
db:
let
folder = "/var/backup/postgres/${db.name}";
current = "${folder}/pg-dump";
compression = lib.optionalString (lib.versionAtLeast config.services.postgresql.package.version "16") "--compress=zstd";
in
{
folders = [ folder ];
preBackupScript = ''
export PATH=${
lib.makeBinPath [
config.services.postgresql.package
config.systemd.package
pkgs.coreutils
pkgs.util-linux
pkgs.zstd
]
}
while [[ "$(systemctl is-active postgresql)" == activating ]]; do
sleep 1
done
mkdir -p "${folder}"
runuser -u postgres -- pg_dump ${compression} --dbname=${db.name} -Fc -c > "${current}.tmp"
mv "${current}.tmp" ${current}
'';
postRestoreScript = ''
export PATH=${
lib.makeBinPath [
config.services.postgresql.package
config.systemd.package
pkgs.coreutils
pkgs.util-linux
pkgs.zstd
pkgs.gnugrep
]
}
while [[ "$(systemctl is-active postgresql)" == activating ]]; do
sleep 1
done
echo "Waiting for postgres to be ready..."
while ! runuser -u postgres -- psql --port=${builtins.toString config.services.postgresql.settings.port} -d postgres -c "" ; do
if ! systemctl is-active postgresql; then exit 1; fi
sleep 0.1
done
if [[ -e "${current}" ]]; then
(
systemctl stop ${lib.concatStringsSep " " db.restore.stopOnRestore}
trap "systemctl start ${lib.concatStringsSep " " db.restore.stopOnRestore}" EXIT
mkdir -p "${folder}"
if runuser -u postgres -- psql -d postgres -c "SELECT 1 FROM pg_database WHERE datname = '${db.name}'" | grep -q 1; then
runuser -u postgres -- dropdb "${db.name}"
fi
runuser -u postgres -- pg_restore -C -d postgres "${current}"
)
else
echo No database backup found, skipping restore
fi
'';
};
createDatabase = db: ''
CREATE DATABASE "${db.name}" ${
lib.concatStringsSep " " (
lib.mapAttrsToList (name: value: "${name} = '${value}'") db.create.options
)
}
'';
cfg = config.clan.postgresql;
userClauses = lib.mapAttrsToList (
_: user:
''$PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"' ''
) cfg.users;
databaseClauses = lib.mapAttrsToList (
name: db:
lib.optionalString db.create.enable ''$PSQL -d postgres -c "SELECT 1 FROM pg_database WHERE datname = '${name}'" | grep -q 1 || $PSQL -d postgres -c ${lib.escapeShellArg (createDatabase db)} ''
) cfg.databases;
in
{
options.clan.postgresql = {
# we are reimplemeting ensureDatabase and ensureUser options here to allow to create databases with options
databases = lib.mkOption {
description = "Databases to create";
default = { };
type = lib.types.attrsOf (
lib.types.submodule (
{ name, ... }:
{
options = {
name = lib.mkOption {
type = lib.types.str;
default = name;
description = "Database name.";
};
service = lib.mkOption {
type = lib.types.str;
default = name;
description = "Service name that we associate with the database.";
};
# set to false, in case the upstream module uses ensureDatabase option
create.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Create the database if it does not exist.";
};
create.options = lib.mkOption {
description = "Options to pass to the CREATE DATABASE command.";
type = lib.types.lazyAttrsOf lib.types.str;
default = { };
example = {
TEMPLATE = "template0";
LC_COLLATE = "C";
LC_CTYPE = "C";
ENCODING = "UTF8";
OWNER = "foo";
};
};
restore.stopOnRestore = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "List of systemd services to stop before restoring the database.";
};
};
}
)
);
};
users = lib.mkOption {
description = "Users to create";
default = { };
type = lib.types.attrsOf (
lib.types.submodule (
{ name, ... }:
{
options.name = lib.mkOption {
description = "User name";
type = lib.types.str;
default = name;
};
}
)
);
};
};
config = {
services.postgresql.settings = {
wal_level = "replica";
max_wal_senders = 3;
};
services.postgresql.enable = true;
# We are duplicating a bit the upstream module but allow to create databases with options
systemd.services.postgresql.postStart = ''
PSQL="psql --port=${builtins.toString config.services.postgresql.settings.port}"
while ! $PSQL -d postgres -c "" 2> /dev/null; do
if ! kill -0 "$MAINPID"; then exit 1; fi
sleep 0.1
done
${lib.concatStringsSep "\n" userClauses}
${lib.concatStringsSep "\n" databaseClauses}
'';
clan.core.state = lib.mapAttrs' (
_: db: lib.nameValuePair db.service (createDatabaseState db)
) config.clan.postgresql.databases;
environment.systemPackages = builtins.map (
db:
let
folder = "/var/backup/postgres/${db.name}";
current = "${folder}/pg-dump";
in
pkgs.writeShellScriptBin "postgres-db-restore-command-${db.name}" ''
export PATH=${
lib.makeBinPath [
config.services.postgresql.package
config.systemd.package
pkgs.coreutils
pkgs.util-linux
pkgs.zstd
pkgs.gnugrep
]
}
while [[ "$(systemctl is-active postgresql)" == activating ]]; do
sleep 1
done
echo "Waiting for postgres to be ready..."
while ! runuser -u postgres -- psql --port=${builtins.toString config.services.postgresql.settings.port} -d postgres -c "" ; do
if ! systemctl is-active postgresql; then exit 1; fi
sleep 0.1
done
if [[ -e "${current}" ]]; then
(
${lib.optionalString (db.restore.stopOnRestore != [ ]) ''
systemctl stop ${builtins.toString db.restore.stopOnRestore}
trap "systemctl start ${builtins.toString db.restore.stopOnRestore}" EXIT
''}
mkdir -p "${folder}"
if runuser -u postgres -- psql -d postgres -c "SELECT 1 FROM pg_database WHERE datname = '${db.name}'" | grep -q 1; then
runuser -u postgres -- dropdb "${db.name}"
fi
runuser -u postgres -- pg_restore -C -d postgres "${current}"
)
else
echo No database backup found, skipping restore
fi
''
) (builtins.attrValues config.clan.postgresql.databases);
};
imports = [
(lib.mkRemovedOptionModule [
"clan"
"postgresql"
] "The postgresql module has been migrated to a clan core option. Use clan.core.postgresql instead")
];
}

View File

@@ -18,8 +18,7 @@
config.clan.core.vars.generators.root-password.files.password-hash.path;
clan.core.vars.generators.root-password = {
files.password-hash =
{
files.password-hash = {
neededFor = "users";
}
// (lib.optionalAttrs (_class == "nixos") {

View File

@@ -32,8 +32,7 @@ in
cfg.certificate.searchDomains != [ ]
) config.clan.core.vars.generators.openssh-cert.files."ssh.id_ed25519-cert.pub".path;
hostKeys =
[
hostKeys = [
{
path = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519".path;
type = "ed25519";
@@ -62,7 +61,8 @@ in
hostNames = [
"localhost"
config.networking.hostName
] ++ (lib.optional (config.networking.domain != null) config.networking.fqdn);
]
++ (lib.optional (config.networking.domain != null) config.networking.fqdn);
publicKey = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519.pub".value;
};

View File

@@ -1,3 +1,27 @@
---
description = "Statically configure syncthing peers through clan"
description = "DEPRECATED: Statically configure syncthing peers through clan"
---
# ⚠️ DEPRECATED
This module has been migrated to the new clanServices system.
Please use the new syncthing service instead:
```nix
{
services.syncthing = {
instances.default = {
roles.peer.machines = {
machine1 = { };
machine2 = { };
machine3 = {
excludeMachines = [ "machine4" ];
};
};
};
};
}
```
The new service provides the same functionality with better integration into clan's inventory system.

View File

@@ -4,6 +4,8 @@
pkgs,
...
}:
# DEPRECATED: This module has been migrated to clanServices/syncthing
# Please use the syncthing service instead: services.syncthing.instances.default.roles.peer.machines = { ... };
let
dir = config.clan.core.settings.directory;
machineVarDir = "${dir}/vars/per-machine/";
@@ -32,8 +34,9 @@ let
value = {
name = machine;
id = (lib.removeSuffix "\n" (builtins.readFile (syncthingPublicKeyPath machine)));
addresses =
[ "dynamic" ]
addresses = [
"dynamic"
]
++ (
if (lib.elem machine networkIpMachines) then
[ "tcp://[${(lib.removeSuffix "\n" (builtins.readFile (zerotierIpMachinePath machine)))}]:22000" ]

View File

@@ -10,7 +10,6 @@ in
{
imports = [
../postgresql
(lib.mkRemovedOptionModule [
"clan"
"vaultwarden"
@@ -57,15 +56,17 @@ in
config = {
clan.postgresql.users.vaultwarden = { };
clan.postgresql.databases.vaultwarden.create.options = {
clan.core.postgresql.enable = true;
clan.core.postgresql.users.vaultwarden = { };
clan.core.postgresql.databases.vaultwarden.create.options = {
TEMPLATE = "template0";
LC_COLLATE = "C";
LC_CTYPE = "C";
ENCODING = "UTF8";
OWNER = "vaultwarden";
};
clan.postgresql.databases.vaultwarden.restore.stopOnRestore = [ "vaultwarden" ];
clan.core.postgresql.databases.vaultwarden.restore.stopOnRestore = [ "vaultwarden" ];
services.nginx = {
enable = true;

View File

@@ -24,7 +24,7 @@
prompts.password.type = "hidden";
prompts.password.persist = true;
prompts.password.description = "You can autogenerate a password, if you leave this prompt blank.";
prompts.password.description = "Leave empty to generate automatically";
script = ''
prompt_value="$(cat "$prompts"/password)"

View File

@@ -21,8 +21,7 @@ in
settings.certificateSearchDomains != [ ]
) config.clan.core.vars.generators.openssh-cert.files."ssh.id_ed25519-cert.pub".path;
hostKeys =
[
hostKeys = [
{
path = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519".path;
type = "ed25519";
@@ -51,7 +50,8 @@ in
hostNames = [
"localhost"
config.networking.hostName
] ++ (lib.optional (config.networking.domain != null) config.networking.fqdn);
]
++ (lib.optional (config.networking.domain != null) config.networking.fqdn);
publicKey = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519.pub".value;
};

View File

@@ -1,29 +0,0 @@
{
lib,
config,
settings,
...
}:
{
services.data-mesher.initNetwork =
let
# for a given machine, read it's public key and remove any new lines
readHostKey =
machine:
let
path = "${config.clan.core.settings.directory}/vars/per-machine/${machine}/data-mesher-host-key/public_key/value";
in
builtins.elemAt (lib.splitString "\n" (builtins.readFile path)) 1;
in
{
enable = true;
keyPath = config.clan.core.vars.generators.data-mesher-network-key.files.private_key.path;
tld = settings.network.tld;
hostTTL = settings.network.hostTTL;
# admin and signer host public keys
signingKeys = builtins.map readHostKey (builtins.attrNames settings.bootstrapNodes);
};
}

View File

@@ -5,31 +5,15 @@ let
{
options = {
bootstrapNodes = lib.mkOption {
type = lib.types.nullOr (lib.types.attrsOf lib.types.str);
# the default bootstrap nodes are any machines with the admin or signers role
# we iterate through those machines, determining an IP address for them based on their VPN
# currently only supports zerotier
# default = builtins.foldl' (
# urls: name:
# let
# ipPath = "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value";
# in
# if builtins.pathExists ipPath then
# let
# ip = builtins.readFile ipPath;
# in
# urls ++ [ "[${ip}]:${builtins.toString settings.network.port}" ]
# else
# urls
# ) [ ] (dmLib.machines config).bootstrap;
type = lib.types.nullOr (lib.types.listOf lib.types.str);
description = ''
A list of bootstrap nodes that act as an initial gateway when joining
the cluster.
'';
example = {
"node1" = "192.168.1.1:7946";
"node2" = "192.168.1.2:7946";
};
example = [
"192.168.1.1:7946"
"192.168.1.2:7946"
];
};
network = {
@@ -55,6 +39,59 @@ let
};
};
};
mkBootstrapNodes =
{
config,
lib,
roles,
settings,
}:
lib.mkDefault (
builtins.foldl' (
urls: name:
let
ipPath = "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value";
in
if builtins.pathExists ipPath then
let
ip = builtins.readFile ipPath;
in
urls ++ [ "[${ip}]:${builtins.toString settings.network.port}" ]
else
urls
) [ ] (builtins.attrNames ((roles.admin.machines or { }) // (roles.signer.machines or { })))
);
mkDmService = dmSettings: config: {
enable = true;
openFirewall = true;
settings = {
log_level = "warn";
state_dir = "/var/lib/data-mesher";
# read network id from vars
network.id = config.clan.core.vars.generators.data-mesher-network-key.files.public_key.value;
host = {
names = [ config.networking.hostName ];
key_path = config.clan.core.vars.generators.data-mesher-host-key.files.private_key.path;
};
cluster = {
port = dmSettings.network.port;
join_interval = "30s";
push_pull_interval = "30s";
interface = dmSettings.network.interface;
bootstrap_nodes = dmSettings.bootstrapNodes;
};
http.port = 7331;
http.interface = "lo";
};
};
in
{
_class = "clan.service";
@@ -67,11 +104,9 @@ in
interface =
{ lib, ... }:
{
imports = [ sharedInterface ];
options = {
network = {
tld = lib.mkOption {
type = lib.types.str;
@@ -89,54 +124,117 @@ in
};
};
perInstance =
{ settings, roles, ... }:
{
nixosModule = {
imports = [
./admin.nix
./shared.nix
];
_module.args = { inherit settings roles; };
extendSettings,
roles,
lib,
...
}:
{
nixosModule =
{ config, ... }:
let
settings = extendSettings {
bootstrapNodes = mkBootstrapNodes {
inherit
config
lib
roles
settings
;
};
};
in
{
imports = [ ./shared.nix ];
services.data-mesher = (mkDmService settings config) // {
initNetwork =
let
# for a given machine, read it's public key and remove any new lines
readHostKey =
machine:
let
path = "${config.clan.core.settings.directory}/vars/per-machine/${machine}/data-mesher-host-key/public_key/value";
in
builtins.elemAt (lib.splitString "\n" (builtins.readFile path)) 1;
in
{
enable = true;
keyPath = config.clan.core.vars.generators.data-mesher-network-key.files.private_key.path;
tld = settings.network.tld;
hostTTL = settings.network.hostTTL;
# admin and signer host public keys
signingKeys = builtins.map readHostKey (
builtins.attrNames ((roles.admin.machines or { }) // (roles.signer.machines or { }))
);
};
};
};
};
};
roles.signer = {
interface =
{ ... }:
{
imports = [ sharedInterface ];
};
interface = sharedInterface;
perInstance =
{ settings, roles, ... }:
{
nixosModule = {
imports = [
./signer.nix
./shared.nix
];
_module.args = { inherit settings roles; };
extendSettings,
lib,
roles,
...
}:
{
nixosModule =
{ config, ... }:
let
settings = extendSettings {
bootstrapNodes = mkBootstrapNodes {
inherit
config
lib
roles
settings
;
};
};
in
{
imports = [ ./shared.nix ];
services.data-mesher = (mkDmService settings config);
};
};
};
roles.peer = {
interface =
{ ... }:
{
imports = [ sharedInterface ];
};
interface = sharedInterface;
perInstance =
{ settings, roles, ... }:
{
nixosModule = {
imports = [
./peer.nix
./shared.nix
];
_module.args = { inherit settings roles; };
extendSettings,
lib,
roles,
...
}:
{
nixosModule =
{ config, ... }:
let
settings = extendSettings {
bootstrapNodes = mkBootstrapNodes {
inherit
config
lib
roles
settings
;
};
};
in
{
imports = [ ./shared.nix ];
services.data-mesher = (mkDmService settings config);
};
};
};
}

View File

@@ -1,39 +1,9 @@
{
config,
settings,
...
}:
{
services.data-mesher = {
enable = true;
openFirewall = true;
settings = {
log_level = "warn";
state_dir = "/var/lib/data-mesher";
# read network id from vars
network.id = config.clan.core.vars.generators.data-mesher-network-key.files.public_key.value;
host = {
names = [ config.networking.hostName ];
key_path = config.clan.core.vars.generators.data-mesher-host-key.files.private_key.path;
};
cluster = {
port = settings.network.port;
join_interval = "30s";
push_pull_interval = "30s";
interface = settings.network.interface;
bootstrap_nodes = (builtins.attrValues settings.bootstrapNodes);
};
http.port = 7331;
http.interface = "lo";
};
};
# Generate host key.
clan.core.vars.generators.data-mesher-host-key = {
files =

View File

@@ -16,11 +16,11 @@
instances = {
data-mesher =
let
bootstrapNodes = {
admin = "[2001:db8:1::1]:7946";
peer = "[2001:db8:1::2]:7946";
# signer = "2001:db8:1::3:7946";
};
bootstrapNodes = [
"[2001:db8:1::1]:7946" # admin
"[2001:db8:1::2]:7946" # peer
# "2001:db8:1::3:7946" #signer
];
in
{
roles.peer.machines.peer.settings = {

View File

@@ -184,8 +184,7 @@
settings.certificate.searchDomains != [ ]
) config.clan.core.vars.generators.openssh-cert.files."ssh.id_ed25519-cert.pub".path;
hostKeys =
[
hostKeys = [
{
path = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519".path;
type = "ed25519";
@@ -201,7 +200,8 @@
hostNames = [
"localhost"
config.networking.hostName
] ++ (lib.optional (config.networking.domain != null) config.networking.fqdn);
]
++ (lib.optional (config.networking.domain != null) config.networking.fqdn);
publicKey = config.clan.core.vars.generators.openssh.files."ssh.id_ed25519.pub".value;
};
};

View File

@@ -0,0 +1,20 @@
## Usage
```nix
{
instances.syncthing = {
roles.peer.tags.all = { };
roles.peer.settings.folders = {
documents = {
path = "~/syncthing/documents";
};
};
};
}
```
Now the folder `~/syncthing/documents` will be shared with all your machines.
## Documentation
Extensive documentation is available on the [Syncthing](https://docs.syncthing.net/) website.

View File

@@ -0,0 +1,243 @@
{ ... }:
{
_class = "clan.service";
manifest.name = "clan-core/syncthing";
manifest.description = "Syncthing is a continuous file synchronization program with automatic peer discovery";
manifest.categories = [
"Utility"
"System"
"Network"
];
manifest.readme = builtins.readFile ./README.md;
roles.peer = {
interface =
{ lib, ... }:
{
options.openDefaultPorts = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to open the default syncthing ports in the firewall.
'';
};
options.folders = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule {
options = {
path = lib.mkOption {
type = lib.types.str;
description = "Path to the folder to sync";
};
devices = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "List of device names to share this folder with. Empty list means all peers and extraDevices.";
};
ignorePerms = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Ignore permission changes";
};
rescanIntervalS = lib.mkOption {
type = lib.types.int;
default = 3600;
description = "Rescan interval in seconds";
};
type = lib.mkOption {
type = lib.types.enum [
"sendreceive"
"sendonly"
"receiveonly"
];
default = "sendreceive";
description = "Folder type";
};
versioning = lib.mkOption {
type = lib.types.nullOr (
lib.types.submodule {
options = {
type = lib.mkOption {
type = lib.types.enum [
"external"
"simple"
"staggered"
"trashcan"
];
description = "Versioning type";
};
params = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
description = "Versioning parameters";
};
};
}
);
default = null;
description = "Versioning configuration";
};
};
}
);
default = { };
description = "Folders to synchronize between all peers";
};
options.extraDevices = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule {
options = {
id = lib.mkOption {
type = lib.types.str;
description = "Device ID of the external syncthing device";
example = "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2";
};
addresses = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "dynamic" ];
description = "List of addresses for the device";
example = [
"dynamic"
"tcp://192.168.1.100:22000"
];
};
name = lib.mkOption {
type = lib.types.str;
default = "";
description = "Human readable name for the device";
};
};
}
);
default = { };
description = "External syncthing devices not managed by clan (e.g., mobile phones)";
example = lib.literalExpression ''
{
phone = {
id = "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2";
name = "My Phone";
addresses = [ "dynamic" ];
};
}
'';
};
};
perInstance =
{
settings,
roles,
...
}:
{
nixosModule =
{
config,
lib,
...
}:
let
allPeerMachines = lib.attrNames roles.peer.machines;
readMachineVar =
machine: varPath: default:
let
fullPath = "${config.clan.core.settings.directory}/vars/per-machine/${machine}/${varPath}";
in
if builtins.pathExists fullPath then
lib.removeSuffix "\n" (builtins.readFile fullPath)
else
default;
peerDevices = lib.listToAttrs (
lib.forEach allPeerMachines (machine: {
name = machine;
value = {
name = machine;
id = readMachineVar machine "syncthing/id/value" "";
addresses = [
"dynamic"
]
++
lib.optional (readMachineVar machine "zerotier/zerotier-ip/value" null != null)
"tcp://[${readMachineVar machine "zerotier/zerotier-ip/value" ""}]:22000";
};
})
);
extraDevicesConfig = lib.mapAttrs (deviceName: deviceConfig: {
inherit (deviceConfig) id addresses;
name = if deviceConfig.name != "" then deviceConfig.name else deviceName;
}) settings.extraDevices;
allDevices = peerDevices // extraDevicesConfig;
validDevices = lib.filterAttrs (_: device: device.id != "") allDevices;
syncthingFolders = lib.mapAttrs (
_folderName: folderConfig:
let
targetDevices =
if folderConfig.devices == [ ] then lib.attrNames validDevices else folderConfig.devices;
in
folderConfig
// {
devices = targetDevices;
}
) settings.folders;
in
{
imports = [
./vars.nix
];
config = lib.mkMerge [
{
services.syncthing = {
enable = true;
configDir = "/var/lib/syncthing";
group = "syncthing";
key = lib.mkDefault config.clan.core.vars.generators.syncthing.files.key.path or null;
cert = lib.mkDefault config.clan.core.vars.generators.syncthing.files.cert.path or null;
settings = {
devices = validDevices;
folders = syncthingFolders;
};
};
}
# Conditionally open firewall ports
(lib.mkIf settings.openDefaultPorts {
services.syncthing.openDefaultPorts = true;
# Syncthing ports: 8384 for remote access to GUI
# 22000 TCP and/or UDP for sync traffic
# 21027/UDP for discovery
# source: https://docs.syncthing.net/users/firewall.html
networking.firewall.interfaces."zt+".allowedTCPPorts = [
8384
22000
];
networking.firewall.interfaces."zt+".allowedUDPPorts = [
22000
21027
];
})
];
};
};
};
perMachine = _: {
nixosModule =
{ lib, ... }:
{
# Activates inotify compatibility on syncthing
# use mkOverride 900 here as it otherwise would collide with the default of the
# upstream nixos xserver.nix
boot.kernel.sysctl."fs.inotify.max_user_watches" = lib.mkOverride 900 524288;
};
};
}

View File

@@ -0,0 +1,54 @@
{
self,
lib,
inputs,
...
}:
let
module = lib.modules.importApply ./default.nix {
inherit (self) packages;
};
in
{
clan.modules = {
syncthing = module;
};
perSystem =
let
unit-test-module = (
self.clanLib.test.flakeModules.makeEvalChecks {
inherit module;
inherit inputs;
fileset = lib.fileset.unions [
# The zerotier service being tested
../../clanServices/syncthing
# Required modules
../../nixosModules/clanCore
# Dependencies like clan-cli
../../pkgs/clan-cli
];
testName = "syncthing";
tests = ./tests/eval-tests.nix;
testArgs = { };
}
);
in
{ ... }:
{
imports = [
unit-test-module
];
/**
1. Prepare the test vars
nix run .#generate-test-vars -- clanServices/syncthing/tests/vm syncthing-service
2. To run the test
nix build .#checks.x86_64-linux.syncthing-service
*/
clan.nixosTests.syncthing-service = {
imports = [ ./tests/vm/default.nix ];
clan.modules.syncthing-service = module;
};
};
}

View File

@@ -0,0 +1,62 @@
{
module,
clanLib,
lib,
...
}:
let
testFlake =
(clanLib.clan {
self = { };
directory = ./vm;
machines.machine1 = {
nixpkgs.hostPlatform = "x86_64-linux";
};
machines.machine2 = {
nixpkgs.hostPlatform = "x86_64-linux";
};
machines.machine3 = {
nixpkgs.hostPlatform = "x86_64-linux";
};
machines.machine4 = {
nixpkgs.hostPlatform = "x86_64-linux";
};
modules.syncthing = module;
inventory.instances = {
default = {
module.name = "syncthing";
module.input = "self";
roles.peer.tags.all = { };
roles.peer.settings.extraDevices.phone = {
id = "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2";
};
};
};
}).config;
in
{
test_machine1_peers = {
expr = {
devices = lib.attrNames testFlake.nixosConfigurations.machine1.config.services.syncthing.settings.devices;
machine4_ID =
testFlake.nixosConfigurations.machine1.config.services.syncthing.settings.devices.machine1.id;
externalPhoneId =
testFlake.nixosConfigurations.machine1.config.services.syncthing.settings.devices.phone.id;
};
expected = {
devices = [
"machine1"
"machine2"
"machine3"
"machine4"
"phone"
];
machine4_ID = "LJOGYGS-RQPWIHV-HD4B3GK-JZPVPK6-VI3IAY5-CWQWIXK-NJSQMFH-KXHOHA4";
externalPhoneId = "P56IOI7-MZJNU2Y-IQGDREY-DM2MGTI-MGL3BXN-PQ6W5BM-TBBZ4TJ-XZWICQ2";
};
};
}

View File

@@ -0,0 +1,95 @@
{
name = "service-syncthing-service";
clan = {
directory = ./.;
test.useContainers = true;
inventory = {
machines.machine1 = { };
machines.machine2 = { };
machines.machine3 = { };
machines.machine4 = { };
instances.default = {
module.name = "syncthing-service";
module.input = "self";
roles.peer.tags.all = { };
roles.peer.settings.folders = {
documents = {
path = "/var/lib/syncthing/documents";
type = "sendreceive";
};
partly_shared = {
devices = [
"machine1"
"machine4"
];
path = "~/music";
type = "sendreceive";
};
};
};
instances.small = {
module.name = "syncthing-service";
module.input = "self";
roles.peer.machines = {
machine3 = { };
machine4 = { };
};
roles.peer.settings.folders = {
pictures = {
path = "~/pictures";
type = "sendreceive";
};
};
};
};
};
testScript =
{ ... }:
''
start_all()
machine1.wait_for_unit("syncthing.service")
machine2.wait_for_unit("syncthing.service")
machine3.wait_for_unit("syncthing.service")
machine4.wait_for_unit("syncthing.service")
machine1.wait_for_open_port(8384)
machine2.wait_for_open_port(8384)
machine3.wait_for_open_port(8384)
machine4.wait_for_open_port(8384)
machine1.wait_for_open_port(22000)
machine2.wait_for_open_port(22000)
machine3.wait_for_open_port(22000)
machine4.wait_for_open_port(22000)
# Check that the correct folders are synchronized
# documents - all
machine1.wait_for_file("/var/lib/syncthing/documents")
machine2.wait_for_file("/var/lib/syncthing/documents")
machine3.wait_for_file("/var/lib/syncthing/documents")
machine4.wait_for_file("/var/lib/syncthing/documents")
# music - machine 1 & 4
machine1.wait_for_file("/var/lib/syncthing/music")
machine4.wait_for_file("/var/lib/syncthing/music")
# pictures - machine 3 & 4
machine3.wait_for_file("/var/lib/syncthing/pictures")
machine4.wait_for_file("/var/lib/syncthing/pictures")
machine1.succeed("echo document > /var/lib/syncthing/documents/document")
machine1.succeed("echo music > /var/lib/syncthing/music/music")
machine3.succeed("echo picture > /var/lib/syncthing/pictures/picture")
machine2.wait_for_file("/var/lib/syncthing/documents/document", 20)
machine3.wait_for_file("/var/lib/syncthing/documents/document", 20)
machine4.wait_for_file("/var/lib/syncthing/documents/document", 20)
machine4.wait_for_file("/var/lib/syncthing/music/music", 20)
machine4.wait_for_file("/var/lib/syncthing/pictures/picture", 20)
'';
}

View File

@@ -0,0 +1,6 @@
[
{
"publickey": "age1numxr6m52fxrm9a7sdw4vdpkp463mm8qtuf5d0p0jde04wydfgtscwdx78",
"type": "age"
}
]

View File

@@ -0,0 +1,6 @@
[
{
"publickey": "age1aqng9vmlgth5aucu5ty2wa0kk9tvk7erj4s07hq03s6emu72fgxsqkrqql",
"type": "age"
}
]

View File

@@ -0,0 +1,6 @@
[
{
"publickey": "age1kul02mg50nxccsl38nvma0enrgx454wq0qdefllj4l0adqkllvls5wuhfr",
"type": "age"
}
]

View File

@@ -0,0 +1,6 @@
[
{
"publickey": "age1kqgx4elusxx4u8409gml5z6tvrsayqsphewsl93mtqn7pl2p5dwq9lujpj",
"type": "age"
}
]

View File

@@ -0,0 +1,15 @@
{
"data": "ENC[AES256_GCM,data:XqOUvbQOkOUrxQAMrUSRGfOl2eYjGZGI5st1iZl9Re8ymQa0LtokGzknfSiSucwfP50YTo7NoQsNCRsq/nS0RWbby0TQceqSVSw=,iv:GT3UrKdanFy4aMRPTwuOIyvP7pT6MXGRIstgKZmvG/4=,tag:Bg3ZzCPpw3KHXwy2Hazqrg==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmWWJuTjBCaUhUeXhSRmZS\nVW9uQ09yOXVJVFFybzJsdmVmaEpOZVdEVVJNCmxxcUU3eG44cTlrY1lnVUFjdlZK\nOXNvR2pDMVJtZXFCbjJlTW1xVGoyTlUKLS0tIDlkMS9FNVJuZGZyODJzZzVRNzJQ\nNWpuaUxpMnRaUWt5bmw1TjBJV0dUOHcKmuHPHwzTUtkl7eZjZ4422C8cxGUnnzg1\nBPwAewDXEgq+clTXpU3UWjSvUNpqIkh9GDRE+qYfYWa7m36TrzACig==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:20:56Z",
"mac": "ENC[AES256_GCM,data:INCJWY2hsAwF049zFpYUTccJvczfgrPQwu+YUt8s/vfdrAk+tmLqCVFh9idG97l/OIEVPLv1gFE3SlcC+kKCNQV4SAyZA62CTeeNdgSSqDDipjrFn4fr1L8ZenCn56/giW0GIB2bBCa5WUYaHtGDoG8f6HSqNIhjnY9/qmDLUWQ=,iv:Utda22592sXOEEKFRSgfP+yLgi6FQGeEFiT61ZiKcws=,tag:UWyEnSNt0ztxTh0FGRzTtA==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../users/admin

View File

@@ -0,0 +1,15 @@
{
"data": "ENC[AES256_GCM,data:bDwReDwUe9t0lXtTRU5r6WY3OvV1qOvkkByG2PbjXtArF4w6y3X/i6SYCnoys3wMvAY32+uH13ETXDS7LR/6W4/+pCAGAb3ATqQ=,iv:84dC7UP6QkHVkLwSvmb/NjXCA9OJ+of49UfitU0Cuoo=,tag:oO15dzpH7BKjawPGI1+F0Q==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBVXpNTkhTU3A1OU5HZm9p\nb2t5YTIxZWU4Z3hZR0ljTUNRT2hvQXEwNjJRCjh4SXY1NWhPVFFTMWswaWo2cFJB\ndUx4V1RiRFQ1UkNQU24zaGlSV3ZYeE0KLS0tIGJ5ak9RS2JnNk0vU3BaMEZLTFI0\nZit5RDlZbWE1QzY0RHQzcU5pZy84TUkK3/HHFBGfA9EpU+WDrlM9w5rbmgOb7ZAi\nVQiO08PVGTOvsEDed5A2oIXImRopcuBATzKoi4DNXpYlpSI/scYuAA==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:21:08Z",
"mac": "ENC[AES256_GCM,data:qdNz02bNRHaWhTLxHqgvMRokpXZHvijyoP2c+KXMQxWP/7884SWQ4xoPSiUeLOPatLXpjXWQ/OBiDqQAzLkbC0/ujJH+BJQtV75xl6ytOjLbYw+hwatAR9C5GxiuFgKjLSsdaqzRlXJsULbf56edXeT+U/u5G8542WjF4AUat00=,iv:FYQRJQaRR0aPFyHyl0QCVPpjTXePXiZ2LjccPaybWh4=,tag:W/xkIgTUoHiTX9azluinAQ==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../users/admin

View File

@@ -0,0 +1,15 @@
{
"data": "ENC[AES256_GCM,data:2B9BeSPThkGX4sBAtm9c5Uc8vz5q+hSs8y0ibQ520s2EOYNWEEwP0e+9CBUmU1n0TKR4FP3HMHA0N7LKHBEXEb513I5lNHWgREc=,iv:qkmur96UjZ1BoWgvvihWk/7VRDg4snMeDA0s1MWdLb4=,tag:3djouRryDjcOFPv9Z8nLvg==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxd2wxQWQwSHl5R0ZlRkxX\nWXBuck04TStoTHRYMDJaQkNRcDQvZFV6U0U0ClROOVpiVXhCVTdzeXpVQlRTYWRP\naWE2R0VtaGwvUk9UcGRWbEZtQlh5ckkKLS0tIDNjdm40UCthbDBwSE9ZYXBwVC9h\nRENrMFV2a21QRjBBYWJ4bFZ3K2NxWDAKr9hgDOuG4lR6dChQCvw/VOQdRNF0Mj1k\nzQRfQaC8nfZGGOJtU09zv5+ZPYJdheRuz91M18qY5uA5qrch5Yrvww==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:21:21Z",
"mac": "ENC[AES256_GCM,data:F6Ombz312QLl0BAkDbhN37KQ8gxMoukRqz8XkqANcdm8HyqfWL/V5Hxdt5Ve3HW4KzLO1lxb9M0/rQbtCj1hKMSnfM4XjP1R4ClLkU0knxeuLwbOtc4nN1qkaIs5fPcR0G9+gv+FnhZqDzosrhogDSWAayqiArwRWscMY4l716w=,iv:KYlcVQQGcWPIsL81VmTRmEBmC6PPT71i3ywcEbSfc5k=,tag:ogDnTv1KtXVtBDvhkjV1pw==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../users/admin

View File

@@ -0,0 +1,15 @@
{
"data": "ENC[AES256_GCM,data:chE6ezkSGLHwvfqrUPiTLEoB63NhzBJgHQnGLe/0mwuR9iST3SpthUlKv7K8cezOvq7VVqJUoOGQSbqtuA04NUvwShVCBbqPjeA=,iv:50IV6ggQVnQpUpU+jfBB2z5/BZYs3lktGMfsWsCtJUs=,tag:BjtV31lmGOT5FposJA91WA==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZSVAzVXcrMWdUQmNCNHo4\neWw5NnJjYkdHbkE1dmJPcDlNb1dpT3kxbFN3Cm1HWVFxblgrNkpaZmtOVHBpdEVF\nWXRMamdSUGxkSk5ic3p3cEZrNG5QZEkKLS0tIDdYOGZRclREL0o1bWZmVTZoMUlu\nQzJPZFlUdFE4NkE0di94QVRzRXgwb1EKkqzL7yOdALLd8I2GnKPG3d61XTbNMs4+\n52M+o+wA8h+mnImbyVOtU9D6AkxvbKywZLmNRukkPqnkLqu5IXoSMQ==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:21:34Z",
"mac": "ENC[AES256_GCM,data:DI61cHhur+0q+gGtXHA6uFyZyKY/hCTvo2MLhhWPCSUWzYJ+j37fHfH1KUZcol+5prhnUIRrC4AoVt+0MeoXrNj+3SyptTVo1MgqNBayQkBMxKYp++SbqlXnlkLD+XOohpw6+f67rGNecjNc/OwCcfXu7PZmFhAFkwC39hUm7l0=,iv:lyinQFzoXo37Zs1QtbM1Jla+KRSMSUcph7bIR6/X1nw=,tag:nFfq7KtaTwZkQ9joCUOE2w==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../users/admin

View File

@@ -0,0 +1,4 @@
{
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"type": "age"
}

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/machine1

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:H+qlkDBtl4xYPz7X56gamUTQfuDE5+m/BkDPK9963A9F,iv:paPRFOnxW8aSt/NYg4afp+M+8svt0j4G6mZaJms/c7o=,tag:9uxvemHUr6Jm0MzvSf9cxQ==,type:str]",
"sops": {
"age": [
{
"recipient": "age1numxr6m52fxrm9a7sdw4vdpkp463mm8qtuf5d0p0jde04wydfgtscwdx78",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBM1ZUaEwwOGVFSVF0dlRX\nQzdMRjJFZU5kSTNDTzVrZ1JzNHdROXpGNlI0CkZXQ0JkalloT2xNNEh5ZG5NT2Vn\nVlFKZTV6aTYwNmF2bVpHM01YdUtLeGMKLS0tIDRCVDZrNk5UdkVOODdnZFptbDlZ\nVnNNMGc4U0FvOHlsU2dwTE03TUtyL1kKoikATM2w95ca39e7K9mOezI9z4hSgUAA\nUiq4GH9Sn4jtuew2FXK2ZqedzgSQuSOob8iaYBUMGdWL7bfn2gZsHQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNMUQxZmxrZDR0OU0vd2tl\ndjlCNEFSeTVFRFRxa01xNXhRTDdFRWJtQkhFCitPV2hDNG9uWXE0a0gyK3NuSnc3\nblBKVjNkMzkyWHVKMzBKV1NzdFVsbXMKLS0tIDdkMTVrSk9pcjhlWkVISWNoQVU4\nTm1sQVlDbUVObFIrRkNObG5nMUdWVE0KWwIpPYL8HwcbUDUS6TG4DyJgAz+xz9lA\nLLRYARWF0zNJlOTHAIEksT14YSU3dplMxunwas2mLBjfYH0DIg2Org==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:20:56Z",
"mac": "ENC[AES256_GCM,data:vN/zqTJO+szaDV1aJc/ZYAbe0ISc7dJvQg8prjVkYIZA+aAh5Hk2Kec6iz+gH23CqapYcsEYJ/qd24eRrSsxPSJuvYWJAgyrPasNXNuGNrsVnkvTyClTUGiBbhoac6yHu66BHVpH15BJUgeSmO7iVD7HD+KPy0p66agJwWQEcn0=,iv:GLRGWh131n5otLJjBGElmMGxGOL1bljGZ8zzTE/fLn4=,tag:a65wuRUzsBtO2rO+hJN0RA==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/machine1

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:F5FWqPeOBJvuwzuomHd4IkWgGeBBmYmgSVeH/TSJKKG4BHAwgam8dT1oTWiKEd/SfLj6LY33N+s4fKqe3bdCCa5/CPC3P7ZP1RW0/fyAdftlXFpbmXjsFf1rsTcTPMPBxOdwbu6+w6ji16sEkKXsN/GQsk6bhT7gxEgy4ZXY1kiiE08TtIG8p6aIcES/sTXhNqvZvWkJeESh1pkVhfP0OSWp0x05RPvxg2FoU1IMcqmySZwL/Ltqp4I3pKNHmLzuTFfFmK10fC9OptBbPmPtp75k0pJcQ0xbzJAyMM1riGzhOOMO1Qj/DXcSPDVugrMYDZRpEqNGg+LPBuCF1l2e7UitPLY4/1g669lCAPR3stiSW3S2NZIsDsW/ubN2qYeMBSBM8NWpQPgcsrKELCgUExD3MFMO9G0Rl3sU1BsYw+jf+kZNZWDL/w9W6vYt73q3Nmu2DfPpGzwKLrnbBpqjgMJKev/9JG/gMejbMe5AK1leyr5TDi97ku3zTvQfur4YMWABiBswmIbgbjBZAJOIKzy10AJTFQbqNoGGaOz22i2RBJLVHU7CKoKyJL99Iun0MjSlIIv5hTZMXDnHPZxvLq4w0htA5U2VJNKg04YKaPpc5ceDLVt9kgEF6OKG7/5nwDoQ/cgjrwbzl85sWpWQgXxwpSDPN8A0rkyZkgxNch/tUASgUoOExJRd6ht0iJiFM3tW25z8NU1ZWijs287hCkXNac63ZnXCa/2gB2zj+iHHLzgGvsxfhFiM2smjC0CSNbteTYr3sgZszrJSDdyI3Aerx92UwN336Iu0pXAvAGh8SBLBdz1CcmAlldK08Z8tFxplqOaqCQ8AtuklfQQeh8/Lx0VW09HIGkjQp88xotW4bZ803rPb7pqJNTzQmSF/4JWtIJXEG8eRbyHXpTM9p9RYvlY7/yK75EH/vwq6eAu6Cm2AwUxLjqMv9uoAad0gij66DflQ93D3hOfJaF+yNLa+HTce/p0Ymqeo/YM3/UUQ/ZHVbPonULXkylYs8JFFVkf3DBJTGYB85UCZ/ObNyXFtk/GuzQ==,iv:zV2Wm02uMdGmdKR0qiYnTdTlyRec85wXwUJOE3mAeWo=,tag:8fw+x1O1/53ff0qOuoph/A==,type:str]",
"sops": {
"age": [
{
"recipient": "age1numxr6m52fxrm9a7sdw4vdpkp463mm8qtuf5d0p0jde04wydfgtscwdx78",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvbTg5alA5SzJoUTZRSFM0\ndk1XaTlkOXo3NVNxYUJCc1FWWG16Q3IwMERnCkU1S1hlT3ZxcTRhaWs4c1BaVkNi\ndEJ0dTNTV1MxN0QrV2dJUzUvRTlsV3MKLS0tIExSb20zTDhuVExUMG9JZGd2bG00\nL0NTanIwZE92N1IxNWR5NW5PTktpK0EKsBMTH1ln0H/dK2AgUBWiGtdcY2ujvu1H\nTR+X3MZfFHQdOIvW4dkhUJt7BnZ5cEOCUizE5oI1+DifjDqk1dd+VQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzNHJsaElGRWc3ZTQrQlBs\nb0NLMmJUNzhyOHFielFZa1VnRDcwQ2JaREU0CnhrR3hGVXF3SnpRZWE5dlc5VFVV\nT1A4SGNoT2s0MU1jdHJ5U0ZvK3l0emcKLS0tIDZULzBXZ294NXBSRVJVRHlwUkpr\nWi90bUpNQlAremhpaWZkVXpRcE5yL1UKp5KUA5g/trDODHcXI7SWjbWR/ozXpxRp\ngHbONszf4y4hjkHOCVvtQ2NmR/cF64nhCswZYlfssUb4aJBVrMlVEw==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:20:56Z",
"mac": "ENC[AES256_GCM,data:3l51gIfAbCnYvWjp6meJGsWQJPI7x4yswFe3WPLGtDJMPrRdOWW5UtU2Qb/rcoKGoSee5OJblOGvkloWTp++HebS2TxKEQ26qU9ycOq27vFt6mfmu4IVcmTqoVM3BPRh9md590lqdLyIsy/BULFBhe0jGBUQbXfDkfewronDOdU=,iv:JkVkjT9G5Z2+u/ZYDaoM8sRk5cBVWYR8fm48Gwfbr/Y=,tag:L9xZSAP3zQZfy6malNIFDw==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
LJOGYGS-RQPWIHV-HD4B3GK-JZPVPK6-VI3IAY5-CWQWIXK-NJSQMFH-KXHOHA4

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/machine1

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:oYhwmtmsMpBKoUnC2sze5M7Qx3y9+ukpt7apmicJJOaDNliI8+NrrYntl56X70U4i+DN3Iz7qwPGGBxjveV0z7kIUEvlDiu3GgQmC/34omktyogb8dH/POoO+Z/E1CxKJEIn+rEU6Oqhg4aPKt/JbveW5wlevlQsMwW1oAtS701JJeY6KU6AfDerU0cS4K5+xw+dFg28pIvIIe02uDzTnFEXZYzvPk7UxfOE0IFxs6/zzPFoBbeFo2WIZZBMQYzP4AQMEiFxxaK7qthBwGuCEc7yMpW1uwVe9CZBfkVkg+wIX4DSm+0j8TiyikobsBxkvPUgqYHNyQaINJp82aGPOzFLsJ6ixf4RqgOzPikzdsyU8phi7waiDPDLf2rhZmnR,iv:9zhjW5wBlNtCxV1kBqzUlZaVRjAbr9LpxypJaIk4RPw=,tag:u1qxkOdHIqkelp/bchb4bg==,type:str]",
"sops": {
"age": [
{
"recipient": "age1numxr6m52fxrm9a7sdw4vdpkp463mm8qtuf5d0p0jde04wydfgtscwdx78",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFZGxnQXhHUWhONitsWnRk\nbSsvSHRYbHJLc1BjOGgyb3Fyem5VeXk0MENRCkpkdGVmb01rK2FCV25Oc1JGY3U4\nK0xIb0UvTCsrV3lDRkIwZmhscDN4TUkKLS0tIG1GOTFPZTlvcXVOMElHYWN5ZFBU\nbjB4Y3FmY2ZMMmZ6cUFlZ0NUL2c3Z2MK+Fn2tf9eX6Iy6GMsa5//fzKDAUUCFa4x\neeCStPrxTbmKWb4T2NxXrZYK73kvOAmEQenUasddZ24/SDhnnrlXbw==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0N0V1Q1l1NS9JK1R1RG8z\nVE9rRnVqQW9mRGNvWHViZWlKS21qSkF4RENFCjl0OU1rMUFHUHVDR3NjSXc1Tnpr\ndE1HUGxQYXMzWW11Q2tlNUdjdXpqdk0KLS0tIGtDZGJpQ3FrbnFZVDJuYjN6bFFN\nZGZqUFNKMmpFSUd5QXFtSFVSWFlpelUKFzm5m/+ReOVDHpvgqAKs5Vwq4XVCPH0K\nzmxtSIAwey9lUxnWNkuKOUG07o89ACsVe6pVPLLqHpGLotvne68GRw==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:20:56Z",
"mac": "ENC[AES256_GCM,data:k92YniWJ8U75G0YJ8yt/Xgi5NtyeoJrSNCCMd+Znj9/JL8mWwevJ9aeR387TK7OjVyKp8TOXTSky3bOqZbr5BTHUQDRr0LlQDO6ozmN3CVOdKpK31hfIotTHNRCUcpq+CoEB4VyjeAAdnzCkvyhckZ/L/CtQpJkSGI02M+mHcwE=,iv:cG5Q75Y0zeGj6EezNY1rRD0ZLtzIxkcKKsSHV8Og1aM=,tag:dsSFoW/XzoeOHmH5sqJzzg==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/machine2

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:ZMxZz2bWSw4lKwXp+kdeblLJxpLxdWyZ9ZPBMkZeZGmf,iv:Dnpmvt8YomnwKeJ9ULWy8iAw9OspZ99glCYDtr2Ymkw=,tag:aBHwYdO8lZmtb8PbjCEeKA==,type:str]",
"sops": {
"age": [
{
"recipient": "age1aqng9vmlgth5aucu5ty2wa0kk9tvk7erj4s07hq03s6emu72fgxsqkrqql",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHSWh4aDdUVnowNDBGRllw\nWitjU1pKQkhSNTdmMjZOUkJ6a1UwY2FkWEhzCjlIeGJvV2JpeTBXcVFCQzY2MXJC\nVlFsY3Uzd3R3cEJuVlZYYXd3dXkxSUEKLS0tIHpnT2Jjb1EzNUpXVlhseTBSdDd6\nYmorNlo3bXY4OGl3WXJQZ2dHbHlUVFUKdVPZd/2QtR/ELpf+vy5sXdGC0tS1N4uE\n5zsRpMBWQptheOUF0tNZAbw264gbYX/fece/myTJLISAPM9wZQd7ug==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5YzdiaDBxYURVcTVLQkF6\nMEVDd04wSXhYV1hKWXlhYVJVaUJtYUxmRGwwClU1SXNpUzJQNXVTTGQ2d2x0QmRV\ndWgyL2hMR2pTZWE5bGsvWmVYWFY5akkKLS0tICtLRHFuVjBlUldZQ2FCWTAza0Yy\nMWRSQTd4WWNjcGtYT1RqcS9ybWp1ZWMKidHF/OqLKVYWo02zHnSLm8BEN+qyJ2+i\nBqg62OqGLt8pbE/r1bxnxia4ZCgfc+xKtsWuARKq/BUBrKlMrBZGEA==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:21:08Z",
"mac": "ENC[AES256_GCM,data:JInxA9c2UQBDFzetaHU8KYNnp1JS3eBPyAMjsP6sNwOsRJBY8enckQANvhXF4Mo6eksx9RUhdcqd5zqO/uupnqV4AuARxwI8Sq3ak1r1fnH4uzOC/wV5hWIHdOfKrePuE1+ayo/mR0xqM3ipxuUfJ/cM0ktYrTvJypR1brHkJyg=,iv:Qs1M3UMUOUuPVG564xJAJAhRsj7GGrs+jqNbFjupGDg=,tag:WjTJT3N7FkVmdv2giNVECQ==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/machine2

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:AiUO8delOdyU9pl1q9DuSegdy/MMh8q30M1hXuinpKVC9Wdtw/NFUVfKSM6wX5d3hkBnZ+BmG75bMIkZXXJ6Cl3Q7l3M60HcBrDQaUYJ3tfuO5dJGSi9Ab5RtnWKmaA/5wtb71sM08PbmmkPXnVI2KgXwX23VnNPdlMXFRYdWuzbTD7U+S7BPKiINjMCyLlMlNfw7YidGufwCkYb6YbpJ+Z6EkfArkH6pGKlB38vaz0rSiVSBa4fxzD4rgx2lh98yJ8VC80O/PdpvYhWub1aHpK1j6gq6InptvLNIyUh0i/9YXHbjpXc/D7hhphF/tA0JUOFMyN0Ses8cvBPHLOMU3uM82aYk9lBfnn74vKsNk5IWeiPskzN6dwmHr6yJHQmDiFp9ll7Ujf9GU5DzJhQsVUFf1xb2wq05YMomEpDdDo0xz35L/w8hFQg5kKLDvzKCiDQuvYz4KdaDZqobZbJ+ICNx0dZHlBg6NGvYK3Q1TpNkR6qHDnOGYC3dbZzeskuLDnD6VScPrLvf0duOrNwE2QP5Eqtk/5r+yP4SJzNK9UMZhYcXIF2koel6dxhlEmJMBLryR/56+GFIWOHIebKUGJhtumj0cl21QAcZgeQ42oy6mza+K17u+SpRwb+Vl+d10zSauxV6jqHE/qec4ZxKyFJH1TDR4R3FcqY3Xb2323r0Lx3Z6h3QSuXaoSz3lZj1FnXNh5x4Xctb87cNd6u/woxL3X105OwMOxaI4SIt81WWSA7do9WFzfNjOUahyullq5RWNOapLPAmMjuSdJBeImFEbKPUst25vgkzs3x4PZkS02s6R+o3h3PoeTZPDIpNIv6Ye6sRy0txhF4nBmHKFNjoClxaDG6esdDlsyu+eyZKRy6/Hvfe/iimlyb+iQMLlmTLoES9B7J0F1JAwJhetdnfoa3pbgZsehb2b0BmqgHbytgA/nOlNWcbae+aX8HCj4Ju0RnYwObbjaVqHQoST/LjnnVJCo1y0UZ4CnOg7MNEKrtxRb3BV8s9Xp3BR3UPFN/gKWBGh/xyHoR3ZEJ0mxt3vjdymqMtBQ=,iv:4slGBU5rnFPaiFXr6Nzuk54ku76XL9+cFOF3IPWbZn4=,tag:+XOCNKwG2PqzY/PTzruouA==,type:str]",
"sops": {
"age": [
{
"recipient": "age1aqng9vmlgth5aucu5ty2wa0kk9tvk7erj4s07hq03s6emu72fgxsqkrqql",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYbVZ0c2tqSEZJNkxTbGpC\nd1U3YVQwQnhmY21pY0R5SnJHL0lVZC81SFFzCjEyV2VwQng5ejc2L0F0ZUFrWDB6\nY1k0blJQbUhRQ0kweGM5Ty9uQXVGS0EKLS0tIG96S1NOQWlidXZORDlrYWZWcFUy\nb2RLa2pKNEhkSFlwdVFMRXlEOStkNVkKYahEQPOfncD2TGeT1JNPWCOG0ScUCJ0K\n2cYRCBxayd/ssfyEL3jV+TVSxFnKrAhtGJzP2xEAZFYJsHoLG+56sQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQcWdtYmpwYmJQV3BNL01x\nY0FURkczRjBKYWlzOFplcDZ4cktGMTNMN0VVCm5FQmorTzRMUzVSd2Y4VS94R050\nR0V5MXUvODh2Q0IxeU9RelJLTUZoWmsKLS0tIFFjT1pyS3NwWi9OY24xTytLN3NZ\nUDdqUEp4ek5tTGxvdFU4SGduR1VBeUkKX013r6b+KL1i3glEcpwgv/KzBEE9N9sj\nqgyZ3S5dhCY1LOquP/xnS7kqZzN+znv61F7577wV1pBy53KPOhl47Q==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:21:08Z",
"mac": "ENC[AES256_GCM,data:Oiq0mU3K+zgaHhdlniCGgx22Sa1tEOmO+ddfDdD2IsMm1c9vAI4O+/5PsF/DE4h0WoORJsn88qfzPDva90YUJKQAJsflMHNk8jjExGhtVLVjxkpZk2EtpBzfg311riYzKl5vSJxLqp67Ks2dHyl8qHEH71dSgkXxVMIhPDUmJCg=,iv:B+Hi7qoRbBT53wH3cBSLoQocdXCcfKenkwnErTweMwY=,tag:88c4zp8a6WNfnJkRZvNFqw==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
K2FAKIB-HUUM7M7-CUPDN5Y-NORZCJP-F43EFQY-UPV4AUO-GAI3VZW-PH5TRQX

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/machine2

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:9U+BF9rhGbNCokJoZEl347m1jJm9n+JOJFXSVrqYM+r2A7NPTDGBKIrnVpGdOM+8VmOIzAIROGb1h+wgQoHbr98YtVRx88D0n9bA7ZtSCNiACXzgTl/swaqYCPgJ5FtPvylNagK92SDwjpVSV7jfLA0kdE49HESg9dWIUlPS2CoORhW+FY4wynXdMEsgZmeRgtYFMubqshX/Ep07qnF9HjQmmDy5XKQ4uEm3HydlZOZvW6USwyRYIcqH8x5p1xzif5zYgUsu1iHAmbhtqzULJoe2rzuM3+7Av0LeGcQpXe3gkX/3+aA/SgGN5mJOtLalcwClow5HH0atlOopgy4523Z+jANvc4XwExpJbSYeJuqUTV20q1X16Kyph1O+XYOW,iv:dS7CcP6oPfdByqIW3sz4AUm2BfVInFmDDkxxbGhySmc=,tag:T5ZbP2hDaKNR/c2J/mlS6g==,type:str]",
"sops": {
"age": [
{
"recipient": "age1aqng9vmlgth5aucu5ty2wa0kk9tvk7erj4s07hq03s6emu72fgxsqkrqql",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvcDZLcXpaODVma1RUNHE0\nMElMRkpRSWJwa2Q3dnFEQ0J2a1JpblZuWVFJCk1tUTV1U3VpYUJqajZKd3l3OHNE\nWDk4TzhQSlluOGdnNlcrUTBxaXJrRVEKLS0tIHdiOVhRTHI1emE5TnMxbEZJdm1W\nR2NxMVRhQ0xDT2o0cE1POXRQT2ZwMmMK+3x5+5pVrGPILDpCpqPKuVX5emmfDvFt\n2HjLIwy/2mWcsktXh2gk8KAD4WWL/JjatTuP+EP5zoOFcL5/U3S5Pg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArS3B3SjQ5ejRLU0xUT245\nWGNkaUErdTQ4ODNqSzY2aVFPeUlabHl2MkNnCnlzcUh0ZjlUcFBXbEJtZ2pDUmNr\nYWRvRG1EbkwrZVE3NlU2cWc4VSswQ3MKLS0tIGYvZFZWOXV3U2hMVU1XOHVCSXdp\nSm1vaC83c01TZms3Z0NqMHhqbzRNUGMKKiRAy58NZDz5rjNA3xYv8/dU4H5L0fo/\nQc3WsEXIIwnVHO0X6WN+qUpF8uTPAZW26wecspdsf/5NOJtM7o8+EA==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:21:08Z",
"mac": "ENC[AES256_GCM,data:QGID6lY6g1qvYXhtXrlNA3E+IjJZRKiB8+CDDLYGwx5vtX32M/WNVfzEs8bUdUDEJTUnfFI6tBHjFbKUVNFmbH2utZ3aiUGVJadVUzuWQf3HO/McyPwRMm47gt29U0iagUTSdVbzSROwdwCfXfZeyJUPXXNZgyA5LqL8puPS+jo=,iv:kZzMQQ+1shjScxxmMVSqK5ckXckgCtxbqa8uBZ9RTZI=,tag:do0RhBzfHmFZc8L90WXTqQ==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/machine3

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:1yqn7/zQsDblJjKK5nK9wLQjmvBTmsr4WAkWvjRJ2FLX,iv:F9+nsOQSgbyr/q498oI1wU/KiOsEvSUec/2VC9bzv8E=,tag:tklcKqwd9MmuJany0pg79g==,type:str]",
"sops": {
"age": [
{
"recipient": "age1kul02mg50nxccsl38nvma0enrgx454wq0qdefllj4l0adqkllvls5wuhfr",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMa054akc5ekFzYVBYZk1Z\ndFoxRGxUcGZaNzF6RjlPRTRXZjh6b2xxTzBjCktuV2Q1WnhnM2NMdGRncFhXWHpG\nRHhlU01PVVNUMEF4K2x0NEhreHRKKzAKLS0tIG5YQ0YzaTlxNGtPTURNY3VtNGhO\ndUxPa1BwdXZ0SVJnZmNqYngwaW1CYjQKngYlXRD28yZ0j7WMmACTRCbi7aJ6xNlN\nor7I530+HUv1s067PRwMiBWbNesiAHHhhZjOsnSiyQ/IYr0R77huyg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBdnhOU2MwY2ZXM0p5Rlg2\nL2FmaDZYT3pIL3llenV1MVZDT0xiVklXWFdFCko2S1BxMFpPc2QxRjExVnhXTVNp\nZStpdis4RDlHcmNobmpNK1FtbFhaSTAKLS0tIHRJblljNTJxS0ZKdWY0dFF1cURP\nZGFDRGZ6ZnozU3NJT3EvSGNEaTlralEKywGQQwjS3bJ2yI8839ycWeknES+r4oMH\ncBG/zSbYTznyMQq2uEhAe74a8zf3pe48EnwPksiCcFLIG5IP1SE6Iw==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:21:21Z",
"mac": "ENC[AES256_GCM,data:06hwqU0gL2x8brDOB3y69igVBHwIkgUiuxYin6FPsjAQO84qsT8suQVGHmqPxHXrPM9fMvu9/Xqio0w/ar8NElQLmUlTkOads/pb/ykuC5mU/645q1spvV65i4C/spGrIA4XtSaeY9APuQGDr8wiQKdnvuAXSsuVSQvEhTpbBqs=,iv:UguEbHUFqZoV2g7DLeUpI8dd3tdoWhfIDLnj0h5i1Wg=,tag:CJ5/uOYKhleP2N1oTlvdxQ==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/machine3

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:Az/IU5qbln8kN2rkY9MK4dMtCgaIQDjXw3aqbFs/Kjh3wLy4jYM4XkvQBeZPqgmonbGtJ50eDLS14g6ABYCmpQtDPHUAVllMPlKJMiGL1/+YPXGfsQyPgOXmgWA6hFI24ZPEsQYCRwbXoDZLJMkTxUMlMQqZTWsXkICbrIYzyJUjkbK0uaSjqbu7vLt7HVgb6WBoFtdXdP3rD2BVo6LEZUTd4mQW9PB0FPms7WKsVnoWQpKmowentae/2cdx1KCvEmoJfvnAGB09woHDGJWzs3VSrlenPVOu1dN9NUaGI61WRLJT4/h47PBEt1Pw0ec5YK2h1Po0pZa8LaYNQ9ewx9cIXsvWpKgvi73zSe70Q4uWT7xHglZHhncR6wECXIpClpQW4Z0a7CgMujfMexK6Dh8fJZ72hA5ZN5RwoxuAlhN0HujFBMk77w5t1QrulyJs8+UQIqfNhrK0LGQKRn4AnW1R0Hc7bOzeE3C50oKmyU1kDyoDChce9A6fGXQ758kUxOYTZy0095LIEL5Q0OdCujdz30a1NiORuuuiXSCCIAfH1rUMeO49Q1T3QF8IRJScvD2Q81ggqeewjzt83d8KbCHWOtPiaAAWzNOHPS7N/x9efTqZmf8YxajnSeQDbqqyRayjFPs5AaTyk1pqFiIRKYH9cKJeEXr/CY6j+ykgDXDksAvg3fVXikdt4E5O/HaWxU55CJZmBfshdC8GWEetRtl6pfA5poTT4L79B6edF6TxtX4R9Js322UTTKxOTJ8fkx/0grXUa+KxUreb+jbN826AyL7kWr5Zt2clXbCFetjASoJn+tMQKeUmE8xe2V4qibTmPlkzuhUIVdVG7G8xg8w31lBSErF9/M4AaaWba93s6GCgqdJ5T7Duf9DnfaiQr011eCmKpfJxsUTGm1Gs55+YIDQZEraOgLr9BWjBGy1gCwQIhZtY/udKzFXvqB/KBr2/mzXNzPNmnrcElKkjxpcHF0RkJ5JGo630k2AOab3+KJzHoRFPI8/VzR2dh3rHQ7SjN2cdiJiGO18sD9Xc+9Me6jgVLrMf8NY=,iv:TIaxmvvQUzzM1hLdb2caQJbTTfgeea1LijeGa/5Jid8=,tag:e77+V1Ok2qNkI3MdUntqIw==,type:str]",
"sops": {
"age": [
{
"recipient": "age1kul02mg50nxccsl38nvma0enrgx454wq0qdefllj4l0adqkllvls5wuhfr",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwaGRjSEo3K2xKZE9HSDVX\nU3lRWWlnbm5QcEx6VG5nS1RyUmUxcnBPL0Y0ClE3VWZjclJybEJCZi9NdVJqeTBG\nM0hhV1FPdzVBT0NLRGZvdFdDMFNYN3MKLS0tIForRURHM1NrU2M3ZnhJb0FFWmwy\nUWo4QmJBS293aWtyRHQ3bTR5RWFpZWMKoGjITft9CoH3cVRXRGx02PTFFQMN8bPU\ngxNLcbWyLVSUvUZE1xYiVMh5aGMLlMoGwgwJBUAb/Cm744Yh6gz5Hg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5RkZxY0E1N0FOd2x4ZFlv\nZW5PbmIwZk9uS200N3N2SS9pU0ZzaU1ibUVVClRYRWlQbDRZNXVjZlp5cXpJRmh6\nUllUT3llVjRHSEpTNC94WjRFeTlIWUEKLS0tIEMvZ2M2VDByeGtpaUp6UFdtSWZC\naGFId2w1OXg4bnd0bFlCNUtSZDJlVTAKFLc3mW7y7M+HOO5TIEPPxznqXkWvUWEl\nVy8DA+hZAAsoRfGseFTD7ny9qJOLwnmpmW2m5dZTuPcRAFk/29DrGw==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:21:21Z",
"mac": "ENC[AES256_GCM,data:Y+XjgRgXHWrLZlmes1j2C7OLXxttm24aNTUUf7voSCUK/z3iIrqhsRs4OZfpLs1JyHNPYMhRu5kT1cjIQfZPwSNWPfw0QVvMGpgoNV8MGQW+G9mUxIvEy+F5yRGXiMd5bybyDgitADHG/6OxBd43IoEfkCpMs06yaEAievk+P9s=,iv:iZ9jP0cNmwHD/EViXzzmM8/6xIY+T2q+aMnQRH/JC1E=,tag:vT9/XUbuq7pltl5d1zLaew==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
UWOQP5L-DE3OCK6-BIQLRGV-JFZ7JTJ-VUYUFDN-F3PJNH4-VS2AJKR-PQJKQQD

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/machine3

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:pzKV11EWtow3USkwNwSO0qLAijfG8UbeVvDDkQJqd/S/lWF5gBTt+33uPTsBj2A0tyVglwHrGIuPl/WSxZqt4jIKogjeWrODzN6YCQC8I9/3JgCaCYKlDM2b4GTZxzC35YXD0CBmCcpGUgAvP3Awr5ga4SmAQLVHSR4MHODODRa2cj4TsReLLIf2JgFd1m4Jd6RS9YrWYBSTOp3btFzdJSISqKAHcIcEzMbb2uS8tBqmoWpG56snrzmchrBifB5jGe2RgN/HLN6+gp8p1MStEVWSBbmGszDtUH4k2hMKYpY+HEqHL2onbc+LIOo4ntpXagwD034Iw/mEfmGkgrf+AmnX8Gm6j/6CW8ILlypRYAvDucKvKRNOnWWrkR6XmiPq,iv:Zp/B5it4q49i3B1XT1F5II8Ajc9xjGpJ35SJQtLsr60=,tag:tqc3YsaNIPrvTR/6AVH8ww==,type:str]",
"sops": {
"age": [
{
"recipient": "age1kul02mg50nxccsl38nvma0enrgx454wq0qdefllj4l0adqkllvls5wuhfr",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4SVJ4S1gvQjJYVTJjTVk4\nUkRrTXBhekhDUWlZQmtJWHJyQkk0Q1Nxcmh3CjdnazFxdmZGaEoxRCtkVnRwOGFW\nMnMzSHZOWUF3aWtmTGk3cTNlWjlpNkEKLS0tIE95VVFDQjRTRTdFNUNud2g5c21z\neHBHeHVMUTYvOHdFVW9kVWtSeFpodzQKXndWkTpSrJbzUpHStyWb4Q5p4p9ASjTc\nGQ4wcd3RLjsox/LfFw7NPXuRLQh7kRFgWxx3akjzeaVCbNOg7N2fKg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuQjBmeFAwQmNiVGhUTmtk\neDhaNTBaRmMyNHdaRVVJanorWE1Mc3kwVDFzCkxEMTFMRjdxYytLRk9HaWlaaTJv\nYTR5ZXRvMllYWHRDTlNXWFZlaXBwRmsKLS0tIDkxRVI3bXdKaXNMUGNWNTBLcVpw\nSXc4WHZDOUg0dFlDZnE3Mi9uMGFMcWMKsdxeoutdG3nwU24n2/qy9oTkhOueXKJ1\nbEuSY7OsQWp2tc6bVwx05G/R9bI9sYtA00FjJLLIR0Xk9M3ngor5/w==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:21:21Z",
"mac": "ENC[AES256_GCM,data:GKWyUqU/FeOQ3um7h+82ZYh4aylxII+li8F55iRvFXRLK4A8J6z37AiDI7TOxqQ2XZIh6b8HZA0BpFN6HlkL+q4DpiuV8eTmke+hLq4H74M/m3Kgv4rQtrBupk2OUdXdsUu6ERJiIxW7XKvAhX6Hvm6GEDSSttbba5L5esi/xQg=,iv:qIy/MQXUpTzcDF6VvY7ERUDLs2XJUK/dNxTIFgfu9Wc=,tag:D2ocRXrxvEtTHX/t85EtpA==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/machine4

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:5Bb2+eeTSoCvrJUFHeEL/ugPADUqkgxzBebyDHrg8v6G,iv:1TWwRiIm0u+zrydC+mlz51Dtz59FfX2fi6QjjO9+jdA=,tag:mkLCH2kbShRc6RdtAFaFtA==,type:str]",
"sops": {
"age": [
{
"recipient": "age1kqgx4elusxx4u8409gml5z6tvrsayqsphewsl93mtqn7pl2p5dwq9lujpj",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUam1sYWxDT3FQZy9ZNlln\nc0o4ZkdVVG1zNlRDbVpxdUJ6NFlUVzZsc1VnCjlobzF0dmtUZlp2U1IwdDdMMlVk\nVUt0ZFc3Ty9UQVNTb2dJc01ZbXFlUEEKLS0tIEVGcnNoSXd5b2NoS1hZNWEyMGdM\nZXgrNGhFMGp5LzlrdE4yOG15b0g3NVUKX46oVCCn8eQ60raWVtxRMVazQUJaPD5Q\n0e95w2IXfcN7YPn+CdIcWEoWJ5dbmGQ1DB9VBWrZQONdi1SHzyHx8g==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6QUZuWG1Ga1JpUUszVmFI\nL1BxTXk0M0hKLzdKVUQ5Tnp3Y2t1bnEreFRFClcxVmFvTWJQNVg4YkxXa0RmcHFk\nNW4yYTRRWFpJeFhTSVc4eEJzSFNFRFUKLS0tIGlhenZuRFcyamZNQmIya3BEWGhu\nckhsUE1QS2tFTUNUcFBqclN2Z3A1SGsK7e3Eoc7J0P5pyn4dqQAn67iBfcgokdNr\nSJqoyukHst9ZDJ04+n19mr02njxMz4MTt1Qw76q9s2sadU8iHsHnog==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:21:34Z",
"mac": "ENC[AES256_GCM,data:9pDg1EWTuPtIs+hfwtgQkX6HrIaI+IWyVY16SVq2bBrEd5uxkXQ2fL9P1tjIwo7iO8mZ6Ji3uTWZa/mqPXwJjdGOV5Jehg3y1S3Y0mPD4yN/Xy/eWrieIly3md+EVcDndFLyKkcnpd78VjJhjvdia2OC64MBqylGoZ1M7sWmj6U=,iv:VrUEQeFg+JeDuQznYgeNZiml8HTcfWv3gy4/mEMlXO8=,tag:bxALn8GfEJY7aQQP2FmSsw==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/machine4

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:WK99B0F4/xudxleTboafseIybqtOaaF1j1Y/e0/DP2XB/Wsr6x+1S89ZaAsEh10221d+n+iz+X7eo2uPpEVqpP68qyLJDw3fGli3ilUaMZ+xxZMtboW4lGjg4q3UlGzD6XApFKCRRBbzqGHRbQAHD7RaBJOmz3TmXSsCXJDMyv9ac9ijf+8ko/mAi2yXEgZwOzaU9vU541Mqtux61GJ9RS+C+VJ2iKg+he+mMFMdbqRRAMB7QMqbeXoOKt5cF6mwUQJBlHqotF4/yy/gqCMlThAVSdLqc5McHTdv/oOQEfWH3UwZFxCP0T8mHxAkgI0ISlSzpESYf/GO1HYZLxggY546TWdJ4Mczq8j4Q118YPVi77++ZsqUuozVHN776pBdJHcLuo37hp/6MNqB2AOeCa8dwiF5bylHRiU+sfFXNNk/J3eO5NDwt3wujAiRsuLPdVQ0u3v8InUPIfbxaBr7FTksi1FLbm6oPBPBAJSIVl7L/MC8HBZZjywP1MCbO1kVpPbyP3C30PyKjvvhkWxHkv4YWbosuG0s7TxSxI2ngRjoPi7hbgmm4zpgiujfDthx93KEEqTxCf+/LBzxuKRuN+eP2y+5qAsRJL+opaETeyi3nap3hEKl1PTg9Vdt6PUW7sUDLWzhkNvAFqW4CbZ5HyKEsvZskdKP1anHXoLahlAzme9ag+aOxEgmlMN7Tdikmo7LxDrtU2GysH2miSj1ezQKkpq9kYkAogA9qBbr8N4o8jn8KZUDkqZXsD3W/WfF+xXVLWoKS5sbOmBvBu4QWGI2IDEBIwl/JRDwp12BKOraKNtG9v2oVm7rq/8Ulb52Z5MpBwHCwT2Yts7Y7RKLnZb+0Z4FWG4lqMz3gVGplZeXEjzbqj8J2YV2hd1Puk0B1OTwadQ2f6mFZRmxUmRLoaTMjGkkVKbaPb4g8G7zQEgJZj2y4FXZx/TqUm3ls04o5kgTuGML0wFrkzjtf3gQ0APwjqqax8/dqUXKWQOr3XeB5Be8OtDMfxws52M9pHjXXZxbPrWxxpqkcC/8fWoNNi0Umz3WKw==,iv:uyy4whMZL8gCqN5UESWxxK7o8THSAkOriyKqUzSvI6A=,tag:gXdVxcdc6nMmD10Y6QxEgw==,type:str]",
"sops": {
"age": [
{
"recipient": "age1kqgx4elusxx4u8409gml5z6tvrsayqsphewsl93mtqn7pl2p5dwq9lujpj",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlakt2TzR5b0NHdjY5YW94\nM1NMcC96Q1RxWjM1aGFybHBFZ0NvOFdhdjB3CkR0dE45aFZ4ck40R0xsTjRXOGt2\ncWpSK0lqV1FITWQ2RnFwS2JDYmNrMHMKLS0tIEpJcm8xK3dOYWNKb0JRTHNncjdr\nUkVLV2tSMGhyamgzVi9IcE9yNDQ1NFEK5HbycP8g2i9tHVbAfqzeXh0Krqsus3xP\n+Ta6lmWW4vP0fvA9IgZcGJXY5gCpEaum6GkkvaB2zhxs7Uddk5bEsQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTTlJWaGQvekR4WUtEODkz\nYlFFMjhTVmZxdGNmU1lUbCtMNFdGeU50Um5RCnF2NTlsVm55VUNxc3k4dTc1c280\nYjVqV3dMZmxxR09VU0dSb0wzQ2NHczgKLS0tIGVFVjBPRFJTbWNjU3g4alJHeHM0\nQThYWlNQVHQ3K3lvS1Y1Si80RWlKSk0KJRpWoQj+VrMGfqlppNj9IHe/o5JzqQUW\nG+HiWay1SgQj14LM7G29fuYt39I9pdwuUMRYXJ4MwVq/wr0raHYoaw==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:21:34Z",
"mac": "ENC[AES256_GCM,data:Rkek09R889QqIfKa11bF1olCmyj9l9xM5bgTBpcSqaJyFUPg4bA8I3PnnAMtIeEGtG8ioh51jHRpbjsp5BuZM+GhrhW0X5NWF3ZlWS+Gx4hyoJSZJGgNVFzvr5Wj3QkS+9mDLrxE/UfLJZU1/raleAV89r2BiXOHhJugNPCSmnw=,iv:NUQZG+DzYOVsVhJnDaqcfAIOmnfRg6GuBamp61e+CoA=,tag:voEZOktdGpScZQrJnwlKnQ==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1 @@
OORI2OC-364PI2W-LFOFX4X-7PB752I-QVLZWG3-EC4XJ4C-RPJQWTV-XYPRQA6

View File

@@ -0,0 +1 @@
../../../../../../sops/machines/machine4

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:e8f43yOO08ww3x9QY4D9K/51v2NdxzxnzKrPCkl0f/ElZv9iWdon2NyknjAA9sJEuy86eY8N0LkgqIodLoc3FoxmfADHU8BbhyFHjtoIpuTNV61eajW7vnVJA4zTtRxrEReNXe02muuHM/1sfXA+ocGKUK4r3pz6nSkKaE5VJFZl7e6gev1H2w6rSq3XMMp9V3AgRvyLtSQziKj3fRFFHhet+AopqU9uMb5t6zHa3Sj8sn8dw0HPrEQYelZyS0iAi0g7OHBMasHrDQYrtfd71SWoARuMN7JzSxE2/F+yeqGmwRdf1EMJf/1Xdrgta/f0Fzmt4+UzuciN6SsREGRBk+nGYQZrqsN+wk5bPet4P68+T2V1EFuCh4X8t25N36L0,iv:TmXHWEb3jgULdlIVOxmfUHL/ZrY4NneVkeJWJUOxzpg=,tag:lGor/T44A4yRCz+VZIVM6Q==,type:str]",
"sops": {
"age": [
{
"recipient": "age1kqgx4elusxx4u8409gml5z6tvrsayqsphewsl93mtqn7pl2p5dwq9lujpj",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsbEhCZ0JGYkVCTEwyeDF2\nYjU3cFNTb2pna2xjSzdHYlVrNDdDZGRCM0RvCmpJbHVsblBsbG1CQkh3NTVHWTEv\nR25HdFpkYktKNWZpZ2tvcEorTWRJZDgKLS0tIG9MQWpERTJnb2dDWlZ1NVgwMkdL\nMWYwamM3bC9pcHRXSk8yYnN4UmhhQnMKyJAUuClhEPLBoq8hDeopp0xkMbtekJrg\nfZXUAA6Wr/cwISLJHAFMa8DdBGBj9ICU6v/AMyo3Zfkv9gV/NTDmfw==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvbU1DMFRoS1Fjc3FwWVJD\nUUdQQm5HelRYT3U1SXNJaFhkd3ZsYmFFQ3hnCnJaa2RpbWQ5WnZwWlJBZnZENnRG\nRENkUWorcFgzZ2hYMjI5Z2o0cHNWcTAKLS0tIGhMQVRmdjB0cWlJOTI2Y1N6dWRr\nSjVIVnpTV1VRS0NaZUwyeUt5ZTlVSEkKfyL4gE731vmdqDjWjPaZiSYkoZVkj4fj\nqKJMwG8VN58NnL06oExzqVGgFKR4CTas0auWQ76cs2gdv5n7Gm2NMw==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-07-31T13:21:34Z",
"mac": "ENC[AES256_GCM,data:KVewXUwkR68EyGj7Ld8JfxWu10+rbLn74Pv8y0Vkh6ZFDmo76XtuamLrH74uNOtDGwGhBM53xzNVf7LHfiWmT3S1dLifrusV4BWXa7XX37ACxGEq2FYg3HuUKG6bYgykKfxc3vLv50NK8cZMSxsAyuP5z7efas6joKHuzA93ZSw=,iv:xnHbVGa67SjGMHiGsjvteOQRtA66kfsWY04ZfdV6Cw0=,tag:9AFqKEn6Hs/LweyQYVDJ0w==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

@@ -0,0 +1 @@
../../../../../../sops/users/admin

View File

@@ -0,0 +1,21 @@
{ pkgs, ... }:
{
clan.core.vars.generators.syncthing = {
files.key = { };
files.cert = { };
files.api = { };
files.id.secret = false;
runtimeInputs = [
pkgs.coreutils
pkgs.gnugrep
pkgs.syncthing
];
script = ''
syncthing generate --config "$out"
mv "$out"/key.pem "$out"/key
mv "$out"/cert.pem "$out"/cert
cat "$out"/config.xml | grep -oP '(?<=<device id=")[^"]+' | uniq > "$out"/id
cat "$out"/config.xml | grep -oP '<apikey>\K[^<]+' | uniq > "$out"/api
'';
};
}

View File

@@ -88,9 +88,17 @@
files.user-password.deploy = false;
prompts.user-password = lib.mkIf settings.prompt {
display = {
group = settings.user;
label = "password";
required = false;
helperText = ''
Your password will be encrypted and stored securely using the secret store you've configured.
'';
};
type = "hidden";
persist = true;
description = "You can autogenerate a password, if you leave this prompt blank.";
description = "Leave empty to generate automatically";
};
runtimeInputs = [

299
devFlake/flake-compat.nix Normal file
View File

@@ -0,0 +1,299 @@
# Compatibility function to allow flakes to be used by
# non-flake-enabled Nix versions. Given a source tree containing a
# 'flake.nix' and 'flake.lock' file, it fetches the flake inputs and
# calls the flake's 'outputs' function. It then returns an attrset
# containing 'defaultNix' (to be used in 'default.nix'), 'shellNix'
# (to be used in 'shell.nix').
{
src,
system ? builtins.currentSystem or "unknown-system",
}:
let
lockFilePath = src + "/flake.lock";
lockFile = builtins.fromJSON (builtins.readFile lockFilePath);
fetchTree =
builtins.fetchTree or (
info:
if info.type == "github" then
{
outPath = fetchTarball (
{
url = "https://api.${info.host or "github.com"}/repos/${info.owner}/${info.repo}/tarball/${info.rev}";
}
// (if info ? narHash then { sha256 = info.narHash; } else { })
);
rev = info.rev;
shortRev = builtins.substring 0 7 info.rev;
lastModified = info.lastModified;
lastModifiedDate = formatSecondsSinceEpoch info.lastModified;
narHash = info.narHash;
}
else if info.type == "git" then
{
outPath = builtins.fetchGit (
{
url = info.url;
}
// (if info ? rev then { inherit (info) rev; } else { })
// (if info ? ref then { inherit (info) ref; } else { })
// (if info ? submodules then { inherit (info) submodules; } else { })
);
lastModified = info.lastModified;
lastModifiedDate = formatSecondsSinceEpoch info.lastModified;
narHash = info.narHash;
revCount = info.revCount or 0;
}
// (
if info ? rev then
{
rev = info.rev;
shortRev = builtins.substring 0 7 info.rev;
}
else
{ }
)
else if info.type == "path" then
{
outPath = builtins.path {
path = info.path;
sha256 = info.narHash;
};
narHash = info.narHash;
}
else if info.type == "tarball" then
{
outPath = fetchTarball (
{ inherit (info) url; } // (if info ? narHash then { sha256 = info.narHash; } else { })
);
}
else if info.type == "gitlab" then
{
inherit (info) rev narHash lastModified;
outPath = fetchTarball (
{
url = "https://${info.host or "gitlab.com"}/api/v4/projects/${info.owner}%2F${info.repo}/repository/archive.tar.gz?sha=${info.rev}";
}
// (if info ? narHash then { sha256 = info.narHash; } else { })
);
shortRev = builtins.substring 0 7 info.rev;
}
else if info.type == "sourcehut" then
{
inherit (info) rev narHash lastModified;
outPath = fetchTarball (
{
url = "https://${info.host or "git.sr.ht"}/${info.owner}/${info.repo}/archive/${info.rev}.tar.gz";
}
// (if info ? narHash then { sha256 = info.narHash; } else { })
);
shortRev = builtins.substring 0 7 info.rev;
}
else
# FIXME: add Mercurial, tarball inputs.
throw "flake input has unsupported input type '${info.type}'"
);
callFlake4 =
flakeSrc: locks:
let
flake = import (flakeSrc + "/flake.nix");
inputs = builtins.mapAttrs (
_n: v:
if v.flake or true then
callFlake4 (fetchTree (v.locked // v.info)) v.inputs
else
fetchTree (v.locked // v.info)
) locks;
outputs = flakeSrc // (flake.outputs (inputs // { self = outputs; }));
in
assert flake.edition == 201909;
outputs;
callLocklessFlake =
flakeSrc:
let
flake = import (flakeSrc + "/flake.nix");
outputs = flakeSrc // (flake.outputs ({ self = outputs; }));
in
outputs;
rootSrc =
let
# Try to clean the source tree by using fetchGit, if this source
# tree is a valid git repository.
tryFetchGit =
src:
if isGit && !isShallow then
let
res = builtins.fetchGit src;
in
if res.rev == "0000000000000000000000000000000000000000" then
removeAttrs res [
"rev"
"shortRev"
]
else
res
else
{
outPath =
# Massage `src` into a store path.
if builtins.isPath src then
if
dirOf (toString src) == builtins.storeDir
# `builtins.storePath` is not available in pure-eval mode.
&& builtins ? currentSystem
then
# If it's already a store path, don't copy it again.
builtins.storePath src
else
"${src}"
else
src;
};
# NB git worktrees have a file for .git, so we don't check the type of .git
isGit = builtins.pathExists (src + "/.git");
isShallow = builtins.pathExists (src + "/.git/shallow");
in
{
lastModified = 0;
lastModifiedDate = formatSecondsSinceEpoch 0;
}
// (if src ? outPath then src else tryFetchGit src);
# Format number of seconds in the Unix epoch as %Y%m%d%H%M%S.
formatSecondsSinceEpoch =
t:
let
rem = x: y: x - x / y * y;
days = t / 86400;
secondsInDay = rem t 86400;
hours = secondsInDay / 3600;
minutes = (rem secondsInDay 3600) / 60;
seconds = rem t 60;
# Courtesy of https://stackoverflow.com/a/32158604.
z = days + 719468;
era = (if z >= 0 then z else z - 146096) / 146097;
doe = z - era * 146097;
yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
y = yoe + era * 400;
doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
mp = (5 * doy + 2) / 153;
d = doy - (153 * mp + 2) / 5 + 1;
m = mp + (if mp < 10 then 3 else -9);
y' = y + (if m <= 2 then 1 else 0);
pad = s: if builtins.stringLength s < 2 then "0" + s else s;
in
"${toString y'}${pad (toString m)}${pad (toString d)}${pad (toString hours)}${pad (toString minutes)}${pad (toString seconds)}";
allNodes = builtins.mapAttrs (
key: node:
let
sourceInfo =
if key == lockFile.root then
rootSrc
else
fetchTree (node.info or { } // removeAttrs node.locked [ "dir" ]);
subdir = if key == lockFile.root then "" else node.locked.dir or "";
outPath = sourceInfo + ((if subdir == "" then "" else "/") + subdir);
flake = import (outPath + "/flake.nix");
inputs = builtins.mapAttrs (_inputName: inputSpec: allNodes.${resolveInput inputSpec}) (
node.inputs or { }
);
# Resolve a input spec into a node name. An input spec is
# either a node name, or a 'follows' path from the root
# node.
resolveInput =
inputSpec: if builtins.isList inputSpec then getInputByPath lockFile.root inputSpec else inputSpec;
# Follow an input path (e.g. ["dwarffs" "nixpkgs"]) from the
# root node, returning the final node.
getInputByPath =
nodeName: path:
if path == [ ] then
nodeName
else
getInputByPath
# Since this could be a 'follows' input, call resolveInput.
(resolveInput lockFile.nodes.${nodeName}.inputs.${builtins.head path})
(builtins.tail path);
outputs = flake.outputs (inputs // { self = result; });
result =
outputs
# We add the sourceInfo attribute for its metadata, as they are
# relevant metadata for the flake. However, the outPath of the
# sourceInfo does not necessarily match the outPath of the flake,
# as the flake may be in a subdirectory of a source.
# This is shadowed in the next //
// sourceInfo
// {
# This shadows the sourceInfo.outPath
inherit outPath;
inherit inputs;
inherit outputs;
inherit sourceInfo;
_type = "flake";
};
in
if node.flake or true then
assert builtins.isFunction flake.outputs;
result
else
sourceInfo
) lockFile.nodes;
result =
if !(builtins.pathExists lockFilePath) then
callLocklessFlake rootSrc
else if lockFile.version == 4 then
callFlake4 rootSrc (lockFile.inputs)
else if lockFile.version >= 5 && lockFile.version <= 7 then
allNodes.${lockFile.root}
else
throw "lock file '${lockFilePath}' has unsupported version ${toString lockFile.version}";
in
rec {
outputs = result;
defaultNix =
builtins.removeAttrs result [ "__functor" ]
// (
if result ? defaultPackage.${system} then { default = result.defaultPackage.${system}; } else { }
)
// (
if result ? packages.${system}.default then
{ default = result.packages.${system}.default; }
else
{ }
);
shellNix =
defaultNix
// (if result ? devShell.${system} then { default = result.devShell.${system}; } else { })
// (
if result ? devShells.${system}.default then
{ default = result.devShells.${system}.default; }
else
{ }
);
}

View File

@@ -15,5 +15,5 @@
inputs.systems.url = "github:nix-systems/default";
outputs = _: { };
outputs = inputs: inputs;
}

View File

@@ -1 +0,0 @@
sha256-LdjcFZLL8WNldUO2LbdqFlss/ERiGeXVqMee0IxV2z0=

View File

@@ -1,12 +0,0 @@
#!/usr/bin/env bash
# Used to update the private dev flake hash reference.
set -euo pipefail
cd "$(dirname "$0")"
echo "Updating $PWD/private.narHash" >&2
nix --extra-experimental-features 'flakes nix-command' flake lock ./private
nix --extra-experimental-features 'flakes nix-command' hash path ./private >./private.narHash
echo OK

View File

@@ -49,12 +49,16 @@ nav:
- Guides:
- Getting Started:
- Creating Your First Clan: guides/getting-started/index.md
- Create USB Installer: guides/getting-started/installer.md
- Add Machines: guides/getting-started/add-machines.md
- Add User: guides/getting-started/add-user.md
- Add Services: guides/getting-started/add-services.md
- Deploy Machine: guides/getting-started/deploy.md
- Continuous Integration: guides/getting-started/check.md
- Deploy to Physical Machine:
- Create USB Installer: guides/getting-started/create-installer.md
- Deploy Physical Machine: guides/getting-started/hardware-report-physical.md
- Deploy to Virtual Machine: guides/getting-started/hardware-report-virtual.md
- Configure Disk Config: guides/getting-started/choose-disk.md
- Update Machine: guides/getting-started/update.md
- Continuous Integration: guides/getting-started/flake-check.md
- Using Services: guides/clanServices.md
- Backup & Restore: guides/backups.md
- Disk Encryption: guides/disk-encryption.md
@@ -81,6 +85,7 @@ nav:
- Inventory: concepts/inventory.md
- Generators: concepts/generators.md
- Autoincludes: concepts/autoincludes.md
- Templates: concepts/templates.md
- Reference:
- Overview: reference/index.md
- Clan Options: options.md
@@ -99,6 +104,7 @@ nav:
- reference/clanServices/packages.md
- reference/clanServices/sshd.md
- reference/clanServices/state-version.md
- reference/clanServices/syncthing.md
- reference/clanServices/trusted-nix-caches.md
- reference/clanServices/users.md
- reference/clanServices/wifi.md
@@ -171,6 +177,7 @@ nav:
- reference/clan.core/deployment.md
- reference/clan.core/facts.md
- reference/clan.core/networking.md
- reference/clan.core/postgresql.md
- reference/clan.core/settings.md
- reference/clan.core/sops.md
- reference/clan.core/state.md

View File

@@ -30,8 +30,7 @@ pkgs.stdenv.mkDerivation {
];
};
nativeBuildInputs =
[
nativeBuildInputs = [
pkgs.python3
uml-c4
]

View File

@@ -1,8 +1,6 @@
{
self,
config,
inputs,
privateInputs ? { },
privateInputs,
...
}:
{
@@ -156,7 +154,8 @@
type = types.submoduleWith {
modules = [
{ noInstanceOptions = true; }
] ++ mapAttrsToList fakeInstanceOptions serviceModules;
]
++ mapAttrsToList fakeInstanceOptions serviceModules;
};
};
}
@@ -169,11 +168,8 @@
# modules = docModules;
# };
packages = lib.optionalAttrs ((privateInputs ? nuschtos) || (inputs ? nuschtos)) {
docs-options =
(privateInputs.nuschtos or inputs.nuschtos)
.packages.${pkgs.stdenv.hostPlatform.system}.mkMultiSearch
{
packages = {
docs-options = privateInputs.nuschtos.packages.${pkgs.stdenv.hostPlatform.system}.mkMultiSearch {
inherit baseHref;
title = "Clan Options";
# scopes = mapAttrsToList mkScope serviceModules;

View File

@@ -0,0 +1,69 @@
# How Templates work
Clan offers the ability to use templates for creating different resources.
It comes with some `<builtin>` templates and discovers all exposed templates from its flake's `inputs`
For example one can list all current templates like this:
```shellSession
$ clan templates list
Available 'clan' templates
├── <builtin>
│ ├── default: Initialize a new clan flake
│ ├── flake-parts: Flake-parts
│ └── minimal: for clans managed via (G)UI
└── inputs.self:
├── default: Initialize a new clan flake
├── flake-parts: Flake-parts
└── minimal: for clans managed via (G)UI
Available 'disko' templates
├── <builtin>
│ └── single-disk: A simple ext4 disk with a single partition
└── inputs.self:
└── single-disk: A simple ext4 disk with a single partition
Available 'machine' templates
├── <builtin>
│ ├── demo-template: Demo machine for the CLAN project
│ ├── flash-installer: Initialize a new flash-installer machine
│ ├── new-machine: Initialize a new machine
│ └── test-morph-template: Morph a machine
└── inputs.self:
├── demo-template: Demo machine for the CLAN project
├── flash-installer: Initialize a new flash-installer machine
├── new-machine: Initialize a new machine
└── test-morph-template: Morph a machine
```
## Using `<builtin>` Templates
Templates are referenced via the `--template` `selector`
clan-core ships its native/builtin templates. Those are referenced if the selector is a plain string ( without `#` or `./.` )
For example:
`clan flakes create --template=flake-parts`
would use the native `<builtin>.flake-parts` template
## Selectors follow nix flake `reference#attribute` syntax
Selectors follow a very similar pattern as Nix's native attribute selection behavior.
Just like `nix build .` would build `packages.x86-linux.default` of the flake in `./.`
`clan flakes create --template=.` would create a clan from your **local** `default` clan template (`templates.clan.default`).
In fact this command would be equivalent, just make it more explicit
`clan flakes create --template=.#clan.templates.clan.default` (explicit path)
## Remote templates
Just like with Nix you could specify a remote url or path to the flake containing the template
`clan flakes create --template=github:owner/repo#foo`
!!! Note "Implementation Note"
Not all features of Nix's attribute selection are currently matched.
There are minor differences in case of unexpected behavior please create an [issue](https://git.clan.lol/clan/clan-core/issues/new)

View File

@@ -60,7 +60,7 @@ If you used `clan-core` as an input attribute for your flake:
```nix
# ↓ module.input = "clan-core"
inputs.clan-core.url = "git+https://git.clan.lol/clan/clan-core"
inputs.clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
```
## Simplified Example

View File

@@ -24,6 +24,10 @@ pkgs.mkShell {
}
```
## Debugging nixos-anywhere
If you encounter a bug in a complex shell script such as `nixos-anywhere`, start by replacing the `nixos-anywhere` command with a local checkout of the project, look in the [contribution](./CONTRIBUTING.md) section for an example.
## The Debug Flag
You can enhance your debugging process with the `--debug` flag in the `clan` command. When you add this flag to any command, it displays all subprocess commands initiated by `clan` in a readable format, along with the source code position that triggered them. This feature makes it easier to understand and trace what's happening under the hood.

View File

@@ -17,7 +17,7 @@ inputs = {
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
clan-core = {
url = "git+https://git.clan.lol/clan/clan-core";
url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
inputs.nixpkgs.follows = "nixpkgs"; # Don't do this if your machines are on nixpkgs stable.
# New
inputs.flake-parts.follows = "flake-parts";

View File

@@ -49,7 +49,7 @@ The example shows how to add a user called `jon`:
2. Add this user to `all` machines
3. Define the `name` of the user to be `jon`
The `users` service creates a `/home/jon` directory, allows `jon` to sign in and will take care of the users password as part of [deployment](./deploy.md).
The `users` service creates a `/home/jon` directory, allows `jon` to sign in and will take care of the user's password.
For more information see [clanService/users](../../reference/clanServices/users.md)

Some files were not shown because too many files have changed in this diff Show More