Initial commit

This commit is contained in:
HotaruBlaze
2026-05-31 09:33:14 +00:00
commit 6bce73c167
12 changed files with 698 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# SOPS age key
age.key
# Encrypted secrets (plaintext versions)
*.yaml.unencrypted
*.yaml.decrypted
# Nix build artifacts
/nix/
/result/
# Temporary files
*.tmp
*.swp
# IDE/Editor files
.idea/
.vscode/
*.sublime-workspace
*.sublime-project
# OS-specific files
.DS_Store
Thumbs.db

8
.sops.yaml Normal file
View File

@@ -0,0 +1,8 @@
---
# sops configuration
# This file tells SOPS how to decrypt your secrets
# It will be used by both the sops CLI and sops-nix
creation_rules:
- path_regex: hosts/[^/]+/secrets\.yaml$
age: age1t9c8j4yclx7wylzry8k0f473p4qt32d0kvgj8ce5k5zqvj2p9g4qsplaw8

27
Dockerfile Normal file
View File

@@ -0,0 +1,27 @@
FROM nixos/nix:latest
# Install dependencies
RUN nix-env -iA nixpkgs.colmena nixpkgs.sops nixpkgs.age nixpkgs.yq nixpkgs.jq nixpkgs TMPEXIT=0
# Create directories
RUN mkdir -p /workspace /etc/sops/keys
# Copy files at build time (these will be part of the image)
COPY . /workspace/
COPY age.key /etc/sops/keys/age.key
# Set working directory
WORKDIR /workspace
# Set environment variables
ENV NIX_CONFIG_DIR=/root/.config/nix
ENV SOPS_AGE_KEY_FILE=/etc/sops/keys/age.key
# Create nix.conf
RUN mkdir -p /root/.config/nix && \
echo "experimental-features = nix-command flakes" > /root/.config/nix/nix.conf
# Make age.key readable
RUN chmod 600 /etc/sops/keys/age.key
CMD ["bash"]

5
Readme.md Normal file
View File

@@ -0,0 +1,5 @@
# Note; you are running on a windows host, so your going to need to work around that for configuring nixos
# Tasks:
- Cleanup the repo for nixos as this is a fresh nixos setup, ive already provided the initial machien as hosts\blade
- Setup colmena for this nixos

244
Taskfile.yml Normal file
View File

