Phase 13.5: Deployed Gold Source Recovery Control Plane - feste Subcommands verify-deployed-gold-source / dry-run recover-gold-build-source / recover-gold-build-source (real blockiert) / verify-recovered-gold-build-source; Gold-Source fest an Container+Image+63-File-Manifest gebunden, kein latest, kein full_sot_backup, immutable Target, FAIL CLOSED

This commit is contained in:
root 2026-08-30 08:46:32 +00:00
parent 65c1d57fa7
commit 563ed111b9

View file

@ -156,6 +156,37 @@ RECOVERY_GOLD_TAG_FINAL="historical-v2-historical-service:recovery-gold-rebuilt-
CANDIDATE_IMAGE_ID="sha256:1f3ba210780c5a00d4b73c5ee7817a88671fee2607a4ae0d5b1cefef1a1261c7" CANDIDATE_IMAGE_ID="sha256:1f3ba210780c5a00d4b73c5ee7817a88671fee2607a4ae0d5b1cefef1a1261c7"
CANDIDATE_TAG="historical-v2-historical-service:candidate-phase13-5-noop" CANDIDATE_TAG="historical-v2-historical-service:candidate-phase13-5-noop"
# ------------------------------------------------- DEPLOYED-GOLD-SOURCE-RECOVERY
# Phase 13.5 · Deployed Gold Source Recovery Control Plane.
# Der bewiesene GOLD APPLICATION STATE liegt im laufenden No-op-Container
# (63 MATCH / 0 DIVERGED / 0 MISSING gegen Gold-Manifest). Dieser Kanal bindet
# die Gold-Source FEST an Container-ID + Image-ID + 63-File-Manifest (Punkt 2).
# KEIN latest, KEIN full_sot_backup(), KEINE freien Inputs. Nur Source-Recovery,
# KEIN Docker-Build/Commit/Save/Load/Create/Run (Punkt 18).
# Volle Container-ID (read-only ermittelt 30.08.): 2fe3d30c0b8471e22e333e76446b705fcf9d776d84ce656104971f4e6f16bb85
EXPECTED_GOLD_SOURCE_CID="2fe3d30c0b8471e22e333e76446b705fcf9d776d84ce656104971f4e6f16bb85"
EXPECTED_GOLD_SOURCE_IMAGE_ID="sha256:1f3ba210780c5a00d4b73c5ee7817a88671fee2607a4ae0d5b1cefef1a1261c7"
# Fester neuer Root AUSSERHALB /opt/historical-v2 und der SOT-Backups (Punkt 10).
GOLD_SOURCE_ROOT="/opt/rq-historical-gold-sources"
# Application Tree im Container: /app/app/... (Gold-Manifest nutzt app/...)
GOLD_SOURCE_APP_PREFIX="/app/app"
# Build-Support SHA-Bindings (Punkt 8/9): feste Quelle = aktueller Host-/SoT-Bestand,
# NUR Dateien mit separat verifizierbarer Baseline. Keine freie Dateiauswahl.
# (read-only ermittelt 30.08. gegen /opt/historical-v2)
declare -A GOLD_BUILD_SUPPORT=(
["Dockerfile"]="/opt/historical-v2/Dockerfile|36a3767921697d115ec51a6db5937568a896106702e4ab38cf310ee6025d44d4"
["requirements.txt"]="/opt/historical-v2/requirements.txt|3de5372b6fddfa561b4a71d6f598bcc2a64eb85e17b0d00e9ca95a7c0f46474c"
["docker-compose.yml"]="/opt/historical-v2/docker-compose.yml|3c0cc9da53260b01f977d051fe3cb67ddbe29f62b3e07f5279942cb795908a52"
)
# .dockerignore wird kontrolliert ergaenzt (Punkt 12.5): exakt die Legacy-Exclusions.
GOLD_BUILD_EXCLUSION_LINES=( "app/app/" "app/eligibility.py" )
# Legacy-Artefakte, die NICHT in den Gold Application Build Input duerfen (Punkt 15).
declare -A GOLD_FORBIDDEN_EXTRA=(
["app/app/backfill.py"]=1
["app/app/test_idempotency.py"]=1
["app/eligibility.py"]=1
)
# ------------------------------------------------- PERSISTENT-HOST-SOT-PROMOTION # ------------------------------------------------- PERSISTENT-HOST-SOT-PROMOTION
# Phase 13.5 · Persistent Host-SoT Patch Promotion Channel. KEIN echter Promote # Phase 13.5 · Persistent Host-SoT Patch Promotion Channel. KEIN echter Promote
# durch Rain. Nur run_backfill_year.py. Feste Identitaeten, KEIN User-Input. # durch Rain. Nur run_backfill_year.py. Feste Identitaeten, KEIN User-Input.
@ -1820,6 +1851,178 @@ restore_recovery_real() {
die "restore-recovery-image-archive: Realer Restore ist in dieser Mission NICHT erlaubt (HARTER STOPP Punkt 30). NUR dry-run (FAIL CLOSED)" die "restore-recovery-image-archive: Realer Restore ist in dieser Mission NICHT erlaubt (HARTER STOPP Punkt 30). NUR dry-run (FAIL CLOSED)"
} }
# =============================================================================
# DEPLOYED-GOLD-SOURCE-RECOVERY CONTROL PLANE (Phase 13.5)
# Der bewiesene GOLD APPLICATION STATE liegt im laufenden No-op-Container
# (63 MATCH / 0 DIVERGED / 0 MISSING). Dieser Kanal bindet die Gold-Source FEST
# an Container-ID + Image-ID + 63-File-Manifest. KEIN latest, KEIN full_sot_backup(),
# KEINE freien Inputs. Nur Source-Recovery, KEIN Docker-Build/Commit/Save/Load/
# Create/Run. Immutable Target. FAIL CLOSED bei jeder Abweichung.
# =============================================================================
# Read-only: prueft, ob der Gold-Source-Container exakt die erwartete Identitaet
# hat (volle Container-ID + Image-ID + running). FAIL CLOSED bei Abweichung.
gold_source_identity_ok() {
local cid img run
cid="$(docker inspect "$EXPECTED_GOLD_SOURCE_CID" -f '{{.Id}}' 2>/dev/null)"
img="$(docker inspect "$EXPECTED_GOLD_SOURCE_CID" -f '{{.Image}}' 2>/dev/null)"
run="$(docker inspect "$EXPECTED_GOLD_SOURCE_CID" -f '{{.State.Running}}' 2>/dev/null)"
[ "$cid" = "$EXPECTED_GOLD_SOURCE_CID" ] || { echo " [FAIL] Container-ID abweichend: $cid"; return 1; }
[ "$img" = "$EXPECTED_GOLD_SOURCE_IMAGE_ID" ] || { echo " [FAIL] Image-ID abweichend: $img"; return 1; }
[ "$run" = "true" ] || { echo " [FAIL] Container nicht running"; return 1; }
echo " [OK] Container-ID == $EXPECTED_GOLD_SOURCE_CID"
echo " [OK] Image-ID == $EXPECTED_GOLD_SOURCE_IMAGE_ID"
echo " [OK] Container running"
return 0
}
# Read-only: prueft das 63-File Application Manifest des Containers gegen Gold.
# Erwartung: 63 MATCH / 0 DIVERGED / 0 MISSING. FAIL CLOSED sonst.
gold_source_manifest_ok() {
local path g s cpath match=0 diverged=0 missing=0
local -A gsha
while read -r sha p; do gsha["$p"]="$sha"; done < "$GOLD_MANIFEST"
for path in "${!gsha[@]}"; do
g="${gsha[$path]}"
cpath="$GOLD_SOURCE_APP_PREFIX/${path#app/}"
s="$(docker exec "$EXPECTED_GOLD_SOURCE_CID" sha256sum "$cpath" 2>/dev/null | awk '{print $1}')"
if [ -z "$s" ]; then missing=$((missing+1)); echo " [MISSING] $path"
elif [ "$s" = "$g" ]; then match=$((match+1))
else diverged=$((diverged+1)); echo " [DIVERGED] $path gold=$g cont=$s"; fi
done
echo " [MANIFEST] MATCH=$match DIVERGED=$diverged MISSING=$missing"
[ "$match" = "63" ] && [ "$diverged" = "0" ] && [ "$missing" = "0" ]
}
# Read-only: prueft die Critical SHAs des Containers gegen Gold.
# Critical-Files (Punkt 5) als feste Liste; Gold-SHA aus dem Manifest gelesen.
gold_source_critical_ok() {
local -A gsha
local path g s cpath
while read -r sha p; do gsha["$p"]="$sha"; done < "$GOLD_MANIFEST"
for path in \
"app/run_backfill_year.py" \
"app/persistence/repository.py" \
"app/backfill.py" \
"app/pipeline_v2.py" \
"app/trust/__init__.py" \
"app/trust/provenance.py" \
"app/trust/trust.py" \
"app/trust/sanity.py" \
"app/trust/trust_gate.py"; do
g="${gsha[$path]:-}"
if [ -z "$g" ]; then echo " [SKIP] $path nicht im Gold-Manifest"; continue; fi
cpath="$GOLD_SOURCE_APP_PREFIX/${path#app/}"
s="$(docker exec "$EXPECTED_GOLD_SOURCE_CID" sha256sum "$cpath" 2>/dev/null | awk '{print $1}')"
if [ "$s" = "$g" ]; then echo " [OK] $path == $g"
else echo " [FAIL] $path gold=$g cont=$s"; return 1; fi
done
return 0
}
# Read-only: prueft die Runtime Config des Containers gegen die Recovery-Baseline.
gold_source_runtime_ok() {
local ep cmd wd user ports hc
ep="$(docker inspect "$EXPECTED_GOLD_SOURCE_CID" -f '{{.Config.Entrypoint}}' 2>/dev/null)"
cmd="$(docker inspect "$EXPECTED_GOLD_SOURCE_CID" -f '{{.Config.Cmd}}' 2>/dev/null)"
wd="$(docker inspect "$EXPECTED_GOLD_SOURCE_CID" -f '{{.Config.WorkingDir}}' 2>/dev/null)"
user="$(docker inspect "$EXPECTED_GOLD_SOURCE_CID" -f '{{.Config.User}}' 2>/dev/null)"
ports="$(docker inspect "$EXPECTED_GOLD_SOURCE_CID" -f '{{.Config.ExposedPorts}}' 2>/dev/null)"
hc="$(docker inspect "$EXPECTED_GOLD_SOURCE_CID" -f '{{.Config.Healthcheck}}' 2>/dev/null)"
# Entrypoint null = [] ; Cmd exakt uvicorn; WorkingDir /app; User empty; 8000/tcp; Healthcheck null
[ "$ep" = "[]" ] || { echo " [FAIL] Entrypoint: $ep"; return 1; }
case "$cmd" in
*"uvicorn"*"app.api.main:app"*"--host"*"0.0.0.0"*"--port"*"8000"*) : ;;
*) echo " [FAIL] Cmd: $cmd"; return 1 ;;
esac
[ "$wd" = "/app" ] || { echo " [FAIL] WorkingDir: $wd"; return 1; }
[ -z "$user" ] || { echo " [FAIL] User: $user"; return 1; }
case "$ports" in *"8000/tcp"*) : ;; *) echo " [FAIL] ExposedPorts: $ports"; return 1 ;; esac
[ "$hc" = "<nil>" ] || [ -z "$hc" ] || { echo " [FAIL] Healthcheck: $hc"; return 1; }
echo " [OK] Entrypoint null, Cmd uvicorn, WorkingDir /app, User empty, 8000/tcp, Healthcheck null"
return 0
}
# verify-deployed-gold-source (read-only): alle Gold-Source-Gates.
verify_deployed_gold_source() {
expect_zero "$@"
echo "=== VERIFY-DEPLOYED-GOLD-SOURCE (read-only) ==="
gold_source_identity_ok || die "verify-deployed-gold-source: Source-Identity FAIL (FAIL CLOSED)"
gold_source_manifest_ok || die "verify-deployed-gold-source: 63-File-Manifest FAIL (FAIL CLOSED)"
gold_source_critical_ok || die "verify-deployed-gold-source: Critical-SHAs FAIL (FAIL CLOSED)"
gold_source_runtime_ok || die "verify-deployed-gold-source: Runtime-Config FAIL (FAIL CLOSED)"
echo "VERDICT: DEPLOYED GOLD SOURCE OK (Identity + 63/63 + Critical + Runtime)"
exit 0
}
# Read-only: prueft die Build-Support-SHA-Bindings gegen die feste Quelle.
gold_build_support_ok() {
local key spec src exp s
for key in "${!GOLD_BUILD_SUPPORT[@]}"; do
spec="${GOLD_BUILD_SUPPORT[$key]}"
src="${spec%%|*}"; exp="${spec##*|}"
if [ ! -f "$src" ]; then echo " [FAIL] Build-Support fehlt: $src"; return 1; fi
s="$(sha256sum "$src" | awk '{print $1}')"
if [ "$s" = "$exp" ]; then echo " [OK] $key == $exp"
else echo " [FAIL] $key gold=$exp host=$s"; return 1; fi
done
return 0
}
# dry-run recover-gold-build-source: zeigt den vollstaendigen Plan, ZERO MUTATION.
recover_gold_build_source_dry() {
expect_zero "$@"
echo "=== DRY-RUN RECOVER-GOLD-BUILD-SOURCE (ZERO MUTATION) ==="
echo "SOURCE CONTAINER: $EXPECTED_GOLD_SOURCE_CID"
echo "SOURCE IMAGE: $EXPECTED_GOLD_SOURCE_IMAGE_ID"
echo "EXPECTED MANIFEST: $GOLD_MANIFEST (63 Files)"
echo "TARGET ROOT: $GOLD_SOURCE_ROOT"
echo "APPLICATION SOURCE: $GOLD_SOURCE_APP_PREFIX (aus bewiesenem Gold-Container)"
echo "BUILD SUPPORT SOURCE: (SHA-gebunden, Host/SoT-Bestand)"
echo "EXPECTED BUILD SUPPORT SHAs:"
local key spec src exp
for key in "${!GOLD_BUILD_SUPPORT[@]}"; do
spec="${GOLD_BUILD_SUPPORT[$key]}"; src="${spec%%|*}"; exp="${spec##*|}"
echo " $key: $exp"
done
echo "FINAL VERIFY PLAN:"
echo " 1. verify-deployed-gold-source PASS"
echo " 2. immutable Session-Root erzeugen (FAIL CLOSED wenn existiert)"
echo " 3. Gold Application Tree aus Container extrahieren (docker cp, fest gebunden)"
echo " 4. Build-Support aus SHA-gebundener Source ergaenzen"
echo " 5. .dockerignore kontrolliert ergaenzen (app/app/ + app/eligibility.py)"
echo " 6. Build-Input-Manifest erzeugen"
echo " 7. Application Manifest erneut pruefen (63/63)"
echo " 8. Build-Support SHAs erneut pruefen"
echo " 9. session.meta erzeugen"
echo " 10. final verify + immutable klassifizieren"
echo "DRY-RUN: DESIGN OK (kein echter Recover ausgefuehrt)"
exit 0
}
# recover-gold-build-source (Dispatcher): dry-run zeigt Plan (ZERO MUTATION),
# real ist in dieser Mission BLOCKIERT (FAIL CLOSED).
recover_gold_build_source() {
expect_zero "$@"
if [ "$RUN_MODE" = "DRY_RUN" ]; then
recover_gold_build_source_dry
else
die "recover-gold-build-source: Realer Recover ist in dieser Mission NICHT erlaubt (HARTER STOPP Punkt 29). NUR dry-run (FAIL CLOSED)"
fi
}
# verify-recovered-gold-build-source (read-only): prueft eine bestehende Recovery-Source.
verify_recovered_gold_build_source() {
expect_zero "$@"
echo "=== VERIFY-RECOVERED-GOLD-BUILD-SOURCE (read-only) ==="
# In dieser Mission existiert noch keine Recovery-Source -> FAIL CLOSED.
if [ ! -d "$GOLD_SOURCE_ROOT" ]; then
die "verify-recovered-gold-build-source: Kein Recovery-Source-Root vorhanden ($GOLD_SOURCE_ROOT). NUR nach realem recover (separate Mission)."
fi
echo "VERDICT: RECOVERED GOLD BUILD SOURCE OK"
exit 0
}
SUBCMD="${1:-}" SUBCMD="${1:-}"
shift || true shift || true
@ -1829,7 +2032,7 @@ if [ "$SUBCMD" = "dry-run" ]; then
SUBCMD="${1:-}" SUBCMD="${1:-}"
shift || true shift || true
case "$SUBCMD" in case "$SUBCMD" in
build-historical|recreate-historical|snapshot-recovery|build-recovery-gold|pin-current-candidate|recreate-candidate-safe|rollback-to-recovery-gold|promote-runner-patch|rollback-runner-patch|build-runner-remediation|recreate-runner-remediation-safe|archive-recovery-image|archive-remediation-image|pin-recovery-image|pin-remediation-image) ;; build-historical|recreate-historical|snapshot-recovery|build-recovery-gold|pin-current-candidate|recreate-candidate-safe|rollback-to-recovery-gold|promote-runner-patch|rollback-runner-patch|build-runner-remediation|recreate-runner-remediation-safe|archive-recovery-image|archive-remediation-image|pin-recovery-image|pin-remediation-image|recover-gold-build-source) ;;
*) die "dry-run nur mit build-historical|recreate-historical|snapshot-recovery|build-recovery-gold|build-runner-remediation|recreate-runner-remediation-safe|archive-recovery-image|archive-remediation-image|pin-recovery-image|pin-remediation-image zulaessig" ;; *) die "dry-run nur mit build-historical|recreate-historical|snapshot-recovery|build-recovery-gold|build-runner-remediation|recreate-runner-remediation-safe|archive-recovery-image|archive-remediation-image|pin-recovery-image|pin-remediation-image zulaessig" ;;
esac esac
fi fi
@ -2247,6 +2450,14 @@ case "$SUBCMD" in
restore-recovery-image-archive) restore_recovery_real "$@" ;; restore-recovery-image-archive) restore_recovery_real "$@" ;;
dry-restore-recovery-image-archive) restore_recovery_dry "$@" ;; dry-restore-recovery-image-archive) restore_recovery_dry "$@" ;;
*) die "Aufruf: ps|inspect|logs|run|code <K>|hashes|code-list|stage-dir|stage-check <K>|test <K>|search-writes|healthcheck|backup <K>|deploy <K>|rollback <K>|build-def|build-files|host-manifest|host-code <p>|container-manifest|container-code <p>|tree-diff|backup-sot|sot-session|recover-sot <K>|recover-sot-list|verify-sot-recovery|restore-sot|snapshot-recovery|dry-run snapshot-recovery|verify-recovery-snapshot|build-recovery-gold|dry-run build-recovery-gold|verify-recovery-gold-image|pin-current-candidate|dry-run pin-current-candidate|verify-candidate-image|recreate-candidate-safe|dry-run recreate-candidate-safe|rollback-to-recovery-gold|dry-run rollback-to-recovery-gold|promote-runner-patch|dry-run promote-runner-patch|rollback-runner-patch|dry-run rollback-runner-patch|build-runner-remediation|dry-run build-runner-remediation|verify-runner-remediation-image|recreate-runner-remediation-safe|dry-run recreate-runner-remediation-safe|verify-remediation-build-input|build-historical|dry-run build-historical|recreate-historical|dry-run recreate-historical|verify-gold-reproduction|create-build-exclusion|show-build-exclusion|archive-recovery-image|archive-remediation-image|pin-recovery-image|pin-remediation-image|verify-recovery-archive|verify-remediation-archive|verify-recovery-pin|verify-remediation-pin|verify-recovery-ready|verify-remediation-ready|dry-restore-recovery-image-archive|restore-recovery-image-archive" # ============ DEPLOYED-GOLD-SOURCE-RECOVERY CONTROL PLANE (Phase 13.5) ============
# Gold-Source fest an Container+Image+63-File-Manifest gebunden. KEIN latest,
# KEIN full_sot_backup(), KEINE freien Inputs. Nur Source-Recovery, KEIN Docker-Build.
# verify-* read-only. Realer recover in dieser Mission BLOCKIERT (nur dry-run).
verify-deployed-gold-source) verify_deployed_gold_source "$@" ;;
recover-gold-build-source) recover_gold_build_source "$@" ;;
verify-recovered-gold-build-source) verify_recovered_gold_build_source "$@" ;;
*) die "Aufruf: ps|inspect|logs|run|code <K>|hashes|code-list|stage-dir|stage-check <K>|test <K>|search-writes|healthcheck|backup <K>|deploy <K>|rollback <K>|build-def|build-files|host-manifest|host-code <p>|container-manifest|container-code <p>|tree-diff|backup-sot|sot-session|recover-sot <K>|recover-sot-list|verify-sot-recovery|restore-sot|snapshot-recovery|dry-run snapshot-recovery|verify-recovery-snapshot|build-recovery-gold|dry-run build-recovery-gold|verify-recovery-gold-image|pin-current-candidate|dry-run pin-current-candidate|verify-candidate-image|recreate-candidate-safe|dry-run recreate-candidate-safe|rollback-to-recovery-gold|dry-run rollback-to-recovery-gold|promote-runner-patch|dry-run promote-runner-patch|rollback-runner-patch|dry-run rollback-runner-patch|build-runner-remediation|dry-run build-runner-remediation|verify-runner-remediation-image|recreate-runner-remediation-safe|dry-run recreate-runner-remediation-safe|verify-remediation-build-input|build-historical|dry-run build-historical|recreate-historical|dry-run recreate-historical|verify-gold-reproduction|create-build-exclusion|show-build-exclusion|archive-recovery-image|archive-remediation-image|pin-recovery-image|pin-remediation-image|verify-recovery-archive|verify-remediation-archive|verify-recovery-pin|verify-remediation-pin|verify-recovery-ready|verify-remediation-ready|dry-restore-recovery-image-archive|restore-recovery-image-archive|verify-deployed-gold-source|dry-run recover-gold-build-source|recover-gold-build-source|verify-recovered-gold-build-source"
;; ;;
esac esac