Compare commits

..

1 Commits

Author SHA1 Message Date
DavHau
588eeabf39 ci performance: add check to ensure nothing depends on the whole repo
Since this project is an ever growing monorepo, having derivations depending on the whole repo leads to bad CI performance, as the cache is busted on every commit.

-> We never want any derivations depend on the whole repo

...except: the test that tests that nothing depends on the whole repo, which is added by this commit.
2025-04-30 11:55:31 +07:00
543 changed files with 13882 additions and 29545 deletions

View File

@@ -1,6 +1,9 @@
name: checks
on:
pull_request:
push:
branches:
- main
jobs:
checks-impure:
runs-on: nix

View File

@@ -1,53 +0,0 @@
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

31
.gitignore vendored
View File

@@ -1,24 +1,20 @@
.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_lib/nixpkgs
**/result
/pkgs/clan-cli/clan_cli/nixpkgs
/pkgs/clan-cli/clan_cli/webui/assets
nixos.qcow2
*.glade~
**/*.glade~
/docs/out
/pkgs/clan-cli/clan_lib/select
.local.env
# macOS stuff
.DS_Store
**/.local.env
# dream2nix
.dream2nix
@@ -43,14 +39,3 @@ repo
node_modules
dist
.webui
# 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

View File

@@ -1,2 +0,0 @@
nixosModules/clanCore/vars/.* @lopter
pkgs/clan-cli/clan_cli/(secrets|vars)/.* @lopter

View File

@@ -1,4 +1,4 @@
# Contributing to Clan
<!-- Local file: docs/CONTRIBUTING.md -->
Go to the Contributing guide at https://docs.clan.lol/guides/contributing/CONTRIBUTING
Go to the Contributing guide at https://docs.clan.lol/manual/contribute/

View File

@@ -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/guides/getting-started/secrets/)<!-- [secrets.md](docs/site/guides/getting-started/secrets.md) -->.
- **Secrets Management**: Securely manage secrets by consulting [secrets](https://docs.clan.lol/getting-started/secrets/)<!-- [secrets.md](docs/site/getting-started/secrets.md) -->.
### Contributing to Clan

View File

@@ -1,64 +0,0 @@
{
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")
'';
}
);
}

View File

@@ -1,8 +0,0 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCOs4+7m04Os/2g8ETcLzP1pUHVKHDKa0PBPdjVsZerfgAAAJDXdRkm13UZ
JgAAAAtzc2gtZWQyNTUxOQAAACCOs4+7m04Os/2g8ETcLzP1pUHVKHDKa0PBPdjVsZerfg
AAAECIgb2FQcgBKMniA+6zm2cwGre60ATu3Sg1GivgAqVJlI6zj7ubTg6z/aDwRNwvM/Wl
QdUocMprQ8E92NWxl6t+AAAAC3BpbnBveEBraXdpAQI=
-----END OPENSSH PRIVATE KEY-----

View File

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

View File

@@ -1,15 +0,0 @@
{
"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"
}
}

View File

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

View File

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

View File

@@ -1 +0,0 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICVVQjCEuryZii1LmJyjx9DX44eJh3qwTTEWlahYONsz nixbld@kiwi

View File

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

View File

@@ -1,19 +0,0 @@
{
"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"
}
}

View File

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

View File

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

View File

@@ -1,19 +0,0 @@
{
"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"
}
}

View File

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

View File

@@ -1,15 +0,0 @@
{
"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"
}
}

View File

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

View File

@@ -147,7 +147,25 @@
perSystem =
{ pkgs, ... }:
let
clanCore = self.checks.x86_64-linux.clan-core-for-checks;
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"
];
};
in
{
checks = pkgs.lib.mkIf pkgs.stdenv.isLinux {
@@ -164,6 +182,11 @@
# import the inventory generated nixosModules
self.clanInternals.inventoryClass.machines.test-backup.machineImports;
clan.core.settings.directory = ./.;
environment.systemPackages = [
(pkgs.writeShellScriptBin "foo" ''
echo ${clanCore}
'')
];
};
testScript = ''

View File

@@ -1,6 +0,0 @@
{ fetchgit }:
fetchgit {
url = "https://git.clan.lol/clan/clan-core.git";
rev = "4b44892b476b32b1ca7d2f637da41562b8ed5456";
sha256 = "15k7dp3m9dcvx75sm900xp70vss2pnx0ywjnpmqyqlmwpvzn663p";
}

View File

@@ -26,7 +26,6 @@ clanLib.test.makeTestClan {
roles.admin.machines = [ "admin1" ];
};
};
instances."test" = {
module.name = "new-service";
roles.peer.machines.peer1 = { };
@@ -34,33 +33,25 @@ clanLib.test.makeTestClan {
modules = {
legacy-module = ./legacy-module;
};
};
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;
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
'';
};
};
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
'';
};
};
};
@@ -78,15 +69,7 @@ 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.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}"
peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.new-service.files.hello.path}")
'';
}
);

View File

@@ -1,6 +1,6 @@
[
{
"publickey": "age12yt078p9ewxy2sh0a36nxdpgglv8wqqftmj4dkj9rgy5fuyn4p0q5nje9m",
"publickey": "age1hd2exjq88h7538y6mvjvexx3u5gp6a03yfn5nj32h2667yyksyaqcuk5qs",
"type": "age"
}
]

View File

@@ -1,6 +1,6 @@
[
{
"publickey": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2",
"publickey": "age19urkt89q45a2wk6a4yaramzufjtnw6nq2snls0v7hmf7tqf73axsfx50tk",
"type": "age"
}
]