@@ -0,0 +1,244 @@
# ------------------------------------------------------------
# Taskfile Dockerbased Nix development & Colmena deployment
# ------------------------------------------------------------
version: "3"
vars:
# Base directory of the repository pass it when you invoke `task`,
# e.g. `task -v ROOT_DIR=$(pwd) …`. You can also export it in your shell.
ROOT_DIR: "{{ .ROOT_DIR }}"
# Convenience paths (expanded by `task` before being passed to Docker)
FLAKE_DIR: "{{ .ROOT_DIR }}"
AGE_KEY_PATH: "{{ .ROOT_DIR }}/age.key"
SECRETS_YAML_PATH: "{{ .ROOT_DIR }}/hosts/blade/secrets.yaml"
# -----------------------------------------------------------------
# Docker helpers (the Nix container always gets the repo mounted at
# /workspace). `DOCKER_NIX_CMD` is used for *any* command that runs
# inside the container.
# -----------------------------------------------------------------
DOCKER_COMPOSE_CMD: "docker compose -f {{ .ROOT_DIR }}/docker-compose.yml"
DOCKER_NIX_CMD: >-
{{ .DOCKER_COMPOSE_CMD }} run --rm
-v "{{ .ROOT_DIR }}:/workspace"
nixos
# -----------------------------------------------------------------
# Docker management
# -----------------------------------------------------------------
tasks:
docker-build:
desc: Build the Docker container for Nix
cmds:
- "{{ .DOCKER_COMPOSE_CMD }} build"
dir: "{{ .ROOT_DIR }}"
# ---------------------------------------------------------------
# NOTE: we now use DOCKER_NIX_CMD (which already has the bindmount)
# ---------------------------------------------------------------
docker-shell:
desc: Enter a shell in the Docker container (repo is mounted)
cmds:
- "{{ .DOCKER_NIX_CMD }} bash"
dir: "{{ .ROOT_DIR }}"
docker-up:
desc: Start the Docker container (detached)
cmds:
- "{{ .DOCKER_COMPOSE_CMD }} up -d"
dir: "{{ .ROOT_DIR }}"
docker-down:
desc: Stop the Docker container
cmds:
- "{{ .DOCKER_COMPOSE_CMD }} down"
dir: "{{ .ROOT_DIR }}"
# -----------------------------------------------------------------
# Agekey handling (SOPS)
# -----------------------------------------------------------------
generate-age-key:
desc: Generate a new age key for a host (default - all hosts without a key)
vars:
# If HOST is supplied we generate only for that host; otherwise we
# iterate over every directory under `hosts/` that lacks an age.key.
TARGET_HOSTS: |
{{- if .HOST -}}
{{ .HOST }}
{{- else -}}
{{- range $dir := (files (printf "%s/hosts/*" .ROOT_DIR)) -}}
{{- $name := base $dir -}}
{{- if not (fileExists (printf "%s/age.key" $dir)) -}}
{{ $name }}
{{- end -}}
{{- end -}}
{{- end }}
cmds:
- |
{{- $list := splitList "\n" .TARGET_HOSTS -}}
{{- range $h := $list }}
echo "🔑 Generating age.key for host '{{ $h }}' ..."
{{ .DOCKER_NIX_CMD }} -v "{{ .ROOT_DIR }}/hosts/{{ $h }}:/workspace/hosts/{{ $h }}" \
nixos age-keygen -o /workspace/hosts/{{ $h }}/age.key
{{- end }}
dir: "{{ .ROOT_DIR }}"
# -----------------------------------------------------------------
# Encrypt / Decrypt (loop over hosts)
# -----------------------------------------------------------------
encrypt-secrets:
desc: |
Encrypt `secrets.yaml` for every host that has an age.key.
Pass HOST=<name> to limit to a single host.
vars:
# Spaceseparated list of hosts that have both age.key & secrets.yaml
TARGET_HOSTS: |
{{- if .HOST -}}
{{ .HOST }}
{{- else -}}
{{- $list := slice -}}
{{- range $dir := (files (printf "%s/hosts/*" .ROOT_DIR)) -}}
{{- $name := base $dir -}}
{{- $age := printf "%s/age.key" $dir -}}
{{- $sec := printf "%s/secrets.yaml" $dir -}}
{{- if (and (fileExists $age) (fileExists $sec)) -}}
{{- $list = append $list $name -}}
{{- end -}}
{{- end -}}
{{- join " " $list -}}
{{- end }}
preconditions:
- sh: test -n "{{ .TARGET_HOSTS }}"
msg: "No hosts with both age.key and secrets.yaml were found."
cmds:
- |
for host in {{ .TARGET_HOSTS }}; do
echo "🔐 Encrypting secrets for host '$host' ..."
{{ .DOCKER_NIX_CMD }} -v "{{ .ROOT_DIR }}/hosts/$host:/workspace/hosts/$host" \
nixos sh -c "\
pub=\$(grep 'public key' /workspace/hosts/$host/age.key | awk '{print \$NF}') && \
sops --encrypt --age=\$pub --in-place /workspace/hosts/$host/secrets.yaml"
done
dir: "{{ .ROOT_DIR }}"
decrypt-secrets:
desc: |
Decrypt `secrets.yaml` for every host that has an age.key.
Decrypted output is written to `secrets.yaml.decrypted`.
Pass HOST=<name> to limit to a single host.
vars:
TARGET_HOSTS: |
{{- if .HOST -}}
{{ .HOST }}
{{- else -}}
{{- $list := slice -}}
{{- range $dir := (files (printf "%s/hosts/*" .ROOT_DIR)) -}}
{{- $name := base $dir -}}
{{- $age := printf "%s/age.key" $dir -}}
{{- $sec := printf "%s/secrets.yaml" $dir -}}
{{- if (and (fileExists $age) (fileExists $sec)) -}}
{{- $list = append $list $name -}}
{{- end -}}
{{- end -}}
{{- join " " $list -}}
{{- end }}
preconditions:
- sh: test -n "{{ .TARGET_HOSTS }}"
msg: "No hosts with both age.key and secrets.yaml were found."
cmds:
- |
for host in {{ .TARGET_HOSTS }}; do
echo "🔓 Decrypting secrets for host '$host' ..."
{{ .DOCKER_NIX_CMD }} -v "{{ .ROOT_DIR }}/hosts/$host:/workspace/hosts/$host" \
nixos sh -c "\
pub=\$(grep '# public key' /workspace/hosts/$host/age.key | awk '{print \$NF}') && \
sops --decrypt --age=\$pub /workspace/hosts/$host/secrets.yaml \
> /workspace/hosts/$host/secrets.yaml.decrypted"
done
dir: "{{ .ROOT_DIR }}"
# -----------------------------------------------------------------
# Nix flake & build
# -----------------------------------------------------------------
flake-check:
desc: Run `nix flake check` on the repository
cmds:
- "{{ .DOCKER_NIX_CMD }} nix flake check --flake /workspace"
dir: "{{ .ROOT_DIR }}"
build-blade:
desc: Build the NixOS configuration for the *blade* host
cmds:
- "{{ .DOCKER_NIX_CMD }} nix build /workspace#nixosConfigurations.blade"
dir: "{{ .ROOT_DIR }}"
# -----------------------------------------------------------------
# Colmena deployment
# -----------------------------------------------------------------
deploy:
desc: Deploy to an arbitrary node (usage - task deploy NODE=blade)
cmds:
- "{{ .DOCKER_NIX_CMD }} colmena apply --on {{ .NODE }}"
dir: "{{ .ROOT_DIR }}"
preconditions:
- sh: test -n "{{ .NODE }}"
msg: "NODE variable is required. Example: task deploy NODE=blade"
- sh: test -f "{{ .ROOT_DIR }}/age.key"
- sh: test -f "{{ .ROOT_DIR }}/hosts/{{ .NODE }}/secrets.yaml"
deploy-blade:
desc: Deploy the configuration to the *blade* host
cmds:
- "{{ .DOCKER_NIX_CMD }} colmena apply --on blade"
dir: "{{ .ROOT_DIR }}"
preconditions:
- sh: test -f "{{ .ROOT_DIR }}/age.key"
- sh: test -f "{{ .ROOT_DIR }}/hosts/blade/secrets.yaml"
# -----------------------------------------------------------------
# Helper: add a new node
# -----------------------------------------------------------------
add-node:
desc: Create a new host directory with skeleton Nix files
prompt: "Enter the name of the new node (e.g., node2):"
cmds:
- mkdir -p "{{ .FLAKE_DIR }}/hosts/{{ .CLI_ARGS }}"
- |
cat > "{{ .FLAKE_DIR }}/hosts/{{ .CLI_ARGS }}/configuration.nix" <<'EOF'
{ config, lib, pkgs, ... }:
{
imports = [
./hardware-configuration.nix
../../common.nix
];
networking.hostName = "{{ .CLI_ARGS }}";
}
EOF
- |
cat > "{{ .FLAKE_DIR }}/hosts/{{ .CLI_ARGS }}/hardware-configuration.nix" <<'EOF'
# Add hardwarespecific configurations here
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
}
EOF
- |
cat > "{{ .FLAKE_DIR }}/hosts/{{ .CLI_ARGS }}/secrets.yaml" <<'EOF'
# Add nodespecific secrets here
EOF
- |
cat > "{{ .FLAKE_DIR }}/hosts/{{ .CLI_ARGS }}/default.nix" <<'EOF'
{ ... }: { imports = [ ./configuration.nix ./hardware-configuration.nix ../../common.nix ]; }
EOF
dir: "{{ .ROOT_DIR }}"
# -----------------------------------------------------------------
# Housekeeping
# -----------------------------------------------------------------
clean:
desc: Remove temporary/decrypted secret files for all hosts
cmds:
- |
find "{{ .ROOT_DIR }}/hosts" -name 'secrets.yaml.decrypted' -delete
silent: true

