{
  "schema_version": 1,
  "channel": "homelab-runtime",
  "command_id": "READER-CHANGE-GATE-INTERFACE-AUDIT-330",
  "status": "OK",
  "rc": 0,
  "host": "pve01",
  "mode": "read-only",
  "component": "cluster-audit",
  "started_at_utc": "2026-07-23T05:00:00Z",
  "finished_at_utc": "2026-07-23T05:00:00Z",
  "reference_register_checked": true,
  "reference_sha256": "5763f2f8edc75fcf6f3951f8c95896d1cc112ceccb6cf5b64a383f540a4deb8e",
  "error_register_checked": true,
  "error_register_sha256": "08b3a769b8e38eaa1acec915352fb328180d70caa7ff6cd9d1ec0da426262752",
  "command_sha256": "0c6a6c62888e384581527e16d35802a1835bef622617b27a301d9077cc33f923",
  "duplicate_failed_command_blocked": false,
  "block_reason": null,
  "execution_started": true,
  "changes_made": false,
  "sanitized": true,
  "secrets_included": false,
  "private_addresses_included": false,
  "raw_evidence_retained_locally": true,
  "raw_evidence_sha256": "cccc50fe97b2ddc7711107f77a40cbebc700089145145f641d4ae62f19876fdd",
  "sanitized_output_sha256": "1c4864ae4d0b36b1d2cf7d1376c7ffa5415174750f0eda9b0201fbb4bb84c1ac",
  "output_truncated_in_json": false,
  "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt",
  "output": "DETAIL=LOCAL_DEPENDENCY=PASS:/usr/bin/test\nDETAIL=LOCAL_DEPENDENCY=PASS:/usr/bin/sha256sum\nDETAIL=LOCAL_DEPENDENCY=PASS:/usr/bin/awk\nDETAIL=LOCAL_DEPENDENCY=PASS:/usr/bin/sed\nDETAIL=LOCAL_DEPENDENCY=PASS:/usr/bin/bash\nDETAIL=CHANGE_GATE_SHA256=90470653345baac426d1ff6d3e3b1109a611ef7ba8d98b7453be0f732ced3597\nDETAIL=CHANGE_GATE_INTEGRITY=PASS\nDETAIL=CHANGE_GATE_SYNTAX=PASS\nDETAIL=CHANGE_GATE_LINE_COUNT=74\nDETAIL=CHANGE_GATE_SOURCE_BEGIN\n#!/usr/bin/bash\nset -Eeuo pipefail\n\nVM=/usr/local/sbin/homelab-vm130-exec\nNG=/usr/local/sbin/homelab-edge-nginx-exec\nVM_SHA=1959773393723b013af7c93cd29a7b833fd607c903d044e2fcef6accaa29c275\nNG_SHA=f900b15bfdd19a1b543037cf8b9c1583e71dc8115bbb5812cf74d474e3e7859b\nTARGET=/opt/npmplus/custom_nginx/server_proxy.conf\nDIR=/opt/npmplus/custom_nginx\n\nfail() {\n    printf 'CHANGE_GATE_ERROR=%s\\n' \"$1\" >&2\n    exit \"${2:-1}\"\n}\n\n(( $# == 2 )) || fail USAGE 64\ncandidate=$1\nexpected_current=$2\n\n[[ \"$expected_current\" =~ ^[0-9a-f]{64}$ ]] || fail EXPECTED_SHA 65\n[[ -x \"$VM\" ]] || fail VM_MISSING 69\n[[ -x \"$NG\" ]] || fail NGINX_HELPER_MISSING 69\n[[ \"$(/usr/bin/sha256sum \"$VM\" | /usr/bin/awk '{print $1}')\" == \"$VM_SHA\" ]] || fail VM_INTEGRITY 70\n[[ \"$(/usr/bin/sha256sum \"$NG\" | /usr/bin/awk '{print $1}')\" == \"$NG_SHA\" ]] || fail NGINX_HELPER_INTEGRITY 70\n\n[[ -f \"$candidate\" && ! -L \"$candidate\" ]] || fail CANDIDATE_TYPE 66\nmeta=$(/usr/bin/stat -c '%U:%G|%a|%s' \"$candidate\")\nIFS='|' read -r owner mode size <<<\"$meta\"\n[[ \"$owner\" == root:root && \"$mode\" == 600 ]] || fail CANDIDATE_METADATA 66\n[[ \"$size\" =~ ^[0-9]+$ ]] && (( size > 0 && size <= 8192 )) || fail CANDIDATE_SIZE 66\n\ncap=$(/usr/bin/systemctl show homelab-reader-v3.service -p Environment --value |\n      /usr/bin/sed -n 's/.*HOMELAB_READER_CAPABILITY=\\([^ ]*\\).*/\\1/p')\n[[ \"$cap\" =~ ^[0-9a-f]{64}$ ]] || fail CAPABILITY 67\n\ntmp=$(/usr/bin/mktemp)\ntrap '/usr/bin/rm -f -- \"$tmp\"' EXIT\n/usr/bin/printf '%s\\n' \\\n  \"location ^~ /.well-known/homelab-reader/${cap}/ {\" \\\n  \"    proxy_pass http://[PRIVATE_IP]:18788/${cap}/;\" \\\n  '    proxy_http_version 1.1;' \\\n  '    proxy_set_header Host $host;' \\\n  '    proxy_set_header Connection \"\";' \\\n  '    proxy_buffering off;' \\\n  '    proxy_cache off;' \\\n  '}' >\"$tmp\"\n/usr/bin/cmp -s \"$candidate\" \"$tmp\" || fail CANDIDATE_CONTENT 66\ncandidate_sha=$(/usr/bin/sha256sum \"$candidate\" | /usr/bin/awk '{print $1}')\n\ntarget_meta=$(\"$VM\" /usr/bin/stat -c '%U:%G|%a|%F' \"$TARGET\")\nIFS='|' read -r target_owner target_mode target_type <<<\"$target_meta\"\n[[ \"$target_owner\" == root:root && \"$target_mode\" == 644 && \"$target_type\" == regular*file ]] || fail TARGET_METADATA 68\ncurrent_sha=$(\"$VM\" /usr/bin/sha256sum \"$TARGET\" | /usr/bin/awk '{print $1}')\n[[ \"$current_sha\" == \"$expected_current\" ]] || fail TARGET_CHANGED 68\n\ndir_meta=$(\"$VM\" /usr/bin/stat -c '%U:%G|%a|%F' \"$DIR\")\nIFS='|' read -r dir_owner dir_mode dir_type <<<\"$dir_meta\"\n[[ \"$dir_owner\" == root:root && \"$dir_mode\" == 755 && \"$dir_type\" == directory ]] || fail DIR_METADATA 68\n\"$VM\" /usr/bin/test -w \"$DIR\" || fail DIR_NOT_WRITABLE 68\n\n\"$NG\" /usr/local/bin/nginx -t >/dev/null 2>&1 || fail NGINX_CONFIG 69\n[[ \"$(/usr/bin/curl -fsS -m5 \"http://[PRIVATE_IP]:18788/${cap}/health\")\" == '{\"status\":\"ok\",\"version\":3}' ]] || fail LOCAL_READER 69\n[[ \"$(\"$VM\" /usr/bin/curl -fsS -m5 \"http://[PRIVATE_IP]:18788/${cap}/health\")\" == '{\"status\":\"ok\",\"version\":3}' ]] || fail EDGE_READER 69\n[[ \"$(/usr/bin/curl -sS -m8 --resolve git.gram1.ru:443:[PRIVATE_IP] -o /dev/null -w '%{http_code}' https://git.gram1.ru/)\" == 200 ]] || fail GITEA 69\n\nprintf 'CHANGE_GATE=READY\\n'\nprintf 'CANDIDATE_SHA256=%s\\n' \"$candidate_sha\"\nprintf 'CURRENT_SHA256=%s\\n' \"$current_sha\"\nprintf 'TARGET_METADATA=%s\\n' \"$target_meta\"\nprintf 'TARGET_DIRECTORY_METADATA=%s\\n' \"$dir_meta\"\nprintf 'NGINX_CURRENT_CONFIG=PASS\\n'\nprintf 'LOCAL_READER_CHECK=PASS\\n'\nprintf 'EDGE_READER_CHECK=PASS\\n'\nprintf 'GITEA_CHECK=PASS\\n'\nDETAIL=CHANGE_GATE_SOURCE_END\nDETAIL=CHANGE_GATE_SOURCE_TRUNCATED=NO\nCHANGES_MADE=NO\n"
}
