Compare commits

..

4 Commits

Author SHA1 Message Date
Qubasa
efdae17663 clan-app: Fix incorrect handling of --target-host in update 2025-06-17 14:09:10 +02:00
Qubasa
0a8bd26c77 clan-app: Fix UI errors 2025-06-17 14:09:10 +02:00
Qubasa
521d9bcd28 genmoon.py: Fix type error 2025-06-17 14:09:10 +02:00
Qubasa
6a3bffad42 clan-lib: Refactor remote host handling to function parameters
This refactoring improves the separation of concerns by moving remote host creation logic from the Machine class to the calling functions, making the code more flexible and testable.
2025-06-17 14:09:10 +02:00
330 changed files with 15480 additions and 11008 deletions

11
.gitignore vendored
View File

@@ -3,6 +3,7 @@
.hypothesis
out.log
.coverage.*
qubeclan
pkgs/repro-hook
testdir
democlan
@@ -19,6 +20,9 @@ nixos.qcow2
# macOS stuff
.DS_Store
# dream2nix
.dream2nix
# python
__pycache__
.coverage
@@ -28,6 +32,13 @@ __pycache__
.ruff_cache
htmlcov
# flatpak
.flatpak-builder
build
build-dir
repo
.env
# node
node_modules
dist

View File

@@ -36,23 +36,29 @@ in
inherit (self) clanLib;
clan-core = self;
};
nixosTests = lib.optionalAttrs (pkgs.stdenv.isLinux) {
nixosTests =
lib.optionalAttrs (pkgs.stdenv.isLinux) {
# Base Tests
secrets = self.clanLib.test.baseTest ./secrets nixosTestArgs;
borgbackup-legacy = self.clanLib.test.baseTest ./borgbackup-legacy nixosTestArgs;
wayland-proxy-virtwl = self.clanLib.test.baseTest ./wayland-proxy-virtwl nixosTestArgs;
# Base Tests
secrets = self.clanLib.test.baseTest ./secrets nixosTestArgs;
borgbackup-legacy = self.clanLib.test.baseTest ./borgbackup-legacy 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;
dummy-inventory-test = import ./dummy-inventory-test nixosTestArgs;
dummy-inventory-test-from-flake = import ./dummy-inventory-test-from-flake nixosTestArgs;
data-mesher = import ./data-mesher nixosTestArgs;
};
dummy-inventory-test = import ./dummy-inventory-test nixosTestArgs;
dummy-inventory-test-from-flake = import ./dummy-inventory-test-from-flake nixosTestArgs;
data-mesher = import ./data-mesher 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
@@ -106,9 +112,6 @@ in
cp ${../flake.lock} $out/flake.lock
'';
};
packages = lib.optionalAttrs (pkgs.stdenv.isLinux) {
run-vm-test-offline = pkgs.callPackage ../pkgs/run-vm-test-offline { };
};
legacyPackages = {
nixosTests =
let

View File

@@ -28,12 +28,6 @@
ROOT=$(git rev-parse --show-toplevel)
cd "$ROOT/pkgs/clan-cli"
# Set up custom git configuration for tests
export GIT_CONFIG_GLOBAL=$(mktemp)
git config --file "$GIT_CONFIG_GLOBAL" user.name "Test User"
git config --file "$GIT_CONFIG_GLOBAL" user.email "test@example.com"
export GIT_CONFIG_SYSTEM=/dev/null
# this disables dynamic dependency loading in clan-cli
export CLAN_NO_DYNAMIC_DEPS=1
@@ -43,9 +37,6 @@
jobs="$((jobs > 13 ? 13 : jobs))"
nix develop "$ROOT#clan-cli" -c bash -c "TMPDIR=/tmp python -m pytest -n $jobs -m impure ./clan_cli $@"
# Clean up temporary git config
rm -f "$GIT_CONFIG_GLOBAL"
'';
};
}

132
checks/mumble/default.nix Normal file
View File

@@ -0,0 +1,132 @@
{
pkgs,
nixosLib,
clan-core,
lib,
...
}:
nixosLib.runTest (
{ ... }:
let
machines = [
"peer1"
"peer2"
];
in
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
hostPkgs = pkgs;
name = "mumble";
defaults =
{ pkgs, modulesPath, ... }:
{
imports = [
(modulesPath + "/../tests/common/x11.nix")
];
clan.services.mumble.user = "alice";
environment.systemPackages = [ pkgs.killall ];
};
clan = {
directory = ./.;
# TODO: container driver does not support: sleep, wait_for_window, send_chars, wait_for_text
test.useContainers = false;
inventory = {
machines = lib.genAttrs machines (_: { });
services = {
mumble.default = {
roles.server.machines = machines;
};
};
};
};
enableOCR = true;
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"):
peer1.wait_for_x()
peer2.wait_for_x()
with subtest("Waiting for murmur"):
peer1.wait_for_unit("murmur.service")
peer2.wait_for_unit("murmur.service")
with subtest("Starting Mumble"):
# starting mumble is blocking
peer1.execute("mumble >&2 &")
peer2.execute("mumble >&2 &")
with subtest("Wait for 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")
for i in range(20):
time.sleep(1)
peer1.send_chars("\n")
peer1.send_chars("\n")
peer2.send_chars("\n")
peer2.send_chars("\n")
if machine_has_text(peer1, r"Mumble Server Connect") and \
machine_has_text(peer2, r"Mumble Server Connect"):
break
else:
raise Exception("Timeout waiting for certificate creation")
with subtest("Check validity of server certificates"):
peer1.execute("killall .mumble-wrapped")
peer1.sleep(1)
peer1.execute("mumble mumble://peer2 >&2 &")
peer1.wait_for_window(r"Mumble")
for i in range(20):
time.sleep(1)
peer1.send_chars("\n")
peer1.send_chars("\n")
if machine_has_text(peer1, "Connected."):
break
else:
raise Exception("Timeout waiting for certificate creation")
peer2.execute("killall .mumble-wrapped")
peer2.sleep(1)
peer2.execute("mumble mumble://peer1 >&2 &")
peer2.wait_for_window(r"Mumble")
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")
'';
}
)

View File

@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUCUjfNkF0CDhTKbO3nNczcsCW4qEwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA2MjcwOTM2NDZaFw0yNDA3
MjcwOTM2NDZaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDCcdZEJvXJIeOKO5pF5XUFvUeJtCCiwfWvWS662bxc
R/5MZucRLqfTNYo9aBv4NITw5kxZsTaaubmS4zSGQoTEAVzqzVdi3a/gNvsdVLb+
7CivpmweLllX/OGsTL0kHPEI+74AYiTBjXfdWV1Y5T1tuwc3G8ATrguQ33Uo5vvF
vcqsbTKcRZC0pB9O/nn4q03GsRdvlpaKakIhjMpRG/uZ3u7wtbyZ+WqjsjxZNfnY
aMyPoaipFqX1v+L7GKlOj2NpyEZFVVwa2ZqhVSYXyDfpAWQFznwKGzD5mjtcyKym
gnv/5LwrpH4Xj+JMt48hN+rPnu5vfXT8Y4KnID30OQW7AgMBAAGjUzBRMB0GA1Ud
DgQWBBQBBO8Wp975pAGioMjkaxANAVInfzAfBgNVHSMEGDAWgBQBBO8Wp975pAGi
oMjkaxANAVInfzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAg
F40MszTZXpR/A1z9B1CcXH47tNK67f8bCMR2dhvXODbpatwSihyxhQjtLb5R6kYH
5Yq/B4yrh303j0CXaobCQ4nQH7zI7fhViww+TzW7vDhgM7ueEyyXrqCXt6JY8avg
TuvIRtJSeWSQJ5aLNaYqmiwMf/tj9W3BMDpctGyLqu1WTSrbpYa9mA5Vudud70Yz
DgZ/aqHilB07cVNqzVYZzRZ56WJlTjGzVevRgnHZqPiZNVrU13H6gtWa3r8aV4Gj
i4F663eRAttj166cRgfl1QqpSG2IprNyV9UfuS2LlUaVNT3y0idawiJ4HhaA8pGB
ZqMUUkA4DSucb6xxEcTK
-----END CERTIFICATE-----

View File

@@ -0,0 +1 @@
AGE-SECRET-KEY-1UCXEUJH6JXF8LFKWFHDM4N9AQE2CCGQZGXLUNV4TKR5KY0KC8FDQ2TY4NX

View File

@@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICHTCCAaKgAwIBAgIIT2gZuvqVFP0wCgYIKoZIzj0EAwIwSjESMBAGA1UEChMJ
U3luY3RoaW5nMSAwHgYDVQQLExdBdXRvbWF0aWNhbGx5IEdlbmVyYXRlZDESMBAG
A1UEAxMJc3luY3RoaW5nMB4XDTIzMTIwNjAwMDAwMFoXDTQzMTIwMTAwMDAwMFow
SjESMBAGA1UEChMJU3luY3RoaW5nMSAwHgYDVQQLExdBdXRvbWF0aWNhbGx5IEdl
bmVyYXRlZDESMBAGA1UEAxMJc3luY3RoaW5nMHYwEAYHKoZIzj0CAQYFK4EEACID
YgAEBAr1CsciwCa0vi7eC6xxuSGijY3txbjtsyFanec/fge4oJBD3rVpaLKFETb3
TvHHsuvblzElcP483MEVq6FMUoxwuL9CzTtpJrRhtwSmAs8AHLFu8irVn8sZjgkL
sXMho1UwUzAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsG
AQUFBwMCMAwGA1UdEwEB/wQCMAAwFAYDVR0RBA0wC4IJc3luY3RoaW5nMAoGCCqG
SM49BAMCA2kAMGYCMQDbrtLgfcyMMIkNQn+PJe9DHYAqj8C47LQcWuIY/nekhOu0
aUfKctEAwyBtI60Y5zcCMQCEdgD/6CNBh7Qqq3z3CKPhlrpxHtCO5tNw17k0jfdH
haCwJInHZvZgclHk4EtFpTw=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDA14Nqo17Xs/xRLGH2KLuyzjKp4eW9iWFobVNM93RZZbECT++W3XcQc
cEc5WVtiPmWgBwYFK4EEACKhZANiAAQECvUKxyLAJrS+Lt4LrHG5IaKNje3FuO2z
IVqd5z9+B7igkEPetWlosoURNvdO8cey69uXMSVw/jzcwRWroUxSjHC4v0LNO2km
tGG3BKYCzwAcsW7yKtWfyxmOCQuxcyE=
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUfENbTtH5nr7giuawwQpDYqUpWJswDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA2MjcwOTQxNDNaFw0yNDA3
MjcwOTQxNDNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCfP6cZhCs9jOnWqyQP12vrOOxlBrWofYZFf9amUA24
AfE7oGcSfkylanmkxzvGqQkhgLAvkHZj/GEvHujKyy8PgcEGP+pwmsfWNQMvU0Dz
j3syjWOTi3eIC/3DoUnHlWCT2qCil/bjqxgU1l7fO/OXUlq5kyvIjln7Za4sUHun
ixe/m96Er6l8a4Mh2pxh2C5pkLCvulkQhjjGG+R6MccH8wwQwmLg5oVBkFEZrnRE
pnRKBI0DvA+wk1aJFAPOI4d8Q5T7o/MyxH3f8TYGHqbeMQFCKwusnlWPRtrNdaIc
gaLvSpR0LVlroXGu8tYmRpvHPByoKGDbgVvO0Bwx8fmRAgMBAAGjUzBRMB0GA1Ud
DgQWBBR7r+mQWNUZ0TpQNwrwjgxgngvOjTAfBgNVHSMEGDAWgBR7r+mQWNUZ0TpQ
NwrwjgxgngvOjTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCO
7B4s6uQEGE8jg3CQgy76oU/D8sazGcP8+/E4JLHSc0Nj49w4ztSpkOVk2HyEtzbm
uR3TreIw+SfqpbiOI/ivVNDbEBsb/vEeq7qPzDH1Bi72plHZNRVhNGGV5rd7ibga
TkfXHKPM9yt8ffffHHiu1ROvb8gg2B6JbQwboU4hvvmmorW7onyTFSYEzZVdNSpv
pUtKPldxYjTnLlbsJdXC4xyCC4PrJt2CC0n0jsWfICJ77LMxIxTODh8oZNjbPg6r
RdI7U/DsD+R072DjbIcrivvigotJM+jihzz5inZwbO8o0WQOHAbJLIG3C3BnRW3A
Ek4u3+HXZMl5a0LGJ76u
-----END CERTIFICATE-----