39
common.nix Normal file
View File

@@ -0,0 +1,39 @@
{ config, lib, pkgs, ... }:
{
# Shared users
users.mutableUsers = false;
users.users.phoenix = {
isNormalUser = true;
description = "Phoenix";
extraGroups = [ "wheel" ];
shell = pkgs.bash;
home = "/home/phoenix";
createHome = true;
useDefaultShell = true;
};
# Shared packages
environment.systemPackages = with pkgs; [
nano
vim
git
curl
wget
sops
age
];
# Shared services
services.openssh.enable = true;
services.openssh.permitRootLogin = "no";
# Timezone and locale
time.timeZone = "Europe/London";
i18n.defaultLocale = "en_US.UTF-8";
console.keyMap = "uk";
# Firewall
networking.firewall.enable = true;
networking.firewall.allowedTCPPorts = [ 22 ]; # SSH
}

28
docker-compose.yml Normal file
View File

@@ -0,0 +1,28 @@
services:
nixos:
image: nixos-builder:latest
container_name: nixos-builder
volumes:
- .:/workspace
- ./age.key:/etc/sops/keys/age.key:ro
- nix_store:/nix
- nix_cache:/root/.cache/nix
environment:
- NIX_CONFIG_DIR=/root/.config/nix
- SOPS_AGE_KEY_FILE=/etc/sops/keys/age.key
- TMPDIR=/tmp
tmpfs:
- /tmp
working_dir: /workspace
tty: true
stdin_open: true
profiles:
- default
security_opt:
- apparmor:unconfined
cap_add:
- SYS_ADMIN
volumes:
nix_store:
nix_cache:

