Compare commits

..

1 Commits

Author SHA1 Message Date
lassulus
1b3f087079 WIP: zerotier refactor 2025-09-23 12:12:22 +02:00
555 changed files with 7023 additions and 17842 deletions

View File

@@ -1,12 +0,0 @@
## Description of the change
<!-- Brief summary of the change if not already clear from the title -->
## Checklist
- [ ] Updated Documentation
- [ ] Added tests
- [ ] Doesn't affect backwards compatibility - or check the next points
- [ ] Add the breaking change and migration details to docs/release-notes.md
- !!! Review from another person is required *BEFORE* merge !!!
- [ ] Add introduction of major feature to docs/release-notes.md

View File

@@ -17,4 +17,4 @@ jobs:
- name: Build clan-app for x86_64-darwin - name: Build clan-app for x86_64-darwin
run: | run: |
nix build .#packages.x86_64-darwin.clan-app --log-format bar-with-logs nix build .#packages.x86_64-darwin.clan-app --system x86_64-darwin --log-format bar-with-logs

View File

@@ -1,10 +1,8 @@
clanServices/.* @pinpox @kenji clanServices/.* @pinpox @kenji
lib/test/container-test-driver/.* @DavHau @mic92 lib/test/container-test-driver/.* @DavHau @mic92
lib/inventory/.* @hsjobeki lib/modules/inventory/.* @hsjobeki
lib/inventoryClass/.* @hsjobeki lib/modules/inventoryClass/.* @hsjobeki
modules/.* @hsjobeki
pkgs/clan-app/ui/.* @hsjobeki @brianmcgee pkgs/clan-app/ui/.* @hsjobeki @brianmcgee
pkgs/clan-app/clan_app/.* @qubasa @hsjobeki pkgs/clan-app/clan_app/.* @qubasa @hsjobeki

4
CONTRIBUTING.md Normal file
View File

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

View File

@@ -1,4 +1,4 @@
Copyright 2023-2025 Clan contributors Copyright 2023-2024 Clan contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in

View File

@@ -19,7 +19,20 @@ let
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { }; nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
in in
{ {
imports = filter pathExists [ imports =
let
clanCoreModulesDir = ../nixosModules/clanCore;
getClanCoreTestModules =
let
moduleNames = attrNames (builtins.readDir clanCoreModulesDir);
testPaths = map (
moduleName: clanCoreModulesDir + "/${moduleName}/tests/flake-module.nix"
) moduleNames;
in
filter pathExists testPaths;
in
getClanCoreTestModules
++ filter pathExists [
./devshell/flake-module.nix ./devshell/flake-module.nix
./flash/flake-module.nix ./flash/flake-module.nix
./installation/flake-module.nix ./installation/flake-module.nix
@@ -27,10 +40,6 @@ in
./morph/flake-module.nix ./morph/flake-module.nix
./nixos-documentation/flake-module.nix ./nixos-documentation/flake-module.nix
./dont-depend-on-repo-root.nix ./dont-depend-on-repo-root.nix
# clan core submodule tests
../nixosModules/clanCore/machine-id/tests/flake-module.nix
../nixosModules/clanCore/postgresql/tests/flake-module.nix
../nixosModules/clanCore/state-version/tests/flake-module.nix
]; ];
flake.check = genAttrs [ "x86_64-linux" "aarch64-darwin" ] ( flake.check = genAttrs [ "x86_64-linux" "aarch64-darwin" ] (
system: system:
@@ -86,13 +95,11 @@ in
# Container Tests # Container Tests
nixos-test-container = self.clanLib.test.containerTest ./container nixosTestArgs; nixos-test-container = self.clanLib.test.containerTest ./container nixosTestArgs;
nixos-systemd-abstraction = self.clanLib.test.containerTest ./systemd-abstraction nixosTestArgs;
nixos-llm-test = self.clanLib.test.containerTest ./llm nixosTestArgs;
nixos-test-user-firewall-iptables = self.clanLib.test.containerTest ./user-firewall/iptables.nix nixosTestArgs; nixos-test-user-firewall-iptables = self.clanLib.test.containerTest ./user-firewall/iptables.nix nixosTestArgs;
nixos-test-user-firewall-nftables = self.clanLib.test.containerTest ./user-firewall/nftables.nix nixosTestArgs; nixos-test-user-firewall-nftables = self.clanLib.test.containerTest ./user-firewall/nftables.nix nixosTestArgs;
nixos-test-extra-python-packages = self.clanLib.test.containerTest ./test-extra-python-packages nixosTestArgs;
service-dummy-test = import ./service-dummy-test nixosTestArgs; service-dummy-test = import ./service-dummy-test nixosTestArgs;
wireguard = import ./wireguard nixosTestArgs;
service-dummy-test-from-flake = import ./service-dummy-test-from-flake nixosTestArgs; service-dummy-test-from-flake = import ./service-dummy-test-from-flake nixosTestArgs;
}; };
@@ -113,7 +120,7 @@ in
) (self.darwinConfigurations or { }) ) (self.darwinConfigurations or { })
// lib.mapAttrs' (n: lib.nameValuePair "package-${n}") ( // lib.mapAttrs' (n: lib.nameValuePair "package-${n}") (
if system == "aarch64-darwin" then if system == "aarch64-darwin" then
lib.filterAttrs (n: _: n != "docs" && n != "deploy-docs" && n != "option-search") packagesToBuild lib.filterAttrs (n: _: n != "docs" && n != "deploy-docs" && n != "docs-options") packagesToBuild
else else
packagesToBuild packagesToBuild
) )

View File