View File

@@ -1,15 +1,15 @@
{
"data": "ENC[AES256_GCM,data:GPpsUhSzWPtTP8EUNKsobFXjYqDldhkkIH6hBk11RsDLAGWdhVrwcISGbhsWpYhvAdPKA84DB6Zqyh9lL2bLM9//ybC1kzY20BQ=,iv:NrxMLdedT2FCkUAD00SwsAHchIsxWvqe7BQekWuJcxw=,tag:pMDXcMyHnLF2t3Qhb1KolA==,type:str]",
"data": "ENC[AES256_GCM,data:hhuFgZcPqht0h3tKxGtheS4GlrVDo4TxH0a9lxgPYj2i12QUmE04rB07A+hu4Z8WNWLYvdM5069mEOZYm3lSeTzBHQPxYZRuVj0=,iv:sA1srRFQqsMlJTAjFcb09tI/Jg2WjOVJL5NZkPwiLoU=,tag:6xXo9FZpmAJw6hCBsWzf8Q==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzb2tWb1ExKzdmUTRzaGVj\nK3cyYTBHZTJwVjM1SzUvbHFiMnVhY05iKzFZCnJTSE1VSVdpcUFLSEJuaE1CZzJD\nWjZxYzN2cUltdThNMVRKU3FIb20vUXMKLS0tIFlHQXRIdnMybDZFUVEzWlQrc1dw\nbUxhZURXblhHd0pka0JIK1FTZEVqdUEKI/rfxQRBc+xGRelhswkJQ9GcZs6lzfgy\nuCxS5JI9npdPLQ/131F3b21+sP5YWqks41uZG+vslM1zQ+BlENNhDw==\n-----END AGE ENCRYPTED FILE-----\n"
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGaGVHeTgrN3dJQ2VITFBM\neWVzbDhjb0pwNUhBUjdUc0p5OTVta1dvSno4ClJxeUc4Z0hiaFRkVlJ1YTA4Lyta\neWdwV005WGYvMUNRVG1qOVdicTk0NUkKLS0tIFQvaDNFS1JMSFlHRXlhc3lsZm03\nYVhDaHNsam5wN1VqdzA3WTZwM1JwV2sKZk/SiZJgjllADdfHLSWuQcU4+LttDpt/\nqqDUATEuqYaALljC/y3COT+grTM2bwGjj6fsfsfiO/EL9iwzD3+7oA==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-05-04T12:44:13Z",
"mac": "ENC[AES256_GCM,data:fWxLHXBWolHVxv6Q7utcy6OVLV13ziswrIYyNKiwy1vsU8i7xvvuGO1HlnE+q43D2WuHR53liKq1UHuf1JMrWzTwZ0PYe+CVugtoEtbR2qu3rK/jAkOyMyhmmHzmf6Rp4ZMCzKgZeC/X2bDKY/z0firHAvjWydEyogutHpvtznM=,iv:OQI3FfkLneqbdztAXVQB3UkHwDPK+0hWu5hZ9m8Oczg=,tag:em6GfS2QHsXs391QKPxfmA==,type:str]",
"lastmodified": "2025-04-09T15:10:16Z",
"mac": "ENC[AES256_GCM,data:xuXj4833G6nhvcRo2ekDxz8G5phltmU8h1GgGofH9WndzrqLKeRSqm/n03IHRW0f4F68XxnyAkfvokVh6vW3LRQAFkqIlXz5U4+zFNcaVaPobS5gHTgxsCoTUoalWPvHWtXd50hUVXeAt8rPfTfeveVGja8bOERk8mvwUPxb6h4=,iv:yP1usA9m8tKl6Z/UK9PaVMJlZlF5qpY4EiM4+ByVlik=,tag:8DgoIhLstp3MRki90VfEvw==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
"version": "3.10.1"
}
}

View File

@@ -1,15 +1,15 @@
{
"data": "ENC[AES256_GCM,data:W3cOkUYL5/YulW2pEISyTlMaA/t7/WBE7BoCdFlqrqgaCL7tG4IV2HgjiPWzIVMs0zvDSaghdEvAIoB4wOf470d1nSWs0/E8SDk=,iv:wXXaZIw3sPY8L/wxsu7+C5v+d3RQRuwxZRP4YLkS8K4=,tag:HeK4okj7O7XDA9JDz2KULw==,type:str]",
"data": "ENC[AES256_GCM,data:rwPhbayGf6mE1E9NCN+LuL7VfWWOfhoJW6H2tNSoyebtyTpM3GO2jWca1+N7hI0juhNkUk+rIsYQYbCa/5DZQiV0/2Jgu4US1XY=,iv:B5mcaQsDjb6BacxGB4Kk88/qLCpVOjQNRvGN+fgUiEo=,tag:Uz0A8kAF5NzFetbv9yHIjQ==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxRC83b3dtSVpXcGovNnVs\nTzFka2J2MEFhYkF1ajVrdjMrNUtPWGRObjM4Cm5zSUR5OGw0T0FaL3BaWmR6L29W\nU2syMFIyMUhFRUZpWFpCT28vWko2ZU0KLS0tIFpHK3BjU1V1L0FrMGtwTGFuU3Mz\nRkV5VjI2Vndod202bUR3RWQwNXpmVzQKNk8/y7M62wTIIKqY4r3ZRk5aUCRUfine\n1LUSHMKa2bRe+hR7nS7AF4BGXp03h2UPY0FP5+U5q8XuIj1jfMX8kg==\n-----END AGE ENCRYPTED FILE-----\n"
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWY0hKQ1dnV0tMYytDMCtj\nTDV4Zk5NeVN0bCtqaWRQV3d4M0VlcGVZMkhZCm02dHZyOGVlYzJ5Z3FlUWNXMVQ0\nb2ZrTXZQRzRNdzFDeWZCVGhlTS9rMm8KLS0tIEJkY1QwOENRYWw3cjIwd3I0bzdz\nOEtQNm1saE5wNWt2UUVnYlN4NWtGdFkKmWHU5ttZoQ3NZu/zkX5VxfC2sMpSOyod\neb7LRhFqPfo5N1XphJcCqr5QUoZOfnH0xFhZ2lxWUS3ItiRpU4VDwg==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"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]",
"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]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
"version": "3.10.1"
}
}

