refactor: make bootstrap read config from flake

This commit is contained in:
Swarsel 2024-12-24 16:01:33 +01:00
parent 5637ab54fc
commit ae63e40f04
Signed by: swarsel
GPG key ID: 26A54C31F2A4FD84
16 changed files with 481 additions and 428 deletions

View file

@ -7,7 +7,7 @@ keys:
- &swarsel 4BE7925262289B476DBBC17B76FD3810215AE097 - &swarsel 4BE7925262289B476DBBC17B76FD3810215AE097
- &hosts - &hosts
- &winters age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63 - &winters age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63
- &toto age1dmxw76fzr958zl23ad5h7gvtnurr8n5ajlqchgx76k6z5yj3z4zsn592fq - &toto age16vzhcvz8tyxj8e0f47fy0z4p3dsg0ak4vl52ut3l07a0tz465cxslmhevl
- &surface age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg - &surface age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg
- &nbl age16lnmuuxfuxxtty3atnhut8wseppwnhp7rdhmxqd5tdvs9qnjffjq42sqyy - &nbl age16lnmuuxfuxxtty3atnhut8wseppwnhp7rdhmxqd5tdvs9qnjffjq42sqyy
- &sync age1glge4e97vgqzh332mqs5990vteezu2m8k4wq3z35jk0q8czw3gks2d7a3h - &sync age1glge4e97vgqzh332mqs5990vteezu2m8k4wq3z35jk0q8czw3gks2d7a3h

View file

