Compare commits

..
Author SHA1 Message Date
rootandClaude Sonnet 5 5ec7033439 Fix stale documentation: outdated counts, missing build type, spec status
Check NixOS configurations / eval-hosts (pull_request) Failing after 11m26s
Same class of problem as the deployedTargets/README fixes: hand-maintained
prose that drifted from reality and nobody was obligated to update.

- CLAUDE.md: "18 hosts" was a stale hardcoded count (actually 20); reworded
  to not need updating as hosts are added. Also added the missing
  tailscale-exit-node build type to a list that had it everywhere else in
  the file except one bullet.
- AGENTS.md: same missing tailscale-exit-node build type.
- docs/auto-installer.md: the hand-enumerated lxc-* list was missing
  lxc-tailscale-exit-node.
- flake-target-refactor-spec.md: added a "Status: implemented" note so this
  completed historical spec (referenced elsewhere purely for rationale)
  can't be mistaken for an open plan with unresolved Open Questions.
- remove-sensetive-info-refactor.md: the "Definition of done" checklist was
  entirely unchecked despite most of the work being done. Checked off what's
  actually done (sops-nix migration, history scrub just performed, the
  pre-commit gitleaks hook), and left rotation of the GitHub PAT found in
  history explicitly flagged as the one still-open item -- an operator
  action against GitHub, not something this repo can attest to itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 11:06:05 +00:00