View File

@@ -1,19 +1,19 @@
{
"data": "ENC[AES256_GCM,data:T8edCvw=,iv:7/G5xt5fv38I9uFzk7WMIr9xQdz/6lFxqOC+18HBg8Q=,tag:F39Cxbgmzml+lZLsZ59Kmg==,type:str]",
"data": "ENC[AES256_GCM,data:bxM9aYMK,iv:SMNYtk9FSyZ1PIfEzayTKKdCnZWdhcyUEiTwFUNb988=,tag:qJYW4+VQyhF1tGPQPTKlOQ==,type:str]",
"sops": {
"age": [
{
"recipient": "age12yt078p9ewxy2sh0a36nxdpgglv8wqqftmj4dkj9rgy5fuyn4p0q5nje9m",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPNUhiYkZWK3dPMHNiRTVM\nRHNvaHFsOFp1c0UxQitwVG0zY01MNDZRV1E4CjEybENoTVIzN29vQ3FtUTRSYmFU\nNXIzQllVSllXRGN2M1B6WXJLdHZSajgKLS0tIDllZ0ZmZUcxMHhDQUpUOEdWbmkv\neUQweHArYTdFSmNteVpuQ3BKdnh0Y0UKs8Hm3D+rXRRfpUVSZM3zYjs6b9z8g10D\nGTkvreUMim4CS22pjdQ3eNA9TGeDXfWXE7XzwXLCb+wVcf7KwbDmvg==\n-----END AGE ENCRYPTED FILE-----\n"
"recipient": "age1hd2exjq88h7538y6mvjvexx3u5gp6a03yfn5nj32h2667yyksyaqcuk5qs",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvZDZYYXdpcXVqRFRnQ2Jx\nTFhFWEJTR290cHZhTXZadFFvcHM4MHVIN3lFCmJhOEZrL3g4TFBZVllxdDFZakJn\nR3NxdXo0eE8vTDh3QlhWOFpVZ0lNUHcKLS0tIEE4dkpCalNzaXJ0Qks3VHJSUzZF\nb2N3NGdjNHJnSUN6bW8welZ1VDdJakEKGKZ7nn1p11IyJB6DMxu2HJMvZ+0+5WpE\nPLWh2NlGJO3XrrL4Fw7xetwbqE+QUZPNl/JbEbu4KLIUGLjqk9JDhQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKSDhpT3cvck9PenZYVEZH\ndFQreVRBdG93L1dBUGlvYjFWcDlHWUJsZUVBCm9DMTJ4UytiYzlEVHNWdUcwS1ds\nT0dhbzAzNDdmbDBCU0dvL2xNeHpXcGsKLS0tIFArbmpsbzU3WnpJdUt1MGN0L1d0\nV1JkTDJYWUxsbmhTQVNOeVRaSUhTODQKk9Vph2eldS5nwuvVX0SCsxEm4B+sO76Z\ndIjJ3OQxzoZmXMaOOuKHC5U0Y75Qn7eXC43w5KHsl2CMIUYsBGJOZw==\n-----END AGE ENCRYPTED FILE-----\n"
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHckJCQVFyb21aT1R0d2Rr\nMWxNMHVqcGxabHBmS0RibW9sN0gyZDI1b1dFCnRWUk5LSWdxV3c4RWVZdUtEN1Fv\nRk4xVmwwT2xrdWVERkJXUVVlVXJjTVUKLS0tIC9ERG9KMGxTNEsrbzFHUGRiVUlm\nRi9qakxoc1FOVVV1TkUrckwxRUVnajQKE8ms/np2NMswden3xkjdC8cXccASLOoN\nu+EaEk69UvBvnOg9VBjyPAraIKgNrTc4WWwz+DOBj1pCwVbu9XxUlA==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-05-04T12:44:14Z",
"mac": "ENC[AES256_GCM,data:6fKrS1eLLUWlHkQpxLFXBRk6f2wa5ADLMViVvYXXGU24ayl9UuNSKrCRHp9cbzhqhti3HdwyNt6TM+2X6qhiiAQanKEB2PF7JRYX74NfNKil9BEDjt5AqqtpSgVv5l7Ku/uSHaPkd2sDmzHsy5Q4bSGxJQokStk1kidrwle+mbc=,iv:I/Aad82L/TCxStM8d8IZICUrwdjRbGx2fuGWqexr21o=,tag:BfgRbGUxhPZzK2fLik1kxA==,type:str]",
"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]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
"version": "3.10.1"
}
}

View File

@@ -1,19 +1,19 @@
{
"data": "ENC[AES256_GCM,data:vp0yW0Gt,iv:FO2cy+UpEl5aRay/LUGu//c82QiVxuKuGSaVh0rGJvc=,tag:vf2RAOPpcRW0HwxHoGy17A==,type:str]",
"data": "ENC[AES256_GCM,data:ImlGIKxE,iv:UUWxjLNRKJCD2WHNpw8lfvCc8rnXPCqc2pni1ODckjE=,tag:HFCqiv31E9bShIIaAEjF0A==,type:str]",
"sops": {
"age": [
{
"recipient": "age12w2ld4vxfyf3hdq2d8la4cu0tye4pq97egvv3me4wary7xkdnq2snh0zx2",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjaFVNMEd2YUxpSm5XVVRi\nY2ZUc3NTOStJUFNMWWVPQTgxZ2tCK1QrMW1ZCjYwMlA4dkIzSlc0TGtvZjcyK3Bi\nM3pob2JOOFUyeVJ6M2JpaTRCZlc1R0kKLS0tIDJMb1dFcVRWckhwYWNCQng0RlFO\nTkw3OGt4dkFIZVY5aVEzZE5mMzJSM0EKUv8bUqg48L2FfYVUVlpXvyZvPye699of\nG6PcjLh1ZMbNCfnsCzr+P8Vdk/F4J/ifxL66lRGfu2xOLxwciwQ+5Q==\n-----END AGE ENCRYPTED FILE-----\n"
"recipient": "age19urkt89q45a2wk6a4yaramzufjtnw6nq2snls0v7hmf7tqf73axsfx50tk",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpTEROZjh6NjBhSlJSc1Av\nSHhjdkhwVUd3VzBZemhQb3dhMlJXalBmZlFjCkZPYkhZZGVOVTNjUWdFU0s4cWFn\nL2NXbkRCdUlMdElnK2lGbG5iV0w1cHMKLS0tIFREcmxDdHlUNVBFVGRVZSt0c0E5\nbnpHaW1Vb3R3ZFFnZVMxY3djSjJmOU0KIwqCSQf5S9oA59BXu7yC/V6yqvCh88pa\nYgmNyBjulytPh1aAfOuNWIGdIxBpcEf+gFjz3EiJY9Kft3fTmhp2bw==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnZ2dDbVhoQngxM3lTSmZF\nUTAwS1lCTGhEMU1GVXpFUzlIUFdqZy9LajF3Ck9mdVpBRjlyVUNhZXZIUFZjUzF1\nNlhFN28vNmwzcUVkNmlzUnpkWjJuZE0KLS0tIHpXVHVlNk9vU1ZPTGRrYStWbmRO\nbDM4U2o1SlEwYWtqOXBqd3BFUTAvMHcKkI8UVd0v+x+ELZ5CoGq9DzlA6DnVNU2r\nrV9wLfbFd7RHxS0/TYZh5tmU42nO3iMYA9FqERQXCtZgXS9KvfqHwQ==\n-----END AGE ENCRYPTED FILE-----\n"
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArN3R4TThibjdYbE9TMDE1\naUhuNDlscExjaktIR2VmTk1OMWtVM0NpTUJZClJUNEcwVDlibExWQk84TTNEWFhp\nMjYyZStHc1N0ZTh1S3VTVk45WGxlWWMKLS0tIHFab25LY1R1d1l6NE5XbHJvQ3lj\nNGsxUldFVHQ5RVJERDlGbi9NY29hNWsKENBTcAS/R/dTGRYdaWv5Mc/YG4bkah5w\nb421ZMQF+r4CYnzUqnwivTG8TMRMqJLavfkutE6ZUfJbbLufrTk5Lw==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"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]",
"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]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
"version": "3.10.1"
}
}

View File

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

View File

@@ -1,19 +0,0 @@
{
"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"
}
}

View File

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

View File

@@ -0,0 +1 @@
This is a dummy script

View File

@@ -14,14 +14,13 @@ in
./installation/flake-module.nix
./morph/flake-module.nix
./nixos-documentation/flake-module.nix
./dont-depend-on-repo-root.nix
./sanity-checks/dont-depend-on-repo-root.nix
];
perSystem =
{
pkgs,
lib,
self',
system,
...
}:
{
@@ -34,45 +33,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
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
# We might want to include this later once everything is fixed
"dont-depend-on-repo-root"
];
# Clan Tests
mumble = import ./mumble nixosTestArgs;
dummy-inventory-test = import ./dummy-inventory-test nixosTestArgs;
data-mesher = import ./data-mesher nixosTestArgs;
syncthing = import ./syncthing nixosTestArgs;
};
flakeOutputs =
lib.mapAttrs' (
name: config: lib.nameValuePair "nixos-${name}" config.config.system.build.toplevel
) (lib.filterAttrs (n: _: !lib.hasPrefix "test-" n) self.nixosConfigurations)
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") packagesToBuild
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") self'.packages
// lib.mapAttrs' (n: lib.nameValuePair "devShell-${n}") self'.devShells
// lib.mapAttrs' (name: config: lib.nameValuePair "home-manager-${name}" config.activation-script) (
self'.legacyPackages.homeConfigurations or { }
@@ -90,10 +77,7 @@ in
schema =
(self.clanLib.inventory.evalClanService {
modules = [ m ];
prefix = [
"checks"
system
];
key = "checks";
}).config.result.api.schema;
in
schema
@@ -107,12 +91,6 @@ 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 =