151
flake.lock generated Normal file
View File

@@ -0,0 +1,151 @@
{
"nodes": {
"colmena": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",
"nix-github-actions": "nix-github-actions",
"nixpkgs": "nixpkgs",
"stable": "stable"
},
"locked": {
"lastModified": 1762034856,
"narHash": "sha256-QVey3iP3UEoiFVXgypyjTvCrsIlA4ecx6Acaz5C8/PQ=",
"owner": "zhaofengli",
"repo": "colmena",
"rev": "349b035a5027f23d88eeb3bc41085d7ee29f18ed",
"type": "github"
},
"original": {
"owner": "zhaofengli",
"repo": "colmena",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1650374568,
"narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "b4a34015c698c7793d592d66adbab377907a2be8",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"colmena",
"nixpkgs"
]
},
"locked": {
"lastModified": 1729742964,
"narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "e04df33f62cdcf93d73e9a04142464753a16db67",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1750134718,
"narHash": "sha256-v263g4GbxXv87hMXMCpjkIxd/viIF7p3JpJrwgKdNiI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9e83b64f727c88a7711a2c463a7b16eedb69a84c",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1688392541,
"narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-22.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1779560665,
"narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"colmena": "colmena",
"nixpkgs": "nixpkgs_2",
"nixpkgs-stable": "nixpkgs-stable"
}
},
"stable": {
"locked": {
"lastModified": 1750133334,
"narHash": "sha256-urV51uWH7fVnhIvsZIELIYalMYsyr2FCalvlRTzqWRw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "36ab78dab7da2e4e27911007033713bab534187b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.05",
"repo": "nixpkgs",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

28
flake.nix Normal file
View File

@@ -0,0 +1,28 @@
{
description = "Colmena Remote NixOS Management";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-22.11";
colmena.url = "github:zhaofengli/colmena";
};
outputs = inputs@{ nixpkgs, colmena, ... }:
{
colmena = {
meta = {
nixpkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [];
};
specialArgs = {
inherit inputs;
};
};
blade = import ./hosts/blade;
};
};
}

96
hosts/blade/default.nix Normal file
View File