@@ -13,6 +13,8 @@
fileSystems."/".device = lib.mkDefault "/dev/vda"; fileSystems."/".device = lib.mkDefault "/dev/vda";
boot.loader.grub.device = lib.mkDefault "/dev/vda"; boot.loader.grub.device = lib.mkDefault "/dev/vda";
# We need to use `mkForce` because we inherit from `test-install-machine`
# which currently hardcodes `nixpkgs.hostPlatform`
nixpkgs.hostPlatform = lib.mkForce system; nixpkgs.hostPlatform = lib.mkForce system;
imports = [ self.nixosModules.test-flash-machine ]; imports = [ self.nixosModules.test-flash-machine ];
@@ -26,9 +28,6 @@
{ {
imports = [ self.nixosModules.test-install-machine-without-system ]; imports = [ self.nixosModules.test-install-machine-without-system ];
# We don't want our system to define any `vars` generators as these can't
# be generated as the flake is inside `/nix/store`.
clan.core.settings.state-version.enable = false;
clan.core.vars.generators.test = lib.mkForce { }; clan.core.vars.generators.test = lib.mkForce { };
disko.devices.disk.main.preCreateHook = lib.mkForce ""; disko.devices.disk.main.preCreateHook = lib.mkForce "";
@@ -60,11 +59,11 @@
pkgs.kbd.out pkgs.kbd.out
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".pkgs.perlPackages.ConfigIniFiles self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".pkgs.perlPackages.ConfigIniFiles
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".pkgs.perlPackages.FileSlurp self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".pkgs.perlPackages.FileSlurp
pkgs.bubblewrap
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.toplevel self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.toplevel
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript
self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript.drvPath self.nixosConfigurations."test-flash-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript.drvPath
(import ../installation/facter-report.nix pkgs.hostPlatform.system)
] ]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs); ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
closureInfo = pkgs.closureInfo { rootPaths = dependencies; }; closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
@@ -88,7 +87,7 @@
substituters = lib.mkForce [ ]; substituters = lib.mkForce [ ];
hashed-mirrors = null; hashed-mirrors = null;
connect-timeout = lib.mkForce 3; connect-timeout = lib.mkForce 3;
flake-registry = ""; flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
experimental-features = [ experimental-features = [
"nix-command" "nix-command"
"flakes" "flakes"

View File

@@ -0,0 +1,10 @@
system:
builtins.fetchurl {
url = "https://git.clan.lol/clan/test-fixtures/raw/commit/4a2bc56d886578124b05060d3fb7eddc38c019f8/nixos-vm-facter-json/${system}.json";
sha256 =
{
aarch64-linux = "sha256:1rlfymk03rmfkm2qgrc8l5kj5i20srx79n1y1h4nzlpwaz0j7hh2";
x86_64-linux = "sha256:16myh0ll2gdwsiwkjw5ba4dl23ppwbsanxx214863j7nvzx42pws";
}
.${system};
}

View File

@@ -1,8 +1,8 @@
{ {
config,
self, self,
lib, lib,
privateInputs, privateInputs,
... ...
}: }:
{ {
@@ -14,8 +14,7 @@
# you can get a new one by adding # you can get a new one by adding
# client.fail("cat test-flake/machines/test-install-machine/facter.json >&2") # client.fail("cat test-flake/machines/test-install-machine/facter.json >&2")
# to the installation test. # to the installation test.
clan.machines = { clan.machines.test-install-machine-without-system = {
test-install-machine-without-system = {
fileSystems."/".device = lib.mkDefault "/dev/vda"; fileSystems."/".device = lib.mkDefault "/dev/vda";
boot.loader.grub.device = lib.mkDefault "/dev/vda"; boot.loader.grub.device = lib.mkDefault "/dev/vda";
@@ -23,28 +22,18 @@
self.nixosModules.test-install-machine-without-system self.nixosModules.test-install-machine-without-system
]; ];
}; };
}
// (lib.listToAttrs ( clan.machines.test-install-machine-with-system =
lib.map ( { pkgs, ... }:
system:
lib.nameValuePair "test-install-machine-${system}" {
imports = [
self.nixosModules.test-install-machine-without-system
(
if privateInputs ? test-fixtures then
{ {
facter.reportPath = privateInputs.test-fixtures + /nixos-vm-facter-json/${system}.json; # https://git.clan.lol/clan/test-fixtures
} facter.reportPath = import ./facter-report.nix pkgs.hostPlatform.system;
else
{ nixpkgs.hostPlatform = system; }
)
];
fileSystems."/".device = lib.mkDefault "/dev/vda"; fileSystems."/".device = lib.mkDefault "/dev/vda";
boot.loader.grub.device = lib.mkDefault "/dev/vda"; boot.loader.grub.device = lib.mkDefault "/dev/vda";
}
) (lib.filter (lib.hasSuffix "linux") config.systems) imports = [ self.nixosModules.test-install-machine-without-system ];
)); };
flake.nixosModules = { flake.nixosModules = {
test-install-machine-without-system = test-install-machine-without-system =
@@ -160,12 +149,13 @@
closureInfo = pkgs.closureInfo { closureInfo = pkgs.closureInfo {
rootPaths = [ rootPaths = [
privateInputs.clan-core-for-checks privateInputs.clan-core-for-checks
self.nixosConfigurations."test-install-machine-${pkgs.hostPlatform.system}".config.system.build.toplevel self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.toplevel
self.nixosConfigurations."test-install-machine-${pkgs.hostPlatform.system}".config.system.build.initialRamdisk self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.initialRamdisk
self.nixosConfigurations."test-install-machine-${pkgs.hostPlatform.system}".config.system.build.diskoScript self.clanInternals.machines.${pkgs.hostPlatform.system}.test-install-machine-with-system.config.system.build.diskoScript
pkgs.stdenv.drvPath pkgs.stdenv.drvPath
pkgs.bash.drvPath pkgs.bash.drvPath
pkgs.buildPackages.xorg.lndir pkgs.buildPackages.xorg.lndir
(import ./facter-report.nix pkgs.hostPlatform.system)
] ]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs); ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
}; };
@@ -215,7 +205,7 @@
# Prepare test flake and Nix store # Prepare test flake and Nix store
flake_dir = prepare_test_flake( flake_dir = prepare_test_flake(
temp_dir, temp_dir,
"${self.checks.${pkgs.hostPlatform.system}.clan-core-for-checks}", "${self.checks.x86_64-linux.clan-core-for-checks}",
"${closureInfo}" "${closureInfo}"
) )
@@ -226,22 +216,6 @@
"${../assets/ssh/privkey}" "${../assets/ssh/privkey}"
) )
# Run clan install from host using port forwarding
clan_cmd = [
"${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
"machines",
"init-hardware-config",
"--debug",
"--flake", str(flake_dir),
"--yes", "test-install-machine-without-system",
"--host-key-check", "none",
"--target-host", f"nonrootuser@localhost:{ssh_conn.host_port}",
"-i", ssh_conn.ssh_key,
"--option", "store", os.environ['CLAN_TEST_STORE']
]
subprocess.run(clan_cmd, check=True)
# Run clan install from host using port forwarding # Run clan install from host using port forwarding
clan_cmd = [ clan_cmd = [
"${self.packages.${pkgs.system}.clan-cli-full}/bin/clan", "${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
@@ -296,7 +270,7 @@
# Prepare test flake and Nix store # Prepare test flake and Nix store
flake_dir = prepare_test_flake( flake_dir = prepare_test_flake(
temp_dir, temp_dir,
"${self.checks.${pkgs.hostPlatform.system}.clan-core-for-checks}", "${self.checks.x86_64-linux.clan-core-for-checks}",
"${closureInfo}" "${closureInfo}"
) )

View File

@@ -15,6 +15,7 @@ let
networking.useNetworkd = true; networking.useNetworkd = true;
services.openssh.enable = true; services.openssh.enable = true;
services.openssh.settings.UseDns = false; services.openssh.settings.UseDns = false;
services.openssh.settings.PasswordAuthentication = false;
system.nixos.variant_id = "installer"; system.nixos.variant_id = "installer";
environment.systemPackages = [ environment.systemPackages = [
pkgs.nixos-facter pkgs.nixos-facter
@@ -146,11 +147,28 @@ let
]; ];
doCheck = false; doCheck = false;
}; };
# Common closure info
closureInfo = pkgs.closureInfo {
rootPaths = [
self.checks.x86_64-linux.clan-core-for-checks
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.initialRamdisk
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
pkgs.stdenv.drvPath
pkgs.bash.drvPath
pkgs.buildPackages.xorg.lndir
]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
};
in in
{ {
inherit inherit
target target
baseTestMachine baseTestMachine
nixosTestLib nixosTestLib
closureInfo
; ;
} }