View File

@@ -43,7 +43,6 @@
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
@@ -81,7 +80,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 ${self.checks.x86_64-linux.clan-core-for-checks} --yes --disk main /dev/vdb test-flash-machine-${pkgs.hostPlatform.system}")
machine.succeed("clan flash write --debug --flake ${../..} --yes --disk main /dev/vdb test-flash-machine-${pkgs.hostPlatform.system}")
'';
} { inherit pkgs self; };
};

View File

@@ -8,6 +8,7 @@ let
{ modulesPath, pkgs, ... }:
let
dependencies = [
self
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.toplevel
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.diskoScript
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.clan.deployment.file
@@ -15,7 +16,6 @@ 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
@@ -198,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 ${self.checks.x86_64-linux.clan-core-for-checks} test-flake && chmod -R +w test-flake")
installer.succeed("cp -r ${../..} 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()
@@ -218,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 ${self.checks.x86_64-linux.clan-core-for-checks} test-flake && chmod -R +w test-flake")
installer.succeed("cp -r ${../..} 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")

View File

@@ -32,6 +32,7 @@
{ pkgs, ... }:
let
dependencies = [
self
pkgs.stdenv.drvPath
pkgs.stdenvNoCC
self.nixosConfigurations.test-morph-machine.config.system.build.toplevel
@@ -43,18 +44,14 @@
{
environment.etc."install-closure".source = "${closureInfo}/store-paths";
system.extraDependencies = dependencies;
virtualisation.memorySize = 2048;
virtualisation.useNixStoreImage = true;
virtualisation.writableStore = true;
environment.systemPackages = [ self.packages.${pkgs.system}.clan-cli-full ];
};
};
testScript = ''
start_all()
actual.fail("cat /etc/testfile")
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")
actual.succeed("env CLAN_DIR=${self} 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; };

View File

@@ -8,8 +8,5 @@
(modulesPath + "/profiles/minimal.nix")
];
virtualisation.useNixStoreImage = true;
virtualisation.writableStore = true;
clan.core.enableRecommendedDefaults = false;
}

View File

@@ -47,20 +47,6 @@ 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"):
@@ -77,53 +63,41 @@ 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")
peer2.wait_for_window(r"Mumble")
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")
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("Wait for server connect"):
peer1.wait_for_window(r"^Mumble Server Connect$")
peer2.wait_for_window(r"^Mumble Server Connect$")
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")
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")
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.")
peer2.execute("killall .mumble-wrapped")
peer2.sleep(1)
peer2.execute("mumble mumble://peer1 >&2 &")
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")
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.")
'';
}
);

