2026-06-30 18:00:26 +02:00
|
|
|
# shellcheck shell=sh disable=SC2034
|
|
|
|
|
# =============================================================================
|
|
|
|
|
# foundation/VERSIONS — determinism pin-file (PLAN-002 §Determinism, baseline D5)
|
|
|
|
|
# =============================================================================
|
|
|
|
|
#
|
|
|
|
|
# WHAT THIS IS
|
|
|
|
|
# The single source of truth for every container image and every host tool the
|
|
|
|
|
# foundation egg depends on. preflight/ and CI both read this file. An upgrade
|
|
|
|
|
# is a one-line diff here (PLAN-002 §7.1).
|
|
|
|
|
#
|
|
|
|
|
# FORMAT
|
|
|
|
|
# Plain `KEY=value`, one per line. This file is BOTH:
|
|
|
|
|
# * shell-`source`-able: `set -a; . ./VERSIONS; set +a`
|
|
|
|
|
# * greppable: `grep '^IMAGE_FORGEJO=' VERSIONS`
|
|
|
|
|
# No spaces around `=`. Values are unquoted simple tokens. Comments start `#`.
|
|
|
|
|
# Keys are UPPER_SNAKE_CASE. Do not use shell expansion in values.
|
|
|
|
|
#
|
|
|
|
|
# IMAGE PINNING (determinism — D5: "No floating tags")
|
|
|
|
|
# The DESIRED end state is to pin every image by DIGEST:
|
|
|
|
|
# IMAGE_<svc>=<repo>:<tag>@sha256:<64-hex-digest>
|
|
|
|
|
# A tag alone is mutable; the digest is the immutable content address.
|
|
|
|
|
#
|
|
|
|
|
# Where a real sha256 digest could NOT be resolved at authoring time (offline /
|
|
|
|
|
# no registry access), the value carries the stable version TAG plus the literal
|
|
|
|
|
# placeholder token `PIN_DIGEST` in the digest position:
|
|
|
|
|
# IMAGE_<svc>=<repo>:<tag>@sha256:PIN_DIGEST
|
|
|
|
|
# preflight treats `PIN_DIGEST` as "not yet pinned" and emits a WARNING (not a
|
|
|
|
|
# failure) so the scaffold is usable before digests are resolved. CI for a real
|
|
|
|
|
# `pulumi up` SHOULD fail-closed on any remaining PIN_DIGEST (gate that later).
|
|
|
|
|
#
|
|
|
|
|
# HOW TO PIN A DIGEST (`pin-digests` procedure — run when online, per image)
|
|
|
|
|
# For each IMAGE_* below, resolve the multi-arch (or linux/amd64) digest and
|
|
|
|
|
# replace `PIN_DIGEST` with the real hex, e.g.:
|
|
|
|
|
#
|
|
|
|
|
# # whole image index digest (recommended — arch-independent):
|
|
|
|
|
# docker manifest inspect caddy:2.10 \
|
|
|
|
|
# --verbose 2>/dev/null | jq -r '.[0].Descriptor.digest // .Descriptor.digest' | head -1
|
|
|
|
|
#
|
|
|
|
|
# # OR, simpler, pull then read the repo digest docker recorded:
|
|
|
|
|
# docker pull caddy:2.10
|
|
|
|
|
# docker inspect --format '{{index .RepoDigests 0}}' caddy:2.10
|
|
|
|
|
# # -> caddy@sha256:<digest> ; copy the <digest> after sha256:
|
|
|
|
|
#
|
|
|
|
|
# # OR with skopeo (no pull):
|
|
|
|
|
# skopeo inspect docker://caddy:2.10 | jq -r '.Digest'
|
|
|
|
|
#
|
|
|
|
|
# Then set: IMAGE_CADDY=caddy:2.10@sha256:<the-real-digest>
|
|
|
|
|
# Commit the VERSIONS diff (conventional commit, e.g. `chore(versions): pin caddy digest`).
|
|
|
|
|
#
|
|
|
|
|
# HOST TOOL VERSIONS are MINIMUMS (>=). preflight compares the installed tool
|
|
|
|
|
# version against TOOL_<x>_MIN using a numeric dotted-version comparison.
|
|
|
|
|
# =============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
# CONTAINER IMAGES (CONTRACT_003 §3.2 — every container the egg runs)
|
|
|
|
|
# Format: IMAGE_<NAME>=<repo>:<tag>@sha256:<digest|PIN_DIGEST>
|
|
|
|
|
# -----------------------------------------------------------------------------
|
2026-06-30 21:54:12 +02:00
|
|
|
# Caddy: the egg runs a CUSTOM build with the Cloudflare DNS-01 plugin (standard
|
|
|
|
|
# caddy:2 lacks it) — recipe + pinned base digests + module version live in
|
|
|
|
|
# containers/caddy-cloudflare/Dockerfile. This is the pinned FINAL base it derives.
|
|
|
|
|
IMAGE_CADDY=caddy:2.10@sha256:c3d7ee5d2b11f9dc54f947f68a734c84e9c9666c92c88a7f30b9cba5da182adb
|
2026-06-30 22:23:39 +02:00
|
|
|
IMAGE_FORGEJO=codeberg.org/forgejo/forgejo:11@sha256:d98d860ea64fd36cb0aabf0b46bbe1a37566b498eee4af0a6b246d5a45759d6d
|
2026-06-30 21:10:34 +02:00
|
|
|
IMAGE_POSTGRES=postgres:17@sha256:5c855ad7b85e68e48a62f34662853f38b57c1c1d80f3a927ab58034fd6d31c5e
|
feat(bootstrap): vault init/unseal + capture to encrypted config (T05)
foundation-vault (hashicorp/vault:1.18, digest-pinned) with integrated raft
storage in foundation-vault-data (-> /vault/file, which the entrypoint chowns to
the vault user), IPC_LOCK for mlock, internal only (8200 unpublished). Init +
unseal reuse the olsitec-core pattern but over docker-exec/SSH (ADR-007): the
foundation-vault-init command inits 1-of-1 Shamir, unseals, and emits keys + root
token on stdout — marked secret and NOT streamed (logging:Stderr) so they never
reach the terminal/logs (D2). run.sh captures them into vaultCredentials:* (the
one bootstrap secret that cannot live in Vault, CONTRACT_002 §2.4) with an
idempotent guard that avoids churning the config. vault-unseal.sh is the
passphrase-gated reboot helper (ADR-004): reads keys from config, unseals over an
SSH stdin pipe. run.sh also now pins the Pulumi backend per-process
(PULUMI_BACKEND_URL) instead of a global `pulumi login`.
Live on cx33 Helsinki: initialized + unsealed (raft 1.18.5), keys captured to
encrypted config, idempotent re-up reuses stored keys, container-restart reseal
recovered by vault-unseal.sh. Acceptance T05 met.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 21:32:52 +02:00
|
|
|
IMAGE_VAULT=hashicorp/vault:1.18@sha256:750bb37c1638fa194ab37053a81618c61bb0491ddec6fccac87c07a8e6cd8166
|
2026-06-30 21:19:53 +02:00
|
|
|
IMAGE_RUSTFS=rustfs/rustfs:latest@sha256:fa19210ac4697c79d7ccca1ec9b0eb91aebacc6691991ffb14014bb3c67e6cc3
|
2026-06-30 22:38:37 +02:00
|
|
|
IMAGE_ACT_RUNNER=code.forgejo.org/forgejo/runner:6@sha256:e8dd2880f2fc81984d2308b93f1bc064dfb41187942300676536c09a3b30043d
|
2026-06-30 18:00:26 +02:00
|
|
|
IMAGE_REGISTRY=registry:2@sha256:PIN_DIGEST
|
|
|
|
|
|
2026-06-30 21:19:53 +02:00
|
|
|
# Tool image: MinIO client `mc` — used transiently (never a long-running service)
|
|
|
|
|
# for S3 control-plane ops against RustFS: bucket creation + service accounts
|
|
|
|
|
# (T04) and backup put/get (T12). RustFS speaks enough of the MinIO admin API.
|
|
|
|
|
IMAGE_MC=minio/mc:latest@sha256:a7fe349ef4bd8521fb8497f55c6042871b2ae640607cf99d9bede5e9bdf11727
|
|
|
|
|
|
2026-06-30 18:00:26 +02:00
|
|
|
# NOTE on specific images:
|
|
|
|
|
# IMAGE_RUSTFS uses `latest` because RustFS does not (yet) publish stable
|
|
|
|
|
# semver tags reliably (PLAN-002 R3 — RustFS is young). MUST be pinned by
|
|
|
|
|
# digest before any real deploy; the digest is what gives determinism here.
|
|
|
|
|
# IMAGE_FORGEJO / IMAGE_ACT_RUNNER pull from Forgejo's own registries
|
|
|
|
|
# (codeberg.org / code.forgejo.org), not Docker Hub.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
# HOST TOOLS (PLAN-002 §9.1 "binary installation" / "host validation")
|
|
|
|
|
# Format: TOOL_<NAME>_MIN=<minimum-version> (preflight enforces installed >= MIN)
|
|
|
|
|
# The matching CLI binary name preflight looks for is in checks/tools.sh.
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
# --- orchestration toolchain ---
|
|
|
|
|
TOOL_PULUMI_MIN=3.140.0
|
|
|
|
|
TOOL_BUN_MIN=1.1.0
|
|
|
|
|
TOOL_NODE_MIN=20.0.0
|
|
|
|
|
|
|
|
|
|
# --- container + scm ---
|
|
|
|
|
TOOL_DOCKER_MIN=24.0.0
|
|
|
|
|
TOOL_GIT_MIN=2.34.0
|
|
|
|
|
|
|
|
|
|
# --- crypto / compression / json (backup + secrets handling) ---
|
|
|
|
|
TOOL_AGE_MIN=1.1.0
|
|
|
|
|
TOOL_ZSTD_MIN=1.5.0
|
|
|
|
|
TOOL_JQ_MIN=1.6
|
|
|
|
|
|
|
|
|
|
# --- service CLIs used by bootstrap / backup / DR ---
|
|
|
|
|
TOOL_VAULT_MIN=1.15.0
|
|
|
|
|
TOOL_PSQL_MIN=15.0
|
|
|
|
|
TOOL_PG_DUMP_MIN=15.0
|
|
|
|
|
|
|
|
|
|
# --- ssh client (Pulumi docker-over-SSH provider, git-over-ssh) ---
|
|
|
|
|
TOOL_OPENSSH_MIN=8.0
|
|
|
|
|
|
|
|
|
|
# --- S3 / RustFS client (bucket ops, backup put/get). MinIO client `mc`. ---
|
|
|
|
|
TOOL_MC_MIN=2023.01.01
|