View File

@@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICHjCCAaOgAwIBAgIJAKbMWefkf1rVMAoGCCqGSM49BAMCMEoxEjAQBgNVBAoT
CVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBHZW5lcmF0ZWQxEjAQ
BgNVBAMTCXN5bmN0aGluZzAeFw0yMzEyMDYwMDAwMDBaFw00MzEyMDEwMDAwMDBa
MEoxEjAQBgNVBAoTCVN5bmN0aGluZzEgMB4GA1UECxMXQXV0b21hdGljYWxseSBH
ZW5lcmF0ZWQxEjAQBgNVBAMTCXN5bmN0aGluZzB2MBAGByqGSM49AgEGBSuBBAAi
A2IABFZTMt4RfsfBue0va7QuNdjfXMI4HfZzJCEcG+b9MtV7FlDmwMKX5fgGykD9
FBbC7yiza3+xCobdMb5bakz1qYJ7nUFCv1mwSDo2eNM+/XE+rJmlre8NwkwGmvzl
h1uhyqNVMFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr
BgEFBQcDAjAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCXN5bmN0aGluZzAKBggq
hkjOPQQDAgNpADBmAjEAwzhsroN6R4/quWeXj6dO5gt5CfSTLkLee6vrcuIP5i1U
rZvJ3OKQVmmGG6IWYe7iAjEAyuq3X2wznaqiw2YK3IDI4qVeYWpCUap0fwRNq7/x
4dC4k+BOzHcuJOwNBIY/bEuK
-----END CERTIFICATE-----

View File

@@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDCXHGpvumKjjDRxB6SsjZOb7duw3w+rdlGQCJTIvRThLjD6zwjnyImi
7c3PD5nWtLqgBwYFK4EEACKhZANiAARWUzLeEX7HwbntL2u0LjXY31zCOB32cyQh
HBvm/TLVexZQ5sDCl+X4BspA/RQWwu8os2t/sQqG3TG+W2pM9amCe51BQr9ZsEg6
NnjTPv1xPqyZpa3vDcJMBpr85Ydboco=
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1 @@
AGE-SECRET-KEY-1UCXEUJH6JXF8LFKWFHDM4N9AQE2CCGQZGXLUNV4TKR5KY0KC8FDQ2TY4NX

View File

@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUCUjfNkF0CDhTKbO3nNczcsCW4qEwDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA2MjcwOTM2NDZaFw0yNDA3
MjcwOTM2NDZaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDCcdZEJvXJIeOKO5pF5XUFvUeJtCCiwfWvWS662bxc
R/5MZucRLqfTNYo9aBv4NITw5kxZsTaaubmS4zSGQoTEAVzqzVdi3a/gNvsdVLb+
7CivpmweLllX/OGsTL0kHPEI+74AYiTBjXfdWV1Y5T1tuwc3G8ATrguQ33Uo5vvF
vcqsbTKcRZC0pB9O/nn4q03GsRdvlpaKakIhjMpRG/uZ3u7wtbyZ+WqjsjxZNfnY
aMyPoaipFqX1v+L7GKlOj2NpyEZFVVwa2ZqhVSYXyDfpAWQFznwKGzD5mjtcyKym
gnv/5LwrpH4Xj+JMt48hN+rPnu5vfXT8Y4KnID30OQW7AgMBAAGjUzBRMB0GA1Ud
DgQWBBQBBO8Wp975pAGioMjkaxANAVInfzAfBgNVHSMEGDAWgBQBBO8Wp975pAGi
oMjkaxANAVInfzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAg
F40MszTZXpR/A1z9B1CcXH47tNK67f8bCMR2dhvXODbpatwSihyxhQjtLb5R6kYH
5Yq/B4yrh303j0CXaobCQ4nQH7zI7fhViww+TzW7vDhgM7ueEyyXrqCXt6JY8avg
TuvIRtJSeWSQJ5aLNaYqmiwMf/tj9W3BMDpctGyLqu1WTSrbpYa9mA5Vudud70Yz
DgZ/aqHilB07cVNqzVYZzRZ56WJlTjGzVevRgnHZqPiZNVrU13H6gtWa3r8aV4Gj
i4F663eRAttj166cRgfl1QqpSG2IprNyV9UfuS2LlUaVNT3y0idawiJ4HhaA8pGB
ZqMUUkA4DSucb6xxEcTK
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDCcdZEJvXJIeOK
O5pF5XUFvUeJtCCiwfWvWS662bxcR/5MZucRLqfTNYo9aBv4NITw5kxZsTaaubmS
4zSGQoTEAVzqzVdi3a/gNvsdVLb+7CivpmweLllX/OGsTL0kHPEI+74AYiTBjXfd
WV1Y5T1tuwc3G8ATrguQ33Uo5vvFvcqsbTKcRZC0pB9O/nn4q03GsRdvlpaKakIh
jMpRG/uZ3u7wtbyZ+WqjsjxZNfnYaMyPoaipFqX1v+L7GKlOj2NpyEZFVVwa2Zqh
VSYXyDfpAWQFznwKGzD5mjtcyKymgnv/5LwrpH4Xj+JMt48hN+rPnu5vfXT8Y4Kn
ID30OQW7AgMBAAECggEAGVKn+/Iy+kG+l2cRvV6XseqnoWhjA69M5swviMgIfuAl
Xx/boeI4mwoS+dJQKi/0zEbB1MB+gwIDB/0s/vs0vS4MQswBQG/skr+2TmiU+Hgb
CF0dIYUZv5rAbScFTumx/mCCqxwc+1QIMzyLKqOYL203EFc92ZJGEVT4th321haZ
8Wd+dllcYAb7BbEeBhCrTqRe9T3zt5reZgtZTquTF5hGm8EAyBp6rLjZK7dyZ9dd
gyIsDbWgPC9vkRc6x/eANn70hgDbYOuoXwAP/qIFnWLL1Zzy8LKUyOsSgQ91S3S3
Il4Lt6lEyU3+61MsCYss7jDoP/7REEjz5h6gfxlFSQKBgQD9u8nhHuwte4/d9VNU
rhSBW9h8IJzwPif/eS8vh9VaS2SjR2dDCcHg6rGYKnexeEzUcx56aQMA+p3nRJwy
Uwnx5BfEWs9FO6yPR8VEI0a2sBp+hoWKJX/Lvat+QCs6IFuGmlQpczD7/RYAkhG4
mwyt/ymqzjukb9mFaeYIltOfPwKBgQDELnkH1ChTUH5u3HgDoelFbzR18okz6dxH
urMbfZMAl8W5h2zAvHsAX5qxyHHankOUsiH2y3BrAgqQtTuIA2a5W7j+yHBkYiEZ
EUNeI9YNA0KU+wwZpVVvRGUsRB5SUBo5LlcSYmX/V32f0oU5Np44i0vjl3Ju8esx
2MLfj1A2hQKBgQDCxtZZZ0h8Pb8Z7wpSFfQNvXi5CLwQvFYuClQLk6VXVErkAJsn
XiUjyGYeXnNVm/i2mcyKwXQZ20k90HBrPU2ED8mi5Ob5ya5Uqw6mmMHe2d7sw81d
WB37RBWSrCXC0DYSZQQ4cYHn3sd2Fqtd4EBijV7qDLjCKU582OdKLqYzNwKBgH31
UKQkJZgIkIThbPT4GewI0GgCRvFb76DmUGUQJTg2Oi86siq1WUwOFiabie5RuxZX
oNLyH8W008/BbO2RMX1FVOvRCciJ8LJFkTl6TM6iDzfUUBqPOuFryoG3Yrh60btw
81rMbqyZIgFhi0QGu2OWnC0Oadyt2tJwV/5t55R5AoGBAPspZttDmOzVkAJDSn9Z
iByYt1KmwBQ6l7LpFg33a7ds9zWqW4+i6r0PzXvSewf/z69L0cAywSk5CaJJjDso
dTlNMqwux01wd6V+nQGR871xnsOg+qzgJ565TJZelWgRmNRUooi4DMp5POJA33xp
rqAISUfW0w2S+q7/5Lm0QiJE
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUfENbTtH5nr7giuawwQpDYqUpWJswDQYJKoZIhvcNAQEL
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA2MjcwOTQxNDNaFw0yNDA3
MjcwOTQxNDNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCfP6cZhCs9jOnWqyQP12vrOOxlBrWofYZFf9amUA24
AfE7oGcSfkylanmkxzvGqQkhgLAvkHZj/GEvHujKyy8PgcEGP+pwmsfWNQMvU0Dz
j3syjWOTi3eIC/3DoUnHlWCT2qCil/bjqxgU1l7fO/OXUlq5kyvIjln7Za4sUHun
ixe/m96Er6l8a4Mh2pxh2C5pkLCvulkQhjjGG+R6MccH8wwQwmLg5oVBkFEZrnRE
pnRKBI0DvA+wk1aJFAPOI4d8Q5T7o/MyxH3f8TYGHqbeMQFCKwusnlWPRtrNdaIc
gaLvSpR0LVlroXGu8tYmRpvHPByoKGDbgVvO0Bwx8fmRAgMBAAGjUzBRMB0GA1Ud
DgQWBBR7r+mQWNUZ0TpQNwrwjgxgngvOjTAfBgNVHSMEGDAWgBR7r+mQWNUZ0TpQ
NwrwjgxgngvOjTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCO
7B4s6uQEGE8jg3CQgy76oU/D8sazGcP8+/E4JLHSc0Nj49w4ztSpkOVk2HyEtzbm
uR3TreIw+SfqpbiOI/ivVNDbEBsb/vEeq7qPzDH1Bi72plHZNRVhNGGV5rd7ibga
TkfXHKPM9yt8ffffHHiu1ROvb8gg2B6JbQwboU4hvvmmorW7onyTFSYEzZVdNSpv
pUtKPldxYjTnLlbsJdXC4xyCC4PrJt2CC0n0jsWfICJ77LMxIxTODh8oZNjbPg6r
RdI7U/DsD+R072DjbIcrivvigotJM+jihzz5inZwbO8o0WQOHAbJLIG3C3BnRW3A
Ek4u3+HXZMl5a0LGJ76u
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCfP6cZhCs9jOnW
qyQP12vrOOxlBrWofYZFf9amUA24AfE7oGcSfkylanmkxzvGqQkhgLAvkHZj/GEv
HujKyy8PgcEGP+pwmsfWNQMvU0Dzj3syjWOTi3eIC/3DoUnHlWCT2qCil/bjqxgU
1l7fO/OXUlq5kyvIjln7Za4sUHunixe/m96Er6l8a4Mh2pxh2C5pkLCvulkQhjjG
G+R6MccH8wwQwmLg5oVBkFEZrnREpnRKBI0DvA+wk1aJFAPOI4d8Q5T7o/MyxH3f
8TYGHqbeMQFCKwusnlWPRtrNdaIcgaLvSpR0LVlroXGu8tYmRpvHPByoKGDbgVvO
0Bwx8fmRAgMBAAECggEACAkjOnNj5zA0IIP0RuRc6rqtmw9ynTTwUJN51lyVxKI8
dQDMEq/S2En+J2VyS7z92/XtbgkBIFx83u7VWl5UWpj2j4UsJFB7IwD7zyiJT4D+
+3cM/kX8Wx4XyQZbfbm47N0MXAgFCkn45hxHH0acLReXwmN9wxoDyl7AIjZRdwvG
Qq0rnOnIc8kkkew7L6AiFwQS8b77eyzua3d6moKXN9hU/kfiJ6YUFG/WLe0pmQA1
HbF27YghfeLnYUt50oDuX6jF6CzQhflchWVq/wn8/cxEpg/RMicWE8ulrTk7o27l
JwCrHrhYEBsPuZO4mxX/DHrAMmhTeFjLaV5bQlz0PQKBgQDgRPSOEixYnKz9iPs/
EDTlji5LA3Rm6TytRCNsjYY6Trw60KcvYqwyDUCiEjruvOQ9mqgBiQm1VHSalrG3
RcbVfpEMouyZbEwmTjS8KdOi5x4Z6AX+4yWDN31jX3b8sktgbxV/HRdg3sA3q7MJ
vExTUuoXg57W+FepIZ+XlhSoQwKBgQC1x6UMAlAeW45/yUUm/LFRcCgb/bdCQx+e
hSb8w3jdvVoNWgx1j7RsjjFKaZUnseK3qQvVfCm4Qjvlz6MpKDxslaUYuR162Ku0
e153z/xc7XRoXyPyPLdGZFlWii30jirB7ZqPdyz6mwlWwqdImNerbUqdFt9R8bId
pYsyHB5zmwKBgBjYCq9iW/9E+/TqI8sMpI95fK9app5v4AThs3rnAqOa7Ucmrh6V
s7Wnui06D8U6r54Tb+EbqTOpM3Gcl/tRg4FLEA5yTfuA/76Ok1D04Tj+mVsNVPyz
dQhgMUe835WGusroA12df2V/x5NjNeYyMdJZMQ2ByyrNQAjAbMmCGq+5AoGBAIj8
ERFysMOfxUvg9b7CkDFJrsAhOzew86P2vYGfIHchGTqUkG0LRTDFGrnzxNXsBGjY
+DUB40Kajx7IkTETxC0jvA1ceq23l/VjPrZVQt0YiC+a+rCyNn7SYkyHxsfTVr9b
ea0BZyDXMntyJrPbkjL6Ik8tDE9pLwuOU84ISJ5fAoGAZ2+Ams/VhdZj/wpRpMky
K4jtS4nzbCmJzzTa6vdVV7Kjer5kFxSFFqMrS/FtJ/RxHeHvxdze9dfGu9jIdTKK
vSzbyQdHFfZgRkmAKfcoN9u567z7Oc74AQ9UgFEGdEVFQUbfWOevmr8KIPt8nDQK
J9HuVfILi1kH0jzDd/64TvA=
-----END PRIVATE KEY-----