View File

@@ -27,7 +27,6 @@
clan-core.checks.${system}
[
"dont-depend-on-repo-root"
"package-dont-depend-on-repo-root"
"package-clan-core-flake"
];
checksOutPaths = map (x: "''${x}") (builtins.attrValues checks);
@@ -48,7 +47,7 @@
'';
in
lib.optionalAttrs (system == "x86_64-linux") {
packages.dont-depend-on-repo-root =
checks.dont-depend-on-repo-root =
pkgs.runCommand
# append repo hash to this tests name to ensure it gets invalidated on each chain
# This is needed because this test is an FOD (due to networking) and would get cached indefinitely.

View File

@@ -1,5 +1,5 @@
---
description = "Convenient Administration for the Clan App"
categories = ["Utility"]
features = [ "inventory", "deprecated" ]
features = [ "inventory" ]
---

View File

@@ -1,3 +1,6 @@
# Dont import this file
# It is only here for backwards compatibility.
# Dont author new modules with this file.
{
imports = [ ./roles/default.nix ];
}

View File

@@ -1,6 +1,5 @@
{ lib, config, ... }:
{
options.clan.admin = {
allowedKeys = lib.mkOption {
default = { };

View File

@@ -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](../../guides/inventory.md).
Don't use borgbackup-static through [inventory](../../manual/inventory.md).
This module implements the `borgbackup` backend and implements sane defaults
for backup management through `borgbackup` for members of the clan.

View File

@@ -12,16 +12,13 @@ let
# currently only supports zerotier
defaultBootstrapNodes = builtins.foldl' (
urls: name:
let
ipPath = "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value";
in
if builtins.pathExists ipPath then
if
builtins.pathExists "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value"
then
let
ip = builtins.readFile ipPath;
ip = builtins.readFile "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value";
in
urls ++ [ "[${ip}]:${builtins.toString cfg.network.port}" ]
urls ++ "${ip}:${cfg.network.port}"
else
urls
) [ ] (dmLib.machines config).bootstrap;
@@ -90,8 +87,7 @@ in
push_pull_interval = "30s";
interface = cfg.network.interface;
bootstrap_nodes = if cfg.bootstrapNodes == null then defaultBootstrapNodes else cfg.bootstrapNodes;
bootstrap_nodes = cfg.bootstrapNodes or defaultBootstrapNodes;
};
http.port = 7331;
@@ -109,7 +105,10 @@ in
private_key = {
inherit owner;
};
public_key.secret = false;
public_key = {
inherit owner;
secret = false;
};
};
runtimeInputs = [
@@ -135,7 +134,10 @@ in
private_key = {
inherit owner;
};
public_key.secret = false;
public_key = {
inherit owner;
secret = false;
};
};
runtimeInputs = [

View File

@@ -210,18 +210,14 @@ in
data_dir = Path('data')
data_dir.mkdir(mode=0o770, exist_ok=True)
# Create a temporary config file
# with appropriate permissions
tmp_config_path = data_dir / '.config.json'
tmp_config_path.touch(mode=0o660, exist_ok=False)
# Write the config with secrets back
with open(tmp_config_path, 'w') as f:
config_path = data_dir / 'config.json'
with open(config_path, 'w') as f:
f.write(json.dumps(config, indent=4))
# Move config into place
config_path = data_dir / 'config.json'
tmp_config_path.rename(config_path)
# Set file permissions to read and write
# only by the user and group
config_path.chmod(0o660)
# Set file permissions to read
# and write only by the user and group

View File

@@ -8,8 +8,8 @@ in
{
# only import available files, as this allows to filter the files for tests.
flake.clanModules = filterAttrs (_name: pathExists) {
auto-upgrade = ./auto-upgrade;
admin = ./admin;
auto-upgrade = ./auto-upgrade;
borgbackup = ./borgbackup;
borgbackup-static = ./borgbackup-static;
deltachat = ./deltachat;

View File

@@ -7,12 +7,8 @@ features = [ "inventory" ]
After the system was installed/deployed the following command can be used to display the root-password:
```bash
clan vars get [machine_name] root-password/root-password
clan secrets get {machine_name}-password
```
See also: [Vars](../../guides/vars-backend.md)
To regenerate the password run:
```
clan vars generate --regenerate [machine_name] --generator root-password
```
See also: [Facts / Secrets](../../getting-started/secrets.md)

View File

@@ -13,12 +13,9 @@ If setting the option prompt to true, the user will be prompted to type in their
After the system was installed/deployed the following command can be used to display the user-password:
```bash
clan vars get [machine_name] root-password/root-password
clan secrets get {machine_name}-user-password
```
See also: [Vars](../../guides/vars-backend.md)
See also: [Facts / Secrets](../../getting-started/secrets.md)
To regenerate the password run:
```
clan vars generate --regenerate [machine_name] --generator user-password
```
To regenerate the password, delete the password files in the clan directory and redeploy the machine.

View File

@@ -0,0 +1,7 @@
---
description = "Configures Wifi networks to join"
features = [ "inventory", "experimental" ]
categories = [ "Network", "System" ]
---
## Experimental wifi module

View File

@@ -0,0 +1,5 @@
{
imports = [
./roles/default.nix
];
}

View File

@@ -0,0 +1,109 @@
{
lib,
config,
pkgs,
...
}:
let
cfg = config.clan.wifi;
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
{
options.clan.wifi = {
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";
};
};
config = lib.mkMerge [
(lib.mkIf (cfg.networks != { }) {
clan.core.vars.generators = lib.mapAttrs' secret_generator cfg.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"
'') cfg.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;
};
};
}
];
}