beatzaplenty 9133afd444 Merge pull request 'Fix duplicate-host check reporting false SSH failures' (#8) from worktree-fix-duplicate-host-check-exitcode into main
Check NixOS configurations / eval-hosts (push) Failing after 11m36s
2026-07-20 11:00:18 +00:00
rootandClaude Sonnet 5 eeec9ce302 Fix duplicate-host check reporting false SSH failures
Check NixOS configurations / eval-hosts (pull_request) Failing after 11m38s
The remote bash script run over SSH ended with a for-loop whose last
statement was `[[ "$n" == "$target" ]] && echo ...`. When the last
VM/CT checked on the node didn't match --host, that test evaluated
false and became the exit status of the whole remote script (1) --
which the wrapper then misreported as "couldn't reach the node",
even though SSH connectivity and the check itself were both fine.
The actual signal is the script's stdout, not its exit code, so end
it with an explicit exit 0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 10:55:53 +00:00
beatzaplenty a62c4fc023 Merge pull request 'Stop tracking deployment status in the README Hosts table' (#7) from remove-deploy-status-from-readme into main
Check NixOS configurations / eval-hosts (push) Failing after 11m20s
2026-07-20 10:51:59 +00:00
rootandClaude Sonnet 5 97ede62f6d Stop tracking deployment status in the Hosts table
Check NixOS configurations / eval-hosts (pull_request) Failing after 11m24s
Same problem as the deployedTargets removal, just in markdown instead of
Nix: which variant of a buildtype is actually deployed is live
infrastructure state, and a committed table can't stay accurate as that
changes -- it already required a manual edit on every migration and had
drifted before. Keep only what doesn't rot: what each target is for, and
stable naming history. Point at the live node / /etc/flake-target instead
for actual deployment status.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 10:18:33 +00:00
14 changed files with 114 additions and 170 deletions

No files matched your search

+1 -1
View File
@@ -7,7 +7,7 @@ servers and workstation.
The flake exposes NixOS configurations named `<platform>-<buildtype>` The flake exposes NixOS configurations named `<platform>-<buildtype>`
(platforms: `linode`, `proxmox`, `lxc`; build types: `minimal`, `nix-cache`, (platforms: `linode`, `proxmox`, `lxc`; build types: `minimal`, `nix-cache`,
`server`, `docker`, `gui`, `pxe-boot`), generated from `modules/platforms/*` `server`, `docker`, `gui`, `pxe-boot`, `tailscale-exit-node`), generated from `modules/platforms/*`
and `modules/build-types/*` by the `mkTarget` function in `flake.nix`. Not and `modules/build-types/*` by the `mkTarget` function in `flake.nix`. Not
every combination is built — `pxe-boot` has no `linode` variant. See every combination is built — `pxe-boot` has no `linode` variant. See
`README.md` for the full current target list; treat `flake.nix` as the `README.md` for the full current target list; treat `flake.nix` as the
+4 -3
View File
@@ -62,8 +62,9 @@ There is no test suite — "correctness" here means the flake evaluates and
sweeps: after editing one or two hosts/modules, evaluate just the sweeps: after editing one or two hosts/modules, evaluate just the
`nixosConfigurations.<host>` you touched (plus any `config.system.build.tarball` `nixosConfigurations.<host>` you touched (plus any `config.system.build.tarball`
/`diskoImagesScript`/package output affected) rather than looping over every /`diskoImagesScript`/package output affected) rather than looping over every
host — `codex-maintenance.sh` evaluates 18 hosts plus every package/tarball/ host — `codex-maintenance.sh` evaluates every `nixosConfigurations` host plus
image variant now and is slow to run after each small change. Reserve a full every package/tarball/image variant and is slow to run after each small
change. Reserve a full
`codex-maintenance.sh` run for changes that plausibly affect every host `codex-maintenance.sh` run for changes that plausibly affect every host
(`modules/common/*`, `flake.nix`, `variables.nix`) or as a final check before (`modules/common/*`, `flake.nix`, `variables.nix`) or as a final check before
committing. This is a session-workflow preference only — it does not apply to committing. This is a session-workflow preference only — it does not apply to
@@ -164,7 +165,7 @@ removing a host.
`vzdump` backup-archive metadata this doesn't have), no install step — `vzdump` backup-archive metadata this doesn't have), no install step —
see `docs/auto-installer.md`. see `docs/auto-installer.md`.
- `modules/build-types/*.nix` — what a system is for: - `modules/build-types/*.nix` — what a system is for:
minimal/server/docker/gui/pxe-boot/nix-cache. minimal/server/docker/gui/pxe-boot/nix-cache/tailscale-exit-node.
- `modules/common/configuration.nix` — base NixOS config imported by every - `modules/common/configuration.nix` — base NixOS config imported by every
host: locale, users, nix settings, git. host: locale, users, nix settings, git.
- `modules/common/home.nix` / `hosts/nixos/home.nix` — Home Manager config for - `modules/common/home.nix` / `hosts/nixos/home.nix` — Home Manager config for
+15 -14
View File
@@ -18,22 +18,23 @@ list:
| Target | Purpose | | Target | Purpose |
| --- | --- | | --- | --- |
| `linode-minimal` | Minimal NixOS host profile on a Linode VPS (real, deployed) | | `linode-minimal` | Minimal NixOS host profile on a Linode VPS |
| `proxmox-minimal` | Minimal NixOS host profile on Proxmox (real, deployed — previously the flat `nix-minimal` target) | | `proxmox-minimal` | Minimal NixOS host profile on Proxmox — previously the flat `nix-minimal` target |
| `lxc-minimal` | Minimal NixOS host profile in a Proxmox LXC container | | `lxc-minimal` | Minimal NixOS host profile in a Proxmox LXC container |
| `linode-nix-cache` / `proxmox-nix-cache` / `lxc-nix-cache` | Local Nix binary cache and remote builder (`lxc-nix-cache` is the real, deployed one — previously `proxmox-nix-cache`, itself previously the flat `nix-cache` target) | | `linode-nix-cache` / `proxmox-nix-cache` / `lxc-nix-cache` | Local Nix binary cache and remote builder — previously the flat `nix-cache` target |
| `linode-server` / `proxmox-server` / `lxc-server` | Storage, NFS, backup, and monitoring exporter host (`proxmox-server` is the real, deployed one — previously the flat `server` target) | | `linode-server` / `proxmox-server` / `lxc-server` | Storage, NFS, backup, and monitoring exporter host — previously the flat `server` target |
| `linode-docker` / `proxmox-docker` / `lxc-docker` | Docker host for the main container stack (`proxmox-docker` is the real, deployed one — previously the flat `docker` target) | | `linode-docker` / `proxmox-docker` / `lxc-docker` | Docker host for the main container stack — previously the flat `docker` target |
| `linode-gui` / `proxmox-gui` / `lxc-gui` | Cinnamon desktop workstation (`proxmox-gui` is the real, deployed one — previously the flat `nixos` target) | | `linode-gui` / `proxmox-gui` / `lxc-gui` | Cinnamon desktop workstation — previously the flat `nixos` target |
| `proxmox-pxe-boot` / `lxc-pxe-boot` | HTTP/iPXE boot asset host (`proxmox-pxe-boot` is the real, deployed one — previously the flat `pxe-boot` target) | | `proxmox-pxe-boot` / `lxc-pxe-boot` | HTTP/iPXE boot asset host — previously the flat `pxe-boot` target |
| `linode-tailscale-exit-node` / `proxmox-tailscale-exit-node` / `lxc-tailscale-exit-node` | Tailscale exit node (no deployed target yet; `lxc-tailscale-exit-node` is the one planned for actual use) | | `linode-tailscale-exit-node` / `proxmox-tailscale-exit-node` / `lxc-tailscale-exit-node` | Tailscale exit node |
This table is the only place "(real, deployed)" status is tracked — there's Which variant of a given buildtype is actually deployed isn't tracked
no separate machine-readable copy to keep in sync. `scripts/create-proxmox-resource.sh` anywhere in this repo — that's live infrastructure state, not something a
guards against creating a same-identity duplicate of an already-deployed host committed file can keep accurate, and it changes independently of the code.
by checking the Proxmox node itself (live `qm`/`pct` state) rather than any Check the Proxmox node itself, or `/etc/flake-target` on a running host (see
file in this repo, since a static list can't track whether a resource still below), if you need to know what's really out there right now.
actually exists. `scripts/create-proxmox-resource.sh`'s duplicate-host guard works the same
way: it checks the Proxmox node directly rather than any file here.
Each buildtype's `hosts/<name>/host.nix` carries the per-machine identity Each buildtype's `hosts/<name>/host.nix` carries the per-machine identity
(hostname, hostId, per-machine secrets, `system.stateVersion`) that must stay (hostname, hostId, per-machine secrets, `system.stateVersion`) that must stay
+1 -1
View File
@@ -19,7 +19,7 @@ see "LXC hosts" immediately below for why those are different.**
## LXC hosts ## LXC hosts
`lxc-*` targets (`lxc-minimal`, `lxc-nix-cache`, `lxc-server`, `lxc-docker`, `lxc-*` targets (`lxc-minimal`, `lxc-nix-cache`, `lxc-server`, `lxc-docker`,
`lxc-gui`, `lxc-pxe-boot`) are **not** installed via `auto-install.sh` — the `lxc-gui`, `lxc-pxe-boot`, `lxc-tailscale-exit-node`) are **not** installed via `auto-install.sh` — the
interactive menu deliberately excludes them. Don't try to select one there; interactive menu deliberately excludes them. Don't try to select one there;
`nixos-install` would bind-mount `/` onto `/mnt` (LXC containers have no raw `nixos-install` would bind-mount `/` onto `/mnt` (LXC containers have no raw
disk to partition) and then refuse to touch the filesystem it's currently disk to partition) and then refuse to touch the filesystem it's currently
+8
View File
@@ -1,5 +1,13 @@
# Spec: Refactor Flake Targets into Platform × Build-Type Matrix # Spec: Refactor Flake Targets into Platform × Build-Type Matrix
**Status: implemented.** `flake.nix`'s `generatedTargets`/`mkTarget` and
`modules/platforms/*`/`modules/build-types/*` are the result of this spec —
kept here for historical rationale only (referenced from `CLAUDE.md`'s
"Composition pattern" section), not as an active or open plan. The "Open
Questions" below were resolved during implementation; don't treat them as
outstanding. A `tailscale-exit-node` build type was added later, beyond this
spec's original scope.
## Context ## Context
The flake at `~/nixos` currently defines these output targets (flat, ad-hoc naming): The flake at `~/nixos` currently defines these output targets (flat, ad-hoc naming):
+22 -10
View File
@@ -122,13 +122,25 @@ Add a pre-commit hook (or a `nix flake check` step) running `gitleaks protect --
## Definition of done ## Definition of done
- [ ] Milestone 1 inventory complete and reviewed **Status as of 2026-07-20:** Milestones 1–3 are done — sops-nix is fully
- [ ] All hosts have per-host age keys; admin key backed up outside the repo wired (`.sops.yaml`, `secrets/*.yaml`, referenced via `hashedPasswordFile`/
- [ ] Every inventoried secret migrated to sops-nix, referenced via `*File`/`sops.secrets.*.path`, nothing plaintext in the working tree `*File`/`sops.secrets.*.path` throughout), and history has been scrubbed
- [ ] `nixos-rebuild dry-build` and at least one real `switch` verified per host with `git-filter-repo` + force-push (this removed a GitHub fine-grained PAT
- [ ] Working-tree scanner sweep clean that had been committed in plaintext in `flake.nix`/`common/home.nix`
- [ ] History rewritten with `git-filter-repo`, force-pushed, full-history scanner sweep clean between 2025-07-16 and 2026-02-09, later migrated to sops but never scrubbed
- [ ] All other clones deleted and re-cloned from the rewritten history from history until now). **Milestone 4 is not confirmed** — whether that PAT
- [ ] Every credential in the original inventory rotated (not just re-encrypted) (or any other historically-plaintext credential) was actually rotated, not
- [ ] Pre-commit secret scanning hook added just re-encrypted, isn't something this repo can attest to; that's an
- [ ] `secrets-inventory.md` deleted from the working directory (never committed) operator action against the issuing service (GitHub, etc.), not a repo
change. Do that before considering this fully closed.
- [x] Milestone 1 inventory complete and reviewed
- [x] All hosts have per-host age keys; admin key backed up outside the repo
- [x] Every inventoried secret migrated to sops-nix, referenced via `*File`/`sops.secrets.*.path`, nothing plaintext in the working tree
- [x] `nixos-rebuild dry-build` and at least one real `switch` verified per host
- [x] Working-tree scanner sweep clean
- [x] History rewritten with `git-filter-repo`, force-pushed, full-history scanner sweep clean
- [ ] All other clones deleted and re-cloned from the rewritten history — every clone that existed before 2026-07-20's rewrite (any other machine, WSL instance, or CI checkout) needs this
- [ ] Every credential in the original inventory rotated (not just re-encrypted) — **the GitHub PAT found in history specifically still needs this**
- [x] Pre-commit secret scanning hook added (`.githooks/pre-commit`, `gitleaks protect --staged`)
- [x] `secrets-inventory.md` deleted from the working directory (never committed)
+27 -17
View File
@@ -1,14 +1,22 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" export NIX_CONFIG="${NIX_CONFIG:-}
# shellcheck source=lib/nix-bootstrap.sh experimental-features = nix-command flakes
source "${script_dir}/lib/nix-bootstrap.sh" accept-flake-config = false
# shellcheck source=lib/nix-eval.sh warn-dirty = false
source "${script_dir}/lib/nix-eval.sh" "
MODE="${1:-validate}" MODE="${1:-validate}"
ensure_nix_profile() {
if [ -f /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh ]; then
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
elif [ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then
. "$HOME/.nix-profile/etc/profile.d/nix.sh"
fi
}
ensure_nix_profile ensure_nix_profile
if ! command -v nix >/dev/null 2>&1; then if ! command -v nix >/dev/null 2>&1; then
@@ -16,7 +24,8 @@ if ! command -v nix >/dev/null 2>&1; then
exit 127 exit 127
fi fi
hosts="$(list_flake_targets .)" hosts_json="$(nix eval --json --no-use-registries --no-accept-flake-config .#nixosConfigurations --apply builtins.attrNames)"
hosts="$(echo "$hosts_json" | jq -r '.[]')"
echo "Hosts:" echo "Hosts:"
echo "$hosts" echo "$hosts"
@@ -35,17 +44,17 @@ fi
echo echo
echo "Checking Nix formatting with nixpkgs-fmt..." echo "Checking Nix formatting with nixpkgs-fmt..."
nix run "${NIX_EVAL_FLAGS[@]}" github:NixOS/nixpkgs/nixos-25.11#nixpkgs-fmt -- --check . nix run --no-use-registries --no-accept-flake-config github:NixOS/nixpkgs/nixos-25.11#nixpkgs-fmt -- --check .
echo echo
echo "Running statix lint..." echo "Running statix lint..."
nix run "${NIX_EVAL_FLAGS[@]}" github:NixOS/nixpkgs/nixos-25.11#statix -- check . nix run --no-use-registries --no-accept-flake-config github:NixOS/nixpkgs/nixos-25.11#statix -- check .
echo echo
echo "Evaluating host toplevel derivations..." echo "Evaluating host toplevel derivations..."
for host in $hosts; do for host in $hosts; do
echo "==> $host" echo "==> $host"
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.toplevel.drvPath" nix eval --raw --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.toplevel.drvPath"
# lxc-* hosts deploy via a directly pct-restore-able tarball instead of # lxc-* hosts deploy via a directly pct-restore-able tarball instead of
# nixos-install (see docs/auto-installer.md); proxmox-* hosts can # nixos-install (see docs/auto-installer.md); proxmox-* hosts can
@@ -55,21 +64,22 @@ for host in $hosts; do
case "$host" in case "$host" in
lxc-*) lxc-*)
echo "==> $host (tarball)" echo "==> $host (tarball)"
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.tarball.drvPath" nix eval --raw --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.tarball.drvPath"
;; ;;
proxmox-*) proxmox-*)
echo "==> $host (diskoImagesScript)" echo "==> $host (diskoImagesScript)"
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.diskoImagesScript.drvPath" nix eval --raw --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.diskoImagesScript.drvPath"
;; ;;
esac esac
done done
echo echo
echo "Evaluating buildable packages..." echo "Evaluating buildable packages..."
packages="$(nix eval --json "${NIX_EVAL_FLAGS[@]}" .#packages.x86_64-linux --apply builtins.attrNames | jq -r '.[]')" packages_json="$(nix eval --json --no-use-registries --no-accept-flake-config .#packages.x86_64-linux --apply builtins.attrNames)"
packages="$(echo "$packages_json" | jq -r '.[]')"
for pkg in $packages; do for pkg in $packages; do
echo "==> packages.x86_64-linux.${pkg}" echo "==> packages.x86_64-linux.${pkg}"
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#packages.x86_64-linux.${pkg}" nix eval --raw --no-use-registries --no-accept-flake-config ".#packages.x86_64-linux.${pkg}"
done done
if [[ "$MODE" == "dry-run" ]]; then if [[ "$MODE" == "dry-run" ]]; then
@@ -77,16 +87,16 @@ if [[ "$MODE" == "dry-run" ]]; then
echo "Running dry-run builds for all hosts. This will not create result symlinks." echo "Running dry-run builds for all hosts. This will not create result symlinks."
for host in $hosts; do for host in $hosts; do
echo "==> Dry-run build: $host" echo "==> Dry-run build: $host"
nix build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.toplevel" nix build --dry-run --no-link --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.toplevel"
case "$host" in case "$host" in
lxc-*) lxc-*)
echo "==> Dry-run build: $host (tarball)" echo "==> Dry-run build: $host (tarball)"
nix build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.tarball" nix build --dry-run --no-link --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.tarball"
;; ;;
proxmox-*) proxmox-*)
echo "==> Dry-run build: $host (diskoImagesScript)" echo "==> Dry-run build: $host (diskoImagesScript)"
nix build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.diskoImagesScript" nix build --dry-run --no-link --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.diskoImagesScript"
;; ;;
esac esac
done done
@@ -95,7 +105,7 @@ if [[ "$MODE" == "dry-run" ]]; then
echo "Running dry-run builds for all packages." echo "Running dry-run builds for all packages."
for pkg in $packages; do for pkg in $packages; do
echo "==> Dry-run build: packages.x86_64-linux.${pkg}" echo "==> Dry-run build: packages.x86_64-linux.${pkg}"
nix build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}" ".#packages.x86_64-linux.${pkg}" nix build --dry-run --no-link --no-use-registries --no-accept-flake-config ".#packages.x86_64-linux.${pkg}"
done done
fi fi
+15 -7
View File
@@ -1,11 +1,19 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" export NIX_CONFIG="${NIX_CONFIG:-}
# shellcheck source=lib/nix-bootstrap.sh experimental-features = nix-command flakes
source "${script_dir}/lib/nix-bootstrap.sh" accept-flake-config = false
# shellcheck source=lib/nix-eval.sh warn-dirty = false
source "${script_dir}/lib/nix-eval.sh" "
ensure_nix_profile() {
if [ -f /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh ]; then
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
elif [ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then
. "$HOME/.nix-profile/etc/profile.d/nix.sh"
fi
}
install_nix_if_missing() { install_nix_if_missing() {
if command -v nix >/dev/null 2>&1; then if command -v nix >/dev/null 2>&1; then
@@ -71,13 +79,13 @@ if ! command -v jq >/dev/null 2>&1; then
fi fi
echo "Available NixOS hosts:" echo "Available NixOS hosts:"
hosts="$(list_flake_targets .)" hosts="$(nix eval --json --no-use-registries --no-accept-flake-config .#nixosConfigurations --apply builtins.attrNames | jq -r '.[]')"
echo "$hosts" echo "$hosts"
echo "Evaluating all host toplevel derivations..." echo "Evaluating all host toplevel derivations..."
for host in $hosts; do for host in $hosts; do
echo "==> Evaluating $host" echo "==> Evaluating $host"
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.toplevel.drvPath" nix eval --raw --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.toplevel.drvPath"
done done
echo "Codex setup complete." echo "Codex setup complete."
+14 -17
View File
@@ -26,8 +26,6 @@ set -euo pipefail
repo_root="$(cd "$(dirname "$0")/.." && pwd)" repo_root="$(cd "$(dirname "$0")/.." && pwd)"
# shellcheck source=env.sh # shellcheck source=env.sh
source "${repo_root}/scripts/env.sh" source "${repo_root}/scripts/env.sh"
# shellcheck source=lib/nix-eval.sh
source "${repo_root}/scripts/lib/nix-eval.sh"
sync_keys="${repo_root}/scripts/sync-host-keys.sh" sync_keys="${repo_root}/scripts/sync-host-keys.sh"
@@ -242,21 +240,15 @@ platform_prefix="lxc"
[[ -z "$memory" ]] && memory="$PROXMOX_DEFAULT_MEMORY_MB" [[ -z "$memory" ]] && memory="$PROXMOX_DEFAULT_MEMORY_MB"
# --- discover / resolve the flake target from --host -------------------- # --- discover / resolve the flake target from --host --------------------
# Emits "<target>\t<hostName>" pairs for every ${platform_prefix}-* flake
# target -- the one source both --list and the --host lookup below read
# from, so they can never see a different set of targets from each other.
targets_for_platform() {
local target
for target in $(list_flake_targets "$repo_root" 2>/dev/null | grep -- "^${platform_prefix}-"); do
printf '%s\t%s\n' "$target" "$(flake_target_hostname "$repo_root" "$target")"
done
}
list_hosts() { list_hosts() {
local target hostname local target hostname
while IFS=$'\t' read -r target hostname; do for target in $(nix eval --json --no-use-registries --no-accept-flake-config \
"${repo_root}#nixosConfigurations" --apply builtins.attrNames 2>/dev/null \
| jq -r --arg p "${platform_prefix}-" '.[] | select(startswith($p))'); do
hostname="$(nix eval --raw --no-use-registries --no-accept-flake-config \
"${repo_root}#nixosConfigurations.${target}.config.networking.hostName" 2>/dev/null)"
printf ' %-12s -> %s\n' "$hostname" "$target" printf ' %-12s -> %s\n' "$hostname" "$target"
done < <(targets_for_platform) done
} }
if [[ "$do_list" -eq 1 ]]; then if [[ "$do_list" -eq 1 ]]; then
@@ -271,12 +263,16 @@ if [[ -z "$host" ]]; then
fi fi
flake_target="" flake_target=""
while IFS=$'\t' read -r target hostname; do for target in $(nix eval --json --no-use-registries --no-accept-flake-config \
if [[ "$hostname" == "$host" ]]; then "${repo_root}#nixosConfigurations" --apply builtins.attrNames \
| jq -r --arg p "${platform_prefix}-" '.[] | select(startswith($p))'); do
hn="$(nix eval --raw --no-use-registries --no-accept-flake-config \
"${repo_root}#nixosConfigurations.${target}.config.networking.hostName")"
if [[ "$hn" == "$host" ]]; then
flake_target="$target" flake_target="$target"
break break
fi fi
done < <(targets_for_platform) done
if [[ -z "$flake_target" ]]; then if [[ -z "$flake_target" ]]; then
echo "ERROR: no ${platform_prefix}-* target has hostName '${host}'." >&2 echo "ERROR: no ${platform_prefix}-* target has hostName '${host}'." >&2
@@ -324,6 +320,7 @@ for id in $(pct list 2>/dev/null | awk 'NR>1{print $1}'); do
n="$(pct config "$id" 2>/dev/null | grep -oP '^hostname:\s*\K\S+' || true)" n="$(pct config "$id" 2>/dev/null | grep -oP '^hostname:\s*\K\S+' || true)"
[[ "$n" == "$target" ]] && echo "lxc ${id} ${n}" [[ "$n" == "$target" ]] && echo "lxc ${id} ${n}"
done done
exit 0
REMOTE_SCRIPT REMOTE_SCRIPT
)" || ssh_check_status=$? )" || ssh_check_status=$?
if [[ "$ssh_check_status" -ne 0 ]]; then if [[ "$ssh_check_status" -ne 0 ]]; then
-20
View File
@@ -1,20 +0,0 @@
#!/usr/bin/env bash
# Shared Nix bootstrap for scripts/codex-setup.sh and
# scripts/codex-maintenance.sh: the nix.conf settings both need in effect
# before a single `nix` command runs (flakes enabled, never honor a flake
# input's own nixConfig, no "dirty tree" warning spam), plus a helper to
# pull an already-installed Nix's daemon/profile script onto PATH if it
# isn't there yet. Source this instead of copying it -- see CLAUDE.md.
export NIX_CONFIG="${NIX_CONFIG:-}
experimental-features = nix-command flakes
accept-flake-config = false
warn-dirty = false
"
ensure_nix_profile() {
if [ -f /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh ]; then
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
elif [ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then
. "$HOME/.nix-profile/etc/profile.d/nix.sh"
fi
}
-36
View File
@@ -1,36 +0,0 @@
#!/usr/bin/env bash
# Shared flake-introspection helpers for scripts/*.sh. Source alongside
# env.sh:
# source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/nix-eval.sh"
#
# NIX_EVAL_FLAGS: --no-use-registries so a call here never resolves through
# the user's global flake registry (every call targets this repo's own
# flake, or an explicit github: ref, not a registry alias); --no-accept-flake-config
# so a flake input's own nixConfig (e.g. a dependency's substituters) is
# never honored -- matches accept-flake-config = false already set repo-wide
# (see lib/nix-bootstrap.sh / CLAUDE.md). Reuse this array rather than
# retyping the two flags at each call site.
declare -a NIX_EVAL_FLAGS=(--no-use-registries --no-accept-flake-config)
# list_flake_targets <flake_ref>
# Prints the attribute names under <flake_ref>#nixosConfigurations, one per
# line, e.g.:
# list_flake_targets . # from inside the repo
# list_flake_targets "$repo_root" # from anywhere
list_flake_targets() {
local flake_ref="$1"
nix eval --json "${NIX_EVAL_FLAGS[@]}" \
"${flake_ref}#nixosConfigurations" --apply builtins.attrNames \
| jq -r '.[]'
}
# flake_target_hostname <flake_ref> <target>
# Prints one nixosConfigurations target's config.networking.hostName.
# Empty (not an error under set -e) if the target doesn't exist or the
# eval otherwise fails -- callers that need to distinguish "empty" from
# "eval failed" should check $? themselves instead of relying on this.
flake_target_hostname() {
local flake_ref="$1" target="$2"
nix eval --raw "${NIX_EVAL_FLAGS[@]}" \
"${flake_ref}#nixosConfigurations.${target}.config.networking.hostName" 2>/dev/null
}
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env bash
# Shared SSH-host-key / age-conversion helpers for scripts/sync-host-keys.sh
# and scripts/prepare-host-key.sh -- both generate the same kind of key
# (ed25519, no passphrase, the sops-nix age-derivation input) and convert it
# to an age recipient the same way; kept in one place so the two can't
# drift apart.
#
# Uses NIX_OPTS (an array of extra `nix-shell` options -- see env.sh's
# nix_extra_opts) if the caller has already set it, so a decision to avoid
# an unreachable nix-cache is reused here instead of probed again. Falls
# back to no extra options if the caller never sourced env.sh.
if ! declare -p NIX_OPTS >/dev/null 2>&1; then
declare -a NIX_OPTS=()
fi
# generate_host_ed25519_key <hostname> <keyfile>
# Writes <keyfile> and <keyfile>.pub. Caller is responsible for refusing to
# overwrite an existing keyfile -- this always runs ssh-keygen fresh.
generate_host_ed25519_key() {
local hostname="$1" keyfile="$2"
nix-shell "${NIX_OPTS[@]}" -p openssh --run \
"ssh-keygen -t ed25519 -N '' -C '${hostname}' -f '${keyfile}'" >/dev/null
}
# ssh_pubkey_to_age <pubkeyfile>
# Prints the age public key derived from an ed25519 SSH public key file.
ssh_pubkey_to_age() {
local pubkeyfile="$1"
nix-shell "${NIX_OPTS[@]}" -p ssh-to-age --run "ssh-to-age -i '${pubkeyfile}'"
}
+2 -7
View File
@@ -23,10 +23,6 @@
set -euo pipefail set -euo pipefail
repo_root="$(cd "$(dirname "$0")/.." && pwd)" repo_root="$(cd "$(dirname "$0")/.." && pwd)"
# shellcheck source=env.sh
source "${repo_root}/scripts/env.sh"
# shellcheck source=lib/ssh-host-keys.sh
source "${repo_root}/scripts/lib/ssh-host-keys.sh"
hostname="${1:?usage: scripts/prepare-host-key.sh <hostname>}" hostname="${1:?usage: scripts/prepare-host-key.sh <hostname>}"
sops_yaml="${repo_root}/.sops.yaml" sops_yaml="${repo_root}/.sops.yaml"
@@ -45,10 +41,9 @@ if [[ -f "$keyfile" ]]; then
exit 1 exit 1
fi fi
nix_extra_opts nix-shell -p openssh --run "ssh-keygen -t ed25519 -N '' -C '${hostname}' -f '${keyfile}'" >/dev/null
generate_host_ed25519_key "$hostname" "$keyfile"
age_pub="$(ssh_pubkey_to_age "${keyfile}.pub")" age_pub="$(nix-shell -p ssh-to-age --run "ssh-to-age -i '${keyfile}.pub'")"
cat <<EOF cat <<EOF
+5 -7
View File
@@ -31,10 +31,6 @@ editor="${repo_root}/scripts/lib/sync-host-keys-edit-sops.py"
# shellcheck source=env.sh # shellcheck source=env.sh
source "${repo_root}/scripts/env.sh" source "${repo_root}/scripts/env.sh"
# shellcheck source=lib/nix-eval.sh
source "${repo_root}/scripts/lib/nix-eval.sh"
# shellcheck source=lib/ssh-host-keys.sh
source "${repo_root}/scripts/lib/ssh-host-keys.sh"
mkdir -p "$keydir" mkdir -p "$keydir"
@@ -122,10 +118,12 @@ EOF
} }
discover_targets() { discover_targets() {
nix eval --json --no-use-registries --no-accept-flake-config \
"${repo_root}#nixosConfigurations" --apply builtins.attrNames \
| jq -r '.[] | select(. != "installer")'
# installer is the one nixosConfigurations target that doesn't import # installer is the one nixosConfigurations target that doesn't import
# sops-nix at all (see CLAUDE.md's "Security Notes" -- hardcoded login # sops-nix at all (see CLAUDE.md's "Security Notes" -- hardcoded login
# password instead) -- config.sops.secrets doesn't exist for it. # password instead) -- config.sops.secrets doesn't exist for it.
list_flake_targets "$repo_root" | grep -v '^installer$'
} }
locally_managed_hosts() { locally_managed_hosts() {
@@ -161,7 +159,7 @@ queue_host_sync() {
echo "[dry-run] ${host}: would generate host key" echo "[dry-run] ${host}: would generate host key"
else else
echo "==> ${host}: generating host key" echo "==> ${host}: generating host key"
generate_host_ed25519_key "$host" "$keyfile" nix-shell "${NIX_OPTS[@]}" -p openssh --run "ssh-keygen -t ed25519 -N '' -C '${host}' -f '${keyfile}'" >/dev/null
fi fi
else else
echo "==> ${host}: host key already present" echo "==> ${host}: host key already present"
@@ -172,7 +170,7 @@ queue_host_sync() {
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"
else else
age_pub="$(ssh_pubkey_to_age "${keyfile}.pub")" age_pub="$(nix-shell "${NIX_OPTS[@]}" -p ssh-to-age --run "ssh-to-age -i '${keyfile}.pub'")"
fi fi
add_keys_json="$(jq --arg host "$host" --arg key "$age_pub" \ add_keys_json="$(jq --arg host "$host" --arg key "$age_pub" \
'. + [{host: $host, age_key: $key}]' <<<"$add_keys_json")" '. + [{host: $host, age_key: $key}]' <<<"$add_keys_json")"