Compare commits

..

1 Commits

Author SHA1 Message Date
Jörg Thalheim
62a1f1a9a9 machines: fix remote-program for darwin nix copy
MacOS doesn't come with a proper login shell for ssh and therefore
doesn't have nix in $PATH as it doesn't source /etc/profile.
This restores the remote-program parameter that was accidentally
removed in commit cff5d61f26.
2025-06-17 13:02:15 +02:00
378 changed files with 16087 additions and 12121 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

@@ -1,6 +1,6 @@
{ fetchgit }:
fetchgit {
url = "https://git.clan.lol/clan/clan-core.git";
rev = "28131afbbcd379a8ff04c79c66c670ef655ed889";
sha256 = "1294cwjlnc341fl6zbggn4rgq8z33gqkcyggjfvk9cf7zdgygrf6";
rev = "13a9b1719835ef4510e4adb6941ddfe9a91d41cb";
sha256 = "sha256-M+pLnpuX+vIsxTFtbBZaNA1OwGQPeSbsMbTiDl1t4vY=";
}

View File

@@ -9,7 +9,6 @@ in
{
imports = filter pathExists [
./backups/flake-module.nix
../nixosModules/clanCore/machine-id/tests/flake-module.nix
./devshell/flake-module.nix
./flash/flake-module.nix
./impure/flake-module.nix
@@ -37,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
@@ -107,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,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:IyVffgpj8MSp2pKoic3+UMYBbeu6Fo/BGslSNQpNoURuulqSze7AZPXry/PnXEWAEvoqkfUURAq8kZdWxu5Go6N8YSkd6/sVOScw2Osa3v/qh9ysEPCuoIwYY1/h9IfUmzNM8Q3hgF4BXhb5D1F2cFQ0fZM8dQU1jn8J0DTfwXlR9ALgWRoCCAXHQxBBc1SYRMlnXbbCACfRxkJHGAtQbwwzsGo/as/Ng+YVUiXdQyLJUv3hwGax793pyX8YKQl+Bu1G3+qZJGlL3lkjL5T/7HIIs81YN9XgsT7G9kvMmR4tJWpHCHGHxU9aofSHSTxUX1lil7GUsR6xSefFEIgbZaQ27KUXwKrlZ8dQKJZIwCiUczkMtrdwBZlKkzqlD+Hi5yGSSUsIQ+lvjeR74UwPXOX/HtvGA+mEgcTSO48FCWmU7ymjvTXyoVf9ui75IZjRyHKfpM8WlFHS+lXCSEN+B6HHHMIEeo1nOpKk6e2qRd28oJHwzoqlms63YFEbPd4d2DTGNVAtO3nhXe4+5IqzvR1DZHoNQO7AiDDAJiz5DKPoOkq/u7FEBVewKQWgZyvt09vC0X+1kxqXeeOXmJILLuRngri5BKLS3YR3hgmKbN31G4KIQm3lJUoE6F0c0Opu5lAJZStwMQcwmflrXdlwYsuzMfc48wcWQYtKenuG+r5DzR0je6iwxaru3XWf/aqUnga700+rFMz5dUVOy8IlJYmpd2hdpjwriTcJyV4KwplWzdjhnb7csjrzgAiHJ5iF2MHzfVgVGje0pk6An2+iUEqoh+fmFRVlA9ZZ923Ksxl+iYRn+29Cde/JgdZu5EywDHHs3yHjfbMnFvoXuAOJ/2TX3cA95DjD2+OGvd78jKExF+Q9rda5IdU6kb93QKVf+OyOSNpTdQhKnJRusou5Q255PqoTguiJIMDoH00qGA2y5k/5q49wl9Xq5jNiU70fyaygoPOfxbghfIeIjT0z2/awaXoUW33jAqXylxY0gTTTNPZI7w4d6hRqixgU27dyCkdAQfN+bnCe6ExXSLczmZf8UfGegjRm4lyKOKgX/QLLgRwStB6BBHGn+a+8NrAH9DFgpof4fJ74LN7Vx5oQTIGbQTvSkjGy4VOez0ajmqjQI516XrBvFbLfAnYA1jGaR0ZRkD6ALISdwFhjlycAc2tAffv9F9WKsrVKWvCU3pqGV0OmWtk5chqWrfvrFY7tj0aVZUUHZBBynksiUx/8Tt5unS3OUap86FiPUknvh5lPEPZqrZc/TNiCN+8oLFAtkOXyEnFSJjZSOB/vLcd+lMkWhcGa+usvE1hGZEaiTOXsN9IOihjcZjcR2YYHR0LlkFxzzlN9ILllt7W1cgKApCovZVmtJuU3fCwjOocP1iBeAFNQfzgJ+6gMqXN8oufCrw7g4v8Y0hNjjEJEriufO+qLIg3nHivcgwy4Cp/fL9wof9UFsaKl2okbP66im7BuDcO/x8ZQ+lk9OjWPSHFJTAPScgiwPchwn74aaHiihjhocyWQ2uVzKn9JkBFkMcoUlAz7xwUFBW7AYvVS/dFQA0xF1l4aki7JdoK6L1JptEu1KpbQ2UHLoIp+W0jFB2MRnSDOEtUHMR9af/vHtgV4xoZoCCHDz/PAlK74C2wK20LO1FoQJdch2/IO6bGiAj832wrLKDp4T2VPooS/hqd6U0Jmvtvcv0vdKbewCDgbfMH3lcydwXl4eQh5lDE8Fm+HXo2Zt8lvqRexeqzyIvGy1P/ndx21uxuacTkp3yi2qTHrdy3IkGMIHTZtfQATRgbxMEpUwHyk2hr08RNqlitbHzgor2+Gn6J/DaLuzvfaFl4p5npjNJ5dGjQqjj5VEz/L16VDeBLL7b9BKorqb7LvZUlqVcCYBcd7S7mHUHT4GhlLaEb67PxA6eJbhdVtyB8o7sTE3tGY8OdBg0ClJB2cEJHS1SqQRS+GdIhuWwZS+Go7YCx9EnnqezMMuEK4lfB+LCGaVDj32XNVtaBhDvdxPuBN5QFgjbWMIOGFV8O3l9dTrlDDwYpmFANG0Wz0pjOzX4CL4LXxNSu9k2SSdPw4iKoe/7Rl3yxVsKhrLOQPW3M6Hc/vULNBMvZxRH32zPE4qKo9SD+h6rwBrC9JiwrWdLJQHtu6Yk8GT5agQNCVC1pogvHVvB7+15UdZ7yjyhUONGOfIHDB2peYame3gF0Nce3dbzqUUE5tNuD4FyKkYZbGNr9WDD9nX1i/7EboDnFh2x6pAU+MRl4oyW0dtHiiTnIR6bE=,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

@@ -23,6 +23,7 @@ in
iwd = ./iwd;
localbackup = ./localbackup;
localsend = ./localsend;
machine-id = ./machine-id;
matrix-synapse = ./matrix-synapse;
moonlight = ./moonlight;
mumble = ./mumble;

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

@@ -0,0 +1,4 @@
---
description = "Sets the /etc/machine-id and exposes it as a nix option"
features = [ "inventory" ]
---

View File

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

View File

@@ -0,0 +1,45 @@
{
config,
pkgs,
lib,
...
}:
let
var = config.clan.core.vars.generators.machine-id.files.machineId or { };
in
{
config = lib.mkMerge [
(lib.mkIf ((var.value or null) != null) {
assertions = [
{
assertion = lib.stringLength var.value == 32;
message = "machineId must be exactly 32 characters long.";
}
];
boot.kernelParams = [
''systemd.machine_id=${var.value}''
];
environment.etc."machine-id" = {
text = var.value;
};
})
{
clan.core.vars.generators.machine-id = {
files.machineId.secret = false;
runtimeInputs = [
pkgs.coreutils
pkgs.bash
];
script = ''
uuid=$(bash ${./uuid4.sh})
# Remove the hyphens from the UUID
uuid_no_hyphens=$(echo -n "$uuid" | tr -d '-')
echo -n "$uuid_no_hyphens" > "$out/machineId"
'';
};
}
];
}

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

@@ -8,15 +8,8 @@
roles.default = {
perInstance.nixosModule =
{ config, pkgs, ... }:
{
config,
pkgs,
lib,
...
}:
{
services.garage.enable = lib.mkDefault true;
systemd.services.garage.serviceConfig = {
LoadCredential = [
"rpc_secret_path:${config.clan.core.vars.generators.garage-shared.files.rpc_secret.path}"

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"
}
}

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