View File

@@ -1,82 +0,0 @@
{ self, pkgs, ... }:
let
cli = self.packages.${pkgs.hostPlatform.system}.clan-cli-full;
ollama-model = pkgs.callPackage ./qwen3-4b-instruct.nix { };
in
{
name = "llm";
nodes = {
peer1 =
{ pkgs, ... }:
{
users.users.text-user = {
isNormalUser = true;
linger = true;
uid = 1000;
extraGroups = [ "systemd-journal" ];
};
# Set environment variables for user systemd
environment.extraInit = ''
if [ "$(id -u)" = "1000" ]; then
export XDG_RUNTIME_DIR="/run/user/1000"
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus"
ollama_dir="$HOME/.ollama"
mkdir -p "$ollama_dir"
ln -sf ${ollama-model}/models "$ollama_dir"/models
fi
'';
# Enable PAM for user systemd sessions
security.pam.services.systemd-user = {
startSession = true;
# Workaround for containers - use pam_permit to avoid helper binary issues
text = pkgs.lib.mkForce ''
account required pam_permit.so
session required pam_permit.so
session required pam_env.so conffile=/etc/pam/environment readenv=0
session required ${pkgs.systemd}/lib/security/pam_systemd.so
'';
};
environment.systemPackages = [
cli
pkgs.ollama
(cli.pythonRuntime.withPackages (
ps: with ps; [
pytest
pytest-xdist
(cli.pythonRuntime.pkgs.toPythonModule cli)
self.legacyPackages.${pkgs.hostPlatform.system}.nixosTestLib
]
))
];
};
};
testScript =
{ ... }:
''
start_all()
peer1.wait_for_unit("multi-user.target")
peer1.wait_for_unit("user@1000.service")
# Fix user journal permissions so text-user can read their own logs
peer1.succeed("chown text-user:systemd-journal /var/log/journal/*/user-1000.journal*")
peer1.succeed("chmod 640 /var/log/journal/*/user-1000.journal*")
# the -o adopts="" is needed to overwrite any args coming from pyproject.toml
# -p no:cacheprovider disables pytest's cacheprovider which tries to write to the nix store in this case
cmd = "su - text-user -c 'pytest -s -n0 -m service_runner -p no:cacheprovider -o addopts="" ${cli.passthru.sourceWithTests}/clan_lib/llm'"
print("Running tests with command: " + cmd)
# Run tests as text-user (environment variables are set automatically)
peer1.succeed(cmd)
'';
}

View File