View File

@@ -1,39 +0,0 @@
{ ... }:
{
_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;
};
};
};
}

View File

@@ -1,6 +0,0 @@
{ lib, ... }:
{
clan.modules = {
admin = lib.modules.importApply ./default.nix { };
};
}

View File

@@ -1,8 +1,5 @@
{ ... }:
{
imports = [
./admin/flake-module.nix
./hello-world/flake-module.nix
./wifi/flake-module.nix
];
}

View File

@@ -3,19 +3,8 @@
{
_class = "clan.service";
manifest.name = "clan-core/hello-word";
manifest.description = "This is a test";
roles.peer = {
interface =
{ lib, ... }:
{
options.foo = lib.mkOption {
type = lib.types.str;
# default = "";
description = "Some option";
};
};
};
roles.peer = { };
perMachine =
{ machine, ... }:

View File

@@ -10,6 +10,9 @@ let
};
in
{
clan.inventory.modules = {
hello-world = module;
};
clan.modules = {
hello-world = module;
};
@@ -47,7 +50,6 @@ in
hello-service = import ./tests/vm/default.nix {
inherit module;
inherit self inputs pkgs;
# clanLib is exposed from inputs.clan-core
clanLib = self.clanLib;
};
};

View File

@@ -18,7 +18,7 @@ let
};
# Register the module for the test
modules.hello-world = module;
inventory.modules.hello-world = module;
# Use the module in the test
inventory.instances = {

View File

@@ -14,9 +14,6 @@ clanLib.test.makeTestClan {
clan = {
directory = ./.;
modules = {
hello-service = module;
};
inventory = {
machines.peer1 = { };
@@ -24,6 +21,10 @@ clanLib.test.makeTestClan {
module.name = "hello-service";
roles.peer.machines.peer1 = { };
};
modules = {
hello-service = module;
};
};
};

View File

@@ -1,115 +0,0 @@
{ packages }:
{ lib, ... }:
let
inherit (lib)
concatMapAttrsStringSep
flip
mapAttrs
;
in
{
_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 =
{ pkgs, config, ... }:
let
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 = {
prompts.ssid.type = "line";
prompts.ssid.persist = true;
prompts.password.type = "hidden";
prompts.password.persist = true;
share = true;
};
};
in
lib.mkIf (settings.networks != { }) {
clan.core.vars.generators = lib.mapAttrs' secret_generator settings.networks;
networking.networkmanager.enable = true;
networking.networkmanager.ensureProfiles.environmentFiles = [
"/run/secrets/NetworkManager/wifi-secrets"
];
networking.networkmanager.ensureProfiles.profiles = flip mapAttrs settings.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";
}
);
# 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
env_file=/run/secrets/NetworkManager/wifi-secrets
mkdir -p $(dirname "$env_file")
: > "$env_file"
# Generate the secrets file
echo "Generating wifi secrets file: $env_file"
${flip (concatMapAttrsStringSep "\n") settings.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
''
)}
'';
};
};
};
};
};
}

View File

@@ -1,36 +0,0 @@
{
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;
};
};
};
}

View File

@@ -1,58 +0,0 @@
{
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 = {
#
# };
};
}

View File

