Compare commits
536 Commits
control-ma
...
feat/clan-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
985f66dac8 | ||
|
|
0a75575e08 | ||
|
|
aa1bbd1180 | ||
|
|
3d6fcd522a | ||
|
|
9b714aa048 | ||
|
|
1b13296444 | ||
|
|
cff5d61f26 | ||
|
|
7bcb0afae9 | ||
|
|
e53ce07dc8 | ||
|
|
768b94fc75 | ||
|
|
6b1d4adb81 | ||
|
|
dc108cc27d | ||
|
|
dcb9358e51 | ||
|
|
104c05e206 | ||
|
|
e5807bfa7e | ||
|
|
0a22218026 | ||
|
|
843e1b24be | ||
|
|
8f98aa854f | ||
|
|
8576016b32 | ||
|
|
0e9124d322 | ||
|
|
2510b2bb77 | ||
|
|
9f298ecb86 | ||
|
|
df0b8cfd45 | ||
|
|
afbb6549a6 | ||
|
|
15ec2067a6 | ||
|
|
b628b12b3e | ||
|
|
74ceb95eb7 | ||
|
|
f8cf9fa172 | ||
|
|
5bc6126873 | ||
|
|
f9779322d0 | ||
|
|
04ca72f5b5 | ||
|
|
5be449740e | ||
|
|
5dc3d27fb4 | ||
|
|
c45c94e045 | ||
|
|
66f35ae637 | ||
|
|
1c0b383183 | ||
|
|
89edfda887 | ||
|
|
ce00c63721 | ||
|
|
80229c5e77 | ||
|
|
39b81a17c3 | ||
|
|
5b80204107 | ||
|
|
55b032094f | ||
|
|
73ab00ac7c | ||
|
|
25e3d27bf2 | ||
|
|
d9e4c93509 | ||
|
|
15f0ea616c | ||
|
|
3285efa55d | ||
|
|
a3078f4e13 | ||
|
|
513d085f59 | ||
|
|
4eac7ad86d | ||
|
|
bfe158c9fa | ||
|
|
188e6d5d16 | ||
|
|
b1054e2ad2 | ||
|
|
db666ce2ce | ||
|
|
1c2ee49047 | ||
|
|
53dcc4e332 | ||
|
|
a9ed972834 | ||
|
|
c34b693a64 | ||
|
|
6dab14c9d4 | ||
|
|
0c099f16bb | ||
|
|
38159d85f7 | ||
|
|
228cdfd0e9 | ||
|
|
bbeef3d76a | ||
|
|
d8cb43ae8d | ||
|
|
d50eac6525 | ||
|
|
3b29c0e006 | ||
|
|
e5f38db60f | ||
|
|
c624163581 | ||
|
|
40e34d9307 | ||
|
|
6863ce136f | ||
|
|
bd92170bed | ||
|
|
d842a13789 | ||
|
|
42de68966b | ||
|
|
68183634bb | ||
|
|
3457542871 | ||
|
|
f6544d1cda | ||
|
|
131c7ea263 | ||
|
|
6b3509c3b1 | ||
|
|
ec19ef1ec5 | ||
|
|
00a439999b | ||
|
|
7e63be2976 | ||
|
|
c059eb37ea | ||
|
|
dc8675fcb6 | ||
|
|
545f498881 | ||
|
|
c55bffa5a5 | ||
|
|
16112e259c | ||
|
|
877107650f | ||
|
|
6da64f6f27 | ||
|
|
cdaf0e8c2b | ||
|
|
2f370a6d33 | ||
|
|
027ae49449 | ||
|
|
6989899338 | ||
|
|
b05c7469ce | ||
|
|
c5540941a6 | ||
|
|
12a5a0d792 | ||
|
|
792826f0cb | ||
|
|
68854f36ff | ||
|
|
463eaff47c | ||
|
|
30e74c408c | ||
|
|
af8dd22369 | ||
|
|
4f4c44c7d4 | ||
|
|
55d1a94947 | ||
|
|
33f3f01e56 | ||
|
|
d15d135e23 | ||
|
|
af69a0d5c3 | ||
|
|
3bdf296205 | ||
|
|
f5e9f305d0 | ||
|
|
050804a917 | ||
|
|
56cc7977f3 | ||
|
|
5195a049a1 | ||
|
|
52ea2ea352 | ||
|
|
7f3459d178 | ||
|
|
494ea192e7 | ||
|
|
6c42baca23 | ||
|
|
f18b219d0c | ||
|
|
72073494a1 | ||
|
|
fb19b7c9de | ||
|
|
88a3f0a6a7 | ||
|
|
ccf8e99a98 | ||
|
|
a22180f980 | ||
|
|
131afefe3a | ||
|
|
d880634d22 | ||
|
|
dc4aae643a | ||
|
|
1df5dfe6d3 | ||
|
|
0f6e5499f6 | ||
|
|
7df15c67d3 | ||
|
|
b10682c7f7 | ||
|
|
43d9fdc77e | ||
|
|
9f27e21bd4 | ||
|
|
2129790bda | ||
|
|
bd70f4cff2 | ||
|
|
fb5839f929 | ||
|
|
76e4ecb6d5 | ||
|
|
dc2abb1352 | ||
|
|
93b23a38dd | ||
|
|
bdddcd05cc | ||
|
|
1f33f7aad4 | ||
|
|
5dbfca0364 | ||
|
|
6525b591e2 | ||
|
|
79e615b5b1 | ||
|
|
9405e40d00 | ||
|
|
cdd9b8a3e4 | ||
|
|
e4ffae84a7 | ||
|
|
df611dd3db | ||
|
|
96feb5bd26 | ||
|
|
989fdbca49 | ||
|
|
88f7b3410e | ||
|
|
ef29e62d9b | ||
|
|
a9f746c1d3 | ||
|
|
b36d641e71 | ||
|
|
04146eeac9 | ||
|
|
3051763cbf | ||
|
|
df5010084f | ||
|
|
e9278ceaba | ||
|
|
0261a954de | ||
|
|
ad5bbdbee0 | ||
|
|
7f62a7993a | ||
|
|
04e842a2a5 | ||
|
|
3986200c74 | ||
|
|
b27d6bb29b | ||
|
|
ed72abcc5c | ||
|
|
64a146400f | ||
|
|
2d665e876e | ||
|
|
fb746b874c | ||
|
|
00eac81c2f | ||
|
|
0b404b9637 | ||
|
|
6a6dd8ad2a | ||
|
|
2e2335a922 | ||
|
|
d5d7cba6a5 | ||
|
|
a5d561b996 | ||
|
|
6b08527929 | ||
|
|
211ca59441 | ||
|
|
29811cd0dc | ||
|
|
e4a6cbc7e3 | ||
|
|
1bcf5a82a6 | ||
|
|
1adcada568 | ||
|
|
80606274ed | ||
|
|
fa270cf74c | ||
|
|
9221231e89 | ||
|
|
74f9ee4326 | ||
|
|
7d6ddc5e59 | ||
|
|
868db1e6f3 | ||
|
|
f210b2f9a6 | ||
|
|
9a642e465f | ||
|
|
0c1a48c9d4 | ||
|
|
56f3fd0a45 | ||
|
|
21f87f169a | ||
|
|
829cdcb826 | ||
|
|
3dc41f7d5f | ||
|
|
f68b372268 | ||
|
|
35bceac822 | ||
|
|
2892bbe93e | ||
|
|
c43d8fa958 | ||
|
|
8220c32142 | ||
|
|
13fa74b8cd | ||
|
|
d765f1078b | ||
|
|
37180ff2af | ||
|
|
766c11f900 | ||
|
|
3841b8ea8c | ||
|
|
bd5c33041d | ||
|
|
d36b6f08fe | ||
|
|
dc424c6970 | ||
|
|
7f63920933 | ||
|
|
a484f2a7fc | ||
|
|
ed3f8f85c8 | ||
|
|
9e8dc584eb | ||
|
|
146f183256 | ||
|
|
cc20e0bf35 | ||
|
|
876027ea02 | ||
|
|
34349ab4c1 | ||
|
|
a2bc237d79 | ||
|
|
3b5f1f2c5c | ||
|
|
2561e3e4d1 | ||
|
|
180e84d9e9 | ||
|
|
cc1f78058b | ||
|
|
6159456024 | ||
|
|
00ee0d807a | ||
|
|
78475e5a82 | ||
|
|
c26cc9503c | ||
|
|
c50cf54e33 | ||
|
|
3eb8af34fc | ||
|
|
172d571804 | ||
|
|
e337b9997c | ||
|
|
f436d91711 | ||
|
|
9a2a72132d | ||
|
|
358ce9e179 | ||
|
|
e4ba4621d0 | ||
|
|
25aa561cb5 | ||
|
|
9742f7516a | ||
|
|
8d7c938138 | ||
|
|
d7bf049058 | ||
|
|
f91e0a99d2 | ||
|
|
4ea40fe8a3 | ||
|
|
c82189d927 | ||
|
|
03ce651de6 | ||
|
|
710258094b | ||
|
|
ebb9da663a | ||
|
|
b383e672dd | ||
|
|
6053d9631f | ||
|
|
1ff5d64a78 | ||
|
|
d397c8ad39 | ||
|
|
974c3f9b09 | ||
|
|
90b0e811a4 | ||
|
|
ec6a86f8ef | ||
|
|
9edc3fb881 | ||
|
|
d5c77cda5b | ||
|
|
d019b3b57d | ||
|
|
2b699e6e81 | ||
|
|
0e1831d0f4 | ||
|
|
e1f0590eac | ||
|
|
90232977e2 | ||
|
|
5b1b74e06f | ||
|
|
800313c2a0 | ||
|
|
0326476d4b | ||
|
|
c08af894b9 | ||
|
|
5e7b34a4c7 | ||
|
|
3dbad7a55b | ||
|
|
2f073470df | ||
|
|
97a229a438 | ||
|
|
8162933711 | ||
|
|
7e92308b70 | ||
|
|
f8540de48e | ||
|
|
742fbd111f | ||
|
|
8b5cfb48c1 | ||
|
|
b5b26a6d13 | ||
|
|
2e4d6b0b2e | ||
|
|
bf88aed318 | ||
|
|
a97ed62e2a | ||
|
|
67406ef6cd | ||
|
|
bbb4a9b5fb | ||
|
|
56ffd896dc | ||
|
|
693f404f4c | ||
|
|
043e7fbb16 | ||
|
|
3520ca5ef3 | ||
|
|
289c70d884 | ||
|
|
603810da5e | ||
|
|
d0989cd0f5 | ||
|
|
973d7706bd | ||
|
|
f722aafc18 | ||
|
|
d825a3348b | ||
|
|
a569a7bc7c | ||
|
|
8b957a128f | ||
|
|
259ac96bc3 | ||
|
|
c77a3b11a8 | ||
|
|
0e50e47f16 | ||
|
|
6194c2a625 | ||
|
|
59e1512cfc | ||
|
|
8e96a8931e | ||
|
|
b14c27c7bb | ||
|
|
8a3aacc00c | ||
|
|
5ac4cc8586 | ||
|
|
84e9835ed2 | ||
|
|
a62b828120 | ||
|
|
5d10b7582a | ||
|
|
27f44a3662 | ||
|
|
f20f8b365a | ||
|
|
78cea7f7c9 | ||
|
|
d9075a5d79 | ||
|
|
09c9c184ab | ||
|
|
daffc95c96 | ||
|
|
a12ba8e56f | ||
|
|
58fafac958 | ||
|
|
0f6e084640 | ||
|
|
e941f2adde | ||
|
|
a3b0c0472a | ||
|
|
e0c407a8a3 | ||
|
|
ce1256304c | ||
|
|
84c0c7e678 | ||
|
|
952859529d | ||
|
|
2e149c7a0e | ||
|
|
6edd804731 | ||
|
|
cfa966d58c | ||
|
|
9f805b9e46 | ||
|
|
120dac6dbc | ||
|
|
16b6df18a5 | ||
|
|
eb50278d1e | ||
|
|
bfc7a74a66 | ||
|
|
b97177363a | ||
|
|
217563740e | ||
|
|
b116df770c | ||
|
|
e836492a80 | ||
|
|
3db8b00898 | ||
|
|
cbaa765154 | ||
|
|
c506692d96 | ||
|
|
04108ff656 | ||
|
|
028a401931 | ||
|
|
aead181fb7 | ||
|
|
f512f0a949 | ||
|
|
77d266bdef | ||
|
|
d73ff02749 | ||
|
|
afae472e63 | ||
|
|
0989bbca36 | ||
|
|
e6f53ac243 | ||
|
|
c80cb601e0 | ||
|
|
f1a53a1255 | ||
|
|
d197f0f6e7 | ||
|
|
f8a647d181 | ||
|
|
21aa95b139 | ||
|
|
968efb97d4 | ||
|
|
ee1095a87e | ||
|
|
573e79322e | ||
|
|
a123e05557 | ||
|
|
aac6066e20 | ||
|
|
14969c5822 | ||
|
|
8fbd465a2e | ||
|
|
005f11b00d | ||
|
|
36bc493a43 | ||
|
|
79271cb41b | ||
|
|
fb11710bdb | ||
|
|
ee8ee2a659 | ||
|
|
70b152b548 | ||
|
|
ce774539b7 | ||
|
|
6670f2fd50 | ||
|
|
a834f210a0 | ||
|
|
7f0a430ec0 | ||
|
|
f5700ef742 | ||
|
|
58b0e21040 | ||
|
|
caaae17dca | ||
|
|
98ae22bd1b | ||
|
|
ef4806f71c | ||
|
|
1de7b8cefd | ||
|
|
5dff187a32 | ||
|
|
5b2a4cc696 | ||
|
|
7eac2ce436 | ||
|
|
36b72ec436 | ||
|
|
a06bd43a1a | ||
|
|
552ea582d6 | ||
|
|
2eaebbd1db | ||
|
|
acfc604435 | ||
|
|
2bee4a8167 | ||
|
|
0ab296a385 | ||
|
|
c9812e7682 | ||
|
|
3c157c4a31 | ||
|
|
ca17b64237 | ||
|
|
5206366ddf | ||
|
|
17e5f63149 | ||
|
|
ccb1f08345 | ||
|
|
a87ab490a3 | ||
|
|
5d26caa15a | ||
|
|
d109090bcf | ||
|
|
4ea4876644 | ||
|
|
8433248c56 | ||
|
|
3bdfa7e405 | ||
|
|
3bddb26b48 | ||
|
|
ef4b5cc9d5 | ||
|
|
bea10f7bc8 | ||
|
|
e8608ac830 | ||
|
|
2ecedb6535 | ||
|
|
a6f17f18a2 | ||
|
|
96fb6c39f4 | ||
|
|
6e26d31ac6 | ||
|
|
77ec1e9e48 | ||
|
|
ea8b1aa34c | ||
|
|
2bd9141d2d | ||
|
|
f788313e97 | ||
|
|
89b70ffa6f | ||
|
|
ed1692574f | ||
|
|
1106c50924 | ||
|
|
e99e47da10 | ||
|
|
67def050fd | ||
|
|
c0d2787dee | ||
|
|
ecc327277c | ||
|
|
0064a8bfbc | ||
|
|
1e8b9def2a | ||
|
|
f0983ede5e | ||
|
|
10bc9e3e44 | ||
|
|
556fd8845e | ||
|
|
fab079af71 | ||
|
|
0370c1cf02 | ||
|
|
aa557f3a96 | ||
|
|
e8699e68b5 | ||
|
|
f8f31d430d | ||
|
|
3d345e0bca | ||
|
|
80711fcf72 | ||
|
|
35684090e3 | ||
|
|
8069b137f3 | ||
|
|
2fba6b15e8 | ||
|
|
cddee0ca86 | ||
|
|
0f3ab641d9 | ||
|
|
d5f90b2730 | ||
|
|
54335221d8 | ||
|
|
76b13476a5 | ||
|
|
bbed94d6de | ||
|
|
23a5c845b0 | ||
|
|
b933dcf2e2 | ||
|
|
8a755fff8c | ||
|
|
5726dd1010 | ||
|
|
b306c748b8 | ||
|
|
2682581c09 | ||
|
|
a0a5827157 | ||
|
|
8638861a87 | ||
|
|
c5a28e2655 | ||
|
|
0af36d0a4d | ||
|
|
34b63ca1d5 | ||
|
|
e24a6e23ad | ||
|
|
fd7ccaca1a | ||
|
|
4251d5ee0b | ||
|
|
0a8839bcc0 | ||
|
|
cb41aaafa1 | ||
|
|
9867b6a894 | ||
|
|
7459566c2b | ||
|
|
1c08d6dd25 | ||
|
|
14f4d65c47 | ||
|
|
43159cc2f0 | ||
|
|
9d8ebfd267 | ||
|
|
1e379f6fa7 | ||
|
|
b32a7749cf | ||
|
|
153da50d6f | ||
|
|
dd3bb314fd | ||
|
|
687f26eef1 | ||
|
|
afdb08643d | ||
|
|
0946d4316e | ||
|
|
462c0764b9 | ||
|
|
a748a27ddc | ||
|
|
baf686e83f | ||
|
|
03ddce83b7 | ||
|
|
45eb73680d | ||
|
|
7d39d49b30 | ||
|
|
698a39fafb | ||
|
|
b633db4f8e | ||
|
|
7b9d18f9eb | ||
|
|
51950329a3 | ||
|
|
16256440e6 | ||
|
|
dfbb860898 | ||
|
|
444fc3f820 | ||
|
|
572ce8885f | ||
|
|
0bee027251 | ||
|
|
334367c3f7 | ||
|
|
2371a5fa78 | ||
|
|
4792d8b1e3 | ||
|
|
ace0328a14 | ||
|
|
66c2d54961 | ||
|
|
e18efdd48f | ||
|
|
8b652866c7 | ||
|
|
7129c38675 | ||
|
|
caacf65dc0 | ||
|
|
f8723ab897 | ||
|
|
b877df4c6e | ||
|
|
bf04eabc21 | ||
|
|
5149ed9318 | ||
|
|
4d84180dd9 | ||
|
|
7571fdef74 | ||
|
|
7d55511d6f | ||
|
|
cbd7157cfc | ||
|
|
25faba4795 | ||
|
|
0f0bab7976 | ||
|
|
f0e18bbdfb | ||
|
|
6d4db71ea3 | ||
|
|
b21c98db7f | ||
|
|
df3fe00b8a | ||
|
|
7371085c05 | ||
|
|
6804327bca | ||
|
|
ec76d5f8e5 | ||
|
|
864cdf33a7 | ||
|
|
e6dbccd8e5 | ||
|
|
30ac51b313 | ||
|
|
00b12c2c51 | ||
|
|
085d726217 | ||
|
|
3e65a76dfe | ||
|
|
929632049e | ||
|
|
0d1e642dfd | ||
|
|
8af68cbd9d | ||
|
|
a44fee9eab | ||
|
|
b27f34aed3 | ||
|
|
cd23c9ff41 | ||
|
|
852a673839 | ||
|
|
763a403e9f | ||
|
|
78f8a080a8 | ||
|
|
af2a00bde3 | ||
|
|
47c44ec7ec | ||
|
|
1f66e90db1 | ||
|
|
89fbf723ca | ||
|
|
548fdfb877 | ||
|
|
e44d34ef99 | ||
|
|
acc6797c22 | ||
|
|
9fbd943f44 | ||
|
|
d42d9ad943 | ||
|
|
3c16ccdb38 | ||
|
|
7b9e431da7 | ||
|
|
05388c9c7e | ||
|
|
fe36aa4161 | ||
|
|
6829c7f2dd | ||
|
|
aa2e8eef88 | ||
|
|
9e07064ec5 | ||
|
|
305a361f56 | ||
|
|
db32e99082 | ||
|
|
50ac0266f5 | ||
|
|
b01691cb64 | ||
|
|
44b237d9be | ||
|
|
f8bbd91c4a | ||
|
|
401de330f8 | ||
|
|
51da7ed5e8 | ||
|
|
e170cc2641 | ||
|
|
8434f0fc35 | ||
|
|
d6bbb42dda | ||
|
|
6539a6a24f |
@@ -1,9 +1,6 @@
|
||||
name: checks
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
checks-impure:
|
||||
runs-on: nix
|
||||
|
||||
53
.gitea/workflows/update-clan-core-for-checks.yml
Normal file
53
.gitea/workflows/update-clan-core-for-checks.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
name: "Update pinned clan-core for checks"
|
||||
on:
|
||||
repository_dispatch:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "51 2 * * *"
|
||||
jobs:
|
||||
update-pinned-clan-core:
|
||||
runs-on: nix
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Update clan-core for checks
|
||||
run: nix run .#update-clan-core-for-checks
|
||||
- name: Create pull request
|
||||
env:
|
||||
CI_BOT_TOKEN: ${{ secrets.CI_BOT_TOKEN }}
|
||||
run: |
|
||||
export GIT_AUTHOR_NAME=clan-bot GIT_AUTHOR_EMAIL=clan-bot@clan.lol GIT_COMMITTER_NAME=clan-bot GIT_COMMITTER_EMAIL=clan-bot@clan.lol
|
||||
git commit -am "Update pinned clan-core for checks"
|
||||
git push origin +HEAD:update-clan-core-for-checks
|
||||
set -x
|
||||
resp=$(nix run --inputs-from . nixpkgs#curl -- -X POST \
|
||||
-H "Authorization: token $CI_BOT_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"head": "update-clan-core-for-checks",
|
||||
"base": "main",
|
||||
"title": "Update Clan Core for Checks",
|
||||
"body": "This PR updates the pinned clan-core flake input that is used for checks."
|
||||
}' \
|
||||
"https://git.clan.lol/api/v1/repos/clan/clan-core/pulls")
|
||||
pr_number=$(echo "$resp" | jq -r '.number')
|
||||
|
||||
# Merge when succeed
|
||||
while true; do
|
||||
resp=$(nix run --inputs-from . nixpkgs#curl -- -X POST \
|
||||
-H "Authorization: token $CI_BOT_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"Do": "merge",
|
||||
"merge_when_checks_succeed": true,
|
||||
"delete_branch_after_merge": true
|
||||
}' \
|
||||
"https://git.clan.lol/api/v1/repos/clan/clan-core/pulls/$pr_number/merge")
|
||||
msg=$(echo $resp | jq -r '.message')
|
||||
if [[ "$msg" != "Please try again later" ]]; then
|
||||
break
|
||||
fi
|
||||
echo "Retrying in 2 seconds..."
|
||||
sleep 2
|
||||
done
|
||||
29
.gitignore
vendored
29
.gitignore
vendored
@@ -1,23 +1,24 @@
|
||||
.direnv
|
||||
**/.nixos-test-history
|
||||
***/.hypothesis
|
||||
.nixos-test-history
|
||||
.hypothesis
|
||||
out.log
|
||||
.coverage.*
|
||||
**/qubeclan
|
||||
qubeclan
|
||||
pkgs/repro-hook
|
||||
**/testdir
|
||||
testdir
|
||||
democlan
|
||||
example_clan
|
||||
**/result
|
||||
/pkgs/clan-cli/clan_cli/nixpkgs
|
||||
result*
|
||||
/pkgs/clan-cli/clan_lib/nixpkgs
|
||||
/pkgs/clan-cli/clan_cli/webui/assets
|
||||
nixos.qcow2
|
||||
**/*.glade~
|
||||
*.glade~
|
||||
/docs/out
|
||||
**/.local.env
|
||||
/pkgs/clan-cli/clan_lib/select
|
||||
.local.env
|
||||
|
||||
# MacOS stuff
|
||||
**/.DS_store
|
||||
# macOS stuff
|
||||
.DS_Store
|
||||
|
||||
# dream2nix
|
||||
.dream2nix
|
||||
@@ -45,3 +46,11 @@ dist
|
||||
|
||||
# TODO: remove after bug in select is fixed
|
||||
select
|
||||
|
||||
# Generated files
|
||||
pkgs/clan-app/ui/api/API.json
|
||||
pkgs/clan-app/ui/api/API.ts
|
||||
pkgs/clan-app/ui/api/Inventory.ts
|
||||
pkgs/clan-app/ui/api/modules_schemas.json
|
||||
pkgs/clan-app/ui/api/schema.json
|
||||
pkgs/clan-app/ui/.fonts
|
||||
|
||||
2
CODEOWNERS
Normal file
2
CODEOWNERS
Normal file
@@ -0,0 +1,2 @@
|
||||
nixosModules/clanCore/vars/.* @lopter
|
||||
pkgs/clan-cli/clan_cli/(secrets|vars)/.* @lopter
|
||||
@@ -1,4 +1,4 @@
|
||||
# Contributing to Clan
|
||||
|
||||
<!-- Local file: docs/CONTRIBUTING.md -->
|
||||
Go to the Contributing guide at https://docs.clan.lol/manual/contribute/
|
||||
Go to the Contributing guide at https://docs.clan.lol/guides/contributing/CONTRIBUTING
|
||||
@@ -24,7 +24,7 @@ If you're new to Clan and eager to dive in, start with our quickstart guide and
|
||||
|
||||
In the Clan ecosystem, security is paramount. Learn how to handle secrets effectively:
|
||||
|
||||
- **Secrets Management**: Securely manage secrets by consulting [secrets](https://docs.clan.lol/getting-started/secrets/)<!-- [secrets.md](docs/site/getting-started/secrets.md) -->.
|
||||
- **Secrets Management**: Securely manage secrets by consulting [secrets](https://docs.clan.lol/guides/getting-started/secrets/)<!-- [secrets.md](docs/site/guides/getting-started/secrets.md) -->.
|
||||
|
||||
### Contributing to Clan
|
||||
|
||||
|
||||
64
checks/admin/default.nix
Normal file
64
checks/admin/default.nix
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
pkgs,
|
||||
self,
|
||||
clanLib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
public-key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII6zj7ubTg6z/aDwRNwvM/WlQdUocMprQ8E92NWxl6t+ test@test";
|
||||
in
|
||||
|
||||
clanLib.test.makeTestClan {
|
||||
inherit pkgs self;
|
||||
nixosTest = (
|
||||
{ ... }:
|
||||
|
||||
{
|
||||
name = "admin";
|
||||
|
||||
clan = {
|
||||
directory = ./.;
|
||||
modules."@clan/admin" = ../../clanServices/admin/default.nix;
|
||||
inventory = {
|
||||
|
||||
machines.client = { };
|
||||
machines.server = { };
|
||||
|
||||
instances = {
|
||||
ssh-test-one = {
|
||||
module.name = "@clan/admin";
|
||||
roles.default.machines."server".settings = {
|
||||
allowedKeys.testkey = public-key;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nodes = {
|
||||
client.environment.etc.private-test-key.source = ./private-test-key;
|
||||
|
||||
server = {
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.UsePAM = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
|
||||
machines = [client, server]
|
||||
for m in machines:
|
||||
m.systemctl("start network-online.target")
|
||||
|
||||
for m in machines:
|
||||
m.wait_for_unit("network-online.target")
|
||||
|
||||
client.succeed(f"ssh -F /dev/null -i /etc/private-test-key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes root@server true &>/dev/null")
|
||||
'';
|
||||
}
|
||||
);
|
||||
}
|
||||
8
checks/admin/private-test-key
Normal file
8
checks/admin/private-test-key
Normal file
@@ -0,0 +1,8 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACCOs4+7m04Os/2g8ETcLzP1pUHVKHDKa0PBPdjVsZerfgAAAJDXdRkm13UZ
|
||||
JgAAAAtzc2gtZWQyNTUxOQAAACCOs4+7m04Os/2g8ETcLzP1pUHVKHDKa0PBPdjVsZerfg
|
||||
AAAECIgb2FQcgBKMniA+6zm2cwGre60ATu3Sg1GivgAqVJlI6zj7ubTg6z/aDwRNwvM/Wl
|
||||
QdUocMprQ8E92NWxl6t+AAAAC3BpbnBveEBraXdpAQI=
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
|
||||
6
checks/admin/sops/machines/server/key.json
Executable file
6
checks/admin/sops/machines/server/key.json
Executable file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"publickey": "age1q4e7nsw5z6mqeqk5u5kug8lwhpq3f276s0t0npwfffwdkfh58gkqxknhjg",
|
||||
"type": "age"
|
||||
}
|
||||
]
|
||||
15
checks/admin/sops/secrets/server-age.key/secret
Normal file
15
checks/admin/sops/secrets/server-age.key/secret
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:ET/FggP6t7L60krfVRvtMjv++xr3zqRsJ58AfnPS1zjTovV5tE9RgnboGY1ieS7fCs4VOL2S6ELtwV1+BTLDQX9s0c5A9cKqjnc=,iv:6EQ6DOqxUdHcOziTxf8kl0sp1Pggu720s5BJ8zA9Je0=,tag:hQMPWaWb4igqDYjwNehlqQ==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRWjhuZkgwNEZTL3JXZHFE\nTC9jSXJGcVd2bnkvOE1qV0d6TzNobFZobndvCmF1UmhVUWtKeVVwS29NY21ONkRn\nZU5sM01kTU9rQVNENi9paUFWbERoWnMKLS0tIEdjZzgwQjFtWlVtRGZwdW9GY0FK\nSER1TTFNVGxFa0ZrclR4MitWVERiSGMK9DNLzlJZelcpP0klwSDMggTAy5ZVOmsZ\niuu8dXMSdIeTd7l8rpZZN27BaKUm8yEDpUmot5Vq9rbZl6SO3ncX+A==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-07T11:45:41Z",
|
||||
"mac": "ENC[AES256_GCM,data:m8eTnPtMzrooEah43mvjwHxQIwR/aq+A1wYyG/rQ75COq/TQepfMiDSrCJKW8x+OKmN/3HZs1b9k659jNNMF+RtMag0+/ovTmr7PQux3IkzWl+R2kU3Y7WDOMweBKY3mTMu6reICE1YVME8vJwhDDbA5JCXJv64rkTz2tfGt4CQ=,iv:/vrwJyEVsfm1cUK//TesY24Makt8YI8mwx5GIhn4038=,tag:H2tS9ohvWJ4TWB6LghcZNg==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
1
checks/admin/sops/secrets/server-age.key/users/admin
Symbolic link
1
checks/admin/sops/secrets/server-age.key/users/admin
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../users/admin
|
||||
4
checks/admin/sops/users/admin/key.json
Normal file
4
checks/admin/sops/users/admin/key.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"type": "age"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICVVQjCEuryZii1LmJyjx9DX44eJh3qwTTEWlahYONsz nixbld@kiwi
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/machines/server
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:yH7IQixe4nudnK4QOsr7VYoJ1YrVLP0Ufvgu7TNWSJnc55khKZHvQiDIlxzCIrAyMgUYwPNmrrZn9PZhgjAQZm7/o6SmP91Efb0yWM55o861El6v59yw0fseo3z6xAisjlg3KwTd5KMrRhzT0HzrjLn89SYRVh7DAWK+Cs7HVGvKVJ1E6AWiJmFPXIB7YaqJ7P4jZW9u7bEMCZabsRRqgS8dWXVXw9VS5ll4bNYQY4x5p2eg6e81zdeY2Y9Gbi5ty1Whqpzko2Pvggu6K4zUDXikM4lWggvIXzfrJA7HNE3xzXw94J45woj1y5FVOzn1Ve5kCc8PjVGaJ32poGkZiiD07kd5PxZuyVexREJpgz29lyB6nRJJeau4gpSG1VHOyNdwwBsBBm+zn6v2rlVzJPTlqmCV1+5UKf8JZKziIDFfi/78kSdtaeX+miJJvyDRkqNpQ7htEI0TAS8yQrkjWEIyaPAWQ2Usa8g1UrEftTlGUi/aMC2ob0qTLQQbhNhlSV/dImzI/qRMqSy2RWeS,iv:EuprKOFKzNLZrGlPtU2mEjmtNPNOcuVDbuvrtYyrerc=,tag:ny/q1AMHIQ8OgUNEE0Cc8w==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1q4e7nsw5z6mqeqk5u5kug8lwhpq3f276s0t0npwfffwdkfh58gkqxknhjg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLODFxUjREa2tOYW9xaHYw\nQlhWZ282UVhiOGRndk0xYnlCQWRYR01qS2hJCllySUZyblJmTkgyZXd5bjVINDBo\nbEhIWmxycVdOVW0xTUxkalF5Y1k2bXcKLS0tIGRRS1VqOG5sanh2dXR5a2FGeXRs\nK3ZUdERCdEkvMmt3ZndPZEM3QUxJZzAKutOr9jHPCL86zEdMWJ6YZmplcr4tDAcN\nncQfC5rddYDW+0y/crwepKTa2FZjQheOY7jobZanU19ai521hqDSVw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxc3NxNGhRYmU3eFNodDZ4\ndnNTeHFnNXBKbUxmNHBjRlFpNG0zdVNpS2d3CjhrOUlSQU5BZVlSdWR3dnNyODZO\nRFBKZWpwWHlOUW03OGlVZlRQUmMrMzQKLS0tIEd6ei9LU3ZFTzlWTUk1c3huS1RQ\nbG1vQzI4ODJkeFcyRnJaQWp1Wk9zSkUKXefMOk/ZT4P6DItfnM82RoOvX4SBn7Fn\nlAoMnSzaRCunDwq7ha05G45gcI2Wjv3urjt0tmdmrmTnFtBSSt23TQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-07T11:45:47Z",
|
||||
"mac": "ENC[AES256_GCM,data:ORCANHbEX13O+zBVLOYyPxYIr1RS3NybTBb23ES7RbiGhSl2t/TXcfPWU5Smuqee0tfcrxL0u1FELZta4IysySW54JlD2907E9OUJWlQ6seOxADla4TMukW2pwhSsUJ9XfjEwC07zYB0alHzO3pY+LG3OAWzyhAlWzHlB5+WqIA=,iv:As+CjAJxKht0PJs3S2WWzho7UBqaUUltBIrYvlzBAbM=,tag:PSyUKaPZZNCxqd6XLPJSCw==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/machines/server
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:5Fa0TQN/Whj311JZuVWXnp+2KJaNZPb/TOnP23T+KktulabcBA9go+/F+8wJbsEH2mf6UDq656p6C+kLIvfBFl2O/WwSOhsl23as9TLbgB6gBq73GjyV81VFsnLYNLHKMq+8nfJHM/WekA==,iv:n5vz3q5N6DplLWibdiCcYDdiN7q1VggzPoIYy9r2ZJw=,tag:FoGXrrJfjHZCUVTS2RESmw==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1q4e7nsw5z6mqeqk5u5kug8lwhpq3f276s0t0npwfffwdkfh58gkqxknhjg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBheXZvUW9YbjBFMi9mZnVk\ncGFPQzFOZkNPMU1HckhtSGtDWExpWVNYRlV3CjdDaDlSd2wzVnhKZGU0aFY0UnZY\nQStPSkxuSmlyOU9aeUdRaEJ2UTRRSm8KLS0tIFd3SG9YdEU5T2tzNk16b2s1SUNj\nWkh2cng5eWd3ZmxVZDhSR2Y1QnFySDgKGb/t+8NqiSGgmFOJc1NmDYZ+PXlANy8V\nuFwUTeqWAv7pOiGC8oessfyTPaJ7gWjz+XfKV5JVVikK2l3J4eAGxg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWM0daWmxCTjAyQStwQ2lM\nNkcyZW9hRmpDelRJR0VVTWhNTGFuZWhCc1RJCm81ZXowZjBhWGpIQTBhQnZLSmQy\nVUNNYjI0bVpqQ21YZS95TW53OUx1YUkKLS0tIDRUUE1zczBDeFJTOTQyVXVkMkYy\ncVVTN3J6TWtwcXVpM0M5c0gxUXpmV2cKwlWrbGLtkO2+PXKoMoHTV5aJpnfVy3RP\n6i8DDpLPGYfVUtWxHx+L+NmMxmw1AvmKSbdB4Y7aSbBW2mea3j1YCg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-07T11:45:50Z",
|
||||
"mac": "ENC[AES256_GCM,data:rwdbGOg8l8fWT2GYFx+PgV3oPxt5+NCHJf3PhG3V2lrRMPRisyf1nKwDsYavTuhv+bZC/qo4LrGylcXsHWdkCe/xBX+/jYLMf6nJZPk8BPzfUpiDnEKwRl05qfRfkIDusnQrlBrE+tqtcool65js7hYIzSi92O/hxbzzfsCUpqk=,iv:lUTNJkr6Zh3MQm/h7Ven4N6xVn4VeTXOEKzxd0HSsCk=,tag:Bwbi4HD9vzso6306y7EZOg==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:sPh+BuT2we+d/GaMv4zPWc3rPhlMsJQC,iv:VwcHUOMaNiao+R8RBtUINffEUhutktKD6KEWLkFxyp4=,tag:SNVKLjjDv+u5XTVczs2/Uw==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJVWNYRGEwVWxDSmE4bTNL\nRlZPeGZabFZZNGFsMEwzV1ZmT1pqNVk4STMwCkg5UER0Vjk3K1RMazVVYjF3SDc2\ndDZHa3VtYjRiWUJET25weXprc0JNUjAKLS0tIDdVb2xNdWxCcjhpSGtGWDV0d2ti\nZENkZGNpSTNzMVVTZVN0ZktLc2VackEKdexhI37pwcnbZbcy30k9Uo5Z7z3NLqlx\nspxJ87SzEwdStTMhiH1iYf62vcyAOTa4HwfXu97MGVPFNw13/VfgCw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-07T11:45:50Z",
|
||||
"mac": "ENC[AES256_GCM,data:tZRh8qj7JUnhXCfqCHJKWEFQ8XLtmo/p0C+eFIK+34enxfB5lG5Lq83wBXLa0D/nqrr58z1rLO+UVDOI5LH1jFxARBZZnUKrVJNTDHa5pUnlnVOFEOoc+R0h2E5Xw9OHaq7aDUh4fT9+gNDpguKggI5fS9KqRnmZ4VrpNccjnkw=,iv:2yI25fcWMog91EMD7bYQy3GS30a7gZHnif93MaE3sZo=,tag:tYqa6zssiU3BCFU5xmDYZQ==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -147,25 +147,7 @@
|
||||
perSystem =
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
clanCore = self.filter {
|
||||
include = [
|
||||
"checks/backups"
|
||||
"checks/flake-module.nix"
|
||||
"clanModules/borgbackup"
|
||||
"clanModules/flake-module.nix"
|
||||
"clanModules/localbackup"
|
||||
"clanModules/packages"
|
||||
"clanModules/single-disk"
|
||||
"clanModules/zerotier"
|
||||
"flake.lock"
|
||||
"flakeModules"
|
||||
"inventory.json"
|
||||
"nixosModules"
|
||||
# Just include everything in 'lib'
|
||||
# If anything changes in /lib that may affect everything
|
||||
"lib"
|
||||
];
|
||||
};
|
||||
clanCore = self.checks.x86_64-linux.clan-core-for-checks;
|
||||
in
|
||||
{
|
||||
checks = pkgs.lib.mkIf pkgs.stdenv.isLinux {
|
||||
@@ -182,11 +164,6 @@
|
||||
# import the inventory generated nixosModules
|
||||
self.clanInternals.inventoryClass.machines.test-backup.machineImports;
|
||||
clan.core.settings.directory = ./.;
|
||||
environment.systemPackages = [
|
||||
(pkgs.writeShellScriptBin "foo" ''
|
||||
echo ${clanCore}
|
||||
'')
|
||||
];
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
|
||||
6
checks/clan-core-for-checks.nix
Normal file
6
checks/clan-core-for-checks.nix
Normal file
@@ -0,0 +1,6 @@
|
||||
{ fetchgit }:
|
||||
fetchgit {
|
||||
url = "https://git.clan.lol/clan/clan-core.git";
|
||||
rev = "843e1b24be6ff9a7015e67b3291216c08f628d3e";
|
||||
sha256 = "1bfm3n9r9k8prbwsh0yzp421y4ahblv407gqihwvcpiqsx6s3b9b";
|
||||
}
|
||||
@@ -26,6 +26,7 @@ clanLib.test.makeTestClan {
|
||||
roles.admin.machines = [ "admin1" ];
|
||||
};
|
||||
};
|
||||
|
||||
instances."test" = {
|
||||
module.name = "new-service";
|
||||
roles.peer.machines.peer1 = { };
|
||||
@@ -33,25 +34,33 @@ clanLib.test.makeTestClan {
|
||||
|
||||
modules = {
|
||||
legacy-module = ./legacy-module;
|
||||
new-service = {
|
||||
_class = "clan.service";
|
||||
manifest.name = "new-service";
|
||||
roles.peer = { };
|
||||
perMachine = {
|
||||
nixosModule = {
|
||||
# This should be generated by:
|
||||
# ./pkgs/scripts/update-vars.py
|
||||
clan.core.vars.generators.new-service = {
|
||||
files.hello = {
|
||||
secret = false;
|
||||
deploy = true;
|
||||
};
|
||||
script = ''
|
||||
# This is a dummy script that does nothing
|
||||
echo "This is a dummy script" > $out/hello
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
modules.new-service = {
|
||||
_class = "clan.service";
|
||||
manifest.name = "new-service";
|
||||
roles.peer = { };
|
||||
perMachine = {
|
||||
nixosModule = {
|
||||
# This should be generated by:
|
||||
# nix run .#generate-test-vars -- checks/dummy-inventory-test dummy-inventory-test
|
||||
clan.core.vars.generators.new-service = {
|
||||
files.not-a-secret = {
|
||||
secret = false;
|
||||
deploy = true;
|
||||
};
|
||||
files.a-secret = {
|
||||
secret = true;
|
||||
deploy = true;
|
||||
owner = "nobody";
|
||||
group = "users";
|
||||
mode = "0644";
|
||||
};
|
||||
script = ''
|
||||
# This is a dummy script that does nothing
|
||||
echo -n "not-a-secret" > $out/not-a-secret
|
||||
echo -n "a-secret" > $out/a-secret
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -69,7 +78,15 @@ clanLib.test.makeTestClan {
|
||||
print(peer1.succeed("systemctl status dummy-service"))
|
||||
|
||||
# peer1 should have the 'hello' file
|
||||
peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.new-service.files.hello.path}")
|
||||
peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.new-service.files.not-a-secret.path}")
|
||||
|
||||
ls_out = peer1.succeed("ls -la ${nodes.peer1.clan.core.vars.generators.new-service.files.a-secret.path}")
|
||||
# Check that the file is owned by 'nobody'
|
||||
assert "nobody" in ls_out, f"File is not owned by 'nobody': {ls_out}"
|
||||
# Check that the file is in the 'users' group
|
||||
assert "users" in ls_out, f"File is not in the 'users' group: {ls_out}"
|
||||
# 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}"
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"publickey": "age1hd2exjq88h7538y6mvjvexx3u5gp6a03yfn5nj32h2667yyksyaqcuk5qs",
|
||||
"publickey": "age12yt078p9ewxy2sh0a36nxdpgglv8wqqftmj4dkj9rgy5fuyn4p0q5nje9m",
|
||||
"type": "age"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"publickey": "age19urkt89q45a2wk6a4yaramzufjtnw6nq2snls0v7hmf7tqf73axsfx50tk",
|
||||
"publickey": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2",
|
||||
"type": "age"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:hhuFgZcPqht0h3tKxGtheS4GlrVDo4TxH0a9lxgPYj2i12QUmE04rB07A+hu4Z8WNWLYvdM5069mEOZYm3lSeTzBHQPxYZRuVj0=,iv:sA1srRFQqsMlJTAjFcb09tI/Jg2WjOVJL5NZkPwiLoU=,tag:6xXo9FZpmAJw6hCBsWzf8Q==,type:str]",
|
||||
"data": "ENC[AES256_GCM,data:GPpsUhSzWPtTP8EUNKsobFXjYqDldhkkIH6hBk11RsDLAGWdhVrwcISGbhsWpYhvAdPKA84DB6Zqyh9lL2bLM9//ybC1kzY20BQ=,iv:NrxMLdedT2FCkUAD00SwsAHchIsxWvqe7BQekWuJcxw=,tag:pMDXcMyHnLF2t3Qhb1KolA==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGaGVHeTgrN3dJQ2VITFBM\neWVzbDhjb0pwNUhBUjdUc0p5OTVta1dvSno4ClJxeUc4Z0hiaFRkVlJ1YTA4Lyta\neWdwV005WGYvMUNRVG1qOVdicTk0NUkKLS0tIFQvaDNFS1JMSFlHRXlhc3lsZm03\nYVhDaHNsam5wN1VqdzA3WTZwM1JwV2sKZk/SiZJgjllADdfHLSWuQcU4+LttDpt/\nqqDUATEuqYaALljC/y3COT+grTM2bwGjj6fsfsfiO/EL9iwzD3+7oA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzb2tWb1ExKzdmUTRzaGVj\nK3cyYTBHZTJwVjM1SzUvbHFiMnVhY05iKzFZCnJTSE1VSVdpcUFLSEJuaE1CZzJD\nWjZxYzN2cUltdThNMVRKU3FIb20vUXMKLS0tIFlHQXRIdnMybDZFUVEzWlQrc1dw\nbUxhZURXblhHd0pka0JIK1FTZEVqdUEKI/rfxQRBc+xGRelhswkJQ9GcZs6lzfgy\nuCxS5JI9npdPLQ/131F3b21+sP5YWqks41uZG+vslM1zQ+BlENNhDw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-09T15:10:16Z",
|
||||
"mac": "ENC[AES256_GCM,data:xuXj4833G6nhvcRo2ekDxz8G5phltmU8h1GgGofH9WndzrqLKeRSqm/n03IHRW0f4F68XxnyAkfvokVh6vW3LRQAFkqIlXz5U4+zFNcaVaPobS5gHTgxsCoTUoalWPvHWtXd50hUVXeAt8rPfTfeveVGja8bOERk8mvwUPxb6h4=,iv:yP1usA9m8tKl6Z/UK9PaVMJlZlF5qpY4EiM4+ByVlik=,tag:8DgoIhLstp3MRki90VfEvw==,type:str]",
|
||||
"lastmodified": "2025-05-04T12:44:13Z",
|
||||
"mac": "ENC[AES256_GCM,data:fWxLHXBWolHVxv6Q7utcy6OVLV13ziswrIYyNKiwy1vsU8i7xvvuGO1HlnE+q43D2WuHR53liKq1UHuf1JMrWzTwZ0PYe+CVugtoEtbR2qu3rK/jAkOyMyhmmHzmf6Rp4ZMCzKgZeC/X2bDKY/z0firHAvjWydEyogutHpvtznM=,iv:OQI3FfkLneqbdztAXVQB3UkHwDPK+0hWu5hZ9m8Oczg=,tag:em6GfS2QHsXs391QKPxfmA==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.1"
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:rwPhbayGf6mE1E9NCN+LuL7VfWWOfhoJW6H2tNSoyebtyTpM3GO2jWca1+N7hI0juhNkUk+rIsYQYbCa/5DZQiV0/2Jgu4US1XY=,iv:B5mcaQsDjb6BacxGB4Kk88/qLCpVOjQNRvGN+fgUiEo=,tag:Uz0A8kAF5NzFetbv9yHIjQ==,type:str]",
|
||||
"data": "ENC[AES256_GCM,data:W3cOkUYL5/YulW2pEISyTlMaA/t7/WBE7BoCdFlqrqgaCL7tG4IV2HgjiPWzIVMs0zvDSaghdEvAIoB4wOf470d1nSWs0/E8SDk=,iv:wXXaZIw3sPY8L/wxsu7+C5v+d3RQRuwxZRP4YLkS8K4=,tag:HeK4okj7O7XDA9JDz2KULw==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWY0hKQ1dnV0tMYytDMCtj\nTDV4Zk5NeVN0bCtqaWRQV3d4M0VlcGVZMkhZCm02dHZyOGVlYzJ5Z3FlUWNXMVQ0\nb2ZrTXZQRzRNdzFDeWZCVGhlTS9rMm8KLS0tIEJkY1QwOENRYWw3cjIwd3I0bzdz\nOEtQNm1saE5wNWt2UUVnYlN4NWtGdFkKmWHU5ttZoQ3NZu/zkX5VxfC2sMpSOyod\neb7LRhFqPfo5N1XphJcCqr5QUoZOfnH0xFhZ2lxWUS3ItiRpU4VDwg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxRC83b3dtSVpXcGovNnVs\nTzFka2J2MEFhYkF1ajVrdjMrNUtPWGRObjM4Cm5zSUR5OGw0T0FaL3BaWmR6L29W\nU2syMFIyMUhFRUZpWFpCT28vWko2ZU0KLS0tIFpHK3BjU1V1L0FrMGtwTGFuU3Mz\nRkV5VjI2Vndod202bUR3RWQwNXpmVzQKNk8/y7M62wTIIKqY4r3ZRk5aUCRUfine\n1LUSHMKa2bRe+hR7nS7AF4BGXp03h2UPY0FP5+U5q8XuIj1jfMX8kg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-09T15:10:41Z",
|
||||
"mac": "ENC[AES256_GCM,data:pab0G2GPjgs59sbiZ8XIV5SdRtq5NPU0yq18FcqiMV8noAL94fyVAY7fb+9HILQWQsEjcykgk9mA2MQ0KpK/XG8+tDQKcBH+F+2aQnw5GJevXmfi7KLTU0P224SNo7EnKlfFruB/+NZ0WBtkbbg1OzekrbplchpSI6BxWz/jASE=,iv:TCj9FCxgfMF2+PJejr67zgGnF+CFS+YeJiejnHbf7j0=,tag:s7r9SqxeqpAkncohYvIQ2Q==,type:str]",
|
||||
"lastmodified": "2025-05-04T12:44:16Z",
|
||||
"mac": "ENC[AES256_GCM,data:yTkQeFvKrN1+5FP+yInsaRWSAG+ZGG0uWF3+gVRvzJTFxab8kT2XkAMc+4D7SKgcjsmwBBb77GNoAKaKByhZ92UaCfZ2X66i7ZmYUwLM1NVVmm+xiwwjsh7PJXlZO/70anTzd1evtlZse0jEmRnV5Y0F0M6YqXmuwU+qGUJU2F8=,iv:sy6ozhXonWVruaQfa7pdEoV5GkNZR/UbbINKAPbgWeg=,tag:VMruQ1KExmlMR7TsGNgMlg==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.1"
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:bxM9aYMK,iv:SMNYtk9FSyZ1PIfEzayTKKdCnZWdhcyUEiTwFUNb988=,tag:qJYW4+VQyhF1tGPQPTKlOQ==,type:str]",
|
||||
"data": "ENC[AES256_GCM,data:T8edCvw=,iv:7/G5xt5fv38I9uFzk7WMIr9xQdz/6lFxqOC+18HBg8Q=,tag:F39Cxbgmzml+lZLsZ59Kmg==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1hd2exjq88h7538y6mvjvexx3u5gp6a03yfn5nj32h2667yyksyaqcuk5qs",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvZDZYYXdpcXVqRFRnQ2Jx\nTFhFWEJTR290cHZhTXZadFFvcHM4MHVIN3lFCmJhOEZrL3g4TFBZVllxdDFZakJn\nR3NxdXo0eE8vTDh3QlhWOFpVZ0lNUHcKLS0tIEE4dkpCalNzaXJ0Qks3VHJSUzZF\nb2N3NGdjNHJnSUN6bW8welZ1VDdJakEKGKZ7nn1p11IyJB6DMxu2HJMvZ+0+5WpE\nPLWh2NlGJO3XrrL4Fw7xetwbqE+QUZPNl/JbEbu4KLIUGLjqk9JDhQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
"recipient": "age12yt078p9ewxy2sh0a36nxdpgglv8wqqftmj4dkj9rgy5fuyn4p0q5nje9m",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPNUhiYkZWK3dPMHNiRTVM\nRHNvaHFsOFp1c0UxQitwVG0zY01MNDZRV1E4CjEybENoTVIzN29vQ3FtUTRSYmFU\nNXIzQllVSllXRGN2M1B6WXJLdHZSajgKLS0tIDllZ0ZmZUcxMHhDQUpUOEdWbmkv\neUQweHArYTdFSmNteVpuQ3BKdnh0Y0UKs8Hm3D+rXRRfpUVSZM3zYjs6b9z8g10D\nGTkvreUMim4CS22pjdQ3eNA9TGeDXfWXE7XzwXLCb+wVcf7KwbDmvg==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHckJCQVFyb21aT1R0d2Rr\nMWxNMHVqcGxabHBmS0RibW9sN0gyZDI1b1dFCnRWUk5LSWdxV3c4RWVZdUtEN1Fv\nRk4xVmwwT2xrdWVERkJXUVVlVXJjTVUKLS0tIC9ERG9KMGxTNEsrbzFHUGRiVUlm\nRi9qakxoc1FOVVV1TkUrckwxRUVnajQKE8ms/np2NMswden3xkjdC8cXccASLOoN\nu+EaEk69UvBvnOg9VBjyPAraIKgNrTc4WWwz+DOBj1pCwVbu9XxUlA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKSDhpT3cvck9PenZYVEZH\ndFQreVRBdG93L1dBUGlvYjFWcDlHWUJsZUVBCm9DMTJ4UytiYzlEVHNWdUcwS1ds\nT0dhbzAzNDdmbDBCU0dvL2xNeHpXcGsKLS0tIFArbmpsbzU3WnpJdUt1MGN0L1d0\nV1JkTDJYWUxsbmhTQVNOeVRaSUhTODQKk9Vph2eldS5nwuvVX0SCsxEm4B+sO76Z\ndIjJ3OQxzoZmXMaOOuKHC5U0Y75Qn7eXC43w5KHsl2CMIUYsBGJOZw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-09T15:10:30Z",
|
||||
"mac": "ENC[AES256_GCM,data:cIwWctUbAFI8TRMxYWy5xqlKDVLMqBIxVv4LInnLqi3AauL0rJ3Z7AxK/wb2dCQM07E1N7YaORNqgUpFC1xo0hObAA8mrPaToPotKDkjua0zuyTUNS1COoraYjZpI/LKwmik/qtk399LMhiC7aHs+IliT9Dd41B8LSMBXwdMldY=,iv:sZ+//BrYH5Ay2JJAGs7K+WfO2ASK82syDlilQjGmgFs=,tag:nY+Af9eQRLwkiHZe85dQ9A==,type:str]",
|
||||
"lastmodified": "2025-05-04T12:44:14Z",
|
||||
"mac": "ENC[AES256_GCM,data:6fKrS1eLLUWlHkQpxLFXBRk6f2wa5ADLMViVvYXXGU24ayl9UuNSKrCRHp9cbzhqhti3HdwyNt6TM+2X6qhiiAQanKEB2PF7JRYX74NfNKil9BEDjt5AqqtpSgVv5l7Ku/uSHaPkd2sDmzHsy5Q4bSGxJQokStk1kidrwle+mbc=,iv:I/Aad82L/TCxStM8d8IZICUrwdjRbGx2fuGWqexr21o=,tag:BfgRbGUxhPZzK2fLik1kxA==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.1"
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
13898
|
||||
18650
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:ImlGIKxE,iv:UUWxjLNRKJCD2WHNpw8lfvCc8rnXPCqc2pni1ODckjE=,tag:HFCqiv31E9bShIIaAEjF0A==,type:str]",
|
||||
"data": "ENC[AES256_GCM,data:vp0yW0Gt,iv:FO2cy+UpEl5aRay/LUGu//c82QiVxuKuGSaVh0rGJvc=,tag:vf2RAOPpcRW0HwxHoGy17A==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age19urkt89q45a2wk6a4yaramzufjtnw6nq2snls0v7hmf7tqf73axsfx50tk",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpTEROZjh6NjBhSlJSc1Av\nSHhjdkhwVUd3VzBZemhQb3dhMlJXalBmZlFjCkZPYkhZZGVOVTNjUWdFU0s4cWFn\nL2NXbkRCdUlMdElnK2lGbG5iV0w1cHMKLS0tIFREcmxDdHlUNVBFVGRVZSt0c0E5\nbnpHaW1Vb3R3ZFFnZVMxY3djSjJmOU0KIwqCSQf5S9oA59BXu7yC/V6yqvCh88pa\nYgmNyBjulytPh1aAfOuNWIGdIxBpcEf+gFjz3EiJY9Kft3fTmhp2bw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
"recipient": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjaFVNMEd2YUxpSm5XVVRi\nY2ZUc3NTOStJUFNMWWVPQTgxZ2tCK1QrMW1ZCjYwMlA4dkIzSlc0TGtvZjcyK3Bi\nM3pob2JOOFUyeVJ6M2JpaTRCZlc1R0kKLS0tIDJMb1dFcVRWckhwYWNCQng0RlFO\nTkw3OGt4dkFIZVY5aVEzZE5mMzJSM0EKUv8bUqg48L2FfYVUVlpXvyZvPye699of\nG6PcjLh1ZMbNCfnsCzr+P8Vdk/F4J/ifxL66lRGfu2xOLxwciwQ+5Q==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArN3R4TThibjdYbE9TMDE1\naUhuNDlscExjaktIR2VmTk1OMWtVM0NpTUJZClJUNEcwVDlibExWQk84TTNEWFhp\nMjYyZStHc1N0ZTh1S3VTVk45WGxlWWMKLS0tIHFab25LY1R1d1l6NE5XbHJvQ3lj\nNGsxUldFVHQ5RVJERDlGbi9NY29hNWsKENBTcAS/R/dTGRYdaWv5Mc/YG4bkah5w\nb421ZMQF+r4CYnzUqnwivTG8TMRMqJLavfkutE6ZUfJbbLufrTk5Lw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnZ2dDbVhoQngxM3lTSmZF\nUTAwS1lCTGhEMU1GVXpFUzlIUFdqZy9LajF3Ck9mdVpBRjlyVUNhZXZIUFZjUzF1\nNlhFN28vNmwzcUVkNmlzUnpkWjJuZE0KLS0tIHpXVHVlNk9vU1ZPTGRrYStWbmRO\nbDM4U2o1SlEwYWtqOXBqd3BFUTAvMHcKkI8UVd0v+x+ELZ5CoGq9DzlA6DnVNU2r\nrV9wLfbFd7RHxS0/TYZh5tmU42nO3iMYA9FqERQXCtZgXS9KvfqHwQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-04-09T15:11:04Z",
|
||||
"mac": "ENC[AES256_GCM,data:JdJzocQZWVprOmZ4Ni04k1tpD1TpFcK5neKy3+0/c3+uPBwjwaMayISKRaa/ILUXlalg60oTqxB4fUFoYVm8KGQVhDwPhO/T1hyYVQqidonrcYfJfCYg00mVSREV/AWqXb7RTnaEBfrdnRJvaAQF9g2qDXGVgzp3eACdlItclv4=,iv:nOw1jQjIWHWwU3SiKpuQgMKXyu8MZYI+zI9UYYd9fCI=,tag:ewUkemIPm/5PkmuUD0EcAQ==,type:str]",
|
||||
"lastmodified": "2025-05-04T12:44:18Z",
|
||||
"mac": "ENC[AES256_GCM,data:1ZZ+ZI1JsHmxTov1bRijfol3kTkyheg2o3ivLsMHRhCmScsUry97hQJchF78+y2Izt7avaQEHYn6pVbYt/0rLrSYD7Ru7ITVxXoYHOiN5Qb98masUzpibZjrdyg5nO+LW5/Hmmwsc3yn/+o3IH1AUYpsxlJRdnHHCmoSOFaiFFM=,iv:OQlgmpOTw4ljujNzqwQ5/0Mz8pQpCSUtqRvj3FJAxDs=,tag:foZvdeW7gK9ZVKkWqnlxGA==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.1"
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
30661
|
||||
6745
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/machines/peer1
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:prFl0EJy8bM=,iv:zITWxf+6Ebk0iB5vhhd7SBQa1HFrIJXm8xpSM+D9I0M=,tag:NZCRMCs1SzNKLBu/KUDKMQ==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0S0RZRWxaZVZvTUhjdWVL\naU9WZmtEcm1qa2JsRmdvdmZmNENMaWFEVUFRCmdoVnRXSGlpRlFjNmVVbDJ5VnFT\nMnVJUlVnM3lxNmZCRTdoRVJ4NW1oYWcKLS0tIFFNbXBFUk1RWnlUTW1SeG1vYzlM\nVVpEclFVOE9PWWQxVkZ0eEgwWndoRWcKDAOHe+FIxqGsc6LhxMy164qjwG6t2Ei2\nP0FSs+bcKMDpudxeuxCjnDm/VoLxOWeuqkB+9K2vSm2W/c/fHTSbrA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2VU5jOEpwYUtDVEVFcVpU\nQkExTVZ3ejZHcGo5TG8zdUQwNktoV09WdUZvCmQ0dE1TOWRFbTlxdVd4WWRxd3VF\nQUNTTkNNT3NKYjQ5dEJDY0xVZ3pZVUUKLS0tIDFjajRZNFJZUTdNeS8yN05FMFZU\ncEtjRjhRbGE0MnRLdk10NkFLMkxqencKGzJ66dHluIghH04RV/FccfEQP07yqnfb\n25Hi0XIVJfXBwje4UEyszrWTxPPwVXdQDQmoNKf76Qy2jYqJ56uksw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-04T12:44:20Z",
|
||||
"mac": "ENC[AES256_GCM,data:FIkilsni5kOdNlVwDuLsQ/zExypHRWdqIBQDNWMLTwe8OrsNPkX+KYutUvt9GaSoGv4iDULaMRoizO/OZUNfc2d8XYSdj0cxOG1Joov4GPUcC/UGyNuQneAejZBKolvlnidKZArofnuK9g+lOTANEUtEXUTnx8L+VahqPZayQas=,iv:NAo6sT3L8OOB3wv1pjr3RY2FwXgVmZ4N0F4BEX4YPUY=,tag:zHwmXygyvkdpASZCodQT9Q==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../../sops/users/admin
|
||||
@@ -1 +0,0 @@
|
||||
This is a dummy script
|
||||
@@ -0,0 +1 @@
|
||||
not-a-secret
|
||||
@@ -14,13 +14,14 @@ in
|
||||
./installation/flake-module.nix
|
||||
./morph/flake-module.nix
|
||||
./nixos-documentation/flake-module.nix
|
||||
./sanity-checks/dont-depend-on-repo-root.nix
|
||||
./dont-depend-on-repo-root.nix
|
||||
];
|
||||
perSystem =
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
self',
|
||||
system,
|
||||
...
|
||||
}:
|
||||
{
|
||||
@@ -33,27 +34,33 @@ in
|
||||
inherit self;
|
||||
inherit (self) clanLib;
|
||||
};
|
||||
nixosTests = lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
||||
# Deltachat is currently marked as broken
|
||||
# deltachat = import ./deltachat nixosTestArgs;
|
||||
nixosTests =
|
||||
lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
||||
# Deltachat is currently marked as broken
|
||||
# deltachat = import ./deltachat nixosTestArgs;
|
||||
|
||||
# Base Tests
|
||||
secrets = self.clanLib.test.baseTest ./secrets nixosTestArgs;
|
||||
borgbackup = self.clanLib.test.baseTest ./borgbackup nixosTestArgs;
|
||||
wayland-proxy-virtwl = self.clanLib.test.baseTest ./wayland-proxy-virtwl nixosTestArgs;
|
||||
# Base Tests
|
||||
secrets = self.clanLib.test.baseTest ./secrets nixosTestArgs;
|
||||
borgbackup = self.clanLib.test.baseTest ./borgbackup nixosTestArgs;
|
||||
wayland-proxy-virtwl = self.clanLib.test.baseTest ./wayland-proxy-virtwl nixosTestArgs;
|
||||
|
||||
# Container Tests
|
||||
container = self.clanLib.test.containerTest ./container nixosTestArgs;
|
||||
zt-tcp-relay = self.clanLib.test.containerTest ./zt-tcp-relay nixosTestArgs;
|
||||
matrix-synapse = self.clanLib.test.containerTest ./matrix-synapse nixosTestArgs;
|
||||
postgresql = self.clanLib.test.containerTest ./postgresql nixosTestArgs;
|
||||
# Container Tests
|
||||
container = self.clanLib.test.containerTest ./container nixosTestArgs;
|
||||
zt-tcp-relay = self.clanLib.test.containerTest ./zt-tcp-relay nixosTestArgs;
|
||||
matrix-synapse = self.clanLib.test.containerTest ./matrix-synapse nixosTestArgs;
|
||||
postgresql = self.clanLib.test.containerTest ./postgresql nixosTestArgs;
|
||||
|
||||
# Clan Tests
|
||||
mumble = import ./mumble nixosTestArgs;
|
||||
dummy-inventory-test = import ./dummy-inventory-test nixosTestArgs;
|
||||
data-mesher = import ./data-mesher nixosTestArgs;
|
||||
syncthing = import ./syncthing nixosTestArgs;
|
||||
};
|
||||
# Clan Tests
|
||||
dummy-inventory-test = import ./dummy-inventory-test nixosTestArgs;
|
||||
admin = import ./admin nixosTestArgs;
|
||||
data-mesher = import ./data-mesher nixosTestArgs;
|
||||
syncthing = import ./syncthing nixosTestArgs;
|
||||
}
|
||||
// lib.optionalAttrs (pkgs.stdenv.hostPlatform.system == "aarch64-linux") {
|
||||
# for some reason this hangs in an odd place in CI, but it works on my machine ...
|
||||
# on aarch64-linux it works though
|
||||
mumble = import ./mumble nixosTestArgs;
|
||||
};
|
||||
|
||||
packagesToBuild = lib.removeAttrs self'.packages [
|
||||
# exclude the check that checks that nothing depends on the repo root
|
||||
@@ -83,7 +90,10 @@ in
|
||||
schema =
|
||||
(self.clanLib.inventory.evalClanService {
|
||||
modules = [ m ];
|
||||
key = "checks";
|
||||
prefix = [
|
||||
"checks"
|
||||
system
|
||||
];
|
||||
}).config.result.api.schema;
|
||||
in
|
||||
schema
|
||||
@@ -97,6 +107,12 @@ in
|
||||
mkdir -p $out
|
||||
cat $schemaFile > $out/allSchemas.json
|
||||
'';
|
||||
|
||||
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
|
||||
cp ${../flake.lock} $out/flake.lock
|
||||
'';
|
||||
};
|
||||
legacyPackages = {
|
||||
nixosTests =
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
let
|
||||
dependencies = [
|
||||
pkgs.disko
|
||||
pkgs.buildPackages.xorg.lndir
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".pkgs.perlPackages.ConfigIniFiles
|
||||
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".pkgs.perlPackages.FileSlurp
|
||||
|
||||
@@ -80,7 +81,7 @@
|
||||
|
||||
# Some distros like to automount disks with spaces
|
||||
machine.succeed('mkdir -p "/mnt/with spaces" && mkfs.ext4 /dev/vdb && mount /dev/vdb "/mnt/with spaces"')
|
||||
machine.succeed("clan flash write --debug --flake ${../..} --yes --disk main /dev/vdb test-flash-machine-${pkgs.hostPlatform.system}")
|
||||
machine.succeed("clan flash write --debug --flake ${self.checks.x86_64-linux.clan-core-for-checks} --yes --disk main /dev/vdb test-flash-machine-${pkgs.hostPlatform.system}")
|
||||
'';
|
||||
} { inherit pkgs self; };
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ let
|
||||
pkgs.bash.drvPath
|
||||
pkgs.nixos-anywhere
|
||||
pkgs.bubblewrap
|
||||
pkgs.buildPackages.xorg.lndir
|
||||
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
|
||||
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
|
||||
in
|
||||
@@ -197,7 +198,7 @@ in
|
||||
installer.succeed("${pkgs.coreutils}/bin/install -Dm 600 ${../assets/ssh/privkey} /root/.ssh/id_ed25519")
|
||||
|
||||
installer.wait_until_succeeds("timeout 2 ssh -o StrictHostKeyChecking=accept-new -v nonrootuser@localhost hostname")
|
||||
installer.succeed("cp -r ${../..} test-flake && chmod -R +w test-flake")
|
||||
installer.succeed("cp -r ${self.checks.x86_64-linux.clan-core-for-checks} test-flake && chmod -R +w test-flake")
|
||||
|
||||
installer.succeed("clan machines install --no-reboot --debug --flake test-flake --yes test-install-machine-without-system --target-host nonrootuser@localhost --update-hardware-config nixos-facter >&2")
|
||||
installer.shutdown()
|
||||
@@ -217,7 +218,7 @@ in
|
||||
installer.start()
|
||||
installer.succeed("${pkgs.coreutils}/bin/install -Dm 600 ${../assets/ssh/privkey} /root/.ssh/id_ed25519")
|
||||
installer.wait_until_succeeds("timeout 2 ssh -o StrictHostKeyChecking=accept-new -v nonrootuser@localhost hostname")
|
||||
installer.succeed("cp -r ${../..} test-flake && chmod -R +w test-flake")
|
||||
installer.succeed("cp -r ${self.checks.x86_64-linux.clan-core-for-checks} test-flake && chmod -R +w test-flake")
|
||||
installer.fail("test -f test-flake/machines/test-install-machine/hardware-configuration.nix")
|
||||
installer.fail("test -f test-flake/machines/test-install-machine/facter.json")
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
dependencies = [
|
||||
self
|
||||
pkgs.stdenv.drvPath
|
||||
pkgs.stdenvNoCC
|
||||
self.nixosConfigurations.test-morph-machine.config.system.build.toplevel
|
||||
@@ -55,7 +54,7 @@
|
||||
testScript = ''
|
||||
start_all()
|
||||
actual.fail("cat /etc/testfile")
|
||||
actual.succeed("env CLAN_DIR=${self} clan machines morph test-morph-template --i-will-be-fired-for-using-this --debug --name test-morph-machine")
|
||||
actual.succeed("env CLAN_DIR=${self.checks.x86_64-linux.clan-core-for-checks} clan machines morph test-morph-template --i-will-be-fired-for-using-this --debug --name test-morph-machine")
|
||||
assert actual.succeed("cat /etc/testfile") == "morphed"
|
||||
'';
|
||||
} { inherit pkgs self; };
|
||||
|
||||
@@ -47,6 +47,20 @@ clanLib.test.makeTestClan {
|
||||
nodes.peer2 = common;
|
||||
|
||||
testScript = ''
|
||||
import time
|
||||
import re
|
||||
|
||||
|
||||
def machine_has_text(machine: Machine, regex: str) -> bool:
|
||||
variants = machine.get_screen_text_variants()
|
||||
# for debugging
|
||||
# machine.screenshot(f"/tmp/{machine.name}.png")
|
||||
for text in variants:
|
||||
print(f"Expecting '{regex}' in '{text}'")
|
||||
if re.search(regex, text) is not None:
|
||||
return True
|
||||
return False
|
||||
|
||||
start_all()
|
||||
|
||||
with subtest("Waiting for x"):
|
||||
@@ -63,41 +77,53 @@ clanLib.test.makeTestClan {
|
||||
peer2.execute("mumble >&2 &")
|
||||
|
||||
with subtest("Wait for Mumble"):
|
||||
peer1.wait_for_window(r"^Mumble$")
|
||||
peer2.wait_for_window(r"^Mumble$")
|
||||
peer1.wait_for_window(r"Mumble")
|
||||
peer2.wait_for_window(r"Mumble")
|
||||
|
||||
with subtest("Wait for certificate creation"):
|
||||
peer1.wait_for_window(r"^Mumble$")
|
||||
peer1.sleep(3) # mumble is slow to register handlers
|
||||
peer1.send_chars("\n")
|
||||
peer1.send_chars("\n")
|
||||
peer2.wait_for_window(r"^Mumble$")
|
||||
peer2.sleep(3) # mumble is slow to register handlers
|
||||
peer2.send_chars("\n")
|
||||
peer2.send_chars("\n")
|
||||
peer1.wait_for_window(r"Mumble")
|
||||
peer2.wait_for_window(r"Mumble")
|
||||
|
||||
with subtest("Wait for server connect"):
|
||||
peer1.wait_for_window(r"^Mumble Server Connect$")
|
||||
peer2.wait_for_window(r"^Mumble Server Connect$")
|
||||
for i in range(20):
|
||||
time.sleep(1)
|
||||
peer1.send_chars("\n")
|
||||
peer1.send_chars("\n")
|
||||
peer2.send_chars("\n")
|
||||
peer2.send_chars("\n")
|
||||
if machine_has_text(peer1, r"Mumble Server Connect") and \
|
||||
machine_has_text(peer2, r"Mumble Server Connect"):
|
||||
break
|
||||
else:
|
||||
raise Exception("Timeout waiting for certificate creation")
|
||||
|
||||
with subtest("Check validity of server certificates"):
|
||||
peer1.execute("killall .mumble-wrapped")
|
||||
peer1.sleep(1)
|
||||
peer1.execute("mumble mumble://peer2 >&2 &")
|
||||
peer1.wait_for_window(r"^Mumble$")
|
||||
peer1.sleep(3) # mumble is slow to register handlers
|
||||
peer1.send_chars("\n")
|
||||
peer1.send_chars("\n")
|
||||
peer1.wait_for_text("Connected.")
|
||||
peer1.wait_for_window(r"Mumble")
|
||||
|
||||
for i in range(20):
|
||||
time.sleep(1)
|
||||
peer1.send_chars("\n")
|
||||
peer1.send_chars("\n")
|
||||
if machine_has_text(peer1, "Connected."):
|
||||
break
|
||||
else:
|
||||
raise Exception("Timeout waiting for certificate creation")
|
||||
|
||||
peer2.execute("killall .mumble-wrapped")
|
||||
peer2.sleep(1)
|
||||
peer2.execute("mumble mumble://peer1 >&2 &")
|
||||
peer2.wait_for_window(r"^Mumble$")
|
||||
peer2.sleep(3) # mumble is slow to register handlers
|
||||
peer2.send_chars("\n")
|
||||
peer2.send_chars("\n")
|
||||
peer2.wait_for_text("Connected.")
|
||||
peer2.wait_for_window(r"Mumble")
|
||||
|
||||
for i in range(20):
|
||||
time.sleep(1)
|
||||
peer2.send_chars("\n")
|
||||
peer2.send_chars("\n")
|
||||
if machine_has_text(peer2, "Connected."):
|
||||
break
|
||||
else:
|
||||
raise Exception("Timeout waiting for certificate creation")
|
||||
'';
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
description = "Convenient Administration for the Clan App"
|
||||
categories = ["Utility"]
|
||||
features = [ "inventory" ]
|
||||
features = [ "inventory", "deprecated" ]
|
||||
---
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# Dont import this file
|
||||
# It is only here for backwards compatibility.
|
||||
# Dont author new modules with this file.
|
||||
{
|
||||
imports = [ ./roles/default.nix ];
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{ lib, config, ... }:
|
||||
{
|
||||
|
||||
options.clan.admin = {
|
||||
allowedKeys = lib.mkOption {
|
||||
default = { };
|
||||
|
||||
@@ -4,7 +4,7 @@ description = "Statically configure borgbackup with sane defaults."
|
||||
!!! Danger "Deprecated"
|
||||
Use [borgbackup](borgbackup.md) instead.
|
||||
|
||||
Don't use borgbackup-static through [inventory](../../manual/inventory.md).
|
||||
Don't use borgbackup-static through [inventory](../../guides/inventory.md).
|
||||
|
||||
This module implements the `borgbackup` backend and implements sane defaults
|
||||
for backup management through `borgbackup` for members of the clan.
|
||||
|
||||
@@ -12,13 +12,16 @@ let
|
||||
# currently only supports zerotier
|
||||
defaultBootstrapNodes = builtins.foldl' (
|
||||
urls: name:
|
||||
if
|
||||
builtins.pathExists "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value"
|
||||
then
|
||||
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 "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value";
|
||||
ip = builtins.readFile ipPath;
|
||||
in
|
||||
urls ++ "${ip}:${cfg.network.port}"
|
||||
urls ++ [ "[${ip}]:${builtins.toString cfg.network.port}" ]
|
||||
else
|
||||
urls
|
||||
) [ ] (dmLib.machines config).bootstrap;
|
||||
@@ -87,7 +90,8 @@ in
|
||||
push_pull_interval = "30s";
|
||||
|
||||
interface = cfg.network.interface;
|
||||
bootstrap_nodes = cfg.bootstrapNodes or defaultBootstrapNodes;
|
||||
|
||||
bootstrap_nodes = if cfg.bootstrapNodes == null then defaultBootstrapNodes else cfg.bootstrapNodes;
|
||||
};
|
||||
|
||||
http.port = 7331;
|
||||
@@ -105,10 +109,7 @@ in
|
||||
private_key = {
|
||||
inherit owner;
|
||||
};
|
||||
public_key = {
|
||||
inherit owner;
|
||||
secret = false;
|
||||
};
|
||||
public_key.secret = false;
|
||||
};
|
||||
|
||||
runtimeInputs = [
|
||||
@@ -134,10 +135,7 @@ in
|
||||
private_key = {
|
||||
inherit owner;
|
||||
};
|
||||
public_key = {
|
||||
inherit owner;
|
||||
secret = false;
|
||||
};
|
||||
public_key.secret = false;
|
||||
};
|
||||
|
||||
runtimeInputs = [
|
||||
|
||||
@@ -8,8 +8,8 @@ in
|
||||
{
|
||||
# only import available files, as this allows to filter the files for tests.
|
||||
flake.clanModules = filterAttrs (_name: pathExists) {
|
||||
admin = ./admin;
|
||||
auto-upgrade = ./auto-upgrade;
|
||||
admin = ./admin;
|
||||
borgbackup = ./borgbackup;
|
||||
borgbackup-static = ./borgbackup-static;
|
||||
deltachat = ./deltachat;
|
||||
|
||||
@@ -10,7 +10,7 @@ After the system was installed/deployed the following command can be used to dis
|
||||
clan vars get [machine_name] root-password/root-password
|
||||
```
|
||||
|
||||
See also: [Vars](../../manual/vars-backend.md)
|
||||
See also: [Vars](../../guides/vars-backend.md)
|
||||
|
||||
To regenerate the password run:
|
||||
```
|
||||
|
||||
@@ -16,7 +16,7 @@ After the system was installed/deployed the following command can be used to dis
|
||||
clan vars get [machine_name] root-password/root-password
|
||||
```
|
||||
|
||||
See also: [Vars](../../manual/vars-backend.md)
|
||||
See also: [Vars](../../guides/vars-backend.md)
|
||||
|
||||
To regenerate the password run:
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
description = "Configures Wifi networks to join"
|
||||
features = [ "inventory", "experimental" ]
|
||||
features = [ "inventory", "experimental", "deprecated" ]
|
||||
categories = [ "Network", "System" ]
|
||||
---
|
||||
|
||||
|
||||
@@ -7,9 +7,18 @@
|
||||
|
||||
let
|
||||
cfg = config.clan.wifi;
|
||||
secret_path =
|
||||
|
||||
inherit (lib)
|
||||
concatMapAttrsStringSep
|
||||
flip
|
||||
mapAttrs
|
||||
;
|
||||
|
||||
password_path =
|
||||
network_name: config.clan.core.vars.generators."iwd.${network_name}".files.password.path;
|
||||
|
||||
ssid_path = network_name: config.clan.core.vars.generators."iwd.${network_name}".files.ssid.path;
|
||||
|
||||
secret_generator = name: value: {
|
||||
name = "iwd.${name}";
|
||||
value = {
|
||||
@@ -49,61 +58,53 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
(lib.mkIf (cfg.networks != { }) {
|
||||
config = lib.mkIf (cfg.networks != { }) {
|
||||
|
||||
clan.core.vars.generators = lib.mapAttrs' secret_generator cfg.networks;
|
||||
clan.core.vars.generators = lib.mapAttrs' secret_generator cfg.networks;
|
||||
|
||||
systemd.services.iwd.partOf = [ "nixos-activation.service" ];
|
||||
networking.networkmanager.enable = true;
|
||||
|
||||
/*
|
||||
script that generates iwd config files inside /var/lib/iwd/clan and symlinks
|
||||
them to /var/lib/iwd.
|
||||
*/
|
||||
systemd.services.iwd.serviceConfig.ExecStartPre = pkgs.writeShellScript "clan-iwd-setup" ''
|
||||
set -e
|
||||
networking.networkmanager.ensureProfiles.environmentFiles = [
|
||||
"/run/secrets/NetworkManager/wifi-secrets"
|
||||
];
|
||||
|
||||
rm -rf /var/lib/iwd/clan
|
||||
mkdir -p /var/lib/iwd/clan
|
||||
networking.networkmanager.ensureProfiles.profiles = flip mapAttrs cfg.networks (
|
||||
name: _network: {
|
||||
connection.id = "$ssid_${name}";
|
||||
connection.type = "wifi";
|
||||
wifi.mode = "infrastructure";
|
||||
wifi.ssid = "$ssid_${name}";
|
||||
wifi-security.psk = "$pw_${name}";
|
||||
wifi-security.key-mgmt = "wpa-psk";
|
||||
}
|
||||
);
|
||||
|
||||
# remove all existing symlinks in /var/lib/iwd
|
||||
${pkgs.findutils}/bin/find /var/lib/iwd -type l -exec rm {} \;
|
||||
# service to generate the environment file containing all secrets, as
|
||||
# expected by the nixos NetworkManager-ensure-profile service
|
||||
systemd.services.NetworkManager-setup-secrets = {
|
||||
description = "Generate wifi secrets for NetworkManager";
|
||||
requiredBy = [ "NetworkManager-ensure-profiles.service" ];
|
||||
partOf = [ "NetworkManager-ensure-profiles.service" ];
|
||||
before = [ "NetworkManager-ensure-profiles.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = pkgs.writeShellScript "wifi-secrets" ''
|
||||
set -euo pipefail
|
||||
|
||||
${toString (
|
||||
lib.mapAttrsToList (name: network: ''
|
||||
passwd=$(cat "${secret_path name}")
|
||||
ssid=$(cat "${ssid_path name}")
|
||||
echo "
|
||||
[Settings]
|
||||
autoConnect=${if network.autoConnect then "true" else "false"}
|
||||
[Security]
|
||||
Passphrase=$passwd
|
||||
" > "/var/lib/iwd/clan/$ssid.psk"
|
||||
'') cfg.networks
|
||||
)}
|
||||
env_file=/run/secrets/NetworkManager/wifi-secrets
|
||||
mkdir -p $(dirname "$env_file")
|
||||
: > "$env_file"
|
||||
|
||||
# link all files in /var/lib/iwd/clan to /var/lib/iwd
|
||||
${pkgs.findutils}/bin/find /var/lib/iwd/clan -type f -exec ln -s {} /var/lib/iwd \;
|
||||
'';
|
||||
})
|
||||
{
|
||||
# disable wpa supplicant
|
||||
networking.wireless.enable = false;
|
||||
|
||||
# Set the network manager backend to iwd
|
||||
networking.networkmanager.wifi.backend = "iwd";
|
||||
|
||||
# Use iwd instead of wpa_supplicant. It has a user friendly CLI
|
||||
networking.wireless.iwd = {
|
||||
enable = true;
|
||||
settings = {
|
||||
Network = {
|
||||
EnableIPv6 = true;
|
||||
RoutePriorityOffset = 300;
|
||||
};
|
||||
Settings.autoConnect = true;
|
||||
};
|
||||
# Generate the secrets file
|
||||
echo "Generating wifi secrets file: $env_file"
|
||||
${flip (concatMapAttrsStringSep "\n") cfg.networks (
|
||||
name: _network: ''
|
||||
echo "ssid_${name}=\"$(cat "${ssid_path name}")\"" >> /run/secrets/NetworkManager/wifi-secrets
|
||||
echo "pw_${name}=\"$(cat "${password_path name}")\"" >> /run/secrets/NetworkManager/wifi-secrets
|
||||
''
|
||||
)}
|
||||
'';
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
39
clanServices/admin/default.nix
Normal file
39
clanServices/admin/default.nix
Normal file
@@ -0,0 +1,39 @@
|
||||
{ ... }:
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/admin";
|
||||
manifest.description = "Convenient Administration for the Clan App";
|
||||
manifest.categories = [ "Utility" ];
|
||||
|
||||
roles.default = {
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
options.allowedKeys = lib.mkOption {
|
||||
default = { };
|
||||
type = lib.types.attrsOf lib.types.str;
|
||||
description = "The allowed public keys for ssh access to the admin user";
|
||||
example = {
|
||||
"key_1" = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD...";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
perInstance =
|
||||
{ settings, ... }:
|
||||
{
|
||||
nixosModule =
|
||||
{ ... }:
|
||||
{
|
||||
|
||||
imports = [
|
||||
../../clanModules/sshd
|
||||
../../clanModules/root-password
|
||||
];
|
||||
|
||||
users.users.root.openssh.authorizedKeys.keys = builtins.attrValues settings.allowedKeys;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
6
clanServices/admin/flake-module.nix
Normal file
6
clanServices/admin/flake-module.nix
Normal file
@@ -0,0 +1,6 @@
|
||||
{ lib, ... }:
|
||||
{
|
||||
clan.modules = {
|
||||
admin = lib.modules.importApply ./default.nix { };
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./admin/flake-module.nix
|
||||
./hello-world/flake-module.nix
|
||||
./wifi/flake-module.nix
|
||||
];
|
||||
}
|
||||
|
||||
@@ -3,8 +3,19 @@
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/hello-word";
|
||||
manifest.description = "This is a test";
|
||||
|
||||
roles.peer = { };
|
||||
roles.peer = {
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
options.foo = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
# default = "";
|
||||
description = "Some option";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
perMachine =
|
||||
{ machine, ... }:
|
||||
|
||||
@@ -10,9 +10,6 @@ let
|
||||
};
|
||||
in
|
||||
{
|
||||
clan.inventory.modules = {
|
||||
hello-world = module;
|
||||
};
|
||||
clan.modules = {
|
||||
hello-world = module;
|
||||
};
|
||||
@@ -50,6 +47,7 @@ in
|
||||
hello-service = import ./tests/vm/default.nix {
|
||||
inherit module;
|
||||
inherit self inputs pkgs;
|
||||
# clanLib is exposed from inputs.clan-core
|
||||
clanLib = self.clanLib;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ let
|
||||
};
|
||||
|
||||
# Register the module for the test
|
||||
inventory.modules.hello-world = module;
|
||||
modules.hello-world = module;
|
||||
|
||||
# Use the module in the test
|
||||
inventory.instances = {
|
||||
|
||||
@@ -14,6 +14,9 @@ clanLib.test.makeTestClan {
|
||||
|
||||
clan = {
|
||||
directory = ./.;
|
||||
modules = {
|
||||
hello-service = module;
|
||||
};
|
||||
inventory = {
|
||||
machines.peer1 = { };
|
||||
|
||||
@@ -21,10 +24,6 @@ clanLib.test.makeTestClan {
|
||||
module.name = "hello-service";
|
||||
roles.peer.machines.peer1 = { };
|
||||
};
|
||||
|
||||
modules = {
|
||||
hello-service = module;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
110
clanServices/wifi/default.nix
Normal file
110
clanServices/wifi/default.nix
Normal file
@@ -0,0 +1,110 @@
|
||||
{ packages }:
|
||||
{ lib, ... }:
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "wifi";
|
||||
|
||||
roles.default = {
|
||||
interface = {
|
||||
options.networks = lib.mkOption {
|
||||
visible = false;
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule (
|
||||
{ ... }:
|
||||
{
|
||||
options = {
|
||||
enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Enable this wifi network";
|
||||
};
|
||||
autoConnect = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = "Automatically try to join this wifi network";
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
default = { };
|
||||
description = "Wifi networks to predefine";
|
||||
};
|
||||
|
||||
};
|
||||
perInstance =
|
||||
{ settings, ... }:
|
||||
{
|
||||
nixosModule =
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
secret_path =
|
||||
network_name: config.clan.core.vars.generators."iwd.${network_name}".files.password.path;
|
||||
ssid_path = network_name: config.clan.core.vars.generators."iwd.${network_name}".files.ssid.path;
|
||||
secret_generator = name: value: {
|
||||
name = "iwd.${name}";
|
||||
value = {
|
||||
prompts.ssid.type = "line";
|
||||
prompts.ssid.persist = true;
|
||||
prompts.password.type = "hidden";
|
||||
prompts.password.persist = true;
|
||||
share = true;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
clan.core.vars.generators = lib.mapAttrs' secret_generator settings.networks;
|
||||
|
||||
systemd.services.iwd.partOf = [ "nixos-activation.service" ];
|
||||
|
||||
/*
|
||||
script that generates iwd config files inside /var/lib/iwd/clan and symlinks
|
||||
them to /var/lib/iwd.
|
||||
*/
|
||||
systemd.services.iwd.serviceConfig.ExecStartPre = pkgs.writeShellScript "clan-iwd-setup" ''
|
||||
set -e
|
||||
|
||||
rm -rf /var/lib/iwd/clan
|
||||
mkdir -p /var/lib/iwd/clan
|
||||
|
||||
# remove all existing symlinks in /var/lib/iwd
|
||||
${pkgs.findutils}/bin/find /var/lib/iwd -type l -exec rm {} \;
|
||||
|
||||
${toString (
|
||||
lib.mapAttrsToList (name: network: ''
|
||||
passwd=$(cat "${secret_path name}")
|
||||
ssid=$(cat "${ssid_path name}")
|
||||
echo "
|
||||
[Settings]
|
||||
autoConnect=${if network.autoConnect then "true" else "false"}
|
||||
[Security]
|
||||
Passphrase=$passwd
|
||||
" > "/var/lib/iwd/clan/$ssid.psk"
|
||||
'') settings.networks
|
||||
)}
|
||||
|
||||
# link all files in /var/lib/iwd/clan to /var/lib/iwd
|
||||
${pkgs.findutils}/bin/find /var/lib/iwd/clan -type f -exec ln -s {} /var/lib/iwd \;
|
||||
'';
|
||||
# disable wpa supplicant
|
||||
networking.wireless.enable = false;
|
||||
|
||||
# Set the network manager backend to iwd
|
||||
networking.networkmanager.wifi.backend = "iwd";
|
||||
|
||||
# Use iwd instead of wpa_supplicant. It has a user friendly CLI
|
||||
networking.wireless.iwd = {
|
||||
enable = true;
|
||||
settings = {
|
||||
Network = {
|
||||
EnableIPv6 = true;
|
||||
RoutePriorityOffset = 300;
|
||||
};
|
||||
Settings.autoConnect = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
36
clanServices/wifi/flake-module.nix
Normal file
36
clanServices/wifi/flake-module.nix
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
self,
|
||||
inputs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
module = lib.modules.importApply ./default.nix {
|
||||
inherit (self) packages;
|
||||
};
|
||||
in
|
||||
{
|
||||
clan.modules = {
|
||||
wifi = module;
|
||||
};
|
||||
perSystem =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
/**
|
||||
1. Prepare the test vars
|
||||
nix run .#generate-test-vars -- clanServices/hello-world/tests/vm hello-service
|
||||
|
||||
2. To run the test
|
||||
nix build .#checks.x86_64-linux.hello-service
|
||||
*/
|
||||
checks =
|
||||
# Currently we don't support nixos-integration tests on darwin
|
||||
lib.optionalAttrs (pkgs.stdenv.isLinux) {
|
||||
wifi-service = import ./tests/vm/default.nix {
|
||||
inherit module;
|
||||
inherit self inputs pkgs;
|
||||
clanLib = self.clanLib;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
58
clanServices/wifi/tests/eval-tests.nix
Normal file
58
clanServices/wifi/tests/eval-tests.nix
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
module,
|
||||
clanLib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
testFlake = clanLib.buildClan {
|
||||
# Point to the folder of the module
|
||||
# TODO: make this optional in buildClan
|
||||
directory = ./..;
|
||||
|
||||
# Create some test machines
|
||||
machines.jon = {
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
};
|
||||
machines.sara = {
|
||||
nixpkgs.hostPlatform = "x86_64-linux";
|
||||
};
|
||||
|
||||
# Register the module for the test
|
||||
inventory.modules.wifi = module;
|
||||
|
||||
# Use the module in the test
|
||||
inventory.instances = {
|
||||
"default" = {
|
||||
module.name = "wifi";
|
||||
roles.default.tags.all = { };
|
||||
roles.default.settings.networks.one = { };
|
||||
roles.default.settings.networks.two = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
# NOTE:
|
||||
# If you wonder why 'self-zerotier-redux':
|
||||
# A local module has prefix 'self', otherwise it is the name of the 'input'
|
||||
# The rest is the name of the service as in the instance 'module.name';
|
||||
#
|
||||
# -> ${module.input}-${module.name}
|
||||
# In this case it is 'self-zerotier-redux'
|
||||
# This is usually only used internally, but we can use it to test the evaluation of service module in isolation
|
||||
# evaluatedService =
|
||||
# testFlake.clanInternals.inventoryClass.distributedServices.importedModulesEvaluated.self-zerotier-redux.config;
|
||||
in
|
||||
{
|
||||
test_simple = {
|
||||
inherit testFlake;
|
||||
|
||||
expr =
|
||||
testFlake.clanInternals.inventoryClass.distributedServices.importedModulesEvaluated.self-wifi.config;
|
||||
expected = 1;
|
||||
|
||||
# expr = {
|
||||
# };
|
||||
# expected = {
|
||||
#
|
||||
# };
|
||||
};
|
||||
}
|
||||
43
clanServices/wifi/tests/vm/default.nix
Normal file
43
clanServices/wifi/tests/vm/default.nix
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
pkgs,
|
||||
self,
|
||||
clanLib,
|
||||
module,
|
||||
...
|
||||
}:
|
||||
clanLib.test.makeTestClan {
|
||||
inherit pkgs self;
|
||||
useContainers = false;
|
||||
nixosTest = (
|
||||
{ ... }:
|
||||
{
|
||||
name = "wifi";
|
||||
|
||||
clan = {
|
||||
directory = ./.;
|
||||
inventory = {
|
||||
modules."@clan/wifi" = module;
|
||||
|
||||
machines.test = { };
|
||||
|
||||
instances = {
|
||||
wg-test-one = {
|
||||
module.name = "@clan/wifi";
|
||||
|
||||
roles.default.machines = {
|
||||
test.settings.networks.one = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
test.wait_for_unit("iwd.service")
|
||||
psk = test.succeed("cat /var/lib/iwd/ssid-one.psk")
|
||||
assert "password-eins" in psk, "Password is incorrect"
|
||||
'';
|
||||
}
|
||||
);
|
||||
}
|
||||
6
clanServices/wifi/tests/vm/sops/machines/test/key.json
Executable file
6
clanServices/wifi/tests/vm/sops/machines/test/key.json
Executable file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"publickey": "age1aex07l3uafv5hdr0h2707jgfsxcu7yhlc7glw3qu26xzn3m9nazsu47jzs",
|
||||
"type": "age"
|
||||
}
|
||||
]
|
||||
15
clanServices/wifi/tests/vm/sops/secrets/test-age.key/secret
Normal file
15
clanServices/wifi/tests/vm/sops/secrets/test-age.key/secret
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:l5kJU4j1D/4TcPT0Ea0c1X3FWRbHCLCnWy22xeEWseBKnd5R8cEPAYflw+xqGNKpDpQOb0K5XCfA5+CFFXyl0oXEnmnIDDCmtqk=,iv:6cF445KqSJiaTfQ+eNqKH4dAFiIaqdSqt1alF80GpFU=,tag:4Fz+MtxiLmV31Nn6NUVAzg==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsOVlvZVdoWDBpNEdRb0U1\nRVRpS0xhaElYVUg1OUJWUlF5a3A1ZWUxeGxJClVjNDNsM2xXWDhsMktYOU1pdUZD\nY3VFeVowbDFmR2dFY2NUc3pEOGFUUU0KLS0tIE9pT0xZMFdwRU5VekNNWmpKQWNh\naTk2eGhGL3QvSlBLOUpJdFJaMnUzVkkKQT3KVYLG3HD9cbLzG46wI5ipxzjLfM8W\nwHezTfnVL9UUztHapdqu2uM2cZjjdGcsacvOCacfxLWzE+7Uk0RMGQ==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-05T10:34:16Z",
|
||||
"mac": "ENC[AES256_GCM,data:VfRQvyeeEAhQzTcG//spQm4VDYKY/aldO46CWx6QhW945H/2PP1OyNehT2PjjHArVy6HzZeLMb1E2tQHEemPvi4F4jjAqeA+SgnwNKEClYcIF021eaPZDeWbxo8MxQcy0QbRH6Aimihyr4GjAb+cYBm43DAgWCG9q9kHKCk7dts=,iv:eyWHtaGAB+/2Vwkq4tYKei60LGSatRM4FSOI0YUddyY=,tag:ElOBNQ5Hf1sTefzYYN3JoA==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
1
clanServices/wifi/tests/vm/sops/secrets/test-age.key/users/admin
Symbolic link
1
clanServices/wifi/tests/vm/sops/secrets/test-age.key/users/admin
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../users/admin
|
||||
4
clanServices/wifi/tests/vm/sops/users/admin/key.json
Normal file
4
clanServices/wifi/tests/vm/sops/users/admin/key.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"type": "age"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../sops/machines/test
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:C0x458T590iWSMBl6w==,iv:K0WawWV9fJbyFg9cD3H0htMAvxSqxVp2spdzNcVUSuI=,tag:Hb1+WGgM12UItrqjx04UdA==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1aex07l3uafv5hdr0h2707jgfsxcu7yhlc7glw3qu26xzn3m9nazsu47jzs",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxOEdHODRlRDFxRVMxSTNG\nazhkaXA3cGhBdUtqNExkZFFrcHlLOFQ1dFZNCndkc0tzZ29sYlB6KzBBZXpWN1hT\nRUl6c0dUY3NXNUVSd1Ixdk5UdkY5cE0KLS0tIDZCeldabUtieUtjUDJsV1BvMEhV\nNmQwcW5pZmx2cjFWWlRJMmY5b1JjQmsKqlFegGpY3zqHXa/qlSKEIQQ4nY/NPwL+\n3NzE2Voon6YLhrYNJAv8YndM5GMiIWQQim3suqdcq5KIRQshhO1x8g==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0NUFKdGIrM2NOWUx6M0Fl\naUp0K3Y5cEVON1haMkpiQ0kydXVvcWQ1WVVJCnl4bTN5Nk5kdjJ6ejB0NkE2aFJJ\nZlJjQzk4MjB1TmlNT0d6VmVScWVCcHMKLS0tIE1mWi9LMkNwc0pGZ3grTWdCTENC\nb1ZOYk41YStYYWJad2hHN2t5ODNQcjgKKP82jzHVDp53eRXg7yX6JgrWtJwcGbWj\nKCacNw6rRpdLOJDRea2uW3kHEVJz1L+T7EALRK9o59DxJfiQvjC/yw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-05T10:35:08Z",
|
||||
"mac": "ENC[AES256_GCM,data:XseZIImMQW4D9YhLmDbMkQiJqX+hQNEMAo8kTUgFE0AQy+72A6IT6kI5C7NnlvRAQmhc2KxtbooFLlYW2OR3cb7M0xCEZPYv76l/j6HXhYpBxuWbJsSz0htkm09OYFfYIpg4AEHXOt3TBJyzK9BWD5RX0Jwgp5x8ZxYxZKP1Zks=,iv:/5ZQ+Bp3mzcfe/OsHn4nToC412fNpiatvrdB/JBhIhM=,tag:I4duVl5DcTGABhBg2nTLnQ==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../sops/users/admin
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../sops/machines/test
|
||||
19
clanServices/wifi/tests/vm/vars/shared/iwd.one/ssid/secret
Normal file
19
clanServices/wifi/tests/vm/vars/shared/iwd.one/ssid/secret
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"data": "ENC[AES256_GCM,data:YisL0uMSKSU=,iv:TWLcznbWfuIPrtYzIqhE+iqa+6jSLatSV51nxapwPow=,tag:fThlc6jY12C/cOFGXbStlg==,type:str]",
|
||||
"sops": {
|
||||
"age": [
|
||||
{
|
||||
"recipient": "age1aex07l3uafv5hdr0h2707jgfsxcu7yhlc7glw3qu26xzn3m9nazsu47jzs",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBudldzU1YrczMxK2RmZHlG\nTTdDZkVCbGhWek43TmRUTTc2NzA1VFN6VmhJCnpEencxSnZVOG83QVY2R1pRek0v\naHNuMzBCWVNGTGNrME8xd0hGaEx1SmcKLS0tIHRRN1BJZE5SZWFXYTlNSEVvekxI\nYlRoWHJ1UFBoSWxJQmd5ZWVIQVNjT0EKhO1ax3q+cIF2YyXJCGg00Zwl4A+ae9gF\n0Ta3aZy6QnHrzOcMGA2HYiefoVvPDlU5zkxjwvxtOZt6TmulumpzBw==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
},
|
||||
{
|
||||
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJYUV3VXByVWFRaVNWQ0pt\ncG81RWFVbFh5SWFKS09xZlV0UGNaZWtDd1U4CmNYaUk3djRhYlBrSnllekk5RGtz\nUU95N1loS0dwbkRvaCt4NW55Rk12V1UKLS0tIDl6QjIxRC9BNmJBYi9BdGhybFcy\nTzVLdG9WcnkwbDZoaWJUTzM1b1dmbmsKzdOOKpZAAJwFc525IZ69RbmAMk79Pt6W\nzGs4qf0xBocYzF0G3kmt8ki8Nvvh5IWvpkIb+NTaQZAPhKNcSmBlzA==\n-----END AGE ENCRYPTED FILE-----\n"
|
||||
}
|
||||
],
|
||||
"lastmodified": "2025-05-05T10:35:08Z",
|
||||
"mac": "ENC[AES256_GCM,data:rv26UZhuyEouasJF1mY71df+7izOCw7dCqG/KjuUogtzt102NAawu4q7jF7j3Xi09cZKTmGllZD6L+R/8cpQ4l6R1JlBI6nbjKPFc4UNMNizW5U4KKHq0ApG9CVWiCDOdlH4Fiqa6vkZQw2dHyTAg5eHYiHtg7wuoiWlF4XnkP8=,iv:/yq+JlMCc1EQkj05Y1D6V4f64Whh+US9YEKOSb08xmI=,tag:jMYqXiDChLYnv3P1+bi3Bw==,type:str]",
|
||||
"unencrypted_suffix": "_unencrypted",
|
||||
"version": "3.10.2"
|
||||
}
|
||||
}
|
||||
1
clanServices/wifi/tests/vm/vars/shared/iwd.one/ssid/users/admin
Symbolic link
1
clanServices/wifi/tests/vm/vars/shared/iwd.one/ssid/users/admin
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../sops/users/admin
|
||||
@@ -1 +0,0 @@
|
||||
see [architecture-decision-record](https://github.com/joelparkerhenderson/architecture-decision-record)
|
||||
@@ -1,4 +1,4 @@
|
||||
{ ... }:
|
||||
{ inputs, ... }:
|
||||
{
|
||||
perSystem =
|
||||
{
|
||||
@@ -39,6 +39,10 @@
|
||||
shellHook = ''
|
||||
echo -e "${ansiEscapes.green}switch to another dev-shell using: select-shell${ansiEscapes.reset}"
|
||||
export PRJ_ROOT=$(git rev-parse --show-toplevel)
|
||||
|
||||
# vendoring / needed for impure tests
|
||||
ln -sfT ${self'.packages.clan-cli.nixpkgs} "$PRJ_ROOT/pkgs/clan-cli/clan_lib/nixpkgs"
|
||||
ln -sfT ${inputs.nix-select} "$PRJ_ROOT/pkgs/clan-cli/clan_lib/select"
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
105
docs/mkdocs.yml
105
docs/mkdocs.yml
@@ -22,7 +22,11 @@ markdown_extensions:
|
||||
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
||||
- pymdownx.tasklist:
|
||||
custom_checkbox: true
|
||||
- pymdownx.superfences
|
||||
- pymdownx.superfences:
|
||||
custom_fences:
|
||||
- name: mermaid
|
||||
class: mermaid
|
||||
format: !!python/name:pymdownx.superfences.fence_code_format
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
- pymdownx.details
|
||||
@@ -42,43 +46,52 @@ exclude_docs: |
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Getting Started:
|
||||
- Setup Clan: getting-started/index.md
|
||||
- Create Installer: getting-started/installer.md
|
||||
- Add Machines: getting-started/configure.md
|
||||
- Secrets & Facts: getting-started/secrets.md
|
||||
- Deploy Machine: getting-started/deploy.md
|
||||
- Continuous Integration: getting-started/check.md
|
||||
- Guides:
|
||||
- Disk Encryption: getting-started/disk-encryption.md
|
||||
- Mesh VPN: getting-started/mesh-vpn.md
|
||||
- Backup & Restore: getting-started/backups.md
|
||||
- Vars Backend: manual/vars-backend.md
|
||||
- Facts Backend: manual/secrets.md
|
||||
- Autoincludes: manual/adding-machines.md
|
||||
- Getting Started:
|
||||
- Creating Your First Clan: guides/getting-started/index.md
|
||||
- Create Installer: guides/getting-started/installer.md
|
||||
- Add Machines: guides/getting-started/add-machines.md
|
||||
- Secrets & Facts: guides/getting-started/secrets.md
|
||||
- Deploy Machine: guides/getting-started/deploy.md
|
||||
- Continuous Integration: guides/getting-started/check.md
|
||||
- clanServices: guides/clanServices.md
|
||||
- Disk Encryption: guides/disk-encryption.md
|
||||
- Mesh VPN: guides/mesh-vpn.md
|
||||
- Backup & Restore: guides/backups.md
|
||||
- Vars Backend: guides/vars-backend.md
|
||||
- Facts Backend: guides/secrets.md
|
||||
- Adding more machines: guides/more-machines.md
|
||||
- Inventory:
|
||||
- Inventory: manual/inventory.md
|
||||
- Instances: manual/distributed-services.md
|
||||
- Secure Boot: manual/secure-boot.md
|
||||
- Flake-parts: manual/flake-parts.md
|
||||
- Inventory: guides/inventory.md
|
||||
- Secure Boot: guides/secure-boot.md
|
||||
- Flake-parts: guides/flake-parts.md
|
||||
- Authoring:
|
||||
- clan.service: authoring/clanServices/index.md
|
||||
- Disk Templates: authoring/templates/disk/disko-templates.md
|
||||
- clanModules: authoring/legacyModules/index.md
|
||||
- clanService: guides/authoring/clanServices/index.md
|
||||
- Disk Template: guides/authoring/templates/disk/disko-templates.md
|
||||
- clanModule: guides/authoring/clanModules/index.md
|
||||
- Contributing:
|
||||
- Contribute: contributing/contribute.md
|
||||
- Debugging: contributing/debugging.md
|
||||
- Testing: contributing/testing.md
|
||||
- Repo Layout: manual/repo-layout.md
|
||||
- Migrate existing Flakes: manual/migration-guide.md
|
||||
- Contribute: guides/contributing/CONTRIBUTING.md
|
||||
- Debugging: guides/contributing/debugging.md
|
||||
- Testing: guides/contributing/testing.md
|
||||
- Migrations:
|
||||
- Migrate existing Flakes: guides/migrations/migration-guide.md
|
||||
- Migrate inventory Services: guides/migrations/migrate-inventory-services.md
|
||||
- Facts Vars Migration: guides/migrations/migration-facts-vars.md
|
||||
- macOS: guides/macos.md
|
||||
- Reference:
|
||||
- Overview: reference/index.md
|
||||
- Clan Services:
|
||||
- Overview: reference/clanServices/index.md
|
||||
- reference/clanServices/admin.md
|
||||
- reference/clanServices/hello-world.md
|
||||
- reference/clanServices/wifi.md
|
||||
- Clan Modules:
|
||||
- Overview:
|
||||
- reference/clanModules/index.md
|
||||
- reference/clanModules/frontmatter/index.md
|
||||
# This is the module overview and should stay at the top
|
||||
- Overview: reference/clanModules/index.md
|
||||
- Frontmatter Format: reference/clanModules/frontmatter/index.md
|
||||
- reference/clanModules/frontmatter/index.md
|
||||
# TODO: display the docs of the clan.service modules
|
||||
- reference/clanModules/admin.md
|
||||
# This is the module overview and should stay at the top
|
||||
- reference/clanModules/borgbackup-static.md
|
||||
- reference/clanModules/data-mesher.md
|
||||
- reference/clanModules/borgbackup.md
|
||||
@@ -119,7 +132,7 @@ nav:
|
||||
- reference/clanModules/zerotier.md
|
||||
- reference/clanModules/zt-tcp-relay.md
|
||||
- CLI:
|
||||
- reference/cli/index.md
|
||||
- Overview: reference/cli/index.md
|
||||
|
||||
- reference/cli/backups.md
|
||||
- reference/cli/facts.md
|
||||
@@ -133,20 +146,30 @@ nav:
|
||||
- reference/cli/state.md
|
||||
- reference/cli/vars.md
|
||||
- reference/cli/vms.md
|
||||
- Clan Core:
|
||||
- reference/clan-core/index.md
|
||||
- NixOS Modules:
|
||||
- clan.core:
|
||||
- Overview: reference/clan.core/index.md
|
||||
|
||||
- reference/clan-core/backups.md
|
||||
- reference/clan-core/deployment.md
|
||||
- reference/clan-core/facts.md
|
||||
- reference/clan-core/networking.md
|
||||
- reference/clan-core/settings.md
|
||||
- reference/clan-core/sops.md
|
||||
- reference/clan-core/state.md
|
||||
- reference/clan-core/vars.md
|
||||
- reference/clan.core/backups.md
|
||||
- reference/clan.core/deployment.md
|
||||
- reference/clan.core/facts.md
|
||||
- reference/clan.core/networking.md
|
||||
- reference/clan.core/settings.md
|
||||
- reference/clan.core/sops.md
|
||||
- reference/clan.core/state.md
|
||||
- reference/clan.core/vars.md
|
||||
- Nix API:
|
||||
- buildClan: reference/nix-api/buildclan.md
|
||||
- Inventory: reference/nix-api/inventory.md
|
||||
- Glossary: reference/glossary.md
|
||||
- Decisions:
|
||||
- Architecture Decisions: decisions/README.md
|
||||
- 01-clanModules: decisions/01-ClanModules.md
|
||||
- 02-clan-api: decisions/02-clan-api.md
|
||||
- 03-adr-numbering-process: decisions/03-adr-numbering-process.md
|
||||
- 04-fetching-nix-from-python: decisions/04-fetching-nix-from-python.md
|
||||
- 05-deployment-parameters: decisions/05-deployment-parameters.md
|
||||
- Template: decisions/_template.md
|
||||
|
||||
docs_dir: site
|
||||
site_dir: out
|
||||
|
||||
@@ -45,7 +45,7 @@ pkgs.stdenv.mkDerivation {
|
||||
ln -snf ${fira-code}/share/fonts/truetype/FiraCode-VF.ttf ./site/static/
|
||||
|
||||
# Copy icons into place
|
||||
cp -af ../pkgs/webview-ui/app/icons ./site/static/
|
||||
cp -af ../pkgs/clan-app/ui/icons ./site/static/
|
||||
'';
|
||||
|
||||
buildPhase = ''
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
# Options available when imported via ` inventory.${moduleName}....${rolesName} `
|
||||
clanModulesViaRoles = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModulesViaRoles);
|
||||
|
||||
# clan service options
|
||||
clanModulesViaService = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModulesViaService);
|
||||
|
||||
# Simply evaluated options (JSON)
|
||||
renderOptions =
|
||||
pkgs.runCommand "render-options"
|
||||
@@ -85,6 +88,7 @@
|
||||
export CLAN_CORE_DOCS=${jsonDocs.clanCore}/share/doc/nixos/options.json
|
||||
# A file that contains the links to all clanModule docs
|
||||
export CLAN_MODULES_VIA_ROLES=${clanModulesViaRoles}
|
||||
export CLAN_MODULES_VIA_SERVICE=${clanModulesViaService}
|
||||
export CLAN_MODULES_VIA_NIX=${clanModulesViaNix}
|
||||
# Frontmatter format for clanModules
|
||||
export CLAN_MODULES_FRONTMATTER_DOCS=${clanModulesFrontmatter}/share/doc/nixos/options.json
|
||||
@@ -100,7 +104,12 @@
|
||||
in
|
||||
{
|
||||
legacyPackages = {
|
||||
inherit jsonDocs clanModulesViaNix clanModulesViaRoles;
|
||||
inherit
|
||||
jsonDocs
|
||||
clanModulesViaNix
|
||||
clanModulesViaRoles
|
||||
clanModulesViaService
|
||||
;
|
||||
};
|
||||
devShells.docs = pkgs.callPackage ./shell.nix {
|
||||
inherit (self'.packages) docs clan-cli-docs inventory-api-docs;
|
||||
|
||||
@@ -36,6 +36,33 @@
|
||||
) rolesOptions
|
||||
) modulesRolesOptions;
|
||||
|
||||
# Test with:
|
||||
# nix build .\#legacyPackages.x86_64-linux.clanModulesViaService
|
||||
clanModulesViaService = lib.mapAttrs (
|
||||
_moduleName: moduleValue:
|
||||
let
|
||||
evaluatedService = clan-core.clanLib.inventory.evalClanService {
|
||||
modules = [ moduleValue ];
|
||||
prefix = [ ];
|
||||
};
|
||||
in
|
||||
{
|
||||
roles = lib.mapAttrs (
|
||||
_roleName: role:
|
||||
|
||||
(nixosOptionsDoc {
|
||||
transformOptions =
|
||||
opt: if lib.strings.hasPrefix "_" opt.name then opt // { visible = false; } else opt;
|
||||
options = (lib.evalModules { modules = [ role.interface ]; }).options;
|
||||
warningsAreErrors = true;
|
||||
}).optionsJSON
|
||||
) evaluatedService.config.roles;
|
||||
|
||||
manifest = evaluatedService.config.manifest;
|
||||
|
||||
}
|
||||
) clan-core.clan.modules;
|
||||
|
||||
clanCore =
|
||||
(nixosOptionsDoc {
|
||||
options =
|
||||
|
||||
@@ -29,8 +29,13 @@ from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from clan_cli.errors import ClanError
|
||||
from clan_lib.api.modules import Frontmatter, extract_frontmatter, get_roles
|
||||
from clan_lib.api.modules import (
|
||||
CategoryInfo,
|
||||
Frontmatter,
|
||||
extract_frontmatter,
|
||||
get_roles,
|
||||
)
|
||||
from clan_lib.errors import ClanError
|
||||
|
||||
# Get environment variables
|
||||
CLAN_CORE_PATH = Path(os.environ["CLAN_CORE_PATH"])
|
||||
@@ -44,6 +49,7 @@ CLAN_MODULES_VIA_NIX = os.environ.get("CLAN_MODULES_VIA_NIX")
|
||||
# Some modules can be imported via inventory
|
||||
CLAN_MODULES_VIA_ROLES = os.environ.get("CLAN_MODULES_VIA_ROLES")
|
||||
|
||||
CLAN_MODULES_VIA_SERVICE = os.environ.get("CLAN_MODULES_VIA_SERVICE")
|
||||
|
||||
OUT = os.environ.get("out")
|
||||
|
||||
@@ -58,7 +64,8 @@ def replace_store_path(text: str) -> tuple[str, str]:
|
||||
res = "https://git.clan.lol/clan/clan-core/src/branch/main/" + str(
|
||||
Path(*Path(text).parts[4:])
|
||||
)
|
||||
name = Path(res).name
|
||||
# name = Path(res).name
|
||||
name = str(Path(*Path(text).parts[4:]))
|
||||
return (res, name)
|
||||
|
||||
|
||||
@@ -149,8 +156,12 @@ def render_option(
|
||||
decls = option.get("declarations", [])
|
||||
if decls:
|
||||
source_path, name = replace_store_path(decls[0])
|
||||
|
||||
name = name.split(",")[0]
|
||||
source_path = source_path.split(",")[0]
|
||||
|
||||
res += f"""
|
||||
:simple-git: [{name}]({source_path})
|
||||
:simple-git: Declared in: [{name}]({source_path})
|
||||
"""
|
||||
res += "\n\n"
|
||||
|
||||
@@ -195,7 +206,12 @@ Some modules are considered 'low-level' or 'expert modules' and are not availabl
|
||||
"""
|
||||
|
||||
|
||||
clan_core_descr = """`clan.core` is always included in each machine `config`.
|
||||
clan_core_descr = """
|
||||
`clan.core` is always present in a clan machine
|
||||
|
||||
* It is a module of class **`nixos`**
|
||||
* Provides a set of common options for every machine (in addition to the NixOS options)
|
||||
|
||||
Your can customize your machines behavior with the configuration [options](#module-options) provided below.
|
||||
"""
|
||||
|
||||
@@ -221,7 +237,8 @@ def produce_clan_modules_frontmatter_docs() -> None:
|
||||
# header
|
||||
output = """# Frontmatter
|
||||
|
||||
Every clan module has a `frontmatter` section within its readme. It provides machine readable metadata about the module.
|
||||
Every clan module has a `frontmatter` section within its readme. It provides
|
||||
machine readable metadata about the module.
|
||||
|
||||
!!! example
|
||||
|
||||
@@ -246,7 +263,8 @@ Every clan module has a `frontmatter` section within its readme. It provides mac
|
||||
|
||||
output += """## Overview
|
||||
|
||||
This provides an overview of the available attributes of the `frontmatter` within the `README.md` of a clan module.
|
||||
This provides an overview of the available attributes of the `frontmatter`
|
||||
within the `README.md` of a clan module.
|
||||
|
||||
"""
|
||||
# for option_name, info in options.items():
|
||||
@@ -279,16 +297,28 @@ def produce_clan_core_docs() -> None:
|
||||
core_outputs: dict[str, str] = {}
|
||||
with CLAN_CORE_DOCS.open() as f:
|
||||
options: dict[str, dict[str, Any]] = json.load(f)
|
||||
module_name = "clan-core"
|
||||
module_name = "clan.core"
|
||||
|
||||
transform = {n.replace("clan.core.", ""): v for n, v in options.items()}
|
||||
split = split_options_by_root(transform)
|
||||
|
||||
# Prepopulate the index file header
|
||||
indexfile = f"{module_name}/index.md"
|
||||
core_outputs[indexfile] = (
|
||||
module_header(module_name) + clan_core_descr + options_head
|
||||
)
|
||||
core_outputs[indexfile] = module_header(module_name) + clan_core_descr
|
||||
|
||||
core_outputs[indexfile] += """!!! info "Submodules"\n"""
|
||||
|
||||
for submodule_name, split_options in split.items():
|
||||
root = options_to_tree(split_options, debug=True)
|
||||
module = root.suboptions[0]
|
||||
module_type = module.info.get("type")
|
||||
if module_type is not None and "submodule" not in module_type:
|
||||
continue
|
||||
core_outputs[indexfile] += (
|
||||
f" - [{submodule_name}](./{submodule_name}.md)\n"
|
||||
)
|
||||
|
||||
core_outputs[indexfile] += options_head
|
||||
|
||||
for submodule_name, split_options in split.items():
|
||||
outfile = f"{module_name}/{submodule_name}.md"
|
||||
@@ -304,7 +334,6 @@ def produce_clan_core_docs() -> None:
|
||||
print("type", module.info.get("type"))
|
||||
|
||||
module_type = module.info.get("type")
|
||||
|
||||
if module_type is not None and "submodule" not in module_type:
|
||||
outfile = indexfile
|
||||
init_level = 2
|
||||
@@ -331,7 +360,7 @@ def produce_clan_core_docs() -> None:
|
||||
|
||||
def render_roles(roles: list[str] | None, module_name: str) -> str:
|
||||
if roles:
|
||||
roles_list = "\n".join([f" - `{r}`" for r in roles])
|
||||
roles_list = "\n".join([f"- `{r}`" for r in roles])
|
||||
return (
|
||||
f"""
|
||||
### Roles
|
||||
@@ -341,17 +370,19 @@ This module can be used via predefined roles
|
||||
{roles_list}
|
||||
"""
|
||||
"""
|
||||
Every role has its own configuration options. Which are each listed below.
|
||||
Every role has its own configuration options, which are each listed below.
|
||||
|
||||
For more information, see the [inventory guide](../../manual/inventory.md).
|
||||
For more information, see the [inventory guide](../../guides/inventory.md).
|
||||
|
||||
??? Example
|
||||
For example the `admin` module adds the following options globally to all machines where it is used.
|
||||
|
||||
`clan.admin.allowedkeys`
|
||||
|
||||
This means there are two equivalent ways to set the `allowedkeys` option. Either via a nixos module or via the inventory interface.
|
||||
**But it is recommended to keep together `imports` and `config` to preserve locality of the module configuration.**
|
||||
This means there are two equivalent ways to set the `allowedkeys` option.
|
||||
Either via a nixos module or via the inventory interface.
|
||||
**But it is recommended to keep together `imports` and `config` to preserve
|
||||
locality of the module configuration.**
|
||||
|
||||
=== "Inventory"
|
||||
|
||||
@@ -383,20 +414,24 @@ For more information, see the [inventory guide](../../manual/inventory.md).
|
||||
return ""
|
||||
|
||||
|
||||
clan_modules_descr = """Clan modules are [NixOS modules](https://wiki.nixos.org/wiki/NixOS_modules) which have been enhanced with additional features provided by Clan, with certain option types restricted to enable configuration through a graphical interface.
|
||||
clan_modules_descr = """
|
||||
Clan modules are [NixOS modules](https://wiki.nixos.org/wiki/NixOS_modules)
|
||||
which have been enhanced with additional features provided by Clan, with
|
||||
certain option types restricted to enable configuration through a graphical
|
||||
interface.
|
||||
|
||||
!!! note "🔹"
|
||||
Modules with this indicator support the [inventory](../../manual/inventory.md) feature.
|
||||
Modules with this indicator support the [inventory](../../guides/inventory.md) feature.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def render_categories(categories: list[str], frontmatter: Frontmatter) -> str:
|
||||
cat_info = frontmatter.categories_info
|
||||
def render_categories(
|
||||
categories: list[str], categories_info: dict[str, CategoryInfo]
|
||||
) -> str:
|
||||
res = """<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;">"""
|
||||
for cat in categories:
|
||||
color = cat_info[cat]["color"]
|
||||
# description = cat_info[cat]["description"]
|
||||
color = categories_info[cat]["color"]
|
||||
res += f"""
|
||||
<div style="background-color: {color}; color: white; padding: 10px; border-radius: 20px; text-align: center;">
|
||||
{cat}
|
||||
@@ -406,6 +441,83 @@ def render_categories(categories: list[str], frontmatter: Frontmatter) -> str:
|
||||
return res
|
||||
|
||||
|
||||
def produce_clan_service_docs() -> None:
|
||||
if not CLAN_MODULES_VIA_SERVICE:
|
||||
msg = f"Environment variables are not set correctly: $CLAN_MODULES_VIA_SERVICE={CLAN_MODULES_VIA_SERVICE}"
|
||||
raise ClanError(msg)
|
||||
|
||||
if not CLAN_CORE_PATH:
|
||||
msg = f"Environment variables are not set correctly: $CLAN_CORE_PATH={CLAN_CORE_PATH}"
|
||||
raise ClanError(msg)
|
||||
|
||||
if not OUT:
|
||||
msg = f"Environment variables are not set correctly: $out={OUT}"
|
||||
raise ClanError(msg)
|
||||
|
||||
indexfile = Path(OUT) / "clanServices/index.md"
|
||||
indexfile.parent.mkdir(
|
||||
parents=True,
|
||||
exist_ok=True,
|
||||
)
|
||||
index = "# Clan Services\n\n"
|
||||
index += """
|
||||
**`clanServices`** are modular building blocks that simplify the configuration and orchestration of multi-host services.
|
||||
|
||||
Each `clanService`:
|
||||
|
||||
* Is a module of class **`clan.service`**
|
||||
* Can define **roles** (e.g., `client`, `server`)
|
||||
* Uses **`inventory.instances`** to configure where and how it is deployed
|
||||
* Replaces the legacy `clanModules` and `inventory.services` system altogether
|
||||
|
||||
!!! Note
|
||||
`clanServices` are part of Clan's next-generation service model and are intended to replace `clanModules`.
|
||||
|
||||
See [Migration Guide](../../guides/migrations/migrate-inventory-services.md) for help on migrating.
|
||||
|
||||
Learn how to use `clanServices` in practice in the [Using clanServices guide](../../guides/clanServices.md).
|
||||
"""
|
||||
|
||||
with indexfile.open("w") as of:
|
||||
of.write(index)
|
||||
|
||||
with Path(CLAN_MODULES_VIA_SERVICE).open() as f3:
|
||||
service_links: dict[str, dict[str, dict[str, Any]]] = json.load(f3)
|
||||
|
||||
for module_name, module_info in service_links.items():
|
||||
output = f"# {module_name}\n\n"
|
||||
# output += f"`clan.modules.{module_name}`\n"
|
||||
output += f"*{module_info['manifest']['description']}*\n"
|
||||
|
||||
fm = Frontmatter("")
|
||||
# output += "## Categories\n\n"
|
||||
output += render_categories(
|
||||
module_info["manifest"]["categories"], fm.categories_info
|
||||
)
|
||||
output += "\n---\n\n## Roles\n"
|
||||
|
||||
output += f"The {module_name} module has the following roles:\n\n"
|
||||
|
||||
for role_name, _ in module_info["roles"].items():
|
||||
output += f"- {role_name}\n"
|
||||
|
||||
for role_name, role_filename in module_info["roles"].items():
|
||||
output += print_options(
|
||||
role_filename,
|
||||
f"## Options for the `{role_name}` role",
|
||||
"This role has no configuration",
|
||||
replace_prefix=f"clan.{module_name}",
|
||||
)
|
||||
|
||||
outfile = Path(OUT) / f"clanServices/{module_name}.md"
|
||||
outfile.parent.mkdir(
|
||||
parents=True,
|
||||
exist_ok=True,
|
||||
)
|
||||
with outfile.open("w") as of:
|
||||
of.write(output)
|
||||
|
||||
|
||||
def produce_clan_modules_docs() -> None:
|
||||
if not CLAN_MODULES_VIA_NIX:
|
||||
msg = f"Environment variables are not set correctly: $CLAN_MODULES_VIA_NIX={CLAN_MODULES_VIA_NIX}"
|
||||
@@ -456,11 +568,27 @@ def produce_clan_modules_docs() -> None:
|
||||
|
||||
# 2. Description from README.md
|
||||
if frontmatter.description:
|
||||
output += f"**{frontmatter.description}**\n\n"
|
||||
output += f"*{frontmatter.description}*\n\n"
|
||||
|
||||
# 2. Deprecation note if the module is deprecated
|
||||
if "deprecated" in frontmatter.features:
|
||||
output += f"""
|
||||
!!! Warning "Deprecated"
|
||||
The `{module_name}` module is deprecated.*
|
||||
|
||||
Use [clanServices/{module_name}](../clanServices/{module_name}.md) instead
|
||||
"""
|
||||
else:
|
||||
output += f"""
|
||||
!!! Warning "Will be deprecated"
|
||||
The `{module_name}` module might eventually be migrated to 'clanServices'*
|
||||
|
||||
See: [clanServices](../../guides/clanServices.md)
|
||||
"""
|
||||
|
||||
# 3. Categories from README.md
|
||||
output += "## Categories\n\n"
|
||||
output += render_categories(frontmatter.categories, frontmatter)
|
||||
output += render_categories(frontmatter.categories, frontmatter.categories_info)
|
||||
output += "\n---\n\n"
|
||||
|
||||
# 3. README.md content
|
||||
@@ -593,7 +721,7 @@ Each attribute is documented below
|
||||
};
|
||||
```
|
||||
|
||||
- **flake-parts**: Each attribute can be defined via `clan.<attribute name>`. See our [flake-parts](../../manual/flake-parts.md) guide.
|
||||
- **flake-parts**: Each attribute can be defined via `clan.<attribute name>`. See our [flake-parts](../../guides/flake-parts.md) guide.
|
||||
|
||||
??? example "flake-parts Example"
|
||||
|
||||
@@ -785,7 +913,7 @@ def options_docs_from_tree(
|
||||
root: Option, init_level: int = 1, prefix: list[str] | None = None
|
||||
) -> str:
|
||||
"""
|
||||
Render the options from the tree structure.
|
||||
eender the options from the tree structure.
|
||||
|
||||
Args:
|
||||
root (Option): The root option node.
|
||||
@@ -829,5 +957,6 @@ if __name__ == "__main__": #
|
||||
produce_inventory_docs()
|
||||
|
||||
produce_clan_modules_docs()
|
||||
produce_clan_service_docs()
|
||||
|
||||
produce_clan_modules_frontmatter_docs()
|
||||
|
||||
@@ -17,6 +17,9 @@ pkgs.mkShell {
|
||||
self'.devShells.default
|
||||
];
|
||||
shellHook = ''
|
||||
git_root=$(git rev-parse --show-toplevel)
|
||||
cd ''${git_root}/docs
|
||||
|
||||
mkdir -p ./site/reference/cli
|
||||
cp -af ${module-docs}/* ./site/reference/
|
||||
cp -af ${clan-cli-docs}/* ./site/reference/cli/
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../CONTRIBUTING.md
|
||||
@@ -1,6 +1,8 @@
|
||||
# Clan service modules
|
||||
|
||||
Status: Accepted
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
@@ -65,9 +67,9 @@ Problems with the current way of writing clanModules:
|
||||
1. No way to retrieve the config of a single service instance, together with its name.
|
||||
2. Directly exporting a single, anonymous nixosModule without any intermediary attribute layers doesn't leave room for exporting other inventory resources such as potentially `vars` or `homeManagerConfig`.
|
||||
3. Can't access multiple config instances individually.
|
||||
Example:
|
||||
```nix
|
||||
inventory = {
|
||||
Example:
|
||||
```nix
|
||||
inventory = {
|
||||
services = {
|
||||
network.c-base = {
|
||||
instanceConfig.ips = {
|
||||
@@ -81,7 +83,7 @@ Problems with the current way of writing clanModules:
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
```
|
||||
This doesn't work because all instance configs are applied to the same namespace. So this results in a conflict currently.
|
||||
Resolving this problem means that new inventory modules cannot be plain nixos modules anymore. If they are configured via `instances` / `instanceConfig` they cannot be configured without using the inventory. (There might be ways to inject instanceConfig but that requires knowledge of inventory internals)
|
||||
|
||||
@@ -256,7 +258,6 @@ The following thoughts went into this:
|
||||
|
||||
We want to implement the system as described. Once we have sufficient data on real world use-cases and modules we might revisit this document along with the updated implementation.
|
||||
|
||||
|
||||
## Real world example
|
||||
|
||||
The following module demonstrates the idea in the example of *borgbackup*.
|
||||
@@ -407,7 +408,7 @@ The following module demonstrates the idea in the example of *borgbackup*.
|
||||
''
|
||||
) (lib.attrValues config.clan.core.state)}
|
||||
|
||||
if [[ ''${#preCommandErrors[@]} -gt 0 ]]; then
|
||||
if [[ ''${preCommandErrors[@]} -gt 0 ]]; then
|
||||
echo "pre-backup commands failed for the following services:"
|
||||
for state in "''${!preCommandErrors[@]}"; do
|
||||
echo " $state"
|
||||
@@ -19,14 +19,12 @@ We might not be sure whether all of those will exist but the architecture should
|
||||
## Decision
|
||||
|
||||
This leads to the conclusion that we should do `library` centric development.
|
||||
With the current `clan` python code beeing a library that can be imported to create various tools ontop of it.
|
||||
With the current `clan` python code being a library that can be imported to create various tools ontop of it.
|
||||
All **CLI** or **UI** related parts should be moved out of the main library.
|
||||
|
||||
*Note: The next person who wants implement any new frontend should do this first. Currently it looks like the TUI is the next one.*
|
||||
|
||||
Imagine roughly the following architecture:
|
||||
|
||||
```mermaid
|
||||
``` mermaid
|
||||
graph TD
|
||||
%% Define styles
|
||||
classDef frontend fill:#f9f,stroke:#333,stroke-width:2px;
|
||||
@@ -75,7 +73,7 @@ Integration tests and smaller unit-tests should both be utilized to ensure the s
|
||||
|
||||
Note: Library function don't have to be json-serializable in general.
|
||||
|
||||
Persistence includes but is not limited to: creating git commits, writing to inventory.json, reading and writing vars and to/from disk in general.
|
||||
Persistence includes but is not limited to: creating git commits, writing to inventory.json, reading and writing vars, and interacting with persisted data in general.
|
||||
|
||||
## Benefits / Drawbacks
|
||||
|
||||
47
docs/site/decisions/03-adr-numbering-process.md
Normal file
47
docs/site/decisions/03-adr-numbering-process.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# ADR Numbering process
|
||||
|
||||
## Status
|
||||
|
||||
Proposed after some conversation between @lassulus, @Mic92, & @lopter.
|
||||
|
||||
## Context
|
||||
|
||||
It can be useful to refer to ADRs by their numbers, rather than their full title. To that end, short and sequential numbers are useful.
|
||||
|
||||
The issue is that an ADR number is effectively assigned when the ADR is merged, before being merged its number is provisional. Because multiple ADRs can be written at the same time, you end-up with multiple provisional ADRs with the same number, for example this is the third ADR-3:
|
||||
|
||||
1. ADR-3-clan-compat: see [#3212];
|
||||
2. ADR-3-fetching-nix-from-python: see [#3452];
|
||||
3. ADR-3-numbering-process: this ADR.
|
||||
|
||||
This situation makes it impossible to refer to an ADR by its number, and why I (@lopter) went with the arbitrary number 7 in [#3196].
|
||||
|
||||
We could solve this problem by using the PR number as the ADR number (@lassulus). The issue is that PR numbers are getting big in clan-core which does not make them easy to remember, or use in conversation and code (@lopter).
|
||||
|
||||
Another approach would be to move the ADRs in a different repository, this would reset the counter back to 1, and make it straightforward to keep ADR and PR numbers in sync (@lopter). The issue then is that ADR are not in context with their changes which makes them more difficult to review (@Mic92).
|
||||
|
||||
## Decision
|
||||
|
||||
A third approach would be to:
|
||||
|
||||
1. Commit ADRs before they are approved, so that the next ADR number gets assigned;
|
||||
1. Open a PR for the proposed ADR;
|
||||
1. Update the ADR file committed in step 1, so that its markdown contents point to the PR that tracks it.
|
||||
|
||||
## Consequences
|
||||
|
||||
### ADR have unique and memorable numbers trough their entire life cycle
|
||||
|
||||
This makes it easier to refer to them in conversation or in code.
|
||||
|
||||
### You need to have commit access to get an ADR number assigned
|
||||
|
||||
This makes it more difficult for someone external to the project to contribute an ADR.
|
||||
|
||||
### Creating a new ADR requires multiple commits
|
||||
|
||||
Maybe a script or CI flow could help with that if it becomes painful.
|
||||
|
||||
[#3212]: https://git.clan.lol/clan/clan-core/pulls/3212/
|
||||
[#3452]: https://git.clan.lol/clan/clan-core/pulls/3452/
|
||||
[#3196]: https://git.clan.lol/clan/clan-core/pulls/3196/
|
||||
97
docs/site/decisions/04-fetching-nix-from-python.md
Normal file
97
docs/site/decisions/04-fetching-nix-from-python.md
Normal file
@@ -0,0 +1,97 @@
|
||||
## Status
|
||||
|
||||
accepted
|
||||
|
||||
## Context
|
||||
|
||||
In our clan-cli we need to get a lot of values from nix into the python runtime. This is used to determine the hostname, the target ips address, scripts to generate vars, file locations and many more.
|
||||
|
||||
Currently we use two different accessing methods:
|
||||
|
||||
### Method 1: deployment.json
|
||||
|
||||
A json file that serializes some predefined values into a JSON file as build-time artifact.
|
||||
|
||||
Downsides:
|
||||
|
||||
* no access to flake level values
|
||||
* all or nothing:
|
||||
* values are either cached via deployment.json or not. So we can only put cheap values into there,
|
||||
* in the past var generation script were added here, which added a huge build time overhead for every time we wanted to do any action
|
||||
* duplicated nix code
|
||||
* values need duplicated nix code, once to define them at the correct place in the module system (clan.core.vars.generators) and code to accumulate them again for the deployment.json (system.clan.deployment.data)
|
||||
* This duality adds unnecessary dependencies to the nixos module system.
|
||||
|
||||
Benefits:
|
||||
|
||||
* Utilize `nix build` for caching the file.
|
||||
* Caching mechanism is very simple.
|
||||
|
||||
|
||||
### Method 2: Direct access:
|
||||
|
||||
Directly calling the evaluator / build sandbox via `nix build` and `nix eval`within the Python code
|
||||
|
||||
|
||||
Downsides:
|
||||
|
||||
* Access is not cached: Static overhead (see below: \~1.5s) is present every time, if we invoke `nix commands`
|
||||
* The static overhead depends obviously which value we need to retrieve, since the `evalModules` overhead depends, whether we evaluate some attribute inside a machine or a flake attribute
|
||||
* Accessing more and more attributes with this method increases the static overhead, which leads to a linear decrease in performance.
|
||||
* Boilerplate for interacting with the CLI and Error handling code is repeated every time.
|
||||
|
||||
Benefits:
|
||||
|
||||
* Simple and native interaction with the `nix commands`is rather intuitive
|
||||
* Custom error handling for each attribute is easy
|
||||
|
||||
This sytem could be enhanced with custom nix expressions, which could be used in places where we don't want to put values into deployment.json or want to fetch flake level values. This also has some downsides:
|
||||
|
||||
* technical debt
|
||||
* we have to maintain custom nix expressions inside python code, embedding code is error prone and the language linters won't help you here, so errors are common and harder to debug.
|
||||
* we need custom error reporting code in case something goes wrong, either the value doesn't exist or there is an reported build error
|
||||
* no caching/custom caching logic
|
||||
* currently there is no infrastructure to cache those extra values, so we would need to store them somewhere, we could either enhance one of the many classes we have or don't cache them at all
|
||||
* even if we implement caching for extra nix expressions, there can be no sharing between extra nix expressions. for example we have 2 nix expressions, one fetches paths and values for all generators and the second one fetches only the values, we still need to execute both of them in both contexts although the second one could be skipped if the first one is already cached
|
||||
|
||||
### Method 3: nix select
|
||||
|
||||
Move all code that extracts nix values into a common class:
|
||||
|
||||
Downsides:
|
||||
* added complexity for maintaining our own DSL
|
||||
|
||||
Benefits:
|
||||
* we can implement an API (select DSL) to get those values from nix without writing complex nix expressions.
|
||||
* we can implement caching of those values beyond the runtime of the CLI
|
||||
* we can use precaching at different endpoints to eliminate most of multiple nix evaluations (except in cases where we have to break the cache or we don't know if we need the value in the value later and getting it is expensive).
|
||||
|
||||
|
||||
|
||||
## Decision
|
||||
|
||||
Use Method 3 (nix select) for extracting values out of nix.
|
||||
|
||||
This adds the Flake class in flake.py with a select method, which takes a selector string and returns a python dict.
|
||||
|
||||
Example:
|
||||
```python
|
||||
from clan_lib.flake import Flake
|
||||
flake = Flake("github:lassulus/superconfig")
|
||||
flake.select("nixosConfigurations.*.config.networking.hostName)
|
||||
```
|
||||
returns:
|
||||
```
|
||||
{
|
||||
"ignavia": "ignavia",
|
||||
"mors": "mors",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
* Faster execution due to caching most things beyond a single execution, if no cache break happens execution is basically instant, because we don't need to run nix again.
|
||||
* Better error reporting, since all nix values go through one chokepoint, we can parse error messages in that chokepoint and report them in a more user friendly way, for example if a value is missing at the expected location inside the module system.
|
||||
* less embedded nix code inside python code
|
||||
* more portable CLI, since we need to import less modules into the module system and most things can be extracted by the python code directly
|
||||
36
docs/site/decisions/05-deployment-parameters.md
Normal file
36
docs/site/decisions/05-deployment-parameters.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# deployment parameters: evalHost, buildHost, targetHost
|
||||
|
||||
## Status
|
||||
|
||||
accepted
|
||||
|
||||
## Context
|
||||
|
||||
Currently different operations (install, update) have different modes. Install always evals locally and pushes the derivation to a remote system. update has a configurable buildHost and targetHost.
|
||||
Confusingly install always evals locally and update always evals on the targetHost, so hosts have different semantics in different operations contexts.
|
||||
|
||||
## Decision
|
||||
|
||||
Add evalHost to make this clear and configurable for the user. This would leave us with:
|
||||
|
||||
- evalHost
|
||||
- buildHost
|
||||
- targetHost
|
||||
|
||||
for the update and install operation.
|
||||
|
||||
`evalHost` would be the machine that evaluates the nixos configuration. if evalHost is not localhost, we upload the non secret vars and the nix archived flake (this is usually the same operation) to the evalMachine.
|
||||
|
||||
`buildHost` would be what is used by the machine to build, it would correspond to `--build-host` on the nixos-rebuild command or `--builders` for nix build.
|
||||
|
||||
`targetHost` would be the machine where the closure gets copied to and activated (either through install or switch-to-configuration). It corresponds to `--targetHost` for nixos-rebuild or where we usually point `nixos-anywhere` to.
|
||||
|
||||
This hosts could be set either through CLI args (or forms for the GUI) or via the inventory. If both are given, the CLI args would take precedence.
|
||||
|
||||
## Consequences
|
||||
|
||||
We now support every deployment model of every tool out there with a bunch of simple flags. The semantics are more clear and we can write some nice documentation.
|
||||
|
||||
The install code has to be reworked, since nixos-anywhere has problems with evalHost and targetHost being the same machine, So we would need to kexec first and use the kexec image (or installer) as the evalHost afterwards.
|
||||
|
||||
In cases where the evalHost doesn't have access to the targetHost or buildHost, we need to setup temporary entries for the lifetime of the command.
|
||||
16
docs/site/decisions/README.md
Normal file
16
docs/site/decisions/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Architecture Decision Records
|
||||
|
||||
This section contains the architecture decisions that have been reviewed and generally agreed upon
|
||||
|
||||
## What is an ADR?
|
||||
|
||||
> An architecture decision record (ADR) is a document that captures an important architecture decision made along with its context and consequences.
|
||||
|
||||
!!! Note
|
||||
For further reading about adr's we recommend [architecture-decision-record](https://github.com/joelparkerhenderson/architecture-decision-record)
|
||||
|
||||
## Crafting a new ADR
|
||||
|
||||
1. Use the [template](./_template.md)
|
||||
2. Create the Pull request and gather feedback
|
||||
3. Retreive your adr-number (see: [numbering](./03-adr-numbering-process.md))
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
title: "Git Based Machine Deployment with Clan-Core"
|
||||
description: ""
|
||||
authors:
|
||||
- Qubasa
|
||||
date: 2024-05-25
|
||||
---
|
||||
## Revolutionizing Server Management
|
||||
|
||||
In the world of server management, countless tools claim to offer seamless deployment of multiple machines. Yet, many fall short, leaving server admins and self-hosting enthusiasts grappling with complexity. Enter the Clan-Core Framework—a groundbreaking all in one solution designed to transform decentralized self-hosting into an effortless and scalable endeavor.
|
||||
|
||||
### The Power of Clan-Core
|
||||
|
||||
Imagine having the power to manage your servers with unparalleled ease, scaling your IT infrastructure like never before. Clan-Core empowers you to do just that. At its core, Clan-Core leverages a single Git repository to define everything about your machines. This central repository utilizes Nix or JSON files to specify configurations, including disk formatting, ensuring a streamlined and unified approach.
|
||||
|
||||
### Simplified Deployment Process
|
||||
|
||||
With Clan-Core, the cumbersome task of bootstrapping a specific ISO is a thing of the past. All you need is SSH access to your Linux server. Clan-Core allows you to overwrite any existing Linux distribution live over SSH, eliminating time-consuming setup processes. This capability means you can deploy updates or new configurations swiftly and efficiently, maximizing uptime and minimizing hassle.
|
||||
|
||||
### Secure and Efficient Secret Management
|
||||
|
||||
Security is paramount in server management, and Clan-Core takes it seriously. Passwords and other sensitive information are encrypted within the Git repository, automatically decrypted during deployment. This not only ensures the safety of your secrets but also simplifies their management. Clan-Core supports sharing secrets with other admins, fostering collaboration and maintaining reproducibillity and security without sacrificing convenience.
|
||||
|
||||
### Services as Apps
|
||||
|
||||
Setting up a service can be quite difficult. Many server adjustments need to be made, from setting up a database to adjusting webserver configurations and generating the correct private keys. However, Clan-Core aims to make setting up a service as easy as installing an application. Through Clan-Core's Module system, everything down to secrets can be automatically set up. This transforms the often daunting task of service setup into a smooth, automated process, making it accessible to all.
|
||||
|
||||
### Decentralized Mesh VPN
|
||||
|
||||
Building on these features is a self-configuring decentralized mesh VPN that interconnects all your machines into a private darknet. This ensures that sensitive services, which might have too much attack surface to be hosted on the public internet, can still be made available privately without the need to worry about potential system compromise. By creating a secure, private network, Clan-Core offers an additional layer of protection for your most critical services.
|
||||
|
||||
### Decentralized Domain Name System
|
||||
|
||||
Current DNS implementations are distributed but not truly decentralized. For Clan-Core, we implemented our own truly decentralized DNS module. This module uses simple flooding and caching algorithms to discover available domains inside the darknet. This approach ensures that your internal domain name system is robust, reliable, and independent of external control, enhancing the resilience and security of your infrastructure.
|
||||
|
||||
|
||||
### A New Era of Decentralized Self-Hosting
|
||||
|
||||
Clan-Core is more than just a tool; it's a paradigm shift in server management. By consolidating machine definitions, secrets and network configuration, into a single, secure repository, it transforms how you manage and scale your infrastructure. Whether you're a seasoned server admin or a self-hosting enthusiast, Clan-Core offers a powerful, user-friendly solution to take your capabilities to the next level.
|
||||
|
||||
|
||||
### Key Features of Clan-Core:
|
||||
|
||||
- **Unified Git Repository**: All machine configurations and secrets stored in a single repository.
|
||||
- **Live Overwrites**: Deploy configurations over existing Linux distributions via SSH.
|
||||
- **Automated Service Setup**: Easily set up services with Clan-Core's Module system.
|
||||
- **Decentralized Mesh VPN**: Securely interconnect all machines into a private darknet.
|
||||
- **Decentralized DNS**: Robust, independent DNS using flooding and caching algorithms.
|
||||
- **Automated Secret Management**: Encrypted secrets that are automatically decrypted during deployment.
|
||||
- **Collaboration Support**: Share secrets securely with other admins.
|
||||
|
||||
|
||||
## Clan-Cores Future
|
||||
|
||||
Our vision for Clan-Core extends far beyond being just another deployment tool. Clan-Core is a framework we've developed to achieve something much greater. We want to put the "personal" back into "personal computing." Our goal is for everyday users to fully customize their phones or laptops and create truly private spaces for friends and family.
|
||||
|
||||
Our first major step is to develop a Graphical User Interface (GUI) that makes configuring all this possible. Initial tests have shown that AI can be leveraged as an alternative to traditional GUIs. This paves the way for a future where people can simply talk to their computers, and they will configure themselves according to the users' wishes.
|
||||
|
||||
By adopting Clan, you're not just embracing a tool—you're joining a movement towards a more efficient, secure, and scalable approach to server management. Join us and revolutionize your IT infrastructure today.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@ This site will guide you through authoring your first module. Explaining which c
|
||||
|
||||
|
||||
!!! Tip
|
||||
External ClanModules can be ad-hoc loaded via [`clan.inventory.modules`](../../reference/nix-api/inventory.md#inventory.modules)
|
||||
External ClanModules can be ad-hoc loaded via [`clan.inventory.modules`](../../../reference/nix-api/inventory.md#inventory.modules)
|
||||
|
||||
## Bootstrapping the `clanModule`
|
||||
|
||||
A ClanModule is a specific subset of a [NixOS Module](https://nix.dev/tutorials/module-system/index.html), but it has some constraints and might be used via the [Inventory](../../manual/inventory.md) interface.
|
||||
A ClanModule is a specific subset of a [NixOS Module](https://nix.dev/tutorials/module-system/index.html), but it has some constraints and might be used via the [Inventory](../../../guides/inventory.md) interface.
|
||||
In fact a `ClanModule` can be thought of as a layer of abstraction on-top of NixOS and/or other ClanModules. It may configure sane defaults and provide an ergonomic interface that is easy to use and can also be used via a UI that is under development currently.
|
||||
|
||||
Because ClanModules should be configurable via `json`/`API` all of its interface (`options`) must be serializable.
|
||||
@@ -48,7 +48,7 @@ clanModules/borgbackup
|
||||
=== "User module"
|
||||
|
||||
If the module should be ad-hoc loaded.
|
||||
It can be made available in any project via the [`clan.inventory.modules`](../../reference/nix-api/inventory.md#inventory.modules) attribute.
|
||||
It can be made available in any project via the [`clan.inventory.modules`](../../../reference/nix-api/inventory.md#inventory.modules) attribute.
|
||||
|
||||
```nix title="flake.nix"
|
||||
# ...
|
||||
@@ -89,7 +89,7 @@ description = "Module A"
|
||||
This is the example module that does xyz.
|
||||
```
|
||||
|
||||
See the [Full Frontmatter reference](../../reference/clanModules/frontmatter/index.md) further details and all supported attributes.
|
||||
See the [Full Frontmatter reference](../../../reference/clanModules/frontmatter/index.md) further details and all supported attributes.
|
||||
|
||||
## Roles
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Authoring a 'clan.service' module
|
||||
|
||||
!!! Tip
|
||||
This is the successor format to the older [clanModules](../legacyModules/index.md)
|
||||
This is the successor format to the older [clanModules](../clanModules/index.md)
|
||||
|
||||
While some features might still be missing we recommend to adapt this format early and give feedback.
|
||||
|
||||
@@ -12,7 +12,7 @@ We discussed the initial architecture in [01-clan-service-modules](https://git.c
|
||||
|
||||
### A Minimal module
|
||||
|
||||
First of all we need to register our module into the `inventory.modules` attribute. Make sure to choose a unique name so the module doesn't have a name collision with any of the core modules.
|
||||
First of all we need to register our module into the `clan.modules` attribute. Make sure to choose a unique name so the module doesn't have a name collision with any of the core modules.
|
||||
|
||||
While not required we recommend to prefix your module attribute name.
|
||||
|
||||
@@ -22,20 +22,15 @@ i.e. `@hsjobeki/customNetworking`
|
||||
|
||||
```nix title="flake.nix"
|
||||
# ...
|
||||
|
||||
outputs = inputs: flake-parts.lib.mkFlake { inherit inputs; } ({
|
||||
outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } ({
|
||||
imports = [ inputs.clan-core.flakeModules.default ];
|
||||
# ...
|
||||
clan = {
|
||||
inventory = {
|
||||
# We could also inline the complete module spec here
|
||||
# For example
|
||||
# {...}: { _class = "clan.service"; ... };
|
||||
modules."@hsjobeki/customNetworking" = import ./service-modules/networking.nix;
|
||||
};
|
||||
|
||||
# If needed: Exporting the module for other people
|
||||
modules."@hsjobeki/customNetworking" = import ./service-modules/networking.nix;
|
||||
# We could also inline the complete module spec here
|
||||
# For example
|
||||
# {...}: { _class = "clan.service"; ... };
|
||||
};
|
||||
})
|
||||
```
|
||||
@@ -221,9 +216,6 @@ outputs = inputs: flake-parts.lib.mkFlake { inherit inputs; } ({self, lib, ...}:
|
||||
# ...
|
||||
clan = {
|
||||
# Register the module
|
||||
inventory.modules."@hsjobeki/messaging" = lib.importApply ./service-modules/messaging.nix { inherit self; };
|
||||
|
||||
# Expose the module for downstream users, 'self' would always point to this flake.
|
||||
modules."@hsjobeki/messaging" = lib.importApply ./service-modules/messaging.nix { inherit self; };
|
||||
};
|
||||
})
|
||||
@@ -250,7 +242,7 @@ outputs = inputs: flake-parts.lib.mkFlake { inherit inputs; } ({self, lib, ...}:
|
||||
# ...
|
||||
clan = {
|
||||
# Register the module
|
||||
inventory.modules."@hsjobeki/messaging" = {
|
||||
modules."@hsjobeki/messaging" = {
|
||||
# Create an option 'myClan' and assign it to 'self'
|
||||
options.myClan = lib.mkOption {
|
||||
default = self;
|
||||
129
docs/site/guides/clanServices.md
Normal file
129
docs/site/guides/clanServices.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Using `clanServices`
|
||||
|
||||
Clan’s `clanServices` system is a composable way to define and deploy services across machines. It replaces the legacy `clanModules` approach and introduces better structure, flexibility, and reuse.
|
||||
|
||||
This guide shows how to **instantiate** a `clanService`, explains how service definitions are structured in your inventory, and how to pick or create services from modules exposed by flakes.
|
||||
|
||||
The term **Multi-host-modules** was introduced previously in the [nixus repository](https://github.com/infinisil/nixus) and represents a similar concept.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
A `clanService` is used in:
|
||||
|
||||
```nix
|
||||
inventory.instances.<instance_name>
|
||||
```
|
||||
|
||||
Each instance includes a reference to a **module specification** — this is how Clan knows which service module to use and where it came from.
|
||||
You can reference services from any flake input, allowing you to compose services from multiple flake sources.
|
||||
|
||||
These operate on a strict *role-based membership model*, meaning machines are added by assigning them specific *roles*.
|
||||
|
||||
---
|
||||
|
||||
## Basic Example
|
||||
|
||||
Example of instantiating a `borgbackup` service using `clan-core`:
|
||||
|
||||
```nix
|
||||
inventory.instances = {
|
||||
# Arbitrary unique name for this 'borgbackup' instance
|
||||
borgbackup-example = {
|
||||
module = {
|
||||
name = "borgbackup"; # <-- Name of the module
|
||||
input = "clan-core"; # <-- The flake input where the service is defined
|
||||
};
|
||||
# Participation of the machines is defined via roles
|
||||
# Right side needs to be an attribute set. Its purpose will become clear later
|
||||
roles.client.machines."machine-a" = {};
|
||||
roles.server.machines."backup-host" = {};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
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"
|
||||
```
|
||||
|
||||
## Configuration Example
|
||||
|
||||
Each role might expose configurable options
|
||||
|
||||
See clan's [clanServices reference](../reference/clanServices/index.md) for available options
|
||||
|
||||
```nix
|
||||
inventory.instances = {
|
||||
borgbackup-example = {
|
||||
module = {
|
||||
name = "borgbackup";
|
||||
input = "clan-core";
|
||||
};
|
||||
roles.client.machines."machine-a" = {
|
||||
# 'client' -Settings of 'machine-a'
|
||||
settings = {
|
||||
backupFolders = [
|
||||
/home
|
||||
/var
|
||||
];
|
||||
};
|
||||
# ---------------------------
|
||||
};
|
||||
roles.server.machines."backup-host" = {};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Tags
|
||||
|
||||
Multiple members can be defined using tags as follows
|
||||
|
||||
```nix
|
||||
inventory.instances = {
|
||||
borgbackup-example = {
|
||||
module = {
|
||||
name = "borgbackup";
|
||||
input = "clan-core";
|
||||
};
|
||||
#
|
||||
# The 'all' -tag targets all machines
|
||||
roles.client.tags."all" = {};
|
||||
# ---------------------------
|
||||
roles.server.machines."backup-host" = {};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Picking a clanService
|
||||
|
||||
You can use services exposed by Clan’s core module library, `clan-core`.
|
||||
|
||||
🔗 See: [List of Available Services in clan-core](../reference/clanServices/index.md)
|
||||
|
||||
## Defining Your Own Service
|
||||
|
||||
You can also author your own `clanService` modules.
|
||||
|
||||
🔗 Learn how to write your own service: [Authoring a clanService](../guides/authoring/clanServices/index.md)
|
||||
|
||||
You might expose your service module from your flake — this makes it easy for other people to also use your module in their clan.
|
||||
|
||||
---
|
||||
|
||||
## 💡 Tips for Working with clanServices
|
||||
|
||||
* You can add multiple inputs to your flake (`clan-core`, `your-org-modules`, etc.) to mix and match services.
|
||||
* Each service instance is isolated by its key in `inventory.instances`, allowing you to deploy multiple versions or roles of the same service type.
|
||||
* Roles can target different machines or be scoped dynamically.
|
||||
|
||||
---
|
||||
|
||||
## What’s Next?
|
||||
|
||||
* [Author your own clanService →](../guides/authoring/clanServices/index.md)
|
||||
* [Migrate from clanModules →](../guides/migrations/migrate-inventory-services.md)
|
||||
<!-- TODO: * [Understand the architecture →](../explanation/clan-architecture.md) -->
|
||||
1
docs/site/guides/contributing/CONTRIBUTING.md
Symbolic link
1
docs/site/guides/contributing/CONTRIBUTING.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../CONTRIBUTING.md
|
||||
@@ -112,7 +112,7 @@ You can execute every test separately by following the tree path `nix run .#chec
|
||||
|
||||
## Test Locally in Devshell with Breakpoints
|
||||
|
||||
To test the cli locally in a development environment and set breakpoints for debugging, follow these steps:
|
||||
To test the CLI locally in a development environment and set breakpoints for debugging, follow these steps:
|
||||
|
||||
1. Run the following command to execute your tests and allow for debugging with breakpoints:
|
||||
```bash
|
||||
@@ -32,7 +32,7 @@ VM tests should be avoided wherever it is possible to implement a cheaper unit t
|
||||
|
||||
Existing nixos vm tests in clan-core can be found by using ripgrep:
|
||||
```shellSession
|
||||
rg "import.*/lib/test-base.nix"
|
||||
rg self.clanLib.test.baseTest
|
||||
```
|
||||
|
||||
### Locating definitions of failing VM tests
|
||||
@@ -50,7 +50,7 @@ example: locating the vm test named `borgbackup`:
|
||||
```shellSession
|
||||
$ rg "borgbackup =" ./checks
|
||||
./checks/flake-module.nix
|
||||
41: borgbackup = import ./borgbackup nixosTestArgs;
|
||||
44- wayland-proxy-virtwl = self.clanLib.test.baseTest ./wayland-proxy-virtwl nixosTestArgs;
|
||||
```
|
||||
|
||||
-> the location of that test is `/checks/flake-module.nix` line `41`.
|
||||
@@ -77,9 +77,9 @@ Locate the definition (see above) and add print statements, like, for example `p
|
||||
|
||||
#### Interactive Shell
|
||||
|
||||
- Execute the vm test outside the nix Sandbox via the following command:
|
||||
- Execute the vm test outside the nix Sandbox via the following command:
|
||||
`nix run .#checks.x86_64-linux.{test-attr-name}.driver -- --interactive`
|
||||
- Then run the commands in the machines manually, like for example:
|
||||
- Then run the commands in the machines manually, like for example:
|
||||
```python3
|
||||
start_all()
|
||||
machine1.succeed("echo hello")
|
||||
@@ -87,7 +87,7 @@ Locate the definition (see above) and add print statements, like, for example `p
|
||||
|
||||
#### Breakpoints
|
||||
|
||||
To get an interactive shell at a specific line in the VM test script, add a `breakpoint()` call before the line to debug, then run the test outside of the sandbox via:
|
||||
To get an interactive shell at a specific line in the VM test script, add a `breakpoint()` call before the line to debug, then run the test outside of the sandbox via:
|
||||
`nix run .#checks.x86_64-linux.{test-attr-name}.driver`
|
||||
|
||||
|
||||
@@ -99,23 +99,23 @@ Basically everything stated under the NixOS VM tests sections applies here, exce
|
||||
|
||||
Limitations:
|
||||
|
||||
- does not yet support networking
|
||||
- supports only one machine as of now
|
||||
|
||||
- Cannot run in interactive mode, however while the container test runs, it logs a nsenter command that can be used to log into each of the container.
|
||||
- setuid binaries don't work
|
||||
|
||||
### Where to find examples for NixOS container tests
|
||||
|
||||
Existing nixos container tests in clan-core can be found by using ripgrep:
|
||||
|
||||
```shellSession
|
||||
rg "import.*/lib/container-test.nix"
|
||||
rg self.clanLib.test.containerTest
|
||||
```
|
||||
|
||||
|
||||
## Python tests via pytest
|
||||
|
||||
Since the clan cli is written in python, the `pytest` framework is used to define unit tests and integration tests via python
|
||||
Since the Clan CLI is written in python, the `pytest` framework is used to define unit tests and integration tests via python
|
||||
|
||||
Due to superior efficiency,
|
||||
Due to superior efficiency,
|
||||
|
||||
### When to use python tests
|
||||
|
||||
@@ -141,7 +141,7 @@ rg "import pytest"
|
||||
If any python test fails in the CI pipeline, an error message like this can be found at the end of the log:
|
||||
```
|
||||
...
|
||||
FAILED tests/test_machines_cli.py::test_machine_delete - clan_cli.errors.ClanError: Template 'new-machine' not in 'inputs.clan-core
|
||||
FAILED tests/test_machines_cli.py::test_machine_delete - clan_lib.errors.ClanError: Template 'new-machine' not in 'inputs.clan-core
|
||||
...
|
||||
```
|
||||
|
||||
@@ -244,7 +244,7 @@ Find the attribute via ripgrep:
|
||||
$ rg "lib-values-eval ="
|
||||
lib/values/flake-module.nix
|
||||
21: lib-values-eval = pkgs.runCommand "tests" { nativeBuildInputs = [ pkgs.nix-unit ]; } ''
|
||||
grmpf@grmpf-nix ~/p/c/clan-core (test-docs)>
|
||||
grmpf@grmpf-nix ~/p/c/clan-core (test-docs)>
|
||||
```
|
||||
|
||||
In this case the test is defined in the file `lib/values/flake-module.nix` line 21
|
||||
@@ -296,9 +296,9 @@ Example:
|
||||
$ nix repl
|
||||
Nix 2.25.5
|
||||
Type :? for help.
|
||||
nix-repl> tests = import ./lib/values/test.nix {}
|
||||
nix-repl> tests = import ./lib/values/test.nix {}
|
||||
|
||||
nix-repl> tests
|
||||
nix-repl> tests
|
||||
{
|
||||
test_attrsOf_attrsOf_submodule = { ... };
|
||||
test_attrsOf_submodule = { ... };
|
||||
@@ -309,7 +309,7 @@ nix-repl> tests
|
||||
test_submodule_with_merging = { ... };
|
||||
}
|
||||
|
||||
nix-repl> tests.test_default.expr
|
||||
nix-repl> tests.test_default.expr
|
||||
{
|
||||
foo = { ... };
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user