@@ -1,70 +0,0 @@
{ pkgs }:
let
# Got them from https://github.com/Gholamrezadar/ollama-direct-downloader
# Download manifest
manifest = pkgs.fetchurl {
url = "https://registry.ollama.ai/v2/library/qwen3/manifests/4b-instruct";
# You'll need to calculate this hash - run the derivation once and it will tell you the correct hash
hash = "sha256-Dtze80WT6sGqK+nH0GxDLc+BlFrcpeyi8nZiwY8Wi6A=";
};
# Download blobs
blob1 = pkgs.fetchurl {
url = "https://registry.ollama.ai/v2/library/qwen3/blobs/sha256:b72accf9724e93698c57cbd3b1af2d3341b3d05ec2089d86d273d97964853cd2";
hash = "sha256-tyrM+XJOk2mMV8vTsa8tM0Gz0F7CCJ2G0nPZeWSFPNI=";
};
blob2 = pkgs.fetchurl {
url = "https://registry.ollama.ai/v2/library/qwen3/blobs/sha256:85e4a5b7b8ef0e48af0e8658f5aaab9c2324c76c1641493f4d1e25fce54b18b9";
hash = "sha256-heSlt7jvDkivDoZY9aqrnCMkx2wWQUk/TR4l/OVLGLk=";
};
blob3 = pkgs.fetchurl {
url = "https://registry.ollama.ai/v2/library/qwen3/blobs/sha256:eade0a07cac7712787bbce23d12f9306adb4781d873d1df6e16f7840fa37afec";
hash = "sha256-6t4KB8rHcSeHu84j0S+TBq20eB2HPR324W94QPo3r+w=";
};
blob4 = pkgs.fetchurl {
url = "https://registry.ollama.ai/v2/library/qwen3/blobs/sha256:d18a5cc71b84bc4af394a31116bd3932b42241de70c77d2b76d69a314ec8aa12";
hash = "sha256-0YpcxxuEvErzlKMRFr05MrQiQd5wx30rdtaaMU7IqhI=";
};
blob5 = pkgs.fetchurl {
url = "https://registry.ollama.ai/v2/library/qwen3/blobs/sha256:0914c7781e001948488d937994217538375b4fd8c1466c5e7a625221abd3ea7a";
hash = "sha256-CRTHeB4AGUhIjZN5lCF1ODdbT9jBRmxeemJSIavT6no=";
};
in
pkgs.stdenv.mkDerivation {
pname = "ollama-qwen3-4b-instruct";
version = "1.0";
dontUnpack = true;
buildPhase = ''
mkdir -p $out/models/manifests/registry.ollama.ai/library/qwen3
mkdir -p $out/models/blobs
# Copy manifest
cp ${manifest} $out/models/manifests/registry.ollama.ai/library/qwen3/4b-instruct
# Copy blobs with correct names
cp ${blob1} $out/models/blobs/sha256-b72accf9724e93698c57cbd3b1af2d3341b3d05ec2089d86d273d97964853cd2
cp ${blob2} $out/models/blobs/sha256-85e4a5b7b8ef0e48af0e8658f5aaab9c2324c76c1641493f4d1e25fce54b18b9
cp ${blob3} $out/models/blobs/sha256-eade0a07cac7712787bbce23d12f9306adb4781d873d1df6e16f7840fa37afec
cp ${blob4} $out/models/blobs/sha256-d18a5cc71b84bc4af394a31116bd3932b42241de70c77d2b76d69a314ec8aa12
cp ${blob5} $out/models/blobs/sha256-0914c7781e001948488d937994217538375b4fd8c1466c5e7a625221abd3ea7a
'';
installPhase = ''
# buildPhase already created everything in $out
:
'';
meta = with pkgs.lib; {
description = "Qwen3 4B Instruct model for Ollama";
license = "apache-2.0";
platforms = platforms.all;
};
}

View File

@@ -35,6 +35,7 @@
pkgs.stdenv.drvPath pkgs.stdenv.drvPath
pkgs.stdenvNoCC pkgs.stdenvNoCC
self.nixosConfigurations.test-morph-machine.config.system.build.toplevel self.nixosConfigurations.test-morph-machine.config.system.build.toplevel
(import ../installation/facter-report.nix pkgs.hostPlatform.system)
] ]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs); ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
closureInfo = pkgs.closureInfo { rootPaths = dependencies; }; closureInfo = pkgs.closureInfo { rootPaths = dependencies; };

View File