View File

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

View File

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

View File

@@ -0,0 +1,15 @@
{
"data": "ENC[AES256_GCM,data:TfEsytctWPCLuo/icbicgRfy7O/txYCllTiLiUlusagGShZyXyIR46TNL9E4XWI2Lce9hIn8zczOdUWaEFPuXcvRMMMWILY3DzI=,iv:zDdq0rdYz/KIwKvIiu9MvKyX9v1pWYxZG3F/7KllBa0=,tag:mTPJGmJ+tKrgYaCZXJ37Nw==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2MmFpbUJuNzRnNGRlQXcy\naEhRanpHbjZpbFZxVkZ2TXFJWk8xYm9lYmlVCmVhRFdDZyt4SjJick1CdnZseWx1\nMGdvaTBYekdBeFUyaHEvTzNJVVM4TncKLS0tIG8rZ1kyTFJTRndQNFVXOC9OTTc5\nZHZGVW1FTzlLQ0RRcjNWeEpVWmVKMDgK7UDm509nexdHqG2xU8CBDZkRStjQIAAN\nDmOz5A8uWpIiyvU2LdOBcc/FQKHaXjB7OAmfT03nJccOeqSF2N3N3g==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-04-16T16:40:26Z",
"mac": "ENC[AES256_GCM,data:5Qe20lbqERvSM5fDY9Orhrtv2U6zholh6uHMq0CqV1OOg+vVWSlqTqJrtz2rD/qQTUECRKzWUHB1D/kgLrJ33lRoEMqrhjmvBfxtDnNjLzoYITlLcYOm9qiv3gOqcrpdBKW10YyNlGP/+Q377Lfbo8tcZ8nmuaT8qA9PYr+AKcs=,iv:IIJEFAvoX9SY3jvkD0xVe1/L6iRPMyzmxeRmpGvZI0I=,tag:1D3BBUjj1suNeL+mVYDiKw==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.1"
}
}

View File

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

View File

@@ -0,0 +1,15 @@
{
"data": "ENC[AES256_GCM,data:NI9y5OdFkBgHf+wfn+ISDL11nh/ud+1RV5SPC64TV4Hvg0w8GKkmjJI5uiGDGI1+FfWwnHWOFexavtM2ZJr/cWfhA6dGKvzrKJc=,iv:itiZFGsGEZD/SH42akh1CLCDbuZxMSj05quMNKwvKg4=,tag:v36FGDDHIuFaABHG9we6ag==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByUVVJek9Ha2ljMkt4U2pi\nSmRRd2g2R0VXZGlySG5TT1E1czFpaWFyNlFjCmRJOThCQWlCNDZnRVRFVHpSTzBW\nOWZCUU5jK2dGQTloOEZMUFFVdk04cXMKLS0tIDVzSTdXRk1UZ3psd29kdnVUcitM\nbFlqb0srUGFCVUhlNzU1dUdTTUkwN0UKAIslz1WCMZWrE+aLPJjeM+wZSXMmwnqx\nyRZT5vVzCPWv2r8sbIjhi1rFbkfF+NXHkzNZD9NS4zddwsDsz5HO1g==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-04-16T16:40:48Z",
"mac": "ENC[AES256_GCM,data:2iDDnVdLPWxYcjdZrDlTb8PzPVOPEZ06QXCFvnZ2gf8ioXPiSY69ZAHRHTGpqCEp5Ve7qTIELbNja2TGU0ONLIcIRWyzqgc4q+G3n2V5fYQURW114pzaK0Ct6r6yR9oZQy8H66uEYQafkyuN2R9++3w5G0LGj8UovPcYQqNEQVo=,iv:TkCAdIgjRpZpsnhhvTfMqGVD/IveFyobYa9SExFWcC4=,tag:4RLhumGqeLT15waqHT0mRg==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.1"
}
}

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDfzCCAmegAwIBAgIUH9AKYdV75FHHBcR4mgfTZB/7eEcwDQYJKoZIhvcNAQEL
BQAwaDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM
DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBENsYW4xDTALBgNVBAsMBENsYW4xDjAM
BgNVBAMMBXBlZXIxMB4XDTI1MDQxNjE2NDAzN1oXDTI1MDUxNjE2NDAzN1owaDEL
MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBG
cmFuY2lzY28xDTALBgNVBAoMBENsYW4xDTALBgNVBAsMBENsYW4xDjAMBgNVBAMM
BXBlZXIxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA80mo3OFSaW8F
Ni/W7WZ70bJoGGFPFK17kiRgPu6+ghDiinmzlAQOt8A/u+egl4FsvT9Oz99TjCN1
zkK3I74ItKmumpGKGPp92bpm62vQZa4g861xKqLlcbOwJwcfofwa8r4PhhjDhdXS
k9vsgiwy0N5FEga79QbDEO/qwSvY+O8yKNG+lNXeOetymKvVbudL8A0je150vmpg
oYfYjH57Oa7DpGaIrOpbZsmaBlYHD5dhfJbuX0Gxuq42gkfcBtxv3NbY0NoPVZFV
jOvhVPyV9Xme/3JAQUSti+Fd2ZfJ+Ayl90ElA5wk25T1JBEEnMYQlQVBqPawX87C
i1EtOysfxQIDAQABoyEwHzAdBgNVHQ4EFgQUFtjyWNCF1Yxd8ymIZ4kE9fXMY5Yw
DQYJKoZIhvcNAQELBQADggEBAAHiQcWDvZjN2VTaWY2cQMYy3m8wkdoJTR20uV2z
MpjY4KwCiMzTtsFe2LhiYMYFETwqHpG+B6ElOghh/+F8l96vQRbcVI9I3XTKs0G4
+zdUtMOyB2XZumB4HBQa3PiXXrA4kAGJV88y5QC4UkZMw6SfwjW8OrtQ5Jim4vUB
PZxY75ZIjw4JhknTqKNua7xehY4TBghRrGZAlD4eon7Yc5bIew6Gw5LHIoszOZgk
9CFEo1XLN5z8aL9L+V8dh2DNNqF4KiXCRNgwqLmLoepL2Xptd90AOZsBI9mGxMP9
YUPsnzcGqcat1x6Fi2Guw++ESDxUp6qKjMGAxPzSXje/TiM=
-----END CERTIFICATE-----

