diff --git a/runtime/history/NEWFI-260908-A-STAGINGEDGE-VPS-FRONTEND-DISCOVERY6.json b/runtime/history/NEWFI-260908-A-STAGINGEDGE-VPS-FRONTEND-DISCOVERY6.json index 234e202b..f7829185 100644 --- a/runtime/history/NEWFI-260908-A-STAGINGEDGE-VPS-FRONTEND-DISCOVERY6.json +++ b/runtime/history/NEWFI-260908-A-STAGINGEDGE-VPS-FRONTEND-DISCOVERY6.json @@ -7,8 +7,8 @@ "host": "pve01", "mode": "read-only", "component": "newfi-staging-vps-frontend-access-discovery-readonly", - "started_at_utc": "2026-09-08T08:27:03Z", - "finished_at_utc": "2026-09-08T08:27:07Z", + "started_at_utc": "2026-09-08T08:40:34Z", + "finished_at_utc": "2026-09-08T08:40:38Z", "reference_register_checked": true, "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", "error_register_checked": true, diff --git a/runtime/latest.json b/runtime/latest.json index d1cf285b..f7829185 100644 --- a/runtime/latest.json +++ b/runtime/latest.json @@ -1,19 +1,19 @@ { "schema_version": 1, "channel": "homelab-runtime", - "command_id": "SIGNALBOT-260908-PACKAGE-SEAL-RCA-043PKGFIX4", - "status": "FAIL", - "rc": 3, + "command_id": "NEWFI-260908-A-STAGINGEDGE-VPS-FRONTEND-DISCOVERY6", + "status": "OK", + "rc": 0, "host": "pve01", "mode": "read-only", - "component": "signalbot-cr0116-package-seal-rca", - "started_at_utc": "2026-09-08T08:32:43Z", - "finished_at_utc": "2026-09-08T08:32:49Z", + "component": "newfi-staging-vps-frontend-access-discovery-readonly", + "started_at_utc": "2026-09-08T08:40:34Z", + "finished_at_utc": "2026-09-08T08:40:38Z", "reference_register_checked": true, "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", "error_register_checked": true, "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", - "command_sha256": "9ced1576bd959feda816a33885905e1bed85156c58d9145f8a4027570dcfa64d", + "command_sha256": "2ad58afd2cf7e1cc3a231fb3ac9b55b4f697d78841c53df32e5263b1c183c524", "duplicate_failed_command_blocked": false, "block_reason": null, "execution_started": true, @@ -21,7 +21,7 @@ "result_contract_valid": true, "result_contract_status": null, "result_contract_error": null, - "command_rc": 3, + "command_rc": 0, "changes_made": false, "rollback_started": false, "rollback_restored": null, @@ -30,9 +30,9 @@ "secrets_included": false, "private_addresses_included": false, "raw_evidence_retained_locally": true, - "raw_evidence_sha256": "f6bfc5e0e811b34437723475517c13047e89a5cd17aa1cba4a3bd9e162dd0583", - "sanitized_output_sha256": "f6bfc5e0e811b34437723475517c13047e89a5cd17aa1cba4a3bd9e162dd0583", + "raw_evidence_sha256": "284d281515727ed750fef17eede5cf72d87468c30b4ab1e2c5458a1dcc30e5b0", + "sanitized_output_sha256": "ab2cb2f4b1af09a6375abf6422ab4ce673bec87b1cb30e0c610e68776b23f81e", "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\nAUTHORITY_CHECK_SCOPE=READABILITY_ONLY_INCIDENTS_NOT_CLOSED\nSB043KF4_PRESTATE={\"active_env\":{\"APP_IMAGE\":\"8020-demonov-shadow:e32760c69a4311728db9066ee8bf2bf66b1e5a70\",\"CODE_IDENTITY\":\"e32760c69a4311728db9066ee8bf2bf66b1e5a70\",\"COLLECTION_START_AT\":\"2026-09-07T00:00:00+00:00\",\"EXPERIMENT_ID\":\"ec477ea41ecbd5cfdef5afc259595aab20674a7d933f41a389329ff05098b338\",\"RUNTIME_MANIFEST_ID\":\"58d3b59200bdc0eb8d448612679d667b194263586cb3198dfc08597935053a1a\"},\"services\":[{\"container_id\":\"a546a70c3e8c263d9b590cd78ee206bcda0cfcdb6662327e8415ca0e29ad1501\",\"image\":\"8020-demonov-shadow:e32760c69a4311728db9066ee8bf2bf66b1e5a70\",\"image_id\":\"sha256:403c4266282a56cb210bed95cd58295692296502913e7440e2133d7b5664c736\",\"restart_count\":15,\"running\":true,\"service\":\"collector\",\"started_at\":\"2026-09-08T08:18:12.235219419Z\"},{\"container_id\":\"d8023f4cbf91f9382e39eec0436c9cebc7665535b42b950e95408c03ff9bf30d\",\"image\":\"8020-demonov-shadow:e32760c69a4311728db9066ee8bf2bf66b1e5a70\",\"image_id\":\"sha256:403c4266282a56cb210bed95cd58295692296502913e7440e2133d7b5664c736\",\"restart_count\":0,\"running\":true,\"service\":\"relay\",\"started_at\":\"2026-09-06T18:48:20.603414848Z\"},{\"container_id\":\"1feeca2d9428cd5a277c026bef34534db307657dcdcd63aff98ea66e44f8b9c5\",\"image\":\"8020-demonov-shadow:e32760c69a4311728db9066ee8bf2bf66b1e5a70\",\"image_id\":\"sha256:403c4266282a56cb210bed95cd58295692296502913e7440e2133d7b5664c736\",\"restart_count\":0,\"running\":true,\"service\":\"shadow\",\"started_at\":\"2026-09-06T18:48:20.768190553Z\"},{\"container_id\":\"889be41d2cee9d327811da78388f834ba6eafe4c0eeae456867ed0df76832d01\",\"image\":\"8020-demonov-shadow:e32760c69a4311728db9066ee8bf2bf66b1e5a70\",\"image_id\":\"sha256:403c4266282a56cb210bed95cd58295692296502913e7440e2133d7b5664c736\",\"restart_count\":0,\"running\":true,\"service\":\"worker\",\"started_at\":\"2026-09-06T18:48:20.586788319Z\"}],\"stage_result\":{\"active_env_unchanged\":true,\"build_context\":\"/opt/stacks/8020-demonov-shadow/.deploy/SIGNALBOT-260908-DEPLOY-STAGE-042FIX5\",\"collector_source_sha256\":\"193dd0c7f9890fdb4026c24dbc5add825f1641d92097e881806705ca452812d5\",\"copy_source\":\"/opt/stacks/8020-demonov-shadow/.deploy/SIGNALBOT-260908-DEPLOY-STAGE-042FIX5/demonov_forward_map\",\"decision\":\"DEPLOY_STAGE_READY\",\"dockerfile\":\"/opt/stacks/8020-demonov-shadow/.deploy/SIGNALBOT-260908-DEPLOY-STAGE-042FIX5/Dockerfile\",\"helper_candidates\":{\"demonov_forward_map.live_prospective\":[{\"name\":\"_hash\",\"pure_candidate\":true,\"signature\":\"(value: 'object') -> 'str'\"}],\"demonov_forward_map.runtime_integrity\":[{\"name\":\"canonical_hash\",\"pure_candidate\":true,\"signature\":\"(payload: 'Any') -> 'str'\"},{\"name\":\"verify_experiment_seal_receipt\",\"pure_candidate\":true,\"signature\":\"(*, bundle: 'FinalProspectiveSealBundle', receipt: 'ExperimentSealReceipt', collection_start_at: 'datetime', receipt_verifier: 'Callable[[ExperimentSealReceipt], bool]') -> 'None'\"},{\"name\":\"required_retained_hashes\",\"pure_candidate\":true,\"signature\":\"(*, runtime_registry: 'RuntimeRegistry', model_registry: 'FinalModelRegistry', state_snapshots: 'Sequence[StateSnapshotManifest]' = (), predictor_runtime: 'PredictorRuntimeManifest | None' = None) -> 'Mapping[str, RetainedArtifactKind]'\"}],\"demonov_forward_map.runtime_manifest\":[{\"name\":\"canonical_hash\",\"pure_candidate\":true,\"signature\":\"(payload: 'Any') -> 'str'\"},{\"name\":\"verify_runtime_manifest\",\"pure_candidate\":true,\"signature\":\"(manifest: 'RuntimeDeploymentManifestV1', *, expected_code_identity: 'str', actual_image_id: 'str', actual_package_identity: 'str') -> 'None'\"}]},\"new_image\":\"8020-demonov-shadow:76e1f58baa544a0e208bba317f0150e5b6d74dbd\",\"new_image_id\":\"sha256:341f71a906c91d3fa9c1d50bd89df2a849dfb0b168bb4551efb444bf61c4ab99\",\"policy\":{\"close_timeout\":5,\"open_timeout\":10,\"ping_interval\":20,\"ping_timeout\":60},\"runtime_activated\":false,\"service_identity_unchanged\":true,\"stage_path\":\"/opt/stacks/8020-demonov-shadow/.deploy/SIGNALBOT-260908-DEPLOY-STAGE-042FIX5\",\"websockets_version\":\"15.0.1\"}}\nSB043KF4_CANONICAL_VALIDATION={\"computed\":\"ab8a8d8f361c0c86f709fe84f27ba4378f9978fb802cbfb2a542ea9833ad48df\",\"expected\":\"58d3b59200bdc0eb8d448612679d667b194263586cb3198dfc08597935053a1a\",\"ok\":false,\"spec\":{\"class_fields\":[\"runtime_manifest_id\",\"code_identity\",\"package_identity\",\"application_image_id\",\"base_image_digest\",\"compose_hash\",\"finality_policy_id\"],\"kind\":\"dict\",\"parts\":[{\"key\":\"schema\",\"kind\":\"const\",\"value\":\"CR0116_RUNTIME_DEPLOYMENT_MANIFEST_V1\"},{\"key\":\"code_identity\",\"kind\":\"field\",\"value\":\"code_identity\"},{\"key\":\"package_identity\",\"kind\":\"field\",\"value\":\"package_identity\"},{\"key\":\"application_image_id\",\"kind\":\"field\",\"value\":\"application_image_id\"},{\"key\":\"base_image_digest\",\"kind\":\"field\",\"value\":\"base_image_digest\"},{\"key\":\"compose_hash\",\"kind\":\"field\",\"value\":\"compose_hash\"},{\"key\":\"finality_policy_id\",\"kind\":\"field\",\"value\":\"finality_policy_id\"}],\"source\":\"def content_hash(self) -> str:\\n return canonical_hash(\\n {\\n \\\"schema\\\": \\\"CR0116_RUNTIME_DEPLOYMENT_MANIFEST_V1\\\",\\n \\\"code_identity\\\": self.code_identity,\\n \\\"package_identity\\\": self.package_identity,\\n \\\"application_image_id\\\": self.application_image_id,\\n \\\"base_image_digest\\\": self.base_image_digest,\\n \\\"compose_hash\\\": self.compose_hash,\\n \\\"finality_policy_id\\\": self.finality_policy_id,\\n }\\n )\"}}\nSB043KF4_ARTIFACT_SPEC={\"class_fields\":[\"artifact_id\",\"source_tree_hash\",\"dependency_lock_hash\",\"build_recipe_hash\",\"executable_artifact_hash\",\"runtime_environment_hash\"],\"fields\":[\"source_tree_hash\",\"dependency_lock_hash\",\"build_recipe_hash\",\"executable_artifact_hash\",\"runtime_environment_hash\"],\"kind\":\"dictcomp\",\"source\":\"def content_hash(self) -> str:\\n return canonical_hash({k: getattr(self, k) for k in (\\n \\\"source_tree_hash\\\", \\\"dependency_lock_hash\\\", \\\"build_recipe_hash\\\", \\\"executable_artifact_hash\\\", \\\"runtime_environment_hash\\\"\\n )})\"}\nSB043KF4_CALLSITES=[{\"name\":\"RuntimeArtifactManifest\",\"node\":\"ClassDef\",\"path\":\"runtime_integrity.py\",\"score\":5,\"source\":\"class RuntimeArtifactManifest:\\n artifact_id: str\\n source_tree_hash: str\\n dependency_lock_hash: str\\n build_recipe_hash: str\\n executable_artifact_hash: str\\n runtime_environment_hash: str\\n\\n def __post_init__(self) -> None:\\n for name in (\\\"source_tree_hash\\\", \\\"dependency_lock_hash\\\", \\\"build_recipe_hash\\\", \\\"executable_artifact_hash\\\", \\\"runtime_environment_hash\\\"):\\n _hex64(getattr(self, name), name)\\n if self.artifact_id != self.content_hash:\\n raise DemonovError(\\\"runtime artifact id must be content-addressed\\\")\\n\\n @property\\n def content_hash(self) -> str:\\n return canonical_hash({k: getattr(self, k) for k in (\\n \\\"source_tree_hash\\\", \\\"dependency_lock_hash\\\", \\\"build_recipe_hash\\\", \\\"executable_artifact_hash\\\", \\\"runtime_environment_hash\\\"\\n )})\\n\\n @classmethod\\n def build(cls, **kwargs) -> \\\"RuntimeArtifactManifest\\\":\\n payload = {k: kwargs[k] for k in (\\\"source_tree_hash\\\", \\\"dependency_lock_hash\\\", \\\"build_recipe_hash\\\", \\\"executable_artifact_hash\\\", \\\"runtime_environment_hash\\\")}\\n return cls(artifact_id=canonical_hash(payload), **kwargs)\",\"source_sha256\":\"8cd1ebf70c003cd4d7b91f18602841426b19c2f54f2d803309c53c22f38cd5c7\"},{\"name\":\"DeploymentAttestation\",\"node\":\"ClassDef\",\"path\":\"runtime_integrity.py\",\"score\":4,\"source\":\"class DeploymentAttestation:\\n attestation_id: str\\n subject_kind: DeploymentSubjectKind\\n subject_id: str\\n actor_id: str\\n source_tree_hash: str\\n dependency_lock_hash: str\\n executable_artifact_hash: str\\n runtime_environment_hash: str\\n effective_config_hash: str\\n deployed_at: datetime\\n issued_at: datetime\\n authority_provider_id: str\\n authority_receipt_hash: str\\n feature_extractor_code_hash: str = \\\"\\\"\\n\\n def __post_init__(self) -> None:\\n _aware(self.deployed_at, \\\"deployment deployed_at\\\"); _aware(self.issued_at, \\\"deployment issued_at\\\")\\n if self.issued_at < self.deployed_at:\\n raise DemonovError(\\\"deployment attestation cannot be issued before deployment\\\")\\n if not self.actor_id or not self.authority_provider_id:\\n raise DemonovError(\\\"deployment attestation identity fields are required\\\")\\n for n in (\\\"subject_id\\\",\\\"source_tree_hash\\\",\\\"dependency_lock_hash\\\",\\\"executable_artifact_hash\\\",\\n \\\"runtime_environment_hash\\\",\\\"effective_config_hash\\\",\\\"authority_receipt_hash\\\"):\\n _hex64(getattr(self,n),n)\\n if self.feature_extractor_code_hash: _hex64(self.feature_extractor_code_hash,\\\"feature_extractor_code_hash\\\")\\n if self.attestation_id != self.content_hash:\\n raise DemonovError(\\\"deployment attestation id must be content-addressed\\\")\\n\\n @property\\n def content_hash(self) -> str:\\n payload={\\\"subject_kind\\\":self.subject_kind,\\\"subject_id\\\":self.subject_id,\\\"actor_id\\\":self.actor_id,\\n \\\"source_tree_hash\\\":self.source_tree_hash,\\\"dependency_lock_hash\\\":self.dependency_lock_hash,\\n \\\"executable_artifact_hash\\\":self.executable_artifact_hash,\\\"runtime_environment_hash\\\":self.runtime_environment_hash,\\n \\\"effective_config_hash\\\":self.effective_config_hash,\\\"deployed_at\\\":self.deployed_at,\\\"issued_at\\\":self.issued_at,\\n \\\"authority_provider_id\\\":self.authority_provider_id,\\\"authority_receipt_hash\\\":self.authority_receipt_hash}\\n if self.feature_extractor_code_hash: payload[\\\"feature_extractor_code_hash\\\"]=self.feature_extractor_code_hash\\n return canonical_hash(payload)\\n\\n @classmethod\\n def build(cls, **kwargs) -> \\\"DeploymentAttestation\\\":\\n payload={k:kwargs[k] for k in (\\\"subject_kind\\\",\\\"subject_id\\\",\\\"actor_id\\\",\\\"source_tree_hash\\\",\\\"dependency_lock_hash\\\",\\n \\\"executable_artifact_hash\\\",\\\"runtime_environment_hash\\\",\\\"effective_config_hash\\\",\\\"deployed_at\\\",\\\"issued_at\\\",\\n \\\"authority_provider_id\\\",\\\"authority_receipt_hash\\\")}\\n if kwargs.get(\\\"feature_extractor_code_hash\\\"): payload[\\\"feature_extractor_code_hash\\\"]=kwargs[\\\"feature_extractor_code_hash\\\"]\\n return cls(attestation_id=canonical_hash(payload),**kwargs)\",\"source_sha256\":\"1b0ca84bb035dfb7ff441ce73bbe8ee5f140a07b4a821f5a9f55770b585ffac7\"},{\"name\":\"verify_final_deployment_attestations\",\"node\":\"FunctionDef\",\"path\":\"runtime_integrity.py\",\"score\":4,\"source\":\"def verify_final_deployment_attestations(*, runtime_registry: RuntimeRegistry, producer_id: str,\\n predictor_runtime: \\\"PredictorRuntimeManifest\\\",\\n attestations: Sequence[DeploymentAttestation],\\n policy: DeploymentAttestationPolicy,\\n collection_start_at: datetime,\\n receipt_verifier: Callable[[DeploymentAttestation], bool]) -> None:\\n _aware(collection_start_at,\\\"collection_start_at\\\")\\n relevant_epochs=[e for e in runtime_registry.epochs if e.producer_id==producer_id and e.contains(collection_start_at)]\\n if len(relevant_epochs)!=1:\\n raise DemonovError(\\\"collection start must resolve to exactly one producer runtime epoch for deployment proof\\\")\\n epoch=relevant_epochs[0]\\n artifact=next((a for a in runtime_registry.artifacts if a.artifact_id==epoch.artifact_id),None)\\n if artifact is None: raise DemonovError(\\\"producer deployment proof references missing runtime artifact\\\")\\n expected={\\n (DeploymentSubjectKind.PRODUCER_RUNTIME_EPOCH,epoch.epoch_id):(\\n producer_id,artifact.source_tree_hash,artifact.dependency_lock_hash,artifact.executable_artifact_hash,\\n artifact.runtime_environment_hash,epoch.config_id,\\\"\\\",epoch.started_at),\\n (DeploymentSubjectKind.PREDICTOR_RUNTIME,predictor_runtime.predictor_runtime_id):(\\n predictor_runtime.consumer_id,predictor_runtime.source_tree_hash,predictor_runtime.dependency_lock_hash,\\n predictor_runtime.executable_artifact_hash,predictor_runtime.runtime_environment_hash,predictor_runtime.effective_config_hash,\\n predictor_runtime.feature_extractor_code_hash,predictor_runtime.started_at),\\n }\\n amap={(a.subject_kind,a.subject_id):a for a in attestations}\\n if len(amap)!=len(attestations): raise DemonovError(\\\"duplicate deployment attestation subject\\\")\\n if set(amap)!=set(expected): raise DemonovError(\\\"deployment attestation set differs from exact producer/predictor subjects\\\")\\n for key,(actor,src,dep,exe,env,cfg,feature,start) in expected.items():\\n a=amap[key]\\n if a.authority_provider_id!=policy.authority_provider_id or not receipt_verifier(a):\\n raise DemonovError(\\\"deployment attestation authority verification failed\\\")\\n if (a.actor_id,a.source_tree_hash,a.dependency_lock_hash,a.executable_artifact_hash,a.runtime_environment_hash,a.effective_config_hash)!=(actor,src,dep,exe,env,cfg):\\n raise DemonovError(\\\"deployment attestation artifact/config identity mismatch\\\")\\n if key[0] is DeploymentSubjectKind.PREDICTOR_RUNTIME and a.feature_extractor_code_hash!=feature:\\n raise DemonovError(\\\"predictor deployment attestation feature extractor mismatch\\\")\\n if key[0] is DeploymentSubjectKind.PRODUCER_RUNTIME_EPOCH and a.feature_extractor_code_hash:\\n raise DemonovError(\\\"producer deployment attestation must not invent challenger feature extractor\\\")\\n if abs((a.deployed_at-start).total_seconds()) > policy.max_start_skew_seconds:\\n raise DemonovError(\\\"deployment attestation outside frozen runtime-start skew\\\")\\n if policy.require_precollection_attestation and a.issued_at >= collection_start_at:\\n raise DemonovError(\\\"deployment attestation must be externally issued strictly before collection start\\\")\",\"source_sha256\":\"9fc5b8d032d6a7b31f6b4e21d15142fe20b1fde4fe30a06fb758ad264bdeac9d\"},{\"name\":\"PredictorRuntimeManifest\",\"node\":\"ClassDef\",\"path\":\"runtime_integrity.py\",\"score\":4,\"source\":\"class PredictorRuntimeManifest:\\n predictor_runtime_id: str\\n consumer_id: str\\n source_tree_hash: str\\n dependency_lock_hash: str\\n runtime_environment_hash: str\\n executable_artifact_hash: str\\n effective_config_hash: str\\n final_model_registry_id: str\\n feature_extractor_code_hash: str\\n started_at: datetime\\n ended_at: datetime | None\\n deployment_receipt_hash: str\\n\\n def __post_init__(self) -> None:\\n _aware(self.started_at, \\\"predictor runtime started_at\\\")\\n if self.ended_at is not None:\\n _aware(self.ended_at, \\\"predictor runtime ended_at\\\")\\n if self.ended_at <= self.started_at: raise DemonovError(\\\"predictor runtime ended_at must follow started_at\\\")\\n if not self.consumer_id or not self.final_model_registry_id:\\n raise DemonovError(\\\"predictor runtime identity fields required\\\")\\n for n in (\\\"source_tree_hash\\\",\\\"dependency_lock_hash\\\",\\\"runtime_environment_hash\\\",\\\"executable_artifact_hash\\\",\\\"effective_config_hash\\\",\\\"feature_extractor_code_hash\\\",\\\"deployment_receipt_hash\\\"):\\n _hex64(getattr(self,n),n)\\n if self.predictor_runtime_id != self.content_hash:\\n raise DemonovError(\\\"predictor runtime id must be content-addressed\\\")\\n\\n @property\\n def content_hash(self) -> str:\\n return canonical_hash({k:getattr(self,k) for k in (\\n \\\"consumer_id\\\",\\\"source_tree_hash\\\",\\\"dependency_lock_hash\\\",\\\"runtime_environment_hash\\\",\\\"executable_artifact_hash\\\",\\\"effective_config_hash\\\",\\n \\\"final_model_registry_id\\\",\\\"feature_extractor_code_hash\\\",\\\"started_at\\\",\\\"ended_at\\\",\\\"deployment_receipt_hash\\\"\\n )})\\n\\n @classmethod\\n def build(cls, **kwargs) -> \\\"PredictorRuntimeManifest\\\":\\n payload={k:kwargs.get(k) for k in (\\n \\\"consumer_id\\\",\\\"source_tree_hash\\\",\\\"dependency_lock_hash\\\",\\\"runtime_environment_hash\\\",\\\"executable_artifact_hash\\\",\\\"effective_config_hash\\\",\\n \\\"final_model_registry_id\\\",\\\"feature_extractor_code_hash\\\",\\\"started_at\\\",\\\"ended_at\\\",\\\"deployment_receipt_hash\\\"\\n )}\\n return cls(predictor_runtime_id=canonical_hash(payload),**kwargs)\\n\\n def contains(self,t:datetime)->bool:\\n _aware(t,\\\"predictor runtime time\\\")\\n return self.started_at <= t and (self.ended_at is None or t < self.ended_at)\",\"source_sha256\":\"6514f10948abbeb0030fb06b509a8a75d796eea4d6ef4101b72e20f688985454\"},{\"name\":\"required_retained_hashes\",\"node\":\"FunctionDef\",\"path\":\"runtime_integrity.py\",\"score\":5,\"source\":\"def required_retained_hashes(*, runtime_registry:RuntimeRegistry, model_registry:FinalModelRegistry,\\n state_snapshots:Sequence[StateSnapshotManifest]=(), predictor_runtime:PredictorRuntimeManifest | None=None)->Mapping[str,RetainedArtifactKind]:\\n \\\"\\\"\\\"Build the minimum blob-level replay corpus required by final prospective proof.\\n\\n Content-addressed policy objects remain in the handoff/registry. This function\\n targets blobs that cannot be reconstructed from identifiers alone.\\n \\\"\\\"\\\"\\n out:dict[str,RetainedArtifactKind]={}\\n for a in runtime_registry.artifacts:\\n out[a.source_tree_hash]=RetainedArtifactKind.SOURCE_TREE\\n out[a.dependency_lock_hash]=RetainedArtifactKind.DEPENDENCY_LOCK\\n out[a.build_recipe_hash]=RetainedArtifactKind.BUILD_RECIPE\\n out[a.executable_artifact_hash]=RetainedArtifactKind.EXECUTABLE_ARTIFACT\\n out[a.runtime_environment_hash]=RetainedArtifactKind.RUNTIME_ENVIRONMENT\\n for a in model_registry.artifacts:\\n out[a.model_freeze_hash]=RetainedArtifactKind.MODEL\\n out[a.transformer_freeze_hash]=RetainedArtifactKind.TRANSFORMER\\n out[a.calibration_freeze_hash]=RetainedArtifactKind.CALIBRATOR\\n out[a.feature_schema_hash]=RetainedArtifactKind.FEATURE_SCHEMA\\n out[a.feature_contract_hash]=RetainedArtifactKind.FEATURE_CONTRACT\\n out[a.training_data_hash]=RetainedArtifactKind.TRAINING_DATA\\n for s in state_snapshots:\\n out[s.serialized_state_hash]=RetainedArtifactKind.STATE_SNAPSHOT\\n if predictor_runtime is not None:\\n out[predictor_runtime.source_tree_hash]=RetainedArtifactKind.SOURCE_TREE\\n out[predictor_runtime.dependency_lock_hash]=RetainedArtifactKind.DEPENDENCY_LOCK\\n out[predictor_runtime.executable_artifact_hash]=RetainedArtifactKind.EXECUTABLE_ARTIFACT\\n out[predictor_runtime.runtime_environment_hash]=RetainedArtifactKind.RUNTIME_ENVIRONMENT\\n out[predictor_runtime.feature_extractor_code_hash]=RetainedArtifactKind.FEATURE_EXTRACTOR_CODE\\n out[predictor_runtime.effective_config_hash]=RetainedArtifactKind.EFFECTIVE_CONFIG\\n return out\",\"source_sha256\":\"ac6e18e76fcbd23144d1870ac03e3c2a4a1149dc4b5cf3f84fc0664228cdab1a\"}]\nSB043KF4_REPRODUCTION={\"derived_new_package_identities\":[],\"fields\":[\"source_tree_hash\",\"dependency_lock_hash\",\"build_recipe_hash\",\"executable_artifact_hash\",\"runtime_environment_hash\"],\"group_sizes\":{\"build_recipe_hash\":10,\"dependency_lock_hash\":9,\"executable_artifact_hash\":14,\"runtime_environment_hash\":13,\"source_tree_hash\":71},\"matches\":[],\"ok\":false,\"reason\":\"NO_MATCH\",\"tested\":1162980,\"total\":1162980}\nSB043KF4_HOLDS=[\"CANONICAL_HASH_NOT_VALIDATED\",\"OLD_PACKAGE_ID_EXACT_REPRODUCTION_FAILED\"]\nSB043KF4_DECISION=HOLD_CANONICAL_HASH_NOT_VALIDATED__OLD_PACKAGE_ID_EXACT_REPRODUCTION_FAILED\nSB043KF4_MUTATION_SCOPE=NONE_READ_ONLY\nSIGNALBOT043KF4_DECISION=HOLD_GUEST_PACKAGE_REPRODUCTION\n" + "output": "VPS_FRONTEND_DISCOVERY6_BEGIN=true\nCOMMAND_ID=NEWFI-260908-A-STAGINGEDGE-VPS-FRONTEND-DISCOVERY6\nMODE=read-only\nMUTATIONS_PERFORMED=NO\nERROR_REGISTER_CHECK=OK\nERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0\nREFERENCE_CHECK=OK\nREFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66\nAUTHORITY_CHECK_SCOPE=READABILITY_ONLY_INCIDENTS_NOT_CLOSED\nRUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d\nRUNNER_SHA_GATE=PASS\nPRIOR_APPLY14_GATE=PASS\nPRIOR_APPLY14_HISTORY_SHA256=774ea70f504928e020983f4da32ac2d4248f158e5f6a275768ebb6c59275235a\nPRIOR_APPLY14_BLOCKER=STRONG_PUBLIC_INGRESS_COUNT_NOT_ONE:0\nOPERATIONAL_VPS_FRONTEND_REFERENCES={\"count\":1600,\"rows\":[{\"line\":2,\"path\":\"/etc/systemd/system/homelab-vps-identity-audit.timer\",\"text\":\"Description=Run Homelab VPS SSH identity audit\"},{\"line\":2,\"path\":\"/etc/systemd/system/homelab-vps-identity-audit.service\",\"text\":\"Description=Homelab VPS SSH identity audit\"},{\"line\":6,\"path\":\"/etc/systemd/system/homelab-vps-identity-audit.service\",\"text\":\"ExecStart=/usr/local/sbin/homelab-vps-identity-audit\"},{\"line\":2,\"path\":\"/etc/systemd/system/timers.target.wants/homelab-vps-identity-audit.timer\",\"text\":\"Description=Run Homelab VPS SSH identity audit\"},{\"line\":3,\"path\":\"/etc/systemd/system/netbird-vps-backup.service.d/10-superseded-noop.conf\",\"text\":\"ExecStart=/usr/local/sbin/homelab-superseded-unit-ok netbird-vps-backup.service superseded_by_current_appbackup_or_trust_proof\"},{\"line\":9,\"path\":\"/usr/local/sbin/homelab-external-probe-vps\",\"text\":\"grep -q \\\"^STATUS=OK\\\" /var/lib/homelab-health/netbird-vps-trust-clean-seal.txt 2>/dev/null || BAD=$((BAD+1))\"},{\"line\":10,\"path\":\"/usr/local/sbin/homelab-external-probe-vps\",\"text\":\"grep -q \\\"^STATUS=OK\\\" /var/lib/homelab-health/netbird-vps-trust-closure.txt 2>/dev/null || BAD=$((BAD+1))\"},{\"line\":12,\"path\":\"/usr/local/sbin/homelab-external-probe-vps\",\"text\":\"grep -q \\\"TRUSTED_NETBIRD_IDENTITY=edge-vm\\\" /var/lib/homelab-health/netbird-vps-trust-clean-seal.txt 2>/dev/null || BAD=$((BAD+1))\"},{\"line\":13,\"path\":\"/usr/local/sbin/homelab-external-probe-vps\",\"text\":\"grep -q \\\"PUBLIC_VPS_DECISION=REJECTED_HOSTKEY_MISMATCH\\\" /var/lib/homelab-health/netbird-vps-trust-clean-seal.txt 2>/dev/null || BAD=$((BAD+1))\"},{\"line\":6,\"path\":\"/usr/local/sbin/homelab-vps-identity-audit\",\"text\":\"H=\\\"/var/lib/homelab-health/vps-identity-audit.txt\\\"\"},{\"line\":23,\"path\":\"/usr/local/sbin/homelab-vps-identity-audit\",\"text\":\"backup=\\\"$(find /mnt/staging/netbird-vps-backups/snapshots -maxdepth 2 -type f -name 'netbird-vps-backup.tgz' 2>/dev/null | sort | tail -n1 || true)\\\"\"},{\"line\":45,\"path\":\"/usr/local/sbin/homelab-vps-identity-audit\",\"text\":\"printf 'STATUS=%s TS=%s TYPE=vps-identity-audit VPS_IP=%s ALIAS=%s ALIAS_IP=%s SSH_TRUST=%s BACKUP_HOSTKEYS=%s KNOWN_HOSTS_UNCHANGED=YES SECRET_PRINTED=REDACTED\\\\n' \\\\\"},{\"line\":13,\"path\":\"/usr/local/sbin/homelab-external-probe-vps-health\",\"text\":\"grep -q \\\"^STATUS=OK\\\" /var/lib/homelab-health/netbird-vps-trust-clean-seal.txt 2>/dev/null || BAD=$((BAD+1))\"},{\"line\":14,\"path\":\"/usr/local/sbin/homelab-external-probe-vps-health\",\"text\":\"grep -q \\\"^STATUS=OK\\\" /var/lib/homelab-health/netbird-vps-trust-closure.txt 2>/dev/null || BAD=$((BAD+1))\"},{\"line\":16,\"path\":\"/usr/local/sbin/homelab-external-probe-vps-health\",\"text\":\"grep -q \\\"TRUSTED_NETBIRD_IDENTITY=edge-vm\\\" /var/lib/homelab-health/netbird-vps-trust-clean-seal.txt 2>/dev/null || BAD=$((BAD+1))\"},{\"line\":17,\"path\":\"/usr/local/sbin/homelab-external-probe-vps-health\",\"text\":\"grep -q \\\"PUBLIC_VPS_DECISION=REJECTED_HOSTKEY_MISMATCH\\\" /var/lib/homelab-health/netbird-vps-trust-clean-seal.txt 2>/dev/null || BAD=$((BAD+1))\"},{\"line\":29,\"path\":\"/srv/homelab-ops/KB_START_HERE.md\",\"text\":\"- Не печатать пароли, PAT, токены, TOTP, private SSH keys.\"},{\"line\":116,\"path\":\"/srv/homelab-ops/observed/current-context.json\",\"text\":\"\\\"remote\\\": \\\"https://git.gram1.ru/homelab-admin/homelab-ops.git\\\",\"},{\"line\":1,\"path\":\"/srv/homelab-ops/observed/source-snapshots/overall.txt\",\"text\":\"STATUS=WARN TS=2026-08-19T05:04:50Z TYPE=overall-health BAD=13 backup-framework.txt=FAIL drift-check.txt=WARN service-registry.txt=OK dependency-map.txt=OK golden-state.txt=OK cluster-passport.txt=WARN final-readiness.txt=WARN safe-autoheal.txt=OK kuma-monitor-policy.txt=OK backup-sla.txt=FAIL backup-coverage-matrix.txt=FAIL extended-appbackup.txt=FAIL residual-review.txt=OK forum-snuffleupagus.txt=OK incident-journal.txt=OK duty-admin-v2.txt=OK node-loss-readiness.txt=OK node-loss-runbooks.txt=OK power-loss-readiness.txt=OK power-loss-runbook.txt=OK ungated-health-backlog.txt=OK pve03-root-cleanup.txt=OK pve03-staging-retention.txt=OK pve03-staging-retention-proof.txt=OK pve03-staging-retention-cleanup.txt=OK pve03-failed-unit-cleanup.txt=OK remote-git-sops-age-readiness.txt=OK remote-git-sops-age-policy.txt=OK desired-state-remote-target-trace.txt=OK desired-state-remote-target.txt=OK desired-state.txt=OK runbooks.txt=OK alerting.txt=OK secret-exposure.txt=OK capacity-risk.txt=OK vm-local-dumps-retention.txt=FAIL vm-local-dumps-cloud.txt=FAIL vm-backup-policy.txt=FAIL vm170-vm171-full-strategy.txt=OK external-probe-vps.txt=OK netbird-vps-trust.txt=OK netbird-vps-trust-closure.txt=OK external-probe-runner-decision.txt=OK netbird-vps-trust-clean-seal.txt=OK live-tail-audit.txt=OK vm-backup.txt=OK cluster-admin-observer.txt=WARN cluster-admin-restricted-probes.txt=WARN cluster-admin-full-observer.txt=WARN cluster-admin-vm-mail-cloud-backup.txt=OK cluster-admin-webpanel-sync.txt=OK cluster-admin-webpanel.txt=OK\"},{\"line\":162,\"path\":\"/srv/homelab-ops/changes/CR-2026-0018/design.md\",\"text\":\"- не вызывается через chat wrapper, SSH forced command или remote API;\"},{\"line\":29,\"path\":\"/srv/homelab-ops/changes/CR-2026-0009/plan.json\",\"text\":\"\\\"overbroad SSH pipeline regex\\\",\"},{\"line\":157,\"path\":\"/srv/homelab-ops/errors/regression-cases.json\",\"text\":\"\\\"required_control\\\": \\\"Every active Skladchik SSH caller and its desired-state copy must be versioned, deployed and verified with StrictHostKeyChecking=yes and the canonical known_hosts file.\\\",\"},{\"line\":222,\"path\":\"/srv/homelab-ops/errors/regression-cases.json\",\"text\":\"\\\"incident\\\": \\\"A read-only diagnostic passed regular-expression metacharacters as direct SSH remote arguments, allowing the remote login shell to reinterpret them and produce syntax and command-not-found errors.\\\",\"},{\"line\":223,\"path\":\"/srv/homelab-ops/errors/regression-cases.json\",\"text\":\"\\\"required_control\\\": \\\"Remote regex, pipelines and shell metacharacters must live in versioned remote scripts transferred over stdin; direct SSH argv is limited to literal commands and paths.\\\",\"},{\"line\":234,\"path\":\"/srv/homelab-ops/errors/regression-cases.json\",\"text\":\"\\\"incident\\\": \\\"An overbroad SSH static test classified a safe local pipeline consuming SSH stdout as a forbidden remote-shell pipeline.\\\",\"},{\"line\":235,\"path\":\"/srv/homelab-ops/errors/regression-cases.json\",\"text\":\"\\\"required_control\\\": \\\"SSH safety tests must match direct remote grep or pgrep execution precisely and must not reject local post-processing pipelines.\\\",\"},{\"line\":125,\"path\":\"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/task.json\",\"text\":\"\\\"compile\\\": \\\"NoCloud-delivered versioned guest-provision.sh; no first-boot SSH host-key trust is required\\\"\"},{\"line\":159,\"path\":\"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/task.json\",\"text\":\"\\\"snikket_domain\\\": \\\"chat.gram1.ru\\\",\"},{\"line\":3,\"path\":\"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/README.txt\",\"text\":\"The task carries split-DNS host configuration, public DNS for snikket_server, TURN NAT mapping, certificate permission reconciliation and recovery SSH key.\"},{\"line\":19,\"path\":\"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/assets/edge-readonly.py\",\"text\":\"if any(x in blob for x in ('nc.gram1.ru','chat.gram1.ru','groups.chat.gram1.ru','share.chat.gram1.ru','[PRIVATE_IP]')):\"},{\"line\":1,\"path\":\"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/assets/snikket.conf\",\"text\":\"SNIKKET_DOMAIN=chat.gram1.ru\"},{\"line\":8,\"path\":\"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/assets/guest-provision.sh\",\"text\":\"export DEBIAN_FRONTEND=noninteractive\"},{\"line\":61,\"path\":\"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/assets/vm150-runtime.py\",\"text\":\"DOMAINS=['chat.gram1.ru','groups.chat.gram1.ru','share.chat.gram1.ru']\"},{\"line\":710,\"path\":\"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/assets/vm150-runtime.py\",\"text\":\"rc,code=curl_edge('chat.gram1.ru','/.well-known/host-meta')\"},{\"line\":752,\"path\":\"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/assets/vm150-runtime.py\",\"text\":\"obj={'schema':'snikket-vm150-seal-v2','status':'SEALED','task_id':TASK_ID,'change_id':CFG['change_id'],'source_head':source_head,'sealed_at_utc':now(),'vmid':150,'domain':'chat.gram1.ru','pre_admin_generation':pre_admin_gen,'new_generation':gen,'new_backup_restore_proven':True,'final_post_admin_backup':True,'old_generation_preserved':OLD_GEN,'mobile_av_test':'PASS','wan_5269_forwarded':False,'admin_account_created':True,'family_user_role':'LIMITED_RECOMMENDED','invite_logged':False}\"},{\"line\":9,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/verify.sh\",\"text\":\"pve03_versioned(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] bash -s -- \\\"$@\\\" < \\\"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\\\"; }\"},{\"line\":10,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/verify.sh\",\"text\":\"pve03_installed(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \\\"$@\\\"; }\"},{\"line\":34,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/verify.sh\",\"text\":\"INSTALLED_WORKER_SHA=\\\"$(ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] /usr/bin/sha256sum /usr/local/libexec/homelab-vm160-cloud-quorum-worker | awk '{print $1}')\\\"\"},{\"line\":9,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/apply.sh\",\"text\":\"pve03_versioned(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] bash -s -- \\\"$@\\\" < \\\"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\\\"; }\"},{\"line\":10,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/apply.sh\",\"text\":\"pve03_installed(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \\\"$@\\\"; }\"},{\"line\":20,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/apply.sh\",\"text\":\"ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] install -m 0755 /tmp/homelab-vm160-cloud-quorum-worker /usr/local/libexec/homelab-vm160-cloud-quorum-worker\"},{\"line\":21,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/apply.sh\",\"text\":\"ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] rm -f /tmp/homelab-vm160-cloud-quorum-worker\"},{\"line\":22,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/apply.sh\",\"text\":\"INSTALLED_WORKER_SHA=\\\"$(ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] /usr/bin/sha256sum /usr/local/libexec/homelab-vm160-cloud-quorum-worker | awk '{print $1}')\\\"\"},{\"line\":9,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/dry-run.sh\",\"text\":\"pve03_versioned(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] bash -s -- \\\"$@\\\" < \\\"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\\\"; }\"},{\"line\":10,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/dry-run.sh\",\"text\":\"pve03_installed(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \\\"$@\\\"; }\"},{\"line\":9,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/preflight.sh\",\"text\":\"pve02_probe(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] bash -s -- \\\"$@\\\" < \\\"$HOMELAB_TASK_DIR/assets/pve02-vm160-preflight-probe.sh\\\"; }\"},{\"line\":10,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/preflight.sh\",\"text\":\"pve03_versioned(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] bash -s -- \\\"$@\\\" < \\\"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\\\"; }\"},{\"line\":11,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/preflight.sh\",\"text\":\"pve03_installed(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \\\"$@\\\"; }\"},{\"line\":9,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/seal.sh\",\"text\":\"pve03_versioned(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] bash -s -- \\\"$@\\\" < \\\"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\\\"; }\"},{\"line\":10,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/seal.sh\",\"text\":\"pve03_installed(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \\\"$@\\\"; }\"},{\"line\":9,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/prepare.sh\",\"text\":\"pve03_versioned(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] bash -s -- \\\"$@\\\" < \\\"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\\\"; }\"},{\"line\":10,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/prepare.sh\",\"text\":\"pve03_installed(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \\\"$@\\\"; }\"},{\"line\":9,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh\",\"text\":\"pve03_versioned(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] bash -s -- \\\"$@\\\" < \\\"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\\\"; }\"},{\"line\":10,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh\",\"text\":\"pve03_installed(){ ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \\\"$@\\\"; }\"},{\"line\":17,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh\",\"text\":\"ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] test -d /mnt/staging/vzdump/vm160-pve02-20260717T223819Z\"},{\"line\":21,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh\",\"text\":\"if ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] test -x /usr/local/libexec/homelab-vm160-cloud-quorum-worker; then pve03_installed restore vm160-pve02-20260717T223819Z; else pve03_versioned restore vm160-pve02-20260717T223819Z; fi\"},{\"line\":23,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh\",\"text\":\"ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] test -s /mnt/staging/vzdump/vm160-pve02-20260717T223819Z/vzdump-qemu-160-20260717T223819Z.vma.zst\"},{\"line\":24,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh\",\"text\":\"test \\\"$(ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] stat -c %s /mnt/staging/vzdump/vm160-pve02-20260717T223819Z/vzdump-qemu-160-20260717T223819Z.vma.zst)\\\" = 84995216465\"},{\"line\":25,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh\",\"text\":\"test \\\"$(ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] sha256sum /mnt/staging/vzdump/vm160-pve02-20260717T223819Z/vzdump-qemu-160-20260717T223819Z.vma.zst | awk '{print $1}')\\\" = 257e10821e62e3e2f9318b238a5302a6db601dd597bfa3e0d77aba07f3b85e72\"},{\"line\":26,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh\",\"text\":\"test \\\"$(ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] readlink /mnt/staging/vzdump/vm160-pve02-latest)\\\" = vm160-pve02-20260717T223819Z\"},{\"line\":29,\"path\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh\",\"text\":\"ssh \\\"${SSH_BASE[@]}\\\" root@[PRIVATE_IP] rm -f /usr/local/libexec/homelab-vm160-cloud-quorum-worker\"},{\"line\":23,\"path\":\"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/verify.sh\",\"text\":\"test \\\"$(ssh \\\"${SSH[@]}\\\" \\\"$EDGE\\\" sudo -n /usr/bin/sha256sum \\\"$EDGE_SCRIPT\\\" | awk 'NR==1{print $1}')\\\" = \\\"$EXPECTED_SHA\\\"\"},{\"line\":24,\"path\":\"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/verify.sh\",\"text\":\"RESULT=\\\"$(ssh \\\"${SSH[@]}\\\" \\\"$EDGE\\\" sudo -n /bin/bash -s -- \\\"$EXPECTED_SHA\\\" < \\\"$HOMELAB_TASK_DIR/assets/edge-verify.sh\\\")\\\"\"},{\"line\":26,\"path\":\"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/apply.sh\",\"text\":\"ssh \\\"${SSH[@]}\\\" \\\"$EDGE\\\" sudo -n /usr/bin/install -m 0755 -o root -g root /tmp/skladchik-reports-monitor-cookie-update-edge.cr7 \\\"$EDGE_SCRIPT\\\"\"},{\"line\":27,\"path\":\"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/apply.sh\",\"text\":\"ssh \\\"${SSH[@]}\\\" \\\"$EDGE\\\" /usr/bin/rm -f /tmp/skladchik-reports-monitor-cookie-update-edge.cr7\"},{\"line\":28,\"path\":\"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/apply.sh\",\"text\":\"test \\\"$(ssh \\\"${SSH[@]}\\\" \\\"$EDGE\\\" sudo -n /usr/bin/sha256sum \\\"$EDGE_SCRIPT\\\" | awk 'NR==1{print $1}')\\\" = \\\"$EXPECTED_SHA\\\"\"},{\"line\":30,\"path\":\"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/apply.sh\",\"text\":\"ssh -tt \\\"${SSH[@]}\\\" \\\"$EDGE\\\" sudo -n /usr/local/sbin/skladchik-reports-monitor-cookie-update-edge\"},{\"line\":35,\"path\":\"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/apply.sh\",\"text\":\"if ! git -C /srv/homelab-desired-state diff --cached --quiet; then git -C /srv/homelab-desired-state commit -m 'CR-2026-0007 enforce strict Skladchik SSH and controlled reauth'; fi\"},{\"line\":32,\"path\":\"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/dry-run.sh\",\"text\":\"RESULT=\\\"$(ssh \\\"${SSH[@]}\\\" \\\"$EDGE\\\" sudo -n /bin/bash -s -- \\\"$EDGE_STAGE\\\" \\\"$CURRENT_EDGE_SHA\\\" < \\\"$HOMELAB_TASK_DIR/assets/edge-dry-run.sh\\\")\\\"\"},{\"line\":20,\"path\":\"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/preflight.sh\",\"text\":\"RESULT=\\\"$(ssh \\\"${SSH[@]}\\\" \\\"$EDGE\\\" sudo -n /bin/bash -s -- \\\"$CURRENT_EDGE_SHA\\\" < \\\"$HOMELAB_TASK_DIR/assets/edge-preflight.sh\\\")\\\"\"},{\"line\":16,\"path\":\"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/seal.sh\",\"text\":\"RESULT=\\\"$(ssh \\\"${SSH[@]}\\\" \\\"$EDGE\\\" sudo -n /bin/bash -s -- \\\"$EDGE_STAGE\\\" < \\\"$HOMELAB_TASK_DIR/assets/edge-seal.sh\\\")\\\"\"},{\"line\":34,\"path\":\"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/prepare.sh\",\"text\":\"RESULT=\\\"$(ssh \\\"${SSH[@]}\\\" \\\"$EDGE\\\" sudo -n /bin/bash -s -- \\\"$EDGE_STAGE\\\" < \\\"$HOMELAB_TASK_DIR/assets/edge-prepare.sh\\\")\\\"\"},{\"line\":18,\"path\":\"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/rollback.sh\",\"text\":\"RESULT=\\\"$(ssh \\\"${SSH[@]}\\\" \\\"$EDGE\\\" sudo -n /bin/bash -s -- \\\"$EDGE_STAGE\\\" < \\\"$HOMELAB_TASK_DIR/assets/edge-rollback.sh\\\")\\\"\"},{\"line\":21,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json\",\"text\":\"\\\"authoritative and public DNS for chat.gram1.ru\\\",\"},{\"line\":22,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json\",\"text\":\"\\\"EDGE-01 public service path at 185.225.35.6\\\",\"},{\"line\":36,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json\",\"text\":\"\\\"Cloudflare A record chat.gram1.ru\\\",\"},{\"line\":41,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json\",\"text\":\"\\\"chat.gram1.ru A record\\\",\"},{\"line\":52,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json\",\"text\":\"\\\"groups.chat.gram1.ru and share.chat.gram1.ru records are unchanged\\\",\"},{\"line\":113,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json\",\"text\":\"\\\"compile\\\": \\\"QGA only; no first-boot SSH dependency\\\"\"},{\"line\":116,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json\",\"text\":\"\\\"host\\\": \\\"EDGE-01 185.225.35.6\\\",\"},{\"line\":128,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json\",\"text\":\"\\\"chat_fqdn\\\": \\\"chat.gram1.ru\\\",\"},{\"line\":130,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json\",\"text\":\"\\\"edge01_public_ip\\\": \\\"185.225.35.6\\\",\"},{\"line\":4,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/README.txt\",\"text\":\"- production traffic cutover for chat.gram1.ru from 95.84.154.183 to EDGE-01 185.225.35.6;\"},{\"line\":6,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/tools/cutover.py\",\"text\":\"CHAT = \\\"chat.gram1.ru\\\"\"},{\"line\":9,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/tools/cutover.py\",\"text\":\"EDGE = \\\"185.225.35.6\\\"\"},{\"line\":16,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/tools/cutover.py\",\"text\":\"EDGE_SSH = [\\\"ssh\\\",\\\"-o\\\",\\\"BatchMode=yes\\\",\\\"-o\\\",\\\"StrictHostKeyChecking=yes\\\",\\\"-o\\\",\\\"ConnectTimeout=8\\\",\\\"debian@[PRIVATE_IP]\\\"]\"},{\"line\":138,\"path\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/tools/cutover.py\",\"text\":\"for host in (CHAT,\\\"groups.chat.gram1.ru\\\",\\\"share.chat.gram1.ru\\\"):\"},{\"line\":120,\"path\":\"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/task.json\",\"text\":\"\\\"host\\\": \\\"EDGE-01 185.225.35.6\\\",\"},{\"line\":137,\"path\":\"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/task.json\",\"text\":\"\\\"edge01_public_ip\\\": \\\"185.225.35.6\\\",\"},{\"line\":24,\"path\":\"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/tools/retire_home_forwards.py\",\"text\":\"EDGE = \\\"185.225.35.6\\\"\"},{\"line\":26,\"path\":\"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/tools/retire_home_forwards.py\",\"text\":\"CHAT = \\\"chat.gram1.ru\\\"\"},{\"line\":30,\"path\":\"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/tools/retire_home_forwards.py\",\"text\":\"EDGE_SSH = [\\\"ssh\\\",\\\"-o\\\",\\\"BatchMode=yes\\\",\\\"-o\\\",\\\"StrictHostKeyChecking=yes\\\",\\\"-o\\\",\\\"ConnectTimeout=8\\\",\\\"debian@[PRIVATE_IP]\\\"]\"},{\"line\":207,\"path\":\"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/tools/retire_home_forwards.py\",\"text\":\"for host in (CHAT,\\\"groups.chat.gram1.ru\\\",\\\"share.chat.gram1.ru\\\"):\"},{\"line\":274,\"path\":\"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/tools/retire_home_forwards.py\",\"text\":\"cmd = EDGE_SSH + [\\\"sudo\\\",\\\"-n\\\",\\\"docker\\\",\\\"exec\\\",\\\"-i\\\",\\\"npmplus\\\",\\\"sh\\\",\\\"-s\\\"]\"},{\"line\":21,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/task.json\",\"text\":\"\\\"authoritative and public DNS for chat.gram1.ru and turn.gram1.ru\\\",\"},{\"line\":42,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/task.json\",\"text\":\"\\\"chat.gram1.ru is not modified\\\",\"},{\"line\":43,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/task.json\",\"text\":\"\\\"groups.chat.gram1.ru and share.chat.gram1.ru are not modified\\\",\"},{\"line\":113,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/task.json\",\"text\":\"\\\"host\\\": \\\"EDGE-01 185.225.35.6\\\",\"},{\"line\":127,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/task.json\",\"text\":\"\\\"chat_fqdn\\\": \\\"chat.gram1.ru\\\",\"},{\"line\":128,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/task.json\",\"text\":\"\\\"edge01_public_ip\\\": \\\"185.225.35.6\\\",\"},{\"line\":6,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py\",\"text\":\"CHAT = \\\"chat.gram1.ru\\\"\"},{\"line\":10,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py\",\"text\":\"EDGE = \\\"185.225.35.6\\\"\"},{\"line\":13,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py\",\"text\":\"EDGE_SSH = [\\\"ssh\\\",\\\"-o\\\",\\\"BatchMode=yes\\\",\\\"-o\\\",\\\"StrictHostKeyChecking=yes\\\",\\\"-o\\\",\\\"ConnectTimeout=8\\\",\\\"debian@[PRIVATE_IP]\\\"]\"},{\"line\":16,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py\",\"text\":\"\\\"_stun._udp.chat.gram1.ru\\\",\"},{\"line\":17,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py\",\"text\":\"\\\"_stuns._tcp.chat.gram1.ru\\\",\"},{\"line\":18,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py\",\"text\":\"\\\"_turn._udp.chat.gram1.ru\\\",\"},{\"line\":19,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py\",\"text\":\"\\\"_turn._tcp.chat.gram1.ru\\\",\"},{\"line\":20,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py\",\"text\":\"\\\"_turns._tcp.chat.gram1.ru\\\",\"},{\"line\":198,\"path\":\"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py\",\"text\":\"for host in (CHAT,\\\"groups.chat.gram1.ru\\\",\\\"share.chat.gram1.ru\\\"):\"},{\"line\":8,\"path\":\"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-post-cutover-hardening-design.md\",\"text\":\"- `chat.gram1.ru` resolves to `185.225.35.6`.\"},{\"line\":9,\"path\":\"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-post-cutover-hardening-design.md\",\"text\":\"- VM150 default route is via `[PRIVATE_IP]`; verified public egress is `185.225.35.6`.\"},{\"line\":10,\"path\":\"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-post-cutover-hardening-design.md\",\"text\":\"- coturn advertises `185.225.35.6/[PRIVATE_IP]`.\"},{\"line\":28,\"path\":\"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-post-cutover-hardening-design.md\",\"text\":\"Verify `chat.gram1.ru`, public Snikket services, VM150 egress and effective coturn external address remain unchanged.\"},{\"line\":7,\"path\":\"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-edge-cutover-design.md\",\"text\":\"Move chat.gram1.ru from home IP 95.84.154.183 to EDGE-01 185.225.35.6. Persistent EDGE-01 and edge-vm inbound transit is already installed and TCP, XMPP STARTTLS, TURN TLS, UDP STUN and HTTPS canaries pass.\"},{\"line\":8,\"path\":\"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-edge-cutover-design.md\",\"text\":\"Before cutover, EDGE-01 ingress is still restricted to source 95.84.154.183, chat.gram1.ru A is still 95.84.154.183, edge-vm table 17777 is ready, and no source rule for VM150 [PRIVATE_IP] is active.\"},{\"line\":11,\"path\":\"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-edge-cutover-design.md\",\"text\":\"CR0080 covers only production traffic cutover: publicize the verified EDGE-01 ingress contract, change only chat.gram1.ru A to 185.225.35.6, wait for DNS convergence, activate VM150 egress through edge-vm and EDGE-01, refresh effective TURN addressing, verify, rollback and seal.\"},{\"line\":16,\"path\":\"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-edge-cutover-design.md\",\"text\":\"EDGE-01 remains manual-operator access only. CR0080 versions the exact public-ingress contract, but the operator applies it from the established root@edge01 session; the pve01 task must not invent an automated EDGE-01 SSH path.\"},{\"line\":21,\"path\":\"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-edge-cutover-design.md\",\"text\":\"Verification covers HTTPS chat/groups/share; TCP 5000,5222,3478,3479,5349,5350; XMPP STARTTLS; TURN TLS; UDP STUN and relay 60000-60199; VM150 outbound IP 185.225.35.6; DNS convergence; and no effective TURN advertisement containing 95.84.154.183.\"},{\"line\":21,\"path\":\"/srv/homelab-ops/docs/superpowers/plans/2026-08-18-gitea-edge-publisher-v3.md\",\"text\":\"- Use strict SSH host verification; never `StrictHostKeyChecking=no`.\"},{\"line\":845,\"path\":\"/srv/homelab-ops/docs/superpowers/plans/2026-08-18-gitea-edge-publisher-v3.md\",\"text\":\"ssh -o BatchMode=yes -o StrictHostKeyChecking=yes edgeadmin@185.225.35.6 'sudo -n true && hostname -s'\"},{\"line\":3,\"path\":\"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-edge-cutover.md\",\"text\":\"**Goal:** finish the traffic cutover of chat.gram1.ru to EDGE-01 while preserving rollback and keeping VM150 source-policy disabled until DNS convergence.\"},{\"line\":23,\"path\":\"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-edge-cutover.md\",\"text\":\"- apply: require explicit EDGE-01 public-ingress operator attestation; change only chat.gram1.ru A to 185.225.35.6; wait for authoritative and public convergence; only then install priority-101 source policy for [PRIVATE_IP] via table 17777 and change VM150 gateway to [PRIVATE_IP]; refresh only the Snikket server container if effective TURN addressing still shows the old origin.\"},{\"line\":24,\"path\":\"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-edge-cutover.md\",\"text\":\"- verify: HTTPS, TCP 5000/5222/3478/3479/5349/5350, XMPP STARTTLS, TURN TLS, UDP STUN, VM150 outbound public IP 185.225.35.6, DNS convergence and effective TURN address without 95.84.154.183.\"},{\"line\":9,\"path\":\"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-turn-legacy-dns-retirement.md\",\"text\":\"**Tech Stack:** Bash phase wrappers, Python 3, `homelab-admin`, `qm guest exec`, SSH to edge-vm, Docker/NPMplus, Cloudflare API, `dig`, `curl`, `openssl`, sockets.\"},{\"line\":15,\"path\":\"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-turn-legacy-dns-retirement.md\",\"text\":\"- Do not modify `chat.gram1.ru`, Snikket containers, VM150 routing, edge-vm routing, EDGE-01 nftables, certificate renewal, or home-router forwards.\"},{\"line\":33,\"path\":\"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-turn-legacy-dns-retirement.md\",\"text\":\"- Consumes: CR0080 sealed path (`chat.gram1.ru=185.225.35.6`, VM150 egress through EDGE, effective coturn external address on EDGE), NPMplus local Cloudflare credential path.\"},{\"line\":88,\"path\":\"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-turn-legacy-dns-retirement.md\",\"text\":\"chat.gram1.ru: 185.225.35.6 at all three views\"},{\"line\":89,\"path\":\"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-turn-legacy-dns-retirement.md\",\"text\":\"VM150 public egress: 185.225.35.6\"},{\"line\":209,\"path\":\"/srv/homelab-ops/tools/collect_context.py\",\"text\":\"\\\"remote\\\": \\\"https://git.gram1.ru/homelab-admin/homelab-ops.git\\\",\"},{\"line\":15,\"path\":\"/srv/homelab-ops/kb/AI_CONTEXT_PUBLIC.md\",\"text\":\"CR0083 is merged/closed. Current P0 sequence starts with Git topology preservation, then canonical post-migration state, recovery/backup refresh, VM160/forum acceptance, permanent new-USA SSH hardening, old-USA observation/retirement, and final seal.\"},{\"line\":18,\"path\":\"/srv/homelab-ops/kb/AI_CONTEXT.md\",\"text\":\"- EDGE: `edgeadmin@185.225.35.6`, key label `edge01-admin-2026`, then `sudo -i`. Do not copy the EDGE private key to pve01.\"},{\"line\":19,\"path\":\"/srv/homelab-ops/kb/AI_CONTEXT.md\",\"text\":\"- Gitea: `https://git.gram1.ru`, repo `homelab-admin/homelab-ops`.\"},{\"line\":32,\"path\":\"/srv/homelab-ops/kb/AI_CONTEXT.md\",\"text\":\"Production NetBird is new USA `46.16.34.129`, Debian 12, hostname `nb2`, NetBird `0.73.2`. Exact deployment caveat: `/api/health` = 404 and combined `:9000/health` = 503; these are not generic readiness gates for this deployment. Old USA `185.139.214.215` has Mailcow/NetBird server stopped and observer disabled, but remains in rollback window. Permanent new-USA operator SSH hardening is still P0.\"},{\"line\":28,\"path\":\"/srv/homelab-ops/kb/state/current.json\",\"text\":\"\\\"gitea_url\\\": \\\"https://git.gram1.ru\\\",\"},{\"line\":86,\"path\":\"/srv/homelab-ops/kb/state/current.json\",\"text\":\"\\\"permanent operator SSH key\\\",\"},{\"line\":6,\"path\":\"/srv/homelab-ops/kb/runbooks/02_PROXMOX.md\",\"text\":\"- SSH readiness does not mean cloud-init/apt is finished.\"},{\"line\":3,\"path\":\"/srv/homelab-ops/kb/runbooks/03_EDGE_NGINX.md\",\"text\":\"Operator path: MobaXterm → `edgeadmin@185.225.35.6` with key `edge01-admin-2026` → `sudo -i`.\"},{\"line\":7,\"path\":\"/srv/homelab-ops/kb/runbooks/04_NETBIRD_USA.md\",\"text\":\"SSH hardening sequence is strict:\"},{\"line\":7,\"path\":\"/srv/homelab-ops/kb/private/access.json\",\"text\":\"\\\"command_hint\\\": \\\"ssh root@100.100.131.41\\\",\"},{\"line\":20,\"path\":\"/srv/homelab-ops/kb/private/access.json\",\"text\":\"\\\"target\\\": \\\"185.225.35.6\\\",\"},{\"line\":22,\"path\":\"/srv/homelab-ops/kb/private/access.json\",\"text\":\"\\\"command_hint\\\": \\\"ssh edgeadmin@185.225.35.6 then sudo -i\\\",\"},{\"line\":63,\"path\":\"/srv/homelab-ops/kb/private/access.json\",\"text\":\"\\\"target\\\": \\\"https://git.gram1.ru\\\",\"},{\"line\":116,\"path\":\"/srv/homelab-ops/kb/lessons/known_failures.json\",\"text\":\"\\\"incident\\\": \\\"ssh ... bash -s consumed stdin and disrupted enclosing command flow.\\\",\"},{\"line\":117,\"path\":\"/srv/homelab-ops/kb/lessons/known_failures.json\",\"text\":\"\\\"prevention\\\": \\\"Use explicit script files/stdin isolation; avoid ssh bash -s inside loops that also read stdin.\\\",\"},{\"line\":378,\"path\":\"/srv/homelab-ops/kb/lessons/known_failures.json\",\"text\":\"\\\"incident\\\": \\\"SSH became reachable before first-boot/cloud-init apt activity released package locks.\\\",\"},{\"line\":513,\"path\":\"/srv/homelab-ops/kb/lessons/known_failures.json\",\"text\":\"\\\"incident\\\": \\\"A top-level exit can close the user’s primary SSH shell/session.\\\",\"},{\"line\":529,\"path\":\"/srv/homelab-ops/kb/lessons/known_failures.json\",\"text\":\"\\\"OPENSSH PRIVATE KEY\\\"\"},{\"line\":68,\"path\":\"/srv/homelab-ops/kb/lessons/known_failures_public.json\",\"text\":\"\\\"incident\\\": \\\"ssh ... bash -s consumed stdin and disrupted enclosing command flow.\\\",\"},{\"line\":69,\"path\":\"/srv/homelab-ops/kb/lessons/known_failures_public.json\",\"text\":\"\\\"prevention\\\": \\\"Use explicit script files/stdin isolation; avoid ssh bash -s inside loops that also read stdin.\\\"\"},{\"line\":219,\"path\":\"/srv/homelab-ops/kb/lessons/known_failures_public.json\",\"text\":\"\\\"incident\\\": \\\"SSH became reachable before first-boot/cloud-init apt activity released package locks.\\\",\"},{\"line\":297,\"path\":\"/srv/homelab-ops/kb/lessons/known_failures_public.json\",\"text\":\"\\\"incident\\\": \\\"A top-level exit can close the user’s primary SSH shell/session.\\\",\"},{\"line\":9,\"path\":\"/srv/homelab-ops/kb/current/04_P0_NEXT.md\",\"text\":\"7. New USA permanent SSH key + key-only hardening with separate-session proof.\"},{\"line\":12,\"path\":\"/srv/homelab-ops/kb/current/04_P0_NEXT.md\",\"text\":\"Do not mix unrelated changes into these gates (e.g. NetBird version upgrade/hostname change during SSH hardening).\"},{\"line\":34,\"path\":\"/srv/homelab-ops/kb/scripts/validate_kb.py\",\"text\":\"openssh_marker = '\"},{\"line\":46,\"path\":\"/srv/homelab-ops/kb/scripts/validate_kb.py\",\"text\":\"for bad in ('/etc/msmtp-postbox.secret','/etc/pvepro-relay-gotify.token','/etc/homelab-git-read/token','/var/lib/homelab-private/secrets/','begin openssh private key'):\"},{\"line\":21,\"path\":\"/srv/homelab-ops/kb/scripts/command_guard.py\",\"text\":\"add('BLOCK','REG-030-MOBAXTERM-TOP-LEVEL-EXIT','Top-level exit can close the operator SSH session.')\"},{\"line\":37,\"path\":\"/srv/homelab-ops/kb/scripts/command_guard.py\",\"text\":\"add('WARN','REG-007-SSH-STDIN-CONSUMPTION','ssh bash -s can consume stdin; isolate script input.')\"},{\"line\":33,\"path\":\"/srv/homelab-ops/tests/test_cr_2026_0080_snikket_edge_cutover.py\",\"text\":\"for needle in (\\\"185.225.35.6\\\",\\\"[PRIVATE_IP]\\\",\\\"snat to [PRIVATE_IP]\\\",\\\"60000-60199\\\",\\\"SNIKKET_PROD_INGRESS_JUMP\\\"):\"},{\"line\":33,\"path\":\"/srv/homelab-ops/tests/test_cr_2026_0081_turn_dns_retire.py\",\"text\":\"for name in (\\\"_turn._udp.turn.gram1.ru\\\",\\\"_turns._tcp.turn.gram1.ru\\\",\\\"_stun._udp.turn.gram1.ru\\\",\\\"_turn._udp.chat.gram1.ru\\\"):\"},{\"line\":69,\"path\":\"/srv/homelab-ops/tests/test_cr_2026_0013_vm160_worker_bootstrap_transition.py\",\"text\":\"actual_pos = self.apply.index('INSTALLED_WORKER_SHA=\\\"$(ssh ')\"},{\"line\":82,\"path\":\"/srv/homelab-ops/tests/test_cr_2026_0013_vm160_worker_bootstrap_transition.py\",\"text\":\"self.assertIn('INSTALLED_WORKER_SHA=\\\"$(ssh ', self.verify)\"},{\"line\":35,\"path\":\"/srv/homelab-ops/tests/test_cr_2026_0081_home_forward_retire.py\",\"text\":\"self.assertEqual(self.task[\\\"source_of_truth\\\"][\\\"edge01_public_ip\\\"], \\\"185.225.35.6\\\")\"},{\"line\":74,\"path\":\"/srv/homelab-ops/tests/test_cr_2026_0007_skladchik_reauth.py\",\"text\":\"self.assertNotIn(\\\"ssh -t -o\\\", texts)\"},{\"line\":72,\"path\":\"/srv/homelab-ops/tests/test_cr_2026_0009_skladchik_concurrency_guard.py\",\"text\":\"\\\"VALUE=$(ssh ${SSH[@]} $EDGE sudo -n /usr/bin/sha256sum /path \\\"\"},{\"line\":54,\"path\":\"/srv/homelab-ops/tests/test_cr_2026_0007_skladchik_semantic_repair.py\",\"text\":\"self.assertIn('ssh -tt \\\"${SSH[@]}\\\" \\\"$EDGE\\\" sudo -n \\\"$EDGE_SCRIPT\\\"', wrapper)\"},{\"line\":951,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"URL=https://git.gram1.ru\"},{\"line\":1899,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Avoid multiline Python directly in SSH one-liner unless base64 encoded.\"},{\"line\":1967,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"4. The only remaining access convenience item is optional: expose the cluster-admin panel via a VPN-only/internal reverse proxy route such as `cluster-admin.vpn.gram1.ru`.\"},{\"line\":2086,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Commands became too slow when they repeatedly ran `homelab-duty-admin-v2`, `appbackupctl restore-check`, `homelab-final-readiness-gate`, full `pvesh` scans, SSH to all nodes, and cloud checks.\"},{\"line\":2323,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- SSH works as `debian@[PRIVATE_IP]`.\"},{\"line\":2445,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- pve01/pve02/pve03 SSH and Proxmox\"},{\"line\":2454,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Optional ports 80/443 on VM150/170/171 are not hard failures because services may live behind reverse proxy or not expose direct ports.\"},{\"line\":2650,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- VM180 should not have unrestricted root SSH access to pve01.\"},{\"line\":2803,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"Configure operator-friendly VPN reverse proxy route: cluster-admin.vpn.gram1.ru -> [PRIVATE_IP]:8080\"},{\"line\":2817,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"Do not accept public VPS 188.127.235.6 changed SSH host key without provider-console fingerprint.\"},{\"line\":2827,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"3. VM180 creation wait for QGA could be safely interrupted after VM creation; SSH was already working.\"},{\"line\":2832,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"8. `http://[PRIVATE_IP]:8080/` is not reachable from a normal browser outside LAN/VPN. Use VPN, SSH tunnel, or internal reverse proxy.\"},{\"line\":2850,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"3. сделать независимый external runner только на host с verified SSH fingerprint;\"},{\"line\":2903,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"6. Do not accept the changed public VPS SSH host key for `188.127.235.6` until independently verified from provider console.\"},{\"line\":3033,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Use ssh -n in loops/remote commands so ssh does not consume loop stdin.\"},{\"line\":3319,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- public VPS `188.127.235.6` is rejected due SSH host-key mismatch.\"},{\"line\":3331,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"Use a new or repaired external host only after its provider-console SSH host fingerprint is verified.\"},{\"line\":3368,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"Use SSH remote URL without embedded credentials, or HTTPS credential helper outside git config.\"},{\"line\":3411,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Do not deploy anything to public VPS 188.127.235.6 until provider-console fingerprint confirms the changed SSH host key.\"},{\"line\":3434,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"1. независимый external runner — нужен новый/починенный внешний host и verified SSH fingerprint;\"},{\"line\":4266,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- For remote loops with ssh, use `ssh -n` so ssh does not consume loop stdin.\"},{\"line\":4441,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- ssh: `ssh debian@[PRIVATE_IP]`\"},{\"line\":4448,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- ssh: `ssh debian@[PRIVATE_IP]`\"},{\"line\":4455,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- ssh: `ssh debian@[PRIVATE_IP]`\"},{\"line\":4810,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- likely frontend/cache;\"},{\"line\":5033,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- For systemd mask checks, avoid broken local `$()` expansion inside SSH strings.\"},{\"line\":5034,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Use direct remote command with single-quoted SSH body when checking systemd state.\"},{\"line\":5134,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- SSH failure bursts;\"},{\"line\":5436,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- кто отвечает за reverse proxy;\"},{\"line\":5727,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- отдельный ssh key;\"},{\"line\":6434,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Запрещены `ssh + heredoc + python` и глубокие вложенные кавычки.\"},{\"line\":6441,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Public SSH на дачный роутер закрыт.\"},{\"line\":6442,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- WG SSH открыт.\"},{\"line\":6472,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- not authoritative: public SSH still open.\"},{\"line\":6475,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- not authoritative: direct public SSH still open.\"},{\"line\":6481,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- REVIEW snapshot: public SSH still open.\"},{\"line\":6683,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"| Core | Gitea | https://git.gram1.ru | public | Git |\"},{\"line\":6740,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Закрыли публичный SSH на дачном роутере, оставив WG SSH доступным.\"},{\"line\":6777,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- direct public dacha SSH closed, WG SSH open.\"},{\"line\":6857,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- line 1156: `### SSH and permissions`\"},{\"line\":7096,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- line 304: `## FORUM_PROD_BULK_IMPORT_PHP85_EMPTY_FRONTEND_20260701`\"},{\"line\":7244,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"| 1156 | `### SSH and permissions` |\"},{\"line\":7455,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"| 304 | `## FORUM_PROD_BULK_IMPORT_PHP85_EMPTY_FRONTEND_20260701` |\"},{\"line\":7540,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- `https://git.gram1.ru`\"},{\"line\":7573,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- `/mnt/staging/netbird-vps-backups/snapshots.`\"},{\"line\":8104,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Proxmox: ssh root@pve01, ssh root@pve02, ssh root@pve03.\"},{\"line\":8105,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Edge VM: ssh debian@[PRIVATE_IP], использовать sudo, root-login не использовать.\"},{\"line\":8106,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Nextcloud VM: ssh debian@[PRIVATE_IP].\"},{\"line\":8107,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Forum-prod: сначала ssh root@pve02, затем ssh -i [SENSITIVE_PATH] root@[PRIVATE_IP].\"},{\"line\":8279,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- SSH port: 2222.\"},{\"line\":8280,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- SSH security-level: private.\"},{\"line\":8285,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- SFTP denied for admin in log; SSH CLI works.\"},{\"line\":8293,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- service ssh enabled.\"},{\"line\":8303,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Netcraze SSH CLI is not a normal POSIX shell.\"},{\"line\":8329,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- SSH Server through NetBird: Disabled.\"},{\"line\":8344,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- pve01 owns many backup/offhost/restore/health/security/NetBird VPS/rclone/sops/scrutiny jobs.\"},{\"line\":8404,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- pve01 timers cover VPN/NetBird health, health metrics, smartctl, disk space, MkDocs refresh, VPS identity audit, storage capacity, quality gate, evidence catalog, backup freshness, docker health, Filebrowser backup/offhost/restore, NPMplus/Kuma backup, NetBird VPS backup/offhost, Authentik/Gitea/Vaultwarden backup, SOPS secret coverage, mail cloud upload/restore, Immich/Memos/Paperless backup/offhost/restore, auto backup, edge-vm vzdump, secret sanity.\"},{\"line\":8552,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Role: edge application host / reverse proxy / monitoring / backup automation host.\"},{\"line\":8620,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- git.gram1.ru -> [PRIVATE_IP].\"},{\"line\":8653,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- git.gram1.ru -> http://127.0.0.1:3002, cert=29, ssl_forced=1, enabled=1.\"},{\"line\":8702,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- cert=29: git.gram1.ru, expires 2026-09-13 17:36:55.\"},{\"line\":8943,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- NetBird VPS backup: STATUS=OK, snapshot under /mnt/staging/netbird-vps-backups/snapshots.\"},{\"line\":8944,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- NetBird VPS offhost: STATUS=OK to pve02.\"},{\"line\":8945,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- NetBird VPS restore validation: STATUS=OK, archive SHA256 recorded.\"},{\"line\":9006,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- https://git.gram1.ru\"},{\"line\":9062,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- external canary checks include nc.gram1.ru, git.gram1.ru, auth.gram1.ru, backup.gram1.ru.\"},{\"line\":9119,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Do not use exit 1 in interactive SSH sessions.\"},{\"line\":9131,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Reason: nested Python inside SSH lost quoting and produced SyntaxError.\"},{\"line\":9150,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Do not use exit 1 in interactive SSH sessions.\"},{\"line\":9162,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"### SSH and permissions\"},{\"line\":9182,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- PVE nodes expose SSH :22, Proxmox :8006 and node-exporter :9100.\"},{\"line\":9202,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- SSH permission correction proof: 160_PVE_ROOT_AUTHORIZED_KEYS_PERMISSIONS_FIXED.txt.\"},{\"line\":9209,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Valid closure condition: target permissions checked with stat -L are 600 for authorized_keys files and [SENSITIVE_PATH] is 700.\"},{\"line\":9305,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Reverse proxy: NPMplus on edge-vm, container npmplus, host networking, admin bound to 127.0.0.1:81.\"},{\"line\":9319,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"| git.gram1.ru | http://127.0.0.1:3002 | edge-vm / gitea | gitea backup/offhost/restore |\"},{\"line\":9400,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Do not use exit 1 in interactive SSH sessions.\"},{\"line\":9528,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Use 154 for Prometheus settled targets and 163 for symlink-aware SSH target permissions.\"},{\"line\":9870,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Discovery proof records DNS, HTTPS/TLS headers, reverse-proxy candidates, compose files, domain references and homepage config candidates without printing secrets.\"},{\"line\":10053,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Netcraze routerbackup SSH access is read-only for backup: show running-config works, but ACL/config commands are denied.\"},{\"line\":10093,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- New VM identity confirmed: forum-prod / forum-prod.gram1.ru, Debian 12 bookworm, SSH OK, qemu-agent OK, chrony OK.\"},{\"line\":10096,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Beget-compatible profile applied: memory_limit 256M, post/upload 1024M, max_input_vars 10000, MariaDB utf8mb4/utf8mb4_unicode_ci, innodb_buffer_pool_size 2G.\"},{\"line\":10101,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Bulk import of five forums reached DB/files/nginx/php-fpm ready state, but frontend body stayed empty under PHP 8.5.7.\"},{\"line\":10113,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Final result: all five frontend/admin HTTP 200, www redirects 301, internal_data 403, no new XenForo errors.\"},{\"line\":10119,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Applies to: SSH enter/exit points, VM prompt confirmations, snapshot confirmations, file copy confirmations, successful health checks, and other obvious next-step transitions.\"},{\"line\":10433,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"CHECK-4: команда не должна иметь вложенный ssh с несколькими уровнями кавычек.\"},{\"line\":10464,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"Нельзя писать sqlite SQL вида j.type in ('object','array') внутри ssh '...'.\"},{\"line\":10465,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"Для sqlite через ssh использовать SQL без одинарных кавычек: char(36), length(j.atom), двойные внешние кавычки, либо отдельный файл.\"},{\"line\":10470,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"edge-vm: ssh debian@[PRIVATE_IP], внутри использовать sudo.\"},{\"line\":10471,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"pve02/pve03: ssh root@pve02 или ssh root@pve03.\"},{\"line\":10476,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"Снова был использован SQL JSON-path в одинарных кавычках внутри ssh '...'.\"},{\"line\":10526,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"27. Ошибка: Python heredoc внутри ssh сломал not_ok диагностику.\"},{\"line\":10549,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"Факт: Python -c внутри ssh потерял кавычки вокруг /tmp/prom-targets-settled.json, data, activeTargets, labels, job, health.\"},{\"line\":10629,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- A portal card is openable only when DNS, reverse-proxy host mapping, and target content are all valid.\"},{\"line\":10632,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Context: attempted Netcraze router ACL apply through SSH stdin/multiline for Homepage Moscow Router monitor fix.\"},{\"line\":10638,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Context: ACL syntax read-only probe loop executed only one command because ssh consumed the loop stdin.\"},{\"line\":10640,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Fix pattern: use ssh -n or redirect SSH stdin away from the command-list loop for all future SSH-in-loop probes.\"},{\"line\":10702,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Rule: do not proceed with OS baseline until SSH failure is diagnosed; likely old known_hosts key or cloud-init/root-key issue.\"},{\"line\":10705,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Context: VM160 first SSH proof after rebuild.\"},{\"line\":10706,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Issue: command substitution $(hostname) inside nested ssh was expanded on pve02 before entering VM160.\"},{\"line\":10708,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Rule: for nested SSH identity checks, run literal hostname commands without local command substitution.\"},{\"line\":10714,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Rule: avoid nested $(...) in VM SSH proofs; use literal remote commands and clean proof.\"},{\"line\":10718,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Issue: nested SSH quoting expanded shell variables incorrectly, producing gzip checks against empty .gz and blank TAR_TOP lines.\"},{\"line\":10722,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"## FORUM_PROD_BULK_IMPORT_PHP85_EMPTY_FRONTEND_20260701\"},{\"line\":10752,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"- Rule: avoid nested SSH heredoc/Python for this task; use simpler commands, generated nginx configs, or uploaded/local script files.\"},{\"line\":11033,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"А самая критичная VM130 edge-vm находится на pve03 и держит reverse proxy, monitoring, backup automation и Docker application host. При этом pve03 — самый ограниченный по диску: local-lvm уже был самым constrained, потому что VM130 имеет 96G OS + 150G media/data, а pve03 staging доходил до 77% при WARN 80%.\"},{\"line\":11102,\"path\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md\",\"text\":\"Файл жёстко требует перед инфраструктурными командами проверять truth files, не использовать огромные paste, не использовать `ssh + heredoc + python`, валидировать скрипты и не печатать секреты. Это оставить как закон.\"},{\"line\":22,\"path\":\"/etc/pve/HOMEPAGE_BACKUP_COVERAGE_MATRIX.md\",\"text\":\"| Gitea | https://git.gram1.ru | 185 | 89 | 105 | EVIDENCE_FOUND_REVIEW |\"},{\"line\":156,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Proxmox: ssh root@pve01, ssh root@pve02, ssh root@pve03.\"},{\"line\":157,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Edge VM: ssh debian@[PRIVATE_IP], использовать sudo, root-login не использовать.\"},{\"line\":158,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Nextcloud VM: ssh debian@[PRIVATE_IP].\"},{\"line\":159,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Forum-prod: сначала ssh root@pve02, затем ssh -i [SENSITIVE_PATH] root@[PRIVATE_IP].\"},{\"line\":331,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- SSH port: 2222.\"},{\"line\":332,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- SSH security-level: private.\"},{\"line\":337,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- SFTP denied for admin in log; SSH CLI works.\"},{\"line\":345,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- service ssh enabled.\"},{\"line\":355,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Netcraze SSH CLI is not a normal POSIX shell.\"},{\"line\":381,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- SSH Server through NetBird: Disabled.\"},{\"line\":396,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- pve01 owns many backup/offhost/restore/health/security/NetBird VPS/rclone/sops/scrutiny jobs.\"},{\"line\":456,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- pve01 timers cover VPN/NetBird health, health metrics, smartctl, disk space, MkDocs refresh, VPS identity audit, storage capacity, quality gate, evidence catalog, backup freshness, docker health, Filebrowser backup/offhost/restore, NPMplus/Kuma backup, NetBird VPS backup/offhost, Authentik/Gitea/Vaultwarden backup, SOPS secret coverage, mail cloud upload/restore, Immich/Memos/Paperless backup/offhost/restore, auto backup, edge-vm vzdump, secret sanity.\"},{\"line\":604,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Role: edge application host / reverse proxy / monitoring / backup automation host.\"},{\"line\":672,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- git.gram1.ru -> [PRIVATE_IP].\"},{\"line\":705,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- git.gram1.ru -> http://127.0.0.1:3002, cert=29, ssl_forced=1, enabled=1.\"},{\"line\":754,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- cert=29: git.gram1.ru, expires 2026-09-13 17:36:55.\"},{\"line\":995,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- NetBird VPS backup: STATUS=OK, snapshot under /mnt/staging/netbird-vps-backups/snapshots.\"},{\"line\":996,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- NetBird VPS offhost: STATUS=OK to pve02.\"},{\"line\":997,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- NetBird VPS restore validation: STATUS=OK, archive SHA256 recorded.\"},{\"line\":1058,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- https://git.gram1.ru\"},{\"line\":1114,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- external canary checks include nc.gram1.ru, git.gram1.ru, auth.gram1.ru, backup.gram1.ru.\"},{\"line\":1171,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Do not use exit 1 in interactive SSH sessions.\"},{\"line\":1183,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Reason: nested Python inside SSH lost quoting and produced SyntaxError.\"},{\"line\":1202,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Do not use exit 1 in interactive SSH sessions.\"},{\"line\":1214,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"### SSH and permissions\"},{\"line\":1234,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- PVE nodes expose SSH :22, Proxmox :8006 and node-exporter :9100.\"},{\"line\":1254,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- SSH permission correction proof: 160_PVE_ROOT_AUTHORIZED_KEYS_PERMISSIONS_FIXED.txt.\"},{\"line\":1261,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Valid closure condition: target permissions checked with stat -L are 600 for authorized_keys files and [SENSITIVE_PATH] is 700.\"},{\"line\":1357,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Reverse proxy: NPMplus on edge-vm, container npmplus, host networking, admin bound to 127.0.0.1:81.\"},{\"line\":1371,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"| git.gram1.ru | http://127.0.0.1:3002 | edge-vm / gitea | gitea backup/offhost/restore |\"},{\"line\":1452,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Do not use exit 1 in interactive SSH sessions.\"},{\"line\":1580,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Use 154 for Prometheus settled targets and 163 for symlink-aware SSH target permissions.\"},{\"line\":1922,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Discovery proof records DNS, HTTPS/TLS headers, reverse-proxy candidates, compose files, domain references and homepage config candidates without printing secrets.\"},{\"line\":2105,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Netcraze routerbackup SSH access is read-only for backup: show running-config works, but ACL/config commands are denied.\"},{\"line\":2145,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- New VM identity confirmed: forum-prod / forum-prod.gram1.ru, Debian 12 bookworm, SSH OK, qemu-agent OK, chrony OK.\"},{\"line\":2148,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Beget-compatible profile applied: memory_limit 256M, post/upload 1024M, max_input_vars 10000, MariaDB utf8mb4/utf8mb4_unicode_ci, innodb_buffer_pool_size 2G.\"},{\"line\":2153,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Bulk import of five forums reached DB/files/nginx/php-fpm ready state, but frontend body stayed empty under PHP 8.5.7.\"},{\"line\":2165,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Final result: all five frontend/admin HTTP 200, www redirects 301, internal_data 403, no new XenForo errors.\"},{\"line\":2171,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Applies to: SSH enter/exit points, VM prompt confirmations, snapshot confirmations, file copy confirmations, successful health checks, and other obvious next-step transitions.\"},{\"line\":2515,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Recommended future path: establish VPN/NetBird/WireGuard or reverse-proxy/private management endpoint first; then issue DNS-01 certificate on a trusted node and deploy cert/key to the router only over that private path.\"},{\"line\":2531,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Public SSH 194.33.48.131:22 closed.\"},{\"line\":2539,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Public SSH remains closed.\"},{\"line\":2546,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Public SSH closed.\"},{\"line\":2558,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Supersedes failed/partial proof 669 because direct SSH was open during that run.\"},{\"line\":2564,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Direct dacha public SSH is closed; WG SSH remains open.\"},{\"line\":2596,\"path\":\"/etc/pve/31_HOMELAB_REFERENCE.md\",\"text\":\"- Direct public SSH to dacha router is closed.\"},{\"line\":2597,\"...\nSYSTEMD_VPS_NETBIRD_UNITS={\"count\":9,\"units\":[{\"err\":\"\",\"lines\":[\"# /etc/systemd/system/homelab-external-probe-vps-health.service\",\"Description=Homelab external probe VPS strategy health\",\"After=network-online.target\",\"ExecStart=/usr/local/sbin/homelab-external-probe-vps-health\"],\"rc\":0,\"unit\":\"homelab-external-probe-vps-health.service\"},{\"err\":\"\",\"lines\":[\"# /etc/systemd/system/homelab-external-probe-vps-health.timer\",\"Description=Homelab external probe VPS strategy health timer\"],\"rc\":0,\"unit\":\"homelab-external-probe-vps-health.timer\"},{\"err\":\"\",\"lines\":[\"# /etc/systemd/system/homelab-vps-identity-audit.service\",\"Description=Homelab VPS SSH identity audit\",\"ExecStart=/usr/local/sbin/homelab-vps-identity-audit\"],\"rc\":0,\"unit\":\"homelab-vps-identity-audit.service\"},{\"err\":\"\",\"lines\":[\"# /etc/systemd/system/homelab-vps-identity-audit.timer\",\"Description=Run Homelab VPS SSH identity audit\"],\"rc\":0,\"unit\":\"homelab-vps-identity-audit.timer\"},{\"err\":\"\",\"lines\":[\"# /etc/systemd/system/netbird-peers-health.service\",\"Description=NetBird peers health check\",\"After=network-online.target\",\"ExecStart=/root/netbird-peers-health.sh\"],\"rc\":0,\"unit\":\"netbird-peers-health.service\"},{\"err\":\"\",\"lines\":[\"# /etc/systemd/system/netbird-peers-health.timer\",\"Description=Run NetBird peers health check\"],\"rc\":0,\"unit\":\"netbird-peers-health.timer\"},{\"err\":\"\",\"lines\":[\"# /etc/systemd/system/netbird.service\",\"Description=NetBird mesh network client\",\"ConditionFileIsExecutable=/usr/bin/netbird\",\"After=network.target syslog.target\",\"ExecStart=/usr/bin/netbird \\\"service\\\" \\\"run\\\" \\\"--log-level\\\" \\\"info\\\" \\\"--daemon-addr\\\" \\\"unix:///var/run/netbird.sock\\\" \\\"--log-file\\\" \\\"/var/log/netbird/client.log\\\"\",\"StandardOutput=file:/var/log/netbird/netbird.out\",\"StandardError=file:/var/log/netbird/netbird.err\",\"EnvironmentFile=-/etc/sysconfig/netbird\",\"Environment=SYSTEMD_UNIT=netbird\"],\"rc\":0,\"unit\":\"netbird.service\"},{\"err\":\"\",\"lines\":[\"# /usr/lib/systemd/system/pveproxy.service\",\"Description=PVE API Proxy Server\",\"ConditionPathExists=/usr/bin/pveproxy\",\"Wants=pve-cluster.service\",\"Wants=pvedaemon.service\",\"Wants=ssh.service\",\"Wants=pve-storage.target\",\"After=pve-storage.target\",\"After=pve-cluster.service\",\"After=pvedaemon.service\",\"After=ssh.service\",\"ExecStartPre=-/usr/bin/pvecm updatecerts --silent\",\"ExecStart=/usr/bin/pveproxy start\",\"ExecStartPost=-sh -c '[ ! -e /var/log/pveam.log ] && /usr/bin/pveupdate'\",\"ExecStop=/usr/bin/pveproxy stop\",\"ExecReload=/usr/bin/pveproxy restart\",\"PIDFile=/run/pveproxy/pveproxy.pid\"],\"rc\":0,\"unit\":\"pveproxy.service\"},{\"err\":\"\",\"lines\":[\"# /usr/lib/systemd/system/spiceproxy.service\",\"Description=PVE SPICE Proxy Server\",\"ConditionPathExists=/usr/bin/spiceproxy\",\"Wants=pveproxy.service\",\"After=pveproxy.service\",\"ExecStart=/usr/bin/spiceproxy start\",\"ExecStop=/usr/bin/spiceproxy stop\",\"ExecReload=/usr/bin/spiceproxy restart\",\"PIDFile=/run/pveproxy/spiceproxy.pid\"],\"rc\":0,\"unit\":\"spiceproxy.service\"}]}\nSSH_METADATA={\"config\":[{\"line\":21,\"path\":\"/etc/ssh/ssh_config\",\"text\":\"Host *\"}],\"key_metadata\":[{\"mode\":\"0o600\",\"path\":\"[SENSITIVE_PATH] Host git.gram1.ru found: line 72 \",{\"algorithm\":\"ssh-ed25519\",\"line_sha256\":\"956d4c61a41d7547b9c63dbbf4f31730f0579f638a5bcdab9b299154bc17913a\"}],\"query\":\"git.gram1.ru\",\"rc\":0},{\"err\":\"\",\"matches\":[],\"query\":\"chat.gram1.ru\",\"rc\":1},{\"err\":\"\",\"matches\":[],\"query\":\"newfi-staging.gram1.ru\",\"rc\":1}]}\nPUBLIC_FRONTEND_SSH_KEYSCAN=[{\"err\":\"\",\"host\":\"185.225.35.6\",\"keys\":[{\"algorithm\":\"ecdsa-sha2-nistp256\",\"line_sha256\":\"ac034f62bb300d5803fb554720d8e893f60de04d2d7b4e4173927a22898844a0\"},{\"algorithm\":\"ssh-rsa\",\"line_sha256\":\"d50e3d759085b7fed47aabfda87e5b8898baaa901558b2ceb86669818bb31367\"},{\"algorithm\":\"ssh-ed25519\",\"line_sha256\":\"7958f5c47286d25debbcdf39d333b2ae99c27a851b09a89e750e40e5f2646d75\"}],\"rc\":0},{\"err\":\"\",\"host\":\"git.gram1.ru\",\"keys\":[{\"algorithm\":\"ssh-rsa\",\"line_sha256\":\"008c80ae05353cedff97a531fbca0f4e626dfbe16822ed82f9868495e887a6e2\"},{\"algorithm\":\"ecdsa-sha2-nistp256\",\"line_sha256\":\"8ef2fdb8c060387ce82f66221713faeef4c7d27895d2dbd92bc87aa375b94172\"},{\"algorithm\":\"ssh-ed25519\",\"line_sha256\":\"e13c7f631cc347a1c52f5816326189542f8d3a1ca6d31ad596aa422aa9e9ac3e\"}],\"rc\":0},{\"err\":\"\",\"host\":\"chat.gram1.ru\",\"keys\":[{\"algorithm\":\"ssh-rsa\",\"line_sha256\":\"15a9ce01047aa6d86b0a7f323187062c126d2f7f8c3d5b7cdf47b89f96f94f7a\"},{\"algorithm\":\"ecdsa-sha2-nistp256\",\"line_sha256\":\"db8a2e358b3d2b62ad43991f34fdc76ee85a43ea39efaf9bb2420a89ee42fdb4\"},{\"algorithm\":\"ssh-ed25519\",\"line_sha256\":\"d5f95e577619b811e15cb286ce4661f3835450b79e138717f7283ffbb4eabc63\"}],\"rc\":0},{\"err\":\"getaddrinfo newfi-staging.gram1.ru: Name or service not known\\ngetaddrinfo newfi-staging.gram1.ru: Name or service not known\\ngetaddrinfo newfi-staging.gram1.ru: Name or service not known\\n\",\"host\":\"newfi-staging.gram1.ru\",\"keys\":[],\"rc\":1}]\nEXPLICIT_SSH_CANDIDATES=[{\"host\":\"[PRIVATE_IP]\",\"source\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/verify.sh:9\",\"user\":\"root\"},{\"host\":\"[PRIVATE_IP]\",\"source\":\"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/preflight.sh:9\",\"user\":\"root\"},{\"host\":\"[PRIVATE_IP]\",\"source\":\"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/tools/cutover.py:16\",\"user\":\"debian\"},{\"host\":\"185.225.35.6\",\"source\":\"/srv/homelab-ops/docs/superpowers/plans/2026-08-18-gitea-edge-publisher-v3.md:845\",\"user\":\"edgeadmin\"},{\"host\":\"100.100.131.41\",\"source\":\"/srv/homelab-ops/kb/private/access.json:7\",\"user\":\"root\"},{\"host\":\"[PRIVATE_IP]\",\"source\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:2323\",\"user\":\"debian\"},{\"host\":\"[PRIVATE_IP]\",\"source\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:4448\",\"user\":\"debian\"},{\"host\":\"[PRIVATE_IP]\",\"source\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:4455\",\"user\":\"debian\"},{\"host\":\"pve01\",\"source\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:8104\",\"user\":\"root\"},{\"host\":\"pve02\",\"source\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:8104\",\"user\":\"root\"},{\"host\":\"pve03.\",\"source\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:8104\",\"user\":\"root\"},{\"host\":\"[PRIVATE_IP].\",\"source\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:8106\",\"user\":\"debian\"},{\"host\":\"[PRIVATE_IP].\",\"source\":\"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:8107\",\"user\":\"root\"},{\"host\":\"[PRIVATE_IP]\",\"source\":\"/etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md:3109\",\"user\":\"edgeadmin\"},{\"host\":\"pve03\",\"source\":\"/etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md:3267\",\"user\":\"root\"},{\"host\":\"[PRIVATE_IP]\",\"source\":\"/root/post-reboot-pve01-check.sh:32\",\"user\":\"debian\"}]\nTRUSTED_FRONTEND_SSH_READONLY_ATTEMPTS=[]\nFINAL_DECISION=NO_TRUSTED_BEGET_FRONTEND_SSH_PATH_VERIFIED\nVPS_FRONTEND_DISCOVERY6_END=true\n" } diff --git a/runtime/latest.txt b/runtime/latest.txt index 941b01aa..84b60d7c 100644 --- a/runtime/latest.txt +++ b/runtime/latest.txt @@ -1,22 +1,22 @@ CHAT_OUTPUT_BEGIN -COMMAND_ID=SIGNALBOT-260908-PACKAGE-SEAL-RCA-043PKGFIX4 -STATUS=FAIL -RC=3 +COMMAND_ID=NEWFI-260908-A-STAGINGEDGE-VPS-FRONTEND-DISCOVERY6 +STATUS=OK +RC=0 HOST=pve01 MODE=read-only -COMPONENT=signalbot-cr0116-package-seal-rca +COMPONENT=newfi-staging-vps-frontend-access-discovery-readonly REFERENCE_REGISTER_CHECK=OK REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 ERROR_REGISTER_CHECK=OK ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 -COMMAND_SHA256=9ced1576bd959feda816a33885905e1bed85156c58d9145f8a4027570dcfa64d +COMMAND_SHA256=2ad58afd2cf7e1cc3a231fb3ac9b55b4f697d78841c53df32e5263b1c183c524 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=3 +COMMAND_RC=0 CHANGES_MADE=false ROLLBACK_STARTED=false ROLLBACK_RESTORED=null @@ -24,21 +24,31 @@ MUTATION_OUTCOME=NO_MUTATION SANITIZED=yes SECRETS_INCLUDED=no PRIVATE_ADDRESSES_INCLUDED=no -RAW_EVIDENCE_SHA256=f6bfc5e0e811b34437723475517c13047e89a5cd17aa1cba4a3bd9e162dd0583 -SANITIZED_OUTPUT_SHA256=f6bfc5e0e811b34437723475517c13047e89a5cd17aa1cba4a3bd9e162dd0583 +RAW_EVIDENCE_SHA256=284d281515727ed750fef17eede5cf72d87468c30b4ab1e2c5458a1dcc30e5b0 +SANITIZED_OUTPUT_SHA256=ab2cb2f4b1af09a6375abf6422ab4ce673bec87b1cb30e0c610e68776b23f81e OUTPUT_BEGIN +VPS_FRONTEND_DISCOVERY6_BEGIN=true +COMMAND_ID=NEWFI-260908-A-STAGINGEDGE-VPS-FRONTEND-DISCOVERY6 +MODE=read-only +MUTATIONS_PERFORMED=NO ERROR_REGISTER_CHECK=OK +ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 REFERENCE_CHECK=OK +REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 AUTHORITY_CHECK_SCOPE=READABILITY_ONLY_INCIDENTS_NOT_CLOSED -SB043KF4_PRESTATE={"active_env":{"APP_IMAGE":"8020-demonov-shadow:e32760c69a4311728db9066ee8bf2bf66b1e5a70","CODE_IDENTITY":"e32760c69a4311728db9066ee8bf2bf66b1e5a70","COLLECTION_START_AT":"2026-09-07T00:00:00+00:00","EXPERIMENT_ID":"ec477ea41ecbd5cfdef5afc259595aab20674a7d933f41a389329ff05098b338","RUNTIME_MANIFEST_ID":"58d3b59200bdc0eb8d448612679d667b194263586cb3198dfc08597935053a1a"},"services":[{"container_id":"a546a70c3e8c263d9b590cd78ee206bcda0cfcdb6662327e8415ca0e29ad1501","image":"8020-demonov-shadow:e32760c69a4311728db9066ee8bf2bf66b1e5a70","image_id":"sha256:403c4266282a56cb210bed95cd58295692296502913e7440e2133d7b5664c736","restart_count":15,"running":true,"service":"collector","started_at":"2026-09-08T08:18:12.235219419Z"},{"container_id":"d8023f4cbf91f9382e39eec0436c9cebc7665535b42b950e95408c03ff9bf30d","image":"8020-demonov-shadow:e32760c69a4311728db9066ee8bf2bf66b1e5a70","image_id":"sha256:403c4266282a56cb210bed95cd58295692296502913e7440e2133d7b5664c736","restart_count":0,"running":true,"service":"relay","started_at":"2026-09-06T18:48:20.603414848Z"},{"container_id":"1feeca2d9428cd5a277c026bef34534db307657dcdcd63aff98ea66e44f8b9c5","image":"8020-demonov-shadow:e32760c69a4311728db9066ee8bf2bf66b1e5a70","image_id":"sha256:403c4266282a56cb210bed95cd58295692296502913e7440e2133d7b5664c736","restart_count":0,"running":true,"service":"shadow","started_at":"2026-09-06T18:48:20.768190553Z"},{"container_id":"889be41d2cee9d327811da78388f834ba6eafe4c0eeae456867ed0df76832d01","image":"8020-demonov-shadow:e32760c69a4311728db9066ee8bf2bf66b1e5a70","image_id":"sha256:403c4266282a56cb210bed95cd58295692296502913e7440e2133d7b5664c736","restart_count":0,"running":true,"service":"worker","started_at":"2026-09-06T18:48:20.586788319Z"}],"stage_result":{"active_env_unchanged":true,"build_context":"/opt/stacks/8020-demonov-shadow/.deploy/SIGNALBOT-260908-DEPLOY-STAGE-042FIX5","collector_source_sha256":"193dd0c7f9890fdb4026c24dbc5add825f1641d92097e881806705ca452812d5","copy_source":"/opt/stacks/8020-demonov-shadow/.deploy/SIGNALBOT-260908-DEPLOY-STAGE-042FIX5/demonov_forward_map","decision":"DEPLOY_STAGE_READY","dockerfile":"/opt/stacks/8020-demonov-shadow/.deploy/SIGNALBOT-260908-DEPLOY-STAGE-042FIX5/Dockerfile","helper_candidates":{"demonov_forward_map.live_prospective":[{"name":"_hash","pure_candidate":true,"signature":"(value: 'object') -> 'str'"}],"demonov_forward_map.runtime_integrity":[{"name":"canonical_hash","pure_candidate":true,"signature":"(payload: 'Any') -> 'str'"},{"name":"verify_experiment_seal_receipt","pure_candidate":true,"signature":"(*, bundle: 'FinalProspectiveSealBundle', receipt: 'ExperimentSealReceipt', collection_start_at: 'datetime', receipt_verifier: 'Callable[[ExperimentSealReceipt], bool]') -> 'None'"},{"name":"required_retained_hashes","pure_candidate":true,"signature":"(*, runtime_registry: 'RuntimeRegistry', model_registry: 'FinalModelRegistry', state_snapshots: 'Sequence[StateSnapshotManifest]' = (), predictor_runtime: 'PredictorRuntimeManifest | None' = None) -> 'Mapping[str, RetainedArtifactKind]'"}],"demonov_forward_map.runtime_manifest":[{"name":"canonical_hash","pure_candidate":true,"signature":"(payload: 'Any') -> 'str'"},{"name":"verify_runtime_manifest","pure_candidate":true,"signature":"(manifest: 'RuntimeDeploymentManifestV1', *, expected_code_identity: 'str', actual_image_id: 'str', actual_package_identity: 'str') -> 'None'"}]},"new_image":"8020-demonov-shadow:76e1f58baa544a0e208bba317f0150e5b6d74dbd","new_image_id":"sha256:341f71a906c91d3fa9c1d50bd89df2a849dfb0b168bb4551efb444bf61c4ab99","policy":{"close_timeout":5,"open_timeout":10,"ping_interval":20,"ping_timeout":60},"runtime_activated":false,"service_identity_unchanged":true,"stage_path":"/opt/stacks/8020-demonov-shadow/.deploy/SIGNALBOT-260908-DEPLOY-STAGE-042FIX5","websockets_version":"15.0.1"}} -SB043KF4_CANONICAL_VALIDATION={"computed":"ab8a8d8f361c0c86f709fe84f27ba4378f9978fb802cbfb2a542ea9833ad48df","expected":"58d3b59200bdc0eb8d448612679d667b194263586cb3198dfc08597935053a1a","ok":false,"spec":{"class_fields":["runtime_manifest_id","code_identity","package_identity","application_image_id","base_image_digest","compose_hash","finality_policy_id"],"kind":"dict","parts":[{"key":"schema","kind":"const","value":"CR0116_RUNTIME_DEPLOYMENT_MANIFEST_V1"},{"key":"code_identity","kind":"field","value":"code_identity"},{"key":"package_identity","kind":"field","value":"package_identity"},{"key":"application_image_id","kind":"field","value":"application_image_id"},{"key":"base_image_digest","kind":"field","value":"base_image_digest"},{"key":"compose_hash","kind":"field","value":"compose_hash"},{"key":"finality_policy_id","kind":"field","value":"finality_policy_id"}],"source":"def content_hash(self) -> str:\n return canonical_hash(\n {\n \"schema\": \"CR0116_RUNTIME_DEPLOYMENT_MANIFEST_V1\",\n \"code_identity\": self.code_identity,\n \"package_identity\": self.package_identity,\n \"application_image_id\": self.application_image_id,\n \"base_image_digest\": self.base_image_digest,\n \"compose_hash\": self.compose_hash,\n \"finality_policy_id\": self.finality_policy_id,\n }\n )"}} -SB043KF4_ARTIFACT_SPEC={"class_fields":["artifact_id","source_tree_hash","dependency_lock_hash","build_recipe_hash","executable_artifact_hash","runtime_environment_hash"],"fields":["source_tree_hash","dependency_lock_hash","build_recipe_hash","executable_artifact_hash","runtime_environment_hash"],"kind":"dictcomp","source":"def content_hash(self) -> str:\n return canonical_hash({k: getattr(self, k) for k in (\n \"source_tree_hash\", \"dependency_lock_hash\", \"build_recipe_hash\", \"executable_artifact_hash\", \"runtime_environment_hash\"\n )})"} -SB043KF4_CALLSITES=[{"name":"RuntimeArtifactManifest","node":"ClassDef","path":"runtime_integrity.py","score":5,"source":"class RuntimeArtifactManifest:\n artifact_id: str\n source_tree_hash: str\n dependency_lock_hash: str\n build_recipe_hash: str\n executable_artifact_hash: str\n runtime_environment_hash: str\n\n def __post_init__(self) -> None:\n for name in (\"source_tree_hash\", \"dependency_lock_hash\", \"build_recipe_hash\", \"executable_artifact_hash\", \"runtime_environment_hash\"):\n _hex64(getattr(self, name), name)\n if self.artifact_id != self.content_hash:\n raise DemonovError(\"runtime artifact id must be content-addressed\")\n\n @property\n def content_hash(self) -> str:\n return canonical_hash({k: getattr(self, k) for k in (\n \"source_tree_hash\", \"dependency_lock_hash\", \"build_recipe_hash\", \"executable_artifact_hash\", \"runtime_environment_hash\"\n )})\n\n @classmethod\n def build(cls, **kwargs) -> \"RuntimeArtifactManifest\":\n payload = {k: kwargs[k] for k in (\"source_tree_hash\", \"dependency_lock_hash\", \"build_recipe_hash\", \"executable_artifact_hash\", \"runtime_environment_hash\")}\n return cls(artifact_id=canonical_hash(payload), **kwargs)","source_sha256":"8cd1ebf70c003cd4d7b91f18602841426b19c2f54f2d803309c53c22f38cd5c7"},{"name":"DeploymentAttestation","node":"ClassDef","path":"runtime_integrity.py","score":4,"source":"class DeploymentAttestation:\n attestation_id: str\n subject_kind: DeploymentSubjectKind\n subject_id: str\n actor_id: str\n source_tree_hash: str\n dependency_lock_hash: str\n executable_artifact_hash: str\n runtime_environment_hash: str\n effective_config_hash: str\n deployed_at: datetime\n issued_at: datetime\n authority_provider_id: str\n authority_receipt_hash: str\n feature_extractor_code_hash: str = \"\"\n\n def __post_init__(self) -> None:\n _aware(self.deployed_at, \"deployment deployed_at\"); _aware(self.issued_at, \"deployment issued_at\")\n if self.issued_at < self.deployed_at:\n raise DemonovError(\"deployment attestation cannot be issued before deployment\")\n if not self.actor_id or not self.authority_provider_id:\n raise DemonovError(\"deployment attestation identity fields are required\")\n for n in (\"subject_id\",\"source_tree_hash\",\"dependency_lock_hash\",\"executable_artifact_hash\",\n \"runtime_environment_hash\",\"effective_config_hash\",\"authority_receipt_hash\"):\n _hex64(getattr(self,n),n)\n if self.feature_extractor_code_hash: _hex64(self.feature_extractor_code_hash,\"feature_extractor_code_hash\")\n if self.attestation_id != self.content_hash:\n raise DemonovError(\"deployment attestation id must be content-addressed\")\n\n @property\n def content_hash(self) -> str:\n payload={\"subject_kind\":self.subject_kind,\"subject_id\":self.subject_id,\"actor_id\":self.actor_id,\n \"source_tree_hash\":self.source_tree_hash,\"dependency_lock_hash\":self.dependency_lock_hash,\n \"executable_artifact_hash\":self.executable_artifact_hash,\"runtime_environment_hash\":self.runtime_environment_hash,\n \"effective_config_hash\":self.effective_config_hash,\"deployed_at\":self.deployed_at,\"issued_at\":self.issued_at,\n \"authority_provider_id\":self.authority_provider_id,\"authority_receipt_hash\":self.authority_receipt_hash}\n if self.feature_extractor_code_hash: payload[\"feature_extractor_code_hash\"]=self.feature_extractor_code_hash\n return canonical_hash(payload)\n\n @classmethod\n def build(cls, **kwargs) -> \"DeploymentAttestation\":\n payload={k:kwargs[k] for k in (\"subject_kind\",\"subject_id\",\"actor_id\",\"source_tree_hash\",\"dependency_lock_hash\",\n \"executable_artifact_hash\",\"runtime_environment_hash\",\"effective_config_hash\",\"deployed_at\",\"issued_at\",\n \"authority_provider_id\",\"authority_receipt_hash\")}\n if kwargs.get(\"feature_extractor_code_hash\"): payload[\"feature_extractor_code_hash\"]=kwargs[\"feature_extractor_code_hash\"]\n return cls(attestation_id=canonical_hash(payload),**kwargs)","source_sha256":"1b0ca84bb035dfb7ff441ce73bbe8ee5f140a07b4a821f5a9f55770b585ffac7"},{"name":"verify_final_deployment_attestations","node":"FunctionDef","path":"runtime_integrity.py","score":4,"source":"def verify_final_deployment_attestations(*, runtime_registry: RuntimeRegistry, producer_id: str,\n predictor_runtime: \"PredictorRuntimeManifest\",\n attestations: Sequence[DeploymentAttestation],\n policy: DeploymentAttestationPolicy,\n collection_start_at: datetime,\n receipt_verifier: Callable[[DeploymentAttestation], bool]) -> None:\n _aware(collection_start_at,\"collection_start_at\")\n relevant_epochs=[e for e in runtime_registry.epochs if e.producer_id==producer_id and e.contains(collection_start_at)]\n if len(relevant_epochs)!=1:\n raise DemonovError(\"collection start must resolve to exactly one producer runtime epoch for deployment proof\")\n epoch=relevant_epochs[0]\n artifact=next((a for a in runtime_registry.artifacts if a.artifact_id==epoch.artifact_id),None)\n if artifact is None: raise DemonovError(\"producer deployment proof references missing runtime artifact\")\n expected={\n (DeploymentSubjectKind.PRODUCER_RUNTIME_EPOCH,epoch.epoch_id):(\n producer_id,artifact.source_tree_hash,artifact.dependency_lock_hash,artifact.executable_artifact_hash,\n artifact.runtime_environment_hash,epoch.config_id,\"\",epoch.started_at),\n (DeploymentSubjectKind.PREDICTOR_RUNTIME,predictor_runtime.predictor_runtime_id):(\n predictor_runtime.consumer_id,predictor_runtime.source_tree_hash,predictor_runtime.dependency_lock_hash,\n predictor_runtime.executable_artifact_hash,predictor_runtime.runtime_environment_hash,predictor_runtime.effective_config_hash,\n predictor_runtime.feature_extractor_code_hash,predictor_runtime.started_at),\n }\n amap={(a.subject_kind,a.subject_id):a for a in attestations}\n if len(amap)!=len(attestations): raise DemonovError(\"duplicate deployment attestation subject\")\n if set(amap)!=set(expected): raise DemonovError(\"deployment attestation set differs from exact producer/predictor subjects\")\n for key,(actor,src,dep,exe,env,cfg,feature,start) in expected.items():\n a=amap[key]\n if a.authority_provider_id!=policy.authority_provider_id or not receipt_verifier(a):\n raise DemonovError(\"deployment attestation authority verification failed\")\n if (a.actor_id,a.source_tree_hash,a.dependency_lock_hash,a.executable_artifact_hash,a.runtime_environment_hash,a.effective_config_hash)!=(actor,src,dep,exe,env,cfg):\n raise DemonovError(\"deployment attestation artifact/config identity mismatch\")\n if key[0] is DeploymentSubjectKind.PREDICTOR_RUNTIME and a.feature_extractor_code_hash!=feature:\n raise DemonovError(\"predictor deployment attestation feature extractor mismatch\")\n if key[0] is DeploymentSubjectKind.PRODUCER_RUNTIME_EPOCH and a.feature_extractor_code_hash:\n raise DemonovError(\"producer deployment attestation must not invent challenger feature extractor\")\n if abs((a.deployed_at-start).total_seconds()) > policy.max_start_skew_seconds:\n raise DemonovError(\"deployment attestation outside frozen runtime-start skew\")\n if policy.require_precollection_attestation and a.issued_at >= collection_start_at:\n raise DemonovError(\"deployment attestation must be externally issued strictly before collection start\")","source_sha256":"9fc5b8d032d6a7b31f6b4e21d15142fe20b1fde4fe30a06fb758ad264bdeac9d"},{"name":"PredictorRuntimeManifest","node":"ClassDef","path":"runtime_integrity.py","score":4,"source":"class PredictorRuntimeManifest:\n predictor_runtime_id: str\n consumer_id: str\n source_tree_hash: str\n dependency_lock_hash: str\n runtime_environment_hash: str\n executable_artifact_hash: str\n effective_config_hash: str\n final_model_registry_id: str\n feature_extractor_code_hash: str\n started_at: datetime\n ended_at: datetime | None\n deployment_receipt_hash: str\n\n def __post_init__(self) -> None:\n _aware(self.started_at, \"predictor runtime started_at\")\n if self.ended_at is not None:\n _aware(self.ended_at, \"predictor runtime ended_at\")\n if self.ended_at <= self.started_at: raise DemonovError(\"predictor runtime ended_at must follow started_at\")\n if not self.consumer_id or not self.final_model_registry_id:\n raise DemonovError(\"predictor runtime identity fields required\")\n for n in (\"source_tree_hash\",\"dependency_lock_hash\",\"runtime_environment_hash\",\"executable_artifact_hash\",\"effective_config_hash\",\"feature_extractor_code_hash\",\"deployment_receipt_hash\"):\n _hex64(getattr(self,n),n)\n if self.predictor_runtime_id != self.content_hash:\n raise DemonovError(\"predictor runtime id must be content-addressed\")\n\n @property\n def content_hash(self) -> str:\n return canonical_hash({k:getattr(self,k) for k in (\n \"consumer_id\",\"source_tree_hash\",\"dependency_lock_hash\",\"runtime_environment_hash\",\"executable_artifact_hash\",\"effective_config_hash\",\n \"final_model_registry_id\",\"feature_extractor_code_hash\",\"started_at\",\"ended_at\",\"deployment_receipt_hash\"\n )})\n\n @classmethod\n def build(cls, **kwargs) -> \"PredictorRuntimeManifest\":\n payload={k:kwargs.get(k) for k in (\n \"consumer_id\",\"source_tree_hash\",\"dependency_lock_hash\",\"runtime_environment_hash\",\"executable_artifact_hash\",\"effective_config_hash\",\n \"final_model_registry_id\",\"feature_extractor_code_hash\",\"started_at\",\"ended_at\",\"deployment_receipt_hash\"\n )}\n return cls(predictor_runtime_id=canonical_hash(payload),**kwargs)\n\n def contains(self,t:datetime)->bool:\n _aware(t,\"predictor runtime time\")\n return self.started_at <= t and (self.ended_at is None or t < self.ended_at)","source_sha256":"6514f10948abbeb0030fb06b509a8a75d796eea4d6ef4101b72e20f688985454"},{"name":"required_retained_hashes","node":"FunctionDef","path":"runtime_integrity.py","score":5,"source":"def required_retained_hashes(*, runtime_registry:RuntimeRegistry, model_registry:FinalModelRegistry,\n state_snapshots:Sequence[StateSnapshotManifest]=(), predictor_runtime:PredictorRuntimeManifest | None=None)->Mapping[str,RetainedArtifactKind]:\n \"\"\"Build the minimum blob-level replay corpus required by final prospective proof.\n\n Content-addressed policy objects remain in the handoff/registry. This function\n targets blobs that cannot be reconstructed from identifiers alone.\n \"\"\"\n out:dict[str,RetainedArtifactKind]={}\n for a in runtime_registry.artifacts:\n out[a.source_tree_hash]=RetainedArtifactKind.SOURCE_TREE\n out[a.dependency_lock_hash]=RetainedArtifactKind.DEPENDENCY_LOCK\n out[a.build_recipe_hash]=RetainedArtifactKind.BUILD_RECIPE\n out[a.executable_artifact_hash]=RetainedArtifactKind.EXECUTABLE_ARTIFACT\n out[a.runtime_environment_hash]=RetainedArtifactKind.RUNTIME_ENVIRONMENT\n for a in model_registry.artifacts:\n out[a.model_freeze_hash]=RetainedArtifactKind.MODEL\n out[a.transformer_freeze_hash]=RetainedArtifactKind.TRANSFORMER\n out[a.calibration_freeze_hash]=RetainedArtifactKind.CALIBRATOR\n out[a.feature_schema_hash]=RetainedArtifactKind.FEATURE_SCHEMA\n out[a.feature_contract_hash]=RetainedArtifactKind.FEATURE_CONTRACT\n out[a.training_data_hash]=RetainedArtifactKind.TRAINING_DATA\n for s in state_snapshots:\n out[s.serialized_state_hash]=RetainedArtifactKind.STATE_SNAPSHOT\n if predictor_runtime is not None:\n out[predictor_runtime.source_tree_hash]=RetainedArtifactKind.SOURCE_TREE\n out[predictor_runtime.dependency_lock_hash]=RetainedArtifactKind.DEPENDENCY_LOCK\n out[predictor_runtime.executable_artifact_hash]=RetainedArtifactKind.EXECUTABLE_ARTIFACT\n out[predictor_runtime.runtime_environment_hash]=RetainedArtifactKind.RUNTIME_ENVIRONMENT\n out[predictor_runtime.feature_extractor_code_hash]=RetainedArtifactKind.FEATURE_EXTRACTOR_CODE\n out[predictor_runtime.effective_config_hash]=RetainedArtifactKind.EFFECTIVE_CONFIG\n return out","source_sha256":"ac6e18e76fcbd23144d1870ac03e3c2a4a1149dc4b5cf3f84fc0664228cdab1a"}] -SB043KF4_REPRODUCTION={"derived_new_package_identities":[],"fields":["source_tree_hash","dependency_lock_hash","build_recipe_hash","executable_artifact_hash","runtime_environment_hash"],"group_sizes":{"build_recipe_hash":10,"dependency_lock_hash":9,"executable_artifact_hash":14,"runtime_environment_hash":13,"source_tree_hash":71},"matches":[],"ok":false,"reason":"NO_MATCH","tested":1162980,"total":1162980} -SB043KF4_HOLDS=["CANONICAL_HASH_NOT_VALIDATED","OLD_PACKAGE_ID_EXACT_REPRODUCTION_FAILED"] -SB043KF4_DECISION=HOLD_CANONICAL_HASH_NOT_VALIDATED__OLD_PACKAGE_ID_EXACT_REPRODUCTION_FAILED -SB043KF4_MUTATION_SCOPE=NONE_READ_ONLY -SIGNALBOT043KF4_DECISION=HOLD_GUEST_PACKAGE_REPRODUCTION +RUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d +RUNNER_SHA_GATE=PASS +PRIOR_APPLY14_GATE=PASS +PRIOR_APPLY14_HISTORY_SHA256=774ea70f504928e020983f4da32ac2d4248f158e5f6a275768ebb6c59275235a +PRIOR_APPLY14_BLOCKER=STRONG_PUBLIC_INGRESS_COUNT_NOT_ONE:0 +OPERATIONAL_VPS_FRONTEND_REFERENCES={"count":1600,"rows":[{"line":2,"path":"/etc/systemd/system/homelab-vps-identity-audit.timer","text":"Description=Run Homelab VPS SSH identity audit"},{"line":2,"path":"/etc/systemd/system/homelab-vps-identity-audit.service","text":"Description=Homelab VPS SSH identity audit"},{"line":6,"path":"/etc/systemd/system/homelab-vps-identity-audit.service","text":"ExecStart=/usr/local/sbin/homelab-vps-identity-audit"},{"line":2,"path":"/etc/systemd/system/timers.target.wants/homelab-vps-identity-audit.timer","text":"Description=Run Homelab VPS SSH identity audit"},{"line":3,"path":"/etc/systemd/system/netbird-vps-backup.service.d/10-superseded-noop.conf","text":"ExecStart=/usr/local/sbin/homelab-superseded-unit-ok netbird-vps-backup.service superseded_by_current_appbackup_or_trust_proof"},{"line":9,"path":"/usr/local/sbin/homelab-external-probe-vps","text":"grep -q \"^STATUS=OK\" /var/lib/homelab-health/netbird-vps-trust-clean-seal.txt 2>/dev/null || BAD=$((BAD+1))"},{"line":10,"path":"/usr/local/sbin/homelab-external-probe-vps","text":"grep -q \"^STATUS=OK\" /var/lib/homelab-health/netbird-vps-trust-closure.txt 2>/dev/null || BAD=$((BAD+1))"},{"line":12,"path":"/usr/local/sbin/homelab-external-probe-vps","text":"grep -q \"TRUSTED_NETBIRD_IDENTITY=edge-vm\" /var/lib/homelab-health/netbird-vps-trust-clean-seal.txt 2>/dev/null || BAD=$((BAD+1))"},{"line":13,"path":"/usr/local/sbin/homelab-external-probe-vps","text":"grep -q \"PUBLIC_VPS_DECISION=REJECTED_HOSTKEY_MISMATCH\" /var/lib/homelab-health/netbird-vps-trust-clean-seal.txt 2>/dev/null || BAD=$((BAD+1))"},{"line":6,"path":"/usr/local/sbin/homelab-vps-identity-audit","text":"H=\"/var/lib/homelab-health/vps-identity-audit.txt\""},{"line":23,"path":"/usr/local/sbin/homelab-vps-identity-audit","text":"backup=\"$(find /mnt/staging/netbird-vps-backups/snapshots -maxdepth 2 -type f -name 'netbird-vps-backup.tgz' 2>/dev/null | sort | tail -n1 || true)\""},{"line":45,"path":"/usr/local/sbin/homelab-vps-identity-audit","text":"printf 'STATUS=%s TS=%s TYPE=vps-identity-audit VPS_IP=%s ALIAS=%s ALIAS_IP=%s SSH_TRUST=%s BACKUP_HOSTKEYS=%s KNOWN_HOSTS_UNCHANGED=YES SECRET_PRINTED=REDACTED\\n' \\"},{"line":13,"path":"/usr/local/sbin/homelab-external-probe-vps-health","text":"grep -q \"^STATUS=OK\" /var/lib/homelab-health/netbird-vps-trust-clean-seal.txt 2>/dev/null || BAD=$((BAD+1))"},{"line":14,"path":"/usr/local/sbin/homelab-external-probe-vps-health","text":"grep -q \"^STATUS=OK\" /var/lib/homelab-health/netbird-vps-trust-closure.txt 2>/dev/null || BAD=$((BAD+1))"},{"line":16,"path":"/usr/local/sbin/homelab-external-probe-vps-health","text":"grep -q \"TRUSTED_NETBIRD_IDENTITY=edge-vm\" /var/lib/homelab-health/netbird-vps-trust-clean-seal.txt 2>/dev/null || BAD=$((BAD+1))"},{"line":17,"path":"/usr/local/sbin/homelab-external-probe-vps-health","text":"grep -q \"PUBLIC_VPS_DECISION=REJECTED_HOSTKEY_MISMATCH\" /var/lib/homelab-health/netbird-vps-trust-clean-seal.txt 2>/dev/null || BAD=$((BAD+1))"},{"line":29,"path":"/srv/homelab-ops/KB_START_HERE.md","text":"- Не печатать пароли, PAT, токены, TOTP, private SSH keys."},{"line":116,"path":"/srv/homelab-ops/observed/current-context.json","text":"\"remote\": \"https://git.gram1.ru/homelab-admin/homelab-ops.git\","},{"line":1,"path":"/srv/homelab-ops/observed/source-snapshots/overall.txt","text":"STATUS=WARN TS=2026-08-19T05:04:50Z TYPE=overall-health BAD=13 backup-framework.txt=FAIL drift-check.txt=WARN service-registry.txt=OK dependency-map.txt=OK golden-state.txt=OK cluster-passport.txt=WARN final-readiness.txt=WARN safe-autoheal.txt=OK kuma-monitor-policy.txt=OK backup-sla.txt=FAIL backup-coverage-matrix.txt=FAIL extended-appbackup.txt=FAIL residual-review.txt=OK forum-snuffleupagus.txt=OK incident-journal.txt=OK duty-admin-v2.txt=OK node-loss-readiness.txt=OK node-loss-runbooks.txt=OK power-loss-readiness.txt=OK power-loss-runbook.txt=OK ungated-health-backlog.txt=OK pve03-root-cleanup.txt=OK pve03-staging-retention.txt=OK pve03-staging-retention-proof.txt=OK pve03-staging-retention-cleanup.txt=OK pve03-failed-unit-cleanup.txt=OK remote-git-sops-age-readiness.txt=OK remote-git-sops-age-policy.txt=OK desired-state-remote-target-trace.txt=OK desired-state-remote-target.txt=OK desired-state.txt=OK runbooks.txt=OK alerting.txt=OK secret-exposure.txt=OK capacity-risk.txt=OK vm-local-dumps-retention.txt=FAIL vm-local-dumps-cloud.txt=FAIL vm-backup-policy.txt=FAIL vm170-vm171-full-strategy.txt=OK external-probe-vps.txt=OK netbird-vps-trust.txt=OK netbird-vps-trust-closure.txt=OK external-probe-runner-decision.txt=OK netbird-vps-trust-clean-seal.txt=OK live-tail-audit.txt=OK vm-backup.txt=OK cluster-admin-observer.txt=WARN cluster-admin-restricted-probes.txt=WARN cluster-admin-full-observer.txt=WARN cluster-admin-vm-mail-cloud-backup.txt=OK cluster-admin-webpanel-sync.txt=OK cluster-admin-webpanel.txt=OK"},{"line":162,"path":"/srv/homelab-ops/changes/CR-2026-0018/design.md","text":"- не вызывается через chat wrapper, SSH forced command или remote API;"},{"line":29,"path":"/srv/homelab-ops/changes/CR-2026-0009/plan.json","text":"\"overbroad SSH pipeline regex\","},{"line":157,"path":"/srv/homelab-ops/errors/regression-cases.json","text":"\"required_control\": \"Every active Skladchik SSH caller and its desired-state copy must be versioned, deployed and verified with StrictHostKeyChecking=yes and the canonical known_hosts file.\","},{"line":222,"path":"/srv/homelab-ops/errors/regression-cases.json","text":"\"incident\": \"A read-only diagnostic passed regular-expression metacharacters as direct SSH remote arguments, allowing the remote login shell to reinterpret them and produce syntax and command-not-found errors.\","},{"line":223,"path":"/srv/homelab-ops/errors/regression-cases.json","text":"\"required_control\": \"Remote regex, pipelines and shell metacharacters must live in versioned remote scripts transferred over stdin; direct SSH argv is limited to literal commands and paths.\","},{"line":234,"path":"/srv/homelab-ops/errors/regression-cases.json","text":"\"incident\": \"An overbroad SSH static test classified a safe local pipeline consuming SSH stdout as a forbidden remote-shell pipeline.\","},{"line":235,"path":"/srv/homelab-ops/errors/regression-cases.json","text":"\"required_control\": \"SSH safety tests must match direct remote grep or pgrep execution precisely and must not reject local post-processing pipelines.\","},{"line":125,"path":"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/task.json","text":"\"compile\": \"NoCloud-delivered versioned guest-provision.sh; no first-boot SSH host-key trust is required\""},{"line":159,"path":"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/task.json","text":"\"snikket_domain\": \"chat.gram1.ru\","},{"line":3,"path":"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/README.txt","text":"The task carries split-DNS host configuration, public DNS for snikket_server, TURN NAT mapping, certificate permission reconciliation and recovery SSH key."},{"line":19,"path":"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/assets/edge-readonly.py","text":"if any(x in blob for x in ('nc.gram1.ru','chat.gram1.ru','groups.chat.gram1.ru','share.chat.gram1.ru','[PRIVATE_IP]')):"},{"line":1,"path":"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/assets/snikket.conf","text":"SNIKKET_DOMAIN=chat.gram1.ru"},{"line":8,"path":"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/assets/guest-provision.sh","text":"export DEBIAN_FRONTEND=noninteractive"},{"line":61,"path":"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/assets/vm150-runtime.py","text":"DOMAINS=['chat.gram1.ru','groups.chat.gram1.ru','share.chat.gram1.ru']"},{"line":710,"path":"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/assets/vm150-runtime.py","text":"rc,code=curl_edge('chat.gram1.ru','/.well-known/host-meta')"},{"line":752,"path":"/srv/homelab-ops/tasks/snikket-vm150-rebuild-v2/assets/vm150-runtime.py","text":"obj={'schema':'snikket-vm150-seal-v2','status':'SEALED','task_id':TASK_ID,'change_id':CFG['change_id'],'source_head':source_head,'sealed_at_utc':now(),'vmid':150,'domain':'chat.gram1.ru','pre_admin_generation':pre_admin_gen,'new_generation':gen,'new_backup_restore_proven':True,'final_post_admin_backup':True,'old_generation_preserved':OLD_GEN,'mobile_av_test':'PASS','wan_5269_forwarded':False,'admin_account_created':True,'family_user_role':'LIMITED_RECOMMENDED','invite_logged':False}"},{"line":9,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/verify.sh","text":"pve03_versioned(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] bash -s -- \"$@\" < \"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\"; }"},{"line":10,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/verify.sh","text":"pve03_installed(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \"$@\"; }"},{"line":34,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/verify.sh","text":"INSTALLED_WORKER_SHA=\"$(ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] /usr/bin/sha256sum /usr/local/libexec/homelab-vm160-cloud-quorum-worker | awk '{print $1}')\""},{"line":9,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/apply.sh","text":"pve03_versioned(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] bash -s -- \"$@\" < \"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\"; }"},{"line":10,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/apply.sh","text":"pve03_installed(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \"$@\"; }"},{"line":20,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/apply.sh","text":"ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] install -m 0755 /tmp/homelab-vm160-cloud-quorum-worker /usr/local/libexec/homelab-vm160-cloud-quorum-worker"},{"line":21,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/apply.sh","text":"ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] rm -f /tmp/homelab-vm160-cloud-quorum-worker"},{"line":22,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/apply.sh","text":"INSTALLED_WORKER_SHA=\"$(ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] /usr/bin/sha256sum /usr/local/libexec/homelab-vm160-cloud-quorum-worker | awk '{print $1}')\""},{"line":9,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/dry-run.sh","text":"pve03_versioned(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] bash -s -- \"$@\" < \"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\"; }"},{"line":10,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/dry-run.sh","text":"pve03_installed(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \"$@\"; }"},{"line":9,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/preflight.sh","text":"pve02_probe(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] bash -s -- \"$@\" < \"$HOMELAB_TASK_DIR/assets/pve02-vm160-preflight-probe.sh\"; }"},{"line":10,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/preflight.sh","text":"pve03_versioned(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] bash -s -- \"$@\" < \"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\"; }"},{"line":11,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/preflight.sh","text":"pve03_installed(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \"$@\"; }"},{"line":9,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/seal.sh","text":"pve03_versioned(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] bash -s -- \"$@\" < \"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\"; }"},{"line":10,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/seal.sh","text":"pve03_installed(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \"$@\"; }"},{"line":9,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/prepare.sh","text":"pve03_versioned(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] bash -s -- \"$@\" < \"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\"; }"},{"line":10,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/prepare.sh","text":"pve03_installed(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \"$@\"; }"},{"line":9,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh","text":"pve03_versioned(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] bash -s -- \"$@\" < \"$HOMELAB_TASK_DIR/assets/vm160-cloud-quorum-worker.sh\"; }"},{"line":10,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh","text":"pve03_installed(){ ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] /usr/local/libexec/homelab-vm160-cloud-quorum-worker \"$@\"; }"},{"line":17,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh","text":"ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] test -d /mnt/staging/vzdump/vm160-pve02-20260717T223819Z"},{"line":21,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh","text":"if ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] test -x /usr/local/libexec/homelab-vm160-cloud-quorum-worker; then pve03_installed restore vm160-pve02-20260717T223819Z; else pve03_versioned restore vm160-pve02-20260717T223819Z; fi"},{"line":23,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh","text":"ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] test -s /mnt/staging/vzdump/vm160-pve02-20260717T223819Z/vzdump-qemu-160-20260717T223819Z.vma.zst"},{"line":24,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh","text":"test \"$(ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] stat -c %s /mnt/staging/vzdump/vm160-pve02-20260717T223819Z/vzdump-qemu-160-20260717T223819Z.vma.zst)\" = 84995216465"},{"line":25,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh","text":"test \"$(ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] sha256sum /mnt/staging/vzdump/vm160-pve02-20260717T223819Z/vzdump-qemu-160-20260717T223819Z.vma.zst | awk '{print $1}')\" = 257e10821e62e3e2f9318b238a5302a6db601dd597bfa3e0d77aba07f3b85e72"},{"line":26,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh","text":"test \"$(ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] readlink /mnt/staging/vzdump/vm160-pve02-latest)\" = vm160-pve02-20260717T223819Z"},{"line":29,"path":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/rollback.sh","text":"ssh \"${SSH_BASE[@]}\" root@[PRIVATE_IP] rm -f /usr/local/libexec/homelab-vm160-cloud-quorum-worker"},{"line":23,"path":"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/verify.sh","text":"test \"$(ssh \"${SSH[@]}\" \"$EDGE\" sudo -n /usr/bin/sha256sum \"$EDGE_SCRIPT\" | awk 'NR==1{print $1}')\" = \"$EXPECTED_SHA\""},{"line":24,"path":"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/verify.sh","text":"RESULT=\"$(ssh \"${SSH[@]}\" \"$EDGE\" sudo -n /bin/bash -s -- \"$EXPECTED_SHA\" < \"$HOMELAB_TASK_DIR/assets/edge-verify.sh\")\""},{"line":26,"path":"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/apply.sh","text":"ssh \"${SSH[@]}\" \"$EDGE\" sudo -n /usr/bin/install -m 0755 -o root -g root /tmp/skladchik-reports-monitor-cookie-update-edge.cr7 \"$EDGE_SCRIPT\""},{"line":27,"path":"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/apply.sh","text":"ssh \"${SSH[@]}\" \"$EDGE\" /usr/bin/rm -f /tmp/skladchik-reports-monitor-cookie-update-edge.cr7"},{"line":28,"path":"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/apply.sh","text":"test \"$(ssh \"${SSH[@]}\" \"$EDGE\" sudo -n /usr/bin/sha256sum \"$EDGE_SCRIPT\" | awk 'NR==1{print $1}')\" = \"$EXPECTED_SHA\""},{"line":30,"path":"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/apply.sh","text":"ssh -tt \"${SSH[@]}\" \"$EDGE\" sudo -n /usr/local/sbin/skladchik-reports-monitor-cookie-update-edge"},{"line":35,"path":"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/apply.sh","text":"if ! git -C /srv/homelab-desired-state diff --cached --quiet; then git -C /srv/homelab-desired-state commit -m 'CR-2026-0007 enforce strict Skladchik SSH and controlled reauth'; fi"},{"line":32,"path":"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/dry-run.sh","text":"RESULT=\"$(ssh \"${SSH[@]}\" \"$EDGE\" sudo -n /bin/bash -s -- \"$EDGE_STAGE\" \"$CURRENT_EDGE_SHA\" < \"$HOMELAB_TASK_DIR/assets/edge-dry-run.sh\")\""},{"line":20,"path":"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/preflight.sh","text":"RESULT=\"$(ssh \"${SSH[@]}\" \"$EDGE\" sudo -n /bin/bash -s -- \"$CURRENT_EDGE_SHA\" < \"$HOMELAB_TASK_DIR/assets/edge-preflight.sh\")\""},{"line":16,"path":"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/seal.sh","text":"RESULT=\"$(ssh \"${SSH[@]}\" \"$EDGE\" sudo -n /bin/bash -s -- \"$EDGE_STAGE\" < \"$HOMELAB_TASK_DIR/assets/edge-seal.sh\")\""},{"line":34,"path":"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/prepare.sh","text":"RESULT=\"$(ssh \"${SSH[@]}\" \"$EDGE\" sudo -n /bin/bash -s -- \"$EDGE_STAGE\" < \"$HOMELAB_TASK_DIR/assets/edge-prepare.sh\")\""},{"line":18,"path":"/srv/homelab-ops/tasks/skladchik-reports-monitor-controlled-reauth-v1/phases/rollback.sh","text":"RESULT=\"$(ssh \"${SSH[@]}\" \"$EDGE\" sudo -n /bin/bash -s -- \"$EDGE_STAGE\" < \"$HOMELAB_TASK_DIR/assets/edge-rollback.sh\")\""},{"line":21,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json","text":"\"authoritative and public DNS for chat.gram1.ru\","},{"line":22,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json","text":"\"EDGE-01 public service path at 185.225.35.6\","},{"line":36,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json","text":"\"Cloudflare A record chat.gram1.ru\","},{"line":41,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json","text":"\"chat.gram1.ru A record\","},{"line":52,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json","text":"\"groups.chat.gram1.ru and share.chat.gram1.ru records are unchanged\","},{"line":113,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json","text":"\"compile\": \"QGA only; no first-boot SSH dependency\""},{"line":116,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json","text":"\"host\": \"EDGE-01 185.225.35.6\","},{"line":128,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json","text":"\"chat_fqdn\": \"chat.gram1.ru\","},{"line":130,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/task.json","text":"\"edge01_public_ip\": \"185.225.35.6\","},{"line":4,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/README.txt","text":"- production traffic cutover for chat.gram1.ru from 95.84.154.183 to EDGE-01 185.225.35.6;"},{"line":6,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/tools/cutover.py","text":"CHAT = \"chat.gram1.ru\""},{"line":9,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/tools/cutover.py","text":"EDGE = \"185.225.35.6\""},{"line":16,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/tools/cutover.py","text":"EDGE_SSH = [\"ssh\",\"-o\",\"BatchMode=yes\",\"-o\",\"StrictHostKeyChecking=yes\",\"-o\",\"ConnectTimeout=8\",\"debian@[PRIVATE_IP]\"]"},{"line":138,"path":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/tools/cutover.py","text":"for host in (CHAT,\"groups.chat.gram1.ru\",\"share.chat.gram1.ru\"):"},{"line":120,"path":"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/task.json","text":"\"host\": \"EDGE-01 185.225.35.6\","},{"line":137,"path":"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/task.json","text":"\"edge01_public_ip\": \"185.225.35.6\","},{"line":24,"path":"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/tools/retire_home_forwards.py","text":"EDGE = \"185.225.35.6\""},{"line":26,"path":"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/tools/retire_home_forwards.py","text":"CHAT = \"chat.gram1.ru\""},{"line":30,"path":"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/tools/retire_home_forwards.py","text":"EDGE_SSH = [\"ssh\",\"-o\",\"BatchMode=yes\",\"-o\",\"StrictHostKeyChecking=yes\",\"-o\",\"ConnectTimeout=8\",\"debian@[PRIVATE_IP]\"]"},{"line":207,"path":"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/tools/retire_home_forwards.py","text":"for host in (CHAT,\"groups.chat.gram1.ru\",\"share.chat.gram1.ru\"):"},{"line":274,"path":"/srv/homelab-ops/tasks/snikket-home-forward-retire-v1/tools/retire_home_forwards.py","text":"cmd = EDGE_SSH + [\"sudo\",\"-n\",\"docker\",\"exec\",\"-i\",\"npmplus\",\"sh\",\"-s\"]"},{"line":21,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/task.json","text":"\"authoritative and public DNS for chat.gram1.ru and turn.gram1.ru\","},{"line":42,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/task.json","text":"\"chat.gram1.ru is not modified\","},{"line":43,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/task.json","text":"\"groups.chat.gram1.ru and share.chat.gram1.ru are not modified\","},{"line":113,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/task.json","text":"\"host\": \"EDGE-01 185.225.35.6\","},{"line":127,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/task.json","text":"\"chat_fqdn\": \"chat.gram1.ru\","},{"line":128,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/task.json","text":"\"edge01_public_ip\": \"185.225.35.6\","},{"line":6,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py","text":"CHAT = \"chat.gram1.ru\""},{"line":10,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py","text":"EDGE = \"185.225.35.6\""},{"line":13,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py","text":"EDGE_SSH = [\"ssh\",\"-o\",\"BatchMode=yes\",\"-o\",\"StrictHostKeyChecking=yes\",\"-o\",\"ConnectTimeout=8\",\"debian@[PRIVATE_IP]\"]"},{"line":16,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py","text":"\"_stun._udp.chat.gram1.ru\","},{"line":17,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py","text":"\"_stuns._tcp.chat.gram1.ru\","},{"line":18,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py","text":"\"_turn._udp.chat.gram1.ru\","},{"line":19,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py","text":"\"_turn._tcp.chat.gram1.ru\","},{"line":20,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py","text":"\"_turns._tcp.chat.gram1.ru\","},{"line":198,"path":"/srv/homelab-ops/tasks/snikket-turn-legacy-dns-retire-v1/tools/retire_turn_dns.py","text":"for host in (CHAT,\"groups.chat.gram1.ru\",\"share.chat.gram1.ru\"):"},{"line":8,"path":"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-post-cutover-hardening-design.md","text":"- `chat.gram1.ru` resolves to `185.225.35.6`."},{"line":9,"path":"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-post-cutover-hardening-design.md","text":"- VM150 default route is via `[PRIVATE_IP]`; verified public egress is `185.225.35.6`."},{"line":10,"path":"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-post-cutover-hardening-design.md","text":"- coturn advertises `185.225.35.6/[PRIVATE_IP]`."},{"line":28,"path":"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-post-cutover-hardening-design.md","text":"Verify `chat.gram1.ru`, public Snikket services, VM150 egress and effective coturn external address remain unchanged."},{"line":7,"path":"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-edge-cutover-design.md","text":"Move chat.gram1.ru from home IP 95.84.154.183 to EDGE-01 185.225.35.6. Persistent EDGE-01 and edge-vm inbound transit is already installed and TCP, XMPP STARTTLS, TURN TLS, UDP STUN and HTTPS canaries pass."},{"line":8,"path":"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-edge-cutover-design.md","text":"Before cutover, EDGE-01 ingress is still restricted to source 95.84.154.183, chat.gram1.ru A is still 95.84.154.183, edge-vm table 17777 is ready, and no source rule for VM150 [PRIVATE_IP] is active."},{"line":11,"path":"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-edge-cutover-design.md","text":"CR0080 covers only production traffic cutover: publicize the verified EDGE-01 ingress contract, change only chat.gram1.ru A to 185.225.35.6, wait for DNS convergence, activate VM150 egress through edge-vm and EDGE-01, refresh effective TURN addressing, verify, rollback and seal."},{"line":16,"path":"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-edge-cutover-design.md","text":"EDGE-01 remains manual-operator access only. CR0080 versions the exact public-ingress contract, but the operator applies it from the established root@edge01 session; the pve01 task must not invent an automated EDGE-01 SSH path."},{"line":21,"path":"/srv/homelab-ops/docs/superpowers/specs/2026-08-17-snikket-edge-cutover-design.md","text":"Verification covers HTTPS chat/groups/share; TCP 5000,5222,3478,3479,5349,5350; XMPP STARTTLS; TURN TLS; UDP STUN and relay 60000-60199; VM150 outbound IP 185.225.35.6; DNS convergence; and no effective TURN advertisement containing 95.84.154.183."},{"line":21,"path":"/srv/homelab-ops/docs/superpowers/plans/2026-08-18-gitea-edge-publisher-v3.md","text":"- Use strict SSH host verification; never `StrictHostKeyChecking=no`."},{"line":845,"path":"/srv/homelab-ops/docs/superpowers/plans/2026-08-18-gitea-edge-publisher-v3.md","text":"ssh -o BatchMode=yes -o StrictHostKeyChecking=yes edgeadmin@185.225.35.6 'sudo -n true && hostname -s'"},{"line":3,"path":"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-edge-cutover.md","text":"**Goal:** finish the traffic cutover of chat.gram1.ru to EDGE-01 while preserving rollback and keeping VM150 source-policy disabled until DNS convergence."},{"line":23,"path":"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-edge-cutover.md","text":"- apply: require explicit EDGE-01 public-ingress operator attestation; change only chat.gram1.ru A to 185.225.35.6; wait for authoritative and public convergence; only then install priority-101 source policy for [PRIVATE_IP] via table 17777 and change VM150 gateway to [PRIVATE_IP]; refresh only the Snikket server container if effective TURN addressing still shows the old origin."},{"line":24,"path":"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-edge-cutover.md","text":"- verify: HTTPS, TCP 5000/5222/3478/3479/5349/5350, XMPP STARTTLS, TURN TLS, UDP STUN, VM150 outbound public IP 185.225.35.6, DNS convergence and effective TURN address without 95.84.154.183."},{"line":9,"path":"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-turn-legacy-dns-retirement.md","text":"**Tech Stack:** Bash phase wrappers, Python 3, `homelab-admin`, `qm guest exec`, SSH to edge-vm, Docker/NPMplus, Cloudflare API, `dig`, `curl`, `openssl`, sockets."},{"line":15,"path":"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-turn-legacy-dns-retirement.md","text":"- Do not modify `chat.gram1.ru`, Snikket containers, VM150 routing, edge-vm routing, EDGE-01 nftables, certificate renewal, or home-router forwards."},{"line":33,"path":"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-turn-legacy-dns-retirement.md","text":"- Consumes: CR0080 sealed path (`chat.gram1.ru=185.225.35.6`, VM150 egress through EDGE, effective coturn external address on EDGE), NPMplus local Cloudflare credential path."},{"line":88,"path":"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-turn-legacy-dns-retirement.md","text":"chat.gram1.ru: 185.225.35.6 at all three views"},{"line":89,"path":"/srv/homelab-ops/docs/superpowers/plans/2026-08-17-snikket-turn-legacy-dns-retirement.md","text":"VM150 public egress: 185.225.35.6"},{"line":209,"path":"/srv/homelab-ops/tools/collect_context.py","text":"\"remote\": \"https://git.gram1.ru/homelab-admin/homelab-ops.git\","},{"line":15,"path":"/srv/homelab-ops/kb/AI_CONTEXT_PUBLIC.md","text":"CR0083 is merged/closed. Current P0 sequence starts with Git topology preservation, then canonical post-migration state, recovery/backup refresh, VM160/forum acceptance, permanent new-USA SSH hardening, old-USA observation/retirement, and final seal."},{"line":18,"path":"/srv/homelab-ops/kb/AI_CONTEXT.md","text":"- EDGE: `edgeadmin@185.225.35.6`, key label `edge01-admin-2026`, then `sudo -i`. Do not copy the EDGE private key to pve01."},{"line":19,"path":"/srv/homelab-ops/kb/AI_CONTEXT.md","text":"- Gitea: `https://git.gram1.ru`, repo `homelab-admin/homelab-ops`."},{"line":32,"path":"/srv/homelab-ops/kb/AI_CONTEXT.md","text":"Production NetBird is new USA `46.16.34.129`, Debian 12, hostname `nb2`, NetBird `0.73.2`. Exact deployment caveat: `/api/health` = 404 and combined `:9000/health` = 503; these are not generic readiness gates for this deployment. Old USA `185.139.214.215` has Mailcow/NetBird server stopped and observer disabled, but remains in rollback window. Permanent new-USA operator SSH hardening is still P0."},{"line":28,"path":"/srv/homelab-ops/kb/state/current.json","text":"\"gitea_url\": \"https://git.gram1.ru\","},{"line":86,"path":"/srv/homelab-ops/kb/state/current.json","text":"\"permanent operator SSH key\","},{"line":6,"path":"/srv/homelab-ops/kb/runbooks/02_PROXMOX.md","text":"- SSH readiness does not mean cloud-init/apt is finished."},{"line":3,"path":"/srv/homelab-ops/kb/runbooks/03_EDGE_NGINX.md","text":"Operator path: MobaXterm → `edgeadmin@185.225.35.6` with key `edge01-admin-2026` → `sudo -i`."},{"line":7,"path":"/srv/homelab-ops/kb/runbooks/04_NETBIRD_USA.md","text":"SSH hardening sequence is strict:"},{"line":7,"path":"/srv/homelab-ops/kb/private/access.json","text":"\"command_hint\": \"ssh root@100.100.131.41\","},{"line":20,"path":"/srv/homelab-ops/kb/private/access.json","text":"\"target\": \"185.225.35.6\","},{"line":22,"path":"/srv/homelab-ops/kb/private/access.json","text":"\"command_hint\": \"ssh edgeadmin@185.225.35.6 then sudo -i\","},{"line":63,"path":"/srv/homelab-ops/kb/private/access.json","text":"\"target\": \"https://git.gram1.ru\","},{"line":116,"path":"/srv/homelab-ops/kb/lessons/known_failures.json","text":"\"incident\": \"ssh ... bash -s consumed stdin and disrupted enclosing command flow.\","},{"line":117,"path":"/srv/homelab-ops/kb/lessons/known_failures.json","text":"\"prevention\": \"Use explicit script files/stdin isolation; avoid ssh bash -s inside loops that also read stdin.\","},{"line":378,"path":"/srv/homelab-ops/kb/lessons/known_failures.json","text":"\"incident\": \"SSH became reachable before first-boot/cloud-init apt activity released package locks.\","},{"line":513,"path":"/srv/homelab-ops/kb/lessons/known_failures.json","text":"\"incident\": \"A top-level exit can close the user’s primary SSH shell/session.\","},{"line":529,"path":"/srv/homelab-ops/kb/lessons/known_failures.json","text":"\"OPENSSH PRIVATE KEY\""},{"line":68,"path":"/srv/homelab-ops/kb/lessons/known_failures_public.json","text":"\"incident\": \"ssh ... bash -s consumed stdin and disrupted enclosing command flow.\","},{"line":69,"path":"/srv/homelab-ops/kb/lessons/known_failures_public.json","text":"\"prevention\": \"Use explicit script files/stdin isolation; avoid ssh bash -s inside loops that also read stdin.\""},{"line":219,"path":"/srv/homelab-ops/kb/lessons/known_failures_public.json","text":"\"incident\": \"SSH became reachable before first-boot/cloud-init apt activity released package locks.\","},{"line":297,"path":"/srv/homelab-ops/kb/lessons/known_failures_public.json","text":"\"incident\": \"A top-level exit can close the user’s primary SSH shell/session.\","},{"line":9,"path":"/srv/homelab-ops/kb/current/04_P0_NEXT.md","text":"7. New USA permanent SSH key + key-only hardening with separate-session proof."},{"line":12,"path":"/srv/homelab-ops/kb/current/04_P0_NEXT.md","text":"Do not mix unrelated changes into these gates (e.g. NetBird version upgrade/hostname change during SSH hardening)."},{"line":34,"path":"/srv/homelab-ops/kb/scripts/validate_kb.py","text":"openssh_marker = '"},{"line":46,"path":"/srv/homelab-ops/kb/scripts/validate_kb.py","text":"for bad in ('/etc/msmtp-postbox.secret','/etc/pvepro-relay-gotify.token','/etc/homelab-git-read/token','/var/lib/homelab-private/secrets/','begin openssh private key'):"},{"line":21,"path":"/srv/homelab-ops/kb/scripts/command_guard.py","text":"add('BLOCK','REG-030-MOBAXTERM-TOP-LEVEL-EXIT','Top-level exit can close the operator SSH session.')"},{"line":37,"path":"/srv/homelab-ops/kb/scripts/command_guard.py","text":"add('WARN','REG-007-SSH-STDIN-CONSUMPTION','ssh bash -s can consume stdin; isolate script input.')"},{"line":33,"path":"/srv/homelab-ops/tests/test_cr_2026_0080_snikket_edge_cutover.py","text":"for needle in (\"185.225.35.6\",\"[PRIVATE_IP]\",\"snat to [PRIVATE_IP]\",\"60000-60199\",\"SNIKKET_PROD_INGRESS_JUMP\"):"},{"line":33,"path":"/srv/homelab-ops/tests/test_cr_2026_0081_turn_dns_retire.py","text":"for name in (\"_turn._udp.turn.gram1.ru\",\"_turns._tcp.turn.gram1.ru\",\"_stun._udp.turn.gram1.ru\",\"_turn._udp.chat.gram1.ru\"):"},{"line":69,"path":"/srv/homelab-ops/tests/test_cr_2026_0013_vm160_worker_bootstrap_transition.py","text":"actual_pos = self.apply.index('INSTALLED_WORKER_SHA=\"$(ssh ')"},{"line":82,"path":"/srv/homelab-ops/tests/test_cr_2026_0013_vm160_worker_bootstrap_transition.py","text":"self.assertIn('INSTALLED_WORKER_SHA=\"$(ssh ', self.verify)"},{"line":35,"path":"/srv/homelab-ops/tests/test_cr_2026_0081_home_forward_retire.py","text":"self.assertEqual(self.task[\"source_of_truth\"][\"edge01_public_ip\"], \"185.225.35.6\")"},{"line":74,"path":"/srv/homelab-ops/tests/test_cr_2026_0007_skladchik_reauth.py","text":"self.assertNotIn(\"ssh -t -o\", texts)"},{"line":72,"path":"/srv/homelab-ops/tests/test_cr_2026_0009_skladchik_concurrency_guard.py","text":"\"VALUE=$(ssh ${SSH[@]} $EDGE sudo -n /usr/bin/sha256sum /path \""},{"line":54,"path":"/srv/homelab-ops/tests/test_cr_2026_0007_skladchik_semantic_repair.py","text":"self.assertIn('ssh -tt \"${SSH[@]}\" \"$EDGE\" sudo -n \"$EDGE_SCRIPT\"', wrapper)"},{"line":951,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"URL=https://git.gram1.ru"},{"line":1899,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Avoid multiline Python directly in SSH one-liner unless base64 encoded."},{"line":1967,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"4. The only remaining access convenience item is optional: expose the cluster-admin panel via a VPN-only/internal reverse proxy route such as `cluster-admin.vpn.gram1.ru`."},{"line":2086,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Commands became too slow when they repeatedly ran `homelab-duty-admin-v2`, `appbackupctl restore-check`, `homelab-final-readiness-gate`, full `pvesh` scans, SSH to all nodes, and cloud checks."},{"line":2323,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- SSH works as `debian@[PRIVATE_IP]`."},{"line":2445,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- pve01/pve02/pve03 SSH and Proxmox"},{"line":2454,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Optional ports 80/443 on VM150/170/171 are not hard failures because services may live behind reverse proxy or not expose direct ports."},{"line":2650,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- VM180 should not have unrestricted root SSH access to pve01."},{"line":2803,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"Configure operator-friendly VPN reverse proxy route: cluster-admin.vpn.gram1.ru -> [PRIVATE_IP]:8080"},{"line":2817,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"Do not accept public VPS 188.127.235.6 changed SSH host key without provider-console fingerprint."},{"line":2827,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"3. VM180 creation wait for QGA could be safely interrupted after VM creation; SSH was already working."},{"line":2832,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"8. `http://[PRIVATE_IP]:8080/` is not reachable from a normal browser outside LAN/VPN. Use VPN, SSH tunnel, or internal reverse proxy."},{"line":2850,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"3. сделать независимый external runner только на host с verified SSH fingerprint;"},{"line":2903,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"6. Do not accept the changed public VPS SSH host key for `188.127.235.6` until independently verified from provider console."},{"line":3033,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Use ssh -n in loops/remote commands so ssh does not consume loop stdin."},{"line":3319,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- public VPS `188.127.235.6` is rejected due SSH host-key mismatch."},{"line":3331,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"Use a new or repaired external host only after its provider-console SSH host fingerprint is verified."},{"line":3368,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"Use SSH remote URL without embedded credentials, or HTTPS credential helper outside git config."},{"line":3411,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Do not deploy anything to public VPS 188.127.235.6 until provider-console fingerprint confirms the changed SSH host key."},{"line":3434,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"1. независимый external runner — нужен новый/починенный внешний host и verified SSH fingerprint;"},{"line":4266,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- For remote loops with ssh, use `ssh -n` so ssh does not consume loop stdin."},{"line":4441,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- ssh: `ssh debian@[PRIVATE_IP]`"},{"line":4448,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- ssh: `ssh debian@[PRIVATE_IP]`"},{"line":4455,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- ssh: `ssh debian@[PRIVATE_IP]`"},{"line":4810,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- likely frontend/cache;"},{"line":5033,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- For systemd mask checks, avoid broken local `$()` expansion inside SSH strings."},{"line":5034,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Use direct remote command with single-quoted SSH body when checking systemd state."},{"line":5134,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- SSH failure bursts;"},{"line":5436,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- кто отвечает за reverse proxy;"},{"line":5727,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- отдельный ssh key;"},{"line":6434,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Запрещены `ssh + heredoc + python` и глубокие вложенные кавычки."},{"line":6441,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Public SSH на дачный роутер закрыт."},{"line":6442,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- WG SSH открыт."},{"line":6472,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- not authoritative: public SSH still open."},{"line":6475,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- not authoritative: direct public SSH still open."},{"line":6481,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- REVIEW snapshot: public SSH still open."},{"line":6683,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"| Core | Gitea | https://git.gram1.ru | public | Git |"},{"line":6740,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Закрыли публичный SSH на дачном роутере, оставив WG SSH доступным."},{"line":6777,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- direct public dacha SSH closed, WG SSH open."},{"line":6857,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- line 1156: `### SSH and permissions`"},{"line":7096,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- line 304: `## FORUM_PROD_BULK_IMPORT_PHP85_EMPTY_FRONTEND_20260701`"},{"line":7244,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"| 1156 | `### SSH and permissions` |"},{"line":7455,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"| 304 | `## FORUM_PROD_BULK_IMPORT_PHP85_EMPTY_FRONTEND_20260701` |"},{"line":7540,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- `https://git.gram1.ru`"},{"line":7573,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- `/mnt/staging/netbird-vps-backups/snapshots.`"},{"line":8104,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Proxmox: ssh root@pve01, ssh root@pve02, ssh root@pve03."},{"line":8105,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Edge VM: ssh debian@[PRIVATE_IP], использовать sudo, root-login не использовать."},{"line":8106,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Nextcloud VM: ssh debian@[PRIVATE_IP]."},{"line":8107,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Forum-prod: сначала ssh root@pve02, затем ssh -i [SENSITIVE_PATH] root@[PRIVATE_IP]."},{"line":8279,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- SSH port: 2222."},{"line":8280,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- SSH security-level: private."},{"line":8285,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- SFTP denied for admin in log; SSH CLI works."},{"line":8293,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- service ssh enabled."},{"line":8303,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Netcraze SSH CLI is not a normal POSIX shell."},{"line":8329,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- SSH Server through NetBird: Disabled."},{"line":8344,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- pve01 owns many backup/offhost/restore/health/security/NetBird VPS/rclone/sops/scrutiny jobs."},{"line":8404,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- pve01 timers cover VPN/NetBird health, health metrics, smartctl, disk space, MkDocs refresh, VPS identity audit, storage capacity, quality gate, evidence catalog, backup freshness, docker health, Filebrowser backup/offhost/restore, NPMplus/Kuma backup, NetBird VPS backup/offhost, Authentik/Gitea/Vaultwarden backup, SOPS secret coverage, mail cloud upload/restore, Immich/Memos/Paperless backup/offhost/restore, auto backup, edge-vm vzdump, secret sanity."},{"line":8552,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Role: edge application host / reverse proxy / monitoring / backup automation host."},{"line":8620,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- git.gram1.ru -> [PRIVATE_IP]."},{"line":8653,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- git.gram1.ru -> http://127.0.0.1:3002, cert=29, ssl_forced=1, enabled=1."},{"line":8702,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- cert=29: git.gram1.ru, expires 2026-09-13 17:36:55."},{"line":8943,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- NetBird VPS backup: STATUS=OK, snapshot under /mnt/staging/netbird-vps-backups/snapshots."},{"line":8944,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- NetBird VPS offhost: STATUS=OK to pve02."},{"line":8945,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- NetBird VPS restore validation: STATUS=OK, archive SHA256 recorded."},{"line":9006,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- https://git.gram1.ru"},{"line":9062,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- external canary checks include nc.gram1.ru, git.gram1.ru, auth.gram1.ru, backup.gram1.ru."},{"line":9119,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Do not use exit 1 in interactive SSH sessions."},{"line":9131,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Reason: nested Python inside SSH lost quoting and produced SyntaxError."},{"line":9150,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Do not use exit 1 in interactive SSH sessions."},{"line":9162,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"### SSH and permissions"},{"line":9182,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- PVE nodes expose SSH :22, Proxmox :8006 and node-exporter :9100."},{"line":9202,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- SSH permission correction proof: 160_PVE_ROOT_AUTHORIZED_KEYS_PERMISSIONS_FIXED.txt."},{"line":9209,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Valid closure condition: target permissions checked with stat -L are 600 for authorized_keys files and [SENSITIVE_PATH] is 700."},{"line":9305,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Reverse proxy: NPMplus on edge-vm, container npmplus, host networking, admin bound to 127.0.0.1:81."},{"line":9319,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"| git.gram1.ru | http://127.0.0.1:3002 | edge-vm / gitea | gitea backup/offhost/restore |"},{"line":9400,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Do not use exit 1 in interactive SSH sessions."},{"line":9528,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Use 154 for Prometheus settled targets and 163 for symlink-aware SSH target permissions."},{"line":9870,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Discovery proof records DNS, HTTPS/TLS headers, reverse-proxy candidates, compose files, domain references and homepage config candidates without printing secrets."},{"line":10053,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Netcraze routerbackup SSH access is read-only for backup: show running-config works, but ACL/config commands are denied."},{"line":10093,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- New VM identity confirmed: forum-prod / forum-prod.gram1.ru, Debian 12 bookworm, SSH OK, qemu-agent OK, chrony OK."},{"line":10096,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Beget-compatible profile applied: memory_limit 256M, post/upload 1024M, max_input_vars 10000, MariaDB utf8mb4/utf8mb4_unicode_ci, innodb_buffer_pool_size 2G."},{"line":10101,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Bulk import of five forums reached DB/files/nginx/php-fpm ready state, but frontend body stayed empty under PHP 8.5.7."},{"line":10113,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Final result: all five frontend/admin HTTP 200, www redirects 301, internal_data 403, no new XenForo errors."},{"line":10119,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Applies to: SSH enter/exit points, VM prompt confirmations, snapshot confirmations, file copy confirmations, successful health checks, and other obvious next-step transitions."},{"line":10433,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"CHECK-4: команда не должна иметь вложенный ssh с несколькими уровнями кавычек."},{"line":10464,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"Нельзя писать sqlite SQL вида j.type in ('object','array') внутри ssh '...'."},{"line":10465,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"Для sqlite через ssh использовать SQL без одинарных кавычек: char(36), length(j.atom), двойные внешние кавычки, либо отдельный файл."},{"line":10470,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"edge-vm: ssh debian@[PRIVATE_IP], внутри использовать sudo."},{"line":10471,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"pve02/pve03: ssh root@pve02 или ssh root@pve03."},{"line":10476,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"Снова был использован SQL JSON-path в одинарных кавычках внутри ssh '...'."},{"line":10526,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"27. Ошибка: Python heredoc внутри ssh сломал not_ok диагностику."},{"line":10549,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"Факт: Python -c внутри ssh потерял кавычки вокруг /tmp/prom-targets-settled.json, data, activeTargets, labels, job, health."},{"line":10629,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- A portal card is openable only when DNS, reverse-proxy host mapping, and target content are all valid."},{"line":10632,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Context: attempted Netcraze router ACL apply through SSH stdin/multiline for Homepage Moscow Router monitor fix."},{"line":10638,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Context: ACL syntax read-only probe loop executed only one command because ssh consumed the loop stdin."},{"line":10640,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Fix pattern: use ssh -n or redirect SSH stdin away from the command-list loop for all future SSH-in-loop probes."},{"line":10702,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Rule: do not proceed with OS baseline until SSH failure is diagnosed; likely old known_hosts key or cloud-init/root-key issue."},{"line":10705,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Context: VM160 first SSH proof after rebuild."},{"line":10706,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Issue: command substitution $(hostname) inside nested ssh was expanded on pve02 before entering VM160."},{"line":10708,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Rule: for nested SSH identity checks, run literal hostname commands without local command substitution."},{"line":10714,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Rule: avoid nested $(...) in VM SSH proofs; use literal remote commands and clean proof."},{"line":10718,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Issue: nested SSH quoting expanded shell variables incorrectly, producing gzip checks against empty .gz and blank TAR_TOP lines."},{"line":10722,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"## FORUM_PROD_BULK_IMPORT_PHP85_EMPTY_FRONTEND_20260701"},{"line":10752,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"- Rule: avoid nested SSH heredoc/Python for this task; use simpler commands, generated nginx configs, or uploaded/local script files."},{"line":11033,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"А самая критичная VM130 edge-vm находится на pve03 и держит reverse proxy, monitoring, backup automation и Docker application host. При этом pve03 — самый ограниченный по диску: local-lvm уже был самым constrained, потому что VM130 имеет 96G OS + 150G media/data, а pve03 staging доходил до 77% при WARN 80%."},{"line":11102,"path":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md","text":"Файл жёстко требует перед инфраструктурными командами проверять truth files, не использовать огромные paste, не использовать `ssh + heredoc + python`, валидировать скрипты и не печатать секреты. Это оставить как закон."},{"line":22,"path":"/etc/pve/HOMEPAGE_BACKUP_COVERAGE_MATRIX.md","text":"| Gitea | https://git.gram1.ru | 185 | 89 | 105 | EVIDENCE_FOUND_REVIEW |"},{"line":156,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Proxmox: ssh root@pve01, ssh root@pve02, ssh root@pve03."},{"line":157,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Edge VM: ssh debian@[PRIVATE_IP], использовать sudo, root-login не использовать."},{"line":158,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Nextcloud VM: ssh debian@[PRIVATE_IP]."},{"line":159,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Forum-prod: сначала ssh root@pve02, затем ssh -i [SENSITIVE_PATH] root@[PRIVATE_IP]."},{"line":331,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- SSH port: 2222."},{"line":332,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- SSH security-level: private."},{"line":337,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- SFTP denied for admin in log; SSH CLI works."},{"line":345,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- service ssh enabled."},{"line":355,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Netcraze SSH CLI is not a normal POSIX shell."},{"line":381,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- SSH Server through NetBird: Disabled."},{"line":396,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- pve01 owns many backup/offhost/restore/health/security/NetBird VPS/rclone/sops/scrutiny jobs."},{"line":456,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- pve01 timers cover VPN/NetBird health, health metrics, smartctl, disk space, MkDocs refresh, VPS identity audit, storage capacity, quality gate, evidence catalog, backup freshness, docker health, Filebrowser backup/offhost/restore, NPMplus/Kuma backup, NetBird VPS backup/offhost, Authentik/Gitea/Vaultwarden backup, SOPS secret coverage, mail cloud upload/restore, Immich/Memos/Paperless backup/offhost/restore, auto backup, edge-vm vzdump, secret sanity."},{"line":604,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Role: edge application host / reverse proxy / monitoring / backup automation host."},{"line":672,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- git.gram1.ru -> [PRIVATE_IP]."},{"line":705,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- git.gram1.ru -> http://127.0.0.1:3002, cert=29, ssl_forced=1, enabled=1."},{"line":754,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- cert=29: git.gram1.ru, expires 2026-09-13 17:36:55."},{"line":995,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- NetBird VPS backup: STATUS=OK, snapshot under /mnt/staging/netbird-vps-backups/snapshots."},{"line":996,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- NetBird VPS offhost: STATUS=OK to pve02."},{"line":997,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- NetBird VPS restore validation: STATUS=OK, archive SHA256 recorded."},{"line":1058,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- https://git.gram1.ru"},{"line":1114,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- external canary checks include nc.gram1.ru, git.gram1.ru, auth.gram1.ru, backup.gram1.ru."},{"line":1171,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Do not use exit 1 in interactive SSH sessions."},{"line":1183,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Reason: nested Python inside SSH lost quoting and produced SyntaxError."},{"line":1202,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Do not use exit 1 in interactive SSH sessions."},{"line":1214,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"### SSH and permissions"},{"line":1234,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- PVE nodes expose SSH :22, Proxmox :8006 and node-exporter :9100."},{"line":1254,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- SSH permission correction proof: 160_PVE_ROOT_AUTHORIZED_KEYS_PERMISSIONS_FIXED.txt."},{"line":1261,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Valid closure condition: target permissions checked with stat -L are 600 for authorized_keys files and [SENSITIVE_PATH] is 700."},{"line":1357,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Reverse proxy: NPMplus on edge-vm, container npmplus, host networking, admin bound to 127.0.0.1:81."},{"line":1371,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"| git.gram1.ru | http://127.0.0.1:3002 | edge-vm / gitea | gitea backup/offhost/restore |"},{"line":1452,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Do not use exit 1 in interactive SSH sessions."},{"line":1580,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Use 154 for Prometheus settled targets and 163 for symlink-aware SSH target permissions."},{"line":1922,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Discovery proof records DNS, HTTPS/TLS headers, reverse-proxy candidates, compose files, domain references and homepage config candidates without printing secrets."},{"line":2105,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Netcraze routerbackup SSH access is read-only for backup: show running-config works, but ACL/config commands are denied."},{"line":2145,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- New VM identity confirmed: forum-prod / forum-prod.gram1.ru, Debian 12 bookworm, SSH OK, qemu-agent OK, chrony OK."},{"line":2148,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Beget-compatible profile applied: memory_limit 256M, post/upload 1024M, max_input_vars 10000, MariaDB utf8mb4/utf8mb4_unicode_ci, innodb_buffer_pool_size 2G."},{"line":2153,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Bulk import of five forums reached DB/files/nginx/php-fpm ready state, but frontend body stayed empty under PHP 8.5.7."},{"line":2165,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Final result: all five frontend/admin HTTP 200, www redirects 301, internal_data 403, no new XenForo errors."},{"line":2171,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Applies to: SSH enter/exit points, VM prompt confirmations, snapshot confirmations, file copy confirmations, successful health checks, and other obvious next-step transitions."},{"line":2515,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Recommended future path: establish VPN/NetBird/WireGuard or reverse-proxy/private management endpoint first; then issue DNS-01 certificate on a trusted node and deploy cert/key to the router only over that private path."},{"line":2531,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Public SSH 194.33.48.131:22 closed."},{"line":2539,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Public SSH remains closed."},{"line":2546,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Public SSH closed."},{"line":2558,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Supersedes failed/partial proof 669 because direct SSH was open during that run."},{"line":2564,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Direct dacha public SSH is closed; WG SSH remains open."},{"line":2596,"path":"/etc/pve/31_HOMELAB_REFERENCE.md","text":"- Direct public SSH to dacha router is closed."},{"line":2597,"... +SYSTEMD_VPS_NETBIRD_UNITS={"count":9,"units":[{"err":"","lines":["# /etc/systemd/system/homelab-external-probe-vps-health.service","Description=Homelab external probe VPS strategy health","After=network-online.target","ExecStart=/usr/local/sbin/homelab-external-probe-vps-health"],"rc":0,"unit":"homelab-external-probe-vps-health.service"},{"err":"","lines":["# /etc/systemd/system/homelab-external-probe-vps-health.timer","Description=Homelab external probe VPS strategy health timer"],"rc":0,"unit":"homelab-external-probe-vps-health.timer"},{"err":"","lines":["# /etc/systemd/system/homelab-vps-identity-audit.service","Description=Homelab VPS SSH identity audit","ExecStart=/usr/local/sbin/homelab-vps-identity-audit"],"rc":0,"unit":"homelab-vps-identity-audit.service"},{"err":"","lines":["# /etc/systemd/system/homelab-vps-identity-audit.timer","Description=Run Homelab VPS SSH identity audit"],"rc":0,"unit":"homelab-vps-identity-audit.timer"},{"err":"","lines":["# /etc/systemd/system/netbird-peers-health.service","Description=NetBird peers health check","After=network-online.target","ExecStart=/root/netbird-peers-health.sh"],"rc":0,"unit":"netbird-peers-health.service"},{"err":"","lines":["# /etc/systemd/system/netbird-peers-health.timer","Description=Run NetBird peers health check"],"rc":0,"unit":"netbird-peers-health.timer"},{"err":"","lines":["# /etc/systemd/system/netbird.service","Description=NetBird mesh network client","ConditionFileIsExecutable=/usr/bin/netbird","After=network.target syslog.target","ExecStart=/usr/bin/netbird \"service\" \"run\" \"--log-level\" \"info\" \"--daemon-addr\" \"unix:///var/run/netbird.sock\" \"--log-file\" \"/var/log/netbird/client.log\"","StandardOutput=file:/var/log/netbird/netbird.out","StandardError=file:/var/log/netbird/netbird.err","EnvironmentFile=-/etc/sysconfig/netbird","Environment=SYSTEMD_UNIT=netbird"],"rc":0,"unit":"netbird.service"},{"err":"","lines":["# /usr/lib/systemd/system/pveproxy.service","Description=PVE API Proxy Server","ConditionPathExists=/usr/bin/pveproxy","Wants=pve-cluster.service","Wants=pvedaemon.service","Wants=ssh.service","Wants=pve-storage.target","After=pve-storage.target","After=pve-cluster.service","After=pvedaemon.service","After=ssh.service","ExecStartPre=-/usr/bin/pvecm updatecerts --silent","ExecStart=/usr/bin/pveproxy start","ExecStartPost=-sh -c '[ ! -e /var/log/pveam.log ] && /usr/bin/pveupdate'","ExecStop=/usr/bin/pveproxy stop","ExecReload=/usr/bin/pveproxy restart","PIDFile=/run/pveproxy/pveproxy.pid"],"rc":0,"unit":"pveproxy.service"},{"err":"","lines":["# /usr/lib/systemd/system/spiceproxy.service","Description=PVE SPICE Proxy Server","ConditionPathExists=/usr/bin/spiceproxy","Wants=pveproxy.service","After=pveproxy.service","ExecStart=/usr/bin/spiceproxy start","ExecStop=/usr/bin/spiceproxy stop","ExecReload=/usr/bin/spiceproxy restart","PIDFile=/run/pveproxy/spiceproxy.pid"],"rc":0,"unit":"spiceproxy.service"}]} +SSH_METADATA={"config":[{"line":21,"path":"/etc/ssh/ssh_config","text":"Host *"}],"key_metadata":[{"mode":"0o600","path":"[SENSITIVE_PATH] Host git.gram1.ru found: line 72 ",{"algorithm":"ssh-ed25519","line_sha256":"956d4c61a41d7547b9c63dbbf4f31730f0579f638a5bcdab9b299154bc17913a"}],"query":"git.gram1.ru","rc":0},{"err":"","matches":[],"query":"chat.gram1.ru","rc":1},{"err":"","matches":[],"query":"newfi-staging.gram1.ru","rc":1}]} +PUBLIC_FRONTEND_SSH_KEYSCAN=[{"err":"","host":"185.225.35.6","keys":[{"algorithm":"ecdsa-sha2-nistp256","line_sha256":"ac034f62bb300d5803fb554720d8e893f60de04d2d7b4e4173927a22898844a0"},{"algorithm":"ssh-rsa","line_sha256":"d50e3d759085b7fed47aabfda87e5b8898baaa901558b2ceb86669818bb31367"},{"algorithm":"ssh-ed25519","line_sha256":"7958f5c47286d25debbcdf39d333b2ae99c27a851b09a89e750e40e5f2646d75"}],"rc":0},{"err":"","host":"git.gram1.ru","keys":[{"algorithm":"ssh-rsa","line_sha256":"008c80ae05353cedff97a531fbca0f4e626dfbe16822ed82f9868495e887a6e2"},{"algorithm":"ecdsa-sha2-nistp256","line_sha256":"8ef2fdb8c060387ce82f66221713faeef4c7d27895d2dbd92bc87aa375b94172"},{"algorithm":"ssh-ed25519","line_sha256":"e13c7f631cc347a1c52f5816326189542f8d3a1ca6d31ad596aa422aa9e9ac3e"}],"rc":0},{"err":"","host":"chat.gram1.ru","keys":[{"algorithm":"ssh-rsa","line_sha256":"15a9ce01047aa6d86b0a7f323187062c126d2f7f8c3d5b7cdf47b89f96f94f7a"},{"algorithm":"ecdsa-sha2-nistp256","line_sha256":"db8a2e358b3d2b62ad43991f34fdc76ee85a43ea39efaf9bb2420a89ee42fdb4"},{"algorithm":"ssh-ed25519","line_sha256":"d5f95e577619b811e15cb286ce4661f3835450b79e138717f7283ffbb4eabc63"}],"rc":0},{"err":"getaddrinfo newfi-staging.gram1.ru: Name or service not known\ngetaddrinfo newfi-staging.gram1.ru: Name or service not known\ngetaddrinfo newfi-staging.gram1.ru: Name or service not known\n","host":"newfi-staging.gram1.ru","keys":[],"rc":1}] +EXPLICIT_SSH_CANDIDATES=[{"host":"[PRIVATE_IP]","source":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/verify.sh:9","user":"root"},{"host":"[PRIVATE_IP]","source":"/srv/homelab-ops/tasks/cluster-cloud-quorum-vm160-canary-v1/phases/preflight.sh:9","user":"root"},{"host":"[PRIVATE_IP]","source":"/srv/homelab-ops/tasks/snikket-edge-cutover-v1/tools/cutover.py:16","user":"debian"},{"host":"185.225.35.6","source":"/srv/homelab-ops/docs/superpowers/plans/2026-08-18-gitea-edge-publisher-v3.md:845","user":"edgeadmin"},{"host":"100.100.131.41","source":"/srv/homelab-ops/kb/private/access.json:7","user":"root"},{"host":"[PRIVATE_IP]","source":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:2323","user":"debian"},{"host":"[PRIVATE_IP]","source":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:4448","user":"debian"},{"host":"[PRIVATE_IP]","source":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:4455","user":"debian"},{"host":"pve01","source":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:8104","user":"root"},{"host":"pve02","source":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:8104","user":"root"},{"host":"pve03.","source":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:8104","user":"root"},{"host":"[PRIVATE_IP].","source":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:8106","user":"debian"},{"host":"[PRIVATE_IP].","source":"/etc/pve/HOMELAB_FULL_HANDOFF_HISTORICAL_2026-07-09.md:8107","user":"root"},{"host":"[PRIVATE_IP]","source":"/etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md:3109","user":"edgeadmin"},{"host":"pve03","source":"/etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md:3267","user":"root"},{"host":"[PRIVATE_IP]","source":"/root/post-reboot-pve01-check.sh:32","user":"debian"}] +TRUSTED_FRONTEND_SSH_READONLY_ATTEMPTS=[] +FINAL_DECISION=NO_TRUSTED_BEGET_FRONTEND_SSH_PATH_VERIFIED +VPS_FRONTEND_DISCOVERY6_END=true OUTPUT_END CHAT_OUTPUT_END