@@ -29,11 +29,9 @@ nixosLib.runTest (
{ nodes, ... }: { nodes, ... }:
'' ''
import subprocess import subprocess
import tempfile from nixos_test_lib.nix_setup import setup_nix_in_nix # type: ignore[import-untyped]
from nixos_test_lib.nix_setup import setup_nix_in_nix
with tempfile.TemporaryDirectory() as temp_dir: setup_nix_in_nix(None) # No closure info for this test
setup_nix_in_nix(temp_dir, None) # No closure info for this test
start_all() start_all()
admin1.wait_for_unit("multi-user.target") admin1.wait_for_unit("multi-user.target")

View File

@@ -27,10 +27,7 @@
modules.new-service = { modules.new-service = {
_class = "clan.service"; _class = "clan.service";
manifest.name = "new-service"; manifest.name = "new-service";
manifest.readme = "Just a sample readme to not trigger the warning."; roles.peer = { };
roles.peer = {
description = "A peer that uses the new-service to generate some files.";
};
perMachine = { perMachine = {
nixosModule = { nixosModule = {
# This should be generated by: # This should be generated by:

View File

@@ -34,10 +34,7 @@ nixosLib.runTest (
modules.new-service = { modules.new-service = {
_class = "clan.service"; _class = "clan.service";
manifest.name = "new-service"; manifest.name = "new-service";
manifest.readme = "Just a sample readme to not trigger the warning."; roles.peer = { };
roles.peer = {
description = "A peer that uses the new-service to generate some files.";
};
perMachine = { perMachine = {
nixosModule = { nixosModule = {
# This should be generated by: # This should be generated by:

View File

@@ -1,67 +0,0 @@
{ self, pkgs, ... }:
let
cli = self.packages.${pkgs.hostPlatform.system}.clan-cli-full;
in
{
name = "systemd-abstraction";
nodes = {
peer1 = {
users.users.text-user = {
isNormalUser = true;
linger = true;
uid = 1000;
extraGroups = [ "systemd-journal" ];
};
# Set environment variables for user systemd
environment.extraInit = ''
if [ "$(id -u)" = "1000" ]; then
export XDG_RUNTIME_DIR="/run/user/1000"
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus"
fi
'';
# Enable PAM for user systemd sessions
security.pam.services.systemd-user = {
startSession = true;
# Workaround for containers - use pam_permit to avoid helper binary issues
text = pkgs.lib.mkForce ''
account required pam_permit.so
session required pam_permit.so
session required pam_env.so conffile=/etc/pam/environment readenv=0
session required ${pkgs.systemd}/lib/security/pam_systemd.so
'';
};
environment.systemPackages = [
cli
(cli.pythonRuntime.withPackages (
ps: with ps; [
pytest
pytest-xdist
]
))
];
};
};
testScript =
{ ... }:
''
start_all()
peer1.wait_for_unit("multi-user.target")
peer1.wait_for_unit("user@1000.service")
# Fix user journal permissions so text-user can read their own logs
peer1.succeed("chown text-user:systemd-journal /var/log/journal/*/user-1000.journal*")
peer1.succeed("chmod 640 /var/log/journal/*/user-1000.journal*")
# Run tests as text-user (environment variables are set automatically)
peer1.succeed("su - text-user -c 'pytest -p no:cacheprovider -o addopts="" -s -n0 ${cli.passthru.sourceWithTests}/clan_lib/service_runner'")
'';
}

View File

@@ -1,26 +0,0 @@
(
{ ... }:
{
name = "test-extra-python-packages";
extraPythonPackages = ps: [ ps.numpy ];
nodes.machine =
{ ... }:
{
networking.hostName = "machine";
};
testScript = ''
import numpy as np
start_all()
machine.wait_for_unit("multi-user.target")
# Test availability of numpy
arr = np.array([1, 2, 3])
print(f"Numpy array: {arr}")
assert len(arr) == 3
'';
}
)

View File

@@ -67,15 +67,6 @@
]; ];
}; };
nix.settings = {
flake-registry = "";
# required for setting the `flake-registry`
experimental-features = [
"nix-command"
"flakes"
];
};
# Define the mounts that exist in the container to prevent them from being stopped # Define the mounts that exist in the container to prevent them from being stopped
fileSystems = { fileSystems = {
"/" = { "/" = {
@@ -115,13 +106,13 @@
let let
closureInfo = pkgs.closureInfo { closureInfo = pkgs.closureInfo {
rootPaths = [ rootPaths = [
self.packages.${pkgs.hostPlatform.system}.clan-cli self.packages.${pkgs.system}.clan-cli
self.checks.${pkgs.hostPlatform.system}.clan-core-for-checks self.checks.${pkgs.system}.clan-core-for-checks
self.clanInternals.machines.${pkgs.hostPlatform.system}.test-update-machine.config.system.build.toplevel self.clanInternals.machines.${pkgs.hostPlatform.system}.test-update-machine.config.system.build.toplevel
pkgs.stdenv.drvPath pkgs.stdenv.drvPath
pkgs.bash.drvPath pkgs.bash.drvPath
pkgs.buildPackages.xorg.lndir pkgs.buildPackages.xorg.lndir
pkgs.bubblewrap (import ../installation/facter-report.nix pkgs.hostPlatform.system)
] ]
++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs); ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
}; };
@@ -132,7 +123,7 @@
imports = [ self.nixosModules.test-update-machine ]; imports = [ self.nixosModules.test-update-machine ];
}; };
extraPythonPackages = _p: [ extraPythonPackages = _p: [
self.legacyPackages.${pkgs.hostPlatform.system}.nixosTestLib self.legacyPackages.${pkgs.system}.nixosTestLib
]; ];
testScript = '' testScript = ''
@@ -154,7 +145,7 @@
# Prepare test flake and Nix store # Prepare test flake and Nix store
flake_dir = prepare_test_flake( flake_dir = prepare_test_flake(
temp_dir, temp_dir,
"${self.checks.${pkgs.hostPlatform.system}.clan-core-for-checks}", "${self.checks.x86_64-linux.clan-core-for-checks}",
"${closureInfo}" "${closureInfo}"
) )
(flake_dir / ".clan-flake").write_text("") # Ensure .clan-flake exists (flake_dir / ".clan-flake").write_text("") # Ensure .clan-flake exists
@@ -221,13 +212,12 @@
[ [
"${pkgs.nix}/bin/nix", "${pkgs.nix}/bin/nix",
"copy", "copy",
"--from",
f"{temp_dir}/store",
"--to", "--to",
"ssh://root@192.168.1.1", "ssh://root@192.168.1.1",
"--no-check-sigs", "--no-check-sigs",
f"${self.packages.${pkgs.hostPlatform.system}.clan-cli}", f"${self.packages.${pkgs.system}.clan-cli}",
"--extra-experimental-features", "nix-command flakes", "--extra-experimental-features", "nix-command flakes",
"--from", f"{os.environ["TMPDIR"]}/store"
], ],
check=True, check=True,
env={ env={
@@ -242,7 +232,7 @@
"-o", "UserKnownHostsFile=/dev/null", "-o", "UserKnownHostsFile=/dev/null",
"-o", "StrictHostKeyChecking=no", "-o", "StrictHostKeyChecking=no",
f"root@192.168.1.1", f"root@192.168.1.1",
"${self.packages.${pkgs.hostPlatform.system}.clan-cli}/bin/clan", "${self.packages.${pkgs.system}.clan-cli}/bin/clan",
"machines", "machines",
"update", "update",
"--debug", "--debug",
@@ -270,7 +260,7 @@
# Run clan update command # Run clan update command
subprocess.run([ subprocess.run([
"${self.packages.${pkgs.hostPlatform.system}.clan-cli-full}/bin/clan", "${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
"machines", "machines",
"update", "update",
"--debug", "--debug",
@@ -297,7 +287,7 @@
# Run clan update command with --build-host # Run clan update command with --build-host
subprocess.run([ subprocess.run([
"${self.packages.${pkgs.hostPlatform.system}.clan-cli-full}/bin/clan", "${self.packages.${pkgs.system}.clan-cli-full}/bin/clan",
"machines", "machines",
"update", "update",
"--debug", "--debug",

View File

@@ -0,0 +1,115 @@
{
pkgs,
nixosLib,
clan-core,
lib,
...
}:
nixosLib.runTest (
{ ... }:
let
machines = [
"controller1"
"controller2"
"peer1"
"peer2"
"peer3"
];
in
{
imports = [
clan-core.modules.nixosTest.clanTest
];
hostPkgs = pkgs;
name = "wireguard";
clan = {
directory = ./.;
modules."@clan/wireguard" = import ../../clanServices/wireguard/default.nix;
inventory = {
machines = lib.genAttrs machines (_: { });
instances = {
/*
wg-test-one
controller2 controller1
peer2 peer1 peer3
*/
wg-test-one = {
module.name = "@clan/wireguard";
module.input = "self";
roles.controller.machines."controller1".settings = {
endpoint = "192.168.1.1";
};
roles.controller.machines."controller2".settings = {
endpoint = "192.168.1.2";
};
roles.peer.machines = {
peer1.settings.controller = "controller1";
peer2.settings.controller = "controller2";
peer3.settings.controller = "controller1";
};
};
# TODO: Will this actually work with conflicting ports? Can we re-use interfaces?
#wg-test-two = {
# module.name = "@clan/wireguard";
# roles.controller.machines."controller1".settings = {
# endpoint = "192.168.1.1";
# port = 51922;
# };
# roles.peer.machines = {
# peer1 = { };
# };
#};
};
};
};
testScript = ''
start_all()
# Show all addresses
machines = [peer1, peer2, peer3, controller1, controller2]
for m in machines:
m.systemctl("start network-online.target")
for m in machines:
m.wait_for_unit("network-online.target")
m.wait_for_unit("systemd-networkd.service")
print("\n\n" + "="*60)
print("STARTING PING TESTS")
print("="*60)
for m1 in machines:
for m2 in machines:
if m1 != m2:
print(f"\n--- Pinging from {m1.name} to {m2.name}.wg-test-one ---")
m1.wait_until_succeeds(f"ping -c1 {m2.name}.wg-test-one >&2")
'';
}
)

View File

@@ -1,25 +0,0 @@
The admin service aggregates components that allow an administrator to log in to and manage the machine.
The following configuration:
1. Enables OpenSSH with root login and adds an SSH public key named`myusersKey` to the machine's authorized_keys via the `allowedKeys` setting.
2. Automatically generates a password for the root user.
```nix
instances = {
admin = {
roles.default.tags = {
all = { };
};
roles.default.settings = {
allowedKeys = {
myusersKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEFDNnynMbFWatSFdANzbJ8iiEKL7+9ZpDaMLrWRQjyH lhebendanz@wintux";
};
};
};
};
```

View File

@@ -1,15 +1,14 @@
{ {
_class = "clan.service"; _class = "clan.service";
manifest.name = "clan-core/admin"; manifest.name = "clan-core/admin";
manifest.description = "Adds a root user with ssh access"; manifest.description = "Convenient Administration for the Clan App";
manifest.categories = [ "Utility" ]; manifest.categories = [ "Utility" ];
manifest.readme = builtins.readFile ./README.md;
roles.default = { roles.default = {
description = "Placeholder role to apply the admin service";
interface = interface =
{ lib, ... }: { lib, ... }:
{ {
options = { options = {
allowedKeys = lib.mkOption { allowedKeys = lib.mkOption {
default = { }; default = { };

View File

@@ -2,7 +2,7 @@ let
public-key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII6zj7ubTg6z/aDwRNwvM/WlQdUocMprQ8E92NWxl6t+ test@test"; public-key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII6zj7ubTg6z/aDwRNwvM/WlQdUocMprQ8E92NWxl6t+ test@test";
in in
{ {
name = "admin"; name = "service-admin";
clan = { clan = {
directory = ./.; directory = ./.;

View File

@@ -9,7 +9,7 @@ inventory.instances = {
}; };
roles.client.machines."jon".settings = { roles.client.machines."jon".settings = {
destinations."storagebox" = { destinations."storagebox" = {
repo = "username@hostname:/./borgbackup"; repo = "username@$hostname:/./borgbackup";
rsh = ''ssh -oPort=23 -i /run/secrets/vars/borgbackup/borgbackup.ssh''; rsh = ''ssh -oPort=23 -i /run/secrets/vars/borgbackup/borgbackup.ssh'';
}; };
}; };

View File

@@ -9,7 +9,7 @@
# TODO: a client can only be in one instance, add constraint # TODO: a client can only be in one instance, add constraint
roles.server = { roles.server = {
description = "A borgbackup server that stores the backups of clients.";
interface = interface =
{ lib, ... }: { lib, ... }:
{ {
@@ -54,7 +54,7 @@
authorizedKeys = [ (builtins.readFile (borgbackupIpMachinePath machineName)) ]; authorizedKeys = [ (builtins.readFile (borgbackupIpMachinePath machineName)) ];
# }; # };
# }) machinesWithKey; # }) machinesWithKey;
}) (roles.client.machines or { }); }) roles.client.machines;
in in
hosts; hosts;
}; };
@@ -62,7 +62,6 @@
}; };
roles.client = { roles.client = {
description = "A borgbackup client that backs up to all borgbackup server roles.";
interface = interface =
{ {
lib, lib,
@@ -188,7 +187,7 @@
config.clan.core.vars.generators.borgbackup.files."borgbackup.ssh".path config.clan.core.vars.generators.borgbackup.files."borgbackup.ssh".path
} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=Yes"; } -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=Yes";
}; };
}) (builtins.attrNames (roles.server.machines or { })); }) (builtins.attrNames roles.server.machines);
in in
(builtins.listToAttrs destinations); (builtins.listToAttrs destinations);

View File

@@ -3,7 +3,7 @@
... ...
}: }:
{ {
name = "borgbackup"; name = "service-borgbackup";
clan = { clan = {
directory = ./.; directory = ./.;

View File

@@ -2,12 +2,12 @@
{ {
_class = "clan.service"; _class = "clan.service";
manifest.name = "certificates"; manifest.name = "certificates";
manifest.description = "Sets up a PKI certificate chain using step-ca"; manifest.description = "Sets up a certificates internal to your Clan";
manifest.categories = [ "Network" ]; manifest.categories = [ "Network" ];
manifest.readme = builtins.readFile ./README.md; manifest.readme = builtins.readFile ./README.md;
roles.ca = { roles.ca = {
description = "A certificate authority that issues and signs certificates for other machines.";
interface = interface =
{ lib, ... }: { lib, ... }:
{ {
@@ -184,7 +184,6 @@
# Empty role, so we can add non-ca machins to the instance to trust the CA # Empty role, so we can add non-ca machins to the instance to trust the CA
roles.default = { roles.default = {
description = "A machine that trusts the CA and can get certificates issued by it.";
interface = interface =
{ lib, ... }: { lib, ... }:
{ {

View File

@@ -45,15 +45,13 @@ inventory = {
# Add the default role to all machines, including `client` # Add the default role to all machines, including `client`
roles.default.tags.all = { }; roles.default.tags.all = { };
# DNS server queries to http://<name>.foo are resolved here # DNS server
roles.server.machines."dnsserver".settings = { roles.server.machines."dnsserver".settings = {
ip = "192.168.1.2"; ip = "192.168.1.2";
tld = "foo"; tld = "foo";
}; };
# First service # First service
# Registers http://one.foo will resolve to 192.168.1.3
# underlying service runs on server01
roles.default.machines."server01".settings = { roles.default.machines."server01".settings = {
ip = "192.168.1.3"; ip = "192.168.1.3";
services = [ "one" ]; services = [ "one" ];

View File

@@ -8,7 +8,7 @@
manifest.readme = builtins.readFile ./README.md; manifest.readme = builtins.readFile ./README.md;
roles.server = { roles.server = {
description = "A DNS server that resolves services in the clan network.";
interface = interface =
{ lib, ... }: { lib, ... }:
{ {
@@ -103,7 +103,6 @@
}; };
roles.default = { roles.default = {
description = "A machine that registers the 'server' role as resolver and registers services under the configured TLD in the resolver.";
interface = interface =
{ lib, ... }: { lib, ... }:
{ {

View File

@@ -1,7 +1,4 @@
{ { ... }:
clanLib,
...
}:
let let
sharedInterface = sharedInterface =
{ lib, ... }: { lib, ... }:
@@ -54,15 +51,15 @@ let
builtins.foldl' ( builtins.foldl' (
urls: name: urls: name:
let let
ip = clanLib.vars.getPublicValue { ipPath = "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value";
flake = config.clan.core.settings.directory;
machine = name;
generator = "zerotier";
file = "zerotier-ip";
default = null;
};
in in
if ip != null then urls ++ [ "[${ip}]:${builtins.toString settings.network.port}" ] else urls if builtins.pathExists ipPath then
let
ip = builtins.readFile ipPath;
in
urls ++ [ "[${ip}]:${builtins.toString settings.network.port}" ]
else
urls
) [ ] (builtins.attrNames ((roles.admin.machines or { }) // (roles.signer.machines or { }))) ) [ ] (builtins.attrNames ((roles.admin.machines or { }) // (roles.signer.machines or { })))
); );
@@ -104,7 +101,6 @@ in
manifest.readme = builtins.readFile ./README.md; manifest.readme = builtins.readFile ./README.md;
roles.admin = { roles.admin = {
description = "A data-mesher admin node that bootstraps the network and can sign new nodes into the network.";
interface = interface =
{ lib, ... }: { lib, ... }:
{ {
@@ -159,14 +155,9 @@ in
readHostKey = readHostKey =
machine: machine:
let let
publicKey = clanLib.vars.getPublicValue { path = "${config.clan.core.settings.directory}/vars/per-machine/${machine}/data-mesher-host-key/public_key/value";
flake = config.clan.core.settings.directory;
inherit machine;
generator = "data-mesher-host-key";
file = "public_key";
};
in in
builtins.elemAt (lib.splitString "\n" publicKey) 1; builtins.elemAt (lib.splitString "\n" (builtins.readFile path)) 1;
in in
{ {
enable = true; enable = true;
@@ -186,7 +177,6 @@ in
}; };
roles.signer = { roles.signer = {
description = "A data-mesher signer node that can sign new nodes into the network.";
interface = sharedInterface; interface = sharedInterface;
perInstance = perInstance =
{ {
@@ -218,7 +208,6 @@ in
}; };
roles.peer = { roles.peer = {
description = "A data-mesher peer node that connects to the network.";
interface = sharedInterface; interface = sharedInterface;
perInstance = perInstance =
{ {

View File

@@ -9,7 +9,7 @@ in
perSystem = perSystem =
{ ... }: { ... }:
{ {
clan.nixosTests.data-mesher = { clan.nixosTests.service-data-mesher = {
imports = [ ./tests/vm/default.nix ]; imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/data-mesher" = module; clan.modules."@clan/data-mesher" = module;
}; };

View File

@@ -2,7 +2,7 @@
... ...
}: }:
{ {
name = "data-mesher"; name = "service-data-mesher";
clan = { clan = {
directory = ./.; directory = ./.;

View File

@@ -2,12 +2,11 @@
{ {
_class = "clan.service"; _class = "clan.service";
manifest.name = "clan-core/dyndns"; manifest.name = "clan-core/dyndns";
manifest.description = "A dynamic DNS service to auto update domain IPs"; manifest.description = "A dynamic DNS service to update domain IPs";
manifest.categories = [ "Network" ]; manifest.categories = [ "Network" ];
manifest.readme = builtins.readFile ./README.md; manifest.readme = builtins.readFile ./README.md;
roles.default = { roles.default = {
description = "Placeholder role to apply the dyndns service";
interface = interface =
{ lib, ... }: { lib, ... }:
{ {

View File

@@ -3,7 +3,7 @@
... ...
}: }:
{ {
name = "dyndns"; name = "service-dyndns";
clan = { clan = {
directory = ./.; directory = ./.;

View File

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

View File

@@ -1,14 +0,0 @@
{
"data": "ENC[AES256_GCM,data:seLxbv590dO0KvMJmtN7WVvUcH27VYwAc3rmyD7q6ZmwCgswOKx55LFnh0stRDKSZa8K7Dq1x7D9adhZtPAMWX8tbJswBeNMPt8=,iv:G52eugxfTi0tTzH4EN4CWmpyv6feSL34++UVSjb0aAo=,tag:6r10/a7kD2hBAmae0nz2OQ==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHVC8wZUZJYUl5MXVNa2k5\ndGV1MnFWbUNLNVdxeEtCVUc3MTd0ck9aeFFBCnFhZW40amVYc3FlN1FPRTFSWTJR\nQzhNOERKbnRnSlJVeElNSEM5ZUJsZGsKLS0tIG1uNnlNN3MweHlYczNRTW9xSytu\neThzUmxKZTJBT2lCcTdiNUI4N3paTVEKgS9j2/GVt1KBoggUj9d6UK/mIlK4niLQ\nzVq2BHt3irxQpkpGUogXH2b86zSAOEJFzsL1Rk8HM1mogTG8jqf0qA==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-10-19T12:49:11Z",
"mac": "ENC[AES256_GCM,data:T/2xw2mvUi8YALyxz78qG/g/xguoUTeHNzcZfXwwSyCXMg9ircsGGLO9SOVWy/QNkibnw3Yp80tXNJyr4oJH28PhFH7RrRp8jzNdopF49ZNJb2IqJ3C7xNYRZMHfjOCd/raka+ehZq8YGilEpXUWLRk1ere9lbBMh1ycL7jJS3c=,iv:FZbY/jTNPM+p4qD41FD0K7B9zoppGuvnUY5hL/EkmYM=,tag:IF5QTyUkHXWthlAGBn9R8w==,type:str]",
"version": "3.11.0"
}
}

View File

@@ -1,18 +0,0 @@
{
"data": "ENC[AES256_GCM,data:Zu+n+DDYP7rQRTS17PJ6Apo=,iv:5WOs81Pj+S85kdC1AlOXSyPMGDfwM5UD8x7nyRZtRYQ=,tag:2JYkGnLugAni49Upv43o2g==,type:str]",
"sops": {
"age": [
{
"recipient": "age164wrhlnake7f7duhzs936lq6w49dtg53hcdyxqwxj0agad6tqg2s2u4yta",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlR3RGQ2ZLTkR3ZWxNVCsv\naXJHRjBiVUVYZVRIY2swY2xubGhmb3pLRkNvCldhQUV2WDlqYjZ4ZUFWYXkvUEEw\nZi9XRWw0Mi9mRENDcnI0aENDR2Z4MHcKLS0tIGFQU3Q4WEErbnBjOHpNR1BSR2cr\nRFg0anE1cHExT0sySmxuUks1R05nczAKZO3R6+f9co2+YGO8HPufoq1fLqqrdTWD\n4zqemMmG2BjMRDumxtcKp8CLaZWlJoP4e/+tonfdoe42qmNF5NJcFw==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzZWo4WGh1cWxKeDhDdlBm\nTVFjVFBIUU9xaGRkanNHaUVUUHN1czNRSUhNCkp5MmwzSGdycmsrZGhaRUhEbXBF\nNUhtdEF6bHZQOGJYUVhFVHlYc3FPODAKLS0tIDBRQ2VGT2IvU1F4MEVabzhYSFJq\nOWZmbGpkQmNSMnNKa0s4K2JXdGgwRlkKUQRREpG5H1mNHSc/cZrdMiSz0veJFR4N\n+W49XL/wQUZwajykwYj++G+dWDO7DQ+fpbB9w4mzbsAmCsXirseTLA==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-10-19T12:49:11Z",
"mac": "ENC[AES256_GCM,data:0msda7WbQQxXQ+juT7yErgT7NADgnzqEZLTQw+4JPuAE4xcqRIYwrrAALaA0GCCM2aIWlICzJigLCuzQUfSUbIzeP79tEHiKez+NOt/xgSM9ljz7GlsmLd0vzkxdt3WSxP+sHxy0S866N2sLMUkLqPGdqeTjB+Jji5ghGhzk9ys=,iv:8UU7iA4SdR6ZlVolm708l2Iea0sQYRT+5wPBBP5tpS0=,tag:VQXslAlqLqs1QEkwW6x6qg==,type:str]",
"version": "3.11.0"
}
}

View File

@@ -2,13 +2,11 @@
{ {
_class = "clan.service"; _class = "clan.service";
manifest.name = "clan-core/emergency-access"; manifest.name = "clan-core/emergency-access";
manifest.description = "Set recovery password for emergency access to machine to debug boot issues"; manifest.description = "Set recovery password for emergency access to machine";
manifest.categories = [ "System" ]; manifest.categories = [ "System" ];
manifest.readme = builtins.readFile ./README.md; manifest.readme = builtins.readFile ./README.md;
roles.default = { roles.default.perInstance = {
description = "Placeholder role to apply the emergency-access service";
perInstance = {
nixosModule = nixosModule =
{ config, pkgs, ... }: { config, pkgs, ... }:
{ {
@@ -31,5 +29,4 @@
}; };
}; };
}; };
};
} }

View File

@@ -1,12 +0,0 @@
[Garage](https://garagehq.deuxfleurs.fr/) is an open-source, S3-compatible distributed object storage service for self-hosting.
This module provisions a single-instance S3 bucket. To customize its behavior, set `services.garage.settings` in your Nix configuration.
Example configuration:
```
instances = {
garage = {
roles.default.machines."server" = {};
};
};
```

View File

@@ -4,10 +4,9 @@
manifest.name = "clan-core/garage"; manifest.name = "clan-core/garage";
manifest.description = "S3-compatible object store for small self-hosted geo-distributed deployments"; manifest.description = "S3-compatible object store for small self-hosted geo-distributed deployments";
manifest.categories = [ "System" ]; manifest.categories = [ "System" ];
manifest.readme = builtins.readFile ./README.md;
roles.default = { roles.default = {
description = "Placeholder role to apply the garage service";
perInstance.nixosModule = perInstance.nixosModule =
{ {
config, config,

View File

@@ -3,7 +3,7 @@
... ...
}: }:
{ {
name = "garage"; name = "service-garage";
clan = { clan = {
directory = ./.; directory = ./.;

View File

@@ -1 +0,0 @@
This a test README just to appease the eval warnings if we don't have one

View File

@@ -9,13 +9,11 @@
_class = "clan.service"; _class = "clan.service";
manifest.name = "clan-core/hello-word"; manifest.name = "clan-core/hello-word";
manifest.description = "This is a test"; manifest.description = "This is a test";
manifest.readme = builtins.readFile ./README.md;
# This service provides two roles: "morning" and "evening". Roles can be # This service provides two roles: "morning" and "evening". Roles can be
# defined in this file directly (e.g. the "morning" role) or split up into a # defined in this file directly (e.g. the "morning" role) or split up into a
# separate file (e.g. the "evening" role) # separate file (e.g. the "evening" role)
roles.morning = { roles.morning = {
description = "A morning greeting machine";
interface = interface =
{ lib, ... }: { lib, ... }:
{ {
@@ -35,13 +33,10 @@
settings, settings,
# The name of this instance of the service # The name of this instance of the service
instanceName,
# The current machine # The current machine
machine,
# All roles of this service, with their assigned machines # All roles of this service, with their assigned machines
roles,
... ...
}: }:
{ {
@@ -72,7 +67,6 @@
# the interface here, so we can see all settings of the service in one place, # the interface here, so we can see all settings of the service in one place,
# but you can also move it to the respective file # but you can also move it to the respective file
roles.evening = { roles.evening = {
description = "An evening greeting machine";
interface = interface =
{ lib, ... }: { lib, ... }:
{ {

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