@ -982,7 +982,10 @@ The interesting part is in the start:
systemFunc = func; systemFunc = func;
in in
systemFunc { systemFunc {
specialArgs = { inherit inputs outputs self; }; specialArgs = {
inherit inputs outputs self;
lib = lib.extend (_: _: { swarselsystems = import ./lib { inherit lib; }; });
};
modules = [ ./hosts/${if isNixos then "nixos" else "darwin"}/${host} ]; modules = [ ./hosts/${if isNixos then "nixos" else "darwin"}/${host} ];
}; };
}; };
@ -1202,6 +1205,9 @@ My work machine. Built for more security, this is the gold standard of my config
{ self, inputs, outputs, config, pkgs, lib, ... }: { self, inputs, outputs, config, pkgs, lib, ... }:
let let
profilesPath = "${self}/profiles"; profilesPath = "${self}/profiles";
sharedOptions = {
isBtrfs = true;
};
in in
{ {
@ -1279,19 +1285,17 @@ My work machine. Built for more security, this is the gold standard of my config
''; '';
}; };
swarselsystems = { swarselsystems = lib.recursiveUpdate {
wallpaper = self + /wallpaper/lenovowp.png; wallpaper = self + /wallpaper/lenovowp.png;
hasBluetooth = true; hasBluetooth = true;
hasFingerprint = true; hasFingerprint = true;
impermanence = false; isImpermanence = false;
isBtrfs = true;
isCrypted = true; isCrypted = true;
}; } sharedOptions;
home-manager.users.swarsel.swarselsystems = { home-manager.users.swarsel.swarselsystems = lib.recursiveUpdate {
isLaptop = true; isLaptop = true;
isNixos = true; isNixos = true;
isBtrfs = true;
flakePath = "/home/swarsel/.dotfiles"; flakePath = "/home/swarsel/.dotfiles";
cpuCount = 16; cpuCount = 16;
# temperatureHwmon = { # temperatureHwmon = {
@ -1416,7 +1420,7 @@ My work machine. Built for more security, this is the gold standard of my config
ans = ". ~/.venvs/ansible/bin/activate"; ans = ". ~/.venvs/ansible/bin/activate";
ans2-15 = ". ~/.venvs/ansible2.15.0/bin/activate"; ans2-15 = ". ~/.venvs/ansible2.15.0/bin/activate";
}; };
}; } sharedOptions;
} }
@ -1478,7 +1482,7 @@ This is my main server that I run at home. It handles most tasks that require bi
swarselsystems = { swarselsystems = {
hasBluetooth = false; hasBluetooth = false;
hasFingerprint = false; hasFingerprint = false;
impermanence = false; isImpermanence = false;
isBtrfs = false; isBtrfs = false;
flakePath = "/home/swarsel/.dotfiles"; flakePath = "/home/swarsel/.dotfiles";
server = { server = {
@ -1713,7 +1717,7 @@ This machine mainly acts as an external sync helper. It manages the following th
swarselsystems = { swarselsystems = {
hasBluetooth = false; hasBluetooth = false;
hasFingerprint = false; hasFingerprint = false;
impermanence = false; isImpermanence = false;
isBtrfs = false; isBtrfs = false;
flakePath = "/root/.dotfiles"; flakePath = "/root/.dotfiles";
server = { server = {
@ -1735,21 +1739,15 @@ This is a slim setup for developing base configuration.
{ self, inputs, outputs, config, pkgs, lib, ... }: { self, inputs, outputs, config, pkgs, lib, ... }:
let let
profilesPath = "${self}/profiles"; profilesPath = "${self}/profiles";
sharedOptions = {
isBtrfs = true;
};
in in
{ {
imports = [ imports = [
inputs.disko.nixosModules.disko inputs.disko.nixosModules.disko
"${self}/hosts/nixos/toto/disk-config.nix" "${self}/hosts/nixos/toto/disk-config.nix"
{
_module.args = {
withSwap = true;
swapSize = "8";
rootDisk = "/dev/vda";
withImpermanence = true;
withEncryption = true;
};
}
./hardware-configuration.nix ./hardware-configuration.nix
inputs.sops-nix.nixosModules.sops inputs.sops-nix.nixosModules.sops
@ -1810,20 +1808,21 @@ This is a slim setup for developing base configuration.
firewall.enable = false; firewall.enable = false;
}; };
swarselsystems = { swarselsystems = lib.recursiveUpdate {
wallpaper = self + /wallpaper/lenovowp.png; wallpaper = self + /wallpaper/lenovowp.png;
impermanence = true; isImpermanence = true;
isBtrfs = true;
isCrypted = true; isCrypted = true;
initialSetup = true; initialSetup = true;
}; isSwap = true;
swapSize = "8G";
rootDisk = "/dev/vda";
} sharedOptions;
home-manager.users.swarsel.swarselsystems = { home-manager.users.swarsel.swarselsystems = lib.recursiveUpdate {
isLaptop = false; isLaptop = false;
isNixos = true; isNixos = true;
isBtrfs = true;
flakePath = "/home/swarsel/.dotfiles"; flakePath = "/home/swarsel/.dotfiles";
}; } sharedOptions;
} }
@ -2800,8 +2799,6 @@ This program sets up a new NixOS host.
echo " -u <target_user> specify target_user with sudo access. nix-config will be cloned to their home." echo " -u <target_user> specify target_user with sudo access. nix-config will be cloned to their home."
echo " Default='${target_user}'." echo " Default='${target_user}'."
echo " --port <ssh_port> specify the ssh port to use for remote access. Default=${ssh_port}." echo " --port <ssh_port> specify the ssh port to use for remote access. Default=${ssh_port}."
echo " --impermanence Use this flag if the target machine has impermanence enabled. WARNING: Assumes /persist path."
echo " --encryption Use this flag if the target machine has full disk encryption enabled."
echo " --debug Enable debug mode." echo " --debug Enable debug mode."
echo " -h | --help Print this help." echo " -h | --help Print this help."
exit 0 exit 0
@ -2886,16 +2883,6 @@ This program sets up a new NixOS host.
shift shift
ssh_port=$1 ssh_port=$1
;; ;;
--temp-override)
shift
temp=$1
;;
--impermanence)
persist_dir="/persist"
;;
--encryption)
disk_encryption=1
;;
--debug) --debug)
set -x set -x
;; ;;
@ -2908,6 +2895,37 @@ This program sets up a new NixOS host.
shift shift
done done
green "~SwarselSystems~ remote installer"
green "Reading system information for $target_hostname ..."
DISK="$(nix eval --raw ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.rootDisk)"
green "Root Disk: $DISK"
CRYPTED="$(nix eval ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.isCrypted)"
if [[ $CRYPTED == "true" ]]; then
green "Encryption: ✓"
disk_encryption=1
else
red "Encryption: X"
disk_encryption=0
fi
IMPERMANENCE="$(nix eval ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.isImpermanence)"
if [[ $IMPERMANENCE == "true" ]]; then
green "Impermanence: ✓"
persist_dir="/persist"
else
red "Impermanence: X"
persist_dir=""
fi
SWAP="$(nix eval ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.isSwap)"
if [[ $SWAP == "true" ]]; then
green "Swap: ✓"
else
red "Swap: X"
fi
ssh_cmd="ssh -oport=${ssh_port} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -t $target_user@$target_destination" ssh_cmd="ssh -oport=${ssh_port} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -t $target_user@$target_destination"
# ssh_root_cmd=$(echo "$ssh_cmd" | sed "s|${target_user}@|root@|") # uses @ in the sed switch to avoid it triggering on the $ssh_key value # ssh_root_cmd=$(echo "$ssh_cmd" | sed "s|${target_user}@|root@|") # uses @ in the sed switch to avoid it triggering on the $ssh_key value
ssh_root_cmd=${ssh_cmd/${target_user}@/root@} ssh_root_cmd=${ssh_cmd/${target_user}@/root@}
@ -3067,6 +3085,10 @@ This program sets up a new NixOS host.
fi fi
#+end_src #+end_src
#+RESULTS:
| trap: | undefined | signal: | exit | | | | |
| [ | Babel | evaluation | exited | with | code | 1 | ] |
#+begin_src nix :tangle pkgs/bootstrap/default.nix #+begin_src nix :tangle pkgs/bootstrap/default.nix
@ -3538,8 +3560,7 @@ let
"wallpaper" "wallpaper"
"hardware" "hardware"
"setup" "setup"
"impermanence" "server"
"filesystem"
"input" "input"
]; ];
@ -3603,22 +3624,45 @@ I usually use =mutableUsers = false= in my NixOS configuration. However, on a ne
#+begin_src nix :tangle modules/nixos/setup.nix #+begin_src nix :tangle modules/nixos/setup.nix
{ lib, ... }: { lib, ... }:
let
inherit (lib) mkOption types;
in
{ {
options.swarselsystems.flakePath = mkOption { options.swarselsystems.user = lib.mkOption {
type = types.str; type = lib.types.str;
default = "swarsel";
};
options.swarselsystems.flakePath = lib.mkOption {
type = lib.types.str;
default = ""; default = "";
}; };
options.swarselsystems.withHomeManager = mkOption { options.swarselsystems.withHomeManager = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = true; default = true;
}; };
options.swarselsystems.isSwap = lib.mkOption {
type = lib.types.bool;
default = true;
};
options.swarselsystems.swapSize = lib.mkOption {
type = lib.types.str;
default = "8G";
};
options.swarselsystems.rootDisk = lib.mkOption {
type = lib.types.str;
default = "";
};
options.swarselsystems.isCrypted = lib.mkEnableOption "uses full disk encryption"; options.swarselsystems.isCrypted = lib.mkEnableOption "uses full disk encryption";
options.swarselsystems.isPublic = lib.mkEnableOption "is a public machine (no secrets)"; options.swarselsystems.isPublic = lib.mkEnableOption "is a public machine (no secrets)";
options.swarselsystems.initialSetup = lib.mkEnableOption "initial setup (no sops keys available)"; options.swarselsystems.initialSetup = lib.mkEnableOption "initial setup (no sops keys available)";
options.swarselsystems.isBtrfs = lib.mkEnableOption "use btrfs filesystem";
options.swarselsystems.isImpermanence = lib.mkEnableOption "use impermanence on this system";
}
#+end_src
***** Server
#+begin_src nix :tangle modules/nixos/server.nix
{ lib, ... }:
{
options.swarselsystems.server.enable = lib.mkEnableOption "is a server machine"; options.swarselsystems.server.enable = lib.mkEnableOption "is a server machine";
options.swarselsystems.server.kavita = lib.mkEnableOption "enable kavita on server"; options.swarselsystems.server.kavita = lib.mkEnableOption "enable kavita on server";
options.swarselsystems.server.jellyfin = lib.mkEnableOption "enable jellyfin on server"; options.swarselsystems.server.jellyfin = lib.mkEnableOption "enable jellyfin on server";
@ -3661,36 +3705,6 @@ This section is for everything input-related on the NixOS side. At the moment, t
} }
#+end_src #+end_src
***** Impermanence
:PROPERTIES:
:CUSTOM_ID: h:e591075d-4a77-4add-bbc8-b711998fa97f
:END:
Option to enable impermanence configurations. This could also be done via optional imports, but impermanence is a "big enough" change to warrant a line in the machine =default.nix=.
#+begin_src nix :tangle modules/nixos/impermanence.nix
{ lib, ... }:
{
options.swarselsystems.impermanence = lib.mkEnableOption "use impermanence on this system";
}
#+end_src
***** Filesystem
:PROPERTIES:
:CUSTOM_ID: h:f77358ee-a80c-403a-be9d-04e7052bc556
:END:
This lets me quickly set flags for "special" file systems. These options mostly function in conjunction with other settings (for example, the =isBtrfs= function is mostly used for impermanence configuration).
#+begin_src nix :tangle modules/nixos/filesystem.nix
{ lib, ... }:
{
options.swarselsystems.isBtrfs = lib.mkEnableOption "use btrfs filesystem";
}
#+end_src
**** home-manager **** home-manager
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: h:ced5841f-c088-4d88-b3a1-7d62aad8837b :CUSTOM_ID: h:ced5841f-c088-4d88-b3a1-7d62aad8837b
@ -5541,16 +5555,12 @@ Normally, doing that also resets the lecture that happens on the first use of =s
{ config, lib, ... }: { config, lib, ... }:
let let
mkIfElse = p: yes: no: if p then yes else no; mkIfElse = p: yes: no: if p then yes else no;
mkIfElseList = p: yes: no: lib.mkMerge [
(lib.mkIf p yes)
(lib.mkIf (!p) no)
];
mapperTarget = mkIfElse config.swarselsystems.isCrypted "/dev/mapper/cryptroot" "/dev/disk/by-label/nixos"; mapperTarget = mkIfElse config.swarselsystems.isCrypted "/dev/mapper/cryptroot" "/dev/disk/by-label/nixos";
in in
{ {
security.sudo.extraConfig = lib.mkIf config.swarselsystems.impermanence '' security.sudo.extraConfig = lib.mkIf config.swarselsystems.isImpermanence ''
# rollback results in sudo lectures after each reboot # rollback results in sudo lectures after each reboot
Defaults lecture = never Defaults lecture = never
''; '';
@ -5561,12 +5571,12 @@ Normally, doing that also resets the lecture that happens on the first use of =s
boot.initrd.systemd.enable = true; boot.initrd.systemd.enable = true;
boot.initrd.systemd.services.rollback = lib.mkIf config.swarselsystems.impermanence { boot.initrd.systemd.services.rollback = lib.mkIf config.swarselsystems.isImpermanence {
description = "Rollback BTRFS root subvolume to a pristine state"; description = "Rollback BTRFS root subvolume to a pristine state";
wantedBy = [ "initrd.target" ]; wantedBy = [ "initrd.target" ];
# make sure it's done after encryption # make sure it's done after encryption
# i.e. LUKS/TPM process # i.e. LUKS/TPM process
after = mkIfElseList config.swarselsystems.isCrypted [ "systemd-cryptsetup@cryptroot.service" ] [ "dev-disk-by\\x2dlabel-nixos.device" ]; after = lib.swarselsystems.mkIfElseList config.swarselsystems.isCrypted [ "systemd-cryptsetup@cryptroot.service" ] [ "dev-disk-by\\x2dlabel-nixos.device" ];
requires = lib.mkIf (!config.swarselsystems.isCrypted) [ "dev-disk-by\\x2dlabel-nixos.device" ]; requires = lib.mkIf (!config.swarselsystems.isCrypted) [ "dev-disk-by\\x2dlabel-nixos.device" ];
# mount the root fs before clearing # mount the root fs before clearing
before = [ "sysroot.mount" ]; before = [ "sysroot.mount" ];
@ -5609,7 +5619,7 @@ Normally, doing that also resets the lecture that happens on the first use of =s
}; };
environment.persistence."/persist" = lib.mkIf config.swarselsystems.impermanence { environment.persistence."/persist" = lib.mkIf config.swarselsystems.isImpermanence {
hideMounts = true; hideMounts = true;
directories = directories =
[ [

View file

@ -151,7 +151,10 @@
systemFunc = func; systemFunc = func;
in in
systemFunc { systemFunc {
specialArgs = { inherit inputs outputs self; }; specialArgs = {
inherit inputs outputs self;
lib = lib.extend (_: _: { swarselsystems = import ./lib { inherit lib; }; });
};
modules = [ ./hosts/${if isNixos then "nixos" else "darwin"}/${host} ]; modules = [ ./hosts/${if isNixos then "nixos" else "darwin"}/${host} ];
}; };
}; };

View file

@ -1,6 +1,9 @@
{ self, inputs, outputs, config, pkgs, lib, ... }: { self, inputs, outputs, config, pkgs, lib, ... }:
let let
profilesPath = "${self}/profiles"; profilesPath = "${self}/profiles";
sharedOptions = {
isBtrfs = true;
};
in in
{ {
@ -78,19 +81,20 @@ in
''; '';
}; };
swarselsystems = { swarselsystems = lib.recursiveUpdate
{
wallpaper = self + /wallpaper/lenovowp.png; wallpaper = self + /wallpaper/lenovowp.png;
hasBluetooth = true; hasBluetooth = true;
hasFingerprint = true; hasFingerprint = true;
impermanence = false; isImpermanence = false;
isBtrfs = true;
isCrypted = true; isCrypted = true;
}; }
sharedOptions;
home-manager.users.swarsel.swarselsystems = { home-manager.users.swarsel.swarselsystems = lib.recursiveUpdate
{
isLaptop = true; isLaptop = true;
isNixos = true; isNixos = true;
isBtrfs = true;
flakePath = "/home/swarsel/.dotfiles"; flakePath = "/home/swarsel/.dotfiles";
cpuCount = 16; cpuCount = 16;
# temperatureHwmon = { # temperatureHwmon = {
@ -215,5 +219,6 @@ in
ans = ". ~/.venvs/ansible/bin/activate"; ans = ". ~/.venvs/ansible/bin/activate";
ans2-15 = ". ~/.venvs/ansible2.15.0/bin/activate"; ans2-15 = ". ~/.venvs/ansible2.15.0/bin/activate";
}; };
}; }
sharedOptions;
} }

