diff --git a/runtime/history/NEWFI-260920-NEWFDOM-MESSENGERS214-BASE213.json b/runtime/history/NEWFI-260920-NEWFDOM-MESSENGERS214-BASE213.json new file mode 100644 index 00000000..d9ed6653 --- /dev/null +++ b/runtime/history/NEWFI-260920-NEWFDOM-MESSENGERS214-BASE213.json @@ -0,0 +1,38 @@ +{ + "schema_version": 1, + "channel": "homelab-runtime", + "command_id": "NEWFI-260920-NEWFDOM-MESSENGERS214-BASE213", + "status": "OK", + "rc": 0, + "host": "pve01", + "mode": "read-only", + "component": "newfi-newfdom-messengers214-backupv2-base-release-authority-aware-readonly", + "started_at_utc": "2026-09-20T02:30:57Z", + "finished_at_utc": "2026-09-20T02:30:57Z", + "reference_register_checked": true, + "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", + "error_register_checked": true, + "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", + "command_sha256": "65d18e767349b7319c7197b07d4122dae093e606f12244bfd676798e5606e937", + "duplicate_failed_command_blocked": false, + "block_reason": null, + "execution_started": true, + "change_declared": false, + "result_contract_valid": true, + "result_contract_status": null, + "result_contract_error": null, + "command_rc": 0, + "changes_made": false, + "rollback_started": false, + "rollback_restored": null, + "mutation_outcome": "NO_MUTATION", + "sanitized": true, + "secrets_included": false, + "private_addresses_included": false, + "raw_evidence_retained_locally": true, + "raw_evidence_sha256": "417d54bc3ee03b085ea2894f80cd2f3c4e8df0652fe89b0f35fa9f5df73e61c3", + "sanitized_output_sha256": "c2da47960582cda8715a7fd595b714f908f504dd277254ed2b277fdb5c900bf3", + "output_truncated_in_json": false, + "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", + "output": "NEWFI213_BEGIN=true\nMODE=read-only\nIMMUTABLE_PLAN_CHECK=PASS\nCHANGES_MADE=false\nMUTATION_OUTCOME=NO_MUTATION\nRUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d\nRUNNER_PIN_MATCH=true\nERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0\nERROR_REGISTER_PIN_MATCH=true\nREFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66\nREFERENCE_PIN_MATCH=true\nWRAPPER_SHA256=5077444065c732451cb84898680f62361b21a24ef9eb4f191253e82ead524ea2\nWRAPPER_PIN_MATCH=true\nOVERLAY_SHA256=ca6876fa9c3b1465b045344a8ad46962de16972c7dadc4ac6257c7d225047793\nOVERLAY_PIN_MATCH=true\nPRODUCTION_ACCEPTANCE_SHA256=708a481f6719ff1ed8d0afec1902650ed36ac631e980611b015419f504f16df0\nPRODUCTION_ACCEPTANCE_PIN_MATCH=true\nAUTHORITY_PIN_MATCH=true\nBACKUP_SOURCE_PIN_MATCH=true\nACCEPTED_RELEASE=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1\nBASE_RELEASE=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1\nMATCHING_FILE_COUNT=26\nFILE_1_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ACCEPTANCE.json\nFILE_1_BYTES=517\nFILE_1_SHA256=904dfccad43b55ad0b53cfbd29fb45fee36fd0fbfa5afa58da5c3cca0ee7153b\nFILE_1_CONTEXT_LINE_COUNT=1\nFILE_1_L1={\"command_id\":\"SUPPORT-260916-INFRA-DR-BACKUPV2-FINAL-REPLACEMENT-PLAN-STABILITY-SCHEMA-RCA-FIX-VALIDATE-PROMOTE-CUTOVER-SEAL-486R1\",\"ct112_manifest_sha256\":\"1ac4f16a54bcdd8b896c9a131abef966d315256aa56da3f83ed0b30845c4fcb0\",\"ct112_payload_bytes\":223875308,\"ct112_payload_sha256\":\"d443b85e76e806830fd9c372d0a09fc5b91ca9c9d35c44fb0ad7968cc996d01d\",\"legacy_ids\":[\"node-pve01\",\"md-relay\",\"spb-ingress\",\"us-netbird\",\"router-netcraze\",\"xf-codevipe\",\"xf-zakrutim\",\"control-bootstrap-v2\"],\"representative_pass\":9,\"schema\":1}\nFILE_2_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/RELEASE_MANIFEST.json\nFILE_2_BYTES=3301\nFILE_2_SHA256=a36dd0db88592ec3d355ef43e6b218da6c9ad649e9ccf8c8f680b96eb23a156e\nFILE_2_CONTEXT_LINE_COUNT=1\nFILE_2_L1={\"files\":{\"ACCEPTANCE.json\":{\"bytes\":517,\"mode\":256,\"sha256\":\"904dfccad43b55ad0b53cfbd29fb45fee36fd0fbfa5afa58da5c3cca0ee7153b\"},\"candidate411/CONTRACT.json\":{\"bytes\":2077,\"mode\":256,\"sha256\":\"a414715f1b91b3701c52073709a80c38218f76e3961618e8af61489211e50c01\"},\"candidate411/PLAN265_REFERENCE.json\":{\"bytes\":7210,\"mode\":256,\"sha256\":\"8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757\"},\"candidate411/PROVENANCE.json\":{\"bytes\":4310,\"mode\":256,\"sha256\":\"303f44dc8e91c8e6a675f9ea406ce4b7532ed98440a9b6807fa593ede9d33254\"},\"candidate411/adapter.py\":{\"bytes\":2989,\"mode\":320,\"sha256\":\"18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3\"},\"candidate411/guest266.py\":{\"bytes\":7385,\"mode\":256,\"sha256\":\"86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868\"},\"candidate411/large264.py\":{\"bytes\":16514,\"mode\":256,\"sha256\":\"5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a\"},\"candidate411/nodeplan263.py\":{\"bytes\":4603,\"mode\":256,\"sha256\":\"db5c16ee24d25aa\nFILE_3_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py\nFILE_3_BYTES=10955\nFILE_3_SHA256=8cac6e627d5e7abe5d3163cdd24911995a1e3f45c776f9373c2aa68784f5ec6c\nFILE_3_CONTEXT_LINE_COUNT=98\nFILE_3_L1=#!/usr/bin/env python3\nFILE_3_L2=from __future__ import annotations\nFILE_3_L3=import hashlib,json,os,secrets,shutil,stat,subprocess,sys,time\nFILE_3_L4=from pathlib import Path\nFILE_3_L5=ROOT=Path(__file__).resolve().parent\nFILE_3_L6=CAND=ROOT/'candidate411'\nFILE_3_L7=if str(CAND) not in sys.path:sys.path.insert(0,str(CAND))\nFILE_3_L8=import guest266,large264 as large,pve265,pve282,transport261 as t,v2core as core\nFILE_3_L9=CT_ID='guest-pve01-lxc-112'\nFILE_3_L10=PLAN=CAND/'PLAN265_REFERENCE.json'\nFILE_3_L11=PLAN_SHA='8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757'\nFILE_3_L12=RCLONE=Path('/usr/bin/rclone'); RCLONE_SHA='20df3d5eac5d4e187ce638e4216354e7fa6778c3c51f0caf48c48acca9686354'\nFILE_3_L13=RCLONE_CFG=Path('/root/.config/rclone/[SENSITIVE_FILE]')\nFILE_3_L14=BINDING_FP='ed48c1345771c3e0f50db9516f13f26bb66047283e2f883d5df272f15e2282b8'\nFILE_3_L15=VZDUMP=Path('/usr/bin/vzdump'); VZDUMP_SHA='21339d009afd55cb7ec613b64c1f493b2a7dbd18b85b4d712909ef6d6710f4ea'\nFILE_3_L16=ZSTD=Path('/usr/bin/zstd'); ZSTD_SHA='3eb7744cf8fd1ecd46d617098ee870b3e7911fe570ffaba662c1c5b7d6456020'\nFILE_3_L26= p=subprocess.run(argv,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=45,check=False,env=ENV)\nFILE_3_L27= if p.returncode!=0 or len(p.stdout)>4*1024*1024:raise RuntimeError('PVESH_READ_FAILED')\nFILE_3_L28= try:return json.loads(p.stdout.decode('utf-8','strict'))\nFILE_3_L29= except (ValueError,UnicodeError):raise RuntimeError('PVESH_JSON') from None\nFILE_3_L30=\nFILE_3_L31=def _baseline():\nFILE_3_L32= b=t.read_secure(PLAN,1024*1024,private=True)[0]\nFILE_3_L33= if t.sha(b)!=PLAN_SHA:raise RuntimeError('PLAN_REFERENCE_IDENTITY')\nFILE_3_L34= try:return json.loads(b.decode('utf-8','strict'))\nFILE_3_L35= except (ValueError,UnicodeError):raise RuntimeError('PLAN_REFERENCE_JSON') from None\nFILE_3_L36=\nFILE_3_L37=def _defaults():\nFILE_3_L38= p=Path('/etc/vzdump.conf');st=os.stat(p,follow_symlinks=False)\nFILE_3_L100= return {'path':str(path),'sha256':want}\nFILE_3_L101=\nFILE_3_L102=def _private_config():\nFILE_3_L103= b,identity=t.read_secure(RCLONE_CFG,t.MAX_CONFIG_BYTES,private=True)\nFILE_3_L104= text=b.decode('utf-8-sig','strict');entries=core.inventory_from_config(text);binding=t.binding(text)\nFILE_3_L105= if len(entries)!=13 or len({x['account_id'] for x in entries})!=13:raise RuntimeError('RCLONE_INVENTORY')\nFILE_3_L106= if hashlib.sha256(binding.encode()).hexdigest()!=BINDING_FP:raise RuntimeError('RCLONE_BINDING')\nFILE_3_L107= return b,entries,binding\nFILE_3_L108=\nFILE_3_L109=def _write_private(path,data):\nFILE_3_L118=class LazyReader:\nFILE_3_L119= def __init__(self,work,entries,binding,guard,deadline,logical_id):\nFILE_3_L120= self.work=Path(work);self.entries=entries;self.binding=binding;self.guard=guard;self.deadline=deadline;self.logical_id=logical_id\nFILE_3_L121= self.snapshot=self.work/'rclone.snapshot';self.inner=None\nFILE_3_L122= def _ensure(self):\nFILE_3_L123= if self.inner is not None:return self.inner\nFILE_3_L124= b,e,bind=_private_config()\nFILE_3_L125= if e!=self.entries or bind!=self.binding:raise RuntimeError('RCLONE_SEMANTIC_DRIFT')\nFILE_3_L126= _write_private(self.snapshot,b)\nFILE_3_L127= self.inner=large.ReadOnlyLarge(str(RCLONE),self.snapshot,e,bind,self.guard,self.work,self.deadline,self.logical_id)\nFILE_3_L128= return self.inner\nFILE_3_L129= def list_generations(self,*a,**k):return self._ensure().list_generations(*a,**k)\nFILE_3_L130= def get(self,*a,**k):return self._ensure().get(*a,**k)\nFILE_3_L131= def read_stream(self,*a,**k):return self._ensure().read_stream(*a,**k)\nFILE_3_L132= def close(self):\nFILE_3_L135=\nFILE_3_L136=def selftest():\nFILE_3_L137= if _sha(PLAN,1024*1024)!=PLAN_SHA:return False\nFILE_3_L138= for p,h in ((RCLONE,RCLONE_SHA),(VZDUMP,VZDUMP_SHA),(ZSTD,ZSTD_SHA)):\nFILE_3_L139= if _sha(p)!=h:return False\nFILE_3_L140= return True\nFILE_3_L141=\nFILE_3_L142=def run_target(target,state_writer=None):\nFILE_3_L143= if not isinstance(target,dict) or target.get('logical_id')!=CT_ID or target.get('vmid')!=112:return (_ for _ in ()).throw(RuntimeError('CT112_DESCRIPTOR'))\nFILE_3_L144= if _sha(PLAN,1024*1024)!=PLAN_SHA:raise RuntimeError('PLAN_REFERENCE_IDENTITY')\nFILE_3_L145= tools={'vzdump':_tool(VZDUMP,VZDUMP_SHA),'zstd':_tool(ZSTD,ZSTD_SHA)};_tool(RCLONE,RCLONE_SHA)\nFILE_3_L146= pvesh_sha=_sha(PVESH);now=int(time.time());current=_fresh_plan(now);job=_select_live_ct112(current,now)\nFILE_3_L147= expected=job['config_sha256'];mapping=_canonical_host_mapping()\nFILE_3_L148= def producer_guard():\nFILE_3_L153= pve282.live_gate(fresh,str(PVESH),pvesh_sha,expected)\nFILE_3_L154= producer_guard()\nFILE_3_L155= cfg,entries,binding=_private_config()\nFILE_3_L156= generation='V2-'+time.strftime('%Y%m%dT%H%M%SZ',time.gmtime())+'-'+secrets.token_hex(6)\nFILE_3_L157= work=STAGING/('backup-v2-'+generation);work.mkdir(mode=0o700,parents=False,exist_ok=False)\nFILE_3_L158= deadline=time.monotonic()+21600\nFILE_3_L159= def cloud_guard():\nFILE_3_L160= _,e,b=_private_config()\nFILE_3_L161= if e!=entries or b!=binding:raise RuntimeError('RCLONE_SEMANTIC_DRIFT')\nFILE_3_L162= writer=large.CloudLarge(str(RCLONE),RCLONE_CFG,entries,binding,cloud_guard,work/'upload',deadline)\nFILE_3_L163= reader=LazyReader(work/'restore',entries,binding,cloud_guard,deadline,CT_ID)\nFILE_3_L164= def produce(j,w):return pve282.produce(j,w,tools,producer_guard,mapping=mapping,expected_config=expected,seconds=14400,emit=lambda k,v:None)\nFILE_3_L165= def compression(path):\nFILE_3_L166= p=subprocess.run([str(ZSTD),'--test','--quiet','--',str(path)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,timeout=3600,check=False,env=ENV)\nFILE_3_L167= return p.returncode==0\nFILE_3_L168= try:\nFILE_3_L169= r=guest266.run_flow(job,work,entries,writer,reader,produce,producer_guard,cloud_guard,compression,generation,lambda k,v:None)\nFILE_3_L170= reps=r.get('replicas')\nFILE_3_L171= if not isinstance(reps,list) or len(reps)!=2 or len({x.get('account_id') for x in reps})!=2:raise RuntimeError('REPLICA_PHYSICAL_INDEPENDENCE')\nFILE_3_L172= if r.get('archive_cloud_restore_verified') is not True or r.get('local_payload_files_remaining')!=0:raise RuntimeError('CT112_RESTORE_CONTRACT')\nFILE_3_L173= out={**r,'status':'OK','cloud_only_independent_restore_verified':True,'local_payload_files_remaining':0}\nFILE_3_L174= reader.close();shutil.rmtree(work)\nFILE_3_L175= if work.exists():raise RuntimeError('CT112_WORKSPACE_CLEANUP')\nFILE_3_L176= if state_writer is not None:\nFILE_3_L177= state_writer(CT_ID,'SUCCESS',generation=r['generation'],payload_sha256=r['payload_sha256'],manifest_sha256=r['manifest_sha256'],replicas=r['replicas'],green_verified=True)\nFILE_3_L178= return out\nFILE_3_L179= except BaseException:\nFILE_3_L180= try:reader.close()\nFILE_3_L181= except BaseException:pass\nFILE_3_L182= raise\nFILE_4_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py\nFILE_4_BYTES=59719\nFILE_4_SHA256=b6e79f087b9f1d21dac4f77a7b46b743299bbb85bc716e80d2ea67c2e038bcd7\nFILE_4_CONTEXT_LINE_COUNT=180\nFILE_4_L1=#!/usr/bin/python3\nFILE_4_L2=import ast, base64, datetime as dt, hashlib, io, json, os, pathlib, random, re, select, shlex, shutil, stat, subprocess, sys, tarfile, tempfile, time\nFILE_4_L3=\nFILE_4_L4=PART_SIZE=536870912\nFILE_4_L5=RESERVE_BYTES=536870912\nFILE_4_L6=STATE_ROOT=pathlib.Path('/var/lib/homelab-backup-v2')\nFILE_4_L7=CONFIG_ROOT=pathlib.Path('/etc/homelab-backup-v2')\nFILE_4_L8=TARGETS_PATH=CONFIG_ROOT/'targets.json'\nFILE_4_L9=WORK_ROOT=pathlib.Path('/mnt/staging/homelab-backup-v2')\nFILE_4_L10=ENGINE_PATH=pathlib.Path('/usr/local/sbin/homelab-backup-v2')\nFILE_4_L11=RCLONE=shutil.which('rclone') or '/usr/bin/rclone'\nFILE_4_L12=SSH=shutil.which('ssh') or '/usr/bin/ssh'\nFILE_4_L13=SCP=shutil.which('scp') or '/usr/bin/scp'\nFILE_4_L14=SSH_KEYSCAN=shutil.which('ssh-keyscan') or '/usr/bin/ssh-keyscan'\nFILE_4_L30=P282_SHA='73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012'\nFILE_4_L31=P265=pathlib.Path('/opt/homelab-backup-v2/releases/pve-producer-265r1/pve265.py')\nFILE_4_L32=P265_SHA='cd2f634414a8e38a3789015aeca5a9b47821f77d7d1fa61dcdbdc866172963be'\nFILE_4_L33=RESULT_CLASSES=('SUCCESS','DEFER','PARTIAL_VALID','FAIL')\nFILE_4_L34=HISTORICAL_RELEASE_GRAPH=[\nFILE_4_L35= (pathlib.Path('/opt/homelab-backup-v2/releases/core-260r1/v2core.py'),'e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2'),\nFILE_4_L36= (pathlib.Path('/opt/homelab-backup-v2/releases/transport-261r1/transport261.py'),'b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688'),\nFILE_4_L51= pathlib.Path('/opt/homelab-backup-v2/releases/large-cloud-264r1'),\nFILE_4_L52= pathlib.Path('/opt/homelab-backup-v2/releases/stream-263r1'),\nFILE_4_L53= pathlib.Path('/opt/homelab-backup-v2/releases/transport-261r1'),\nFILE_4_L54= pathlib.Path('/opt/homelab-backup-v2/releases/restore-262r1'),\nFILE_4_L55= pathlib.Path('/opt/homelab-backup-v2/releases/core-260r1'),\nFILE_4_L56= pathlib.Path('/opt/homelab-backup-v2/releases/vm-backup-283r1'),\nFILE_4_L57=]\nFILE_4_L58=_HISTORICAL_HELPER = r'''import importlib,importlib.util,json,pathlib,sys\nFILE_4_L59=mode=sys.argv[1]\nFILE_4_L60=if mode=='import_all':\nFILE_4_L61= out={}\nFILE_4_L74='''\nFILE_4_L75=os.umask(0o077)\nFILE_4_L76=\nFILE_4_L77=REMOTES=[\nFILE_4_L78= ('pve01-mail-01-crypt','pve01-mail-01'),('pve01-mail-02-crypt','pve01-mail-02'),('pve01-mail-03-crypt','pve01-mail-03'),('pve01-mail-04-crypt','pve01-mail-04'),('pve01-mail-05-crypt','pve01-mail-05'),\nFILE_4_L79= ('pve02-mail-01-crypt','pve02-mail-01'),('pve02-mail-02-crypt','pve02-mail-02'),('pve02-mail-03-crypt','pve02-mail-03'),('pve02-mail-04-crypt','pve02-mail-04'),\nFILE_4_L80= ('pve03-mail-01-crypt','pve03-mail-01'),('pve03-mail-02-crypt','pve03-mail-02'),('pve03-mail-03-crypt','pve03-mail-03'),('pve03-mail-04-crypt','pve03-mail-04')]\nFILE_4_L81=\nFILE_4_L82=class BackupError(RuntimeError):\nFILE_4_L83= def __init__(self, code, detail=''):\nFILE_4_L102= if not b: break\nFILE_4_L103= h.update(b); n+=len(b)\nFILE_4_L104= if remain is not None: remain-=len(b)\nFILE_4_L105= if length is not None and n!=length: raise BackupError('LOCAL_PART_SHORT_READ')\nFILE_4_L106= return h.hexdigest()\nFILE_4_L107=\nFILE_4_L108=def part_plan(n):\nFILE_4_L109= if not isinstance(n,int) or n<=0: raise BackupError('PAYLOAD_SIZE_INVALID')\nFILE_4_L110= out=[]; off=0; idx=1\nFILE_4_L111= while offtimeout:\nFILE_4_L314= p.kill(); p.wait(); raise BackupError('RCLONE_CAT_TIMEOUT')\nFILE_4_L315= b=p.stdout.read(1024*1024)\nFILE_4_L316= if not b: break\nFILE_4_L317= h.update(b); n+=len(b)\nFILE_4_L318= p.stderr.read(); rc=p.wait()\nFILE_4_L319= if rc!=0: raise BackupError('RCLONE_CAT_FAILED',str(rc))\nFILE_4_L320= if expected_bytes is not None and n!=expected_bytes: raise BackupError('RCLONE_CAT_SIZE_MISMATCH')\nFILE_4_L321= return h.hexdigest(),n\nFILE_4_L322=\nFILE_4_L323=def rclone_cat_bytes(remote_path,max_bytes=4*1024*1024):\nFILE_4_L324= p=run(rclone_base_args()+['cat',remote_path,'--contimeout','20s','--timeout','60s'],timeout=90)\nFILE_4_L325= if len(p.stdout)>max_bytes: raise BackupError('RCLONE_SMALL_OBJECT_TOO_LARGE')\nFILE_4_L326= return p.stdout\nFILE_4_L327=\nFILE_4_L328=def generation_id(logical_id):\nFILE_4_L329= ts=dt.datetime.now(dt.timezone.utc).strftime('%Y%m%dT%H%M%SZ')\nFILE_4_L330= return f'v2-{logical_id}-{ts}-{os.urandom(6).hex()}'\nFILE_4_L331=\nFILE_4_L332=def upload_and_seal(t,payload_path,source_meta):\nFILE_4_L333= payload_path=pathlib.Path(payload_path); n=payload_path.stat().st_size\nFILE_4_L334= psha=sha256_file(payload_path); reps,bad,healthy=allocator(n)\nFILE_4_L335= gen=source_meta['generation']; rel=remote_generation_path(t['scope'],t['logical_id'],gen)\nFILE_4_L336= if remote_exists_any(t['scope'],t['logical_id'],gen): raise BackupError('GENERATION_COLLISION')\nFILE_4_L337= parts=part_plan(n)\nFILE_4_L338= for p in parts: p['sha256']=sha256_file(payload_path,p['offset'],p['bytes'])\nFILE_4_L339= manifest=make_manifest(t['scope'],t['logical_id'],gen,n,psha,parts,reps,source_meta)\nFILE_4_L340= mb=canonical_json(manifest)+b'\\n'; msha=sha256_bytes(mb)\nFILE_4_L341= for rep in reps:\nFILE_4_L342= base=rep['crypt']+':'+rel\nFILE_4_L343= for part in parts:\nFILE_4_L344= rp=base+'/parts/'+part['name']\nFILE_5_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py\nFILE_5_BYTES=4214\nFILE_5_SHA256=b3050a8f95e6d9826a4f0786cd3ca32012b6ab10486d10e4981b50b8a0affff2\nFILE_5_CONTEXT_LINE_COUNT=60\nFILE_5_L3=\nFILE_5_L4=This module does not discover credentials, modify scheduler state, or implement the\nFILE_5_L5=CT112 producer/cloud path. It routes CT112 to an explicitly supplied callback and\nFILE_5_L6=isolates legacy engine state writes into a candidate-owned state directory.\nFILE_5_L7=\"\"\"\nFILE_5_L8=from __future__ import annotations\nFILE_5_L9=\nFILE_5_L10=import json\nFILE_5_L11=import os\nFILE_5_L12=from pathlib import Path\nFILE_5_L13=from typing import Callable, Iterable, Mapping, Any\nFILE_5_L14=\nFILE_5_L15=LEGACY_IDS=(\nFILE_5_L16= 'node-pve01',\nFILE_5_L24=)\nFILE_5_L25=CT112_ID='guest-pve01-lxc-112'\nFILE_5_L26=REPRESENTATIVE_IDS=(CT112_ID,)+LEGACY_IDS\nFILE_5_L27=CANDIDATE_ROOT=Path(__file__).resolve().parent\nFILE_5_L28=STATE_ROOT=CANDIDATE_ROOT/'state'\nFILE_5_L29=\nFILE_5_L30=class ContractError(RuntimeError):\nFILE_5_L31= pass\nFILE_5_L36= raise ContractError('TARGET_LOGICAL_ID')\nFILE_5_L37= return value\nFILE_5_L38=\nFILE_5_L39=def partition_targets(targets: Iterable[Mapping[str, Any]]):\nFILE_5_L40= selected={}\nFILE_5_L41= wanted=set(REPRESENTATIVE_IDS)\nFILE_5_L42= for target in targets:\nFILE_5_L62= with os.fdopen(fd,'wb',closefd=True) as f:\nFILE_5_L63= f.write(data);f.flush();os.fsync(f.fileno())\nFILE_5_L64= os.replace(tmp,path)\nFILE_5_L65= except BaseException:\nFILE_5_L66= try: os.unlink(tmp)\nFILE_5_L67= except FileNotFoundError: pass\nFILE_5_L68= raise\nFILE_5_L72= def write_state(logical_id,result,**kw):\nFILE_5_L73= if logical_id not in LEGACY_IDS:\nFILE_5_L74= raise ContractError('STATE_TARGET_FORBIDDEN')\nFILE_5_L75= payload={'logical_id':logical_id,'result':result,'meta':kw,'schema_version':1}\nFILE_5_L76= _atomic_json(root/(logical_id+'.json'),payload)\nFILE_5_L77= return write_state\nFILE_5_L78=\nFILE_5_L79=def run_legacy_lane(engine, target: Mapping[str, Any]):\nFILE_5_L97= raise ContractError('CT112_RESULT_SCHEMA')\nFILE_5_L98= if result.get('logical_id')!=CT112_ID or result.get('status')!='OK':\nFILE_5_L99= raise ContractError('CT112_RESULT_STATUS')\nFILE_5_L100= if result.get('cloud_only_independent_restore_verified') is not True:\nFILE_5_L101= raise ContractError('CT112_RESTORE_NOT_VERIFIED')\nFILE_5_L102= if result.get('local_payload_files_remaining')!=0:\nFILE_5_L103= raise ContractError('CT112_LOCAL_PAYLOAD_REMAINS')\nFILE_5_L104= return result\nFILE_5_L105=\nFILE_5_L106=def run_representative(engine, targets, ct112_callback: Callable[[Mapping[str, Any]], Any]):\nFILE_5_L107= if not callable(ct112_callback):\nFILE_5_L108= raise ContractError('CT112_CALLBACK_REQUIRED')\nFILE_5_L109= legacy,ct112=partition_targets(targets)\nFILE_5_L110= results=[_validate_ct112_result(ct112_callback(ct112))]\nFILE_5_L111= for target in legacy:\nFILE_5_L112= results.append(run_legacy_lane(engine,target))\nFILE_6_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py\nFILE_6_BYTES=5055\nFILE_6_SHA256=2b5eb4fc0f6f466a0d3e5a007af987092b6c17baa3ff9241a6afc2c2e97df1d5\nFILE_6_CONTEXT_LINE_COUNT=43\nFILE_6_L1=#!/usr/bin/env python3\nFILE_6_L2=from __future__ import annotations\nFILE_6_L3=import errno,fcntl,importlib.util,os,sys\nFILE_6_L4=from pathlib import Path\nFILE_6_L5=ROOT=Path(__file__).resolve().parent\nFILE_6_L6=CT_ID='guest-pve01-lxc-112'\nFILE_6_L7=LEGACY_IDS=('node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2')\nFILE_6_L35= 'guest_type':'lxc','due_seconds':due,'enabled':True,'green1':True}\nFILE_6_L36=\nFILE_6_L37=def load_targets():\nFILE_6_L38= base=canonical_targets();return base+[_ct_descriptor(base)]\nFILE_6_L39=\nFILE_6_L40=def backup_target(t,green_verify=False):\nFILE_6_L41= if not isinstance(t,dict) or t.get('logical_id')!=CT_ID:\nFILE_6_L42= return _legacy_backup_target(t,green_verify)\nFILE_6_L43= try:\nFILE_6_L44= r=ct112.run_target(t,state_writer=legacy.write_state)\nFILE_6_L45= return {'logical_id':CT_ID,'generation':r['generation'],'result':'SUCCESS','payload_bytes':r['payload_bytes'],\nFILE_6_L46= 'replicas':2,'manifest_sha256':r['manifest_sha256'],'payload_sha256':r['payload_sha256']}\nFILE_6_L47= except BaseException:\nFILE_6_L48= try: legacy.write_state(CT_ID,'FAIL',reason='CT112_PIPELINE_FAILED')\nFILE_6_L49= except BaseException: pass\nFILE_6_L50= raise legacy.BackupError('CT112_PIPELINE_FAILED') from None\nFILE_6_L51=\nFILE_6_L52=legacy.load_targets=load_targets\nFILE_6_L53=legacy.backup_target=backup_target\nFILE_6_L54=\nFILE_6_L55=def _legacy_reused_boundary_selftest():\nFILE_6_L56= if legacy.PART_SIZE!=536870912: raise RuntimeError('LEGACY_PART_SIZE')\nFILE_6_L57= if len(legacy.REMOTES)!=13 or len({p for _,p in legacy.REMOTES})!=13: raise RuntimeError('LEGACY_REMOTES')\nFILE_6_L58= d=legacy.default_targets()\nFILE_6_L59= if not isinstance(d,list) or len(d)<28: raise RuntimeError('LEGACY_DEFAULT_TARGET_COUNT')\nFILE_6_L60= want={'ct112',*LEGACY_IDS}\nFILE_6_L67=\nFILE_6_L68=def selftest():\nFILE_6_L69= _legacy_reused_boundary_selftest()\nFILE_6_L70= base=canonical_targets(); eff=load_targets()\nFILE_6_L71= if len(eff)!=29 or sum(1 for x in eff if x.get('logical_id')==CT_ID)!=1: raise RuntimeError('EFFECTIVE_TARGETS')\nFILE_6_L72= legacy_rows,ct=driver.partition_targets(eff)\nFILE_6_L73= if [x['logical_id'] for x in legacy_rows]!=list(LEGACY_IDS) or ct.get('vmid')!=112: raise RuntimeError('REPRESENTATIVE_PARTITION')\nFILE_6_L74= if ct112.selftest() is not True: raise RuntimeError('CT112_HANDLER_SELFTEST')\nFILE_6_L75= print('SELFTEST_REPLACEMENT486=PASS',flush=True);return 0\nFILE_6_L76=\nFILE_7_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/CONTRACT.json\nFILE_7_BYTES=2077\nFILE_7_SHA256=a414715f1b91b3701c52073709a80c38218f76e3961618e8af61489211e50c01\nFILE_7_CONTEXT_LINE_COUNT=1\nFILE_7_L1={\"candidate\":\"pve-lxc-adapter-411r1\",\"next_gate\":\"PVE_LXC_REAL_ENVIRONMENT_DRY_RUN\",\"owner\":{\"MappedScratch\":\"pve270\",\"capture_archive\":\"pve270\",\"dump_argv\":\"pve265\",\"pve282.old\":\"pve265\",\"pve282.proven\":\"pve270\",\"validate_mapping\":\"pve270\"},\"schema\":1,\"signatures\":{\"pve265\":{\"dump_argv\":{\"args\":[\"job\",\"executable\",\"tmpdir\",\"now\"],\"defaults\":0,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonly\":[],\"vararg\":null},\"make_plan\":{\"args\":[\"rows\",\"configs\",\"storage_rows\",\"defaults\",\"staging_free\",\"now\",\"node\"],\"defaults\":1,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonly\":[],\"vararg\":null},\"produce\":{\"args\":[\"job\",\"work\",\"tools\",\"guard\",\"seconds\"],\"defaults\":1,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonly\":[],\"vararg\":null},\"validate_job\":{\"args\":[\"job\",\"now\"],\"defaults\":0,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonly\":[],\"vararg\":null}},\"pve270\":{\"capture_archive\":{\"args\":[\"argv\",\"destination\",\"cap\",\"seconds\",\"check\"],\"defaults\":0,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonl\nFILE_8_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/PLAN265_REFERENCE.json\nFILE_8_BYTES=7210\nFILE_8_SHA256=8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757\nFILE_8_CONTEXT_LINE_COUNT=1\nFILE_8_L1={\"all_guest_coverage_eligible\":true,\"backup_executed\":false,\"capacity_policy\":\"four archive copies plus 4GiB; capped producer; actual compressed size still unknown\",\"collected_at_utc\":\"2026-09-12T06:57:40.611329+00:00\",\"full_host_coverage\":false,\"global_blockers\":[],\"global_defaults\":{\"blockers\":[],\"keys\":[],\"present\":true,\"sha256\":\"cf34c8e3812547954bb1d08b50212b6e148ee9cf2b629e895a843ed49d623b38\"},\"guest_membership_and_configs_stable\":true,\"jobs\":[{\"application_consistency_verified\":false,\"archive_cap_bytes\":9516036916,\"blockers\":[],\"cdrom_count\":0,\"cdrom_media_included\":false,\"compression_dependent\":false,\"config_sha256\":\"09957aacd41ba9eeaf5fd4eda13936d580c9082a46895433379aa9dd0bad5969\",\"configured_archive_bound_bytes\":9516036916,\"configured_disk_bytes\":8589934592,\"configured_full_cycle_fits\":true,\"coverage_eligible\":true,\"disks\":[{\"backup_setting\":\"included_default_rootfs\",\"class\":\"managed-volume\",\"configured_size_bytes\":8589934592,\"slot\":\"rootfs\",\"storage\":\"local-lvm\"}],\"global_def\nFILE_9_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/PROVENANCE.json\nFILE_9_BYTES=4310\nFILE_9_SHA256=303f44dc8e91c8e6a675f9ea406ce4b7532ed98440a9b6807fa593ede9d33254\nFILE_9_CONTEXT_LINE_COUNT=1\nFILE_9_L1={\"anchors\":{\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-EXACT-CALL-GRAPH-PROOF-401R1\":\"840b3f1e8b023899c0554305a97af2d55dc71cf9\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-IMMUTABLE-ADAPTER-CANDIDATE-STAGE-FIXTURE-RCA-FIX-407R1\":\"0261475cdd35d3b762de0a29e46f3acd0b9af229\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-IMMUTABLE-ADAPTER-CANDIDATE-STAGE-RCA-FIX-403R1\":\"e3f753ce9cf95e21750e9ad7f9574cafc199b3ce\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERL270-HISTORICAL-TEST-RCA-404R1\":\"58f61ce62c3b6a57d8c5fc2465d2537d6728f9d5\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-EXEC-FS-RCA-409R1\":\"0db666a5a1f4796b567663adfc5568817e87efa5\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-NEED-TRACE-RCA-408R1\":\"44cdff7dab9e60aff1d69c12d2c32a4b26d04de8\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-PRODUCTION-VS-FIXTURE-RCA-406R1\":\"4c5836ff0ef73cb2ef306857be713383ba5e85f4\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-ROOT-SUITE-STRUCTURED-RCA-410R1\":\"4673bfaf04877370bc4ac02fef37885eb82cb1eb\nFILE_10_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/adapter.py\nFILE_10_BYTES=2989\nFILE_10_SHA256=18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3\nFILE_10_CONTEXT_LINE_COUNT=30\nFILE_10_L1=#!/usr/bin/python3\nFILE_10_L2=import hashlib,inspect,json,pathlib,sys\nFILE_10_L3=BASE=pathlib.Path(__file__).resolve().parent\nFILE_10_L4=EXPECTED={\nFILE_10_L5='v2core.py':'e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2',\nFILE_10_L6='transport261.py':'b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688',\nFILE_10_L7='restore262.py':'56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1',\nFILE_10_L8='stream263.py':'a83d03a86b93a855716658652dd073fcb281cd387fc6200fe4bc2a922f39ea16',\nFILE_10_L9='nodeplan263.py':'db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060',\nFILE_10_L10='large264.py':'5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a',\nFILE_10_L19= return h.hexdigest()\nFILE_10_L20=def verify_sources():\nFILE_10_L21= for name,want in EXPECTED.items():\nFILE_10_L22= p=BASE/name\nFILE_10_L23= if not p.is_file() or p.is_symlink() or sha(p)!=want:raise RuntimeError('SOURCE_IDENTITY_'+name)\nFILE_10_L24=def load_graph():\nFILE_10_L25= import guest266,pve265,pve270,pve282\nFILE_10_L26= if hasattr(guest266,'dump_argv'):raise RuntimeError('GUEST266_UNEXPECTED_DUMP_ARGV')\nFILE_10_L27= if not hasattr(pve265,'dump_argv'):raise RuntimeError('PVE265_DUMP_ARGV_MISSING')\nFILE_10_L28= if getattr(pve270,'old',None) is not pve265:raise RuntimeError('PVE270_OLD_NOT_PVE265')\nFILE_10_L35=def describe():\nFILE_10_L36= _,p265,p270,p282=load_graph()\nFILE_10_L37= funcs={'pve265.dump_argv':p265.dump_argv,'pve265.validate_job':p265.validate_job,'pve265.make_plan':p265.make_plan,'pve270.validate_mapping':p270.validate_mapping,'pve270.capture_archive':p270.capture_archive,'pve270.produce':p270.produce,'pve282.select112':p282.select112,'pve282.produce':p282.produce}\nFILE_10_L38= return {'schema':1,'bundle':'pve-lxc-adapter-411r1','source_sha256':EXPECTED,'signatures':{k:str(inspect.signature(v)) for k,v in funcs.items()},'owner':{'dump_argv':'pve265','MappedScratch':'pve270','capture_archive':'pve270','validate_mapping':'pve270','pve282.old':'pve265','pve282.proven':'pve270'}}\nFILE_10_L39=def main():\nFILE_10_L40= verify_sources(); mode=sys.argv[1] if len(sys.argv)>1 else 'selftest'; d=describe()\nFILE_10_L41= if mode not in ('selftest','describe'):raise SystemExit(2)\nFILE_10_L42= print(json.dumps(d,sort_keys=True,separators=(',',':')))\nFILE_10_L43= if mode=='selftest':print('ADAPTER_SELFTEST=PASS')\nFILE_10_L44=if __name__=='__main__':main()\nFILE_11_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/guest266.py\nFILE_11_BYTES=7385\nFILE_11_SHA256=86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868\nFILE_11_CONTEXT_LINE_COUNT=75\nFILE_11_L1=\"\"\"266R1 bounded real-guest orchestration. Import has no side effects.\nFILE_11_L2=\nFILE_11_L3=One small LXC rootfs, no compression-dependent admission. No node-wide approval,\nFILE_11_L4=no guest restore/start/delete, no cloud deletion. Archive CRC/SHA is not boot proof.\nFILE_11_L5=\"\"\"\nFILE_11_L6=import copy,os,time\nFILE_11_L7=from pathlib import Path\nFILE_11_L8=import v2core as core\nFILE_11_L9=import transport261 as t\nFILE_11_L10=import stream263 as stream\nFILE_11_L11=import large264 as large\nFILE_11_L12=import pve265 as producer\nFILE_11_L13=\nFILE_11_L14=MAX_CONFIGURED_BYTES=8*1024**3\nFILE_11_L15=RESERVE_BYTES=536870912\nFILE_11_L25=\nFILE_11_L26=def choose(plan,now):\nFILE_11_L27= rows=plan_rows(plan);t.need(plan.get('global_blockers')==[],'GLOBAL_DEFAULT_BLOCK')\nFILE_11_L28= candidates=[]\nFILE_11_L29= for job in rows:\nFILE_11_L30= if not (job.get('type')=='lxc' and job.get('coverage_eligible') is True and\nFILE_11_L31= job.get('blockers')==[] and job.get('compression_dependent') is False and\nFILE_11_L35= disks[0].get('class')=='managed-volume' and disks[0].get('backup_setting') in ('included','included_default_rootfs')):continue\nFILE_11_L36= n=job.get('configured_disk_bytes')\nFILE_11_L37= if type(n) is not int or not 0=max(1024**3,chosen['configured_disk_bytes']),'SNAPSHOT_HEADROOM_UNPROVEN')\nFILE_11_L45= return copy.deepcopy(chosen)\nFILE_11_L46=\nFILE_11_L47=def same_state(before,after,vmid):\nFILE_11_L48= old=plan_rows(before);new=plan_rows(after)\nFILE_11_L64= if type(task.get('endtime')) is int and task.get('status') not in (None,'RUNNING'):continue\nFILE_11_L65= kind=task['type'];target=str(task.get('id',''))\nFILE_11_L66= if kind in ('vncshell','vncterm','vncproxy','spiceproxy'):continue\nFILE_11_L67= t.need(target!=str(vmid) and not any(x in kind.lower() for x in ('dump','restore','migrat','snapshot','clone','move_disk','resize')),'COMPETING_PVE_TASK')\nFILE_11_L68=\nFILE_11_L69=def check_result(result,job,work):\nFILE_11_L70= t.need(type(result) is dict,'PRODUCER_RESULT_SCHEMA')\nFILE_11_L80= finally:os.close(fd)\nFILE_11_L81= return expected\nFILE_11_L82=\nFILE_11_L83=def run_flow(job,work,entries,cloud,reader,produce,state_guard,guard,compression_check,generation,say,clock=lambda:int(time.time())):\nFILE_11_L84= \"\"\"Caller provides real fresh PVE guards; simulation callbacks live in tests only.\"\"\"\nFILE_11_L85= producer.validate_job(job,clock());core.validate_inventory(entries)\nFILE_11_L86= t.need(job['type']=='lxc' and job.get('compression_dependent') is False,'FIRST_GUEST_POLICY')\nFILE_11_L87= t.need(core.valid_name(generation),'GENERATION_ID')\nFILE_11_L88= t.need(all(callable(f) for f in (produce,state_guard,guard,compression_check,say,clock)),'CALLBACKS_REQUIRED')\nFILE_11_L89= work=Path(work);state_guard();guard()\nFILE_11_L90= for sub in ('producer','upload','restore'):t.make_dir(work/sub,exclusive=True)\nFILE_11_L91= say('PHASE','REAL_SINGLE_GUEST_SNAPSHOT')\nFILE_11_L92= produced=produce(job,work/'producer')\nFILE_11_L93= source=check_result(produced,job,work/'producer');state_guard();guard()\nFILE_11_L94= t.create(work/'PRODUCER.json',core.canonical_bytes(produced))\nFILE_11_L95= say('PRODUCER_RC',0);say('PAYLOAD_BYTES',produced['size']);say('ARCHIVE_SHA256',produced['sha256'])\nFILE_11_L96= logical='guest-%s-%s-%d'%(job['node'],job['type'],job['vmid'])\nFILE_11_L97= say('PHASE','TWO_REPLICA_UPLOAD_AND_READBACK')\nFILE_11_L98= up=large.upload(source,work/'upload',entries,cloud,generation,logical,RESERVE_BYTES,guard,clock=clock)\nFILE_11_L99= t.need(up['manifest']['payload_bytes']==produced['size'] and up['manifest']['payload_sha256']==produced['sha256'],'MANIFEST_PRODUCER_MISMATCH')\nFILE_11_L100= say('SEALED_QUORUM_VERIFIED','YES');mh=core.manifest_hash(up['manifest']);say('MANIFEST_SHA256',mh)\nFILE_11_L101= say('PHASE','CLOUD_DISCOVERY_AND_ARCHIVE_RECONSTRUCTION')\nFILE_11_L102= found=large.discover(reader,entries,logical,mh)\nFILE_11_L103= restored=large.restore(work/'restore',reader,found)\nFILE_11_L104= t.need(compression_check(work/'restore/restored.bin') is True,'RECONSTRUCTED_COMPRESSION_FAILED')\nFILE_11_L105= state_guard();guard();say('PHASE','GUARDED_LOCAL_PAYLOAD_CLEANUP')\nFILE_11_L106= count=large.cleanup(up,restored,reader,guard)\nFILE_11_L107= return {'state':'SINGLE_GUEST_ARCHIVE_SEALED_AND_CLOUD_RECONSTRUCTED','vmid':job['vmid'],'type':job['type'],'node':job['node'],\nFILE_11_L108= 'generation':generation,'logical_id':logical,'manifest_sha256':mh,'payload_bytes':produced['size'],'payload_sha256':produced['sha256'],\nFILE_11_L109= 'replicas':up['manifest']['replicas'],'parts_count':len(up['manifest']['parts']),'archive_cloud_restore_verified':True,\nFILE_11_L110= 'guest_restore_verified':False,'application_consistency_verified':False,'whole_node_backup':False,'local_payload_files_remaining':0,\nFILE_11_L111= 'files_deleted':count,'accounts_scanned':found['accounts_scanned'],'config_sha256':job['config_sha256']}\nFILE_12_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py\nFILE_12_BYTES=16514\nFILE_12_SHA256=5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a\nFILE_12_CONTEXT_LINE_COUNT=180\nFILE_12_L1=\"\"\"264R1 streaming cloud adapter and synthetic boundary proof.\nFILE_12_L2=\nFILE_12_L3=No work on import. No guest/backup producer and no cloud deletion. Existing\nFILE_12_L4=small-canary adapters are not modified. Only caller-owned local payload is removed,\nFILE_12_L5=after two cloud seals and a separately cloud-discovered restore have been checked.\nFILE_12_L6=\"\"\"\nFILE_12_L7=import hashlib\nFILE_12_L8=import os\nFILE_12_L9=from pathlib import Path\nFILE_12_L10=import selectors\nFILE_12_L11=import signal\nFILE_12_L12=import subprocess\nFILE_12_L13=import time\nFILE_12_L14=import v2core as core\nFILE_12_L15=import transport261 as t\nFILE_12_L16=import restore262 as r\nFILE_12_L17=import stream263 as s\nFILE_12_L18=\nFILE_12_L19=BOUNDARY_BYTES=536870929\nFILE_12_L20=LOGICAL_ID='stream-canary-pve01'\nFILE_12_L21=\nFILE_12_L22=\nFILE_12_L23=def process_stream(argv,env,size,digest,consume,seconds):\nFILE_12_L24= \"\"\"Drain both pipes, hashing stdout incrementally; never retain the whole part.\"\"\"\nFILE_12_L25= t.need(type(argv) is list and argv and seconds>0,'STREAM_PROCESS_ARGUMENTS')\nFILE_12_L26= sink=s.DigestSink(size,digest)\nFILE_12_L27= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,\nFILE_12_L38= remaining=deadline-time.monotonic();t.need(remaining>0,'STREAM_TIMEOUT')\nFILE_12_L39= rc=p.wait(timeout=remaining);t.need(rc==0,'STREAM_PROCESS_RC_FAILED')\nFILE_12_L40= result=sink.finish();complete=True;return result\nFILE_12_L41= except subprocess.TimeoutExpired:raise t.Stop('STREAM_TIMEOUT') from None\nFILE_12_L42= finally:\nFILE_12_L43= if not complete or p.poll() is None:\nFILE_12_L44= try:os.killpg(p.pid,signal.SIGKILL)\nFILE_12_L53= '--cache-dir',str(Path(work)/'cache')]+args\nFILE_12_L54=\nFILE_12_L55=\nFILE_12_L56=class CloudLarge(t.RcloneIO):\nFILE_12_L57= \"\"\"Reuse the existing crypt inventory/namespace guards, not its in-memory payload I/O.\"\"\"\nFILE_12_L58= def call(self,args,limit):\nFILE_12_L59= t.need(type(args) is list and args and args[0] in ('about','lsjson','copyto','cat'),'RCLONE_VERB_FORBIDDEN')\nFILE_12_L60= t.need(type(limit) is int and 0<=limit<=core.MANIFEST_MAX_BYTES+1,'METADATA_BOUND')\nFILE_12_L61= self.check();seconds=min(3600 if args[0]=='copyto' else 90,self.deadline-time.monotonic())\nFILE_12_L62= t.need(seconds>0,'CLOUD_DEADLINE');self.calls+=1\nFILE_12_L63= if args[0]=='copyto':self.writes_attempted+=1\nFILE_12_L64= rc,out=t.bounded_process(flags(self.executable,self.config_path,self.work,args),self.env,limit,seconds)\nFILE_12_L65= self.check();return rc,out\nFILE_12_L66= def put(self,remote,name,path):\nFILE_12_L67= dest=self.address(remote,name)\nFILE_12_L68= if name in ('manifest.json','SEALED.json'):\nFILE_12_L69= return super().put(remote,name,path)\nFILE_12_L70= part=next((p for p in self.manifest['parts'] if p['name']==name),None)\nFILE_12_L71= t.need(part is not None,'PART_NOT_IN_MANIFEST')\nFILE_12_L72= n,h,identity=s.hash_file(path,part['size'])\nFILE_12_L73= t.need(n==part['size'] and h==part['sha256'],'UPLOAD_SOURCE_HASH')\nFILE_12_L74= rc,_=self.call(['lsjson',dest,'--stat'],65536)\nFILE_12_L75= t.need(rc in (3,4),'OBJECT_NOT_PROVEN_ABSENT')\nFILE_12_L76= with s.file_handle(path,part['size']) as (_,size,seen):\nFILE_12_L77= t.need(seen==identity,'UPLOAD_SOURCE_CHANGED')\nFILE_12_L78= rc,_=self.call(['copyto',str(path),dest,'--immutable','--no-update-modtime'],65536)\nFILE_12_L79= t.need(rc==0,'CLOUD_COPY_FAILED')\nFILE_12_L80= def read_stream(self,remote,name,size,digest,consume):\nFILE_12_L81= address=self.address(remote,name)\nFILE_12_L82= part=next((p for p in self.manifest['parts'] if p['name']==name),None)\nFILE_12_L83= t.need(part is not None and part['size']==size and part['sha256']==digest,'READBACK_EXPECTATION')\nFILE_12_L84= self.check();seconds=min(3600,self.deadline-time.monotonic());t.need(seconds>0,'CLOUD_DEADLINE')\nFILE_12_L85= self.calls+=1\nFILE_12_L86= out=process_stream(flags(self.executable,self.config_path,self.work,['cat',address,'--count',str(size+1)]),\nFILE_12_L90=\nFILE_12_L91=class ReadOnlyLarge(r.ReadOnlyCloud):\nFILE_12_L92= \"\"\"Metadata uses inherited bounded reads; this class has no upload API.\"\"\"\nFILE_12_L93= def read_stream(self,remote,generation,name,size,digest,consume):\nFILE_12_L94= t.need(type(size) is int and 00,'RESTORE_DEADLINE')\nFILE_12_L98= self.calls+=1\nFILE_12_L99= out=process_stream(flags(self.executable,self.snapshot,self.work,['cat',address,'--count',str(size+1)]),\nFILE_12_L100= self.env,size,digest,consume,seconds)\nFILE_12_L116=\nFILE_12_L117=\nFILE_12_L118=def receipt(rep,m,whole,count):\nFILE_12_L119= t.need(count==m['payload_bytes'] and whole.hexdigest()==m['payload_sha256'],'PAYLOAD_READBACK_MISMATCH')\nFILE_12_L120= return {**rep,'generation':m['generation'],'manifest_sha256':core.manifest_hash(m),\nFILE_12_L121= 'payload_sha256':whole.hexdigest(),'payload_bytes':count,'parts':m['parts'],\nFILE_12_L122= 'method':'download-sha256','ok':True}\nFILE_12_L123=\nFILE_12_L124=\nFILE_12_L125=def upload(source,work,entries,cloud,generation,logical_id,reserve,guard,clock=lambda:int(time.time())):\nFILE_12_L126= \"\"\"Upload and seal; retain all source bytes here. Cleanup is a later gated operation.\"\"\"\nFILE_12_L127= work=Path(work);source=Path(source);guard();core.validate_inventory(entries)\nFILE_12_L128= t.need(core.valid_name(generation) and core.valid_name(logical_id),'GENERATION_ID')\nFILE_12_L129= partdir=work/'parts';t.make_dir(partdir,exclusive=True)\nFILE_12_L130= record=s.split_file(source,partdir)\nFILE_12_L131= t.create(work/'parts-record.json',core.canonical_bytes(record))\nFILE_12_L132= quotas={e['account_id']:cloud.quota(e['crypt_remote']) for e in entries}\nFILE_12_L133= allocation=core.allocate(entries,quotas,record['payload_bytes'],reserve,clock())\nFILE_12_L134= m=core.make_manifest(generation,logical_id,record['payload_bytes'],record['payload_sha256'],record['parts'],allocation)\nFILE_12_L135= mb=core.canonical_bytes(m);mh=core.manifest_hash(m);cloud.bind(m)\nFILE_12_L136= t.create(work/'allocation.json',core.canonical_bytes(allocation));t.create(work/'manifest.json',mb)\nFILE_12_L137= for rep in m['replicas']:\nFILE_12_L138= guard();cloud.absent(rep['crypt_remote'])\nFILE_12_L139= receipts=[]\nFILE_12_L140= for rep in m['replicas']:\nFILE_12_L141= guard();remote=rep['crypt_remote'];q=cloud.quota(remote)\nFILE_12_L142= t.need(core.quota_reason(q,clock()) is None and q['free']>=allocation['required_free_bytes'],'SELECTED_ACCOUNT_CAPACITY_CHANGED')\nFILE_12_L143= s.verify_parts(partdir,record)\nFILE_12_L144= for part in m['parts']:cloud.put(remote,part['name'],partdir/part['name'])\nFILE_12_L145= cloud.put(remote,'manifest.json',work/'manifest.json')\nFILE_12_L146= t.need(cloud.get(remote,'manifest.json',len(mb)+1)==mb,'MANIFEST_READBACK_MISMATCH')\nFILE_12_L147= whole=hashlib.sha256();count=0\nFILE_12_L148= for part in m['parts']:\nFILE_12_L149= observed=cloud.read_stream(remote,part['name'],part['size'],part['sha256'],whole.update)\nFILE_12_L150= t.need(observed=={'size':part['size'],'sha256':part['sha256']},'PART_READBACK_MISMATCH');count+=observed['size']\nFILE_12_L151= receipts.append(receipt(rep,m,whole,count));t.create(work/('receipt-%d.json'%len(receipts)),core.canonical_bytes(receipts[-1]))\nFILE_12_L152= core.verify_receipts(m,receipts)\nFILE_12_L153= seal={'schema_version':2,'state':'SEALED','generation':generation,'logical_id':logical_id,\nFILE_12_L154= 'manifest_sha256':mh,'replicas':m['replicas'],'receipts':receipts,\nFILE_12_L155= 'acceptance':'IDENTICAL_SEAL_REQUIRED_ON_BOTH_NAMED_ACCOUNTS'}\nFILE_12_L156= sb=core.canonical_bytes(seal);t.need(len(sb)<=core.SEAL_MAX_BYTES,'SEAL_BOUND')\nFILE_12_L157= r.validate_seal(sb,entries,generation,logical_id);t.create(work/'SEALED.json',sb)\nFILE_12_L158= for rep in m['replicas']:\nFILE_12_L159= guard();cloud.put(rep['crypt_remote'],'SEALED.json',work/'SEALED.json')\nFILE_12_L160= for rep in m['replicas']:\nFILE_12_L161= t.need(cloud.get(rep['crypt_remote'],'SEALED.json',len(sb)+1)==sb,'SEALED_QUORUM_REQUIRED')\nFILE_12_L162= guard();s.verify_parts(partdir,record)\nFILE_12_L163= t.create(work/'QUORUM_VERIFIED.json',core.canonical_bytes({'manifest_sha256':mh,'seal_sha256':t.sha(sb)}))\nFILE_12_L164= return {'sealed':True,'manifest':m,'seal_bytes':sb,'parts_record':record,'source':str(source),'work':str(work)}\nFILE_12_L165=\nFILE_12_L166=\nFILE_12_L167=def discover(cloud,entries,logical_id,expected_hash):\nFILE_12_L168= \"\"\"Find placement from all cloud catalogs, using only an external manifest digest.\"\"\"\nFILE_12_L169= core.validate_inventory(entries)\nFILE_12_L170= t.need(core.valid_name(logical_id) and core.valid_hash(expected_hash),'DISCOVERY_INPUT')\nFILE_12_L171= listed={};generations=set()\nFILE_12_L172= for e in entries:\nFILE_12_L173= gs=cloud.list_generations(e['crypt_remote'])\nFILE_12_L174= t.need(type(gs) is list and len(gs)<=r.MAX_GENERATIONS and all(core.valid_name(g) for g in gs),'GENERATION_LIST_INVALID')\nFILE_12_L175= t.need(len(gs)==len(set(gs)),'GENERATION_LIST_DUPLICATE');listed[e['crypt_remote']]=set(gs);generations.update(gs)\nFILE_12_L176= t.need(len(generations)<=r.MAX_GENERATIONS,'TOTAL_GENERATIONS_BOUND')\nFILE_12_L177= candidates=[]\nFILE_12_L178= for gen in sorted(generations):\nFILE_12_L179= seen={}\nFILE_12_L180= for e in entries:\nFILE_12_L181= remote=e['crypt_remote']\nFILE_12_L182= if gen not in listed[remote]:continue\nFILE_12_L183= b=cloud.get(remote,gen,'SEALED.json',core.SEAL_MAX_BYTES+1,missing_ok=True)\nFILE_12_L184= if b is None:continue\nFILE_12_L185= seal=r.validate_seal(b,entries,gen,logical_id)\nFILE_12_L186= t.need(remote in {x['crypt_remote'] for x in seal['replicas']},'UNNAMED_SEAL_LOCATION');seen[remote]=(b,seal)\nFILE_12_L187= if not seen:continue\nFILE_12_L188= sb,seal=next(iter(seen.values()))\nFILE_12_L189= if seal['manifest_sha256']!=expected_hash:continue\nFILE_12_L190= named={x['crypt_remote'] for x in seal['replicas']}\nFILE_12_L191= t.need(set(seen)==named and all(b==sb for b,_ in seen.values()),'IDENTICAL_TWO_SEALS_REQUIRED')\nFILE_12_L192= blobs=[]\nFILE_12_L193= for rep in seal['replicas']:\nFILE_12_L194= b=cloud.get(rep['crypt_remote'],gen,'manifest.json',core.MANIFEST_MAX_BYTES+1)\nFILE_12_L195= t.need(type(b) is bytes and t.sha(b)==expected_hash,'MANIFEST_DIGEST_MISMATCH')\nFILE_12_L196= m=r.strict_json(b,core.MANIFEST_MAX_BYTES);core.validate_manifest(m)\nFILE_12_L197= t.need(m['generation']==gen and m['logical_id']==logical_id and m['replicas']==seal['replicas'],'MANIFEST_PLACEMENT')\nFILE_12_L198= t.need(core.canonical_bytes(m)==b,'MANIFEST_CANONICAL_BYTES');core.verify_receipts(m,seal['receipts']);blobs.append(b)\nFILE_12_L199= t.need(len(blobs)==2 and blobs[0]==blobs[1],'TWO_MANIFEST_COPIES_REQUIRED')\nFILE_12_L200= candidates.append({'manifest':m,'seal_bytes':sb,'accounts_scanned':len(listed)})\nFILE_12_L201= t.need(len(candidates)==1,'EXACTLY_ONE_ACCEPTED_GENERATION_REQUIRED')\nFILE_12_L202= return candidates[0]\nFILE_12_L203=\nFILE_12_L204=\nFILE_12_L205=def fresh_seals(cloud,discovery):\nFILE_12_L206= m=discovery['manifest'];core.validate_manifest(m);sb=discovery['seal_bytes']\nFILE_12_L207= for rep in m['replicas']:\nFILE_12_L208= t.need(cloud.get(rep['crypt_remote'],m['generation'],'SEALED.json',len(sb)+1)==sb,'SEAL_CHANGED_DURING_RESTORE')\nFILE_12_L209=\nFILE_12_L210=\nFILE_12_L211=def download_part(cloud,rep,m,part,path,whole):\nFILE_12_L212= path=Path(path);parent=t.open_dir(path.parent);fd=None\nFILE_12_L213= try:\nFILE_12_L214= fd=os.open(path.name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600,dir_fd=parent)\nFILE_12_L215= def consume(block):whole.update(block);s.write_all(fd,block)\nFILE_12_L216= result=cloud.read_stream(rep['crypt_remote'],m['generation'],part['name'],part['size'],part['sha256'],consume)\nFILE_13_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/nodeplan263.py\nFILE_13_BYTES=4603\nFILE_13_SHA256=db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060\nFILE_13_CONTEXT_LINE_COUNT=23\nFILE_13_L1=\"\"\"Pure, allowlisted PVE metadata projection. Does not execute any command.\"\"\"\nFILE_13_L2=from decimal import Decimal,InvalidOperation\nFILE_13_L3=import hashlib\nFILE_13_L4=import re\nFILE_13_L5=import v2core as core\nFILE_13_L6=\nFILE_13_L7=class PlanError(ValueError):pass\nFILE_13_L8=\nFILE_13_L15= if type(value) is not str or not re.fullmatch(r'\\d+(?:\\.\\d+)?[KMGTPE]?',value):return None\nFILE_13_L16= try:\nFILE_13_L17= suffix=value[-1] if value[-1] in 'KMGTPE' else ''\nFILE_13_L18= base=Decimal(value[:-1] if suffix else value)\nFILE_13_L19= b=base*(1024**('KMGTPE'.index(suffix)+1) if suffix else 1)\nFILE_13_L20= return int(b) if b==int(b) and 0time.monotonic(),'PRODUCER_TIMEOUT')\nFILE_14_L175= try:rc=p.wait(timeout=deadline-time.monotonic())\nFILE_14_L176= except subprocess.TimeoutExpired:raise PVEError('PRODUCER_TIMEOUT') from None\nFILE_14_L177= need(rc==0,'PRODUCER_RC_FAILED_PARTIAL_RETAINED');need(total>0,'EMPTY_ARCHIVE');check()\nFILE_14_L178= st=os.fstat(fd);named=os.stat(dest.name,dir_fd=parent,follow_symlinks=False)\nFILE_14_L179= need(st.st_dev==initial.st_dev and st.st_ino==initial.st_ino and t.ident(st)==t.ident(named) and\nFILE_14_L180= st.st_nlink==1 and st.st_size==total,'PRODUCER_FILE_IDENTITY_CHANGED')\nFILE_14_L200= \"\"\"Future explicit execution API. Caller must recheck full plan/coverage in guard.\nFILE_14_L201=\nFILE_14_L202= Compression CRC and SHA are verified; guest bootability remains unproved.\nFILE_14_L203= This function has no cloud/seal/delete operations and returns a readonly artifact.\nFILE_14_L204= \"\"\"\nFILE_14_L205= need(callable(guard),'GUARD_REQUIRED');guard();validate_job(job,int(time.time()));validate_tools(tools)\nFILE_14_L206= work=Path(work);parent=stream.private_dir(work)\nFILE_14_L209= tmp=work/'tmp';t.make_dir(tmp,exclusive=True)\nFILE_14_L210= dest=work/('guest-%d.%s.zst'%(job['vmid'],'tar' if job['type']=='lxc' else 'vma'))\nFILE_14_L211= def free_guard():\nFILE_14_L212= v=os.statvfs(work);need(v.f_frsize*v.f_bavail>=LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED')\nFILE_14_L213= free_guard()\nFILE_14_L214= result=capture_archive(dump_argv(job,tools['vzdump']['path'],tmp,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard)\nFILE_14_L215= guard();validate_tools(tools)\nFILE_14_L219= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH')\nFILE_14_L220= guard();validate_tools(tools)\nFILE_14_L221= return {**result,'path':str(dest),'vmid':job['vmid'],'type':job['type'],'node':job['node'],\nFILE_14_L222= 'config_sha256':job['config_sha256'],'compression_test_ok':True,'restored':False,\nFILE_14_L223= 'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True}\nFILE_15_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py\nFILE_15_BYTES=19595\nFILE_15_SHA256=97015277a4f3f8129c0fd11eb70c06293026366f1f8232a97feeed513a2dbcef\nFILE_15_CONTEXT_LINE_COUNT=140\nFILE_15_L3=Does not modify installed pve265, old workspaces, staging permissions or guest config.\nFILE_15_L4=Only explicit produce() executes vzdump. Private bounded stderr/exit metadata retained.\nFILE_15_L5=\"\"\"\nFILE_15_L6=import hashlib,json,os,re,secrets,selectors,signal,stat,subprocess,sys,time\nFILE_15_L7=from pathlib import Path\nFILE_15_L8=import v2core as core\nFILE_15_L9=import transport261 as t\nFILE_15_L10=import stream263 as stream\nFILE_15_L11=import pve265 as old\nFILE_15_L12=need=old.need\nFILE_15_L13=MAX_STDERR=2*1024**2\nFILE_15_L14=EXPECTED_CFG='09957aacd41ba9eeaf5fd4eda13936d580c9082a46895433379aa9dd0bad5969'\nFILE_15_L39=def live_gate(job,pvesh_path,pvesh_sha):\nFILE_15_L40= need(job.get('vmid')==110 and job.get('type')=='lxc' and job.get('node')=='pve01' and job.get('config_sha256')==EXPECTED_CFG,'ONLY_ACCEPTED_CT110')\nFILE_15_L41= vals=[]\nFILE_15_L42= for part in ('config','status/current','snapshot'):\nFILE_15_L43= need(t.sha(t.read_secure(Path(pvesh_path),16*1024**2)[0])==pvesh_sha,'PVESH_CHANGED')\nFILE_15_L44= rc,b=t.bounded_process([str(pvesh_path),'get','/nodes/pve01/lxc/110/'+part,'--output-format','json'],dict(old.ENV),262144,30)\nFILE_15_L45= need(rc==0,'CT_GATE_READ_FAILED')\nFILE_15_L46= try:vals.append(json.loads(b))\nFILE_15_L47= except (ValueError,UnicodeError):raise old.PVEError('CT_GATE_JSON') from None\nFILE_15_L48= validate_ct(*vals,job['config_sha256'])\nFILE_15_L49= return True\nFILE_15_L50=\nFILE_15_L59= def __init__(self,path,alias):\nFILE_15_L60= self.path=Path(path);self.alias=Path(alias);self.parent=None;self.fd=None\nFILE_15_L61= need(self.path.name=='perl' and self.alias.name=='perl5.40.1' and self.path.parent==self.alias.parent,'PERL_PATH_POLICY')\nFILE_15_L62= need(self.path.is_absolute() and '..' not in self.path.parts,'PERL_PATH_POLICY')\nFILE_15_L63= try:\nFILE_15_L64= self.parent=os.open('/',os.O_RDONLY|os.O_DIRECTORY)\nFILE_15_L65= for part in self.path.parts[1:-1]:\nFILE_15_L66= nxt=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=self.parent)\nFILE_15_L67= os.close(self.parent);self.parent=nxt\nFILE_15_L68= s=os.fstat(nxt)\nFILE_15_L69= need(s.st_uid==0 and not s.st_mode&0o022,'PERL_PARENT_UNTRUSTED')\nFILE_15_L75= need(os.pread(self.fd,4,0)==b'\\x7fELF','PERL_ELF_REQUIRED')\nFILE_15_L76= self.identity=t.ident(s)+(s.st_nlink,)\nFILE_15_L77= self.sha256=self._digest();self.check()\nFILE_15_L78= except BaseException:\nFILE_15_L79= self.close();raise\nFILE_15_L80= def _digest(self):\nFILE_15_L81= s=os.fstat(self.fd);need(00,'PERL_TIMEOUT')\nFILE_15_L123= rc=p.wait(timeout=left);self.check();return rc,bytes(out)\nFILE_15_L124= except subprocess.TimeoutExpired:raise old.PVEError('PERL_TIMEOUT') from None\nFILE_15_L125= finally:\nFILE_15_L126= if p is not None:\nFILE_15_L127= if p.poll() is None:\nFILE_15_L150= for p,h in pins.items():need(t.sha(t.read_secure(Path(p),2*1024**2)[0])==h,'PVE_MAPPING_SOURCE_CHANGED')\nFILE_15_L151= pin.check()\nFILE_15_L152= try:d=json.loads(b)\nFILE_15_L153= except (ValueError,UnicodeError):raise old.PVEError('PVE_IDMAP_JSON') from None\nFILE_15_L154= return validate_mapping(d)\nFILE_15_L155=\nFILE_15_L156=def _parent(path):\nFILE_15_L157= path=Path(path)\nFILE_15_L158= need(path.is_absolute() and '..' not in path.parts,'TEMP_PARENT_PATH')\nFILE_15_L159= fd=os.open('/',os.O_RDONLY|os.O_DIRECTORY)\nFILE_15_L160= try:\nFILE_15_L161= for i,part in enumerate(path.parts[1:]):\nFILE_15_L162= nxt=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=fd);os.close(fd);fd=nxt\nFILE_15_L163= s=os.fstat(fd);last=i==len(path.parts)-2\nFILE_15_L164= need(s.st_uid==0,'TEMP_PARENT_OWNER')\nFILE_15_L165= need(not s.st_mode&0o022 or last and s.st_mode&stat.S_ISVTX,'TEMP_PARENT_WRITABLE')\nFILE_15_L166= result=fd;fd=None;return result\nFILE_15_L170=\nFILE_15_L171=class MappedScratch:\nFILE_15_L172= \"\"\"Root-owned, mapped-GID traverse-only, no access for others. No recursive cleanup.\"\"\"\nFILE_15_L173= def __init__(self,gid,base='/var/tmp'):\nFILE_15_L174= need(type(gid) is int and gid==100000,'TEMP_GID_POLICY')\nFILE_15_L175= self.parent=_parent(base);self.fd=None;self.gid=gid\nFILE_15_L176= self.name='homelab-vzdump-268-'+secrets.token_hex(16);self.path=Path(base)/self.name\nFILE_15_L177= try:\nFILE_15_L178= os.mkdir(self.name,0o700,dir_fd=self.parent)\nFILE_15_L179= self.fd=os.open(self.name,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=self.parent)\nFILE_15_L181= os.fchown(self.fd,0,gid);os.fchmod(self.fd,0o710)\nFILE_15_L182= s=os.fstat(self.fd);self.identity=(s.st_dev,s.st_ino)\nFILE_15_L183= os.fsync(self.parent);self.check()\nFILE_15_L184= except BaseException:\nFILE_15_L185= if self.fd is not None:os.close(self.fd);self.fd=None\nFILE_15_L186= os.close(self.parent);self.parent=None\nFILE_15_L187= raise\nFILE_15_L199= name='access-probe';fd=os.open(name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o644,dir_fd=self.fd)\nFILE_15_L200= try:os.fchmod(fd,0o644);stream.write_all(fd,b'HOMELAB_ACCESS_PROBE\\n');os.fsync(fd);identity=t.ident(os.fstat(fd))\nFILE_15_L201= finally:os.close(fd)\nFILE_15_L202= positive='''import os,sys\nFILE_15_L203=p,private=sys.argv[1:]\nFILE_15_L204=assert open(p+'/access-probe','rb').read()==b'HOMELAB_ACCESS_PROBE\\\\n'\nFILE_15_L205=for action in (lambda:os.listdir(p),lambda:os.open(p+'/forbidden',os.O_WRONLY|os.O_CREAT,0o600),lambda:os.open(private,os.O_RDONLY|os.O_DIRECTORY)):\nFILE_15_L207= except PermissionError:continue\nFILE_15_L208= raise SystemExit(31)\nFILE_15_L209='''\nFILE_15_L210= negative='''import sys\nFILE_15_L211=try:open(sys.argv[1]+'/access-probe','rb').read()\nFILE_15_L212=except PermissionError:raise SystemExit(0)\nFILE_15_L213=raise SystemExit(32)\nFILE_15_L256= b=os.read(key.fileobj.fileno(),65536)\nFILE_15_L257= if not b:sel.unregister(key.fileobj);continue\nFILE_15_L258= if key.fileobj is p.stdout:\nFILE_15_L259= need(total+len(b)<=cap,'ARCHIVE_CAP_REACHED_PARTIAL_RETAINED')\nFILE_15_L260= stream.write_all(fd,b);digest.update(b);total+=len(b)\nFILE_15_L261= else:\nFILE_15_L262= err_total+=len(b);eh.update(b);chunk=b[:max(0,MAX_STDERR-err_kept)]\nFILE_15_L263= if chunk:stream.write_all(efd,chunk);err_kept+=len(chunk)\nFILE_15_L264= need(deadline>time.monotonic(),'PRODUCER_TIMEOUT')\nFILE_15_L265= try:rc=p.wait(timeout=deadline-time.monotonic())\nFILE_15_L266= except subprocess.TimeoutExpired:raise old.PVEError('PRODUCER_TIMEOUT') from None\nFILE_15_L267= need(rc==0,'PRODUCER_RC_FAILED_PARTIAL_RETAINED');need(total>0,'EMPTY_ARCHIVE');check()\nFILE_15_L268= st=os.fstat(fd);named=os.stat(dest.name,dir_fd=parent,follow_symlinks=False)\nFILE_15_L269= need(st.st_dev==initial.st_dev and st.st_ino==initial.st_ino and t.ident(st)==t.ident(named) and st.st_nlink==1 and st.st_size==total,'PRODUCER_FILE_IDENTITY_CHANGED')\nFILE_15_L270= os.fchmod(fd,0o400);os.fsync(fd);os.fsync(parent);good=True\nFILE_15_L306= scratch.probe(uid,work);emit('MAPPED_UID_ACCESS_AND_ISOLATION_PROBE','PASS')\nFILE_15_L307= def free_guard():\nFILE_15_L308= scratch.check()\nFILE_15_L309= for path,reserve,reason in ((work,old.LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED'),(scratch.path,256*1024**2,'PVE_TEMP_RESERVE_LOW')):\nFILE_15_L310= v=os.statvfs(path);need(v.f_frsize*v.f_bavail>=reserve,reason)\nFILE_15_L311= free_guard();guard();dest=work/'guest-110.tar.zst'\nFILE_15_L312= result=capture_archive(old.dump_argv(job,tools['vzdump']['path'],scratch.path,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard)\nFILE_15_L317= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH')\nFILE_15_L318= guard();old.validate_tools(tools);scratch.close(empty_only=True);complete=True;emit('PVE_TEMP_EMPTY_REMOVED','YES')\nFILE_15_L319= return {**result,'path':str(dest),'vmid':110,'type':'lxc','node':job['node'],'config_sha256':job['config_sha256'],\nFILE_15_L320= 'compression_test_ok':True,'restored':False,'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True}\nFILE_15_L321= finally:\nFILE_15_L322= m=work/'PRODUCER_PROCESS.json'\nFILE_15_L323= if m.exists():\nFILE_16_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve282.py\nFILE_16_BYTES=6195\nFILE_16_SHA256=73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012\nFILE_16_CONTEXT_LINE_COUNT=58\nFILE_16_L1=\"\"\"282R1 bounded CT112 adapter; proven 270 capture/temp/Perl implementation unchanged.\nFILE_16_L2=Expected source hash must be supplied from the immutable-hash-bound 265 plan.\nFILE_16_L3=No changes to source guest configuration; no source110 backup or guest deletion.\nFILE_16_L4=\"\"\"\nFILE_16_L5=import copy,json,os,time\nFILE_16_L6=from pathlib import Path\nFILE_16_L7=import v2core as core\nFILE_16_L8=import transport261 as t\nFILE_16_L9=import stream263 as stream\nFILE_16_L10=import pve265 as old\nFILE_16_L11=import pve270 as proven\nFILE_16_L12=import guest266 as g\nFILE_16_L13=need=old.need\nFILE_16_L14=PerlPin=proven.PerlPin\nFILE_16_L15=host_mapping=proven.host_mapping\nFILE_16_L26= need(core.valid_hash(expected_config) and job.get('config_sha256')==expected_config,'CT112_PLAN_CONFIG_ANCHOR')\nFILE_16_L27= return True\nFILE_16_L28=\nFILE_16_L29=def select112(current,baseline,now):\nFILE_16_L30= rows=g.plan_rows(current);previous=g.plan_rows(baseline)\nFILE_16_L31= by_id={j['vmid']:j for j in previous}\nFILE_16_L32= need(TARGET_VMID in by_id,'CT112_MISSING_FROM_ACCEPTED265')\nFILE_16_L33= g.same_state(baseline,current,TARGET_VMID)\nFILE_16_L34= need({j['vmid'] for j in rows}==set(by_id),'ACCEPTED265_MEMBERSHIP_CHANGED')\nFILE_16_L35= for row in rows:\nFILE_16_L36= need(row['config_sha256']==by_id[row['vmid']]['config_sha256'],'ACCEPTED265_GUEST_CONFIG_CHANGED')\nFILE_16_L37= selected=[j for j in rows if j['vmid']==TARGET_VMID]\nFILE_16_L38= need(len(selected)==1,'CT112_NOT_UNIQUE')\nFILE_16_L39= job=copy.deepcopy(selected[0]);old.validate_job(job,now)\nFILE_16_L40= need(current.get('global_blockers')==[],'GLOBAL_DEFAULT_BLOCK')\nFILE_16_L41= need(job.get('compression_dependent') is False and job.get('configured_full_cycle_fits') is True,'CT112_FULL_CYCLE_SPACE_UNPROVEN')\nFILE_16_L42= disks=job.get('disks');need(type(disks) is list and len(disks)==1,'CT112_SINGLE_ROOTFS_REQUIRED')\nFILE_16_L53=def live_gate(job,pvesh_path,pvesh_sha,expected_config):\nFILE_16_L54= require_target(job,expected_config)\nFILE_16_L55= vals=[]\nFILE_16_L56= for part in ('config','status/current','snapshot'):\nFILE_16_L57= need(t.sha(t.read_secure(Path(pvesh_path),16*1024**2)[0])==pvesh_sha,'PVESH_CHANGED')\nFILE_16_L58= rc,b=t.bounded_process([str(pvesh_path),'get','/nodes/pve01/lxc/112/'+part,'--output-format','json'],dict(old.ENV),262144,30)\nFILE_16_L59= need(rc==0,'CT112_GATE_READ_FAILED')\nFILE_16_L60= try:vals.append(json.loads(b))\nFILE_16_L61= except (ValueError,UnicodeError):raise old.PVEError('CT112_GATE_JSON') from None\nFILE_16_L62= validate_ct(*vals,expected_config)\nFILE_16_L63= return True\nFILE_16_L64=\nFILE_16_L79= scratch.probe(uid,work);emit('MAPPED_UID_ACCESS_AND_ISOLATION_PROBE','PASS')\nFILE_16_L80= def free_guard():\nFILE_16_L81= scratch.check()\nFILE_16_L82= for path,reserve,reason in ((work,old.LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED'),(scratch.path,256*1024**2,'PVE_TEMP_RESERVE_LOW')):\nFILE_16_L83= v=os.statvfs(path);need(v.f_frsize*v.f_bavail>=reserve,reason)\nFILE_16_L84= free_guard();guard();dest=work/'guest-112.tar.zst'\nFILE_16_L85= result=capture_archive(old.dump_argv(job,tools['vzdump']['path'],scratch.path,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard)\nFILE_16_L90= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH')\nFILE_16_L91= guard();old.validate_tools(tools);scratch.close(empty_only=True);complete=True;emit('PVE_TEMP_EMPTY_REMOVED','YES')\nFILE_16_L92= return {**result,'path':str(dest),'vmid':112,'type':'lxc','node':job['node'],'config_sha256':job['config_sha256'],\nFILE_16_L93= 'compression_test_ok':True,'restored':False,'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True}\nFILE_16_L94= finally:\nFILE_16_L95= m=work/'PRODUCER_PROCESS.json'\nFILE_16_L96= if m.exists():\nFILE_17_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/restore262.py\nFILE_17_BYTES=14701\nFILE_17_SHA256=56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1\nFILE_17_CONTEXT_LINE_COUNT=180\nFILE_17_L1=\"\"\"262R1 bounded cloud-discovered recovery. No live action on import.\nFILE_17_L2=\nFILE_17_L3=Cloud access is strictly lsjson/cat. No source-local workspace/manifest/receipt\nFILE_17_L4=is read. The external accepted manifest digest is a trust anchor, not placement.\nFILE_17_L5=\"\"\"\nFILE_17_L6=import hashlib\nFILE_17_L7=import io\nFILE_17_L8=import json\nFILE_17_L9=import os\nFILE_17_L10=from pathlib import Path\nFILE_17_L11=import re\nFILE_17_L12=import stat\nFILE_17_L13=import tarfile\nFILE_17_L14=import time\nFILE_17_L15=import v2core as core\nFILE_17_L16=import transport261 as t\nFILE_17_L17=\nFILE_17_L18=SOURCE_NAMES=frozenset(('etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md','etc/pve/31_HOMELAB_REFERENCE.md',\nFILE_17_L19= 'usr/local/sbin/homelab-control-backup','etc/systemd/system/homelab-control-backup.service'))\nFILE_17_L20=ALL_NAMES=SOURCE_NAMES|{'SOURCE_INDEX.json'}\nFILE_17_L21=MAX_GENERATIONS=64\nFILE_17_L22=SEAL_KEYS={'schema_version','state','generation','logical_id','manifest_sha256','replicas','receipts','acceptance'}\nFILE_17_L23=\nFILE_17_L24=def strict_json(data,limit):\nFILE_17_L25= t.need(type(data) is bytes and len(data)<=limit,'JSON_BOUND')\nFILE_17_L29= t.need(k not in result,'JSON_DUPLICATE_KEY');result[k]=v\nFILE_17_L30= return result\nFILE_17_L31= def bad_constant(value):raise t.Stop('JSON_NONFINITE_NUMBER')\nDEPENDENCY_PATH_COUNT=28\nDEPENDENCY_PATH_1=/opt/homelab-backup-v2/releases/core-260r1\nDEPENDENCY_PATH_2=/opt/homelab-backup-v2/releases/core-260r1/v2core.py\nDEPENDENCY_PATH_3=/opt/homelab-backup-v2/releases/ct-backup-282r1\nDEPENDENCY_PATH_4=/opt/homelab-backup-v2/releases/ct-backup-282r1/guest266.py\nDEPENDENCY_PATH_5=/opt/homelab-backup-v2/releases/ct-backup-282r1/pve282.py\nDEPENDENCY_PATH_6=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_guest266.py\nDEPENDENCY_PATH_7=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_perl270.py\nDEPENDENCY_PATH_8=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_producer282.py\nDEPENDENCY_PATH_9=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_pve282.py\nDEPENDENCY_PATH_10=/opt/homelab-backup-v2/releases/large-cloud-264r1\nDEPENDENCY_PATH_11=/opt/homelab-backup-v2/releases/large-cloud-264r1/large264.py\nDEPENDENCY_PATH_12=/opt/homelab-backup-v2/releases/pve-producer-265r1\nDEPENDENCY_PATH_13=/opt/homelab-backup-v2/releases/pve-producer-265r1/PLAN.json\nDEPENDENCY_PATH_14=/opt/homelab-backup-v2/releases/pve-producer-265r1/pve265.py\nDEPENDENCY_PATH_15=/opt/homelab-backup-v2/releases/pve-producer-265r1/test_pve265.py\nDEPENDENCY_PATH_16=/opt/homelab-backup-v2/releases/pve-retry-268r1\nDEPENDENCY_PATH_17=/opt/homelab-backup-v2/releases/pve-retry-270r1\nDEPENDENCY_PATH_18=/opt/homelab-backup-v2/releases/pve-retry-270r1/pve270.py\nDEPENDENCY_PATH_19=/opt/homelab-backup-v2/releases/pve-retry-270r1/test_pve270.py\nDEPENDENCY_PATH_20=/opt/homelab-backup-v2/releases/pve-run-266r1\nDEPENDENCY_PATH_21=/opt/homelab-backup-v2/releases/restore-262r1\nDEPENDENCY_PATH_22=/opt/homelab-backup-v2/releases/restore-262r1/restore262.py\nDEPENDENCY_PATH_23=/opt/homelab-backup-v2/releases/stream-263r1\nDEPENDENCY_PATH_24=/opt/homelab-backup-v2/releases/stream-263r1/nodeplan263.py\nDEPENDENCY_PATH_25=/opt/homelab-backup-v2/releases/stream-263r1/stream263.py\nDEPENDENCY_PATH_26=/opt/homelab-backup-v2/releases/transport-261r1\nDEPENDENCY_PATH_27=/opt/homelab-backup-v2/releases/transport-261r1/transport261.py\nDEPENDENCY_PATH_28=/opt/homelab-backup-v2/releases/vm-backup-283r1\nHAS_RCLONE_SOURCE=true\nHAS_OBJECT_PATH_CONTRACT=true\nDECISION=PASS_BACKUP213_EXACT_CLOUD_OBJECT_CONTRACT_CAPTURED\nNEXT_GATE=BUILD_CORRECTED_CLOUD_GENERATION_RECOVERY\nCLOUD_READS=0\nCLOUD_WRITES=0\nVPS_SSH_ATTEMPTS=0\nMESSENGERS214_CLOSED=false\nMAJOR_STAGES_REMAINING=8\nNEWFI213_END=true\n" +} diff --git a/runtime/history/NEWFI-260920-NEWFDOM-MESSENGERS214-BASE213.txt b/runtime/history/NEWFI-260920-NEWFDOM-MESSENGERS214-BASE213.txt new file mode 100644 index 00000000..446db3f8 --- /dev/null +++ b/runtime/history/NEWFI-260920-NEWFDOM-MESSENGERS214-BASE213.txt @@ -0,0 +1,1161 @@ +CHAT_OUTPUT_BEGIN +COMMAND_ID=NEWFI-260920-NEWFDOM-MESSENGERS214-BASE213 +STATUS=OK +RC=0 +HOST=pve01 +MODE=read-only +COMPONENT=newfi-newfdom-messengers214-backupv2-base-release-authority-aware-readonly +REFERENCE_REGISTER_CHECK=OK +REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 +ERROR_REGISTER_CHECK=OK +ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 +COMMAND_SHA256=65d18e767349b7319c7197b07d4122dae093e606f12244bfd676798e5606e937 +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=0 +CHANGES_MADE=false +ROLLBACK_STARTED=false +ROLLBACK_RESTORED=null +MUTATION_OUTCOME=NO_MUTATION +SANITIZED=yes +SECRETS_INCLUDED=no +PRIVATE_ADDRESSES_INCLUDED=no +RAW_EVIDENCE_SHA256=417d54bc3ee03b085ea2894f80cd2f3c4e8df0652fe89b0f35fa9f5df73e61c3 +SANITIZED_OUTPUT_SHA256=c2da47960582cda8715a7fd595b714f908f504dd277254ed2b277fdb5c900bf3 +OUTPUT_BEGIN +NEWFI213_BEGIN=true +MODE=read-only +IMMUTABLE_PLAN_CHECK=PASS +CHANGES_MADE=false +MUTATION_OUTCOME=NO_MUTATION +RUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d +RUNNER_PIN_MATCH=true +ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 +ERROR_REGISTER_PIN_MATCH=true +REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 +REFERENCE_PIN_MATCH=true +WRAPPER_SHA256=5077444065c732451cb84898680f62361b21a24ef9eb4f191253e82ead524ea2 +WRAPPER_PIN_MATCH=true +OVERLAY_SHA256=ca6876fa9c3b1465b045344a8ad46962de16972c7dadc4ac6257c7d225047793 +OVERLAY_PIN_MATCH=true +PRODUCTION_ACCEPTANCE_SHA256=708a481f6719ff1ed8d0afec1902650ed36ac631e980611b015419f504f16df0 +PRODUCTION_ACCEPTANCE_PIN_MATCH=true +AUTHORITY_PIN_MATCH=true +BACKUP_SOURCE_PIN_MATCH=true +ACCEPTED_RELEASE=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1 +BASE_RELEASE=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1 +MATCHING_FILE_COUNT=26 +FILE_1_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ACCEPTANCE.json +FILE_1_BYTES=517 +FILE_1_SHA256=904dfccad43b55ad0b53cfbd29fb45fee36fd0fbfa5afa58da5c3cca0ee7153b +FILE_1_CONTEXT_LINE_COUNT=1 +FILE_1_L1={"command_id":"SUPPORT-260916-INFRA-DR-BACKUPV2-FINAL-REPLACEMENT-PLAN-STABILITY-SCHEMA-RCA-FIX-VALIDATE-PROMOTE-CUTOVER-SEAL-486R1","ct112_manifest_sha256":"1ac4f16a54bcdd8b896c9a131abef966d315256aa56da3f83ed0b30845c4fcb0","ct112_payload_bytes":223875308,"ct112_payload_sha256":"d443b85e76e806830fd9c372d0a09fc5b91ca9c9d35c44fb0ad7968cc996d01d","legacy_ids":["node-pve01","md-relay","spb-ingress","us-netbird","router-netcraze","xf-codevipe","xf-zakrutim","control-bootstrap-v2"],"representative_pass":9,"schema":1} +FILE_2_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/RELEASE_MANIFEST.json +FILE_2_BYTES=3301 +FILE_2_SHA256=a36dd0db88592ec3d355ef43e6b218da6c9ad649e9ccf8c8f680b96eb23a156e +FILE_2_CONTEXT_LINE_COUNT=1 +FILE_2_L1={"files":{"ACCEPTANCE.json":{"bytes":517,"mode":256,"sha256":"904dfccad43b55ad0b53cfbd29fb45fee36fd0fbfa5afa58da5c3cca0ee7153b"},"candidate411/CONTRACT.json":{"bytes":2077,"mode":256,"sha256":"a414715f1b91b3701c52073709a80c38218f76e3961618e8af61489211e50c01"},"candidate411/PLAN265_REFERENCE.json":{"bytes":7210,"mode":256,"sha256":"8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757"},"candidate411/PROVENANCE.json":{"bytes":4310,"mode":256,"sha256":"303f44dc8e91c8e6a675f9ea406ce4b7532ed98440a9b6807fa593ede9d33254"},"candidate411/adapter.py":{"bytes":2989,"mode":320,"sha256":"18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3"},"candidate411/guest266.py":{"bytes":7385,"mode":256,"sha256":"86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868"},"candidate411/large264.py":{"bytes":16514,"mode":256,"sha256":"5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a"},"candidate411/nodeplan263.py":{"bytes":4603,"mode":256,"sha256":"db5c16ee24d25aa +FILE_3_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py +FILE_3_BYTES=10955 +FILE_3_SHA256=8cac6e627d5e7abe5d3163cdd24911995a1e3f45c776f9373c2aa68784f5ec6c +FILE_3_CONTEXT_LINE_COUNT=98 +FILE_3_L1=#!/usr/bin/env python3 +FILE_3_L2=from __future__ import annotations +FILE_3_L3=import hashlib,json,os,secrets,shutil,stat,subprocess,sys,time +FILE_3_L4=from pathlib import Path +FILE_3_L5=ROOT=Path(__file__).resolve().parent +FILE_3_L6=CAND=ROOT/'candidate411' +FILE_3_L7=if str(CAND) not in sys.path:sys.path.insert(0,str(CAND)) +FILE_3_L8=import guest266,large264 as large,pve265,pve282,transport261 as t,v2core as core +FILE_3_L9=CT_ID='guest-pve01-lxc-112' +FILE_3_L10=PLAN=CAND/'PLAN265_REFERENCE.json' +FILE_3_L11=PLAN_SHA='8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757' +FILE_3_L12=RCLONE=Path('/usr/bin/rclone'); RCLONE_SHA='20df3d5eac5d4e187ce638e4216354e7fa6778c3c51f0caf48c48acca9686354' +FILE_3_L13=RCLONE_CFG=Path('/root/.config/rclone/[SENSITIVE_FILE]') +FILE_3_L14=BINDING_FP='ed48c1345771c3e0f50db9516f13f26bb66047283e2f883d5df272f15e2282b8' +FILE_3_L15=VZDUMP=Path('/usr/bin/vzdump'); VZDUMP_SHA='21339d009afd55cb7ec613b64c1f493b2a7dbd18b85b4d712909ef6d6710f4ea' +FILE_3_L16=ZSTD=Path('/usr/bin/zstd'); ZSTD_SHA='3eb7744cf8fd1ecd46d617098ee870b3e7911fe570ffaba662c1c5b7d6456020' +FILE_3_L26= p=subprocess.run(argv,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=45,check=False,env=ENV) +FILE_3_L27= if p.returncode!=0 or len(p.stdout)>4*1024*1024:raise RuntimeError('PVESH_READ_FAILED') +FILE_3_L28= try:return json.loads(p.stdout.decode('utf-8','strict')) +FILE_3_L29= except (ValueError,UnicodeError):raise RuntimeError('PVESH_JSON') from None +FILE_3_L30= +FILE_3_L31=def _baseline(): +FILE_3_L32= b=t.read_secure(PLAN,1024*1024,private=True)[0] +FILE_3_L33= if t.sha(b)!=PLAN_SHA:raise RuntimeError('PLAN_REFERENCE_IDENTITY') +FILE_3_L34= try:return json.loads(b.decode('utf-8','strict')) +FILE_3_L35= except (ValueError,UnicodeError):raise RuntimeError('PLAN_REFERENCE_JSON') from None +FILE_3_L36= +FILE_3_L37=def _defaults(): +FILE_3_L38= p=Path('/etc/vzdump.conf');st=os.stat(p,follow_symlinks=False) +FILE_3_L100= return {'path':str(path),'sha256':want} +FILE_3_L101= +FILE_3_L102=def _private_config(): +FILE_3_L103= b,identity=t.read_secure(RCLONE_CFG,t.MAX_CONFIG_BYTES,private=True) +FILE_3_L104= text=b.decode('utf-8-sig','strict');entries=core.inventory_from_config(text);binding=t.binding(text) +FILE_3_L105= if len(entries)!=13 or len({x['account_id'] for x in entries})!=13:raise RuntimeError('RCLONE_INVENTORY') +FILE_3_L106= if hashlib.sha256(binding.encode()).hexdigest()!=BINDING_FP:raise RuntimeError('RCLONE_BINDING') +FILE_3_L107= return b,entries,binding +FILE_3_L108= +FILE_3_L109=def _write_private(path,data): +FILE_3_L118=class LazyReader: +FILE_3_L119= def __init__(self,work,entries,binding,guard,deadline,logical_id): +FILE_3_L120= self.work=Path(work);self.entries=entries;self.binding=binding;self.guard=guard;self.deadline=deadline;self.logical_id=logical_id +FILE_3_L121= self.snapshot=self.work/'rclone.snapshot';self.inner=None +FILE_3_L122= def _ensure(self): +FILE_3_L123= if self.inner is not None:return self.inner +FILE_3_L124= b,e,bind=_private_config() +FILE_3_L125= if e!=self.entries or bind!=self.binding:raise RuntimeError('RCLONE_SEMANTIC_DRIFT') +FILE_3_L126= _write_private(self.snapshot,b) +FILE_3_L127= self.inner=large.ReadOnlyLarge(str(RCLONE),self.snapshot,e,bind,self.guard,self.work,self.deadline,self.logical_id) +FILE_3_L128= return self.inner +FILE_3_L129= def list_generations(self,*a,**k):return self._ensure().list_generations(*a,**k) +FILE_3_L130= def get(self,*a,**k):return self._ensure().get(*a,**k) +FILE_3_L131= def read_stream(self,*a,**k):return self._ensure().read_stream(*a,**k) +FILE_3_L132= def close(self): +FILE_3_L135= +FILE_3_L136=def selftest(): +FILE_3_L137= if _sha(PLAN,1024*1024)!=PLAN_SHA:return False +FILE_3_L138= for p,h in ((RCLONE,RCLONE_SHA),(VZDUMP,VZDUMP_SHA),(ZSTD,ZSTD_SHA)): +FILE_3_L139= if _sha(p)!=h:return False +FILE_3_L140= return True +FILE_3_L141= +FILE_3_L142=def run_target(target,state_writer=None): +FILE_3_L143= if not isinstance(target,dict) or target.get('logical_id')!=CT_ID or target.get('vmid')!=112:return (_ for _ in ()).throw(RuntimeError('CT112_DESCRIPTOR')) +FILE_3_L144= if _sha(PLAN,1024*1024)!=PLAN_SHA:raise RuntimeError('PLAN_REFERENCE_IDENTITY') +FILE_3_L145= tools={'vzdump':_tool(VZDUMP,VZDUMP_SHA),'zstd':_tool(ZSTD,ZSTD_SHA)};_tool(RCLONE,RCLONE_SHA) +FILE_3_L146= pvesh_sha=_sha(PVESH);now=int(time.time());current=_fresh_plan(now);job=_select_live_ct112(current,now) +FILE_3_L147= expected=job['config_sha256'];mapping=_canonical_host_mapping() +FILE_3_L148= def producer_guard(): +FILE_3_L153= pve282.live_gate(fresh,str(PVESH),pvesh_sha,expected) +FILE_3_L154= producer_guard() +FILE_3_L155= cfg,entries,binding=_private_config() +FILE_3_L156= generation='V2-'+time.strftime('%Y%m%dT%H%M%SZ',time.gmtime())+'-'+secrets.token_hex(6) +FILE_3_L157= work=STAGING/('backup-v2-'+generation);work.mkdir(mode=0o700,parents=False,exist_ok=False) +FILE_3_L158= deadline=time.monotonic()+21600 +FILE_3_L159= def cloud_guard(): +FILE_3_L160= _,e,b=_private_config() +FILE_3_L161= if e!=entries or b!=binding:raise RuntimeError('RCLONE_SEMANTIC_DRIFT') +FILE_3_L162= writer=large.CloudLarge(str(RCLONE),RCLONE_CFG,entries,binding,cloud_guard,work/'upload',deadline) +FILE_3_L163= reader=LazyReader(work/'restore',entries,binding,cloud_guard,deadline,CT_ID) +FILE_3_L164= def produce(j,w):return pve282.produce(j,w,tools,producer_guard,mapping=mapping,expected_config=expected,seconds=14400,emit=lambda k,v:None) +FILE_3_L165= def compression(path): +FILE_3_L166= p=subprocess.run([str(ZSTD),'--test','--quiet','--',str(path)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,timeout=3600,check=False,env=ENV) +FILE_3_L167= return p.returncode==0 +FILE_3_L168= try: +FILE_3_L169= r=guest266.run_flow(job,work,entries,writer,reader,produce,producer_guard,cloud_guard,compression,generation,lambda k,v:None) +FILE_3_L170= reps=r.get('replicas') +FILE_3_L171= if not isinstance(reps,list) or len(reps)!=2 or len({x.get('account_id') for x in reps})!=2:raise RuntimeError('REPLICA_PHYSICAL_INDEPENDENCE') +FILE_3_L172= if r.get('archive_cloud_restore_verified') is not True or r.get('local_payload_files_remaining')!=0:raise RuntimeError('CT112_RESTORE_CONTRACT') +FILE_3_L173= out={**r,'status':'OK','cloud_only_independent_restore_verified':True,'local_payload_files_remaining':0} +FILE_3_L174= reader.close();shutil.rmtree(work) +FILE_3_L175= if work.exists():raise RuntimeError('CT112_WORKSPACE_CLEANUP') +FILE_3_L176= if state_writer is not None: +FILE_3_L177= state_writer(CT_ID,'SUCCESS',generation=r['generation'],payload_sha256=r['payload_sha256'],manifest_sha256=r['manifest_sha256'],replicas=r['replicas'],green_verified=True) +FILE_3_L178= return out +FILE_3_L179= except BaseException: +FILE_3_L180= try:reader.close() +FILE_3_L181= except BaseException:pass +FILE_3_L182= raise +FILE_4_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py +FILE_4_BYTES=59719 +FILE_4_SHA256=b6e79f087b9f1d21dac4f77a7b46b743299bbb85bc716e80d2ea67c2e038bcd7 +FILE_4_CONTEXT_LINE_COUNT=180 +FILE_4_L1=#!/usr/bin/python3 +FILE_4_L2=import ast, base64, datetime as dt, hashlib, io, json, os, pathlib, random, re, select, shlex, shutil, stat, subprocess, sys, tarfile, tempfile, time +FILE_4_L3= +FILE_4_L4=PART_SIZE=536870912 +FILE_4_L5=RESERVE_BYTES=536870912 +FILE_4_L6=STATE_ROOT=pathlib.Path('/var/lib/homelab-backup-v2') +FILE_4_L7=CONFIG_ROOT=pathlib.Path('/etc/homelab-backup-v2') +FILE_4_L8=TARGETS_PATH=CONFIG_ROOT/'targets.json' +FILE_4_L9=WORK_ROOT=pathlib.Path('/mnt/staging/homelab-backup-v2') +FILE_4_L10=ENGINE_PATH=pathlib.Path('/usr/local/sbin/homelab-backup-v2') +FILE_4_L11=RCLONE=shutil.which('rclone') or '/usr/bin/rclone' +FILE_4_L12=SSH=shutil.which('ssh') or '/usr/bin/ssh' +FILE_4_L13=SCP=shutil.which('scp') or '/usr/bin/scp' +FILE_4_L14=SSH_KEYSCAN=shutil.which('ssh-keyscan') or '/usr/bin/ssh-keyscan' +FILE_4_L30=P282_SHA='73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012' +FILE_4_L31=P265=pathlib.Path('/opt/homelab-backup-v2/releases/pve-producer-265r1/pve265.py') +FILE_4_L32=P265_SHA='cd2f634414a8e38a3789015aeca5a9b47821f77d7d1fa61dcdbdc866172963be' +FILE_4_L33=RESULT_CLASSES=('SUCCESS','DEFER','PARTIAL_VALID','FAIL') +FILE_4_L34=HISTORICAL_RELEASE_GRAPH=[ +FILE_4_L35= (pathlib.Path('/opt/homelab-backup-v2/releases/core-260r1/v2core.py'),'e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2'), +FILE_4_L36= (pathlib.Path('/opt/homelab-backup-v2/releases/transport-261r1/transport261.py'),'b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688'), +FILE_4_L51= pathlib.Path('/opt/homelab-backup-v2/releases/large-cloud-264r1'), +FILE_4_L52= pathlib.Path('/opt/homelab-backup-v2/releases/stream-263r1'), +FILE_4_L53= pathlib.Path('/opt/homelab-backup-v2/releases/transport-261r1'), +FILE_4_L54= pathlib.Path('/opt/homelab-backup-v2/releases/restore-262r1'), +FILE_4_L55= pathlib.Path('/opt/homelab-backup-v2/releases/core-260r1'), +FILE_4_L56= pathlib.Path('/opt/homelab-backup-v2/releases/vm-backup-283r1'), +FILE_4_L57=] +FILE_4_L58=_HISTORICAL_HELPER = r'''import importlib,importlib.util,json,pathlib,sys +FILE_4_L59=mode=sys.argv[1] +FILE_4_L60=if mode=='import_all': +FILE_4_L61= out={} +FILE_4_L74=''' +FILE_4_L75=os.umask(0o077) +FILE_4_L76= +FILE_4_L77=REMOTES=[ +FILE_4_L78= ('pve01-mail-01-crypt','pve01-mail-01'),('pve01-mail-02-crypt','pve01-mail-02'),('pve01-mail-03-crypt','pve01-mail-03'),('pve01-mail-04-crypt','pve01-mail-04'),('pve01-mail-05-crypt','pve01-mail-05'), +FILE_4_L79= ('pve02-mail-01-crypt','pve02-mail-01'),('pve02-mail-02-crypt','pve02-mail-02'),('pve02-mail-03-crypt','pve02-mail-03'),('pve02-mail-04-crypt','pve02-mail-04'), +FILE_4_L80= ('pve03-mail-01-crypt','pve03-mail-01'),('pve03-mail-02-crypt','pve03-mail-02'),('pve03-mail-03-crypt','pve03-mail-03'),('pve03-mail-04-crypt','pve03-mail-04')] +FILE_4_L81= +FILE_4_L82=class BackupError(RuntimeError): +FILE_4_L83= def __init__(self, code, detail=''): +FILE_4_L102= if not b: break +FILE_4_L103= h.update(b); n+=len(b) +FILE_4_L104= if remain is not None: remain-=len(b) +FILE_4_L105= if length is not None and n!=length: raise BackupError('LOCAL_PART_SHORT_READ') +FILE_4_L106= return h.hexdigest() +FILE_4_L107= +FILE_4_L108=def part_plan(n): +FILE_4_L109= if not isinstance(n,int) or n<=0: raise BackupError('PAYLOAD_SIZE_INVALID') +FILE_4_L110= out=[]; off=0; idx=1 +FILE_4_L111= while offtimeout: +FILE_4_L314= p.kill(); p.wait(); raise BackupError('RCLONE_CAT_TIMEOUT') +FILE_4_L315= b=p.stdout.read(1024*1024) +FILE_4_L316= if not b: break +FILE_4_L317= h.update(b); n+=len(b) +FILE_4_L318= p.stderr.read(); rc=p.wait() +FILE_4_L319= if rc!=0: raise BackupError('RCLONE_CAT_FAILED',str(rc)) +FILE_4_L320= if expected_bytes is not None and n!=expected_bytes: raise BackupError('RCLONE_CAT_SIZE_MISMATCH') +FILE_4_L321= return h.hexdigest(),n +FILE_4_L322= +FILE_4_L323=def rclone_cat_bytes(remote_path,max_bytes=4*1024*1024): +FILE_4_L324= p=run(rclone_base_args()+['cat',remote_path,'--contimeout','20s','--timeout','60s'],timeout=90) +FILE_4_L325= if len(p.stdout)>max_bytes: raise BackupError('RCLONE_SMALL_OBJECT_TOO_LARGE') +FILE_4_L326= return p.stdout +FILE_4_L327= +FILE_4_L328=def generation_id(logical_id): +FILE_4_L329= ts=dt.datetime.now(dt.timezone.utc).strftime('%Y%m%dT%H%M%SZ') +FILE_4_L330= return f'v2-{logical_id}-{ts}-{os.urandom(6).hex()}' +FILE_4_L331= +FILE_4_L332=def upload_and_seal(t,payload_path,source_meta): +FILE_4_L333= payload_path=pathlib.Path(payload_path); n=payload_path.stat().st_size +FILE_4_L334= psha=sha256_file(payload_path); reps,bad,healthy=allocator(n) +FILE_4_L335= gen=source_meta['generation']; rel=remote_generation_path(t['scope'],t['logical_id'],gen) +FILE_4_L336= if remote_exists_any(t['scope'],t['logical_id'],gen): raise BackupError('GENERATION_COLLISION') +FILE_4_L337= parts=part_plan(n) +FILE_4_L338= for p in parts: p['sha256']=sha256_file(payload_path,p['offset'],p['bytes']) +FILE_4_L339= manifest=make_manifest(t['scope'],t['logical_id'],gen,n,psha,parts,reps,source_meta) +FILE_4_L340= mb=canonical_json(manifest)+b'\n'; msha=sha256_bytes(mb) +FILE_4_L341= for rep in reps: +FILE_4_L342= base=rep['crypt']+':'+rel +FILE_4_L343= for part in parts: +FILE_4_L344= rp=base+'/parts/'+part['name'] +FILE_5_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py +FILE_5_BYTES=4214 +FILE_5_SHA256=b3050a8f95e6d9826a4f0786cd3ca32012b6ab10486d10e4981b50b8a0affff2 +FILE_5_CONTEXT_LINE_COUNT=60 +FILE_5_L3= +FILE_5_L4=This module does not discover credentials, modify scheduler state, or implement the +FILE_5_L5=CT112 producer/cloud path. It routes CT112 to an explicitly supplied callback and +FILE_5_L6=isolates legacy engine state writes into a candidate-owned state directory. +FILE_5_L7=""" +FILE_5_L8=from __future__ import annotations +FILE_5_L9= +FILE_5_L10=import json +FILE_5_L11=import os +FILE_5_L12=from pathlib import Path +FILE_5_L13=from typing import Callable, Iterable, Mapping, Any +FILE_5_L14= +FILE_5_L15=LEGACY_IDS=( +FILE_5_L16= 'node-pve01', +FILE_5_L24=) +FILE_5_L25=CT112_ID='guest-pve01-lxc-112' +FILE_5_L26=REPRESENTATIVE_IDS=(CT112_ID,)+LEGACY_IDS +FILE_5_L27=CANDIDATE_ROOT=Path(__file__).resolve().parent +FILE_5_L28=STATE_ROOT=CANDIDATE_ROOT/'state' +FILE_5_L29= +FILE_5_L30=class ContractError(RuntimeError): +FILE_5_L31= pass +FILE_5_L36= raise ContractError('TARGET_LOGICAL_ID') +FILE_5_L37= return value +FILE_5_L38= +FILE_5_L39=def partition_targets(targets: Iterable[Mapping[str, Any]]): +FILE_5_L40= selected={} +FILE_5_L41= wanted=set(REPRESENTATIVE_IDS) +FILE_5_L42= for target in targets: +FILE_5_L62= with os.fdopen(fd,'wb',closefd=True) as f: +FILE_5_L63= f.write(data);f.flush();os.fsync(f.fileno()) +FILE_5_L64= os.replace(tmp,path) +FILE_5_L65= except BaseException: +FILE_5_L66= try: os.unlink(tmp) +FILE_5_L67= except FileNotFoundError: pass +FILE_5_L68= raise +FILE_5_L72= def write_state(logical_id,result,**kw): +FILE_5_L73= if logical_id not in LEGACY_IDS: +FILE_5_L74= raise ContractError('STATE_TARGET_FORBIDDEN') +FILE_5_L75= payload={'logical_id':logical_id,'result':result,'meta':kw,'schema_version':1} +FILE_5_L76= _atomic_json(root/(logical_id+'.json'),payload) +FILE_5_L77= return write_state +FILE_5_L78= +FILE_5_L79=def run_legacy_lane(engine, target: Mapping[str, Any]): +FILE_5_L97= raise ContractError('CT112_RESULT_SCHEMA') +FILE_5_L98= if result.get('logical_id')!=CT112_ID or result.get('status')!='OK': +FILE_5_L99= raise ContractError('CT112_RESULT_STATUS') +FILE_5_L100= if result.get('cloud_only_independent_restore_verified') is not True: +FILE_5_L101= raise ContractError('CT112_RESTORE_NOT_VERIFIED') +FILE_5_L102= if result.get('local_payload_files_remaining')!=0: +FILE_5_L103= raise ContractError('CT112_LOCAL_PAYLOAD_REMAINS') +FILE_5_L104= return result +FILE_5_L105= +FILE_5_L106=def run_representative(engine, targets, ct112_callback: Callable[[Mapping[str, Any]], Any]): +FILE_5_L107= if not callable(ct112_callback): +FILE_5_L108= raise ContractError('CT112_CALLBACK_REQUIRED') +FILE_5_L109= legacy,ct112=partition_targets(targets) +FILE_5_L110= results=[_validate_ct112_result(ct112_callback(ct112))] +FILE_5_L111= for target in legacy: +FILE_5_L112= results.append(run_legacy_lane(engine,target)) +FILE_6_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py +FILE_6_BYTES=5055 +FILE_6_SHA256=2b5eb4fc0f6f466a0d3e5a007af987092b6c17baa3ff9241a6afc2c2e97df1d5 +FILE_6_CONTEXT_LINE_COUNT=43 +FILE_6_L1=#!/usr/bin/env python3 +FILE_6_L2=from __future__ import annotations +FILE_6_L3=import errno,fcntl,importlib.util,os,sys +FILE_6_L4=from pathlib import Path +FILE_6_L5=ROOT=Path(__file__).resolve().parent +FILE_6_L6=CT_ID='guest-pve01-lxc-112' +FILE_6_L7=LEGACY_IDS=('node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2') +FILE_6_L35= 'guest_type':'lxc','due_seconds':due,'enabled':True,'green1':True} +FILE_6_L36= +FILE_6_L37=def load_targets(): +FILE_6_L38= base=canonical_targets();return base+[_ct_descriptor(base)] +FILE_6_L39= +FILE_6_L40=def backup_target(t,green_verify=False): +FILE_6_L41= if not isinstance(t,dict) or t.get('logical_id')!=CT_ID: +FILE_6_L42= return _legacy_backup_target(t,green_verify) +FILE_6_L43= try: +FILE_6_L44= r=ct112.run_target(t,state_writer=legacy.write_state) +FILE_6_L45= return {'logical_id':CT_ID,'generation':r['generation'],'result':'SUCCESS','payload_bytes':r['payload_bytes'], +FILE_6_L46= 'replicas':2,'manifest_sha256':r['manifest_sha256'],'payload_sha256':r['payload_sha256']} +FILE_6_L47= except BaseException: +FILE_6_L48= try: legacy.write_state(CT_ID,'FAIL',reason='CT112_PIPELINE_FAILED') +FILE_6_L49= except BaseException: pass +FILE_6_L50= raise legacy.BackupError('CT112_PIPELINE_FAILED') from None +FILE_6_L51= +FILE_6_L52=legacy.load_targets=load_targets +FILE_6_L53=legacy.backup_target=backup_target +FILE_6_L54= +FILE_6_L55=def _legacy_reused_boundary_selftest(): +FILE_6_L56= if legacy.PART_SIZE!=536870912: raise RuntimeError('LEGACY_PART_SIZE') +FILE_6_L57= if len(legacy.REMOTES)!=13 or len({p for _,p in legacy.REMOTES})!=13: raise RuntimeError('LEGACY_REMOTES') +FILE_6_L58= d=legacy.default_targets() +FILE_6_L59= if not isinstance(d,list) or len(d)<28: raise RuntimeError('LEGACY_DEFAULT_TARGET_COUNT') +FILE_6_L60= want={'ct112',*LEGACY_IDS} +FILE_6_L67= +FILE_6_L68=def selftest(): +FILE_6_L69= _legacy_reused_boundary_selftest() +FILE_6_L70= base=canonical_targets(); eff=load_targets() +FILE_6_L71= if len(eff)!=29 or sum(1 for x in eff if x.get('logical_id')==CT_ID)!=1: raise RuntimeError('EFFECTIVE_TARGETS') +FILE_6_L72= legacy_rows,ct=driver.partition_targets(eff) +FILE_6_L73= if [x['logical_id'] for x in legacy_rows]!=list(LEGACY_IDS) or ct.get('vmid')!=112: raise RuntimeError('REPRESENTATIVE_PARTITION') +FILE_6_L74= if ct112.selftest() is not True: raise RuntimeError('CT112_HANDLER_SELFTEST') +FILE_6_L75= print('SELFTEST_REPLACEMENT486=PASS',flush=True);return 0 +FILE_6_L76= +FILE_7_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/CONTRACT.json +FILE_7_BYTES=2077 +FILE_7_SHA256=a414715f1b91b3701c52073709a80c38218f76e3961618e8af61489211e50c01 +FILE_7_CONTEXT_LINE_COUNT=1 +FILE_7_L1={"candidate":"pve-lxc-adapter-411r1","next_gate":"PVE_LXC_REAL_ENVIRONMENT_DRY_RUN","owner":{"MappedScratch":"pve270","capture_archive":"pve270","dump_argv":"pve265","pve282.old":"pve265","pve282.proven":"pve270","validate_mapping":"pve270"},"schema":1,"signatures":{"pve265":{"dump_argv":{"args":["job","executable","tmpdir","now"],"defaults":0,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonly":[],"vararg":null},"make_plan":{"args":["rows","configs","storage_rows","defaults","staging_free","now","node"],"defaults":1,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonly":[],"vararg":null},"produce":{"args":["job","work","tools","guard","seconds"],"defaults":1,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonly":[],"vararg":null},"validate_job":{"args":["job","now"],"defaults":0,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonly":[],"vararg":null}},"pve270":{"capture_archive":{"args":["argv","destination","cap","seconds","check"],"defaults":0,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonl +FILE_8_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/PLAN265_REFERENCE.json +FILE_8_BYTES=7210 +FILE_8_SHA256=8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757 +FILE_8_CONTEXT_LINE_COUNT=1 +FILE_8_L1={"all_guest_coverage_eligible":true,"backup_executed":false,"capacity_policy":"four archive copies plus 4GiB; capped producer; actual compressed size still unknown","collected_at_utc":"2026-09-12T06:57:40.611329+00:00","full_host_coverage":false,"global_blockers":[],"global_defaults":{"blockers":[],"keys":[],"present":true,"sha256":"cf34c8e3812547954bb1d08b50212b6e148ee9cf2b629e895a843ed49d623b38"},"guest_membership_and_configs_stable":true,"jobs":[{"application_consistency_verified":false,"archive_cap_bytes":9516036916,"blockers":[],"cdrom_count":0,"cdrom_media_included":false,"compression_dependent":false,"config_sha256":"09957aacd41ba9eeaf5fd4eda13936d580c9082a46895433379aa9dd0bad5969","configured_archive_bound_bytes":9516036916,"configured_disk_bytes":8589934592,"configured_full_cycle_fits":true,"coverage_eligible":true,"disks":[{"backup_setting":"included_default_rootfs","class":"managed-volume","configured_size_bytes":8589934592,"slot":"rootfs","storage":"local-lvm"}],"global_def +FILE_9_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/PROVENANCE.json +FILE_9_BYTES=4310 +FILE_9_SHA256=303f44dc8e91c8e6a675f9ea406ce4b7532ed98440a9b6807fa593ede9d33254 +FILE_9_CONTEXT_LINE_COUNT=1 +FILE_9_L1={"anchors":{"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-EXACT-CALL-GRAPH-PROOF-401R1":"840b3f1e8b023899c0554305a97af2d55dc71cf9","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-IMMUTABLE-ADAPTER-CANDIDATE-STAGE-FIXTURE-RCA-FIX-407R1":"0261475cdd35d3b762de0a29e46f3acd0b9af229","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-IMMUTABLE-ADAPTER-CANDIDATE-STAGE-RCA-FIX-403R1":"e3f753ce9cf95e21750e9ad7f9574cafc199b3ce","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERL270-HISTORICAL-TEST-RCA-404R1":"58f61ce62c3b6a57d8c5fc2465d2537d6728f9d5","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-EXEC-FS-RCA-409R1":"0db666a5a1f4796b567663adfc5568817e87efa5","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-NEED-TRACE-RCA-408R1":"44cdff7dab9e60aff1d69c12d2c32a4b26d04de8","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-PRODUCTION-VS-FIXTURE-RCA-406R1":"4c5836ff0ef73cb2ef306857be713383ba5e85f4","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-ROOT-SUITE-STRUCTURED-RCA-410R1":"4673bfaf04877370bc4ac02fef37885eb82cb1eb +FILE_10_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/adapter.py +FILE_10_BYTES=2989 +FILE_10_SHA256=18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3 +FILE_10_CONTEXT_LINE_COUNT=30 +FILE_10_L1=#!/usr/bin/python3 +FILE_10_L2=import hashlib,inspect,json,pathlib,sys +FILE_10_L3=BASE=pathlib.Path(__file__).resolve().parent +FILE_10_L4=EXPECTED={ +FILE_10_L5='v2core.py':'e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2', +FILE_10_L6='transport261.py':'b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688', +FILE_10_L7='restore262.py':'56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1', +FILE_10_L8='stream263.py':'a83d03a86b93a855716658652dd073fcb281cd387fc6200fe4bc2a922f39ea16', +FILE_10_L9='nodeplan263.py':'db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060', +FILE_10_L10='large264.py':'5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a', +FILE_10_L19= return h.hexdigest() +FILE_10_L20=def verify_sources(): +FILE_10_L21= for name,want in EXPECTED.items(): +FILE_10_L22= p=BASE/name +FILE_10_L23= if not p.is_file() or p.is_symlink() or sha(p)!=want:raise RuntimeError('SOURCE_IDENTITY_'+name) +FILE_10_L24=def load_graph(): +FILE_10_L25= import guest266,pve265,pve270,pve282 +FILE_10_L26= if hasattr(guest266,'dump_argv'):raise RuntimeError('GUEST266_UNEXPECTED_DUMP_ARGV') +FILE_10_L27= if not hasattr(pve265,'dump_argv'):raise RuntimeError('PVE265_DUMP_ARGV_MISSING') +FILE_10_L28= if getattr(pve270,'old',None) is not pve265:raise RuntimeError('PVE270_OLD_NOT_PVE265') +FILE_10_L35=def describe(): +FILE_10_L36= _,p265,p270,p282=load_graph() +FILE_10_L37= funcs={'pve265.dump_argv':p265.dump_argv,'pve265.validate_job':p265.validate_job,'pve265.make_plan':p265.make_plan,'pve270.validate_mapping':p270.validate_mapping,'pve270.capture_archive':p270.capture_archive,'pve270.produce':p270.produce,'pve282.select112':p282.select112,'pve282.produce':p282.produce} +FILE_10_L38= return {'schema':1,'bundle':'pve-lxc-adapter-411r1','source_sha256':EXPECTED,'signatures':{k:str(inspect.signature(v)) for k,v in funcs.items()},'owner':{'dump_argv':'pve265','MappedScratch':'pve270','capture_archive':'pve270','validate_mapping':'pve270','pve282.old':'pve265','pve282.proven':'pve270'}} +FILE_10_L39=def main(): +FILE_10_L40= verify_sources(); mode=sys.argv[1] if len(sys.argv)>1 else 'selftest'; d=describe() +FILE_10_L41= if mode not in ('selftest','describe'):raise SystemExit(2) +FILE_10_L42= print(json.dumps(d,sort_keys=True,separators=(',',':'))) +FILE_10_L43= if mode=='selftest':print('ADAPTER_SELFTEST=PASS') +FILE_10_L44=if __name__=='__main__':main() +FILE_11_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/guest266.py +FILE_11_BYTES=7385 +FILE_11_SHA256=86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868 +FILE_11_CONTEXT_LINE_COUNT=75 +FILE_11_L1="""266R1 bounded real-guest orchestration. Import has no side effects. +FILE_11_L2= +FILE_11_L3=One small LXC rootfs, no compression-dependent admission. No node-wide approval, +FILE_11_L4=no guest restore/start/delete, no cloud deletion. Archive CRC/SHA is not boot proof. +FILE_11_L5=""" +FILE_11_L6=import copy,os,time +FILE_11_L7=from pathlib import Path +FILE_11_L8=import v2core as core +FILE_11_L9=import transport261 as t +FILE_11_L10=import stream263 as stream +FILE_11_L11=import large264 as large +FILE_11_L12=import pve265 as producer +FILE_11_L13= +FILE_11_L14=MAX_CONFIGURED_BYTES=8*1024**3 +FILE_11_L15=RESERVE_BYTES=536870912 +FILE_11_L25= +FILE_11_L26=def choose(plan,now): +FILE_11_L27= rows=plan_rows(plan);t.need(plan.get('global_blockers')==[],'GLOBAL_DEFAULT_BLOCK') +FILE_11_L28= candidates=[] +FILE_11_L29= for job in rows: +FILE_11_L30= if not (job.get('type')=='lxc' and job.get('coverage_eligible') is True and +FILE_11_L31= job.get('blockers')==[] and job.get('compression_dependent') is False and +FILE_11_L35= disks[0].get('class')=='managed-volume' and disks[0].get('backup_setting') in ('included','included_default_rootfs')):continue +FILE_11_L36= n=job.get('configured_disk_bytes') +FILE_11_L37= if type(n) is not int or not 0=max(1024**3,chosen['configured_disk_bytes']),'SNAPSHOT_HEADROOM_UNPROVEN') +FILE_11_L45= return copy.deepcopy(chosen) +FILE_11_L46= +FILE_11_L47=def same_state(before,after,vmid): +FILE_11_L48= old=plan_rows(before);new=plan_rows(after) +FILE_11_L64= if type(task.get('endtime')) is int and task.get('status') not in (None,'RUNNING'):continue +FILE_11_L65= kind=task['type'];target=str(task.get('id','')) +FILE_11_L66= if kind in ('vncshell','vncterm','vncproxy','spiceproxy'):continue +FILE_11_L67= t.need(target!=str(vmid) and not any(x in kind.lower() for x in ('dump','restore','migrat','snapshot','clone','move_disk','resize')),'COMPETING_PVE_TASK') +FILE_11_L68= +FILE_11_L69=def check_result(result,job,work): +FILE_11_L70= t.need(type(result) is dict,'PRODUCER_RESULT_SCHEMA') +FILE_11_L80= finally:os.close(fd) +FILE_11_L81= return expected +FILE_11_L82= +FILE_11_L83=def run_flow(job,work,entries,cloud,reader,produce,state_guard,guard,compression_check,generation,say,clock=lambda:int(time.time())): +FILE_11_L84= """Caller provides real fresh PVE guards; simulation callbacks live in tests only.""" +FILE_11_L85= producer.validate_job(job,clock());core.validate_inventory(entries) +FILE_11_L86= t.need(job['type']=='lxc' and job.get('compression_dependent') is False,'FIRST_GUEST_POLICY') +FILE_11_L87= t.need(core.valid_name(generation),'GENERATION_ID') +FILE_11_L88= t.need(all(callable(f) for f in (produce,state_guard,guard,compression_check,say,clock)),'CALLBACKS_REQUIRED') +FILE_11_L89= work=Path(work);state_guard();guard() +FILE_11_L90= for sub in ('producer','upload','restore'):t.make_dir(work/sub,exclusive=True) +FILE_11_L91= say('PHASE','REAL_SINGLE_GUEST_SNAPSHOT') +FILE_11_L92= produced=produce(job,work/'producer') +FILE_11_L93= source=check_result(produced,job,work/'producer');state_guard();guard() +FILE_11_L94= t.create(work/'PRODUCER.json',core.canonical_bytes(produced)) +FILE_11_L95= say('PRODUCER_RC',0);say('PAYLOAD_BYTES',produced['size']);say('ARCHIVE_SHA256',produced['sha256']) +FILE_11_L96= logical='guest-%s-%s-%d'%(job['node'],job['type'],job['vmid']) +FILE_11_L97= say('PHASE','TWO_REPLICA_UPLOAD_AND_READBACK') +FILE_11_L98= up=large.upload(source,work/'upload',entries,cloud,generation,logical,RESERVE_BYTES,guard,clock=clock) +FILE_11_L99= t.need(up['manifest']['payload_bytes']==produced['size'] and up['manifest']['payload_sha256']==produced['sha256'],'MANIFEST_PRODUCER_MISMATCH') +FILE_11_L100= say('SEALED_QUORUM_VERIFIED','YES');mh=core.manifest_hash(up['manifest']);say('MANIFEST_SHA256',mh) +FILE_11_L101= say('PHASE','CLOUD_DISCOVERY_AND_ARCHIVE_RECONSTRUCTION') +FILE_11_L102= found=large.discover(reader,entries,logical,mh) +FILE_11_L103= restored=large.restore(work/'restore',reader,found) +FILE_11_L104= t.need(compression_check(work/'restore/restored.bin') is True,'RECONSTRUCTED_COMPRESSION_FAILED') +FILE_11_L105= state_guard();guard();say('PHASE','GUARDED_LOCAL_PAYLOAD_CLEANUP') +FILE_11_L106= count=large.cleanup(up,restored,reader,guard) +FILE_11_L107= return {'state':'SINGLE_GUEST_ARCHIVE_SEALED_AND_CLOUD_RECONSTRUCTED','vmid':job['vmid'],'type':job['type'],'node':job['node'], +FILE_11_L108= 'generation':generation,'logical_id':logical,'manifest_sha256':mh,'payload_bytes':produced['size'],'payload_sha256':produced['sha256'], +FILE_11_L109= 'replicas':up['manifest']['replicas'],'parts_count':len(up['manifest']['parts']),'archive_cloud_restore_verified':True, +FILE_11_L110= 'guest_restore_verified':False,'application_consistency_verified':False,'whole_node_backup':False,'local_payload_files_remaining':0, +FILE_11_L111= 'files_deleted':count,'accounts_scanned':found['accounts_scanned'],'config_sha256':job['config_sha256']} +FILE_12_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py +FILE_12_BYTES=16514 +FILE_12_SHA256=5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a +FILE_12_CONTEXT_LINE_COUNT=180 +FILE_12_L1="""264R1 streaming cloud adapter and synthetic boundary proof. +FILE_12_L2= +FILE_12_L3=No work on import. No guest/backup producer and no cloud deletion. Existing +FILE_12_L4=small-canary adapters are not modified. Only caller-owned local payload is removed, +FILE_12_L5=after two cloud seals and a separately cloud-discovered restore have been checked. +FILE_12_L6=""" +FILE_12_L7=import hashlib +FILE_12_L8=import os +FILE_12_L9=from pathlib import Path +FILE_12_L10=import selectors +FILE_12_L11=import signal +FILE_12_L12=import subprocess +FILE_12_L13=import time +FILE_12_L14=import v2core as core +FILE_12_L15=import transport261 as t +FILE_12_L16=import restore262 as r +FILE_12_L17=import stream263 as s +FILE_12_L18= +FILE_12_L19=BOUNDARY_BYTES=536870929 +FILE_12_L20=LOGICAL_ID='stream-canary-pve01' +FILE_12_L21= +FILE_12_L22= +FILE_12_L23=def process_stream(argv,env,size,digest,consume,seconds): +FILE_12_L24= """Drain both pipes, hashing stdout incrementally; never retain the whole part.""" +FILE_12_L25= t.need(type(argv) is list and argv and seconds>0,'STREAM_PROCESS_ARGUMENTS') +FILE_12_L26= sink=s.DigestSink(size,digest) +FILE_12_L27= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE, +FILE_12_L38= remaining=deadline-time.monotonic();t.need(remaining>0,'STREAM_TIMEOUT') +FILE_12_L39= rc=p.wait(timeout=remaining);t.need(rc==0,'STREAM_PROCESS_RC_FAILED') +FILE_12_L40= result=sink.finish();complete=True;return result +FILE_12_L41= except subprocess.TimeoutExpired:raise t.Stop('STREAM_TIMEOUT') from None +FILE_12_L42= finally: +FILE_12_L43= if not complete or p.poll() is None: +FILE_12_L44= try:os.killpg(p.pid,signal.SIGKILL) +FILE_12_L53= '--cache-dir',str(Path(work)/'cache')]+args +FILE_12_L54= +FILE_12_L55= +FILE_12_L56=class CloudLarge(t.RcloneIO): +FILE_12_L57= """Reuse the existing crypt inventory/namespace guards, not its in-memory payload I/O.""" +FILE_12_L58= def call(self,args,limit): +FILE_12_L59= t.need(type(args) is list and args and args[0] in ('about','lsjson','copyto','cat'),'RCLONE_VERB_FORBIDDEN') +FILE_12_L60= t.need(type(limit) is int and 0<=limit<=core.MANIFEST_MAX_BYTES+1,'METADATA_BOUND') +FILE_12_L61= self.check();seconds=min(3600 if args[0]=='copyto' else 90,self.deadline-time.monotonic()) +FILE_12_L62= t.need(seconds>0,'CLOUD_DEADLINE');self.calls+=1 +FILE_12_L63= if args[0]=='copyto':self.writes_attempted+=1 +FILE_12_L64= rc,out=t.bounded_process(flags(self.executable,self.config_path,self.work,args),self.env,limit,seconds) +FILE_12_L65= self.check();return rc,out +FILE_12_L66= def put(self,remote,name,path): +FILE_12_L67= dest=self.address(remote,name) +FILE_12_L68= if name in ('manifest.json','SEALED.json'): +FILE_12_L69= return super().put(remote,name,path) +FILE_12_L70= part=next((p for p in self.manifest['parts'] if p['name']==name),None) +FILE_12_L71= t.need(part is not None,'PART_NOT_IN_MANIFEST') +FILE_12_L72= n,h,identity=s.hash_file(path,part['size']) +FILE_12_L73= t.need(n==part['size'] and h==part['sha256'],'UPLOAD_SOURCE_HASH') +FILE_12_L74= rc,_=self.call(['lsjson',dest,'--stat'],65536) +FILE_12_L75= t.need(rc in (3,4),'OBJECT_NOT_PROVEN_ABSENT') +FILE_12_L76= with s.file_handle(path,part['size']) as (_,size,seen): +FILE_12_L77= t.need(seen==identity,'UPLOAD_SOURCE_CHANGED') +FILE_12_L78= rc,_=self.call(['copyto',str(path),dest,'--immutable','--no-update-modtime'],65536) +FILE_12_L79= t.need(rc==0,'CLOUD_COPY_FAILED') +FILE_12_L80= def read_stream(self,remote,name,size,digest,consume): +FILE_12_L81= address=self.address(remote,name) +FILE_12_L82= part=next((p for p in self.manifest['parts'] if p['name']==name),None) +FILE_12_L83= t.need(part is not None and part['size']==size and part['sha256']==digest,'READBACK_EXPECTATION') +FILE_12_L84= self.check();seconds=min(3600,self.deadline-time.monotonic());t.need(seconds>0,'CLOUD_DEADLINE') +FILE_12_L85= self.calls+=1 +FILE_12_L86= out=process_stream(flags(self.executable,self.config_path,self.work,['cat',address,'--count',str(size+1)]), +FILE_12_L90= +FILE_12_L91=class ReadOnlyLarge(r.ReadOnlyCloud): +FILE_12_L92= """Metadata uses inherited bounded reads; this class has no upload API.""" +FILE_12_L93= def read_stream(self,remote,generation,name,size,digest,consume): +FILE_12_L94= t.need(type(size) is int and 00,'RESTORE_DEADLINE') +FILE_12_L98= self.calls+=1 +FILE_12_L99= out=process_stream(flags(self.executable,self.snapshot,self.work,['cat',address,'--count',str(size+1)]), +FILE_12_L100= self.env,size,digest,consume,seconds) +FILE_12_L116= +FILE_12_L117= +FILE_12_L118=def receipt(rep,m,whole,count): +FILE_12_L119= t.need(count==m['payload_bytes'] and whole.hexdigest()==m['payload_sha256'],'PAYLOAD_READBACK_MISMATCH') +FILE_12_L120= return {**rep,'generation':m['generation'],'manifest_sha256':core.manifest_hash(m), +FILE_12_L121= 'payload_sha256':whole.hexdigest(),'payload_bytes':count,'parts':m['parts'], +FILE_12_L122= 'method':'download-sha256','ok':True} +FILE_12_L123= +FILE_12_L124= +FILE_12_L125=def upload(source,work,entries,cloud,generation,logical_id,reserve,guard,clock=lambda:int(time.time())): +FILE_12_L126= """Upload and seal; retain all source bytes here. Cleanup is a later gated operation.""" +FILE_12_L127= work=Path(work);source=Path(source);guard();core.validate_inventory(entries) +FILE_12_L128= t.need(core.valid_name(generation) and core.valid_name(logical_id),'GENERATION_ID') +FILE_12_L129= partdir=work/'parts';t.make_dir(partdir,exclusive=True) +FILE_12_L130= record=s.split_file(source,partdir) +FILE_12_L131= t.create(work/'parts-record.json',core.canonical_bytes(record)) +FILE_12_L132= quotas={e['account_id']:cloud.quota(e['crypt_remote']) for e in entries} +FILE_12_L133= allocation=core.allocate(entries,quotas,record['payload_bytes'],reserve,clock()) +FILE_12_L134= m=core.make_manifest(generation,logical_id,record['payload_bytes'],record['payload_sha256'],record['parts'],allocation) +FILE_12_L135= mb=core.canonical_bytes(m);mh=core.manifest_hash(m);cloud.bind(m) +FILE_12_L136= t.create(work/'allocation.json',core.canonical_bytes(allocation));t.create(work/'manifest.json',mb) +FILE_12_L137= for rep in m['replicas']: +FILE_12_L138= guard();cloud.absent(rep['crypt_remote']) +FILE_12_L139= receipts=[] +FILE_12_L140= for rep in m['replicas']: +FILE_12_L141= guard();remote=rep['crypt_remote'];q=cloud.quota(remote) +FILE_12_L142= t.need(core.quota_reason(q,clock()) is None and q['free']>=allocation['required_free_bytes'],'SELECTED_ACCOUNT_CAPACITY_CHANGED') +FILE_12_L143= s.verify_parts(partdir,record) +FILE_12_L144= for part in m['parts']:cloud.put(remote,part['name'],partdir/part['name']) +FILE_12_L145= cloud.put(remote,'manifest.json',work/'manifest.json') +FILE_12_L146= t.need(cloud.get(remote,'manifest.json',len(mb)+1)==mb,'MANIFEST_READBACK_MISMATCH') +FILE_12_L147= whole=hashlib.sha256();count=0 +FILE_12_L148= for part in m['parts']: +FILE_12_L149= observed=cloud.read_stream(remote,part['name'],part['size'],part['sha256'],whole.update) +FILE_12_L150= t.need(observed=={'size':part['size'],'sha256':part['sha256']},'PART_READBACK_MISMATCH');count+=observed['size'] +FILE_12_L151= receipts.append(receipt(rep,m,whole,count));t.create(work/('receipt-%d.json'%len(receipts)),core.canonical_bytes(receipts[-1])) +FILE_12_L152= core.verify_receipts(m,receipts) +FILE_12_L153= seal={'schema_version':2,'state':'SEALED','generation':generation,'logical_id':logical_id, +FILE_12_L154= 'manifest_sha256':mh,'replicas':m['replicas'],'receipts':receipts, +FILE_12_L155= 'acceptance':'IDENTICAL_SEAL_REQUIRED_ON_BOTH_NAMED_ACCOUNTS'} +FILE_12_L156= sb=core.canonical_bytes(seal);t.need(len(sb)<=core.SEAL_MAX_BYTES,'SEAL_BOUND') +FILE_12_L157= r.validate_seal(sb,entries,generation,logical_id);t.create(work/'SEALED.json',sb) +FILE_12_L158= for rep in m['replicas']: +FILE_12_L159= guard();cloud.put(rep['crypt_remote'],'SEALED.json',work/'SEALED.json') +FILE_12_L160= for rep in m['replicas']: +FILE_12_L161= t.need(cloud.get(rep['crypt_remote'],'SEALED.json',len(sb)+1)==sb,'SEALED_QUORUM_REQUIRED') +FILE_12_L162= guard();s.verify_parts(partdir,record) +FILE_12_L163= t.create(work/'QUORUM_VERIFIED.json',core.canonical_bytes({'manifest_sha256':mh,'seal_sha256':t.sha(sb)})) +FILE_12_L164= return {'sealed':True,'manifest':m,'seal_bytes':sb,'parts_record':record,'source':str(source),'work':str(work)} +FILE_12_L165= +FILE_12_L166= +FILE_12_L167=def discover(cloud,entries,logical_id,expected_hash): +FILE_12_L168= """Find placement from all cloud catalogs, using only an external manifest digest.""" +FILE_12_L169= core.validate_inventory(entries) +FILE_12_L170= t.need(core.valid_name(logical_id) and core.valid_hash(expected_hash),'DISCOVERY_INPUT') +FILE_12_L171= listed={};generations=set() +FILE_12_L172= for e in entries: +FILE_12_L173= gs=cloud.list_generations(e['crypt_remote']) +FILE_12_L174= t.need(type(gs) is list and len(gs)<=r.MAX_GENERATIONS and all(core.valid_name(g) for g in gs),'GENERATION_LIST_INVALID') +FILE_12_L175= t.need(len(gs)==len(set(gs)),'GENERATION_LIST_DUPLICATE');listed[e['crypt_remote']]=set(gs);generations.update(gs) +FILE_12_L176= t.need(len(generations)<=r.MAX_GENERATIONS,'TOTAL_GENERATIONS_BOUND') +FILE_12_L177= candidates=[] +FILE_12_L178= for gen in sorted(generations): +FILE_12_L179= seen={} +FILE_12_L180= for e in entries: +FILE_12_L181= remote=e['crypt_remote'] +FILE_12_L182= if gen not in listed[remote]:continue +FILE_12_L183= b=cloud.get(remote,gen,'SEALED.json',core.SEAL_MAX_BYTES+1,missing_ok=True) +FILE_12_L184= if b is None:continue +FILE_12_L185= seal=r.validate_seal(b,entries,gen,logical_id) +FILE_12_L186= t.need(remote in {x['crypt_remote'] for x in seal['replicas']},'UNNAMED_SEAL_LOCATION');seen[remote]=(b,seal) +FILE_12_L187= if not seen:continue +FILE_12_L188= sb,seal=next(iter(seen.values())) +FILE_12_L189= if seal['manifest_sha256']!=expected_hash:continue +FILE_12_L190= named={x['crypt_remote'] for x in seal['replicas']} +FILE_12_L191= t.need(set(seen)==named and all(b==sb for b,_ in seen.values()),'IDENTICAL_TWO_SEALS_REQUIRED') +FILE_12_L192= blobs=[] +FILE_12_L193= for rep in seal['replicas']: +FILE_12_L194= b=cloud.get(rep['crypt_remote'],gen,'manifest.json',core.MANIFEST_MAX_BYTES+1) +FILE_12_L195= t.need(type(b) is bytes and t.sha(b)==expected_hash,'MANIFEST_DIGEST_MISMATCH') +FILE_12_L196= m=r.strict_json(b,core.MANIFEST_MAX_BYTES);core.validate_manifest(m) +FILE_12_L197= t.need(m['generation']==gen and m['logical_id']==logical_id and m['replicas']==seal['replicas'],'MANIFEST_PLACEMENT') +FILE_12_L198= t.need(core.canonical_bytes(m)==b,'MANIFEST_CANONICAL_BYTES');core.verify_receipts(m,seal['receipts']);blobs.append(b) +FILE_12_L199= t.need(len(blobs)==2 and blobs[0]==blobs[1],'TWO_MANIFEST_COPIES_REQUIRED') +FILE_12_L200= candidates.append({'manifest':m,'seal_bytes':sb,'accounts_scanned':len(listed)}) +FILE_12_L201= t.need(len(candidates)==1,'EXACTLY_ONE_ACCEPTED_GENERATION_REQUIRED') +FILE_12_L202= return candidates[0] +FILE_12_L203= +FILE_12_L204= +FILE_12_L205=def fresh_seals(cloud,discovery): +FILE_12_L206= m=discovery['manifest'];core.validate_manifest(m);sb=discovery['seal_bytes'] +FILE_12_L207= for rep in m['replicas']: +FILE_12_L208= t.need(cloud.get(rep['crypt_remote'],m['generation'],'SEALED.json',len(sb)+1)==sb,'SEAL_CHANGED_DURING_RESTORE') +FILE_12_L209= +FILE_12_L210= +FILE_12_L211=def download_part(cloud,rep,m,part,path,whole): +FILE_12_L212= path=Path(path);parent=t.open_dir(path.parent);fd=None +FILE_12_L213= try: +FILE_12_L214= fd=os.open(path.name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600,dir_fd=parent) +FILE_12_L215= def consume(block):whole.update(block);s.write_all(fd,block) +FILE_12_L216= result=cloud.read_stream(rep['crypt_remote'],m['generation'],part['name'],part['size'],part['sha256'],consume) +FILE_13_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/nodeplan263.py +FILE_13_BYTES=4603 +FILE_13_SHA256=db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060 +FILE_13_CONTEXT_LINE_COUNT=23 +FILE_13_L1="""Pure, allowlisted PVE metadata projection. Does not execute any command.""" +FILE_13_L2=from decimal import Decimal,InvalidOperation +FILE_13_L3=import hashlib +FILE_13_L4=import re +FILE_13_L5=import v2core as core +FILE_13_L6= +FILE_13_L7=class PlanError(ValueError):pass +FILE_13_L8= +FILE_13_L15= if type(value) is not str or not re.fullmatch(r'\d+(?:\.\d+)?[KMGTPE]?',value):return None +FILE_13_L16= try: +FILE_13_L17= suffix=value[-1] if value[-1] in 'KMGTPE' else '' +FILE_13_L18= base=Decimal(value[:-1] if suffix else value) +FILE_13_L19= b=base*(1024**('KMGTPE'.index(suffix)+1) if suffix else 1) +FILE_13_L20= return int(b) if b==int(b) and 0time.monotonic(),'PRODUCER_TIMEOUT') +FILE_14_L175= try:rc=p.wait(timeout=deadline-time.monotonic()) +FILE_14_L176= except subprocess.TimeoutExpired:raise PVEError('PRODUCER_TIMEOUT') from None +FILE_14_L177= need(rc==0,'PRODUCER_RC_FAILED_PARTIAL_RETAINED');need(total>0,'EMPTY_ARCHIVE');check() +FILE_14_L178= st=os.fstat(fd);named=os.stat(dest.name,dir_fd=parent,follow_symlinks=False) +FILE_14_L179= need(st.st_dev==initial.st_dev and st.st_ino==initial.st_ino and t.ident(st)==t.ident(named) and +FILE_14_L180= st.st_nlink==1 and st.st_size==total,'PRODUCER_FILE_IDENTITY_CHANGED') +FILE_14_L200= """Future explicit execution API. Caller must recheck full plan/coverage in guard. +FILE_14_L201= +FILE_14_L202= Compression CRC and SHA are verified; guest bootability remains unproved. +FILE_14_L203= This function has no cloud/seal/delete operations and returns a readonly artifact. +FILE_14_L204= """ +FILE_14_L205= need(callable(guard),'GUARD_REQUIRED');guard();validate_job(job,int(time.time()));validate_tools(tools) +FILE_14_L206= work=Path(work);parent=stream.private_dir(work) +FILE_14_L209= tmp=work/'tmp';t.make_dir(tmp,exclusive=True) +FILE_14_L210= dest=work/('guest-%d.%s.zst'%(job['vmid'],'tar' if job['type']=='lxc' else 'vma')) +FILE_14_L211= def free_guard(): +FILE_14_L212= v=os.statvfs(work);need(v.f_frsize*v.f_bavail>=LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED') +FILE_14_L213= free_guard() +FILE_14_L214= result=capture_archive(dump_argv(job,tools['vzdump']['path'],tmp,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard) +FILE_14_L215= guard();validate_tools(tools) +FILE_14_L219= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH') +FILE_14_L220= guard();validate_tools(tools) +FILE_14_L221= return {**result,'path':str(dest),'vmid':job['vmid'],'type':job['type'],'node':job['node'], +FILE_14_L222= 'config_sha256':job['config_sha256'],'compression_test_ok':True,'restored':False, +FILE_14_L223= 'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True} +FILE_15_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py +FILE_15_BYTES=19595 +FILE_15_SHA256=97015277a4f3f8129c0fd11eb70c06293026366f1f8232a97feeed513a2dbcef +FILE_15_CONTEXT_LINE_COUNT=140 +FILE_15_L3=Does not modify installed pve265, old workspaces, staging permissions or guest config. +FILE_15_L4=Only explicit produce() executes vzdump. Private bounded stderr/exit metadata retained. +FILE_15_L5=""" +FILE_15_L6=import hashlib,json,os,re,secrets,selectors,signal,stat,subprocess,sys,time +FILE_15_L7=from pathlib import Path +FILE_15_L8=import v2core as core +FILE_15_L9=import transport261 as t +FILE_15_L10=import stream263 as stream +FILE_15_L11=import pve265 as old +FILE_15_L12=need=old.need +FILE_15_L13=MAX_STDERR=2*1024**2 +FILE_15_L14=EXPECTED_CFG='09957aacd41ba9eeaf5fd4eda13936d580c9082a46895433379aa9dd0bad5969' +FILE_15_L39=def live_gate(job,pvesh_path,pvesh_sha): +FILE_15_L40= need(job.get('vmid')==110 and job.get('type')=='lxc' and job.get('node')=='pve01' and job.get('config_sha256')==EXPECTED_CFG,'ONLY_ACCEPTED_CT110') +FILE_15_L41= vals=[] +FILE_15_L42= for part in ('config','status/current','snapshot'): +FILE_15_L43= need(t.sha(t.read_secure(Path(pvesh_path),16*1024**2)[0])==pvesh_sha,'PVESH_CHANGED') +FILE_15_L44= rc,b=t.bounded_process([str(pvesh_path),'get','/nodes/pve01/lxc/110/'+part,'--output-format','json'],dict(old.ENV),262144,30) +FILE_15_L45= need(rc==0,'CT_GATE_READ_FAILED') +FILE_15_L46= try:vals.append(json.loads(b)) +FILE_15_L47= except (ValueError,UnicodeError):raise old.PVEError('CT_GATE_JSON') from None +FILE_15_L48= validate_ct(*vals,job['config_sha256']) +FILE_15_L49= return True +FILE_15_L50= +FILE_15_L59= def __init__(self,path,alias): +FILE_15_L60= self.path=Path(path);self.alias=Path(alias);self.parent=None;self.fd=None +FILE_15_L61= need(self.path.name=='perl' and self.alias.name=='perl5.40.1' and self.path.parent==self.alias.parent,'PERL_PATH_POLICY') +FILE_15_L62= need(self.path.is_absolute() and '..' not in self.path.parts,'PERL_PATH_POLICY') +FILE_15_L63= try: +FILE_15_L64= self.parent=os.open('/',os.O_RDONLY|os.O_DIRECTORY) +FILE_15_L65= for part in self.path.parts[1:-1]: +FILE_15_L66= nxt=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=self.parent) +FILE_15_L67= os.close(self.parent);self.parent=nxt +FILE_15_L68= s=os.fstat(nxt) +FILE_15_L69= need(s.st_uid==0 and not s.st_mode&0o022,'PERL_PARENT_UNTRUSTED') +FILE_15_L75= need(os.pread(self.fd,4,0)==b'\x7fELF','PERL_ELF_REQUIRED') +FILE_15_L76= self.identity=t.ident(s)+(s.st_nlink,) +FILE_15_L77= self.sha256=self._digest();self.check() +FILE_15_L78= except BaseException: +FILE_15_L79= self.close();raise +FILE_15_L80= def _digest(self): +FILE_15_L81= s=os.fstat(self.fd);need(00,'PERL_TIMEOUT') +FILE_15_L123= rc=p.wait(timeout=left);self.check();return rc,bytes(out) +FILE_15_L124= except subprocess.TimeoutExpired:raise old.PVEError('PERL_TIMEOUT') from None +FILE_15_L125= finally: +FILE_15_L126= if p is not None: +FILE_15_L127= if p.poll() is None: +FILE_15_L150= for p,h in pins.items():need(t.sha(t.read_secure(Path(p),2*1024**2)[0])==h,'PVE_MAPPING_SOURCE_CHANGED') +FILE_15_L151= pin.check() +FILE_15_L152= try:d=json.loads(b) +FILE_15_L153= except (ValueError,UnicodeError):raise old.PVEError('PVE_IDMAP_JSON') from None +FILE_15_L154= return validate_mapping(d) +FILE_15_L155= +FILE_15_L156=def _parent(path): +FILE_15_L157= path=Path(path) +FILE_15_L158= need(path.is_absolute() and '..' not in path.parts,'TEMP_PARENT_PATH') +FILE_15_L159= fd=os.open('/',os.O_RDONLY|os.O_DIRECTORY) +FILE_15_L160= try: +FILE_15_L161= for i,part in enumerate(path.parts[1:]): +FILE_15_L162= nxt=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=fd);os.close(fd);fd=nxt +FILE_15_L163= s=os.fstat(fd);last=i==len(path.parts)-2 +FILE_15_L164= need(s.st_uid==0,'TEMP_PARENT_OWNER') +FILE_15_L165= need(not s.st_mode&0o022 or last and s.st_mode&stat.S_ISVTX,'TEMP_PARENT_WRITABLE') +FILE_15_L166= result=fd;fd=None;return result +FILE_15_L170= +FILE_15_L171=class MappedScratch: +FILE_15_L172= """Root-owned, mapped-GID traverse-only, no access for others. No recursive cleanup.""" +FILE_15_L173= def __init__(self,gid,base='/var/tmp'): +FILE_15_L174= need(type(gid) is int and gid==100000,'TEMP_GID_POLICY') +FILE_15_L175= self.parent=_parent(base);self.fd=None;self.gid=gid +FILE_15_L176= self.name='homelab-vzdump-268-'+secrets.token_hex(16);self.path=Path(base)/self.name +FILE_15_L177= try: +FILE_15_L178= os.mkdir(self.name,0o700,dir_fd=self.parent) +FILE_15_L179= self.fd=os.open(self.name,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=self.parent) +FILE_15_L181= os.fchown(self.fd,0,gid);os.fchmod(self.fd,0o710) +FILE_15_L182= s=os.fstat(self.fd);self.identity=(s.st_dev,s.st_ino) +FILE_15_L183= os.fsync(self.parent);self.check() +FILE_15_L184= except BaseException: +FILE_15_L185= if self.fd is not None:os.close(self.fd);self.fd=None +FILE_15_L186= os.close(self.parent);self.parent=None +FILE_15_L187= raise +FILE_15_L199= name='access-probe';fd=os.open(name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o644,dir_fd=self.fd) +FILE_15_L200= try:os.fchmod(fd,0o644);stream.write_all(fd,b'HOMELAB_ACCESS_PROBE\n');os.fsync(fd);identity=t.ident(os.fstat(fd)) +FILE_15_L201= finally:os.close(fd) +FILE_15_L202= positive='''import os,sys +FILE_15_L203=p,private=sys.argv[1:] +FILE_15_L204=assert open(p+'/access-probe','rb').read()==b'HOMELAB_ACCESS_PROBE\\n' +FILE_15_L205=for action in (lambda:os.listdir(p),lambda:os.open(p+'/forbidden',os.O_WRONLY|os.O_CREAT,0o600),lambda:os.open(private,os.O_RDONLY|os.O_DIRECTORY)): +FILE_15_L207= except PermissionError:continue +FILE_15_L208= raise SystemExit(31) +FILE_15_L209=''' +FILE_15_L210= negative='''import sys +FILE_15_L211=try:open(sys.argv[1]+'/access-probe','rb').read() +FILE_15_L212=except PermissionError:raise SystemExit(0) +FILE_15_L213=raise SystemExit(32) +FILE_15_L256= b=os.read(key.fileobj.fileno(),65536) +FILE_15_L257= if not b:sel.unregister(key.fileobj);continue +FILE_15_L258= if key.fileobj is p.stdout: +FILE_15_L259= need(total+len(b)<=cap,'ARCHIVE_CAP_REACHED_PARTIAL_RETAINED') +FILE_15_L260= stream.write_all(fd,b);digest.update(b);total+=len(b) +FILE_15_L261= else: +FILE_15_L262= err_total+=len(b);eh.update(b);chunk=b[:max(0,MAX_STDERR-err_kept)] +FILE_15_L263= if chunk:stream.write_all(efd,chunk);err_kept+=len(chunk) +FILE_15_L264= need(deadline>time.monotonic(),'PRODUCER_TIMEOUT') +FILE_15_L265= try:rc=p.wait(timeout=deadline-time.monotonic()) +FILE_15_L266= except subprocess.TimeoutExpired:raise old.PVEError('PRODUCER_TIMEOUT') from None +FILE_15_L267= need(rc==0,'PRODUCER_RC_FAILED_PARTIAL_RETAINED');need(total>0,'EMPTY_ARCHIVE');check() +FILE_15_L268= st=os.fstat(fd);named=os.stat(dest.name,dir_fd=parent,follow_symlinks=False) +FILE_15_L269= need(st.st_dev==initial.st_dev and st.st_ino==initial.st_ino and t.ident(st)==t.ident(named) and st.st_nlink==1 and st.st_size==total,'PRODUCER_FILE_IDENTITY_CHANGED') +FILE_15_L270= os.fchmod(fd,0o400);os.fsync(fd);os.fsync(parent);good=True +FILE_15_L306= scratch.probe(uid,work);emit('MAPPED_UID_ACCESS_AND_ISOLATION_PROBE','PASS') +FILE_15_L307= def free_guard(): +FILE_15_L308= scratch.check() +FILE_15_L309= for path,reserve,reason in ((work,old.LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED'),(scratch.path,256*1024**2,'PVE_TEMP_RESERVE_LOW')): +FILE_15_L310= v=os.statvfs(path);need(v.f_frsize*v.f_bavail>=reserve,reason) +FILE_15_L311= free_guard();guard();dest=work/'guest-110.tar.zst' +FILE_15_L312= result=capture_archive(old.dump_argv(job,tools['vzdump']['path'],scratch.path,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard) +FILE_15_L317= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH') +FILE_15_L318= guard();old.validate_tools(tools);scratch.close(empty_only=True);complete=True;emit('PVE_TEMP_EMPTY_REMOVED','YES') +FILE_15_L319= return {**result,'path':str(dest),'vmid':110,'type':'lxc','node':job['node'],'config_sha256':job['config_sha256'], +FILE_15_L320= 'compression_test_ok':True,'restored':False,'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True} +FILE_15_L321= finally: +FILE_15_L322= m=work/'PRODUCER_PROCESS.json' +FILE_15_L323= if m.exists(): +FILE_16_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve282.py +FILE_16_BYTES=6195 +FILE_16_SHA256=73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012 +FILE_16_CONTEXT_LINE_COUNT=58 +FILE_16_L1="""282R1 bounded CT112 adapter; proven 270 capture/temp/Perl implementation unchanged. +FILE_16_L2=Expected source hash must be supplied from the immutable-hash-bound 265 plan. +FILE_16_L3=No changes to source guest configuration; no source110 backup or guest deletion. +FILE_16_L4=""" +FILE_16_L5=import copy,json,os,time +FILE_16_L6=from pathlib import Path +FILE_16_L7=import v2core as core +FILE_16_L8=import transport261 as t +FILE_16_L9=import stream263 as stream +FILE_16_L10=import pve265 as old +FILE_16_L11=import pve270 as proven +FILE_16_L12=import guest266 as g +FILE_16_L13=need=old.need +FILE_16_L14=PerlPin=proven.PerlPin +FILE_16_L15=host_mapping=proven.host_mapping +FILE_16_L26= need(core.valid_hash(expected_config) and job.get('config_sha256')==expected_config,'CT112_PLAN_CONFIG_ANCHOR') +FILE_16_L27= return True +FILE_16_L28= +FILE_16_L29=def select112(current,baseline,now): +FILE_16_L30= rows=g.plan_rows(current);previous=g.plan_rows(baseline) +FILE_16_L31= by_id={j['vmid']:j for j in previous} +FILE_16_L32= need(TARGET_VMID in by_id,'CT112_MISSING_FROM_ACCEPTED265') +FILE_16_L33= g.same_state(baseline,current,TARGET_VMID) +FILE_16_L34= need({j['vmid'] for j in rows}==set(by_id),'ACCEPTED265_MEMBERSHIP_CHANGED') +FILE_16_L35= for row in rows: +FILE_16_L36= need(row['config_sha256']==by_id[row['vmid']]['config_sha256'],'ACCEPTED265_GUEST_CONFIG_CHANGED') +FILE_16_L37= selected=[j for j in rows if j['vmid']==TARGET_VMID] +FILE_16_L38= need(len(selected)==1,'CT112_NOT_UNIQUE') +FILE_16_L39= job=copy.deepcopy(selected[0]);old.validate_job(job,now) +FILE_16_L40= need(current.get('global_blockers')==[],'GLOBAL_DEFAULT_BLOCK') +FILE_16_L41= need(job.get('compression_dependent') is False and job.get('configured_full_cycle_fits') is True,'CT112_FULL_CYCLE_SPACE_UNPROVEN') +FILE_16_L42= disks=job.get('disks');need(type(disks) is list and len(disks)==1,'CT112_SINGLE_ROOTFS_REQUIRED') +FILE_16_L53=def live_gate(job,pvesh_path,pvesh_sha,expected_config): +FILE_16_L54= require_target(job,expected_config) +FILE_16_L55= vals=[] +FILE_16_L56= for part in ('config','status/current','snapshot'): +FILE_16_L57= need(t.sha(t.read_secure(Path(pvesh_path),16*1024**2)[0])==pvesh_sha,'PVESH_CHANGED') +FILE_16_L58= rc,b=t.bounded_process([str(pvesh_path),'get','/nodes/pve01/lxc/112/'+part,'--output-format','json'],dict(old.ENV),262144,30) +FILE_16_L59= need(rc==0,'CT112_GATE_READ_FAILED') +FILE_16_L60= try:vals.append(json.loads(b)) +FILE_16_L61= except (ValueError,UnicodeError):raise old.PVEError('CT112_GATE_JSON') from None +FILE_16_L62= validate_ct(*vals,expected_config) +FILE_16_L63= return True +FILE_16_L64= +FILE_16_L79= scratch.probe(uid,work);emit('MAPPED_UID_ACCESS_AND_ISOLATION_PROBE','PASS') +FILE_16_L80= def free_guard(): +FILE_16_L81= scratch.check() +FILE_16_L82= for path,reserve,reason in ((work,old.LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED'),(scratch.path,256*1024**2,'PVE_TEMP_RESERVE_LOW')): +FILE_16_L83= v=os.statvfs(path);need(v.f_frsize*v.f_bavail>=reserve,reason) +FILE_16_L84= free_guard();guard();dest=work/'guest-112.tar.zst' +FILE_16_L85= result=capture_archive(old.dump_argv(job,tools['vzdump']['path'],scratch.path,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard) +FILE_16_L90= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH') +FILE_16_L91= guard();old.validate_tools(tools);scratch.close(empty_only=True);complete=True;emit('PVE_TEMP_EMPTY_REMOVED','YES') +FILE_16_L92= return {**result,'path':str(dest),'vmid':112,'type':'lxc','node':job['node'],'config_sha256':job['config_sha256'], +FILE_16_L93= 'compression_test_ok':True,'restored':False,'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True} +FILE_16_L94= finally: +FILE_16_L95= m=work/'PRODUCER_PROCESS.json' +FILE_16_L96= if m.exists(): +FILE_17_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/restore262.py +FILE_17_BYTES=14701 +FILE_17_SHA256=56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1 +FILE_17_CONTEXT_LINE_COUNT=180 +FILE_17_L1="""262R1 bounded cloud-discovered recovery. No live action on import. +FILE_17_L2= +FILE_17_L3=Cloud access is strictly lsjson/cat. No source-local workspace/manifest/receipt +FILE_17_L4=is read. The external accepted manifest digest is a trust anchor, not placement. +FILE_17_L5=""" +FILE_17_L6=import hashlib +FILE_17_L7=import io +FILE_17_L8=import json +FILE_17_L9=import os +FILE_17_L10=from pathlib import Path +FILE_17_L11=import re +FILE_17_L12=import stat +FILE_17_L13=import tarfile +FILE_17_L14=import time +FILE_17_L15=import v2core as core +FILE_17_L16=import transport261 as t +FILE_17_L17= +FILE_17_L18=SOURCE_NAMES=frozenset(('etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md','etc/pve/31_HOMELAB_REFERENCE.md', +FILE_17_L19= 'usr/local/sbin/homelab-control-backup','etc/systemd/system/homelab-control-backup.service')) +FILE_17_L20=ALL_NAMES=SOURCE_NAMES|{'SOURCE_INDEX.json'} +FILE_17_L21=MAX_GENERATIONS=64 +FILE_17_L22=SEAL_KEYS={'schema_version','state','generation','logical_id','manifest_sha256','replicas','receipts','acceptance'} +FILE_17_L23= +FILE_17_L24=def strict_json(data,limit): +FILE_17_L25= t.need(type(data) is bytes and len(data)<=limit,'JSON_BOUND') +FILE_17_L29= t.need(k not in result,'JSON_DUPLICATE_KEY');result[k]=v +FILE_17_L30= return result +FILE_17_L31= def bad_constant(value):raise t.Stop('JSON_NONFINITE_NUMBER') +DEPENDENCY_PATH_COUNT=28 +DEPENDENCY_PATH_1=/opt/homelab-backup-v2/releases/core-260r1 +DEPENDENCY_PATH_2=/opt/homelab-backup-v2/releases/core-260r1/v2core.py +DEPENDENCY_PATH_3=/opt/homelab-backup-v2/releases/ct-backup-282r1 +DEPENDENCY_PATH_4=/opt/homelab-backup-v2/releases/ct-backup-282r1/guest266.py +DEPENDENCY_PATH_5=/opt/homelab-backup-v2/releases/ct-backup-282r1/pve282.py +DEPENDENCY_PATH_6=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_guest266.py +DEPENDENCY_PATH_7=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_perl270.py +DEPENDENCY_PATH_8=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_producer282.py +DEPENDENCY_PATH_9=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_pve282.py +DEPENDENCY_PATH_10=/opt/homelab-backup-v2/releases/large-cloud-264r1 +DEPENDENCY_PATH_11=/opt/homelab-backup-v2/releases/large-cloud-264r1/large264.py +DEPENDENCY_PATH_12=/opt/homelab-backup-v2/releases/pve-producer-265r1 +DEPENDENCY_PATH_13=/opt/homelab-backup-v2/releases/pve-producer-265r1/PLAN.json +DEPENDENCY_PATH_14=/opt/homelab-backup-v2/releases/pve-producer-265r1/pve265.py +DEPENDENCY_PATH_15=/opt/homelab-backup-v2/releases/pve-producer-265r1/test_pve265.py +DEPENDENCY_PATH_16=/opt/homelab-backup-v2/releases/pve-retry-268r1 +DEPENDENCY_PATH_17=/opt/homelab-backup-v2/releases/pve-retry-270r1 +DEPENDENCY_PATH_18=/opt/homelab-backup-v2/releases/pve-retry-270r1/pve270.py +DEPENDENCY_PATH_19=/opt/homelab-backup-v2/releases/pve-retry-270r1/test_pve270.py +DEPENDENCY_PATH_20=/opt/homelab-backup-v2/releases/pve-run-266r1 +DEPENDENCY_PATH_21=/opt/homelab-backup-v2/releases/restore-262r1 +DEPENDENCY_PATH_22=/opt/homelab-backup-v2/releases/restore-262r1/restore262.py +DEPENDENCY_PATH_23=/opt/homelab-backup-v2/releases/stream-263r1 +DEPENDENCY_PATH_24=/opt/homelab-backup-v2/releases/stream-263r1/nodeplan263.py +DEPENDENCY_PATH_25=/opt/homelab-backup-v2/releases/stream-263r1/stream263.py +DEPENDENCY_PATH_26=/opt/homelab-backup-v2/releases/transport-261r1 +DEPENDENCY_PATH_27=/opt/homelab-backup-v2/releases/transport-261r1/transport261.py +DEPENDENCY_PATH_28=/opt/homelab-backup-v2/releases/vm-backup-283r1 +HAS_RCLONE_SOURCE=true +HAS_OBJECT_PATH_CONTRACT=true +DECISION=PASS_BACKUP213_EXACT_CLOUD_OBJECT_CONTRACT_CAPTURED +NEXT_GATE=BUILD_CORRECTED_CLOUD_GENERATION_RECOVERY +CLOUD_READS=0 +CLOUD_WRITES=0 +VPS_SSH_ATTEMPTS=0 +MESSENGERS214_CLOSED=false +MAJOR_STAGES_REMAINING=8 +NEWFI213_END=true + +OUTPUT_END +CHAT_OUTPUT_END diff --git a/runtime/latest.json b/runtime/latest.json index d77be464..d9ed6653 100644 --- a/runtime/latest.json +++ b/runtime/latest.json @@ -1 +1,38 @@ -{"schema_version":1,"channel":"homelab-runtime","command_id":"CONTEXT-AUTO-20260920T021701Z","status":"OK","rc":0,"host":"pve01","mode":"read-only","component":"cluster-context","started_at_utc":"2026-09-20T02:17:01Z","finished_at_utc":"2026-09-20T02:17:02Z","reference_register_checked":true,"reference_sha256":"5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66","error_register_checked":true,"error_register_sha256":"3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0","changes_made":false,"sanitized":true,"secrets_included":false,"private_addresses_included":false,"output":"AUTOMATIC_CLUSTER_CONTEXT_REFRESH=OK"} +{ + "schema_version": 1, + "channel": "homelab-runtime", + "command_id": "NEWFI-260920-NEWFDOM-MESSENGERS214-BASE213", + "status": "OK", + "rc": 0, + "host": "pve01", + "mode": "read-only", + "component": "newfi-newfdom-messengers214-backupv2-base-release-authority-aware-readonly", + "started_at_utc": "2026-09-20T02:30:57Z", + "finished_at_utc": "2026-09-20T02:30:57Z", + "reference_register_checked": true, + "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", + "error_register_checked": true, + "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", + "command_sha256": "65d18e767349b7319c7197b07d4122dae093e606f12244bfd676798e5606e937", + "duplicate_failed_command_blocked": false, + "block_reason": null, + "execution_started": true, + "change_declared": false, + "result_contract_valid": true, + "result_contract_status": null, + "result_contract_error": null, + "command_rc": 0, + "changes_made": false, + "rollback_started": false, + "rollback_restored": null, + "mutation_outcome": "NO_MUTATION", + "sanitized": true, + "secrets_included": false, + "private_addresses_included": false, + "raw_evidence_retained_locally": true, + "raw_evidence_sha256": "417d54bc3ee03b085ea2894f80cd2f3c4e8df0652fe89b0f35fa9f5df73e61c3", + "sanitized_output_sha256": "c2da47960582cda8715a7fd595b714f908f504dd277254ed2b277fdb5c900bf3", + "output_truncated_in_json": false, + "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", + "output": "NEWFI213_BEGIN=true\nMODE=read-only\nIMMUTABLE_PLAN_CHECK=PASS\nCHANGES_MADE=false\nMUTATION_OUTCOME=NO_MUTATION\nRUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d\nRUNNER_PIN_MATCH=true\nERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0\nERROR_REGISTER_PIN_MATCH=true\nREFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66\nREFERENCE_PIN_MATCH=true\nWRAPPER_SHA256=5077444065c732451cb84898680f62361b21a24ef9eb4f191253e82ead524ea2\nWRAPPER_PIN_MATCH=true\nOVERLAY_SHA256=ca6876fa9c3b1465b045344a8ad46962de16972c7dadc4ac6257c7d225047793\nOVERLAY_PIN_MATCH=true\nPRODUCTION_ACCEPTANCE_SHA256=708a481f6719ff1ed8d0afec1902650ed36ac631e980611b015419f504f16df0\nPRODUCTION_ACCEPTANCE_PIN_MATCH=true\nAUTHORITY_PIN_MATCH=true\nBACKUP_SOURCE_PIN_MATCH=true\nACCEPTED_RELEASE=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1\nBASE_RELEASE=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1\nMATCHING_FILE_COUNT=26\nFILE_1_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ACCEPTANCE.json\nFILE_1_BYTES=517\nFILE_1_SHA256=904dfccad43b55ad0b53cfbd29fb45fee36fd0fbfa5afa58da5c3cca0ee7153b\nFILE_1_CONTEXT_LINE_COUNT=1\nFILE_1_L1={\"command_id\":\"SUPPORT-260916-INFRA-DR-BACKUPV2-FINAL-REPLACEMENT-PLAN-STABILITY-SCHEMA-RCA-FIX-VALIDATE-PROMOTE-CUTOVER-SEAL-486R1\",\"ct112_manifest_sha256\":\"1ac4f16a54bcdd8b896c9a131abef966d315256aa56da3f83ed0b30845c4fcb0\",\"ct112_payload_bytes\":223875308,\"ct112_payload_sha256\":\"d443b85e76e806830fd9c372d0a09fc5b91ca9c9d35c44fb0ad7968cc996d01d\",\"legacy_ids\":[\"node-pve01\",\"md-relay\",\"spb-ingress\",\"us-netbird\",\"router-netcraze\",\"xf-codevipe\",\"xf-zakrutim\",\"control-bootstrap-v2\"],\"representative_pass\":9,\"schema\":1}\nFILE_2_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/RELEASE_MANIFEST.json\nFILE_2_BYTES=3301\nFILE_2_SHA256=a36dd0db88592ec3d355ef43e6b218da6c9ad649e9ccf8c8f680b96eb23a156e\nFILE_2_CONTEXT_LINE_COUNT=1\nFILE_2_L1={\"files\":{\"ACCEPTANCE.json\":{\"bytes\":517,\"mode\":256,\"sha256\":\"904dfccad43b55ad0b53cfbd29fb45fee36fd0fbfa5afa58da5c3cca0ee7153b\"},\"candidate411/CONTRACT.json\":{\"bytes\":2077,\"mode\":256,\"sha256\":\"a414715f1b91b3701c52073709a80c38218f76e3961618e8af61489211e50c01\"},\"candidate411/PLAN265_REFERENCE.json\":{\"bytes\":7210,\"mode\":256,\"sha256\":\"8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757\"},\"candidate411/PROVENANCE.json\":{\"bytes\":4310,\"mode\":256,\"sha256\":\"303f44dc8e91c8e6a675f9ea406ce4b7532ed98440a9b6807fa593ede9d33254\"},\"candidate411/adapter.py\":{\"bytes\":2989,\"mode\":320,\"sha256\":\"18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3\"},\"candidate411/guest266.py\":{\"bytes\":7385,\"mode\":256,\"sha256\":\"86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868\"},\"candidate411/large264.py\":{\"bytes\":16514,\"mode\":256,\"sha256\":\"5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a\"},\"candidate411/nodeplan263.py\":{\"bytes\":4603,\"mode\":256,\"sha256\":\"db5c16ee24d25aa\nFILE_3_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py\nFILE_3_BYTES=10955\nFILE_3_SHA256=8cac6e627d5e7abe5d3163cdd24911995a1e3f45c776f9373c2aa68784f5ec6c\nFILE_3_CONTEXT_LINE_COUNT=98\nFILE_3_L1=#!/usr/bin/env python3\nFILE_3_L2=from __future__ import annotations\nFILE_3_L3=import hashlib,json,os,secrets,shutil,stat,subprocess,sys,time\nFILE_3_L4=from pathlib import Path\nFILE_3_L5=ROOT=Path(__file__).resolve().parent\nFILE_3_L6=CAND=ROOT/'candidate411'\nFILE_3_L7=if str(CAND) not in sys.path:sys.path.insert(0,str(CAND))\nFILE_3_L8=import guest266,large264 as large,pve265,pve282,transport261 as t,v2core as core\nFILE_3_L9=CT_ID='guest-pve01-lxc-112'\nFILE_3_L10=PLAN=CAND/'PLAN265_REFERENCE.json'\nFILE_3_L11=PLAN_SHA='8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757'\nFILE_3_L12=RCLONE=Path('/usr/bin/rclone'); RCLONE_SHA='20df3d5eac5d4e187ce638e4216354e7fa6778c3c51f0caf48c48acca9686354'\nFILE_3_L13=RCLONE_CFG=Path('/root/.config/rclone/[SENSITIVE_FILE]')\nFILE_3_L14=BINDING_FP='ed48c1345771c3e0f50db9516f13f26bb66047283e2f883d5df272f15e2282b8'\nFILE_3_L15=VZDUMP=Path('/usr/bin/vzdump'); VZDUMP_SHA='21339d009afd55cb7ec613b64c1f493b2a7dbd18b85b4d712909ef6d6710f4ea'\nFILE_3_L16=ZSTD=Path('/usr/bin/zstd'); ZSTD_SHA='3eb7744cf8fd1ecd46d617098ee870b3e7911fe570ffaba662c1c5b7d6456020'\nFILE_3_L26= p=subprocess.run(argv,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=45,check=False,env=ENV)\nFILE_3_L27= if p.returncode!=0 or len(p.stdout)>4*1024*1024:raise RuntimeError('PVESH_READ_FAILED')\nFILE_3_L28= try:return json.loads(p.stdout.decode('utf-8','strict'))\nFILE_3_L29= except (ValueError,UnicodeError):raise RuntimeError('PVESH_JSON') from None\nFILE_3_L30=\nFILE_3_L31=def _baseline():\nFILE_3_L32= b=t.read_secure(PLAN,1024*1024,private=True)[0]\nFILE_3_L33= if t.sha(b)!=PLAN_SHA:raise RuntimeError('PLAN_REFERENCE_IDENTITY')\nFILE_3_L34= try:return json.loads(b.decode('utf-8','strict'))\nFILE_3_L35= except (ValueError,UnicodeError):raise RuntimeError('PLAN_REFERENCE_JSON') from None\nFILE_3_L36=\nFILE_3_L37=def _defaults():\nFILE_3_L38= p=Path('/etc/vzdump.conf');st=os.stat(p,follow_symlinks=False)\nFILE_3_L100= return {'path':str(path),'sha256':want}\nFILE_3_L101=\nFILE_3_L102=def _private_config():\nFILE_3_L103= b,identity=t.read_secure(RCLONE_CFG,t.MAX_CONFIG_BYTES,private=True)\nFILE_3_L104= text=b.decode('utf-8-sig','strict');entries=core.inventory_from_config(text);binding=t.binding(text)\nFILE_3_L105= if len(entries)!=13 or len({x['account_id'] for x in entries})!=13:raise RuntimeError('RCLONE_INVENTORY')\nFILE_3_L106= if hashlib.sha256(binding.encode()).hexdigest()!=BINDING_FP:raise RuntimeError('RCLONE_BINDING')\nFILE_3_L107= return b,entries,binding\nFILE_3_L108=\nFILE_3_L109=def _write_private(path,data):\nFILE_3_L118=class LazyReader:\nFILE_3_L119= def __init__(self,work,entries,binding,guard,deadline,logical_id):\nFILE_3_L120= self.work=Path(work);self.entries=entries;self.binding=binding;self.guard=guard;self.deadline=deadline;self.logical_id=logical_id\nFILE_3_L121= self.snapshot=self.work/'rclone.snapshot';self.inner=None\nFILE_3_L122= def _ensure(self):\nFILE_3_L123= if self.inner is not None:return self.inner\nFILE_3_L124= b,e,bind=_private_config()\nFILE_3_L125= if e!=self.entries or bind!=self.binding:raise RuntimeError('RCLONE_SEMANTIC_DRIFT')\nFILE_3_L126= _write_private(self.snapshot,b)\nFILE_3_L127= self.inner=large.ReadOnlyLarge(str(RCLONE),self.snapshot,e,bind,self.guard,self.work,self.deadline,self.logical_id)\nFILE_3_L128= return self.inner\nFILE_3_L129= def list_generations(self,*a,**k):return self._ensure().list_generations(*a,**k)\nFILE_3_L130= def get(self,*a,**k):return self._ensure().get(*a,**k)\nFILE_3_L131= def read_stream(self,*a,**k):return self._ensure().read_stream(*a,**k)\nFILE_3_L132= def close(self):\nFILE_3_L135=\nFILE_3_L136=def selftest():\nFILE_3_L137= if _sha(PLAN,1024*1024)!=PLAN_SHA:return False\nFILE_3_L138= for p,h in ((RCLONE,RCLONE_SHA),(VZDUMP,VZDUMP_SHA),(ZSTD,ZSTD_SHA)):\nFILE_3_L139= if _sha(p)!=h:return False\nFILE_3_L140= return True\nFILE_3_L141=\nFILE_3_L142=def run_target(target,state_writer=None):\nFILE_3_L143= if not isinstance(target,dict) or target.get('logical_id')!=CT_ID or target.get('vmid')!=112:return (_ for _ in ()).throw(RuntimeError('CT112_DESCRIPTOR'))\nFILE_3_L144= if _sha(PLAN,1024*1024)!=PLAN_SHA:raise RuntimeError('PLAN_REFERENCE_IDENTITY')\nFILE_3_L145= tools={'vzdump':_tool(VZDUMP,VZDUMP_SHA),'zstd':_tool(ZSTD,ZSTD_SHA)};_tool(RCLONE,RCLONE_SHA)\nFILE_3_L146= pvesh_sha=_sha(PVESH);now=int(time.time());current=_fresh_plan(now);job=_select_live_ct112(current,now)\nFILE_3_L147= expected=job['config_sha256'];mapping=_canonical_host_mapping()\nFILE_3_L148= def producer_guard():\nFILE_3_L153= pve282.live_gate(fresh,str(PVESH),pvesh_sha,expected)\nFILE_3_L154= producer_guard()\nFILE_3_L155= cfg,entries,binding=_private_config()\nFILE_3_L156= generation='V2-'+time.strftime('%Y%m%dT%H%M%SZ',time.gmtime())+'-'+secrets.token_hex(6)\nFILE_3_L157= work=STAGING/('backup-v2-'+generation);work.mkdir(mode=0o700,parents=False,exist_ok=False)\nFILE_3_L158= deadline=time.monotonic()+21600\nFILE_3_L159= def cloud_guard():\nFILE_3_L160= _,e,b=_private_config()\nFILE_3_L161= if e!=entries or b!=binding:raise RuntimeError('RCLONE_SEMANTIC_DRIFT')\nFILE_3_L162= writer=large.CloudLarge(str(RCLONE),RCLONE_CFG,entries,binding,cloud_guard,work/'upload',deadline)\nFILE_3_L163= reader=LazyReader(work/'restore',entries,binding,cloud_guard,deadline,CT_ID)\nFILE_3_L164= def produce(j,w):return pve282.produce(j,w,tools,producer_guard,mapping=mapping,expected_config=expected,seconds=14400,emit=lambda k,v:None)\nFILE_3_L165= def compression(path):\nFILE_3_L166= p=subprocess.run([str(ZSTD),'--test','--quiet','--',str(path)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,timeout=3600,check=False,env=ENV)\nFILE_3_L167= return p.returncode==0\nFILE_3_L168= try:\nFILE_3_L169= r=guest266.run_flow(job,work,entries,writer,reader,produce,producer_guard,cloud_guard,compression,generation,lambda k,v:None)\nFILE_3_L170= reps=r.get('replicas')\nFILE_3_L171= if not isinstance(reps,list) or len(reps)!=2 or len({x.get('account_id') for x in reps})!=2:raise RuntimeError('REPLICA_PHYSICAL_INDEPENDENCE')\nFILE_3_L172= if r.get('archive_cloud_restore_verified') is not True or r.get('local_payload_files_remaining')!=0:raise RuntimeError('CT112_RESTORE_CONTRACT')\nFILE_3_L173= out={**r,'status':'OK','cloud_only_independent_restore_verified':True,'local_payload_files_remaining':0}\nFILE_3_L174= reader.close();shutil.rmtree(work)\nFILE_3_L175= if work.exists():raise RuntimeError('CT112_WORKSPACE_CLEANUP')\nFILE_3_L176= if state_writer is not None:\nFILE_3_L177= state_writer(CT_ID,'SUCCESS',generation=r['generation'],payload_sha256=r['payload_sha256'],manifest_sha256=r['manifest_sha256'],replicas=r['replicas'],green_verified=True)\nFILE_3_L178= return out\nFILE_3_L179= except BaseException:\nFILE_3_L180= try:reader.close()\nFILE_3_L181= except BaseException:pass\nFILE_3_L182= raise\nFILE_4_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py\nFILE_4_BYTES=59719\nFILE_4_SHA256=b6e79f087b9f1d21dac4f77a7b46b743299bbb85bc716e80d2ea67c2e038bcd7\nFILE_4_CONTEXT_LINE_COUNT=180\nFILE_4_L1=#!/usr/bin/python3\nFILE_4_L2=import ast, base64, datetime as dt, hashlib, io, json, os, pathlib, random, re, select, shlex, shutil, stat, subprocess, sys, tarfile, tempfile, time\nFILE_4_L3=\nFILE_4_L4=PART_SIZE=536870912\nFILE_4_L5=RESERVE_BYTES=536870912\nFILE_4_L6=STATE_ROOT=pathlib.Path('/var/lib/homelab-backup-v2')\nFILE_4_L7=CONFIG_ROOT=pathlib.Path('/etc/homelab-backup-v2')\nFILE_4_L8=TARGETS_PATH=CONFIG_ROOT/'targets.json'\nFILE_4_L9=WORK_ROOT=pathlib.Path('/mnt/staging/homelab-backup-v2')\nFILE_4_L10=ENGINE_PATH=pathlib.Path('/usr/local/sbin/homelab-backup-v2')\nFILE_4_L11=RCLONE=shutil.which('rclone') or '/usr/bin/rclone'\nFILE_4_L12=SSH=shutil.which('ssh') or '/usr/bin/ssh'\nFILE_4_L13=SCP=shutil.which('scp') or '/usr/bin/scp'\nFILE_4_L14=SSH_KEYSCAN=shutil.which('ssh-keyscan') or '/usr/bin/ssh-keyscan'\nFILE_4_L30=P282_SHA='73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012'\nFILE_4_L31=P265=pathlib.Path('/opt/homelab-backup-v2/releases/pve-producer-265r1/pve265.py')\nFILE_4_L32=P265_SHA='cd2f634414a8e38a3789015aeca5a9b47821f77d7d1fa61dcdbdc866172963be'\nFILE_4_L33=RESULT_CLASSES=('SUCCESS','DEFER','PARTIAL_VALID','FAIL')\nFILE_4_L34=HISTORICAL_RELEASE_GRAPH=[\nFILE_4_L35= (pathlib.Path('/opt/homelab-backup-v2/releases/core-260r1/v2core.py'),'e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2'),\nFILE_4_L36= (pathlib.Path('/opt/homelab-backup-v2/releases/transport-261r1/transport261.py'),'b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688'),\nFILE_4_L51= pathlib.Path('/opt/homelab-backup-v2/releases/large-cloud-264r1'),\nFILE_4_L52= pathlib.Path('/opt/homelab-backup-v2/releases/stream-263r1'),\nFILE_4_L53= pathlib.Path('/opt/homelab-backup-v2/releases/transport-261r1'),\nFILE_4_L54= pathlib.Path('/opt/homelab-backup-v2/releases/restore-262r1'),\nFILE_4_L55= pathlib.Path('/opt/homelab-backup-v2/releases/core-260r1'),\nFILE_4_L56= pathlib.Path('/opt/homelab-backup-v2/releases/vm-backup-283r1'),\nFILE_4_L57=]\nFILE_4_L58=_HISTORICAL_HELPER = r'''import importlib,importlib.util,json,pathlib,sys\nFILE_4_L59=mode=sys.argv[1]\nFILE_4_L60=if mode=='import_all':\nFILE_4_L61= out={}\nFILE_4_L74='''\nFILE_4_L75=os.umask(0o077)\nFILE_4_L76=\nFILE_4_L77=REMOTES=[\nFILE_4_L78= ('pve01-mail-01-crypt','pve01-mail-01'),('pve01-mail-02-crypt','pve01-mail-02'),('pve01-mail-03-crypt','pve01-mail-03'),('pve01-mail-04-crypt','pve01-mail-04'),('pve01-mail-05-crypt','pve01-mail-05'),\nFILE_4_L79= ('pve02-mail-01-crypt','pve02-mail-01'),('pve02-mail-02-crypt','pve02-mail-02'),('pve02-mail-03-crypt','pve02-mail-03'),('pve02-mail-04-crypt','pve02-mail-04'),\nFILE_4_L80= ('pve03-mail-01-crypt','pve03-mail-01'),('pve03-mail-02-crypt','pve03-mail-02'),('pve03-mail-03-crypt','pve03-mail-03'),('pve03-mail-04-crypt','pve03-mail-04')]\nFILE_4_L81=\nFILE_4_L82=class BackupError(RuntimeError):\nFILE_4_L83= def __init__(self, code, detail=''):\nFILE_4_L102= if not b: break\nFILE_4_L103= h.update(b); n+=len(b)\nFILE_4_L104= if remain is not None: remain-=len(b)\nFILE_4_L105= if length is not None and n!=length: raise BackupError('LOCAL_PART_SHORT_READ')\nFILE_4_L106= return h.hexdigest()\nFILE_4_L107=\nFILE_4_L108=def part_plan(n):\nFILE_4_L109= if not isinstance(n,int) or n<=0: raise BackupError('PAYLOAD_SIZE_INVALID')\nFILE_4_L110= out=[]; off=0; idx=1\nFILE_4_L111= while offtimeout:\nFILE_4_L314= p.kill(); p.wait(); raise BackupError('RCLONE_CAT_TIMEOUT')\nFILE_4_L315= b=p.stdout.read(1024*1024)\nFILE_4_L316= if not b: break\nFILE_4_L317= h.update(b); n+=len(b)\nFILE_4_L318= p.stderr.read(); rc=p.wait()\nFILE_4_L319= if rc!=0: raise BackupError('RCLONE_CAT_FAILED',str(rc))\nFILE_4_L320= if expected_bytes is not None and n!=expected_bytes: raise BackupError('RCLONE_CAT_SIZE_MISMATCH')\nFILE_4_L321= return h.hexdigest(),n\nFILE_4_L322=\nFILE_4_L323=def rclone_cat_bytes(remote_path,max_bytes=4*1024*1024):\nFILE_4_L324= p=run(rclone_base_args()+['cat',remote_path,'--contimeout','20s','--timeout','60s'],timeout=90)\nFILE_4_L325= if len(p.stdout)>max_bytes: raise BackupError('RCLONE_SMALL_OBJECT_TOO_LARGE')\nFILE_4_L326= return p.stdout\nFILE_4_L327=\nFILE_4_L328=def generation_id(logical_id):\nFILE_4_L329= ts=dt.datetime.now(dt.timezone.utc).strftime('%Y%m%dT%H%M%SZ')\nFILE_4_L330= return f'v2-{logical_id}-{ts}-{os.urandom(6).hex()}'\nFILE_4_L331=\nFILE_4_L332=def upload_and_seal(t,payload_path,source_meta):\nFILE_4_L333= payload_path=pathlib.Path(payload_path); n=payload_path.stat().st_size\nFILE_4_L334= psha=sha256_file(payload_path); reps,bad,healthy=allocator(n)\nFILE_4_L335= gen=source_meta['generation']; rel=remote_generation_path(t['scope'],t['logical_id'],gen)\nFILE_4_L336= if remote_exists_any(t['scope'],t['logical_id'],gen): raise BackupError('GENERATION_COLLISION')\nFILE_4_L337= parts=part_plan(n)\nFILE_4_L338= for p in parts: p['sha256']=sha256_file(payload_path,p['offset'],p['bytes'])\nFILE_4_L339= manifest=make_manifest(t['scope'],t['logical_id'],gen,n,psha,parts,reps,source_meta)\nFILE_4_L340= mb=canonical_json(manifest)+b'\\n'; msha=sha256_bytes(mb)\nFILE_4_L341= for rep in reps:\nFILE_4_L342= base=rep['crypt']+':'+rel\nFILE_4_L343= for part in parts:\nFILE_4_L344= rp=base+'/parts/'+part['name']\nFILE_5_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py\nFILE_5_BYTES=4214\nFILE_5_SHA256=b3050a8f95e6d9826a4f0786cd3ca32012b6ab10486d10e4981b50b8a0affff2\nFILE_5_CONTEXT_LINE_COUNT=60\nFILE_5_L3=\nFILE_5_L4=This module does not discover credentials, modify scheduler state, or implement the\nFILE_5_L5=CT112 producer/cloud path. It routes CT112 to an explicitly supplied callback and\nFILE_5_L6=isolates legacy engine state writes into a candidate-owned state directory.\nFILE_5_L7=\"\"\"\nFILE_5_L8=from __future__ import annotations\nFILE_5_L9=\nFILE_5_L10=import json\nFILE_5_L11=import os\nFILE_5_L12=from pathlib import Path\nFILE_5_L13=from typing import Callable, Iterable, Mapping, Any\nFILE_5_L14=\nFILE_5_L15=LEGACY_IDS=(\nFILE_5_L16= 'node-pve01',\nFILE_5_L24=)\nFILE_5_L25=CT112_ID='guest-pve01-lxc-112'\nFILE_5_L26=REPRESENTATIVE_IDS=(CT112_ID,)+LEGACY_IDS\nFILE_5_L27=CANDIDATE_ROOT=Path(__file__).resolve().parent\nFILE_5_L28=STATE_ROOT=CANDIDATE_ROOT/'state'\nFILE_5_L29=\nFILE_5_L30=class ContractError(RuntimeError):\nFILE_5_L31= pass\nFILE_5_L36= raise ContractError('TARGET_LOGICAL_ID')\nFILE_5_L37= return value\nFILE_5_L38=\nFILE_5_L39=def partition_targets(targets: Iterable[Mapping[str, Any]]):\nFILE_5_L40= selected={}\nFILE_5_L41= wanted=set(REPRESENTATIVE_IDS)\nFILE_5_L42= for target in targets:\nFILE_5_L62= with os.fdopen(fd,'wb',closefd=True) as f:\nFILE_5_L63= f.write(data);f.flush();os.fsync(f.fileno())\nFILE_5_L64= os.replace(tmp,path)\nFILE_5_L65= except BaseException:\nFILE_5_L66= try: os.unlink(tmp)\nFILE_5_L67= except FileNotFoundError: pass\nFILE_5_L68= raise\nFILE_5_L72= def write_state(logical_id,result,**kw):\nFILE_5_L73= if logical_id not in LEGACY_IDS:\nFILE_5_L74= raise ContractError('STATE_TARGET_FORBIDDEN')\nFILE_5_L75= payload={'logical_id':logical_id,'result':result,'meta':kw,'schema_version':1}\nFILE_5_L76= _atomic_json(root/(logical_id+'.json'),payload)\nFILE_5_L77= return write_state\nFILE_5_L78=\nFILE_5_L79=def run_legacy_lane(engine, target: Mapping[str, Any]):\nFILE_5_L97= raise ContractError('CT112_RESULT_SCHEMA')\nFILE_5_L98= if result.get('logical_id')!=CT112_ID or result.get('status')!='OK':\nFILE_5_L99= raise ContractError('CT112_RESULT_STATUS')\nFILE_5_L100= if result.get('cloud_only_independent_restore_verified') is not True:\nFILE_5_L101= raise ContractError('CT112_RESTORE_NOT_VERIFIED')\nFILE_5_L102= if result.get('local_payload_files_remaining')!=0:\nFILE_5_L103= raise ContractError('CT112_LOCAL_PAYLOAD_REMAINS')\nFILE_5_L104= return result\nFILE_5_L105=\nFILE_5_L106=def run_representative(engine, targets, ct112_callback: Callable[[Mapping[str, Any]], Any]):\nFILE_5_L107= if not callable(ct112_callback):\nFILE_5_L108= raise ContractError('CT112_CALLBACK_REQUIRED')\nFILE_5_L109= legacy,ct112=partition_targets(targets)\nFILE_5_L110= results=[_validate_ct112_result(ct112_callback(ct112))]\nFILE_5_L111= for target in legacy:\nFILE_5_L112= results.append(run_legacy_lane(engine,target))\nFILE_6_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py\nFILE_6_BYTES=5055\nFILE_6_SHA256=2b5eb4fc0f6f466a0d3e5a007af987092b6c17baa3ff9241a6afc2c2e97df1d5\nFILE_6_CONTEXT_LINE_COUNT=43\nFILE_6_L1=#!/usr/bin/env python3\nFILE_6_L2=from __future__ import annotations\nFILE_6_L3=import errno,fcntl,importlib.util,os,sys\nFILE_6_L4=from pathlib import Path\nFILE_6_L5=ROOT=Path(__file__).resolve().parent\nFILE_6_L6=CT_ID='guest-pve01-lxc-112'\nFILE_6_L7=LEGACY_IDS=('node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2')\nFILE_6_L35= 'guest_type':'lxc','due_seconds':due,'enabled':True,'green1':True}\nFILE_6_L36=\nFILE_6_L37=def load_targets():\nFILE_6_L38= base=canonical_targets();return base+[_ct_descriptor(base)]\nFILE_6_L39=\nFILE_6_L40=def backup_target(t,green_verify=False):\nFILE_6_L41= if not isinstance(t,dict) or t.get('logical_id')!=CT_ID:\nFILE_6_L42= return _legacy_backup_target(t,green_verify)\nFILE_6_L43= try:\nFILE_6_L44= r=ct112.run_target(t,state_writer=legacy.write_state)\nFILE_6_L45= return {'logical_id':CT_ID,'generation':r['generation'],'result':'SUCCESS','payload_bytes':r['payload_bytes'],\nFILE_6_L46= 'replicas':2,'manifest_sha256':r['manifest_sha256'],'payload_sha256':r['payload_sha256']}\nFILE_6_L47= except BaseException:\nFILE_6_L48= try: legacy.write_state(CT_ID,'FAIL',reason='CT112_PIPELINE_FAILED')\nFILE_6_L49= except BaseException: pass\nFILE_6_L50= raise legacy.BackupError('CT112_PIPELINE_FAILED') from None\nFILE_6_L51=\nFILE_6_L52=legacy.load_targets=load_targets\nFILE_6_L53=legacy.backup_target=backup_target\nFILE_6_L54=\nFILE_6_L55=def _legacy_reused_boundary_selftest():\nFILE_6_L56= if legacy.PART_SIZE!=536870912: raise RuntimeError('LEGACY_PART_SIZE')\nFILE_6_L57= if len(legacy.REMOTES)!=13 or len({p for _,p in legacy.REMOTES})!=13: raise RuntimeError('LEGACY_REMOTES')\nFILE_6_L58= d=legacy.default_targets()\nFILE_6_L59= if not isinstance(d,list) or len(d)<28: raise RuntimeError('LEGACY_DEFAULT_TARGET_COUNT')\nFILE_6_L60= want={'ct112',*LEGACY_IDS}\nFILE_6_L67=\nFILE_6_L68=def selftest():\nFILE_6_L69= _legacy_reused_boundary_selftest()\nFILE_6_L70= base=canonical_targets(); eff=load_targets()\nFILE_6_L71= if len(eff)!=29 or sum(1 for x in eff if x.get('logical_id')==CT_ID)!=1: raise RuntimeError('EFFECTIVE_TARGETS')\nFILE_6_L72= legacy_rows,ct=driver.partition_targets(eff)\nFILE_6_L73= if [x['logical_id'] for x in legacy_rows]!=list(LEGACY_IDS) or ct.get('vmid')!=112: raise RuntimeError('REPRESENTATIVE_PARTITION')\nFILE_6_L74= if ct112.selftest() is not True: raise RuntimeError('CT112_HANDLER_SELFTEST')\nFILE_6_L75= print('SELFTEST_REPLACEMENT486=PASS',flush=True);return 0\nFILE_6_L76=\nFILE_7_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/CONTRACT.json\nFILE_7_BYTES=2077\nFILE_7_SHA256=a414715f1b91b3701c52073709a80c38218f76e3961618e8af61489211e50c01\nFILE_7_CONTEXT_LINE_COUNT=1\nFILE_7_L1={\"candidate\":\"pve-lxc-adapter-411r1\",\"next_gate\":\"PVE_LXC_REAL_ENVIRONMENT_DRY_RUN\",\"owner\":{\"MappedScratch\":\"pve270\",\"capture_archive\":\"pve270\",\"dump_argv\":\"pve265\",\"pve282.old\":\"pve265\",\"pve282.proven\":\"pve270\",\"validate_mapping\":\"pve270\"},\"schema\":1,\"signatures\":{\"pve265\":{\"dump_argv\":{\"args\":[\"job\",\"executable\",\"tmpdir\",\"now\"],\"defaults\":0,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonly\":[],\"vararg\":null},\"make_plan\":{\"args\":[\"rows\",\"configs\",\"storage_rows\",\"defaults\",\"staging_free\",\"now\",\"node\"],\"defaults\":1,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonly\":[],\"vararg\":null},\"produce\":{\"args\":[\"job\",\"work\",\"tools\",\"guard\",\"seconds\"],\"defaults\":1,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonly\":[],\"vararg\":null},\"validate_job\":{\"args\":[\"job\",\"now\"],\"defaults\":0,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonly\":[],\"vararg\":null}},\"pve270\":{\"capture_archive\":{\"args\":[\"argv\",\"destination\",\"cap\",\"seconds\",\"check\"],\"defaults\":0,\"kw_defaults\":[],\"kwarg\":null,\"kwonly\":[],\"posonl\nFILE_8_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/PLAN265_REFERENCE.json\nFILE_8_BYTES=7210\nFILE_8_SHA256=8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757\nFILE_8_CONTEXT_LINE_COUNT=1\nFILE_8_L1={\"all_guest_coverage_eligible\":true,\"backup_executed\":false,\"capacity_policy\":\"four archive copies plus 4GiB; capped producer; actual compressed size still unknown\",\"collected_at_utc\":\"2026-09-12T06:57:40.611329+00:00\",\"full_host_coverage\":false,\"global_blockers\":[],\"global_defaults\":{\"blockers\":[],\"keys\":[],\"present\":true,\"sha256\":\"cf34c8e3812547954bb1d08b50212b6e148ee9cf2b629e895a843ed49d623b38\"},\"guest_membership_and_configs_stable\":true,\"jobs\":[{\"application_consistency_verified\":false,\"archive_cap_bytes\":9516036916,\"blockers\":[],\"cdrom_count\":0,\"cdrom_media_included\":false,\"compression_dependent\":false,\"config_sha256\":\"09957aacd41ba9eeaf5fd4eda13936d580c9082a46895433379aa9dd0bad5969\",\"configured_archive_bound_bytes\":9516036916,\"configured_disk_bytes\":8589934592,\"configured_full_cycle_fits\":true,\"coverage_eligible\":true,\"disks\":[{\"backup_setting\":\"included_default_rootfs\",\"class\":\"managed-volume\",\"configured_size_bytes\":8589934592,\"slot\":\"rootfs\",\"storage\":\"local-lvm\"}],\"global_def\nFILE_9_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/PROVENANCE.json\nFILE_9_BYTES=4310\nFILE_9_SHA256=303f44dc8e91c8e6a675f9ea406ce4b7532ed98440a9b6807fa593ede9d33254\nFILE_9_CONTEXT_LINE_COUNT=1\nFILE_9_L1={\"anchors\":{\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-EXACT-CALL-GRAPH-PROOF-401R1\":\"840b3f1e8b023899c0554305a97af2d55dc71cf9\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-IMMUTABLE-ADAPTER-CANDIDATE-STAGE-FIXTURE-RCA-FIX-407R1\":\"0261475cdd35d3b762de0a29e46f3acd0b9af229\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-IMMUTABLE-ADAPTER-CANDIDATE-STAGE-RCA-FIX-403R1\":\"e3f753ce9cf95e21750e9ad7f9574cafc199b3ce\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERL270-HISTORICAL-TEST-RCA-404R1\":\"58f61ce62c3b6a57d8c5fc2465d2537d6728f9d5\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-EXEC-FS-RCA-409R1\":\"0db666a5a1f4796b567663adfc5568817e87efa5\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-NEED-TRACE-RCA-408R1\":\"44cdff7dab9e60aff1d69c12d2c32a4b26d04de8\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-PRODUCTION-VS-FIXTURE-RCA-406R1\":\"4c5836ff0ef73cb2ef306857be713383ba5e85f4\",\"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-ROOT-SUITE-STRUCTURED-RCA-410R1\":\"4673bfaf04877370bc4ac02fef37885eb82cb1eb\nFILE_10_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/adapter.py\nFILE_10_BYTES=2989\nFILE_10_SHA256=18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3\nFILE_10_CONTEXT_LINE_COUNT=30\nFILE_10_L1=#!/usr/bin/python3\nFILE_10_L2=import hashlib,inspect,json,pathlib,sys\nFILE_10_L3=BASE=pathlib.Path(__file__).resolve().parent\nFILE_10_L4=EXPECTED={\nFILE_10_L5='v2core.py':'e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2',\nFILE_10_L6='transport261.py':'b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688',\nFILE_10_L7='restore262.py':'56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1',\nFILE_10_L8='stream263.py':'a83d03a86b93a855716658652dd073fcb281cd387fc6200fe4bc2a922f39ea16',\nFILE_10_L9='nodeplan263.py':'db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060',\nFILE_10_L10='large264.py':'5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a',\nFILE_10_L19= return h.hexdigest()\nFILE_10_L20=def verify_sources():\nFILE_10_L21= for name,want in EXPECTED.items():\nFILE_10_L22= p=BASE/name\nFILE_10_L23= if not p.is_file() or p.is_symlink() or sha(p)!=want:raise RuntimeError('SOURCE_IDENTITY_'+name)\nFILE_10_L24=def load_graph():\nFILE_10_L25= import guest266,pve265,pve270,pve282\nFILE_10_L26= if hasattr(guest266,'dump_argv'):raise RuntimeError('GUEST266_UNEXPECTED_DUMP_ARGV')\nFILE_10_L27= if not hasattr(pve265,'dump_argv'):raise RuntimeError('PVE265_DUMP_ARGV_MISSING')\nFILE_10_L28= if getattr(pve270,'old',None) is not pve265:raise RuntimeError('PVE270_OLD_NOT_PVE265')\nFILE_10_L35=def describe():\nFILE_10_L36= _,p265,p270,p282=load_graph()\nFILE_10_L37= funcs={'pve265.dump_argv':p265.dump_argv,'pve265.validate_job':p265.validate_job,'pve265.make_plan':p265.make_plan,'pve270.validate_mapping':p270.validate_mapping,'pve270.capture_archive':p270.capture_archive,'pve270.produce':p270.produce,'pve282.select112':p282.select112,'pve282.produce':p282.produce}\nFILE_10_L38= return {'schema':1,'bundle':'pve-lxc-adapter-411r1','source_sha256':EXPECTED,'signatures':{k:str(inspect.signature(v)) for k,v in funcs.items()},'owner':{'dump_argv':'pve265','MappedScratch':'pve270','capture_archive':'pve270','validate_mapping':'pve270','pve282.old':'pve265','pve282.proven':'pve270'}}\nFILE_10_L39=def main():\nFILE_10_L40= verify_sources(); mode=sys.argv[1] if len(sys.argv)>1 else 'selftest'; d=describe()\nFILE_10_L41= if mode not in ('selftest','describe'):raise SystemExit(2)\nFILE_10_L42= print(json.dumps(d,sort_keys=True,separators=(',',':')))\nFILE_10_L43= if mode=='selftest':print('ADAPTER_SELFTEST=PASS')\nFILE_10_L44=if __name__=='__main__':main()\nFILE_11_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/guest266.py\nFILE_11_BYTES=7385\nFILE_11_SHA256=86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868\nFILE_11_CONTEXT_LINE_COUNT=75\nFILE_11_L1=\"\"\"266R1 bounded real-guest orchestration. Import has no side effects.\nFILE_11_L2=\nFILE_11_L3=One small LXC rootfs, no compression-dependent admission. No node-wide approval,\nFILE_11_L4=no guest restore/start/delete, no cloud deletion. Archive CRC/SHA is not boot proof.\nFILE_11_L5=\"\"\"\nFILE_11_L6=import copy,os,time\nFILE_11_L7=from pathlib import Path\nFILE_11_L8=import v2core as core\nFILE_11_L9=import transport261 as t\nFILE_11_L10=import stream263 as stream\nFILE_11_L11=import large264 as large\nFILE_11_L12=import pve265 as producer\nFILE_11_L13=\nFILE_11_L14=MAX_CONFIGURED_BYTES=8*1024**3\nFILE_11_L15=RESERVE_BYTES=536870912\nFILE_11_L25=\nFILE_11_L26=def choose(plan,now):\nFILE_11_L27= rows=plan_rows(plan);t.need(plan.get('global_blockers')==[],'GLOBAL_DEFAULT_BLOCK')\nFILE_11_L28= candidates=[]\nFILE_11_L29= for job in rows:\nFILE_11_L30= if not (job.get('type')=='lxc' and job.get('coverage_eligible') is True and\nFILE_11_L31= job.get('blockers')==[] and job.get('compression_dependent') is False and\nFILE_11_L35= disks[0].get('class')=='managed-volume' and disks[0].get('backup_setting') in ('included','included_default_rootfs')):continue\nFILE_11_L36= n=job.get('configured_disk_bytes')\nFILE_11_L37= if type(n) is not int or not 0=max(1024**3,chosen['configured_disk_bytes']),'SNAPSHOT_HEADROOM_UNPROVEN')\nFILE_11_L45= return copy.deepcopy(chosen)\nFILE_11_L46=\nFILE_11_L47=def same_state(before,after,vmid):\nFILE_11_L48= old=plan_rows(before);new=plan_rows(after)\nFILE_11_L64= if type(task.get('endtime')) is int and task.get('status') not in (None,'RUNNING'):continue\nFILE_11_L65= kind=task['type'];target=str(task.get('id',''))\nFILE_11_L66= if kind in ('vncshell','vncterm','vncproxy','spiceproxy'):continue\nFILE_11_L67= t.need(target!=str(vmid) and not any(x in kind.lower() for x in ('dump','restore','migrat','snapshot','clone','move_disk','resize')),'COMPETING_PVE_TASK')\nFILE_11_L68=\nFILE_11_L69=def check_result(result,job,work):\nFILE_11_L70= t.need(type(result) is dict,'PRODUCER_RESULT_SCHEMA')\nFILE_11_L80= finally:os.close(fd)\nFILE_11_L81= return expected\nFILE_11_L82=\nFILE_11_L83=def run_flow(job,work,entries,cloud,reader,produce,state_guard,guard,compression_check,generation,say,clock=lambda:int(time.time())):\nFILE_11_L84= \"\"\"Caller provides real fresh PVE guards; simulation callbacks live in tests only.\"\"\"\nFILE_11_L85= producer.validate_job(job,clock());core.validate_inventory(entries)\nFILE_11_L86= t.need(job['type']=='lxc' and job.get('compression_dependent') is False,'FIRST_GUEST_POLICY')\nFILE_11_L87= t.need(core.valid_name(generation),'GENERATION_ID')\nFILE_11_L88= t.need(all(callable(f) for f in (produce,state_guard,guard,compression_check,say,clock)),'CALLBACKS_REQUIRED')\nFILE_11_L89= work=Path(work);state_guard();guard()\nFILE_11_L90= for sub in ('producer','upload','restore'):t.make_dir(work/sub,exclusive=True)\nFILE_11_L91= say('PHASE','REAL_SINGLE_GUEST_SNAPSHOT')\nFILE_11_L92= produced=produce(job,work/'producer')\nFILE_11_L93= source=check_result(produced,job,work/'producer');state_guard();guard()\nFILE_11_L94= t.create(work/'PRODUCER.json',core.canonical_bytes(produced))\nFILE_11_L95= say('PRODUCER_RC',0);say('PAYLOAD_BYTES',produced['size']);say('ARCHIVE_SHA256',produced['sha256'])\nFILE_11_L96= logical='guest-%s-%s-%d'%(job['node'],job['type'],job['vmid'])\nFILE_11_L97= say('PHASE','TWO_REPLICA_UPLOAD_AND_READBACK')\nFILE_11_L98= up=large.upload(source,work/'upload',entries,cloud,generation,logical,RESERVE_BYTES,guard,clock=clock)\nFILE_11_L99= t.need(up['manifest']['payload_bytes']==produced['size'] and up['manifest']['payload_sha256']==produced['sha256'],'MANIFEST_PRODUCER_MISMATCH')\nFILE_11_L100= say('SEALED_QUORUM_VERIFIED','YES');mh=core.manifest_hash(up['manifest']);say('MANIFEST_SHA256',mh)\nFILE_11_L101= say('PHASE','CLOUD_DISCOVERY_AND_ARCHIVE_RECONSTRUCTION')\nFILE_11_L102= found=large.discover(reader,entries,logical,mh)\nFILE_11_L103= restored=large.restore(work/'restore',reader,found)\nFILE_11_L104= t.need(compression_check(work/'restore/restored.bin') is True,'RECONSTRUCTED_COMPRESSION_FAILED')\nFILE_11_L105= state_guard();guard();say('PHASE','GUARDED_LOCAL_PAYLOAD_CLEANUP')\nFILE_11_L106= count=large.cleanup(up,restored,reader,guard)\nFILE_11_L107= return {'state':'SINGLE_GUEST_ARCHIVE_SEALED_AND_CLOUD_RECONSTRUCTED','vmid':job['vmid'],'type':job['type'],'node':job['node'],\nFILE_11_L108= 'generation':generation,'logical_id':logical,'manifest_sha256':mh,'payload_bytes':produced['size'],'payload_sha256':produced['sha256'],\nFILE_11_L109= 'replicas':up['manifest']['replicas'],'parts_count':len(up['manifest']['parts']),'archive_cloud_restore_verified':True,\nFILE_11_L110= 'guest_restore_verified':False,'application_consistency_verified':False,'whole_node_backup':False,'local_payload_files_remaining':0,\nFILE_11_L111= 'files_deleted':count,'accounts_scanned':found['accounts_scanned'],'config_sha256':job['config_sha256']}\nFILE_12_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py\nFILE_12_BYTES=16514\nFILE_12_SHA256=5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a\nFILE_12_CONTEXT_LINE_COUNT=180\nFILE_12_L1=\"\"\"264R1 streaming cloud adapter and synthetic boundary proof.\nFILE_12_L2=\nFILE_12_L3=No work on import. No guest/backup producer and no cloud deletion. Existing\nFILE_12_L4=small-canary adapters are not modified. Only caller-owned local payload is removed,\nFILE_12_L5=after two cloud seals and a separately cloud-discovered restore have been checked.\nFILE_12_L6=\"\"\"\nFILE_12_L7=import hashlib\nFILE_12_L8=import os\nFILE_12_L9=from pathlib import Path\nFILE_12_L10=import selectors\nFILE_12_L11=import signal\nFILE_12_L12=import subprocess\nFILE_12_L13=import time\nFILE_12_L14=import v2core as core\nFILE_12_L15=import transport261 as t\nFILE_12_L16=import restore262 as r\nFILE_12_L17=import stream263 as s\nFILE_12_L18=\nFILE_12_L19=BOUNDARY_BYTES=536870929\nFILE_12_L20=LOGICAL_ID='stream-canary-pve01'\nFILE_12_L21=\nFILE_12_L22=\nFILE_12_L23=def process_stream(argv,env,size,digest,consume,seconds):\nFILE_12_L24= \"\"\"Drain both pipes, hashing stdout incrementally; never retain the whole part.\"\"\"\nFILE_12_L25= t.need(type(argv) is list and argv and seconds>0,'STREAM_PROCESS_ARGUMENTS')\nFILE_12_L26= sink=s.DigestSink(size,digest)\nFILE_12_L27= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE,\nFILE_12_L38= remaining=deadline-time.monotonic();t.need(remaining>0,'STREAM_TIMEOUT')\nFILE_12_L39= rc=p.wait(timeout=remaining);t.need(rc==0,'STREAM_PROCESS_RC_FAILED')\nFILE_12_L40= result=sink.finish();complete=True;return result\nFILE_12_L41= except subprocess.TimeoutExpired:raise t.Stop('STREAM_TIMEOUT') from None\nFILE_12_L42= finally:\nFILE_12_L43= if not complete or p.poll() is None:\nFILE_12_L44= try:os.killpg(p.pid,signal.SIGKILL)\nFILE_12_L53= '--cache-dir',str(Path(work)/'cache')]+args\nFILE_12_L54=\nFILE_12_L55=\nFILE_12_L56=class CloudLarge(t.RcloneIO):\nFILE_12_L57= \"\"\"Reuse the existing crypt inventory/namespace guards, not its in-memory payload I/O.\"\"\"\nFILE_12_L58= def call(self,args,limit):\nFILE_12_L59= t.need(type(args) is list and args and args[0] in ('about','lsjson','copyto','cat'),'RCLONE_VERB_FORBIDDEN')\nFILE_12_L60= t.need(type(limit) is int and 0<=limit<=core.MANIFEST_MAX_BYTES+1,'METADATA_BOUND')\nFILE_12_L61= self.check();seconds=min(3600 if args[0]=='copyto' else 90,self.deadline-time.monotonic())\nFILE_12_L62= t.need(seconds>0,'CLOUD_DEADLINE');self.calls+=1\nFILE_12_L63= if args[0]=='copyto':self.writes_attempted+=1\nFILE_12_L64= rc,out=t.bounded_process(flags(self.executable,self.config_path,self.work,args),self.env,limit,seconds)\nFILE_12_L65= self.check();return rc,out\nFILE_12_L66= def put(self,remote,name,path):\nFILE_12_L67= dest=self.address(remote,name)\nFILE_12_L68= if name in ('manifest.json','SEALED.json'):\nFILE_12_L69= return super().put(remote,name,path)\nFILE_12_L70= part=next((p for p in self.manifest['parts'] if p['name']==name),None)\nFILE_12_L71= t.need(part is not None,'PART_NOT_IN_MANIFEST')\nFILE_12_L72= n,h,identity=s.hash_file(path,part['size'])\nFILE_12_L73= t.need(n==part['size'] and h==part['sha256'],'UPLOAD_SOURCE_HASH')\nFILE_12_L74= rc,_=self.call(['lsjson',dest,'--stat'],65536)\nFILE_12_L75= t.need(rc in (3,4),'OBJECT_NOT_PROVEN_ABSENT')\nFILE_12_L76= with s.file_handle(path,part['size']) as (_,size,seen):\nFILE_12_L77= t.need(seen==identity,'UPLOAD_SOURCE_CHANGED')\nFILE_12_L78= rc,_=self.call(['copyto',str(path),dest,'--immutable','--no-update-modtime'],65536)\nFILE_12_L79= t.need(rc==0,'CLOUD_COPY_FAILED')\nFILE_12_L80= def read_stream(self,remote,name,size,digest,consume):\nFILE_12_L81= address=self.address(remote,name)\nFILE_12_L82= part=next((p for p in self.manifest['parts'] if p['name']==name),None)\nFILE_12_L83= t.need(part is not None and part['size']==size and part['sha256']==digest,'READBACK_EXPECTATION')\nFILE_12_L84= self.check();seconds=min(3600,self.deadline-time.monotonic());t.need(seconds>0,'CLOUD_DEADLINE')\nFILE_12_L85= self.calls+=1\nFILE_12_L86= out=process_stream(flags(self.executable,self.config_path,self.work,['cat',address,'--count',str(size+1)]),\nFILE_12_L90=\nFILE_12_L91=class ReadOnlyLarge(r.ReadOnlyCloud):\nFILE_12_L92= \"\"\"Metadata uses inherited bounded reads; this class has no upload API.\"\"\"\nFILE_12_L93= def read_stream(self,remote,generation,name,size,digest,consume):\nFILE_12_L94= t.need(type(size) is int and 00,'RESTORE_DEADLINE')\nFILE_12_L98= self.calls+=1\nFILE_12_L99= out=process_stream(flags(self.executable,self.snapshot,self.work,['cat',address,'--count',str(size+1)]),\nFILE_12_L100= self.env,size,digest,consume,seconds)\nFILE_12_L116=\nFILE_12_L117=\nFILE_12_L118=def receipt(rep,m,whole,count):\nFILE_12_L119= t.need(count==m['payload_bytes'] and whole.hexdigest()==m['payload_sha256'],'PAYLOAD_READBACK_MISMATCH')\nFILE_12_L120= return {**rep,'generation':m['generation'],'manifest_sha256':core.manifest_hash(m),\nFILE_12_L121= 'payload_sha256':whole.hexdigest(),'payload_bytes':count,'parts':m['parts'],\nFILE_12_L122= 'method':'download-sha256','ok':True}\nFILE_12_L123=\nFILE_12_L124=\nFILE_12_L125=def upload(source,work,entries,cloud,generation,logical_id,reserve,guard,clock=lambda:int(time.time())):\nFILE_12_L126= \"\"\"Upload and seal; retain all source bytes here. Cleanup is a later gated operation.\"\"\"\nFILE_12_L127= work=Path(work);source=Path(source);guard();core.validate_inventory(entries)\nFILE_12_L128= t.need(core.valid_name(generation) and core.valid_name(logical_id),'GENERATION_ID')\nFILE_12_L129= partdir=work/'parts';t.make_dir(partdir,exclusive=True)\nFILE_12_L130= record=s.split_file(source,partdir)\nFILE_12_L131= t.create(work/'parts-record.json',core.canonical_bytes(record))\nFILE_12_L132= quotas={e['account_id']:cloud.quota(e['crypt_remote']) for e in entries}\nFILE_12_L133= allocation=core.allocate(entries,quotas,record['payload_bytes'],reserve,clock())\nFILE_12_L134= m=core.make_manifest(generation,logical_id,record['payload_bytes'],record['payload_sha256'],record['parts'],allocation)\nFILE_12_L135= mb=core.canonical_bytes(m);mh=core.manifest_hash(m);cloud.bind(m)\nFILE_12_L136= t.create(work/'allocation.json',core.canonical_bytes(allocation));t.create(work/'manifest.json',mb)\nFILE_12_L137= for rep in m['replicas']:\nFILE_12_L138= guard();cloud.absent(rep['crypt_remote'])\nFILE_12_L139= receipts=[]\nFILE_12_L140= for rep in m['replicas']:\nFILE_12_L141= guard();remote=rep['crypt_remote'];q=cloud.quota(remote)\nFILE_12_L142= t.need(core.quota_reason(q,clock()) is None and q['free']>=allocation['required_free_bytes'],'SELECTED_ACCOUNT_CAPACITY_CHANGED')\nFILE_12_L143= s.verify_parts(partdir,record)\nFILE_12_L144= for part in m['parts']:cloud.put(remote,part['name'],partdir/part['name'])\nFILE_12_L145= cloud.put(remote,'manifest.json',work/'manifest.json')\nFILE_12_L146= t.need(cloud.get(remote,'manifest.json',len(mb)+1)==mb,'MANIFEST_READBACK_MISMATCH')\nFILE_12_L147= whole=hashlib.sha256();count=0\nFILE_12_L148= for part in m['parts']:\nFILE_12_L149= observed=cloud.read_stream(remote,part['name'],part['size'],part['sha256'],whole.update)\nFILE_12_L150= t.need(observed=={'size':part['size'],'sha256':part['sha256']},'PART_READBACK_MISMATCH');count+=observed['size']\nFILE_12_L151= receipts.append(receipt(rep,m,whole,count));t.create(work/('receipt-%d.json'%len(receipts)),core.canonical_bytes(receipts[-1]))\nFILE_12_L152= core.verify_receipts(m,receipts)\nFILE_12_L153= seal={'schema_version':2,'state':'SEALED','generation':generation,'logical_id':logical_id,\nFILE_12_L154= 'manifest_sha256':mh,'replicas':m['replicas'],'receipts':receipts,\nFILE_12_L155= 'acceptance':'IDENTICAL_SEAL_REQUIRED_ON_BOTH_NAMED_ACCOUNTS'}\nFILE_12_L156= sb=core.canonical_bytes(seal);t.need(len(sb)<=core.SEAL_MAX_BYTES,'SEAL_BOUND')\nFILE_12_L157= r.validate_seal(sb,entries,generation,logical_id);t.create(work/'SEALED.json',sb)\nFILE_12_L158= for rep in m['replicas']:\nFILE_12_L159= guard();cloud.put(rep['crypt_remote'],'SEALED.json',work/'SEALED.json')\nFILE_12_L160= for rep in m['replicas']:\nFILE_12_L161= t.need(cloud.get(rep['crypt_remote'],'SEALED.json',len(sb)+1)==sb,'SEALED_QUORUM_REQUIRED')\nFILE_12_L162= guard();s.verify_parts(partdir,record)\nFILE_12_L163= t.create(work/'QUORUM_VERIFIED.json',core.canonical_bytes({'manifest_sha256':mh,'seal_sha256':t.sha(sb)}))\nFILE_12_L164= return {'sealed':True,'manifest':m,'seal_bytes':sb,'parts_record':record,'source':str(source),'work':str(work)}\nFILE_12_L165=\nFILE_12_L166=\nFILE_12_L167=def discover(cloud,entries,logical_id,expected_hash):\nFILE_12_L168= \"\"\"Find placement from all cloud catalogs, using only an external manifest digest.\"\"\"\nFILE_12_L169= core.validate_inventory(entries)\nFILE_12_L170= t.need(core.valid_name(logical_id) and core.valid_hash(expected_hash),'DISCOVERY_INPUT')\nFILE_12_L171= listed={};generations=set()\nFILE_12_L172= for e in entries:\nFILE_12_L173= gs=cloud.list_generations(e['crypt_remote'])\nFILE_12_L174= t.need(type(gs) is list and len(gs)<=r.MAX_GENERATIONS and all(core.valid_name(g) for g in gs),'GENERATION_LIST_INVALID')\nFILE_12_L175= t.need(len(gs)==len(set(gs)),'GENERATION_LIST_DUPLICATE');listed[e['crypt_remote']]=set(gs);generations.update(gs)\nFILE_12_L176= t.need(len(generations)<=r.MAX_GENERATIONS,'TOTAL_GENERATIONS_BOUND')\nFILE_12_L177= candidates=[]\nFILE_12_L178= for gen in sorted(generations):\nFILE_12_L179= seen={}\nFILE_12_L180= for e in entries:\nFILE_12_L181= remote=e['crypt_remote']\nFILE_12_L182= if gen not in listed[remote]:continue\nFILE_12_L183= b=cloud.get(remote,gen,'SEALED.json',core.SEAL_MAX_BYTES+1,missing_ok=True)\nFILE_12_L184= if b is None:continue\nFILE_12_L185= seal=r.validate_seal(b,entries,gen,logical_id)\nFILE_12_L186= t.need(remote in {x['crypt_remote'] for x in seal['replicas']},'UNNAMED_SEAL_LOCATION');seen[remote]=(b,seal)\nFILE_12_L187= if not seen:continue\nFILE_12_L188= sb,seal=next(iter(seen.values()))\nFILE_12_L189= if seal['manifest_sha256']!=expected_hash:continue\nFILE_12_L190= named={x['crypt_remote'] for x in seal['replicas']}\nFILE_12_L191= t.need(set(seen)==named and all(b==sb for b,_ in seen.values()),'IDENTICAL_TWO_SEALS_REQUIRED')\nFILE_12_L192= blobs=[]\nFILE_12_L193= for rep in seal['replicas']:\nFILE_12_L194= b=cloud.get(rep['crypt_remote'],gen,'manifest.json',core.MANIFEST_MAX_BYTES+1)\nFILE_12_L195= t.need(type(b) is bytes and t.sha(b)==expected_hash,'MANIFEST_DIGEST_MISMATCH')\nFILE_12_L196= m=r.strict_json(b,core.MANIFEST_MAX_BYTES);core.validate_manifest(m)\nFILE_12_L197= t.need(m['generation']==gen and m['logical_id']==logical_id and m['replicas']==seal['replicas'],'MANIFEST_PLACEMENT')\nFILE_12_L198= t.need(core.canonical_bytes(m)==b,'MANIFEST_CANONICAL_BYTES');core.verify_receipts(m,seal['receipts']);blobs.append(b)\nFILE_12_L199= t.need(len(blobs)==2 and blobs[0]==blobs[1],'TWO_MANIFEST_COPIES_REQUIRED')\nFILE_12_L200= candidates.append({'manifest':m,'seal_bytes':sb,'accounts_scanned':len(listed)})\nFILE_12_L201= t.need(len(candidates)==1,'EXACTLY_ONE_ACCEPTED_GENERATION_REQUIRED')\nFILE_12_L202= return candidates[0]\nFILE_12_L203=\nFILE_12_L204=\nFILE_12_L205=def fresh_seals(cloud,discovery):\nFILE_12_L206= m=discovery['manifest'];core.validate_manifest(m);sb=discovery['seal_bytes']\nFILE_12_L207= for rep in m['replicas']:\nFILE_12_L208= t.need(cloud.get(rep['crypt_remote'],m['generation'],'SEALED.json',len(sb)+1)==sb,'SEAL_CHANGED_DURING_RESTORE')\nFILE_12_L209=\nFILE_12_L210=\nFILE_12_L211=def download_part(cloud,rep,m,part,path,whole):\nFILE_12_L212= path=Path(path);parent=t.open_dir(path.parent);fd=None\nFILE_12_L213= try:\nFILE_12_L214= fd=os.open(path.name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600,dir_fd=parent)\nFILE_12_L215= def consume(block):whole.update(block);s.write_all(fd,block)\nFILE_12_L216= result=cloud.read_stream(rep['crypt_remote'],m['generation'],part['name'],part['size'],part['sha256'],consume)\nFILE_13_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/nodeplan263.py\nFILE_13_BYTES=4603\nFILE_13_SHA256=db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060\nFILE_13_CONTEXT_LINE_COUNT=23\nFILE_13_L1=\"\"\"Pure, allowlisted PVE metadata projection. Does not execute any command.\"\"\"\nFILE_13_L2=from decimal import Decimal,InvalidOperation\nFILE_13_L3=import hashlib\nFILE_13_L4=import re\nFILE_13_L5=import v2core as core\nFILE_13_L6=\nFILE_13_L7=class PlanError(ValueError):pass\nFILE_13_L8=\nFILE_13_L15= if type(value) is not str or not re.fullmatch(r'\\d+(?:\\.\\d+)?[KMGTPE]?',value):return None\nFILE_13_L16= try:\nFILE_13_L17= suffix=value[-1] if value[-1] in 'KMGTPE' else ''\nFILE_13_L18= base=Decimal(value[:-1] if suffix else value)\nFILE_13_L19= b=base*(1024**('KMGTPE'.index(suffix)+1) if suffix else 1)\nFILE_13_L20= return int(b) if b==int(b) and 0time.monotonic(),'PRODUCER_TIMEOUT')\nFILE_14_L175= try:rc=p.wait(timeout=deadline-time.monotonic())\nFILE_14_L176= except subprocess.TimeoutExpired:raise PVEError('PRODUCER_TIMEOUT') from None\nFILE_14_L177= need(rc==0,'PRODUCER_RC_FAILED_PARTIAL_RETAINED');need(total>0,'EMPTY_ARCHIVE');check()\nFILE_14_L178= st=os.fstat(fd);named=os.stat(dest.name,dir_fd=parent,follow_symlinks=False)\nFILE_14_L179= need(st.st_dev==initial.st_dev and st.st_ino==initial.st_ino and t.ident(st)==t.ident(named) and\nFILE_14_L180= st.st_nlink==1 and st.st_size==total,'PRODUCER_FILE_IDENTITY_CHANGED')\nFILE_14_L200= \"\"\"Future explicit execution API. Caller must recheck full plan/coverage in guard.\nFILE_14_L201=\nFILE_14_L202= Compression CRC and SHA are verified; guest bootability remains unproved.\nFILE_14_L203= This function has no cloud/seal/delete operations and returns a readonly artifact.\nFILE_14_L204= \"\"\"\nFILE_14_L205= need(callable(guard),'GUARD_REQUIRED');guard();validate_job(job,int(time.time()));validate_tools(tools)\nFILE_14_L206= work=Path(work);parent=stream.private_dir(work)\nFILE_14_L209= tmp=work/'tmp';t.make_dir(tmp,exclusive=True)\nFILE_14_L210= dest=work/('guest-%d.%s.zst'%(job['vmid'],'tar' if job['type']=='lxc' else 'vma'))\nFILE_14_L211= def free_guard():\nFILE_14_L212= v=os.statvfs(work);need(v.f_frsize*v.f_bavail>=LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED')\nFILE_14_L213= free_guard()\nFILE_14_L214= result=capture_archive(dump_argv(job,tools['vzdump']['path'],tmp,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard)\nFILE_14_L215= guard();validate_tools(tools)\nFILE_14_L219= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH')\nFILE_14_L220= guard();validate_tools(tools)\nFILE_14_L221= return {**result,'path':str(dest),'vmid':job['vmid'],'type':job['type'],'node':job['node'],\nFILE_14_L222= 'config_sha256':job['config_sha256'],'compression_test_ok':True,'restored':False,\nFILE_14_L223= 'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True}\nFILE_15_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py\nFILE_15_BYTES=19595\nFILE_15_SHA256=97015277a4f3f8129c0fd11eb70c06293026366f1f8232a97feeed513a2dbcef\nFILE_15_CONTEXT_LINE_COUNT=140\nFILE_15_L3=Does not modify installed pve265, old workspaces, staging permissions or guest config.\nFILE_15_L4=Only explicit produce() executes vzdump. Private bounded stderr/exit metadata retained.\nFILE_15_L5=\"\"\"\nFILE_15_L6=import hashlib,json,os,re,secrets,selectors,signal,stat,subprocess,sys,time\nFILE_15_L7=from pathlib import Path\nFILE_15_L8=import v2core as core\nFILE_15_L9=import transport261 as t\nFILE_15_L10=import stream263 as stream\nFILE_15_L11=import pve265 as old\nFILE_15_L12=need=old.need\nFILE_15_L13=MAX_STDERR=2*1024**2\nFILE_15_L14=EXPECTED_CFG='09957aacd41ba9eeaf5fd4eda13936d580c9082a46895433379aa9dd0bad5969'\nFILE_15_L39=def live_gate(job,pvesh_path,pvesh_sha):\nFILE_15_L40= need(job.get('vmid')==110 and job.get('type')=='lxc' and job.get('node')=='pve01' and job.get('config_sha256')==EXPECTED_CFG,'ONLY_ACCEPTED_CT110')\nFILE_15_L41= vals=[]\nFILE_15_L42= for part in ('config','status/current','snapshot'):\nFILE_15_L43= need(t.sha(t.read_secure(Path(pvesh_path),16*1024**2)[0])==pvesh_sha,'PVESH_CHANGED')\nFILE_15_L44= rc,b=t.bounded_process([str(pvesh_path),'get','/nodes/pve01/lxc/110/'+part,'--output-format','json'],dict(old.ENV),262144,30)\nFILE_15_L45= need(rc==0,'CT_GATE_READ_FAILED')\nFILE_15_L46= try:vals.append(json.loads(b))\nFILE_15_L47= except (ValueError,UnicodeError):raise old.PVEError('CT_GATE_JSON') from None\nFILE_15_L48= validate_ct(*vals,job['config_sha256'])\nFILE_15_L49= return True\nFILE_15_L50=\nFILE_15_L59= def __init__(self,path,alias):\nFILE_15_L60= self.path=Path(path);self.alias=Path(alias);self.parent=None;self.fd=None\nFILE_15_L61= need(self.path.name=='perl' and self.alias.name=='perl5.40.1' and self.path.parent==self.alias.parent,'PERL_PATH_POLICY')\nFILE_15_L62= need(self.path.is_absolute() and '..' not in self.path.parts,'PERL_PATH_POLICY')\nFILE_15_L63= try:\nFILE_15_L64= self.parent=os.open('/',os.O_RDONLY|os.O_DIRECTORY)\nFILE_15_L65= for part in self.path.parts[1:-1]:\nFILE_15_L66= nxt=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=self.parent)\nFILE_15_L67= os.close(self.parent);self.parent=nxt\nFILE_15_L68= s=os.fstat(nxt)\nFILE_15_L69= need(s.st_uid==0 and not s.st_mode&0o022,'PERL_PARENT_UNTRUSTED')\nFILE_15_L75= need(os.pread(self.fd,4,0)==b'\\x7fELF','PERL_ELF_REQUIRED')\nFILE_15_L76= self.identity=t.ident(s)+(s.st_nlink,)\nFILE_15_L77= self.sha256=self._digest();self.check()\nFILE_15_L78= except BaseException:\nFILE_15_L79= self.close();raise\nFILE_15_L80= def _digest(self):\nFILE_15_L81= s=os.fstat(self.fd);need(00,'PERL_TIMEOUT')\nFILE_15_L123= rc=p.wait(timeout=left);self.check();return rc,bytes(out)\nFILE_15_L124= except subprocess.TimeoutExpired:raise old.PVEError('PERL_TIMEOUT') from None\nFILE_15_L125= finally:\nFILE_15_L126= if p is not None:\nFILE_15_L127= if p.poll() is None:\nFILE_15_L150= for p,h in pins.items():need(t.sha(t.read_secure(Path(p),2*1024**2)[0])==h,'PVE_MAPPING_SOURCE_CHANGED')\nFILE_15_L151= pin.check()\nFILE_15_L152= try:d=json.loads(b)\nFILE_15_L153= except (ValueError,UnicodeError):raise old.PVEError('PVE_IDMAP_JSON') from None\nFILE_15_L154= return validate_mapping(d)\nFILE_15_L155=\nFILE_15_L156=def _parent(path):\nFILE_15_L157= path=Path(path)\nFILE_15_L158= need(path.is_absolute() and '..' not in path.parts,'TEMP_PARENT_PATH')\nFILE_15_L159= fd=os.open('/',os.O_RDONLY|os.O_DIRECTORY)\nFILE_15_L160= try:\nFILE_15_L161= for i,part in enumerate(path.parts[1:]):\nFILE_15_L162= nxt=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=fd);os.close(fd);fd=nxt\nFILE_15_L163= s=os.fstat(fd);last=i==len(path.parts)-2\nFILE_15_L164= need(s.st_uid==0,'TEMP_PARENT_OWNER')\nFILE_15_L165= need(not s.st_mode&0o022 or last and s.st_mode&stat.S_ISVTX,'TEMP_PARENT_WRITABLE')\nFILE_15_L166= result=fd;fd=None;return result\nFILE_15_L170=\nFILE_15_L171=class MappedScratch:\nFILE_15_L172= \"\"\"Root-owned, mapped-GID traverse-only, no access for others. No recursive cleanup.\"\"\"\nFILE_15_L173= def __init__(self,gid,base='/var/tmp'):\nFILE_15_L174= need(type(gid) is int and gid==100000,'TEMP_GID_POLICY')\nFILE_15_L175= self.parent=_parent(base);self.fd=None;self.gid=gid\nFILE_15_L176= self.name='homelab-vzdump-268-'+secrets.token_hex(16);self.path=Path(base)/self.name\nFILE_15_L177= try:\nFILE_15_L178= os.mkdir(self.name,0o700,dir_fd=self.parent)\nFILE_15_L179= self.fd=os.open(self.name,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=self.parent)\nFILE_15_L181= os.fchown(self.fd,0,gid);os.fchmod(self.fd,0o710)\nFILE_15_L182= s=os.fstat(self.fd);self.identity=(s.st_dev,s.st_ino)\nFILE_15_L183= os.fsync(self.parent);self.check()\nFILE_15_L184= except BaseException:\nFILE_15_L185= if self.fd is not None:os.close(self.fd);self.fd=None\nFILE_15_L186= os.close(self.parent);self.parent=None\nFILE_15_L187= raise\nFILE_15_L199= name='access-probe';fd=os.open(name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o644,dir_fd=self.fd)\nFILE_15_L200= try:os.fchmod(fd,0o644);stream.write_all(fd,b'HOMELAB_ACCESS_PROBE\\n');os.fsync(fd);identity=t.ident(os.fstat(fd))\nFILE_15_L201= finally:os.close(fd)\nFILE_15_L202= positive='''import os,sys\nFILE_15_L203=p,private=sys.argv[1:]\nFILE_15_L204=assert open(p+'/access-probe','rb').read()==b'HOMELAB_ACCESS_PROBE\\\\n'\nFILE_15_L205=for action in (lambda:os.listdir(p),lambda:os.open(p+'/forbidden',os.O_WRONLY|os.O_CREAT,0o600),lambda:os.open(private,os.O_RDONLY|os.O_DIRECTORY)):\nFILE_15_L207= except PermissionError:continue\nFILE_15_L208= raise SystemExit(31)\nFILE_15_L209='''\nFILE_15_L210= negative='''import sys\nFILE_15_L211=try:open(sys.argv[1]+'/access-probe','rb').read()\nFILE_15_L212=except PermissionError:raise SystemExit(0)\nFILE_15_L213=raise SystemExit(32)\nFILE_15_L256= b=os.read(key.fileobj.fileno(),65536)\nFILE_15_L257= if not b:sel.unregister(key.fileobj);continue\nFILE_15_L258= if key.fileobj is p.stdout:\nFILE_15_L259= need(total+len(b)<=cap,'ARCHIVE_CAP_REACHED_PARTIAL_RETAINED')\nFILE_15_L260= stream.write_all(fd,b);digest.update(b);total+=len(b)\nFILE_15_L261= else:\nFILE_15_L262= err_total+=len(b);eh.update(b);chunk=b[:max(0,MAX_STDERR-err_kept)]\nFILE_15_L263= if chunk:stream.write_all(efd,chunk);err_kept+=len(chunk)\nFILE_15_L264= need(deadline>time.monotonic(),'PRODUCER_TIMEOUT')\nFILE_15_L265= try:rc=p.wait(timeout=deadline-time.monotonic())\nFILE_15_L266= except subprocess.TimeoutExpired:raise old.PVEError('PRODUCER_TIMEOUT') from None\nFILE_15_L267= need(rc==0,'PRODUCER_RC_FAILED_PARTIAL_RETAINED');need(total>0,'EMPTY_ARCHIVE');check()\nFILE_15_L268= st=os.fstat(fd);named=os.stat(dest.name,dir_fd=parent,follow_symlinks=False)\nFILE_15_L269= need(st.st_dev==initial.st_dev and st.st_ino==initial.st_ino and t.ident(st)==t.ident(named) and st.st_nlink==1 and st.st_size==total,'PRODUCER_FILE_IDENTITY_CHANGED')\nFILE_15_L270= os.fchmod(fd,0o400);os.fsync(fd);os.fsync(parent);good=True\nFILE_15_L306= scratch.probe(uid,work);emit('MAPPED_UID_ACCESS_AND_ISOLATION_PROBE','PASS')\nFILE_15_L307= def free_guard():\nFILE_15_L308= scratch.check()\nFILE_15_L309= for path,reserve,reason in ((work,old.LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED'),(scratch.path,256*1024**2,'PVE_TEMP_RESERVE_LOW')):\nFILE_15_L310= v=os.statvfs(path);need(v.f_frsize*v.f_bavail>=reserve,reason)\nFILE_15_L311= free_guard();guard();dest=work/'guest-110.tar.zst'\nFILE_15_L312= result=capture_archive(old.dump_argv(job,tools['vzdump']['path'],scratch.path,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard)\nFILE_15_L317= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH')\nFILE_15_L318= guard();old.validate_tools(tools);scratch.close(empty_only=True);complete=True;emit('PVE_TEMP_EMPTY_REMOVED','YES')\nFILE_15_L319= return {**result,'path':str(dest),'vmid':110,'type':'lxc','node':job['node'],'config_sha256':job['config_sha256'],\nFILE_15_L320= 'compression_test_ok':True,'restored':False,'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True}\nFILE_15_L321= finally:\nFILE_15_L322= m=work/'PRODUCER_PROCESS.json'\nFILE_15_L323= if m.exists():\nFILE_16_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve282.py\nFILE_16_BYTES=6195\nFILE_16_SHA256=73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012\nFILE_16_CONTEXT_LINE_COUNT=58\nFILE_16_L1=\"\"\"282R1 bounded CT112 adapter; proven 270 capture/temp/Perl implementation unchanged.\nFILE_16_L2=Expected source hash must be supplied from the immutable-hash-bound 265 plan.\nFILE_16_L3=No changes to source guest configuration; no source110 backup or guest deletion.\nFILE_16_L4=\"\"\"\nFILE_16_L5=import copy,json,os,time\nFILE_16_L6=from pathlib import Path\nFILE_16_L7=import v2core as core\nFILE_16_L8=import transport261 as t\nFILE_16_L9=import stream263 as stream\nFILE_16_L10=import pve265 as old\nFILE_16_L11=import pve270 as proven\nFILE_16_L12=import guest266 as g\nFILE_16_L13=need=old.need\nFILE_16_L14=PerlPin=proven.PerlPin\nFILE_16_L15=host_mapping=proven.host_mapping\nFILE_16_L26= need(core.valid_hash(expected_config) and job.get('config_sha256')==expected_config,'CT112_PLAN_CONFIG_ANCHOR')\nFILE_16_L27= return True\nFILE_16_L28=\nFILE_16_L29=def select112(current,baseline,now):\nFILE_16_L30= rows=g.plan_rows(current);previous=g.plan_rows(baseline)\nFILE_16_L31= by_id={j['vmid']:j for j in previous}\nFILE_16_L32= need(TARGET_VMID in by_id,'CT112_MISSING_FROM_ACCEPTED265')\nFILE_16_L33= g.same_state(baseline,current,TARGET_VMID)\nFILE_16_L34= need({j['vmid'] for j in rows}==set(by_id),'ACCEPTED265_MEMBERSHIP_CHANGED')\nFILE_16_L35= for row in rows:\nFILE_16_L36= need(row['config_sha256']==by_id[row['vmid']]['config_sha256'],'ACCEPTED265_GUEST_CONFIG_CHANGED')\nFILE_16_L37= selected=[j for j in rows if j['vmid']==TARGET_VMID]\nFILE_16_L38= need(len(selected)==1,'CT112_NOT_UNIQUE')\nFILE_16_L39= job=copy.deepcopy(selected[0]);old.validate_job(job,now)\nFILE_16_L40= need(current.get('global_blockers')==[],'GLOBAL_DEFAULT_BLOCK')\nFILE_16_L41= need(job.get('compression_dependent') is False and job.get('configured_full_cycle_fits') is True,'CT112_FULL_CYCLE_SPACE_UNPROVEN')\nFILE_16_L42= disks=job.get('disks');need(type(disks) is list and len(disks)==1,'CT112_SINGLE_ROOTFS_REQUIRED')\nFILE_16_L53=def live_gate(job,pvesh_path,pvesh_sha,expected_config):\nFILE_16_L54= require_target(job,expected_config)\nFILE_16_L55= vals=[]\nFILE_16_L56= for part in ('config','status/current','snapshot'):\nFILE_16_L57= need(t.sha(t.read_secure(Path(pvesh_path),16*1024**2)[0])==pvesh_sha,'PVESH_CHANGED')\nFILE_16_L58= rc,b=t.bounded_process([str(pvesh_path),'get','/nodes/pve01/lxc/112/'+part,'--output-format','json'],dict(old.ENV),262144,30)\nFILE_16_L59= need(rc==0,'CT112_GATE_READ_FAILED')\nFILE_16_L60= try:vals.append(json.loads(b))\nFILE_16_L61= except (ValueError,UnicodeError):raise old.PVEError('CT112_GATE_JSON') from None\nFILE_16_L62= validate_ct(*vals,expected_config)\nFILE_16_L63= return True\nFILE_16_L64=\nFILE_16_L79= scratch.probe(uid,work);emit('MAPPED_UID_ACCESS_AND_ISOLATION_PROBE','PASS')\nFILE_16_L80= def free_guard():\nFILE_16_L81= scratch.check()\nFILE_16_L82= for path,reserve,reason in ((work,old.LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED'),(scratch.path,256*1024**2,'PVE_TEMP_RESERVE_LOW')):\nFILE_16_L83= v=os.statvfs(path);need(v.f_frsize*v.f_bavail>=reserve,reason)\nFILE_16_L84= free_guard();guard();dest=work/'guest-112.tar.zst'\nFILE_16_L85= result=capture_archive(old.dump_argv(job,tools['vzdump']['path'],scratch.path,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard)\nFILE_16_L90= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH')\nFILE_16_L91= guard();old.validate_tools(tools);scratch.close(empty_only=True);complete=True;emit('PVE_TEMP_EMPTY_REMOVED','YES')\nFILE_16_L92= return {**result,'path':str(dest),'vmid':112,'type':'lxc','node':job['node'],'config_sha256':job['config_sha256'],\nFILE_16_L93= 'compression_test_ok':True,'restored':False,'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True}\nFILE_16_L94= finally:\nFILE_16_L95= m=work/'PRODUCER_PROCESS.json'\nFILE_16_L96= if m.exists():\nFILE_17_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/restore262.py\nFILE_17_BYTES=14701\nFILE_17_SHA256=56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1\nFILE_17_CONTEXT_LINE_COUNT=180\nFILE_17_L1=\"\"\"262R1 bounded cloud-discovered recovery. No live action on import.\nFILE_17_L2=\nFILE_17_L3=Cloud access is strictly lsjson/cat. No source-local workspace/manifest/receipt\nFILE_17_L4=is read. The external accepted manifest digest is a trust anchor, not placement.\nFILE_17_L5=\"\"\"\nFILE_17_L6=import hashlib\nFILE_17_L7=import io\nFILE_17_L8=import json\nFILE_17_L9=import os\nFILE_17_L10=from pathlib import Path\nFILE_17_L11=import re\nFILE_17_L12=import stat\nFILE_17_L13=import tarfile\nFILE_17_L14=import time\nFILE_17_L15=import v2core as core\nFILE_17_L16=import transport261 as t\nFILE_17_L17=\nFILE_17_L18=SOURCE_NAMES=frozenset(('etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md','etc/pve/31_HOMELAB_REFERENCE.md',\nFILE_17_L19= 'usr/local/sbin/homelab-control-backup','etc/systemd/system/homelab-control-backup.service'))\nFILE_17_L20=ALL_NAMES=SOURCE_NAMES|{'SOURCE_INDEX.json'}\nFILE_17_L21=MAX_GENERATIONS=64\nFILE_17_L22=SEAL_KEYS={'schema_version','state','generation','logical_id','manifest_sha256','replicas','receipts','acceptance'}\nFILE_17_L23=\nFILE_17_L24=def strict_json(data,limit):\nFILE_17_L25= t.need(type(data) is bytes and len(data)<=limit,'JSON_BOUND')\nFILE_17_L29= t.need(k not in result,'JSON_DUPLICATE_KEY');result[k]=v\nFILE_17_L30= return result\nFILE_17_L31= def bad_constant(value):raise t.Stop('JSON_NONFINITE_NUMBER')\nDEPENDENCY_PATH_COUNT=28\nDEPENDENCY_PATH_1=/opt/homelab-backup-v2/releases/core-260r1\nDEPENDENCY_PATH_2=/opt/homelab-backup-v2/releases/core-260r1/v2core.py\nDEPENDENCY_PATH_3=/opt/homelab-backup-v2/releases/ct-backup-282r1\nDEPENDENCY_PATH_4=/opt/homelab-backup-v2/releases/ct-backup-282r1/guest266.py\nDEPENDENCY_PATH_5=/opt/homelab-backup-v2/releases/ct-backup-282r1/pve282.py\nDEPENDENCY_PATH_6=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_guest266.py\nDEPENDENCY_PATH_7=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_perl270.py\nDEPENDENCY_PATH_8=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_producer282.py\nDEPENDENCY_PATH_9=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_pve282.py\nDEPENDENCY_PATH_10=/opt/homelab-backup-v2/releases/large-cloud-264r1\nDEPENDENCY_PATH_11=/opt/homelab-backup-v2/releases/large-cloud-264r1/large264.py\nDEPENDENCY_PATH_12=/opt/homelab-backup-v2/releases/pve-producer-265r1\nDEPENDENCY_PATH_13=/opt/homelab-backup-v2/releases/pve-producer-265r1/PLAN.json\nDEPENDENCY_PATH_14=/opt/homelab-backup-v2/releases/pve-producer-265r1/pve265.py\nDEPENDENCY_PATH_15=/opt/homelab-backup-v2/releases/pve-producer-265r1/test_pve265.py\nDEPENDENCY_PATH_16=/opt/homelab-backup-v2/releases/pve-retry-268r1\nDEPENDENCY_PATH_17=/opt/homelab-backup-v2/releases/pve-retry-270r1\nDEPENDENCY_PATH_18=/opt/homelab-backup-v2/releases/pve-retry-270r1/pve270.py\nDEPENDENCY_PATH_19=/opt/homelab-backup-v2/releases/pve-retry-270r1/test_pve270.py\nDEPENDENCY_PATH_20=/opt/homelab-backup-v2/releases/pve-run-266r1\nDEPENDENCY_PATH_21=/opt/homelab-backup-v2/releases/restore-262r1\nDEPENDENCY_PATH_22=/opt/homelab-backup-v2/releases/restore-262r1/restore262.py\nDEPENDENCY_PATH_23=/opt/homelab-backup-v2/releases/stream-263r1\nDEPENDENCY_PATH_24=/opt/homelab-backup-v2/releases/stream-263r1/nodeplan263.py\nDEPENDENCY_PATH_25=/opt/homelab-backup-v2/releases/stream-263r1/stream263.py\nDEPENDENCY_PATH_26=/opt/homelab-backup-v2/releases/transport-261r1\nDEPENDENCY_PATH_27=/opt/homelab-backup-v2/releases/transport-261r1/transport261.py\nDEPENDENCY_PATH_28=/opt/homelab-backup-v2/releases/vm-backup-283r1\nHAS_RCLONE_SOURCE=true\nHAS_OBJECT_PATH_CONTRACT=true\nDECISION=PASS_BACKUP213_EXACT_CLOUD_OBJECT_CONTRACT_CAPTURED\nNEXT_GATE=BUILD_CORRECTED_CLOUD_GENERATION_RECOVERY\nCLOUD_READS=0\nCLOUD_WRITES=0\nVPS_SSH_ATTEMPTS=0\nMESSENGERS214_CLOSED=false\nMAJOR_STAGES_REMAINING=8\nNEWFI213_END=true\n" +} diff --git a/runtime/latest.txt b/runtime/latest.txt index 5997e7e4..446db3f8 100644 --- a/runtime/latest.txt +++ b/runtime/latest.txt @@ -1,162 +1,1161 @@ CHAT_OUTPUT_BEGIN -COMMAND_ID=CONTEXT-AUTO-[PRIVATE_IP]701Z +COMMAND_ID=NEWFI-260920-NEWFDOM-MESSENGERS214-BASE213 STATUS=OK RC=0 HOST=pve01 -COMPONENT=cluster-context -REFERENCE_SHA[PRIVATE_IP]e5154c41cb[PRIVATE_IP]aca68090be[PRIVATE_IP]bf[PRIVATE_IP]df[PRIVATE_IP] -ERROR_REGISTER_SHA[PRIVATE_IP]ec0f527ed3afeed[PRIVATE_IP]ee[PRIVATE_IP]bbfb[PRIVATE_IP]af4ba7ba0 +MODE=read-only +COMPONENT=newfi-newfdom-messengers214-backupv2-base-release-authority-aware-readonly +REFERENCE_REGISTER_CHECK=OK +REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 +ERROR_REGISTER_CHECK=OK +ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 +COMMAND_SHA256=65d18e767349b7319c7197b07d4122dae093e606f12244bfd676798e5606e937 +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=0 +CHANGES_MADE=false +ROLLBACK_STARTED=false +ROLLBACK_RESTORED=null +MUTATION_OUTCOME=NO_MUTATION +SANITIZED=yes +SECRETS_INCLUDED=no +PRIVATE_ADDRESSES_INCLUDED=no +RAW_EVIDENCE_SHA256=417d54bc3ee03b085ea2894f80cd2f3c4e8df0652fe89b0f35fa9f5df73e61c3 +SANITIZED_OUTPUT_SHA256=c2da47960582cda8715a7fd595b714f908f504dd277254ed2b277fdb5c900bf3 OUTPUT_BEGIN -GENERATED_AT_UTC=[PRIVATE_IP]T02:17:01Z -Cluster information -------------------- -Name: homelab -Config Version: 3 -Transport: knet -Secure auth: on +NEWFI213_BEGIN=true +MODE=read-only +IMMUTABLE_PLAN_CHECK=PASS +CHANGES_MADE=false +MUTATION_OUTCOME=NO_MUTATION +RUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d +RUNNER_PIN_MATCH=true +ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 +ERROR_REGISTER_PIN_MATCH=true +REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 +REFERENCE_PIN_MATCH=true +WRAPPER_SHA256=5077444065c732451cb84898680f62361b21a24ef9eb4f191253e82ead524ea2 +WRAPPER_PIN_MATCH=true +OVERLAY_SHA256=ca6876fa9c3b1465b045344a8ad46962de16972c7dadc4ac6257c7d225047793 +OVERLAY_PIN_MATCH=true +PRODUCTION_ACCEPTANCE_SHA256=708a481f6719ff1ed8d0afec1902650ed36ac631e980611b015419f504f16df0 +PRODUCTION_ACCEPTANCE_PIN_MATCH=true +AUTHORITY_PIN_MATCH=true +BACKUP_SOURCE_PIN_MATCH=true +ACCEPTED_RELEASE=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1 +BASE_RELEASE=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1 +MATCHING_FILE_COUNT=26 +FILE_1_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ACCEPTANCE.json +FILE_1_BYTES=517 +FILE_1_SHA256=904dfccad43b55ad0b53cfbd29fb45fee36fd0fbfa5afa58da5c3cca0ee7153b +FILE_1_CONTEXT_LINE_COUNT=1 +FILE_1_L1={"command_id":"SUPPORT-260916-INFRA-DR-BACKUPV2-FINAL-REPLACEMENT-PLAN-STABILITY-SCHEMA-RCA-FIX-VALIDATE-PROMOTE-CUTOVER-SEAL-486R1","ct112_manifest_sha256":"1ac4f16a54bcdd8b896c9a131abef966d315256aa56da3f83ed0b30845c4fcb0","ct112_payload_bytes":223875308,"ct112_payload_sha256":"d443b85e76e806830fd9c372d0a09fc5b91ca9c9d35c44fb0ad7968cc996d01d","legacy_ids":["node-pve01","md-relay","spb-ingress","us-netbird","router-netcraze","xf-codevipe","xf-zakrutim","control-bootstrap-v2"],"representative_pass":9,"schema":1} +FILE_2_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/RELEASE_MANIFEST.json +FILE_2_BYTES=3301 +FILE_2_SHA256=a36dd0db88592ec3d355ef43e6b218da6c9ad649e9ccf8c8f680b96eb23a156e +FILE_2_CONTEXT_LINE_COUNT=1 +FILE_2_L1={"files":{"ACCEPTANCE.json":{"bytes":517,"mode":256,"sha256":"904dfccad43b55ad0b53cfbd29fb45fee36fd0fbfa5afa58da5c3cca0ee7153b"},"candidate411/CONTRACT.json":{"bytes":2077,"mode":256,"sha256":"a414715f1b91b3701c52073709a80c38218f76e3961618e8af61489211e50c01"},"candidate411/PLAN265_REFERENCE.json":{"bytes":7210,"mode":256,"sha256":"8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757"},"candidate411/PROVENANCE.json":{"bytes":4310,"mode":256,"sha256":"303f44dc8e91c8e6a675f9ea406ce4b7532ed98440a9b6807fa593ede9d33254"},"candidate411/adapter.py":{"bytes":2989,"mode":320,"sha256":"18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3"},"candidate411/guest266.py":{"bytes":7385,"mode":256,"sha256":"86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868"},"candidate411/large264.py":{"bytes":16514,"mode":256,"sha256":"5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a"},"candidate411/nodeplan263.py":{"bytes":4603,"mode":256,"sha256":"db5c16ee24d25aa +FILE_3_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/ct112_handler.py +FILE_3_BYTES=10955 +FILE_3_SHA256=8cac6e627d5e7abe5d3163cdd24911995a1e3f45c776f9373c2aa68784f5ec6c +FILE_3_CONTEXT_LINE_COUNT=98 +FILE_3_L1=#!/usr/bin/env python3 +FILE_3_L2=from __future__ import annotations +FILE_3_L3=import hashlib,json,os,secrets,shutil,stat,subprocess,sys,time +FILE_3_L4=from pathlib import Path +FILE_3_L5=ROOT=Path(__file__).resolve().parent +FILE_3_L6=CAND=ROOT/'candidate411' +FILE_3_L7=if str(CAND) not in sys.path:sys.path.insert(0,str(CAND)) +FILE_3_L8=import guest266,large264 as large,pve265,pve282,transport261 as t,v2core as core +FILE_3_L9=CT_ID='guest-pve01-lxc-112' +FILE_3_L10=PLAN=CAND/'PLAN265_REFERENCE.json' +FILE_3_L11=PLAN_SHA='8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757' +FILE_3_L12=RCLONE=Path('/usr/bin/rclone'); RCLONE_SHA='20df3d5eac5d4e187ce638e4216354e7fa6778c3c51f0caf48c48acca9686354' +FILE_3_L13=RCLONE_CFG=Path('/root/.config/rclone/[SENSITIVE_FILE]') +FILE_3_L14=BINDING_FP='ed48c1345771c3e0f50db9516f13f26bb66047283e2f883d5df272f15e2282b8' +FILE_3_L15=VZDUMP=Path('/usr/bin/vzdump'); VZDUMP_SHA='21339d009afd55cb7ec613b64c1f493b2a7dbd18b85b4d712909ef6d6710f4ea' +FILE_3_L16=ZSTD=Path('/usr/bin/zstd'); ZSTD_SHA='3eb7744cf8fd1ecd46d617098ee870b3e7911fe570ffaba662c1c5b7d6456020' +FILE_3_L26= p=subprocess.run(argv,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=45,check=False,env=ENV) +FILE_3_L27= if p.returncode!=0 or len(p.stdout)>4*1024*1024:raise RuntimeError('PVESH_READ_FAILED') +FILE_3_L28= try:return json.loads(p.stdout.decode('utf-8','strict')) +FILE_3_L29= except (ValueError,UnicodeError):raise RuntimeError('PVESH_JSON') from None +FILE_3_L30= +FILE_3_L31=def _baseline(): +FILE_3_L32= b=t.read_secure(PLAN,1024*1024,private=True)[0] +FILE_3_L33= if t.sha(b)!=PLAN_SHA:raise RuntimeError('PLAN_REFERENCE_IDENTITY') +FILE_3_L34= try:return json.loads(b.decode('utf-8','strict')) +FILE_3_L35= except (ValueError,UnicodeError):raise RuntimeError('PLAN_REFERENCE_JSON') from None +FILE_3_L36= +FILE_3_L37=def _defaults(): +FILE_3_L38= p=Path('/etc/vzdump.conf');st=os.stat(p,follow_symlinks=False) +FILE_3_L100= return {'path':str(path),'sha256':want} +FILE_3_L101= +FILE_3_L102=def _private_config(): +FILE_3_L103= b,identity=t.read_secure(RCLONE_CFG,t.MAX_CONFIG_BYTES,private=True) +FILE_3_L104= text=b.decode('utf-8-sig','strict');entries=core.inventory_from_config(text);binding=t.binding(text) +FILE_3_L105= if len(entries)!=13 or len({x['account_id'] for x in entries})!=13:raise RuntimeError('RCLONE_INVENTORY') +FILE_3_L106= if hashlib.sha256(binding.encode()).hexdigest()!=BINDING_FP:raise RuntimeError('RCLONE_BINDING') +FILE_3_L107= return b,entries,binding +FILE_3_L108= +FILE_3_L109=def _write_private(path,data): +FILE_3_L118=class LazyReader: +FILE_3_L119= def __init__(self,work,entries,binding,guard,deadline,logical_id): +FILE_3_L120= self.work=Path(work);self.entries=entries;self.binding=binding;self.guard=guard;self.deadline=deadline;self.logical_id=logical_id +FILE_3_L121= self.snapshot=self.work/'rclone.snapshot';self.inner=None +FILE_3_L122= def _ensure(self): +FILE_3_L123= if self.inner is not None:return self.inner +FILE_3_L124= b,e,bind=_private_config() +FILE_3_L125= if e!=self.entries or bind!=self.binding:raise RuntimeError('RCLONE_SEMANTIC_DRIFT') +FILE_3_L126= _write_private(self.snapshot,b) +FILE_3_L127= self.inner=large.ReadOnlyLarge(str(RCLONE),self.snapshot,e,bind,self.guard,self.work,self.deadline,self.logical_id) +FILE_3_L128= return self.inner +FILE_3_L129= def list_generations(self,*a,**k):return self._ensure().list_generations(*a,**k) +FILE_3_L130= def get(self,*a,**k):return self._ensure().get(*a,**k) +FILE_3_L131= def read_stream(self,*a,**k):return self._ensure().read_stream(*a,**k) +FILE_3_L132= def close(self): +FILE_3_L135= +FILE_3_L136=def selftest(): +FILE_3_L137= if _sha(PLAN,1024*1024)!=PLAN_SHA:return False +FILE_3_L138= for p,h in ((RCLONE,RCLONE_SHA),(VZDUMP,VZDUMP_SHA),(ZSTD,ZSTD_SHA)): +FILE_3_L139= if _sha(p)!=h:return False +FILE_3_L140= return True +FILE_3_L141= +FILE_3_L142=def run_target(target,state_writer=None): +FILE_3_L143= if not isinstance(target,dict) or target.get('logical_id')!=CT_ID or target.get('vmid')!=112:return (_ for _ in ()).throw(RuntimeError('CT112_DESCRIPTOR')) +FILE_3_L144= if _sha(PLAN,1024*1024)!=PLAN_SHA:raise RuntimeError('PLAN_REFERENCE_IDENTITY') +FILE_3_L145= tools={'vzdump':_tool(VZDUMP,VZDUMP_SHA),'zstd':_tool(ZSTD,ZSTD_SHA)};_tool(RCLONE,RCLONE_SHA) +FILE_3_L146= pvesh_sha=_sha(PVESH);now=int(time.time());current=_fresh_plan(now);job=_select_live_ct112(current,now) +FILE_3_L147= expected=job['config_sha256'];mapping=_canonical_host_mapping() +FILE_3_L148= def producer_guard(): +FILE_3_L153= pve282.live_gate(fresh,str(PVESH),pvesh_sha,expected) +FILE_3_L154= producer_guard() +FILE_3_L155= cfg,entries,binding=_private_config() +FILE_3_L156= generation='V2-'+time.strftime('%Y%m%dT%H%M%SZ',time.gmtime())+'-'+secrets.token_hex(6) +FILE_3_L157= work=STAGING/('backup-v2-'+generation);work.mkdir(mode=0o700,parents=False,exist_ok=False) +FILE_3_L158= deadline=time.monotonic()+21600 +FILE_3_L159= def cloud_guard(): +FILE_3_L160= _,e,b=_private_config() +FILE_3_L161= if e!=entries or b!=binding:raise RuntimeError('RCLONE_SEMANTIC_DRIFT') +FILE_3_L162= writer=large.CloudLarge(str(RCLONE),RCLONE_CFG,entries,binding,cloud_guard,work/'upload',deadline) +FILE_3_L163= reader=LazyReader(work/'restore',entries,binding,cloud_guard,deadline,CT_ID) +FILE_3_L164= def produce(j,w):return pve282.produce(j,w,tools,producer_guard,mapping=mapping,expected_config=expected,seconds=14400,emit=lambda k,v:None) +FILE_3_L165= def compression(path): +FILE_3_L166= p=subprocess.run([str(ZSTD),'--test','--quiet','--',str(path)],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,timeout=3600,check=False,env=ENV) +FILE_3_L167= return p.returncode==0 +FILE_3_L168= try: +FILE_3_L169= r=guest266.run_flow(job,work,entries,writer,reader,produce,producer_guard,cloud_guard,compression,generation,lambda k,v:None) +FILE_3_L170= reps=r.get('replicas') +FILE_3_L171= if not isinstance(reps,list) or len(reps)!=2 or len({x.get('account_id') for x in reps})!=2:raise RuntimeError('REPLICA_PHYSICAL_INDEPENDENCE') +FILE_3_L172= if r.get('archive_cloud_restore_verified') is not True or r.get('local_payload_files_remaining')!=0:raise RuntimeError('CT112_RESTORE_CONTRACT') +FILE_3_L173= out={**r,'status':'OK','cloud_only_independent_restore_verified':True,'local_payload_files_remaining':0} +FILE_3_L174= reader.close();shutil.rmtree(work) +FILE_3_L175= if work.exists():raise RuntimeError('CT112_WORKSPACE_CLEANUP') +FILE_3_L176= if state_writer is not None: +FILE_3_L177= state_writer(CT_ID,'SUCCESS',generation=r['generation'],payload_sha256=r['payload_sha256'],manifest_sha256=r['manifest_sha256'],replicas=r['replicas'],green_verified=True) +FILE_3_L178= return out +FILE_3_L179= except BaseException: +FILE_3_L180= try:reader.close() +FILE_3_L181= except BaseException:pass +FILE_3_L182= raise +FILE_4_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/legacy_engine.py +FILE_4_BYTES=59719 +FILE_4_SHA256=b6e79f087b9f1d21dac4f77a7b46b743299bbb85bc716e80d2ea67c2e038bcd7 +FILE_4_CONTEXT_LINE_COUNT=180 +FILE_4_L1=#!/usr/bin/python3 +FILE_4_L2=import ast, base64, datetime as dt, hashlib, io, json, os, pathlib, random, re, select, shlex, shutil, stat, subprocess, sys, tarfile, tempfile, time +FILE_4_L3= +FILE_4_L4=PART_SIZE=536870912 +FILE_4_L5=RESERVE_BYTES=536870912 +FILE_4_L6=STATE_ROOT=pathlib.Path('/var/lib/homelab-backup-v2') +FILE_4_L7=CONFIG_ROOT=pathlib.Path('/etc/homelab-backup-v2') +FILE_4_L8=TARGETS_PATH=CONFIG_ROOT/'targets.json' +FILE_4_L9=WORK_ROOT=pathlib.Path('/mnt/staging/homelab-backup-v2') +FILE_4_L10=ENGINE_PATH=pathlib.Path('/usr/local/sbin/homelab-backup-v2') +FILE_4_L11=RCLONE=shutil.which('rclone') or '/usr/bin/rclone' +FILE_4_L12=SSH=shutil.which('ssh') or '/usr/bin/ssh' +FILE_4_L13=SCP=shutil.which('scp') or '/usr/bin/scp' +FILE_4_L14=SSH_KEYSCAN=shutil.which('ssh-keyscan') or '/usr/bin/ssh-keyscan' +FILE_4_L30=P282_SHA='73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012' +FILE_4_L31=P265=pathlib.Path('/opt/homelab-backup-v2/releases/pve-producer-265r1/pve265.py') +FILE_4_L32=P265_SHA='cd2f634414a8e38a3789015aeca5a9b47821f77d7d1fa61dcdbdc866172963be' +FILE_4_L33=RESULT_CLASSES=('SUCCESS','DEFER','PARTIAL_VALID','FAIL') +FILE_4_L34=HISTORICAL_RELEASE_GRAPH=[ +FILE_4_L35= (pathlib.Path('/opt/homelab-backup-v2/releases/core-260r1/v2core.py'),'e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2'), +FILE_4_L36= (pathlib.Path('/opt/homelab-backup-v2/releases/transport-261r1/transport261.py'),'b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688'), +FILE_4_L51= pathlib.Path('/opt/homelab-backup-v2/releases/large-cloud-264r1'), +FILE_4_L52= pathlib.Path('/opt/homelab-backup-v2/releases/stream-263r1'), +FILE_4_L53= pathlib.Path('/opt/homelab-backup-v2/releases/transport-261r1'), +FILE_4_L54= pathlib.Path('/opt/homelab-backup-v2/releases/restore-262r1'), +FILE_4_L55= pathlib.Path('/opt/homelab-backup-v2/releases/core-260r1'), +FILE_4_L56= pathlib.Path('/opt/homelab-backup-v2/releases/vm-backup-283r1'), +FILE_4_L57=] +FILE_4_L58=_HISTORICAL_HELPER = r'''import importlib,importlib.util,json,pathlib,sys +FILE_4_L59=mode=sys.argv[1] +FILE_4_L60=if mode=='import_all': +FILE_4_L61= out={} +FILE_4_L74=''' +FILE_4_L75=os.umask(0o077) +FILE_4_L76= +FILE_4_L77=REMOTES=[ +FILE_4_L78= ('pve01-mail-01-crypt','pve01-mail-01'),('pve01-mail-02-crypt','pve01-mail-02'),('pve01-mail-03-crypt','pve01-mail-03'),('pve01-mail-04-crypt','pve01-mail-04'),('pve01-mail-05-crypt','pve01-mail-05'), +FILE_4_L79= ('pve02-mail-01-crypt','pve02-mail-01'),('pve02-mail-02-crypt','pve02-mail-02'),('pve02-mail-03-crypt','pve02-mail-03'),('pve02-mail-04-crypt','pve02-mail-04'), +FILE_4_L80= ('pve03-mail-01-crypt','pve03-mail-01'),('pve03-mail-02-crypt','pve03-mail-02'),('pve03-mail-03-crypt','pve03-mail-03'),('pve03-mail-04-crypt','pve03-mail-04')] +FILE_4_L81= +FILE_4_L82=class BackupError(RuntimeError): +FILE_4_L83= def __init__(self, code, detail=''): +FILE_4_L102= if not b: break +FILE_4_L103= h.update(b); n+=len(b) +FILE_4_L104= if remain is not None: remain-=len(b) +FILE_4_L105= if length is not None and n!=length: raise BackupError('LOCAL_PART_SHORT_READ') +FILE_4_L106= return h.hexdigest() +FILE_4_L107= +FILE_4_L108=def part_plan(n): +FILE_4_L109= if not isinstance(n,int) or n<=0: raise BackupError('PAYLOAD_SIZE_INVALID') +FILE_4_L110= out=[]; off=0; idx=1 +FILE_4_L111= while offtimeout: +FILE_4_L314= p.kill(); p.wait(); raise BackupError('RCLONE_CAT_TIMEOUT') +FILE_4_L315= b=p.stdout.read(1024*1024) +FILE_4_L316= if not b: break +FILE_4_L317= h.update(b); n+=len(b) +FILE_4_L318= p.stderr.read(); rc=p.wait() +FILE_4_L319= if rc!=0: raise BackupError('RCLONE_CAT_FAILED',str(rc)) +FILE_4_L320= if expected_bytes is not None and n!=expected_bytes: raise BackupError('RCLONE_CAT_SIZE_MISMATCH') +FILE_4_L321= return h.hexdigest(),n +FILE_4_L322= +FILE_4_L323=def rclone_cat_bytes(remote_path,max_bytes=4*1024*1024): +FILE_4_L324= p=run(rclone_base_args()+['cat',remote_path,'--contimeout','20s','--timeout','60s'],timeout=90) +FILE_4_L325= if len(p.stdout)>max_bytes: raise BackupError('RCLONE_SMALL_OBJECT_TOO_LARGE') +FILE_4_L326= return p.stdout +FILE_4_L327= +FILE_4_L328=def generation_id(logical_id): +FILE_4_L329= ts=dt.datetime.now(dt.timezone.utc).strftime('%Y%m%dT%H%M%SZ') +FILE_4_L330= return f'v2-{logical_id}-{ts}-{os.urandom(6).hex()}' +FILE_4_L331= +FILE_4_L332=def upload_and_seal(t,payload_path,source_meta): +FILE_4_L333= payload_path=pathlib.Path(payload_path); n=payload_path.stat().st_size +FILE_4_L334= psha=sha256_file(payload_path); reps,bad,healthy=allocator(n) +FILE_4_L335= gen=source_meta['generation']; rel=remote_generation_path(t['scope'],t['logical_id'],gen) +FILE_4_L336= if remote_exists_any(t['scope'],t['logical_id'],gen): raise BackupError('GENERATION_COLLISION') +FILE_4_L337= parts=part_plan(n) +FILE_4_L338= for p in parts: p['sha256']=sha256_file(payload_path,p['offset'],p['bytes']) +FILE_4_L339= manifest=make_manifest(t['scope'],t['logical_id'],gen,n,psha,parts,reps,source_meta) +FILE_4_L340= mb=canonical_json(manifest)+b'\n'; msha=sha256_bytes(mb) +FILE_4_L341= for rep in reps: +FILE_4_L342= base=rep['crypt']+':'+rel +FILE_4_L343= for part in parts: +FILE_4_L344= rp=base+'/parts/'+part['name'] +FILE_5_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/representative_driver.py +FILE_5_BYTES=4214 +FILE_5_SHA256=b3050a8f95e6d9826a4f0786cd3ca32012b6ab10486d10e4981b50b8a0affff2 +FILE_5_CONTEXT_LINE_COUNT=60 +FILE_5_L3= +FILE_5_L4=This module does not discover credentials, modify scheduler state, or implement the +FILE_5_L5=CT112 producer/cloud path. It routes CT112 to an explicitly supplied callback and +FILE_5_L6=isolates legacy engine state writes into a candidate-owned state directory. +FILE_5_L7=""" +FILE_5_L8=from __future__ import annotations +FILE_5_L9= +FILE_5_L10=import json +FILE_5_L11=import os +FILE_5_L12=from pathlib import Path +FILE_5_L13=from typing import Callable, Iterable, Mapping, Any +FILE_5_L14= +FILE_5_L15=LEGACY_IDS=( +FILE_5_L16= 'node-pve01', +FILE_5_L24=) +FILE_5_L25=CT112_ID='guest-pve01-lxc-112' +FILE_5_L26=REPRESENTATIVE_IDS=(CT112_ID,)+LEGACY_IDS +FILE_5_L27=CANDIDATE_ROOT=Path(__file__).resolve().parent +FILE_5_L28=STATE_ROOT=CANDIDATE_ROOT/'state' +FILE_5_L29= +FILE_5_L30=class ContractError(RuntimeError): +FILE_5_L31= pass +FILE_5_L36= raise ContractError('TARGET_LOGICAL_ID') +FILE_5_L37= return value +FILE_5_L38= +FILE_5_L39=def partition_targets(targets: Iterable[Mapping[str, Any]]): +FILE_5_L40= selected={} +FILE_5_L41= wanted=set(REPRESENTATIVE_IDS) +FILE_5_L42= for target in targets: +FILE_5_L62= with os.fdopen(fd,'wb',closefd=True) as f: +FILE_5_L63= f.write(data);f.flush();os.fsync(f.fileno()) +FILE_5_L64= os.replace(tmp,path) +FILE_5_L65= except BaseException: +FILE_5_L66= try: os.unlink(tmp) +FILE_5_L67= except FileNotFoundError: pass +FILE_5_L68= raise +FILE_5_L72= def write_state(logical_id,result,**kw): +FILE_5_L73= if logical_id not in LEGACY_IDS: +FILE_5_L74= raise ContractError('STATE_TARGET_FORBIDDEN') +FILE_5_L75= payload={'logical_id':logical_id,'result':result,'meta':kw,'schema_version':1} +FILE_5_L76= _atomic_json(root/(logical_id+'.json'),payload) +FILE_5_L77= return write_state +FILE_5_L78= +FILE_5_L79=def run_legacy_lane(engine, target: Mapping[str, Any]): +FILE_5_L97= raise ContractError('CT112_RESULT_SCHEMA') +FILE_5_L98= if result.get('logical_id')!=CT112_ID or result.get('status')!='OK': +FILE_5_L99= raise ContractError('CT112_RESULT_STATUS') +FILE_5_L100= if result.get('cloud_only_independent_restore_verified') is not True: +FILE_5_L101= raise ContractError('CT112_RESTORE_NOT_VERIFIED') +FILE_5_L102= if result.get('local_payload_files_remaining')!=0: +FILE_5_L103= raise ContractError('CT112_LOCAL_PAYLOAD_REMAINS') +FILE_5_L104= return result +FILE_5_L105= +FILE_5_L106=def run_representative(engine, targets, ct112_callback: Callable[[Mapping[str, Any]], Any]): +FILE_5_L107= if not callable(ct112_callback): +FILE_5_L108= raise ContractError('CT112_CALLBACK_REQUIRED') +FILE_5_L109= legacy,ct112=partition_targets(targets) +FILE_5_L110= results=[_validate_ct112_result(ct112_callback(ct112))] +FILE_5_L111= for target in legacy: +FILE_5_L112= results.append(run_legacy_lane(engine,target)) +FILE_6_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/wrapper.py +FILE_6_BYTES=5055 +FILE_6_SHA256=2b5eb4fc0f6f466a0d3e5a007af987092b6c17baa3ff9241a6afc2c2e97df1d5 +FILE_6_CONTEXT_LINE_COUNT=43 +FILE_6_L1=#!/usr/bin/env python3 +FILE_6_L2=from __future__ import annotations +FILE_6_L3=import errno,fcntl,importlib.util,os,sys +FILE_6_L4=from pathlib import Path +FILE_6_L5=ROOT=Path(__file__).resolve().parent +FILE_6_L6=CT_ID='guest-pve01-lxc-112' +FILE_6_L7=LEGACY_IDS=('node-pve01','md-relay','spb-ingress','us-netbird','router-netcraze','xf-codevipe','xf-zakrutim','control-bootstrap-v2') +FILE_6_L35= 'guest_type':'lxc','due_seconds':due,'enabled':True,'green1':True} +FILE_6_L36= +FILE_6_L37=def load_targets(): +FILE_6_L38= base=canonical_targets();return base+[_ct_descriptor(base)] +FILE_6_L39= +FILE_6_L40=def backup_target(t,green_verify=False): +FILE_6_L41= if not isinstance(t,dict) or t.get('logical_id')!=CT_ID: +FILE_6_L42= return _legacy_backup_target(t,green_verify) +FILE_6_L43= try: +FILE_6_L44= r=ct112.run_target(t,state_writer=legacy.write_state) +FILE_6_L45= return {'logical_id':CT_ID,'generation':r['generation'],'result':'SUCCESS','payload_bytes':r['payload_bytes'], +FILE_6_L46= 'replicas':2,'manifest_sha256':r['manifest_sha256'],'payload_sha256':r['payload_sha256']} +FILE_6_L47= except BaseException: +FILE_6_L48= try: legacy.write_state(CT_ID,'FAIL',reason='CT112_PIPELINE_FAILED') +FILE_6_L49= except BaseException: pass +FILE_6_L50= raise legacy.BackupError('CT112_PIPELINE_FAILED') from None +FILE_6_L51= +FILE_6_L52=legacy.load_targets=load_targets +FILE_6_L53=legacy.backup_target=backup_target +FILE_6_L54= +FILE_6_L55=def _legacy_reused_boundary_selftest(): +FILE_6_L56= if legacy.PART_SIZE!=536870912: raise RuntimeError('LEGACY_PART_SIZE') +FILE_6_L57= if len(legacy.REMOTES)!=13 or len({p for _,p in legacy.REMOTES})!=13: raise RuntimeError('LEGACY_REMOTES') +FILE_6_L58= d=legacy.default_targets() +FILE_6_L59= if not isinstance(d,list) or len(d)<28: raise RuntimeError('LEGACY_DEFAULT_TARGET_COUNT') +FILE_6_L60= want={'ct112',*LEGACY_IDS} +FILE_6_L67= +FILE_6_L68=def selftest(): +FILE_6_L69= _legacy_reused_boundary_selftest() +FILE_6_L70= base=canonical_targets(); eff=load_targets() +FILE_6_L71= if len(eff)!=29 or sum(1 for x in eff if x.get('logical_id')==CT_ID)!=1: raise RuntimeError('EFFECTIVE_TARGETS') +FILE_6_L72= legacy_rows,ct=driver.partition_targets(eff) +FILE_6_L73= if [x['logical_id'] for x in legacy_rows]!=list(LEGACY_IDS) or ct.get('vmid')!=112: raise RuntimeError('REPRESENTATIVE_PARTITION') +FILE_6_L74= if ct112.selftest() is not True: raise RuntimeError('CT112_HANDLER_SELFTEST') +FILE_6_L75= print('SELFTEST_REPLACEMENT486=PASS',flush=True);return 0 +FILE_6_L76= +FILE_7_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/CONTRACT.json +FILE_7_BYTES=2077 +FILE_7_SHA256=a414715f1b91b3701c52073709a80c38218f76e3961618e8af61489211e50c01 +FILE_7_CONTEXT_LINE_COUNT=1 +FILE_7_L1={"candidate":"pve-lxc-adapter-411r1","next_gate":"PVE_LXC_REAL_ENVIRONMENT_DRY_RUN","owner":{"MappedScratch":"pve270","capture_archive":"pve270","dump_argv":"pve265","pve282.old":"pve265","pve282.proven":"pve270","validate_mapping":"pve270"},"schema":1,"signatures":{"pve265":{"dump_argv":{"args":["job","executable","tmpdir","now"],"defaults":0,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonly":[],"vararg":null},"make_plan":{"args":["rows","configs","storage_rows","defaults","staging_free","now","node"],"defaults":1,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonly":[],"vararg":null},"produce":{"args":["job","work","tools","guard","seconds"],"defaults":1,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonly":[],"vararg":null},"validate_job":{"args":["job","now"],"defaults":0,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonly":[],"vararg":null}},"pve270":{"capture_archive":{"args":["argv","destination","cap","seconds","check"],"defaults":0,"kw_defaults":[],"kwarg":null,"kwonly":[],"posonl +FILE_8_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/PLAN265_REFERENCE.json +FILE_8_BYTES=7210 +FILE_8_SHA256=8d36e82080be93a8c929a9211191fa868166b9e7b6152c4c7689fa52b6720757 +FILE_8_CONTEXT_LINE_COUNT=1 +FILE_8_L1={"all_guest_coverage_eligible":true,"backup_executed":false,"capacity_policy":"four archive copies plus 4GiB; capped producer; actual compressed size still unknown","collected_at_utc":"2026-09-12T06:57:40.611329+00:00","full_host_coverage":false,"global_blockers":[],"global_defaults":{"blockers":[],"keys":[],"present":true,"sha256":"cf34c8e3812547954bb1d08b50212b6e148ee9cf2b629e895a843ed49d623b38"},"guest_membership_and_configs_stable":true,"jobs":[{"application_consistency_verified":false,"archive_cap_bytes":9516036916,"blockers":[],"cdrom_count":0,"cdrom_media_included":false,"compression_dependent":false,"config_sha256":"09957aacd41ba9eeaf5fd4eda13936d580c9082a46895433379aa9dd0bad5969","configured_archive_bound_bytes":9516036916,"configured_disk_bytes":8589934592,"configured_full_cycle_fits":true,"coverage_eligible":true,"disks":[{"backup_setting":"included_default_rootfs","class":"managed-volume","configured_size_bytes":8589934592,"slot":"rootfs","storage":"local-lvm"}],"global_def +FILE_9_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/PROVENANCE.json +FILE_9_BYTES=4310 +FILE_9_SHA256=303f44dc8e91c8e6a675f9ea406ce4b7532ed98440a9b6807fa593ede9d33254 +FILE_9_CONTEXT_LINE_COUNT=1 +FILE_9_L1={"anchors":{"SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-EXACT-CALL-GRAPH-PROOF-401R1":"840b3f1e8b023899c0554305a97af2d55dc71cf9","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-IMMUTABLE-ADAPTER-CANDIDATE-STAGE-FIXTURE-RCA-FIX-407R1":"0261475cdd35d3b762de0a29e46f3acd0b9af229","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-IMMUTABLE-ADAPTER-CANDIDATE-STAGE-RCA-FIX-403R1":"e3f753ce9cf95e21750e9ad7f9574cafc199b3ce","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERL270-HISTORICAL-TEST-RCA-404R1":"58f61ce62c3b6a57d8c5fc2465d2537d6728f9d5","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-EXEC-FS-RCA-409R1":"0db666a5a1f4796b567663adfc5568817e87efa5","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-NEED-TRACE-RCA-408R1":"44cdff7dab9e60aff1d69c12d2c32a4b26d04de8","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-PRODUCTION-VS-FIXTURE-RCA-406R1":"4c5836ff0ef73cb2ef306857be713383ba5e85f4","SUPPORT-260915-INFRA-DR-BACKUPV2-PVE-LXC-PERLPIN-ROOT-SUITE-STRUCTURED-RCA-410R1":"4673bfaf04877370bc4ac02fef37885eb82cb1eb +FILE_10_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/adapter.py +FILE_10_BYTES=2989 +FILE_10_SHA256=18eb29cef165371b48bcb78e1a456e1939e6eff253787218133162cc89ab9fa3 +FILE_10_CONTEXT_LINE_COUNT=30 +FILE_10_L1=#!/usr/bin/python3 +FILE_10_L2=import hashlib,inspect,json,pathlib,sys +FILE_10_L3=BASE=pathlib.Path(__file__).resolve().parent +FILE_10_L4=EXPECTED={ +FILE_10_L5='v2core.py':'e285bf9a5678d07dcbb90261f9f93e7826ff524e04cfec780bf5a3c4b0dc29f2', +FILE_10_L6='transport261.py':'b9b92f957bf441cb5d1702f4881897328e6c0338f8c3f6d41d27d9a349b37688', +FILE_10_L7='restore262.py':'56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1', +FILE_10_L8='stream263.py':'a83d03a86b93a855716658652dd073fcb281cd387fc6200fe4bc2a922f39ea16', +FILE_10_L9='nodeplan263.py':'db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060', +FILE_10_L10='large264.py':'5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a', +FILE_10_L19= return h.hexdigest() +FILE_10_L20=def verify_sources(): +FILE_10_L21= for name,want in EXPECTED.items(): +FILE_10_L22= p=BASE/name +FILE_10_L23= if not p.is_file() or p.is_symlink() or sha(p)!=want:raise RuntimeError('SOURCE_IDENTITY_'+name) +FILE_10_L24=def load_graph(): +FILE_10_L25= import guest266,pve265,pve270,pve282 +FILE_10_L26= if hasattr(guest266,'dump_argv'):raise RuntimeError('GUEST266_UNEXPECTED_DUMP_ARGV') +FILE_10_L27= if not hasattr(pve265,'dump_argv'):raise RuntimeError('PVE265_DUMP_ARGV_MISSING') +FILE_10_L28= if getattr(pve270,'old',None) is not pve265:raise RuntimeError('PVE270_OLD_NOT_PVE265') +FILE_10_L35=def describe(): +FILE_10_L36= _,p265,p270,p282=load_graph() +FILE_10_L37= funcs={'pve265.dump_argv':p265.dump_argv,'pve265.validate_job':p265.validate_job,'pve265.make_plan':p265.make_plan,'pve270.validate_mapping':p270.validate_mapping,'pve270.capture_archive':p270.capture_archive,'pve270.produce':p270.produce,'pve282.select112':p282.select112,'pve282.produce':p282.produce} +FILE_10_L38= return {'schema':1,'bundle':'pve-lxc-adapter-411r1','source_sha256':EXPECTED,'signatures':{k:str(inspect.signature(v)) for k,v in funcs.items()},'owner':{'dump_argv':'pve265','MappedScratch':'pve270','capture_archive':'pve270','validate_mapping':'pve270','pve282.old':'pve265','pve282.proven':'pve270'}} +FILE_10_L39=def main(): +FILE_10_L40= verify_sources(); mode=sys.argv[1] if len(sys.argv)>1 else 'selftest'; d=describe() +FILE_10_L41= if mode not in ('selftest','describe'):raise SystemExit(2) +FILE_10_L42= print(json.dumps(d,sort_keys=True,separators=(',',':'))) +FILE_10_L43= if mode=='selftest':print('ADAPTER_SELFTEST=PASS') +FILE_10_L44=if __name__=='__main__':main() +FILE_11_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/guest266.py +FILE_11_BYTES=7385 +FILE_11_SHA256=86433bc424d5fa5017c20f7a237c82874b02357a3283660d8a6b0039ef2bc868 +FILE_11_CONTEXT_LINE_COUNT=75 +FILE_11_L1="""266R1 bounded real-guest orchestration. Import has no side effects. +FILE_11_L2= +FILE_11_L3=One small LXC rootfs, no compression-dependent admission. No node-wide approval, +FILE_11_L4=no guest restore/start/delete, no cloud deletion. Archive CRC/SHA is not boot proof. +FILE_11_L5=""" +FILE_11_L6=import copy,os,time +FILE_11_L7=from pathlib import Path +FILE_11_L8=import v2core as core +FILE_11_L9=import transport261 as t +FILE_11_L10=import stream263 as stream +FILE_11_L11=import large264 as large +FILE_11_L12=import pve265 as producer +FILE_11_L13= +FILE_11_L14=MAX_CONFIGURED_BYTES=8*1024**3 +FILE_11_L15=RESERVE_BYTES=536870912 +FILE_11_L25= +FILE_11_L26=def choose(plan,now): +FILE_11_L27= rows=plan_rows(plan);t.need(plan.get('global_blockers')==[],'GLOBAL_DEFAULT_BLOCK') +FILE_11_L28= candidates=[] +FILE_11_L29= for job in rows: +FILE_11_L30= if not (job.get('type')=='lxc' and job.get('coverage_eligible') is True and +FILE_11_L31= job.get('blockers')==[] and job.get('compression_dependent') is False and +FILE_11_L35= disks[0].get('class')=='managed-volume' and disks[0].get('backup_setting') in ('included','included_default_rootfs')):continue +FILE_11_L36= n=job.get('configured_disk_bytes') +FILE_11_L37= if type(n) is not int or not 0=max(1024**3,chosen['configured_disk_bytes']),'SNAPSHOT_HEADROOM_UNPROVEN') +FILE_11_L45= return copy.deepcopy(chosen) +FILE_11_L46= +FILE_11_L47=def same_state(before,after,vmid): +FILE_11_L48= old=plan_rows(before);new=plan_rows(after) +FILE_11_L64= if type(task.get('endtime')) is int and task.get('status') not in (None,'RUNNING'):continue +FILE_11_L65= kind=task['type'];target=str(task.get('id','')) +FILE_11_L66= if kind in ('vncshell','vncterm','vncproxy','spiceproxy'):continue +FILE_11_L67= t.need(target!=str(vmid) and not any(x in kind.lower() for x in ('dump','restore','migrat','snapshot','clone','move_disk','resize')),'COMPETING_PVE_TASK') +FILE_11_L68= +FILE_11_L69=def check_result(result,job,work): +FILE_11_L70= t.need(type(result) is dict,'PRODUCER_RESULT_SCHEMA') +FILE_11_L80= finally:os.close(fd) +FILE_11_L81= return expected +FILE_11_L82= +FILE_11_L83=def run_flow(job,work,entries,cloud,reader,produce,state_guard,guard,compression_check,generation,say,clock=lambda:int(time.time())): +FILE_11_L84= """Caller provides real fresh PVE guards; simulation callbacks live in tests only.""" +FILE_11_L85= producer.validate_job(job,clock());core.validate_inventory(entries) +FILE_11_L86= t.need(job['type']=='lxc' and job.get('compression_dependent') is False,'FIRST_GUEST_POLICY') +FILE_11_L87= t.need(core.valid_name(generation),'GENERATION_ID') +FILE_11_L88= t.need(all(callable(f) for f in (produce,state_guard,guard,compression_check,say,clock)),'CALLBACKS_REQUIRED') +FILE_11_L89= work=Path(work);state_guard();guard() +FILE_11_L90= for sub in ('producer','upload','restore'):t.make_dir(work/sub,exclusive=True) +FILE_11_L91= say('PHASE','REAL_SINGLE_GUEST_SNAPSHOT') +FILE_11_L92= produced=produce(job,work/'producer') +FILE_11_L93= source=check_result(produced,job,work/'producer');state_guard();guard() +FILE_11_L94= t.create(work/'PRODUCER.json',core.canonical_bytes(produced)) +FILE_11_L95= say('PRODUCER_RC',0);say('PAYLOAD_BYTES',produced['size']);say('ARCHIVE_SHA256',produced['sha256']) +FILE_11_L96= logical='guest-%s-%s-%d'%(job['node'],job['type'],job['vmid']) +FILE_11_L97= say('PHASE','TWO_REPLICA_UPLOAD_AND_READBACK') +FILE_11_L98= up=large.upload(source,work/'upload',entries,cloud,generation,logical,RESERVE_BYTES,guard,clock=clock) +FILE_11_L99= t.need(up['manifest']['payload_bytes']==produced['size'] and up['manifest']['payload_sha256']==produced['sha256'],'MANIFEST_PRODUCER_MISMATCH') +FILE_11_L100= say('SEALED_QUORUM_VERIFIED','YES');mh=core.manifest_hash(up['manifest']);say('MANIFEST_SHA256',mh) +FILE_11_L101= say('PHASE','CLOUD_DISCOVERY_AND_ARCHIVE_RECONSTRUCTION') +FILE_11_L102= found=large.discover(reader,entries,logical,mh) +FILE_11_L103= restored=large.restore(work/'restore',reader,found) +FILE_11_L104= t.need(compression_check(work/'restore/restored.bin') is True,'RECONSTRUCTED_COMPRESSION_FAILED') +FILE_11_L105= state_guard();guard();say('PHASE','GUARDED_LOCAL_PAYLOAD_CLEANUP') +FILE_11_L106= count=large.cleanup(up,restored,reader,guard) +FILE_11_L107= return {'state':'SINGLE_GUEST_ARCHIVE_SEALED_AND_CLOUD_RECONSTRUCTED','vmid':job['vmid'],'type':job['type'],'node':job['node'], +FILE_11_L108= 'generation':generation,'logical_id':logical,'manifest_sha256':mh,'payload_bytes':produced['size'],'payload_sha256':produced['sha256'], +FILE_11_L109= 'replicas':up['manifest']['replicas'],'parts_count':len(up['manifest']['parts']),'archive_cloud_restore_verified':True, +FILE_11_L110= 'guest_restore_verified':False,'application_consistency_verified':False,'whole_node_backup':False,'local_payload_files_remaining':0, +FILE_11_L111= 'files_deleted':count,'accounts_scanned':found['accounts_scanned'],'config_sha256':job['config_sha256']} +FILE_12_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/large264.py +FILE_12_BYTES=16514 +FILE_12_SHA256=5cc4641fc35e4dd554495a36146da7dbbb7f142d1450e04cc5d3c89678b8497a +FILE_12_CONTEXT_LINE_COUNT=180 +FILE_12_L1="""264R1 streaming cloud adapter and synthetic boundary proof. +FILE_12_L2= +FILE_12_L3=No work on import. No guest/backup producer and no cloud deletion. Existing +FILE_12_L4=small-canary adapters are not modified. Only caller-owned local payload is removed, +FILE_12_L5=after two cloud seals and a separately cloud-discovered restore have been checked. +FILE_12_L6=""" +FILE_12_L7=import hashlib +FILE_12_L8=import os +FILE_12_L9=from pathlib import Path +FILE_12_L10=import selectors +FILE_12_L11=import signal +FILE_12_L12=import subprocess +FILE_12_L13=import time +FILE_12_L14=import v2core as core +FILE_12_L15=import transport261 as t +FILE_12_L16=import restore262 as r +FILE_12_L17=import stream263 as s +FILE_12_L18= +FILE_12_L19=BOUNDARY_BYTES=536870929 +FILE_12_L20=LOGICAL_ID='stream-canary-pve01' +FILE_12_L21= +FILE_12_L22= +FILE_12_L23=def process_stream(argv,env,size,digest,consume,seconds): +FILE_12_L24= """Drain both pipes, hashing stdout incrementally; never retain the whole part.""" +FILE_12_L25= t.need(type(argv) is list and argv and seconds>0,'STREAM_PROCESS_ARGUMENTS') +FILE_12_L26= sink=s.DigestSink(size,digest) +FILE_12_L27= p=subprocess.Popen(argv,stdin=subprocess.DEVNULL,stdout=subprocess.PIPE,stderr=subprocess.PIPE, +FILE_12_L38= remaining=deadline-time.monotonic();t.need(remaining>0,'STREAM_TIMEOUT') +FILE_12_L39= rc=p.wait(timeout=remaining);t.need(rc==0,'STREAM_PROCESS_RC_FAILED') +FILE_12_L40= result=sink.finish();complete=True;return result +FILE_12_L41= except subprocess.TimeoutExpired:raise t.Stop('STREAM_TIMEOUT') from None +FILE_12_L42= finally: +FILE_12_L43= if not complete or p.poll() is None: +FILE_12_L44= try:os.killpg(p.pid,signal.SIGKILL) +FILE_12_L53= '--cache-dir',str(Path(work)/'cache')]+args +FILE_12_L54= +FILE_12_L55= +FILE_12_L56=class CloudLarge(t.RcloneIO): +FILE_12_L57= """Reuse the existing crypt inventory/namespace guards, not its in-memory payload I/O.""" +FILE_12_L58= def call(self,args,limit): +FILE_12_L59= t.need(type(args) is list and args and args[0] in ('about','lsjson','copyto','cat'),'RCLONE_VERB_FORBIDDEN') +FILE_12_L60= t.need(type(limit) is int and 0<=limit<=core.MANIFEST_MAX_BYTES+1,'METADATA_BOUND') +FILE_12_L61= self.check();seconds=min(3600 if args[0]=='copyto' else 90,self.deadline-time.monotonic()) +FILE_12_L62= t.need(seconds>0,'CLOUD_DEADLINE');self.calls+=1 +FILE_12_L63= if args[0]=='copyto':self.writes_attempted+=1 +FILE_12_L64= rc,out=t.bounded_process(flags(self.executable,self.config_path,self.work,args),self.env,limit,seconds) +FILE_12_L65= self.check();return rc,out +FILE_12_L66= def put(self,remote,name,path): +FILE_12_L67= dest=self.address(remote,name) +FILE_12_L68= if name in ('manifest.json','SEALED.json'): +FILE_12_L69= return super().put(remote,name,path) +FILE_12_L70= part=next((p for p in self.manifest['parts'] if p['name']==name),None) +FILE_12_L71= t.need(part is not None,'PART_NOT_IN_MANIFEST') +FILE_12_L72= n,h,identity=s.hash_file(path,part['size']) +FILE_12_L73= t.need(n==part['size'] and h==part['sha256'],'UPLOAD_SOURCE_HASH') +FILE_12_L74= rc,_=self.call(['lsjson',dest,'--stat'],65536) +FILE_12_L75= t.need(rc in (3,4),'OBJECT_NOT_PROVEN_ABSENT') +FILE_12_L76= with s.file_handle(path,part['size']) as (_,size,seen): +FILE_12_L77= t.need(seen==identity,'UPLOAD_SOURCE_CHANGED') +FILE_12_L78= rc,_=self.call(['copyto',str(path),dest,'--immutable','--no-update-modtime'],65536) +FILE_12_L79= t.need(rc==0,'CLOUD_COPY_FAILED') +FILE_12_L80= def read_stream(self,remote,name,size,digest,consume): +FILE_12_L81= address=self.address(remote,name) +FILE_12_L82= part=next((p for p in self.manifest['parts'] if p['name']==name),None) +FILE_12_L83= t.need(part is not None and part['size']==size and part['sha256']==digest,'READBACK_EXPECTATION') +FILE_12_L84= self.check();seconds=min(3600,self.deadline-time.monotonic());t.need(seconds>0,'CLOUD_DEADLINE') +FILE_12_L85= self.calls+=1 +FILE_12_L86= out=process_stream(flags(self.executable,self.config_path,self.work,['cat',address,'--count',str(size+1)]), +FILE_12_L90= +FILE_12_L91=class ReadOnlyLarge(r.ReadOnlyCloud): +FILE_12_L92= """Metadata uses inherited bounded reads; this class has no upload API.""" +FILE_12_L93= def read_stream(self,remote,generation,name,size,digest,consume): +FILE_12_L94= t.need(type(size) is int and 00,'RESTORE_DEADLINE') +FILE_12_L98= self.calls+=1 +FILE_12_L99= out=process_stream(flags(self.executable,self.snapshot,self.work,['cat',address,'--count',str(size+1)]), +FILE_12_L100= self.env,size,digest,consume,seconds) +FILE_12_L116= +FILE_12_L117= +FILE_12_L118=def receipt(rep,m,whole,count): +FILE_12_L119= t.need(count==m['payload_bytes'] and whole.hexdigest()==m['payload_sha256'],'PAYLOAD_READBACK_MISMATCH') +FILE_12_L120= return {**rep,'generation':m['generation'],'manifest_sha256':core.manifest_hash(m), +FILE_12_L121= 'payload_sha256':whole.hexdigest(),'payload_bytes':count,'parts':m['parts'], +FILE_12_L122= 'method':'download-sha256','ok':True} +FILE_12_L123= +FILE_12_L124= +FILE_12_L125=def upload(source,work,entries,cloud,generation,logical_id,reserve,guard,clock=lambda:int(time.time())): +FILE_12_L126= """Upload and seal; retain all source bytes here. Cleanup is a later gated operation.""" +FILE_12_L127= work=Path(work);source=Path(source);guard();core.validate_inventory(entries) +FILE_12_L128= t.need(core.valid_name(generation) and core.valid_name(logical_id),'GENERATION_ID') +FILE_12_L129= partdir=work/'parts';t.make_dir(partdir,exclusive=True) +FILE_12_L130= record=s.split_file(source,partdir) +FILE_12_L131= t.create(work/'parts-record.json',core.canonical_bytes(record)) +FILE_12_L132= quotas={e['account_id']:cloud.quota(e['crypt_remote']) for e in entries} +FILE_12_L133= allocation=core.allocate(entries,quotas,record['payload_bytes'],reserve,clock()) +FILE_12_L134= m=core.make_manifest(generation,logical_id,record['payload_bytes'],record['payload_sha256'],record['parts'],allocation) +FILE_12_L135= mb=core.canonical_bytes(m);mh=core.manifest_hash(m);cloud.bind(m) +FILE_12_L136= t.create(work/'allocation.json',core.canonical_bytes(allocation));t.create(work/'manifest.json',mb) +FILE_12_L137= for rep in m['replicas']: +FILE_12_L138= guard();cloud.absent(rep['crypt_remote']) +FILE_12_L139= receipts=[] +FILE_12_L140= for rep in m['replicas']: +FILE_12_L141= guard();remote=rep['crypt_remote'];q=cloud.quota(remote) +FILE_12_L142= t.need(core.quota_reason(q,clock()) is None and q['free']>=allocation['required_free_bytes'],'SELECTED_ACCOUNT_CAPACITY_CHANGED') +FILE_12_L143= s.verify_parts(partdir,record) +FILE_12_L144= for part in m['parts']:cloud.put(remote,part['name'],partdir/part['name']) +FILE_12_L145= cloud.put(remote,'manifest.json',work/'manifest.json') +FILE_12_L146= t.need(cloud.get(remote,'manifest.json',len(mb)+1)==mb,'MANIFEST_READBACK_MISMATCH') +FILE_12_L147= whole=hashlib.sha256();count=0 +FILE_12_L148= for part in m['parts']: +FILE_12_L149= observed=cloud.read_stream(remote,part['name'],part['size'],part['sha256'],whole.update) +FILE_12_L150= t.need(observed=={'size':part['size'],'sha256':part['sha256']},'PART_READBACK_MISMATCH');count+=observed['size'] +FILE_12_L151= receipts.append(receipt(rep,m,whole,count));t.create(work/('receipt-%d.json'%len(receipts)),core.canonical_bytes(receipts[-1])) +FILE_12_L152= core.verify_receipts(m,receipts) +FILE_12_L153= seal={'schema_version':2,'state':'SEALED','generation':generation,'logical_id':logical_id, +FILE_12_L154= 'manifest_sha256':mh,'replicas':m['replicas'],'receipts':receipts, +FILE_12_L155= 'acceptance':'IDENTICAL_SEAL_REQUIRED_ON_BOTH_NAMED_ACCOUNTS'} +FILE_12_L156= sb=core.canonical_bytes(seal);t.need(len(sb)<=core.SEAL_MAX_BYTES,'SEAL_BOUND') +FILE_12_L157= r.validate_seal(sb,entries,generation,logical_id);t.create(work/'SEALED.json',sb) +FILE_12_L158= for rep in m['replicas']: +FILE_12_L159= guard();cloud.put(rep['crypt_remote'],'SEALED.json',work/'SEALED.json') +FILE_12_L160= for rep in m['replicas']: +FILE_12_L161= t.need(cloud.get(rep['crypt_remote'],'SEALED.json',len(sb)+1)==sb,'SEALED_QUORUM_REQUIRED') +FILE_12_L162= guard();s.verify_parts(partdir,record) +FILE_12_L163= t.create(work/'QUORUM_VERIFIED.json',core.canonical_bytes({'manifest_sha256':mh,'seal_sha256':t.sha(sb)})) +FILE_12_L164= return {'sealed':True,'manifest':m,'seal_bytes':sb,'parts_record':record,'source':str(source),'work':str(work)} +FILE_12_L165= +FILE_12_L166= +FILE_12_L167=def discover(cloud,entries,logical_id,expected_hash): +FILE_12_L168= """Find placement from all cloud catalogs, using only an external manifest digest.""" +FILE_12_L169= core.validate_inventory(entries) +FILE_12_L170= t.need(core.valid_name(logical_id) and core.valid_hash(expected_hash),'DISCOVERY_INPUT') +FILE_12_L171= listed={};generations=set() +FILE_12_L172= for e in entries: +FILE_12_L173= gs=cloud.list_generations(e['crypt_remote']) +FILE_12_L174= t.need(type(gs) is list and len(gs)<=r.MAX_GENERATIONS and all(core.valid_name(g) for g in gs),'GENERATION_LIST_INVALID') +FILE_12_L175= t.need(len(gs)==len(set(gs)),'GENERATION_LIST_DUPLICATE');listed[e['crypt_remote']]=set(gs);generations.update(gs) +FILE_12_L176= t.need(len(generations)<=r.MAX_GENERATIONS,'TOTAL_GENERATIONS_BOUND') +FILE_12_L177= candidates=[] +FILE_12_L178= for gen in sorted(generations): +FILE_12_L179= seen={} +FILE_12_L180= for e in entries: +FILE_12_L181= remote=e['crypt_remote'] +FILE_12_L182= if gen not in listed[remote]:continue +FILE_12_L183= b=cloud.get(remote,gen,'SEALED.json',core.SEAL_MAX_BYTES+1,missing_ok=True) +FILE_12_L184= if b is None:continue +FILE_12_L185= seal=r.validate_seal(b,entries,gen,logical_id) +FILE_12_L186= t.need(remote in {x['crypt_remote'] for x in seal['replicas']},'UNNAMED_SEAL_LOCATION');seen[remote]=(b,seal) +FILE_12_L187= if not seen:continue +FILE_12_L188= sb,seal=next(iter(seen.values())) +FILE_12_L189= if seal['manifest_sha256']!=expected_hash:continue +FILE_12_L190= named={x['crypt_remote'] for x in seal['replicas']} +FILE_12_L191= t.need(set(seen)==named and all(b==sb for b,_ in seen.values()),'IDENTICAL_TWO_SEALS_REQUIRED') +FILE_12_L192= blobs=[] +FILE_12_L193= for rep in seal['replicas']: +FILE_12_L194= b=cloud.get(rep['crypt_remote'],gen,'manifest.json',core.MANIFEST_MAX_BYTES+1) +FILE_12_L195= t.need(type(b) is bytes and t.sha(b)==expected_hash,'MANIFEST_DIGEST_MISMATCH') +FILE_12_L196= m=r.strict_json(b,core.MANIFEST_MAX_BYTES);core.validate_manifest(m) +FILE_12_L197= t.need(m['generation']==gen and m['logical_id']==logical_id and m['replicas']==seal['replicas'],'MANIFEST_PLACEMENT') +FILE_12_L198= t.need(core.canonical_bytes(m)==b,'MANIFEST_CANONICAL_BYTES');core.verify_receipts(m,seal['receipts']);blobs.append(b) +FILE_12_L199= t.need(len(blobs)==2 and blobs[0]==blobs[1],'TWO_MANIFEST_COPIES_REQUIRED') +FILE_12_L200= candidates.append({'manifest':m,'seal_bytes':sb,'accounts_scanned':len(listed)}) +FILE_12_L201= t.need(len(candidates)==1,'EXACTLY_ONE_ACCEPTED_GENERATION_REQUIRED') +FILE_12_L202= return candidates[0] +FILE_12_L203= +FILE_12_L204= +FILE_12_L205=def fresh_seals(cloud,discovery): +FILE_12_L206= m=discovery['manifest'];core.validate_manifest(m);sb=discovery['seal_bytes'] +FILE_12_L207= for rep in m['replicas']: +FILE_12_L208= t.need(cloud.get(rep['crypt_remote'],m['generation'],'SEALED.json',len(sb)+1)==sb,'SEAL_CHANGED_DURING_RESTORE') +FILE_12_L209= +FILE_12_L210= +FILE_12_L211=def download_part(cloud,rep,m,part,path,whole): +FILE_12_L212= path=Path(path);parent=t.open_dir(path.parent);fd=None +FILE_12_L213= try: +FILE_12_L214= fd=os.open(path.name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600,dir_fd=parent) +FILE_12_L215= def consume(block):whole.update(block);s.write_all(fd,block) +FILE_12_L216= result=cloud.read_stream(rep['crypt_remote'],m['generation'],part['name'],part['size'],part['sha256'],consume) +FILE_13_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/nodeplan263.py +FILE_13_BYTES=4603 +FILE_13_SHA256=db5c16ee24d25aa1694530332d3190ab7cc5d4f8eb69067d025e27ad5c995060 +FILE_13_CONTEXT_LINE_COUNT=23 +FILE_13_L1="""Pure, allowlisted PVE metadata projection. Does not execute any command.""" +FILE_13_L2=from decimal import Decimal,InvalidOperation +FILE_13_L3=import hashlib +FILE_13_L4=import re +FILE_13_L5=import v2core as core +FILE_13_L6= +FILE_13_L7=class PlanError(ValueError):pass +FILE_13_L8= +FILE_13_L15= if type(value) is not str or not re.fullmatch(r'\d+(?:\.\d+)?[KMGTPE]?',value):return None +FILE_13_L16= try: +FILE_13_L17= suffix=value[-1] if value[-1] in 'KMGTPE' else '' +FILE_13_L18= base=Decimal(value[:-1] if suffix else value) +FILE_13_L19= b=base*(1024**('KMGTPE'.index(suffix)+1) if suffix else 1) +FILE_13_L20= return int(b) if b==int(b) and 0time.monotonic(),'PRODUCER_TIMEOUT') +FILE_14_L175= try:rc=p.wait(timeout=deadline-time.monotonic()) +FILE_14_L176= except subprocess.TimeoutExpired:raise PVEError('PRODUCER_TIMEOUT') from None +FILE_14_L177= need(rc==0,'PRODUCER_RC_FAILED_PARTIAL_RETAINED');need(total>0,'EMPTY_ARCHIVE');check() +FILE_14_L178= st=os.fstat(fd);named=os.stat(dest.name,dir_fd=parent,follow_symlinks=False) +FILE_14_L179= need(st.st_dev==initial.st_dev and st.st_ino==initial.st_ino and t.ident(st)==t.ident(named) and +FILE_14_L180= st.st_nlink==1 and st.st_size==total,'PRODUCER_FILE_IDENTITY_CHANGED') +FILE_14_L200= """Future explicit execution API. Caller must recheck full plan/coverage in guard. +FILE_14_L201= +FILE_14_L202= Compression CRC and SHA are verified; guest bootability remains unproved. +FILE_14_L203= This function has no cloud/seal/delete operations and returns a readonly artifact. +FILE_14_L204= """ +FILE_14_L205= need(callable(guard),'GUARD_REQUIRED');guard();validate_job(job,int(time.time()));validate_tools(tools) +FILE_14_L206= work=Path(work);parent=stream.private_dir(work) +FILE_14_L209= tmp=work/'tmp';t.make_dir(tmp,exclusive=True) +FILE_14_L210= dest=work/('guest-%d.%s.zst'%(job['vmid'],'tar' if job['type']=='lxc' else 'vma')) +FILE_14_L211= def free_guard(): +FILE_14_L212= v=os.statvfs(work);need(v.f_frsize*v.f_bavail>=LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED') +FILE_14_L213= free_guard() +FILE_14_L214= result=capture_archive(dump_argv(job,tools['vzdump']['path'],tmp,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard) +FILE_14_L215= guard();validate_tools(tools) +FILE_14_L219= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH') +FILE_14_L220= guard();validate_tools(tools) +FILE_14_L221= return {**result,'path':str(dest),'vmid':job['vmid'],'type':job['type'],'node':job['node'], +FILE_14_L222= 'config_sha256':job['config_sha256'],'compression_test_ok':True,'restored':False, +FILE_14_L223= 'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True} +FILE_15_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve270.py +FILE_15_BYTES=19595 +FILE_15_SHA256=97015277a4f3f8129c0fd11eb70c06293026366f1f8232a97feeed513a2dbcef +FILE_15_CONTEXT_LINE_COUNT=140 +FILE_15_L3=Does not modify installed pve265, old workspaces, staging permissions or guest config. +FILE_15_L4=Only explicit produce() executes vzdump. Private bounded stderr/exit metadata retained. +FILE_15_L5=""" +FILE_15_L6=import hashlib,json,os,re,secrets,selectors,signal,stat,subprocess,sys,time +FILE_15_L7=from pathlib import Path +FILE_15_L8=import v2core as core +FILE_15_L9=import transport261 as t +FILE_15_L10=import stream263 as stream +FILE_15_L11=import pve265 as old +FILE_15_L12=need=old.need +FILE_15_L13=MAX_STDERR=2*1024**2 +FILE_15_L14=EXPECTED_CFG='09957aacd41ba9eeaf5fd4eda13936d580c9082a46895433379aa9dd0bad5969' +FILE_15_L39=def live_gate(job,pvesh_path,pvesh_sha): +FILE_15_L40= need(job.get('vmid')==110 and job.get('type')=='lxc' and job.get('node')=='pve01' and job.get('config_sha256')==EXPECTED_CFG,'ONLY_ACCEPTED_CT110') +FILE_15_L41= vals=[] +FILE_15_L42= for part in ('config','status/current','snapshot'): +FILE_15_L43= need(t.sha(t.read_secure(Path(pvesh_path),16*1024**2)[0])==pvesh_sha,'PVESH_CHANGED') +FILE_15_L44= rc,b=t.bounded_process([str(pvesh_path),'get','/nodes/pve01/lxc/110/'+part,'--output-format','json'],dict(old.ENV),262144,30) +FILE_15_L45= need(rc==0,'CT_GATE_READ_FAILED') +FILE_15_L46= try:vals.append(json.loads(b)) +FILE_15_L47= except (ValueError,UnicodeError):raise old.PVEError('CT_GATE_JSON') from None +FILE_15_L48= validate_ct(*vals,job['config_sha256']) +FILE_15_L49= return True +FILE_15_L50= +FILE_15_L59= def __init__(self,path,alias): +FILE_15_L60= self.path=Path(path);self.alias=Path(alias);self.parent=None;self.fd=None +FILE_15_L61= need(self.path.name=='perl' and self.alias.name=='perl5.40.1' and self.path.parent==self.alias.parent,'PERL_PATH_POLICY') +FILE_15_L62= need(self.path.is_absolute() and '..' not in self.path.parts,'PERL_PATH_POLICY') +FILE_15_L63= try: +FILE_15_L64= self.parent=os.open('/',os.O_RDONLY|os.O_DIRECTORY) +FILE_15_L65= for part in self.path.parts[1:-1]: +FILE_15_L66= nxt=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=self.parent) +FILE_15_L67= os.close(self.parent);self.parent=nxt +FILE_15_L68= s=os.fstat(nxt) +FILE_15_L69= need(s.st_uid==0 and not s.st_mode&0o022,'PERL_PARENT_UNTRUSTED') +FILE_15_L75= need(os.pread(self.fd,4,0)==b'\x7fELF','PERL_ELF_REQUIRED') +FILE_15_L76= self.identity=t.ident(s)+(s.st_nlink,) +FILE_15_L77= self.sha256=self._digest();self.check() +FILE_15_L78= except BaseException: +FILE_15_L79= self.close();raise +FILE_15_L80= def _digest(self): +FILE_15_L81= s=os.fstat(self.fd);need(00,'PERL_TIMEOUT') +FILE_15_L123= rc=p.wait(timeout=left);self.check();return rc,bytes(out) +FILE_15_L124= except subprocess.TimeoutExpired:raise old.PVEError('PERL_TIMEOUT') from None +FILE_15_L125= finally: +FILE_15_L126= if p is not None: +FILE_15_L127= if p.poll() is None: +FILE_15_L150= for p,h in pins.items():need(t.sha(t.read_secure(Path(p),2*1024**2)[0])==h,'PVE_MAPPING_SOURCE_CHANGED') +FILE_15_L151= pin.check() +FILE_15_L152= try:d=json.loads(b) +FILE_15_L153= except (ValueError,UnicodeError):raise old.PVEError('PVE_IDMAP_JSON') from None +FILE_15_L154= return validate_mapping(d) +FILE_15_L155= +FILE_15_L156=def _parent(path): +FILE_15_L157= path=Path(path) +FILE_15_L158= need(path.is_absolute() and '..' not in path.parts,'TEMP_PARENT_PATH') +FILE_15_L159= fd=os.open('/',os.O_RDONLY|os.O_DIRECTORY) +FILE_15_L160= try: +FILE_15_L161= for i,part in enumerate(path.parts[1:]): +FILE_15_L162= nxt=os.open(part,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=fd);os.close(fd);fd=nxt +FILE_15_L163= s=os.fstat(fd);last=i==len(path.parts)-2 +FILE_15_L164= need(s.st_uid==0,'TEMP_PARENT_OWNER') +FILE_15_L165= need(not s.st_mode&0o022 or last and s.st_mode&stat.S_ISVTX,'TEMP_PARENT_WRITABLE') +FILE_15_L166= result=fd;fd=None;return result +FILE_15_L170= +FILE_15_L171=class MappedScratch: +FILE_15_L172= """Root-owned, mapped-GID traverse-only, no access for others. No recursive cleanup.""" +FILE_15_L173= def __init__(self,gid,base='/var/tmp'): +FILE_15_L174= need(type(gid) is int and gid==100000,'TEMP_GID_POLICY') +FILE_15_L175= self.parent=_parent(base);self.fd=None;self.gid=gid +FILE_15_L176= self.name='homelab-vzdump-268-'+secrets.token_hex(16);self.path=Path(base)/self.name +FILE_15_L177= try: +FILE_15_L178= os.mkdir(self.name,0o700,dir_fd=self.parent) +FILE_15_L179= self.fd=os.open(self.name,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW,dir_fd=self.parent) +FILE_15_L181= os.fchown(self.fd,0,gid);os.fchmod(self.fd,0o710) +FILE_15_L182= s=os.fstat(self.fd);self.identity=(s.st_dev,s.st_ino) +FILE_15_L183= os.fsync(self.parent);self.check() +FILE_15_L184= except BaseException: +FILE_15_L185= if self.fd is not None:os.close(self.fd);self.fd=None +FILE_15_L186= os.close(self.parent);self.parent=None +FILE_15_L187= raise +FILE_15_L199= name='access-probe';fd=os.open(name,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o644,dir_fd=self.fd) +FILE_15_L200= try:os.fchmod(fd,0o644);stream.write_all(fd,b'HOMELAB_ACCESS_PROBE\n');os.fsync(fd);identity=t.ident(os.fstat(fd)) +FILE_15_L201= finally:os.close(fd) +FILE_15_L202= positive='''import os,sys +FILE_15_L203=p,private=sys.argv[1:] +FILE_15_L204=assert open(p+'/access-probe','rb').read()==b'HOMELAB_ACCESS_PROBE\\n' +FILE_15_L205=for action in (lambda:os.listdir(p),lambda:os.open(p+'/forbidden',os.O_WRONLY|os.O_CREAT,0o600),lambda:os.open(private,os.O_RDONLY|os.O_DIRECTORY)): +FILE_15_L207= except PermissionError:continue +FILE_15_L208= raise SystemExit(31) +FILE_15_L209=''' +FILE_15_L210= negative='''import sys +FILE_15_L211=try:open(sys.argv[1]+'/access-probe','rb').read() +FILE_15_L212=except PermissionError:raise SystemExit(0) +FILE_15_L213=raise SystemExit(32) +FILE_15_L256= b=os.read(key.fileobj.fileno(),65536) +FILE_15_L257= if not b:sel.unregister(key.fileobj);continue +FILE_15_L258= if key.fileobj is p.stdout: +FILE_15_L259= need(total+len(b)<=cap,'ARCHIVE_CAP_REACHED_PARTIAL_RETAINED') +FILE_15_L260= stream.write_all(fd,b);digest.update(b);total+=len(b) +FILE_15_L261= else: +FILE_15_L262= err_total+=len(b);eh.update(b);chunk=b[:max(0,MAX_STDERR-err_kept)] +FILE_15_L263= if chunk:stream.write_all(efd,chunk);err_kept+=len(chunk) +FILE_15_L264= need(deadline>time.monotonic(),'PRODUCER_TIMEOUT') +FILE_15_L265= try:rc=p.wait(timeout=deadline-time.monotonic()) +FILE_15_L266= except subprocess.TimeoutExpired:raise old.PVEError('PRODUCER_TIMEOUT') from None +FILE_15_L267= need(rc==0,'PRODUCER_RC_FAILED_PARTIAL_RETAINED');need(total>0,'EMPTY_ARCHIVE');check() +FILE_15_L268= st=os.fstat(fd);named=os.stat(dest.name,dir_fd=parent,follow_symlinks=False) +FILE_15_L269= need(st.st_dev==initial.st_dev and st.st_ino==initial.st_ino and t.ident(st)==t.ident(named) and st.st_nlink==1 and st.st_size==total,'PRODUCER_FILE_IDENTITY_CHANGED') +FILE_15_L270= os.fchmod(fd,0o400);os.fsync(fd);os.fsync(parent);good=True +FILE_15_L306= scratch.probe(uid,work);emit('MAPPED_UID_ACCESS_AND_ISOLATION_PROBE','PASS') +FILE_15_L307= def free_guard(): +FILE_15_L308= scratch.check() +FILE_15_L309= for path,reserve,reason in ((work,old.LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED'),(scratch.path,256*1024**2,'PVE_TEMP_RESERVE_LOW')): +FILE_15_L310= v=os.statvfs(path);need(v.f_frsize*v.f_bavail>=reserve,reason) +FILE_15_L311= free_guard();guard();dest=work/'guest-110.tar.zst' +FILE_15_L312= result=capture_archive(old.dump_argv(job,tools['vzdump']['path'],scratch.path,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard) +FILE_15_L317= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH') +FILE_15_L318= guard();old.validate_tools(tools);scratch.close(empty_only=True);complete=True;emit('PVE_TEMP_EMPTY_REMOVED','YES') +FILE_15_L319= return {**result,'path':str(dest),'vmid':110,'type':'lxc','node':job['node'],'config_sha256':job['config_sha256'], +FILE_15_L320= 'compression_test_ok':True,'restored':False,'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True} +FILE_15_L321= finally: +FILE_15_L322= m=work/'PRODUCER_PROCESS.json' +FILE_15_L323= if m.exists(): +FILE_16_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/pve282.py +FILE_16_BYTES=6195 +FILE_16_SHA256=73c6cb7773eb8e82d1e7ed5ab4733b4afb88f74ae69c09125d0378d6e480e012 +FILE_16_CONTEXT_LINE_COUNT=58 +FILE_16_L1="""282R1 bounded CT112 adapter; proven 270 capture/temp/Perl implementation unchanged. +FILE_16_L2=Expected source hash must be supplied from the immutable-hash-bound 265 plan. +FILE_16_L3=No changes to source guest configuration; no source110 backup or guest deletion. +FILE_16_L4=""" +FILE_16_L5=import copy,json,os,time +FILE_16_L6=from pathlib import Path +FILE_16_L7=import v2core as core +FILE_16_L8=import transport261 as t +FILE_16_L9=import stream263 as stream +FILE_16_L10=import pve265 as old +FILE_16_L11=import pve270 as proven +FILE_16_L12=import guest266 as g +FILE_16_L13=need=old.need +FILE_16_L14=PerlPin=proven.PerlPin +FILE_16_L15=host_mapping=proven.host_mapping +FILE_16_L26= need(core.valid_hash(expected_config) and job.get('config_sha256')==expected_config,'CT112_PLAN_CONFIG_ANCHOR') +FILE_16_L27= return True +FILE_16_L28= +FILE_16_L29=def select112(current,baseline,now): +FILE_16_L30= rows=g.plan_rows(current);previous=g.plan_rows(baseline) +FILE_16_L31= by_id={j['vmid']:j for j in previous} +FILE_16_L32= need(TARGET_VMID in by_id,'CT112_MISSING_FROM_ACCEPTED265') +FILE_16_L33= g.same_state(baseline,current,TARGET_VMID) +FILE_16_L34= need({j['vmid'] for j in rows}==set(by_id),'ACCEPTED265_MEMBERSHIP_CHANGED') +FILE_16_L35= for row in rows: +FILE_16_L36= need(row['config_sha256']==by_id[row['vmid']]['config_sha256'],'ACCEPTED265_GUEST_CONFIG_CHANGED') +FILE_16_L37= selected=[j for j in rows if j['vmid']==TARGET_VMID] +FILE_16_L38= need(len(selected)==1,'CT112_NOT_UNIQUE') +FILE_16_L39= job=copy.deepcopy(selected[0]);old.validate_job(job,now) +FILE_16_L40= need(current.get('global_blockers')==[],'GLOBAL_DEFAULT_BLOCK') +FILE_16_L41= need(job.get('compression_dependent') is False and job.get('configured_full_cycle_fits') is True,'CT112_FULL_CYCLE_SPACE_UNPROVEN') +FILE_16_L42= disks=job.get('disks');need(type(disks) is list and len(disks)==1,'CT112_SINGLE_ROOTFS_REQUIRED') +FILE_16_L53=def live_gate(job,pvesh_path,pvesh_sha,expected_config): +FILE_16_L54= require_target(job,expected_config) +FILE_16_L55= vals=[] +FILE_16_L56= for part in ('config','status/current','snapshot'): +FILE_16_L57= need(t.sha(t.read_secure(Path(pvesh_path),16*1024**2)[0])==pvesh_sha,'PVESH_CHANGED') +FILE_16_L58= rc,b=t.bounded_process([str(pvesh_path),'get','/nodes/pve01/lxc/112/'+part,'--output-format','json'],dict(old.ENV),262144,30) +FILE_16_L59= need(rc==0,'CT112_GATE_READ_FAILED') +FILE_16_L60= try:vals.append(json.loads(b)) +FILE_16_L61= except (ValueError,UnicodeError):raise old.PVEError('CT112_GATE_JSON') from None +FILE_16_L62= validate_ct(*vals,expected_config) +FILE_16_L63= return True +FILE_16_L64= +FILE_16_L79= scratch.probe(uid,work);emit('MAPPED_UID_ACCESS_AND_ISOLATION_PROBE','PASS') +FILE_16_L80= def free_guard(): +FILE_16_L81= scratch.check() +FILE_16_L82= for path,reserve,reason in ((work,old.LOCAL_RESERVE,'STAGING_RESERVE_REACHED_PARTIAL_RETAINED'),(scratch.path,256*1024**2,'PVE_TEMP_RESERVE_LOW')): +FILE_16_L83= v=os.statvfs(path);need(v.f_frsize*v.f_bavail>=reserve,reason) +FILE_16_L84= free_guard();guard();dest=work/'guest-112.tar.zst' +FILE_16_L85= result=capture_archive(old.dump_argv(job,tools['vzdump']['path'],scratch.path,int(time.time())),dest,job['archive_cap_bytes'],seconds,free_guard) +FILE_16_L90= need(n==result['size'] and h==result['sha256'] and list(identity)==result['identity'],'PRODUCER_READBACK_MISMATCH') +FILE_16_L91= guard();old.validate_tools(tools);scratch.close(empty_only=True);complete=True;emit('PVE_TEMP_EMPTY_REMOVED','YES') +FILE_16_L92= return {**result,'path':str(dest),'vmid':112,'type':'lxc','node':job['node'],'config_sha256':job['config_sha256'], +FILE_16_L93= 'compression_test_ok':True,'restored':False,'state':'PRODUCED_COMPRESSION_CHECKED_NOT_CLOUD_SEALED','local_payload_retained':True} +FILE_16_L94= finally: +FILE_16_L95= m=work/'PRODUCER_PROCESS.json' +FILE_16_L96= if m.exists(): +FILE_17_PATH=/opt/homelab-backup-v2/releases/backup-v2-accepted-486r1/candidate411/restore262.py +FILE_17_BYTES=14701 +FILE_17_SHA256=56d1b961c8d5654d5e2ee2755268db46bd2202bca11e69788febf991cfaa79f1 +FILE_17_CONTEXT_LINE_COUNT=180 +FILE_17_L1="""262R1 bounded cloud-discovered recovery. No live action on import. +FILE_17_L2= +FILE_17_L3=Cloud access is strictly lsjson/cat. No source-local workspace/manifest/receipt +FILE_17_L4=is read. The external accepted manifest digest is a trust anchor, not placement. +FILE_17_L5=""" +FILE_17_L6=import hashlib +FILE_17_L7=import io +FILE_17_L8=import json +FILE_17_L9=import os +FILE_17_L10=from pathlib import Path +FILE_17_L11=import re +FILE_17_L12=import stat +FILE_17_L13=import tarfile +FILE_17_L14=import time +FILE_17_L15=import v2core as core +FILE_17_L16=import transport261 as t +FILE_17_L17= +FILE_17_L18=SOURCE_NAMES=frozenset(('etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md','etc/pve/31_HOMELAB_REFERENCE.md', +FILE_17_L19= 'usr/local/sbin/homelab-control-backup','etc/systemd/system/homelab-control-backup.service')) +FILE_17_L20=ALL_NAMES=SOURCE_NAMES|{'SOURCE_INDEX.json'} +FILE_17_L21=MAX_GENERATIONS=64 +FILE_17_L22=SEAL_KEYS={'schema_version','state','generation','logical_id','manifest_sha256','replicas','receipts','acceptance'} +FILE_17_L23= +FILE_17_L24=def strict_json(data,limit): +FILE_17_L25= t.need(type(data) is bytes and len(data)<=limit,'JSON_BOUND') +FILE_17_L29= t.need(k not in result,'JSON_DUPLICATE_KEY');result[k]=v +FILE_17_L30= return result +FILE_17_L31= def bad_constant(value):raise t.Stop('JSON_NONFINITE_NUMBER') +DEPENDENCY_PATH_COUNT=28 +DEPENDENCY_PATH_1=/opt/homelab-backup-v2/releases/core-260r1 +DEPENDENCY_PATH_2=/opt/homelab-backup-v2/releases/core-260r1/v2core.py +DEPENDENCY_PATH_3=/opt/homelab-backup-v2/releases/ct-backup-282r1 +DEPENDENCY_PATH_4=/opt/homelab-backup-v2/releases/ct-backup-282r1/guest266.py +DEPENDENCY_PATH_5=/opt/homelab-backup-v2/releases/ct-backup-282r1/pve282.py +DEPENDENCY_PATH_6=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_guest266.py +DEPENDENCY_PATH_7=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_perl270.py +DEPENDENCY_PATH_8=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_producer282.py +DEPENDENCY_PATH_9=/opt/homelab-backup-v2/releases/ct-backup-282r1/test_pve282.py +DEPENDENCY_PATH_10=/opt/homelab-backup-v2/releases/large-cloud-264r1 +DEPENDENCY_PATH_11=/opt/homelab-backup-v2/releases/large-cloud-264r1/large264.py +DEPENDENCY_PATH_12=/opt/homelab-backup-v2/releases/pve-producer-265r1 +DEPENDENCY_PATH_13=/opt/homelab-backup-v2/releases/pve-producer-265r1/PLAN.json +DEPENDENCY_PATH_14=/opt/homelab-backup-v2/releases/pve-producer-265r1/pve265.py +DEPENDENCY_PATH_15=/opt/homelab-backup-v2/releases/pve-producer-265r1/test_pve265.py +DEPENDENCY_PATH_16=/opt/homelab-backup-v2/releases/pve-retry-268r1 +DEPENDENCY_PATH_17=/opt/homelab-backup-v2/releases/pve-retry-270r1 +DEPENDENCY_PATH_18=/opt/homelab-backup-v2/releases/pve-retry-270r1/pve270.py +DEPENDENCY_PATH_19=/opt/homelab-backup-v2/releases/pve-retry-270r1/test_pve270.py +DEPENDENCY_PATH_20=/opt/homelab-backup-v2/releases/pve-run-266r1 +DEPENDENCY_PATH_21=/opt/homelab-backup-v2/releases/restore-262r1 +DEPENDENCY_PATH_22=/opt/homelab-backup-v2/releases/restore-262r1/restore262.py +DEPENDENCY_PATH_23=/opt/homelab-backup-v2/releases/stream-263r1 +DEPENDENCY_PATH_24=/opt/homelab-backup-v2/releases/stream-263r1/nodeplan263.py +DEPENDENCY_PATH_25=/opt/homelab-backup-v2/releases/stream-263r1/stream263.py +DEPENDENCY_PATH_26=/opt/homelab-backup-v2/releases/transport-261r1 +DEPENDENCY_PATH_27=/opt/homelab-backup-v2/releases/transport-261r1/transport261.py +DEPENDENCY_PATH_28=/opt/homelab-backup-v2/releases/vm-backup-283r1 +HAS_RCLONE_SOURCE=true +HAS_OBJECT_PATH_CONTRACT=true +DECISION=PASS_BACKUP213_EXACT_CLOUD_OBJECT_CONTRACT_CAPTURED +NEXT_GATE=BUILD_CORRECTED_CLOUD_GENERATION_RECOVERY +CLOUD_READS=0 +CLOUD_WRITES=0 +VPS_SSH_ATTEMPTS=0 +MESSENGERS214_CLOSED=false +MAJOR_STAGES_REMAINING=8 +NEWFI213_END=true -Quorum information ------------------- -Date: Sun Sep [PRIVATE_IP] 2026 -Quorum provider: corosync_votequorum -Nodes: 3 -Node ID: [PRIVATE_IP] -Ring ID: 1.130 -Quorate: Yes - -Votequorum information ----------------------- -Expected votes: 3 -Highest expected: 3 -Total votes: 3 -Quorum: 2 -Flags: Quorate - -Membership information ----------------------- - Nodeid Votes Name -[PRIVATE_IP] [PRIVATE_IP].11 (local) -[PRIVATE_IP] [PRIVATE_IP].13 -[PRIVATE_IP] [PRIVATE_IP].12 - -Membership information ----------------------- - Nodeid Votes Name - 1 1 pve01 (local) - 2 1 pve03 - 3 1 pve02 -[{"cpu":[PRIVATE_IP]904699,"disk":0,"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"qemu/100","maxcpu":4,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":[PRIVATE_IP],"name":"pvepro-prod","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve01","status":"running","template":0,"type":"qemu","uptime":4594,"vmid":100},{"cpu":0,"disk":0,"diskread":0,"diskwrite":0,"id":"qemu/101","maxcpu":4,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":0,"memhost":0,"name":"pvepro-staging-v1","netin":0,"netout":0,"node":"pve01","status":"stopped","template":0,"type":"qemu","uptime":0,"vmid":101},{"cpu":[PRIVATE_IP][PRIVATE_IP],"disk":[PRIVATE_IP],"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"lxc/110","maxcpu":1,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":0,"name":"dns1","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve01","status":"running","template":0,"type":"lxc","uptime":[PRIVATE_IP],"vmid":110},{"cpu":[PRIVATE_IP][PRIVATE_IP],"disk":[PRIVATE_IP],"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"lxc/111","maxcpu":1,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":0,"name":"dns2","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve02","status":"running","template":0,"type":"lxc","uptime":[PRIVATE_IP],"vmid":111},{"cpu":[PRIVATE_IP]157e-05,"disk":[PRIVATE_IP],"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"lxc/112","maxcpu":1,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":0,"name":"unbound1","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve01","status":"running","template":0,"type":"lxc","uptime":[PRIVATE_IP],"vmid":112},{"cpu":0,"disk":[PRIVATE_IP],"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"lxc/113","maxcpu":1,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":0,"name":"unbound2","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve02","status":"running","template":0,"type":"lxc","uptime":[PRIVATE_IP],"vmid":113},{"cpu":[PRIVATE_IP]27009,"disk":0,"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"qemu/130","maxcpu":4,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":[PRIVATE_IP],"name":"edge-vm","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve03","status":"running","template":0,"type":"qemu","uptime":[PRIVATE_IP],"vmid":130},{"cpu":[PRIVATE_IP]862343,"disk":0,"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"qemu/150","maxcpu":4,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":[PRIVATE_IP],"name":"snikket","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve01","status":"running","template":0,"type":"qemu","uptime":[PRIVATE_IP],"vmid":150},{"cpu":[PRIVATE_IP]094225,"disk":0,"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"qemu/160","maxcpu":4,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":[PRIVATE_IP],"name":"forum-prod","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve02","status":"running","template":0,"type":"qemu","uptime":[PRIVATE_IP],"vmid":160},{"cpu":[PRIVATE_IP]32693,"disk":0,"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"qemu/170","maxcpu":4,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":[PRIVATE_IP],"name":"core-apps","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve01","status":"running","template":0,"type":"qemu","uptime":[PRIVATE_IP],"vmid":170},{"cpu":[PRIVATE_IP]14229,"disk":0,"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"qemu/171","maxcpu":4,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":[PRIVATE_IP],"name":"monitoring","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve01","status":"running","template":0,"type":"qemu","uptime":[PRIVATE_IP],"vmid":171},{"cpu":[PRIVATE_IP]682815,"disk":0,"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"qemu/190","maxcpu":4,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":[PRIVATE_IP],"name":"homelab-ops-worker","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve03","status":"running","template":0,"type":"qemu","uptime":[PRIVATE_IP],"vmid":190},{"cpu":[PRIVATE_IP]872932,"disk":0,"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"qemu/210","maxcpu":2,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":[PRIVATE_IP],"name":"newfi-prod","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve01","status":"running","template":0,"type":"qemu","uptime":[PRIVATE_IP],"vmid":210},{"cpu":[PRIVATE_IP]593234,"disk":0,"diskread":[PRIVATE_IP],"diskwrite":[PRIVATE_IP],"id":"qemu/211","maxcpu":2,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"memhost":[PRIVATE_IP],"name":"newfi-staging","netin":[PRIVATE_IP],"netout":[PRIVATE_IP],"node":"pve03","status":"running","template":0,"type":"qemu","uptime":[PRIVATE_IP],"vmid":211},{"cpu":0,"disk":0,"diskread":0,"diskwrite":0,"id":"qemu/9130","maxcpu":4,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":0,"memhost":0,"name":"edge-cold-standby","netin":0,"netout":0,"node":"pve02","status":"stopped","template":0,"type":"qemu","uptime":0,"vmid":9130},{"cgroup-mode":2,"cpu":[PRIVATE_IP]64639,"disk":[PRIVATE_IP],"id":"node/pve01","level":"","maxcpu":24,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"node":"pve01","status":"online","type":"node","uptime":[PRIVATE_IP]},{"cgroup-mode":2,"cpu":[PRIVATE_IP]6608,"disk":[PRIVATE_IP],"id":"node/pve02","level":"","maxcpu":4,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"node":"pve02","status":"online","type":"node","uptime":[PRIVATE_IP]},{"cgroup-mode":2,"cpu":[PRIVATE_IP]39373,"disk":[PRIVATE_IP],"id":"node/pve03","level":"","maxcpu":8,"maxdisk":[PRIVATE_IP],"maxmem":[PRIVATE_IP],"mem":[PRIVATE_IP],"node":"pve03","status":"online","type":"node","uptime":[PRIVATE_IP]},{"content":"iso,import,vztmpl,backup","disk":[PRIVATE_IP],"id":"storage/pve01/local","maxdisk":[PRIVATE_IP],"node":"pve01","plugintype":"dir","shared":0,"status":"available","storage":"local","type":"storage"},{"content":"iso,import,vztmpl,backup","disk":[PRIVATE_IP],"id":"storage/pve02/local","maxdisk":[PRIVATE_IP],"node":"pve02","plugintype":"dir","shared":0,"status":"available","storage":"local","type":"storage"},{"content":"iso,import,vztmpl,backup","disk":[PRIVATE_IP],"id":"storage/pve03/local","maxdisk":[PRIVATE_IP],"node":"pve03","plugintype":"dir","shared":0,"status":"available","storage":"local","type":"storage"},{"content":"images,rootdir","disk":[PRIVATE_IP],"id":"storage/pve01/local-lvm","maxdisk":[PRIVATE_IP],"node":"pve01","plugintype":"lvmthin","shared":0,"status":"available","storage":"local-lvm","type":"storage"},{"content":"images,rootdir","disk":[PRIVATE_IP],"id":"storage/pve02/local-lvm","maxdisk":[PRIVATE_IP],"node":"pve02","plugintype":"lvmthin","shared":0,"status":"available","storage":"local-lvm","type":"storage"},{"content":"images,rootdir","disk":[PRIVATE_IP],"id":"storage/pve03/local-lvm","maxdisk":[PRIVATE_IP],"node":"pve03","plugintype":"lvmthin","shared":0,"status":"available","storage":"local-lvm","type":"storage"},{"id":"network/pve01/zone/localnetwork","network":"localnetwork","network-type":"zone","node":"pve01","status":"ok","type":"network"},{"id":"network/pve02/zone/localnetwork","network":"localnetwork","network-type":"zone","node":"pve02","status":"ok","type":"network"},{"id":"network/pve03/zone/localnetwork","network":"localnetwork","network-type":"zone","node":"pve03","status":"ok","type":"network"}] -Name Type Status Total (KiB) Used (KiB) Available (KiB) % -local dir active [PRIVATE_IP] [PRIVATE_IP] [PRIVATE_IP] 37.18% -local-lvm lvmthin active [PRIVATE_IP] [PRIVATE_IP] [PRIVATE_IP] 15.53% - UNIT LOAD ACTIVE SUB DESCRIPTION -● homelab-app-cloud-quorum-queue.service loaded failed failed Homelab canonical application cloud quorum backup queue -● homelab-backup-retention-gc.service loaded failed failed Homelab owner-aware cloud retention GC -● homelab-vm-cloud-quorum-queue.service loaded failed failed Homelab canonical VM/CT cloud quorum backup queue - -Legend: LOAD → Reflects whether the unit definition was properly loaded. - ACTIVE → The high-level unit activation state, i.e. generalization of SUB. - SUB → The low-level unit activation state, values depend on unit type. - -3 loaded units listed. -NEXT LEFT LAST PASSED UNIT ACTIVATES -Sun [PRIVATE_IP] 05:17:09 MSK 6s Sun [PRIVATE_IP] 05:16:07 MSK 54s ago homelab-router-watchdog.timer homelab-router-watchdog.service -Sun [PRIVATE_IP] 05:17:36 MSK 33s Sun [PRIVATE_IP] 05:12:13 MSK 4min 48s ago homelab-health-metrics.timer homelab-health-metrics.service -Sun [PRIVATE_IP] 05:17:40 MSK 38s Sun [PRIVATE_IP] 05:16:40 MSK 21s ago homelab-private-vpn-hosts-health.timer homelab-private-vpn-hosts-health.service -Sun [PRIVATE_IP] 05:18:16 MSK 1min 13s Sun [PRIVATE_IP] 05:03:16 MSK 13min ago prometheus-node-exporter-apt.timer prometheus-node-exporter-apt.service -Sun [PRIVATE_IP] 05:19:50 MSK 2min 47s Sun [PRIVATE_IP] 05:14:50 MSK 2min 12s ago homelab-smartctl-textfile.timer homelab-smartctl-textfile.service -Sun [PRIVATE_IP] 05:19:56 MSK 2min 53s Sun [PRIVATE_IP] 04:46:58 MSK 30min ago homelab-reference-refresh.timer homelab-reference-refresh.service -Sun [PRIVATE_IP] 05:19:58 MSK 2min 55s Sun [PRIVATE_IP] 05:14:58 MSK 2min 4s ago netbird-peers-health.timer netbird-peers-health.service -Sun [PRIVATE_IP] 05:22:58 MSK 5min Sun [PRIVATE_IP] 05:07:58 MSK 9min ago homelab-disk-space-health.timer homelab-disk-space-health.service -Sun [PRIVATE_IP] 05:22:58 MSK 5min Sun [PRIVATE_IP] 05:07:58 MSK 9min ago prometheus-node-exporter-nvme.timer prometheus-node-exporter-nvme.service -Sun [PRIVATE_IP] 05:23:16 MSK 6min Sun [PRIVATE_IP] 05:13:16 MSK 3min 46s ago skladchik-reports-monitor-supervisor.timer skladchik-reports-monitor-supervisor.service -Sun [PRIVATE_IP] 05:25:58 MSK 8min Sun [PRIVATE_IP] 05:10:58 MSK 6min ago homelab-pve03-staging-capacity-health.timer homelab-pve03-staging-capacity-health.service -Sun [PRIVATE_IP] 05:30:00 MSK 12min Sun [PRIVATE_IP] 05:15:05 MSK 1min 57s ago homelab-incident-journal.timer homelab-incident-journal.service -Sun [PRIVATE_IP] 05:30:29 MSK 13min Sun [PRIVATE_IP] 05:16:16 MSK 46s ago homelab-forum-snuffleupagus-health.timer homelab-forum-snuffleupagus-health.service -Sun [PRIVATE_IP] 05:31:02 MSK 13min Sun [PRIVATE_IP] 05:15:13 MSK 1min 48s ago homelab-external-probe-vps-health.timer homelab-external-probe-vps-health.service -Sun [PRIVATE_IP] 05:31:19 MSK 14min Sun [PRIVATE_IP] 05:16:16 MSK 46s ago homelab-duty-admin-v2.timer homelab-duty-admin-v2.service -Sun [PRIVATE_IP] 05:32:11 MSK 15min Sun [PRIVATE_IP] 05:04:57 MSK 12min ago homelab-mkdocs-auto-refresh.timer homelab-mkdocs-auto-refresh.service -Sun [PRIVATE_IP] 05:36:01 MSK 18min Sun [PRIVATE_IP] 05:06:11 MSK 10min ago homelab-backup-audit.timer homelab-backup-audit.service -Sun [PRIVATE_IP] 05:56:19 MSK 39min Sat [PRIVATE_IP] 23:50:32 MSK 5h 26min ago homelab-evidence-root.timer homelab-evidence-root.service -Sun [PRIVATE_IP] 06:10:28 MSK 53min Sat [PRIVATE_IP] 06:15:16 MSK 23h ago apt-daily-upgrade.timer apt-daily-upgrade.service -Sun [PRIVATE_IP] 07:09:03 MSK 1h 52min Sun [PRIVATE_IP] 00:59:30 MSK 4h 17min ago homelab-mailru-trash-gc.timer homelab-mailru-trash-gc.service -Sun [PRIVATE_IP] 07:11:14 MSK 1h 54min Sat [PRIVATE_IP] 07:18:31 MSK 21h ago homelab-drift-check.timer homelab-drift-check.service -Sun [PRIVATE_IP] 07:34:28 MSK 2h 17min Sat [PRIVATE_IP] 07:38:42 MSK 21h ago homelab-service-registry-check.timer homelab-service-registry-check.service -Sun [PRIVATE_IP] 07:40:18 MSK 2h 23min Sun [PRIVATE_IP] 07:35:56 MSK 6 days ago homelab-rotating-boot-drill.timer homelab-rotating-boot-drill.service -Sun [PRIVATE_IP] 07:46:29 MSK 2h 29min Sat [PRIVATE_IP] 07:40:16 MSK 21h ago homelab-dependency-map-check.timer homelab-dependency-map-check.service -Sun [PRIVATE_IP] 07:46:56 MSK 2h 29min Sat [PRIVATE_IP] 07:38:16 MSK 21h ago homelab-alerting-health.timer homelab-alerting-health.service -Sun [PRIVATE_IP] 07:57:31 MSK 2h 40min Sat [PRIVATE_IP] 07:56:58 MSK 21h ago homelab-desired-state-sync.timer homelab-desired-state-sync.service -Sun [PRIVATE_IP] 07:58:07 MSK 2h 41min Sat [PRIVATE_IP] 07:48:00 MSK 21h ago homelab-runbook-generate.timer homelab-runbook-generate.service -Sun [PRIVATE_IP] 07:58:31 MSK 2h 41min Sat [PRIVATE_IP] 07:56:58 MSK 21h ago homelab-duty-admin-report.timer homelab-duty-admin-report.service -Sun [PRIVATE_IP] 08:01:43 MSK 2h 44min Sat [PRIVATE_IP] 08:15:16 MSK 21h ago homelab-overall-health.timer homelab-overall-health.service -Sun [PRIVATE_IP] 08:06:27 MSK 2h 49min Sat [PRIVATE_IP] 11:53:32 MSK 17h ago man-db.timer man-db.service -Sun [PRIVATE_IP] 08:07:47 MSK 2h 50min Sat [PRIVATE_IP] 08:08:49 MSK 21h ago homelab-kuma-monitor-policy.timer homelab-kuma-monitor-policy.service -Sun [PRIVATE_IP] 08:19:26 MSK 3h 2min Sun [PRIVATE_IP] 03:55:58 MSK 1h 21min ago apt-daily.timer apt-daily.service -Sun [PRIVATE_IP] 08:24:01 MSK 3h 6min Sat [PRIVATE_IP] 08:19:39 MSK 20h ago homelab-final-readiness-gate.timer homelab-final-readiness-gate.service -Sun [PRIVATE_IP] 08:42:10 MSK 3h 25min Sat [PRIVATE_IP] 08:31:16 MSK 20h ago homelab-capacity-risk.timer homelab-capacity-risk.service -Sun [PRIVATE_IP] 08:46:34 MSK 3h 29min Sat [PRIVATE_IP] 08:41:46 MSK 20h ago homelab-secret-exposure-guard.timer homelab-secret-exposure-guard.service -Sun [PRIVATE_IP] 08:54:41 MSK 3h 37min Sat [PRIVATE_IP] 08:48:43 MSK 20h ago homelab-cluster-passport.timer homelab-cluster-passport.service -Sun [PRIVATE_IP] 09:01:11 MSK 3h 44min Sat [PRIVATE_IP] 08:51:16 MSK 20h ago homelab-golden-state-index.timer homelab-golden-state-index.service -Sun [PRIVATE_IP] 10:15:00 MSK 4h 57min Sat [PRIVATE_IP] 10:15:00 MSK 19h ago skladchik-reports-monitor-monthly-selftest-watch.timer skladchik-reports-monitor-monthly-selftest-watch.service -Sun [PRIVATE_IP] 10:35:58 MSK 5h 18min Sun [PRIVATE_IP] 04:35:58 MSK 41min ago homelab-vps-identity-audit.timer homelab-vps-identity-audit.service -Sun [PRIVATE_IP] 16:47:58 MSK 11h Sat [PRIVATE_IP] 16:47:58 MSK 12h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service -Mon [PRIVATE_IP] 00:00:00 MSK 18h Sun [PRIVATE_IP] 00:00:12 MSK 5h 16min ago dpkg-db-backup.timer dpkg-db-backup.service -Mon [PRIVATE_IP] 00:04:42 MSK 18h Mon [PRIVATE_IP] 00:10:39 MSK 6 days ago homelab-secret-sanity.timer homelab-secret-sanity.service -Mon [PRIVATE_IP] 00:07:36 MSK 18h Sun [PRIVATE_IP] 00:13:12 MSK 5h 3min ago homelab-evidence-catalog.timer homelab-evidence-catalog.service -Mon [PRIVATE_IP] 00:07:48 MSK 18h Sun [PRIVATE_IP] 00:02:02 MSK 5h 14min ago homelab-docker-health.timer homelab-docker-health.service -Mon [PRIVATE_IP] 00:09:14 MSK 18h Mon [PRIVATE_IP] 00:30:56 MSK 6 days ago fstrim.timer fstrim.service -Mon [PRIVATE_IP] 00:16:19 MSK 18h Sun [PRIVATE_IP] 00:01:46 MSK 5h 15min ago homelab-storage-capacity.timer homelab-storage-capacity.service -Mon [PRIVATE_IP] 00:17:22 MSK 19h Sun [PRIVATE_IP] 00:13:55 MSK 5h 3min ago homelab-quality-gate.timer homelab-quality-gate.service -Mon [PRIVATE_IP] 00:23:49 MSK 19h Sun [PRIVATE_IP] 00:04:32 MSK 5h 12min ago logrotate.timer logrotate.service -Mon [PRIVATE_IP] 02:49:53 MSK 21h Sun [PRIVATE_IP] 01:11:58 MSK 4h 5min ago pve-daily-update.timer pve-daily-update.service -Mon [PRIVATE_IP] 03:22:30 MSK 22h Sun [PRIVATE_IP] 03:34:58 MSK 1h 42min ago homelab-backup-retention-gc.timer homelab-backup-retention-gc.service -Sun [PRIVATE_IP] 03:10:17 MSK 6 days Sun [PRIVATE_IP] 03:10:58 MSK 2h 6min ago xfs_scrub_all.timer xfs_scrub_all.service -Sun [PRIVATE_IP] 03:10:23 MSK 6 days Sun [PRIVATE_IP] 03:10:12 MSK 2h 6min ago e2scrub_all.timer e2scrub_all.service -- - Sun [PRIVATE_IP] 04:57:13 MSK 19min ago homelab-backup-v2.timer homelab-backup-v2.service -- - - - prometheus-node-exporter-ipmitool-sensor.timer prometheus-node-exporter-ipmitool-sensor.service -- - - - prometheus-node-exporter-mellanox-hca-temp.timer prometheus-node-exporter-mellanox-hca-temp.service -- - - - prometheus-node-exporter-smartmon.timer prometheus-node-exporter-smartmon.service - -56 timers listed. -RUNTIME_MANIFEST_ID=df[PRIVATE_IP]ac9b5abd[PRIVATE_IP]b[PRIVATE_IP]d89496fb2bde[PRIVATE_IP] -EXPERIMENT_ID=5462ee[PRIVATE_IP]ebe[PRIVATE_IP]feb2ad[PRIVATE_IP]1ad87bbac0b8108 -COLLECTION_START_AT=[PRIVATE_IP]T[PRIVATE_IP]:00 -FIRST_COMPLETE_D1_CLOSE=[PRIVATE_IP]T[PRIVATE_IP]:00 -PRODUCTION_LAUNCH_ALLOWED=False -EXECUTION_AUTHORITY=False -RISK_AUTHORITY=False -STAGE_A_HEALTH=AWAITING_PRIMARY_FINALITY -FORENSIC_PREVIOUS_EPOCH_PRESERVED=YES - -## NEWFI_TASK6_AUTH_CHECKPOINT_V[PRIVATE_IP] -- Scope: historical accepted AUTH test, not a current SMTP connection test. -- Newfi approved SMTP account/sender: aleisaev@yandex.ru. -- Endpoint used by accepted test: smtp.yandex.ru:465 with certificate verification; single PLAIN initial response accepted with code 235. -- Evidence: incident ledger ### NEWFI_TASK6_YANDEX_AUTH_V2_OBSERVATION and ### NEWFI_TASK6_AUTH_PROOF_CLOSE_V1. -- No password file written, no application configuration changed, no mail sent by the accepted AUTH test. -- Persistent transport installation and application delivery are not proven by this checkpoint. Task6 is not complete. -- AUTH challenge and missing-file incidents closed; launch/session incident remains OPEN with original cause unproven. -- This update does not change VM211, VM160, routing, board state or Git. -- Other incidents and infrastructure health were not assessed. - - -## NEWFI_PRIVATE_STRUCTURE92_WRONG_MARKER_SCOPE_[PRIVATE_IP] CLOSURE -- Status: CLOSED -- RCA: NEWFI92 inspected the pve01 NEWFI90 attempt marker, which had not been finalized because the outer shell exited immediately after the guest returned RC3 following its successful rollback. The guest marker contained the true final rollback state. -- Correction: NEWFI93 required both the guest FINISHED/ROLLED_BACK record and a fresh exact demo-state readback before reconciling the outer NEWFI90 marker. NEWFI92 itself never reserved an attempt or mutated state. - - -## NEWFI_PRIVATE_NEWFI95_NESTED_TRIPLE_QUOTE_PARSE_[PRIVATE_IP] -- Status: OPEN -- Command: NEWFI95 -- Symptom: after the content-structure precheck passed, the pve03 Python wrapper failed to parse at the PHP line beginning with $root. No Portal discovery or application mutation was reached. -- RCA: an outer raw triple-single-quoted guest Python payload contained an inner raw triple-single-quoted PHP payload. The inner delimiter terminated the outer Python string, causing PHP source to be parsed as Python. -- Safety: NEWFI95 was read-only, created no attempt marker, and did not reach the intended nested guest execution. Do not replay NEWFI95. - - -## NEWFI_PRIVATE_NEWFI95_NESTED_TRIPLE_QUOTE_PARSE_[PRIVATE_IP] CLOSURE -- Status: CLOSED -- RCA: an inner triple-single-quoted PHP literal terminated the outer triple-single-quoted Python guest payload in NEWFI95. The wrapper therefore failed parsing before the intended Portal discovery executed. -- Correction: NEWFI96 removed the nested Python payload layer entirely and passed one Python program directly to VM211 through qm guest exec stdin. The read-only Portal contract completed with QGA exit 0 and the implementation worktree remained unchanged. OUTPUT_END CHAT_OUTPUT_END