View File

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

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data: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,iv:IZYhje9AgGRe0gQcodG/PQAaRBipBC/7F8qAkG35cxc=,tag:jpXpm1eghy/668gT0bmqMA==,type:str]",
"sops": {
"age": [
{
"recipient": "age1987metkajgdefk0sfhjqjjtczy9eu2lsg700rwcac6hhy2alhdsshjmpw8",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3MDdhSTZMbXlSdDVNVVZU\ndkFyVVI0eDhOUHZRU2FFalVNR3g5dUY5T25FCnl0aXpZRVpaR1hvdm5kSHplOE0x\nckloNFF3OVhNTnAxY2ZpZjNFV3plVXMKLS0tIG4yU0w2c1VGbDVCTUhYbjVrMXhr\nb0dpUnp2YUFWSERSRTVVK3g0WTNKWE0KpUfYS71F/1J1G38/ymd/+bWhABmze1GC\nehgSMymmVdsq+ZjHdJ1XcCyecsn/9aFcaZkEbASiLU8ecLNQOEGgRQ==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWeXBUOU13M2VvZVNBNUZW\nMy9VV1dMV1FlQU9qekhZWitwb3JISTFwdENBCnB5ZHpNK29DRHBoZ2M4dEJ6UVpq\nWHFOM1lYS0ROQ2NpSTNUdkZqUkorWGsKLS0tIDhaalVJNE1oU0N3WUtodnlsQWla\nUTVmTnhPTHVCWXUyK1ZESGR1Ym5CMXcK3YqyKO/FTdxcxVy5zBGg+JCOWMBOxqd2\n9+FgUJaYaizGy+HLpP5jgtjgz7k504yqEQCo9aQ1CzbvNHom5tAu7A==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-04-16T16:40:41Z",
"mac": "ENC[AES256_GCM,data:R8fWg7Vwq2mnjbTTtyYuLWwrmB6TZYZVx9xPcO5NOvGAABNIxtAVSe9yTpV25OlJiXruTNhPHDxfjwDW8Nad47Sd9fV9QzH36uygT9DOaVrrOD/TH5ojvpCuognofuJ8YHgUsq+yhiQs0QKi5efUrtRVDcXXr8s/UazyuG3vYzk=,iv:eBpSr8GKvG51govZWtqTVMWsWZDctDQ2vVgMm/jq62U=,tag:Yth78awXPAPa/7J+WxTDug==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.1"
}
}

View File

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

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDfzCCAmegAwIBAgIUYuUk46fwZ4CBcJ40NWnT9VDIEPUwDQYJKoZIhvcNAQEL
BQAwaDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM
DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBENsYW4xDTALBgNVBAsMBENsYW4xDjAM
BgNVBAMMBXBlZXIyMB4XDTI1MDQxNjE2NDA1OVoXDTI1MDUxNjE2NDA1OVowaDEL
MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBG
cmFuY2lzY28xDTALBgNVBAoMBENsYW4xDTALBgNVBAsMBENsYW4xDjAMBgNVBAMM
BXBlZXIyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA45nKnn0r3HwU
qqSRuOXbou8zpdf+5i+e1h7pmunXR7WPxPBP09t6i+99BO27GcID59zGMquabpNS
dFhj+p+KZkqN+4sokZmyBU1civQqiwX2n5KtoaG0fU3gFFK6pfx3OQawQ6mJ50GU
HhA2R3CuA0rXcssr6oPynj9z6pbaL7mKckOWE804xIWZuMEoWNdQEKmUmE5d1ioa
edlblzwhqZSS+zAAeUvmb+YUEL6T54lCYYqPPnmwmiwfYFSBGu/SGyFtIijbCuIZ
TJMDzzutx1/3Dsv2pOKC0uPb5qRcmdRePAzgBFSna4MNgfbpGHFkGPJgjiue0VIC
qyedlpF5UQIDAQABoyEwHzAdBgNVHQ4EFgQUuIeLdxGVyhFbgFRtFbPIIJWw1R0w
DQYJKoZIhvcNAQELBQADggEBAFj26XejazrXOfa67o8vGoZrR2TGXOLFWFeplO8B
29AruG9poH+sInyxYo1RWAQLQMfDud/yGg73EeYylULbG1bBznKYLLHdvy4l6eXt
SEVkEMruH0Kw93zt+NqvSO3bHCX+la1rjizyDcD4iu93xUg2uPSBmVpVpW/aeBCN
3eF4FbBocUexmIWaygmMPY5yFY2tAf+OinBf4uSWcKEpFikIqAxQWRSDMWm8xFwY
CG7rhfpwDauagpZtkjKkrrRedhdfGiXbxOVtYlBULuUMOggEI+ElpbD0UhyEYCsD
XoJn7AOC0sYCGpj2F1ESwFX/5EhyciLjMuVwohFVcyWWg+Q=
-----END CERTIFICATE-----

View File

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

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data: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,iv:zNaVGK5hNxziOoPTbwaUhUwBuFbCiGNrfVMpeMxL3JI=,tag:6v8Hf4Symd1T16MOEChtcA==,type:str]",
"sops": {
"age": [
{
"recipient": "age1fndalxxeduekn5s8q3znl73vjfx2n8kydylyrc2j3aurc93pypvs6pcql4",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1VGVjRkdJOGx3c05YM28y\nM3dCbkU4TXBHK1VVOFFkY3FQVk0rQVp0d1g0CnZPR3FtUGlCb2lKSVc1Z3VtM0JM\nV1ZtZ3NVVndvak43cStIRWZxWldKSncKLS0tIEdJVHFFTzdaNklLVHdURndGa3Qy\nc2lEZ1hER3dGL0FKNUZrSkxMOXMvOGsKHGJ44Ey6mR3rV6NPPmn/QTsyjL08wCzu\nkUdD0jgSMLwInX5R9Gh9+Zbc9NIfEgSzLr6up6UlgW/4iWvM4oFPRg==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjcHVweTFZenhZZzVDZ2ts\nTnNxNkZLWnVQRmpoa0ZldHpxdWt0Sy9jRVFFClExS2FMM3hiSlRQR2lmb25RTEo0\nRTRGdmxCaXJoeXdNaVU3cGRIRFlibWsKLS0tIFFzVFhCR2hSOStYNk5yNmc5UkZl\nTHdWSUZTZUIyUEp2OFR0SFpzMzFFd0EKlsRWNJjapPefXxyuUtFWlPs/UIC9V1N7\nF7Ek+TAKl11SwGGA2qla1yvnDOxkZvFg7gWsurZeEBH4PuPZ1OE/Yg==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-04-16T16:41:03Z",
"mac": "ENC[AES256_GCM,data:1DcuXden9WAF3frVjOMgpt0nniqiGEAA4SubPLk86GODEaOXxZSVStX1rr0GCF0t0tR4O4jl4cnRvZHF9Zjj7smA5Wf8jPpbSCrZX4oBo/HP3UU+A78yxSrj4gmoeH4m/aaJv0co77Vwcm/HglE6Q89Oc9BUqE2e4FGVmDUZTws=,iv:OAa2hvuw6aUcp3qKkRpDeLMDcq9Kkn/Bc+86DzV5h5g=,tag:wVrs9oyfaCAv3gZxsxbMPg==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.1"
}
}

View File

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

View File

@@ -13,12 +13,7 @@ in
description = "Flake reference";
};
};
config = {
warnings = [
"The clan.auto-upgrade module is deprecated and will be removed on 2025-07-15. Please migrate to using the system.autoUpgrade NixOS option directly."
];
system.autoUpgrade = {
inherit (cfg) flake;
enable = true;

View File

@@ -1,13 +1,5 @@
{ config, pkgs, ... }:
{
config,
pkgs,
...
}:
{
warnings = [
"The clan.deltachat module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
];
networking.firewall.interfaces."zt+".allowedTCPPorts = [ 25 ]; # smtp with other hosts
environment.systemPackages = [ pkgs.deltachat-desktop ];

View File

@@ -1,9 +1,4 @@
_: {
warnings = [
"The clan.ergochat module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
];
services.ergochat = {
enable = true;

View File

@@ -11,9 +11,6 @@
] "Importing the module will already enable the service.")
];
config = {
warnings = [
"The clan.heisenbridge module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
];
services.heisenbridge = {
enable = true;
homeserver = "http://localhost:8008"; # TODO: Sync with matrix-synapse

View File

@@ -81,7 +81,15 @@ in
})
{
warnings = [
"The clan.iwd module is deprecated and will be removed on 2025-07-15. Please migrate to a user-maintained configuration or use the wifi service."
''
The clan module `iwd` is deprecated and replaced by the clan service `wifi`
Please migrate your config to the new service (see: https://docs.clan.lol/reference/clanServices/wifi/)
To keep passwords after migrating the config, use:
clan vars get <your-machine> iwd.<network-name>/ssid | clan vars set <your-machine> wifi.<network-name>/network-name
and:
clan vars get <your-machine> iwd.<network-name>/password | clan vars set <your-machine> wifi.<network-name>/password
''
];
# disable wpa supplicant

View File

@@ -38,10 +38,6 @@ in
] "Importing the module will already enable the service.")
];
config = {
warnings = [
"The clan.localsend module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
];
clan.core.state.localsend.folders = [
"/var/localsend"
];

View File

@@ -4,10 +4,6 @@ let
defaultPort = 48011;
in
{
warnings = [
"The clan.moonlight module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
];
hardware.opengl.enable = true;
environment.systemPackages = [
pkgs.moonlight-qt

View File

@@ -37,10 +37,6 @@ in
};
config = {
warnings = [
"The clan.mumble module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
];
services.murmur = {
enable = true;
logDays = -1;

View File

@@ -17,10 +17,6 @@ let
listenPort = 48011;
in
{
warnings = [
"The clan.sunshine module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
];
networking.firewall = {
allowedTCPPorts = [
47984

View File

@@ -1,8 +1,4 @@
_: {
warnings = [
"The clan.thelounge module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
];
services.thelounge = {
enable = true;
public = true;

View File

@@ -1,8 +1,4 @@
{
warnings = [
"The clan.xfce module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
];
services.xserver = {
enable = true;
desktopManager.xfce.enable = true;

View File

@@ -13,10 +13,6 @@
};
};
config = {
warnings = [
"The clan.zt-tcp-relay module is deprecated and will be removed on 2025-07-15. Please migrate to user-maintained configuration."
];
networking.firewall.allowedTCPPorts = [ config.clan.zt-tcp-relay.port ];
systemd.services.zt-tcp-relay = {

View File

@@ -1,18 +1,17 @@
{ lib, ... }:
let
module = lib.modules.importApply ./default.nix { };
in
{ lib, self, ... }:
{
clan.modules = {
admin = module;
admin = lib.modules.importApply ./default.nix { };
};
perSystem =
{ ... }:
{ pkgs, ... }:
{
clan.nixosTests.admin = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/admin" = module;
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
admin = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};
}

View File

@@ -1,45 +1,62 @@
{
pkgs,
nixosLib,
clan-core,
...
}:
let
public-key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII6zj7ubTg6z/aDwRNwvM/WlQdUocMprQ8E92NWxl6t+ test@test";
in
{
name = "admin";
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
clan = {
directory = ./.;
inventory = {
hostPkgs = pkgs;
machines.client = { };
machines.server = { };
name = "admin";
instances = {
ssh-test-one = {
module.name = "@clan/admin";
roles.default.machines."server".settings = {
allowedKeys.testkey = public-key;
clan = {
directory = ./.;
modules."@clan/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;
nodes = {
client.environment.etc.private-test-key.source = ./private-test-key;
server = {
services.openssh.enable = true;
server = {
services.openssh.enable = true;
};
};
};
testScript = ''
start_all()
testScript = ''
start_all()
machines = [client, server]
for m in machines:
m.systemctl("start network-online.target")
machines = [client, server]
for m in machines:
m.systemctl("start network-online.target")
for m in machines:
m.wait_for_unit("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")
'';
}
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

@@ -0,0 +1,33 @@
{ ... }:
{
_class = "clan.service";
manifest.name = "clan-core/auto-upgrade";
manifest.description = "Automatic system upgrade for the Clan App";
manifest.categories = [ "System" ];
roles.default = {
interface =
{ lib, ... }:
{
options.flake = lib.mkOption {
type = lib.types.str;
description = "Flake reference";
};
};
perInstance =
{ settings, ... }:
{
nixosModule =
{ ... }:
{
system.autoUpgrade = {
inherit (settings) flake;
enable = true;
dates = "02:00";
randomizedDelaySec = "45min";
};
};
};
};
}

View File

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

View File

@@ -1,18 +1,17 @@
{ lib, ... }:
let
module = lib.modules.importApply ./default.nix { };
in
{ lib, self, ... }:
{
clan.modules = {
borgbackup = module;
borgbackup = lib.modules.importApply ./default.nix { };
};
perSystem =
{ ... }:
{ pkgs, ... }:
{
clan.nixosTests.borgbackup = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/borgbackup" = module;
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
borgbackup = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};
}

View File

@@ -1,112 +1,118 @@
{
module,
pkgs,
nixosLib,
clan-core,
...
}:
{
name = "borgbackup";
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
clan = {
directory = ./.;
test.useContainers = true;
inventory = {
hostPkgs = pkgs;
machines.clientone = { };
machines.serverone = { };
name = "borgbackup";
instances = {
borgone = {
clan = {
directory = ./.;
test.useContainers = true;
modules."@clan/borgbackup" = ../../default.nix;
inventory = {
module.name = "@clan/borgbackup";
machines.clientone = { };
machines.serverone = { };
roles.client.machines."clientone" = { };
roles.server.machines."serverone".settings.directory = "/tmp/borg-test";
instances = {
borgone = {
module.name = "@clan/borgbackup";
roles.client.machines."clientone" = { };
roles.server.machines."serverone".settings.directory = "/tmp/borg-test";
};
};
};
};
};
nodes = {
serverone = {
services.openssh.enable = true;
# Needed so PAM doesn't see the user as locked
users.users.borg.password = "borg";
};
clientone =
{
config,
pkgs,
clan-core,
...
}:
let
dependencies = [
clan-core
pkgs.stdenv.drvPath
] ++ builtins.map (i: i.outPath) (builtins.attrValues clan-core.inputs);
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
in
{
nodes = {
serverone = {
services.openssh.enable = true;
users.users.root.openssh.authorizedKeys.keyFiles = [ ../../../../checks/assets/ssh/pubkey ];
clan.core.networking.targetHost = config.networking.hostName;
environment.systemPackages = [ clan-core.packages.${pkgs.system}.clan-cli ];
environment.etc.install-closure.source = "${closureInfo}/store-paths";
nix.settings = {
substituters = pkgs.lib.mkForce [ ];
hashed-mirrors = null;
connect-timeout = pkgs.lib.mkForce 3;
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
};
system.extraDependencies = dependencies;
clan.core.state.test-backups.folders = [ "/var/test-backups" ];
# Needed so PAM doesn't see the user as locked
users.users.borg.password = "borg";
};
};
clientone =
{ config, pkgs, ... }:
let
dependencies = [
clan-core
pkgs.stdenv.drvPath
] ++ builtins.map (i: i.outPath) (builtins.attrValues clan-core.inputs);
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
testScript = ''
import json
start_all()
in
{
machines = [clientone, serverone]
services.openssh.enable = true;
for m in machines:
m.systemctl("start network-online.target")
users.users.root.openssh.authorizedKeys.keyFiles = [ ../../../../checks/assets/ssh/pubkey ];
for m in machines:
m.wait_for_unit("network-online.target")
clan.core.networking.targetHost = config.networking.hostName;
# dummy data
clientone.succeed("mkdir -p /var/test-backups /var/test-service")
clientone.succeed("echo testing > /var/test-backups/somefile")
environment.systemPackages = [ clan-core.packages.${pkgs.system}.clan-cli ];
clientone.succeed("${pkgs.coreutils}/bin/install -Dm 600 ${../../../../checks/assets/ssh/privkey} /root/.ssh/id_ed25519")
clientone.succeed("${pkgs.coreutils}/bin/touch /root/.ssh/known_hosts")
clientone.wait_until_succeeds("timeout 2 ssh -o StrictHostKeyChecking=accept-new localhost hostname")
clientone.wait_until_succeeds("timeout 2 ssh -o StrictHostKeyChecking=accept-new $(hostname) hostname")
environment.etc.install-closure.source = "${closureInfo}/store-paths";
nix.settings = {
substituters = pkgs.lib.mkForce [ ];
hashed-mirrors = null;
connect-timeout = pkgs.lib.mkForce 3;
flake-registry = pkgs.writeText "flake-registry" ''{"flakes":[],"version":2}'';
};
system.extraDependencies = dependencies;
# create
clientone.succeed("borgbackup-create >&2")
clientone.wait_until_succeeds("! systemctl is-active borgbackup-job-serverone >&2")
clan.core.state.test-backups.folders = [ "/var/test-backups" ];
};
# list
backup_id = json.loads(clientone.succeed("borg-job-serverone list --json"))["archives"][0]["archive"]
out = clientone.succeed("borgbackup-list").strip()
print(out)
assert backup_id in out, f"backup {backup_id} not found in {out}"
};
# borgbackup restore
clientone.succeed("rm -f /var/test-backups/somefile")
clientone.succeed(f"NAME='serverone::borg@serverone:.::{backup_id}' borgbackup-restore >&2")
assert clientone.succeed("cat /var/test-backups/somefile").strip() == "testing", "restore failed"
'';
}
testScript = ''
import json
start_all()
machines = [clientone, serverone]
for m in machines:
m.systemctl("start network-online.target")
for m in machines:
m.wait_for_unit("network-online.target")
# dummy data
clientone.succeed("mkdir -p /var/test-backups /var/test-service")
clientone.succeed("echo testing > /var/test-backups/somefile")
clientone.succeed("${pkgs.coreutils}/bin/install -Dm 600 ${../../../../checks/assets/ssh/privkey} /root/.ssh/id_ed25519")
clientone.succeed("${pkgs.coreutils}/bin/touch /root/.ssh/known_hosts")
clientone.wait_until_succeeds("timeout 2 ssh -o StrictHostKeyChecking=accept-new localhost hostname")
clientone.wait_until_succeeds("timeout 2 ssh -o StrictHostKeyChecking=accept-new $(hostname) hostname")
# create
clientone.succeed("borgbackup-create >&2")
clientone.wait_until_succeeds("! systemctl is-active borgbackup-job-serverone >&2")
# list
backup_id = json.loads(clientone.succeed("borg-job-serverone list --json"))["archives"][0]["archive"]
out = clientone.succeed("borgbackup-list").strip()
print(out)
assert backup_id in out, f"backup {backup_id} not found in {out}"
# borgbackup restore
clientone.succeed("rm -f /var/test-backups/somefile")
clientone.succeed(f"NAME='serverone::borg@serverone:.::{backup_id}' borgbackup-restore >&2")
assert clientone.succeed("cat /var/test-backups/somefile").strip() == "testing", "restore failed"
'';
}
)

View File

@@ -0,0 +1,164 @@
{ ... }:
{
_class = "clan.service";
manifest.name = "clan-core/deltachat";
manifest.description = "Email-based instant messaging for Desktop";
manifest.categories = [ "Social" ];
roles.default = {
interface =
{ ... }:
{
options = { };
};
perInstance =
{ settings, ... }:
{
nixosModule =
{ config, pkgs, ... }:
{
networking.firewall.interfaces."zt+".allowedTCPPorts = [ 25 ]; # smtp with other hosts
environment.systemPackages = [ pkgs.deltachat-desktop ];
services.maddy =
let
domain = "${config.clan.core.settings.machine.name}.local";
in
{
enable = true;
primaryDomain = domain;
config = ''
# Minimal configuration with TLS disabled, adapted from upstream example
# configuration here https://github.com/foxcpp/maddy/blob/master/maddy.conf
# Do not use this in unencrypted networks!
auth.pass_table local_authdb {
table sql_table {
driver sqlite3
dsn credentials.db
table_name passwords
}
}
storage.imapsql local_mailboxes {
driver sqlite3
dsn imapsql.db
}
table.chain local_rewrites {
optional_step regexp "(.+)\+(.+)@(.+)" "$1@$3"
optional_step static {
entry postmaster postmaster@$(primary_domain)
}
optional_step file /etc/maddy/aliases
}
msgpipeline local_routing {
destination postmaster $(local_domains) {
modify {
replace_rcpt &local_rewrites
}
deliver_to &local_mailboxes
}
default_destination {
reject 550 5.1.1 "User doesn't exist"
}
}
smtp tcp://[::]:25 {
limits {
all rate 20 1s
all concurrency 10
}
dmarc yes
check {
require_mx_record
dkim
spf
}
source $(local_domains) {
reject 501 5.1.8 "Use Submission for outgoing SMTP"
}
default_source {
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
reject 550 5.1.1 "User doesn't exist"
}
}
}
submission tcp://[::1]:587 {
limits {
all rate 50 1s
}
auth &local_authdb
source $(local_domains) {
check {
authorize_sender {
prepare_email &local_rewrites
user_to_email identity
}
}
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
modify {
dkim $(primary_domain) $(local_domains) default
}
deliver_to &remote_queue
}
}
default_source {
reject 501 5.1.8 "Non-local sender domain"
}
}
target.remote outbound_delivery {
limits {
destination rate 20 1s
destination concurrency 10
}
mx_auth {
dane
mtasts {
cache fs
fs_dir mtasts_cache/
}
local_policy {
min_tls_level encrypted
min_mx_level none
}
}
}
target.queue remote_queue {
target &outbound_delivery
autogenerated_msg_domain $(primary_domain)
bounce {
destination postmaster $(local_domains) {
deliver_to &local_routing
}
default_destination {
reject 550 5.0.0 "Refusing to send DSNs to non-local addresses"
}
}
}
imap tcp://[::1]:143 {
auth &local_authdb
storage &local_mailboxes
}
'';
ensureAccounts = [ "user@${domain}" ];
ensureCredentials = {
"user@${domain}".passwordFile = pkgs.writeText "dummy" "foobar";
};
};
};
};
};
}

View File

@@ -0,0 +1,17 @@
{ lib, self, ... }:
{
clan.modules = {
deltachat = lib.modules.importApply ./default.nix { };
};
perSystem =
{ pkgs, ... }:
{
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
deltachat = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};
}

View File

@@ -0,0 +1,50 @@
{
pkgs,
nixosLib,
clan-core,
...
}:
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
hostPkgs = pkgs;
name = "deltachat";
clan = {
directory = ./.;
modules."@clan/deltachat" = ../../default.nix;
inventory = {
machines.server = { };
instances = {
deltachat-test = {
module.name = "@clan/deltachat";
roles.default.machines."server".settings = { };
};
};
};
};
nodes = {
server = { };
};
testScript = ''
start_all()
server.wait_for_unit("maddy")
# imap
server.succeed("${pkgs.netcat}/bin/nc -z -v ::1 143")
# smtp submission
server.succeed("${pkgs.netcat}/bin/nc -z -v ::1 587")
# smtp
server.succeed("${pkgs.netcat}/bin/nc -z -v ::1 25")
'';
}
)

View File

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

View File

@@ -0,0 +1,36 @@
{ ... }:
{
_class = "clan.service";
manifest.name = "clan-core/ergochat";
manifest.description = "A modern IRC server";
manifest.categories = [ "Social" ];
roles.default = {
interface =
{ ... }:
{
options = { };
};
perInstance =
{ settings, ... }:
{
nixosModule =
{ ... }:
{
services.ergochat = {
enable = true;
settings = {
datastore = {
autoupgrade = true;
path = "/var/lib/ergo/ircd.db";
};
};
};
clan.core.state.ergochat.folders = [ "/var/lib/ergo" ];
};
};
};
}

View File

@@ -0,0 +1,17 @@
{ lib, self, ... }:
{
clan.modules = {
ergochat = lib.modules.importApply ./default.nix { };
};
perSystem =
{ pkgs, ... }:
{
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
ergochat = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};
}

View File

@@ -0,0 +1,51 @@
{
pkgs,
nixosLib,
clan-core,
...
}:
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
hostPkgs = pkgs;
name = "ergochat";
clan = {
directory = ./.;
modules."@clan/ergochat" = ../../default.nix;
inventory = {
machines.server = { };
instances = {
ergochat-test = {
module.name = "@clan/ergochat";
roles.default.machines."server".settings = { };
};
};
};
};
nodes = {
server = { };
};
testScript = ''
start_all()
server.wait_for_unit("ergochat")
# Check that ergochat is running
server.succeed("systemctl status ergochat")
# Check that the data directory exists
server.succeed("test -d /var/lib/ergo")
# Check that the server is listening on the correct ports
server.succeed("${pkgs.netcat}/bin/nc -z -v ::1 6667")
'';
}
)

View File

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

View File

@@ -1,19 +1,18 @@
{ lib, ... }:
let
module = lib.modules.importApply ./default.nix { };
in
{ lib, self, ... }:
{
clan.modules = {
garage = module;
garage = lib.modules.importApply ./default.nix { };
};
perSystem =
{ ... }:
{ pkgs, ... }:
{
clan.nixosTests.garage = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/garage" = module;
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
garage = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};
}

View File

@@ -1,76 +1,87 @@
{
module,
pkgs,
nixosLib,
clan-core,
...
}:
{
name = "garage";
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
clan = {
directory = ./.;
inventory = {
machines.server = { };
hostPkgs = pkgs;
instances = {
garage-test = {
module.name = "@clan/garage";
roles.default.machines."server".settings = { };
};
};
};
};
name = "garage";
nodes = {
server = {
services.garage = {
enable = true;
package = pkgs.garage;
settings = {
clan = {
directory = ./.;
modules."@clan/garage" = ../../default.nix;
inventory = {
machines.server = { };
metadata_dir = "/var/lib/garage/meta";
data_dir = "/var/lib/garage/data";
db_engine = "sqlite";
replication_factor = 1;
rpc_bind_addr = "127.0.0.1:3901";
s3_api = {
api_bind_addr = "127.0.0.1:3900";
s3_region = "garage";
root_domain = ".s3.garage";
};
s3_web = {
bind_addr = "127.0.0.1:3902";
root_domain = ".web.garage";
};
admin = {
api_bind_addr = "127.0.0.1:3903";
instances = {
garage-test = {
module.name = "@clan/garage";
roles.default.machines."server".settings = { };
};
};
};
};
};
testScript = ''
start_all()
nodes = {
server = {
services.garage = {
enable = true;
package = pkgs.garage;
settings = {
server.wait_for_unit("network-online.target")
server.wait_for_unit("garage")
metadata_dir = "/var/lib/garage/meta";
data_dir = "/var/lib/garage/data";
db_engine = "sqlite";
# Check that garage is running
server.succeed("systemctl status garage")
replication_factor = 1;
# Check that the data directories exist
server.succeed("test -d /var/lib/garage/meta")
server.succeed("test -d /var/lib/garage/data")
rpc_bind_addr = "127.0.0.1:3901";
# Check that the ports are open to confirm that garage is running
server.wait_until_succeeds("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 3901")
server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 3900")
server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 3902")
server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 3903")
'';
}
s3_api = {
api_bind_addr = "127.0.0.1:3900";
s3_region = "garage";
root_domain = ".s3.garage";
};
s3_web = {
bind_addr = "127.0.0.1:3902";
root_domain = ".web.garage";
};
admin = {
api_bind_addr = "127.0.0.1:3903";
};
};
};
};
};
testScript = ''
start_all()
server.wait_for_unit("network-online.target")
server.wait_for_unit("garage")
# Check that garage is running
server.succeed("systemctl status garage")
# Check that the data directories exist
server.succeed("test -d /var/lib/garage/meta")
server.succeed("test -d /var/lib/garage/data")
# Check that the ports are open to confirm that garage is running
server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 3901")
server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 3900")
server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 3902")
server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 3903")
'';
}
)

View File

@@ -0,0 +1,35 @@
{ ... }:
{
_class = "clan.service";
manifest.name = "clan-core/heisenbridge";
manifest.description = "A matrix bridge to communicate with IRC";
manifest.categories = [ "Social" ];
roles.default = {
interface =
{ lib, ... }:
{
options.homeserver = lib.mkOption {
type = lib.types.str;
default = "http://localhost:8008";
description = "URL of the Matrix homeserver";
};
};
perInstance =
{ settings, ... }:
{
nixosModule = {
services.heisenbridge = {
enable = true;
homeserver = settings.homeserver;
};
services.matrix-synapse.settings.app_service_config_files = [
"/var/lib/heisenbridge/registration.yml"
];
};
};
};
}

View File

@@ -0,0 +1,17 @@
{ lib, self, ... }:
{
clan.modules = {
heisenbridge = lib.modules.importApply ./default.nix { };
};
perSystem =
{ pkgs, ... }:
{
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
heisenbridge = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};
}

View File

@@ -0,0 +1,65 @@
{
pkgs,
nixosLib,
clan-core,
...
}:
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
hostPkgs = pkgs;
name = "heisenbridge";
clan = {
directory = ./.;
modules."@clan/heisenbridge" = ../../default.nix;
inventory = {
machines.server = { };
instances = {
heisenbridge-test = {
module.name = "@clan/heisenbridge";
roles.default.machines."server".settings = {
homeserver = "http://127.0.0.1:8008";
};
};
};
};
};
nodes = {
server = {
# Setup a minimal matrix-synapse to test with
services.matrix-synapse = {
enable = true;
settings.server_name = "example.com";
settings.database = {
name = "sqlite3";
};
};
};
};
testScript = ''
start_all()
server.wait_for_unit("matrix-synapse")
server.wait_for_unit("heisenbridge")
# Check that heisenbridge is running
server.succeed("systemctl status heisenbridge")
# Wait for the bridge to initialize
server.wait_until_succeeds("journalctl -u heisenbridge | grep -q 'bridge is now running'")
# Check that heisenbridge is listening on the default port
server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 9898")
'';
}
)

View File

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

View File

@@ -14,7 +14,7 @@ in
hello-world = module;
};
perSystem =
{ ... }:
{ pkgs, ... }:
let
# Module that contains the tests
# This module adds:
@@ -41,10 +41,15 @@ in
2. To run the test
nix build .#checks.x86_64-linux.hello-service
*/
clan.nixosTests.hello-service = {
imports = [ ./tests/vm/default.nix ];
clan.modules.hello-service = module;
};
checks =
# Currently we don't support nixos-integration tests on darwin
lib.optionalAttrs (pkgs.stdenv.isLinux) {
hello-service = import ./tests/vm/default.nix {
inherit module;
inherit self inputs pkgs;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
clan-core = self;
};
};
};
}

View File

@@ -44,7 +44,10 @@ in
test_simple = {
inherit testFlake;
expr = { };
expected = { };
expr = {
};
expected = {
};
};
}

View File

@@ -1,29 +1,44 @@
{
pkgs,
nixosLib,
clan-core,
module,
...
}:
{
name = "hello-service";
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
clan = {
directory = ./.;
inventory = {
machines.peer1 = { };
hostPkgs = pkgs;
instances."test" = {
module.name = "hello-service";
roles.peer.machines.peer1 = { };
name = "hello-service";
clan = {
directory = ./.;
modules = {
hello-service = module;
};
inventory = {
machines.peer1 = { };
instances."test" = {
module.name = "hello-service";
roles.peer.machines.peer1 = { };
};
};
};
};
testScript =
{ nodes, ... }:
''
start_all()
testScript =
{ nodes, ... }:
''
start_all()
# peer1 should have the 'hello' file
value = peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.hello.files.hello.path}")
assert value.strip() == "Hello world from peer1", value
'';
}
# peer1 should have the 'hello' file
value = peer1.succeed("cat ${nodes.peer1.clan.core.vars.generators.hello.files.hello.path}")
assert value.strip() == "Hello world from peer1", value
'';
}
)

View File

@@ -0,0 +1,17 @@
LocalSend is a free, open-source alternative to AirDrop that allows you to
securely share files and messages with nearby devices over your local network
without needing an internet connection.
## Example Usage
```nix
inventory.instances = {
localsend = {
module = {
name = "localsend";
input = "clan";
};
roles.default.machines.draper = { };
};
}
```

View File

@@ -0,0 +1,82 @@
{ ... }:
{
_class = "clan.service";
manifest.name = "localsend";
manifest.description = "Local network file sharing application";
manifest.categories = [ "Utility" ];
roles.default = {
interface =
{ lib, ... }:
{
options = {
displayName = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "The name that localsend will use to display your instance.";
};
package = lib.mkOption {
type = lib.types.nullOr lib.types.package;
default = null;
defaultText = "pkgs.localsend of the machine";
description = "The localsend package to use.";
};
ipv4Addr = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "192.168.56.2/24";
description = "Optional IPv4 address for ZeroTier network. Only needed until IPv6 multicasting is supported.";
};
};
};
perInstance =
{
settings,
...
}:
{
nixosModule =
{
pkgs,
lib,
...
}:
{
config = {
clan.core.state.localsend.folders = [ "/var/localsend" ];
environment.systemPackages =
let
localsend-ensure-config = pkgs.writers.writePython3Bin "localsend-ensure-config" {
} ./localsend-ensure-config.py;
localsend = pkgs.writeShellScriptBin "localsend" ''
set -xeu
${lib.getExe localsend-ensure-config} ${
lib.optionalString (settings.displayName != null) settings.displayName
}
${if settings.package != null then lib.getExe settings.package else lib.getExe pkgs.localsend}
'';
in
[ localsend ];
networking.firewall.allowedTCPPorts = [ 53317 ];
# This is currently needed because there is no ipv6 multicasting support yet
systemd.network.networks = lib.mkIf (settings.ipv4Addr != null) {
"09-zerotier" = {
networkConfig = {
Address = settings.ipv4Addr;
};
};
};
};
};
};
};
}

View File

@@ -0,0 +1,18 @@
{ lib, self, ... }:
{
clan.modules = {
localsend = lib.modules.importApply ./default.nix { };
};
perSystem =
{ pkgs, ... }:
{
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
localsend = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};
}

View File

@@ -0,0 +1,64 @@
import json
import sys
from pathlib import Path
def load_json(file_path: Path) -> dict[str, any]:
try:
with file_path.open("r") as file:
return json.load(file)
except FileNotFoundError:
return {}
def save_json(file_path: Path, data: dict[str, any]) -> None:
with file_path.open("w") as file:
json.dump(data, file, indent=4)
def update_json(file_path: Path, updates: dict[str, any]) -> None:
data = load_json(file_path)
data.update(updates)
save_json(file_path, data)
def config_location() -> str:
config_file = "shared_preferences.json"
config_directory = ".local/share/org.localsend.localsend_app"
config_path = Path.home() / Path(config_directory) / Path(config_file)
return config_path
def ensure_config_directory() -> None:
config_directory = Path(config_location()).parent
config_directory.mkdir(parents=True, exist_ok=True)
def load_config() -> dict[str, any]:
return load_json(config_location())
def save_config(data: dict[str, any]) -> None:
save_json(config_location(), data)
def update_username(username: str, data: dict[str, any]) -> dict[str, any]:
data["flutter.ls_alias"] = username
return data
def main(argv: list[str]) -> None:
try:
display_name = argv[1]
except IndexError:
# This is not an error, just don't update the name
print("No display name provided.")
sys.exit(0)
ensure_config_directory()
updated_data = update_username(display_name, load_config())
save_config(updated_data)
if __name__ == "__main__":
main(sys.argv[:2])

View File

@@ -0,0 +1,51 @@
{
pkgs,
nixosLib,
clan-core,
...
}:
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
hostPkgs = pkgs;
name = "localsend";
clan = {
directory = ./.;
modules."@clan/localsend" = ../../default.nix;
inventory = {
machines.server = { };
instances = {
localsend-test = {
module.name = "@clan/localsend";
roles.default.machines."server".settings = {
displayName = "Test Instance";
ipv4Addr = "192.168.56.2/24";
};
};
};
};
};
nodes = {
server = { };
};
testScript = ''
start_all()
# Check that the localsend wrapper script is available
server.succeed("command -v localsend")
# Verify the 09-zerotier network is configured with the specified IP address
server.succeed("grep -q 'Address=192.168.56.2/24' /etc/systemd/network/09-zerotier.network")
'';
}
)

View File

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

View File

@@ -1,18 +1,17 @@
{ lib, ... }:
let
module = lib.modules.importApply ./default.nix { };
in
{ lib, self, ... }:
{
clan.modules = {
mycelium = module;
mycelium = lib.modules.importApply ./default.nix { };
};
perSystem =
{ ... }:
{ pkgs, ... }:
{
clan.nixosTests.mycelium = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/mycelium" = module;
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
mycelium = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};
}

View File

@@ -1,42 +1,53 @@
{
module,
pkgs,
nixosLib,
clan-core,
...
}:
{
name = "mycelium";
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
clan = {
hostPkgs = pkgs;
test.useContainers = false;
directory = ./.;
inventory = {
machines.server = { };
name = "mycelium";
instances = {
mycelium-test = {
module.name = "@clan/mycelium";
roles.peer.machines."server".settings = {
openFirewall = true;
addHostedPublicNodes = true;
clan = {
test.useContainers = false;
directory = ./.;
modules."@clan/mycelium" = ../../default.nix;
inventory = {
machines.server = { };
instances = {
mycelium-test = {
module.name = "@clan/mycelium";
roles.peer.machines."server".settings = {
openFirewall = true;
addHostedPublicNodes = true;
};
};
};
};
};
};
nodes = {
server = { };
};
nodes = {
server = { };
};
testScript = ''
start_all()
testScript = ''
start_all()
# Check that mycelium service is running
server.wait_for_unit("mycelium")
server.succeed("systemctl status mycelium")
# Check that mycelium service is running
server.wait_for_unit("mycelium")
server.succeed("systemctl status mycelium")
# Check that mycelium is listening on its default port
server.wait_until_succeeds("${pkgs.iproute2}/bin/ss -tulpn | grep -q 'mycelium'", 10)
'';
}
# Check that mycelium is listening on its default port
server.wait_until_succeeds("${pkgs.iproute2}/bin/ss -tulpn | grep -q 'mycelium'", 10)
'';
}
)

View File

@@ -1,19 +1,18 @@
{ lib, ... }:
let
module = lib.modules.importApply ./default.nix { };
in
{ lib, self, ... }:
{
clan.modules = {
packages = module;
packages = lib.modules.importApply ./default.nix { };
};
perSystem =
{ ... }:
{ pkgs, ... }:
{
clan.nixosTests.packages = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/packages" = module;
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
packages = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};

View File

@@ -1,28 +1,41 @@
{
module,
pkgs,
nixosLib,
clan-core,
...
}:
{
name = "packages";
clan = {
directory = ./.;
inventory = {
machines.server = { };
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
instances.default = {
module.name = "@clan/packages";
roles.default.machines."server".settings = {
packages = [ "cbonsai" ];
hostPkgs = pkgs;
name = "packages";
clan = {
directory = ./.;
modules."@clan/packages" = ../../default.nix;
inventory = {
machines.server = { };
instances.default = {
module.name = "@clan/packages";
roles.default.machines."server".settings = {
packages = [ "cbonsai" ];
};
};
};
};
};
nodes.server = { };
nodes.server = { };
testScript = ''
start_all()
server.succeed("cbonsai")
'';
}
testScript = ''
start_all()
server.succeed("cbonsai")
'';
}
)

View File

@@ -1,19 +1,18 @@
{ lib, ... }:
let
module = lib.modules.importApply ./default.nix { };
in
{ lib, self, ... }:
{
clan.modules = {
sshd = module;
sshd = lib.modules.importApply ./default.nix { };
};
perSystem =
{ ... }:
{ pkgs, ... }:
{
clan.nixosTests.sshd = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/sshd" = module;
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
sshd = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};

View File

@@ -1,50 +1,62 @@
{
module,
pkgs,
nixosLib,
clan-core,
...
}:
{
name = "sshd";
clan = {
directory = ./.;
inventory = {
machines.server = { };
machines.client = { };
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
instances = {
sshd-test = {
module.name = "@clan/sshd";
roles.server.machines."server".settings = {
certificate.searchDomains = [ "example.com" ];
hostKeys.rsa.enable = true;
};
roles.client.machines."client".settings = {
certificate.searchDomains = [ "example.com" ];
hostPkgs = pkgs;
name = "sshd";
clan = {
directory = ./.;
modules."@clan/sshd" = ../../default.nix;
inventory = {
machines.server = { };
machines.client = { };
instances = {
sshd-test = {
module.name = "@clan/sshd";
roles.server.machines."server".settings = {
certificate.searchDomains = [ "example.com" ];
hostKeys.rsa.enable = true;
};
roles.client.machines."client".settings = {
certificate.searchDomains = [ "example.com" ];
};
};
};
};
};
};
nodes = {
server = { };
client = { };
};
nodes = {
server = { };
client = { };
};
testScript = ''
start_all()
testScript = ''
start_all()
# Check that sshd port is open on the server
server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 22")
# Check that sshd port is open on the server
server.succeed("${pkgs.netcat}/bin/nc -z -v 127.0.0.1 22")
# Check that /etc/ssh/ssh_known_hosts contains the required CA string on the server
server.succeed("grep '^@cert-authority ssh-ca,\*.example.com ssh-ed25519 ' /etc/ssh/ssh_known_hosts")
# Check that /etc/ssh/ssh_known_hosts contains the required CA string on the server
server.succeed("grep '^@cert-authority ssh-ca,\*.example.com ssh-ed25519 ' /etc/ssh/ssh_known_hosts")
# Check that server contains a line starting with 'localhost,server ssh-ed25519'
server.succeed("grep '^localhost,server ssh-ed25519 ' /etc/ssh/ssh_known_hosts")
# Check that server contains a line starting with 'localhost,server ssh-ed25519'
server.succeed("grep '^localhost,server ssh-ed25519 ' /etc/ssh/ssh_known_hosts")
# Check that /etc/ssh/ssh_known_hosts contains the required CA string on the client
client.succeed("grep '^.cert-authority ssh-ca.*example.com ssh-ed25519 ' /etc/ssh/ssh_known_hosts")
'';
}
# Check that /etc/ssh/ssh_known_hosts contains the required CA string on the client
client.succeed("grep '^.cert-authority ssh-ca.*example.com ssh-ed25519 ' /etc/ssh/ssh_known_hosts")
'';
}
)

View File

@@ -1,16 +1,19 @@
{ lib, ... }:
let
module = lib.modules.importApply ./default.nix { };
in
{ lib, self, ... }:
{
clan.modules.state-version = module;
perSystem =
{ ... }:
{
clan.nixosTests.state-version = {
imports = [ ./tests/vm/default.nix ];
clan.modules = {
state-version = lib.modules.importApply ./default.nix { };
};
clan.modules."@clan/state-version" = module;
perSystem =
{ pkgs, ... }:
{
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
state-version = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};
}

View File

@@ -1,20 +1,37 @@
{
name = "state-version";
pkgs,
nixosLib,
clan-core,
...
}:
clan = {
directory = ./.;
inventory = {
machines.server = { };
instances.default = {
module.name = "@clan/state-version";
roles.default.machines."server" = { };
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
hostPkgs = pkgs;
name = "state-version";
clan = {
directory = ./.;
modules."@clan/state-version" = ../../default.nix;
inventory = {
machines.server = { };
instances.default = {
module.name = "@clan/state-version";
roles.default.machines."server" = { };
};
};
};
};
nodes.server = { };
nodes.server = { };
testScript = ''
start_all()
'';
}
testScript = ''
start_all()
'';
}
)

View File

@@ -1,16 +1,17 @@
{ lib, ... }:
let
module = lib.modules.importApply ./default.nix { };
in
{ lib, self, ... }:
{
clan.modules.trusted-nix-caches = module;
clan.modules = {
trusted-nix-caches = lib.modules.importApply ./default.nix { };
};
perSystem =
{ ... }:
{ pkgs, ... }:
{
clan.nixosTests.trusted-nix-caches = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/trusted-nix-caches" = module;
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
trusted-nix-caches = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};
}

View File

@@ -1,24 +1,40 @@
{
name = "trusted-nix-caches";
pkgs,
nixosLib,
clan-core,
...
}:
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
clan = {
directory = ./.;
inventory = {
machines.server = { };
hostPkgs = pkgs;
instances = {
trusted-nix-caches = {
module.name = "@clan/trusted-nix-caches";
roles.default.machines."server" = { };
name = "trusted-nix-caches";
clan = {
directory = ./.;
modules."@clan/trusted-nix-caches" = ../../default.nix;
inventory = {
machines.server = { };
instances = {
trusted-nix-caches = {
module.name = "@clan/trusted-nix-caches";
roles.default.machines."server" = { };
};
};
};
};
};
nodes.server = { };
nodes.server = { };
testScript = ''
start_all()
server.succeed("grep -q 'cache.clan.lol' /etc/nix/nix.conf")
'';
}
testScript = ''
start_all()
server.succeed("grep -q 'cache.clan.lol' /etc/nix/nix.conf")
'';
}
)

View File

@@ -1,16 +1,18 @@
{ lib, ... }:
let
module = lib.modules.importApply ./default.nix { };
in
{ lib, self, ... }:
{
clan.modules.users = module;
clan.modules = {
users = lib.modules.importApply ./default.nix { };
};
perSystem =
{ ... }:
{ pkgs, ... }:
{
clan.nixosTests.users = {
imports = [ ./tests/vm/default.nix ];
clan.modules."@clan/users" = module;
checks = lib.optionalAttrs (pkgs.stdenv.isLinux) {
users = import ./tests/vm/default.nix {
inherit pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};
}

View File

@@ -1,50 +1,67 @@
{
name = "users";
pkgs,
nixosLib,
clan-core,
...
}:
clan = {
directory = ./.;
inventory = {
machines.server = { };
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
instances = {
root-password-test = {
module.name = "@clan/users";
roles.default.machines."server".settings = {
user = "root";
prompt = false;
hostPkgs = pkgs;
name = "users";
clan = {
directory = ./.;
modules."@clan/users" = ../../default.nix;
inventory = {
machines.server = { };
instances = {
root-password-test = {
module.name = "@clan/users";
roles.default.machines."server".settings = {
user = "root";
prompt = false;
};
};
};
user-password-test = {
module.name = "@clan/users";
roles.default.machines."server".settings = {
user = "testuser";
prompt = false;
user-password-test = {
module.name = "@clan/users";
roles.default.machines."server".settings = {
user = "testuser";
prompt = false;
};
};
};
};
};
};
nodes = {
server = {
users.users.testuser.group = "testuser";
users.groups.testuser = { };
users.users.testuser.isNormalUser = true;
nodes = {
server = {
users.users.testuser.group = "testuser";
users.groups.testuser = { };
users.users.testuser.isNormalUser = true;
};
};
};
testScript = ''
start_all()
testScript = ''
start_all()
server.wait_for_unit("multi-user.target")
server.wait_for_unit("multi-user.target")
# Check that the testuser account exists
server.succeed("id testuser")
# Check that the testuser account exists
server.succeed("id testuser")
# Try to log in as the user using the generated password
# TODO: fix
# password = server.succeed("cat /run/clan/vars/user-password/user-password").strip()
# server.succeed(f"echo '{password}' | su - testuser -c 'echo Login successful'")
# Try to log in as the user using the generated password
# TODO: fix
# password = server.succeed("cat /run/clan/vars/user-password/user-password").strip()
# server.succeed(f"echo '{password}' | su - testuser -c 'echo Login successful'")
'';
}
'';
}
)

View File

@@ -1,5 +1,6 @@
{
self,
inputs,
lib,
...
}:
@@ -9,14 +10,28 @@ let
};
in
{
clan.modules.wifi = module;
clan.modules = {
wifi = module;
};
perSystem =
{ ... }:
{ pkgs, ... }:
{
clan.nixosTests.wifi = {
imports = [ ./tests/vm/default.nix ];
/**
1. Prepare the test vars
nix run .#generate-test-vars -- clanServices/hello-world/tests/vm hello-service
clan.modules."@clan/wifi" = module;
};
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 inputs pkgs;
clan-core = self;
nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
};
};
};
}

View File

@@ -1,29 +1,46 @@
{
name = "wifi";
pkgs,
nixosLib,
clan-core,
module,
...
}:
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
clan = {
directory = ./.;
test.useContainers = false;
inventory = {
hostPkgs = pkgs;
machines.test = { };
name = "wifi-service";
instances = {
wg-test-one = {
module.name = "@clan/wifi";
clan = {
directory = ./.;
test.useContainers = false;
modules."@clan/wifi" = module;
inventory = {
roles.default.machines = {
test.settings.networks.one = { };
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"
'';
}
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

@@ -8,7 +8,9 @@ let
module = lib.modules.importApply ./default.nix { };
in
{
clan.modules.zerotier = module;
clan.modules = {
zerotier = module;
};
perSystem =
{ ... }:
let
@@ -26,11 +28,11 @@ in
imports = [
unit-test-module
];
clan.nixosTests.zerotier = {
imports = [ ./tests/vm/default.nix ];
clan.modules.zerotier = module;
};
# zerotier = import ./tests/vm/default.nix {
# inherit module;
# inherit inputs pkgs;
# clan-core = self;
# nixosLib = import (self.inputs.nixpkgs + "/nixos/lib") { };
# };
};
}

View File

@@ -1,27 +1,43 @@
{
name = "zerotier";
pkgs,
nixosLib,
clan-core,
module,
...
}:
nixosLib.runTest (
{ ... }:
{
imports = [
clan-core.modules.nixosVmTest.clanTest
];
clan = {
directory = ./.;
inventory = {
hostPkgs = pkgs;
machines.jon = { };
machines.sara = { };
machines.bam = { };
name = "zerotier";
instances = {
"zerotier" = {
module.name = "zerotier";
clan = {
directory = ./.;
modules."zerotier" = module;
inventory = {
roles.peer.tags.all = { };
roles.controller.machines.bam = { };
roles.moon.machines = { };
machines.jon = { };
machines.sara = { };
machines.bam = { };
instances = {
"zerotier" = {
module.name = "zerotier";
roles.peer.tags.all = { };
roles.controller.machines.bam = { };
};
};
};
};
};
# This is not an actual vm test, this is a workaround to
# generate the needed vars for the eval test.
testScript = "";
}
# This is not an actual vm test, this is a workaround to
# generate the needed vars for the eval test.
testScript = '''';
}
)

View File

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

View File

@@ -0,0 +1,15 @@
{
"data": "ENC[AES256_GCM,data:AGYme1x1pE7SVk6HowmIYMN3EHNaZglW97geihpDCkKqArq/zD2IHxbgo8OtXmaNws16i0R6LehWJTL21fVmnAEA9GNZQOE/Y4Q=,iv:Kc3bDcOwJmxHnnlBweUbqDE77VVFZFelEGpmpfBSct8=,tag:m4kzx3nOtexD91kisQafFw==,type:str]",
"sops": {
"age": [
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTc2Q5NTY1ejl5ODhSOXhv\nVUFrb0xvblErWEY1R0k3UXNBQk5Ja1MwaERVCmdISk1RSGFUL2FRMWlPSFdERjB6\nalltcHZLd21XOVFuaExSRUNQc1VmdjAKLS0tIGg0ZGdvbm9wbC9Jd255cHNmVWxP\nWStOQS9EQW9WQUtLZVp5SDBmM1ByaEEKzviyWc0yLbDMwk/CHhTwntrjA5LX44Wu\nNdlsQG/yfRaqRL1TKZztT9RnX0293gOEZFvoYZasEJJAIeBoZvN6VQ==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-05-29T13:14:51Z",
"mac": "ENC[AES256_GCM,data:uCk2e5aFHZhttLkIdvDU3KARN7PiHKLtXsqxmuLkZP903XhDTCuj1GH6S0C9UN5LftlaVjCEaqlgx68cCNwTc9bTUnhSdVVjMWy0gjxKZ1Y25YzOMlEmOAk/TZqUvnMn/cUL8KOeBnymPbAeqLm8yATjwsyx5+GrFrIVxwGQzUA=,iv:UMX2Ik0xlcljMZyBhjOpvYcsJCC5Wb6d/rgbTFb+6oM=,tag:HH05tFDzOcRrQ8TTXxrDyw==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:iNOb,iv:24+bKY5u61JYsvLHV8TIUBVmJPV1aX/BJr//c7le68o=,tag:ANCOrzvnukvqyKGf+L8gFQ==,type:str]",
"sops": {
"age": [
{
"recipient": "age13ahclyps97532zt2sfta5zrfx976d3r2jmctj8d36vj9x5v5ffqq304fqf",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxN2EwVHN3SENVTjdjZGRi\nQmJOWlNGYmpmM1BnZnpYWGhaSlRaUVJIODFRCkhhMUhyZzVWWk53SDBwSVBVZGVY\nVUpMTm9qWTIzc3VwdGJHcUVWVzFlV0UKLS0tIDBBVXdlS1FFbzNPSnlZWWtEaDJi\nK215OWQvMVRCRUZyQjFZckJFbHBZeDQK2cqgDnGM5uIm834dbQ3bi3nQA5nPq6Bf\n0+sezXuY55GdFS6OxIgI5/KcitHzDE0WHOvklIGDCSysoXIQ3QXanA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0NDB5SVcrU0V6akYwbDlv\na1BuSm5XbjYwN2ZkZWtIcnhBVHBTWGFxd24wCnZTVGlPRm5uZEd3QXYwdFRMS09K\nWWw5N2RJZ3d4N0VDMWZmM2lkYVM4VncKLS0tIGplTDVka1VoUVdXMU9VS3hYSlZ1\nRjZGL25hQWxHWEx3OXdQamJiNG9KaDgKk94uXPuCE/M4Hz/7hVKJPHuzQfbOQi/9\nVfR2i17Hjcq08l68Xzn+DllQEAFdts2fS96Pu4FFKfiLK7INl/fUOg==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-05-29T13:15:02Z",
"mac": "ENC[AES256_GCM,data:4beXC5ONY5RLChluoVkklpDnaf/KCjlUzpQkFVSp7vauQmMKeTK40xqfvY5d+64u/OKRTIdc38KQTwhZ0pYzOv1LcJOWbHrGu7XadlALKgyUqKOZy03G2O8y0IF6t/LUK8TaNFnNvNteFsfD36/+wkRaxPJe7MKXGqPhWf6RC78=,iv:FR/PQUZqL3HnyVbW+H1QlZMmgFxA5juSb88wuatIlHM=,tag:parvZw3y9ZHieZ8pmUjCZQ==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
{
"data": "ENC[AES256_GCM,data:HHWyM9d6StpKc6uTxg==,iv:blDyfL/xSThCt+dhxeR5eOLa11OsIkbe+w4ReLBv754=,tag:qGHcDXS4DWdUIXUvtLc5XQ==,type:str]",
"sops": {
"age": [
{
"recipient": "age13ahclyps97532zt2sfta5zrfx976d3r2jmctj8d36vj9x5v5ffqq304fqf",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPdkQyYnQ1UzlCWEFtdnJh\nMWlBK0RGcENTMmRITWM5SSs2Mkt2N0ZKdm5VClNTS0NuR05OVHY3QkFLZWt6bTUx\nMzJLc2Vib1ZUbW1VM0lhYXFFeEhOaEEKLS0tIHVoODVOK3BUU2JDZkJkN2I2Wm1L\nMWM0TUNQazljZS9uWXRKRFlxWmd0clUKg1YhJoRea05c24hCuZKYvqyvjuu965KD\nr4GLtyqQ6wt9sn50Rzx5cAY/Ac684DNFJVZ1RwG1NTB2kmXcVP8SJA==\n-----END AGE ENCRYPTED FILE-----\n"
},
{
"recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoZTA5QXpsOXR3L2FKcnJD\neUxzNVp3M2VQMFFaUUxwNXQ4UTlXa01rR0IwCjkyU2hmdlVYbWY4WUpVK0J1ZC9Q\nRjVkYWlGTlh1MFY3R3FxMEZHODZXMmcKLS0tIFV3bGdvUEtnT21wRWJveEQwdTBV\nbGFUUExBZWR1enQ0c0l0dUY3TnErM3cKutl5cv8dSlpQA7SXUYWJq1M0yLmko/Bx\nUvxxGGLQaK0Mp81Z5mOsjNhcVQrY160AyVnWJ0z39cqOJq9PpXRP+A==\n-----END AGE ENCRYPTED FILE-----\n"
}
],
"lastmodified": "2025-05-29T13:15:02Z",
"mac": "ENC[AES256_GCM,data:Y2FFQevNHSJrEtCmGHQXcpfyof0v2IF8ey79g7EfGj13An4ylhvogsVjRtfMkQvKD5GZykswZgmh+PmKUIzRoc+cvnMLu0iBzleYv+KzpYqtvUpdK0+NQn/4cKOoafajwNV7EuCQh+SkJgSGjNSbMs8xtIb4q9DmJyTcTbG0JQ4=,iv:xmA/cEhl/J0Z+8QR2GFiGWRw4aH/C4HmO+Qd4e25utw=,tag:/hG5S/EmRt8CjAy8DfBoqg==,type:str]",
"unencrypted_suffix": "_unencrypted",
"version": "3.10.2"
}
}

View File

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

View File

@@ -83,11 +83,16 @@ nav:
- Services:
- Overview: reference/clanServices/index.md
- reference/clanServices/admin.md
- reference/clanServices/auto-upgrade.md
- reference/clanServices/borgbackup.md
- reference/clanServices/deltachat.md
- reference/clanServices/emergency-access.md
- reference/clanServices/ergochat.md
- reference/clanServices/garage.md
- reference/clanServices/heisenbridge.md
- reference/clanServices/hello-world.md
- reference/clanServices/importer.md
- reference/clanServices/localsend.md
- reference/clanServices/mycelium.md
- reference/clanServices/packages.md
- reference/clanServices/sshd.md

View File

@@ -104,7 +104,7 @@ Limitations:
### Where to find examples for NixOS container tests
Existing NixOS container tests in clan-core can be found by using `ripgrep`:
Existing nixos container tests in clan-core can be found by using ripgrep:
```shellSession
rg self.clanLib.test.containerTest
@@ -131,7 +131,7 @@ Due to superior efficiency,
### Finding examples of python tests
Existing python tests in clan-core can be found by using `ripgrep`:
Existing python tests in clan-core can be found by using ripgrep:
```shellSession
rg "import pytest"
```
@@ -196,17 +196,17 @@ Nix eval tests are good for testing any nix logic, including
- nix functions
- nix libraries
- modules for the NixOS module system
- modules for the nixos module system
When not to use
- tests that require building nix derivations (except some very cheap ones)
- tests that require running programs written in other languages
- tests that require building or running NixOS machines
- tests that require building or running nixos machines
### Finding examples of nix eval tests
Existing nix eval tests can be found via this `ripgrep` command:
Existing nix eval tests can be found via this ripgrep command:
```shellSession
rg "nix-unit --eval-store"
@@ -284,11 +284,11 @@ Add `lib.trace` or `lib.traceVal` statements in order to print some variables du
#### Nix repl
Use `nix repl` to evaluate and inspect the test.
Use `nix repl` to evaluate to inspec the test.
Each test consists of an `expr` (expression) and an `expected` field. `nix-unit` simply checks if `expr == expected` and prints the diff if that's not the case.
Each test consists opf an `expr` (expression) and an `expected` field. `nix-unit` simply checks if `expr == expected` and prints the diff if that's not the case.
`nix repl` can be used to inspect an `expr` manually, or any other variables that you choose to expose.
`nix repl` can be used to inspect `expr` manually, or any other variables that you choose to expose.
Example:

26
flake.lock generated
View File

@@ -16,11 +16,11 @@
]
},
"locked": {
"lastModified": 1750183842,
"narHash": "sha256-znYkJ+9GUNQCmFtEhGvMRZPRP3fdGmbiuTyyrJRKUGA=",
"rev": "cb75111e4c99c7a960cfdd0d743f75663e36cbfa",
"lastModified": 1749836431,
"narHash": "sha256-TygKDHK5tAzl9vPh0HcCzWQ5leNh1fCratxkHLFFaRA=",
"rev": "b7cc9ee5989081fe7f11959b647792f267319441",
"type": "tarball",
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/cb75111e4c99c7a960cfdd0d743f75663e36cbfa.tar.gz"
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/b7cc9ee5989081fe7f11959b647792f267319441.tar.gz"
},
"original": {
"type": "tarball",
@@ -74,11 +74,11 @@
]
},
"locked": {
"lastModified": 1750618568,
"narHash": "sha256-w9EG5FOXrjXGfbqCcQg9x1lMnTwzNDW5BMXp8ddy15E=",
"lastModified": 1749873626,
"narHash": "sha256-1Mc/D/1RwwmDKY59f4IpDBgcQttxffm+4o0m67lQ8hc=",
"owner": "nix-darwin",
"repo": "nix-darwin",
"rev": "1dd19f19e4b53a1fd2e8e738a08dd5fe635ec7e5",
"rev": "2f140d6ac8840c6089163fb43ba95220c230f22b",
"type": "github"
},
"original": {
@@ -102,11 +102,11 @@
},
"nixos-facter-modules": {
"locked": {
"lastModified": 1750412875,
"narHash": "sha256-uP9Xxw5XcFwjX9lNoYRpybOnIIe1BHfZu5vJnnPg3Jc=",
"lastModified": 1743671943,
"narHash": "sha256-7sYig0+RcrR3sOL5M+2spbpFUHyEP7cnUvCaqFOBjyU=",
"owner": "nix-community",
"repo": "nixos-facter-modules",
"rev": "14df13c84552a7d1f33c1cd18336128fbc43f920",
"rev": "58ad9691670d293a15221d4a78818e0088d2e086",
"type": "github"
},
"original": {
@@ -118,10 +118,10 @@
"nixpkgs": {
"locked": {
"lastModified": 315532800,
"narHash": "sha256-VgDAFPxHNhCfC7rI5I5wFqdiVJBH43zUefVo8hwo7cI=",
"rev": "41da1e3ea8e23e094e5e3eeb1e6b830468a7399e",
"narHash": "sha256-lvaKRckKFtUXIQTiK1e/0JnZRlEm/y0olZwbkl8ArrY=",
"rev": "08fcb0dcb59df0344652b38ea6326a2d8271baff",
"type": "tarball",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre814815.41da1e3ea8e2/nixexprs.tar.xz"
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre812400.08fcb0dcb59d/nixexprs.tar.xz"
},
"original": {
"type": "tarball",

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