@@ -0,0 +1,96 @@
{
name,
nodes,
pkgs,
lib,
inputs,
...
}:
{
deployment = {
targetHost = "192.168.1.69"; # also supports an IP address
targetPort = 22;
targetUser = "phoenix";
buildOnTarget = true;
keys.phoenix.keyFile = "/home/nixos/.ssh/id_ed25519.pub";
};
imports = [
./hardware-configuration.nix
];
# boot.loader.grub.enable = true;
networking.useDHCP = false;
networking.interfaces.eth0.useDHCP = true;
time.timeZone = "America/New_York";
networking.hostName = "blade";
services.xserver.xkb.layout = "uk";
i18n.defaultLocale = "en_US.UTF-8";
nix.settings.trusted-users = [
"root"
"@wheel"
];
users.users.phoenix = {
isNormalUser = true;
extraGroups = [
"wheel"
"networkmanager"
];
home = "/home/phoenix";
packages = with pkgs; [
vim
tree
lego
];
openssh.authorizedKeys.keys = [
"sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBMPNyAyMrqlI3tUI39TqcgYBciIC+aY2UNAunFqylhhzTVA14rluMjFqHf9HKs9SHZ52nLEV58/BxlnMeMtRc+cAAAAEc3NoOg== phoenix@DESKTOP-1A9MAJO"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMoM3FANeYtWLJIiTL+nU0XzZft2psiSgbCmNfQZBXgy nixos@nixos"
];
};
systemd.user.services.dbus = {
restartIfChanged = false;
};
security.sudo.extraRules = [
{
users = [ "phoenix" ];
commands = [
{
command = "ALL";
options = [ "NOPASSWD" ]; # "SETENV" # Adding the following could be a good idea
}
];
}
];
environment.systemPackages = with pkgs; [
inetutils
mtr
sysstat
dig
openssl
];
services.openssh = {
enable = true;
settings.PermitRootLogin = "yes";
};
networking.firewall.allowedTCPPorts = [
22
80
443
];
# networking.firewall.allowedUDPPorts = [ ... ];
networking.firewall.enable = true;
networking.usePredictableInterfaceNames = false;
# Bootloader
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
system.stateVersion = "25.11";
}

View File

@@ -0,0 +1,31 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[ (modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "uas" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/64882a2d-c966-4fc9-96f0-413c6d835848";
fsType = "ext4";
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/8381-A6DD";
fsType = "vfat";
options = [ "fmask=0022" "dmask=0022" ];
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

17
hosts/blade/secrets.yaml Normal file
View File

@@ -0,0 +1,17 @@
phoenix_password: ENC[AES256_GCM,data:v39j4NSTWfDcg1em5eat7APzEAOdgxWZ7RVhtErVFfVsvK7lof3mmdjfcqaRiJL0VZunY6/idAIG9m18p6+14+pre3Esrr56iw==,iv:ut2M0g+tgVwNA2MGb0OhQRLevcQp8IkkYvnJlDzIK3Q=,tag:AtL7MAntOPSp37T+12jy3A==,type:str]
root_password: ENC[AES256_GCM,data:qn/nJm1Wt9GPVh1qifIIxOp89WWGMuAL52JHoHC3S+0HHFkAds+ZuvuXWnwt6t9WG3v8QdBXbGEK+KlEwdyke5OTfnWQXr0udA==,iv:Ve1IAI3vRGnUSgJoSYKGl3swLKjgozFL5I7crbAjtWw=,tag:6IBWbriKr1bt0IWuppLlRg==,type:str]
sops:
age:
- recipient: age1t9c8j4yclx7wylzry8k0f473p4qt32d0kvgj8ce5k5zqvj2p9g4qsplaw8
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQL0lPQWh1ME5Ibm1vSnF6
WHg5VDZGaEt2bVFMN2tVUm9rbjNBVTdNdWo0Ck9aeVI2NVpDR3l3Qk5GTE1EN0I0
L2JtLzg3aFJqaEpURWs4cy9iZWNVNEkKLS0tIHhnUEdQVm5odk04QjJLKzNPdnU4
MVpEejNnN0xNYWRxeVFLV2c5MTBhbmMKi4y6u2MaqjnnNR/jKXPZDBX2xfnkbwZ3
ZP/Eu4qwakyow6pmn0Xw4IMizkAmylQWQL7T2lfoqatZ2sAyUQj2Yg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-05-28T14:31:52Z"
mac: ENC[AES256_GCM,data:dzNPYMmmDV4VjFeGa0FtcPbGQmVo48V7M8kqnmH4HsZJRUlBO+sNclQbBibGjne5hZYP6h2dfRb3gXYV4OmmLUcaQenMRLAUGQvR80wAIRFcD+v7ax2oqiG0sTkodtG8dFfeKwpWvWLTL6IuVuEZ0+yVp9pvtqHPdrB0c73rqzY=,iv:T6d4B4s0OY/InJ03y4COPG0Ms0WlwN1WQjK+lThnjzM=,tag:Jni2/94F+SX1149/cBK2dA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.11.0