"""Copy only an explicit reviewed source list into a new immutable release bundle.""" from __future__ import annotations import argparse import io import json import os import re import shutil import subprocess import tarfile import tempfile import time from datetime import UTC, datetime from pathlib import Path, PurePosixPath from release_update import (MAX_RELEASE_BYTES, SCHEMA, ReleaseError, confined, digest, durable_write, manifest_payload_sha256, read_bytes, source_path) from operator_runtime_closure import ( OPERATOR_COMPANION_SOURCES, OPERATOR_PACKAGE_PREFIX, OperatorRuntimeClosureError, build_operator_runtime_manifest, operator_package_runtime_sources, operator_runtime_requested, ) FRONTEND_DIST_PREFIX = "aureon-os-upstream/frontend/dist/" FRONTEND_DIST_INDEX = FRONTEND_DIST_PREFIX + "index.html" FRONTEND_SOURCE_ROOT = "aureon-os-upstream/frontend" FRONTEND_BUILD_SCHEMA = "aureon.frontend-reproducible-build.v1" FRONTEND_BUILD_RECIPE = ("npm ci --ignore-scripts --no-audit --no-fund", "npm run build") FRONTEND_REQUIRED_ENV = frozenset( {"VITE_REQUIRE_AUTH", "VITE_SUPABASE_URL", "VITE_SUPABASE_PUBLISHABLE_KEY"} ) FRONTEND_ALLOWED_ENV = FRONTEND_REQUIRED_ENV | frozenset( { "VITE_AUREON_MANIFEST_BASE", "VITE_NEWS_API_ENDPOINT", "VITE_NEWS_PROXY_URL", "VITE_NEXUS_WS_URL", "VITE_SUPPORT_PAYMENT_URLS", "VITE_SUPPORT_PROMPT_HOURS", "VITE_SUPPORT_SNOOZE_HOURS", } ) VM_RUNTIME_TRIGGER_SOURCES = frozenset( { "deployment/google-cloud/cloud_read_organ.py", "deployment/google-cloud/cloud_trade_receipt_collector.py", "deployment/google-cloud/cloud_order_intent_bridge.py", } ) TOKEN_BROKER_SOURCES = frozenset( { "deployment/google-cloud/aureon-cloud-token-broker.service", "deployment/google-cloud/vm_token_broker.py", "deployment/google-cloud/vm_workload_identity.py", } ) PUBLIC_BRIDGE_SOURCE = "deployment/google-cloud/public-entry/public_entry.py" PUBLIC_BRIDGE_UNIT_SOURCE = ( "deployment/google-cloud/public-entry/aureon-public-bridge.service" ) PUBLIC_BRIDGE_SOURCES = frozenset( {PUBLIC_BRIDGE_SOURCE, PUBLIC_BRIDGE_UNIT_SOURCE} ) BRIDGE_CERTIFICATE_RECEIPT_SCHEMA = ( "aureon.gcp.vm-public-bridge-certificate-receipt.v1" ) def _git(source: Path, *args: str) -> str: try: result = subprocess.run( ["git", "-C", str(source), *args], capture_output=True, text=True, timeout=30, check=False, ) except (OSError, subprocess.TimeoutExpired) as error: raise ReleaseError("source_git_identity_unavailable") from error if result.returncode != 0: raise ReleaseError("source_git_identity_unavailable") return result.stdout.strip() def _git_blob(source: Path, revision: str, relative: str) -> bytes: """Read exact committed bytes so bundle hashes ignore checkout EOL policy.""" try: result = subprocess.run( ["git", "-C", str(source), "cat-file", "blob", f"{revision}:{relative}"], capture_output=True, timeout=30, check=False, ) except (OSError, subprocess.TimeoutExpired) as error: raise ReleaseError("committed_source_bytes_unavailable") from error if result.returncode != 0 or len(result.stdout) > 32 * 1024 * 1024: raise ReleaseError("committed_source_bytes_unavailable") return result.stdout def _bind_git_source(source: Path, revision: str, selected_paths: list[str]) -> tuple[str, str]: root = _git(source, "rev-parse", "--show-toplevel") if os.path.normcase(str(Path(root).resolve())) != os.path.normcase(str(source.resolve())): raise ReleaseError("source_must_be_git_checkout_root") head = _git(source, "rev-parse", "--verify", "HEAD") tree = _git(source, "rev-parse", "--verify", "HEAD^{tree}") if head != revision or not re.fullmatch(r"[0-9a-f]{40}", head) or not re.fullmatch(r"[0-9a-f]{40}", tree): raise ReleaseError("release_revision_does_not_match_git_head") if _git(source, "status", "--porcelain=v1", "--untracked-files=no"): raise ReleaseError("tracked_source_checkout_must_be_clean") for relative in selected_paths: # Frontend artifacts are rebuilt from the exact commit below. They are # deliberately ignored by Git so generated bytes cannot masquerade as # reviewed source. if relative.startswith(FRONTEND_DIST_PREFIX): continue try: _git(source, "ls-files", "--error-unmatch", "--", relative) except ReleaseError as error: raise ReleaseError("selected_source_not_tracked_at_revision") from error return head, tree def _frontend_dist_inventory(source: Path) -> list[str]: root = source / "aureon-os-upstream/frontend/dist" if root.is_symlink() or not root.is_dir(): raise ReleaseError("frontend_dist_root_required") paths: list[str] = [] for path in root.rglob("*"): if path.is_symlink(): raise ReleaseError("frontend_dist_link_forbidden") if path.is_file(): paths.append(path.relative_to(source).as_posix()) return sorted(paths) def _operator_package_git_inventory(source: Path, revision: str) -> list[str]: """Read package paths from the immutable commit tree, never the worktree.""" output = _git( source, "ls-tree", "-r", "--name-only", revision, "--", OPERATOR_PACKAGE_PREFIX.rstrip("/"), ) return output.splitlines() def _git_archive(source: Path, revision: str, prefix: str) -> bytes: try: result = subprocess.run( ["git", "-C", str(source), "archive", "--format=tar", revision, "--", prefix], capture_output=True, timeout=120, check=False, ) except (OSError, subprocess.TimeoutExpired) as error: raise ReleaseError("frontend_commit_archive_unavailable") from error if result.returncode != 0 or not result.stdout: raise ReleaseError("frontend_commit_archive_unavailable") return result.stdout def _extract_regular_archive(raw: bytes, destination: Path, prefix: str) -> None: """Extract a Git archive without permitting links or path traversal.""" try: with tarfile.open(fileobj=io.BytesIO(raw), mode="r:") as archive: for member in archive.getmembers(): name = PurePosixPath(member.name) normalized = str(name) is_prefix_directory = member.isdir() and ( normalized == prefix or prefix.startswith(normalized + "/") ) if ( name.is_absolute() or ".." in name.parts or ( not normalized.startswith(prefix + "/") and not is_prefix_directory ) or member.issym() or member.islnk() or member.isdev() ): raise ReleaseError("frontend_commit_archive_invalid") target = destination.joinpath(*name.parts) if member.isdir(): target.mkdir(parents=True, exist_ok=True) continue if not member.isfile(): raise ReleaseError("frontend_commit_archive_invalid") target.parent.mkdir(parents=True, exist_ok=True) handle = archive.extractfile(member) if handle is None: raise ReleaseError("frontend_commit_archive_invalid") data = handle.read(MAX_RELEASE_BYTES + 1) if len(data) > MAX_RELEASE_BYTES: raise ReleaseError("frontend_commit_archive_invalid") target.write_bytes(data) except (tarfile.TarError, OSError) as error: raise ReleaseError("frontend_commit_archive_invalid") from error def _validate_frontend_environment(value: dict[str, str] | None) -> dict[str, str]: if not isinstance(value, dict) or set(value) - FRONTEND_ALLOWED_ENV: raise ReleaseError("frontend_public_build_environment_invalid") if not FRONTEND_REQUIRED_ENV.issubset(value) or value.get("VITE_REQUIRE_AUTH") != "1": raise ReleaseError("frontend_authenticated_build_environment_required") if re.fullmatch(r"https://[a-z0-9-]+\.supabase\.co", value.get("VITE_SUPABASE_URL", "")) is None: raise ReleaseError("frontend_supabase_url_invalid") key = value.get("VITE_SUPABASE_PUBLISHABLE_KEY", "") if not isinstance(key, str) or not 20 <= len(key) <= 4096 or re.search(r"\s", key): raise ReleaseError("frontend_supabase_publishable_key_invalid") for name, item in value.items(): if not isinstance(name, str) or not isinstance(item, str) or len(item) > 8192 or "\x00" in item: raise ReleaseError("frontend_public_build_environment_invalid") if any(marker in name for marker in ("SECRET", "PASSWORD", "PRIVATE", "SERVICE_ROLE")): raise ReleaseError("frontend_secret_build_environment_forbidden") return dict(sorted(value.items())) def _frontend_toolchain_executables( *, platform: str | None = None, resolver=None ) -> tuple[str, str]: lookup = shutil.which if resolver is None else resolver npm_name = "npm.cmd" if (os.name if platform is None else platform) == "nt" else "npm" npm = lookup(npm_name) node = lookup("node") if npm is None or node is None: raise ReleaseError("frontend_pinned_builder_unavailable") return npm, node def _default_frontend_builder( source: Path, revision: str, environment: dict[str, str] ) -> tuple[dict[str, bytes], dict[str, str]]: """Rebuild dist from a clean Git archive and a lockfile-only install.""" npm, node = _frontend_toolchain_executables() raw = _git_archive(source, revision, FRONTEND_SOURCE_ROOT) with tempfile.TemporaryDirectory(prefix="aureon-frontend-build-") as temporary: root = Path(temporary) _extract_regular_archive(raw, root, FRONTEND_SOURCE_ROOT) frontend = root / FRONTEND_SOURCE_ROOT if not (frontend / "package-lock.json").is_file(): raise ReleaseError("frontend_lockfile_required") clean_env = { key: os.environ[key] for key in ("PATH", "SystemRoot", "SYSTEMROOT", "WINDIR", "COMSPEC", "PATHEXT") if key in os.environ } clean_env.update( { "CI": "1", "TZ": "UTC", "NPM_CONFIG_AUDIT": "false", "NPM_CONFIG_FUND": "false", "NPM_CONFIG_UPDATE_NOTIFIER": "false", "npm_config_cache": str(root / "npm-cache"), **environment, } ) for command in ( [npm, "ci", "--ignore-scripts", "--no-audit", "--no-fund"], [npm, "run", "build"], ): try: result = subprocess.run( command, cwd=frontend, env=clean_env, capture_output=True, timeout=900, check=False, ) except (OSError, subprocess.TimeoutExpired) as error: raise ReleaseError("frontend_reproducible_build_failed") from error if result.returncode != 0: raise ReleaseError("frontend_reproducible_build_failed") files: dict[str, bytes] = {} dist = frontend / "dist" if dist.is_symlink() or not dist.is_dir(): raise ReleaseError("frontend_reproducible_build_failed") for path in sorted(dist.rglob("*")): if path.is_symlink(): raise ReleaseError("frontend_dist_link_forbidden") if path.is_file(): relative = FRONTEND_DIST_PREFIX + path.relative_to(dist).as_posix() files[relative] = read_bytes(path) try: node_version = subprocess.run( [node, "--version"], capture_output=True, text=True, timeout=10, check=True ).stdout.strip() npm_version = subprocess.run( [npm, "--version"], capture_output=True, text=True, timeout=10, check=True ).stdout.strip() except (OSError, subprocess.SubprocessError) as error: raise ReleaseError("frontend_pinned_builder_unavailable") from error return files, {"node": node_version, "npm": npm_version} def _vm_python_runtime_manifest( package_tree: list[str], rows: list[dict], *, source_git_tree: str ) -> dict: try: required = frozenset(operator_package_runtime_sources(package_tree)) | OPERATOR_COMPANION_SOURCES except OperatorRuntimeClosureError as error: raise ReleaseError(str(error)) from error row_by_path = {row["path"]: row for row in rows} if not required.issubset(row_by_path): raise ReleaseError("complete_vm_python_runtime_sources_required") selected = [row_by_path[path] for path in sorted(required)] return { "schema": "aureon.vm-python-runtime-closure.v1", "source_git_tree": source_git_tree, "sources": sorted(required), "files": len(required), "payload_sha256": manifest_payload_sha256(selected), } def _component_manifest(rows: list[dict], required: frozenset[str], reason: str) -> dict: row_by_path = {row["path"]: row for row in rows} if not required.issubset(row_by_path): raise ReleaseError(reason) selected = [row_by_path[path] for path in sorted(required)] return { "sources": sorted(required), "files": len(required), "payload_sha256": manifest_payload_sha256(selected), } def _parse_receipt_time(value: object) -> float: if not isinstance(value, str) or len(value) > 64: raise ReleaseError("bridge_certificate_fingerprint_receipt_invalid") try: return datetime.fromisoformat(value.replace("Z", "+00:00")).astimezone(UTC).timestamp() except (ValueError, TypeError, OverflowError) as error: raise ReleaseError("bridge_certificate_fingerprint_receipt_invalid") from error def _certificate_receipt( path: Path | None, expected_sha256: str, revision: str, *, now: float | None = None, ) -> dict: if path is None or re.fullmatch(r"[0-9a-f]{64}", expected_sha256 or "") is None: raise ReleaseError("bridge_certificate_fingerprint_receipt_required") raw = read_bytes(path, 1024 * 1024) if digest(raw) != expected_sha256: raise ReleaseError("bridge_certificate_fingerprint_receipt_hash_mismatch") try: value = json.loads(raw) except (ValueError, TypeError, UnicodeError) as error: raise ReleaseError("bridge_certificate_fingerprint_receipt_invalid") from error recorded = _parse_receipt_time(value.get("recorded_at")) not_before = _parse_receipt_time(value.get("not_before")) not_after = _parse_receipt_time(value.get("not_after")) current = time.time() if now is None else float(now) if recorded > current + 300 or current - recorded > 21_600: raise ReleaseError("bridge_certificate_fingerprint_receipt_not_fresh") if ( value.get("schema") != BRIDGE_CERTIFICATE_RECEIPT_SCHEMA or value.get("status") != "passed" or value.get("project") != "aureon-trading" or re.fullmatch(r"[a-z0-9-]+-[a-z0-9]+[0-9]-[a-z]", str(value.get("zone") or "")) is None or re.fullmatch(r"[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?", str(value.get("instance") or "")) is None or value.get("source_revision") != revision or value.get("certificate_path") != "/etc/aureon-public/bridge-cert.pem" or value.get("algorithm") != "x509-der-sha256" or re.fullmatch(r"[0-9a-f]{64}", str(value.get("certificate_sha256") or "")) is None or value.get("required_ip_sans") != ["10.40.0.2"] or value.get("minimum_remaining_validity_seconds") != 86_400 or isinstance(value.get("minimum_remaining_validity_seconds"), bool) or not_before > recorded or recorded > not_after or not_before > current or not_after - current < 86_400 or value.get("private_key_included") is not False or value.get("secret_values_recorded") is not False ): raise ReleaseError("bridge_certificate_fingerprint_receipt_invalid") return { "schema": value["schema"], "sha256": expected_sha256, "recorded_at": value["recorded_at"], "project": value["project"], "zone": value["zone"], "instance": value["instance"], "fingerprint_sha256": value["certificate_sha256"], "required_ip_sans": value["required_ip_sans"], "not_before": value["not_before"], "not_after": value["not_after"], "minimum_remaining_validity_seconds": value[ "minimum_remaining_validity_seconds" ], } def build( source: Path, selected: list, destination: Path, revision: str, release_id: str, *, frontend_build_environment: dict[str, str] | None = None, frontend_builder=None, bridge_certificate_receipt: Path | None = None, bridge_certificate_receipt_sha256: str = "", ) -> dict: if not re.fullmatch(r"[0-9a-f]{40}", revision) or not re.fullmatch(r"[0-9]{8}T[0-9]{6}Z-[0-9a-f]{8,16}", release_id): raise ReleaseError("exact_revision_and_dated_release_required") if not isinstance(selected, list) or not selected or len(selected) > 20000: raise ReleaseError("explicit_bounded_source_list_required") if destination.exists() or destination.is_symlink(): raise ReleaseError("bundle_destination_must_be_new") if destination.resolve().is_relative_to(source.resolve()): raise ReleaseError("bundle_must_be_outside_source_checkout") seen, selected_paths, selected_items = set(), [], [] for item in selected: relative = source_path(item if isinstance(item, str) else item.get("path")) if relative.casefold() in seen: raise ReleaseError("duplicate_source_selection") seen.add(relative.casefold()) selected_paths.append(relative) selected_items.append((item, relative)) git_revision, git_tree = _bind_git_source(source, revision, selected_paths) rows, payload, total = [], [], 0 for item, relative in selected_items: data = ( read_bytes(confined(source, relative)) if relative.startswith(FRONTEND_DIST_PREFIX) else _git_blob(source, revision, relative) ) hashed = digest(data) if isinstance(item, dict) and item.get("sha256") != hashed: raise ReleaseError("reviewed_source_hash_changed") total += len(data) if total > MAX_RELEASE_BYTES: raise ReleaseError("release_size_limit") rows.append({"path": relative, "sha256": hashed, "size": len(data)}) payload.append((relative, data)) package_tree = _operator_package_git_inventory(source, revision) operator_runtime = None if operator_runtime_requested(selected_paths): try: operator_runtime = build_operator_runtime_manifest( package_tree, rows, source_git_tree=git_tree, ) except OperatorRuntimeClosureError as error: raise ReleaseError(str(error)) from error vm_python_runtime = None if operator_runtime is not None or set(selected_paths) & VM_RUNTIME_TRIGGER_SOURCES: vm_python_runtime = _vm_python_runtime_manifest( package_tree, rows, source_git_tree=git_tree ) selected_dist = sorted(path for path in selected_paths if path.startswith(FRONTEND_DIST_PREFIX)) frontend_dist = None if selected_dist: expected_dist = _frontend_dist_inventory(source) if FRONTEND_DIST_INDEX not in selected_dist or selected_dist != expected_dist: raise ReleaseError("complete_frontend_dist_inventory_required") public_environment = _validate_frontend_environment(frontend_build_environment) frontend_rows = [row for row in rows if row["path"].startswith(FRONTEND_DIST_PREFIX)] rebuilt, toolchain = (frontend_builder or _default_frontend_builder)( source, revision, public_environment ) actual = { relative: read_bytes(confined(source, relative)) for relative in expected_dist } if set(rebuilt) != set(actual) or any( digest(rebuilt[path]) != digest(actual[path]) for path in actual ): raise ReleaseError("frontend_dist_not_reproducible_from_commit") frontend_dist = { "schema": FRONTEND_BUILD_SCHEMA, "entrypoint": FRONTEND_DIST_INDEX, "files": len(frontend_rows), "sha256": manifest_payload_sha256(frontend_rows), "source_git_revision": git_revision, "source_git_tree": git_tree, "recipe": list(FRONTEND_BUILD_RECIPE), "environment_keys": sorted(public_environment), "environment_sha256": digest( json.dumps(public_environment, sort_keys=True, separators=(",", ":")).encode() ), "toolchain": toolchain, } broker = None if set(selected_paths) & {"deployment/google-cloud/aureon-cloud-token-broker.service", "deployment/google-cloud/vm_token_broker.py"}: broker = _component_manifest( rows, TOKEN_BROKER_SOURCES, "complete_token_broker_sources_required" ) broker.update( { "schema": "aureon.vm-token-broker-release.v1", "source_git_tree": git_tree, } ) public_bridge = None if set(selected_paths) & PUBLIC_BRIDGE_SOURCES: certificate_receipt = _certificate_receipt( bridge_certificate_receipt, bridge_certificate_receipt_sha256, revision, ) public_bridge = _component_manifest( rows, PUBLIC_BRIDGE_SOURCES, "complete_public_bridge_sources_required" ) row_by_path = {row["path"]: row for row in rows} public_bridge.update( { "schema": "aureon.vm-public-bridge-release.v1", "revision": revision, "source_git_tree": git_tree, "source_sha256": row_by_path[PUBLIC_BRIDGE_SOURCE]["sha256"], "unit_sha256": row_by_path[PUBLIC_BRIDGE_UNIT_SOURCE]["sha256"], "certificate": { "path": "/etc/aureon-public/bridge-cert.pem", "algorithm": "x509-der-sha256", "sha256": certificate_receipt["fingerprint_sha256"], "receipt": certificate_receipt, }, } ) destination.mkdir(parents=True, mode=0o700) (destination / "files").mkdir() for relative, data in payload: durable_write(confined(destination / "files", relative), data, 0o644) manifest = {"schema": SCHEMA, "revision": revision, "release_id": release_id, "source_git_revision": git_revision, "source_git_tree": git_tree, "payload_sha256": manifest_payload_sha256(rows), "frontend_dist": frontend_dist, "operator_runtime": operator_runtime, "vm_python_runtime": vm_python_runtime, "token_broker": broker, "public_bridge": public_bridge, "files": rows, "bytes": total, "contains_runtime_state": False} durable_write(destination / "manifest.json", (json.dumps(manifest, sort_keys=True, indent=2) + "\n").encode()) return manifest def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--source", required=True, type=Path) parser.add_argument("--paths", required=True, type=Path) parser.add_argument("--destination", required=True, type=Path) parser.add_argument("--revision", required=True) parser.add_argument("--release-id", required=True) parser.add_argument("--frontend-build-env", type=Path) parser.add_argument("--bridge-certificate-receipt", type=Path) parser.add_argument("--bridge-certificate-receipt-sha256", default="") args = parser.parse_args() selected = json.loads(read_bytes(args.paths, 4 * 1024 * 1024)) frontend_environment = None if args.frontend_build_env is not None: frontend_environment = json.loads( read_bytes(args.frontend_build_env, 64 * 1024) ) manifest = build( args.source, selected, args.destination, args.revision, args.release_id, frontend_build_environment=frontend_environment, bridge_certificate_receipt=args.bridge_certificate_receipt, bridge_certificate_receipt_sha256=args.bridge_certificate_receipt_sha256, ) print(json.dumps({"files": len(manifest["files"]), "bytes": manifest["bytes"], "revision": manifest["revision"]})) if __name__ == "__main__": main()