{ "schema_version": 1, "channel": "homelab-runtime", "command_id": "SUPPORT-260919-HOME-GRAM1-EDGE-HEALTH-EXECSTART-RCA-READONLY-580R1", "status": "OK", "rc": 0, "host": "pve01", "mode": "read-only", "component": "home-gram1-edge-health-execstart-rca-readonly", "started_at_utc": "2026-09-18T23:15:59Z", "finished_at_utc": "2026-09-18T23:16:06Z", "reference_register_checked": true, "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", "error_register_checked": true, "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", "command_sha256": "b54be6889c67c56a364b9db78a4733f57e9559aaba8a3150f703b46d103162cb", "duplicate_failed_command_blocked": false, "block_reason": null, "execution_started": true, "change_declared": false, "result_contract_valid": true, "result_contract_status": null, "result_contract_error": null, "command_rc": 0, "changes_made": false, "rollback_started": false, "rollback_restored": null, "mutation_outcome": "NO_MUTATION", "sanitized": true, "secrets_included": false, "private_addresses_included": false, "raw_evidence_retained_locally": true, "raw_evidence_sha256": "9ef50dd89f2a7f3f727c26804c4c668b415eb1a0021208e8028c61e40e130ff1", "sanitized_output_sha256": "bb9329225da751ed6bf84a8199846f44b2be424f678370b813bec5e66a138dda", "output_truncated_in_json": false, "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", "output": "ERROR_REGISTER_CHECK=OK\nREFERENCE_CHECK=OK\nEDGE130_RC=0\nEDGE130_BEGIN\n{\n \"exitcode\" : 0,\n \"exited\" : 1,\n \"out-data\" : \"HOST=edge-vm\\nFILE=/usr/local/sbin/homelab-router-moscow-health-http|exists=True|exec=True\\nSHA=/usr/local/sbin/homelab-router-moscow-health-http|c1cd98bed29ed7cc83aad30f5b6d9afcfbd721f954f46f38fe6008a0a2db2df2|size=781\\nROUTE_MARKERS=/usr/local/sbin/homelab-router-moscow-health-http|backup=False|npmplus=False|router=False\\nHEAD=/usr/local/sbin/homelab-router-moscow-health-http\\n#!/usr/bin/env python3\\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\\nimport socket\\nTARGET = (\\\"[PRIVATE_IP]\\\", 5080)\\nclass H(BaseHTTPRequestHandler):\\n def log_message(self, *args):\\n return\\n def do_GET(self):\\n ok = False\\n try:\\n with socket.create_connection(TARGET, 3):\\n ok = True\\n except Exception:\\n ok = False\\n code = 200 if ok else 503\\n body = (\\\"OK router_moscow_tcp_5080\\\\n\\\" if ok else \\\"FAIL router_moscow_tcp_5080\\\\n\\\").encode()\\n self.send_response(code)\\n self.send_header(\\\"Content-Type\\\", \\\"text/plain\\\")\\n self.send_header(\\\"Content-Length\\\", str(len(body)))\\n self.end_headers()\\n self.wfile.write(body)\\nHTTPServer((\\\"0.0.0.0\\\", 18083), H).serve_forever()\\nFILE=/usr/local/sbin/router-moscow-health|exists=True|exec=True\\nSHA=/usr/local/sbin/router-moscow-health|ccf25de98494625cd88015411c82c9be2d4349970d6d4c6c353c45f787225225|size=6704\\nROUTE_MARKERS=/usr/local/sbin/router-moscow-health|backup=False|npmplus=True|router=True\\nHEAD=/usr/local/sbin/router-moscow-health\\n#!/usr/bin/env python3\\nimport json\\nimport socket\\nimport ssl\\nimport subprocess\\nimport time\\nimport urllib.error\\nimport urllib.request\\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\\n\\nHOST = \\\"[PRIVATE_IP]\\\"\\nPORT = 18091\\n\\ndef tcp(host, port, timeout=3):\\n try:\\n with socket.create_connection((host, int(port)), timeout=timeout):\\n return True\\n except Exception:\\n return False\\n\\ndef docker_running(name):\\n try:\\n out = subprocess.check_output([\\\"docker\\\", \\\"inspect\\\", \\\"-f\\\", \\\"{{.State.Running}}\\\", name], text=True, timeout=5)\\n return out.strip() == \\\"true\\\"\\n except Exception:\\n return False\\n\\ndef http_probe(url, timeout=5, ok_statuses=None, accept_2xx_4xx=False):\\n result = {\\\"url\\\": url, \\\"ok\\\": False, \\\"reachable\\\": False, \\\"status\\\": None, \\\"error\\\": None}\\n try:\\n ctx = ssl._create_unverified_context()\\n req = urllib.request.Request(url, headers={\\\"User-Agent\\\": \\\"homelab-health/1.0\\\"})\\n try:\\n with urllib.request.urlopen(req, timeout=timeout, context=ctx) as r:\\n result[\\\"status\\\"] = int(getattr(r, \\\"status\\\", 0) or 0)\\n result[\\\"reachable\\\"] = True\\n except urllib.error.HTTPError as e:\\n result[\\\"status\\\"] = int(e.code)\\n result[\\\"reachable\\\"] = True\\n\\n status = int(result[\\\"status\\\"] or 0)\\n if ok_statuses is not None:\\n result[\\\"ok\\\"] = status in set(ok_statuses)\\n elif accept_2xx_4xx:\\n result[\\\"ok\\\"] = 200 <= status < 500\\n else:\\n result[\\\"ok\\\"] = 200 <= status < 400\\n except Exception as e:\\n result[\\\"error\\\"] = type(e).__name__ + \\\": \\\" + str(e)[:160]\\n return result\\n\\ndef response(service, ok, checks, note=\\\"\\\"):\\n return {\\\"service\\\": service, \\\"ok\\\": bool(ok), \\\"checks\\\": checks, \\\"note\\\": note, \\\"ts\\\": int(time.time())}\\n\\ndef router_status():\\n checks = {\\n \\\"lan_http_5080\\\": http_probe(\\\"http://[PRIVATE_IP]:5080/\\\", accept_2xx_4xx=True),\\n \\\"lan_https_5083\\\": http_probe(\\\"https://[PRIVATE_IP]:5083/\\\", accept_2xx_4xx=True),\\n \\\"public_https_5083\\\": http_probe(\\\"https://aleisaevn.netcraze.pro:5083/\\\", accept_2xx_4xx=True),\\n \\\"tcp_egress_1_1_1_1_443\\\": {\\\"ok\\\": tcp(\\\"1.1.1.1\\\", 443), \\\"host\\\": \\\"1.1.1.1\\\", \\\"port\\\": 443},\\n }\\n ok = checks[\\\"lan_http_5080\\\"][\\\"ok\\\"] or checks[\\\"lan_https_5083\\\"][\\\"ok\\\"] or checks[\\\"public_https_5083\\\"][\\\"ok\\\"]\\n return response(\\\"router-moscow-health\\\", ok, checks, \\\"HTTP 401/403 from router UI is accepted as reachable.\\\")\\n\\ndef npmplus_status():\\n checks = {\\n \\\"container_running\\\": {\\\"ok\\\": docker_running(\\\"npmplus\\\")},\\n \\\"tcp_127_0_0_1_80\\\": {\\\"ok\\\": tcp(\\\"127.0.0.1\\\", 80)},\\n \\\"tcp_127_0_0_1_443\\\": {\\\"ok\\\": tcp(\\\"127.0.0.1\\\", 443)},\\n \\\"tcp_127_0_0_1_81\\\": {\\\"ok\\\": tcp(\\\"127.0.0.1\\\", 81)},\\n }\\n return response(\\\"npmplus-health\\\", all(v.get(\\\"ok\\\") for v in checks.values()), checks)\\n\\ndef loki_status():\\n checks = {\\n \\\"container_running\\\": {\\\"ok\\\": docker_running(\\\"loki\\\")},\\n \\\"ready\\\": http_probe(\\\"http://127.0.0.1:3100/ready\\\", ok_statuses={200}),\\n }\\n return response(\\\"loki-health\\\", checks[\\\"container_running\\\"][\\\"ok\\\"] and checks[\\\"ready\\\"][\\\"ok\\\"], checks)\\n\\nSYSTEMCTL_STATUS_RC=0\\nSYSTEMCTL_STATUS_BEGIN\\n● homelab-router-moscow-health-http.service - Homelab Moscow router HTTP health endpoint\\n Loaded: loaded (/etc/systemd/system/homelab-router-moscow-health-http.service; enabled; preset: enabled)\\n Active: active (running) since Sat 2026-09-19 02:11:51 MSK; 4min 9s ago\\n Invocation: ce51eceb072440f2b90b98b2f2f3991e\\n Main PID: 3546764 (python3)\\n Tasks: 1 (limit: 14331)\\n Memory: 8.5M (peak: 8.7M)\\n CPU: 86ms\\n CGroup: /system.slice/homelab-router-moscow-health-http.service\\n └─3546764 python3 /usr/local/sbin/homelab-router-moscow-health-http\\n\\nSep 19 02:11:51 edge-vm systemd[1]: Started homelab-router-moscow-health-http.service - Homelab Moscow router HTTP health endpoint.\\n\\nSYSTEMCTL_STATUS_END\\nLISTEN_18091_RC=0\\nLISTEN_18091_BEGIN\\nLISTEN 0 5 [PRIVATE_IP]:18091 0.0.0.0:* users:((\\\"python3\\\",pid=723,fd=3))\\n\\nLISTEN_18091_END\\nPID=3546764\\nPROC_CMDLINE=python3 /usr/local/sbin/homelab-router-moscow-health-http \\nPROC_EXE=/usr/bin/python3.13\\nPROC_CWD=/\\nLISTENER_ADDR_COUNT=1\\nLISTENER_PROBE=http://[PRIVATE_IP]:18091/npmplus|RC=0|RESULT=200|[PRIVATE_IP]|http://[PRIVATE_IP]:18091/npmplus|ERR=\\nLISTENER_PROBE=http://[PRIVATE_IP]:18091/router-moscow|RC=28|RESULT=000|[PRIVATE_IP]|http://[PRIVATE_IP]:18091/router-moscow|ERR=curl: (28) Operation timed out after 5002 milliseconds with 0 bytes received \\nEDGE580_END\\n\",\n \"out-truncated\" : 0\n}\n\nEDGE130_END\nHOME580R1_DECISION=EXECSTART_AND_LISTENER_RCA_COMPLETE\nHOME580R1_NEXT_GATE=PATCH_ACTUAL_EDGE_HEALTH_EXECUTABLE_OR_VALIDATE_CORRECT_BIND\nHOMELAB_RESULT_CONTRACT={\"version\":1,\"command_id\":\"SUPPORT-260919-HOME-GRAM1-EDGE-HEALTH-EXECSTART-RCA-READONLY-580R1\",\"status\":\"OK\",\"changes_made\":false,\"rollback_started\":false,\"rollback_restored\":null}\n" }