{ "schema_version": 1, "channel": "homelab-runtime", "command_id": "HOMELAB-V13-ADAPTER-RISK-SCHEMA-READ-ONLY-20260805T121700Z", "status": "OK", "rc": 0, "host": "pve01", "mode": "verify", "component": "homelab-command-adapter-schema", "started_at_utc": "2026-08-05T12:21:30Z", "finished_at_utc": "2026-08-05T12:21:32Z", "reference_register_checked": true, "reference_sha256": "5763f2f8edc75fcf6f3951f8c95896d1cc112ceccb6cf5b64a383f540a4deb8e", "error_register_checked": true, "error_register_sha256": "24934a2c7fa5c26f6e828583c4aefac7f143b40c0171736ed7571e617d55ab83", "command_sha256": "8f5ea9cbbb504993bd994b501118c5e9eff048c652ee8819f91f9e1e372631f8", "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": "d491cbc5d16aa000a95ff0b394d333365a4aca69b7dd8d831e7ec7dfdedbf068", "sanitized_output_sha256": "d491cbc5d16aa000a95ff0b394d333365a4aca69b7dd8d831e7ec7dfdedbf068", "output_truncated_in_json": false, "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", "output": "HOMELAB_RESULT_CONTRACT={\"changes_made\":false,\"command_id\":\"HOMELAB-V13-ADAPTER-RISK-SCHEMA-READ-ONLY-20260805T121700Z\",\"command_rc\":0,\"control_plane_error\":null,\"exact_invalid_risk_source_found\":true,\"matching_file_count\":2,\"matching_file_manifest_sha256\":\"af904aec1040e2968d87c5e61b6ced9f013d49a0c7f5e0a48f70b292df1a966c\",\"mutation_outcome\":\"NO_MUTATION\",\"rc\":0,\"report_bytes\":20215,\"report_sha256\":\"4b4749cedde3bcaed3f4b01824f2c5de02d90e6525a59e5fd4e8b1b545344a95\",\"risk_candidate_tokens\":[\"HIGH\",\"READ_ONLY\",\"REVERSIBLE\"],\"risk_enum_confirmed\":true,\"rollback_restored\":false,\"rollback_started\":false,\"status\":\"OK\",\"version\":1}\n{\"candidate_roots\":[\"/\",\"/etc/systemd/system\",\"/etc/systemd/system/homelab-command-adapter.service\",\"/opt\",\"/srv\",\"/usr/bin\",\"/usr/bin/python3.13\",\"/usr/local\",\"/usr/local/lib\",\"/usr/local/sbin\",\"/usr/local/sbin/homelab-command-adapter\"],\"changes_made\":false,\"command_id\":\"HOMELAB-V13-ADAPTER-RISK-SCHEMA-READ-ONLY-20260805T121700Z\",\"mutation_outcome\":\"NO_MUTATION\",\"read_only\":true,\"risk_enum\":{\"candidate_tokens\":[\"HIGH\",\"READ_ONLY\",\"REVERSIBLE\"],\"exact_enum_confirmed\":true,\"proof\":[{\"context\":[{\"line\":380,\"text\":\" ):\"},{\"line\":381,\"text\":\" raise AdapterError(\\\"invalid timeout_seconds\\\")\"},{\"line\":382,\"text\":\"\"},{\"line\":383,\"text\":\" risk = document.get(\\\"risk\\\")\"},{\"line\":384,\"text\":\" if risk not in ALLOWED_RISKS:\"},{\"line\":385,\"text\":\" raise AdapterError(\\\"invalid risk\\\")\"},{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"}],\"match_line\":383,\"path\":\"/usr/local/sbin/homelab-command-adapter\",\"tokens\":[\"HIGH\",\"REVERSIBLE\"]},{\"context\":[{\"line\":381,\"text\":\" raise AdapterError(\\\"invalid timeout_seconds\\\")\"},{\"line\":382,\"text\":\"\"},{\"line\":383,\"text\":\" risk = document.get(\\\"risk\\\")\"},{\"line\":384,\"text\":\" if risk not in ALLOWED_RISKS:\"},{\"line\":385,\"text\":\" raise AdapterError(\\\"invalid risk\\\")\"},{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"},{\"line\":387,\"text\":\" raise AdapterError(\\\"change capsule risk must be REVERSIBLE or HIGH\\\")\"}],\"match_line\":384,\"path\":\"/usr/local/sbin/homelab-command-adapter\",\"tokens\":[\"HIGH\",\"REVERSIBLE\"]},{\"context\":[{\"line\":382,\"text\":\"\"},{\"line\":383,\"text\":\" risk = document.get(\\\"risk\\\")\"},{\"line\":384,\"text\":\" if risk not in ALLOWED_RISKS:\"},{\"line\":385,\"text\":\" raise AdapterError(\\\"invalid risk\\\")\"},{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"},{\"line\":387,\"text\":\" raise AdapterError(\\\"change capsule risk must be REVERSIBLE or HIGH\\\")\"},{\"line\":388,\"text\":\" if mode != \\\"change\\\" and risk != \\\"READ_ONLY\\\":\"}],\"match_line\":385,\"path\":\"/usr/local/sbin/homelab-command-adapter\",\"tokens\":[\"HIGH\",\"READ_ONLY\",\"REVERSIBLE\"]},{\"context\":[{\"line\":383,\"text\":\" risk = document.get(\\\"risk\\\")\"},{\"line\":384,\"text\":\" if risk not in ALLOWED_RISKS:\"},{\"line\":385,\"text\":\" raise AdapterError(\\\"invalid risk\\\")\"},{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"},{\"line\":387,\"text\":\" raise AdapterError(\\\"change capsule risk must be REVERSIBLE or HIGH\\\")\"},{\"line\":388,\"text\":\" if mode != \\\"change\\\" and risk != \\\"READ_ONLY\\\":\"},{\"line\":389,\"text\":\" raise AdapterError(\\\"non-change capsule risk must be READ_ONLY\\\")\"}],\"match_line\":386,\"path\":\"/usr/local/sbin/homelab-command-adapter\",\"tokens\":[\"HIGH\",\"READ_ONLY\",\"REVERSIBLE\"]},{\"context\":[{\"line\":384,\"text\":\" if risk not in ALLOWED_RISKS:\"},{\"line\":385,\"text\":\" raise AdapterError(\\\"invalid risk\\\")\"},{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"},{\"line\":387,\"text\":\" raise AdapterError(\\\"change capsule risk must be REVERSIBLE or HIGH\\\")\"},{\"line\":388,\"text\":\" if mode != \\\"change\\\" and risk != \\\"READ_ONLY\\\":\"},{\"line\":389,\"text\":\" raise AdapterError(\\\"non-change capsule risk must be READ_ONLY\\\")\"},{\"line\":390,\"text\":\"\"}],\"match_line\":387,\"path\":\"/usr/local/sbin/homelab-command-adapter\",\"tokens\":[\"HIGH\",\"READ_ONLY\",\"REVERSIBLE\"]},{\"context\":[{\"line\":385,\"text\":\" raise AdapterError(\\\"invalid risk\\\")\"},{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"},{\"line\":387,\"text\":\" raise AdapterError(\\\"change capsule risk must be REVERSIBLE or HIGH\\\")\"},{\"line\":388,\"text\":\" if mode != \\\"change\\\" and risk != \\\"READ_ONLY\\\":\"},{\"line\":389,\"text\":\" raise AdapterError(\\\"non-change capsule risk must be READ_ONLY\\\")\"},{\"line\":390,\"text\":\"\"},{\"line\":391,\"text\":\" flags = document.get(\\\"change_flags\\\")\"}],\"match_line\":388,\"path\":\"/usr/local/sbin/homelab-command-adapter\",\"tokens\":[\"HIGH\",\"READ_ONLY\",\"REVERSIBLE\"]},{\"context\":[{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"},{\"line\":387,\"text\":\" raise AdapterError(\\\"change capsule risk must be REVERSIBLE or HIGH\\\")\"},{\"line\":388,\"text\":\" if mode != \\\"change\\\" and risk != \\\"READ_ONLY\\\":\"},{\"line\":389,\"text\":\" raise AdapterError(\\\"non-change capsule risk must be READ_ONLY\\\")\"},{\"line\":390,\"text\":\"\"},{\"line\":391,\"text\":\" flags = document.get(\\\"change_flags\\\")\"},{\"line\":392,\"text\":\" if not isinstance(flags, dict):\"}],\"match_line\":389,\"path\":\"/usr/local/sbin/homelab-command-adapter\",\"tokens\":[\"HIGH\",\"READ_ONLY\",\"REVERSIBLE\"]}]},\"rollback_restored\":false,\"rollback_started\":false,\"scan\":{\"exact_invalid_mode_matches\":1,\"exact_invalid_risk_matches\":1,\"files_scanned\":3,\"matching_file_manifest_sha256\":\"af904aec1040e2968d87c5e61b6ced9f013d49a0c7f5e0a48f70b292df1a966c\",\"matching_files\":2,\"uppercase_enum_tokens\":[\"FAIL\",\"HIGH\",\"NONE\",\"READ_ONLY\",\"REVERSIBLE\",\"UNKNOWN\",\"USAGE\"]},\"schema\":1,\"source_matches\":[{\"bytes\":21321,\"matches\":[{\"context\":[{\"line\":8,\"text\":\"import sys\"},{\"line\":9,\"text\":\"from pathlib import Path\"},{\"line\":10,\"text\":\"\"},{\"line\":11,\"text\":\"raw_path, command_id, mode, command_rc = sys.argv[1:]\"},{\"line\":12,\"text\":\"command_rc = int(command_rc)\"},{\"line\":13,\"text\":\"\"},{\"line\":14,\"text\":\"result = {\"}],\"match_line\":11},{\"context\":[{\"line\":21,\"text\":\" \\\"error\\\": \\\"NONE\\\",\"},{\"line\":22,\"text\":\"}\"},{\"line\":23,\"text\":\"\"},{\"line\":24,\"text\":\"if mode == \\\"change\\\":\"},{\"line\":25,\"text\":\" result = {\"},{\"line\":26,\"text\":\" \\\"valid\\\": False,\"},{\"line\":27,\"text\":\" \\\"status\\\": \\\"UNKNOWN\\\",\"}],\"match_line\":24},{\"context\":[{\"line\":188,\"text\":\"usage() {\"},{\"line\":189,\"text\":\" cat >&2 <<'USAGE'\"},{\"line\":190,\"text\":\"Usage:\"},{\"line\":191,\"text\":\" homelab-chat-run COMMAND_ID MODE COMPONENT -- command [arguments...]\"},{\"line\":192,\"text\":\"\"},{\"line\":193,\"text\":\"MODE:\"},{\"line\":194,\"text\":\" read-only\"}],\"match_line\":191},{\"context\":[{\"line\":190,\"text\":\"Usage:\"},{\"line\":191,\"text\":\" homelab-chat-run COMMAND_ID MODE COMPONENT -- command [arguments...]\"},{\"line\":192,\"text\":\"\"},{\"line\":193,\"text\":\"MODE:\"},{\"line\":194,\"text\":\" read-only\"},{\"line\":195,\"text\":\" verify\"},{\"line\":196,\"text\":\" change\"}],\"match_line\":193},{\"context\":[{\"line\":206,\"text\":\"[ \\\"$#\\\" -ge 5 ] || usage\"},{\"line\":207,\"text\":\"\"},{\"line\":208,\"text\":\"COMMAND_ID=\\\"$1\\\"\"},{\"line\":209,\"text\":\"MODE=\\\"$2\\\"\"},{\"line\":210,\"text\":\"COMPONENT=\\\"$3\\\"\"},{\"line\":211,\"text\":\"shift 3\"},{\"line\":212,\"text\":\"\"}],\"match_line\":209},{\"context\":[{\"line\":230,\"text\":\" ;;\"},{\"line\":231,\"text\":\"esac\"},{\"line\":232,\"text\":\"\"},{\"line\":233,\"text\":\"case \\\"$MODE\\\" in\"},{\"line\":234,\"text\":\" read-only|verify|change|transport-test)\"},{\"line\":235,\"text\":\" ;;\"},{\"line\":236,\"text\":\" *)\"}],\"match_line\":233},{\"context\":[{\"line\":304,\"text\":\"\"},{\"line\":305,\"text\":\"COMMAND_SHA256=\\\"$(\"},{\"line\":306,\"text\":\" {\"},{\"line\":307,\"text\":\" printf '%s\\\\0' \\\"$MODE\\\"\"},{\"line\":308,\"text\":\" printf '%s\\\\0' \\\"$COMPONENT\\\"\"},{\"line\":309,\"text\":\" printf '%s\\\\0' \\\"$@\\\"\"},{\"line\":310,\"text\":\" } |\"}],\"match_line\":307},{\"context\":[{\"line\":337,\"text\":\"RESULT_CONTRACT_ERROR='NONE'\"},{\"line\":338,\"text\":\"COMMAND_RC=''\"},{\"line\":339,\"text\":\"\"},{\"line\":340,\"text\":\"if [ \\\"$MODE\\\" = change ]; then\"},{\"line\":341,\"text\":\" CHANGE_DECLARED=true\"},{\"line\":342,\"text\":\" RESULT_CONTRACT_VALID=false\"},{\"line\":343,\"text\":\" CONTRACT_STATUS='UNKNOWN'\"}],\"match_line\":340},{\"context\":[{\"line\":416,\"text\":\" read_mutation_contract \\\\\"},{\"line\":417,\"text\":\" \\\"$RAW_FILE\\\" \\\\\"},{\"line\":418,\"text\":\" \\\"$COMMAND_ID\\\" \\\\\"},{\"line\":419,\"text\":\" \\\"$MODE\\\" \\\\\"},{\"line\":420,\"text\":\" \\\"$COMMAND_RC\\\"\"},{\"line\":421,\"text\":\" )\"},{\"line\":422,\"text\":\"\"}],\"match_line\":419},{\"context\":[{\"line\":420,\"text\":\" \\\"$COMMAND_RC\\\"\"},{\"line\":421,\"text\":\" )\"},{\"line\":422,\"text\":\"\"},{\"line\":423,\"text\":\" if [ \\\"$MODE\\\" = change ]; then\"},{\"line\":424,\"text\":\" if [ \\\"$RESULT_CONTRACT_VALID\\\" != true ]; then\"},{\"line\":425,\"text\":\" RC=76\"},{\"line\":426,\"text\":\" STATUS='FAIL'\"}],\"match_line\":423},{\"context\":[{\"line\":547,\"text\":\" \\\"$STATUS\\\" \\\\\"},{\"line\":548,\"text\":\" \\\"$RC\\\" \\\\\"},{\"line\":549,\"text\":\" \\\"$HOST_SHORT\\\" \\\\\"},{\"line\":550,\"text\":\" \\\"$MODE\\\" \\\\\"},{\"line\":551,\"text\":\" \\\"$COMPONENT\\\" \\\\\"},{\"line\":552,\"text\":\" \\\"$STARTED_AT\\\" \\\\\"},{\"line\":553,\"text\":\" \\\"$FINISHED_AT\\\" \\\\\"}],\"match_line\":550},{\"context\":[{\"line\":579,\"text\":\" status,\"},{\"line\":580,\"text\":\" rc,\"},{\"line\":581,\"text\":\" host,\"},{\"line\":582,\"text\":\" mode,\"},{\"line\":583,\"text\":\" component,\"},{\"line\":584,\"text\":\" started,\"},{\"line\":585,\"text\":\" finished,\"}],\"match_line\":582},{\"context\":[{\"line\":634,\"text\":\" \\\"status\\\": status,\"},{\"line\":635,\"text\":\" \\\"rc\\\": int(rc),\"},{\"line\":636,\"text\":\" \\\"host\\\": host,\"},{\"line\":637,\"text\":\" \\\"mode\\\": mode,\"},{\"line\":638,\"text\":\" \\\"component\\\": component,\"},{\"line\":639,\"text\":\" \\\"started_at_utc\\\": started,\"},{\"line\":640,\"text\":\" \\\"finished_at_utc\\\": finished,\"}],\"match_line\":637},{\"context\":[{\"line\":694,\"text\":\" echo \\\"STATUS=$STATUS\\\"\"},{\"line\":695,\"text\":\" echo \\\"RC=$RC\\\"\"},{\"line\":696,\"text\":\" echo \\\"HOST=$HOST_SHORT\\\"\"},{\"line\":697,\"text\":\" echo \\\"MODE=$MODE\\\"\"},{\"line\":698,\"text\":\" echo \\\"COMPONENT=$COMPONENT\\\"\"},{\"line\":699,\"text\":\" echo 'REFERENCE_REGISTER_CHECK=OK'\"},{\"line\":700,\"text\":\" echo \\\"REFERENCE_SHA256=$REFERENCE_SHA256\\\"\"}],\"match_line\":697}],\"path\":\"/usr/local/sbin/homelab-chat-run\",\"sha256\":\"b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d\"},{\"bytes\":86019,\"matches\":[{\"context\":[{\"line\":119,\"text\":\" \\\"transport-test\\\",\"},{\"line\":120,\"text\":\"}\"},{\"line\":121,\"text\":\"ALLOWED_RISKS = {\"},{\"line\":122,\"text\":\" \\\"READ_ONLY\\\",\"},{\"line\":123,\"text\":\" \\\"REVERSIBLE\\\",\"},{\"line\":124,\"text\":\" \\\"HIGH\\\",\"},{\"line\":125,\"text\":\"}\"}],\"match_line\":122},{\"context\":[{\"line\":129,\"text\":\" \\\"command_id\\\",\"},{\"line\":130,\"text\":\" \\\"title\\\",\"},{\"line\":131,\"text\":\" \\\"summary\\\",\"},{\"line\":132,\"text\":\" \\\"mode\\\",\"},{\"line\":133,\"text\":\" \\\"component\\\",\"},{\"line\":134,\"text\":\" \\\"target\\\",\"},{\"line\":135,\"text\":\" \\\"timeout_seconds\\\",\"}],\"match_line\":132},{\"context\":[{\"line\":133,\"text\":\" \\\"component\\\",\"},{\"line\":134,\"text\":\" \\\"target\\\",\"},{\"line\":135,\"text\":\" \\\"timeout_seconds\\\",\"},{\"line\":136,\"text\":\" \\\"risk\\\",\"},{\"line\":137,\"text\":\" \\\"change_flags\\\",\"},{\"line\":138,\"text\":\" \\\"rollback\\\",\"},{\"line\":139,\"text\":\" \\\"argv\\\",\"}],\"match_line\":136},{\"context\":[{\"line\":143,\"text\":\"}\"},{\"line\":144,\"text\":\"ALLOWED_PAYLOAD_FIELDS = {\"},{\"line\":145,\"text\":\" \\\"path\\\",\"},{\"line\":146,\"text\":\" \\\"mode\\\",\"},{\"line\":147,\"text\":\" \\\"sha256\\\",\"},{\"line\":148,\"text\":\" \\\"content_b64\\\",\"},{\"line\":149,\"text\":\"}\"}],\"match_line\":146},{\"context\":[{\"line\":262,\"text\":\"\"},{\"line\":263,\"text\":\"def ensure_layout() -> None:\"},{\"line\":264,\"text\":\" portal = pwd.getpwnam(PORTAL_USER)\"},{\"line\":265,\"text\":\" for path, mode in (\"},{\"line\":266,\"text\":\" (ROOT, 0o750),\"},{\"line\":267,\"text\":\" (STATE_DIR, 0o750),\"},{\"line\":268,\"text\":\" (CAPSULE_DIR, 0o700),\"}],\"match_line\":265},{\"context\":[{\"line\":270,\"text\":\" ):\"},{\"line\":271,\"text\":\" path.mkdir(parents=True, exist_ok=True)\"},{\"line\":272,\"text\":\" os.chown(path, 0, portal.pw_gid)\"},{\"line\":273,\"text\":\" os.chmod(path, mode)\"},{\"line\":274,\"text\":\"\"},{\"line\":275,\"text\":\"\"},{\"line\":276,\"text\":\"def unique_json_loads(raw: bytes) -> dict[str, Any]:\"}],\"match_line\":273},{\"context\":[{\"line\":332,\"text\":\"\"},{\"line\":333,\"text\":\" if document.get(\\\"schema_version\\\") != 1:\"},{\"line\":334,\"text\":\" raise AdapterError(\\\"unsupported capsule schema\\\")\"},{\"line\":335,\"text\":\" if document.get(\\\"kind\\\") != \\\"homelab-command-capsule\\\":\"},{\"line\":336,\"text\":\" raise AdapterError(\\\"invalid capsule kind\\\")\"},{\"line\":337,\"text\":\"\"},{\"line\":338,\"text\":\" command_id = validate_text(\"}],\"match_line\":335},{\"context\":[{\"line\":366,\"text\":\" maximum=2000,\"},{\"line\":367,\"text\":\" )\"},{\"line\":368,\"text\":\"\"},{\"line\":369,\"text\":\" mode = document.get(\\\"mode\\\")\"},{\"line\":370,\"text\":\" if mode not in ALLOWED_MODES:\"},{\"line\":371,\"text\":\" raise AdapterError(\\\"invalid mode\\\")\"},{\"line\":372,\"text\":\" if document.get(\\\"target\\\") != \\\"pve01\\\":\"}],\"match_line\":369},{\"context\":[{\"line\":367,\"text\":\" )\"},{\"line\":368,\"text\":\"\"},{\"line\":369,\"text\":\" mode = document.get(\\\"mode\\\")\"},{\"line\":370,\"text\":\" if mode not in ALLOWED_MODES:\"},{\"line\":371,\"text\":\" raise AdapterError(\\\"invalid mode\\\")\"},{\"line\":372,\"text\":\" if document.get(\\\"target\\\") != \\\"pve01\\\":\"},{\"line\":373,\"text\":\" raise AdapterError(\\\"only pve01 target is supported\\\")\"}],\"match_line\":370},{\"context\":[{\"line\":368,\"text\":\"\"},{\"line\":369,\"text\":\" mode = document.get(\\\"mode\\\")\"},{\"line\":370,\"text\":\" if mode not in ALLOWED_MODES:\"},{\"line\":371,\"text\":\" raise AdapterError(\\\"invalid mode\\\")\"},{\"line\":372,\"text\":\" if document.get(\\\"target\\\") != \\\"pve01\\\":\"},{\"line\":373,\"text\":\" raise AdapterError(\\\"only pve01 target is supported\\\")\"},{\"line\":374,\"text\":\"\"}],\"match_line\":371},{\"context\":[{\"line\":380,\"text\":\" ):\"},{\"line\":381,\"text\":\" raise AdapterError(\\\"invalid timeout_seconds\\\")\"},{\"line\":382,\"text\":\"\"},{\"line\":383,\"text\":\" risk = document.get(\\\"risk\\\")\"},{\"line\":384,\"text\":\" if risk not in ALLOWED_RISKS:\"},{\"line\":385,\"text\":\" raise AdapterError(\\\"invalid risk\\\")\"},{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"}],\"match_line\":383},{\"context\":[{\"line\":381,\"text\":\" raise AdapterError(\\\"invalid timeout_seconds\\\")\"},{\"line\":382,\"text\":\"\"},{\"line\":383,\"text\":\" risk = document.get(\\\"risk\\\")\"},{\"line\":384,\"text\":\" if risk not in ALLOWED_RISKS:\"},{\"line\":385,\"text\":\" raise AdapterError(\\\"invalid risk\\\")\"},{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"},{\"line\":387,\"text\":\" raise AdapterError(\\\"change capsule risk must be REVERSIBLE or HIGH\\\")\"}],\"match_line\":384},{\"context\":[{\"line\":382,\"text\":\"\"},{\"line\":383,\"text\":\" risk = document.get(\\\"risk\\\")\"},{\"line\":384,\"text\":\" if risk not in ALLOWED_RISKS:\"},{\"line\":385,\"text\":\" raise AdapterError(\\\"invalid risk\\\")\"},{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"},{\"line\":387,\"text\":\" raise AdapterError(\\\"change capsule risk must be REVERSIBLE or HIGH\\\")\"},{\"line\":388,\"text\":\" if mode != \\\"change\\\" and risk != \\\"READ_ONLY\\\":\"}],\"match_line\":385},{\"context\":[{\"line\":383,\"text\":\" risk = document.get(\\\"risk\\\")\"},{\"line\":384,\"text\":\" if risk not in ALLOWED_RISKS:\"},{\"line\":385,\"text\":\" raise AdapterError(\\\"invalid risk\\\")\"},{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"},{\"line\":387,\"text\":\" raise AdapterError(\\\"change capsule risk must be REVERSIBLE or HIGH\\\")\"},{\"line\":388,\"text\":\" if mode != \\\"change\\\" and risk != \\\"READ_ONLY\\\":\"},{\"line\":389,\"text\":\" raise AdapterError(\\\"non-change capsule risk must be READ_ONLY\\\")\"}],\"match_line\":386},{\"context\":[{\"line\":384,\"text\":\" if risk not in ALLOWED_RISKS:\"},{\"line\":385,\"text\":\" raise AdapterError(\\\"invalid risk\\\")\"},{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"},{\"line\":387,\"text\":\" raise AdapterError(\\\"change capsule risk must be REVERSIBLE or HIGH\\\")\"},{\"line\":388,\"text\":\" if mode != \\\"change\\\" and risk != \\\"READ_ONLY\\\":\"},{\"line\":389,\"text\":\" raise AdapterError(\\\"non-change capsule risk must be READ_ONLY\\\")\"},{\"line\":390,\"text\":\"\"}],\"match_line\":387},{\"context\":[{\"line\":385,\"text\":\" raise AdapterError(\\\"invalid risk\\\")\"},{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"},{\"line\":387,\"text\":\" raise AdapterError(\\\"change capsule risk must be REVERSIBLE or HIGH\\\")\"},{\"line\":388,\"text\":\" if mode != \\\"change\\\" and risk != \\\"READ_ONLY\\\":\"},{\"line\":389,\"text\":\" raise AdapterError(\\\"non-change capsule risk must be READ_ONLY\\\")\"},{\"line\":390,\"text\":\"\"},{\"line\":391,\"text\":\" flags = document.get(\\\"change_flags\\\")\"}],\"match_line\":388},{\"context\":[{\"line\":386,\"text\":\" if mode == \\\"change\\\" and risk not in {\\\"REVERSIBLE\\\", \\\"HIGH\\\"}:\"},{\"line\":387,\"text\":\" raise AdapterError(\\\"change capsule risk must be REVERSIBLE or HIGH\\\")\"},{\"line\":388,\"text\":\" if mode != \\\"change\\\" and risk != \\\"READ_ONLY\\\":\"},{\"line\":389,\"text\":\" raise AdapterError(\\\"non-change capsule risk must be READ_ONLY\\\")\"},{\"line\":390,\"text\":\"\"},{\"line\":391,\"text\":\" flags = document.get(\\\"change_flags\\\")\"},{\"line\":392,\"text\":\" if not isinstance(flags, dict):\"}],\"match_line\":389},{\"context\":[{\"line\":396,\"text\":\" if any(type(value) is not bool for value in flags.values()):\"},{\"line\":397,\"text\":\" raise AdapterError(\\\"change_flags values must be boolean\\\")\"},{\"line\":398,\"text\":\"\"},{\"line\":399,\"text\":\" if mode != \\\"change\\\" and any(flags.values()):\"},{\"line\":400,\"text\":\" raise AdapterError(\"},{\"line\":401,\"text\":\" \\\"non-change capsule cannot declare changes\\\"\"},{\"line\":402,\"text\":\" )\"}],\"match_line\":399},{\"context\":[{\"line\":400,\"text\":\" raise AdapterError(\"},{\"line\":401,\"text\":\" \\\"non-change capsule cannot declare changes\\\"\"},{\"line\":402,\"text\":\" )\"},{\"line\":403,\"text\":\" if mode == \\\"change\\\" and not flags[\\\"production\\\"]:\"},{\"line\":404,\"text\":\" raise AdapterError(\"},{\"line\":405,\"text\":\" \\\"change capsule must declare production change\\\"\"},{\"line\":406,\"text\":\" )\"}],\"match_line\":403},{\"context\":[{\"line\":418,\"text\":\" minimum=1,\"},{\"line\":419,\"text\":\" maximum=2000,\"},{\"line\":420,\"text\":\" )\"},{\"line\":421,\"text\":\" if mode == \\\"change\\\" and rollback[\\\"available\\\"] is not True:\"},{\"line\":422,\"text\":\" raise AdapterError(\"},{\"line\":423,\"text\":\" \\\"change capsule requires available rollback\\\"\"},{\"line\":424,\"text\":\" )\"}],\"match_line\":421}],\"path\":\"/usr/local/sbin/homelab-command-adapter\",\"sha256\":\"a1a7a615c99568bf915d084cc476f0ebf54ab0eedf3ecd578c4b0e072f58c43d\"}],\"status\":\"OK\",\"systemd_adapter\":[{\"cat_rc\":1,\"cat_stderr\":\"No files found for homelab-adapter.service.\\n\",\"show\":{\"ActiveState\":\"inactive\",\"DropInPaths\":\"\",\"FragmentPath\":\"\",\"LoadState\":\"not-found\",\"MainPID\":\"0\",\"SubState\":\"dead\",\"WorkingDirectory\":\"\"},\"show_rc\":0,\"show_stderr\":\"\",\"unit\":\"homelab-adapter.service\",\"unit_text\":\"\"},{\"cat_rc\":0,\"cat_stderr\":\"\",\"process\":{\"cmdline\":[\"/usr/bin/python3\",\"-B\",\"/usr/local/sbin/homelab-command-adapter\",\"--serve\"],\"cwd\":\"/\",\"exe\":\"/usr/bin/python3.13\",\"pid\":1131444},\"show\":{\"ActiveState\":\"active\",\"DropInPaths\":\"\",\"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=0 ; code=(null) ; status=0/0 }\",\"FragmentPath\":\"/etc/systemd/system/homelab-command-adapter.service\",\"LoadState\":\"loaded\",\"MainPID\":\"1131444\",\"SubState\":\"running\",\"WorkingDirectory\":\"\"},\"show_rc\":0,\"show_stderr\":\"\",\"unit\":\"homelab-command-adapter.service\",\"unit_text\":\"# /etc/systemd/system/homelab-command-adapter.service\\n[Unit]\\nDescription=Homelab command adapter\\nAfter=network-online.target\\nWants=network-online.target\\n\\n[Service]\\nType=simple\\nExecStart=/usr/bin/python3 -B /usr/local/sbin/homelab-command-adapter --serve\\nUser=root\\nGroup=homelab-command-portal\\nUMask=0027\\nRuntimeDirectory=homelab-command-adapter\\nRuntimeDirectoryMode=0750\\nRestart=on-failure\\nRestartSec=2\\nNoNewPrivileges=false\\nPrivateTmp=true\\nProtectClock=true\\nProtectControlGroups=true\\nProtectKernelLogs=true\\nProtectKernelModules=true\\nProtectKernelTunables=true\\nRestrictRealtime=true\\nLockPersonality=true\\nMemoryDenyWriteExecute=true\\nSystemCallArchitectures=native\\nTimeoutStopSec=15\\n\\n[Install]\\nWantedBy=multi-user.target\"}]}\n" }