@@ -1,43 +0,0 @@
{
pkgs,
self,
clanLib,
module,
...
}:
clanLib.test.makeTestClan {
inherit pkgs self;
useContainers = false;
nixosTest = (
{ ... }:
{
name = "wifi";
clan = {
directory = ./.;
modules."@clan/wifi" = module;
inventory = {
machines.test = { };
instances = {
wg-test-one = {
module.name = "@clan/wifi";
roles.default.machines = {
test.settings.networks.one = { };
};
};
};
};
};
testScript = ''
start_all()
test.wait_for_unit("NetworkManager.service")
psk = test.succeed("cat /run/NetworkManager/system-connections/one.nmconnection")
assert "password-eins" in psk, "Password is incorrect"
'';
}
);
}

View File

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

View File

@@ -1,15 +0,0 @@
{
"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"
}
}

View File

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

View File

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

View File

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

View File

@@ -1,19 +0,0 @@
{
"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"
}
}

View File

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

View File

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

View File

@@ -1,19 +0,0 @@
{
"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"
}
}

View File

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

View File

@@ -1,8 +1,6 @@
# Clan service modules
## Status
Accepted
Status: Accepted
## Context
@@ -67,9 +65,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 = {
@@ -83,7 +81,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)
@@ -258,6 +256,7 @@ 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*.
@@ -408,7 +407,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"

View File

@@ -19,12 +19,14 @@ 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 being a library that can be imported to create various tools ontop of it.
With the current `clan` python code beeing 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;
@@ -73,7 +75,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 interacting with persisted data 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.
## Benefits / Drawbacks

1
decisions/README.md Normal file
View File

