{
  "schema_version": 1,
  "channel": "homelab-runtime",
  "command_id": "RUN7A-DELIVERY-PREPLAN-DISCOVERY-20260726T153105Z",
  "status": "OK",
  "rc": 0,
  "host": "pve01",
  "mode": "verify",
  "component": "evidence-delivery",
  "started_at_utc": "2026-07-26T15:32:54Z",
  "finished_at_utc": "2026-07-26T15:33:10Z",
  "reference_register_checked": true,
  "reference_sha256": "5763f2f8edc75fcf6f3951f8c95896d1cc112ceccb6cf5b64a383f540a4deb8e",
  "error_register_checked": true,
  "error_register_sha256": "24934a2c7fa5c26f6e828583c4aefac7f143b40c0171736ed7571e617d55ab83",
  "command_sha256": "72e4d7cfc355fa26e684146bf7b62cece7b11d539ee4fae0ef84c613bcf36a6e",
  "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": "80c0aa4ae374f18ac41e5c6df736a45e2f144cf5a23db6ccf7554c5a03844a98",
  "sanitized_output_sha256": "edac687cd4d0ffb36c68d91fbc10d7554c37707b62c4d09b09b8e88db7ce13c3",
  "output_truncated_in_json": false,
  "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt",
  "output": "RUN7A_DELIVERY_PREPLAN_DISCOVERY=PASS\nPREPLAN_BASIS_SHA256=6ae59d4b7877710758ecb8e315a8322e71c475b6dcda680864eff8c1f7e7e0d9\nREPORT_SHA256=319d433bb77302d92977e66bec320130c69eca85e70e05f6f8350bba4f1a7d6b\n{\"anti_regression\":{\"RUN7A-AR-001-SANITIZER-LINE-BOUNDARY\":{\"checks\":{\"gateway_discovery_by_listener_not_name_guess\":true,\"multiline_false_positive_closed\":true,\"no_long_inline_remote_shell\":true,\"password_removed\":true,\"private_ip_removed\":true,\"remote_stat_single_argument_format\":true,\"userinfo_removed\":true},\"status\":\"PASS\"},\"RUN7A-AR-002-REMOTE-STAT-NO-SPACES\":{\"status\":\"PASS\"},\"RUN7A-AR-003-GATEWAY-BY-LISTENER-CGROUP\":{\"status\":\"PASS\"},\"RUN7A-AR-004-NO-INLINE-REMOTE-BODY\":{\"status\":\"PASS\"},\"RUN7A-AR-005-NO-MUTATION\":{\"backup_changed\":false,\"cluster_changed\":false,\"gitea_changed\":false,\"production_changed\":false,\"status\":\"PASS\"}},\"architecture\":{\"phase_1\":{\"name\":\"IMMUTABLE_PUBLIC_RESULT\",\"requirements\":[\"publish runtime/by-command/<COMMAND_ID>.json\",\"publish runtime/by-command/<COMMAND_ID>.txt\",\"fail closed on same COMMAND_ID with different SHA\",\"idempotent success on same COMMAND_ID and same SHA\",\"retain latest.json/latest.txt as non-authoritative convenience\",\"runner polls exact immutable by-command JSON\",\"runner verifies command_id and command_sha256\",\"full_sanitized_output_url points to immutable TXT\",\"atomic write, flock, git commit and git push\"],\"scope\":[\"/usr/local/sbin/homelab-chat-run\",\"/usr/local/sbin/homelab-runtime-receive\"]},\"phase_2\":{\"name\":\"DETERMINISTIC_FULL_EVIDENCE_RECEIPT\",\"requirements\":[\"deterministic receipt URL known before execution\",\"receipt capability derived from 256-bit capsule SHA\",\"receipt metadata uploaded only after evidence SHA verification\",\"uniform 404 for missing, invalid and expired receipt\",\"Cache-Control: no-store\",\"Content-Disposition contains COMMAND_ID and short SHA\",\"no ListBucket\",\"no permanent MinIO credentials in Gateway\",\"evidence SHA verified before HTTP 200\"],\"scope\":[\"/usr/local/sbin/homelab-command-adapter\",\"/opt/stacks/evidence-gw/gateway.py\",\"evidence-gw.service\"]},\"phase_3\":{\"name\":\"QUALITY_GATE_PLAN_APPLY_ENFORCEMENT\",\"requirements\":[\"schema-v1 read-only compatibility\",\"schema-v2 mandatory for change\",\"exact PLAN capsule/evidence/plan SHA binding\",\"no unresolved mutating failure\",\"Portal displays all gate fields\",\"Execute disabled on gate failure\",\"diagnostics before rollback\"],\"scope\":[\"/usr/local/sbin/homelab-command-adapter\",\"/usr/local/sbin/homelab-command-portal\"],\"source_plan_sha256\":\"99fac83369ee8d5098c07cf243398d8b6b144e4fbc9ef9b39ef75436b109d607\"}},\"blockers\":[],\"changes\":{\"backup\":false,\"cluster\":false,\"gitea\":false,\"production\":false},\"checked_at_utc\":\"2026-07-26T15:33:10.356156+00:00\",\"command_id\":\"RUN7A-DELIVERY-PREPLAN-DISCOVERY-20260726T153105Z\",\"edge_gateway\":{\"cgroup_rc\":0,\"cmdline\":[\"/usr/bin/python3\",\"/opt/stacks/evidence-gw/gateway.py\",\"--config\",\"/etc/evidence-gw/config.json\"],\"cmdline_rc\":0,\"exe\":\"/usr/bin/python3.13\",\"exe_rc\":0,\"install_help_rc\":0,\"listener_excerpt\":\"LISTEN 0      5           127.0.0.1:18093 0.0.0.0:* users:((\\\"python3\\\",pid=2384852,fd=3))                                                                                                            \",\"listener_found\":true,\"listener_rc\":0,\"nginx_rc\":125,\"nginx_relevant\":[],\"pid\":2384852,\"script_features\":{\"has_content_disposition\":true,\"has_expiry\":true,\"has_list_bucket\":false,\"has_no_store\":true,\"has_object_key\":false,\"has_receipt_route\":false,\"has_sha256\":true},\"script_path\":\"/opt/stacks/evidence-gw/gateway.py\",\"script_sha256\":\"27de57be0924fe8d86d38879b3bcdba8d4ade817b3a42219dd65d8aacc9601cb\",\"script_sha_rc\":0,\"script_source_bytes\":14158,\"script_source_rc\":0,\"script_source_redacted\":\"#!/usr/bin/env python3\\nfrom __future__ import annotations\\n\\nimport argparse\\nimport hashlib\\nimport hmac\\nimport http.server\\nimport json\\nimport os\\nimport re\\nimport secrets\\nimport tempfile\\nimport threading\\nimport time\\nimport urllib.error\\nimport urllib.parse\\nimport urllib.request\\nfrom pathlib import Path\\nfrom typing import Any\\n\\nKEY_SEGMENT = re.compile(r\\\"^[A-Za-z0-9._-]+$\\\")\\nSHA256_RE = re.compile(r\\\"^[0-9a-f]{64}$\\\")\\n\\n\\ndef load_json(path: Path) -> dict[str, Any]:\\n    value = json.loads(path.read_text(encoding=\\\"utf-8\\\"))\\n    if not isinstance(value, dict):\\n        raise RuntimeError(\\\"configuration must be a JSON object\\\")\\n    return value\\n\\n\\ndef validate_key(key: str) -> str:\\n    if not key or len(key) > 512 or key.startswith(\\\"/\\\"):\\n        raise ValueError(\\\"invalid object key\\\")\\n    segments = key.split(\\\"/\\\")\\n    if any(\\n        not segment\\n        or segment in {\\\".\\\", \\\"..\\\"}\\n        or KEY_SEGMENT.fullmatch(segment) is None\\n        for segment in segments\\n    ):\\n        raise ValueError(\\\"invalid object key\\\")\\n    return key\\n\\n\\ndef canonical_path(key: str) -> str:\\n    return \\\"/e/\\\" + urllib.parse.quote(\\n        validate_key(key),\\n        safe=\\\"/._-\\\",\\n    )\\n\\n\\ndef signature(secret: bytes, path: str, expiry: int, sha256: str) -> str:\\n    message = f\\\"{path}\\\\n{expiry}\\\\n{sha256}\\\".encode(\\\"utf-8\\\")\\n    return hmac.new(secret, message, hashlib.sha256).hexdigest()\\n\\n\\ndef security_headers(handler: http.server.BaseHTTPRequestHandler) -> None:\\n    handler.send_header(\\\"Cache-Control\\\", \\\"no-store, max-age=0\\\")\\n    handler.send_header(\\\"Pragma\\\", \\\"no-cache\\\")\\n    handler.send_header(\\\"X-Content-Type-Options\\\", \\\"nosniff\\\")\\n    handler.send_header(\\\"Referrer-Policy\\\", \\\"no-referrer\\\")\\n    handler.send_header(\\\"Content-Security-Policy\\\", \\\"default-src 'none'\\\")\\n    handler.send_header(\\\"Cross-Origin-Resource-Policy\\\", \\\"same-origin\\\")\\n\\n\\nclass GatewayServer(http.server.ThreadingHTTPServer):\\n    daemon_threads = True\\n    allow_reuse_address = True\\n\\n    def __init__(\\n        self,\\n        address: tuple[str, int],\\n        handler: type[http.server.BaseHTTPRequestHandler],\\n        *,\\n        config: dict[str, Any],\\n        secret: [REDACTED]\\n    ) -> None:\\n        super().__init__(address, handler)\\n        self.config = config\\n        self.secret = secret\\n        self.slots = threading.BoundedSemaphore(\\n            int(config[\\\"max_concurrent_downloads\\\"])\\n        )\\n\\n\\nclass Handler(http.server.BaseHTTPRequestHandler):\\n    server_version = \\\"EvidenceGateway/1\\\"\\n    sys_version = \\\"\\\"\\n\\n    @property\\n    def gateway(self) -> GatewayServer:\\n        return self.server  # type: ignore[return-value]\\n\\n    def log_message(self, fmt: str, *args: object) -> None:\\n        return\\n\\n    def _json(\\n        self,\\n        status: int,\\n        payload: dict[str, Any],\\n    ) -> None:\\n        body = (\\n            json.dumps(\\n                payload,\\n                separators=(\\\",\\\", \\\":\\\"),\\n                sort_keys=True,\\n            )\\n            + \\\"\\\\n\\\"\\n        ).encode()\\n        self.send_response(status)\\n        self.send_header(\\\"Content-Type\\\", \\\"application/json\\\")\\n        self.send_header(\\\"Content-Length\\\", str(len(body)))\\n        security_headers(self)\\n        self.end_headers()\\n        if self.command != \\\"HEAD\\\":\\n            self.wfile.write(body)\\n\\n    def _event(\\n        self,\\n        *,\\n        status: int,\\n        key: str | None,\\n        size: int | None,\\n        started: float,\\n    ) -> None:\\n        event = {\\n            \\\"event\\\": \\\"download\\\",\\n            \\\"status\\\": status,\\n            \\\"key_sha256\\\": (\\n                hashlib.sha256(key.encode()).hexdigest()\\n                if key is not None\\n                else None\\n            ),\\n            \\\"bytes\\\": size,\\n            \\\"duration_ms\\\": int((time.monotonic() - started) * 1000),\\n            \\\"remote\\\": self.client_address[0],\\n        }\\n        print(\\n            json.dumps(event, separators=(\\\",\\\", \\\":\\\"), sort_keys=True),\\n            flush=True,\\n        )\\n\\n    def do_HEAD(self) -> None:\\n        self._handle_download()\\n\\n    def do_GET(self) -> None:\\n        parsed = urllib.parse.urlsplit(self.path)\\n        if parsed.path == \\\"/healthz\\\":\\n            self._json(\\n                200,\\n                {\\n                    \\\"status\\\": \\\"ok\\\",\\n                    \\\"service\\\": \\\"evidence-gateway\\\",\\n                },\\n            )\\n            return\\n        self._handle_download()\\n\\n    def do_POST(self) -> None:\\n        self._json(405, {\\\"error\\\": \\\"method_not_allowed\\\"})\\n\\n    do_PUT = do_POST\\n    do_DELETE = do_POST\\n    do_PATCH = do_POST\\n    do_OPTIONS = do_POST\\n\\n    def _handle_download(self) -> None:\\n        started = time.monotonic()\\n        key: str | None = None\\n        size: int | None = None\\n        status = 500\\n\\n        if not self.gateway.slots.acquire(blocking=False):\\n            status = 503\\n            self._json(status, {\\\"error\\\": \\\"busy\\\"})\\n            self._event(\\n                status=status,\\n                key=None,\\n                size=None,\\n                started=started,\\n            )\\n            return\\n\\n        try:\\n            parsed = urllib.parse.urlsplit(self.path)\\n            if not parsed.path.startswith(\\\"/e/\\\"):\\n                status = 404\\n                self._json(status, {\\\"error\\\": \\\"not_found\\\"})\\n                return\\n\\n            try:\\n                decoded_key = urllib.parse.unquote(\\n                    parsed.path[len(\\\"/e/\\\") :],\\n                    errors=\\\"strict\\\",\\n                )\\n                key = validate_key(decoded_key)\\n            except Exception:\\n                status = 400\\n                self._json(status, {\\\"error\\\": \\\"invalid_key\\\"})\\n                return\\n\\n            if canonical_path(key) != parsed.path:\\n                status = 400\\n                self._json(status, {\\\"error\\\": \\\"noncanonical_path\\\"})\\n                return\\n\\n            query = urllib.parse.parse_qs(\\n                parsed.query,\\n                keep_blank_values=True,\\n                strict_parsing=True,\\n            )\\n            if set(query) != {\\\"exp\\\", \\\"sha256\\\", \\\"sig\\\"} or any(\\n                len(values) != 1 for values in query.values()\\n            ):\\n                status = 400\\n                self._json(status, {\\\"error\\\": \\\"invalid_query\\\"})\\n                return\\n\\n            try:\\n                expiry = int(query[\\\"exp\\\"][0])\\n            except ValueError:\\n                status = 400\\n                self._json(status, {\\\"error\\\": \\\"invalid_expiry\\\"})\\n                return\\n\\n            expected_sha = query[\\\"sha256\\\"][0].lower()\\n            provided_sig = query[\\\"sig\\\"][0].lower()\\n            now = int(time.time())\\n            max_ttl = int(self.gateway.config[\\\"max_ttl_seconds\\\"])\\n\\n            if expiry < now:\\n                status = 410\\n                self._json(status, {\\\"error\\\": \\\"expired\\\"})\\n                return\\n            if expiry - now > max_ttl:\\n                status = 403\\n                self._json(status, {\\\"error\\\": \\\"ttl_exceeded\\\"})\\n                return\\n            if SHA256_RE.fullmatch(expected_sha) is None:\\n                status = 400\\n                self._json(status, {\\\"error\\\": \\\"invalid_sha256\\\"})\\n                return\\n            if SHA256_RE.fullmatch(provided_sig) is None:\\n                status = 403\\n                self._json(status, {\\\"error\\\": \\\"invalid_signature\\\"})\\n                return\\n\\n            wanted_sig = signature(\\n                self.gateway.secret,\\n                parsed.path,\\n                expiry,\\n                expected_sha,\\n            )\\n            if not secrets.compare_digest(provided_sig, wanted_sig):\\n                status = 403\\n                self._json(status, {\\\"error\\\": \\\"invalid_signature\\\"})\\n                return\\n\\n            minio_base = str(self.gateway.config[\\\"minio_base\\\"]).rstrip(\\\"/\\\")\\n            bucket = str(self.gateway.config[\\\"bucket\\\"])\\n            object_url = (\\n                minio_base\\n                + \\\"/\\\"\\n                + urllib.parse.quote(bucket, safe=\\\"\\\")\\n                + \\\"/\\\"\\n                + urllib.parse.quote(key, safe=\\\"/._-\\\")\\n            )\\n            request = urllib.request.Request(\\n                object_url,\\n                method=\\\"GET\\\",\\n                headers={\\n                    \\\"User-Agent\\\": \\\"evidence-gateway/1\\\",\\n                    \\\"Accept\\\": \\\"application/octet-stream\\\",\\n                },\\n            )\\n\\n            max_bytes = int(self.gateway.config[\\\"max_object_bytes\\\"])\\n            tmp_dir = str(self.gateway.config[\\\"tmp_dir\\\"])\\n            digest = hashlib.sha256()\\n\\n            try:\\n                response = urllib.request.urlopen(\\n                    request,\\n                    timeout=float(\\n                        self.gateway.config[\\\"minio_timeout_seconds\\\"]\\n                    ),\\n                )\\n            except urllib.error.HTTPError as exc:\\n                if exc.code == 404:\\n                    status = 404\\n                    self._json(status, {\\\"error\\\": \\\"not_found\\\"})\\n                elif exc.code == 403:\\n                    status = 502\\n                    self._json(status, {\\\"error\\\": \\\"upstream_denied\\\"})\\n                else:\\n                    status = 502\\n                    self._json(status, {\\\"error\\\": \\\"upstream_error\\\"})\\n                return\\n            except Exception:\\n                status = 502\\n                self._json(status, {\\\"error\\\": \\\"upstream_unavailable\\\"})\\n                return\\n\\n            content_length = response.headers.get(\\\"Content-Length\\\")\\n            if content_length is not None:\\n                try:\\n                    if int(content_length) > max_bytes:\\n                        response.close()\\n                        status = 413\\n                        self._json(status, {\\\"error\\\": \\\"object_too_large\\\"})\\n                        return\\n                except ValueError:\\n                    response.close()\\n                    status = 502\\n                    self._json(status, {\\\"error\\\": \\\"invalid_upstream_length\\\"})\\n                    return\\n\\n            with tempfile.NamedTemporaryFile(\\n                mode=\\\"w+b\\\",\\n                dir=tmp_dir,\\n                prefix=\\\"download-\\\",\\n                delete=True,\\n            ) as temporary:\\n                total = 0\\n                while True:\\n                    block = response.read(1024 * 1024)\\n                    if not block:\\n                        break\\n                    total += len(block)\\n                    if total > max_bytes:\\n                        response.close()\\n                        status = 413\\n                        self._json(status, {\\\"error\\\": \\\"object_too_large\\\"})\\n                        return\\n                    digest.update(block)\\n                    temporary.write(block)\\n                response.close()\\n\\n                actual_sha = digest.hexdigest()\\n                if not secrets.compare_digest(actual_sha, expected_sha):\\n                    status = 502\\n                    self._json(status, {\\\"error\\\": \\\"sha256_mismatch\\\"})\\n                    return\\n\\n                size = total\\n                temporary.flush()\\n                temporary.seek(0)\\n\\n                self.send_response(200)\\n                self.send_header(\\n                    \\\"Content-Type\\\",\\n                    \\\"application/octet-stream\\\",\\n                )\\n                self.send_header(\\\"Content-Length\\\", str(total))\\n                self.send_header(\\n                    \\\"Content-Disposition\\\",\\n                    'attachment; filename=\\\"evidence.bin\\\"',\\n                )\\n                self.send_header(\\\"ETag\\\", f'\\\"sha256:{actual_sha}\\\"')\\n                security_headers(self)\\n                self.end_headers()\\n\\n                if self.command != \\\"HEAD\\\":\\n                    while True:\\n                        block = temporary.read(1024 * 1024)\\n                        if not block:\\n                            break\\n                        self.wfile.write(block)\\n\\n            status = 200\\n        finally:\\n            self.gateway.slots.release()\\n            self._event(\\n                status=status,\\n                key=key,\\n                size=size,\\n                started=started,\\n            )\\n\\n\\ndef validate_config(config: dict[str, Any]) -> None:\\n    required = {\\n        \\\"bind\\\",\\n        \\\"port\\\",\\n        \\\"minio_base\\\",\\n        \\\"bucket\\\",\\n        \\\"max_ttl_seconds\\\",\\n        \\\"max_object_bytes\\\",\\n        \\\"max_concurrent_downloads\\\",\\n        \\\"minio_timeout_seconds\\\",\\n        \\\"tmp_dir\\\",\\n        \\\"secret_file\\\",\\n    }\\n    if set(config) != required:\\n        raise RuntimeError(\\\"configuration keys mismatch\\\")\\n    if config[\\\"bind\\\"] != \\\"127.0.0.1\\\":\\n        raise RuntimeError(\\\"gateway must bind to loopback\\\")\\n    if not (1 <= int(config[\\\"port\\\"]) <= 65535):\\n        raise RuntimeError(\\\"invalid port\\\")\\n    if not str(config[\\\"minio_base\\\"]).startswith(\\\"http://[PRIVATE_IP]:9002\\\"):\\n        raise RuntimeError(\\\"unexpected MinIO endpoint\\\")\\n    validate_key(\\\"selftest/object.txt\\\")\\n\\n\\ndef self_test() -> None:\\n    secret = [REDACTED]\\n    key = \\\"commands/CMD-123/evidence.tgz\\\"\\n    path = canonical_path(key)\\n    assert path == \\\"/e/commands/CMD-123/evidence.tgz\\\"\\n    sig = signature(secret, path, 2000000000, \\\"a\\\" * 64)\\n    assert len(sig) == 64\\n    assert validate_key(key) == key\\n    for bad in (\\\"\\\", \\\"../x\\\", \\\"a//b\\\", \\\"/a\\\", \\\"a/%2F\\\"):\\n        try:\\n            validate_key(bad)\\n        except ValueError:\\n            pass\\n        else:\\n            raise AssertionError(f\\\"invalid key accepted: {bad!r}\\\")\\n    print(\\\"SELF_TEST=PASS\\\")\\n\\n\\ndef main() -> int:\\n    parser = argparse.ArgumentParser()\\n    parser.add_argument(\\n        \\\"--config\\\",\\n        default=\\\"/etc/evidence-gw/config.json\\\",\\n    )\\n    parser.add_argument(\\\"--self-test\\\", action=\\\"store_true\\\")\\n    args = parser.parse_args()\\n\\n    if args.self_test:\\n        self_test()\\n        return 0\\n\\n    config = load_json(Path(args.config))\\n    validate_config(config)\\n\\n    secret_file = Path(str(config[\\\"secret_file\\\"]))\\n    secret = [REDACTED]\\n    if len(secret) != 32:\\n        raise RuntimeError(\\\"gateway secret must be 32 bytes\\\")\\n\\n    tmp_dir = Path(str(config[\\\"tmp_dir\\\"]))\\n    tmp_dir.mkdir(parents=True, exist_ok=True)\\n\\n    server = GatewayServer(\\n        (str(config[\\\"bind\\\"]), int(config[\\\"port\\\"])),\\n        Handler,\\n        config=config,\\n        secret=[REDACTED]\\n    )\\n    server.serve_forever(poll_interval=0.5)\\n    return 0\\n\\n\\nif __name__ == \\\"__main__\\\":\\n    raise SystemExit(main())\\n\",\"script_source_sha256\":\"27de57be0924fe8d86d38879b3bcdba8d4ade817b3a42219dd65d8aacc9601cb\",\"signer_sha256\":\"523490816d058eddf1273e73cc58ca41db1ad3da24092bc8f9831823ef27c6b1\",\"signer_sha_rc\":0,\"unit\":\"evidence-gw.service\",\"unit_cat_rc\":0,\"unit_show\":\"MainPID=2384852\\nResult=success\\nNRestarts=0\\nExecMainStatus=0\\nExecStart={ path=/usr/bin/python3 ; argv[]=/usr/bin/python3 /opt/stacks/evidence-gw/gateway.py --config /etc/evidence-gw/config.json ; ignore_errors=no ; start_time=[Sat 2026-07-25 22:06:01 MSK] ; stop_time=[n/a] ; pid=2384852 ; code=(null) ; status=0/0 }\\nUser=evidence-gw\\nGroup=evidence-gw\\nId=evidence-gw.service\\nLoadState=loaded\\nActiveState=active\\nSubState=running\\nFragmentPath=/etc/systemd/system/evidence-gw.service\\nUnitFileState=enabled\\n\",\"unit_show_rc\":0,\"unit_source_redacted\":\"# /etc/systemd/system/evidence-gw.service\\n[Unit]\\nDescription=Homelab Evidence Gateway\\nAfter=network-online.target\\nWants=network-online.target\\n\\n[Service]\\nType=simple\\nUser=evidence-gw\\nGroup=evidence-gw\\nExecStart=/usr/bin/python3 /opt/stacks/evidence-gw/gateway.py --config /etc/evidence-gw/config.json\\nRestart=on-failure\\nRestartSec=2s\\nTimeoutStartSec=30s\\nTimeoutStopSec=15s\\nUMask=0077\\nNoNewPrivileges=yes\\nPrivateTmp=yes\\nPrivateDevices=yes\\nProtectSystem=strict\\nProtectHome=yes\\nProtectKernelTunables=yes\\nProtectKernelModules=yes\\nProtectControlGroups=yes\\nLockPersonality=yes\\nMemoryDenyWriteExecute=yes\\nRestrictSUIDSGID=yes\\nRestrictRealtime=yes\\nRemoveIPC=yes\\nReadWritePaths=/var/lib/evidence-gw\\nCapabilityBoundingSet=\\nAmbientCapabilities=\\nLimitNOFILE=1024\\n\\n[Install]\\nWantedBy=multi-user.target\\n\",\"unit_source_sha256\":\"8af420c05d185181bee0d6847271b4947db2b1dddf510541ae40027c5ba34026\"},\"local_integrity\":{\"/etc/systemd/system/homelab-command-adapter.service\":{\"bytes\":665,\"exists\":true,\"gid\":0,\"is_symlink\":false,\"mode\":\"0644\",\"owner\":\"root\",\"path\":\"/etc/systemd/system/homelab-command-adapter.service\",\"resolved_path\":\"/etc/systemd/system/homelab-command-adapter.service\",\"sha256\":\"74d0f37878652ed9d46b53ae8556bbb30ebd683741f293c3f59b3527876ed2d7\",\"uid\":0},\"/etc/systemd/system/homelab-command-portal.service\":{\"bytes\":981,\"exists\":true,\"gid\":0,\"is_symlink\":false,\"mode\":\"0644\",\"owner\":\"root\",\"path\":\"/etc/systemd/system/homelab-command-portal.service\",\"resolved_path\":\"/etc/systemd/system/homelab-command-portal.service\",\"sha256\":\"8453eab30f5c3ccd285fc9f70fff18f564809bb27f7bf84f51f2ba593aa85263\",\"uid\":0},\"/usr/local/sbin/homelab-chat-run\":{\"bytes\":11864,\"exists\":true,\"gid\":0,\"is_symlink\":false,\"mode\":\"0750\",\"owner\":\"root\",\"path\":\"/usr/local/sbin/homelab-chat-run\",\"resolved_path\":\"/usr/local/sbin/homelab-chat-run\",\"sha256\":\"2a88defef9517f89bc60934dce82097f8e316b08af9417e95167ea5d759a10d9\",\"uid\":0},\"/usr/local/sbin/homelab-command-adapter\":{\"bytes\":77441,\"exists\":true,\"gid\":0,\"is_symlink\":false,\"mode\":\"0750\",\"owner\":\"root\",\"path\":\"/usr/local/sbin/homelab-command-adapter\",\"resolved_path\":\"/usr/local/sbin/homelab-command-adapter\",\"sha256\":\"f2410cd328e2837b99e8ce4a28e25cb031a6bdd596d0286ac77a1822ef1cecd0\",\"uid\":0},\"/usr/local/sbin/homelab-command-portal\":{\"bytes\":39362,\"exists\":true,\"gid\":990,\"is_symlink\":false,\"mode\":\"0750\",\"owner\":\"root\",\"path\":\"/usr/local/sbin/homelab-command-portal\",\"resolved_path\":\"/usr/local/sbin/homelab-command-portal\",\"sha256\":\"8056faf4ef6348f0b06319ad3c49fbfeeadf25b6370f857ea19569bd27652ee2\",\"uid\":0},\"/usr/local/sbin/homelab-vm130-exec\":{\"bytes\":1333,\"exists\":true,\"gid\":0,\"is_symlink\":false,\"mode\":\"0750\",\"owner\":\"root\",\"path\":\"/usr/local/sbin/homelab-vm130-exec\",\"resolved_path\":\"/usr/local/sbin/homelab-vm130-exec\",\"sha256\":\"1959773393723b013af7c93cd29a7b833fd607c903d044e2fcef6accaa29c275\",\"uid\":0}},\"preplan_basis_sha256\":\"6ae59d4b7877710758ecb8e315a8322e71c475b6dcda680864eff8c1f7e7e0d9\",\"receiver\":{\"anchors\":[{\"line\":45,\"text\":\"flock -w 60 9 || {\"},{\"line\":121,\"text\":\"    \\\"$STAGE_DIR/latest.json\\\"\"},{\"line\":128,\"text\":\"    \\\"$STAGE_DIR/latest.txt\\\"\"},{\"line\":170,\"text\":\"    refs/remotes/origin/assistant-runtime\"},{\"line\":174,\"text\":\"        -B assistant-runtime \\\\\"},{\"line\":175,\"text\":\"        origin/assistant-runtime\"},{\"line\":179,\"text\":\"        --orphan assistant-runtime\"},{\"line\":184,\"text\":\"mkdir -p runtime/history\"},{\"line\":186,\"text\":\"cp \\\"$STAGE/latest.json\\\" runtime/latest.json\"},{\"line\":187,\"text\":\"cp \\\"$STAGE/latest.txt\\\" runtime/latest.txt\"},{\"line\":188,\"text\":\"cp \\\"$STAGE/latest.json\\\" \\\"runtime/history/${COMMAND_ID}.json\\\"\"},{\"line\":189,\"text\":\"cp \\\"$STAGE/latest.txt\\\" \\\"runtime/history/${COMMAND_ID}.txt\\\"\"},{\"line\":192,\"text\":\"git add \\\\\"},{\"line\":194,\"text\":\"    runtime/latest.json \\\\\"},{\"line\":195,\"text\":\"    runtime/latest.txt \\\\\"},{\"line\":196,\"text\":\"    \\\"runtime/history/${COMMAND_ID}.json\\\"\"},{\"line\":198,\"text\":\"git add \\\"runtime/history/${COMMAND_ID}.txt\\\"\"},{\"line\":204,\"text\":\"        git commit \\\\\"},{\"line\":210,\"text\":\"GIT_ASKPASS=/data/gitea/runtime-auth/askpass GIT_TERMINAL_PROMPT=0 git push --quiet http://127.0.0.1:3000/homelab-admin/homelab-public-context.git HEAD:refs/heads/assistant-runtime\"},{\"line\":219,\"text\":\"JSON_TMP=\\\"$(mktemp \\\"${PUBLIC_DIR}/.latest.json.XXXXXX\\\")\\\"\"},{\"line\":220,\"text\":\"TXT_TMP=\\\"$(mktemp \\\"${PUBLIC_DIR}/.latest.txt.XXXXXX\\\")\\\"\"},{\"line\":238,\"text\":\"    \\\"branch\\\": \\\"assistant-runtime\\\",\"},{\"line\":244,\"text\":\"        \\\"homelab-runtime/latest.json\\\"\"},{\"line\":248,\"text\":\"        \\\"homelab-runtime/latest.txt\\\"\"},{\"line\":255,\"text\":\"mv -f \\\"$JSON_TMP\\\" \\\"$PUBLIC_DIR/latest.json\\\"\"},{\"line\":256,\"text\":\"mv -f \\\"$TXT_TMP\\\" \\\"$PUBLIC_DIR/latest.txt\\\"\"},{\"line\":257,\"text\":\"mv -f \\\"$INDEX_TMP\\\" \\\"$PUBLIC_DIR/index.json\\\"\"},{\"line\":261,\"text\":\"    http://127.0.0.1:3002/.well-known/homelab-runtime/latest.json |\"},{\"line\":266,\"text\":\"    \\\"http://127.0.0.1:3002/homelab-admin/homelab-public-context/raw/branch/assistant-runtime/runtime/latest.json\\\" |\"}],\"features\":{\"has_atomic_replace\":true,\"has_by_command\":false,\"has_flock\":true,\"has_git_commit\":true,\"has_git_push\":true,\"has_latest_json\":true,\"has_latest_txt\":true},\"path\":\"/usr/local/sbin/homelab-runtime-receive\",\"sha256\":\"6f4eadda35241d984c30084430f60c456e943d3b17fea6b8c82cf3d5d6a318f4\",\"sha_rc\":0,\"source_bytes\":6549,\"source_rc\":0,\"source_redacted\":\"#!/usr/bin/env bash\\nset -Eeuo pipefail\\numask 027\\n\\nHOST_DATA='/opt/stacks/gitea/data'\\nBARE_CONTAINER='/data/git/repositories/homelab-admin/homelab-public-context.git'\\n\\nPUBLIC_DIR=\\\"${HOST_DATA}/gitea/public/.well-known/homelab-runtime\\\"\\nSTAGE_DIR=\\\"${HOST_DATA}/gitea/runtime-stage\\\"\\nWORK_ROOT=\\\"${HOST_DATA}/gitea/runtime-work\\\"\\n\\nLOCK='/run/lock/homelab-runtime-receive.lock'\\n\\n[ \\\"$#\\\" -eq 3 ] || {\\n    echo 'RESULT=FAIL'\\n    echo 'REASON=USAGE'\\n    exit 64\\n}\\n\\nCOMMAND_ID=\\\"$1\\\"\\nJSON_FILE=\\\"$2\\\"\\nTXT_FILE=\\\"$3\\\"\\n\\ncase \\\"$COMMAND_ID\\\" in\\n    ''|*[!A-Za-z0-9._-]*)\\n        echo 'RESULT=FAIL'\\n        echo 'REASON=INVALID_COMMAND_ID'\\n        exit 65\\n        ;;\\nesac\\n\\n[ -f \\\"$JSON_FILE\\\" ] || {\\n    echo 'RESULT=FAIL'\\n    echo 'REASON=JSON_FILE_MISSING'\\n    exit 66\\n}\\n\\n[ -f \\\"$TXT_FILE\\\" ] || {\\n    echo 'RESULT=FAIL'\\n    echo 'REASON=TXT_FILE_MISSING'\\n    exit 67\\n}\\n\\nexec 9>\\\"$LOCK\\\"\\nflock -w 60 9 || {\\n    echo 'RESULT=FAIL'\\n    echo 'REASON=RECEIVER_LOCK_TIMEOUT'\\n    exit 68\\n}\\n\\npython3 - \\\"$JSON_FILE\\\" \\\"$TXT_FILE\\\" \\\"$COMMAND_ID\\\" <<'PY'\\nimport json\\nimport re\\nimport sys\\nfrom pathlib import Path\\n\\njson_path = Path(sys.argv[1])\\ntext_path = Path(sys.argv[2])\\nexpected_id = sys.argv[3]\\n\\nif json_path.stat().st_size > 524_288:\\n    raise SystemExit(\\\"JSON_TOO_LARGE\\\")\\n\\nif text_path.stat().st_size > 1_048_576:\\n    raise SystemExit(\\\"TEXT_TOO_LARGE\\\")\\n\\njson_text = json_path.read_text(encoding=\\\"utf-8\\\")\\ntext = text_path.read_text(encoding=\\\"utf-8\\\")\\n\\ndata = json.loads(json_text)\\n\\nif data.get(\\\"command_id\\\") != expected_id:\\n    raise SystemExit(\\\"COMMAND_ID_MISMATCH\\\")\\n\\nif data.get(\\\"sanitized\\\") is not True:\\n    raise SystemExit(\\\"SANITIZED_FLAG_REQUIRED\\\")\\n\\nif data.get(\\\"secrets_included\\\") is not False:\\n    raise SystemExit(\\\"SECRETS_FLAG_INVALID\\\")\\n\\nif data.get(\\\"private_addresses_included\\\") is not False:\\n    raise SystemExit(\\\"PRIVATE_ADDRESS_FLAG_INVALID\\\")\\n\\ncombined = json_text + \\\"\\\\n\\\" + text\\n\\npatterns = [\\n    r\\\"-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----\\\",\\n    r\\\"(?i)(password|passwd|passphrase|authorization|api[_-]?key|access[_-]?token|refresh[_-]?token|private[_-]?key)\\\\s*[:=]\\\\s*\\\\S+\\\",\\n    r\\\"(?i)/root/\\\\.ssh\\\",\\n    r\\\"(?i)[SENSITIVE_PATH]\\\",\\n    r\\\"(?i)rclone\\\\.conf\\\",\\n    r\\\"\\\\b10(?:\\\\.\\\\d{1,3}){3}\\\\b\\\",\\n    r\\\"\\\\b192\\\\.168(?:\\\\.\\\\d{1,3}){2}\\\\b\\\",\\n    r\\\"\\\\b172\\\\.(?:1[6-9]|2\\\\d|3[01])(?:\\\\.\\\\d{1,3}){2}\\\\b\\\",\\n]\\n\\nfor pattern in patterns:\\n    if re.search(pattern, combined):\\n        raise SystemExit(\\\"FORBIDDEN_PUBLIC_CONTENT\\\")\\n\\nprint(\\\"PAYLOAD_VALIDATION=OK\\\")\\nPY\\n\\nGIT_UID=\\\"$(docker exec gitea id -u git)\\\"\\nGIT_GID=\\\"$(docker exec gitea id -g git)\\\"\\n\\nmkdir -p \\\\\\n    \\\"$PUBLIC_DIR\\\" \\\\\\n    \\\"$STAGE_DIR\\\" \\\\\\n    \\\"$WORK_ROOT\\\"\\n\\nchown \\\"$GIT_UID:$GIT_GID\\\" \\\\\\n    \\\"$STAGE_DIR\\\" \\\\\\n    \\\"$WORK_ROOT\\\"\\n\\ninstall \\\\\\n    -o \\\"$GIT_UID\\\" \\\\\\n    -g \\\"$GIT_GID\\\" \\\\\\n    -m 0644 \\\\\\n    \\\"$JSON_FILE\\\" \\\\\\n    \\\"$STAGE_DIR/latest.json\\\"\\n\\ninstall \\\\\\n    -o \\\"$GIT_UID\\\" \\\\\\n    -g \\\"$GIT_GID\\\" \\\\\\n    -m 0644 \\\\\\n    \\\"$TXT_FILE\\\" \\\\\\n    \\\"$STAGE_DIR/latest.txt\\\"\\n\\ncat > \\\"$STAGE_DIR/README.md\\\" <<'DOC'\\n# Homelab assistant runtime\\n\\nSanitized command results for read-only assistant inspection.\\n\\nCredentials, authentication data, private keys, private IP addresses and\\nunfiltered configuration files are prohibited.\\nDOC\\n\\nchown \\\"$GIT_UID:$GIT_GID\\\" \\\"$STAGE_DIR/README.md\\\"\\nchmod 0644 \\\"$STAGE_DIR/README.md\\\"\\n\\nPUBLISHED_AT=\\\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\\\"\\nWORK_CONTAINER=\\\"/data/gitea/runtime-work/${COMMAND_ID}.$$\\\"\\n\\nCOMMIT_SHA=\\\"$(\\n    docker exec \\\\\\n        -u \\\"${GIT_UID}:${GIT_GID}\\\" \\\\\\n        -e COMMAND_ID=\\\"$COMMAND_ID\\\" \\\\\\n        -e PUBLISHED_AT=\\\"$PUBLISHED_AT\\\" \\\\\\n        -e WORK_CONTAINER=\\\"$WORK_CONTAINER\\\" \\\\\\n        gitea sh -lc '\\nset -eu\\n\\nBARE=\\\"/data/git/repositories/homelab-admin/homelab-public-context.git\\\"\\nSTAGE=\\\"/data/gitea/runtime-stage\\\"\\nWORK=\\\"$WORK_CONTAINER\\\"\\n\\nrm -rf \\\"$WORK\\\"\\n\\ngit clone --quiet \\\"$BARE\\\" \\\"$WORK\\\"\\n\\ncd \\\"$WORK\\\"\\n\\ngit config user.name \\\"homelab-runtime-publisher\\\"\\ngit config user.email \\\"homelab-runtime@localhost\\\"\\n\\nif git show-ref \\\\\\n    --verify \\\\\\n    --quiet \\\\\\n    refs/remotes/origin/assistant-runtime\\nthen\\n    git checkout \\\\\\n        --quiet \\\\\\n        -B assistant-runtime \\\\\\n        origin/assistant-runtime\\nelse\\n    git checkout \\\\\\n        --quiet \\\\\\n        --orphan assistant-runtime\\n\\n    git rm -rf . >/dev/null 2>&1 || true\\nfi\\n\\nmkdir -p runtime/history\\n\\ncp \\\"$STAGE/latest.json\\\" runtime/latest.json\\ncp \\\"$STAGE/latest.txt\\\" runtime/latest.txt\\ncp \\\"$STAGE/latest.json\\\" \\\"runtime/history/${COMMAND_ID}.json\\\"\\ncp \\\"$STAGE/latest.txt\\\" \\\"runtime/history/${COMMAND_ID}.txt\\\"\\ncp \\\"$STAGE/README.md\\\" README.md\\n\\ngit add \\\\\\n    README.md \\\\\\n    runtime/latest.json \\\\\\n    runtime/latest.txt \\\\\\n    \\\"runtime/history/${COMMAND_ID}.json\\\"\\n\\ngit add \\\"runtime/history/${COMMAND_ID}.txt\\\"\\n\\nif ! git diff --cached --quiet\\nthen\\n    GIT_AUTHOR_DATE=\\\"$PUBLISHED_AT\\\" \\\\\\n    GIT_COMMITTER_DATE=\\\"$PUBLISHED_AT\\\" \\\\\\n        git commit \\\\\\n            --quiet \\\\\\n            -m \\\"runtime: publish ${COMMAND_ID}\\\"\\nfi\\n\\nCOMMIT=\\\"$(git rev-parse HEAD)\\\"\\nGIT_ASKPASS=/data/gitea/runtime-auth/askpass GIT_TERMINAL_PROMPT=0 git push --quiet http://127.0.0.1:3000/homelab-admin/homelab-public-context.git HEAD:refs/heads/assistant-runtime\\n\\n\\nrm -rf \\\"$WORK\\\"\\n\\nprintf \\\"%s\\\\n\\\" \\\"$COMMIT\\\"\\n'\\n)\\\"\\n\\nJSON_TMP=\\\"$(mktemp \\\"${PUBLIC_DIR}/.latest.json.XXXXXX\\\")\\\"\\nTXT_TMP=\\\"$(mktemp \\\"${PUBLIC_DIR}/.latest.txt.XXXXXX\\\")\\\"\\nINDEX_TMP=\\\"$(mktemp \\\"${PUBLIC_DIR}/.index.json.XXXXXX\\\")\\\"\\n\\ninstall -m 0644 \\\"$JSON_FILE\\\" \\\"$JSON_TMP\\\"\\ninstall -m 0644 \\\"$TXT_FILE\\\" \\\"$TXT_TMP\\\"\\n\\npython3 - \\\\\\n    \\\"$COMMAND_ID\\\" \\\\\\n    \\\"$PUBLISHED_AT\\\" \\\\\\n    \\\"$COMMIT_SHA\\\" >\\\"$INDEX_TMP\\\" <<'PY'\\nimport json\\nimport sys\\n\\ncommand_id, published_at, commit_sha = sys.argv[1:4]\\n\\nprint(json.dumps({\\n    \\\"schema_version\\\": 1,\\n    \\\"channel\\\": \\\"homelab-runtime\\\",\\n    \\\"branch\\\": \\\"assistant-runtime\\\",\\n    \\\"latest_command_id\\\": command_id,\\n    \\\"published_at_utc\\\": published_at,\\n    \\\"commit_sha\\\": commit_sha,\\n    \\\"latest_json_url\\\": (\\n        \\\"https://git.gram1.ru/.well-known/\\\"\\n        \\\"homelab-runtime/latest.json\\\"\\n    ),\\n    \\\"latest_text_url\\\": (\\n        \\\"https://git.gram1.ru/.well-known/\\\"\\n        \\\"homelab-runtime/latest.txt\\\"\\n    )\\n}, ensure_ascii=False, indent=2))\\nPY\\n\\nchmod 0644 \\\"$INDEX_TMP\\\"\\n\\nmv -f \\\"$JSON_TMP\\\" \\\"$PUBLIC_DIR/latest.json\\\"\\nmv -f \\\"$TXT_TMP\\\" \\\"$PUBLIC_DIR/latest.txt\\\"\\nmv -f \\\"$INDEX_TMP\\\" \\\"$PUBLIC_DIR/index.json\\\"\\n\\ncurl -fsS \\\\\\n    --max-time 10 \\\\\\n    http://127.0.0.1:3002/.well-known/homelab-runtime/latest.json |\\ngrep -F \\\"$COMMAND_ID\\\" >/dev/null\\n\\ncurl -fsS \\\\\\n    --max-time 10 \\\\\\n    \\\"http://127.0.0.1:3002/homelab-admin/homelab-public-context/raw/branch/assistant-runtime/runtime/latest.json\\\" |\\ngrep -F \\\"$COMMAND_ID\\\" >/dev/null\\n\\necho 'RESULT=OK'\\necho \\\"COMMAND_ID=$COMMAND_ID\\\"\\necho \\\"COMMIT_SHA=$COMMIT_SHA\\\"\\necho 'STATIC_PUBLICATION=OK'\\necho 'RAW_BRANCH_PUBLICATION=OK'\\n\",\"source_sha256\":\"6f4eadda35241d984c30084430f60c456e943d3b17fea6b8c82cf3d5d6a318f4\",\"stat\":\"=root:root:750:6549\",\"stat_rc\":0,\"stat_stderr\":\"\",\"sudo_install_help_excerpt\":\"Usage: /usr/bin/install [OPTION]... [-T] SOURCE DEST\\n  or:  /usr/bin/install [OPTION]... SOURCE... DIRECTORY\\n  or:  /usr/bin/install [OPTION]... -t DIRECTORY SOURCE...\\n  or:  /usr/bin/install [OPTION]... -d DIRECTORY...\\n\\nThis install program copies files (often just compiled) into destination\\nlocations you choose.  If you want to download and install a ready-to-use\\npackage on a GNU/Linux system, you should instead be using a package manager\\nlike yum(1) or apt-get(1).\\n\\nIn the first three forms, copy SOURCE to DEST or multiple SOURCE(s) to\\nthe existing DIRECTORY, while setting permission modes and owner/group.\\nIn the 4th form, create all components of the given DIRECTORY(ies).\\n\\nMandatory arguments to long options are mandatory for short options too.\\n      --backup[=CONTROL]  make a backup of each existing destination file\\n  -b                  like --backup but does not accept an argument\\n  -c                  (ignored)\\n  -C, --compare       compare content of source and destination file\",\"sudo_install_help_rc\":0},\"report_sha256\":\"319d433bb77302d92977e66bec320130c69eca85e70e05f6f8350bba4f1a7d6b\",\"rollback_started\":false,\"runner\":{\"anchors\":[{\"line\":117,\"text\":\"TXT_FILE=\\\"$PUBLIC_DIR/${COMMAND_ID}-${STAMP}-latest.txt\\\"\"},{\"line\":406,\"text\":\"        \\\"homelab-runtime/latest.txt\\\"\"},{\"line\":482,\"text\":\"    sudo -n /usr/local/sbin/homelab-runtime-receive \\\\\"},{\"line\":496,\"text\":\"PUBLIC_URL='https://git.gram1.ru/.well-known/homelab-runtime/latest.json'\"},{\"line\":503,\"text\":\"            \\\"$PUBLIC_URL\\\" 2>/dev/null ||\"},{\"line\":540,\"text\":\"echo 'GITEA_PUBLICATION=OK'\"},{\"line\":541,\"text\":\"echo \\\"PUBLIC_URL=$PUBLIC_URL\\\"\"}],\"bytes\":11864,\"features\":{\"has_by_command\":false,\"latest_json_authoritative\":true,\"latest_txt_authoritative\":true,\"public_visibility_retry\":true,\"receiver_call\":true},\"sha256\":\"2a88defef9517f89bc60934dce82097f8e316b08af9417e95167ea5d759a10d9\"},\"schema\":1,\"source_lock\":{\"edge_signer_sha256\":\"523490816d058eddf1273e73cc58ca41db1ad3da24092bc8f9831823ef27c6b1\",\"gateway_script_path\":\"/opt/stacks/evidence-gw/gateway.py\",\"gateway_script_sha256\":\"27de57be0924fe8d86d38879b3bcdba8d4ade817b3a42219dd65d8aacc9601cb\",\"gateway_unit\":\"evidence-gw.service\",\"gateway_unit_sha256\":\"8af420c05d185181bee0d6847271b4947db2b1dddf510541ae40027c5ba34026\",\"local\":{\"/etc/systemd/system/homelab-command-adapter.service\":\"74d0f37878652ed9d46b53ae8556bbb30ebd683741f293c3f59b3527876ed2d7\",\"/etc/systemd/system/homelab-command-portal.service\":\"8453eab30f5c3ccd285fc9f70fff18f564809bb27f7bf84f51f2ba593aa85263\",\"/usr/local/sbin/homelab-chat-run\":\"2a88defef9517f89bc60934dce82097f8e316b08af9417e95167ea5d759a10d9\",\"/usr/local/sbin/homelab-command-adapter\":\"f2410cd328e2837b99e8ce4a28e25cb031a6bdd596d0286ac77a1822ef1cecd0\",\"/usr/local/sbin/homelab-command-portal\":\"8056faf4ef6348f0b06319ad3c49fbfeeadf25b6370f857ea19569bd27652ee2\",\"/usr/local/sbin/homelab-vm130-exec\":\"1959773393723b013af7c93cd29a7b833fd607c903d044e2fcef6accaa29c275\"},\"receiver_sha256\":\"6f4eadda35241d984c30084430f60c456e943d3b17fea6b8c82cf3d5d6a318f4\"},\"stage\":\"RUN7A_DELIVERY_PREPLAN_DISCOVERY\",\"state_inventory\":{\"counts\":{\"FAIL\":4,\"IMPORTED\":1,\"OK\":4,\"RUNNING\":1},\"diagnostic_failure_count\":4,\"duplicates\":{},\"stale_running\":[],\"unresolved_mutating\":[]},\"status\":\"PASS\",\"units\":{\"adapter\":{\"ActiveState\":\"active\",\"ExecMainStatus\":\"0\",\"ExecStart\":\"{ path=/usr/bin/python3 ; argv[]=/usr/bin/python3 -B /usr/local/sbin/homelab-command-adapter --serve ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=364681 ; code=(null) ; status=0/0 }\",\"FragmentPath\":\"/etc/systemd/system/homelab-command-adapter.service\",\"Group\":\"homelab-command-portal\",\"Id\":\"homelab-command-adapter.service\",\"LoadState\":\"loaded\",\"MainPID\":\"364681\",\"NRestarts\":\"0\",\"Result\":\"success\",\"SubState\":\"running\",\"UnitFileState\":\"enabled\",\"User\":\"root\",\"show_rc\":0},\"portal\":{\"ActiveState\":\"active\",\"ExecMainStatus\":\"0\",\"ExecStart\":\"{ path=/usr/bin/python3 ; argv[]=/usr/bin/python3 -B /usr/local/sbin/homelab-command-portal ; ignore_errors=no ; start_time=[Sun 2026-07-26 09:18:49 MSK] ; stop_time=[n/a] ; pid=536586 ; code=(null) ; status=0/0 }\",\"FragmentPath\":\"/etc/systemd/system/homelab-command-portal.service\",\"Group\":\"homelab-command-portal\",\"Id\":\"homelab-command-portal.service\",\"LoadState\":\"loaded\",\"MainPID\":\"536586\",\"NRestarts\":\"0\",\"Result\":\"success\",\"SubState\":\"running\",\"UnitFileState\":\"enabled\",\"User\":\"homelab-command-portal\",\"show_rc\":0}},\"warnings\":[]}\n"
}
