CHAT_OUTPUT_BEGIN
COMMAND_ID=NEWFI-260921-NEWFDOM-DR221-PREFLIGHT399
STATUS=OK
RC=0
HOST=pve01
MODE=read-only
COMPONENT=newfi-dr221-current-backup-restore-contract-preflight
REFERENCE_REGISTER_CHECK=OK
REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66
ERROR_REGISTER_CHECK=OK
ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0
COMMAND_SHA256=d156bf6eb6af9791575c95444afc7064500a83372f377c6b64c92c05a8f1640b
DUPLICATE_FAILED_COMMAND_BLOCKED=false
EXECUTION_STARTED=true
CHANGE_DECLARED=false
RESULT_CONTRACT_VALID=true
RESULT_CONTRACT_STATUS=NOT_APPLICABLE
RESULT_CONTRACT_ERROR=NONE
COMMAND_RC=0
CHANGES_MADE=false
ROLLBACK_STARTED=false
ROLLBACK_RESTORED=null
MUTATION_OUTCOME=NO_MUTATION
SANITIZED=yes
SECRETS_INCLUDED=no
PRIVATE_ADDRESSES_INCLUDED=no
RAW_EVIDENCE_SHA256=63a70305030d9344d1975c09a2466596b73b6ebfea50a04eb0e2c68606d8a4be
SANITIZED_OUTPUT_SHA256=63a70305030d9344d1975c09a2466596b73b6ebfea50a04eb0e2c68606d8a4be
OUTPUT_BEGIN
DR221_PREFLIGHT399_BEGIN=true
COMMAND_ID=NEWFI-260921-NEWFDOM-DR221-PREFLIGHT399
IMMUTABLE_PLAN_CHECK=PASS
PREDECESSOR_PROD220_CLOSED=true
PREDECESSOR_COMMAND_ID=NEWFI-260921-NEWFDOM-PROD220-RECOVERY397
GIT_STATE={"branch":"implementation/newfi-closed-mvp-20260906","clean":true,"head":"9c4814db67759dc09a5469d254c41a7b73f4d89a","rcs":[0,0,0,0],"remote":"9c4814db67759dc09a5469d254c41a7b73f4d89a"}
VM210_CLUSTER={"rc":0,"rows":[{"maxdisk":53687091200,"maxmem":4294967296,"name":"newfi-prod","node":"pve01","status":"running","type":"qemu","uptime":1271347,"vmid":210}]}
VM210_QM_STATUS={"rc":0,"stderr_sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","stdout":"status: running"}
VM210_CONFIG_SAFE={"config":{"agent":"enabled=1","boot":"order=scsi0","cores":"2","ide2":"local-lvm:vm-210-cloudinit,media=cdrom","memory":"4096","name":"newfi-prod","onboot":"1","ostype":"l26","scsi0":"local-lvm:vm-210-disk-0,discard=on,size=50G,ssd=1","scsihw":"virtio-scsi-pci","serial0":"socket"},"rc":0,"stderr_sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}
RESTORE_TEMP_VMID_CANDIDATES=[9210,9211,9212,9213,9214,9215,9216,9217,9218,9219]
PVE_STORAGE_SAFE={"rc":255,"rows":[],"stderr_sha256":"392a23116a78a702472c91f3d369a097fc3e924b89800aa3a8b26910c9f685ec"}
BACKUP_RESTORE_HELPERS=[{"bytes":7394,"contract_lines":[{"line":12,"text":"source \"$CONFIG\""},{"line":27,"text":"MANIFEST=\"$STATE_DIR/manifest-${RUN_STAMP}.txt\""},{"line":92,"text":"for cmd in vzdump qm zstd sha256sum rclone rsync ssh flock; do"},{"line":100,"text":"for VMID in $VMIDS; do"},{"line":102,"text":"echo \"=== VM $VMID PRECHECK ===\""},{"line":104,"text":"STATUS=$(qm status \"$VMID\" | awk '{print $2}')"},{"line":106,"text":"echo \"VM $VMID is not running: $STATUS\" >&2"},{"line":110,"text":"timeout 30 qm guest cmd \"$VMID\" ping >/dev/null"},{"line":112,"text":"BEFORE_FILE=\"$STATE_DIR/vm${VMID}-before-${RUN_STAMP}.txt\""},{"line":114,"text":"qm guest exec \"$VMID\" -- /bin/sh -lc '"},{"line":130,"text":"-name \"vzdump-qemu-${VMID}-*.vma.zst\" \\"},{"line":136,"text":"echo \"=== VZDUMP VM $VMID ===\""},{"line":138,"text":"vzdump \"$VMID\" \\"},{"line":147,"text":"ARCHIVE=$("},{"line":151,"text":"-name \"vzdump-qemu-${VMID}-*.vma.zst\" \\"},{"line":159,"text":"[[ -n \"$ARCHIVE\" && -f \"$ARCHIVE\" ]] || {"},{"line":160,"text":"echo \"New archive for VM $VMID not found\" >&2"},{"line":164,"text":"echo \"ARCHIVE=$ARCHIVE\""},{"line":165,"text":"du -h \"$ARCHIVE\""},{"line":168,"text":"zstd -t \"$ARCHIVE\""},{"line":170,"text":"SHA_FILE=\"${ARCHIVE}.sha256\""},{"line":172,"text":"cd \"$(dirname \"$ARCHIVE\")\""},{"line":173,"text":"sha256sum \"$(basename \"$ARCHIVE\")\""},{"line":177,"text":"echo \"=== VM $VMID POSTCHECK ===\""},{"line":179,"text":"qm status \"$VMID\""},{"line":180,"text":"timeout 30 qm guest cmd \"$VMID\" ping >/dev/null"},{"line":182,"text":"AFTER_FILE=\"$STATE_DIR/vm${VMID}-after-${RUN_STAMP}.txt\""},{"line":184,"text":"qm guest exec \"$VMID\" -- /bin/sh -lc '"},{"line":198,"text":"CREATED_ARCHIVES+=(\"$ARCHIVE\")"},{"line":201,"text":"echo \"VMID=$VMID\""},{"line":202,"text":"echo \"ARCHIVE=$ARCHIVE\""},{"line":203,"text":"echo \"SIZE_BYTES=$(stat -c %s \"$ARCHIVE\")\""},{"line":206,"text":"} >>\"$MANIFEST\""},{"line":219,"text":"for ARCHIVE in \"${CREATED_ARCHIVES[@]}\"; do"},{"line":220,"text":"NAME=$(basename \"$ARCHIVE\")"},{"line":223,"text":"--archive \\"},{"line":229,"text":"\"$ARCHIVE\" \\"},{"line":230,"text":"\"${ARCHIVE}.sha256\" \\"},{"line":254,"text":"echo \"=== CLOUD UPLOAD CHUNKED ===\""},{"line":255,"text":"for REMOTE in \"$CLOUD_REMOTE_1\" \"$CLOUD_REMOTE_2\"; do"},{"line":256,"text":"DEST_ROOT=\"${REMOTE}${CLOUD_BASE}/${TIER}/${RUN_STAMP}\""},{"line":257,"text":"echo \"REMOTE=$REMOTE\""},{"line":259,"text":"for ARCHIVE in \"${CREATED_ARCHIVES[@]}\"; do"},{"line":260,"text":"NAME=$(basename \"$ARCHIVE\")"},{"line":261,"text":"CHUNK_TMP_BASE=\"$BACKUP_DIR/.rclone-chunks\" homelab-rclone-copy-chunked \"$ARCHIVE\" \"${DEST_ROOT}/${NAME}\""},{"line":283,"text":"echo \"MANIFEST=$MANIFEST\""}],"gid":0,"mode":"0700","path":"/usr/local/sbin/homelab-vm-full-backup","sha256":"ec16bde2cccf105bc2bd0550517ec8bbca2cbf116645b0bf3cfa15f8ec5ad022","shebang":"#!/usr/bin/env bash","uid":0},{"bytes":12618,"contract_lines":[{"line":5,"text":"source /etc/homelab-vm-full-backup/config.env"},{"line":7,"text":"LOCK=/run/lock/homelab-vm-full-restore-drill.lock"},{"line":10,"text":"echo \"Another restore drill is already running\" >&2"},{"line":16,"text":"LOG=\"$LOG_DIR/restore-drill-${RUN_STAMP}.log\""},{"line":17,"text":"HEALTH=\"$STATE_DIR/latest-restore-drill.txt\""},{"line":19,"text":"WORK=\"$STATE_DIR/restore-drill-${RUN_STAMP}\""},{"line":30,"text":"local vmid end_epoch duration"},{"line":32,"text":"for vmid in \"${CREATED_VMIDS[@]}\"; do"},{"line":33,"text":"if qm status \"$vmid\" >/dev/null 2>&1; then"},{"line":34,"text":"qm stop \"$vmid\" --skiplock 1 >/dev/null 2>&1 || true"},{"line":37,"text":"[[ \"$(qm status \"$vmid\" 2>/dev/null |"},{"line":43,"text":"qm destroy \"$vmid\" \\"},{"line":66,"text":"cp -f \"$HEALTH\" /var/lib/homelab-health/vm170-vm171-full-restore-drill.txt"},{"line":77,"text":"-name \"vzdump-qemu-${source_vmid}-*.vma.zst\" \\"},{"line":85,"text":"local vmid=$1"},{"line":95,"text":"qm set \"$vmid\" \"--$key\" \"${value},link_down=1\""},{"line":96,"text":"done < <(qm config \"$vmid\")"},{"line":100,"text":"local vmid=$1"},{"line":103,"text":"until timeout 10 qm guest cmd \"$vmid\" ping >/dev/null 2>&1; do"},{"line":105,"text":"echo \"QGA timeout for VM $vmid\" >&2"},{"line":114,"text":"local vmid=$1"},{"line":117,"text":"until qm guest exec \"$vmid\" -- /bin/sh -lc '"},{"line":122,"text":"echo \"Docker timeout for VM $vmid\" >&2"},{"line":131,"text":"local vmid=$1"},{"line":133,"text":"local tmp=\"$WORK/qga-${vmid}-${RANDOM}.json\""},{"line":135,"text":"qm guest exec \"$vmid\" -- \"$@\" >\"$tmp\""},{"line":141,"text":"with open(sys.argv[1], encoding=\"utf-8\") as fh:"},{"line":270,"text":"local archive"},{"line":272,"text":"archive=$(latest_archive \"$source_vmid\")"},{"line":274,"text":"[[ -n \"$archive\" && -f \"$archive\" ]] || {"},{"line":275,"text":"echo \"Archive not found for VM $source_vmid\" >&2"},{"line":279,"text":"[[ -f \"${archive}.sha256\" ]] || {"},{"line":280,"text":"echo \"Checksum file missing: ${archive}.sha256\" >&2"},{"line":288,"text":"echo \"ARCHIVE=$archive\""},{"line":291,"text":"if qm status \"$test_vmid\" >/dev/null 2>&1; then"},{"line":292,"text":"echo \"Test VMID $test_vmid already exists; refusing restore\" >&2"},{"line":297,"text":"cd \"$(dirname \"$archive\")\""},{"line":298,"text":"sha256sum -c \"$(basename \"${archive}.sha256\")\""},{"line":301,"text":"zstd -t \"$archive\""},{"line":303,"text":"qmrestore \\"},{"line":304,"text":"\"$archive\" \\"},{"line":306,"text":"--storage \"$RESTORE_STORAGE\" \\"},{"line":313,"text":"qm set \"$test_vmid\" \\"},{"line":314,"text":"--name \"restore-test-${source_vmid}-${RUN_STAMP}\""},{"line":317,"text":"qm config \"$test_vmid\""},{"line":319,"text":"if qm config \"$test_vmid\" |"},{"line":326,"text":"qm start \"$test_vmid\""},{"line":331,"text":"qm guest cmd \"$test_vmid\" get-host-name"},{"line":332,"text":"qm guest cmd \"$test_vmid\" get-osinfo"},{"line":336,"text":"echo \"=== RESTORE NETWORK ISOLATION PREP ===\""},{"line":339,"text":"RESTORE_TEST_IP=\"$(qm config \"$RESTORE_TEST_VMID\" | sed -n \"s/^ipconfig0: ip=\\([^,/]*\\).*/\\1/p\" | head -n1)\""},{"line":343,"text":"qm guest exec \"$RESTORE_TEST_VMID\" --timeout 180 -- bash -lc \"ip addr add ${RESTORE_TEST_IP}/32 dev lo 2>/dev/null || true; docker start dockge-agent bookstack-db bookstack prometheus 2>/dev/null || true; sleep 60; docker start dockge-agent bookstack-db bookst..."},{"line":348,"text":"# RESTORE DOCKER CHECK OVERRIDE V2 - isolated restore drill semantics"},{"line":350,"text":"local vmid=\"$1\""},{"line":352,"text":"restore_ip=\"$(qm config \"$vmid\" | sed -n 's/^ipconfig0: ip=\\([^,/]*\\).*/\\1/p' | head -n1 || true)\""},{"line":354,"text":"echo \"RESTORE_TEST_VMID=$vmid\""},{"line":358,"text":"qm guest exec \"$vmid\" --timeout 120 -- bash -lc \"ip link set lo up || true; ip addr add ${restore_ip}/32 dev lo 2>/dev/null || true\" || true"},{"line":361,"text":"qm guest exec \"$vmid\" --timeout 120 -- bash -lc \"systemctl is-active --quiet docker\" >/dev/null"},{"line":362,"text":"qm guest exec \"$vmid\" --timeout 120 -- bash -lc \"docker start bookstack-db bookstack dockge-agent prometheus >/dev/null 2>&1 || true\" >/dev/null 2>&1 || true"},{"line":368,"text":"out=\"$(qm guest exec \"$vmid\" --timeout 120 -- bash -lc 'docker ps -a --format \"{{.Names}}|{{.Status}}\" | sort; echo \"__TOTAL__=$(docker ps -a -q | wc -l)\"; echo \"__RUNNING__=$(docker ps -q | wc -l)\"; bad=\"$(docker ps -a --format \"{{.Names}}|{{.Status}}\" | awk ..."},{"line":375,"text":"qm guest exec \"$vmid\" --timeout 120 -- bash -lc \"docker start bookstack-db bookstack dockge-agent prometheus >/dev/null 2>&1 || true\" >/dev/null 2>&1 || true"},{"line":395,"text":"qm stop \"$test_vmid\" --skiplock 1 >/dev/null 2>&1 || true; echo \"RESTORE_TEST_VM_${source_vmid}_STOPPED_AFTER_CHECK=yes\""},{"line":424,"text":"cp -f \"$HEALTH\" /var/lib/homelab-health/vm170-vm171-full-restore-drill.txt"},{"line":428,"text":"for vmid in \"${CREATED_VMIDS[@]}\"; do"},{"line":429,"text":"qm stop \"$vmid\" --skiplock 1 || true"},{"line":432,"text":"[[ \"$(qm status \"$vmid\" 2>/dev/null |"},{"line":437,"text":"qm destroy \"$vmid\" \\"}],"gid":0,"mode":"0700","path":"/usr/local/sbin/homelab-vm-full-restore-drill","sha256":"57bba1b7a774b4cfe904dfeb671028bc0427abf04d463e28d8605dbc142faf13","shebang":"#!/usr/bin/env bash","uid":0},{"bytes":8681,"contract_lines":[{"line":5,"text":"WORKER=/usr/local/libexec/homelab-vm-cloud-quorum-node-worker"},{"line":6,"text":"PROBE=/usr/local/libexec/homelab-vm-cloud-quorum-freshness-probe"},{"line":7,"text":"LOCK=/run/lock/homelab-vm-cloud-quorum-queue.lock"},{"line":26,"text":"local vmid=\"$1\""},{"line":27,"text":"pvesh get /cluster/resources --type vm --output-format json | jq -er --argjson id \"$vmid\" '.[]|select(.vmid==$id)|.node'"},{"line":47,"text":"local vmid=\"$1\" action=\"$2\" generation=\"${3:-}\" owner observed host"},{"line":48,"text":"owner=\"$(expected_owner \"$vmid\")\""},{"line":49,"text":"observed=\"$(observed_owner \"$vmid\")\""},{"line":50,"text":"[[ \"$observed\" == \"$owner\" ]] || die \"CLUSTER_OWNER_MISMATCH_${vmid}_${observed}_EXPECTED_${owner}\" 65"},{"line":51,"text":"if [[ \"$owner\" == \"pve01\" ]]; then"},{"line":52,"text":"if [[ \"$action\" == \"verify\" ]]; then \"$WORKER\" verify \"$vmid\" \"$generation\"; else \"$WORKER\" \"$action\" \"$vmid\"; fi"},{"line":54,"text":"host=\"$(owner_host \"$owner\")\""},{"line":56,"text":"ssh \"${SSH_BASE[@]}\" \"$host\" \"$WORKER\" verify \"$vmid\" \"$generation\""},{"line":58,"text":"ssh \"${SSH_BASE[@]}\" \"$host\" \"$WORKER\" \"$action\" \"$vmid\""},{"line":63,"text":"local vmid=\"$1\" owner observed host"},{"line":64,"text":"owner=\"$(expected_owner \"$vmid\")\""},{"line":65,"text":"observed=\"$(observed_owner \"$vmid\")\""},{"line":66,"text":"[[ \"$observed\" == \"$owner\" ]] || die \"CLUSTER_OWNER_MISMATCH_${vmid}_${observed}_EXPECTED_${owner}\" 65"},{"line":67,"text":"if [[ \"$owner\" == \"pve01\" ]]; then"},{"line":68,"text":"\"$PROBE\" decision \"$vmid\""},{"line":70,"text":"host=\"$(owner_host \"$owner\")\""},{"line":71,"text":"ssh \"${SSH_BASE[@]}\" \"$host\" \"$PROBE\" decision \"$vmid\""},{"line":75,"text":"local vmid"},{"line":76,"text":"for vmid in \"${ALL_VMIDS[@]}\"; do"},{"line":77,"text":"printf 'QUEUE_PLAN VMID=%s EXPECTED_OWNER=%s OBSERVED_OWNER=%s\\n' \"$vmid\" \"$(expected_owner \"$vmid\")\" \"$(observed_owner \"$vmid\")\""},{"line":81,"text":"local vmid=\"$1\" now_epoch=\"$2\" out decision generation sealed_epoch age"},{"line":82,"text":"out=\"$(run_probe \"$vmid\")\""},{"line":87,"text":"generation=\"$(awk -F= '$1==\"GENERATION\"{print $2}' <<<\"$out\" | tail -1)\""},{"line":88,"text":"printf 'QUEUE_FRESHNESS VMID=%s DECISION=BACKUP_RESUME GENERATION=%s\\n' \"$vmid\" \"$generation\""},{"line":91,"text":"generation=\"$(awk -F= '$1==\"GENERATION\"{print $2}' <<<\"$out\" | tail -1)\""},{"line":94,"text":"printf 'QUEUE_FRESHNESS VMID=%s DECISION=BACKUP_UNVERIFIED REASON=SEALED_EPOCH_INVALID\\n' \"$vmid\""},{"line":99,"text":"printf 'QUEUE_FRESHNESS VMID=%s DECISION=SKIP_FRESH GENERATION=%s AGE_SECONDS=%s DUE_SECONDS=%s FRESHNESS_SECONDS=%s\\n' \"$vmid\" \"$generation\" \"$age\" \"$DUE_SECONDS\" \"$FRESHNESS_SECONDS\""},{"line":101,"text":"printf 'QUEUE_FRESHNESS VMID=%s DECISION=BACKUP_DUE GENERATION=%s AGE_SECONDS=%s DUE_SECONDS=%s FRESHNESS_SECONDS=%s\\n' \"$vmid\" \"$generation\" \"$age\" \"$DUE_SECONDS\" \"$FRESHNESS_SECONDS\""},{"line":103,"text":"printf 'QUEUE_FRESHNESS VMID=%s DECISION=BACKUP_STALE GENERATION=%s AGE_SECONDS=%s DUE_SECONDS=%s FRESHNESS_SECONDS=%s\\n' \"$vmid\" \"$generation\" \"$age\" \"$DUE_SECONDS\" \"$FRESHNESS_SECONDS\""},{"line":105,"text":"printf 'QUEUE_FRESHNESS VMID=%s DECISION=BACKUP_UNVERIFIED REASON=SEALED_TIMESTAMP_IN_FUTURE GENERATION=%s AGE_SECONDS=%s\\n' \"$vmid\" \"$generation\" \"$age\""},{"line":109,"text":"printf 'QUEUE_FRESHNESS VMID=%s DECISION=BACKUP_UNVERIFIED\\n' \"$vmid\""},{"line":112,"text":"die \"FRESHNESS_DECISION_INVALID_${vmid}_${decision:-EMPTY}\" 65"},{"line":117,"text":"local vmid now_epoch out decision"},{"line":119,"text":"for vmid in \"${ALL_VMIDS[@]}\"; do"},{"line":121,"text":"out=\"$(freshness_decision \"$vmid\" \"$now_epoch\")\""},{"line":124,"text":"[[ -n \"$decision\" ]] || die \"FRESHNESS_PLAN_DECISION_MISSING_$vmid\" 65"},{"line":125,"text":"printf 'QUEUE_FRESHNESS_PLAN VMID=%s DECISION=%s\\n' \"$vmid\" \"$decision\""},{"line":129,"text":"local vmid out generation"},{"line":134,"text":"for vmid in \"$@\"; do"},{"line":135,"text":"printf 'QUEUE_BEGIN VMID=%s OWNER=%s MODE=FORCE\\n' \"$vmid\" \"$(expected_owner \"$vmid\")\""},{"line":136,"text":"out=\"$(exec 8>&-; exec 9>&-; run_worker \"$vmid\" backup)\""},{"line":138,"text":"generation=\"$(awk -F= '$1==\"GENERATION\"{print $2}' <<<\"$out\" | tail -1)\""},{"line":139,"text":"[[ -n \"$generation\" ]] || die \"GENERATION_NOT_RETURNED_$vmid\" 65"},{"line":140,"text":"printf 'QUEUE_SEALED VMID=%s GENERATION=%s\\n' \"$vmid\" \"$generation\""},{"line":147,"text":"local vmid=\"$1\" freshness decision now_epoch out generation"},{"line":149,"text":"expected_owner \"$vmid\" >/dev/null"},{"line":152,"text":"printf 'QUEUE_DEFERRED KIND=VM REASON=GLOBAL_QUEUE_LOCK_BUSY VMID=%s\\n' \"$vmid\""},{"line":157,"text":"printf 'QUEUE_DEFERRED KIND=VM REASON=GLOBAL_HEAVY_LOCK_BUSY VMID=%s\\n' \"$vmid\""},{"line":161,"text":"freshness=\"$(exec 8>&-; exec 9>&-; freshness_decision \"$vmid\" \"$now_epoch\")\""},{"line":166,"text":"generation=\"$(awk '/^QUEUE_FRESHNESS /{for(i=1;i<=NF;i++)if($i~/^GENERATION=/){sub(/^GENERATION=/,\"\",$i);print $i}}' <<<\"$freshness\" | tail -1)\""},{"line":167,"text":"printf 'QUEUE_DISPATCH_RESULT KIND=VM VMID=%s RESULT=SKIP_FRESH GENERATION=%s\\n' \"$vmid\" \"$generation\""},{"line":171,"text":"*) die \"FRESHNESS_RECHECK_INVALID_${vmid}_${decision:-EMPTY}\" 65 ;;"},{"line":173,"text":"printf 'QUEUE_BEGIN VMID=%s OWNER=%s MODE=%s DISPATCH=ONE_TARGET\\n' \"$vmid\" \"$(expected_owner \"$vmid\")\" \"$decision\""},{"line":174,"text":"out=\"$(exec 8>&-; exec 9>&-; run_worker \"$vmid\" backup)\""},{"line":176,"text":"generation=\"$(awk -F= '$1==\"GENERATION\"{print $2}' <<<\"$out\" | tail -1)\""},{"line":177,"text":"[[ -n \"$generation\" ]] || die \"GENERATION_NOT_RETURNED_$vmid\" 65"},{"line":178,"text":"printf 'QUEUE_SEALED VMID=%s GENERATION=%s DISPATCH=ONE_TARGET\\n' \"$vmid\" \"$generation\""},{"line":179,"text":"printf 'QUEUE_DISPATCH_RESULT KIND=VM VMID=%s RESULT=SEALED GENERATION=%s\\n' \"$vmid\" \"$generation\""}],"gid":0,"mode":"0755","path":"/usr/local/sbin/homelab-vm-cloud-quorum-queue","sha256":"fbc27ad67fc3d3f51934e1d73180fe0df8c9d15f129f34ebba39c56aa1d03612","shebang":"#!/usr/bin/env bash","uid":0},{"bytes":17702,"contract_lines":[{"line":4,"text":"TRANSPORT=/usr/local/libexec/homelab-cloud-quorum-payload"},{"line":6,"text":"STATE_ROOT=/var/lib/homelab-backup/cloud-quorum"},{"line":7,"text":"STAGE_ROOT=/mnt/staging/homelab-vm-cloud-quorum"},{"line":8,"text":"RESTORE_STATE_ROOT=/var/lib/homelab-backup/vm-restore-validation"},{"line":9,"text":"RESTORE_WORK_ROOT=/mnt/staging/homelab-vm-restore-validation"},{"line":15,"text":"for tool in jq sha256sum zstd tar rclone stat find sort awk date mkfifo tee wc vma; do command -v \"$tool\" >/dev/null 2>&1 || die \"TOOL_MISSING_${tool}\" 69; done"},{"line":21,"text":"active_backup_restore(){ local rc; if pgrep -af '(^|/)(vzdump|qmrestore|pctrestore|vma|zstd|rclone)([[:space:]]|$)'; then return 0; else rc=$?; [[ \"$rc\" == 1 ]] && return 0; die \"PGREP_FAILED_$rc\" 69; fi; }"},{"line":22,"text":"snapshot_count_160(){ qm listsnapshot 160 | awk '/->/ && $0 !~ /current/{n++} END{print n+0}'; }"},{"line":23,"text":"vm_lock(){ qm config \"$1\" | awk -F': ' '$1==\"lock\"{print $2; found=1} END{if(!found) print \"none\"}'; }"},{"line":43,"text":"local vmid=\"$1\" type=\"$2\""},{"line":44,"text":"if [[ \"$type\" == \"qemu\" ]]; then qm status \"$vmid\" | awk '{print $2}'; else pct status \"$vmid\" | awk '{print $2}'; fi"},{"line":47,"text":"local vmid=\"$1\" owner type status expect_status"},{"line":48,"text":"owner=\"$(expected_owner \"$vmid\")\""},{"line":49,"text":"type=\"$(expected_type \"$vmid\")\""},{"line":50,"text":"[[ \"$HOST\" == \"$owner\" ]] || die \"OWNER_MISMATCH_${vmid}_${HOST}_EXPECTED_${owner}\" 65"},{"line":52,"text":"[[ -s \"/etc/pve/qemu-server/${vmid}.conf\" ]] || die \"QEMU_CONFIG_MISSING_$vmid\" 66"},{"line":54,"text":"[[ -s \"/etc/pve/lxc/${vmid}.conf\" ]] || die \"LXC_CONFIG_MISSING_$vmid\" 66"},{"line":56,"text":"status=\"$(current_status \"$vmid\" \"$type\")\""},{"line":57,"text":"expect_status=\"$(expected_status \"$vmid\")\""},{"line":58,"text":"[[ \"$status\" == \"$expect_status\" ]] || die \"STATUS_MISMATCH_${vmid}_${status}_EXPECTED_${expect_status}\" 66"},{"line":59,"text":"if [[ \"$vmid\" == \"160\" ]]; then [[ \"$(snapshot_count_160)\" == \"0\" ]] || die \"VM160_SNAPSHOT_COUNT_CHANGED\" 66; fi"},{"line":60,"text":"if [[ \"$vmid\" == \"9130\" ]]; then [[ \"$(vm_lock 9130)\" == \"none\" ]] || die \"VM9130_LOCK_PRESENT\" 66; fi"},{"line":65,"text":"local vmid=\"$1\" type=\"$2\" status expect_status"},{"line":66,"text":"status=\"$(current_status \"$vmid\" \"$type\")\""},{"line":67,"text":"expect_status=\"$(expected_status \"$vmid\")\""},{"line":68,"text":"[[ \"$status\" == \"$expect_status\" ]] || die \"POST_STATUS_MISMATCH_${vmid}_${status}_EXPECTED_${expect_status}\" 66"},{"line":69,"text":"if [[ \"$vmid\" == \"160\" ]]; then [[ \"$(snapshot_count_160)\" == \"0\" ]] || die \"VM160_SNAPSHOT_COUNT_CHANGED_POST\" 66; fi"},{"line":70,"text":"if [[ \"$vmid\" == \"9130\" ]]; then [[ \"$(vm_lock 9130)\" == \"none\" ]] || die \"VM9130_LOCK_PRESENT_POST\" 66; fi"},{"line":73,"text":"local vmid=\"$1\" dir=\"$STATE_ROOT/vm${vmid}\" f"},{"line":81,"text":"local vmid=\"$1\" gen=\"$2\" f state sha"},{"line":82,"text":"f=\"$(restore_dir \"$vmid\")/$gen.json\"; state=\"$STATE_ROOT/vm${vmid}/$gen.json\""},{"line":83,"text":"[[ -s \"$f\" && ! -L \"$f\" && -s \"$state\" && ! -L \"$state\" ]] || return 1"},{"line":84,"text":"sha=\"$(jq -er '.archive_sha256' \"$state\")\" || return 1"},{"line":85,"text":"jq -e --arg l \"vm${vmid}\" --arg g \"$gen\" --arg s \"$state\" --arg sha \"$sha\" '.schema==\"homelab-vm-restore-validation-v1\" and .logical_id==$l and .generation==$g and .transport_state==\"SEALED\" and .transport_state_file==$s and .restore_source==\"Cloud A\" and .par..."},{"line":88,"text":"local vmid=\"$1\" gen=\"$2\" state=\"$3\" work=\"$4\" base manifest expected_msha got_msha expected_count expected_size idx name size psha count total"},{"line":89,"text":"base=\"$(jq -er '.remote_a_base' \"$state\")\"; expected_msha=\"$(jq -er '.parts_manifest_sha256' \"$state\")\"; expected_count=\"$(jq -er '.part_count' \"$state\")\"; expected_size=\"$(jq -er '.archive_size_bytes' \"$state\")\""},{"line":90,"text":"[[ \"$base\" == \"${REMOTE_A%/}/vm${vmid}/$gen\" ]] || die \"RESTORE_A_BASE_MISMATCH_$vmid\" 70"},{"line":91,"text":"manifest=\"$work/PARTS.tsv\"; rclone cat \"${base%/}/PARTS.tsv\" >\"$manifest\""},{"line":92,"text":"got_msha=\"$(sha256sum \"$manifest\"|awk '{print $1}')\"; [[ \"$got_msha\" == \"$expected_msha\" ]] || die \"RESTORE_A_MANIFEST_SHA_MISMATCH_$vmid\" 70"},{"line":95,"text":"[[ \"$idx\" =~ ^[0-9]{8}$ && \"$name\" == part-????????.bin && \"$size\" =~ ^[1-9][0-9]*$ && \"$psha\" =~ ^[0-9a-f]{64}$ ]] || die \"RESTORE_A_MANIFEST_ROW_INVALID_$vmid\" 70"},{"line":96,"text":"(( size <= 536870912 )) || die \"RESTORE_A_PART_EXCEEDS_BOUND_$vmid\" 70"},{"line":97,"text":"[[ \"$idx\" == \"$(printf '%08d' \"$count\")\" ]] || die \"RESTORE_A_MANIFEST_INDEX_INVALID_$vmid\" 70"},{"line":99,"text":"done <\"$manifest\""},{"line":100,"text":"[[ \"$count\" == \"$expected_count\" ]] || die \"RESTORE_A_PART_COUNT_MISMATCH_$vmid\" 70"},{"line":101,"text":"[[ \"$total\" == \"$expected_size\" ]] || die \"RESTORE_A_MANIFEST_TOTAL_SIZE_MISMATCH_$vmid\" 70"},{"line":105,"text":"local vmid=\"$1\" base=\"$2\" manifest=\"$3\" work=\"$4\" idx name size psha part actual count"},{"line":106,"text":"count=0; part=\"$work/current.part\""},{"line":108,"text":"rm -f -- \"$part\"; rclone cat \"${base%/}/payload.parts/$name\" >\"$part\""},{"line":109,"text":"[[ -s \"$part\" && ! -L \"$part\" && \"$(stat -c %s \"$part\")\" == \"$size\" ]] || die \"RESTORE_A_PART_SIZE_MISMATCH_$vmid\" 70"},{"line":110,"text":"actual=\"$(sha256sum \"$part\"|awk '{print $1}')\"; [[ \"$actual\" == \"$psha\" ]] || die \"RESTORE_A_PART_SHA_MISMATCH_$vmid\" 70"},{"line":111,"text":"cat -- \"$part\"; rm -f -- \"$part\"; count=$((count+1))"},{"line":112,"text":"done <\"$manifest\""},{"line":115,"text":"local vmid=\"$1\" gen=\"$2\" logical state work payload_name expected_sha expected_size base manifest fifo sha_out sha_pid pipe_rc sha_rc actual_sha config config_sha members member_count cfg_member level out tmp"},{"line":116,"text":"assert_local_identity \"$vmid\"; logical=\"vm${vmid}\"; state=\"$STATE_ROOT/$logical/$gen.json\""},{"line":117,"text":"[[ -s \"$state\" && ! -L \"$state\" ]] || die \"RESTORE_STATE_MISSING_$vmid\" 65"},{"line":118,"text":"jq -e '.schema==\"homelab-cloud-quorum-payload-v1\" and .status==\"SEALED\" and .cloud_quorum==2 and .cloud_a_verified==true and .cloud_b_verified==true and .part_hashes_verified==true and .seal_written==true and .cloud_a_seal_verified==true and .cloud_b_seal_veri..."},{"line":119,"text":"if restore_ok \"$vmid\" \"$gen\"; then printf 'RESTORE_VALIDATION_STATUS=OK\\nVMID=%s\\nGENERATION=%s\\nRESULT=ALREADY_VALIDATED\\nRESTORE_EVIDENCE=%s\\n' \"$vmid\" \"$gen\" \"$(restore_dir \"$vmid\")/$gen.json\"; return 0; fi"},{"line":121,"text":"payload_name=\"$(jq -er '.payload_name' \"$state\")\"; expected_sha=\"$(jq -er '.archive_sha256' \"$state\")\"; expected_size=\"$(jq -er '.archive_size_bytes' \"$state\")\""},{"line":122,"text":"[[ \"$payload_name\" == \"vzdump-lxc-${vmid}-\"*.tar.zst || \"$payload_name\" == \"vzdump-qemu-${vmid}-\"*.vma.zst ]] || die \"RESTORE_PAYLOAD_NAME_INVALID_$vmid\" 70"},{"line":123,"text":"work=\"$RESTORE_WORK_ROOT/$logical/$gen\"; if [[ -e \"$work\" ]]; then [[ -d \"$work\" && ! -L \"$work\" ]] || die \"RESTORE_WORK_UNSAFE_$vmid\" 70; rm -rf --one-file-system \"$work\"; fi; mkdir -p \"$work\"; chmod 0700 \"$RESTORE_WORK_ROOT\" \"$RESTORE_WORK_ROOT/$logical\" \"$w..."},{"line":124,"text":"base=\"$(prepare_cloud_a_manifest \"$vmid\" \"$gen\" \"$state\" \"$work\")\"; manifest=\"$work/PARTS.tsv\"; fifo=\"$work/archive.sha.fifo\"; sha_out=\"$work/archive.sha256\"; mkfifo -m 0600 \"$fifo\""},{"line":127,"text":"members=\"$work/members.txt\"; if stream_cloud_a \"$vmid\" \"$base\" \"$manifest\" \"$work\" | tee \"$fifo\" | zstd -dc | tar -tf - >\"$members\"; then pipe_rc=0; else pipe_rc=$?; fi"},{"line":129,"text":"if stream_cloud_a \"$vmid\" \"$base\" \"$manifest\" \"$work\" | tee \"$fifo\" | zstd -t - >/dev/null; then pipe_rc=0; else pipe_rc=$?; fi"},{"line":132,"text":"(( pipe_rc == 0 && sha_rc == 0 )) || die \"RESTORE_A_STREAM_VALIDATION_FAILED_${vmid}_${pipe_rc}_${sha_rc}\" 70"},{"line":133,"text":"actual_sha=\"$(awk 'NR==1{print $1}' \"$sha_out\")\"; [[ \"$actual_sha\" == \"$expected_sha\" ]] || die \"RESTORE_A_PAYLOAD_SHA_MISMATCH_$vmid\" 70"},{"line":136,"text":"[[ -s \"$members\" ]] || die \"RESTORE_LXC_ARCHIVE_EMPTY_$vmid\" 70"},{"line":137,"text":"awk '$0 ~ /^\\// || $0 ~ /(^|\\/)\\.\\.(\\/|$)/ {bad=1} END{exit bad}' \"$members\" || die \"RESTORE_LXC_MEMBER_UNSAFE_$vmid\" 70"},{"line":138,"text":"member_count=\"$(wc -l <\"$members\")\"; [[ \"$member_count\" =~ ^[1-9][0-9]*$ ]] || die \"RESTORE_LXC_MEMBER_COUNT_INVALID_$vmid\" 70"},{"line":139,"text":"cfg_member=\"$(awk '$0==\"./etc/vzdump/pct.conf\" || $0==\"etc/vzdump/pct.conf\" {print; exit}' \"$members\")\"; [[ -n \"$cfg_member\" ]] || die \"RESTORE_LXC_CONFIG_MEMBER_MISSING_$vmid\" 70"},{"line":140,"text":"if ( set +o pipefail; stream_cloud_a \"$vmid\" \"$base\" \"$manifest\" \"$work\" | zstd -dc | tar -xOf - \"$cfg_member\" >\"$config\" ); then :; else die \"RESTORE_LXC_CONFIG_EXTRACT_FAILED_$vmid\" 70; fi"},{"line":141,"text":"level=\"cloud-a-full-readback+part-sha+archive-sha+zstd+tar-list+config\""},{"line":143,"text":"if ( set +o pipefail; stream_cloud_a \"$vmid\" \"$base\" \"$manifest\" \"$work\" | zstd -dc | vma config - >\"$config\" ); then :; else die \"RESTORE_QEMU_CONFIG_EXTRACT_FAILED_$vmid\" 70; fi"},{"line":144,"text":"level=\"cloud-a-full-readback+part-sha+archive-sha+zstd+vma-config\""},{"line":146,"text":"[[ -s \"$config\" && ! -L \"$config\" ]] || die \"RESTORE_CONFIG_EMPTY_$vmid\" 70; config_sha=\"$(sha256sum \"$config\"|awk '{print $1}')\""},{"line":147,"text":"out=\"$(restore_dir \"$vmid\")/$gen.json\"; mkdir -p \"$(dirname \"$out\")\"; chmod 0700 \"$RESTORE_STATE_ROOT\" \"$(dirname \"$out\")\"; tmp=\"${out}.new.$$\""},{"line":148,"text":"jq -n --arg logical \"$logical\" --arg generation \"$gen\" --argjson vmid \"$vmid\" --arg vm_type \"$VM_TYPE\" --arg state \"$state\" --arg sha \"$expected_sha\" --arg config_sha \"$config_sha\" --arg level \"$level\" --argjson member_count \"$member_count\" --arg at \"$(date -u..."},{"line":150,"text":"restore_ok \"$vmid\" \"$gen\" || die \"RESTORE_EVIDENCE_POSTWRITE_INVALID_$vmid\" 70"},{"line":151,"text":"printf 'RESTORE_VALIDATION_STATUS=OK\\nVMID=%s\\nGENERATION=%s\\nRESTORE_EVIDENCE=%s\\n' \"$vmid\" \"$gen\" \"$out\""},{"line":154,"text":"local vmid=\"$1\" owner type status"},{"line":155,"text":"assert_local_identity \"$vmid\""}],"gid":0,"mode":"0750","path":"/usr/local/libexec/homelab-vm-cloud-quorum-node-worker","sha256":"91bdd116be865560a38105dffbd1a66d687232035e2359e4a5be4d225e9c61d1","shebang":"#!/usr/bin/env bash","uid":0},{"bytes":38039,"contract_lines":[{"line":5,"text":"SCHEMA=\"homelab-cloud-quorum-payload-v1\""},{"line":19,"text":"TMP_BASE=\"${HOMELAB_QUORUM_TMP_BASE:-/mnt/staging/homelab-cloud-quorum-tmp}\""},{"line":33,"text":"if [[ -n \"$WORK\" && \"$WORK\" == \"$TMP_BASE\"/.quorum.* && -d \"$WORK\" ]]; then"},{"line":41,"text":"for tool in jq rclone sha256sum dd stat sort mktemp flock awk cmp date; do"},{"line":46,"text":"usage(){"},{"line":47,"text":"printf 'usage: %s backup-delete LOGICAL_ID GENERATION PAYLOAD REMOTE_A_ROOT REMOTE_B_ROOT STATE_ROOT\\n' \"$0\" >&2"},{"line":48,"text":"printf '       %s verify-sealed LOGICAL_ID GENERATION REMOTE_A_ROOT REMOTE_B_ROOT STATE_ROOT\\n' \"$0\" >&2"},{"line":55,"text":"[[ \"$#\" -eq 7 ]] || { usage; die \"ARGUMENT_COUNT_INVALID\" 64; }"},{"line":57,"text":"GENERATION=\"$3\""},{"line":64,"text":"[[ \"$#\" -eq 6 ]] || { usage; die \"ARGUMENT_COUNT_INVALID\" 64; }"},{"line":66,"text":"GENERATION=\"$3\""},{"line":73,"text":"usage"},{"line":79,"text":"<SENSITIVE_LINE_WITHHELD>"},{"line":84,"text":"if [[ \"$TEST_MODE\" != \"1\" && \"$STATE_ROOT\" != \"/var/lib/homelab-backup/cloud-quorum\" ]]; then"},{"line":89,"text":"STATE_FILE=\"$STATE_DIR/$GENERATION.json\""},{"line":90,"text":"REMOTE_A_BASE=\"${REMOTE_A_ROOT%/}/$LOGICAL_ID/$GENERATION\""},{"line":91,"text":"REMOTE_B_BASE=\"${REMOTE_B_ROOT%/}/$LOGICAL_ID/$GENERATION\""},{"line":106,"text":"STATE_FILE=\"$STATE_DIR/$GENERATION.json\""},{"line":121,"text":"WORK=\"$(mktemp -d \"$TMP_BASE/.quorum.XXXXXX\")\""},{"line":122,"text":"[[ \"$WORK\" == \"$TMP_BASE\"/.quorum.* ]] || die \"WORK_PATH_INVALID\" 70"},{"line":154,"text":"--arg generation \"$GENERATION\" \\"},{"line":160,"text":".generation==$generation and"},{"line":190,"text":"--arg generation \"$GENERATION\" \\"},{"line":202,"text":"'{schema:$schema,status:$status,logical_id:$logical_id,generation:$generation,"},{"line":380,"text":"RCLONE_BIN=\"$(command -v rclone)\" || die \"RCLONE_MISSING\" 69"},{"line":384,"text":"RCLONE_BIN=\"$(command -v rclone)\" || die \"RCLONE_MISSING\" 69"},{"line":397,"text":"root_listing=\"$WORK/remote-${label}-root.lsf\""},{"line":398,"text":"logical_listing=\"$WORK/remote-${label}-logical.lsf\""},{"line":403,"text":"if ! awk -v target=\"$LOGICAL_ID/\" '$0==target{found=1} END{exit !found}' \"$root_listing\"; then"},{"line":410,"text":"if awk -v target=\"$GENERATION/\" '$0==target{found=1} END{exit !found}' \"$logical_listing\"; then"},{"line":560,"text":"PARTS_MANIFEST=\"$WORK/PARTS.tsv\""},{"line":561,"text":"PART_FILE=\"$WORK/current.part\""},{"line":567,"text":"printf 'part-%08d.bin\\n' \"$idx\""},{"line":614,"text":"$2!=sprintf(\"part-%08d.bin\",NR-1) {exit 1}"},{"line":630,"text":"printf '%s\\n' \"${base%/}/payload.parts\""},{"line":636,"text":"printf '%s\\n' \"${base%/}/PARTS.tsv\""},{"line":642,"text":"[[ \"$CURRENT_PART_NAME\" == part-????????.bin ]] || die \"CURRENT_PART_NAME_INVALID\" 65"},{"line":676,"text":"$2!=sprintf(\"part-%08d.bin\",NR-1) {exit 1}"},{"line":700,"text":"remote_manifest=\"$WORK/remote-${label}-PARTS.tsv\""},{"line":701,"text":"hash_out=\"$WORK/remote-${label}-reconstructed.sha256\""},{"line":710,"text":"[[ \"$idx\" =~ ^[0-9]{8}$ && \"$name\" == part-????????.bin && \"$size\" =~ ^[1-9][0-9]*$ && \"$sha\" =~ ^[0-9a-f]{64}$ ]] || die \"REMOTE_${label}_MANIFEST_ROW_INVALID\" 65"},{"line":831,"text":"schema:\"homelab-cloud-quorum-seal-v1\","},{"line":834,"text":"generation:.generation,"},{"line":851,"text":".schema==\"homelab-cloud-quorum-seal-v1\" and"},{"line":854,"text":"(.generation|type==\"string\" and length>0) and"}],"gid":0,"mode":"0750","path":"/usr/local/libexec/homelab-cloud-quorum-payload","sha256":"df9e19485e5d87112bf58bf77bdddf3b9b896418dd85ff945a31cd5b6817f352","shebang":"#!/usr/bin/env bash","uid":0},{"bytes":1656,"contract_lines":[],"gid":0,"mode":"0750","path":"/usr/local/sbin/homelab-backup-v2","sha256":"5077444065c732451cb84898680f62361b21a24ef9eb4f191253e82ead524ea2","shebang":"#!/usr/bin/python3","uid":0}]
BACKUP_RESTORE_HELPER_MISSING=[]
BACKUP_RESTORE_UNITS=[{"bytes":240,"contract_lines":[{"line":2,"text":"Description=Homelab canonical VM/CT cloud quorum backup queue"},{"line":3,"text":"After=network-online.target"},{"line":4,"text":"Wants=network-online.target"},{"line":8,"text":"ExecStart=/usr/local/sbin/homelab-vm-cloud-quorum-queue all"}],"gid":0,"mode":"0644","path":"/etc/systemd/system/homelab-vm-cloud-quorum-queue.service","sha256":"6aa2c72d3eda795f53d25292a11225ae13b418651cef9cce040564ee873fe759","shebang":null,"uid":0},{"bytes":244,"contract_lines":[{"line":2,"text":"Description=Run canonical Homelab VM/CT cloud quorum freshness queue every 4 hours"},{"line":8,"text":"Unit=homelab-vm-cloud-quorum-queue.service"},{"line":11,"text":"WantedBy=timers.target"}],"gid":0,"mode":"0644","path":"/etc/systemd/system/homelab-vm-cloud-quorum-queue.timer","sha256":"9a78c9ea507116cbb9f5645fe697e34964c905b017cc98b17977cd5006aa5050","shebang":null,"uid":0}]
BACKUPV2_CONFIG_CONTRACTS=[{"meta":{"bytes":5105,"mode":"0600","path":"/etc/homelab-backup-v2/targets.json","sha256":"a723befc7fd42d70af9088406598f18156dc3b4c76d8b34c4bb725dfaa48dd38"},"safe":{"targets":[{"enabled":true,"logical_id":"ct110","scope":"pve","type":"pve_guest","vmid":110},{"enabled":true,"logical_id":"ct111","scope":"pve","type":"pve_guest","vmid":111},{"enabled":true,"logical_id":"ct112","scope":"pve","type":"pve_guest","vmid":112},{"enabled":true,"logical_id":"ct113","scope":"pve","type":"pve_guest","vmid":113},{"enabled":true,"logical_id":"vm130","scope":"pve","type":"pve_guest","vmid":130},{"enabled":true,"logical_id":"vm150","scope":"pve","type":"pve_guest","vmid":150},{"enabled":true,"logical_id":"vm160","scope":"pve","type":"pve_guest","vmid":160},{"enabled":true,"logical_id":"vm170","scope":"pve","type":"pve_guest","vmid":170},{"enabled":true,"logical_id":"vm171","scope":"pve","type":"pve_guest","vmid":171},{"enabled":true,"logical_id":"vm190","scope":"pve","type":"pve_guest","vmid":190},{"enabled":true,"logical_id":"vm210","scope":"pve","type":"pve_guest","vmid":210},{"enabled":true,"logical_id":"vm211","scope":"pve","type":"pve_guest","vmid":211},{"enabled":true,"logical_id":"vm9130","scope":"pve","type":"pve_guest","vmid":9130},{"enabled":true,"logical_id":"node-pve01","node":"pve01","scope":"node","type":"pve_node_bundle"},{"enabled":true,"logical_id":"node-pve02","node":"pve02","scope":"node","type":"pve_node_bundle"},{"enabled":true,"logical_id":"node-pve03","node":"pve03","scope":"node","type":"pve_node_bundle"},{"enabled":true,"logical_id":"md-relay","scope":"vps","type":"vps_md"},{"enabled":true,"logical_id":"spb-ingress","scope":"vps","type":"vps_spb"},{"enabled":true,"logical_id":"us-netbird","scope":"vps","type":"vps_us"},{"enabled":true,"logical_id":"router-netcraze","scope":"router","type":"router"},{"enabled":true,"logical_id":"xf-codevipe","node":"pve02","scope":"xenforo","type":"xenforo","vmid":160},{"enabled":true,"logical_id":"xf-dsmods","node":"pve02","scope":"xenforo","type":"xenforo","vmid":160},{"enabled":true,"logical_id":"xf-kingofwolk","node":"pve02","scope":"xenforo","type":"xenforo","vmid":160},{"enabled":true,"logical_id":"xf-newfi","node":"pve01","scope":"xenforo","type":"xenforo","vmid":210},{"enabled":true,"logical_id":"xf-gamevipe","node":"pve02","scope":"xenforo","type":"xenforo","vmid":160},{"enabled":true,"logical_id":"xf-hkmods","node":"pve02","scope":"xenforo","type":"xenforo","vmid":160},{"enabled":true,"logical_id":"xf-zakrutim","node":"pve02","scope":"xenforo","type":"xenforo","vmid":160},{"enabled":true,"logical_id":"control-bootstrap-v2","scope":"control","type":"control"}]}},{"meta":{"bytes":5067,"mode":"0600","path":"/etc/homelab-backup-v2/production-acceptance.json","sha256":"708a481f6719ff1ed8d0afec1902650ed36ac631e980611b015419f504f16df0"},"safe":{"accepted_release":"/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1","overlay503":{"base_release":"/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1","lxc_ct110_proof":{"generation":"v2-ct110-20260917T061845Z-07b0eba22842","node":"pve01","replicas":2},"lxc_ct111_proof":{"generation":"v2-ct111-20260917T062204Z-e4a17ab027e0","node":"pve02","replicas":2},"qemu_historical_proofs":["283R1_VM210_VZDUMP_RC0_TWO_REPLICAS","294R1_QEMU_LOG_SIDECAR_OBSERVED","295R1_QEMU_SIDECARS_REMOVED_1"]},"overlay504":{"base_release":"/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1"}}}]
RCLONE_BINARY_SHA256=20df3d5eac5d4e187ce638e4216354e7fa6778c3c51f0caf48c48acca9686354
RCLONE_REMOTES_SAFE={"count":26,"crypt_count":13,"crypt_names":["pve01-mail-01-crypt","pve01-mail-02-crypt","pve01-mail-03-crypt","pve01-mail-04-crypt","pve01-mail-05-crypt","pve02-mail-01-crypt","pve02-mail-02-crypt","pve02-mail-03-crypt","pve02-mail-04-crypt","pve03-mail-01-crypt","pve03-mail-02-crypt","pve03-mail-03-crypt","pve03-mail-04-crypt"],"rc":0,"stderr_sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}
CURRENT_REPO_VERIFIERS=[{"bytes":1037,"contract_lines":[{"line":12,"text":"\"permission_group_id\":\"resource\","},{"line":18,"text":"assert a[\"resources_kb\"]==\"public_read_only\""},{"line":20,"text":"assert a[\"gallery\"]==\"registered_only\""},{"line":23,"text":"assert a[\"future_medical\"]==\"private_by_default_owner_scoped_at_MEDICAL218\""},{"line":24,"text":"assert d[\"mutation\"][\"guest_resource_viewUpdateAttach\"]==\"remain_deny\""}],"name":"verify-access203.py","sha256":"c96393d93d3671f7b625737798c5c46fa67b8985bf90bb4e7668d37cce645acc"},{"bytes":4599,"contract_lines":[{"line":11,"text":"test \"$(runuser -u xfp_newfi -- php8.3 \"$R/cmd.php\" --version)\" = \"XenForo 2.3.12\""},{"line":21,"text":"errors=$(mariadb --defaults-extra-file=/etc/newfi/db.cnf --batch --skip-column-names -e \"SELECT COUNT(*) FROM xf_error_log\")"},{"line":26,"text":"printf \"ADDON_ROWS_BEGIN\\n%s\\nADDON_ROWS_END\\nLANGUAGE_ROWS_BEGIN\\n%s\\nLANGUAGE_ROWS_END\\nRU_PHRASE_ROWS_BEGIN\\n%s\\nRU_PHRASE_ROWS_END\\nDEFAULT_LANGUAGE_ID=%s\\nRU_PHRASE_COUNT=%s\\nDB_TABLES=%s\\nBOARD_ROW=%s|%s|%s\\nERROR_LOG_COUNT=%s\\nMANUAL_JOB_COUNT=%s\\n\" \"$a..."},{"line":36,"text":"test \"$errors\" -eq 0"},{"line":45,"text":"test \"$(systemctl is-active cron)\" = active"}],"name":"verify-addons-language.sh","sha256":"c825959b40cb6713aa7932e290d0e1b104ad2e3173d361bf6de617669f0324f0"},{"bytes":1165,"contract_lines":[{"line":7,"text":"if len(b)<24 or b[:8]!=b'\\x89PNG\\r\\n\\x1a\\n' or b[12:16]!=b'IHDR': raise RuntimeError('png')"},{"line":10,"text":"if cfg['checkpoint']!='NEWFI-260907-BRAND198E': raise RuntimeError('checkpoint')"},{"line":11,"text":"if cfg['roadmap_before']!=24 or cfg['roadmap_after_pass']!=23: raise RuntimeError('counter')"},{"line":12,"text":"if cfg['manual_profile_assets']!={'avatar':'skipped_user_already_installed','profile_banner':'skipped_user_already_installed'}: raise RuntimeError('manual')"},{"line":13,"text":"if cfg['header_logo']!='deferred_to_STYLE199_no_manual_asset_overwrite': raise RuntimeError('logo')"},{"line":16,"text":"if len(b)!=s['bytes'] or hashlib.sha256(b).hexdigest()!=s['sha256'] or png_size(b)!=(s['width'],s['height']): raise RuntimeError(key)"},{"line":17,"text":"if s['sha256'][:12] not in s['public_path']: raise RuntimeError('cache-bust:'+key)"}],"name":"verify-brand-assets.py","sha256":"efaf9bd3af3ec7d28a8ffd3f3ddf88d25f9e76bbd1c5e262e902523716ede6af"},{"bytes":933,"contract_lines":[],"name":"verify-community202.py","sha256":"0f4dd2f53788550a12e3d33622ee1b094680037794fe7ae05a58295185699fcb"},{"bytes":6212,"contract_lines":[{"line":4,"text":"REG=/etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md"},{"line":13,"text":"printf '\\033[31mERROR_REGISTER_CHECK=OK\\033[0m\\n'"},{"line":30,"text":"(\"/etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md\",\"ERROR_REGISTER\"),"},{"line":124,"text":"\"ORDER BY resource_category_id\""},{"line":130,"text":"\"ORDER BY resource_category_id\""},{"line":205,"text":"\"resources\":q(\"SELECT COUNT(*) FROM xf_rm_resource\"),"},{"line":206,"text":"\"errors\":q(\"SELECT COUNT(*) FROM xf_error_log\"),"},{"line":225,"text":"and proof[\"resources\"]==\"0\""},{"line":226,"text":"and proof[\"errors\"]==\"0\""}],"name":"verify-content-structure.sh","sha256":"e08bf27cf6a66d438773ce2a7239593804cb06d312c4622b3d576a9c53d27fba"},{"bytes":4890,"contract_lines":[{"line":7,"text":"GUARD='test \"$(id -u):$(hostname -s)\" = 0:pve03 || exit 72; for f in /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md /etc/pve/31_HOMELAB_REFERENCE.md; do test -f \"$f\" && test -r \"$f\" && test -s \"$f\" && cat -- \"$f\" >/dev/null || exit 73; done; '"},{"line":12,"text":"raise RuntimeError(p.stderr.decode(\"utf-8\",\"replace\")[:3000])"},{"line":15,"text":"rows=json.loads(run([\"/usr/bin/pvesh\",\"get\",\"/cluster/resources\",\"--type\",\"vm\",\"--output-format\",\"json\"],t=30).decode())"},{"line":31,"text":"$addon=$db->fetchRow(\"SELECT addon_id,active,is_processing,last_pending_action FROM xf_addon WHERE addon_id='Newfi/Dogs'\");"},{"line":32,"text":"$route=$db->fetchRow(\"SELECT route_type,route_prefix,controller FROM xf_route WHERE addon_id='Newfi/Dogs' AND route_type='public' AND route_prefix='dogs'\");"},{"line":36,"text":"'route_rows'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_route WHERE addon_id='Newfi/Dogs'\"),"},{"line":37,"text":"'template_rows'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_template WHERE addon_id='Newfi/Dogs'\"),"},{"line":38,"text":"'template_exact'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_template WHERE addon_id='Newfi/Dogs' AND type='public' AND title='newfi_dogs'\"),"},{"line":41,"text":"'dogs'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_newfi_dog\"),"},{"line":42,"text":"'errors'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_error_log\"),"},{"line":43,"text":"'jobs'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_job WHERE unique_key LIKE '%Newfi/Dogs%' OR execute_data LIKE '%Newfi/Dogs%'\"),"},{"line":47,"text":"'controller_loads'=>class_exists('Newfi\\\\Dogs\\\\Pub\\\\Controller\\\\Dogs'),"},{"line":48,"text":"'view_loads'=>class_exists('Newfi\\\\Dogs\\\\Pub\\\\View\\\\Dogs'),"},{"line":49,"text":"'entity_loads'=>class_exists('Newfi\\\\Dogs\\\\Entity\\\\Dog')"},{"line":51,"text":"echo \"NEWFI_DOGS_VERIFY=\".json_encode($out,JSON_UNESCAPED_SLASHES).PHP_EOL;"},{"line":60,"text":"line=next((x for x in str(d.get(\"out-data\",\"\")).splitlines() if x.startswith(\"NEWFI_DOGS_VERIFY=\")),None)"},{"line":72,"text":"str(a.get(\"addon_id\"))==\"Newfi/Dogs\" and str(a.get(\"active\"))==\"1\" and str(a.get(\"is_processing\"))==\"0\""},{"line":74,"text":"and route.get(\"route_prefix\")==\"dogs\" and route.get(\"controller\")==\"Newfi\\\\Dogs:Dogs\""},{"line":77,"text":"and r[\"dogs\"]==0 and r[\"errors\"]==0 and r[\"jobs\"]==0"},{"line":78,"text":"and r[\"board\"]==\"0\" and r[\"index\"]==\"portal/\" and r[\"style\"]==\"4\""},{"line":82,"text":"print(\"NEWFI_DOGS_VERIFY=\"+json.dumps(r,ensure_ascii=False,sort_keys=True))"}],"name":"verify-dogs-staging.py","sha256":"ce4d3de672356fae4f44beab659d8fc708e814480a9d02b97291db63bd053100"},{"bytes":5621,"contract_lines":[{"line":7,"text":"GUARD='test \"$(id -u):$(hostname -s)\" = 0:pve03 || exit 72; for f in /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md /etc/pve/31_HOMELAB_REFERENCE.md; do test -f \"$f\" && test -r \"$f\" && test -s \"$f\" && cat -- \"$f\" >/dev/null || exit 73; done; '"},{"line":12,"text":"raise RuntimeError(p.stderr.decode(\"utf-8\",\"replace\")[:3000])"},{"line":15,"text":"rows=json.loads(run([\"/usr/bin/pvesh\",\"get\",\"/cluster/resources\",\"--type\",\"vm\",\"--output-format\",\"json\"],t=30).decode())"},{"line":34,"text":"$addon=$db->fetchRow(\"SELECT addon_id,active,is_processing,last_pending_action FROM xf_addon WHERE addon_id='Newfi/Events'\");"},{"line":35,"text":"$route=$db->fetchRow(\"SELECT route_type,route_prefix,controller FROM xf_route WHERE addon_id='Newfi/Events' AND route_type='public' AND route_prefix='events'\");"},{"line":39,"text":"'route_rows'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_route WHERE addon_id='Newfi/Events'\"),"},{"line":40,"text":"'template_rows'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_template WHERE addon_id='Newfi/Events'\"),"},{"line":41,"text":"'template_exact'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_template WHERE addon_id='Newfi/Events' AND type='public' AND title='newfi_events'\"),"},{"line":43,"text":"'events'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_newfi_event\"),"},{"line":46,"text":"'errors'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_error_log\"),"},{"line":50,"text":"'jobs'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_job WHERE unique_key LIKE '%Newfi/Events%' OR execute_data LIKE '%Newfi/Events%'\"),"},{"line":51,"text":"'controller_loads'=>class_exists('Newfi\\\\Events\\\\Pub\\\\Controller\\\\Events'),"},{"line":52,"text":"'view_loads'=>class_exists('Newfi\\\\Events\\\\Pub\\\\View\\\\Events'),"},{"line":53,"text":"'event_entity_loads'=>class_exists('Newfi\\\\Events\\\\Entity\\\\Event'),"},{"line":54,"text":"'source_entity_loads'=>class_exists('Newfi\\\\Events\\\\Entity\\\\EventSource'),"},{"line":55,"text":"'change_entity_loads'=>class_exists('Newfi\\\\Events\\\\Entity\\\\EventChange')"},{"line":57,"text":"echo \"NEWFI_EVENTS_VERIFY=\".json_encode($out,JSON_UNESCAPED_SLASHES).PHP_EOL;"},{"line":66,"text":"line=next((x for x in str(d.get(\"out-data\",\"\")).splitlines() if x.startswith(\"NEWFI_EVENTS_VERIFY=\")),None)"},{"line":79,"text":"str(a.get(\"addon_id\"))==\"Newfi/Events\" and str(a.get(\"active\"))==\"1\" and str(a.get(\"is_processing\"))==\"0\""},{"line":81,"text":"and route.get(\"route_prefix\")==\"events\" and route.get(\"controller\")==\"Newfi\\\\Events:Events\""},{"line":84,"text":"and r[\"events\"]==0 and r[\"sources\"]==0 and r[\"changes\"]==0 and r[\"errors\"]==0 and r[\"jobs\"]==0"},{"line":85,"text":"and r[\"board\"]==\"0\" and r[\"index\"]==\"portal/\" and r[\"style\"]==\"4\""},{"line":89,"text":"print(\"NEWFI_EVENTS_VERIFY=\"+json.dumps(r,ensure_ascii=False,sort_keys=True))"}],"name":"verify-events-staging.py","sha256":"7235e9dda979c8a6091ac424ebaf241781bac0de94b5148acd7f677485b32ff0"},{"bytes":893,"contract_lines":[{"line":21,"text":"assert pr[\"portal_templates_xml_sha256\"]==\"a15da1480f674df015cf8443dc4d4cb540334e26cc8fcfc06e4eb34aaa455921\""}],"name":"verify-forum201.py","sha256":"5349228435019a6c7ffc4867f620ac25336cb48fa4042b5793d03899c1aa1b2f"},{"bytes":5019,"contract_lines":[{"line":4,"text":"REG=/etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md"},{"line":11,"text":"BOUNDED=/var/lib/homelab-backup/vm-restore-validation/vm211/$GEN.json"},{"line":12,"text":"FULL=/var/lib/homelab-backup/vm-restore-validation/vm211/${GEN}-newfi-full.json"},{"line":17,"text":"printf '\\033[31mERROR_REGISTER_CHECK=OK\\033[0m\\n\\033[31mREFERENCE_CHECK=OK\\033[0m\\n'"},{"line":48,"text":"print(\"\\033[31mERROR_REGISTER_CHECK=OK\\033[0m\")"},{"line":53,"text":"print(\"STOP=RESTORE_EVIDENCE_INVALID:\"+path)"},{"line":78,"text":"\"restored_guest_proof\","},{"line":97,"text":"and bounded.get(\"schema\")==\"homelab-vm-restore-validation-v1\""},{"line":105,"text":"and full.get(\"schema\")==\"newfi-foundation-full-restore-v1\""},{"line":115,"text":"and full.get(\"full_restore_proven\") is True"},{"line":130,"text":"and guest.get(\"xf_error_log_zero\") is True"},{"line":134,"text":"print(\"NEWFI_FOUNDATION_RESTORE_VERIFY=FAIL\")"},{"line":144,"text":"print(\"NEWFI_FOUNDATION_RESTORE_VERIFY=FAIL_TEST_VM_RESIDUE\")"},{"line":162,"text":"print(\"NEWFI_FOUNDATION_RESTORE_VERIFY=FAIL_TEST_LV_RESIDUE\")"},{"line":181,"text":"print(\"NEWFI_FOUNDATION_RESTORE_VERIFY=FAIL_REMOTE_SEAL\")"},{"line":184,"text":"print(\"NEWFI_FOUNDATION_RESTORE_VERIFY=PASS\")"},{"line":187,"text":"print(\"FULL_RESTORE_PROVEN=true\")"}],"name":"verify-foundation-restore.sh","sha256":"cc8b821ffd64c8b2d780eb6efaf96f3724230c2bfe50c7db08c4162f79dc2186"},{"bytes":6256,"contract_lines":[{"line":8,"text":"GUARD='test \"$(id -u):$(hostname -s)\" = 0:pve03 || exit 72; for f in /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md /etc/pve/31_HOMELAB_REFERENCE.md; do test -f \"$f\" && test -r \"$f\" && test -s \"$f\" && cat -- \"$f\" >/dev/null || exit 73; done; '"},{"line":13,"text":"raise RuntimeError(p.stderr.decode(\"utf-8\",\"replace\")[:3000])"},{"line":17,"text":"for f in [\"/etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md\",\"/etc/pve/31_HOMELAB_REFERENCE.md\"]:"},{"line":22,"text":"print(\"\\x1b[31mERROR_REGISTER_CHECK=OK\\x1b[0m\")"},{"line":26,"text":"rows=json.loads(run([\"/usr/bin/pvesh\",\"get\",\"/cluster/resources\",\"--type\",\"vm\",\"--output-format\",\"json\"],t=30).decode())"},{"line":33,"text":"portal=(root/\"src/addons/Newfi/Portal/_data/templates.xml\").read_text(encoding=\"utf-8\")"},{"line":34,"text":"portal_ok=(\"\u0413\u0430\u043b\u0435\u0440\u0435\u044f\" in portal and \"link('media')\" in portal and \"http://\" not in portal and \"https://\" not in portal)"},{"line":46,"text":"$related=$db->fetchAll(\"SELECT job_id,unique_key,execute_class FROM xf_job WHERE unique_key LIKE '%Newfi/%' OR unique_key LIKE '%Gallery%' OR unique_key LIKE '%XFMG%' OR execute_class LIKE 'XFMG\\\\\\\\%' ORDER BY job_id\");"},{"line":68,"text":"'dogs'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_newfi_dog\"),"},{"line":71,"text":"'errors'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_error_log\"),"},{"line":78,"text":"'dog_entity'=>class_exists('Newfi\\\\Dogs\\\\Entity\\\\Dog'),"},{"line":81,"text":"echo \"NEWFI_GALLERY_VERIFY=\".json_encode($out,JSON_UNESCAPED_SLASHES).PHP_EOL;"},{"line":90,"text":"line=next((x for x in str(d.get(\"out-data\",\"\")).splitlines() if x.startswith(\"NEWFI_GALLERY_VERIFY=\")),None)"},{"line":99,"text":"portal_ok"},{"line":114,"text":"and r[\"dogs\"]==0"},{"line":117,"text":"and r[\"errors\"]==0"},{"line":119,"text":"and r[\"index\"]==\"portal/\""},{"line":128,"text":"r[\"portal_native_media_link\"]=portal_ok"},{"line":130,"text":"print(\"NEWFI_GALLERY_VERIFY=\"+json.dumps(r,ensure_ascii=False,sort_keys=True))"}],"name":"verify-gallery-staging.py","sha256":"22db19841305aecacec79e6a12a8f048b0c1efa0d68404b3f376a7aa8c980e45"},{"bytes":3731,"contract_lines":[{"line":9,"text":"if p.returncode: raise RuntimeError(p.stderr.decode(\"utf-8\",\"replace\")[:3000])"},{"line":11,"text":"rows=json.loads(run([\"/usr/bin/pvesh\",\"get\",\"/cluster/resources\",\"--type\",\"vm\",\"--output-format\",\"json\"],t=30).decode())"},{"line":18,"text":"$cat=$db->fetchRow(\"SELECT resource_category_id,parent_category_id,HEX(title) AS title_hex,HEX(description) AS description_hex,resource_count,lft,rgt,depth FROM xf_rm_category WHERE resource_category_id=1\");"},{"line":20,"text":"'resources'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_rm_resource\"),"},{"line":21,"text":"'updates'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_rm_resource_update\"),"},{"line":22,"text":"'versions'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_rm_resource_version\"),"},{"line":23,"text":"'errors'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_error_log\"),"},{"line":29,"text":"guard='test \"$(id -u):$(hostname -s)\" = 0:pve03 || exit 72; for f in /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md /etc/pve/31_HOMELAB_REFERENCE.md; do test -f \"$f\" && test -r \"$f\" && test -s \"$f\" && cat -- \"$f\" >/dev/null || exit 73; done; exec /usr/sbin/qm gu..."},{"line":37,"text":"ok=(r[\"category_count\"]==1 and str(c.get(\"resource_category_id\"))==\"1\" and str(c.get(\"parent_category_id\"))==\"0\""},{"line":39,"text":"and int(c.get(\"resource_count\",0))==0 and int(c.get(\"lft\",0))==1 and int(c.get(\"rgt\",0))==2 and int(c.get(\"depth\",0))==0"},{"line":40,"text":"and r[\"resources\"]==0 and r[\"updates\"]==0 and r[\"versions\"]==0 and r[\"errors\"]==0"},{"line":41,"text":"and r[\"board\"]==\"0\" and r[\"index\"]==\"portal/\" and r[\"style\"]==\"4\")"}],"name":"verify-kb-xfrm.py","sha256":"4402881a2e6e19192f97b96a4a83c57d8ce078cfbdb7d1480a3c9ceca8077ca4"},{"bytes":1240,"contract_lines":[{"line":7,"text":"assert d[\"root\"]=={\"mode\":\"container_only\",\"resource_category_id\":1,\"title\":\"\u0411\u0430\u0437\u0430 \u0437\u043d\u0430\u043d\u0438\u0439\"}"}],"name":"verify-kb204.py","sha256":"0192589f8a497302edbe738ae04ccad7c878c9b30f08e94203ba5cff5fccbf87"},{"bytes":5256,"contract_lines":[{"line":7,"text":"GUARD='test \"$(id -u):$(hostname -s)\" = 0:pve03 || exit 72; for f in /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md /etc/pve/31_HOMELAB_REFERENCE.md; do test -f \"$f\" && test -r \"$f\" && test -s \"$f\" && cat -- \"$f\" >/dev/null || exit 73; done; '"},{"line":14,"text":"if p.returncode: raise RuntimeError(\"subprocess:\"+Path(a[0]).name)"},{"line":21,"text":"raise RuntimeError(\"qga\")"},{"line":26,"text":"if b'\"result\":\"PASS\"' not in c: raise RuntimeError(\"content\")"},{"line":27,"text":"p=run([\"/usr/bin/python3\",str(ROOT/\"scripts/verify-portal-staging.py\")],t=420).stdout.decode(\"utf-8\",\"strict\")"},{"line":28,"text":"pl=next((x for x in p.splitlines() if x.startswith(\"NEWFI_PORTAL_VERIFY=\")),None)"},{"line":29,"text":"if not pl or json.loads(pl.split(\"=\",1)[1]).get(\"result\")!=\"PASS\": raise RuntimeError(\"portal\")"},{"line":32,"text":"nav=(ROOT/\"src/addons/Newfi/Portal/_data/navigation.xml\").read_text(encoding=\"utf-8\")"},{"line":33,"text":"phr=(ROOT/\"src/addons/Newfi/Portal/_data/phrases.xml\").read_text(encoding=\"utf-8\")"},{"line":34,"text":"tpl=(ROOT/\"src/addons/Newfi/Portal/_data/templates.xml\").read_text(encoding=\"utf-8\")"},{"line":35,"text":"if 'navigation_id=\"newfiPortal\"' not in nav or 'display_order=\"150\"' not in nav or \"{{ link('portal') }}\" not in nav:"},{"line":36,"text":"raise RuntimeError(\"nav-source\")"},{"line":37,"text":"if 'title=\"nav.newfiPortal\"' not in phr or \"<![CDATA[Newfi]]>\" not in phr:"},{"line":38,"text":"raise RuntimeError(\"phrase-source\")"},{"line":39,"text":"for key in (\"whats_new_new_posts\",\"xfrm_whats_new_overview_new_resources\"):"},{"line":41,"text":"raise RuntimeError(\"widget-source:\"+key)"},{"line":44,"text":"rows=json.loads(run([\"/usr/bin/pvesh\",\"get\",\"/cluster/resources\",\"--type\",\"vm\",\"--output-format\",\"json\"],t=30).stdout.decode())"},{"line":45,"text":"if any(str(x.get(\"vmid\"))==\"210\" for x in rows if isinstance(x,dict)): raise RuntimeError(\"vm210\")"},{"line":47,"text":"if len(b)!=PARKED_BYTES or hashlib.sha256(b).hexdigest()!=PARKED_SHA: raise RuntimeError(\"parked\")"},{"line":55,"text":"$a=$app->addOnManager()->getById('Newfi/Portal');"},{"line":56,"text":"$nav=$db->fetchRow(\"SELECT navigation_id,parent_navigation_id,display_order,navigation_type_id,enabled,addon_id,type_config FROM xf_navigation WHERE navigation_id='newfiPortal'\");"},{"line":57,"text":"$keys=$db->fetchAllColumn(\"SELECT widget_key FROM xf_widget WHERE widget_key IN ('whats_new_new_posts','xfrm_whats_new_overview_new_resources') ORDER BY widget_key\");"},{"line":59,"text":"$phrase=(string)\\XF::phrase('nav.newfiPortal');"},{"line":60,"text":"$html=$app->templater()->renderTemplate('public:newfi_portal', []);"},{"line":70,"text":"'errors'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_error_log\"),"},{"line":79,"text":"and navr[\"navigation_id\"]==\"newfiPortal\" and navr[\"parent_navigation_id\"]==\"\""},{"line":81,"text":"and int(navr[\"enabled\"])==1 and navr[\"addon_id\"]==\"Newfi/Portal\""},{"line":83,"text":"and sorted(r[\"widget_keys\"])==[\"whats_new_new_posts\",\"xfrm_whats_new_overview_new_resources\"]"},{"line":86,"text":"and r[\"errors\"]==0 and r[\"board_active\"]==\"0\" and r[\"index_route\"]==\"portal/\"):"},{"line":87,"text":"raise RuntimeError(\"runtime\")"}],"name":"verify-portal-navigation.py","sha256":"25c0e846c93b1cfaa23072ad062da6ce32f7df823db642dac18d1150dae05cec"},{"bytes":5003,"contract_lines":[{"line":7,"text":"GUARD='test \"$(id -u):$(hostname -s)\" = 0:pve03 || exit 72; for f in /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md /etc/pve/31_HOMELAB_REFERENCE.md; do test -f \"$f\" && test -r \"$f\" && test -s \"$f\" && cat -- \"$f\" >/dev/null || exit 73; done; '"},{"line":11,"text":"\"src/addons/Newfi/Portal/addon.json\","},{"line":12,"text":"\"src/addons/Newfi/Portal/Pub/Controller/Portal.php\","},{"line":13,"text":"\"src/addons/Newfi/Portal/Pub/View/Portal.php\","},{"line":14,"text":"\"src/addons/Newfi/Portal/_data/routes.xml\","},{"line":15,"text":"\"src/addons/Newfi/Portal/_data/templates.xml\","},{"line":20,"text":"if p.returncode: raise RuntimeError(\"subprocess:\"+Path(a[0]).name)"},{"line":26,"text":"raise RuntimeError(\"qga\")"},{"line":29,"text":"if not (ROOT/r).is_file(): raise RuntimeError(\"missing:\"+r)"},{"line":31,"text":"raise RuntimeError(\"content\")"},{"line":32,"text":"rows=json.loads(run([\"/usr/bin/pvesh\",\"get\",\"/cluster/resources\",\"--type\",\"vm\",\"--output-format\",\"json\"],t=30).stdout.decode())"},{"line":33,"text":"if any(str(x.get(\"vmid\"))==\"210\" for x in rows if isinstance(x,dict)): raise RuntimeError(\"vm210\")"},{"line":35,"text":"if len(b)!=PARKED_BYTES or hashlib.sha256(b).hexdigest()!=PARKED_SHA: raise RuntimeError(\"parked\")"},{"line":42,"text":"$a=$app->addOnManager()->getById('Newfi/Portal');"},{"line":43,"text":"$row=$db->fetchRow(\"SELECT addon_id,active,is_processing,last_pending_action FROM xf_addon WHERE addon_id='Newfi/Portal'\");"},{"line":48,"text":"'route_rows'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_route WHERE addon_id='Newfi/Portal' AND route_type='public' AND route_prefix='portal'\"),"},{"line":49,"text":"'route_controller'=>(string)$db->fetchOne(\"SELECT controller FROM xf_route WHERE addon_id='Newfi/Portal' AND route_type='public' AND route_prefix='portal' LIMIT 1\"),"},{"line":50,"text":"'template_rows'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_template WHERE addon_id='Newfi/Portal' AND type='public' AND title='newfi_portal'\"),"},{"line":53,"text":"'errors'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_error_log\"),"},{"line":56,"text":"'resources'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_rm_resource\"),"},{"line":57,"text":"'controller_loads'=>class_exists('Newfi\\\\Portal\\\\Pub\\\\Controller\\\\Portal'),"},{"line":58,"text":"'view_loads'=>class_exists('Newfi\\\\Portal\\\\Pub\\\\View\\\\Portal')"},{"line":67,"text":"and r[\"route_rows\"]==1 and r[\"route_controller\"]==\"Newfi\\\\Portal:Portal\""},{"line":68,"text":"and r[\"template_rows\"]==1 and r[\"index_route\"]==\"portal/\" and r[\"board_active\"]==\"0\""},{"line":69,"text":"and r[\"errors\"]==0 and r[\"threads\"]==0 and r[\"posts\"]==0 and r[\"resources\"]==0"},{"line":71,"text":"raise RuntimeError(\"runtime\")"},{"line":75,"text":"if guest!=repo: raise RuntimeError(\"hash\")"},{"line":76,"text":"print(\"NEWFI_PORTAL_VERIFY=\"+json.dumps({\"result\":\"PASS\",\"runtime\":r,\"public_parked_exact\":True,\"vm210_absent\":True},sort_keys=True))"}],"name":"verify-portal-staging.py","sha256":"07cdf461aa7126a9519b209f431c0dbff093b3984f372fa855bb6bf53920fab9"},{"bytes":10992,"contract_lines":[{"line":11,"text":"CONFIG=ROOT/\"config/newfi-pwa-staging.json\""},{"line":38,"text":"'for f in /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md /etc/pve/31_HOMELAB_REFERENCE.md; do '"},{"line":43,"text":"class VerifyError(RuntimeError):"},{"line":49,"text":"print(\"NEWFI_PWA_VERIFY=\"+json.dumps(d,ensure_ascii=False,sort_keys=True))"},{"line":61,"text":"raise VerifyError(\"QGA_TRANSPORT_RC\")"},{"line":65,"text":"raise VerifyError(\"QGA_JSON\")"},{"line":67,"text":"raise VerifyError(\"QGA_NOT_EXITED\")"},{"line":69,"text":"raise VerifyError(\"QGA_TRUNCATED\")"},{"line":72,"text":"raise VerifyError(\"QGA_EXITCODE\")"},{"line":75,"text":"GUEST='\\nimport hashlib\\nimport json\\nimport subprocess\\n\\nENV={\"PATH\":\"/usr/sbin:/usr/bin:/sbin:/bin\",\"LC_ALL\":\"C\"}\\nDB=\"/etc/newfi/db.cnf\"\\n\\ndef run(argv, timeout=40):\\n    p=subprocess.run(\\n        argv,stdout=subprocess.PIPE,stderr=subprocess.PIPE,\\n    ..."},{"line":79,"text":"\"/usr/bin/pvesh\",\"get\",\"/cluster/resources\","},{"line":83,"text":"raise VerifyError(\"CLUSTER_QUERY\")"},{"line":91,"text":"raise VerifyError(\"VM210_PRESENT\")"},{"line":96,"text":"raise VerifyError(\"VM211_NOT_RUNNING_PVE03\")"},{"line":104,"text":"raise VerifyError(\"PUBLIC_FETCH\")"},{"line":106,"text":"raise VerifyError(\"PUBLIC_NOT_PARKED_EXACT\")"},{"line":112,"text":"\"checkpoint\":\"native-xenforo-pwa-staging\","},{"line":114,"text":"\"custom_pwa_layer\":False,"},{"line":124,"text":"raise VerifyError(\"CONFIG:\"+k)"},{"line":126,"text":"compile(GUEST,\"<verify-pwa-staging-guest>\",\"exec\")"},{"line":135,"text":"raise VerifyError(\"GUEST_RC:\"+str(rc)+\":\"+err[-600:])"},{"line":137,"text":"raise VerifyError(\"GUEST_STDERR:\"+err[-600:])"},{"line":138,"text":"marker=next((x for x in out.splitlines() if x.startswith(\"NEWFI174_PWA_GUEST=\")),None)"},{"line":140,"text":"raise VerifyError(\"GUEST_MARKER\")"},{"line":145,"text":"\"index\":\"portal/\","},{"line":147,"text":"\"errors\":0,"},{"line":166,"text":"raise VerifyError(\"GUEST_STATE:\"+k+\":\"+repr(g.get(k)))"},{"line":182,"text":"raise VerifyError(\"HTTP_MANIFEST_FETCH\")"},{"line":185,"text":"raise VerifyError(\"HTTP_MANIFEST_BODY\")"},{"line":190,"text":"\"id\":\"/index.php?_pwa=1\","}],"name":"verify-pwa-staging.py","sha256":"ad9fd930cd5b52ffd836fcf41f4859bbac910540423d1b8692102ec4c8054f0e"},{"bytes":4335,"contract_lines":[{"line":6,"text":"TARGETS={\"forum\":[\"viewContent\",\"viewOthers\"],\"general\":[\"viewNode\",\"viewMemberList\",\"viewProfile\",\"search\"],\"profilePost\":[\"view\"],\"resource\":[\"view\",\"viewUpdateAttach\"],\"xfmg\":[\"view\",\"viewComments\",\"viewNote\"]}"},{"line":11,"text":"raise RuntimeError(p.stderr.decode(\"utf-8\",\"replace\")[:3000])"},{"line":13,"text":"rows=json.loads(run([\"/usr/bin/pvesh\",\"get\",\"/cluster/resources\",\"--type\",\"vm\",\"--output-format\",\"json\"],t=30).decode())"},{"line":46,"text":"'errors'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_error_log\"),"},{"line":52,"text":"guard='test \"$(id -u):$(hostname -s)\" = 0:pve03 || exit 72; for f in /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md /etc/pve/31_HOMELAB_REFERENCE.md; do test -f \"$f\" && test -r \"$f\" && test -s \"$f\" && cat -- \"$f\" >/dev/null || exit 73; done; exec /usr/sbin/qm gu..."},{"line":63,"text":"if r[\"result\"]!=\"PASS\" or r[\"boardDb\"]!=\"0\" or r[\"boardRuntime\"] is not False or r[\"index\"]!=\"portal/\" or r[\"styleDb\"]!=\"4\" or r[\"styleRuntime\"]!=4 or r[\"errors\"]!=0 or r[\"contentPermissionRows\"]!=0:"}],"name":"verify-rights-privacy.py","sha256":"2916a5e942f2315a5e90d06cd66a067b84c546c74d7f354e07f7daed306efca6"},{"bytes":1863,"contract_lines":[],"name":"verify-staging-host.sh","sha256":"9c210b305b07fc4d922f73bccd73c5ec0778337c049c96e076864c93e05ff2d6"},{"bytes":10983,"contract_lines":[{"line":150,"text":").read_text(\"utf-8\",errors=\"replace\")"},{"line":276,"text":"vhost=pathlib.Path(\"/etc/nginx/sites-enabled/xf-newfi.conf\").read_text(\"utf-8\",errors=\"replace\")"},{"line":277,"text":"pool=pathlib.Path(\"/etc/php/8.3/fpm/pool.d/xf-newfi.conf\").read_text(\"utf-8\",errors=\"replace\")"},{"line":311,"text":"\"--execute=SELECT COUNT(*) FROM xf_error_log\""},{"line":314,"text":"errors=int(o.strip()) if rc==0 else -1"},{"line":316,"text":"errors=-1"},{"line":317,"text":"facts[\"xf_error_log_count\"]=errors"},{"line":318,"text":"check(\"xf_error_log_zero\",errors==0)"}],"name":"verify-staging-security.sh","sha256":"adec12a6ef5ca9ecf14a2c4cb12c810629948f985553c509361264b033af98cf"},{"bytes":4857,"contract_lines":[{"line":8,"text":"GUARD='test \"$(id -u):$(hostname -s)\" = 0:pve03 || exit 72; for f in /etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md /etc/pve/31_HOMELAB_REFERENCE.md; do test -f \"$f\" && test -r \"$f\" && test -s \"$f\" && cat -- \"$f\" >/dev/null || exit 73; done; '"},{"line":15,"text":"if p.returncode: raise RuntimeError(\"subprocess:\"+Path(a[0]).name)"},{"line":22,"text":"raise RuntimeError(\"qga\")"},{"line":35,"text":"raise RuntimeError(\"declaration\")"},{"line":40,"text":"[\"/usr/bin/python3\",str(ROOT/\"scripts/verify-portal-staging.py\")],"},{"line":41,"text":"[\"/usr/bin/python3\",str(ROOT/\"scripts/verify-portal-navigation.py\")]"},{"line":45,"text":"rows=json.loads(run([\"/usr/bin/pvesh\",\"get\",\"/cluster/resources\",\"--type\",\"vm\",\"--output-format\",\"json\"],t=30).stdout.decode())"},{"line":46,"text":"if any(str(x.get(\"vmid\"))==\"210\" for x in rows if isinstance(x,dict)): raise RuntimeError(\"vm210\")"},{"line":48,"text":"if len(body)!=PARKED_BYTES or hashlib.sha256(body).hexdigest()!=PARKED_SHA: raise RuntimeError(\"parked\")"},{"line":75,"text":"'errors'=>(int)$db->fetchOne(\"SELECT COUNT(*) FROM xf_error_log\")"},{"line":81,"text":"raise RuntimeError(\"style-match\")"},{"line":98,"text":"and r[\"index_route\"]==\"portal/\""},{"line":99,"text":"and r[\"errors\"]==0"},{"line":101,"text":"raise RuntimeError(\"style-runtime\")"}],"name":"verify-style-staging.py","sha256":"2a3bc464da8d0dee93e2fd16a8edfd6a7f750a8d28653d5f5bf77691fb86afad"},{"bytes":694,"contract_lines":[{"line":14,"text":"assert \"brand198/newfidom-pwa-192.db0307c1a668.png\" in v[\"publicLogoUrl\"][\"default\"]"}],"name":"verify-style199.py","sha256":"4fc81cc1d99aac2302c0a0e5e73cbf82959fb2df099cfbeb14db6cb8a40bcca6"},{"bytes":3787,"contract_lines":[],"name":"verify-web-foundation.sh","sha256":"f7ad539ed2b5c3d7f09aaea0e84b82679a608b60d105271bcb62f0443bb2d84b"},{"bytes":4035,"contract_lines":[{"line":6,"text":"RUNNER_EXPECTED=$(sha256sum \"$ROOT/deploy/cron/newfi-xenforo-run-jobs.sh\" | awk \"{print \\$1}\")"},{"line":7,"text":"CRON_EXPECTED=$(sha256sum \"$ROOT/deploy/cron/newfi-xenforo-run-jobs.cron\" | awk \"{print \\$1}\")"},{"line":14,"text":"test \"$(runuser -u xfp_newfi -- php8.3 \"$R/cmd.php\" --version)\" = \"XenForo 2.3.12\""},{"line":15,"text":"runuser -u xfp_newfi -- php8.3 \"$R/cmd.php\" list --raw | grep -Eq \"^xf:install[[:space:]]+Installs XenForo$\""},{"line":18,"text":"errors=$(mariadb --defaults-extra-file=/etc/newfi/db.cnf --batch --skip-column-names -e \"SELECT COUNT(*) FROM xf_error_log\")"},{"line":22,"text":"printf \"DB_TABLES=%s\\nERROR_LOG_COUNT=%s\\nMANUAL_JOB_COUNT=%s\\nADMIN_USER=%s\\n\" \"$tables\" \"$errors\" \"$manual\" \"$admin\""},{"line":25,"text":"test \"$errors\" -eq 0"},{"line":35,"text":"test \"$(systemctl is-active cron)\" = active"},{"line":36,"text":"test \"$(systemctl is-enabled cron)\" = enabled"},{"line":37,"text":"test \"$(stat -c %U:%G /root/scripts/newfi-xenforo-run-jobs.sh)\" = root:root"},{"line":38,"text":"test \"$(stat -c %a /root/scripts/newfi-xenforo-run-jobs.sh)\" = 700"},{"line":39,"text":"test \"$(stat -c %U:%G /etc/cron.d/newfi-xenforo-run-jobs)\" = root:root"},{"line":40,"text":"test \"$(stat -c %a /etc/cron.d/newfi-xenforo-run-jobs)\" = 644"},{"line":41,"text":"printf \"RUNNER_LIVE_SHA256=%s\\n\" \"$(sha256sum /root/scripts/newfi-xenforo-run-jobs.sh | cut -d\" \" -f1)\""},{"line":42,"text":"printf \"CRON_LIVE_SHA256=%s\\n\" \"$(sha256sum /etc/cron.d/newfi-xenforo-run-jobs | cut -d\" \" -f1)\""},{"line":60,"text":"grep -q \"CRON_LIVE_SHA256=$CRON_EXPECTED\" <<<\"$out\""}],"name":"verify-xenforo-core.sh","sha256":"6b6bd2362bdc703f28a5f3d765b26ee2deaefbd74b036c95b2c28b9cd358f277"}]
BACKUP_UNIT_STATE={"homelab-vm-cloud-quorum-queue.service":{"active":"failed","active_rc":3,"enabled":"static","enabled_rc":0},"homelab-vm-cloud-quorum-queue.timer":{"active":"inactive","active_rc":3,"enabled":"disabled","enabled_rc":1}}
DECISION=PASS_DR221_PREFLIGHT_CURRENT_BACKUP_RESTORE_CONTRACT_CAPTURED
NEXT_GATE=RUN_DR221_FINAL_CURRENT_PRODUCTION_BACKUP_RESTORE_ACCEPTANCE
ACTIVE_MAJOR_STAGE=DR221
MAJOR_STAGES_REMAINING=1
HELPERS_EXECUTED=0
CLOUD_READS=0
CLOUD_WRITES=0
VM_MUTATIONS=0
PRODUCTION_MUTATION=false
HOMELAB_RESULT_CONTRACT={"changes_made":false,"command_id":"NEWFI-260921-NEWFDOM-DR221-PREFLIGHT399","rollback_restored":null,"rollback_started":false,"status":"OK","version":1}
DR221_PREFLIGHT399_END=true

OUTPUT_END
CHAT_OUTPUT_END