@@ -0,0 +1 @@
see [architecture-decision-record](https://github.com/joelparkerhenderson/architecture-decision-record)

View File

@@ -1,4 +1,4 @@
{ inputs, ... }:
{ ... }:
{
perSystem =
{
@@ -39,10 +39,6 @@
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"
'';
};
};

View File

@@ -22,11 +22,7 @@ markdown_extensions:
emoji_generator: !!python/name:material.extensions.emoji.to_svg
- pymdownx.tasklist:
custom_checkbox: true
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
- pymdownx.details
@@ -46,51 +42,43 @@ 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:
- 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
- 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
- Inventory:
- Inventory: guides/inventory.md
- Secure Boot: guides/secure-boot.md
- Flake-parts: guides/flake-parts.md
- Inventory: manual/inventory.md
- Instances: manual/distributed-services.md
- Secure Boot: manual/secure-boot.md
- Flake-parts: manual/flake-parts.md
- Authoring:
- clanService: guides/authoring/clanServices/index.md
- Disk Template: guides/authoring/templates/disk/disko-templates.md
- clanModule: guides/authoring/clanModules/index.md
- clan.service: authoring/clanServices/index.md
- Disk Templates: authoring/templates/disk/disko-templates.md
- clanModules: authoring/legacyModules/index.md
- Contributing:
- 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
- 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
- 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
# TODO: display the docs of the clan.service modules
- reference/clanModules/admin.md
- Overview:
- reference/clanModules/index.md
- reference/clanModules/frontmatter/index.md
# This is the module overview and should stay at the top
- reference/clanModules/admin.md
- reference/clanModules/borgbackup-static.md
- reference/clanModules/data-mesher.md
- reference/clanModules/borgbackup.md
@@ -131,7 +119,7 @@ nav:
- reference/clanModules/zerotier.md
- reference/clanModules/zt-tcp-relay.md
- CLI:
- Overview: reference/cli/index.md
- reference/cli/index.md
- reference/cli/backups.md
- reference/cli/facts.md
@@ -145,30 +133,20 @@ nav:
- reference/cli/state.md
- reference/cli/vars.md
- reference/cli/vms.md
- NixOS Modules:
- clan.core:
- Overview: reference/clan.core/index.md
- Clan Core:
- 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

View File

@@ -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/clan-app/ui/icons ./site/static/
cp -af ../pkgs/webview-ui/app/icons ./site/static/
'';
buildPhase = ''

View File

@@ -26,7 +26,8 @@ writeShellScriptBin "deploy-docs" ''
trap "rm -rf $tmpdir" EXIT
if [ -n "''${SSH_HOMEPAGE_KEY-}" ]; then
( umask 0177 && echo "$SSH_HOMEPAGE_KEY" > "$tmpdir/ssh_key" )
echo "$SSH_HOMEPAGE_KEY" > "$tmpdir/ssh_key"
chmod 600 "$tmpdir/ssh_key"
sshExtraArgs="-i $tmpdir/ssh_key"
else
sshExtraArgs=

View File

@@ -36,9 +36,6 @@
# 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"
@@ -88,7 +85,6 @@
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
@@ -104,12 +100,7 @@
in
{
legacyPackages = {
inherit
jsonDocs
clanModulesViaNix
clanModulesViaRoles
clanModulesViaService
;
inherit jsonDocs clanModulesViaNix clanModulesViaRoles;
};
devShells.docs = pkgs.callPackage ./shell.nix {
inherit (self'.packages) docs clan-cli-docs inventory-api-docs;

View File

@@ -36,33 +36,6 @@
) 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 =

View File

@@ -29,13 +29,8 @@ from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
from clan_lib.api.modules import (
CategoryInfo,
Frontmatter,
extract_frontmatter,
get_roles,
)
from clan_lib.errors import ClanError
from clan_cli.errors import ClanError
from clan_lib.api.modules import Frontmatter, extract_frontmatter, get_roles
# Get environment variables
CLAN_CORE_PATH = Path(os.environ["CLAN_CORE_PATH"])
@@ -49,7 +44,6 @@ 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")
@@ -64,8 +58,7 @@ 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 = str(Path(*Path(text).parts[4:]))
name = Path(res).name
return (res, name)
@@ -156,12 +149,8 @@ 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: Declared in: [{name}]({source_path})
:simple-git: [{name}]({source_path})
"""
res += "\n\n"
@@ -206,12 +195,7 @@ Some modules are considered 'low-level' or 'expert modules' and are not availabl
"""
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)
clan_core_descr = """`clan.core` is always included in each machine `config`.
Your can customize your machines behavior with the configuration [options](#module-options) provided below.
"""
@@ -237,8 +221,7 @@ 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
@@ -263,8 +246,7 @@ machine readable metadata about the module.
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():
@@ -297,28 +279,16 @@ 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
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
core_outputs[indexfile] = (
module_header(module_name) + clan_core_descr + options_head
)
for submodule_name, split_options in split.items():
outfile = f"{module_name}/{submodule_name}.md"
@@ -334,6 +304,7 @@ 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
@@ -360,7 +331,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
@@ -370,19 +341,17 @@ 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](../../guides/inventory.md).
For more information, see the [inventory guide](../../manual/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"
@@ -414,24 +383,20 @@ For more information, see the [inventory guide](../../guides/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](../../guides/inventory.md) feature.
Modules with this indicator support the [inventory](../../manual/inventory.md) feature.
"""
def render_categories(
categories: list[str], categories_info: dict[str, CategoryInfo]
) -> str:
def render_categories(categories: list[str], frontmatter: Frontmatter) -> str:
cat_info = frontmatter.categories_info
res = """<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;">"""
for cat in categories:
color = categories_info[cat]["color"]
color = cat_info[cat]["color"]
# description = cat_info[cat]["description"]
res += f"""
<div style="background-color: {color}; color: white; padding: 10px; border-radius: 20px; text-align: center;">
{cat}
@@ -441,83 +406,6 @@ def render_categories(
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}"
@@ -568,27 +456,11 @@ def produce_clan_modules_docs() -> None:
# 2. Description from README.md
if frontmatter.description:
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)
"""
output += f"**{frontmatter.description}**\n\n"
# 3. Categories from README.md
output += "## Categories\n\n"
output += render_categories(frontmatter.categories, frontmatter.categories_info)
output += render_categories(frontmatter.categories, frontmatter)
output += "\n---\n\n"
# 3. README.md content
@@ -721,7 +593,7 @@ Each attribute is documented below
};
```
- **flake-parts**: Each attribute can be defined via `clan.<attribute name>`. See our [flake-parts](../../guides/flake-parts.md) guide.
- **flake-parts**: Each attribute can be defined via `clan.<attribute name>`. See our [flake-parts](../../manual/flake-parts.md) guide.
??? example "flake-parts Example"
@@ -913,7 +785,7 @@ def options_docs_from_tree(
root: Option, init_level: int = 1, prefix: list[str] | None = None
) -> str:
"""
eender the options from the tree structure.
Render the options from the tree structure.
Args:
root (Option): The root option node.
@@ -957,6 +829,5 @@ if __name__ == "__main__": #
produce_inventory_docs()
produce_clan_modules_docs()
produce_clan_service_docs()
produce_clan_modules_frontmatter_docs()

View File

@@ -17,9 +17,6 @@ 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/

View File

@@ -1,7 +1,7 @@
# Authoring a 'clan.service' module
!!! Tip
This is the successor format to the older [clanModules](../clanModules/index.md)
This is the successor format to the older [clanModules](../legacyModules/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 `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.
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.
While not required we recommend to prefix your module attribute name.
@@ -22,15 +22,20 @@ i.e. `@hsjobeki/customNetworking`
```nix title="flake.nix"
# ...
outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } ({
outputs = 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"; ... };
};
})
```
@@ -216,6 +221,9 @@ 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; };
};
})
@@ -242,7 +250,7 @@ outputs = inputs: flake-parts.lib.mkFlake { inherit inputs; } ({self, lib, ...}:
# ...
clan = {
# Register the module
modules."@hsjobeki/messaging" = {
inventory.modules."@hsjobeki/messaging" = {
# Create an option 'myClan' and assign it to 'self'
options.myClan = lib.mkOption {
default = self;

View File

@@ -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](../../../guides/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](../../manual/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

View File

@@ -0,0 +1 @@
../../CONTRIBUTING.md

View File

@@ -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

View File

@@ -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 self.clanLib.test.baseTest
rg "import.*/lib/test-base.nix"
```
### 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
44- wayland-proxy-virtwl = self.clanLib.test.baseTest ./wayland-proxy-virtwl nixosTestArgs;
41: borgbackup = import ./borgbackup 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:
- 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
- does not yet support networking
- supports only one machine as of now
### Where to find examples for NixOS container tests
Existing nixos container tests in clan-core can be found by using ripgrep:
```shellSession
rg self.clanLib.test.containerTest
rg "import.*/lib/container-test.nix"
```
## 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_lib.errors.ClanError: Template 'new-machine' not in 'inputs.clan-core
FAILED tests/test_machines_cli.py::test_machine_delete - clan_cli.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 = { ... };
}

View File

@@ -1,47 +0,0 @@
# 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/

View File

@@ -1,97 +0,0 @@
## 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

View File

@@ -1,36 +0,0 @@
# 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.

View File

@@ -1,16 +0,0 @@
# 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))

View File

@@ -0,0 +1,63 @@
---
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.

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