Compare commits

..
Author SHA1 Message Date
beatzaplentyandClaude Sonnet 4.6 e9b225d2d6 fix(tailscale-router): add MASQUERADE to POSTROUTING, not nixos-nat-post
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m31s
extraCommands runs after nixos-nat-post is deleted but before it is
re-created, so -A nixos-nat-post silently fails every time.  POSTROUTING
is a built-in chain that always exists; target it directly instead.

The -C idempotency check prevents duplicate rules on firewall reloads.
Drop networking.nat.enable -- it was only needed for the sub-chain that
turned out to be the wrong target.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-26 10:00:47 +10:00
beatzaplentyandClaude Sonnet 4.6 781b1d324e fix(tailscale-router): actually insert MASQUERADE rule via extraCommands
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m34s
networking.nat.externalInterface without internalInterfaces creates the
nixos-nat-post chain but inserts no MASQUERADE rule into it — confirmed
by inspecting the live firewall-start script on the deployed host.

Add the rule explicitly via firewall.extraCommands targeting nixos-nat-post,
scoped to LAN source traffic (vars.lanCidr) going out tailscale0.
extraStopCommands removes it on firewall stop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-26 09:54:33 +10:00
beatzaplentyandClaude Sonnet 4.6 cda2132d6a fix(tailscale-router): masquerade LAN traffic into Tailscale
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m31s
Without SNAT on tailscale0, Tailscale drops forwarded packets from LAN
source IPs (192.168.2.x) because they are not recognised Tailscale
addresses.  With networking.nat.externalInterface = "tailscale0", all
traffic leaving through the Tailscale tunnel is masqueraded to the
router's own Tailscale IP (100.x.x.x), making it indistinguishable from
locally-originated traffic.  Conntrack handles the return path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-26 09:45:07 +10:00
beatzaplenty 6c1cc821a0 Merge pull request 'feat(tailscale): enable UDP GRO forwarding on subnet router uplink' (#68) from worktree-polished-wandering-toast into main
Check NixOS configurations / eval-hosts (push) Successful in 10m34s
Reviewed-on: #68
2026-07-25 23:14:33 +00:00
beatzaplentyandClaude Sonnet 4.6 4be064572d feat(tailscale): enable UDP GRO forwarding on subnet router uplink
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m42s
Adds a oneshot systemd service that sets ethtool rx-udp-gro-forwarding on
and rx-gro-list off on the default-route interface at boot, silencing
Tailscale's warning about suboptimal UDP GRO forwarding on subnet routers.
Interface is discovered dynamically via `ip route get` so it works on all
platforms regardless of NIC naming.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-26 09:13:28 +10:00
beatzaplenty 89d506180d Merge pull request 'fix(create-proxmox-resource): fix VM disk never attaching after import' (#67) from worktree-warm-discovering-moon into main
Check NixOS configurations / eval-hosts (push) Successful in 10m33s
Reviewed-on: #67
2026-07-25 22:57:21 +00:00
beatzaplentyandClaude Sonnet 4.6 6e1e992652 fix(proxmox): embed SSH host key via NIXOS_HOST_KEYS_DIR so sops can decrypt on first boot
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m30s
--pre-format-files placed the key on the QEMU builder VM's rootfs, not the
target disk. nixos-install chroots into the target and runs sshd-keygen, which
found no key in the chroot and generated a fresh (unregistered) one. sops then
could not decrypt on first boot because the key didn't match .sops.yaml, leaving
both root and nixos with '!' in /etc/shadow even after mutableUsers = false was
set (hashedPasswordFile pointed to paths sops never wrote).

Fix modules/platforms/proxmox.nix to embed the clan SSH host key in
environment.etc via NIXOS_HOST_KEYS_DIR at eval time -- the same pattern
lxc.nix uses. nixos-install's own activation places the key on the target disk,
sshd-keygen finds it already present and skips generation, and sops decrypts
correctly on first boot. Includes the same preserveSshHostKey/restoreSshHostKey
activation scripts as lxc.nix so subsequent nixos-rebuild switch calls (without
NIXOS_HOST_KEYS_DIR) don't remove the key as "obsolete" from environment.etc.

Update create-proxmox-resource.sh: switch VM builds from
  ./result-<target> --pre-format-files ... --build-memory 2048
to
  NIXOS_HOST_KEYS_DIR=$(pwd)/host-keys nix build --impure ... diskoImagesScript
  ./result-<target> --build-memory 2048
matching the LXC build path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011uRcikkTp3D5VbXj2DwNpQ
2026-07-26 08:54:39 +10:00
beatzaplentyandClaude Sonnet 4.6 5467c2e140 fix(create-proxmox-resource): fix VM disk never attaching after import
Three bugs combined to leave every VM build with a shell but no boot disk:

1. The remote build script moved the raw image to /var/lib/vz/import/ before
   qm importdisk could use it. If the mv failed (cross-filesystem copy, sudo
   path, or any other reason) the remote script exited non-zero -- but the
   local script's set -e handling of the SSH heredoc was inconsistent, so
   qm create sometimes ran anyway, leaving a diskless VM shell.

   Fix: skip the mv entirely. The diskoImagesScript writes <hostname>.raw into
   its CWD (the remote repo dir, $out = $PWD at invocation). Import directly
   from that path; clean it up after a successful import.

2. The qm importdisk output regex expected "Successfully imported disk as '...'"
   but current Proxmox emits "unusedN: successfully imported disk '...'"
   (lowercase, no "as"). The grep returned no match and exited 1.

3. The disk_id assignment used $(... | grep ...) without || true inside the
   substitution. With set -euo pipefail, a non-zero grep exit aborts the
   script before the fallback could run -- so the VM was always left with an
   unattached unused0 disk.

   Fix: update the primary regex to match the actual PVE format; add || true
   inside the substitution so set -e never fires on a grep miss; add a qm
   config fallback (scan for unusedN: lines) that works regardless of PVE
   output format changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011uRcikkTp3D5VbXj2DwNpQ
2026-07-26 08:54:39 +10:00
beatzaplenty 123cd2b3d7 Merge branch 'main' of https://gitea.lan.ddnsgeek.com/beatzaplenty/nixos
Check NixOS configurations / eval-hosts (push) Successful in 10m28s
2026-07-26 08:23:44 +10:00
beatzaplenty 006dd8097a updated vm HDD size 2026-07-26 08:23:30 +10:00
beatzaplenty 18cd6e884e Merge pull request 'fix(common): set mutableUsers = false to fix password setup on disk images' (#65) from worktree-warm-discovering-moon into main
Check NixOS configurations / eval-hosts (push) Successful in 10m36s
Reviewed-on: #65
2026-07-25 21:37:34 +00:00
beatzaplenty 3102d66337 Merge pull request 'fix(create-proxmox-resource): case-insensitive importdisk parse + warn on --disk-size for VMs' (#64) from worktree-gentle-cuddling-hippo into main
Check NixOS configurations / eval-hosts (push) Successful in 10m22s
Reviewed-on: #64
2026-07-25 21:37:14 +00:00
beatzaplentyandClaude Sonnet 4.6 dfa5452af5 fix(common): set mutableUsers = false to fix password setup on disk images
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m31s
When a proxmox-* disk image is built, activation runs during the image
build without a valid sops age key (the SSH host key doesn't exist yet),
so root and nixos land in /etc/shadow with locked '!' entries. With the
default mutableUsers = true, update-users-groups.pl preserves existing
shadow entries for accounts that already exist, so hashedPasswordFile is
silently ignored on every subsequent boot — passwords are never fixed.

Setting mutableUsers = false forces update-users-groups.pl to apply
hashedPasswordFile unconditionally on every activation. On first real
boot the sops-decrypted hash is now written regardless of whether the
account already existed in shadow from the image build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011uRcikkTp3D5VbXj2DwNpQ
2026-07-26 07:36:06 +10:00
beatzaplentyandClaude Sonnet 4.6 852ba2240f fix(create-proxmox-resource): case-insensitive importdisk parse + warn on --disk-size for VMs
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m22s
qm importdisk in QEMU 11.x outputs lowercase "successfully imported disk
as '...'" rather than the capitalised form the original grep expected.
The case mismatch made disk_id always empty, which caused the script to
exit 1 after qm create had already run -- leaving the VM with only an
EFI disk, no scsi0, and boot order still set to net0.

Fix by adding -i (case-insensitive) to the grep. Both the old capitalised
format (where the disk id had an "unused0:" prefix inside the quotes) and
the new lowercase format are handled correctly: the sed strip of unused0:
is preserved for backward compatibility, and the regex result is identical
either way.

Also add an early warning when --disk-size is passed for --type vm: the
flag is LXC-only for create mode and was silently ignored, leaving users
expecting a different size than the proxmoxImageSize in variables.nix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-26 06:40:54 +10:00
beatzaplenty 096dff4fa0 Merge pull request 'docs(sync-host-keys): fix stale host-keys/ references in comments and usage' (#63) from fix-stale-wording into main
Check NixOS configurations / eval-hosts (push) Successful in 10m23s
Reviewed-on: #63
2026-07-25 14:36:31 +00:00
beatzaplentyandClaude Sonnet 4.6 b5f749daa9 docs(sync-host-keys): fix stale host-keys/ references in comments and usage
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m24s
After the clan vars migration all keys are in vars/per-machine/, not
host-keys/. Update:
- File header: "existing clan var is never overwritten" (not host-keys/ file)
- Header --remove/--regenerate description: mention clan vars as primary
- usage() --remove, --regenerate-all-keys, --dry-run text
- cmd_remove/cmd_regenerate_all empty-guard messages
- README.md vars/per-machine/ row: "all deployed hosts" (not "LXC hosts")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
2026-07-26 00:11:03 +10:00
beatzaplenty adaf53d647 Merge pull request 'fix(sync-host-keys): extend --remove/--regenerate to cover clan vars' (#62) from fix-sync-host-keys-clan-vars into main
Check NixOS configurations / eval-hosts (push) Successful in 10m22s
Reviewed-on: #62
2026-07-25 14:02:11 +00:00
beatzaplentyandClaude Sonnet 4.6 01679f1639 fix(sync-host-keys): extend --remove/--regenerate to cover clan vars
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m26s
locally_managed_hosts() only scanned host-keys/ (now empty for all
current targets), so --remove and --regenerate-all-keys silently did
nothing. Fix:

- locally_managed_hosts(): also yields targets from
  vars/per-machine/*/openssh/ssh_host_ed25519_key/secret, deduped
- cmd_remove: shows [clan-vars] or [host-keys/] label per entry;
  deletes vars/per-machine/<target>/openssh/ in addition to host-keys/
- cmd_regenerate_all: same -- removes clan vars dirs before regenerating

Also update CLAUDE.md and README.md to reflect that all flake targets
now use clan vars (not just lxc-*); host-keys/ is only for the
auto-installer's own pre-seeding path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
2026-07-25 23:50:38 +10:00
beatzaplenty 8f4c88347d Merge pull request 'Worktree phase0 provision ordering fix' (#61) from worktree-phase0-provision-ordering-fix into main
Check NixOS configurations / eval-hosts (push) Successful in 10m19s
Reviewed-on: #61
2026-07-25 12:20:21 +00:00
beatzaplentyandClaude Sonnet 4.6 e9832d87c4 chore(vars): bulk clan vars SSH host keys for all remaining 20 flake targets
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m24s
Adds vars/per-machine/<target>/openssh/ for every flake target except
lxc-tor-relay and lxc-nix-cache (already committed). 18 targets recovered
from pve1 host-keys/ backup; lxc-gui and proxmox-minimal have no prior
live deployment and no backup key, so fresh ed25519 keys were generated —
their .sops.yaml anchors were updated to match.

All secrets are admin-only encrypted (matching clan_generate_ssh_key
convention). Age fingerprints verified against .sops.yaml anchors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
2026-07-25 21:54:37 +10:00
beatzaplentyandClaude Sonnet 4.6 08aac4261f fix(secrets): correct corrupted sops fingerprints for proxmox-minimal and lxc-gui
The previous Phase 4 commit had sed-mangled fingerprints for these two
targets (old and new fingerprints concatenated into one line). The correct
new fingerprints are:
  - proxmox-minimal: age19m0m7vdfg... (freshly generated, no prior key on pve1)
  - lxc-gui:        age1rrxqea6q6... (freshly generated, no prior key on pve1)

Re-run sops updatekeys on common.yaml and gui.yaml to match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
2026-07-25 21:54:25 +10:00
beatzaplenty 2526b2dca7 Merge pull request 'chore(secrets): Phase 4 — remove stale sops.yaml anchors and re-encrypt' (#60) from worktree-phase0-provision-ordering-fix into main
Check NixOS configurations / eval-hosts (push) Successful in 10m19s
Merge PR #60: Phase 4 — remove stale sops.yaml anchors
2026-07-25 11:32:25 +00:00
beatzaplentyandClaude Sonnet 4.6 2df53fd5d7 chore(secrets): Phase 4 — remove stale sops.yaml anchors and re-encrypt
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m21s
Remove four stale age key anchors from .sops.yaml that correspond to
non-lxc build-type variants that were never deployed (or are now
superseded by their lxc-* counterparts):

  &docker   → superseded by &lxc-docker (active running host)
  &server   → superseded by &lxc-server (active running host)
  &nix-cache → superseded by &lxc-nix-cache (active running host)
  &nix-minimal → superseded by &lxc-minimal (active running host)

Also remove the secrets/docker.yaml creation_rules block entirely since
that file does not exist.

Re-encrypt secrets/common.yaml, secrets/nix-cache.yaml, and
secrets/server.yaml with sops updatekeys to drop the stale recipients.
The four removed keys can no longer decrypt these files.

Update README.md and CLAUDE.md to clarify that deployed lxc-* hosts
now use clan vars (vars/per-machine/<target>/openssh/) rather than the
gitignored host-keys/ directory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
2026-07-25 21:31:56 +10:00
beatzaplenty 5e2ff76cf7 Merge pull request 'refactor(provision): Phase 3 — remove legacy host-keys/ fallback' (#59) from worktree-phase0-provision-ordering-fix into main
Check NixOS configurations / eval-hosts (push) Successful in 10m23s
Merge PR #59: Phase 3 — remove legacy host-keys/ fallback
2026-07-25 11:24:55 +00:00
beatzaplentyandClaude Sonnet 4.6 e8c4122460 refactor(provision): Phase 3 — remove legacy host-keys/ fallback
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m22s
All actively deployed lxc-* hosts now have clan vars. Remove the legacy
scp -pr host-keys/ fallback in sync_remote_host_keys(): instead of
silently copying the gitignored directory, error clearly if no clan var
exists for the target and tell the operator how to generate one.

Also extend the uncommitted-changes check to cover vars/per-machine/ in
addition to .sops.yaml and secrets/, since clan vars must be committed
before the remote build git-pulls them.

Update the script header and sync_remote_host_keys comment to reflect
the new clan-only key flow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
2026-07-25 21:24:32 +10:00
beatzaplenty 5db41b1166 Merge pull request 'chore(vars): clan vars SSH host key for lxc-nix-cache' (#58) from worktree-phase0-provision-ordering-fix into main
Check NixOS configurations / eval-hosts (push) Successful in 10m22s
Merge PR #58: clan vars SSH host key for lxc-nix-cache
2026-07-25 11:18:19 +00:00
beatzaplentyandClaude Sonnet 4.6 ea7794dc05 chore(vars): commit clan vars SSH host key for lxc-nix-cache
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m27s
Encrypted private key matches the running container's key and the
vars.nixCacheHostKey in variables.nix (no rotation). Age fingerprint
age1ufg390... matches the &lxc-nix-cache anchor in .sops.yaml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
2026-07-25 21:10:46 +10:00
beatzaplenty 67752fb1e8 Merge pull request 'fix(lxc): clan vars for lxc-tor-relay + sops-reinstall service fix (network.target)' (#57) from worktree-phase0-provision-ordering-fix into main
Check NixOS configurations / eval-hosts (push) Successful in 10m30s
Merge PR #57: fix(lxc): sops-reinstall to network.target + clan vars for lxc-tor-relay
2026-07-25 09:55:25 +00:00
beatzaplentyandClaude Sonnet 4.6 1a14b1d4d3 fix(lxc): move sops-reinstall service from sysinit to network.target
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m34s
nixos-lxc-sops-reinstall.service called switch-to-configuration test at
sysinit.target time (DefaultDependencies=false), before D-Bus was up.
D-Bus is required to restart systemd targets after activation scripts
run. The service reported failure on every boot (exit 1: "Failed to open
dbus connection") even though secrets were correctly installed, because
the D-Bus call happens after activation scripts complete.

Move the service to network.target so basic.target (which includes
dbus-broker.service) runs first. Also drop DefaultDependencies=false so
systemd auto-adds After=basic.target. Add SuccessExitStatus=11 to handle
the edge case where switch-to-configuration holds the lock during a
concurrent rebuild (exit 11 = "Could not acquire lock" -- the rebuild's
own activation already installed the secrets, so treating it as success
is correct).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
2026-07-25 19:50:35 +10:00
beatzaplentyandClaude Sonnet 4.6 68aea4cdcc chore(vars): commit clan vars SSH host key for lxc-tor-relay
The key was generated in a prior session but not committed — the clan vars
files existed only in that session's working tree. Recovered the original
private key from pve1's host-keys/ backup (fingerprint age16kqf... matches
the &lxc-tor-relay anchor already in .sops.yaml), re-encrypted for admin
age key only, and stored in the canonical clan vars layout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
2026-07-25 19:29:21 +10:00
beatzaplenty 0853952269 Merge pull request 'feat(provision): Phase 0-2 + fix — clan-core SSH host keys, activation ordering, boot-time sops' (#56) from worktree-phase0-provision-ordering-fix into main
Check NixOS configurations / eval-hosts (push) Successful in 10m33s
Merge feat(provision): Phase 0-2 + fix — clan-core SSH host keys, activation ordering, boot-time sops
2026-07-25 09:16:43 +00:00
beatzaplentyandClaude Sonnet 4.6 055577ee91 fix(lxc): fix activation ordering and add boot-time sops reinstall
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m33s
Two bugs prevented nixos-rebuild switch from working on lxc-* hosts after
first boot, both confirmed live on a deployed lxc-tor-relay container:

1. Ordering bug: preserveSshHostKey had no explicit deps, so the topological
   sort placed it at position 7 — after etc at position 5. By the time it
   tried to save the SSH key, etc had already removed it as "obsolete"
   (absent from the current generation's environment.etc when built without
   NIXOS_HOST_KEYS_DIR). Consolidate all four system.activationScripts entries
   into one block and add etc = { deps = ["preserveSshHostKey"]; } and
   setupSecrets = { deps = ["restoreSshHostKey"]; } to enforce the correct
   save→etc→restore→sops chain.

2. No boot-time secrets: /run/secrets is a tmpfs cleared on every reboot, and
   sops-nix does NOT generate a boot-time service in this configuration
   (confirmed live: no sops-nix.service in systemctl list-unit-files).
   Add nixos-lxc-sops-reinstall.service, modelled after sops-nix's own service
   placement (wantedBy/before sysinit.target, DefaultDependencies=false), so
   secrets are reinstalled before basic.target on every non-first boot.
   ConditionPathExists skips it on first boot; nixos-lxc-first-boot-activate
   handles that case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
2026-07-25 19:07:30 +10:00
beatzaplentyandClaude Sonnet 4.6 a63e1c70c3 feat(provision): Phase 2 — migrate SSH host keys to clan vars
Replaces the gitignored host-keys/ directory with clan vars as the
authoritative storage for SSH host keys. Keys are now generated as
sops-binary-encrypted clan var files (admin-key only) and checked into
vars/per-machine/<target>/openssh/, eliminating the plaintext private
key that previously had to live outside the repo.

Changes:
- modules/clan/ssh-host-key.nix: clan vars generator for the ed25519
  SSH host key pair (neededFor="activation" — not mapped to sops.secrets,
  delivered via tarball baking for LXC or --pre-format-files for VMs)
- flake.nix: add clanCore module + required settings to every mkTarget;
  deduplicate bundled disko/sops-nix via follows; all 27 hosts eval clean
- flake.lock: updated to reflect the new follows constraints
- scripts/lib/clan-vars.sh: new helper library with
  clan_ssh_key_exists / clan_ssh_pubkey_path / clan_decrypt_ssh_key /
  clan_generate_ssh_key for use by the provisioning and sync scripts
- scripts/secrets/sync-host-keys.sh: queue_host_sync() now checks clan
  vars first; generates via clan_generate_ssh_key if no key exists;
  derives age fingerprint from clan pub key for .sops.yaml registration
- scripts/proxmox/create-proxmox-resource.sh: key management simplified
  (sync-host-keys.sh now generates the key if missing, so the inline
  prepare-host-key.sh call is gone); sync_remote_host_keys() decrypts
  the clan key into a temp dir and scps just the two files to the node
  when a clan key exists, falling back to the old host-keys/ scp for
  any remaining legacy entries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2EJ4qTsM5KUqhS5c3GAwx
2026-07-25 18:22:33 +10:00
beatzaplentyandClaude Sonnet 4.6 a9745b594b feat(flake): add clan-core 26.05 as a flake input (Phase 1, no behavior change)
Introduces clan-core pinned to its 26.05 release alongside the existing nixpkgs
26.05 input. No host configuration is changed — this is a pure dependency
addition so Phase 2 (per-host vars/secret management migration) has the input
available without a separate flake.lock bump.

clan-core.inputs.nixpkgs.follows = "nixpkgs" keeps a single nixpkgs closure.
sops-nix remains as a flake input; vars layers on top of it rather than
replacing it (clan's sops storage backend still needs sops-nix).

All hosts evaluate cleanly (codex-maintenance.sh --full-check equivalent
triggered by the flake.nix change).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 17:29:29 +10:00
beatzaplentyandClaude Sonnet 4.6 78bb784265 fix(provision): block build until sops changes are committed, guard missing host keys
Three ordering-related fixes to the Proxmox provisioning flow:

1. prepare-host-key.sh: make idempotent -- if the key already exists, print
   a note and exit 0 instead of erroring. The caller (create-proxmox-resource.sh)
   already guards standalone calls, but the script itself should be safe to
   run directly on a host that was already keyed.

2. create-proxmox-resource.sh: after sync-host-keys.sh updates .sops.yaml /
   secrets/, detect uncommitted changes and block with a prompt until the
   operator confirms they've committed and pushed. The PVE node's git pull
   only picks up committed+pushed state; without this gate, a new host's sops
   recipient is missing from the secrets files the image build uses, so the
   host can't decrypt secrets on first boot.

3. create-proxmox-resource.sh: add an explicit existence check for the host
   key in both the LXC and VM remote build heredocs, before it's passed as
   --pre-format-files / NIXOS_HOST_KEYS_DIR input. Gives a clear error
   pointing at sync-host-keys.sh instead of a raw `cp: cannot stat` from
   disko deep in the build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 17:14:25 +10:00
beatzaplentyandClaude Sonnet 4.6 784e064fa2 fix(create-proxmox-resource): use absolute path for --pre-format-files
Check NixOS configurations / eval-hosts (push) Successful in 10m20s
The disko images script does `cd "$TMPDIR"` before parsing its arguments,
so relative paths passed to --pre-format-files resolve against the temp
dir instead of the repo root. Use $(pwd) to capture the absolute repo
path before the disko script changes directory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 16:45:04 +10:00
beatzaplenty 4a5afa6c1c Merge pull request 'fix(tailscale): rename tailscale-subnet-router to tailscale-router everywhere' (#55) from worktree-fix-tailscale-host-dir into main
Check NixOS configurations / eval-hosts (push) Successful in 10m33s
fix(tailscale): rename tailscale-subnet-router to tailscale-router everywhere
2026-07-25 06:37:18 +00:00
beatzaplentyandClaude Sonnet 4.6 c844ccc4e3 fix(tailscale): rename tailscale-subnet-router → tailscale-router everywhere
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m28s
Complete the rename so all identifiers match networking.hostName:
- flake.nix: attribute names and buildType strings
  (linode/proxmox/lxc-tailscale-subnet-router → *-tailscale-router)
- modules/build-types/tailscale-subnet-router.nix → tailscale-router.nix
- .sops.yaml: anchor and alias names (age keys unchanged, no re-encrypt needed)
- host-keys/: local gitignored key files renamed (not committed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 16:33:03 +10:00
beatzaplentyandClaude Sonnet 4.6 9a0aea8f89 fix(create-proxmox-resource): auto-generate missing host key before sync
Check NixOS configurations / eval-hosts (push) Successful in 10m19s
If host-keys/<target>_ssh_host_ed25519_key doesn't exist, run
prepare-host-key.sh to generate it before sync-host-keys.sh runs.
Prevents sync-host-keys.sh from hitting its SKIP/exit-1 path (anchor
in .sops.yaml but no local key) and the downstream disko build failure
when --pre-format-files can't find the key file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 16:26:49 +10:00
beatzaplenty b013e28dcd Merge pull request 'fix(tailscale): rename hosts/tailscale-subnet-router → hosts/tailscale-router' (#54) from worktree-fix-tailscale-host-dir into main
Check NixOS configurations / eval-hosts (push) Successful in 10m32s
Reviewed-on: #54
2026-07-25 06:24:59 +00:00
beatzaplentyandClaude Sonnet 4.6 5a56030f6e fix(tailscale): rename hosts/tailscale-subnet-router → hosts/tailscale-router
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m30s
The directory name was tailscale-subnet-router but networking.hostName
was already tailscale-router, causing a mismatch that confused scripts
comparing directory paths against actual hostnames. Flake attribute
names (linode/proxmox/lxc-tailscale-subnet-router) and .sops.yaml
anchors are unchanged — they describe the build type, not the hostname.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 16:24:13 +10:00
beatzaplentyandClaude Sonnet 4.6 f8719437ba fix(nix-cache): use FQDN to fix hostname resolution on clients
Check NixOS configurations / eval-hosts (push) Successful in 10m30s
systemd-resolved only uses LLMNR for single-label hostnames, never DNS —
same issue mount-data.nix already documented and fixed for NFS by switching
to server.sweet.home. Change the substituter URL, SSH knownHosts, and
remote-builder hostName from bare "nix-cache" to "nix-cache.sweet.home",
and update nginx's virtualHost to match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 16:01:56 +10:00
beatzaplentyandClaude Sonnet 4.6 9e34b9cbb9 fix(push-host-keys): detect non-interactive stdin, direct to SUDO_PASS
Check NixOS configurations / eval-hosts (push) Successful in 10m19s
read exits non-zero when stdin is not a terminal (set -e killed the
script silently). Catch that and emit a clear error pointing to the
SUDO_PASS environment variable rather than crashing with no output.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 15:55:50 +10:00
beatzaplenty 4dabd725f0 sync nix-cache ssh key
Check NixOS configurations / eval-hosts (push) Successful in 10m34s
2026-07-25 15:55:04 +10:00
beatzaplentyandClaude Sonnet 4.6 2743d664a5 fix(push-host-keys): remove /dev/tty probe, plain read is sufficient
Check NixOS configurations / eval-hosts (push) Successful in 10m22s
/dev/tty exists as a device node even without a controlling terminal,
so -r/-w tests pass but opening it fails. Plain 'read -r -s' from stdin
is enough: works interactively from a real terminal, and from a non-tty
context the caller should set SUDO_PASS in the environment instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 15:53:00 +10:00
beatzaplentyandClaude Sonnet 4.6 cfa34f3565 fix(push-host-keys): fall back to stdin when /dev/tty unavailable
Check NixOS configurations / eval-hosts (push) Successful in 10m19s
Environments without a controlling terminal (containers, CI agents)
don't have /dev/tty. Try it first for the sudo password prompt, fall
back to plain stdin so the script works in both contexts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 15:51:04 +10:00
beatzaplentyandClaude Sonnet 4.6 e021b49412 fix(push-host-keys): prompt sudo password once, pass via sudo -S
Check NixOS configurations / eval-hosts (push) Successful in 10m22s
Instead of ssh -t (requires PTY on both sides), prompt for the sudo
password once at startup and pipe it to each remote invocation via
sudo -S. This works from any context -- interactive terminal, background
agent, or script -- with no PTY needed on either end.

Also accepts SUDO_PASS from the environment for non-interactive callers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 15:49:27 +10:00
beatzaplentyandClaude Sonnet 4.6 0c29c6a93d fix(push-host-keys): fix sudo PTY allocation failure
Check NixOS configurations / eval-hosts (push) Successful in 10m28s
ssh -t won't allocate a PTY when its own stdin is redirected (by a
heredoc). Replaced the heredoc-fed 'sudo bash -s' with commands passed
as an argument string so stdin stays free and -t can properly allocate
a PTY for the sudo password prompt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-25 15:39:06 +10:00
beatzaplenty c329988cdd secrets: update recipients and re-encrypt for host key changes
Check NixOS configurations / eval-hosts (push) Failing after 12m10s
2026-07-25 15:31:22 +10:00
beatzaplenty 7a2b5ecf71 Merge pull request 'feat(secrets): add push-host-keys.sh; integrate into sync/recover scripts' (#53) from worktree-push-host-keys into main
Check NixOS configurations / eval-hosts (push) Successful in 10m21s
Reviewed-on: #53
2026-07-25 05:30:32 +00:00
72 changed files with 1379 additions and 510 deletions
+33 -52
View File
@@ -1,32 +1,27 @@
keys: keys:
- &admin age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad - &admin age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad
- &docker age19gfn2yedg76dmztm4hncr7vf3r3c9j0qpt4rap7y7gersjk4m3ks2lhd0e - &proxmox-minimal age19m0m7vdfg86yqy8l5mmle5jdd0unrn3f55t232w8h5ey42cqw34sfpt32n
- &server age1ll6hj5ggruetgjwjfnplpn5xtq35uhlcdflksx3xmnjm6s3uad9sz70jkf - &lxc-gui age1rrxqea6q6pn39sw8y5te63h2py8jgjl9v0jyper86w3ggtn67upqg3ah39
- &nix-cache age120le4a5l8dh3lyfgvmj3d9ksmej6ajs5mer5y7r0vfg3x9fn69dqf8xgzu - &baremetal-gui age1adur9g330gua4l6ndk8cqjg35qc8yxwgme6wrl2hpylcc7vxm38q05ejuy
- &nix-minimal age120whqj96g26lsgy4udvgsn8dc9lumh8jeu3a564fx79rjr5lxffqmrljuu - &linode-docker age17e89ty6p0fw24daanen57wg8uald9s025t3wwxsw269svwpmgvrshfvfvt
- &proxmox-minimal age10at8862478urh0eeuwh8hzln6ck78jgwtztgxatwqlzwagg77y5snm4xzg - &linode-gui age1hrx8qj02fj2ea6d4g9vqhyj9hl7fppkjqfdx2l37py3h6pdkr95s8n8rvs
- &lxc-nix-cache age1xjst4frdh0th6q8m7p7u9g5af7ty5jqeum0p6z8a52a9q7st7ewqw8yl9j - &linode-minimal age1e7l8dusgmgfzd2cxrrzwepzjxt69hzqj4epee0cs27u6yg4kxcuqm34ncx
- &lxc-docker age1ezk9x53zt8kcnscdm80jcyf0xq97vndv7jsn3rl8cc0cwm2jmpmq372dzs - &linode-nix-cache age1jcx3yajjhghn8qh8za3yeu8nxykzlg3p4nrv03vnfvzl0mzayg2qmg940e
- &lxc-minimal age1jy444f9d9stygj4p3w9kh54cqcfr654tvr75tdvee5cxsgtdtc9q3v60ep - &linode-server age1sweerhrga9yf8x6sv0apz4ed4g48rnlcq34rpv20t0rcelwgpgeqwvndzz
- &lxc-pxe-boot age1fxxzpnfse8nd9wz78ht3m0plrmraacf4cpga0pe8fm2tdnqcgy8q7qsyvp - &linode-tailscale-router age1f7usptjx9rv4rxauasve200gxtdt9jkqhhdqstlf20wvlm7u75rsjfw50m
- &lxc-gui age190htw7prp4vln076dxjx3gxxaq06h0zl0te7cqgpx79vl3lhkaes8suy05 - &lxc-docker age17jqc66x9yeshfgd9v78mj483r4zzarqdtuxtrkxe4x5mw679gphshd94th
- &proxmox-server age1ukpqxzl44mnjpy5r96sfuc5sqzm47u4k8ujjh5qdgy6jvl9uqgpspymqfk - &lxc-minimal age1px0h5l9zp2dww0m8fncrc82kfdmzplsfv2ltat7sna28xpg09pqqcl3s2k
- &vm-server age15kh7akxlx7zn00tey79rq2g8lgs4j5y77rcnyfxrxap8ckfu0a9sqvtdhh - &lxc-nix-cache age1ufg390ydrmma849t9xfkxxl5xvdkk6mngnlzhmy7mvuaje8sgcmsmnq6l7
- &baremetal-gui age1ehkswwz2pqaz4svzh7ela5tdnssl8kn6d4vwwxd6zwg8exfpd43syyrrjp - &lxc-pxe-boot age16j42pdc5dr6wnj7xayhkqdj2rny9u68fcqejs50hqq42scssh4gsnrrnlt
- &lxc-tor-relay age1gl5ujmhd2pe37acd0eaar8tmnf2na28n8l28s8xct40vqpxvsdwqf49g4l - &lxc-server age1nruncs4l0ufk7yuc4des8p99c0alfndl0lhsws8tycl5pplfp56s30af5f
- &linode-docker age1eu65wsmez68gegnufl0gqrs0e6w2409mypjlajlq383d7l2e3pjqy5kn6v - &lxc-tailscale-router age1k7d2du5mejsmv5rzavm4xwgpthqvcfsehduquv28nzs53zppa3kqngfxq2
- &linode-gui age17pwyghxr6lq06fw46gwqzhc9ut4paz28rpwx5pmv3cxwak6rgyjsw7lk3w - &lxc-tor-relay age16kqfmvz4e23hmdlqresnyw69ej604s320mmd49h4hm3fhqchtgyqrws0k2
- &linode-minimal age1jukmg69cqxnjd0lp5f534jhqe65rxew2hufcmyjxa3rkw6ayef6s2ylcmy - &proxmox-docker age1arhf2q45zw6wf2uevju4savp575x3m2tfvved5zzq3ay92ynua9s3cm92c
- &linode-nix-cache age1tzsrtwd3p3lrr9g7nv3z5nvmzsz54t2uc6tfqwutp6usmav83s2sck25cc - &proxmox-gui age19mn8zrxl8zpps9yvrh4euquvygpp4fp8queg7xc6qhtnl4ng8c9qx02qwn
- &linode-server age1rf4kj99wuq59k7w8ar326djmgmpl9hcwlnuag07f8gauq8c3y5mqne87s4 - &proxmox-nix-cache age1jlltcv5jcnm40z5k0q6hv053k2rqpqvemtuecdwn527uw8uqz4es3x7m68
- &linode-tailscale-exit-node age1d0zhx7u3mfs3nktl67npey87cze4dwsfvfvgaje4rh3gwv500yssckcf6u - &proxmox-pxe-boot age1ug787sgt6st6k82fgkrug2lzltw4qsukrrqqs3w27ewwqj8rg4hsxcmylz
- &lxc-server age1jc6wx33hdhgwhk6nzy5rr8fkgmqxk9um639tk5h632nqfyaw8czskdptj9 - &proxmox-server age1529taqdwr6t0w7cvzmty0d5y5593wffl0krt48j6uc4u39k56g2qf6ywtp
- &lxc-tailscale-exit-node age1pgykvq4pmxhhjrqupcp99fyad2uht40pt79dkzg66cfvsjy5apjqls8u68 - &proxmox-tailscale-router age1zhfyuzlq40reuqlr34gf77852nhs3t6mqfzrqmas8z6sxk7tcfhsungrm0
- &proxmox-docker age19ht95nv8uhz2shjmakeut8mc3l5spvrcxs3thhe85an7u02r6sysyv457n
- &proxmox-gui age1mhaze5tgvc9lwjpml6dnp3xc292337wgm6376yh6tq4fn492ndjq9h23dq
- &proxmox-nix-cache age15me6sx0f8r58xh9v7aqrj6e99n7eu555jkpw422txpkqt4r02v0qpahlyq
- &proxmox-pxe-boot age1q3hu6eh3mt4saey7dc3yu04s7knnk2ygpm9xt6mg6rqtem6l0uyq4wzul5
- &proxmox-tailscale-exit-node age1tczst3x7thwtcz4vce4rg6kmlsszzm6j45kn2nv860z9wa93a92s05ppmc
creation_rules: creation_rules:
# Shared across every currently-deployed host: root/nixos password hash, # Shared across every currently-deployed host: root/nixos password hash,
@@ -37,58 +32,44 @@ creation_rules:
key_groups: key_groups:
- age: - age:
- *admin - *admin
- *docker
- *server
- *nix-cache
- *lxc-minimal
- *nix-minimal
- *lxc-nix-cache
- *proxmox-minimal - *proxmox-minimal
- *lxc-docker
- *lxc-pxe-boot
- *lxc-gui - *lxc-gui
- *proxmox-server
- *vm-server
- *baremetal-gui - *baremetal-gui
- *lxc-tor-relay
- *linode-docker - *linode-docker
- *linode-gui - *linode-gui
- *linode-minimal - *linode-minimal
- *linode-nix-cache - *linode-nix-cache
- *linode-server - *linode-server
- *linode-tailscale-exit-node - *linode-tailscale-router
- *lxc-docker
- *lxc-minimal
- *lxc-nix-cache
- *lxc-pxe-boot
- *lxc-server - *lxc-server
- *lxc-tailscale-exit-node - *lxc-tailscale-router
- *lxc-tor-relay
- *proxmox-docker - *proxmox-docker
- *proxmox-gui - *proxmox-gui
- *proxmox-nix-cache - *proxmox-nix-cache
- *proxmox-pxe-boot - *proxmox-pxe-boot
- *proxmox-tailscale-exit-node - *proxmox-server
- *proxmox-tailscale-router
- path_regex: secrets/nix-cache\.yaml$ - path_regex: secrets/nix-cache\.yaml$
key_groups: key_groups:
- age: - age:
- *admin - *admin
- *nix-cache
- *lxc-nix-cache
- *linode-nix-cache - *linode-nix-cache
- *lxc-nix-cache
- *proxmox-nix-cache - *proxmox-nix-cache
- path_regex: secrets/server\.yaml$ - path_regex: secrets/server\.yaml$
key_groups: key_groups:
- age: - age:
- *admin - *admin
- *server
- *proxmox-server
- *vm-server
- *linode-server - *linode-server
- *lxc-server - *lxc-server
- *proxmox-server
- path_regex: secrets/docker\.yaml$
key_groups:
- age:
- *admin
- *docker
- path_regex: secrets/tor-relay\.yaml$ - path_regex: secrets/tor-relay\.yaml$
key_groups: key_groups:
+12 -5
View File
@@ -27,9 +27,13 @@ machines when deployed.
template for a *real* host — every other host uses sops-nix template for a *real* host — every other host uses sops-nix
(`hashedPasswordFile`, see "Security Notes" in `README.md`). Flag any *new* (`hashedPasswordFile`, see "Security Notes" in `README.md`). Flag any *new*
secret-like string you encounter instead of committing it. secret-like string you encounter instead of committing it.
- `host-keys/` is gitignored — locally-generated *private* SSH host keys for - `host-keys/` is gitignored — used only by the auto-installer's own
the auto-installer (see `docs/auto-installer.md`). Never commit its environment for pre-seeding non-LXC host keys before first boot (see
contents; if `git status` ever shows it as trackable, something is wrong. `docs/auto-installer.md`). Never commit its contents; if `git status`
ever shows it as trackable, something is wrong. All deployed hosts use
clan vars (`vars/per-machine/<target>/openssh/`, committed and
sops-encrypted) for their SSH host keys — those ARE tracked by git and
belong in the repo.
### Two Proxmox nodes: `pve1.sweet.home` (production) and `pve-test.sweet.home` (sandbox) ### Two Proxmox nodes: `pve1.sweet.home` (production) and `pve-test.sweet.home` (sandbox)
@@ -203,8 +207,11 @@ instead of copying it.
- `scripts/secrets/sync-host-keys.sh` — generates/registers SSH host keys - `scripts/secrets/sync-host-keys.sh` — generates/registers SSH host keys
and their `.sops.yaml`/`secrets/*.yaml` recipients for flake targets, and their `.sops.yaml`/`secrets/*.yaml` recipients for flake targets,
idempotently (`--all`, `<target>`, `--remove`, `--regenerate-all-keys`, idempotently (`--all`, `<target>`, `--remove`, `--regenerate-all-keys`,
all with `--dry-run`). The primary tool for provisioning a new host's all with `--dry-run`). Stores keys as clan vars
secrets access — see "Creating a new machine" in `docs/auto-installer.md`. (`vars/per-machine/<target>/openssh/`, committed and sops-encrypted) for
all flake targets. The primary tool for provisioning a new host's
secrets access — see "Creating a new machine" in
`docs/auto-installer.md`.
- `scripts/secrets/prepare-host-key.sh` — narrower predecessor: generates a - `scripts/secrets/prepare-host-key.sh` — narrower predecessor: generates a
key by an arbitrary name without touching `.sops.yaml`. Still useful to key by an arbitrary name without touching `.sops.yaml`. Still useful to
pre-generate a key before its flake target exists yet, since pre-generate a key before its flake target exists yet, since
+5 -2
View File
@@ -72,7 +72,8 @@ nix eval --json .#nixosConfigurations --apply builtins.attrNames | jq -r '.[]'
| `modules/common/` | Shared NixOS config, Home Manager, aliases imported by every host | | `modules/common/` | Shared NixOS config, Home Manager, aliases imported by every host |
| `modules/nix-cache/` | Binary cache and remote builder client/server modules | | `modules/nix-cache/` | Binary cache and remote builder client/server modules |
| `modules/installer/` | Auto-installer environment (ISO, also served as PXE netboot) — see `docs/auto-installer.md` | | `modules/installer/` | Auto-installer environment (ISO, also served as PXE netboot) — see `docs/auto-installer.md` |
| `host-keys/` | Gitignored, locally-generated SSH host keys for the auto-installer — see `docs/auto-installer.md` | | `host-keys/` | Gitignored; only used by the auto-installer environment for pre-seeding SSH host keys before first boot — see `docs/auto-installer.md`. All deployed hosts use clan vars (`vars/per-machine/<target>/openssh/`) instead |
| `vars/per-machine/` | Clan vars: committed, sops-encrypted SSH host keys for all deployed hosts; read by `create-proxmox-resource.sh` at deploy time |
| `docs/` | Operational notes for cache, builders, lock updates, boot services, the auto-installer, and Proxmox image builds | | `docs/` | Operational notes for cache, builders, lock updates, boot services, the auto-installer, and Proxmox image builds |
| `scripts/` | Codex setup, validation, host-key, release-bump, and Proxmox resource helpers | | `scripts/` | Codex setup, validation, host-key, release-bump, and Proxmox resource helpers |
@@ -162,7 +163,9 @@ sops-nix-everywhere: it has a hardcoded login password instead (no stable
per-boot host key for sops-nix to derive from on ephemeral media) — see per-boot host key for sops-nix to derive from on ephemeral media) — see
"Host keys" in `docs/auto-installer.md` for why, and how the private keys it "Host keys" in `docs/auto-installer.md` for why, and how the private keys it
*does* pre-seed for target hosts stay out of git via the gitignored *does* pre-seed for target hosts stay out of git via the gitignored
`host-keys/` directory. `host-keys/` directory. All deployed hosts use clan vars
(`vars/per-machine/<target>/openssh/`, committed and sops-encrypted) for
their SSH host keys.
This repository's git *history* still contains secrets committed before this This repository's git *history* still contains secrets committed before this
migration (see `remove-sensetive-info-refactor.md`) — those are being migration (see `remove-sensetive-info-refactor.md`) — those are being
Generated
+151 -1
View File
@@ -1,5 +1,62 @@
{ {
"nodes": { "nodes": {
"clan-core": {
"inputs": {
"data-mesher": "data-mesher",
"disko": [
"disko"
],
"flake-parts": "flake-parts",
"nix-darwin": "nix-darwin",
"nix-select": "nix-select",
"nixpkgs": [
"nixpkgs"
],
"sops-nix": [
"sops-nix"
],
"systems": "systems",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1783497933,
"narHash": "sha256-TxmwEews6URFPqOWEHNychtXbFDgLZjbOfEXtvtOm6U=",
"rev": "3dc0221ca09033599fe98055e9bbc81bdf32732a",
"type": "tarball",
"url": "https://git.clan.lol/api/v1/repos/clan/clan-core/archive/3dc0221ca09033599fe98055e9bbc81bdf32732a.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://git.clan.lol/clan/clan-core/archive/26.05.tar.gz"
}
},
"data-mesher": {
"inputs": {
"flake-parts": [
"clan-core",
"flake-parts"
],
"nixpkgs": [
"clan-core",
"nixpkgs"
],
"treefmt-nix": [
"clan-core",
"treefmt-nix"
]
},
"locked": {
"lastModified": 1778718524,
"narHash": "sha256-pXLoI6Ax0EnUK6r34UM1vibVC7CfTu6j72R2692ZzPs=",
"rev": "12c552ad547d87254f33f33bddd1a2cdbeac754d",
"type": "tarball",
"url": "https://git.clan.lol/api/v1/repos/clan/data-mesher/archive/12c552ad547d87254f33f33bddd1a2cdbeac754d.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://git.clan.lol/clan/data-mesher/archive/main.tar.gz"
}
},
"disko": { "disko": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -51,9 +108,30 @@
"type": "github" "type": "github"
} }
}, },
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"clan-core",
"nixpkgs"
]
},
"locked": {
"lastModified": 1778716662,
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": { "flake-utils": {
"inputs": { "inputs": {
"systems": "systems" "systems": "systems_2"
}, },
"locked": { "locked": {
"lastModified": 1694529238, "lastModified": 1694529238,
@@ -109,6 +187,40 @@
"type": "github" "type": "github"
} }
}, },
"nix-darwin": {
"inputs": {
"nixpkgs": [
"clan-core",
"nixpkgs"
]
},
"locked": {
"lastModified": 1779036909,
"narHash": "sha256-zXcwYQGCT6pzinK+1dBB2ekTVtfxGZAapb3Evdcu4fY=",
"owner": "nix-darwin",
"repo": "nix-darwin",
"rev": "56c666e108467d87d13508936aade6d567f2a501",
"type": "github"
},
"original": {
"owner": "nix-darwin",
"repo": "nix-darwin",
"type": "github"
}
},
"nix-select": {
"locked": {
"lastModified": 1763303120,
"narHash": "sha256-yxcNOha7Cfv2nhVpz9ZXSNKk0R7wt4AiBklJ8D24rVg=",
"rev": "3d1e3860bef36857a01a2ddecba7cdb0a14c35a9",
"type": "tarball",
"url": "https://git.clan.lol/api/v1/repos/clan/nix-select/archive/3d1e3860bef36857a01a2ddecba7cdb0a14c35a9.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://git.clan.lol/clan/nix-select/archive/main.tar.gz"
}
},
"nixos-conf-editor": { "nixos-conf-editor": {
"inputs": { "inputs": {
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
@@ -163,6 +275,7 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"clan-core": "clan-core",
"disko": "disko", "disko": "disko",
"home-manager": "home-manager", "home-manager": "home-manager",
"nixos-conf-editor": "nixos-conf-editor", "nixos-conf-editor": "nixos-conf-editor",
@@ -214,6 +327,22 @@
} }
}, },
"systems": { "systems": {
"locked": {
"lastModified": 1774449309,
"narHash": "sha256-brhZ8DmuGtzkCYHJg4HEd602amKm89Y9ytsFZ5uWD1w=",
"owner": "nix-systems",
"repo": "default",
"rev": "c29398b59d2048c4ab79345812849c9bd15e9150",
"type": "github"
},
"original": {
"owner": "nix-systems",
"ref": "future-26.11",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": { "locked": {
"lastModified": 1681028828, "lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
@@ -227,6 +356,27 @@
"repo": "default", "repo": "default",
"type": "github" "type": "github"
} }
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"clan-core",
"nixpkgs"
]
},
"locked": {
"lastModified": 1780220602,
"narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "db947814a175b7ca6ded66e21383d938df01c227",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",
+32 -3
View File
@@ -16,6 +16,19 @@
url = "github:Mic92/sops-nix"; url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
clan-core = {
url = "https://git.clan.lol/clan/clan-core/archive/26.05.tar.gz";
# Deduplicate modules: clan-core bundles its own disko and sops-nix
# (both imported by nixosModules.clanCore). Without follows, we'd get
# two different versions of each, and disko's _module.args.diskoLib
# unique option would conflict. With follows, clan-core uses the same
# store paths as us, so NixOS deduplicates the imports.
inputs = {
nixpkgs.follows = "nixpkgs";
disko.follows = "disko";
sops-nix.follows = "sops-nix";
};
};
}; };
outputs = { self, nixpkgs, nixos-conf-editor, home-manager, sops-nix, ... } @ inputs: outputs = { self, nixpkgs, nixos-conf-editor, home-manager, sops-nix, ... } @ inputs:
@@ -41,6 +54,22 @@
modules = [ modules = [
inputs.disko.nixosModules.disko inputs.disko.nixosModules.disko
sops-nix.nixosModules.sops sops-nix.nixosModules.sops
inputs.clan-core.nixosModules.clanCore
{
# Required clan settings. directory is the flake root (where
# vars/ and sops/ directories live); machine.name is the flake
# target name (matches what clan vars generate uses as the key
# under vars/per-machine/). enableRecommendedDefaults = false
# is mandatory: without it, clan unconditionally enables
# networking.useNetworkd, adds packages, and tweaks nix settings
# -- none of which belong here.
clan.core = {
settings.directory = self;
settings.machine.name = flakeTarget;
enableRecommendedDefaults = false;
};
}
./modules/clan/ssh-host-key.nix
./modules/common/configuration.nix ./modules/common/configuration.nix
./modules/platforms/${platform}.nix ./modules/platforms/${platform}.nix
./modules/build-types/${buildType}.nix ./modules/build-types/${buildType}.nix
@@ -96,9 +125,9 @@
proxmox-pxe-boot = mkTarget { platform = "proxmox"; buildType = "pxe-boot"; hostPath = ./hosts/pxe-boot/host.nix; }; proxmox-pxe-boot = mkTarget { platform = "proxmox"; buildType = "pxe-boot"; hostPath = ./hosts/pxe-boot/host.nix; };
lxc-pxe-boot = mkTarget { platform = "lxc"; buildType = "pxe-boot"; hostPath = ./hosts/pxe-boot/host.nix; }; lxc-pxe-boot = mkTarget { platform = "lxc"; buildType = "pxe-boot"; hostPath = ./hosts/pxe-boot/host.nix; };
linode-tailscale-subnet-router = mkTarget { platform = "linode"; buildType = "tailscale-subnet-router"; hostPath = ./hosts/tailscale-subnet-router/host.nix; }; linode-tailscale-router = mkTarget { platform = "linode"; buildType = "tailscale-router"; hostPath = ./hosts/tailscale-router/host.nix; };
proxmox-tailscale-subnet-router = mkTarget { platform = "proxmox"; buildType = "tailscale-subnet-router"; hostPath = ./hosts/tailscale-subnet-router/host.nix; }; proxmox-tailscale-router = mkTarget { platform = "proxmox"; buildType = "tailscale-router"; hostPath = ./hosts/tailscale-router/host.nix; };
lxc-tailscale-subnet-router = mkTarget { platform = "lxc"; buildType = "tailscale-subnet-router"; hostPath = ./hosts/tailscale-subnet-router/host.nix; }; lxc-tailscale-router = mkTarget { platform = "lxc"; buildType = "tailscale-router"; hostPath = ./hosts/tailscale-router/host.nix; };
lxc-tor-relay = mkTarget { platform = "lxc"; buildType = "tor-relay"; hostPath = ./hosts/tor-relay/host.nix; }; lxc-tor-relay = mkTarget { platform = "lxc"; buildType = "tor-relay"; hostPath = ./hosts/tor-relay/host.nix; };
}; };
+1 -1
View File
@@ -27,7 +27,7 @@
# Optional: set environment vars # Optional: set environment vars
sessionVariables = { sessionVariables = {
EDITOR = "vim"; EDITOR = "vim";
SOPS_AGE_KEY_FILE = "/home/nixos/Nextcloud/Filing Cabinet/keys/nixos-sops-age-key-txt"; SOPS_AGE_KEY_FILE = "~/.config/sops/age/keys.txt";
}; };
file = { file = {
+42
View File
@@ -0,0 +1,42 @@
{ vars, ... }:
{
imports = [
../tailscale/subnet-router.nix
];
# "server", not "both": this build type advertises LAN subnet routes but
# doesn't use another tailscale exit node itself, so it doesn't need the
# "client"-side loose reverse-path filtering that "both" would also enable.
# Deliberately kept explicit here (not just relying on subnet-router.nix's
# own setting) so the intent is clear at the build-type level.
services.tailscale.useRoutingFeatures = "server";
# Advertise the LAN subnet so Tailscale peers can route back to LAN machines.
# Must also be approved in the Tailscale admin console (Machines → Edit route settings).
services.tailscale.extraUpFlags = [ "--advertise-routes=${vars.lanCidr}" ];
networking.firewall = {
# Forwarded subnet-router traffic arrives on tailscale0 already
# tailscale-authenticated -- the firewall's normal per-port allow-list
# would otherwise drop it. Standard NixOS/Tailscale subnet-router guidance.
trustedInterfaces = [ "tailscale0" ];
# SNAT LAN traffic going into Tailscale so the remote peer sees it as
# coming from this router's Tailscale IP rather than a raw LAN IP.
# Without this, Tailscale drops forwarded packets whose source is not a
# recognised Tailscale address.
#
# We target POSTROUTING directly (always-existing built-in chain) rather
# than nixos-nat-post: extraCommands runs after the old nixos-nat-post is
# deleted but before the new one is created, so -A nixos-nat-post silently
# fails. The -C check makes the rule idempotent across firewall reloads.
extraCommands = ''
iptables -t nat -C POSTROUTING -s ${vars.lanCidr} -o tailscale0 -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -s ${vars.lanCidr} -o tailscale0 -j MASQUERADE
'';
extraStopCommands = ''
iptables -t nat -D POSTROUTING -s ${vars.lanCidr} -o tailscale0 -j MASQUERADE 2>/dev/null || true
'';
};
}
@@ -1,19 +0,0 @@
{ ... }:
{
imports = [
../tailscale/subnet-router.nix
];
# "server", not "both": this build type advertises LAN subnet routes but
# doesn't use another tailscale exit node itself, so it doesn't need the
# "client"-side loose reverse-path filtering that "both" would also enable.
# Deliberately kept explicit here (not just relying on subnet-router.nix's
# own setting) so the intent is clear at the build-type level.
services.tailscale.useRoutingFeatures = "server";
# Forwarded subnet-router traffic arrives on tailscale0 already
# tailscale-authenticated -- the firewall's normal per-port allow-list
# would otherwise drop it. Standard NixOS/Tailscale subnet-router guidance.
networking.firewall.trustedInterfaces = [ "tailscale0" ];
}
+30
View File
@@ -0,0 +1,30 @@
{ pkgs, ... }: {
# Defines the SSH host key as a clan vars generator so that:
# - `clan vars generate <target>` creates and encrypts the key pair
# - The private key lives at vars/per-machine/<target>/openssh/ssh_host_ed25519_key/secret
# (sops binary-encrypted, admin-key-only; decrypted by the build script)
# - The public key lives at vars/per-machine/<target>/openssh/ssh_host_ed25519_key.pub/value
# (plaintext; used by sync-host-keys.sh to derive the sops age fingerprint)
#
# neededFor = "activation" means clan's deployment tool would upload this
# before running nixos-rebuild/nixos-install (for VM/baremetal via
# nixos-anywhere). For lxc-* hosts, the build script bakes it into the
# tarball directly via NIXOS_HOST_KEYS_DIR -- the neededFor value here
# simply ensures it is NOT mapped to sops.secrets (which would try to
# decrypt it at runtime as a regular service secret, which is wrong: the
# SSH host key reaches the container via the tarball, not sops).
clan.core.vars.generators.openssh = {
files."ssh_host_ed25519_key" = {
secret = true;
neededFor = "activation";
};
files."ssh_host_ed25519_key.pub" = {
secret = false;
neededFor = "activation";
};
runtimeInputs = [ pkgs.openssh ];
script = ''
ssh-keygen -t ed25519 -N "" -C "" -f "$out/ssh_host_ed25519_key"
'';
};
}
+12 -4
View File
@@ -61,13 +61,20 @@
!include ${config.sops.templates."nix-github-token.conf".path} !include ${config.sops.templates."nix-github-token.conf".path}
''; '';
#Set root password users = {
users.users.root = { # With mutableUsers = false, update-users-groups.pl enforces hashedPasswordFile
# on every activation regardless of whether the account already exists in
# /etc/shadow. The default (true) only applies hashedPasswordFile to newly-
# created accounts — which means a freshly-built proxmox disk image (where
# activation runs without a usable sops key, so both accounts land in shadow
# with !) will never have its passwords fixed by subsequent boots.
mutableUsers = false;
users.root = {
hashedPasswordFile = config.sops.secrets."root-hashedPassword".path; hashedPasswordFile = config.sops.secrets."root-hashedPassword".path;
}; };
# Define a user account. Don't forget to set a password with passwd. users.${vars.primaryUser} = {
users.users.${vars.primaryUser} = {
isNormalUser = true; isNormalUser = true;
extraGroups = [ "wheel" ]; # Enable sudo for the user. extraGroups = [ "wheel" ]; # Enable sudo for the user.
packages = with pkgs; [ packages = with pkgs; [
@@ -80,6 +87,7 @@
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGygkCljN6uKpdJbHTOQtn8ZnH+wKXDLAwrDFbLrE/65 nixos@nixos" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGygkCljN6uKpdJbHTOQtn8ZnH+wKXDLAwrDFbLrE/65 nixos@nixos"
]; ];
}; };
};
# Enable the OpenSSH daemon. # Enable the OpenSSH daemon.
+1 -1
View File
@@ -3,7 +3,7 @@
{ {
nix.settings = { nix.settings = {
substituters = [ substituters = [
"http://${vars.nixCacheHost}" "http://${vars.nixCacheHost}.${vars.homeDomain}"
"https://cache.nixos.org/" "https://cache.nixos.org/"
]; ];
trusted-public-keys = [ trusted-public-keys = [
+4 -4
View File
@@ -8,12 +8,12 @@
# dedicated keypair). If this host doesn't have one yet: # dedicated keypair). If this host doesn't have one yet:
# sudo -u root ssh-keygen -t ed25519 -N '' -f /root/.ssh/id_ed25519 # sudo -u root ssh-keygen -t ed25519 -N '' -f /root/.ssh/id_ed25519
# # then add its .pub to vars.remoteBuilderAuthorizedKeys and rebuild nix-cache # # then add its .pub to vars.remoteBuilderAuthorizedKeys and rebuild nix-cache
# sudo ssh -i /root/.ssh/id_ed25519 nixremote@nix-cache nix-store --version # sudo ssh -i /root/.ssh/id_ed25519 nixremote@nix-cache.sweet.home nix-store --version
# Trust nix-cache's SSH host key declaratively so the nix-daemon (root) # Trust nix-cache's SSH host key declaratively so the nix-daemon (root)
# can connect the first time without a manual ssh-keyscan/known_hosts # can connect the first time without a manual ssh-keyscan/known_hosts
# step on every new client. # step on every new client.
programs.ssh.knownHosts.${vars.nixCacheHost} = { programs.ssh.knownHosts."${vars.nixCacheHost}.${vars.homeDomain}" = {
hostNames = [ vars.nixCacheHost ]; hostNames = [ "${vars.nixCacheHost}.${vars.homeDomain}" ];
publicKey = vars.nixCacheHostKey; publicKey = vars.nixCacheHostKey;
}; };
@@ -22,7 +22,7 @@
buildMachines = [ buildMachines = [
{ {
hostName = vars.nixCacheHost; hostName = "${vars.nixCacheHost}.${vars.homeDomain}";
sshUser = vars.remoteBuilderUser; sshUser = vars.remoteBuilderUser;
sshKey = "/root/.ssh/id_ed25519"; sshKey = "/root/.ssh/id_ed25519";
inherit (pkgs.stdenv.hostPlatform) system; inherit (pkgs.stdenv.hostPlatform) system;
+1 -1
View File
@@ -20,7 +20,7 @@
nginx = { nginx = {
enable = true; enable = true;
recommendedProxySettings = true; recommendedProxySettings = true;
virtualHosts.${vars.nixCacheHost} = { virtualHosts."${vars.nixCacheHost}.${vars.homeDomain}" = {
locations."/" = { locations."/" = {
proxyPass = "http://${config.services.nix-serve.bindAddress}:${toString config.services.nix-serve.port}"; proxyPass = "http://${config.services.nix-serve.bindAddress}:${toString config.services.nix-serve.port}";
}; };
+62 -13
View File
@@ -114,22 +114,25 @@ in
# /etc/ssh/ssh_host_ed25519_key; deletion cascades into every sops secret # /etc/ssh/ssh_host_ed25519_key; deletion cascades into every sops secret
# failing with "Error getting data key: 0 successful groups required, got 0". # failing with "Error getting data key: 0 successful groups required, got 0".
# #
# Fix: two activation scripts that bracket the etc step. # Fix: activation scripts that bracket the etc step, with explicit deps
# preserveSshHostKey — no deps, runs before etc — saves the live key to # to enforce the correct ordering. Without deps the topological sort places
# /run (tmpfs) before etc can delete it. # preserveSshHostKey AFTER etc (confirmed live on a deployed lxc-tor-relay:
# restoreSshHostKey — deps=[etc], runs after etc — reinstalls the key via # position 7 vs etc's position 5) -- the key is already gone by the time it
# `install` (atomic, sets mode) if etc removed it. # tries to save it. The etc/setupSecrets entries ADD to existing deps
# The resulting file is not registered in environment.etc # (types.listOf concatenates across module definitions).
# for either the previous or current generation, so system.activationScripts = {
# subsequent rebuilds leave it alone permanently. # Saves the live key to /run before etc can delete it.
system.activationScripts.preserveSshHostKey = '' preserveSshHostKey = ''
if [ -f /etc/ssh/ssh_host_ed25519_key ]; then if [ -f /etc/ssh/ssh_host_ed25519_key ]; then
cp /etc/ssh/ssh_host_ed25519_key /run/sshd-host-key-preserve.tmp cp /etc/ssh/ssh_host_ed25519_key /run/sshd-host-key-preserve.tmp
cp /etc/ssh/ssh_host_ed25519_key.pub /run/sshd-host-key-preserve.pub.tmp cp /etc/ssh/ssh_host_ed25519_key.pub /run/sshd-host-key-preserve.pub.tmp
fi fi
''; '';
system.activationScripts.restoreSshHostKey = { # Reinstalls the key after etc runs if it was removed as "obsolete".
# The resulting file is not registered in environment.etc for either
# generation, so subsequent rebuilds leave it alone permanently.
restoreSshHostKey = {
deps = [ "etc" ]; deps = [ "etc" ];
text = '' text = ''
if [ ! -f /etc/ssh/ssh_host_ed25519_key ] && [ -f /run/sshd-host-key-preserve.tmp ]; then if [ ! -f /etc/ssh/ssh_host_ed25519_key ] && [ -f /run/sshd-host-key-preserve.tmp ]; then
@@ -140,15 +143,24 @@ in
''; '';
}; };
# Force etc to wait until the key is saved, and sops to wait until the
# key is restored. Without these the topological sort breaks the chain.
etc = { deps = [ "preserveSshHostKey" ]; };
setupSecrets = { deps = [ "restoreSshHostKey" ]; };
};
# virtualisation/proxmox-lxc.nix (imported above) registers the Nix # virtualisation/proxmox-lxc.nix (imported above) registers the Nix
# store DB via a systemd service (register-nix-paths) -- it never runs # store DB via a systemd service (register-nix-paths) -- it never runs
# an activation script at all. Confirmed live this means neither # an activation script at all. Confirmed live this means neither
# sops-nix's "for users" secrets (password hashes -- installed by the # sops-nix's "for users" secrets (password hashes -- installed by the
# activation script itself, not a systemd service, since they need to # activation script itself, not a systemd service, since they need to
# exist *before* user creation) nor the user-creation step that # exist *before* user creation) nor the user-creation step that
# consumes them ever run on a real lxc-* boot. Regular secrets # consumes them ever run on a real lxc-* boot. In this config sops-nix
# (nix-serve's key, beszel's token, etc.) work anyway because sops-nix # does NOT generate its own boot-time service (confirmed live: no
# provides its own systemd service for those. # sops-nix.service in systemctl list-unit-files on a deployed
# lxc-tor-relay container); /run/secrets is a tmpfs cleared on every
# reboot, so secrets must be reinstalled on each non-first boot by
# nixos-lxc-sops-reinstall (below).
# #
# A systemd service, not boot.postBootCommands: tried that first (it's # A systemd service, not boot.postBootCommands: tried that first (it's
# a genuine, generally-invoked hook -- nixos/modules/system/boot/stage-2-init.sh, # a genuine, generally-invoked hook -- nixos/modules/system/boot/stage-2-init.sh,
@@ -199,4 +211,41 @@ in
touch /var/lib/nixos-lxc-first-boot-activated touch /var/lib/nixos-lxc-first-boot-activated
''; '';
}; };
# Reinstalls sops secrets on every non-first boot. /run/secrets is a
# tmpfs that is cleared on each reboot; without this service, secrets
# are permanently absent after the first boot and every service that
# reads from /run/secrets fails on start.
#
# wantedBy/before network.target: switch-to-configuration test requires
# D-Bus to restart systemd targets after running activation scripts. D-Bus
# is available once basic.target completes (the default After=basic.target
# that DefaultDependencies would otherwise add). Placing the service before
# network.target ensures secrets are ready before any network-dependent
# service (including beszel-agent and nix-serve) starts, while running late
# enough that D-Bus is already up.
#
# ConditionPathExists=... skips this service on the genuine first boot
# (the marker doesn't exist yet); nixos-lxc-first-boot-activate handles
# that case. On every subsequent boot the condition passes and secrets
# are reinstalled before user services start.
#
# SuccessExitStatus=11: switch-to-configuration exits 11 when it cannot
# acquire the activation lock (another switch is already in progress).
# During a nixos-rebuild switch the activation already installs secrets, so
# treating the lock-held case as success is correct.
systemd.services.nixos-lxc-sops-reinstall = {
description = "Reinstall sops secrets on each non-first boot (LXC, /run is tmpfs)";
wantedBy = [ "network.target" ];
before = [ "network.target" ];
unitConfig.ConditionPathExists = "/var/lib/nixos-lxc-first-boot-activated";
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
SuccessExitStatus = "11";
};
script = ''
/run/current-system/bin/switch-to-configuration test
'';
};
} }
+73 -1
View File
@@ -1,9 +1,81 @@
{ ... }: { lib, flakeTarget, ... }:
let
# Bakes this exact flake target's pre-generated SSH host key straight
# into /etc/ssh/ -- mirrors lxc.nix's builtins.getEnv pattern (impure
# and empty under normal `nix build`/`nix eval`, so this is a no-op
# unless explicitly opted into with NIXOS_HOST_KEYS_DIR=... --impure).
#
# Unlike --pre-format-files (which places files on the QEMU builder VM's
# rootfs, not the target disk), embedding via environment.etc here means
# nixos-install's own activation step installs the key onto the target
# disk. sshd-keygen then finds it already present and skips generation,
# so the disk image boots with the clan-registered key and sops can
# decrypt on first boot.
#
# Without this, nixos-install's sshd-keygen activation generates a fresh
# key (unregistered in .sops.yaml), sops decryption fails permanently,
# and password hashes are never applied -- confirmed live: passwords
# stayed '!' even with mutableUsers = false because hashedPasswordFile
# pointed to a path that sops never wrote.
hostKeysDirStr = builtins.getEnv "NIXOS_HOST_KEYS_DIR";
hasHostKeysDir = hostKeysDirStr != "" && builtins.pathExists hostKeysDirStr;
hostKeysDir = /. + hostKeysDirStr;
privKeyFile = hostKeysDir + "/${flakeTarget}_ssh_host_ed25519_key";
pubKeyFile = hostKeysDir + "/${flakeTarget}_ssh_host_ed25519_key.pub";
hasKeyForThisTarget =
hasHostKeysDir
&& builtins.pathExists privKeyFile
&& builtins.pathExists pubKeyFile;
in
{ {
imports = [ imports = [
../hardware-configuration/vm/proxmox.nix ../hardware-configuration/vm/proxmox.nix
../boot/efi.nix ../boot/efi.nix
../disko/proxmox.nix ../disko/proxmox.nix
]; ];
environment.etc = lib.mkIf hasKeyForThisTarget {
"ssh/ssh_host_ed25519_key" = {
source = privKeyFile;
mode = "0600";
};
"ssh/ssh_host_ed25519_key.pub" = {
source = pubKeyFile;
mode = "0644";
};
};
# NixOS's etc activation removes any /etc file that was in the previous
# generation's environment.etc but is absent from the current one. Since
# the SSH key is only in environment.etc during the --impure build (when
# NIXOS_HOST_KEYS_DIR is set), normal rebuilds would remove it as
# "obsolete". These scripts mirror lxc.nix's approach: save the live key
# before etc runs, restore it after. Without the explicit deps, the
# topological sort places preserveSshHostKey after etc (confirmed live on
# lxc-tor-relay: position 7 vs etc's position 5), so the key is gone
# before it can be saved.
system.activationScripts = {
preserveSshHostKey = ''
if [ -f /etc/ssh/ssh_host_ed25519_key ]; then
cp /etc/ssh/ssh_host_ed25519_key /run/sshd-host-key-preserve.tmp
cp /etc/ssh/ssh_host_ed25519_key.pub /run/sshd-host-key-preserve.pub.tmp
fi
'';
restoreSshHostKey = {
deps = [ "etc" ];
text = ''
if [ ! -f /etc/ssh/ssh_host_ed25519_key ] && [ -f /run/sshd-host-key-preserve.tmp ]; then
install -m 0600 /run/sshd-host-key-preserve.tmp /etc/ssh/ssh_host_ed25519_key
install -m 0644 /run/sshd-host-key-preserve.pub.tmp /etc/ssh/ssh_host_ed25519_key.pub
fi
rm -f /run/sshd-host-key-preserve.tmp /run/sshd-host-key-preserve.pub.tmp
'';
};
etc = { deps = [ "preserveSshHostKey" ]; };
setupSecrets = { deps = [ "restoreSshHostKey" ]; };
};
} }
+21 -1
View File
@@ -1,4 +1,4 @@
_: { pkgs, ... }:
{ {
imports = [ ./enable-service.nix ]; imports = [ ./enable-service.nix ];
@@ -12,4 +12,24 @@ _:
# instead of relaying through DERP. # instead of relaying through DERP.
openFirewall = true; openFirewall = true;
}; };
# Tailscale recommends these ethtool flags on the uplink interface to get
# full UDP GRO throughput on subnet routers (https://tailscale.com/s/ethtool-config-udp-gro).
# The interface is derived from the default route so it works regardless of
# what the NIC is named on a given host.
systemd.services.tailscale-udp-gro = {
description = "Enable UDP GRO forwarding on uplink for Tailscale subnet router";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
path = [ pkgs.ethtool pkgs.iproute2 ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = pkgs.writeShellScript "tailscale-udp-gro" ''
NETDEV=$(ip -o route get 8.8.8.8 | cut -f 5 -d " ")
ethtool -K "$NETDEV" rx-udp-gro-forwarding on rx-gro-list off
'';
};
};
} }
+106
View File
@@ -0,0 +1,106 @@
#!/usr/bin/env bash
# Clan vars helpers: manage SSH host keys stored as clan vars (sops-encrypted
# binary files under vars/per-machine/<target>/openssh/) instead of the
# gitignored host-keys/ directory.
#
# Layout (per clan's convention):
# vars/per-machine/<target>/openssh/ssh_host_ed25519_key/secret -- sops binary (admin-encrypted)
# vars/per-machine/<target>/openssh/ssh_host_ed25519_key.pub/value -- plaintext SSH pubkey
#
# Sourced by create-proxmox-resource.sh and sync-host-keys.sh.
# Depends on sops-age.sh and ssh-host-keys.sh being sourced first (for
# sops_yaml_admin_pubkey, ssh_pubkey_to_age, and NIX_OPTS).
if ! declare -p NIX_OPTS >/dev/null 2>&1; then
declare -a NIX_OPTS=()
fi
# clan_ssh_key_exists <target> <repo_root>
# Returns 0 if clan vars hold a SSH host key for <target>, 1 otherwise.
clan_ssh_key_exists() {
local target="$1" repo_root="$2"
[[ -f "${repo_root}/vars/per-machine/${target}/openssh/ssh_host_ed25519_key/secret" ]]
}
# clan_ssh_pubkey_path <target> <repo_root>
# Prints the path to the plaintext SSH public key value file.
clan_ssh_pubkey_path() {
local target="$1" repo_root="$2"
echo "${repo_root}/vars/per-machine/${target}/openssh/ssh_host_ed25519_key.pub/value"
}
# clan_decrypt_ssh_key <target> <repo_root> <dest_dir>
# Decrypts the sops-encrypted SSH host private key for <target> into <dest_dir>,
# naming it <target>_ssh_host_ed25519_key (to match NIXOS_HOST_KEYS_DIR
# conventions that lxc.nix and the disko build already expect). Also copies
# the plaintext public key. The caller is responsible for protecting and
# cleaning up <dest_dir>.
clan_decrypt_ssh_key() {
local target="$1" repo_root="$2" dest_dir="$3"
local secret="${repo_root}/vars/per-machine/${target}/openssh/ssh_host_ed25519_key/secret"
local pubval="${repo_root}/vars/per-machine/${target}/openssh/ssh_host_ed25519_key.pub/value"
local dest_priv="${dest_dir}/${target}_ssh_host_ed25519_key"
local dest_pub="${dest_dir}/${target}_ssh_host_ed25519_key.pub"
nix-shell "${NIX_OPTS[@]}" -p sops --run \
"sops -d --output-type binary '${secret}'" > "$dest_priv"
chmod 0600 "$dest_priv"
cp "$pubval" "$dest_pub"
}
# clan_generate_ssh_key <target> <repo_root>
# Generates a new SSH host key pair and stores it in clan vars format:
# - private key: sops binary-encrypted for the admin age key
# - public key: plaintext value file
# Idempotent: if the secret already exists, prints a note and returns 0.
# Requires sops_yaml_admin_pubkey (from sops-age.sh) to be available.
clan_generate_ssh_key() {
local target="$1" repo_root="$2"
local var_base="${repo_root}/vars/per-machine/${target}/openssh"
local secret_dir="${var_base}/ssh_host_ed25519_key"
local pubval_dir="${var_base}/ssh_host_ed25519_key.pub"
if [[ -f "${secret_dir}/secret" ]]; then
echo "Clan SSH host key for ${target} already exists -- skipping generation."
return 0
fi
# Resolve admin age public key from .sops.yaml
local admin_pubkey
admin_pubkey="$(sops_yaml_admin_pubkey "${repo_root}/.sops.yaml")"
if [[ -z "$admin_pubkey" ]]; then
echo "ERROR: Could not find &admin age key in ${repo_root}/.sops.yaml" >&2
return 1
fi
# Generate the SSH key pair in a secure temp directory
local tmpdir
tmpdir="$(mktemp -d)"
local priv_tmp="${tmpdir}/ssh_host_ed25519_key"
# shellcheck disable=SC2064
trap "rm -rf '${tmpdir}'" RETURN
nix-shell "${NIX_OPTS[@]}" -p openssh --run \
"ssh-keygen -t ed25519 -N '' -C '${target}' -f '${priv_tmp}'" >/dev/null
# Create a minimal sops config that uses only the admin age key -- this
# prevents sops from merging in ALL recipients from .sops.yaml (which
# would unnecessarily encrypt for every host's key, not just admin).
local sops_cfg="${tmpdir}/sops-config.json"
printf '{"creation_rules":[{"key_groups":[{"age":["%s"]}]}]}\n' \
"$admin_pubkey" > "$sops_cfg"
# Encrypt the private key in sops binary format (admin-only recipient)
mkdir -p "$secret_dir" "$pubval_dir"
nix-shell "${NIX_OPTS[@]}" -p sops --run \
"sops -e --config '${sops_cfg}' --input-type binary '${priv_tmp}'" \
> "${secret_dir}/secret"
# Store the public key as a plaintext value file
cp "${priv_tmp}.pub" "${pubval_dir}/value"
echo "Generated and stored clan SSH host key for ${target}."
echo " Private key: ${secret_dir}/secret (sops binary, admin-key encrypted)"
echo " Public key: ${pubval_dir}/value"
}
@@ -38,7 +38,7 @@
set -euo pipefail set -euo pipefail
: "${NIX_CACHE_HOST:=nix-cache}" : "${NIX_CACHE_HOST:=nix-cache}"
: "${NIX_CACHE_HOST_KEY:=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPeWgMsdaiz4axT/deFc1+0B5bN+GX/NOeW9bbQ0c/IT lxc-nix-cache}" : "${NIX_CACHE_HOST_KEY:=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICuHUxGNH6ei3BZD+EfZs3l4X8uJNcjQiOsM/G4yo4O/ lxc-nix-cache}"
: "${REMOTE_BUILDER_USER:=nixremote}" : "${REMOTE_BUILDER_USER:=nixremote}"
CACHE_PUB_KEY="cache.local-1:usoWYanY3Kpq2+kDIS2nhWoLZiRxanmdysdzqCFBHW4=" CACHE_PUB_KEY="cache.local-1:usoWYanY3Kpq2+kDIS2nhWoLZiRxanmdysdzqCFBHW4="
+153 -42
View File
@@ -8,9 +8,12 @@
# script -- there's no multi-gigabyte image to transfer afterward. The first # script -- there's no multi-gigabyte image to transfer afterward. The first
# time a node doesn't have that repo path yet, it's bootstrapped: cloned from # time a node doesn't have that repo path yet, it's bootstrapped: cloned from
# this checkout's own `origin` remote, then scripts/codex-setup.sh installs # this checkout's own `origin` remote, then scripts/codex-setup.sh installs
# the build tooling (Nix, etc.). Every run after that just `git pull`s it and # the build tooling (Nix, etc.). Every run after that just `git pull`s it.
# copies over the locally-managed host-keys/ (gitignored, so a git pull # SSH host keys are stored as clan vars (vars/per-machine/<target>/openssh/,
# alone wouldn't carry it) before building. # committed and sops-encrypted) -- the script decrypts them locally and
# copies only the two files for this target to the node's host-keys/ before
# building. A target with no clan var is an error (generate one first with
# scripts/secrets/sync-host-keys.sh <target>).
# #
# --node (default: $PROXMOX_HOST, see scripts/env.sh) picks which of the two # --node (default: $PROXMOX_HOST, see scripts/env.sh) picks which of the two
# LAN Proxmox nodes this runs against: production, pve1.sweet.home # LAN Proxmox nodes this runs against: production, pve1.sweet.home
@@ -59,6 +62,10 @@ source "${repo_root}/scripts/env.sh"
source "${repo_root}/scripts/lib/nix-eval.sh" source "${repo_root}/scripts/lib/nix-eval.sh"
# shellcheck source=../lib/confirm.sh # shellcheck source=../lib/confirm.sh
source "${repo_root}/scripts/lib/confirm.sh" source "${repo_root}/scripts/lib/confirm.sh"
# shellcheck source=../lib/sops-age.sh
source "${repo_root}/scripts/lib/sops-age.sh"
# shellcheck source=../lib/clan-vars.sh
source "${repo_root}/scripts/lib/clan-vars.sh"
sync_keys="${repo_root}/scripts/secrets/sync-host-keys.sh" sync_keys="${repo_root}/scripts/secrets/sync-host-keys.sh"
@@ -295,6 +302,12 @@ platform_prefix="lxc"
[[ -z "$cores" ]] && cores="$PROXMOX_DEFAULT_CORES" [[ -z "$cores" ]] && cores="$PROXMOX_DEFAULT_CORES"
[[ -z "$memory" ]] && memory="$PROXMOX_DEFAULT_MEMORY_MB" [[ -z "$memory" ]] && memory="$PROXMOX_DEFAULT_MEMORY_MB"
if [[ "$type" == "vm" && -n "$disk_size" ]]; then
echo "WARNING: --disk-size is LXC-only for create mode and is ignored for VMs." >&2
echo " VM disk size comes from proxmoxImageSize in variables.nix (currently ${disk_size}G was requested)." >&2
echo " To expand after creation, use: --modify --vmid <n> --grow-disk <GB>" >&2
fi
# --- discover / resolve the flake target from --host -------------------- # --- discover / resolve the flake target from --host --------------------
# Emits "<target>\t<hostName>" pairs for every ${platform_prefix}-* flake # Emits "<target>\t<hostName>" pairs for every ${platform_prefix}-* flake
# target -- the one source both --list and the --host lookup below read # target -- the one source both --list and the --host lookup below read
@@ -347,6 +360,14 @@ fi
# feeds straight into the guest's real hostname) disagree with host.nix. # feeds straight into the guest's real hostname) disagree with host.nix.
[[ -z "$name" ]] && name="$host" [[ -z "$name" ]] && name="$host"
# For VM builds: the diskoImagesScript (run via QEMU on the node) writes the
# raw disk image as <hostname>.raw into the CWD it was called from (the remote
# repo dir), not to /var/lib/vz/import/ or anywhere else. Import directly from
# there -- no intermediate mv that can fail crossing filesystem boundaries or
# leave a stale file on error.
vm_built_raw=""
[[ "$type" == "vm" ]] && vm_built_raw="${remote_repo_dir}/${host}.raw"
# --- refuse to duplicate a host that's already live on the node --------- # --- refuse to duplicate a host that's already live on the node ---------
# Queries the node itself (qm/pct's own name/hostname config), not any # Queries the node itself (qm/pct's own name/hostname config), not any
# static list in this repo -- a file can't track whether a resource still # static list in this repo -- a file can't track whether a resource still
@@ -485,12 +506,37 @@ echo "Target: ${flake_target} (host=${host}, type=${type}) -> Proxmox resource '
nix_extra_opts nix_extra_opts
# --- make sure this target has a registered host key -------------------- # --- make sure this target has a registered host key --------------------
# sync-host-keys.sh is idempotent and generates the key (via clan vars) if
# no key exists yet -- the old inline prepare-host-key.sh call is gone.
echo echo
echo "==> Ensuring host key exists and is registered..." echo "==> Ensuring host key exists and is registered..."
sync_args=("$flake_target") sync_args=("$flake_target")
[[ "$dry_run" -eq 1 ]] && sync_args+=(--dry-run) [[ "$dry_run" -eq 1 ]] && sync_args+=(--dry-run)
bash "$sync_keys" "${sync_args[@]}" bash "$sync_keys" "${sync_args[@]}"
# If sync-host-keys.sh changed .sops.yaml, secrets/, or vars/per-machine/,
# those changes must be committed and pushed before the remote `git pull`
# below picks them up -- the PVE node builds from whatever HEAD is checked
# out there, not the local working tree. Uncommitted clan vars or sops
# recipients mean the image builds fine but the host cannot decrypt its
# secrets on first boot. Block until the operator confirms they've pushed.
if [[ "$dry_run" -eq 0 ]]; then
_dirty="$(git -C "$repo_root" status --porcelain -- .sops.yaml secrets/ vars/per-machine/ 2>/dev/null || true)"
if [[ -n "$_dirty" ]]; then
echo
echo "==> COMMIT + PUSH REQUIRED before the remote build can succeed:"
echo " Uncommitted changes in .sops.yaml, secrets/, or vars/per-machine/."
echo " The PVE node builds from the git-tracked flake, so these changes"
echo " must be committed and pushed first -- otherwise the image build will"
echo " succeed but the host cannot decrypt its secrets on first boot."
echo
git -C "$repo_root" status --short -- .sops.yaml secrets/ vars/per-machine/ || true
echo
read -rp " Commit and push those changes, then press Enter to continue (Ctrl-C to abort): "
fi
unset _dirty
fi
# --- VMID: pick one, and refuse to touch anything that already exists --- # --- VMID: pick one, and refuse to touch anything that already exists ---
echo echo
if [[ -z "$vmid" ]]; then if [[ -z "$vmid" ]]; then
@@ -613,21 +659,35 @@ ensure_remote_repo() {
fi fi
} }
# --- sync locally-managed host-keys/ to the node --------------------------- # --- sync host key to the node ---------------------------------------------
# Gitignored (see .gitignore), so `git pull` above never carries it -- both # SSH host keys are stored as clan vars (vars/per-machine/<target>/openssh/).
# build paths need it present as NIXOS_HOST_KEYS_DIR / --pre-format-files # Decrypt locally and scp just the two files for this target to the node's
# input on the node itself now that the build runs there. scp (not rsync, # host-keys/ directory, where the remote build script picks them up via
# not already a dependency anywhere else in this repo) mirrors how this # NIXOS_HOST_KEYS_DIR (LXC) or --pre-format-files (VM). A target with no
# script already transfers the --image case below. # clan var is an error -- generate one first with sync-host-keys.sh.
sync_remote_host_keys() { sync_remote_host_keys() {
echo echo
echo "==> Syncing host-keys/ to ${node}..." echo "==> Syncing host key for ${flake_target} to ${node}..."
if [[ "$dry_run" -eq 1 ]]; then if [[ "$dry_run" -eq 1 ]]; then
echo "[dry-run] would copy ${repo_root}/host-keys/ to ${ssh_target}:${remote_repo_dir}/host-keys/" echo "[dry-run] would decrypt clan SSH key for ${flake_target} and copy to ${ssh_target}:${remote_repo_dir}/host-keys/"
return return
fi fi
if ! clan_ssh_key_exists "$flake_target" "$repo_root"; then
echo "ERROR: no clan SSH key found for ${flake_target}" >&2
echo " (expected: ${repo_root}/vars/per-machine/${flake_target}/openssh/ssh_host_ed25519_key/secret)" >&2
echo " Generate one first: bash scripts/secrets/sync-host-keys.sh ${flake_target}" >&2
exit 1
fi
local tmpdir
tmpdir="$(mktemp -d)"
# shellcheck disable=SC2064
trap "rm -rf '${tmpdir}'" RETURN
echo " Decrypting clan SSH key for ${flake_target}..."
clan_decrypt_ssh_key "$flake_target" "$repo_root" "$tmpdir"
ssh "$ssh_target" "mkdir -p '${remote_repo_dir}/host-keys'" ssh "$ssh_target" "mkdir -p '${remote_repo_dir}/host-keys'"
scp -pr "${repo_root}/host-keys/." "${ssh_target}:${remote_repo_dir}/host-keys/" scp -p "${tmpdir}/${flake_target}_ssh_host_ed25519_key" \
"${tmpdir}/${flake_target}_ssh_host_ed25519_key.pub" \
"${ssh_target}:${remote_repo_dir}/host-keys/"
} }
# --- build (or reuse an image already on the node) ------------------------ # --- build (or reuse an image already on the node) ------------------------
@@ -642,10 +702,14 @@ if [[ -n "$image" ]]; then
elif [[ "$force_rebuild" -eq 1 ]]; then elif [[ "$force_rebuild" -eq 1 ]]; then
echo "--force-rebuild: skipping the existing-image check on ${node}." echo "--force-rebuild: skipping the existing-image check on ${node}."
else else
echo "==> Checking whether ${node} already has ${remote_path}..." # VMs: check for the raw image in the remote repo dir (where disko writes it).
# LXC: check for the tarball in iso_storage (where the LXC build stages it).
_check_path="$remote_path"
[[ "$type" == "vm" ]] && _check_path="$vm_built_raw"
echo "==> Checking whether ${node} already has ${_check_path}..."
if [[ "$dry_run" -eq 1 ]]; then if [[ "$dry_run" -eq 1 ]]; then
echo "[dry-run] would check: ssh ${ssh_target} -- test -f ${remote_path}" echo "[dry-run] would check: ssh ${ssh_target} -- test -f ${_check_path}"
elif ssh "$ssh_target" "test -f '${remote_path}'" 2>/dev/null; then elif ssh "$ssh_target" "test -f '${_check_path}'" 2>/dev/null; then
echo "Found it -- reusing, skipping build (use --force-rebuild to override)." echo "Found it -- reusing, skipping build (use --force-rebuild to override)."
image_already_remote=1 image_already_remote=1
else else
@@ -696,6 +760,12 @@ cd "$repo_dir"
# right after a successful install. # right after a successful install.
. scripts/lib/nix-bootstrap.sh . scripts/lib/nix-bootstrap.sh
ensure_nix_profile ensure_nix_profile
if [[ ! -f "host-keys/${target}_ssh_host_ed25519_key" ]]; then
echo "ERROR: host-keys/${target}_ssh_host_ed25519_key not found in ${repo_dir}." >&2
echo "Generate the key locally (scripts/secrets/sync-host-keys.sh ${target})" >&2
echo "and ensure it was synced here before starting the build." >&2
exit 1
fi
NIXOS_HOST_KEYS_DIR="$(pwd)/host-keys" nix build --impure \ NIXOS_HOST_KEYS_DIR="$(pwd)/host-keys" nix build --impure \
--no-use-registries --no-accept-flake-config "${NIX_OPTS[@]}" \ --no-use-registries --no-accept-flake-config "${NIX_OPTS[@]}" \
".#nixosConfigurations.${target}.config.system.build.tarball" \ ".#nixosConfigurations.${target}.config.system.build.tarball" \
@@ -714,46 +784,60 @@ REMOTE_SCRIPT
fi fi
else else
if [[ "$dry_run" -eq 1 ]]; then if [[ "$dry_run" -eq 1 ]]; then
echo "[dry-run] would build on ${node}: nix build --no-use-registries --no-accept-flake-config${nix_opts_display} \\" echo "[dry-run] would build on ${node}: NIXOS_HOST_KEYS_DIR=\$(pwd)/host-keys nix build --impure --no-use-registries --no-accept-flake-config${nix_opts_display} \\"
echo "[dry-run] .#nixosConfigurations.${flake_target}.config.system.build.diskoImagesScript" echo "[dry-run] .#nixosConfigurations.${flake_target}.config.system.build.diskoImagesScript"
echo "[dry-run] would run: ${sudo_display}./result-${flake_target} \\" echo "[dry-run] would run: ${sudo_display}./result-${flake_target} --build-memory 2048"
echo "[dry-run] --pre-format-files host-keys/${flake_target}_ssh_host_ed25519_key /etc/ssh/ssh_host_ed25519_key \\" echo "[dry-run] image will be at ${vm_built_raw} (imported from there; no mv to /var/lib/vz/import/)"
echo "[dry-run] --pre-format-files host-keys/${flake_target}_ssh_host_ed25519_key.pub /etc/ssh/ssh_host_ed25519_key.pub \\"
echo "[dry-run] --build-memory 2048"
echo "[dry-run] would stage the result at ${remote_path}"
local_image="<built-image>.raw" local_image="<built-image>.raw"
else else
echo "==> Building Disko image for ${flake_target} on ${node}..." echo "==> Building Disko image for ${flake_target} on ${node}..."
# See the LXC branch above for why this is one %q-quoted command # See the LXC branch above for why this is one %q-quoted command
# string rather than separate ssh argv elements. # string rather than separate ssh argv elements.
printf -v remote_cmd 'bash -s -- %q %q %q %q %q %q' \ # $7 = image_name (hostname, the diskoImagesScript's own output filename).
"$remote_repo_dir" "$flake_target" "$remote_dir" "$remote_filename" "$NIX_EXTRA_OPTS" "$sudo_prefix" #
# NIXOS_HOST_KEYS_DIR + --impure: modules/platforms/proxmox.nix reads
# this env var at eval time (like lxc.nix) to embed the clan SSH host
# key in environment.etc. nixos-install's own activation then places the
# key on the target disk, so sshd-keygen finds it already present and
# skips generation. --pre-format-files put the key on the QEMU builder
# VM's rootfs (not the target disk), so sshd-keygen regenerated a fresh
# key -- one not registered in .sops.yaml -- and sops could never decrypt.
printf -v remote_cmd 'bash -s -- %q %q %q %q %q %q %q' \
"$remote_repo_dir" "$flake_target" "$remote_dir" "$remote_filename" "$NIX_EXTRA_OPTS" "$sudo_prefix" "$host"
ssh "$ssh_target" "$remote_cmd" <<'REMOTE_SCRIPT' ssh "$ssh_target" "$remote_cmd" <<'REMOTE_SCRIPT'
set -euo pipefail set -euo pipefail
repo_dir="$1"; target="$2"; dest_dir="$3"; dest_name="$4"; nix_extra_opts_str="$5"; sudo_pfx="$6" repo_dir="$1"; target="$2"; dest_dir="$3"; dest_name="$4"; nix_extra_opts_str="$5"; sudo_pfx="$6"; image_name="$7"
declare -a NIX_OPTS=() declare -a NIX_OPTS=()
[[ -n "$nix_extra_opts_str" ]] && eval "NIX_OPTS=(${nix_extra_opts_str})" [[ -n "$nix_extra_opts_str" ]] && eval "NIX_OPTS=(${nix_extra_opts_str})"
cd "$repo_dir" cd "$repo_dir"
. scripts/lib/nix-bootstrap.sh . scripts/lib/nix-bootstrap.sh
ensure_nix_profile ensure_nix_profile
nix build --no-use-registries --no-accept-flake-config "${NIX_OPTS[@]}" \ if [[ ! -f "host-keys/${target}_ssh_host_ed25519_key" ]]; then
".#nixosConfigurations.${target}.config.system.build.diskoImagesScript" \ echo "ERROR: host-keys/${target}_ssh_host_ed25519_key not found in ${repo_dir}." >&2
--out-link "result-${target}" echo "Generate the key locally (scripts/secrets/sync-host-keys.sh ${target})" >&2
$sudo_pfx "./result-${target}" \ echo "and ensure it was synced here before starting the build." >&2
--pre-format-files "host-keys/${target}_ssh_host_ed25519_key" /etc/ssh/ssh_host_ed25519_key \
--pre-format-files "host-keys/${target}_ssh_host_ed25519_key.pub" /etc/ssh/ssh_host_ed25519_key.pub \
--build-memory 2048
built="$(find . -maxdepth 1 -name '*.raw' -newer "result-${target}" | head -1)"
if [[ -z "$built" ]]; then
echo "ERROR: no .raw image found in ${repo_dir} after build." >&2
exit 1 exit 1
fi fi
$sudo_pfx mkdir -p "$dest_dir" # Build diskoImagesScript with NIXOS_HOST_KEYS_DIR so proxmox.nix embeds the
$sudo_pfx mv "$built" "${dest_dir}/${dest_name}" # clan SSH key in environment.etc (same as lxc.nix). This causes nixos-install
echo "Built and staged: ${dest_dir}/${dest_name}" # to place the key on the target disk, so sshd-keygen finds it and skips
# generation -- the disk image boots with the registered key, sops decrypts.
NIXOS_HOST_KEYS_DIR="$(pwd)/host-keys" nix build --impure \
--no-use-registries --no-accept-flake-config "${NIX_OPTS[@]}" \
".#nixosConfigurations.${target}.config.system.build.diskoImagesScript" \
--out-link "result-${target}"
# Remove any stale .raw from a previous failed build so the post-build check
# below is unambiguous (diskoImagesScript writes to CWD as ${image_name}.raw).
$sudo_pfx rm -f "${image_name}.raw" 2>/dev/null || true
$sudo_pfx "./result-${target}" --build-memory 2048
if [[ ! -f "${image_name}.raw" ]]; then
echo "ERROR: ${image_name}.raw not found in ${repo_dir} after build -- disko/QEMU may have failed." >&2
exit 1
fi
echo "Built image: ${repo_dir}/${image_name}.raw"
REMOTE_SCRIPT REMOTE_SCRIPT
local_image="$remote_path" local_image="$vm_built_raw"
echo "Built on ${node}: ${remote_path}" echo "Built on ${node}: ${vm_built_raw}"
fi fi
fi fi
fi fi
@@ -831,14 +915,35 @@ else
--net0 virtio,bridge=${bridge} --bios ovmf --machine q35 --scsihw virtio-scsi-pci \ --net0 virtio,bridge=${bridge} --bios ovmf --machine q35 --scsihw virtio-scsi-pci \
--efidisk0 ${storage}:1,efitype=4m,pre-enrolled-keys=0 --agent enabled=1" --efidisk0 ${storage}:1,efitype=4m,pre-enrolled-keys=0 --agent enabled=1"
# VMs built on the node: import from the repo dir (where disko/QEMU wrote it).
# VMs from --image: import from remote_path (where scp uploaded it).
_import_path="${remote_path}"
[[ -z "$image" ]] && _import_path="${vm_built_raw}"
if [[ "$dry_run" -eq 1 ]]; then if [[ "$dry_run" -eq 1 ]]; then
echo "[dry-run] ssh ${ssh_target} -- ${sudo_display}qm importdisk ${vmid} ${remote_path} ${storage}" echo "[dry-run] ssh ${ssh_target} -- ${sudo_display}qm importdisk ${vmid} ${_import_path} ${storage}"
echo "[dry-run] (would parse the resulting disk identifier from that output)" echo "[dry-run] (would parse the resulting disk identifier from that output)"
echo "[dry-run] ssh ${ssh_target} -- ${sudo_display}qm set ${vmid} --scsi0 ${storage}:<parsed-disk-id>" echo "[dry-run] ssh ${ssh_target} -- ${sudo_display}qm set ${vmid} --scsi0 ${storage}:<parsed-disk-id>"
else else
importdisk_output="$(ssh "$ssh_target" "${sudo_prefix} qm importdisk ${vmid} ${remote_path} ${storage}")" if ! importdisk_output="$(ssh "$ssh_target" "${sudo_prefix} qm importdisk ${vmid} ${_import_path} ${storage}" 2>&1)"; then
echo "ERROR: qm importdisk failed:" >&2
echo "${importdisk_output}" >&2
exit 1
fi
echo "$importdisk_output" echo "$importdisk_output"
disk_id="$(echo "$importdisk_output" | grep -oP "(?<=Successfully imported disk as ')[^']+" | sed 's/^unused[0-9]*://')" # PVE output format: "unusedN: successfully imported disk '<storage>:<vol>'"
# (lowercase "successfully", no "as"; the primary regex targets this form; the
# || true inside the substitution prevents set -e from aborting when grep finds
# no match -- without it the script would silently exit before reaching the
# fallback whenever the PVE format doesn't match).
disk_id="$(echo "$importdisk_output" | grep -oP "successfully imported disk '\\K[^']+" || true)"
if [[ -z "$disk_id" ]]; then
# Fallback for other PVE output variants: read qm config directly.
unused_line="$(ssh "$ssh_target" "${sudo_prefix} qm config ${vmid}" | grep '^unused[0-9]*:' | head -1 || true)"
if [[ -n "$unused_line" ]]; then
disk_id="${unused_line#*: }"
echo "Note: disk ID resolved from qm config: ${disk_id}"
fi
fi
if [[ -z "$disk_id" ]]; then if [[ -z "$disk_id" ]]; then
echo "ERROR: couldn't parse the imported disk identifier from qm importdisk's output above." >&2 echo "ERROR: couldn't parse the imported disk identifier from qm importdisk's output above." >&2
echo "The VM shell (${vmid}) and imported disk both exist -- finish attaching it by hand:" >&2 echo "The VM shell (${vmid}) and imported disk both exist -- finish attaching it by hand:" >&2
@@ -847,6 +952,12 @@ else
exit 1 exit 1
fi fi
remote "${sudo_prefix} qm set ${vmid} --scsi0 ${disk_id}" remote "${sudo_prefix} qm set ${vmid} --scsi0 ${disk_id}"
# The disk data is now in ZFS; remove the source raw file (only for images
# we built on the node -- --image uploads are the operator's to manage).
if [[ -z "$image" ]]; then
ssh "$ssh_target" "${sudo_prefix} rm -f '${_import_path}'" 2>/dev/null || \
echo "Warning: couldn't remove ${_import_path} from ${node} -- you can delete it manually" >&2
fi
fi fi
remote "${sudo_prefix} qm set ${vmid} --boot order=scsi0" remote "${sudo_prefix} qm set ${vmid} --boot order=scsi0"
remote "${sudo_prefix} qm start ${vmid}" remote "${sudo_prefix} qm start ${vmid}"
+3 -2
View File
@@ -41,8 +41,9 @@ mkdir -p "$keydir"
keyfile="${keydir}/${hostname}_ssh_host_ed25519_key" keyfile="${keydir}/${hostname}_ssh_host_ed25519_key"
if [[ -f "$keyfile" ]]; then if [[ -f "$keyfile" ]]; then
echo "ERROR: $keyfile already exists. Remove it first if you want to regenerate." >&2 echo "Key already exists: ${keyfile}"
exit 1 echo "Reusing the existing key. Remove it first if you want to regenerate."
exit 0
fi fi
nix_extra_opts nix_extra_opts
+52 -31
View File
@@ -11,17 +11,16 @@
# sync-host-keys.sh --regenerate-all-keys Remove and freshly regenerate # sync-host-keys.sh --regenerate-all-keys Remove and freshly regenerate
# every locally-managed key. # every locally-managed key.
# #
# "Generate/register" is idempotent and additive only: an existing # "Generate/register" is idempotent and additive only: an existing clan
# host-keys/ file is never touched, and .sops.yaml only ever gains an # var is never overwritten, and .sops.yaml only ever gains an anchor/alias
# anchor/alias it doesn't already have -- safe to re-run any time, e.g. # it doesn't already have -- safe to re-run any time, e.g. right after
# right after adding a new host to flake.nix. # adding a new host to flake.nix.
# #
# --remove and --regenerate-all-keys only ever operate on anchors that have # --remove and --regenerate-all-keys only ever operate on anchors that
# a corresponding host-keys/<name>_ssh_host_ed25519_key file. Anchors # have a corresponding clan var (vars/per-machine/<name>/openssh/) or
# without one (&admin, and any anchor for an already-deployed host whose # host-keys/ file. Anchors without either (&admin) are never listed,
# real /etc/ssh key was registered by hand, e.g. &docker/&server/&nix-cache # removed, or regenerated -- this tooling only ever touches keys it itself
# today) are never listed, removed, or regenerated -- this tooling only # manages.
# ever touches keys it itself manages.
set -euo pipefail set -euo pipefail
repo_root="$(cd "$(dirname "$0")/../.." && pwd)" repo_root="$(cd "$(dirname "$0")/../.." && pwd)"
@@ -39,6 +38,8 @@ source "${repo_root}/scripts/lib/ssh-host-keys.sh"
source "${repo_root}/scripts/lib/sops-age.sh" source "${repo_root}/scripts/lib/sops-age.sh"
# shellcheck source=../lib/confirm.sh # shellcheck source=../lib/confirm.sh
source "${repo_root}/scripts/lib/confirm.sh" source "${repo_root}/scripts/lib/confirm.sh"
# shellcheck source=../lib/clan-vars.sh
source "${repo_root}/scripts/lib/clan-vars.sh"
mkdir -p "$keydir" mkdir -p "$keydir"
@@ -54,13 +55,14 @@ Usage: $0 --all [--dry-run]
<flake-target> Same, for just one target (e.g. lxc-server). <flake-target> Same, for just one target (e.g. lxc-server).
Reports if it already has one. Reports if it already has one.
--remove Interactively pick one locally-managed key to --remove Interactively pick one locally-managed key to
remove from .sops.yaml and host-keys/. remove from .sops.yaml and vars/per-machine/
(or host-keys/ for legacy keys).
--regenerate-all-keys Remove every locally-managed key and generate --regenerate-all-keys Remove every locally-managed key and generate
fresh replacements for every current flake fresh clan-var replacements for every current
target. Destructive -- requires typed flake target. Destructive -- requires typed
confirmation. confirmation.
--dry-run Combine with any of the above: print what would --dry-run Combine with any of the above: print what would
change (host-keys/ files, .sops.yaml anchors and change (clan vars, .sops.yaml anchors and
key_groups, which secrets/*.yaml would be key_groups, which secrets/*.yaml would be
re-encrypted) without touching anything. No keys re-encrypted) without touching anything. No keys
generated, no files written, no sops calls, generated, no files written, no sops calls,
@@ -133,10 +135,17 @@ discover_targets() {
} }
locally_managed_hosts() { locally_managed_hosts() {
{
for f in "$keydir"/*_ssh_host_ed25519_key.pub; do for f in "$keydir"/*_ssh_host_ed25519_key.pub; do
[[ -e "$f" ]] || continue [[ -e "$f" ]] || continue
basename "$f" _ssh_host_ed25519_key.pub basename "$f" _ssh_host_ed25519_key.pub
done done
local d
for d in "${repo_root}/vars/per-machine"/*/openssh/ssh_host_ed25519_key/secret; do
[[ -f "$d" ]] || continue
basename "$(dirname "$(dirname "$(dirname "$d")")")"
done
} | sort -u
} }
add_keys_json="[]" add_keys_json="[]"
@@ -146,13 +155,15 @@ dry_run=0
queue_host_sync() { queue_host_sync() {
local host="$1" local host="$1"
local keyfile="${keydir}/${host}_ssh_host_ed25519_key" local keyfile="${keydir}/${host}_ssh_host_ed25519_key"
local has_local_key=0 has_anchor=0 local has_local_key=0 has_clan_key=0 has_anchor=0
[[ -f "$keyfile" ]] && has_local_key=1 [[ -f "$keyfile" ]] && has_local_key=1
clan_ssh_key_exists "$host" "$repo_root" && has_clan_key=1
grep -qE "^ - &${host} age1" "$sops_yaml" && has_anchor=1 grep -qE "^ - &${host} age1" "$sops_yaml" && has_anchor=1
if [[ "$has_local_key" -eq 0 && "$has_anchor" -eq 1 ]]; then if [[ "$has_local_key" -eq 0 && "$has_clan_key" -eq 0 && "$has_anchor" -eq 1 ]]; then
echo "SKIP ${host}: .sops.yaml already has an &${host} anchor, but" echo "SKIP ${host}: .sops.yaml already has an &${host} anchor, but"
echo " host-keys/${host}_ssh_host_ed25519_key is missing locally." echo " neither host-keys/${host}_ssh_host_ed25519_key nor"
echo " vars/per-machine/${host}/openssh/ exist locally."
echo " Not generating a replacement -- it wouldn't match whatever's" echo " Not generating a replacement -- it wouldn't match whatever's"
echo " already registered (and possibly deployed). Remove the" echo " already registered (and possibly deployed). Remove the"
echo " &${host} line from .sops.yaml first if you really want a" echo " &${host} line from .sops.yaml first if you really want a"
@@ -160,21 +171,26 @@ queue_host_sync() {
return 1 return 1
fi fi
if [[ "$has_local_key" -eq 0 ]]; then if [[ "$has_local_key" -eq 0 && "$has_clan_key" -eq 0 ]]; then
if [[ "$dry_run" -eq 1 ]]; then if [[ "$dry_run" -eq 1 ]]; then
echo "[dry-run] ${host}: would generate host key" echo "[dry-run] ${host}: would generate host key via clan vars"
else else
echo "==> ${host}: generating host key" echo "==> ${host}: generating host key via clan vars"
generate_host_ed25519_key "$host" "$keyfile" clan_generate_ssh_key "$host" "$repo_root"
has_clan_key=1
fi fi
elif [[ "$has_clan_key" -eq 1 ]]; then
echo "==> ${host}: clan-managed SSH host key already present"
else else
echo "==> ${host}: host key already present" echo "==> ${host}: host key already present (host-keys/)"
fi fi
if [[ "$has_anchor" -eq 0 ]]; then if [[ "$has_anchor" -eq 0 ]]; then
local age_pub local age_pub
if [[ "$dry_run" -eq 1 ]]; then if [[ "$dry_run" -eq 1 ]]; then
age_pub="dry-run-placeholder-not-a-real-key" age_pub="dry-run-placeholder-not-a-real-key"
elif [[ "$has_clan_key" -eq 1 ]]; then
age_pub="$(ssh_pubkey_to_age "$(clan_ssh_pubkey_path "$host" "$repo_root")")"
else else
age_pub="$(ssh_pubkey_to_age "${keyfile}.pub")" age_pub="$(ssh_pubkey_to_age "${keyfile}.pub")"
fi fi
@@ -299,7 +315,7 @@ cmd_remove() {
local hosts local hosts
mapfile -t hosts < <(locally_managed_hosts) mapfile -t hosts < <(locally_managed_hosts)
if [[ "${#hosts[@]}" -eq 0 ]]; then if [[ "${#hosts[@]}" -eq 0 ]]; then
echo "No locally-managed keys in host-keys/ -- nothing to remove." echo "No locally-managed keys found (checked host-keys/ and vars/per-machine/) -- nothing to remove."
return return
fi fi
@@ -308,7 +324,9 @@ cmd_remove() {
for host in "${hosts[@]}"; do for host in "${hosts[@]}"; do
local registered="not registered in .sops.yaml" local registered="not registered in .sops.yaml"
grep -qE "^ - &${host} age1" "$sops_yaml" && registered="registered in .sops.yaml" grep -qE "^ - &${host} age1" "$sops_yaml" && registered="registered in .sops.yaml"
printf ' %d) %s (%s)\n' "$i" "$host" "$registered" local where="host-keys/"
clan_ssh_key_exists "$host" "$repo_root" && where="clan-vars"
printf ' %d) %s [%s, %s]\n' "$i" "$host" "$where" "$registered"
i=$((i + 1)) i=$((i + 1))
done done
@@ -325,7 +343,7 @@ cmd_remove() {
local target="${hosts[$((choice - 1))]}" local target="${hosts[$((choice - 1))]}"
if [[ "$dry_run" -ne 1 ]]; then if [[ "$dry_run" -ne 1 ]]; then
read -rp "Really remove '${target}'? Its host-keys/ files will be deleted and it will lose access to every secrets file it can currently decrypt. (y/N): " confirm read -rp "Really remove '${target}'? Its key files will be deleted and it will lose access to every secrets file it can currently decrypt. (y/N): " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
echo "Cancelled." echo "Cancelled."
return return
@@ -338,11 +356,13 @@ cmd_remove() {
apply_edit_plan "$plan" apply_edit_plan "$plan"
if [[ "$dry_run" -eq 1 ]]; then if [[ "$dry_run" -eq 1 ]]; then
echo "[dry-run] would delete host-keys/${target}_ssh_host_ed25519_key(.pub)." echo "[dry-run] would delete host-keys/${target}_ssh_host_ed25519_key(.pub) if present."
echo "[dry-run] would delete vars/per-machine/${target}/openssh/ if present."
echo "[dry-run] Nothing was changed. Re-run without --dry-run to apply this." echo "[dry-run] Nothing was changed. Re-run without --dry-run to apply this."
else else
rm -f "${keydir}/${target}_ssh_host_ed25519_key" "${keydir}/${target}_ssh_host_ed25519_key.pub" rm -f "${keydir}/${target}_ssh_host_ed25519_key" "${keydir}/${target}_ssh_host_ed25519_key.pub"
echo "Removed host-keys/${target}_ssh_host_ed25519_key(.pub)." rm -rf "${repo_root}/vars/per-machine/${target}/openssh"
echo "Removed key for ${target} (host-keys/ and/or vars/per-machine/ as applicable)."
echo echo
echo "Review the diff, then commit and push." echo "Review the diff, then commit and push."
fi fi
@@ -352,7 +372,7 @@ cmd_regenerate_all() {
local hosts local hosts
mapfile -t hosts < <(locally_managed_hosts) mapfile -t hosts < <(locally_managed_hosts)
if [[ "${#hosts[@]}" -eq 0 ]]; then if [[ "${#hosts[@]}" -eq 0 ]]; then
echo "No locally-managed keys in host-keys/ -- nothing to regenerate." echo "No locally-managed keys found (checked host-keys/ and vars/per-machine/) -- nothing to regenerate."
return return
fi fi
@@ -381,8 +401,8 @@ cmd_regenerate_all() {
apply_edit_plan "$plan" apply_edit_plan "$plan"
if [[ "$dry_run" -eq 1 ]]; then if [[ "$dry_run" -eq 1 ]]; then
echo "[dry-run] would delete ${#hosts[@]} host-keys/ file pair(s)." echo "[dry-run] would delete ${#hosts[@]} key pair(s) from host-keys/ and/or vars/per-machine/."
echo "[dry-run] would then generate fresh replacements for the same hosts" echo "[dry-run] would then generate fresh clan vars replacements for the same hosts"
echo "[dry-run] (not simulated further here -- run without --dry-run, or" echo "[dry-run] (not simulated further here -- run without --dry-run, or"
echo "[dry-run] preview a specific target with: $0 <target> --dry-run)." echo "[dry-run] preview a specific target with: $0 <target> --dry-run)."
echo echo
@@ -394,8 +414,9 @@ cmd_regenerate_all() {
local host local host
for host in "${hosts[@]}"; do for host in "${hosts[@]}"; do
rm -f "${keydir}/${host}_ssh_host_ed25519_key" "${keydir}/${host}_ssh_host_ed25519_key.pub" rm -f "${keydir}/${host}_ssh_host_ed25519_key" "${keydir}/${host}_ssh_host_ed25519_key.pub"
rm -rf "${repo_root}/vars/per-machine/${host}/openssh"
done done
echo "Removed ${#hosts[@]} host-keys/ file pair(s)." echo "Removed ${#hosts[@]} key pair(s)."
echo echo
echo "Regenerating fresh keys for every current flake target..." echo "Regenerating fresh keys for every current flake target..."
+137 -182
View File
@@ -5,256 +5,211 @@ sops:
age: age:
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwU3A1MFJvZFJkL2d3UzNv YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrMGd3ZVNlNXdmOUZMUzdQ
eUF5eFhkUGIyVVpjSEovaUZKdUlVeWxncEdjCkVOQjA0Q1FtbElFdTB2Rkp5aWl6 TW1acEs0NFA2Q01rM2dkc1h0NHkzQmhiWFZZCjMyK202VWdlaGhsZW04MnVwUVdO
THdmcXVzcnUxeVZ5VUdFMmVKa0tXSzAKLS0tIG1ZcFZwUEoxRGcwakIyRVY3dzNT alA0Q2FETThsYkhSS0hKdHBaS3VaY28KLS0tIG1Gdk8yalREOUtIZTUyY2p1UHlJ
VjlMTFdXYVVaeUpzTnNXMVhwalQybnMKjEHhkLMCGy3Q5mHPg7WSp7IBncKw6cCV eG5iQnJsaTJBY3Y1dkw1c0VEaDQwdDQKfV04fLy32Lp2ZQ2VnvQ0h/Vsf+qdaJiv
1WD9H47oGdW0BgBdi4rK4H7fmashw6TxjHZ1JfuCKps4mvvVWb2llg== DnLXGZ9hE5yzpKWkQIRgqYGBkF8PkH0YC4OIaVkA53wrtjqS4ZHR9Q==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRM3A0b0Jub04wcFZWelRo YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjWFNRY2FiK3VkSm1RdHBn
L1RyZmRNS2pTV1FPVXNEbVF1SncxTXFvWnhVClRjSFVtOVIrVVd6VnoxR3NvSEpY cWl1ZStLcTRFZWY5VVI5N0FhODZvR1ltM0FvCnBHZUtTUm9QeHNlbVBoZEx1V3Fa
Y2lJU01lbWlUcTVraEFiNjJTeUlOdXcKLS0tIDliMElZTElVL0VNNVBseUVhQy9u Nk9iMmJKVnhocEpERi9leE1ySUtNMFkKLS0tIDRRYkxnbU90S2RyMHdJNzRJNXBi
YnFmM29mUFMxUXhYS1R3ZnpEZmJtSlkKWoXICCvylDiMJ/YWR8oRgIZlSYb9Ubkl QjRmZFhVakVic2tYODZHcWtJRmNQTDQK7G8eSJInt11P0DiL9uzNQ/ZHHLVNIYPe
H6BeOPVs1qegS3Hav6vJrd+OMv1rd8rxWw5B5MYhNUEG81cdX5fgkA== bvlhuGkEuQ/+j5sVSKOfSI2Y7CvM7TpE3APyKBcLG3ajYg6F/Ev3SA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age19gfn2yedg76dmztm4hncr7vf3r3c9j0qpt4rap7y7gersjk4m3ks2lhd0e recipient: age19m0m7vdfg86yqy8l5mmle5jdd0unrn3f55t232w8h5ey42cqw34sfpt32n
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiZS9ibXZ5VzF4UEcyQmhE YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1S3dOcFdwV2NoMW1oMnY1
N1JrOFNsMUgxbC8zRmQ4MWlxbVN1Ny9JTEhrCkM1Zk1IK2JNMnlFVjNPNzBaOVNz VGdxeXJVR1lsbzNVZHlGb0NGOVo0SndiakVjCmg0QlNnZDV5RlFja2hCbVRXV1VF
K0lodkVBSnhSQkFvSm5TUmVCaFFRZ3cKLS0tICtaSE5Udnp5WTNIemI2MVJWNVpq S1ZtbC9KU0U1ZW9zeVoyR3hxNW1XTFUKLS0tIFUzWTJhTzM4QnpWV3h1OFU0N3BK
Nm1XOTRHbVdYczBZTDQ4a3hwRVV3MGsKmOmKG4ixQhLu+TO6bsdtD1Iwnzj45o73 RnF1N2k0S0lIVitoNDJLUmZqdHRzZVkKUfNg24p8zxb3749v/A1BOKCNw75AUKpf
M5cWNcPlKa4cF7JfzHMl7jzvjTzHN9NY1KjBS60EABTzvKGqYVfJng== RUmFCw5DDWF2aNM0mZqcjjVmJ/FRKV2HXwwUGsHPKSOTnKfOUlPNKA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1ll6hj5ggruetgjwjfnplpn5xtq35uhlcdflksx3xmnjm6s3uad9sz70jkf recipient: age1rrxqea6q6pn39sw8y5te63h2py8jgjl9v0jyper86w3ggtn67upqg3ah39
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQQUh2YzJ1MTViYkNRSlFJ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzazl5MUpUNEVNMTdLWXBp
OVM5cy8zcldvRHlmY2pIL0pPeWVDQitqcFVNCnhWWjYycGNaRzliZGxQUGdOUlEx c1hkaVhjcTZXTUNsWS96QVVWb0RVQlZ4VlNvCnE3TFRySU5jTFk2WjBONUQyQUhl
Q1Y3bWplZjc3WHdYUFpOUWQ0NkZOTEEKLS0tIFB5b0pOQVVWM3pRelR0SmlwS1FW U1lNTDFTRmZhMUFyZmpVY2xpaUVxRW8KLS0tIE9MQ1M2U2ZXQmRCVll2UGRWL1RG
dnUxTklVa0N3RUl3bUIxRkU2Y3FtNncK7v0Q/fyygDAUr5aSgmillRb0AEE2+JHE K2tnU0NOKzU5dkpiekF0Vk1VM0ZZZDgKK13aFypGAqrKWPOr3UwtXI1EoXf1+UzS
P+ae0gqe2L/i3h01XdiqxAflVXzCF8rhFIG6jf+XCmxQx0eDUSzbLA== rBqcwnX6WPxSKUwWoins4Aojek4QhbhY4R5ei6rRS0KEQeryGxy8bg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age120le4a5l8dh3lyfgvmj3d9ksmej6ajs5mer5y7r0vfg3x9fn69dqf8xgzu recipient: age1adur9g330gua4l6ndk8cqjg35qc8yxwgme6wrl2hpylcc7vxm38q05ejuy
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhRzVrSDQzWGNoL21GU1FC YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCZUpVT1p4TGZyNC9qMm5G
Z3ByeSsrRU42MWI1WVBjOXNFQ1RyOUluYTN3CkxLQjFBcXFYVS8wTFhkakZDSkZB UzNHV08rWURjY2lqS3FTWFdoc0FYYTFjb0NBCk1mZ2JzaXk5RmExNE9xWGZ5K0pv
MmZRS1BCNi9Kdy9wa1FRYk9RT0V5TTQKLS0tIGpTV0I4UGZXY3BNYUNZdkxzUFYv UEtqMkltV0dIWll5eVBUVVRNOUNDWUUKLS0tIGpKNVJudUM1UGNvaGl1UDBOeFA1
ZEN2L1F0ZXAyb2gwWTRNZDRwbjViMlEKUScRb9D360Ns/ofOn8voUNiJwzJxt7k8 RjUyRlZ6a0Y4SXNsL21zSURVRk9KTFEKU1L6BQ6ZlYQQtqx3uF/uM5CQ1ercmvRT
9hwsBIiCDvNQtw5tlgEirSD0z5cZJ+6iWiyZbh1FJmFLfmlu8Y43sQ== TL3r2/Y07gE7CjRn3pR9z0co8KndGzxV6YR+ubyWptwBS8KQh5stkw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1jy444f9d9stygj4p3w9kh54cqcfr654tvr75tdvee5cxsgtdtc9q3v60ep recipient: age17e89ty6p0fw24daanen57wg8uald9s025t3wwxsw269svwpmgvrshfvfvt
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1OTNFVTdHNVlBY1dDVkFH YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsaWxxWS9xQXViY3VnUEx2
ZU4xaVlWTkNzcjFKaDBZczU3dTFrSkd1TFJ3CjVxTXpoWXNwemEyU3hhWjZqeVl2 V09FbTI0WGtNbW0yclhOSGZDbG5NUTNaTkFFCitkcjJ3OE9BSnN4bjFWcE9nYVBk
cEZrMWh1V3IxWnFLVE0zR1dkY0o1alEKLS0tIEhwM3ZyNHFIbU5OYzVkYnlkT204 ZzMyVHlJQ2wwdU5JOXdCQm9oNkhNd2MKLS0tIE9tRzFYS05vSkUwWFRkaTdtc0k0
RG9ocGd6NWdoRHdVanJPcWFNaStFTFEKFhmCp5Dpxev/ltZaNq4MC5JcTcKJnjUD blVoMWV0QklBVkluT0Z4NHYyS1F0blUKO+Uc0of/V77ZUZOsxTzeH8/LmmAOQt+J
Nqby+fqJTcu9WFGqNHHra+I0+ROSAeE9ohhfqOZZg+6HNgzdjqmCEw== x/COHxnLCnZ4eWI6q1a0Qn5Br15OJYTxUI2QTV4goTnXBNUDo9wdpQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age120whqj96g26lsgy4udvgsn8dc9lumh8jeu3a564fx79rjr5lxffqmrljuu recipient: age1hrx8qj02fj2ea6d4g9vqhyj9hl7fppkjqfdx2l37py3h6pdkr95s8n8rvs
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqMUptazlMYjNwekVXRG5C YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsLzFnZkdVdGQ2dHRwcnRt
Rk5YWE9tTm5XVzFOVitjNFEzc1lrRmNqZlVFCmk5R2M3OWZ5SlBoV1YrdkVVbXpQ Ui8rc1NoQzNKSHVzTk0vYU1vMlRxcjBKZ2hNCmhLYXVGSis4RU9HNGVxZkpvUkd5
dU1jTTNVWmdYRGtUczBaVmpQMDdpLzAKLS0tIGVMTFRmdUlySTk5WkE2M1VDQXZm L0xvalhDYTQ4N21OcHRheTlkaUxvTkEKLS0tIFVkRGxtLzhQT0paV2U3ZnNScVdn
c0xHUXVCdFJrVFA3M0dHdUROdE50QTgKq5nhd/jlJkMIPCjYsuiMgY4VsW68zEf3 alZnaVppeGI3OUVscGpONkk3YTRXd3MK61na8x5qX7+dyMHasDz2dj7yeaUlX8me
qADcbbuYWKfQi/lz92tgMP+d+3GoRjVBIQ2my0giZD8c7ZhibXSLVw== N4/SIk1JDBhv9G7mdKLbKhSF1UJrSY7TJqJqx8/dqEc0uG3vptA1ew==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1xjst4frdh0th6q8m7p7u9g5af7ty5jqeum0p6z8a52a9q7st7ewqw8yl9j recipient: age1e7l8dusgmgfzd2cxrrzwepzjxt69hzqj4epee0cs27u6yg4kxcuqm34ncx
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4aklLVk5XaUl6dHVsNC9J YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4ZEtvOUhMU1FRWWpJQjF4
YUorZGlDTWs4aFIzK0o1dXJSeElLZWpvcWtrCkpOWmFuOUZSUUtNNHZ2dnpENG5v NWphNXp5M0dLZXhkZndhT1Y3L09maytHazJVCm1MeEtMWXg1Zjg3bFVnZEorci9J
Q1MwbVN0bXl1bDA4b2hwNzlBRnFEY1kKLS0tIFZPRHhyYTRRSG15ZU1uSXIyZmVC bkNZQU9Ta1dDTFFHaGFWQVBpK3pYRDQKLS0tIEhPVGliRDR3ZTF2aEl3ZnJEYWtR
eXA5c2FpY1BWamk3dzNXMUtteE91OWsKV+yTdt1MW/7FUxy64abvwjN4OQPaw81l anh0SEpnVW8xdXNkZEZQSjcxU1BHMFEKVRJUA71fi1QawB2TnuTWMYhzQR18u4M2
NsOLGSMbZHbQFrqyW1aXTfqYkN1wSuH5rYSZwBL+8gRLBEkFwQtfGw== s1V4j4TwYyyKZFoNvt8kOUayjC499c5OBUufYs6G2ciC6gK2A9E0EQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age10at8862478urh0eeuwh8hzln6ck78jgwtztgxatwqlzwagg77y5snm4xzg recipient: age1jcx3yajjhghn8qh8za3yeu8nxykzlg3p4nrv03vnfvzl0mzayg2qmg940e
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwbmpEN0lrNitUcEdyNjFT YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTV2IzSjBEMjUwZjB0dCtj
ZUFCUXA2STFTaTFxUXdrWTRuOG51cThTZ2dnCmhadmpNOTJKS0ZvNmFRdkVLWTJ4 aTdZVlEvYkVRbTNjUC9ZRVdNZVhPQTJEd1ZZClc3NDJiR1BVYkZkdVVxMVZGc0VN
ZUpwNGFxMGp2eDdKdjlNTmVCVnc2MncKLS0tIDY3eUZ3SmZwTXhMYkZqOWVTb2Vy dWdSSXBFR2xxR0xrV2thRmUwSS96S0UKLS0tIHRRTXVlUi9UYnFRRlhsU21HZVY4
WCtxb2toY0UwRmxSemZza2VDOEk0U0EKzk4+Ik7eGUyot4/pCLV7rMY+aNpHFvM1 SDFYd0NwVEtVZXNsWUI1a1ZZU2xNRGMKuQUhOq2FRD+PGn5OkdODZItbxCzRKjne
jlShEeqOrPiU1VQ0t4nWSI5E00rpZv+cxs/o1ii9RSUEi7/3VDgHtQ== E60UOYtHjanuGjJ1svuR9cYsLZz7lLOwItklecYaQYpMRZEwzzBGCQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1ezk9x53zt8kcnscdm80jcyf0xq97vndv7jsn3rl8cc0cwm2jmpmq372dzs recipient: age1sweerhrga9yf8x6sv0apz4ed4g48rnlcq34rpv20t0rcelwgpgeqwvndzz
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJdHBScXYyZEg4Ky9qVUZn YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrSjJoUVUyd1JqRm1ZQzZx
RWpMd3VPVEp0cUc2OUJmWHZwOW1DL3ByVXo0CkNWcldRNVpnVll5eVJTV3FBMTVC SFJ2cWNUY0E4b3FndVA5Y0hIOEZnZUlVWlhBCm1vY1luOXZBelRUTmF5Y1NMeDBn
WUJ4a3RZRUFuNFB6b01RVWJLNTZZc3cKLS0tIEF0ckhmWGZ0Y25jcnUyajNxZkMy cE1BTDErc041UjJCWTBQbnk0Wk80dkEKLS0tIDVSZzd1UktvZGdyanFUMkVORUtl
akEyWjUrM3RUOGZOZDgxSGlIT2VrejAKXjLTsZ/Qf+77sA2d8kxNublGsCJNiNUa eVB5TnJkMlp6dUpXSTlxRlplZ2NxUlEK0AYOxIbswjM0SUASDfmZ7PqcEU844fgI
voxDqyXEYAP9JHk7LZr2iNz2j6lnrJbB80sSerZJAhok8A5cZ/RWsw== ycFWVSEPodwUZ6UFoYXhHlJzHFcgpLvwUd1PMktLHe1qrZ7GOQJIMA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1fxxzpnfse8nd9wz78ht3m0plrmraacf4cpga0pe8fm2tdnqcgy8q7qsyvp recipient: age1f7usptjx9rv4rxauasve200gxtdt9jkqhhdqstlf20wvlm7u75rsjfw50m
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIUjFYQTIrUjFEUytDZDJj YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPakhQcE92ZU0zYmk2QS9D
eGU2UlBRV3NobkdyNy9rdjkvWDhVVi84OXlFCjlXSXBmVHljMTJwa25Zc0dIdlFD TG0xc1JPZXZCZ0tZOXA3MGNLVnBlZGVtRFRzCmIvMHhQKzFVWCtpMTQrQUhGVGJp
RGRkaTI5UmI3ek1oTjZjc2M5UGlHYjAKLS0tIDF2QVpzajdObjhHZVV0dnJXMFZl RU1jbldYckw3TXI2SlNpZVBIZHRsWWcKLS0tIGtJTUtJejFxem5jajFQUDFTQWU1
VFlJT0M0VjBvSHlrVXoraitkd0IrTjQKU0/plWrjbN5BL782Hls0idImBUHEDg8o VnlxYmVlNG04ay9ETi9FRmVYQXVoRkUK9oFNolI7jRjo9RUs1g4ghrx7aYV4U/ce
9svRbtapGOqwEVuMoLhPGhJ+FjjHZxYIDVj1o3ZHzs7aVMgiODdAzQ== ZTc2tFh57+7aKgrDi+2W3jwhfkjvBsThk//p5mLlqEEgw2lwlnhvPA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age190htw7prp4vln076dxjx3gxxaq06h0zl0te7cqgpx79vl3lhkaes8suy05 recipient: age17jqc66x9yeshfgd9v78mj483r4zzarqdtuxtrkxe4x5mw679gphshd94th
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhRW8xNnNEalNmSmtvaUJM YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoVnh6dFAwTkY4cHJpaEs4
OUQzQ1M5RFQzNHBVc1NZSUJTekZ3azRwalg4Ck8xUm5HR3UrdUJaOGIyait4ZlFk QnZPeXZHK0tXYWZPNytmYXVsdGRWQVI3RlhRClFVb2I5OVZzZFNrRXFaa0JTUkRJ
RFFXTlY1SlZncy9VQnR1bTVWR3ZZcmsKLS0tIHM2K0oxMWNld3NQSHJhb1ZneWxL OC9GQ1V5K0JhWlhkUjU1WStCa1lPV1kKLS0tIEhzdnBBZkRnK0NtV1FuTkVsNlgv
d0J1NEhlMXRnMlZMRkdEMFhwUi9vVkUKgI1e28oU9zTUzGpiZSQUT1k4HVTtPeeV QzVEcEVkQm5NL0Z5dUU1U0ZFaTJITnMKaWE9vlrOpQstr6FGP5ObdilsCYk4kYAj
aqVMxoomQd9Q8Hg9NFmMSqXvbAJm5NC7NVdwUzI5WrfUIGe01a1bFA== /phboR+Ym7QDTyUF9LZXJCU54YJp6vEWkRnlJFqC75UW/v/lgBhBMQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1ukpqxzl44mnjpy5r96sfuc5sqzm47u4k8ujjh5qdgy6jvl9uqgpspymqfk recipient: age1px0h5l9zp2dww0m8fncrc82kfdmzplsfv2ltat7sna28xpg09pqqcl3s2k
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCSVZkRHVWOWRUSU04cEdL YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkUHAzWk1KblJxVVZZV3FM
VG82aXZhTzlaSlZaME9uRDJiUE5ORmlxN244ClVsUlRDVlVhQ0FyaldRQkRLejRY WndzRGRtbkw2azRVWnBuZmhKWElqRFk0RENzCk5DMHlMVWpwbXEwVUhkaFZUbkp2
dHpicE1xektRWXdELzRkZXFwOVJJbUEKLS0tIDRDRXQxbFRJMWFIckIyVnI4ZXRY QXNlZFV4SjBEdmR6UEw0N1JOUnhNKzAKLS0tIHo5RkNDUk1ESWRHQmV6bzkvSTlP
OE85QjZOM2R6SnBjRnlYNlZsUFVUZ2cKpdpIA9FO6aX8nSxup6OSYBRGmVAw7uUA dk1GQ0Y3V0dTRlByb2xUOERVOTVwbVEKY4sAHyAhvGSYJzPuufWUIQD2xZcSt/nX
yYwMaRZTd+d/1Xec9WJI03e8oAb0u4WMbok1G3eI1FQJYU22XQ0fDg== t2ZFXu891/QdEzyUXCIzdwAV+Y/LjvroIlCp5Hkbrk0s7N+ghqsB1A==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age15kh7akxlx7zn00tey79rq2g8lgs4j5y77rcnyfxrxap8ckfu0a9sqvtdhh recipient: age1ufg390ydrmma849t9xfkxxl5xvdkk6mngnlzhmy7mvuaje8sgcmsmnq6l7
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKS0hjSStKdW0walhuVDlQ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBBYVkwYXpYSjdmM1FpbDl3
bVBkL21sVEJ5VGlEcDhsOS9IU2VOR3pmdVFZCmhjSFJac045cWNhV0IwNGRVMGxE QWpycXoyL1AyOEpZUmtpbjl3MFAwTkJoOWpVCmVDd0FBUWxaQmZCU2VmNkZGMk9o
QS9KUHVIZGFQVVZwcFJmTXRSc2VadkUKLS0tIGlXUUZoZ1Q0K1pvRk9HRnM3SC9t TUdLNGtac2N4REg2eVF1eVh0WnNaTE0KLS0tIDJvcFErSjRiWmhPMmpadjROOHdt
aUc3ME9IMmVteiswUzQ4dWRYTW1ZYVUKogiJ89uSOoeOVklAIox4qzsvNaOhqTIn NXp6Y1JpdHFlSlRoa3JTaEt3emdnalUKjoFfZAiKMPF3noX+K0+vc3+p/XUHnhic
DFVVP+nUZuYyqvIhPStAt/O9UpZiBKsTi7rNuTCfzDF5yHMVqQRMtg== k888KdUwcZYl2/dAIc8UDSggbMnncJAJgoezoCHLkj97GNNAD7E+gQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1ehkswwz2pqaz4svzh7ela5tdnssl8kn6d4vwwxd6zwg8exfpd43syyrrjp recipient: age16j42pdc5dr6wnj7xayhkqdj2rny9u68fcqejs50hqq42scssh4gsnrrnlt
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLUGxMZnQzN3JJY2ZENy9T YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqYnQyYzF5cktZNG5PcThy
WGlZQ1htZHRjV1E0YVp6ZE9rdHZKS3AwY2p3Ck12eGF5bEkzc2IxQzFjRFVnZTRi SEszVEwrUkQ1VVRsM3pSTlRQaHVLN0VuSHhrCmN1Z3pwNlFsbDN2UGI0KzYyallM
YzZLTlQ2dk9PVjA4ZHRpNTJ4V2U0Q3cKLS0tIFBVVG5VLzdXRjY1THN3QlJMbDNM SHJ5eklQeEIxSlhiYW5PUlpJcG5KNjQKLS0tIEk0QkpMdlBlRjVYMmJaMzJUbDNm
bzd5eERLbVVVd1dYOVdVWEFLTWdVbTgK5GAY4iM1+xMBarDrvd/I5VPZxvJJREBd K25pZldwd3JoZi9vdURoa3Myb2RQNG8K6N6bO2YKooPfpKihgsYqilfz/yAYCLZD
pB8G7TpkwapsXeLguoSHy8rgc9smMlb0QhpARtyoBJv2bDr0nQRUcg== XJ/THgT4URX2VNvSspvBtN8luOiJUVcchp5WtL2m9jARL5txEcDorA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1gl5ujmhd2pe37acd0eaar8tmnf2na28n8l28s8xct40vqpxvsdwqf49g4l recipient: age1nruncs4l0ufk7yuc4des8p99c0alfndl0lhsws8tycl5pplfp56s30af5f
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0c0V0bTMzOGdmbnhUenJP YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvY0VpeW1nazVvRWJFaGNU
ZXZDVGh5R1pxRDBlQTlqeEpjWFE2Z2FodlFBCklxNTZtc1M2MVNRZ3p4TE5LcU5m RGVzVjRmWWV4dnJzRHdsaW9ENVZYck5lWGtVClNtd3ppelowRjZpRFFSMC9EK09n
UzlEZkxwTU91VkJ5U2pkbHppSmNuY1kKLS0tIEFvVzl2bHNvVFhKSTJjU0c2YjZ6 b2hqNnkzejdrTnhYNGNKblpteDNLRWcKLS0tIE02bWVjazRWNEVKbURITGlQODlR
UCtjRytHY2J2amdaRHlyYXJDM1JLWTAKX2hb4vJQ43uwCoSOqaofoIUCnWuDeF5t cTJJVnBVdGIrdzBoSXExelNrVk1XcEUKc77o2EX7PCm/HjUo5GsUiQdm488WB2mg
ir8fkAjpvFFuCTzi5AbajCkncyFXVF7tRTFzWSoj/73FdT3yQ1696w== wHd/qDbQhF1W75RrVTuIKgtEtrRjZqpmr8toe+aHJizPofcrToUfzw==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1eu65wsmez68gegnufl0gqrs0e6w2409mypjlajlq383d7l2e3pjqy5kn6v recipient: age1k7d2du5mejsmv5rzavm4xwgpthqvcfsehduquv28nzs53zppa3kqngfxq2
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA4VjVuUDZvYThhelBRWXph YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSMmZnd1pXRWh5NDVSN2xq
QmZZZkw4MFpmNWt4b0pEa1daM2NnQ0ljUXlvCmZDbkp1d1EwNDYrWXI4cDYxME96 MVhTN3N1OFpkcUh0a0tjYzJnWUlINGNVd3k0CmZkTnBac0l1dllxazdLY2l0Rzli
OUJpR0VpM1BkMk1TRTIwOEJDaUxieDgKLS0tIHRmSlFBQWl4VEtVNm8wRGZtanFv bE9sNTBVSkJNaWF1T3c0WktoOHl0NU0KLS0tIHEyeWZTUjdQeUN6U2t0d3JwNTBX
RkhyNWppNC9WZHQyajZra0lrYlUzdUEK4GRvBd6bxV5LTRMvoOXrYJJMHKUpX3Bh ZE1Za0tXb0gwc1FSakVYdU9OTHkyd28KkwmlzSYP8XofB0VGag+S18+S2TyQjLrM
oSmmcFkphujOhnZ839jt4XnjdYoTfQ1b/M380Sijn9xhOrHfTWqinA== qaXtbBtLzJGNDhe9FhAKTPFcjTLWbohlG69vxcImyCyCns+QQ+gvug==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age17pwyghxr6lq06fw46gwqzhc9ut4paz28rpwx5pmv3cxwak6rgyjsw7lk3w recipient: age16kqfmvz4e23hmdlqresnyw69ej604s320mmd49h4hm3fhqchtgyqrws0k2
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByT1pPU2RGeEJ2RHFnQ2Jk YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2Q2RMWmZVOEwrOC9VcUxp
WnRISFk4bGRVVzNxOHZKQ3A3dlA1Rno4WTNzCjlsVXRmWk1ZbVkxaTdPTkNjZWVv VVE4R2NqYlZWZlhKSTBHRks4bDNoaUliMWlJCm5FbWdZS05GY0VLc0sxY2x1U21V
QUFLcTlsOW9yZkpic0VGdWtUVHJYWUkKLS0tIFFQTlg5ODBIMG43cTgycGpGQnZx eExwK29GVVBqYlRPZ0l5RWVXRFhRNlEKLS0tIDl6dVZJQndwVStFVEJnRHRyMW1W
VEJzaWtvN2NNSHFwSkQyRC8wSGJqcHMKH+UR0NyxjXoWLR6NmiGWphSrl2CgGL3Z NnFqc1F0SGJqT0xmREpaN21EdnlJK3MKRPE5rfFpVnH5wAOkuB5pNMlMd3omcpku
KuIZPGGKFELe79AlH6bOxkcJ7BcYBfVKRFrLzYLfV283DteAfxGMSw== do2hFZwyI7t80jxF4+g3J7EolOx8AGjpc9Ba7Gj6IMDjye728q5N+g==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1jukmg69cqxnjd0lp5f534jhqe65rxew2hufcmyjxa3rkw6ayef6s2ylcmy recipient: age1arhf2q45zw6wf2uevju4savp575x3m2tfvved5zzq3ay92ynua9s3cm92c
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsNHdueS9XN20wRzMvQnoz YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhSG94VE96TVlTNkEyclFE
NUlKeThWSUMxQ2c2ampxbzEvQS9XSWMrdFN3Ckk4YUtVTXdzTmg3QUxjTXhubHA4 SmdCSkJpeWlVWDFIMDhwUEkxL1RUTDJ5UG5FCjhKRDB6VGtwTVozdUVzbUxGL3BW
azVsQ21yajlteVBjUHBGanYzU2xramsKLS0tIHhBMFNvZnJRc2ROd29vZ2VmdWlI SnR3cmpSN2RxNnl4QmNvT2lkYmtoVFkKLS0tIHd2V2h2Wk5xOXlISzhjVzBsVkhz
UStoVitWNjUvYUpkRTc4VFkrV0FOSUkK48TGNM55gQ+MKV8YGIYclv/eMz0nAZ6R VVpRenVnSVpHUWJqV0JHNXNWWXJOdW8Kv7PJSTDbwFOAcl7pynALaJiTXU/87bSF
ZiY0XSP/2XE06Y2f3NKBtEauCRGqgwPFt74ITjFOAwitsqIhD0N2Uw== F3HQllYOwOoibGzBCe18H2N+VxyNxoQL9OWe0TvOIR6bgHFIIF0/Dg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1tzsrtwd3p3lrr9g7nv3z5nvmzsz54t2uc6tfqwutp6usmav83s2sck25cc recipient: age19mn8zrxl8zpps9yvrh4euquvygpp4fp8queg7xc6qhtnl4ng8c9qx02qwn
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYcUxHbFljWEdBdzBsdklB YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsMEZPUExRVHFFb3pSVHNO
VE1IVVNoWFV3YWUzd2RON2ladVVxUThHMXgwClpXVHZUbDV3SHJlSUlTVXhONDQr cGJFN1ZzTDFVNEdneVpMZ253ekFJNjVtYkNnCnRGQjU2Q3dsRGRFV25LQ3pCbTJE
aG11YXFWWk11UVRnUG1MOE5FUWRKejQKLS0tIDl2V2M4RXNGTWtIVkgxem56UnZK OTROaFBiT01xb200S1pUK0NYaTQ3R2sKLS0tIEdiQlZTbi9Vcm0zc2t6bHplZktF
RHZXRUZESDFPRzFhUGhJKzlvODJiMjgKN793k0tnuFcfs9PSgdmDXefZ1euAEfC6 ekRySENXcjBuR2psdHZSSUJrR0xUdjgKvBsmnC+cbq5TUDFjXCyImIoPKvh8wsjE
DMJ4sXJgwFGX459k0sG8f64eaZrQasg//tcz07J/gRx2D3VyaC6a5g== 7Shk7Act8Jayrhx0lXBDRmfpHRrB4L16rDSmqO0DTE48VhT3TiFyug==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1rf4kj99wuq59k7w8ar326djmgmpl9hcwlnuag07f8gauq8c3y5mqne87s4 recipient: age1jlltcv5jcnm40z5k0q6hv053k2rqpqvemtuecdwn527uw8uqz4es3x7m68
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBrejFCSG5VcFgzb1VwNXNL YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAxNDlya3RmYzNhU2p6RkVw
VnFmVHdaNk1UcnlkRFNscVJ5RFJSbXQrL2tzCllmZ0VVZUpSLyt5UEd6aFNQcnhl cHJVWWY0Yi93cW1uZi9FWkVONmtpRWh5SzFzCmtWYTRIY3BkTWU5R2Jsa0ZJK3kz
QnNqT1o0Y0pIQWtyVC9OMmtOOXFQK0kKLS0tIGdvV0M0dHc0Rnd6RkJ6KzlOSEg1 SnZvZ3YwaGtoMVZ3V2laTlBBK3UyTmMKLS0tIFdpZWtqeGlacjlLbmFySHlSUUlj
YkM3Y1lpNjdBdUxLWlMzOHFwTGJ3bVUK92lLtBdwmLeRD/I+v2h7+InJA71fr6vE RmRqQWVHK0FUT3VDbFhLbXQ5WDhLeEEKcDkgV34lUFJRIHRoLB8F2IOvGAM93sM+
5+BDHs8LAAIgu37QsQKUBKVsEp/elgm73+PQkWgW0/lbHnhevIE3NQ== AkmaM4+WRcGeYWQKMG2x6cYCUKFaT1lDXuWZ9kI8Fd7b9gTSnQMs6w==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1d0zhx7u3mfs3nktl67npey87cze4dwsfvfvgaje4rh3gwv500yssckcf6u recipient: age1ug787sgt6st6k82fgkrug2lzltw4qsukrrqqs3w27ewwqj8rg4hsxcmylz
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaR01RUDJhOHRtMldiMWxB YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjeFM4ajhHeVd4bzlqZmJ1
U2tSdTExWndFUU1KUmc0M0Ivb0lFRm1NckE0CmZTWERSMVl1SGtENC9keHZGQWMz MUxYMUpZVjdjR0NGTzVteVB6WFBQeFI0ZkZrCk55TEt0Zjdwbk51RnhYclNzam1H
bWJnSEdCRXFiMkxESEcwMmtaaXZ5UVEKLS0tIDdsdTVseEFmVE0vbTJtVWVnUkQy cExKZXQxWFVLa1pDNFpkcGZzcnl6a0kKLS0tIFJ4ZEdJc3JVaEc5RU1aZk1uYm1l
a0FuOUdvaUc5ZCtkZHBmRGFiNXEySjAK6W065nWaOSAs5HFfKRh8Dcbs4VEClXXK d3RHS3hHSkRKRXFnN21FQmh0TlNtNmcKdc2G/1dhTJen6iT9kUWZM5OzCmDVprgx
TyKkVcBMxsB3axT7wE7uLi3soHWqaI/tKZceqXfrDuh3m+jNHqglYQ== WN1Bl3JzYhLsNKn794887bVAICVqbXqkdpEZztNIS5n/Rw6geKsNvQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1jc6wx33hdhgwhk6nzy5rr8fkgmqxk9um639tk5h632nqfyaw8czskdptj9 recipient: age1529taqdwr6t0w7cvzmty0d5y5593wffl0krt48j6uc4u39k56g2qf6ywtp
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKQUQ4UW1GcjNDQ3BDUjVr YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYa0gxNnBwNjNNTTgxKzgv
QzZ6L2FjN3QwZzlNMTRab1prQmZHRmNZLzM0ClZER3FwcmJwc1dhbzRWYUdONFRr YkQwTjZxb25tQ053Ny9tSW4wNGVYYnlIaFZNCkVvOFNPOFkwQzhYZGxjb0FOZzJ5
YlZRbE9CRk41UWR5emVtY204eFRxRlEKLS0tIGFoblJ1RWtuN0d1Q0pqWVZyRStZ ZjNXam1ZTWZrS1M4cGhhcHZaT1NjblkKLS0tIEdvL1dDTHpWcWF0S3ZqNkxrQW52
eGg0T0tHN0llQnl0cUw2blB4OENGRm8KHvfSTxceDjVizRzXZ/5GH5NRYzK7AH1K VlYwa29sZVloOS9qajJWQWFzY2FKRmsKy074SLdttogXsWycaFX8xso4ek7Cbjph
AP6/JxOP2m+wXqI2xa6kna4k0xaUOZTEo3al59UM5yTcZGlBj87Y/A== MMEhZd/svmnSiYM81nmeaze7qXEUcsZXuSmZCYATTBEGtx/Srll8aA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1pgykvq4pmxhhjrqupcp99fyad2uht40pt79dkzg66cfvsjy5apjqls8u68 recipient: age1zhfyuzlq40reuqlr34gf77852nhs3t6mqfzrqmas8z6sxk7tcfhsungrm0
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwMUIrTWx2QWtuOEFiQjhs
SFlLZVlJM2VTTzJhOWVWbmNkS21MbFZyWWp3Ckp4Rms3djdMWnhKVHh2aEJJN1Zw
N0ZhamZMYlV2djQ0ZGovNGFmVVJNb2cKLS0tIEQwcUlqM1NweWxuWHZBZE9sc21V
ajlqSVVEcUpvUkR0ZFQ3NmNYSjJ1b2MKc6Hj9JU+jNYNzGzzGyIRZjRVGolowl0g
2pXB3VEVTfuAzDTH0hXiKSw8AgJdmNzR0QziXRwgoRIlZqSFKiykow==
-----END AGE ENCRYPTED FILE-----
recipient: age19ht95nv8uhz2shjmakeut8mc3l5spvrcxs3thhe85an7u02r6sysyv457n
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRZkREQ3RTbDEvSm83S0tV
UXcydk5kaDhKdHN3UzZJZ3FlUDRLTkQxSEFNCkcyblo5N0U2Zm4rSW1KUloxUHFY
VlFNbDJKc2dzTEdjYU0zYXo4NW5OWGMKLS0tIFo1MDBDSlJzUVg3TzdqTU1HeVQ3
VVNhMnZ3bC9jQXpRR09wOGdjLzMyK2sKwBokd7Jvo2Vohzipi8vJPsbxoVWoI/MQ
p2K/TpeWO44A33llWVGevwo8QvGu6zR1ANgUo5b3loj553hR6l6qJw==
-----END AGE ENCRYPTED FILE-----
recipient: age1mhaze5tgvc9lwjpml6dnp3xc292337wgm6376yh6tq4fn492ndjq9h23dq
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3QzBuMWE0VGdTMCtIYjZZ
SEo4OWN3ZldnenNFWCs2a0VXN21CK21XWlE4ClpzWFM4dllROGJsYXRTSGhFQjFi
Z3JpYlBCMmh5bjJQekUycWxSanFoaTQKLS0tIGdTWDdRdU1jS0VablNSYWVjdCtm
K3E0Szc3Rko2aFM2b2hzR3hCcDUrS3MKk09b4/pWkp2FqQRbB/Ggf//DN2mfWHxt
ofR0g7o9hg19a9/t34BD1eBfoTtIWfrWTAsZsJ/4+9u3YhFfubeGDA==
-----END AGE ENCRYPTED FILE-----
recipient: age15me6sx0f8r58xh9v7aqrj6e99n7eu555jkpw422txpkqt4r02v0qpahlyq
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQNU5Va3VHazNyNElWUVU2
Q3NCbzVucDVsNkVSMDIyZFhuYjluR0xNV25zClBTMkdlaDN1c2pOa04wVWI1NDNk
RHZBK0dNWG40TkhxM1NQOHBnektJOGcKLS0tIEp1WStuSDMxejVIeTVFYzFNZ0gx
Y2tUdHdEb3MySm5MT3k1YjNvQnpkaDQKroAPWf5iUKvdK3dHdwBtJT/jQjgi0kCe
6Yuk1ZKlS+6Scf0UkIG/NR0wvebM6O+SvNIcngz/3EVAJyU5mAgwIw==
-----END AGE ENCRYPTED FILE-----
recipient: age1q3hu6eh3mt4saey7dc3yu04s7knnk2ygpm9xt6mg6rqtem6l0uyq4wzul5
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJdFNBS2Z2SE9pQUliczFB
NExlWFp4ZXAxL0FabWQycVJ1eG1XRTB5RUR3CllFRFhVQjgycGxxdCtmQ21oRzly
T2FNSEd4R3R3MFRkYzYxSndWcGxNVmMKLS0tIGpvMExFeVFHZ2FMckoxYUhSWGVp
ME4xK2VSb0ZSUDBvUFA4Qm5ZeXF6bVEKLdktQF5NQUipJ7X0+d8KJiIC17i60FST
GYg9NR4NKk9m1fxs4UpZD26tzTtPF0MzCJjrzu2/bt2sx6sbOm/2cg==
-----END AGE ENCRYPTED FILE-----
recipient: age1tczst3x7thwtcz4vce4rg6kmlsszzm6j45kn2nv860z9wa93a92s05ppmc
lastmodified: "2026-07-23T21:15:41Z" lastmodified: "2026-07-23T21:15:41Z"
mac: ENC[AES256_GCM,data:qFhnPra6IE3wyKQ4WKweON0S0YtD5I0adGZVfA0m6BVilN6bX5oC/1j5NK2oHrsz920hSl0SOF8LrpqOrUyGjSRkPsN4kq8qr9bJcrX4URiktP0oRden5LLt6hf+ZRP7WmRXFqixPkPHJnZIoAvkNnTFce7cDq5NEAHkKUEKG7k=,iv:nyblUDGeu3TUfFivYylOn3C/HITj99qiPI2+mh8AGh4=,tag:FrtRzSCylC4wlIoqZdfx7w==,type:str] mac: ENC[AES256_GCM,data:qFhnPra6IE3wyKQ4WKweON0S0YtD5I0adGZVfA0m6BVilN6bX5oC/1j5NK2oHrsz920hSl0SOF8LrpqOrUyGjSRkPsN4kq8qr9bJcrX4URiktP0oRden5LLt6hf+ZRP7WmRXFqixPkPHJnZIoAvkNnTFce7cDq5NEAHkKUEKG7k=,iv:nyblUDGeu3TUfFivYylOn3C/HITj99qiPI2+mh8AGh4=,tag:FrtRzSCylC4wlIoqZdfx7w==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
+29 -29
View File
@@ -3,49 +3,49 @@ sops:
age: age:
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvMGJzZjVoandxc1FCaDR6 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzTFFlZHdGUzk0b1Zva1U5
Z0hKTVFYRU9JMzA5djk1V3VBZjBETDJDS2xBCnZaRTkzTGg5OVBwckw0cXA4bzFi V1UwREEwS05icWxYNEdKTE8rQ3lJMjM5YXdzCkw4OE4xWVUzVWZMaXg5OFo1UG8z
OHJpdHFwRFBGbXZCbzUrKytGTGx2S2sKLS0tIGNSdW1rQmdOL2lpek91N051L3gz WkNwK20yb09rV2VVSENwNWUvTmhJNk0KLS0tIGVPWUhFS2RDcTNjY2JLaWxvcTZt
K0duYnF5RlE3cmNXUVN3V0hLM0lUWlUKRYmIfAAfGCJOFwlimYk6bAvIKLokKufR Z2RscURQdDVCMUdiVng2YWRMSlEvVVEKmyd3re6AaKn4gBjoT0x3e/zJznvJFYKn
OYnDcUZuPlj9cx/FJNC+7/K7WWmfrpEEeNOVzv7JdTDLjWBslTCWGQ== ugKu3EsUX+gbailPmY1ss9+MVtpJFGZa2FiM0x1wSMKm6UJH0aPhVA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGNHJPODlpaHpIRFNVUlhF YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtK2tud3pWSmd2aC95ZkV4
WTJsdjB6Rmd6RlEvdDVXckduUkwxUGhVOFF3CkVJMXYzUVRkVjV0VVkzbGc1UlUr RkVvZXNrK09ZVmZsYWhqZ3JlaXBCd1NRWGhzCk5lelhSN2N6N2VhWEVXUjhLTjZH
VVVXU0wwYkpzU2pZdmlpaVB3enZ1djAKLS0tIGdkeFVrYkdzM1ZNdElJamhpajFp V2VlMm5XdUtuK2d0MjIyRi9xeTh2ZXcKLS0tIDh5Rk1UT0dWblBkUXYzU3YzVGkw
YVZuVCtqUW95R3VHTlBadVoyZUZvNU0K/AtAp1bZ3QXKcVChHfMZVI9AHzTktXIB OFk0bkJ5RXZpWE4rK05QUHlLQktYSmsK/HsVEIhBEIo3qqVWdUJEWnHZiKB3uHVH
JNAPKJAxQycMOYPNFklyslOj+oKmvKm5AdXx4XPYW5qOK4k2HMDIQw== R+nJGuXa2B/oUoxEwMP2YBHwwjLLiJCTYy+aQtiPdTrVq0YJ0HmC7w==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age190htw7prp4vln076dxjx3gxxaq06h0zl0te7cqgpx79vl3lhkaes8suy05 recipient: age1rrxqea6q6pn39sw8y5te63h2py8jgjl9v0jyper86w3ggtn67upqg3ah39
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0a0dPSmdFbFRjQk1OWWNo YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpVVVxWE1nUDBLTVFUaGJJ
Q09VSHhmNlUrVjVXQzcrdi9hVklnRjliWG5vCjJ3bVRUNWdPV3d6STEzcjFxZVQr Vm9EUldwRGcvbXhuT3JPd3N0WXo2S0gwRnh3CktabkdaSndaZUNSeUJGRzFKcjlH
MXZvdEVOVTlkWEVVNFV5UkUzWThSc2MKLS0tIGIwR1dNMnI3dlIxQ1d6NjhxZ3Vk b0x0SFdEQ0VqNWdQcEkxb0drVVJNcVUKLS0tICtyVGRqUmFtRHV1SlpXSVd3N2VN
a1N5aHQvUU5PSEgwVlprcUNEeVorVGsKmmdhYnpOqk3x+5P0h4AfGJco1PGLmAtS elQrVFdhWTJ1R2pJbjdYa2w4NmRmc2sKJHqLYdNQJcna71KNhGF80iS1hIYG1U1w
67hvtBQo1KSTwFyaYQUByfiTwIeMSu50SxvLi56ogT24CNADW6FjUQ== I2kihepJsrmYr76ld9k+u1ZfnuIuJ1ozYsZothE+dr4pV0k8s6wkpg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1ehkswwz2pqaz4svzh7ela5tdnssl8kn6d4vwwxd6zwg8exfpd43syyrrjp recipient: age1adur9g330gua4l6ndk8cqjg35qc8yxwgme6wrl2hpylcc7vxm38q05ejuy
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTTXYwS0dXUEI2WjRhZHNp YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSWXJxUEk5Mkp5eTZXamFR
Qzd5STdlWUNFTG04UEdySUhSODF2NklvNVhrCkxFd1pVSjRmb0hPaFpyWCtoQi9K SXZ4L1JTSGNaQjFiVXFGaEdvWkV2ZzBlVzJJCmd4NmUzZEdCbi9vdWdkVGZRL2Qv
eHVBcUU0amt3K1BONnV5Vnc2Z0JoazQKLS0tIGFrL0I5SjJYYThUS0Y0VVlraW9x cnVXa05xd2gzaXh1SnlMZmtueGpZMHMKLS0tIFowdnhFVGFheURQU1V2M0ZuM1Y0
Z1VLT1hpNWswd3FsWWVGRTkrNkJOZG8KY74xa+6Pfc9lpupTodTBa1hqIrotE2Gu NTJFWXBEYUxmOU9ROTkxWGhYUmlqMHMK3pexvc16BLKjh2meqtNm3M1zyLQ3eEsz
H5+0g5D9838zxf/p7pzxh7rE7eo2gd9JT9bW2I4Pwn257Omblsyv1A== 7C5WkdcSpCkW1lPDGtW7pEdAIL15StD4x7ut4MkSk0BjG1S+RpDbzA==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age17pwyghxr6lq06fw46gwqzhc9ut4paz28rpwx5pmv3cxwak6rgyjsw7lk3w recipient: age1hrx8qj02fj2ea6d4g9vqhyj9hl7fppkjqfdx2l37py3h6pdkr95s8n8rvs
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpUExNcmw2S3hSKytvMVQr YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkR0o0SFh1L2xjZ1RjSC9K
ZGJkVStyVjgvTURPSTRyMXZkdjdpRnhQZFV3CkVtQzBsUjFlRjNsc2dwU0R4UnlL ZnFyb1lJbnlMbENYN0VjSVQyU1F6RDBrL2hjCnRwTUp6Y1hJWldVeFdQZSttZ1Vw
YkhtZWVNSW5zWkhJM0NwOUpkVlJGQVUKLS0tIEJiTWxQT0dPeUI0TTIvRys0Sk9s L0dCS0ZROFArb0ppVzB5WmV4bWI5alEKLS0tIDh4VDV2TFhhaUp4L09jYm52UCsr
RDFmcjZ6UGkrbWlFb1h0bmdRUlQwT28KK5vH6Svg8lFUKqXSzweb9405B0Z91TaW NGh5a3VMY2ZMZVBQbmRHeWsrQnZVWDQKR1UeSZ/EzZEXMqyjB1I2SHELv8Ha/tmI
65G51im23qSOeD+NLBHP+nqIwYrjDlODrgOeW9Up/9rE5rLgFfjoOw== kJKs2WT1RtDhAiTrbty3f4oVrXWSYKZr40kNiP/RLbUcH1s65ys/tQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1mhaze5tgvc9lwjpml6dnp3xc292337wgm6376yh6tq4fn492ndjq9h23dq recipient: age19mn8zrxl8zpps9yvrh4euquvygpp4fp8queg7xc6qhtnl4ng8c9qx02qwn
lastmodified: "2026-07-22T01:15:21Z" lastmodified: "2026-07-22T01:15:21Z"
mac: ENC[AES256_GCM,data:dC/oIqMUHkOh3AocOwP7Gc6XGH3L+nTqJfhFNts1DNbRXsopNIxVBtIz2pEhwnWSQrqPisDLmPHFBwRpGVn01u8w8IU1FKbAKC0J2nJXF8ozpInbjzDOmehqPWZG7yaKoq8cwAnp5XOk+IVO4l6tPxLxkExU5fT2ALuMq+sgOko=,iv:jaVyArpf6zMCFa6J9X1aQMGrmFq+W2CPZdWO6vVW68c=,tag:S+qF8/FkgHc4uW0e4ICmSQ==,type:str] mac: ENC[AES256_GCM,data:dC/oIqMUHkOh3AocOwP7Gc6XGH3L+nTqJfhFNts1DNbRXsopNIxVBtIz2pEhwnWSQrqPisDLmPHFBwRpGVn01u8w8IU1FKbAKC0J2nJXF8ozpInbjzDOmehqPWZG7yaKoq8cwAnp5XOk+IVO4l6tPxLxkExU5fT2ALuMq+sgOko=,iv:jaVyArpf6zMCFa6J9X1aQMGrmFq+W2CPZdWO6vVW68c=,tag:S+qF8/FkgHc4uW0e4ICmSQ==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
+23 -32
View File
@@ -4,49 +4,40 @@ sops:
age: age:
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYcFFSVFVxOWp2dCtEVzg4 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwMklZTFJuR3NYbkFvV0l0
VFF3Mk94Q0xVSDZNL1lBSXBKNzVXaU05TnlzCnhueTd1ek1BZ1JpZUMwM3ZBUHFh eUhMWU4vMHpnN1NKVThuVVdiOFpkZW9rTjBVCjJabWkvOFpOSm1hdEdlZTYxc3BP
UVFKZjduZUpUejJ0enBXK0pwMjZKSDQKLS0tIHJ1Mi9OQThrWndiWFE3WTRJREJN WkRURDFEQzMvRFlka1VnaU9zRjhiSEkKLS0tIHZXRG9GaE5iVjg3M3I0SzhtN0JP
Z2E5WTI5dzB3WDFtaFRoUERZa3N6TDQKy2FK57wDNLSPl2/L/FUBUP8rJZuAOqAR UlJVUzRzN3NEZWxXZHJ6RW1oYWwxS2MKonnhq7YDg4v93PZtoaLANDy8mdRCenjo
I0hBe4epAKL28PlJ4M9P77qqg893beZK3UIJQ5O6d5fGPMNTJiIzAg== FjRUzozMLpgWBll4DwRWikejsrRofRBwlcsiIdrBr90f8Lr9pHdQ/Q==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoaEZ5clpxV0ZQWmhSckxY YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsWDJQYzM5Z1JRT3FlVkJy
a3I5djhISUphMmV1RU9DREJsZFpDdktyaUJzClBQSUNvN054aXp4SzNkMmNqNVhP TkVaSXdzanYwbnpOM1d2RFh0c2NaSUFmdW1ZCit4SVFxSS9HNXhVM2gveERGVS81
QUdnUEppMmdENzUraHAyRVJLWm95V00KLS0tIHZuVXh4SkdhVU05YzV6VlI2c2RS NVFQaUV2RGR5Q3kyUTQ5eEpDVXJVMWcKLS0tIHdHSy9WM0o3Q0o1THhXZW11K2Vp
SWttRW9nS29MM0lJT2x0QS9rVU4yb2cKYZ1gts1qRFdod8PFWl6Tg0Ry0sXhKMrP NTNtSGM2UDFuWDBEdS9tbTY1ZWt6UlUK8z5qoi0kGn0ES3m9khummuU51rkR0Sb9
VwttGQj0iDg6UudB+qa2ngFgAc0C++OJebuLMFnK1K+PvUpfw2nRQg== TWT92+BWvPdNrAsDFjv0fgpUKyTMzN72EzHZKAJCIM3crUG9I0tX2g==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age120le4a5l8dh3lyfgvmj3d9ksmej6ajs5mer5y7r0vfg3x9fn69dqf8xgzu recipient: age1jcx3yajjhghn8qh8za3yeu8nxykzlg3p4nrv03vnfvzl0mzayg2qmg940e
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKNXBOeS9od1lBd0VGSDRX YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpY3o4SEpwZEdFQmVnOTVV
RE5BM3g0Zk53ZzBhdW9XUFhscmpHdEV1aG1vCnU0OGZZdENBdHd0Tzd5b05BR3RR d2NqL0VudHM4VjdDK1N4dWdlS0lGR1V6a0c0CmJUTHlsbEMwaEU0Kzdaa21lRFRm
ZGNZRlVSVk41YzgyRnlHK1NUaXhqZVUKLS0tIGFKcWF3VGdibldnMndmSis5Skc5 RTRnTUlGUG9GQVB4U2pzTHdRY09udkEKLS0tIGlFQW9Wd0N5WFg2WUpCQzhWUm5v
aEZJVGh1UDFVUldrRndVZURGbVJMUXMKia/z+zwpfcaILCt4fpy7wsR0QkFKf07d aG1VVWV5ajBmc2o4ckgyQWpWaFVxVmcKisAw40bGQBRH+u6uNygfYpfb7iEgfEHj
QwkV+gEfM3EydhcA7invDieIKbcOUoKKgOmf2sXMgW07CsjG1QXk6w== E+g9n2WeVH8kUzD2O1o6VAu4m/SVuI4+IQ77j9GEWmlI/wgp8wKXgQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1xjst4frdh0th6q8m7p7u9g5af7ty5jqeum0p6z8a52a9q7st7ewqw8yl9j recipient: age1ufg390ydrmma849t9xfkxxl5xvdkk6mngnlzhmy7mvuaje8sgcmsmnq6l7
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpcTJHRy9CZlVWcERMRzlL YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1elpNZXVJbmJWSU85aUV6
MVFyVHlRT1hnQTBsSlBYOWtoSzVyejJIM0RzCmFKM3J5cE1BejhHYVl0VzNTSXdj NG9YTWVBeWxiRHUveStTQnl4NC8rT2VNdHpNCldxZVJYNVhUR2V3Vk41VnJxenVT
dENsTCtJT0JFT3I0eGJtOFMvdjFmTm8KLS0tIE1zSmhBMjNnelQrdkJ3eVR6SFo1 WS9rRWlKcGpVdVJPakkwUTY2a2xQTlkKLS0tIFhFVTRucFNuS0pMK0FNRk1ndnFO
QXJtMVRuS1daOU53dmxGdFIwN0ZOVW8KS0XIDkbp+1W+wUDHkaKXZ84QpcO0zK54 SlVvakczaktUa2VLY3RLYUdVRzFyamcKIhctg0mbYL7OE08dRwj5wMu2x+O8/BMu
FrMzkChowtm/a+RSLVadBGDQv2R4gV89NArP/YAEQa7GK9aHvDlH/g== IqA477+noQ/Rjrszb2hEvxID7keogcDUWMWQzQvdMc22+3mvAr9qIg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1tzsrtwd3p3lrr9g7nv3z5nvmzsz54t2uc6tfqwutp6usmav83s2sck25cc recipient: age1jlltcv5jcnm40z5k0q6hv053k2rqpqvemtuecdwn527uw8uqz4es3x7m68
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2bnJZUFVVRTBrZExoSWJV
NDI2WUluaWJHVVdiR1JBUjhJT0owaTNoL1JBCjh2Y3ZvZXYrSE4raGtrcXVZai9L
cUFERVRvMEZPaC9oa01NSHR1ejBIdk0KLS0tIG5ZNmZqZWZxKzd4c2pYOEhwa0li
NDRQV1hDZ3ZwZksyTUtjcEVGWVpFMTgKmpLn5Ov03MKITQ7QihjK4GsYlHQiAFAc
pI7z78qQv34e7DLLJotxbeWYyX/IOe+DvUm9T8NGY4ujI2y3HjXPlQ==
-----END AGE ENCRYPTED FILE-----
recipient: age15me6sx0f8r58xh9v7aqrj6e99n7eu555jkpw422txpkqt4r02v0qpahlyq
lastmodified: "2026-07-19T23:30:21Z" lastmodified: "2026-07-19T23:30:21Z"
mac: ENC[AES256_GCM,data:kLGE2xawQT7mx+sfw68hmGk5nCEGiEjZrqTEl9B1dtQmTrMwmoVr/1RISi4LfJrwxy31mDgff4lcIL4wIJuM373uk3X8j4RNyYQNTfKEkORT6r8NHeepNs267O77pKGd7OmcM4MT/BqOnB8ELS7Wlf2ect7CAlvUUVyc8icxgZE=,iv:EYLDsHYHZ1XOQXafOTqHHWpk/OBNq/R6IJnOBYV33E4=,tag:thxrCPC5oGvDjhK7Dz87YA==,type:str] mac: ENC[AES256_GCM,data:kLGE2xawQT7mx+sfw68hmGk5nCEGiEjZrqTEl9B1dtQmTrMwmoVr/1RISi4LfJrwxy31mDgff4lcIL4wIJuM373uk3X8j4RNyYQNTfKEkORT6r8NHeepNs267O77pKGd7OmcM4MT/BqOnB8ELS7Wlf2ect7CAlvUUVyc8icxgZE=,iv:EYLDsHYHZ1XOQXafOTqHHWpk/OBNq/R6IJnOBYV33E4=,tag:thxrCPC5oGvDjhK7Dz87YA==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
+23 -41
View File
@@ -3,58 +3,40 @@ sops:
age: age:
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwbWFOSlB0ckJFc2hiQTlh YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZeDQyNFh3dm1YZ2FTMjdK
eEo1NzZSTWovRXNnTGJDWTVZRUsvbjQ2NGx3CithSDVSbFpCYW9KQjJndzR3aVlL cDBWcDE2a1c1S0s1enhWRnVuMlVJc0gyTXk0CmxWNkRJMDhkeGpRTGltaitnVkZS
MHBDRmQxSEpTZ1FsRXFWQlE2aFc1VGsKLS0tIDc5bmI4WWtvcStZVk5WNDhva1dk a3Q5TW4zYm5Ja2FETEhJcGF0N2ZKbmcKLS0tIDN0a1FqRGNOY3Y1UWxvUU8zWU1m
cEVKM2ZCTGdLM2NEbmRudk1SdDNzMTAKB6QCrxxNzazSbif28j6RNIfdfUTKxklM bE9DVzZESG1HTEhVWUdJOTF0bDhRVGcKP6OoyDAGLB9jQ69jpFyho5eaeK9XtZgN
Xip5xoLjTRNOjq561OS34gaMA1GE27YQqsqtgiU15i7SZ7IGefWpZQ== RlSJpBm2Jo19h/crpH9AWXUAIG0BWueyr8mwBu12cQdFIU3IyZT6gg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2MDVmZGp0bEpwZWRLZFFM YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwRVFQcHpkQkNlSDhhM1Zp
ckY1eERsSHFuMnhPK3J0eFRLY3lHYnlEK0NnCmNhQjlnUTNFOVF0UVBLendHYmdx bUZyOFduQXg1cDd6ZlNObFgzL2hmUmJhM1JFCnprMldPQXJNVW10dVRqQTdWcGlv
aXVIa2hmNENUNGdZL2FmbTlNekd5NUkKLS0tIDdwbmFGZVFlaG94UEgvOUNxdVlz RnBYWWFsaVNrMkJpS0pkOGlQQzlJVVUKLS0tIFNsMEEwZTREZ1lwWFJGdE5YSVVU
VHJZenplYVVPa2NUSUFWeEN5bGQ1MkUKT5CqEiCN61aOpvMlBp0effS3bh345E7u ZEZ1bVpFMEQ5N0g0L2RacUpLMWQrVDQKxPzq6f960purgAmUJw6IZnZSnhkzNE8r
wfxrMwMhrEzfnmpkvcafLCn9PMicqLuz48J9ipF5IHLf7MIvbT31ow== CSrFDowKTZI2KRdCtQ5fGhEoWO0ZPgVNxYV0KH7JBttylcpRLm6r5w==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1ll6hj5ggruetgjwjfnplpn5xtq35uhlcdflksx3xmnjm6s3uad9sz70jkf recipient: age1sweerhrga9yf8x6sv0apz4ed4g48rnlcq34rpv20t0rcelwgpgeqwvndzz
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPS05tWG41Z3BwREhxUVRY YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0czVoT3ZyYTZhOEQ3RWtR
ZVBMQjhxRDNwUDBjVE1jNnlVOWZtcE81Smk0CnlHaEl2OTd2UnJhWmlHNmY2V3NQ bVl1MW5vNERxUEpmNXV0MGVBRE9ySnBjT3pzCkM2aUVpZjg0SkNVTnRRMlhyMTN6
SUJKakZOZkdXeFhnZTF6dmxzTGFzZjAKLS0tIFhwKy84Z0l3cEhSQ0NBbVJIKytM NlFrZDVKV09Yc0tuKzFzR0ZtQ2t6WkkKLS0tIG1mbUNFdHBycS9UOGc2cjNpeHVm
QzhxbTY0SkdkOWN5VDU2d0dTMzlGN00KtgobQ2hsilZnvoq/BesaM/RcYbVNHxSh NUd1NThRQlZXeG1WbmR5Y3pTYXRKc3MKwSnE+0bGmxOAQUje6jHxuzIIyD6ZAwVz
vEA80nUhyEdRim8WP/yu5KrgHsEVQ9ryclU4B49F1pINdAVaGRo9LA== b5AAYwbGRagKj6fimsHBUmi4ohyG1huIGGOU8HiUYpu4PGJgOscztg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1ukpqxzl44mnjpy5r96sfuc5sqzm47u4k8ujjh5qdgy6jvl9uqgpspymqfk recipient: age1nruncs4l0ufk7yuc4des8p99c0alfndl0lhsws8tycl5pplfp56s30af5f
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5NUxUMHdrbzl0OEl2RXoy YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlVGRDMWNDZUR6c1VpUjRK
S1ovL05lTXhSNXBXZEtWU2hsdXhBZHlqc21rCnY0a1Z4QXZnTnJyTDR0WVgyYk5m WDY3L2lNcWFFcm1UV3RPMjlqYnBGVEJLcFFzCjFxck4wdlp0Wmtzc1RKNS82MXpK
cHJJZkhoM2lNaG51TmY3K0hZeXZKM1kKLS0tIGU2MmV1TTFTY3QyQVNEb2ZxYmlU ZHBzOGhkc3ZuZUE2UmpUSTgycWdLSGMKLS0tIFUyYjczeUFWU2FyMlBTdzAxMTBE
c1doTkVLZEhKeDJTWmtLWjIrclRUenMKRem1nKPK/ghuipzOLTf6V3Ke5LK5S6LW VzhaVzlSL05nZzNmR0ZjNEFPTXYycHcKfiJ0KjdxtLWsXxsWKzAL+H3hYYjHrYO9
5ExYf7y++FC90IqkAqeIA67l2KihcPE0DqnBmwrDlNgL5tXyhYV39g== BjKknq1ZQJM0sB/Tid+GLqDwKi966MQK+AwHF5MqbsHW7eE5bO1nwg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age15kh7akxlx7zn00tey79rq2g8lgs4j5y77rcnyfxrxap8ckfu0a9sqvtdhh recipient: age1529taqdwr6t0w7cvzmty0d5y5593wffl0krt48j6uc4u39k56g2qf6ywtp
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvZkxwdUhOU3diRVFjSkhH
SmFDbjYzblFUZ2NLRmpiVGxuUGVrWC9BWERZCkl6MVJ0M3dvNGU0UmQ5Nm1VWmRm
ckVjSENWb05qMWtLQzNJd0tUWmgydWsKLS0tICtydnQyUXluWmxwWVpQWVRqOW9L
VjhoelJCZjM4aHh4UG0wcUJMSVFGbncK6B1Rou2axRoRQNXudD6BVq5buz9MJapB
vYMEhQPVCU2SinltqIrgd00qjgE7+oPnGr5pTFSQisJLlN3X1LUbhQ==
-----END AGE ENCRYPTED FILE-----
recipient: age1rf4kj99wuq59k7w8ar326djmgmpl9hcwlnuag07f8gauq8c3y5mqne87s4
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvV0E2MnpDZzNkbC9DNGZ4
U1JLQ3J3ZHo2cEpraFlSdTA0V01wdlNaMkE0CjQ0SDBXT2tYU2VmZjNja0M0VG5K
Z1FkVWhYODVDNkhBR0o1NW43SjZRZW8KLS0tIEtrMFI0YjZIQzd5bHVMSmRSWi83
U20zMkg2UWpEclMxalNhMThlWXdhaDQKX6qsiRZqlFVhq5lP5B/XVqStq8Impvx9
MTmVRuMzFQ5qTs4BjhIqO25Ez+CI2GAq2K10prdSEin69gSTxntkIg==
-----END AGE ENCRYPTED FILE-----
recipient: age1jc6wx33hdhgwhk6nzy5rr8fkgmqxk9um639tk5h632nqfyaw8czskdptj9
lastmodified: "2026-07-19T02:30:40Z" lastmodified: "2026-07-19T02:30:40Z"
mac: ENC[AES256_GCM,data:rKHZjU/MH08ASTlu32HZO9uWmsBYuMCEC6M8gwVhzuWvmablnP05tS2z13XfaWaCEUXk6kmGJKuU0zu5+IKVZgamCF6DAMtxQb6bVCaLsoAm/GSqWQ5VI9eHqgnSSdN/o3ul/33Rf8iBQo4aw8FFAmDVuNz8bfAn0QefFTj0ByI=,iv:JD2gtqRinOY77etg6PUmZNovkYl1Q3F6ZvRi4x7RznQ=,tag:/5IMpWKRVt+l1luCTQE0BA==,type:str] mac: ENC[AES256_GCM,data:rKHZjU/MH08ASTlu32HZO9uWmsBYuMCEC6M8gwVhzuWvmablnP05tS2z13XfaWaCEUXk6kmGJKuU0zu5+IKVZgamCF6DAMtxQb6bVCaLsoAm/GSqWQ5VI9eHqgnSSdN/o3ul/33Rf8iBQo4aw8FFAmDVuNz8bfAn0QefFTj0ByI=,iv:JD2gtqRinOY77etg6PUmZNovkYl1Q3F6ZvRi4x7RznQ=,tag:/5IMpWKRVt+l1luCTQE0BA==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
+11 -11
View File
@@ -3,22 +3,22 @@ sops:
age: age:
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5QVJmWlBlNmFTQ2RUaWdI YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJL25EUUZack1FMzlnMmdk
amdKLzdRVVdxdW5ROU9CRldKanU5QXkzc3hZClBGRWp5ajNFWWcrdlJaM2hEK1B0 Z1hnejZRNnMwVHpmWkFNdGcyeHVsNnRsSGtZCk9OdDhhcnR1WW9ZMEZ1OUVYbm1n
emllQ2JLSkZFMG03WjNvQkc3RThrUlkKLS0tIElnR0tZVTlvTk00OGhrZUorUGJo RmZRVy8wb1J3emJBK3Rrd1d4U1dYUDAKLS0tIGJaaElvSk1sOTBOM0lKck16OUtu
RGYraGU1dUg4SGJGcnVqdklINTdNbHcKeAotrwsYxsE4ktOzUgEaIibRprGf7pXQ NnRZb3U0ZndmaHBZTm8zczhWdE1oaEUKkf6fLomAHoKPhuM4e9q96YmmH+h4VrEj
5GttilLcmfQNBeaA8z870impTdj6EZfUVItwaIzhN0dG9TcK7oJBoA== 2x0rnwBwOoRzYWutB2MVtlsphAZmZ/PK0tEecT2MM0XXayVG/33qdg==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad recipient: age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad
- enc: | - enc: |
-----BEGIN AGE ENCRYPTED FILE----- -----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHbFY4WVp6OVNydnJ4eFc4 YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpeEJvaHl4akc1TFdEVGV2
OEFDTzBTSzlVQkh5Vko4Rk80VURHSjlqbzBBCnRJNTJZRUNPaGNVUm9FV0ptdjVD czg3OVprU3p4ejNpTktXZEpneDgrOEhrZGlZCmJwT1dhSkZneHE5UmR0WTd5UENq
d3RUV3l2VERUMXhZVGRXRmsyR05lQk0KLS0tIC9RM052SHRrVjdyZkRhMk04RWI4 VHJEWG1EekJLY2pRZldtVGtxTHlGaGMKLS0tIEYrWHE0WTgyUlIwdktmNzNIS3FW
UHVxYXp5N0NKaCtmZ291MjNNakFwb1kKB3RlWa0adUCgaIUxGaN8Uzwkak7EtmX2 ZVRvT1dHa1Vzc2RSakVISzdMTlpnVGsKeT+edn4+LUkVtpRUNd/gKX3H1HG2bvNo
rBnOkujIVWMy9KHPltZtfRAQrY7nS2njhK1hFfTSomqdoJMFgH/iCQ== c8iI6qr/l6oxfP85OrKYFDU9IGvDMxSSdbixHtojPEb5OKVurV0WPQ==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
recipient: age1gl5ujmhd2pe37acd0eaar8tmnf2na28n8l28s8xct40vqpxvsdwqf49g4l recipient: age16kqfmvz4e23hmdlqresnyw69ej604s320mmd49h4hm3fhqchtgyqrws0k2
lastmodified: "2026-07-23T23:32:57Z" lastmodified: "2026-07-23T23:32:57Z"
mac: ENC[AES256_GCM,data:l9a/yNRoxY1hvSkLuR4N7deeKue/1JPlSvZvJfCSNbQ21p1qR433BbSDYvfW+kXQXS8GVcfgXSd9ywNzgVvkA5lR1++uYsZBLbYxJ+s3TKWs6/yECAZ0eM1KBA0BEm7cLSsHTOwd+2WspvmCYir++FDO9XRuS3guiMnQBglDf/E=,iv:eE6GVqexQNSiLYfmTTUdUx5AO//wyjSIsr96xAX1pcI=,tag:CNsoyDZYLUt5Seu7W5wJrw==,type:str] mac: ENC[AES256_GCM,data:l9a/yNRoxY1hvSkLuR4N7deeKue/1JPlSvZvJfCSNbQ21p1qR433BbSDYvfW+kXQXS8GVcfgXSd9ywNzgVvkA5lR1++uYsZBLbYxJ+s3TKWs6/yECAZ0eM1KBA0BEm7cLSsHTOwd+2WspvmCYir++FDO9XRuS3guiMnQBglDf/E=,iv:eE6GVqexQNSiLYfmTTUdUx5AO//wyjSIsr96xAX1pcI=,tag:CNsoyDZYLUt5Seu7W5wJrw==,type:str]
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
+2 -2
View File
@@ -26,7 +26,7 @@
# fresh client that has never manually ssh'd to nix-cache before. Update # fresh client that has never manually ssh'd to nix-cache before. Update
# this if nix-cache's host key is ever rotated or the host is rebuilt # this if nix-cache's host key is ever rotated or the host is rebuilt
# from scratch. # from scratch.
nixCacheHostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPeWgMsdaiz4axT/deFc1+0B5bN+GX/NOeW9bbQ0c/IT lxc-nix-cache"; nixCacheHostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICuHUxGNH6ei3BZD+EfZs3l4X8uJNcjQiOsM/G4yo4O/ lxc-nix-cache";
# Public keys authorized to SSH in as remoteBuilderUser on the nix-cache # Public keys authorized to SSH in as remoteBuilderUser on the nix-cache
# host (modules/nix-cache/server.nix) — one per client host that's allowed # host (modules/nix-cache/server.nix) — one per client host that's allowed
@@ -166,7 +166,7 @@
# build (modules/disko/proxmox.nix, config.system.build.diskoImagesScript # build (modules/disko/proxmox.nix, config.system.build.diskoImagesScript
# — see docs/proxmox-images.md). Root fills whatever's left after the ESP # — see docs/proxmox-images.md). Root fills whatever's left after the ESP
# and swap partitions within this total. # and swap partitions within this total.
proxmoxImageSize = "20G"; proxmoxImageSize = "50G";
# nix-cache's Nix store garbage collection retention # nix-cache's Nix store garbage collection retention
# (modules/nix-cache/server.nix). # (modules/nix-cache/server.nix).
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPwUaVHP4MHZXicMAYGOe2ME1tp+CJShr8WocevlGWMh baremetal-gui
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:iVu2256Uzr2ThOGmFvCe48vWxCTJFflKUZ5vFSlFn+NomD69+y7RXf0ZICYOIH9t8pDAT3ZQDoMDw/kbPHxIj2R2U4AAt2H5QmKddTK3YCR8GgPde6Jp//v8NxLv+ZkAfj4W532rdaJ4CYrga9m8rOZi/6FubldUjBAVLVBiHxX86nCMbP5z2YTdHi2/IfEVK4Agy63oPZ0yD0T7QcXEX5F5eMcd6ceIDjJoROsQw5yVwUnjREo/Sgv5mQy9wBaf4ZtLw+eY5mJECIXjxhSkz417N31GB0D57A9N+WrEm8BEeuIbd+x7zdTSnZRsCZXLI3HRmkFH8bK75G9kPpXrpTQ5TT58LwDNuAGUK9K6dS/h8WAD8JRhtLhnEdCZObpxwoxOT2cCredwYZSdoVVirhuQI5EsGiTU1SGP78guAqpXvwvfbgIo2Qvic/e9OPQCHWLO4YrWonc6VUHhh/HmPOmaSrMcCFHE027fGXP8uTUEzOyE5hzmcM0d0WOX26xJ7PttbdKcLF3dZDnJQphn,iv:vpaX2prOsN/4Ke2HvoNHHp86DQpcPC6EtfgWvlgRGlQ=,tag:2Fv0oy99E88owAnB1fkvDw==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsU2pBT3M1TGJUYVlWUjNp\nN3JudVdVamN0TnF6cjlXN3BJRW5HbDFIZTBZCmhyRlQyNkl4bjBjOEhMZVhmQWxK\nVEJvaEpnTDREOXlKV3VxYzdkcW5tUEkKLS0tIHROY2RZeXFKQXBUeTZLODEzTGxZ\nSHl4WFhmOWVVR2NEcUxQSCtObU8xWkUKa/qmGyWYuEjf+BCoag5H9cA4ovW+ro8V\nhGsi3GqPEFfrI+qx/e6JqxazYpwfwIEaMZljhfgoFzg0I1U7OsnpSQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:21Z",
"mac": "ENC[AES256_GCM,data:7xbxAir+/3FbbJU4L4qRpZIYr/gqyoHjGxJvYYVLZ5yZLs8k/UiTG/qlu2zVkN2yfQaUzAu4/bQco5vOin6pwFodRH8BK4fbVIOheExzN1fk2uayejt/wWRiWj8w+qH+HaWLejlvrmbNced9HTiGViAHa+EZv3OUphlZKAdk/3Q=,iv:ZBuC8htjmKKc84kCBRHj7TdRdiPaEGeVMl+8ydOfIMc=,tag:uO4evh0xShWmG8kY/AkOWQ==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO+Ub4qK5Asqgi3A17zEy/+VOQ4ozL85ZBvlJnyhLZHD linode-docker
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:KpK5pPE33YtraIHSylq1MmlDiRvpDdwz9QTHsZCVrGuB3M19y5EXCBnARXreEHQyRgXWgeL4XZUKhR10xzeq/TqqZdKROtGn+LNwDM2f0OxLLxwPoqAqGQ2yEH2WbQdui28zEPcG1qKxzSVH2P2LIdLLghVTVeR1MM+sWgcJC5aoQGjjHt1tgGdQDv/jfGz9gDB9KRlJ6BVCdOdbkR4Ui5QFJX3edwvHRXsZUvlDNoVNrnQnGXUtw9FI2Ma+4mZ06x9Jv53LgAGsVf+Bu5USAn2VUKZDZu6Tfcdg8gb9iDKQrb+1jDq/dIb4/rh8fJwynMyG+pdrLtAoHs1D2K/3itglifs4sX8XDxSotMJWBB/KZUqdI2TIjv3fnBgkENbMFiv2rboWULRKA8z+viP2phS7kwm+mp7bGqRn36Bs90O5iVgifu/PYVykdnKggDbCyTbuKqdoq/C/s2oO7WbQISmjj+HjRrQ4wQd74iUvYjV2364AhwDQ+1L/OCR7HYr/l/NRpBcrjI5Kxaa5Usef,iv:GfqfGPjKxpOhdLJvJWTduYe2FbIKrMlKevErvnxfOa0=,tag:khM375z1e04y/JebLz/VgA==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBSbS96Rmc4eDJkNGNUV2c3\nS3FCYnY1TCtOWXh1MzBPbnRnL09VbzNxblZBCkp5NkpxUXlJbUFjay9KbjVZeGc3\nTzgxRk0yKzU0SFBGelBDZnVHVzNaNlUKLS0tIFZRWXFUVW56a2owWk9TYXZPTE9r\nbmZHcG5ZN1dla3pXa3ZhOUdjR2dyTVkKaqAlfNkc2wzjB2//7DzW7JWg2BZd0Vqe\nO9YttWf4ikU6vfM+M/yiWGpWJ4U8p/3PBmT3ZLQBRGRtcyPby5DxZQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:23Z",
"mac": "ENC[AES256_GCM,data:+arWx20i4iAlFhbzH8/R9jggzJEP2PlEzyBnbAiDYZhp+2vfbsn+28WBzmjhi4AXz8CbBrhhFS6IFmYQZSFyYFuT943qoo82d/8E6p1DoyEEBL856SmkuVKpCq6IpWEJmkj8j7z3yyrpXva0aagReP+8+agWI+wWdNmG9M2tBRQ=,iv:Edqregq0OXRM4AWJ9EOtR/dZCObRepHnP4s8SYYkKHo=,tag:bBSiY7EVtli41w9s6JbQVw==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOFHRs2HkjhOL/Ilii8PiwxGkQNsstmGn/Bs2Vx/OWsI linode-gui
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:SRq6nUjsMjpM5CXHpAMN2j20/2LbdvHfwqOOoWMTDkW+oC8tLhQfKHpXzKFcKVfqiqjZqaS8J1pmb59buDuYvShXm21i+nRTfhzOHAvdQJNYghBKVLt10RuIDdgPuhpi7r9y5hPZrF+jFSyk4wLcoE+F9FpktTPZwrGnjd5kT1btehuwbjQFIgl23L9E7BtJMBOecUNc3xTLymaEi26kWga8zFXQMLiyLLP4ZPSrjhSeZpXx6zGj2iIHKnPSsaYTFhfoWbiOIzCZrSpV6GsDIsH4MDEwNjwk8fPL7VuGFoMJev0vM0IGeoKB1Jn7HIe6an1t8DoX1TZHk9NA0L2IJR7nglP5I9CHjDJzQZ4hOZvmy7PZd0M8P4be5sQ72Yl2ZgsX9y4ZhANJ+IvYMI7VM1GamcQqbuYrzYCc+zqVeq75FJYUW/fkcKpQihWjQeSO/PAzevdMH4H1k810MEQJSbQxeBB6xc7bArwip2vMvrmDAvBeJCkkJlDvLW5acnbX5MMiP+FiDOsTrggOSL6B,iv:g87PWSi9TyN4GwZdYAxUbRpJvtoYSon6tIoz4SC5bWg=,tag:hDem9tgzD0/lg44g3Us0vg==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByQWUxUHpyZkk5aFNFMTVY\nc09vcHdDUFIwUHA1TVhwblVPMWRiS0dTaHlBCmtHWk9YTVRTcGJndjQ3bmhIWEJL\nMkFVdzNmU3RRUGN6clhZNGRtVnRTQjgKLS0tIFoxWnBsa2Y2MTM2SjBjNjhrUDNx\nU1dyZkhpZHZkb2toeUFpS2tLN2Fic1EKUxQ1J57TJytHqIuLowiSyoS/nJPxSoZI\ni35CHWWE0+Y344m/DmdJPspvYZSI3rY1JbgaqjIKZrshDggZVTka2A==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:25Z",
"mac": "ENC[AES256_GCM,data:ARpnwhj3Y88r6FsqPFBUK/Nth/R6Kfel5cVtlKqjkAdK1FB5BMmdxNbbOn2AH7+zlzdH7qRKvkH49CS6P+bfLaIJBkdLanzF0lSzxq8+88fgqFhJi+mroAZGH6OJaVcHVdTytaE2b8xAMeoz07e1Smp07F6Sp5mwHemAB9EHi38=,iv:JrYUaIGeJYLcgKj845irmKPH05tD9z6Rabv1B5tVmZw=,tag:gK5X/dBJvrWF8LTpdhytAg==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPp8ZFWz97BFJJxHE+3w0/RpIXpRV64XE0eNBYl3jpSz linode-minimal
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:b9t5ALXBMo3Y1HJZkIeCsrevIe8ZCmthgvKOU21MKJqV6UZF6JxUwh/ZVZG2aTBDU7yiocGHHoxGOcTqC21RgpEPQTRkklQbO33fvb3bh35pQTxPH8J5ZVgjLgThXDDE7pYcSWClJwxznfJQXiefLyJwxqowBIL2FAdLiWc6Em0lThbcbdlwLhxsmUR375tjRyp41gsYYUZOAdAwlNOVXi3EuMK3l6lEVY14cUJj4JdDGBErPy6l46R/jOkwTeDrsxRWIsOhR+Dq0MVo5TllYoTKpTvQEQQV4pG8v0XrTsjhrPPvbQ0y3QKI6KkU1f50d0eHPKeCrJOe4puQ9K2VAxPnzwWO1UTw3wY4SsE8DXE0wXM2auAdQbDXRpPidOXwyYTR+Rr7DQ7avHUqRDkV9ZW27EadnNQWSCiomdwkbTg0i89+cyDRf6Wh4viYtBH59WFt1qkGX4nFnvSyx84SzH1/uSVjJ2CE7NJQ7/8NWZHVjqDQcKDrONYvP3resCFGKTu3miAArTin1+NQ8HvzfH41FV41BRsMQDXP,iv:OUUUl4lyZPCTQcw+RELE7W6fWoCHnJmKzN2ELJwXp9Q=,tag:jS7TtEsoUipHiMgT/rQUBQ==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArSGJ0VFAyazlrNGRENGhT\nWDhnd1BreHJ1Ukg0a3B1d1hDcWdRWUdUVDNrCkkwQUtNaEZjdnJXNmx3SjhWVUpV\nVGNlQnh6U1dwY0JCY3NMdXppT1oyc00KLS0tIHlMeitMT21JQkE3UFg4V3FIL2pk\ndVVFV3dpU1RMWGEydHl6TjYyRlJQMGsKrGJc51sxBuSFcVj07otUJoZnaPZhwVPp\nSOat4/BsH/NgWN2W1b+x+qMZQhlwwUy/f4oIC0y2XSvDfif5hUNwJw==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:28Z",
"mac": "ENC[AES256_GCM,data:3C/etclYVQ/T3AhF+BfvYxTSCemmhPAHPUUsfLLB/B/VVMYQUMcy8RGpe1jWdtlRfszuJ7UyRsFOgWrpbUQiRTFSDIkTRb6hlPBuK85MVDuEbGCQb8e2ztnYgGBm0fHqTf4c5+QEKoVFZE2QMnAdjGSn1tJmaOW6zcDRFQOHLKE=,iv:0sE1bbWSQabtcMa+mi9ym7/MXEaEKhTFlkDNWruDQ+k=,tag:dRzgBJ//7O1x51M1deCn2Q==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOBJ/vcsXr8yc/dGQNkEJcBwtnApunrrK8BdNJZPk/D3 linode-nix-cache
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:+VpjDTycBuzaUzZuWfHPrXEPXAOip17PxYNbNTufr52EQxfME/VbvMlhvHeh7eysPFduH4IUHVdjulpymlSvPt5+BpE+o29WmwvdRThUEWKArXS+dYBdVtcZppHG0DcBvg6ogWWpKpnn/nyC1nqSlOPXbLvH8A/9iF4JrB8L9wvi8CnvhetEbzbUy9T7PnJuG7nv0GUZdqcIr+srxG+imdGPonpce+HZVAWbAUNo2duGMm51SnJKZUvN17wVqgjbFcK/D7GETyU/XOavHCoDrkum7EUcO0i7/9AvWLMdcEqTXekMSNE9v6ZCoFjO6lbWDORwxgaqiNgu9rFh9PtZ7Ji1wyvk0FQ2MXrRCMjRnxrrm3RQNXbYnZpKjEcXT+Y4jCCEL00SwQAwqcR4GvOAkyA2fog+kvT00Kpu2lr3ZV0flxDxmbDOCHc9FWcf34FxWQxKTpTKYAbh3t+9EHF/fZzrpaB/Lwvt4+9grwkq0HtdV409asKMhPObz/b41U1fw9u5OJ/drsKbDBD89vnvp4+Lj7cSbJVkObVk,iv:h5xMpGCXUd/Jo2OfolPGDO9Rcd4Q9pV4bGnfFMhPhwk=,tag:ABE3c9TNH83Bu2ogNj1Zxw==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5eUQyV1Y5dUdpR0RVUm1F\ndEZseWFRbEhQTFJxQ2hvVlJZVjBBQVRsaUZjCnJBNVFibG5tVmwvNTFYV0IyQ3BR\ncTlZN0U5R2U4RmpVOFU4MTNKOHZqSDAKLS0tIDZNeXF0NDRQM2pNckYwUlFzRVFm\nMkM5MHJJcThqeTh3OWxleWZNblBabFkK1RISRRs4CZn07ounWKO6tZo3OGLY5K52\nW+PAxOpvREtGsQiPq8dQFTxjfg5YyfHkK6o976eKD3Y2VM7pDo8kew==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:30Z",
"mac": "ENC[AES256_GCM,data:ZWtdVL9q67Zo10eiCNCe+bZ8pUqPjUcKDD8M7N6+N4l1jjngkOajJ4wpft4UQOmAmzB4RSWyajpXxwOg324H5KylYAFxfc6QH22bTPY2gaa5hD/NPnpM9kxZlcI3WsElI2xZBGo6n0nD9q5JV254QMBPLlg5EkAs7KXPY1hXFTc=,iv:LDXaZAmxIWQzSBK8KdNJ2nrFOwr3+vuiZDRW54usGj8=,tag:dwXewWjVBtc5qDaLyFz4qg==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPW/X9Mdrqs0wLR7XbEDTihk7TEkNZ3LcCeXoa2ITSDA linode-server
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:hzI2cJ81+Psv35MMZO06UHpC8F9FLVkfUzn0ReuR+WoUds8D5zhDFbQHeR6ByeHTuK8Hlj712okhoXh6Vm7l3WmhLJZkr8IGLsV9W+P2PYityjuOtBPrphrKUeSqxJfjQ31/EhZLOqw/508XUmQDNcs1/n2g0TtQ2UQSTAuO8r1OlRfRSPizvVdj7lu+Vqg3dDBRETSOJAYIh8XQXoQWl3M4dS7jOYUvgc4EsYOWrvPPPS+8xXzctBPeToasY0IyWtLzoeNajCs5EpH5u7S5K9S40/vFPa45Ic2IxEkb1HHClyN6lXWbnc5QlJ5HPrq+2YBU2y9dJHd9DQiwEbcEBIvR3/Lo9puVDppXx9kez7i4XR1UHU7WVu39nQZuP50BQQazj+eM0/HJapXfjoqtaaq0qAGN4onnmRd0vEmrZZl4WMXmx8m9HhxdNJoZ4C2Wb2skdHZok88tPzjd4r7xKzVhAbB+wqS4LML6FmCNk+m0USQXa25USXs0JDV7lgiTFQJPE/67JGa820WFicGs,iv:YmOeK2Ha3yBXumVO9strgLgqNmPOcnqwUDJv5QNR1WM=,tag:Z7kZvQLDOKx3uX4dRXJqeQ==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBOSWl5ajRSVndlMHJJQTFp\nYzRRU3RQMEVzazB0Sm5FdWZmMzBRaUl5RUFvCjgwRUZWdUpvUjkzWHFVdThIZ1ZV\nM2RMNy9pUWF4VVBCVHBGWHgwakhKQ2cKLS0tIERIOTVVZm55QWkvL25SRGZkZmtI\nT2o3ZEJNQ2hmTDNoRUx2Z3UxcmlyekUKLOajmvRfLdCJL74PKSgBtIXDuAVd8NwM\nh4BtDs0hONOz82JaBqFw8Uz28hVFG/gcS80br1o2klqPd2gN62PCVw==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:33Z",
"mac": "ENC[AES256_GCM,data:JJC+12gTCsDVvMWRtL5cj50kf1n6Xn2j1hNBnwvZXUA9Pdd96SXt61U/Q80h8GZ7Ycs/slsV7h5f3g6+8tV0PcdBM/vy0vPM5qX1zMySyMv2p+dkJb9MwQpPg2xAQ9jjYM9237p5n9nysgu74h4V7ccBqmBzp764bL9wx6hEzS4=,iv:U6u1GFvoxaqxmHv1zCht24nW0ZMJR2b4pZqCG3bGNzs=,tag:MocwywOBg/1rUuoSADuJVA==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJEPWLRc9oCcn9tqY+a+yMJPGaqDx67vNpzC6mpyXqFp linode-tailscale-subnet-router
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:OeRskwkEPIKfgzK1409bt/5Raw4HNgUvkfB1nBUlFZghjTn9nuZqWoIjclgaV+qcZWqi86YHL2QYU20fAOOINM8pppknh4iH2WLgkde8LCxFdijWq6hn1TWaLOBVOn5UxNNj/rSzyXqjImaKOXbxrOHJ4/snDaVJccIhqo7BfQfJlXf1eHyuqdAl33y/2NRYxd/6+cqntlALT61yNFEpX1iyoO7NyCsniYYvfOU3hzlW4Y7OLQT2wiVuns5Tt/ty1IRk2LU3AnpyklMnSalwFSBs4CWjzow36AOmmDRL/fUiQ2I4JJjsYmXlatvl1zYoG5Cr0utnclr6juUkpb7W4Pa4ZtA4xQIpbIBpo2b6RHedM9i48RA+JhrxaFnyM8Qhq0HJAd7usVRPtXLWEP7uHs89e8emSoam84gW27Ayyv6zWEZGungAzghNLcCEvc0JlQXYfkVla/gxCIKTTUNtch+aGAn/hSNNPXQH4E2Jcs8/zdXvyHl67Y6okrmcytn9ZdV7nSJ2HbjzOyLZOomiix0uHSeU5/t9BlhfBKPeHpapt0xv+ZAujKAuXalkSapP,iv:3doK6Rx3p8dLHBmqa6x/gi2qdQul0m5Er9fEbYaHa54=,tag:k+bzF7uBu/gt+Qt43vPQkw==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlUmxGdGhpQmZXQmtURXBp\nVHdhMlpHWjh3R0lvSzNJcFZ2RFhoMFBYZTNrCitpQXFpRlU5RlpOSnJkQ3BVb0I4\nWWt5aWtPbDVHRUlPOEpLRGZTQzI1U3MKLS0tIG5semJrN3RIMjcwdW42NGgxcWh3\nQjlKL0N6Ly9ZWEUyazVINC9YL0dLeXcKh46uyhNdyOYketjOHBP71Ad5Yz3TRb4u\nVZrcMuXT3+fbTumtvySu7sCMh2/UsKJxVXGnW20Rac7MrJEk4xX38g==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:35Z",
"mac": "ENC[AES256_GCM,data:UeBDJaEaiA/6FvmTDnyKqXqNH079RG/W2iRY/92i5DRw94+DkXckcQbv0Cw30maRAJnu04JUhPSwdCsrDM8m/ajqZYTK/VuWKVt7CMFxeA3quHYD4IlPpvh9lLuBLZ5/7sfvRMxRUN83Acr5JqFUiX4wUGcgMTUdnwU+t96hDyw=,iv:LoErePhZTtvxvxNwyllTafup/Q79xgpH306jx0xwUjI=,tag:qvTLlr33yi2eVwsQqAX8Ag==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILWVWRVrhvVLI8e65/nQXUXoM4PctQDC2HQpA3VHMFKE lxc-docker
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:4ZcerznWIChXPeVnf/LxMC7XMt9icvoTz9U7ysgRkWdrpdiV1NFQ8k9QD8ORcoTNEhFKMHTmr5HCCsJmnq9RIxCXrWPSPVWGXq9PvdjzlUv8BaYRH6EUCoSsbjqMgi/YQKr5kBV8WcqFBaHw3txm71Np8r++bb4p+Kx8DctupC0EVybttLMMSmhH+2a2+54gZ2qDIQSCApnqaal7JeuVMLkitkFOeekpuotyt4ZGBpM6v/CggSEQhQ/SuuxMRQEpLDS6ccRbws5Jo0Jz7ZuHfkufm4JC96X89pPFSSTK72u1xRiHqNzW0s+oFm8Bbjj5p7jrx89itgodlr9m1bb4RWxScEtGTrVY5Um7sVRIIF+/acez4GwAodeE5WlzXqGsrwPRJk5iYvZHTWjBAZCQC04lZst1fHhC5PAOsslwSfWLxXI1QD8jCki/hFXfZpxqfRTZ0CFqKVcZYrgPKH4dpkuUsDLe0zAmzraONdT55QbbyVV9PbB4+0ZJlwYIrRSa0oMaiWkT8imj8abGKXNd,iv:PCDcSAN0GkuLcDkqapMjUqQBKVYJte/RI4j8cx0X0RY=,tag:Zpb0GP9OxyC3FRi4BwDwvQ==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA2aHBnVGJybDQ5c2hmOHNO\nRlZSMW80WkRJbjJPdkN1RmNiWnVhTlRpSlF3CmVDelNrUm1tRVJrWXNXOWdkNDhi\nN3puamJEc2RNLzFJYnMrSTE3VDEwTWcKLS0tIEIyYXJLMFd6YWFYNGlDbWM4SnJ5\nUGFxck4rYjduSCttU0JRWVI3Q2k3a28KYIgRQ/qXgBlk1XjA34keP8d26EvPpLAI\nCtAjAI3YiX/titHYUPA4YYcrqnVDdgEfS6Z402AXUzY2yePdzMF8Mw==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:37Z",
"mac": "ENC[AES256_GCM,data:ZQlaXZFog31nzrGqM2Sj3B9FjkdNhEMSZUNC7QLXcG74ny4XSkqciOdruXtQ6Ay5i+1Z6voTooObSJmKfFNZbU/6G165jXzmuvQ3X6DL2SI8HI6czt8bbmIiUCyEeikqeIf9UF64yuEYj0PjayEez69rHGbzUuAsp2t+72inoq8=,iv:+MBEx7EeSmExE8of9Xtfzas8fIff0E3SppfsDgopggs=,tag:ggBB/OEIeAmKt7veKoPHBQ==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAChD7dJfSEYgyOulaehMua36lWswcAsVusQWmJ1susx lxc-gui
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:VEW/6Sml7VnLGbI9lrdeaqrvVaIBtfOliAc5FgX/hzOa8xoXIjscoTn6CbFTN12lDS2/PSQ4hIBgGqIIAhhfOwYDnZWejNDdYhDBqGO1QeTnOOMAFDEdVQeAcSsfYzv3LA1RFjcKJcP3feTE0MNdTYAEhqxQ4oQ84hmM2IQ83Np5Il9vLspF4nZ0YmzCRKfMksQ8kPBsdpG1K5EDMVlH9ZYICcleMB5nkFIVfkbXgrhvtKoP2/EURVOH0k8u1TntGp3ip8kf63qrYP0HjCUi7GpU3fTSD+dFjFMkLtYIftSqScvJmnF/8Fq5yPMxnq7Y/Tdfped5PK/bNyMgUYV/C84CsN+RJey5wv0/E3Yf1hjMEVVZza9zw+MfINejyPnbY46T1uTtmRXPN/XW7UZVOkEFbqVGr8WFoKw0odCgCgXmAgzcbTg2OIb5DMiKo3+ZWxm6xg8wmTEFoY70v6VEa5rRofKAiteI05V2OkHRpQWcT02cPPjKXUkt0KuRFYI+oHsYRTac13G9C9DRofNR,iv:4cEUM9xBLGGzyVFgqjp651n5gMwUOzaXbMe+8/3Vw8s=,tag:v4t9YOfFGg+CDJibQXhSyQ==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWVVV4cTdGSFo2L2k3Sy9r\nanVQUHBoaTFyUkxOS0swTjJZRDA2UGZNemxjCmRVQndTZWl6c0hXRWUybXRNd2cw\nSGE0aTlGWXBKdmdRc2FqOUxoeUZUVlkKLS0tIHRUV3YxeWlrSFFvWmZubnRZN1Ro\nemR3S0RSLzE1cDlLaVI1TTVSeXRWWXMKgDgm6pvONdl5J5jfOR/t+Z4CDF+bIX47\nm344Ba+3ucI2Ecktr6+PWfcJ3Xp3afZQhOgjlg+A+XR9eN2CIwZw+A==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:48:12Z",
"mac": "ENC[AES256_GCM,data:P3VpYl7m7AL0bl71P4EqCEz+2IAjpDykzq0nkoPQGq/uTO75OcZJXowgdeUy6iat1IkX0vsuZ6kSl+54hQUs9ZOVCFku2QavpqWUjXgyxi15GJJtC8/4OKJWSliPHyObGRaAq70KB00LAZEP+QsSLVXGraz7qzidXh6HVuldIeA=,iv:+p9mckbRciUG2JoYEj6/WaWpqyL1rqlZ2aIh4aZH6AY=,tag:rrziwm7l0jNNmWFnI2GbEA==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII6hAokaz4VQ1MHBQ80fhlT7WFshzZph2GDt7jf6Gaj+ lxc-minimal
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:c4zQurvcJbIbjxewdE0oldS4iFOaawY83OJ9NbyH9Zri7Rw3++dsHyAbleN3PEKy/u75FVW+oWTmAx6dp/buUNfmxD0OYpUs8+ZBons+kI0rpOwXfag1Ns8CMGzyk1Wr0tRwmjLSMN7h1x+JyRu3eMe1YOgJOvNg3+kITars1DGCe80H0YTBZsU/XzzWDhW5g6ENpLsl4Ds633ohyHNzwdmSaXPuKNiv3eL3VihGDD1tFz4eq/SZM0ikJ2e1r+iyPUts0JW05BKIUnVqu6fNGMolOGtEiWnN0GStzo4wxPdv+jMK2gyFZNeNV6ei4qA5hXUe5kEEGTqmQvhqxz4uFzXZrVNkOT92ZBq2azZv8wyxPlea3yLBLicu+OR5CHDpVcxioV8Y/orFQ+XRjJhPVif0xCXoNXkBejvVLAQfH/7KpezdzLENiCqruY/WnW9Fs/+A5Uv5VxaExhrI9xWZ/ojMDdCJJVEV/o4X+uCg4/HIiows/dMOnVnvbRFGHAuKp/pCGGNmLQ3Ys0TJXDFF,iv:SePxW87sw+YdRbc7MPwBbh26efQrBd7S2FDrjaK/5Yc=,tag:8iH8gFRDTMegsbEdgd2boA==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKanMxRk43RGhvL2Z3MnpD\nVTZvV2lRNFgvaDZEYWNDbHN6akhHdjJHY2g0ClArMWkvWTFNeUxMd01lVFF4QnYv\nWTF3MGtWU1VFZkJQTnZPVlBHL3hsQ0EKLS0tIFBWMllSNGZXNGpPNythc0MxN1hE\nMFNodUJyV0xDc0JrVFQxSGF3RnZXVk0KyskQhrNomMc/0LKfuPoVwdUWVPMh5NH5\ntf+3izlcaDmS/BDFRHwAZRZ2mVFAj1g6JtPZI/ZSQQt2KbaLdEr7yw==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:40Z",
"mac": "ENC[AES256_GCM,data:de0rtksPfBeVSAEUkyQ6xez5FRZ2I36JjwiOW3QrGwFFa0uZ9TPe9uaYh+l2xj6fei6HS3KLD4WDNq26GTDh8mjKlKIINOjEyBBqq9R56+UP6lQ1XDApiOTK/w22Y9zmEIBAQ7qi0WpzmYl14VeOlK/DxpPvog3SCMQP4zHW+nw=,iv:qO+2QH52v0oQ2z/VnZUqSc3AzJElS/pO+AalEudmFY4=,tag:fZboSJ7Ae68eDqf6u4Gqcw==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICuHUxGNH6ei3BZD+EfZs3l4X8uJNcjQiOsM/G4yo4O/ lxc-nix-cache
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:nm1TQswDfqLgzwOZTpllM96/nqW1xyOoUmbmxL3kEbygnU9kKruuijhoLMSigti8Cgb4onyI3OFkOaNnBShGaXsrFHwIH4rwd4CRv4mHLFTqs/hTXxMxo98gKdhJYuCLXOOdhWrOfOO0GofmxLEX12AUhjS/cxtJWXqINIbmOzPm0rHVVx56r6kj1MojGssV3y68gbioSF1StGG+OIQIXL1LRBWknyTJ+NK3aViXn7oBglHzL9D74Lz5v8iFQQroZ4sBPi+ov0CKAFaghRxl7T7jGA9pO354ZbLdiGmpqtYFazCGq21hwjpWyobAog/Iidngm9Mz0wleB1pEpkCaEpUsjVYQkAoknITasCGhwpmFH0F9v8Du87rJ4jdWT3W8qh3uPlCHw7e5QwQYE/I9v1HU6Au+liQJWeAbbwt9wRC5NMETCqH1PDDpRjt19Le2ECXjstBRR4b9wQQkeDefgcFibQK156/i2twhAqIgz+RqIZ05aQYfy8Kfj3c7G34PEdOdz1P3bsAgISVpgMQO,iv:Cth8/Wlf7vJo050aDNt1XyFUDiihyuMxv6WH3yEZGXM=,tag:qM1/MVU3J2Kso8w0fzEvIQ==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqWFF4ekdNRG05bysxMGRz\nbkt0QmRKT2hsUzZWVXRxY0ZjenRXMXhrT1JNClE4WlRmU0grRG5BSjM5NWZwc1o0\nSFJ3dGRsR0pwYlJhRkV4MjJqNVFWL2MKLS0tIFBSVTU5aFVZNm5qZGtIU05XbDd1\neTd5Y1puYjBkKzlVRDVvaW9pL1B0R28KZbutmxm33H8qOE0jSDr+mrx6nFESmKLg\nTfjZBp1W0SDRVJfUbRQJvcp1ECMJIS5cWzXDoXmLnaeLFzawBPbIeQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:10:21Z",
"mac": "ENC[AES256_GCM,data:IQ/XcH2boEt97exSgTUxuZwH+IwFHA0ota3Y15T9DP3tpN/Tvp4zWOqHYtiPfRf/Ea8Si/LDHwlVnuLIhFYtrMmTzJjKf8Xa+eN2bACzn1BAGKKkY1uQH2tIJ1HIxC08x0L7YhkGPVNMFcu2FsNB0bk57+iwUb/w5p4m5O4AZ6Q=,iv:/BkRQEGvUImCewxsfrpV+EYZVnfRlIChASpBXBAtppA=,tag:DAkApHsRza9DXOnKhhVNNA==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGY9pDiJDumHVu+IVu/11qEPFmEbwoYbJqUvfwc2LOdo lxc-pxe-boot
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:bqh2QqW6YynFbDvr50/akjZN3XL0NehZK0+bNd2c5Mk/GzPUi+HzoXPUbMEvs1WXWWXTcIHmLuaCO8x/FbZ2+T4g7GSTK2E2qSt3CwO9XAE4ODoFc6KM8Yg6sK05Zj1NotUKZ8lfjLqEhaIL9g4/Ge+pEWlPSno+jNtzPE9QJxY+H7kCbjf3Qn+49PXl5P2Y/3Q1+rpbJ7kvdDx592o8lKsftWgZQJc3gDWIhIPFGfyAZr5nM5j9cioALbICJ8epVfHJsFgNMuxBi+vONltjoxYN7VG4Zqy1U6ch6nDY15FIhx+3w0xAo/q9dakFYSR0Q+MHIWTddQUmmWYUtI4TAHDtLC4qAKmP1PtF88hDHmO4NjpM8LuRt1giun+naJiIWmu6jsifcbMB9kmai2KjCU8+29/r9yuTySFA5ttIWTM6D8UA7wQGDuMT1df1fJUu9JQtVCMPxjy3Nulu+KQOwat5BxhG4yFuZgd4VRIjOWRpxFtIqnjyXZvOdRWu2Fwj4pGSnyJH5W69vo5seTnL,iv:3jnfp5x0Sm/bA5okh3LmDw2LaVFPD3C1W3rrkf3lNSQ=,tag:5CJ5ocs6DJDXNtwexbZ+Rg==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBuVk5MejJRR1RVQ2hPbTRE\nbnZQaXpudjZ0WHZ5RU0rWDBCamphVWJIaDEwCkMrSkowSkdwaEZJT3FDMi9FZE5D\nbEZQOFg0M29jajc5Lzl0TUI0YTNCZ2sKLS0tIDgzVy9nNE9ZSnZucTVwbDZpN1Ja\nWnVDaEJUZjNlUm84ODZvdVZ0OFJuaDQKeMz9nFIYwVR2DTkqcko+CkkDOBNAUqYy\nfWTR1bYB6TEIDncp4T3cTH/EOf2xJuLxYli4QuJJqxWHjyho5+csbQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:42Z",
"mac": "ENC[AES256_GCM,data:A9Y/BtPGkaafNDiED1d95xepHdgmu/Y9a0jU1+7KqJLGJhxQKCExW31PYgJ8dDB75BNGE/3ZgwU1bWbyqjM5liZVNmMWjRhJTJEvC2dWIupaO3RcReoiRMXIeVh9IfNeFZ6ytare/kqpUgWyYbfCAni4/a5C7W2F0SsdugOCrKA=,iv:CQOn9b0Ld0gbKGIycZA7duBq/jfN66l0H58csTq9XXM=,tag:OSGV8CKZ7SkesEwuPIf8cg==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILVRdEddGd+AMNe4kXbmA9UXK8JRsPEuxPx1vhNT9ZG8 lxc-server
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:JTmbN++hNMddBdMKIdzqZ6hLD1So9hyAzwbce/QvqWBsa1At85cMRUx+P1Yi4Sq2aXCg1Tf2g8XC9DXsVfMusUGEHrHbJApwkia/xHNMcFOohscUkPltXnm/lRilm4hwJUv6ay6UyLvIqMQAzzC8YWebHkKo00rxRxQWQ77wjmUG4dJeujlK5wMH0zTXUkrf7tIdJ20Zzg1BrAU6s4FYLvRvcUSW2ROu+sF2Sxgy1qVNJfDrRnY4REh9013tTSB5IlgJzUSQLs1vHr7EpIfmA5MCCSkZjplGCKvxkcNHB3aaOkrxkGa18JeWoklRen31UPU9zMUhTjAZ6VbbIagxzaFGKVP27cOiHQPNpEIMXwaRzoprjBX2PJ/Bs6pxe6hBpmiKOb40XdtxFQx2rofPcbQwklTpM5SFwvK+/bgawy1m8O3NHMhcw1qvd+6KjUdqy++/ivFC2Nyy+VfUDyGu0JLK3X+YqU6JXXMcUWOq79pVQIL3q1ofbhVHQ8p17+V3wQQUB3kPo4EEcKikI7Z9,iv:TVYVDOiTsgXaIcuJdnd3djPWXCMDDGjpefAW0MR+7Es=,tag:jIYO1oatbvInp77d4fC8mQ==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5QlNzNEFqSkhKTVhWVisr\nS21zdVBZSnpITDdrb243c0pUdDR5WXl1bFVrCmkxaWR6U01sLzhLL0MwRGh1VG5D\nNEFiU1FtNXRBdGtPellFTXYraitCUTgKLS0tIFdwWGFnMWFCN1ZvK2VBMWR2VTJZ\nRlQ4dEpmczA4bEdvUkNhaC93OXhDTkEK0QBkaLV1mbTMlSnjmN4x+qljGipHM/DQ\nUlmBYyi3nEOrI36I/Mm8yoBZai/qWqdg6IG+sxDE49ZOLp9PhpAWww==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:44Z",
"mac": "ENC[AES256_GCM,data:/l6pa3LE7+kFYqH2pv2RIcYBycLrZfpb92Al3SIU2tMeFUUvh+C8q8P9CgmAaiQuQ8S2dfYIJVx67zc7cRqI/UL/dFvDSv0YaMTGQ8Wn4fIXSh01EL0f/QVaIfb+uuvyEsdjy2ScWTWcCf2ICnC/zaMmp+xP+MmR1DRBM8KPIkY=,iv:xsmaYZ6dyHLU3BVfT3jxbfWWeBvYKMT+D9MtRxF9jlo=,tag:VzOiutHQJGHqb5UCI+cxSA==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMrWIWzBAi0oQv1S9vEhQCRXgadRcjC85VBgMLnS+iB9 lxc-tailscale-subnet-router
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:4Ff2KrOzngnkgek1JwfppCpulOqRVe6iCvZza7lf7caBIV8wQYCIxeUyU/cJhzsvg5juVQ6pfGTnTLm/FH99C87Jb18ZhNjaamWyECP2o2i2OL80DCmksOQbVC8qGYo+ZeFBFD2YPM1sYCpQnfLzukL43tyFjwHLZboPlIxNhj9vv6r+whqvNOouYEPkHFTmReBq+NQ+yKXWj4BWlypY/T9b+SdB4uVxtN4X3YLkEe8YwGUcNDTZiVN8O5W5Scu/RBQvpYAfGjbLoII5QyiRjEr4bvIy4IiZwEIgbhcUQDtQqyB/LOFXFbQ1Ul4Z5BzRoqUXhVjczBjZADX7NudEfKvrC1Mzqw8ZyvEfTCEEBLzSLKpid4t5BPY2rMT3AZh7wDdiF+wZfpeHHK8FT9HKUtPEygKjU9e+RJTsntht7fgi03tDRr7PWfqpg2ck7NZSENcysVUxTeQ0AOG+PqNyZtko1WNpPG9xL/TbkKujGWYcNpQlIQCiPwnLe5AkkNck56gSPog/omeyJhvAAjNwtQEVlzbnLFDuGO2tfyEAzi+4Tcg=,iv:al+5uMqohHFSpJvsC/2lfyP2hc6R0sn9cB2D38BnYbM=,tag:IZQwkk3kyHVlCT2efKja2Q==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4ajB5NGZweFFZQTRpNTFS\nbU5STjJUaXNGYWN6SWFQOEcydEFDNm1kRWpJCk1OMURjWWVhMDNuN3AwYVpmVzly\nQmZpdGl2amtMK3NFN0FHUlFCTFUxbTgKLS0tIG9mQ3JHT01wbStzdkVpZW01TzVu\nMEhUczlOYUE0cnBCU1lnWlR4SGVJdUUKVI84+94n/NYyfhSZKDUFWPgKJKdMj1bo\n18uPdhPHisqcrwxhuueppr5s5aYYL+K3GVARQ0jHITkouySQVx/7gg==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:47Z",
"mac": "ENC[AES256_GCM,data:SnhysyclReIK3SPZGqFlK+EPz81XgPdvDVzyJ7KSOwwxlylOgQ3xAVkhgv9IQsU8c2Hx8MI8bMKPc0vFWq/VndfRqORDR+sjlcfGrHj+GQwHBI+QEXjCVZdeXbKUgt9XEjOeJB0Qtc5Rb51sQvo9C5yaQUpXvgM9TNPyX29768w=,iv:8EmP7K9+eVPgRSGIuzv3AIj5ZmTJeVox9vVa7z8HjeU=,tag:cchBEBXO9yc5GHv20KzpuA==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOAzES7wlgL7gIyxOHbmwhzI8SJ5uMSEd26u+ek6knVh lxc-tor-relay
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:Gx0ZxQ4OZUKUDFEPYjOEgTERHOFDYO+O36qA6h7+wp6G3ld4hTs4NhODdeCpz3xq7/EZ09Jej/ZZriIhiTUp7IjDIjR+hOsIWW7XQNUqBDfq/nO2Fz6nOd7ph1fgQfaSUvmUBo5qr68VmDs6ArnkDJ5TFNTwqypC9Ou/HXvKEvqfG+XjL8FT05ZG3iTqH68O73DlBhePhVuWby/8txyCL+fz9pXaqyFIGxmsMDE3d0Itveyi/bReOnf4g6UvCjnrA1nMpTB3rQLzVF0TvxfTv9XfaeV3On6eu8M+qUk9fqv0ytKS6+IcozuCax5kLMIGz7Y4kbw+yolgotWaM33Wu+e+UAZMHSksWAJAI/zqxHwRcyY5uEH8DJdqz6EtA06Yssup89iqMA4Nz88l+LpZ+1BPeY6PlhbubP+X1x34mbDixO8EguuUG908vd2jKi8f7GtYzDXm3ECN1iodiAXVJZ3TEGfYPfhvH6p9kSkwS9Jug1GVP5pDaPPkwvtsLjq2fV0HB/4qJQAkVJmwRz2L,iv:3sN+t1HvduJjlj+l0Jp9jqWIh0q1bwz4WiPPDLXyNzg=,tag:Qk3P3xoHoOhYQL3xzmJpaw==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBETjR1YzVoaUtGTlo4QnUv\nbVE4ZXZmMmJ4ZzFhcXlaVWNEWHAxVW4xSm44CmlOaEVxSGpKelZwTEFCcitvcThE\na1B5bVdDUk56L2ZMVDJNVkpJalZ3eTgKLS0tIGU5NUM0bDVyaVc0WGVrbFF1VzFx\nT0p3a1Z0czd4bTRzUVRURmh1MzhUelUKaaL1TewLGICnl/gdAFQHHBM/ik2qUjAy\n2dNemNJrMuX7Rd9DCZPeFIqNHRVX1sIhJo3/dC1UJc235l1NLcSsSA==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T09:28:42Z",
"mac": "ENC[AES256_GCM,data:h/MkejHqwuJbc63HyJs8ki1aTt7bx7HVhsQCI45hHjN/WeEJxiZVcmHEXsL/dwPg/Q7H8OImC2sl6Gt7FtGwfN9O6JwYwl6BxBBoD+lZscgrPrNqp1I9u3KhgyOF7ziQhiWH2Lk3/OVa9kuFs3xYjcKxVIA+vW6VB54QkLNBcPM=,iv:BjtKnZ/B7RvWq1MBzWzudPGvntXnk9kYpzULTpK62EU=,tag:f71WOUHcjbcHHC7KIjHqrA==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPq6ShKnUQC7DCta8IKhGGLQKMqF9kzI7X7tCy1t9v4+ proxmox-docker
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:MHkOXJFt/IIHxGwEypEKTe63P/6Aieeon0aWl+AdSB4aU1UJk+nYFPix/nmO9VwKJF+nQmIZPdxoa31DxMS0B8OMPLfrX/WiNQwK4rhoiNh/KnfZeEt4XMjhCpnijL6Wzx+PnpauEKHrBE8OUT97Ufglh6ufaaRM2om4X5+QuF6kCs8THjS5Qf7BVEkGG8JWNiExdpu960Lll50yTuZmDXymP36RNSKIH228H+MyXahS9eedgRX8UmUt69Tt0omDujnP/f0q80+4ofUkG7dd341qV4ptN6dDDZhryZr4+sFJKcdNvUbjEqin8JnzPGPPC3oWWC+vPo7RFBjbsboI8go9L9A76WxifQYeMTfnq1GUGJo0+GN6KF26l9jUO6ywOQcYA/KzK6A6aZWVyEjd9H+3ki01jFV1EBrvhhZKi5tgzejBx8f8/xSX4M9m4q0jAYQuesRb1X97HqZOLTkQ0edb1VeeZUTGjcZJMOQ6TCuZwhhCxjX1jUniqdjxIugZWhVMke6v4sqZShEVVuCWp2MBjQgp9yhDRukg,iv:VQEfOh8KI/VjEzRhGO2iuecB+RLvOw+NrxjrIti2Mz4=,tag:zcOhYmoJJOzgVylIm/MnDg==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhbXRoKzc3UHlMM3R0V2FL\nS0tBTmpUNkpqcjVmcWVsbmxkNVJSSFhxNTNZCmdDeUVsdTZaNExvWGN2cFV6YkEr\nZTR6THViTy9rcjdocjJUK1NOWDBjNWsKLS0tIDB4b2xidFVkeWJLQTFDLzM3TEpH\nY2VFTml4UjZ3ZmRqOHljb21kRGdrS1kKLW2BJrxvZa8cdpfNtsP1BeCFWAEgYTL/\nwYm+bgQWfbbpMBKrsXr0F9YSRQoBMSZfqkdgGu8ZYVzEzlnLOVtR2A==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:49Z",
"mac": "ENC[AES256_GCM,data:MjomDV9wi7t0bVSNHIdsVmCkW7Ij+SONTQxOMrB0zTki1hckkrjXjZSL5nhx/taX9Os/NAFbNXuNtmEy0sKk5XcjvsevcJ0OJO6Qo/SXuR111y1qE52gCPiTnwJ6X5QZTrT116YV3eAXtYEPAbOOr6HWN0TzpCh/KJgB8EMqtCM=,iv:5nE08tNcMQTv6UsPT42VJ41pNJV7Q2qpMnonB4y+m5M=,tag:oWcQXgia+7S4MbkHpO15Ng==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN3ERrFuAC8EJIXCFm1YoOsRVX3cGdFZ9F6NJ9/GM5CA proxmox-gui
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:GXUp4zdYyYWwZMpi30LFec+hlCpnYwKNUh6KnxApZ7WrWe+KZu/yS9q2t1QeheZAnJvPs/fRgIEpFUZyPzyJQ5G2+x8pFKPfwLT1s7KHpLdWWv3D5Mgj4206AOaXkSDVFhi9+g1jHJ5RyIbp2yt2iVxTtQKjVMm6lRz/5d51uX6+C/2VFyKvKzNzh+jCpOHVy3GdjvnVRECFIaqXHgPdgx4MUXbvXAJtzFYo7QzIONELd9N7j4KSp4FAoDp6wsCjEaLdtpi/fSAhQOgct/UkpZZFIeWJMaySRWqqM9DbiCFoqgP45SFlGr2x3Ddu2XnS+2UnK1cngJf9YqI0NgAI79VdD9tUhZdeJxKchJMiez2u1mMAXQk8/kzhcx/TeE3D53eMPc3u1WQ51W5bDnw9jcIkq59ObZpQfniuruQsnTdh4+lp3KCZqoTmr0AJ5uSq+JiZ3Db6DUSn7FbRZ3noU5g53x+FzPUvHKsE4yYrXpb6BA1f8uwmWmjQGPcM6NypnlkwReui+l4/SdIwYWZh,iv:fUNvLbV1wDaw8bzzqm0IPPA2EiBEgZlaG1lVJT1RGik=,tag:h5HYXZCjCjewA+3RXKT5cg==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwRlNMSnVpVFZEUi9aMGU0\naUVuZmZwRVlMRGdOSW1XUXdPY3Y3QzVEV0JnCm15SjRtbUp2YUdhU2N2bTYzSTlI\najdjRW05UDJPZXJjZWMvcU9QT3Jma2MKLS0tIHc5ZWJDR2c3bTFYZE8ya09kWnVN\nUmFNU3hOY0xyWkFKSWJMVEprZ3duUmsKxi/Any/b8dEEGa+IZvngrMh9MpUWTcws\nlZHdPct2RvPXhzVYA/rjzuRhVYIeV7xUCz4cj5U6/EzYQ2zTWELSqQ==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:51Z",
"mac": "ENC[AES256_GCM,data:GFJlYxvwX/xfApXBezhfVTHRWlx32HCxbR+6bO0MvNGjUzrX/janFUyoxzvErgIIB/K6AIXZEOYieUChcHrnwAXdli+LrexGR2Zqs8r/h53zDZdw3GcfQp2DwO2QyMmfzWxL/VWe9o+5+N2JZmvzdJwXxOU6KjngrLtu4sJa31Q=,iv:n5fg03aHKvl6LfoPHFhWBUO5503W2n92TYkuYEvC3fE=,tag:utwkOMJxQCx+REoa0iMWWA==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHUq7LyCSKLVO0tQL1dmPyVc5UdoFY6uD4rXF/V4TtCe proxmox-minimal
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:D+19DKqBRWHyk52WxqSm9D7keLVUKfS9sIOXG4dVkLkYIEhz8HaTz2WuLF0vXjiA9oHQDP2z38SzyRsgJhY1ZY67JW/DSMzJnRDARpbZDtY4Mf/gUqQOp2LsCcg2XKeYdGpeEV+oPJC3E/kxEPa1xWNn6ceaDc/Evih9Aj2vKWwGuHuiSfrJlj+JN4F4+9HHMljHZGoMcwzCP/DxeHsd1Jlu6Jua4N0R6bt+Msb4ZYi+b5jmxUvDjiY7TEwIeM5gpF+DKIKJM9hFexp9I8/Vz/a/8m7sRXZ2KdyBHau0h3jf6o7BX0CV9Q0Yy7hWL/f4vgHaTMWf9EaTtLMZuLiemfh5tihgxSOdRbty/szv0i/NuW7NJBPFxAwi0h1YTLFoVqnzmJlWyICBfupBG2Kr7u0s3aEyfRPPtLyt7UvJKwqzP0dwVyt2RcDInDYO7/jtPDzeSQmcLn9lEM6eSoG3LFYfRLAqR2/60h1cLcNEP5PoKmLbiJr1YrHtTR7RDQ4sD61mdJ5e3utHrunXQB8bl14kUrJsHfq0F1Ws,iv:TsaEI69Y/RiIVRcxLfXqjQCy9RAuADXcqAmhWbJjfZc=,tag:86ZYokLxazR2NbUWsa2ynw==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtUEN2TThubWZkUTQvVS9F\nQXpYdDhzOFAwYlBOc3dGb1BHbDR3TFpZa0g0CmlxZ2ZEMURocGM5SjBDRTNJZ0wy\nZVJLdlFBREpVVjREQWQ2ZzlFUDVXcWsKLS0tIGpYM2ZWc0Z6a0pMaXRNRUJBVkxS\nNzRtWndmSEpFNHErbHNIUWpUU21ZR0UK4juygayPUhFBZcmpAguLM22DrQFRVAx0\nXx5JTCaIasw0dJC84z48uA/YszBNZaP+nNK/extypWOGOCaNkDWf8A==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:48:16Z",
"mac": "ENC[AES256_GCM,data:WdsFXIFoBScYEdgLc+Bux28xiPsIU0HQQr7yll8G5s/Jg/WiJmHxBWiICB6NJxAE1Ql7geYaSRIxhaircbHVAGJeHugWmNUCesbwnz62dmgtoGVMMivEk7rditvY/hfKSyOFif6OCmDee4eazwCGe0QTLZxi6o/D9Q0GotcuEoA=,iv:BlPUSOeMFf21BFma+o3EpFkK/GK498ThC9Tg4e7ryzU=,tag:k/wqFcMjiautX9Os3hVfZg==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA/3GukHJVXpoaZXTPvqayJMP1esfmWRQEFqaZGikQeu proxmox-nix-cache
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:3iuIJCZqTeLxt4efssSc9l5LzXnH3HPP1NZYG7SXqvrTybjQtD5W2j4su967TQbYjqYWss3MRidi/mkeo9OEaD0BKZrJXAp6GoBPBAihoQkR5MzkQTaLg1IGLXsA+7Y8mhT/lV+7N4y+UAvlcWz7vYSFND9o1J6NWcSUBkiJ8ni9JV1MdOxHKlqlSbRZNpmtazxO47A39YQEjo7EWHnbK4rpQT8mbYfvZsrpWrip0nBNpGW5+8Pm4mN19UfoKWFygJ9pA+TDFlBUO47PopZC1Phf1MeEqeSDTj2DrOpGoEsJb8DksN6aM76dbjd2ITvNKAnH6i4hUD9uv9YsuA5LHOdtbrzTcyx+WcL49w62jBZCM4HMygZXU0OZf2Z5pcNyyLvqLRA8zry3noDJASj8Hc13QP26g3Ktz8qAVtSvtf2FLOcztg2DS4cNhyJLYIEjWyaIVcjHqRvIq9eDGHq3hYEPP5T/xRa9QGbCvUS9y4Z8Y6R6Sa7Cgq5XwvA3IJgaQaI4TvXKqhEVItTJfIBpHhkJpwWFP2bxzJjR,iv:5aFXnMjXZyAcXzCROkELPPKFiGHXD+v5G8ye9zx2QL8=,tag:XAyUuLmAwwE/j7kyuHTnsw==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDa2E0NE9mRHFRTXJkeUR3\nMnVwOTU1TFFicnlJVTRLWjNWajluMzBKdzJZCmpOTlhFanQ4NzBJYjM5eUxvcEpB\neTdJc1l4S1FxRkhNR2lDaW4ybEV4dmsKLS0tIGloZHA0RWlQdEpSem52dEJGSkNs\ncHpCcTF2NjVFUmFPTUplRlhxVlZ2YVEKaqyHTKl0PxYSJRGpWcbIm6Tm12+wwIxO\n0drWsPxTXZnJqLuE8dJW3TeGwvlAch3qXnls2LNdg7gIBwwHDBwEbA==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:54Z",
"mac": "ENC[AES256_GCM,data:IbNj6J/M0shZWlUyxkNihUjQe/CF2VFKYizhKEEQ6SgJgR+GmlXv5J6DDRB/1YTbvW95/Tx/nUXtTMDZFchBleofMzjYuCYosAvMFmL6mEP4ucO8F7KvZc//ih/4DgbGAITv2GfWPfMrKeawdrR74HIdbHu5y6sNBlP0bObKd1U=,iv:6/EZH0d/PVdzPGtGgxnXs4CPQnJyAyNPx34oYGi7aRU=,tag:o4uzfNJOm/3NGPhonjC5aQ==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP1Y6RNcaAkmEj3/rFRdFVROQopp9JHY2qz37Jas+Zdi proxmox-pxe-boot
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:Cn4v3C4w181McHwRnNkSioeCM48eAcKXRWyRpeffrQHER/D2fe9/+uAu25WHgRnjreMVc7GbnydJ7ECdoY+GKP755QA0J583Nn0+EexxITsxKEqRRb2MD9eQFZXPNtfmBHgwDEVPEzuCSYF3Ykx3ipeoItKUyzHDn82LbTxTm47EBaaNROk10DnslCburmWQc1O5drQWgY5U2Vv7+nsYe4TNCHJ1hIhRvH6J3Nr0wLNV595us2OE0gmOqd+uX67kPUI6qQTXxVFCJA3FESAfWMhfuJZJ+SYaRwzVrRrmDtEGPTcghxIzmRJYB3pSnx28d8iOy1AU9PJnO+NYfSxrZ+0F5aUeFBjIFo36CUTwC9I0Lg2wgZ3Fp2o9NZ6DerF5aBYkNmrNchzdeVOifFYFXtNzNDo4Ho4iZOQEjbbiCmOmKl6sLWqkalr+C7FRj+v+KPAAzKV62uaUF8pMeeGL+KQil3tTIP3w/UMhdQIha9BisG+qbNmgOzSit2i1cENVPSi2g2zzIPZte6ss57RTlkLlEotzUdZfJOFy,iv:lDEwgdMIlz45LLIYTLsdedszmH4qK2Yfj1QWxhAq+qI=,tag:l175SGKP9ggDZENqCCf5gQ==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvSVhCTll1WUhyd2hvVmM0\nN3E2bk1CMC9scVFXY0YxQUdMWkM4VUZaU0NvClYvUWVrU01UNnU0ajVnUkN1R2dv\nRnp4aDByYXhtMVFuWnR6Nk9DWGJBSTQKLS0tIE9xUFNHQnFKT1liNFRoZUc2Tmtx\nTk9uNnpVRjVqUllEZ0xHMTMwTkc3ajAK1OIf3fcCH39KM1+YIMNhPTLldRebIMpp\nsE+eLfMAfl9WwGEppJswyPDMcqt4GFLzIWgE7BfTyeyirr5LtijxRg==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:56Z",
"mac": "ENC[AES256_GCM,data:eVLCtoh6jguoyLdMUPz85HtDTXWIJ3ci27vPns1MNY6KLi8jR3uaksVVy5kdP/b4aeTJwez5U/Mm5HQN0rIw+RuRN5flGe3oiCK5a8Eig4WYBr9icpYc9N817jQZ8ov+WdrJewCzZPzkzZ8VszzGbjsXBCRtQIAsReV0IJqhUx8=,iv:/OqU+b8+SmWWvilIOLkUb6J9RnGML8c0bS8+pRwiUJE=,tag:ugh/hq+XXb1DxK7iRqV+8g==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQHSubxjvaIV9Xp5ABJSKsajCZBGyGmjsdaA2TWCFP8 proxmox-server
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:GdVHSekFtRtppf8xxMPsDE0cu8Hwq3ol0iJTfrwKwaAB0IW8YS8TFAeRnfOI5AE3P1JUaGDZ1Od4UXu4Jk2+uYCgwz074qc1WukzUsd8Q3G03ojdrTA13uH8YCUTRsm3bQfB+kmH5qefn9j88UJq7TYiTLe3U0DUU/B83lgyiFIygP8CKWDgFH3jWZOWPmyVa4Z1fMWVsiNhjO4hHduamN/I2FOY7U2TI5+zcYgGeunjnniLHcQdiO0iK9TOP5SckOVPZOy6daKzGcckEq03NV4y+miSucEUHlcQqMUyvsJwETIaCq28xR2MDTyDz42SL2HIodb7FyREtxd91W6gPN1j51yUZxt4WA9R8/WczDcUgmROK+HInPp+ktjWqv2ymXXV5xidxh7m+vrGTPC+YQqyFD4eIp3+h2S/vyzHimBa2rdEp9NNXTQlH3CfL2aLC95FOme+uKTDqWA2PHfB0rQ5uhHAuAgaX5vb/GTiQii5dq4k2O4MDf9u+fdPuOSCaGdlirWmSE45SvaXLJ3M2zQq7M/UTE+wSFyc,iv:EaIsYnGxf42LWQ3hzBU/HsBda/FHInhjZkwsPLcZMEY=,tag:16OuAs3Ram+xd4RfWEBXkw==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBETE4vOHhldGRFRjdYaXpi\nSmgzTFlqL3Qzak1CelRBR29jZzR1V0pOQWw0CkhSWDkvbkJWaDVHMzlKNDZUdzhH\nWGoyWXQ1KzJZQU1kQzFvaDk2V2lWQmsKLS0tIHlTY2tua2F1TXBhamsvcDZ0bmI1\nZVFwUGtQWW1HNTM3UUs4bHRZU0xKRDgKBG2iI9JP0lhU5VCWXrpN1b2rYEYk8sOZ\n9FUO14KKMg9QRfSa2iHOa84DByx2hxVRc9wLukBUpkKOAjGSxeGMeg==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:45:58Z",
"mac": "ENC[AES256_GCM,data:jG7ohweKKndoadidquejYG/1w9iL+9Xb5/IsU/C9fn/Tq5RXEjrxO37COY8sAD7dvQf7iBNsly7upsjtHaMK8ybjQDaa6IQhoGBlfSOA2O968klaJZRQRiLPzCRet252KXzOtvDscrBvyYItvyqjnW5qBbw7lfT4y2J4OA2ieC0=,iv:mMo7Y6XEjQcchNOY6eaw5LOmjFKaQKx84o9bnGNXCKI=,tag:aHkPjs7uVfv8h0REHN8YmA==,type:str]",
"version": "3.13.2"
}
}
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGe6gDil6RNriND/L04T6KoI6UWACCDB8atsX11mCPcW proxmox-tailscale-subnet-router
@@ -0,0 +1,14 @@
{
"data": "ENC[AES256_GCM,data:ZSUd0uBwDJfFpFtxpA1AxT4XFLU2421ioWMjvEcaIVdQxg3bxnoZ+wX0WT3z2wiN8kDU/fqHsbi39E5rbd+B2y049QQyrYJB+f6xFZTNpCKp6QZWbnhTKVS5vukJn1XlfbEnsL4Bh0Lu7oxt7G4dyrROJToSy2FRu5NbwFPPcTkpuiJAaHM7Rf4e+d/UaCLlv+OXY88PDT/H0GIRCziCvJWgzdsFyiISrkoPyDRfxCNKvXL67X7UQLaiSVHxXhoPXP8ijuqcV4BN2rDRkBd3JW7Lpl/4dKAVHo8FMBQl3E3Cq9y60ykmp1dDGmT+Mk3+jKvj3pzBz3CufF49xbdvRgkSQfrdlrliR8ycMFJs4aXkc8/anievclnA9HLHm9ZkvhD20IyCDPfXGtOMYte8pwXl/m+yq9DkuLPZ9ZbAk/kvnjlfnRneFpNJA8Yr3Zv6PR015WVj5eZPQtLeyuiP0RDLcAwaoE7SXOQ9jXDJscdcLHWq4nTM5EZ3umfBZbqQ8liyQ4Ms/q3WQw7nue6xwKKON/w1XfTNh30edy0zKaLhCVPvkHIdzMTO7L0luLdP,iv:4Le0pgv/hsh147Xuw7X5PA7GoO99AcXlMaLpWDvrtMY=,tag:LTPGT++AYkUxUOK2rmKFFQ==,type:str]",
"sops": {
"age": [
{
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBKeGptMG9BamRzcmkwcXhp\nU2JEdE83c2VUSDBGbGRkN1ZUdm43bWhiZkh3ClNzV1luT01kaGdNOUFZaUVhSlFp\nUVhqbDl5ZDNXVFVLdzlTdXJvL0NBQm8KLS0tIGNTUzBzcldyY2h4TGJRQUJkY3B5\nZ3BDT0FzOVNBODRHMG5kS1BFQ3dObWcKCciGptbHrUqu537NKSU5bkZS/5m2Bt6I\nZABtvmRYaXc+GPse5a98N6+ERcdiefmLcV1tim3aRdqUxBizRFyg7g==\n-----END AGE ENCRYPTED FILE-----\n",
"recipient": "age1njap586hc0q43kr03g6c8eqhdsmk8zcafkl3f83xwlc2gqhlmfgs4tmwad"
}
],
"lastmodified": "2026-07-25T11:46:01Z",
"mac": "ENC[AES256_GCM,data:ZEOAb9JqsWLzithSiFeKxZtRzk86+6SToHAWM72XqHeb6ThDa4/+aUMnWZ2Qw8ym+udi37mvfvFqil8lQyPZwIl5fgP6+q/Am3oYkyi/C6rJfLQqkVhvh/AhU5GA//xbQq1P7xObnrMH/yoc/HHQ1EZdI0uj6aIW23jYw4p/g+4=,iv:IQIWRACGISPVEizBuiN8Cyw1BatOK1Acwlx8+hRwqRg=,tag:SPtlBirCPVenq8w0XfQPug==,type:str]",
"version": "3.13.2"
}
}