View file

@ -79,7 +79,7 @@ in
swarselsystems = { swarselsystems = {
hasBluetooth = false; hasBluetooth = false;
hasFingerprint = false; hasFingerprint = false;
impermanence = false; isImpermanence = false;
isBtrfs = false; isBtrfs = false;
flakePath = "/root/.dotfiles"; flakePath = "/root/.dotfiles";
server = { server = {

View file

@ -1,21 +1,15 @@
{ self, inputs, outputs, config, pkgs, lib, ... }: { self, inputs, outputs, config, pkgs, lib, ... }:
let let
profilesPath = "${self}/profiles"; profilesPath = "${self}/profiles";
sharedOptions = {
isBtrfs = true;
};
in in
{ {
imports = [ imports = [
inputs.disko.nixosModules.disko inputs.disko.nixosModules.disko
"${self}/hosts/nixos/toto/disk-config.nix" "${self}/hosts/nixos/toto/disk-config.nix"
{
_module.args = {
withSwap = true;
swapSize = "8";
rootDisk = "/dev/vda";
withImpermanence = true;
withEncryption = true;
};
}
./hardware-configuration.nix ./hardware-configuration.nix
inputs.sops-nix.nixosModules.sops inputs.sops-nix.nixosModules.sops
@ -76,19 +70,24 @@ in
firewall.enable = false; firewall.enable = false;
}; };
swarselsystems = { swarselsystems = lib.recursiveUpdate
{
wallpaper = self + /wallpaper/lenovowp.png; wallpaper = self + /wallpaper/lenovowp.png;
impermanence = true; isImpermanence = true;
isBtrfs = true;
isCrypted = true; isCrypted = true;
initialSetup = true; initialSetup = true;
}; isSwap = true;
swapSize = "8G";
rootDisk = "/dev/vda";
}
sharedOptions;
home-manager.users.swarsel.swarselsystems = { home-manager.users.swarsel.swarselsystems = lib.recursiveUpdate
{
isLaptop = false; isLaptop = false;
isNixos = true; isNixos = true;
isBtrfs = true;
flakePath = "/home/swarsel/.dotfiles"; flakePath = "/home/swarsel/.dotfiles";
}; }
sharedOptions;
} }

View file

@ -1,11 +1,8 @@
# NOTE: ... is needed because dikso passes diskoFile # NOTE: ... is needed because dikso passes diskoFile
{ lib { lib
, pkgs , pkgs
, config
, rootDisk , rootDisk
, swapSize ? "8"
, withSwap ? true
, withEncryption ? true
, withImpermanence ? true
, ... , ...
}: }:
let let
@ -20,7 +17,7 @@ let
"noatime" "noatime"
]; ];
}; };
"/home" = lib.mkIf withImpermanence { "/home" = lib.mkIf config.swarselsystems.isImpermanence {
mountpoint = "/home"; mountpoint = "/home";
mountOptions = [ mountOptions = [
"subvol=home" "subvol=home"
@ -28,7 +25,7 @@ let
"noatime" "noatime"
]; ];
}; };
"/persist" = lib.mkIf withImpermanence { "/persist" = lib.mkIf config.swarselsystems.isImpermanence {
mountpoint = "/persist"; mountpoint = "/persist";
mountOptions = [ mountOptions = [
"subvol=persist" "subvol=persist"
@ -36,7 +33,7 @@ let
"noatime" "noatime"
]; ];
}; };
"/log" = lib.mkIf withImpermanence { "/log" = lib.mkIf config.swarselsystems.isImpermanence {
mountpoint = "/var/log"; mountpoint = "/var/log";
mountOptions = [ mountOptions = [
"subvol=log" "subvol=log"
@ -52,9 +49,9 @@ let
"noatime" "noatime"
]; ];
}; };
"/swap" = lib.mkIf withSwap { "/swap" = lib.mkIf config.swarselsystems.isSwap {
mountpoint = "/.swapvol"; mountpoint = "/.swapvol";
swap.swapfile.size = "${swapSize}G"; swap.swapfile.size = config.swarselsystems.swapSize;
}; };
}; };
in in
@ -63,7 +60,7 @@ in
disk = { disk = {
disk0 = { disk0 = {
type = "disk"; type = "disk";
device = rootDisk; device = config.swarselsystems.rootDisk;
content = { content = {
type = "gpt"; type = "gpt";
partitions = { partitions = {
@ -79,11 +76,11 @@ in
mountOptions = [ "defaults" ]; mountOptions = [ "defaults" ];
}; };
}; };
root = lib.mkIf (!withEncryption) { root = lib.mkIf (!config.swarselsystems.isCrypted) {
size = "100%"; size = "100%";
content = { content = {
inherit type subvolumes extraArgs; inherit type subvolumes extraArgs;
postCreateHook = lib.mkIf withImpermanence '' postCreateHook = lib.mkIf config.swarselsystems.isImpermanence ''
MNTPOINT=$(mktemp -d) MNTPOINT=$(mktemp -d)
mount "/dev/disk/by-label/nixos" "$MNTPOINT" -o subvolid=5 mount "/dev/disk/by-label/nixos" "$MNTPOINT" -o subvolid=5
trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT
@ -91,7 +88,7 @@ in
''; '';
}; };
}; };
luks = lib.mkIf withEncryption { luks = lib.mkIf config.swarselsystems.isCrypted {
size = "100%"; size = "100%";
content = { content = {
type = "luks"; type = "luks";
@ -107,7 +104,7 @@ in
}; };
content = { content = {
inherit type subvolumes extraArgs; inherit type subvolumes extraArgs;
postCreateHook = lib.mkIf withImpermanence '' postCreateHook = lib.mkIf config.swarselsystems.isImpermanence ''
MNTPOINT=$(mktemp -d) MNTPOINT=$(mktemp -d)
mount "/dev/mapper/cryptroot" "$MNTPOINT" -o subvolid=5 mount "/dev/mapper/cryptroot" "$MNTPOINT" -o subvolid=5
trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT
@ -122,8 +119,8 @@ in
}; };
}; };
fileSystems."/persist".neededForBoot = lib.mkIf withImpermanence true; fileSystems."/persist".neededForBoot = lib.mkIf config.swarselsystems.isImpermanence true;
fileSystems."/home".neededForBoot = lib.mkIf withImpermanence true; fileSystems."/home".neededForBoot = lib.mkIf config.swarselsystems.isImpermanence true;
environment.systemPackages = [ environment.systemPackages = [
pkgs.yubikey-manager pkgs.yubikey-manager

View file

@ -46,7 +46,7 @@ in
swarselsystems = { swarselsystems = {
hasBluetooth = false; hasBluetooth = false;
hasFingerprint = false; hasFingerprint = false;
impermanence = false; isImpermanence = false;
isBtrfs = false; isBtrfs = false;
flakePath = "/home/swarsel/.dotfiles"; flakePath = "/home/swarsel/.dotfiles";
server = { server = {

7
lib/default.nix Normal file
View file

@ -0,0 +1,7 @@
{ lib, ... }:
{
mkIfElseList = p: yes: no: lib.mkMerge [
(lib.mkIf p yes)
(lib.mkIf (!p) no)
];
}

View file

@ -3,8 +3,7 @@ let
"wallpaper" "wallpaper"
"hardware" "hardware"
"setup" "setup"
"impermanence" "server"
"filesystem"
"input" "input"
]; ];

22
modules/nixos/server.nix Normal file
View file

@ -0,0 +1,22 @@
{ lib, ... }:
{
options.swarselsystems.server.enable = lib.mkEnableOption "is a server machine";
options.swarselsystems.server.kavita = lib.mkEnableOption "enable kavita on server";
options.swarselsystems.server.jellyfin = lib.mkEnableOption "enable jellyfin on server";
options.swarselsystems.server.navidrome = lib.mkEnableOption "enable navidrome on server";
options.swarselsystems.server.spotifyd = lib.mkEnableOption "enable spotifyd on server";
options.swarselsystems.server.mpd = lib.mkEnableOption "enable mpd on server";
options.swarselsystems.server.matrix = lib.mkEnableOption "enable matrix on server";
options.swarselsystems.server.nextcloud = lib.mkEnableOption "enable nextcloud on server";
options.swarselsystems.server.immich = lib.mkEnableOption "enable immich on server";
options.swarselsystems.server.paperless = lib.mkEnableOption "enable paperless on server";
options.swarselsystems.server.transmission = lib.mkEnableOption "enable transmission and friends on server";
options.swarselsystems.server.syncthing = lib.mkEnableOption "enable syncthing on server";
options.swarselsystems.server.restic = lib.mkEnableOption "enable restic backups on server";
options.swarselsystems.server.monitoring = lib.mkEnableOption "enable monitoring on server";
options.swarselsystems.server.jenkins = lib.mkEnableOption "enable jenkins on server";
options.swarselsystems.server.emacs = lib.mkEnableOption "enable emacs server on server";
options.swarselsystems.server.forgejo = lib.mkEnableOption "enable forgejo on server";
options.swarselsystems.server.ankisync = lib.mkEnableOption "enable ankisync on server";
options.swarselsystems.server.freshrss = lib.mkEnableOption "enable freshrss on server";
}

View file

@ -1,37 +1,33 @@
{ lib, ... }: { lib, ... }:
let
inherit (lib) mkOption types;
in
{ {
options.swarselsystems.flakePath = mkOption { options.swarselsystems.user = lib.mkOption {
type = types.str; type = lib.types.str;
default = "swarsel";
};
options.swarselsystems.flakePath = lib.mkOption {
type = lib.types.str;
default = ""; default = "";
}; };
options.swarselsystems.withHomeManager = mkOption { options.swarselsystems.withHomeManager = lib.mkOption {
type = types.bool; type = lib.types.bool;
default = true; default = true;
}; };
options.swarselsystems.isSwap = lib.mkOption {
type = lib.types.bool;
default = true;
};
options.swarselsystems.swapSize = lib.mkOption {
type = lib.types.str;
default = "8G";
};
options.swarselsystems.rootDisk = lib.mkOption {
type = lib.types.str;
default = "";
};
options.swarselsystems.isCrypted = lib.mkEnableOption "uses full disk encryption"; options.swarselsystems.isCrypted = lib.mkEnableOption "uses full disk encryption";
options.swarselsystems.isPublic = lib.mkEnableOption "is a public machine (no secrets)"; options.swarselsystems.isPublic = lib.mkEnableOption "is a public machine (no secrets)";
options.swarselsystems.initialSetup = lib.mkEnableOption "initial setup (no sops keys available)"; options.swarselsystems.initialSetup = lib.mkEnableOption "initial setup (no sops keys available)";
options.swarselsystems.server.enable = lib.mkEnableOption "is a server machine";
options.swarselsystems.server.kavita = lib.mkEnableOption "enable kavita on server"; options.swarselsystems.isBtrfs = lib.mkEnableOption "use btrfs filesystem";
options.swarselsystems.server.jellyfin = lib.mkEnableOption "enable jellyfin on server"; options.swarselsystems.isImpermanence = lib.mkEnableOption "use impermanence on this system";
options.swarselsystems.server.navidrome = lib.mkEnableOption "enable navidrome on server";
options.swarselsystems.server.spotifyd = lib.mkEnableOption "enable spotifyd on server";
options.swarselsystems.server.mpd = lib.mkEnableOption "enable mpd on server";
options.swarselsystems.server.matrix = lib.mkEnableOption "enable matrix on server";
options.swarselsystems.server.nextcloud = lib.mkEnableOption "enable nextcloud on server";
options.swarselsystems.server.immich = lib.mkEnableOption "enable immich on server";
options.swarselsystems.server.paperless = lib.mkEnableOption "enable paperless on server";
options.swarselsystems.server.transmission = lib.mkEnableOption "enable transmission and friends on server";
options.swarselsystems.server.syncthing = lib.mkEnableOption "enable syncthing on server";
options.swarselsystems.server.restic = lib.mkEnableOption "enable restic backups on server";
options.swarselsystems.server.monitoring = lib.mkEnableOption "enable monitoring on server";
options.swarselsystems.server.jenkins = lib.mkEnableOption "enable jenkins on server";
options.swarselsystems.server.emacs = lib.mkEnableOption "enable emacs server on server";
options.swarselsystems.server.forgejo = lib.mkEnableOption "enable forgejo on server";
options.swarselsystems.server.ankisync = lib.mkEnableOption "enable ankisync on server";
options.swarselsystems.server.freshrss = lib.mkEnableOption "enable freshrss on server";
} }

View file

@ -1,16 +1,12 @@
{ config, lib, ... }: { config, lib, ... }:
let let
mkIfElse = p: yes: no: if p then yes else no; mkIfElse = p: yes: no: if p then yes else no;
mkIfElseList = p: yes: no: lib.mkMerge [
(lib.mkIf p yes)
(lib.mkIf (!p) no)
];
mapperTarget = mkIfElse config.swarselsystems.isCrypted "/dev/mapper/cryptroot" "/dev/disk/by-label/nixos"; mapperTarget = mkIfElse config.swarselsystems.isCrypted "/dev/mapper/cryptroot" "/dev/disk/by-label/nixos";
in in
{ {
security.sudo.extraConfig = lib.mkIf config.swarselsystems.impermanence '' security.sudo.extraConfig = lib.mkIf config.swarselsystems.isImpermanence ''
# rollback results in sudo lectures after each reboot # rollback results in sudo lectures after each reboot
Defaults lecture = never Defaults lecture = never
''; '';
@ -21,12 +17,12 @@ in
boot.initrd.systemd.enable = true; boot.initrd.systemd.enable = true;
boot.initrd.systemd.services.rollback = lib.mkIf config.swarselsystems.impermanence { boot.initrd.systemd.services.rollback = lib.mkIf config.swarselsystems.isImpermanence {
description = "Rollback BTRFS root subvolume to a pristine state"; description = "Rollback BTRFS root subvolume to a pristine state";
wantedBy = [ "initrd.target" ]; wantedBy = [ "initrd.target" ];
# make sure it's done after encryption # make sure it's done after encryption
# i.e. LUKS/TPM process # i.e. LUKS/TPM process
after = mkIfElseList config.swarselsystems.isCrypted [ "systemd-cryptsetup@cryptroot.service" ] [ "dev-disk-by\\x2dlabel-nixos.device" ]; after = lib.swarselsystems.mkIfElseList config.swarselsystems.isCrypted [ "systemd-cryptsetup@cryptroot.service" ] [ "dev-disk-by\\x2dlabel-nixos.device" ];
requires = lib.mkIf (!config.swarselsystems.isCrypted) [ "dev-disk-by\\x2dlabel-nixos.device" ]; requires = lib.mkIf (!config.swarselsystems.isCrypted) [ "dev-disk-by\\x2dlabel-nixos.device" ];
# mount the root fs before clearing # mount the root fs before clearing
before = [ "sysroot.mount" ]; before = [ "sysroot.mount" ];
@ -69,7 +65,7 @@ in
}; };
environment.persistence."/persist" = lib.mkIf config.swarselsystems.impermanence { environment.persistence."/persist" = lib.mkIf config.swarselsystems.isImpermanence {
hideMounts = true; hideMounts = true;
directories = directories =
[ [

View file

@ -24,8 +24,6 @@ function help_and_exit() {
echo " -u <target_user> specify target_user with sudo access. nix-config will be cloned to their home." echo " -u <target_user> specify target_user with sudo access. nix-config will be cloned to their home."
echo " Default='${target_user}'." echo " Default='${target_user}'."
echo " --port <ssh_port> specify the ssh port to use for remote access. Default=${ssh_port}." echo " --port <ssh_port> specify the ssh port to use for remote access. Default=${ssh_port}."
echo " --impermanence Use this flag if the target machine has impermanence enabled. WARNING: Assumes /persist path."
echo " --encryption Use this flag if the target machine has full disk encryption enabled."
echo " --debug Enable debug mode." echo " --debug Enable debug mode."
echo " -h | --help Print this help." echo " -h | --help Print this help."
exit 0 exit 0
@ -110,16 +108,6 @@ while [[ $# -gt 0 ]]; do
shift shift
ssh_port=$1 ssh_port=$1
;; ;;
--temp-override)
shift
temp=$1
;;
--impermanence)
persist_dir="/persist"
;;
--encryption)
disk_encryption=1
;;
--debug) --debug)
set -x set -x
;; ;;
@ -132,6 +120,37 @@ while [[ $# -gt 0 ]]; do
shift shift
done done
green "~SwarselSystems~ remote installer"
green "Reading system information for $target_hostname ..."
DISK="$(nix eval --raw ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.rootDisk)"
green "Root Disk: $DISK"
CRYPTED="$(nix eval ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.isCrypted)"
if [[ $CRYPTED == "true" ]]; then
green "Encryption: ✓"
disk_encryption=1
else
red "Encryption: X"
disk_encryption=0
fi
IMPERMANENCE="$(nix eval ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.isImpermanence)"
if [[ $IMPERMANENCE == "true" ]]; then
green "Impermanence: ✓"
persist_dir="/persist"
else
red "Impermanence: X"
persist_dir=""
fi
SWAP="$(nix eval ~/.dotfiles#nixosConfigurations."$target_hostname".config.swarselsystems.isSwap)"
if [[ $SWAP == "true" ]]; then
green "Swap: ✓"
else
red "Swap: X"
fi
ssh_cmd="ssh -oport=${ssh_port} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -t $target_user@$target_destination" ssh_cmd="ssh -oport=${ssh_port} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -t $target_user@$target_destination"
# ssh_root_cmd=$(echo "$ssh_cmd" | sed "s|${target_user}@|root@|") # uses @ in the sed switch to avoid it triggering on the $ssh_key value # ssh_root_cmd=$(echo "$ssh_cmd" | sed "s|${target_user}@|root@|") # uses @ in the sed switch to avoid it triggering on the $ssh_key value
ssh_root_cmd=${ssh_cmd/${target_user}@/root@} ssh_root_cmd=${ssh_cmd/${target_user}@/root@}

View file

@ -9,62 +9,62 @@ sops:
- recipient: age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63 - recipient: age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpd0JYN2tiNW1IRHJtcXlX YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpRko1ZnZsMUM2L1VmNGQ4
SEQvOUVZYjJzb1ZVamdpbG91OXZpMkhWTkQ4CjVsTlp3eTR0OENhanh4clR5OXJX MnhOZTZmdlBLQ2dra0xMY3E0NmZPQUpuU1Q4CjNDSDNnVGNCV05aUWpWRnZkWEt2
NnU2QVdVbXVRTXRJWlgwSGFkdEMyWjAKLS0tIG5NQzFqREIrWmZBUGZSVVU5RWVE Y1FlbEdTZ290SjRKc1hKRzN0bUNwVDAKLS0tIHFjVEk0S045NGgzMUFDUitIdEx4
VzlnVmhzc2U3d3B4aEs2a0dCaHJQbHMKpDLehdlCXhnEKOjzFr7rliJtETSsyUDp dlRRWUtiYldYQ3V0QzFyRHNIblNFNm8KkPXunwFKo/4klAZhkAXikg7UpuFC1EP/
HixMSMosuN3xs7+7nodPBHsX7Z9kvbwV60inqzHUWsq4SzHrqQozbg== kf6roOcQx41hPSqAWzivySwPgRUO3iygFw4jonYaFZJik/wIo9OulA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1dmxw76fzr958zl23ad5h7gvtnurr8n5ajlqchgx76k6z5yj3z4zsn592fq - recipient: age16vzhcvz8tyxj8e0f47fy0z4p3dsg0ak4vl52ut3l07a0tz465cxslmhevl
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVcTIwWVl5VHlnUUFuMkky YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYUmhXcS8rWmNQUGVudGxP
N1BBNnNBVkJVKzZqeXRiVVJ2OUNoUjIraWtrClE2NDJYTng3S2dHL29wcm5NRkxY STZPaVB3VlFHYXhDbUZYdjhENFFOL1FCUEZVClJ2cFhwNlRpVnAzRThQZ1FuNHNi
L1NZY21xd09FUDN0QTdsTEVpZjNoWXMKLS0tIFlTWFNEcmwwNnRuYmhCcTVUZXAx T3pyWm5aNDVaUnJHcjYzWVVOUHBwWUEKLS0tIFdKSXF5TGw2aDJ3NGdhZ0RGWGpu
cW04RVNxeDlkcDZmT3JadUNDQUdXRlEKnXWWqgObJStUF0DdWKX2M71UYk/6n4wX MjBYTktmeE9EcDNad0VKV1dhME9UbVkKYrDIQ8/DfeJ/3ITfw9/51i2N44hqnIi3
XdmPH0crXBelfwmSvX1+Pkwj+JowzXmMaVR0Y0XC2SMXdzWShhk40w== 4hHKaefSiHU0glUHUdYhg/F17Hgh5MuhZ0LB4dfIYngWNgFDwnC4aQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg - recipient: age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjcUZGcTBmR2ZHRW0rcVRj YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNWFNvaE5VQms2VFNOK25D
cCt1Wi9JNWJpbEtNVmVwWmJBN2VLM05SVlRRCkdOMVhFTXd3QUJFWGZyTys0NXgv NTdocVF5QTMwSDEreE02cWVVaHIyQ2dqcVRnClB4QzAzTkRVb1hSL1VjYW5IOEIr
Y1MwL2pWNmZiUGdCTnpDR1RqOHQ2UDgKLS0tIFFrUDg5eFpWcXpNOERWZzJTa1ph SG9adlpacGd1ck8vTU1IOTNyS2Y4cjAKLS0tIDQxT3lOUjhpQ1FNNGZKbnpXOVJG
SlQxWE5VWlFHVlY0VE1zTjFUa054bG8KjXhLyIPnL/nGAR9l2d+J2s29ZxjzH5+U aS9qY0crYXpsSFRxT280VzllTE1kdWcKoaATszZ1H4b05vpEzQXkffJuwQESbTyW
BtavBDsS1/Ldsu4OQsY7TFJrTdOzMMRsNVWedprFyJjx0o8FO9AwWw== nBE5WYRqUHBFWeve1Ssq3AaYpNEht+MAYA0YlA3TsJc5scSbkFXi4g==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2024-07-16T23:56:59Z" lastmodified: "2024-07-16T23:56:59Z"
mac: ENC[AES256_GCM,data:bo8SHGmkNGQqR8nnlIKvAMzd+4vWJ19u9Kga2U1mOEYKMCyZ2nTXju6e327ppmx6KJUnzzieS7F8myE/5jzfd1+LyAN7QlL1xixtyLZH784Eh3c3Rd3sXKO/Tuj00gSsz8PsXzq8VK5RdR6NggxhMM6l3Mji4mTQibEzFQ0XPwo=,iv:6mAVBuMwxkO/ms0O/lpLEAg9lzVtZywMbwhL7diB4Z4=,tag:oGnwY5Ikc8qOrwNyiWqtGg==,type:str] mac: ENC[AES256_GCM,data:bo8SHGmkNGQqR8nnlIKvAMzd+4vWJ19u9Kga2U1mOEYKMCyZ2nTXju6e327ppmx6KJUnzzieS7F8myE/5jzfd1+LyAN7QlL1xixtyLZH784Eh3c3Rd3sXKO/Tuj00gSsz8PsXzq8VK5RdR6NggxhMM6l3Mji4mTQibEzFQ0XPwo=,iv:6mAVBuMwxkO/ms0O/lpLEAg9lzVtZywMbwhL7diB4Z4=,tag:oGnwY5Ikc8qOrwNyiWqtGg==,type:str]
pgp: pgp:
- created_at: "2024-12-24T11:28:02Z" - created_at: "2024-12-24T14:59:45Z"
enc: |- enc: |-
-----BEGIN PGP MESSAGE----- -----BEGIN PGP MESSAGE-----
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
=HWX2 =8uFY
-----END PGP MESSAGE----- -----END PGP MESSAGE-----
fp: 4BE7925262289B476DBBC17B76FD3810215AE097 fp: 4BE7925262289B476DBBC17B76FD3810215AE097
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted

View file

@ -34,71 +34,71 @@ sops:
- recipient: age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63 - recipient: age1h72072slm2pthn9m2qwjsyy2dsazc6hz97kpzh4gksvv0r2jqecqul8w63
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0WXZ1RC9WV2lUQnNkOCs5 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLbm9WeGd0U0NEMzhpTDhE
Y0xIQzdsSlhnS2RMaTE1NGhPREkyblVvMVdvCnpaRXVDOXAwK0ErVU9EVEoxZnUy SzBpRG5Xc1VUeFdSaXRjMHBKNVltNTY4dHcwCm5DRzFSdG9ML2h2QVlYNm5pUC9Z
eXFid0g1VXJLSFJIbklrYWpiMWZiWFEKLS0tIGdzZzNyam5kM3BROXJCL0ZCemxT TTh4TDB2bjhWdWFNQ2ozN09sZWw1WTAKLS0tIDUrcHVmWUc3dkxSS3MrZXB6ZHNi
VjF4RHQ2anZUbGNXUlNCUEJXNkhBUUEK0m8++qCry3GPrVy3AKieBWBUjX01Bexe QzZVR3p2VlRyZjNqZk9ZTDBXOWdxYnMK3ZT951oj6lSP+3a1sQL88GUE/jlhfoWy
82GKZqeezxZIud3BJmWKW7KtoAQ5KxC8rLhJzJIczGPbHiVgKx1WNw== tkoKh2wNofg3BX9jMCgWm2LFcYxX1fMOoxhXxK2XNEV5et8gxHIxEw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1dmxw76fzr958zl23ad5h7gvtnurr8n5ajlqchgx76k6z5yj3z4zsn592fq - recipient: age16vzhcvz8tyxj8e0f47fy0z4p3dsg0ak4vl52ut3l07a0tz465cxslmhevl
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVVXo1T2lWdEEwWjRPRUxk YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiZTlsOG02b2h3a1FaVW03
S3gvcGN0cXRLaW0yOUNqUUUvMEh1TzhsZlhBCmZFUFhIRVZGL2c2RmR2UHNRUVZC RTFHOXhnaU94elNxNU5jV3hYUWczS25NOGdNClFRYUdoN2pDeEQyenhyYTNDMGxE
RWlMTnVIMjJGaFU2andyejk3dWttUm8KLS0tIFBoRDNDQ1MrNVhXRzRtTTdveDNI U3Z1elFwVlRXNCtpSlF6TmNYMEgyem8KLS0tIHYrZjB2NVhUdFFXcmFVNnJyd3NM
eWdaL0ZGczNodVEySWxvVUlqc2hBN00KRjokYKoTskN+d1yhzeOBfFFA3csnMsmx eHpZOStRRWZROU9qd3FZUXU5amhsWjgKIx6s5IpwAkcdRgqjlmMqQTGgx7abZ7OU
UYtzuycE2+9qPv0nrQ5AtNqkdsPuLNnF9D0QXRyzSsqJ67axELehpw== C5BWpFIARLNUcBOKdORT8fT1m1EnmXKawxitVPHrhAJibvi9XuZIWw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg - recipient: age1zlnxraee6tddr07xn59mx5rdexw8qxryd53eqlsajasfhfy78fkq705dfg
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJRUd6SkFyaHZLTnJGK05v YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsNm5GWTZabmx0Lzd4VFNH
ZGZ4NGgreE9ueWZ5b2tzVHRUcmdZTnYxRzJJCmRkbmhXQXBXaG1Hc3ovem9PSUdE cFRRYUpCeTMxQ01LOThNRzY1T1FiWmt2TUVjClNWbDBnQk9lSmE1MVhieXh0ZENw
dGZrcERvTlBIOFpHQUNmUXNpcHJaN1EKLS0tIDJtVC94YXh0dUZjcjhDcFJpbTdN TjN0NnZlTlFMYmoyRTh0NDJHWTRUU2sKLS0tIEVCZElydnI3V0pBQ205b1hqV1U3
alNjWnJLSVlTNVliRERwdlpQYU9EaWMKynCfIpvVraG6HZMtExcE+qf7EbXPra3t aDdsUWc0bkdrcFBZeHhFc3hHeGhvL3MKt1sJlwjY4zc07tIp6qihcGu8UMdgr968
Bo0BO1fgh+Z1pdxYa71ruFdfBF4BqtRlLlJKqKfIjLmNU1mEh3Wv1Q== KYSO3fGr6XfRWwfzVb9h7FBsWK8ttar0tCK1JF9Hjjp0W9Oqu+AQGA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
- recipient: age16lnmuuxfuxxtty3atnhut8wseppwnhp7rdhmxqd5tdvs9qnjffjq42sqyy - recipient: age16lnmuuxfuxxtty3atnhut8wseppwnhp7rdhmxqd5tdvs9qnjffjq42sqyy
enc: | enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuUzhpKzdZdHNZRXNFTG1Z YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2cFFqMDJ5SUQrT1c0VHRH
OHVMTUNrMmNnWU9JQTFFbExpTkZ5ZnlDUEdRCmk3eEZHZlBuTTIxVS84Q2ExNzVo MmhYbFJBcjZJTjlFNHBtTk9uWTl0dnNud0Q0ClJBVkhzWXNqdGZIWE9DR3pzMDdq
OGhOU0NUTCsweVZYcStRd0NXOEhkYm8KLS0tIE50NTExZFM3akJXMDNHMVhkaysz cXBwWWJKWlhwM2RVTCtHbU4yZjdCdVEKLS0tIEFwZFIzakNHT2ovNmoxUGtXTUFD
OFBJMWZNcEtUWkVWdk1SRTVreUJaVFUK6oMy5pliMIBJ3juN7E2UKU+OzN7iYJy/ RVdWbGt0aHcrUGlNWTdUZ2xMa25mYlUKY6AOmHg9+ApJXeoyliXxvqtjwaVLSjH0
aB2MpEUc0ABd47X5aWgUTmz+bK6hSewiji9LM1mPQOeaqLMeNrrKpg== 6cuZSd05iOSHpR2vbg9jvRiXKXBS6DSN/BoIn+JUif8jY8cTQCMqDQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2024-12-18T22:08:25Z" lastmodified: "2024-12-18T22:08:25Z"
mac: ENC[AES256_GCM,data:vU+7/VLEzwDOrScD/HTo9JBf4ixtmcBmjtSUEtaVHwZuPMJ0OpydwcBPYKvhvU6z9xNecCcuiY7beJ5sq7fnv8XY66TOZWq/2tTZPXdJwpfAyHqBhf7uoCiOmSl53tpWzUFbfT8fQQwjKmBO1079Op24WNWzG3w0i9BsoYTYrpY=,iv:9bLlRg28paoitcK1lFc4ipsgMVvr/zECNozwXU2qJTM=,tag:uRviXeIlwUnvJqHwWuoYcA==,type:str] mac: ENC[AES256_GCM,data:vU+7/VLEzwDOrScD/HTo9JBf4ixtmcBmjtSUEtaVHwZuPMJ0OpydwcBPYKvhvU6z9xNecCcuiY7beJ5sq7fnv8XY66TOZWq/2tTZPXdJwpfAyHqBhf7uoCiOmSl53tpWzUFbfT8fQQwjKmBO1079Op24WNWzG3w0i9BsoYTYrpY=,iv:9bLlRg28paoitcK1lFc4ipsgMVvr/zECNozwXU2qJTM=,tag:uRviXeIlwUnvJqHwWuoYcA==,type:str]
pgp: pgp:
- created_at: "2024-12-24T11:28:03Z" - created_at: "2024-12-24T14:59:46Z"
enc: |- enc: |-
-----BEGIN PGP MESSAGE----- -----BEGIN PGP MESSAGE-----
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
=KXeu =pQM4
-----END PGP MESSAGE----- -----END PGP MESSAGE-----
fp: 4BE7925262289B476DBBC17B76FD3810215AE097 fp: 4BE7925262289B476DBBC17B76FD3810215AE097
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted