#!/usr/bin/env python3 """Reproducible source owner package; no installation or admission authority. Fixed runtime destinations only. Native ELF/signature and external policy inputs are separately pinned release inputs. Refuses overwriting an existing artifact. """ import hashlib import ast import io import json from pathlib import Path import sys import tarfile ROOT=Path(__file__).resolve().parents[1] sys.path.insert(0,str(ROOT/'scripts')) def modules(root): # Read the provisioner's declaration without executing privileged modules. tree=ast.parse((root/'scripts/provision-d09-lifecycle.py').read_text()) declared=None for node in tree.body: if isinstance(node,ast.Assign) and any(isinstance(t,ast.Name) and t.id=='MODULES' for t in node.targets): declared=ast.literal_eval(node.value) if not declared: raise ValueError('missing provisioner module declaration') names=set(declared) for pattern in ('c01_*.py','d09_*.py','host_*.py'): names.update(p.name for p in (root/'scripts').glob(pattern)) return sorted(names) def package(destination): files={} for name in modules(ROOT): files['usr/libexec/'+name]=(ROOT/'scripts'/name,0o644) if name.startswith(('c01_','host_')) or name=='runtime_owner.py': files['usr/lib/archipelago/'+name]=(ROOT/'scripts'/name,0o644) for source,target in {'d09-lifecycle-owner.py':'archipelago-d09-owner', 'd09-lifecycle-client.py':'archipelago-d09ctl','provision-d09-lifecycle.py':'archipelago-d09-provision', 'dashboard-public-guard.py':'archipelago-d09-public-guard'}.items(): files['usr/libexec/'+target]=(ROOT/'scripts'/source,0o755) for source,target in {'prepare-terminal-workspace.sh':'archipelago-d09-prepare-workspace', '60-terminal-workspace.conf':'archipelago-d09-terminal-workspace.conf'}.items(): files['usr/libexec/'+target]=(ROOT/'image-recipe/configs'/source,0o755 if source.endswith('.sh') else 0o644) # Pre-read all inputs so an absent helper cannot leave a partial artifact. payload={} for target,(source,mode) in files.items(): if source.is_symlink() or not source.is_file(): raise ValueError(f'not a regular package input: {source}') payload[target]=(source.read_bytes(),mode) manifest={} with tarfile.open(destination,'x') as archive: for target,(raw,mode) in sorted(payload.items()): manifest[target]=hashlib.sha256(raw).hexdigest() info=tarfile.TarInfo(target);info.mode=mode;info.uid=info.gid=info.mtime=0;info.size=len(raw) archive.addfile(info,io.BytesIO(raw)) raw=json.dumps(manifest,sort_keys=True,separators=(',',':')).encode() info=tarfile.TarInfo('D09-C01-SOURCE-SHA256.json');info.mode=0o444;info.size=len(raw) archive.addfile(info,io.BytesIO(raw)) return manifest if __name__=='__main__': if len(sys.argv)!=2: raise SystemExit('one new artifact pathname required') package(Path(sys.argv[1]))