From b1ff5a462985e3fc6547d1ea1c4cd288c1b7ce39 Mon Sep 17 00:00:00 2001 From: twentyOne2x Date: Sun, 12 Jul 2026 05:18:14 +0200 Subject: [PATCH] Add passwordless GCP operator and full Leo benchmark --- .github/workflows/gcp-iap-operator.yml | 196 ++ .gitignore | 1 + .../current-truth-index.md | 8 + ...-working-leo-source-baseline-20260712.json | 1 + ...s-auto-deploy-runtime-nonchange-current.md | 40 + ...260711t1816z-canonical-parity-receipt.json | 116 ++ ops/plan_gcp_iap_operator_access.py | 752 +++++++ scripts/gcp_iap_operator.sh | 603 ++++++ .../run_gcp_generated_db_working_leo_suite.py | 1772 +++++++++++++++++ ...test_gcp_generated_db_working_leo_suite.py | 645 ++++++ tests/test_gcp_iap_operator_access.py | 404 ++++ 11 files changed, 4538 insertions(+) create mode 100644 .github/workflows/gcp-iap-operator.yml create mode 100644 docs/reports/leo-working-state-20260709/gcp-working-leo-source-baseline-20260712.json create mode 100644 docs/reports/leo-working-state-20260709/pr77-vps-auto-deploy-runtime-nonchange-current.md create mode 100644 docs/reports/leo-working-state-20260709/teleo_clone_20260711t1816z-canonical-parity-receipt.json create mode 100755 ops/plan_gcp_iap_operator_access.py create mode 100755 scripts/gcp_iap_operator.sh create mode 100644 scripts/run_gcp_generated_db_working_leo_suite.py create mode 100644 tests/test_gcp_generated_db_working_leo_suite.py create mode 100644 tests/test_gcp_iap_operator_access.py diff --git a/.github/workflows/gcp-iap-operator.yml b/.github/workflows/gcp-iap-operator.yml new file mode 100644 index 0000000..efde64e --- /dev/null +++ b/.github/workflows/gcp-iap-operator.yml @@ -0,0 +1,196 @@ +name: gcp-iap-operator + +on: + workflow_dispatch: + inputs: + operation: + description: Fixed reviewed operation + required: true + type: choice + options: + - status + - direct-claim-replay + - cleanup-clone + request_id: + description: iap- followed by 12-32 lowercase letters or digits + required: true + type: string + target_db: + description: teleo_clone_status for status, otherwise the exact generated clone database + required: true + default: teleo_clone_status + type: string + +permissions: + contents: read + id-token: write + +concurrency: + group: gcp-iap-operator-${{ github.run_id }} + cancel-in-progress: false + +env: + PROJECT_ID: teleo-501523 + WORKLOAD_IDENTITY_PROVIDER: projects/785938879453/locations/global/workloadIdentityPools/github-actions/providers/teleo-iap-operator + STATUS_SERVICE_ACCOUNT: sa-teleo-iap-status@teleo-501523.iam.gserviceaccount.com + CLONE_SERVICE_ACCOUNT: sa-teleo-iap-clone-operator@teleo-501523.iam.gserviceaccount.com + EXPECTED_WORKFLOW_REF: living-ip/teleo-infrastructure/.github/workflows/gcp-iap-operator.yml@refs/heads/main + DISPATCHER_VERSION: teleo-gcp-iap-operator-v1 + BUNDLE_DIR: ${{ runner.temp }}/gcp-iap-operator-bundle + RESULT_DIR: ${{ runner.temp }}/gcp-iap-operator-result + +jobs: + operate: + name: Fixed IAP operator command + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - name: Check out the dispatched main revision + uses: actions/checkout@v4 + + - name: Validate fixed inputs and select identity + id: validate + shell: bash + env: + OPERATION: ${{ inputs.operation }} + REQUEST_ID: ${{ inputs.request_id }} + TARGET_DB: ${{ inputs.target_db }} + run: | + set -euo pipefail + [[ "${GITHUB_REF}" == "refs/heads/main" ]] || exit 2 + [[ "${GITHUB_WORKFLOW_REF}" == "${EXPECTED_WORKFLOW_REF}" ]] || exit 2 + request_id_re='^iap-[a-z0-9]{12,32}$' + target_db_re='^teleo_clone_[a-z0-9][a-z0-9_]{0,50}$' + [[ "${REQUEST_ID}" =~ ${request_id_re} ]] || exit 2 + [[ "${TARGET_DB}" =~ ${target_db_re} ]] || exit 2 + case "${OPERATION}" in + status) + [[ "${TARGET_DB}" == "teleo_clone_status" ]] || exit 2 + service_account="${STATUS_SERVICE_ACCOUNT}" + ;; + direct-claim-replay|cleanup-clone) + [[ "${TARGET_DB}" != "teleo_clone_status" ]] || exit 2 + service_account="${CLONE_SERVICE_ACCOUNT}" + ;; + *) exit 2 ;; + esac + printf 'service_account=%s\n' "${service_account}" >> "${GITHUB_OUTPUT}" + install -d -m 0700 "${RESULT_DIR}" + : > "${RESULT_DIR}/result.json" + chmod 0600 "${RESULT_DIR}/result.json" + + - name: Build strict reviewed operation bundle + if: ${{ inputs.operation != 'status' }} + shell: bash + env: + OPERATION: ${{ inputs.operation }} + REQUEST_ID: ${{ inputs.request_id }} + TARGET_DB: ${{ inputs.target_db }} + run: | + set -euo pipefail + python3 - <<'PY' + import gzip + import hashlib + import io + import json + import os + import subprocess + import tarfile + from pathlib import Path + + root = Path.cwd().resolve() + operation = os.environ["OPERATION"] + request_id = os.environ["REQUEST_ID"] + target_db = os.environ["TARGET_DB"] + common = ["scripts/gcp_iap_operator.sh"] + direct_claim = [ + "scripts/run_gcp_generated_db_direct_claim_suite.py", + "scripts/run_leo_clone_bound_handler_checkpoint.py", + "scripts/working_leo_open_ended_benchmark.py", + "hermes-agent/leoclean-bin/cloudsql_memory_tool.py", + "ops/postgres_parity_manifest.sql", + f"docs/reports/leo-working-state-20260709/{target_db}-canonical-parity-receipt.json", + ] + allowed = common + (direct_claim if operation == "direct-claim-replay" else []) + if operation not in {"direct-claim-replay", "cleanup-clone"}: + raise SystemExit("bundle requested for a non-clone operation") + head = subprocess.check_output(["git", "rev-parse", "HEAD"], text=True).strip() + if head != os.environ["GITHUB_SHA"]: + raise SystemExit("checkout HEAD does not match GITHUB_SHA") + files = {} + payloads = {} + for relative in allowed: + subprocess.run( + ["git", "ls-files", "--error-unmatch", "--", relative], + check=True, + stdout=subprocess.DEVNULL, + ) + path = root / relative + if not path.is_file() or path.is_symlink() or root not in path.resolve().parents: + raise SystemExit(f"unsafe or missing tracked bundle file: {relative}") + data = path.read_bytes() + payloads[relative] = data + files[relative] = {"sha256": hashlib.sha256(data).hexdigest(), "size": len(data)} + manifest = { + "schema": "livingip.gcpIapOperatorBundle.v1", + "dispatcher_version": os.environ["DISPATCHER_VERSION"], + "operation": operation, + "request_id": request_id, + "target_db": target_db, + "git_commit": head, + "ref": os.environ["GITHUB_REF"], + "workflow_ref": os.environ["GITHUB_WORKFLOW_REF"], + "files": files, + } + manifest_bytes = (json.dumps(manifest, indent=2, sort_keys=True) + "\n").encode() + bundle_dir = Path(os.environ["BUNDLE_DIR"]) + bundle_dir.mkdir(mode=0o700, parents=True, exist_ok=True) + os.chmod(bundle_dir, 0o700) + bundle_path = bundle_dir / f"{request_id}.tar.gz" + with bundle_path.open("xb") as raw: + with gzip.GzipFile(fileobj=raw, mode="wb", mtime=0) as compressed: + with tarfile.open(fileobj=compressed, mode="w", format=tarfile.PAX_FORMAT) as archive: + for relative, data in sorted({**payloads, "bundle-manifest.json": manifest_bytes}.items()): + info = tarfile.TarInfo(relative) + info.size = len(data) + info.mode = 0o600 + info.mtime = 0 + info.uid = info.gid = 0 + info.uname = info.gname = "root" + archive.addfile(info, io.BytesIO(data)) + os.chmod(bundle_path, 0o600) + PY + + - name: Authenticate with short-lived workload identity federation + id: auth + uses: google-github-actions/auth@v3 + with: + workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ steps.validate.outputs.service_account }} + create_credentials_file: true + cleanup_credentials: true + export_environment_variables: true + + - name: Install Google Cloud CLI + uses: google-github-actions/setup-gcloud@v3 + with: + project_id: ${{ env.PROJECT_ID }} + + - name: Run fixed IAP operation + shell: bash + env: + OPERATION: ${{ inputs.operation }} + REQUEST_ID: ${{ inputs.request_id }} + TARGET_DB: ${{ inputs.target_db }} + run: | + set -euo pipefail + scripts/gcp_iap_operator.sh "${OPERATION}" "${REQUEST_ID}" "${TARGET_DB}" + + - name: Upload sanitized result + if: always() + uses: actions/upload-artifact@v4 + with: + name: gcp-iap-operator-${{ github.run_id }} + path: ${{ env.RESULT_DIR }}/result.json + if-no-files-found: error + retention-days: 7 diff --git a/.gitignore b/.gitignore index d5191aa..cbcc5f3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ __pycache__/ # Secrets (never commit) secrets/ +gha-creds-*.json # Logs logs/ diff --git a/docs/reports/leo-working-state-20260709/current-truth-index.md b/docs/reports/leo-working-state-20260709/current-truth-index.md index f195e4f..2188b4c 100644 --- a/docs/reports/leo-working-state-20260709/current-truth-index.md +++ b/docs/reports/leo-working-state-20260709/current-truth-index.md @@ -130,6 +130,11 @@ Use this file before making status claims. Prefer fresh VPS/GCP readbacks when c - Current GCP Leo runtime observability: `docs/reports/leo-working-state-20260709/gcp-leo-runtime-observability-current.md` - Current GCP Leo runtime observability JSON: `docs/reports/leo-working-state-20260709/gcp-leo-runtime-observability-current.json` - Prior auth-capable GCP blocker: `docs/reports/leo-working-state-20260709/gcp-db-parity-current-blocker-20260709.json` +- PR #77 VPS auto-deploy/runtime non-change readback: `docs/reports/leo-working-state-20260709/pr77-vps-auto-deploy-runtime-nonchange-current.md` +- Independent VPS source baseline for the GCP working-Leo suite: `docs/reports/leo-working-state-20260709/gcp-working-leo-source-baseline-20260712.json` +- Disposable GCP clone canonical-parity receipt: `docs/reports/leo-working-state-20260709/teleo_clone_20260711t1816z-canonical-parity-receipt.json` +- Passwordless GCP operator bootstrap plan: `ops/plan_gcp_iap_operator_access.py` +- Passwordless fixed-operation GCP workflow: `.github/workflows/gcp-iap-operator.yml` ## Images @@ -190,3 +195,6 @@ Use this file before making status claims. Prefer fresh VPS/GCP readbacks when c - A live Telegram regression canary should be rerun after any production apply; no production apply has been authorized or executed. - GCP control-plane Chrome readback now proves project `teleo-501523`, running VM `teleo-prod-1` in `europe-west6-a`, and one available private-only PostgreSQL `16.14` Cloud SQL instance, `teleo-pgvector-standby`, in `europe-west6-c`. Cloud SQL Studio is reachable but exposes no enabled existing IAM database principal and requires built-in password authentication; no credential was submitted and no SQL ran. Database-row/runtime/DC-01-through-DC-06 parity therefore remains unproven. The exact next gate is an already-existing authenticated Studio principal or a separately authorized IAM/database-user enablement window. - GCP Console observability now separately proves `teleo-prod-1` is running and that serial output through `2026-07-10T15:57:54Z` contains recurring successful `leoclean-cloudsql-memory-sync.service` cycles. The latest exact `leoclean-gcp-prod-parallel.service` lifecycle event in the retained serial buffer is a Hermes start on July 9 with no later exact stop/failure. Cloud Logging has `92` guest/platform records over seven days but zero matches for `leoclean`, `hermes`, `gateway`, `postgres`, or `teleo-kb`, and Ops Agent is absent. This is bounded unit-activity evidence, not current PID/restart/SHA/profile/model/auth/Telegram/DB-row parity; no-post Cory execution remains unsafe and was not attempted. The agent-created Console tab was closed and no GCP/DB/service/IAM/deploy/credential/Telegram mutation occurred. +- PR #77 auto-deployed to the VPS checkout at merge `1a0abd882d00c1b58fe203f42de42d4eeea61cbf` without restarting Leo: the deploy journal says `No Python changes - services not restarted`, the gateway remained PID `2999690` with `NRestarts=0`, and the apply worker/timer remained disabled and inactive. This proves source synchronization and runtime non-change, not a GCP replay. +- A passwordless GCP operator is implemented and locally validated but is not yet bootstrapped in project `teleo-501523`. It uses GitHub OIDC/Workload Identity Federation, separate status and clone-operation service accounts, IAP TCP forwarding, OS Login, five-minute SSH keys, and a root-owned dispatcher limited to `status`, `direct-claim-replay`, and `cleanup-clone`. It stores no Google password, local refresh token, service-account JSON key, or API key. One final authenticated GCP admin session is still required to install and verify the IAM/IAP/OS Login/dispatcher path; the old public `/32` rule must remain enabled until the workflow status artifact passes. +- The broader GCP working-Leo runner now has a complete `20`-prompt catalog and requires `32` GCP-executable non-tautological composition checks plus lifecycle, role-separation, immutable-source, and secret-redaction gates. Local tests and validation-only execution pass, and an independent VPS source baseline is retained with counts `1837/4145/4670/4916/17/26`; live GCP execution remains unproven because the SHA-bound target-identity receipt, four separated database roles/credentials, and passwordless operator bootstrap are not yet present. This does not inherit or claim the VPS-only `34/34` result. diff --git a/docs/reports/leo-working-state-20260709/gcp-working-leo-source-baseline-20260712.json b/docs/reports/leo-working-state-20260709/gcp-working-leo-source-baseline-20260712.json new file mode 100644 index 0000000..2eb01a9 --- /dev/null +++ b/docs/reports/leo-working-state-20260709/gcp-working-leo-source-baseline-20260712.json @@ -0,0 +1 @@ +{"artifact": "gcp_working_leo_source_baseline", "captured_at_utc": "2026-07-12T02:57:31.103344+00:00", "gate_schema": {"apply_role_exists": true, "approval_table": null, "approve_function": null, "assert_function": null, "finish_function": null, "review_principals_table": null, "review_role_exists": false}, "guard_snapshot": {"counts": {"kb_stage.kb_proposals": 26, "public.claim_edges": 4916, "public.claim_evidence": 4670, "public.claims": 1837, "public.reasoning_tools": 17, "public.sources": 4145}, "database_identity": {"current_database": "teleo", "postmaster_started_at": "2026-07-03 00:17:20.873566+00", "system_identifier": "7649789040005668902", "transaction_read_only": "on"}, "rowset_md5": {"kb_stage.kb_proposals": "2b31c5fddc9fa3548cf1d7874b5f417a", "public.claim_edges": "7b78b7753e50eeafbcecefa588f668dc", "public.claim_evidence": "244387a16b6210ad3d89850251062bb2", "public.claims": "ea11d4b63a00406a0d7b2d4d4ade0123", "public.reasoning_tools": "fb0330410709f53e3815725cc8de6eaf", "public.sources": "8209986c34f9d84681459c7033e012a5"}}, "instance": "teleo-prod-1", "problems": [], "project": "teleo-501523", "status": "pass"} diff --git a/docs/reports/leo-working-state-20260709/pr77-vps-auto-deploy-runtime-nonchange-current.md b/docs/reports/leo-working-state-20260709/pr77-vps-auto-deploy-runtime-nonchange-current.md new file mode 100644 index 0000000..370ccbc --- /dev/null +++ b/docs/reports/leo-working-state-20260709/pr77-vps-auto-deploy-runtime-nonchange-current.md @@ -0,0 +1,40 @@ +# PR #77 VPS Auto-Deploy Runtime Readback + +Observed UTC: `2026-07-12T00:11:37Z` + +## Result + +The VPS auto-deploy timer fast-forwarded +`/opt/teleo-eval/workspaces/deploy-infra` and +`/opt/teleo-eval/.last-deploy-sha` from merge `3b534abd` to PR #77 merge +`1a0abd882d00c1b58fe203f42de42d4eeea61cbf`. + +This was a source-checkout synchronization, not a live Leo feature or process +change: + +- The auto-deploy journal says `No Python changes - services not restarted`. +- The deploy checkout is clean on `main...github/main`. +- `leoclean-gateway.service` remains active/running on PID `2999690`, with + `NRestarts=0` and start time `2026-07-10T03:47:11Z`. +- `teleo-kb-apply-worker.service` remains disabled, inactive, and dead, with + no main process and no restart. +- `teleo-kb-apply-worker.timer` remains disabled, inactive, and dead, with no + last trigger. + +## Auto-Deploy Journal + +```text +2026-07-12T00:08:03+00:00 New commits: 3b534abd -> 1a0abd88 +2026-07-12T00:08:10+00:00 Syntax check passed +2026-07-12T00:08:11+00:00 Files synced +2026-07-12T00:08:11+00:00 No Python changes - services not restarted +2026-07-12T00:08:11+00:00 Deploy complete: 1a0abd8 Merge pull request #77 from living-ip/codex/gcp-canonical-clone-proof-20260711 +``` + +## Claim Ceiling + +PR #77 is present in the VPS deploy checkout, but the live Leo gateway did not +restart and the KB apply worker stayed disabled. This proves the hardening +scripts did not change the currently running VPS Leo. It does not prove a +future restart would preserve behavior, and it does not prove the hardened GCP +replay until that replay runs against the disposable Cloud SQL database. diff --git a/docs/reports/leo-working-state-20260709/teleo_clone_20260711t1816z-canonical-parity-receipt.json b/docs/reports/leo-working-state-20260709/teleo_clone_20260711t1816z-canonical-parity-receipt.json new file mode 100644 index 0000000..7c704a6 --- /dev/null +++ b/docs/reports/leo-working-state-20260709/teleo_clone_20260711t1816z-canonical-parity-receipt.json @@ -0,0 +1,116 @@ +{ + "artifact": "canonical_postgres_parity_verification", + "details": { + "application_role_mismatches": {}, + "performance": [ + { + "query": "claim_edges_by_claim_id", + "source_ms": 0.079, + "source_ratio": 0.3944, + "target_ms": 1.972 + }, + { + "query": "claim_evidence_by_claim_id", + "source_ms": 0.072, + "source_ratio": 0.2068, + "target_ms": 1.034 + }, + { + "query": "count_claims", + "source_ms": 0.293, + "source_ratio": 0.3056, + "target_ms": 1.528 + }, + { + "query": "pending_proposals_latest", + "source_ms": 0.034, + "source_ratio": 0.0062, + "target_ms": 0.031 + } + ], + "performance_problems": [], + "required_extension_mismatches": {}, + "source_connection": { + "database_collation": null, + "database_ctype": null, + "server_address": null, + "server_port": null, + "ssl": null, + "ssl_version": null + }, + "source_database": "teleo", + "source_table_count": 39, + "source_total_rows": 52164, + "structural_hashes": { + "columns": { + "source": "50b502967aa85b88bd204c7b9035de60bf21a6a6973817e8b8121d0cd9faab81", + "target": "50b502967aa85b88bd204c7b9035de60bf21a6a6973817e8b8121d0cd9faab81" + }, + "constraints": { + "source": "8b26d26fda56f09325e0603aae48051da08dfc6c35322fa88880acdabb8ad859", + "target": "8b26d26fda56f09325e0603aae48051da08dfc6c35322fa88880acdabb8ad859" + }, + "functions": { + "source": "4bddee626b3ab62af236493001d2a09f2d56c084beb7e6c69cd082ce4aba155b", + "target": "4bddee626b3ab62af236493001d2a09f2d56c084beb7e6c69cd082ce4aba155b" + }, + "indexes": { + "source": "5c4f7567e9ad0f25da0ab15f650a1005397ae9c727af1ab41e2675c46a5ebce5", + "target": "5c4f7567e9ad0f25da0ab15f650a1005397ae9c727af1ab41e2675c46a5ebce5" + }, + "policies": { + "source": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945", + "target": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945" + }, + "schemas": { + "source": "2fdf551b403db2d79813c8fd2869dadfeba261fac2aaa718b355cd7414640979", + "target": "2fdf551b403db2d79813c8fd2869dadfeba261fac2aaa718b355cd7414640979" + }, + "sequences": { + "source": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945", + "target": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945" + }, + "triggers": { + "source": "4d5f3394480c8410035dac5457e144c7db80fa0820ff49beb93aa819a7282c64", + "target": "4d5f3394480c8410035dac5457e144c7db80fa0820ff49beb93aa819a7282c64" + }, + "types": { + "source": "365e82ac205319d25d14c8b16026978f0e6023ae0c58767f700d7f2ea659b70b", + "target": "365e82ac205319d25d14c8b16026978f0e6023ae0c58767f700d7f2ea659b70b" + }, + "views": { + "source": "5c33fa260f25dccffc214f795c66e3dbae89d91d0dbf8a98122ace1dcda46799", + "target": "5c33fa260f25dccffc214f795c66e3dbae89d91d0dbf8a98122ace1dcda46799" + } + }, + "table_mismatches": [], + "target_connection": { + "database_collation": "en_US.UTF8", + "database_ctype": "en_US.UTF8", + "server_address": "10.61.0.3", + "server_port": 5432, + "ssl": true, + "ssl_version": "TLSv1.3" + }, + "target_database": "teleo_clone_20260711t1816z", + "target_extra_application_roles": [], + "target_table_count": 39, + "target_total_rows": 52164 + }, + "error": null, + "generated_at_utc": "2026-07-11T18:25:05.711214+00:00", + "not_proven_by_this_artifact": [ + "GCP staging Leo composition replay", + "ongoing replication after the manifest timestamps", + "production cutover" + ], + "private_connectivity": { + "required": false, + "status": "not_applicable_local_restore" + }, + "problems": [], + "scope": "local_restore", + "source_manifest": "/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/outputs/gcp-canonical-exported-snapshot-20260711T0727Z/source-manifest.jsonl", + "status": "pass", + "target_manifest": "/Users/user/Documents/Codex/2026-07-09/019f34eb-d297-72d0-b7e2-b222d5515ab9-load/outputs/gcp-cloudsql-canonical-clone-20260711T1816Z/target-manifest-role-parity.jsonl" +} diff --git a/ops/plan_gcp_iap_operator_access.py b/ops/plan_gcp_iap_operator_access.py new file mode 100755 index 0000000..d9dae94 --- /dev/null +++ b/ops/plan_gcp_iap_operator_access.py @@ -0,0 +1,752 @@ +#!/usr/bin/env python3 +"""Emit the dry-run bootstrap and revocation plan for GitHub-to-GCP IAP SSH.""" + +from __future__ import annotations + +import argparse +import json +import shlex + +PROJECT = "teleo-501523" +PROJECT_NUMBER = "785938879453" +GITHUB_REPOSITORY = "living-ip/teleo-infrastructure" +WORKFLOW_PATH = ".github/workflows/gcp-iap-operator.yml" +WORKFLOW_REF = f"{GITHUB_REPOSITORY}/{WORKFLOW_PATH}@refs/heads/main" +POOL = "github-actions" +PROVIDER = "teleo-iap-operator" +PROVIDER_RESOURCE = f"projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/{POOL}/providers/{PROVIDER}" + +ZONE = "europe-west6-a" +INSTANCE = "teleo-prod-1" +INSTANCE_INTERNAL_IP = "10.60.0.3" +NETWORK = "teleo-staging-net" +TARGET_TAG = "teleo-prod-ssh" +VM_SERVICE_ACCOUNT = f"sa-teleo-prod-vm@{PROJECT}.iam.gserviceaccount.com" +STATUS_SERVICE_ACCOUNT_ID = "sa-teleo-iap-status" +STATUS_SERVICE_ACCOUNT = f"{STATUS_SERVICE_ACCOUNT_ID}@{PROJECT}.iam.gserviceaccount.com" +CLONE_SERVICE_ACCOUNT_ID = "sa-teleo-iap-clone-operator" +CLONE_SERVICE_ACCOUNT = f"{CLONE_SERVICE_ACCOUNT_ID}@{PROJECT}.iam.gserviceaccount.com" + +CUSTOM_ROLE_ID = "teleoIapSshDiscovery" +CUSTOM_ROLE = f"projects/{PROJECT}/roles/{CUSTOM_ROLE_ID}" +CUSTOM_PERMISSIONS = [ + "compute.instances.get", + "compute.instances.list", + "compute.projects.get", +] +IAP_ROLE = "roles/iap.tunnelResourceAccessor" +IAP_CONDITION = f'destination.ip == "{INSTANCE_INTERNAL_IP}" && destination.port == 22' +PROVIDER_CONDITION = " && ".join( + [ + f"assertion.repository == '{GITHUB_REPOSITORY}'", + "assertion.ref == 'refs/heads/main'", + f"assertion.workflow_ref == '{WORKFLOW_REF}'", + "assertion.event_name == 'workflow_dispatch'", + ] +) +ATTRIBUTE_MAPPING = ",".join( + [ + "google.subject=assertion.sub", + "attribute.repository=assertion.repository", + "attribute.ref=assertion.ref", + "attribute.workflow_ref=assertion.workflow_ref", + "attribute.event_name=assertion.event_name", + ] +) +WIF_PRINCIPAL = ( + f"principal://iam.googleapis.com/projects/{PROJECT_NUMBER}/locations/global/" + f"workloadIdentityPools/{POOL}/subject/repo:{GITHUB_REPOSITORY}:ref:refs/heads/main" +) + +IAP_FIREWALL_RULE = "teleo-prod-allow-ssh-iap" +CHANGING_PUBLIC_RULE = "teleo-prod-allow-ssh-current-ip" +IAP_SOURCE_RANGE = "35.235.240.0/20" +DISPATCHER_VERSION = "teleo-gcp-iap-operator-v1" + +FORBIDDEN_OPERATOR_ROLES = [ + "roles/compute.admin", + "roles/secretmanager.admin", + "roles/secretmanager.secretAccessor", + "roles/cloudsql.admin", + "roles/artifactregistry.admin", + "roles/artifactregistry.writer", +] + + +def shell_join(parts: list[str]) -> str: + return " ".join(shlex.quote(part) for part in parts) + + +def service_account_member(email: str) -> str: + return f"serviceAccount:{email}" + + +def bootstrap_commands() -> list[str]: + accounts = [ + (STATUS_SERVICE_ACCOUNT_ID, "Teleo IAP status canary"), + (CLONE_SERVICE_ACCOUNT_ID, "Teleo IAP clone operator"), + ] + commands = [ + shell_join( + [ + "gcloud", + "services", + "enable", + "compute.googleapis.com", + "iap.googleapis.com", + "iamcredentials.googleapis.com", + "sts.googleapis.com", + "--project", + PROJECT, + ] + ), + shell_join( + [ + "gcloud", + "iam", + "workload-identity-pools", + "describe", + POOL, + "--location", + "global", + "--project", + PROJECT, + ] + ), + shell_join( + [ + "gcloud", + "iam", + "workload-identity-pools", + "providers", + "create-oidc", + PROVIDER, + "--location", + "global", + "--workload-identity-pool", + POOL, + "--project", + PROJECT, + "--issuer-uri", + "https://token.actions.githubusercontent.com", + "--attribute-mapping", + ATTRIBUTE_MAPPING, + "--attribute-condition", + PROVIDER_CONDITION, + "--display-name", + "Teleo fixed IAP operator workflow", + ] + ), + *[ + shell_join( + [ + "gcloud", + "iam", + "service-accounts", + "create", + account_id, + "--project", + PROJECT, + "--display-name", + display_name, + ] + ) + for account_id, display_name in accounts + ], + shell_join( + [ + "gcloud", + "iam", + "roles", + "create", + CUSTOM_ROLE_ID, + "--project", + PROJECT, + "--title", + "Teleo exact-instance SSH discovery", + "--description", + "Only the instance and project reads required by gcloud compute ssh", + "--permissions", + ",".join(CUSTOM_PERMISSIONS), + "--stage", + "GA", + ] + ), + ] + + for account in (STATUS_SERVICE_ACCOUNT, CLONE_SERVICE_ACCOUNT): + member = service_account_member(account) + commands.extend( + [ + shell_join( + [ + "gcloud", + "iam", + "service-accounts", + "add-iam-policy-binding", + account, + "--project", + PROJECT, + "--member", + WIF_PRINCIPAL, + "--role", + "roles/iam.workloadIdentityUser", + ] + ), + shell_join( + [ + "gcloud", + "projects", + "add-iam-policy-binding", + PROJECT, + "--member", + member, + "--role", + CUSTOM_ROLE, + ] + ), + shell_join( + [ + "gcloud", + "projects", + "add-iam-policy-binding", + PROJECT, + "--member", + member, + "--role", + IAP_ROLE, + "--condition", + f"expression={IAP_CONDITION},title=teleo-prod-1-ssh-only", + ] + ), + shell_join( + [ + "gcloud", + "iam", + "service-accounts", + "add-iam-policy-binding", + VM_SERVICE_ACCOUNT, + "--project", + PROJECT, + "--member", + member, + "--role", + "roles/iam.serviceAccountUser", + ] + ), + ] + ) + + commands.extend( + [ + shell_join( + [ + "gcloud", + "compute", + "instances", + "add-iam-policy-binding", + INSTANCE, + "--zone", + ZONE, + "--project", + PROJECT, + "--member", + service_account_member(STATUS_SERVICE_ACCOUNT), + "--role", + "roles/compute.osLogin", + ] + ), + shell_join( + [ + "gcloud", + "compute", + "instances", + "add-iam-policy-binding", + INSTANCE, + "--zone", + ZONE, + "--project", + PROJECT, + "--member", + service_account_member(CLONE_SERVICE_ACCOUNT), + "--role", + "roles/compute.osAdminLogin", + ] + ), + shell_join( + [ + "gcloud", + "compute", + "instances", + "add-metadata", + INSTANCE, + "--zone", + ZONE, + "--project", + PROJECT, + "--metadata", + "enable-oslogin=TRUE", + ] + ), + shell_join( + [ + "gcloud", + "compute", + "firewall-rules", + "create", + IAP_FIREWALL_RULE, + "--project", + PROJECT, + "--network", + NETWORK, + "--direction", + "INGRESS", + "--action", + "ALLOW", + "--rules", + "tcp:22", + "--source-ranges", + IAP_SOURCE_RANGE, + "--target-tags", + TARGET_TAG, + "--enable-logging", + ] + ), + "# Install the reviewed dispatcher through the existing admin path before removing that path.", + 'BOOTSTRAP_SSH_DIR=$(mktemp -d "${TMPDIR:-/tmp}/teleo-iap-bootstrap.XXXXXX"); ' + 'chmod 0700 "${BOOTSTRAP_SSH_DIR}"; ' + 'BOOTSTRAP_SSH_KEY="${BOOTSTRAP_SSH_DIR}/ssh_key"; ' + "trap 'rm -rf \"${BOOTSTRAP_SSH_DIR}\"' EXIT", + "install -m 0600 scripts/gcp_iap_operator.sh " + '"${BOOTSTRAP_SSH_DIR}/gcp_iap_operator.sh"; ' + '(cd "${BOOTSTRAP_SSH_DIR}" && sha256sum gcp_iap_operator.sh > dispatcher.sha256); ' + 'chmod 0600 "${BOOTSTRAP_SSH_DIR}/dispatcher.sha256"', + "gcloud compute scp " + '"${BOOTSTRAP_SSH_DIR}/gcp_iap_operator.sh" ' + '"${BOOTSTRAP_SSH_DIR}/dispatcher.sha256" ' + f"{INSTANCE}:/tmp/ --zone {ZONE} --project {PROJECT} " + '--tunnel-through-iap --ssh-key-file="${BOOTSTRAP_SSH_KEY}" ' + '--ssh-key-expire-after=5m', + shell_join( + [ + "gcloud", + "compute", + "ssh", + INSTANCE, + "--zone", + ZONE, + "--project", + PROJECT, + "--command", + "cd /tmp && sha256sum -c dispatcher.sha256 && " + f"grep -F 'readonly DISPATCHER_VERSION=\"{DISPATCHER_VERSION}\"' gcp_iap_operator.sh && " + "sudo install -o root -g root -m 0755 /tmp/gcp_iap_operator.sh " + "/usr/local/sbin/teleo-gcp-iap-operator && " + "test \"$(sudo sha256sum /usr/local/sbin/teleo-gcp-iap-operator | awk '{print $1}')\" " + "= \"$(awk '{print $1}' /tmp/dispatcher.sha256)\" && " + "sudo install -d -o root -g root -m 0700 /var/lib/teleo-gcp-iap-operator/runs && " + "rm -f /tmp/gcp_iap_operator.sh /tmp/dispatcher.sha256", + ] + ) + + ' --tunnel-through-iap --ssh-key-file="${BOOTSTRAP_SSH_KEY}" ' + '--ssh-key-expire-after=5m', + ] + ) + return commands + + +def status_verification_commands() -> list[str]: + request_id = "iap-bootstrapstatus01" + return [ + shell_join( + [ + "gh", + "workflow", + "run", + "gcp-iap-operator.yml", + "--repo", + GITHUB_REPOSITORY, + "--ref", + "main", + "-f", + "operation=status", + "-f", + f"request_id={request_id}", + "-f", + "target_db=teleo_clone_status", + ] + ), + shell_join( + [ + "gh", + "run", + "list", + "--repo", + GITHUB_REPOSITORY, + "--workflow", + "gcp-iap-operator.yml", + "--event", + "workflow_dispatch", + "--limit", + "1", + ] + ), + "IAP_RUN_ID=$(gh run list --repo " + f"{GITHUB_REPOSITORY} --workflow gcp-iap-operator.yml --event workflow_dispatch --limit 1 " + "--json databaseId --jq '.[0].databaseId'); test -n \"${IAP_RUN_ID}\"", + 'IAP_VERIFY_DIR=$(mktemp -d "${TMPDIR:-/tmp}/teleo-iap-verify.XXXXXX"); ' + 'chmod 0700 "${IAP_VERIFY_DIR}"; trap \'rm -rf "${IAP_VERIFY_DIR}"\' EXIT', + 'gh run watch "${IAP_RUN_ID}" --repo ' + GITHUB_REPOSITORY + " --exit-status", + 'gh run download "${IAP_RUN_ID}" --repo ' + + GITHUB_REPOSITORY + + ' --name "gcp-iap-operator-${IAP_RUN_ID}" --dir "${IAP_VERIFY_DIR}"', + "python3 -c 'import json,sys; p=json.load(open(sys.argv[1])); " + "import hashlib,pathlib; " + 'r=p.get("remote_result") or {}; assert p.get("status")=="pass"; ' + f'assert p.get("request_id")=="{request_id}"; ' + 'assert p.get("operation")=="status"; ' + f'assert r.get("expected_internal_ip")=="{INSTANCE_INTERNAL_IP}"; ' + f'assert r.get("dispatcher_version")=="{DISPATCHER_VERSION}"; ' + 'assert r.get("dispatcher_sha256")==hashlib.sha256(' + 'pathlib.Path("scripts/gcp_iap_operator.sh").read_bytes()).hexdigest(); ' + 'assert r.get("active_state")=="active"\' "${IAP_VERIFY_DIR}/result.json"', + "# The old public /32 rule may be disabled only after every command above succeeds.", + shell_join( + [ + "gcloud", + "compute", + "firewall-rules", + "update", + CHANGING_PUBLIC_RULE, + "--project", + PROJECT, + "--disabled", + ] + ), + shell_join( + [ + "gcloud", + "compute", + "firewall-rules", + "describe", + CHANGING_PUBLIC_RULE, + "--project", + PROJECT, + "--format=value(disabled,sourceRanges)", + ] + ), + ] + + +def revocation_commands() -> list[str]: + commands = [ + "# Remove the remote dispatcher while IAP access still exists, then revoke cloud access.", + 'REVOKE_SSH_DIR=$(mktemp -d "${TMPDIR:-/tmp}/teleo-iap-revoke.XXXXXX"); ' + 'chmod 0700 "${REVOKE_SSH_DIR}"; REVOKE_SSH_KEY="${REVOKE_SSH_DIR}/ssh_key"; ' + "trap 'rm -rf \"${REVOKE_SSH_DIR}\"' EXIT", + shell_join( + [ + "gcloud", + "compute", + "ssh", + INSTANCE, + "--zone", + ZONE, + "--project", + PROJECT, + "--tunnel-through-iap", + "--command", + "sudo rm -f /usr/local/sbin/teleo-gcp-iap-operator", + ] + ) + + ' --ssh-key-file="${REVOKE_SSH_KEY}" --ssh-key-expire-after=5m', + shell_join( + [ + "gcloud", + "iam", + "workload-identity-pools", + "providers", + "update-oidc", + PROVIDER, + "--location", + "global", + "--workload-identity-pool", + POOL, + "--project", + PROJECT, + "--disabled", + ] + ), + shell_join( + [ + "gcloud", + "compute", + "firewall-rules", + "delete", + IAP_FIREWALL_RULE, + "--project", + PROJECT, + "--quiet", + ] + ), + ] + for account, os_role in ( + (STATUS_SERVICE_ACCOUNT, "roles/compute.osLogin"), + (CLONE_SERVICE_ACCOUNT, "roles/compute.osAdminLogin"), + ): + member = service_account_member(account) + commands.extend( + [ + shell_join( + [ + "gcloud", + "compute", + "instances", + "remove-iam-policy-binding", + INSTANCE, + "--zone", + ZONE, + "--project", + PROJECT, + "--member", + member, + "--role", + os_role, + ] + ), + shell_join( + [ + "gcloud", + "projects", + "remove-iam-policy-binding", + PROJECT, + "--member", + member, + "--role", + CUSTOM_ROLE, + ] + ), + shell_join( + [ + "gcloud", + "projects", + "remove-iam-policy-binding", + PROJECT, + "--member", + member, + "--role", + IAP_ROLE, + "--condition", + f"expression={IAP_CONDITION},title=teleo-prod-1-ssh-only", + ] + ), + shell_join( + [ + "gcloud", + "iam", + "service-accounts", + "remove-iam-policy-binding", + VM_SERVICE_ACCOUNT, + "--project", + PROJECT, + "--member", + member, + "--role", + "roles/iam.serviceAccountUser", + ] + ), + shell_join( + [ + "gcloud", + "iam", + "service-accounts", + "delete", + account, + "--project", + PROJECT, + "--quiet", + ] + ), + ] + ) + commands.extend( + [ + shell_join( + [ + "gcloud", + "iam", + "workload-identity-pools", + "providers", + "delete", + PROVIDER, + "--location", + "global", + "--workload-identity-pool", + POOL, + "--project", + PROJECT, + "--quiet", + ] + ), + shell_join( + [ + "gcloud", + "iam", + "roles", + "delete", + CUSTOM_ROLE_ID, + "--project", + PROJECT, + "--quiet", + ] + ), + ] + ) + return commands + + +def build_plan() -> dict[str, object]: + return { + "artifact": "teleo_gcp_iap_operator_access_plan", + "mode": "dry_run_emit_only", + "executes_commands": False, + "project": PROJECT, + "target": { + "instance": INSTANCE, + "zone": ZONE, + "internal_ip": INSTANCE_INTERNAL_IP, + "network": NETWORK, + "target_tag": TARGET_TAG, + }, + "github": { + "repository": GITHUB_REPOSITORY, + "ref": "refs/heads/main", + "workflow_ref": WORKFLOW_REF, + "event_name": "workflow_dispatch", + }, + "wif": { + "provider": PROVIDER_RESOURCE, + "provider_condition": PROVIDER_CONDITION, + "attribute_mapping": ATTRIBUTE_MAPPING, + "principal": WIF_PRINCIPAL, + "long_lived_google_credentials": False, + }, + "identities": { + "status": { + "service_account": STATUS_SERVICE_ACCOUNT, + "os_login_role": "roles/compute.osLogin", + "sudo": False, + "operations": ["status"], + }, + "clone_operator": { + "service_account": CLONE_SERVICE_ACCOUNT, + "os_login_role": "roles/compute.osAdminLogin", + "sudo": True, + "operations": ["direct-claim-replay", "cleanup-clone"], + "blast_radius": ( + "OS Admin can become root on teleo-prod-1. The identity is separated from status, " + "the provider is pinned to the main workflow_dispatch workflow, and the checked-in " + "workflow invokes only the root-owned fixed-operation dispatcher. Branch protection " + "and workflow review remain required controls because OIDC does not attest file content." + ), + }, + }, + "gcloud_ssh_custom_role": { + "role": CUSTOM_ROLE, + "permissions": CUSTOM_PERMISSIONS, + "binding_scope": f"projects/{PROJECT}", + "rationale": ( + "The official gcloud IAP SSH permission table requires instance get/list plus project lookup. " + "This project-level role is read-only; exact destination authorization remains restricted by " + "the IAP destination condition and instance-level OS Login binding." + ), + }, + "iap": { + "role": IAP_ROLE, + "condition": IAP_CONDITION, + "firewall": { + "rule": IAP_FIREWALL_RULE, + "source_range": IAP_SOURCE_RANGE, + "protocol_ports": "tcp:22", + "target_tag": TARGET_TAG, + }, + }, + "service_account_user": { + "target_service_account": VM_SERVICE_ACCOUNT, + "role": "roles/iam.serviceAccountUser", + "rationale": "Required to connect to a VM that runs as the dedicated VM service account.", + }, + "explicitly_absent": { + "roles": FORBIDDEN_OPERATOR_ROLES, + "permissions": [ + "compute.instances.setMetadata", + "compute.projects.setCommonInstanceMetadata", + "secretmanager.versions.access", + "cloudsql.instances.update", + "artifactregistry.repositories.uploadArtifacts", + ], + "credentials": [ + "Google password", + "local user refresh token", + "service-account JSON key", + "API key", + ], + }, + "bootstrap_commands": bootstrap_commands(), + "post_bootstrap_verification": status_verification_commands(), + "public_ingress_cutover": { + "old_rule": CHANGING_PUBLIC_RULE, + "disable_only_after": "successful workflow status artifact independently inspected", + "automatic_fallback_to_public_ssh": False, + }, + "audit_logging": { + "guidance": ( + "Before enabling the operator workflow, merge project IAM auditConfigs rather than replacing " + "the policy. Enable DATA_READ for iap.googleapis.com and DATA_WRITE for iap.googleapis.com, " + "plus ADMIN_READ for " + "compute.googleapis.com and iam.googleapis.com; retain IAP TCP tunnel and OS Login audit " + "entries, firewall-rule logs, " + "GitHub run ID, request_id, target_db, and sanitized result SHA-256. Never log auth outputs." + ), + "readback_commands": [ + f"gcloud projects get-iam-policy {PROJECT} --format=json", + ( + "gcloud logging read " + '\'protoPayload.serviceName="iap.googleapis.com" AND ' + f'resource.labels.project_id="{PROJECT}"\' --project {PROJECT} --limit 20 --format=json' + ), + ( + "gcloud logging read " + f'\'resource.type="gce_firewall_rule" AND resource.labels.firewall_rule_id="{IAP_FIREWALL_RULE}"\' ' + f"--project {PROJECT} --limit 20 --format=json" + ), + ], + }, + "independent_verification": [ + "Use an auditor identity that cannot impersonate either operator service account.", + "Confirm the provider condition, both service-account policies, instance IAM policy, IAP condition, and firewall source/tag.", + "Dispatch status from gh on main, inspect the sanitized artifact, and confirm no public /32 rule was disabled before that pass.", + "Confirm the workflow created no service-account key and the runner removed its five-minute SSH key and auth credential file.", + ], + "revocation_commands": revocation_commands(), + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--format", choices=("json", "shell"), default="json") + args = parser.parse_args() + plan = build_plan() + if args.format == "shell": + print("# DRY RUN: review these commands; this program never executes them.") + for section in ("bootstrap_commands", "post_bootstrap_verification", "revocation_commands"): + print(f"\n# {section}") + for command in plan[section]: + print(command) + else: + print(json.dumps(plan, indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/gcp_iap_operator.sh b/scripts/gcp_iap_operator.sh new file mode 100755 index 0000000..a0eafba --- /dev/null +++ b/scripts/gcp_iap_operator.sh @@ -0,0 +1,603 @@ +#!/usr/bin/env bash +set -Eeuo pipefail +umask 077 + +readonly DISPATCHER_VERSION="teleo-gcp-iap-operator-v1" +readonly EXPECTED_WORKFLOW_REF="living-ip/teleo-infrastructure/.github/workflows/gcp-iap-operator.yml@refs/heads/main" +readonly PROJECT_ID="teleo-501523" +readonly INSTANCE="teleo-prod-1" +readonly ZONE="europe-west6-a" +readonly INSTANCE_INTERNAL_IP="10.60.0.3" +readonly CLOUDSQL_HOST="10.61.0.3" +readonly CLOUDSQL_SECRET="gcp-teleo-pgvector-standby-postgres-password" +readonly REMOTE_DISPATCHER="/usr/local/sbin/teleo-gcp-iap-operator" +readonly REMOTE_RUN_ROOT="/var/lib/teleo-gcp-iap-operator/runs" +readonly LIVE_PROFILE="/home/teleo/.hermes/profiles/leoclean" +readonly LIVE_SERVICE="leoclean-gcp-prod-parallel.service" +readonly STATUS_TARGET="teleo_clone_status" +readonly REQUEST_ID_RE='^iap-[a-z0-9]{12,32}$' +readonly TARGET_DB_RE='^teleo_clone_[a-z0-9][a-z0-9_]{0,50}$' + +RUNNER_TEMP_DIR="" +VALIDATED_BUNDLE_DIR="" +VALIDATED_BUNDLE_SHA256="" +VALIDATED_BUNDLE_COMMIT="" + +die() { + printf 'gcp_iap_operator: %s\n' "$1" >&2 + exit 2 +} + +cleanup_runner_temp() { + if [[ -n "$RUNNER_TEMP_DIR" && -d "$RUNNER_TEMP_DIR" && "$RUNNER_TEMP_DIR" == "${RUNNER_TEMP:-}/"* ]]; then + rm -rf -- "$RUNNER_TEMP_DIR" + fi +} + +validate_inputs() { + local operation="$1" + local request_id="$2" + local target_db="$3" + + case "$operation" in + status | direct-claim-replay | cleanup-clone) ;; + *) die "operation is not allowlisted" ;; + esac + [[ "$request_id" =~ $REQUEST_ID_RE ]] || die "request_id does not match the required format" + [[ "$target_db" =~ $TARGET_DB_RE ]] || die "target_db must be a bounded teleo_clone_* identifier" + if [[ "$operation" == "status" && "$target_db" != "$STATUS_TARGET" ]]; then + die "status requires the fixed teleo_clone_status sentinel" + fi + if [[ "$operation" != "status" && "$target_db" == "$STATUS_TARGET" ]]; then + die "clone operations cannot use the status sentinel" + fi +} + +emit_json() { + local operation="$1" + local request_id="$2" + local target_db="$3" + local status="$4" + shift 4 + python3 - "$operation" "$request_id" "$target_db" "$status" "$@" <<'PY' +import json +import sys + +operation, request_id, target_db, status, *pairs = sys.argv[1:] +payload = { + "schema": "livingip.gcpIapOperatorRemoteResult.v1", + "operation": operation, + "request_id": request_id, + "target_db": target_db, + "status": status, +} +for pair in pairs: + key, separator, value = pair.partition("=") + if not separator: + raise SystemExit("invalid result field") + if key in {"nrestarts", "result_bytes"}: + payload[key] = int(value) + elif key in {"no_message_send", "live_service_unchanged", "live_profile_unchanged"}: + payload[key] = value == "true" + else: + payload[key] = value +print(json.dumps(payload, sort_keys=True)) +PY +} + +remote_status() { + local request_id="$1" + local target_db="$2" + local active_state sub_state nrestarts dispatcher_hash dispatcher_state + + [[ -x "$REMOTE_DISPATCHER" && ! -L "$REMOTE_DISPATCHER" ]] || die "root-owned remote dispatcher is absent" + dispatcher_state="$(stat -c '%U:%G:%a' "$REMOTE_DISPATCHER")" + [[ "$dispatcher_state" == "root:root:755" ]] || die "remote dispatcher ownership or mode drifted" + dispatcher_hash="$(sha256sum "$REMOTE_DISPATCHER" | awk '{print $1}')" + [[ "$dispatcher_hash" =~ ^[0-9a-f]{64}$ ]] || die "remote dispatcher hash readback was invalid" + active_state="$(systemctl show "$LIVE_SERVICE" --property=ActiveState --value)" + sub_state="$(systemctl show "$LIVE_SERVICE" --property=SubState --value)" + nrestarts="$(systemctl show "$LIVE_SERVICE" --property=NRestarts --value)" + [[ "$active_state" =~ ^[a-z]+$ ]] || die "service ActiveState readback was invalid" + [[ "$sub_state" =~ ^[a-z-]+$ ]] || die "service SubState readback was invalid" + [[ "$nrestarts" =~ ^[0-9]+$ ]] || die "service NRestarts readback was invalid" + + emit_json \ + "status" "$request_id" "$target_db" "pass" \ + "instance=$INSTANCE" \ + "expected_internal_ip=$INSTANCE_INTERNAL_IP" \ + "active_state=$active_state" \ + "sub_state=$sub_state" \ + "nrestarts=$nrestarts" \ + "dispatcher_version=$DISPATCHER_VERSION" \ + "dispatcher_sha256=$dispatcher_hash" +} + +require_root() { + [[ "$EUID" -eq 0 ]] || die "clone operations require the isolated OS Admin identity" + [[ -n "${SUDO_USER:-}" && "$SUDO_USER" != "root" ]] || die "clone operation lacks a distinct OS Login caller" +} + +parity_receipt_relative() { + local target_db="$1" + printf 'docs/reports/leo-working-state-20260709/%s-canonical-parity-receipt.json\n' "$target_db" +} + +write_run_marker() { + local marker="$1" + local request_id="$2" + local target_db="$3" + printf 'request_id=%s\ntarget_db=%s\n' "$request_id" "$target_db" >"$marker" + chmod 0600 "$marker" +} + +validate_run_dir() { + local run_dir="$1" + local request_id="$2" + local target_db="$3" + local marker="$run_dir/.run-owner" + local resolved expected + + [[ -d "$run_dir" && ! -L "$run_dir" ]] || die "exact run-owned directory is absent or unsafe" + resolved="$(realpath "$run_dir")" + expected="$REMOTE_RUN_ROOT/$request_id" + [[ "$resolved" == "$expected" ]] || die "run-owned directory escaped its fixed root" + [[ "$(stat -c '%U:%G:%a' "$run_dir")" == "root:root:700" ]] || die "run directory ownership or mode drifted" + [[ -f "$marker" && ! -L "$marker" ]] || die "run ownership marker is absent or unsafe" + [[ "$(cat "$marker")" == $'request_id='"$request_id"$'\ntarget_db='"$target_db" ]] || { + die "run ownership marker does not match request_id and target_db" + } +} + +remote_upload_path() { + local request_id="$1" + local passwd_entry home resolved upload + + passwd_entry="$(getent passwd "$SUDO_USER")" + [[ -n "$passwd_entry" ]] || die "OS Login caller has no passwd entry" + home="$(cut -d: -f6 <<<"$passwd_entry")" + [[ "$home" == /* && -d "$home" && ! -L "$home" ]] || die "OS Login caller home is unsafe" + resolved="$(realpath "$home")" + [[ "$resolved" == "$home" ]] || die "OS Login caller home path drifted" + upload="$home/.teleo-iap-upload-$request_id.tar.gz" + [[ -f "$upload" && ! -L "$upload" ]] || die "request-derived bundle upload is absent or unsafe" + [[ "$(stat -c '%U:%a' "$upload")" == "$SUDO_USER:600" ]] || die "bundle upload owner or mode is unsafe" + printf '%s\n' "$upload" +} + +validate_and_extract_bundle() { + local archive="$1" + local bundle_dir="$2" + local operation="$3" + local request_id="$4" + local target_db="$5" + + python3 - \ + "$archive" "$bundle_dir" "$operation" "$request_id" "$target_db" \ + "$REMOTE_DISPATCHER" "$DISPATCHER_VERSION" "$EXPECTED_WORKFLOW_REF" <<'PY' +import hashlib +import json +import os +import re +import sys +import tarfile +from pathlib import Path + +archive_path = Path(sys.argv[1]) +bundle_dir = Path(sys.argv[2]).resolve() +operation, request_id, target_db = sys.argv[3:6] +dispatcher_path = Path(sys.argv[6]) +dispatcher_version = sys.argv[7] +expected_workflow_ref = sys.argv[8] +common = ["scripts/gcp_iap_operator.sh"] +direct_claim = [ + "scripts/run_gcp_generated_db_direct_claim_suite.py", + "scripts/run_leo_clone_bound_handler_checkpoint.py", + "scripts/working_leo_open_ended_benchmark.py", + "hermes-agent/leoclean-bin/cloudsql_memory_tool.py", + "ops/postgres_parity_manifest.sql", + f"docs/reports/leo-working-state-20260709/{target_db}-canonical-parity-receipt.json", +] +expected = common + (direct_claim if operation == "direct-claim-replay" else []) +if operation not in {"direct-claim-replay", "cleanup-clone"}: + raise SystemExit("unexpected bundle operation") +if archive_path.stat().st_size > 5 * 1024 * 1024: + raise SystemExit("bundle archive exceeds size ceiling") + +with tarfile.open(archive_path, mode="r:gz") as archive: + members = archive.getmembers() + expected_members = sorted([*expected, "bundle-manifest.json"]) + names = [member.name for member in members] + if sorted(names) != expected_members or len(names) != len(set(names)): + raise SystemExit("bundle member allowlist mismatch") + if any(not member.isfile() or member.size > 2 * 1024 * 1024 for member in members): + raise SystemExit("bundle contains a link, special entry, or oversized file") + manifest_member = archive.getmember("bundle-manifest.json") + if manifest_member.size > 64 * 1024: + raise SystemExit("bundle manifest exceeds size ceiling") + manifest = json.loads(archive.extractfile(manifest_member).read()) + required = { + "schema": "livingip.gcpIapOperatorBundle.v1", + "dispatcher_version": dispatcher_version, + "operation": operation, + "request_id": request_id, + "target_db": target_db, + "ref": "refs/heads/main", + "workflow_ref": expected_workflow_ref, + } + if any(manifest.get(key) != value for key, value in required.items()): + raise SystemExit("bundle manifest context mismatch") + if not re.fullmatch(r"[0-9a-f]{40}", str(manifest.get("git_commit") or "")): + raise SystemExit("bundle manifest commit is invalid") + files = manifest.get("files") + if not isinstance(files, dict) or sorted(files) != sorted(expected): + raise SystemExit("bundle manifest file allowlist mismatch") + payloads = {} + for relative in expected: + member = archive.getmember(relative) + data = archive.extractfile(member).read() + receipt = files.get(relative) or {} + if receipt.get("size") != len(data) or receipt.get("sha256") != hashlib.sha256(data).hexdigest(): + raise SystemExit(f"bundle hash mismatch: {relative}") + payloads[relative] = data + dispatcher_sha = hashlib.sha256(dispatcher_path.read_bytes()).hexdigest() + if dispatcher_sha != files["scripts/gcp_iap_operator.sh"]["sha256"]: + raise SystemExit("installed dispatcher does not match the reviewed main bundle") + manifest_bytes = archive.extractfile(manifest_member).read() + +for relative, data in sorted({**payloads, "bundle-manifest.json": manifest_bytes}.items()): + destination = (bundle_dir / relative).resolve() + if bundle_dir not in destination.parents: + raise SystemExit("bundle destination escaped the run directory") + destination.parent.mkdir(mode=0o700, parents=True, exist_ok=True) + os.chmod(destination.parent, 0o700) + with destination.open("xb") as handle: + handle.write(data) + os.chmod(destination, 0o600) +PY +} + +receive_and_validate_bundle() { + local operation="$1" + local request_id="$2" + local target_db="$3" + local upload run_dir archive bundle_dir + + require_root + upload="$(remote_upload_path "$request_id")" + run_dir="$REMOTE_RUN_ROOT/$request_id" + if [[ "$operation" == "direct-claim-replay" ]]; then + [[ ! -e "$run_dir" ]] || die "request_id already owns a remote run directory" + install -d -o root -g root -m 0700 "$run_dir" + write_run_marker "$run_dir/.run-owner" "$request_id" "$target_db" + else + validate_run_dir "$run_dir" "$request_id" "$target_db" + fi + archive="$run_dir/$operation-bundle.tar.gz" + bundle_dir="$run_dir/$operation-bundle" + [[ ! -e "$archive" && ! -e "$bundle_dir" ]] || die "operation bundle already exists in run directory" + mv -- "$upload" "$archive" + chown root:root "$archive" + chmod 0600 "$archive" + install -d -o root -g root -m 0700 "$bundle_dir" + validate_and_extract_bundle "$archive" "$bundle_dir" "$operation" "$request_id" "$target_db" || { + die "bundle membership, hash, context, or dispatcher validation failed" + } + VALIDATED_BUNDLE_DIR="$bundle_dir" + VALIDATED_BUNDLE_SHA256="$(sha256sum "$archive" | awk '{print $1}')" + VALIDATED_BUNDLE_COMMIT="$( + python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["git_commit"])' \ + "$bundle_dir/bundle-manifest.json" + )" +} + +remote_direct_claim_replay() { + local request_id="$1" + local target_db="$2" + local receipt_relative receipt_path run_dir output_path stdout_path stderr_path report_sha result_bytes + + receive_and_validate_bundle "direct-claim-replay" "$request_id" "$target_db" + receipt_relative="$(parity_receipt_relative "$target_db")" + receipt_path="$VALIDATED_BUNDLE_DIR/$receipt_relative" + [[ -s "$receipt_path" && ! -L "$receipt_path" ]] || die "target-specific tracked parity receipt is absent" + [[ -d "$LIVE_PROFILE" ]] || die "live profile required for temporary copy is absent" + + run_dir="$REMOTE_RUN_ROOT/$request_id" + output_path="$run_dir/direct-claim-result.json" + stdout_path="$run_dir/direct-claim.stdout" + stderr_path="$run_dir/direct-claim.stderr" + : >"$stdout_path" + : >"$stderr_path" + chmod 0600 "$stdout_path" "$stderr_path" + + if ! ( + cd "$VALIDATED_BUNDLE_DIR" + /usr/bin/python3 scripts/run_gcp_generated_db_direct_claim_suite.py \ + --execute \ + --target-db "$target_db" \ + --cloudsql-tool hermes-agent/leoclean-bin/cloudsql_memory_tool.py \ + --manifest-sql ops/postgres_parity_manifest.sql \ + --parity-receipt "$receipt_path" \ + --output "$output_path" \ + --host "$CLOUDSQL_HOST" \ + --project "$PROJECT_ID" \ + --password-secret "$CLOUDSQL_SECRET" \ + --service "$LIVE_SERVICE" \ + --live-profile "$LIVE_PROFILE" \ + --run-id "$request_id" + ) >"$stdout_path" 2>"$stderr_path"; then + die "reviewed six-turn clone-bound replay failed; private run files were retained for cleanup" + fi + + [[ -s "$output_path" && ! -L "$output_path" ]] || die "six-turn replay did not retain its private receipt" + python3 - "$output_path" "$target_db" <<'PY' +import json +import sys +from pathlib import Path + +payload = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) +target_db = sys.argv[2] +checks = payload.get("checks") or {} +required = { + "status": payload.get("status") == "pass", + "target": payload.get("target_database") == target_db, + "six_replies": checks.get("six_replies_returned") is True, + "no_message_send": payload.get("posted_to_telegram") is False, + "service_unchanged": checks.get("live_service_unchanged") is True, + "profile_unchanged": checks.get("live_profile_unchanged") is True, + "database_unchanged": checks.get("generated_database_unchanged") is True, + "temporary_profile_removed": checks.get("temporary_profile_removed") is True, +} +failed = sorted(name for name, ok in required.items() if not ok) +if failed: + raise SystemExit("replay receipt failed fixed checks: " + ", ".join(failed)) +PY + chmod 0600 "$output_path" + report_sha="$(sha256sum "$output_path" | awk '{print $1}')" + result_bytes="$(wc -c <"$output_path" | tr -d ' ')" + emit_json \ + "direct-claim-replay" "$request_id" "$target_db" "pass" \ + "bundle_commit=$VALIDATED_BUNDLE_COMMIT" \ + "bundle_sha256=$VALIDATED_BUNDLE_SHA256" \ + "result_sha256=$report_sha" \ + "result_bytes=$result_bytes" \ + "no_message_send=true" \ + "live_service_unchanged=true" \ + "live_profile_unchanged=true" +} + +remote_cleanup_clone() { + local request_id="$1" + local target_db="$2" + local run_dir password exists remaining + + receive_and_validate_bundle "cleanup-clone" "$request_id" "$target_db" + run_dir="$REMOTE_RUN_ROOT/$request_id" + validate_run_dir "$run_dir" "$request_id" "$target_db" + command -v gcloud >/dev/null 2>&1 || die "gcloud is unavailable for secret-backed clone cleanup" + command -v psql >/dev/null 2>&1 || die "psql is unavailable for clone cleanup" + + password="$(gcloud secrets versions access latest --secret="$CLOUDSQL_SECRET" --project="$PROJECT_ID" --quiet)" + [[ -n "$password" ]] || die "clone cleanup credential resolved empty" + export PGPASSWORD="$password" + export PGOPTIONS="-c statement_timeout=30000" + exists="$( + psql "host=$CLOUDSQL_HOST port=5432 dbname=postgres user=postgres sslmode=require connect_timeout=8" \ + -X -Atq -v ON_ERROR_STOP=1 -v "target_db=$target_db" \ + -c "select exists(select 1 from pg_database where datname = :'target_db');" + )" + [[ "$exists" == "t" ]] || die "marked clone database is absent; refusing directory-only cleanup" + + psql "host=$CLOUDSQL_HOST port=5432 dbname=postgres user=postgres sslmode=require connect_timeout=8" \ + -X -q -v ON_ERROR_STOP=1 -v "target_db=$target_db" <<'SQL' +select pg_terminate_backend(pid) +from pg_stat_activity +where datname = :'target_db' and pid <> pg_backend_pid(); +drop database :"target_db"; +SQL + remaining="$( + psql "host=$CLOUDSQL_HOST port=5432 dbname=postgres user=postgres sslmode=require connect_timeout=8" \ + -X -Atq -v ON_ERROR_STOP=1 -v "target_db=$target_db" \ + -c "select count(*) from pg_database where datname = :'target_db';" + )" + unset PGPASSWORD PGOPTIONS + password="" + [[ "$remaining" == "0" ]] || die "clone database remained after cleanup" + + validate_run_dir "$run_dir" "$request_id" "$target_db" + rm -rf --one-file-system -- "$run_dir" + [[ ! -e "$run_dir" ]] || die "exact run-owned directory remained after cleanup" + emit_json \ + "cleanup-clone" "$request_id" "$target_db" "pass" \ + "bundle_commit=$VALIDATED_BUNDLE_COMMIT" \ + "bundle_sha256=$VALIDATED_BUNDLE_SHA256" \ + "clone_database_remaining=0" +} + +remote_main() { + [[ "$#" -eq 3 ]] || die "remote mode requires operation, request_id, and target_db" + local operation="$1" + local request_id="$2" + local target_db="$3" + validate_inputs "$operation" "$request_id" "$target_db" + case "$operation" in + status) remote_status "$request_id" "$target_db" ;; + direct-claim-replay) remote_direct_claim_replay "$request_id" "$target_db" ;; + cleanup-clone) remote_cleanup_clone "$request_id" "$target_db" ;; + esac +} + +write_sanitized_result() { + local output_path="$1" + local raw_stdout="$2" + local operation="$3" + local request_id="$4" + local target_db="$5" + local exit_code="$6" + RESULT_PATH="$output_path" RAW_STDOUT="$raw_stdout" OPERATION="$operation" \ + REQUEST_ID="$request_id" TARGET_DB="$target_db" EXIT_CODE="$exit_code" python3 - <<'PY' +import hashlib +import json +import os +from pathlib import Path + +raw = Path(os.environ["RAW_STDOUT"]).read_bytes() +exit_code = int(os.environ["EXIT_CODE"]) +remote = {} +if exit_code == 0: + try: + candidate = json.loads(raw.decode("utf-8").splitlines()[-1]) + allowed = { + "operation", + "request_id", + "target_db", + "status", + "instance", + "expected_internal_ip", + "active_state", + "sub_state", + "nrestarts", + "dispatcher_version", + "dispatcher_sha256", + "bundle_commit", + "bundle_sha256", + "result_sha256", + "result_bytes", + "no_message_send", + "live_service_unchanged", + "live_profile_unchanged", + "clone_database_remaining", + } + remote = {key: value for key, value in candidate.items() if key in allowed} + except (UnicodeDecodeError, json.JSONDecodeError, IndexError): + exit_code = 90 + +payload = { + "schema": "livingip.gcpIapOperatorSanitizedResult.v1", + "operation": os.environ["OPERATION"], + "request_id": os.environ["REQUEST_ID"], + "target_db": os.environ["TARGET_DB"], + "status": "pass" if exit_code == 0 and remote.get("status") == "pass" else "fail", + "ssh_exit_code": exit_code, + "remote_result": remote, + "remote_stdout_sha256": hashlib.sha256(raw).hexdigest(), + "remote_stdout_bytes": len(raw), + "raw_stdout_retained": False, + "raw_stderr_retained": False, + "credential_values_logged": False, + "ssh_debug_enabled": False, +} +Path(os.environ["RESULT_PATH"]).write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8") +PY + chmod 0600 "$output_path" +} + +validate_local_bundle() { + local request_id="$1" + local expected path resolved bundle_root mode + + bundle_root="$RUNNER_TEMP/gcp-iap-operator-bundle" + expected="$bundle_root/$request_id.tar.gz" + path="$expected" + [[ -f "$path" && ! -L "$path" ]] || die "fixed workflow bundle is absent or unsafe" + resolved="$(realpath "$path")" + [[ "$resolved" == "$expected" ]] || die "workflow bundle escaped its request-derived path" + mode="$(python3 -c 'import os,stat,sys; print(oct(stat.S_IMODE(os.stat(sys.argv[1]).st_mode))[2:])' "$path")" + [[ "$mode" == "600" ]] || die "workflow bundle mode is not private" + printf '%s\n' "$path" +} + +runner_main() { + [[ "$#" -eq 3 ]] || die "runner mode requires operation, request_id, and target_db" + local operation="$1" + local request_id="$2" + local target_db="$3" + local temp_dir result_dir result_path raw_stdout raw_stderr scp_stdout ssh_key remote_command exit_code + local sanitized_status bundle_path remote_upload + local -a remote_argv + + validate_inputs "$operation" "$request_id" "$target_db" + [[ -n "${RUNNER_TEMP:-}" && "$RUNNER_TEMP" == /* ]] || die "RUNNER_TEMP must be an absolute runner path" + [[ -n "${RESULT_DIR:-}" && "$RESULT_DIR" == "$RUNNER_TEMP"/* ]] || { + die "RESULT_DIR must be a fixed child of RUNNER_TEMP" + } + command -v gcloud >/dev/null 2>&1 || die "gcloud is unavailable" + install -d -m 0700 "$RESULT_DIR" + result_dir="$(realpath "$RESULT_DIR")" + [[ "$result_dir" == "$RUNNER_TEMP"/* ]] || die "RESULT_DIR escaped RUNNER_TEMP" + result_path="$result_dir/result.json" + : >"$result_path" + chmod 0600 "$result_path" + + temp_dir="$(mktemp -d "$RUNNER_TEMP/teleo-iap-operator.XXXXXX")" + RUNNER_TEMP_DIR="$temp_dir" + chmod 0700 "$temp_dir" + trap cleanup_runner_temp EXIT + trap 'exit 129' HUP + trap 'exit 130' INT + trap 'exit 143' TERM + raw_stdout="$temp_dir/remote.stdout" + raw_stderr="$temp_dir/transport.stderr" + scp_stdout="$temp_dir/scp.stdout" + ssh_key="$temp_dir/ssh_key" + : >"$raw_stdout" + : >"$raw_stderr" + : >"$scp_stdout" + chmod 0600 "$raw_stdout" "$raw_stderr" "$scp_stdout" + + if [[ "$operation" != "status" ]]; then + bundle_path="$(validate_local_bundle "$request_id")" + remote_upload="$INSTANCE:.teleo-iap-upload-$request_id.tar.gz" + set +e + gcloud compute scp "$bundle_path" "$remote_upload" \ + --project="$PROJECT_ID" \ + --zone="$ZONE" \ + --tunnel-through-iap \ + --ssh-key-file="$ssh_key" \ + --ssh-key-expire-after=5m \ + --scp-flag=-p \ + --quiet >"$scp_stdout" 2>>"$raw_stderr" + exit_code=$? + set -e + if [[ "$exit_code" -ne 0 ]]; then + write_sanitized_result "$result_path" "$raw_stdout" "$operation" "$request_id" "$target_db" "$exit_code" + printf 'sanitized_result=%s\n' "$result_path" + return "$exit_code" + fi + fi + + if [[ "$operation" == "status" ]]; then + remote_argv=("$REMOTE_DISPATCHER" --remote "$operation" "$request_id" "$target_db") + else + remote_argv=(sudo -n "$REMOTE_DISPATCHER" --remote "$operation" "$request_id" "$target_db") + fi + printf -v remote_command '%q ' "${remote_argv[@]}" + + set +e + gcloud compute ssh "$INSTANCE" \ + --project="$PROJECT_ID" \ + --zone="$ZONE" \ + --tunnel-through-iap \ + --ssh-key-file="$ssh_key" \ + --ssh-key-expire-after=5m \ + --command="$remote_command" \ + --quiet >"$raw_stdout" 2>>"$raw_stderr" + exit_code=$? + set -e + [[ ! -e "$ssh_key" ]] || chmod 0600 "$ssh_key" + [[ ! -e "$ssh_key.pub" ]] || chmod 0600 "$ssh_key.pub" + write_sanitized_result "$result_path" "$raw_stdout" "$operation" "$request_id" "$target_db" "$exit_code" + sanitized_status="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["status"])' "$result_path")" + if [[ "$exit_code" -eq 0 && "$sanitized_status" != "pass" ]]; then + exit_code=90 + fi + printf 'sanitized_result=%s\n' "$result_path" + return "$exit_code" +} + +if [[ "${1:-}" == "--remote" ]]; then + shift + remote_main "$@" +else + runner_main "$@" +fi diff --git a/scripts/run_gcp_generated_db_working_leo_suite.py b/scripts/run_gcp_generated_db_working_leo_suite.py new file mode 100644 index 0000000..1e81462 --- /dev/null +++ b/scripts/run_gcp_generated_db_working_leo_suite.py @@ -0,0 +1,1772 @@ +#!/usr/bin/env python3 +"""Run the complete no-send Working Leo suite against one generated Cloud SQL DB. + +The script is an orchestration and transport adapter. Benchmark semantics, +composition packets/checks, proposal lifecycle transitions, and GatewayRunner +session behavior remain owned by their existing modules. +""" + +from __future__ import annotations + +import argparse +import ast +import asyncio +import copy +import hashlib +import ipaddress +import json +import os +import shlex +import subprocess +import sys +import tempfile +import textwrap +import uuid +from collections.abc import Callable +from pathlib import Path +from types import SimpleNamespace +from typing import Any + +HERE = Path(__file__).resolve().parent +REPO_ROOT = HERE.parent +sys.path.insert(0, str(HERE)) + +import apply_proposal as apply_proposal # noqa: E402 +import approve_proposal # noqa: E402 +import run_approve_claim_clone_canary as guarded # noqa: E402 +import run_gcp_generated_db_direct_claim_suite as gcp # noqa: E402 +import run_leo_clone_bound_handler_checkpoint as bound # noqa: E402 +import run_leo_clone_composition_checkpoint as composition # noqa: E402 +import run_leo_clone_lifecycle_checkpoint as lifecycle # noqa: E402 +import working_leo_open_ended_benchmark as benchmark # noqa: E402 + +SCHEMA = "livingip.gcpGeneratedDbWorkingLeoSuite.v1" +VALIDATION_SCHEMA = "livingip.gcpGeneratedDbWorkingLeoValidation.v1" +TARGET_DB_PREFIX = "teleo_clone_" +EXPECTED_CATALOG_COUNT = 20 +EXPECTED_COMPOSITION_CHECK_COUNT = 34 +ALLOWED_WRITE_PURPOSES = frozenset({"stage", "review", "apply"}) +DEFAULT_STAGE_ROLE = "kb_stage" +DEFAULT_REVIEW_ROLE = "kb_review" +DEFAULT_APPLY_ROLE = "kb_apply" +TARGET_IDENTITY_ARTIFACT = "gcp_generated_database_target_identity" +SOURCE_BASELINE_ARTIFACT = "gcp_working_leo_source_baseline" +ROLE_RECEIPT_PREFIX = "__GCP_ROLE_RECEIPT__" +UNSUPPORTED_INHERITED_COMPOSITION_CHECKS = frozenset( + { + "production_database_unchanged", + "production_gate_schema_unchanged", + } +) +UNSUPPORTED_INHERITED_LIFECYCLE_CHECKS = frozenset({"production_row_fingerprints_unchanged"}) + +PINNED_RUNTIME_PATHS = ( + Path(__file__).resolve(), + HERE / "run_gcp_generated_db_direct_claim_suite.py", + HERE / "run_leo_clone_bound_handler_checkpoint.py", + HERE / "run_leo_clone_composition_checkpoint.py", + HERE / "run_leo_clone_lifecycle_checkpoint.py", + HERE / "working_leo_open_ended_benchmark.py", + HERE / "stage_normalized_proposal.py", + HERE / "approve_proposal.py", + HERE / "apply_proposal.py", +) + + +def sha256_json(value: Any) -> str: + payload = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + return hashlib.sha256(payload.encode()).hexdigest() + + +def safe_message(exc: BaseException, *, secret_values: tuple[str, ...] = ()) -> str: + return bound.redact_text(str(exc), secret_values=secret_values) + + +def load_pinned_receipt(path: Path, expected_sha256: str, artifact: str) -> tuple[dict[str, Any], str]: + try: + content = path.read_bytes() + payload = json.loads(content) + except (OSError, json.JSONDecodeError) as exc: + raise bound.CheckpointError(f"{artifact} receipt is unreadable") from exc + actual_sha256 = hashlib.sha256(content).hexdigest() + if actual_sha256 != expected_sha256: + raise bound.CheckpointError(f"{artifact} receipt does not match its supplied SHA-256 pin") + if not isinstance(payload, dict) or payload.get("artifact") != artifact: + raise bound.CheckpointError(f"{artifact} receipt has the wrong artifact type") + if payload.get("status") != "pass" or payload.get("problems") != []: + raise bound.CheckpointError(f"{artifact} receipt is not a clean pass") + return payload, actual_sha256 + + +def validate_target_identity_receipt( + args: argparse.Namespace, path: Path, expected_sha256: str +) -> dict[str, Any]: + payload, actual_sha256 = load_pinned_receipt(path, expected_sha256, TARGET_IDENTITY_ARTIFACT) + required = { + "target_database": args.target_db, + "project": args.project, + "instance": args.instance, + "server_address": args.host, + } + mismatches = [key for key, expected in required.items() if payload.get(key) != expected] + if mismatches: + raise bound.CheckpointError("target identity receipt mismatch: " + ", ".join(sorted(mismatches))) + if not str(payload.get("system_identifier") or "").strip(): + raise bound.CheckpointError("target identity receipt is missing system_identifier") + try: + address = ipaddress.ip_address(str(payload.get("server_address") or "")) + except ValueError as exc: + raise bound.CheckpointError("target identity receipt has an invalid server address") from exc + if not address.is_private: + raise bound.CheckpointError("target identity receipt server address is not private") + return { + "sha256": actual_sha256, + "target_database": payload["target_database"], + "project": payload["project"], + "instance": payload["instance"], + "server_address": payload["server_address"], + "system_identifier": str(payload["system_identifier"]), + "captured_at_utc": payload.get("captured_at_utc"), + } + + +def validate_source_baseline_receipt( + args: argparse.Namespace, path: Path, expected_sha256: str +) -> dict[str, Any]: + payload, actual_sha256 = load_pinned_receipt(path, expected_sha256, SOURCE_BASELINE_ARTIFACT) + if payload.get("project") != args.project or payload.get("instance") != args.instance: + raise bound.CheckpointError("source baseline receipt project/instance context does not match") + guard = payload.get("guard_snapshot") + gate = payload.get("gate_schema") + if not isinstance(guard, dict) or not isinstance(gate, dict): + raise bound.CheckpointError("source baseline receipt is missing guard_snapshot or gate_schema") + identity = guard.get("database_identity") or {} + if not identity.get("current_database") or not identity.get("system_identifier"): + raise bound.CheckpointError("source baseline receipt has incomplete physical database identity") + for section in ("counts", "rowset_md5"): + values = guard.get(section) + missing = [table for table in composition.PRODUCTION_TABLES if table not in (values or {})] + if missing: + raise bound.CheckpointError( + f"source baseline receipt {section} is missing tables: " + ", ".join(sorted(missing)) + ) + return { + "sha256": actual_sha256, + "project": payload["project"], + "instance": payload["instance"], + "source_database": identity["current_database"], + "source_system_identifier": str(identity["system_identifier"]), + "guard_snapshot": guard, + "gate_schema": gate, + "captured_at_utc": payload.get("captured_at_utc"), + } + + +def runtime_manifest() -> list[dict[str, Any]]: + return [ + { + "repo_relative_path": path.relative_to(REPO_ROOT).as_posix(), + "sha256": hashlib.sha256(path.read_bytes()).hexdigest(), + "size_bytes": path.stat().st_size, + } + for path in PINNED_RUNTIME_PATHS + ] + + +def full_catalog() -> list[dict[str, Any]]: + catalog = benchmark.prompt_catalog(include_cory_style=True, include_direct_claim_followups=True) + if len(catalog) != EXPECTED_CATALOG_COUNT: + raise bound.CheckpointError( + f"Working Leo catalog drifted: expected {EXPECTED_CATALOG_COUNT}, found {len(catalog)}" + ) + return catalog + + +def composition_check_contract() -> list[str]: + """Read the existing composition check names without restating them here.""" + tree = ast.parse(textwrap.dedent(Path(composition.__file__).read_text(encoding="utf-8"))) + for node in ast.walk(tree): + if not isinstance(node, ast.Dict): + continue + keys = [key.value for key in node.keys if isinstance(key, ast.Constant) and isinstance(key.value, str)] + if "source_packet_validated" in keys and "isolated_restart_and_memory_survived" in keys: + if len(keys) != EXPECTED_COMPOSITION_CHECK_COUNT: + raise bound.CheckpointError( + "composition scorer drifted: " + f"expected {EXPECTED_COMPOSITION_CHECK_COUNT} checks, found {len(keys)}" + ) + return keys + raise bound.CheckpointError("existing composition scorer contract could not be located") + + +def validate_target_database(target_db: str) -> None: + if not bound.SAFE_DB_NAME_RE.fullmatch(target_db): + raise bound.CheckpointError("--target-db must be a safe explicit Postgres database name") + if not target_db.startswith(TARGET_DB_PREFIX): + raise bound.CheckpointError( + f"--target-db must start with {TARGET_DB_PREFIX!r}; live/fallback databases are forbidden" + ) + + +def validate_local_inputs(args: argparse.Namespace) -> dict[str, Any]: + validate_target_database(args.target_db) + tool_sha = gcp.validate_reviewed_file( + args.cloudsql_tool, + gcp.REVIEWED_CLOUDSQL_TOOL_SHA256, + "Cloud SQL bridge helper", + ) + manifest_sha = gcp.validate_reviewed_file( + args.manifest_sql, + gcp.REVIEWED_MANIFEST_SQL_SHA256, + "Postgres parity manifest", + ) + parity = gcp.validate_parity_receipt(args.parity_receipt, args.target_db) + catalog = full_catalog() + composition_checks = composition_check_contract() + gcp_composition_checks = [ + name for name in composition_checks if name not in UNSUPPORTED_INHERITED_COMPOSITION_CHECKS + ] + source_manifest = runtime_manifest() + target_receipt_path = getattr(args, "target_identity_receipt", None) + target_receipt_pin = getattr(args, "target_identity_sha256", None) + source_receipt_path = getattr(args, "source_baseline_receipt", None) + source_receipt_pin = getattr(args, "source_baseline_sha256", None) + missing_execute_inputs = [ + flag + for flag, value in ( + ("--target-identity-receipt", target_receipt_path), + ("--target-identity-sha256", target_receipt_pin), + ("--source-baseline-receipt", source_receipt_path), + ("--source-baseline-sha256", source_receipt_pin), + ("--instance", getattr(args, "instance", None)), + ) + if not value + ] + target_identity_receipt = ( + validate_target_identity_receipt(args, target_receipt_path, target_receipt_pin) + if target_receipt_path and target_receipt_pin + else None + ) + source_baseline_receipt = ( + validate_source_baseline_receipt(args, source_receipt_path, source_receipt_pin) + if source_receipt_path and source_receipt_pin + else None + ) + if getattr(args, "execute", False) and missing_execute_inputs: + raise bound.CheckpointError("execute inputs are missing: " + ", ".join(missing_execute_inputs)) + return { + "reviewed_inputs": { + "cloudsql_tool_sha256": tool_sha, + "manifest_sql_sha256": manifest_sha, + }, + "parity_receipt": parity, + "runtime_manifest": source_manifest, + "runtime_manifest_sha256": sha256_json(source_manifest), + "catalog_contract": { + "count": len(catalog), + "ids": [row["id"] for row in catalog], + "catalog_sha256": sha256_json(catalog), + "scorer": "working_leo_open_ended_benchmark.score_results", + }, + "composition_contract": { + "inherited_check_count": len(composition_checks), + "inherited_check_names": composition_checks, + "inherited_checks_sha256": sha256_json(composition_checks), + "inherited_34_of_34_claimable": False, + "unsupported_in_gcp": sorted(UNSUPPORTED_INHERITED_COMPOSITION_CHECKS), + "gcp_required_check_count": len(gcp_composition_checks), + "gcp_required_check_names": gcp_composition_checks, + "gcp_required_checks_sha256": sha256_json(gcp_composition_checks), + "scorer": "run_leo_clone_composition_checkpoint.run_checkpoint", + }, + "target_identity_receipt": target_identity_receipt, + "source_baseline_receipt": source_baseline_receipt, + "execute_input_contract": { + "ready": not missing_execute_inputs, + "missing": missing_execute_inputs, + }, + } + + +def capability_receipt_sql() -> str: + return f"""select {apply_proposal.sql_literal(ROLE_RECEIPT_PREFIX)} || jsonb_build_object( + 'session_user', session_user, + 'current_user', current_user, + 'current_database', current_database(), + 'transaction_read_only', current_setting('transaction_read_only'), + 'default_transaction_read_only', current_setting('default_transaction_read_only'), + 'is_superuser', coalesce((select rolsuper from pg_roles where rolname = session_user), false), + 'can_select_proposals', has_table_privilege(session_user, 'kb_stage.kb_proposals', 'SELECT'), + 'can_insert_proposals', has_table_privilege(session_user, 'kb_stage.kb_proposals', 'INSERT'), + 'can_update_proposals', has_table_privilege(session_user, 'kb_stage.kb_proposals', 'UPDATE'), + 'can_approve', has_function_privilege( + session_user, 'kb_stage.approve_strict_proposal(uuid,text,jsonb,text,text)', 'EXECUTE' + ), + 'can_assert_apply', has_function_privilege( + session_user, 'kb_stage.assert_approved_proposal(uuid,text,jsonb,text,uuid,timestamptz,text)', 'EXECUTE' + ), + 'can_finish_apply', has_function_privilege( + session_user, + 'kb_stage.finish_approved_proposal(uuid,text,jsonb,text,uuid,timestamptz,text,text)', + 'EXECUTE' + ), + 'can_insert_canonical', + has_table_privilege(session_user, 'public.claims', 'INSERT') + and has_table_privilege(session_user, 'public.sources', 'INSERT') + and has_table_privilege(session_user, 'public.claim_evidence', 'INSERT') + and has_table_privilege(session_user, 'public.claim_edges', 'INSERT') +)::text; +""" + + +def validate_capability_receipt( + receipt: dict[str, Any], *, args: argparse.Namespace, purpose: str, role: str, writable: bool +) -> None: + expected_roles = { + "read": args.read_role, + "stage": args.stage_role, + "review": "kb_review", + "apply": "kb_apply", + } + if role != expected_roles[purpose]: + raise bound.CheckpointError(f"{purpose} phase used an unexpected role") + checks = { + "session_user": receipt.get("session_user") == role, + "current_user": receipt.get("current_user") == role, + "current_database": receipt.get("current_database") == args.target_db, + "default_transaction_read_only": receipt.get("default_transaction_read_only") + == ("off" if writable else "on"), + "not_superuser": receipt.get("is_superuser") is False, + "can_select_proposals": receipt.get("can_select_proposals") is True, + "cannot_update_proposals": receipt.get("can_update_proposals") is False, + } + expected_capabilities = { + "read": { + "can_insert_proposals": False, + "can_approve": False, + "can_assert_apply": False, + "can_finish_apply": False, + "can_insert_canonical": False, + }, + "stage": { + "can_insert_proposals": True, + "can_approve": False, + "can_assert_apply": False, + "can_finish_apply": False, + "can_insert_canonical": False, + }, + "review": { + "can_insert_proposals": False, + "can_approve": True, + "can_assert_apply": False, + "can_finish_apply": False, + "can_insert_canonical": False, + }, + "apply": { + "can_insert_proposals": False, + "can_approve": False, + "can_assert_apply": True, + "can_finish_apply": True, + "can_insert_canonical": True, + }, + } + checks.update( + { + name: receipt.get(name) is expected + for name, expected in expected_capabilities[purpose].items() + } + ) + failed = sorted(name for name, passed in checks.items() if not passed) + if failed: + raise bound.CheckpointError(f"{purpose} phase capability receipt failed: " + ", ".join(failed)) + + +class CloudSqlExecutor: + """Exact-target Cloud SQL transport with phase-scoped write authority.""" + + def __init__(self, args: argparse.Namespace) -> None: + self.args = args + self.receipts: list[dict[str, Any]] = [] + + def _password(self, secret_name: str) -> str: + try: + value = gcp.run( + [ + "gcloud", + "secrets", + "versions", + "access", + "latest", + f"--secret={secret_name}", + f"--project={self.args.project}", + ] + ).strip() + except BaseException as exc: + raise bound.CheckpointError("Cloud SQL credential resolution failed") from exc + if not value: + raise bound.CheckpointError(f"Cloud SQL credential secret {secret_name!r} resolved empty") + return value + + def execute( + self, + sql: str, + *, + role: str, + password_secret: str, + purpose: str = "read", + writable: bool = False, + ) -> str: + if purpose not in {"read", *ALLOWED_WRITE_PURPOSES}: + raise bound.CheckpointError("unknown database authority phase") + if writable and purpose not in ALLOWED_WRITE_PURPOSES: + raise bound.CheckpointError("write mode is allowed only for stage/review/apply controller phases") + if writable and not getattr(self.args, f"allow_{purpose}", False): + raise bound.CheckpointError(f"controller phase {purpose!r} was not explicitly authorized") + validate_target_database(self.args.target_db) + if not bound.SAFE_DB_NAME_RE.fullmatch(role): + raise bound.CheckpointError("Cloud SQL role must be an explicit safe identifier") + if writable and role == "postgres": + raise bound.CheckpointError("postgres write authority is forbidden") + if purpose == "stage" and role in { + self.args.read_role, + self.args.review_role, + self.args.apply_role, + "postgres", + }: + raise bound.CheckpointError("stage authority must use a dedicated non-postgres role") + if purpose == "review" and role != "kb_review": + raise bound.CheckpointError("review authority must use kb_review") + if purpose == "apply" and role != "kb_apply": + raise bound.CheckpointError("apply authority must use kb_apply") + password = self._password(password_secret) + pgoptions = "-c default_transaction_read_only=off" if writable else "-c default_transaction_read_only=on" + identity_guard = """\\set ON_ERROR_STOP on +select current_database() = :'expected_database' as exact_database_bound \\gset +\\if :exact_database_bound +\\else + \\echo 'refusing database fallback' >&2 + \\quit 97 +\\endif +""" + command = [ + "psql", + ( + f"host={self.args.host} port=5432 dbname={self.args.target_db} " + f"user={role} sslmode=require connect_timeout=8" + ), + "-X", + "-Atq", + "-v", + "ON_ERROR_STOP=1", + "-v", + f"expected_database={self.args.target_db}", + ] + env = {**os.environ, "PGPASSWORD": password, "PGOPTIONS": pgoptions} + try: + try: + output = gcp.run( + command, + env=env, + input_text=identity_guard + capability_receipt_sql() + sql, + ) + except BaseException as exc: + raise bound.CheckpointError(safe_message(exc, secret_values=(password,))) from exc + finally: + env.pop("PGPASSWORD", None) + env.pop("PGOPTIONS", None) + password = "" + lines = output.splitlines() + receipt_lines = [line for line in lines if line.startswith(ROLE_RECEIPT_PREFIX)] + if len(receipt_lines) != 1: + raise bound.CheckpointError(f"{purpose} phase did not return exactly one capability receipt") + try: + capability = json.loads(receipt_lines[0][len(ROLE_RECEIPT_PREFIX) :]) + except json.JSONDecodeError as exc: + raise bound.CheckpointError(f"{purpose} phase returned an invalid capability receipt") from exc + validate_capability_receipt(capability, args=self.args, purpose=purpose, role=role, writable=writable) + self.receipts.append( + { + "purpose": purpose, + "role": role, + "target_database": self.args.target_db, + "writable": writable, + "default_transaction_read_only": "off" if writable else "on", + "sql_sha256": hashlib.sha256(sql.encode()).hexdigest(), + "explicit_database_guard": True, + "capability_receipt": capability, + } + ) + return "\n".join(line for line in lines if not line.startswith(ROLE_RECEIPT_PREFIX)) + ( + "\n" if output.endswith("\n") else "" + ) + + def read( + self, + sql: str, + *, + role: str | None = None, + secret: str | None = None, + purpose: str = "read", + ) -> str: + return self.execute( + sql, + role=role or self.args.read_role, + password_secret=secret or self.args.password_secret, + purpose=purpose, + ) + + def stage(self, sql: str) -> str: + return self.execute( + sql, + role=self.args.stage_role, + password_secret=self.args.stage_password_secret, + purpose="stage", + writable=True, + ) + + def review(self, sql: str) -> str: + return self.execute( + sql, + role=self.args.review_role, + password_secret=self.args.review_password_secret, + purpose="review", + writable=True, + ) + + def apply(self, sql: str) -> str: + return self.execute( + sql, + role=self.args.apply_role, + password_secret=self.args.apply_password_secret, + purpose="apply", + writable=True, + ) + + def json(self, sql: str, *, stage: bool = False) -> Any: + output = self.stage(sql) if stage else self.read(sql) + rows = [line.strip() for line in output.splitlines() if line.strip().startswith(("{", "["))] + if not rows: + raise bound.CheckpointError("Cloud SQL query returned no JSON row") + try: + return json.loads(rows[-1]) + except json.JSONDecodeError as exc: + raise bound.CheckpointError("Cloud SQL query returned invalid JSON") from exc + + +def verify_distinct_credential_values(executor: CloudSqlExecutor) -> dict[str, Any]: + values: list[str] = [] + try: + for secret_name in ( + executor.args.password_secret, + executor.args.stage_password_secret, + executor.args.review_password_secret, + executor.args.apply_password_secret, + ): + values.append(executor._password(secret_name)) + if len(set(values)) != len(values): + raise bound.CheckpointError("read/stage/review/apply credential values must be distinct") + return {"checked": True, "distinct": True, "credential_count": len(values)} + finally: + for index in range(len(values)): + values[index] = "" + values.clear() + + +def _completed(command: list[str], returncode: int, stdout: str = "", stderr: str = "") -> subprocess.CompletedProcess[str]: + return subprocess.CompletedProcess(command, returncode, stdout, stderr) + + +def _load_proposal(executor: CloudSqlExecutor, proposal_id: str, *, role: str, secret: str) -> dict[str, Any]: + args = SimpleNamespace(proposal_id=proposal_id) + sql = f"""select jsonb_build_object( + 'id', id::text, 'proposal_type', proposal_type, 'status', status, + 'payload', payload, 'reviewed_by_handle', reviewed_by_handle, + 'reviewed_by_agent_id', reviewed_by_agent_id::text, + 'reviewed_at', reviewed_at::text, 'review_note', review_note +)::text from kb_stage.kb_proposals +where id = {apply_proposal.sql_literal(args.proposal_id)}::uuid;""" + purpose = "review" if role == executor.args.review_role else "apply" + output = executor.read(sql, role=role, secret=secret, purpose=purpose).strip() + if not output: + raise bound.CheckpointError(f"proposal not found in generated database: {proposal_id}") + return json.loads(output.splitlines()[-1]) + + +def _review_command(executor: CloudSqlExecutor, proposal_id: str, *, dry_run: bool) -> subprocess.CompletedProcess[str]: + command = ["gcp-controller-review", proposal_id] + try: + proposal = _load_proposal( + executor, + proposal_id, + role=executor.args.review_role, + secret=executor.args.review_password_secret, + ) + approve_proposal.assert_approvable(proposal, lifecycle.REVIEWER_HANDLE, lifecycle.REVIEW_NOTE) + sql = approve_proposal.build_approve_sql(proposal, lifecycle.REVIEWER_HANDLE, lifecycle.REVIEW_NOTE) + if dry_run: + return _completed(command, 0, sql) + output = executor.review(sql) + return _completed(command, 0, output) + except (Exception, SystemExit) as exc: + return _completed(command, 1, stderr=safe_message(exc)) + + +def _apply_command(executor: CloudSqlExecutor, proposal_id: str, *, dry_run: bool) -> subprocess.CompletedProcess[str]: + command = ["gcp-controller-apply", proposal_id] + try: + proposal = _load_proposal( + executor, + proposal_id, + role=executor.args.apply_role, + secret=executor.args.apply_password_secret, + ) + apply_proposal.assert_applyable(proposal) + sql = apply_proposal.build_apply_sql(proposal, lifecycle.APPLIED_BY_HANDLE) + if dry_run: + return _completed(command, 0, sql) + output = executor.apply(sql) + return _completed(command, 0, output) + except (Exception, SystemExit) as exc: + return _completed(command, 1, stderr=safe_message(exc)) + + +def _target_identity(args: argparse.Namespace, db_identity: dict[str, Any]) -> dict[str, Any]: + return { + "id": f"cloudsql:{db_identity['system_identifier']}:{args.target_db}", + "name": args.target_db, + "image": "cloudsql-postgres", + "labels": {bound.DISPOSABLE_LABEL_KEY: bound.DISPOSABLE_LABEL_VALUE}, + "network_mode": "none", + "ports": {}, + "mount_sources": [], + "started_at": None, + } + + +def assert_live_target_identity( + args: argparse.Namespace, retained: dict[str, Any], live: dict[str, Any] +) -> None: + checks = { + "target_database": live.get("current_database") == retained.get("target_database") == args.target_db, + "system_identifier": str(live.get("system_identifier") or "") + == str(retained.get("system_identifier") or ""), + "server_address": str(live.get("server_address") or "") + == str(retained.get("server_address") or "") + == args.host, + "project": retained.get("project") == args.project, + "instance": retained.get("instance") == args.instance, + "tls": live.get("ssl") is True, + "default_read_only": live.get("default_transaction_read_only") == "on", + } + failed = sorted(name for name, passed in checks.items() if not passed) + if failed: + raise bound.CheckpointError("live target identity does not match retained receipt: " + ", ".join(failed)) + + +def _source_identity(source_baseline: dict[str, Any]) -> dict[str, Any]: + identity = source_baseline["guard_snapshot"]["database_identity"] + return { + "id": f"retained-source:{identity['system_identifier']}:{identity['current_database']}", + "name": str(identity["current_database"]), + "image": "retained-source-baseline-receipt", + "labels": {}, + "network_mode": "reference-only", + "ports": {}, + "mount_sources": [], + "started_at": None, + } + + +def _build_gcp_read_wrapper( + args: argparse.Namespace, + *, + cloudsql_tool: Path, + tool_log: Path, + run_nonce: str, + prompt_id: str | None = None, +) -> str: + wrapper = gcp.build_cloudsql_wrapper( + cloudsql_tool=cloudsql_tool, + tool_log=tool_log, + target_db=args.target_db, + host=args.host, + project=args.project, + password_secret=args.password_secret, + run_nonce=run_nonce, + ) + wrapper = wrapper.replace( + f"PASSWORD_SECRET={shlex.quote(args.password_secret)}\n", + f"PASSWORD_SECRET={shlex.quote(args.password_secret)}\nREAD_ROLE={shlex.quote(args.read_role)}\n", + 1, + ) + wrapper = wrapper.replace("user=postgres sslmode=require", "user=$READ_ROLE sslmode=require", 1) + wrapper = wrapper.replace( + " 'default_transaction_read_only', current_setting('default_transaction_read_only')\n", + " 'default_transaction_read_only', current_setting('default_transaction_read_only'),\n" + " 'session_user', session_user,\n" + " 'current_user', current_user\n", + 1, + ) + wrapper = wrapper.replace( + '--password-secret "$PASSWORD_SECRET" "$@"', + '--password-secret "$PASSWORD_SECRET" --user "$READ_ROLE" "$@"', + 1, + ) + if prompt_id is not None: + encoded_prompt_id = json.dumps(prompt_id) + wrapper = wrapper.replace( + ' "run_nonce": run_nonce,\n "container":', + f' "run_nonce": run_nonce,\n "prompt_id": {encoded_prompt_id},\n "container":', + ) + if "user=$READ_ROLE" not in wrapper or '--user "$READ_ROLE"' not in wrapper: + raise bound.CheckpointError("reviewed Cloud SQL wrapper could not be rebound to the dedicated read role") + return wrapper + + +def _cloudsql_lifecycle_wrapper( + args: argparse.Namespace, + *, + tool_log: Path, + run_marker: str, + run_nonce: str, + cloudsql_tool: Path | None = None, +) -> str: + base = _build_gcp_read_wrapper( + args, + cloudsql_tool=cloudsql_tool or args.cloudsql_tool, + tool_log=tool_log, + run_nonce=run_nonce, + ) + marker = "if ! PGPASSWORD=\"$(gcloud secrets versions access latest" + if marker not in base: + raise bound.CheckpointError("reviewed Cloud SQL wrapper shape changed; lifecycle injection refused") + internal = shlex.join( + [ + str(Path(sys.executable).resolve()), + str(Path(__file__).resolve()), + lifecycle.INTERNAL_STAGE_COMMAND, + "--target-db", + args.target_db, + "--host", + args.host, + "--project", + args.project, + "--instance", + args.instance, + "--password-secret", + args.password_secret, + "--read-role", + args.read_role, + "--stage-password-secret", + args.stage_password_secret, + "--stage-role", + args.stage_role, + "--run-marker", + run_marker, + "--tool-log", + str(tool_log), + "--run-nonce", + run_nonce, + "--target-identity-receipt", + str(args.target_identity_receipt), + "--target-identity-sha256", + args.target_identity_sha256, + ] + ) + branch = f"""if [[ "${{1:-}}" == {shlex.quote(lifecycle.STAGE_COMMAND)} ]]; then + if [[ "$#" -ne 1 ]]; then + printf '%s\\n' '{lifecycle.STAGE_COMMAND} accepts no model-supplied arguments' >&2 + exit 64 + fi + exec {internal} +fi + +""" + return base.replace(marker, branch + marker, 1) + + +class RuntimeAdapter: + """Temporarily route existing clone runner transport through Cloud SQL.""" + + def __init__( + self, + args: argparse.Namespace, + executor: CloudSqlExecutor, + source_baseline: dict[str, Any], + db_identity: dict[str, Any], + ) -> None: + self.args = args + self.executor = executor + self.source_baseline = source_baseline + self.target = _target_identity(args, db_identity) + self.source = _source_identity(source_baseline) + self.originals: list[tuple[Any, str, Any]] = [] + + def patch(self, module: Any, name: str, replacement: Any) -> None: + self.originals.append((module, name, getattr(module, name))) + setattr(module, name, replacement) + + def _psql_json(self, container: str, database: str, sql: str) -> Any: + self._assert_target(container, database) + normalized = sql.lower() + stage = "normalized_stage_result" in normalized or "lifecycle_stage_result" in normalized + if not stage and any(token in normalized for token in ("insert into", "update ", "delete from", "drop ")): + raise bound.CheckpointError("unclassified SQL write refused by GCP runtime adapter") + return self.executor.json(sql, stage=stage) + + def _assert_target(self, container: str, database: str) -> None: + if database != self.args.target_db or container not in {self.args.target_db, self.target["id"]}: + raise bound.CheckpointError("database transport attempted to rebind away from supplied generated DB") + + def _container_identity(self, container: str) -> dict[str, Any]: + if container in {self.args.target_db, self.target["id"]}: + return copy.deepcopy(self.target) + if container == bound.PRODUCTION_CONTAINER: + return copy.deepcopy(self.source) + raise bound.CheckpointError(f"unknown database identity requested: {container!r}") + + def _guard_snapshot(self, container: str, database: str, *, tables: tuple[str, ...]) -> dict[str, Any]: + if container == bound.PRODUCTION_CONTAINER: + return copy.deepcopy(self.source_baseline["guard_snapshot"]) + self._assert_target(container, database) + return self.saved_guard_snapshot(self.target["id"], self.args.target_db, tables=tables) + + def _gate_schema(self, container: str, database: str) -> dict[str, Any]: + if container == bound.PRODUCTION_CONTAINER: + return copy.deepcopy(self.source_baseline["gate_schema"]) + self._assert_target(container, database) + return self.saved_gate_schema(self.target["id"], self.args.target_db) + + def _bound_patch_bridge(self, temp_profile: Path, _container: str, _database: str) -> dict[str, Any]: + bridge = gcp.patch_temp_bridge(self.args, temp_profile) + wrapper = Path(bridge["wrapper_path"]) + wrapper_text = _build_gcp_read_wrapper( + self.args, + cloudsql_tool=temp_profile / "bin" / "cloudsql_memory_tool.py", + tool_log=Path(bridge["tool_log_path"]), + run_nonce=bridge["run_nonce"], + ) + bound._detach_and_write(wrapper, wrapper_text, mode=0o700) + bridge["wrapper_sha256"] = hashlib.sha256(wrapper_text.encode()).hexdigest() + bridge["bridge_skill_path"] = str(temp_profile / "skills" / "teleo-kb-bridge" / "SKILL.md") + bridge["bound_container_id"] = self.target["id"] + return bridge + + def _read_tool_proof(self, path: Path, _container: str, _database: str, **kwargs: Any) -> dict[str, Any]: + return self.saved_read_tool_proof(path, gcp.SOURCE_COMPUTE, self.args.target_db, **kwargs) + + def _copy_profile(self, *, run_id: str, prompt_id: str, **_kwargs: Any) -> tuple[Path, dict[str, Any]]: + return self.saved_copy_profile(run_id=run_id, prompt_id=prompt_id, live_profile=self.args.live_profile) + + def _copy_auth(self, temp_profile: Path, **_kwargs: Any) -> dict[str, Any]: + return self.saved_copy_auth(temp_profile, live_profile=self.args.live_profile) + + def _guarded_psql(self, container: str, database: str, sql: str, *, tuples: bool = True) -> str: + del tuples + self._assert_target(container, database) + return self.executor.read(sql) + + def _approve(self, _guard_args: argparse.Namespace, proposal_id: str, database: str, *, dry_run: bool = False) -> subprocess.CompletedProcess[str]: + if database != self.args.target_db: + raise bound.CheckpointError("review attempted database fallback") + return _review_command(self.executor, proposal_id, dry_run=dry_run) + + def _apply(self, _guard_args: argparse.Namespace, proposal_id: str, database: str, *, dry_run: bool = False) -> subprocess.CompletedProcess[str]: + if database != self.args.target_db: + raise bound.CheckpointError("apply attempted database fallback") + return _apply_command(self.executor, proposal_id, dry_run=dry_run) + + def _lifecycle_wrapper(self, *, tool_log: Path, run_marker: str, run_nonce: str, **_kwargs: Any) -> str: + return _cloudsql_lifecycle_wrapper( + self.args, + tool_log=tool_log, + run_marker=run_marker, + run_nonce=run_nonce, + cloudsql_tool=tool_log.parent / "bin" / "cloudsql_memory_tool.py", + ) + + def __enter__(self) -> RuntimeAdapter: + try: + self.saved_psql_json = bound._psql_json + self.saved_guard_snapshot = bound.database_guard_snapshot + self.saved_gate_schema = composition.gate_schema_manifest + self.saved_read_tool_proof = bound.read_tool_proof + self.saved_copy_profile = bound.copy_profile + self.saved_copy_auth = bound.copy_ephemeral_model_auth + self.patch(bound, "_psql_json", self._psql_json) + self.target_gate = self.saved_gate_schema(self.target["id"], self.args.target_db) + + self.patch(bound, "container_identity", self._container_identity) + self.patch(bound, "database_guard_snapshot", self._guard_snapshot) + self.patch(bound, "service_state", lambda: gcp.service_state(self.args.service)) + self.patch(bound, "bridge_hashes", lambda _profile=None: gcp.profile_hashes(self.args.live_profile)) + self.patch(bound, "patch_temp_bridge", self._bound_patch_bridge) + self.patch(bound, "read_tool_proof", self._read_tool_proof) + self.patch(bound, "copy_profile", self._copy_profile) + self.patch(bound, "copy_ephemeral_model_auth", self._copy_auth) + self.patch(guarded, "_psql", self._guarded_psql) + self.patch(guarded, "_approve", self._approve) + self.patch(guarded, "_apply", self._apply) + self.patch( + lifecycle, + "assert_disposable_target", + self._assert_disposable_target, + ) + self.patch(lifecycle, "build_lifecycle_wrapper", self._lifecycle_wrapper) + self.patch(composition, "gate_schema_manifest", self._gate_schema) + return self + except BaseException: + self._restore() + raise + + def _assert_disposable_target( + self, container: str, database: str + ) -> tuple[dict[str, Any], dict[str, Any]]: + self._assert_target(container, database) + target = self._container_identity(container) + source = self._container_identity(bound.PRODUCTION_CONTAINER) + bound.validate_disposable_target_identity(target, source) + return target, source + + def __exit__(self, _type: Any, _value: Any, _traceback: Any) -> None: + self._restore() + + def _restore(self) -> None: + for module, name, original in reversed(self.originals): + setattr(module, name, original) + self.originals.clear() + + +def database_identity_receipt(executor: CloudSqlExecutor) -> dict[str, Any]: + return executor.json( + """begin transaction read only; +select jsonb_build_object( + 'current_database', current_database(), + 'system_identifier', (select system_identifier::text from pg_control_system()), + 'server_address', host(inet_server_addr()), + 'server_port', inet_server_port(), + 'ssl', coalesce((select ssl from pg_stat_ssl where pid=pg_backend_pid()), false), + 'transaction_read_only', current_setting('transaction_read_only'), + 'default_transaction_read_only', current_setting('default_transaction_read_only'), + 'session_user', session_user, + 'current_user', current_user +)::text; +rollback; +""" + ) + + +def _catalog_turn_args(args: argparse.Namespace, prompt: dict[str, Any]) -> SimpleNamespace: + return SimpleNamespace( + chat_id=args.chat_id, + user_id=args.user_id, + user_name=args.user_name, + prompt=prompt["message"], + prompt_id=prompt["id"], + run_id=args.run_id, + turn_timeout=args.turn_timeout, + ) + + +async def run_catalog(args: argparse.Namespace, db_identity: dict[str, Any]) -> dict[str, Any]: + catalog = full_catalog() + report: dict[str, Any] = { + "catalog_count": len(catalog), + "results": [], + "errors": [], + } + for index, prompt in enumerate(catalog, 1): + try: + result = await run_catalog_prompt(args, prompt, db_identity, index=index) + except Exception as exc: + result = { + "turn": index, + "prompt_id": prompt["id"], + "dimension": prompt["dimension"], + "prompt": prompt["message"], + "reply": "", + "evidence_pass": False, + "pass": False, + "errors": [{"type": type(exc).__name__, "message": safe_message(exc)}], + } + report["errors"].append( + {"prompt_id": prompt["id"], "type": type(exc).__name__, "message": safe_message(exc)} + ) + report["results"].append(result) + report["score"] = benchmark.score_results( + report["results"], + include_cory_style=True, + include_direct_claim_followups=True, + ) + scores = {row["prompt_id"]: row for row in report["score"]["scores"]} + for result in report["results"]: + scorer_pass = scores.get(result["prompt_id"], {}).get("pass") is True + result["scorer_pass"] = scorer_pass + result["pass"] = scorer_pass and result.get("evidence_pass") is True + isolation = catalog_session_isolation(report["results"]) + report["session_isolation"] = isolation + report["checks"] = { + "catalog_20_of_20": report.get("score", {}).get("pass") is True + and report.get("score", {}).get("passes") == EXPECTED_CATALOG_COUNT + and len(report["results"]) == EXPECTED_CATALOG_COUNT, + "every_scored_prompt_has_target_bound_read_evidence": bool(report["results"]) + and all(result.get("evidence_pass") is True for result in report["results"]), + "every_prompt_passes_scorer_and_evidence": bool(report["results"]) + and all(result.get("pass") is True for result in report["results"]), + "send_and_delivery_absent_per_prompt": bool(report["results"]) + and all((result.get("safety") or {}).get("send_and_delivery_absent") is True for result in report["results"]), + "every_gateway_child_and_profile_cleaned": bool(report["results"]) + and all((result.get("cleanup") or {}).get("passes") is True for result in report["results"]), + "fresh_private_profile_per_prompt": isolation["all_profiles_unique"] is True, + "dc_prompts_isolated_from_oe_cs_context": isolation["dc_isolated"] is True, + } + report["status"] = "pass" if not report["errors"] and all(report["checks"].values()) else "fail" + return report + + +def prompt_scoped_tool_evidence( + path: Path, + *, + prompt_id: str, + run_nonce: str, + target_db: str, + db_identity: dict[str, Any], + read_role: str, +) -> dict[str, Any]: + proof = bound.read_tool_proof( + path, + gcp.SOURCE_COMPUTE, + target_db, + run_nonce=run_nonce, + database_identity=db_identity, + require_database_read_only=True, + require_default_read_only=True, + ) + events: list[dict[str, Any]] = [] + parse_errors: list[str] = [] + for number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): + try: + events.append(json.loads(line)) + except json.JSONDecodeError as exc: + parse_errors.append(f"line {number}: {exc}") + prompt_and_nonce_match = bool(events) and all( + event.get("prompt_id") == prompt_id and event.get("run_nonce") == run_nonce for event in events + ) + successful_reads = [ + invocation + for invocation in proof.get("invocations", []) + if invocation.get("returncode") == 0 + and invocation.get("argv") + and invocation["argv"][0] in bound.READ_ONLY_BRIDGE_COMMANDS + ] + role_bound = bool(proof.get("invocations")) and all( + (invocation.get("database_identity") or {}).get("session_user") == read_role + and (invocation.get("database_identity") or {}).get("current_user") == read_role + for invocation in proof["invocations"] + ) + checks = { + "log_parsed": not parse_errors, + "prompt_and_run_nonce_match": prompt_and_nonce_match, + "target_bound_without_fallback": proof.get("all_bound_to_supplied_target") is True, + "default_read_only": proof.get("default_read_only_required") is True, + "all_invocations_successful": proof.get("all_completed_successfully") is True, + "relevant_successful_read_present": bool(successful_reads), + "dedicated_read_role_bound": role_bound, + } + return { + "prompt_id": prompt_id, + "run_nonce": run_nonce, + "checks": checks, + "pass": all(checks.values()), + "proof": proof, + "parse_errors": parse_errors, + } + + +async def run_catalog_prompt( + args: argparse.Namespace, + prompt: dict[str, Any], + db_identity: dict[str, Any], + *, + index: int, +) -> dict[str, Any]: + temp_profile: Path | None = None + provider_environment: dict[str, str] = {} + profile_id = uuid.uuid4().hex + result: dict[str, Any] = { + "turn": index, + "prompt_id": prompt["id"], + "dimension": prompt["dimension"], + "prompt": prompt["message"], + "private_profile_id": profile_id, + "prior_prompt_ids": [], + "fresh_private_profile": False, + "errors": [], + } + try: + temp_profile, copy_audit = bound.copy_profile( + run_id=f"{args.run_id}-{prompt['id']}", + prompt_id=prompt["id"], + live_profile=args.live_profile, + ) + result["profile_copy_audit"] = copy_audit + result["fresh_private_profile"] = copy_audit.get("passes") is True + result["model_auth_binding"] = bound.copy_ephemeral_model_auth( + temp_profile, + live_profile=args.live_profile, + ) + provider_environment, environment_audit = bound.load_ephemeral_provider_environment(temp_profile) + result["model_auth_binding"]["environment_binding"] = environment_audit + bridge = gcp.patch_temp_bridge(args, temp_profile) + wrapper = Path(bridge["wrapper_path"]) + wrapper_text = _build_gcp_read_wrapper( + args, + cloudsql_tool=temp_profile / "bin" / "cloudsql_memory_tool.py", + tool_log=Path(bridge["tool_log_path"]), + run_nonce=bridge["run_nonce"], + prompt_id=prompt["id"], + ) + bound._detach_and_write(wrapper, wrapper_text, mode=0o700) + gateway = await bound.invoke_gateway_subprocess( + _catalog_turn_args(args, prompt), + temp_profile, + provider_environment=provider_environment, + ) + evidence = prompt_scoped_tool_evidence( + Path(bridge["tool_log_path"]), + prompt_id=prompt["id"], + run_nonce=bridge["run_nonce"], + target_db=args.target_db, + db_identity=db_identity, + read_role=args.read_role, + ) + surface = gateway.get("tool_surface") or {} + result.update( + { + "reply": gateway.get("reply") or "", + "gateway": gateway, + "tool_evidence": evidence, + "evidence_pass": evidence["pass"], + "persisted_session_id": (gateway.get("transcript_tool_trace") or {}).get("session_id"), + "safety": { + "send_and_delivery_absent": gateway.get("posted_to_telegram") is False + and surface.get("send_message_tool_enabled") is False + and surface.get("gateway_adapter_count") == 0, + }, + } + ) + except Exception as exc: + result["reply"] = "" + result["evidence_pass"] = False + result["errors"].append( + { + "type": type(exc).__name__, + "message": safe_message(exc, secret_values=tuple(provider_environment.values())), + } + ) + finally: + provider_environment.clear() + child_cleanup = bound.cleanup_active_gateway_children() + root = temp_profile.parent if temp_profile else None + removed = bound.remove_tree(root) if not bound._ACTIVE_GATEWAY_CHILDREN else False + absent = bound.temp_path_absent(root) + result["cleanup"] = { + "gateway_children": child_cleanup, + "profile_removed": removed, + "profile_absent": absent, + "registry_clear": not bound._ACTIVE_GATEWAY_CHILDREN, + "passes": removed and absent and not bound._ACTIVE_GATEWAY_CHILDREN, + } + return result + + +def catalog_session_isolation(results: list[dict[str, Any]]) -> dict[str, bool]: + profile_ids = [result.get("private_profile_id") for result in results] + dc = [result for result in results if str(result.get("prompt_id") or "").startswith("DC-")] + session_ids = [result.get("persisted_session_id") for result in dc] + return { + "all_profiles_unique": bool(profile_ids) + and all(profile_ids) + and len(profile_ids) == len(set(profile_ids)), + "dc_isolated": len(dc) == len(benchmark.CORY_DIRECT_CLAIM_FOLLOWUP_SCENARIOS) + and all(result.get("fresh_private_profile") is True and result.get("prior_prompt_ids") == [] for result in dc) + and all(session_ids) + and len(session_ids) == len(set(session_ids)), + } + + +def catalog_gate_pass(catalog_report: dict[str, Any]) -> bool: + checks = catalog_report.get("checks") or {} + return catalog_report.get("status") == "pass" and bool(checks) and all(checks.values()) + + +async def run_existing_component( + args: argparse.Namespace, + executor: CloudSqlExecutor, + source_baseline: dict[str, Any], + target_identity: dict[str, Any], + *, + kind: str, +) -> dict[str, Any]: + db_identity = database_identity_receipt(executor) + gcp.validate_database_identity(db_identity, args.target_db) + assert_live_target_identity(args, target_identity, db_identity) + with tempfile.TemporaryDirectory(prefix=f"gcp-working-leo-{kind}-") as private_dir: + Path(private_dir).chmod(0o700) + placeholder = Path(private_dir) / "credential-route-only" + placeholder.write_text("credentials resolve through named GCP secrets\n", encoding="utf-8") + output = Path(private_dir) / f"{kind}.json" + common = { + "container": args.target_db, + "db": args.target_db, + "output": output, + "run_marker": lifecycle.new_marker(f"gcp-{kind}"), + "conversation_marker": lifecycle.new_marker(f"gcp-{kind}-memory"), + "chat_id": args.chat_id, + "user_id": args.user_id, + "user_name": args.user_name, + "turn_timeout": args.turn_timeout, + "review_secrets_file": str(placeholder), + "apply_secrets_file": str(placeholder), + "copy_model_auth": True, + "operator_review": args.allow_review, + "guarded_apply": args.allow_apply, + } + component_args = SimpleNamespace(**common) + with RuntimeAdapter(args, executor, source_baseline, db_identity): + if kind == "composition": + result = await composition.run_checkpoint(component_args) + elif kind == "lifecycle": + result = await lifecycle.run_lifecycle(component_args) + else: + raise AssertionError(f"unknown component {kind!r}") + unsupported = ( + UNSUPPORTED_INHERITED_COMPOSITION_CHECKS + if kind == "composition" + else UNSUPPORTED_INHERITED_LIFECYCLE_CHECKS + ) + result["unsupported_inherited_check_values"] = { + name: (result.get("checks") or {}).get(name) for name in sorted(unsupported) + } + for name in unsupported: + (result.get("checks") or {})[name] = None + result["inherited_status_before_gcp_adaptation"] = result.get("status") + result["status"] = "requires_gcp_aggregate_evaluation" + result["claim_ceiling"] = ( + "This embedded component report preserves existing composition/lifecycle semantics against the pinned " + "target and retained source baseline. Source postflight invariants listed as unsupported were not run " + "live and are not counted; only the aggregate GCP evaluation may declare this component passing." + ) + return result + + +def database_manifest_receipt(executor: CloudSqlExecutor, args: argparse.Namespace) -> dict[str, Any]: + rows = gcp.normalize_database_manifest_rows(executor.read(args.manifest_sql.read_text(encoding="utf-8"))) + return { + "target_database": args.target_db, + "rows": rows, + "sha256": sha256_json(rows), + "table_count": sum(row.get("kind") == "table" for row in rows), + "total_rows": sum(int(row.get("row_count") or 0) for row in rows if row.get("kind") == "table"), + } + + +def database_fingerprint_from_manifest(manifest: dict[str, Any]) -> dict[str, Any]: + identity = next(row for row in manifest["rows"] if row.get("kind") == "identity") + return { + "sha256": manifest["sha256"], + "table_count": manifest["table_count"], + "total_rows": manifest["total_rows"], + "database": identity.get("database"), + "server_address": identity.get("server_address"), + "ssl": identity.get("ssl"), + "transaction_read_only": identity.get("transaction_read_only"), + } + + +def _object_fingerprint(before: Any, after: Any) -> dict[str, Any]: + return { + "before": before, + "after": after, + "before_sha256": sha256_json(before), + "after_sha256": sha256_json(after), + "changed": before != after, + } + + +def _object_change_manifest(composition_report: dict[str, Any], lifecycle_report: dict[str, Any]) -> dict[str, Any]: + normalized = composition_report.get("normalized_child") or {} + lifecycle_fixture = lifecycle_report.get("fixture_ids") or {} + composition_rows = composition_report.get("final_canonical_rows") or {} + lifecycle_rows = lifecycle_report.get("final_canonical_rows") or {} + objects = { + "composition_proposal": _object_fingerprint( + None, + (composition_report.get("guarded_apply") or {}).get("applied_readback"), + ), + "composition_approval": _object_fingerprint( + None, + (composition_report.get("operator_review") or {}).get("approval_snapshot"), + ), + "composition_canonical_rows": _object_fingerprint( + {key: [] for key in composition_rows}, + composition_rows, + ), + "lifecycle_proposal": _object_fingerprint(None, lifecycle_report.get("final_applied_readback")), + "lifecycle_approval": _object_fingerprint( + None, + lifecycle_report.get("immutable_approval_readback_after_apply"), + ), + "lifecycle_canonical_rows": _object_fingerprint( + {key: [] for key in lifecycle_rows}, + lifecycle_rows, + ), + } + identifiers = { + "composition_proposal_id": normalized.get("id"), + "composition_claim_ids": (normalized.get("planned_row_ids") or {}).get("claims") or [], + "composition_source_ids": (normalized.get("planned_row_ids") or {}).get("sources") or [], + "lifecycle_proposal_id": lifecycle_fixture.get("proposal_id"), + "lifecycle_claim_id": lifecycle_fixture.get("claim_id"), + "lifecycle_source_id": lifecycle_fixture.get("source_id"), + } + return { + "identifiers": identifiers, + "objects": objects, + "sha256": sha256_json({"identifiers": identifiers, "objects": objects}), + "scope": "generated_rows_only", + "composition_unrelated_rows_unchanged": (composition_report.get("checks") or {}).get( + "unrelated_clone_rows_unchanged" + ), + "lifecycle_unrelated_rows_unchanged": (lifecycle_report.get("checks") or {}).get( + "all_unrelated_clone_rows_unchanged" + ), + } + + +async def run_live_suite(args: argparse.Namespace, validated: dict[str, Any]) -> dict[str, Any]: + executor = CloudSqlExecutor(args) + target_identity = validated.get("target_identity_receipt") or {} + source_baseline = validated.get("source_baseline_receipt") or {} + report: dict[str, Any] = { + "schema": SCHEMA, + "mode": "gcp_generated_db_working_leo_no_send", + "target_database": args.target_db, + "required_tier": "gcp_disposable_copied_cloudsql_database", + "posted_to_telegram": False, + "production_profile_mutation_attempted": False, + "systemd_restart_attempted": False, + "model_authority": ["read", "stage_pending_proposal"], + "controller_authority": { + "stage": args.allow_stage, + "review": args.allow_review, + "apply": args.allow_apply, + "roles": { + "read": args.read_role, + "stage": args.stage_role, + "review": args.review_role, + "apply": args.apply_role, + }, + }, + **validated, + "errors": [], + } + report["credential_value_separation"] = verify_distinct_credential_values(executor) + before_service = gcp.service_state(args.service) + before_profile = gcp.profile_hashes(args.live_profile) + before_identity = database_identity_receipt(executor) + gcp.validate_database_identity(before_identity, args.target_db) + assert_live_target_identity(args, target_identity, before_identity) + before_manifest = database_manifest_receipt(executor, args) + before_fingerprint = database_fingerprint_from_manifest(before_manifest) + report.update( + { + "service_before": before_service, + "live_profile_hashes_before": before_profile, + "database_identity_before": before_identity, + "database_fingerprint_before": before_fingerprint, + "database_manifest_before": before_manifest, + } + ) + phases: tuple[tuple[str, Callable[[], Any]], ...] = ( + ("catalog", lambda: run_catalog(args, before_identity)), + ( + "composition", + lambda: run_existing_component( + args, + executor, + source_baseline, + target_identity, + kind="composition", + ), + ), + ( + "lifecycle", + lambda: run_existing_component( + args, + executor, + source_baseline, + target_identity, + kind="lifecycle", + ), + ), + ) + for name, start in phases: + try: + report[name] = await start() + except Exception as exc: + message = safe_message(exc) + report[name] = {"status": "fail", "errors": [{"type": type(exc).__name__, "message": message}]} + report["errors"].append({"phase": name, "type": type(exc).__name__, "message": message}) + break + + report["gateway_child_cleanup"] = bound.cleanup_active_gateway_children() + postflight: tuple[tuple[str, Callable[[], Any]], ...] = ( + ("service_after", lambda: gcp.service_state(args.service)), + ("live_profile_hashes_after", lambda: gcp.profile_hashes(args.live_profile)), + ("database_identity_after", lambda: database_identity_receipt(executor)), + ("database_manifest_after", lambda: database_manifest_receipt(executor, args)), + ) + for name, capture in postflight: + try: + report[name] = capture() + except Exception as exc: + report[name] = None + report["errors"].append( + {"phase": name, "type": type(exc).__name__, "message": safe_message(exc)} + ) + report["database_fingerprint_after"] = ( + database_fingerprint_from_manifest(report["database_manifest_after"]) + if report.get("database_manifest_after") + else None + ) + if report.get("database_identity_after"): + try: + gcp.validate_database_identity(report["database_identity_after"], args.target_db) + assert_live_target_identity(args, target_identity, report["database_identity_after"]) + except Exception as exc: + report["errors"].append( + { + "phase": "database_identity_after", + "type": type(exc).__name__, + "message": safe_message(exc), + } + ) + + composition_report = report.get("composition") or {} + lifecycle_report = report.get("lifecycle") or {} + report["object_level_changes"] = _object_change_manifest(composition_report, lifecycle_report) + report["database_operation_receipts"] = executor.receipts + runtime_after = runtime_manifest() + report["runtime_manifest_after"] = runtime_after + composition_checks = composition_report.get("checks") or {} + lifecycle_checks = lifecycle_report.get("checks") or {} + gcp_required_names = validated["composition_contract"]["gcp_required_check_names"] + gcp_composition_results = {name: composition_checks.get(name) is True for name in gcp_required_names} + report["composition_gcp_evaluation"] = { + "inherited_34_of_34_claimed": False, + "unsupported_inherited_checks": sorted(UNSUPPORTED_INHERITED_COMPOSITION_CHECKS), + "required_checks": gcp_composition_results, + "passed": bool(gcp_composition_results) + and all(gcp_composition_results.values()) + and not composition_report.get("errors"), + } + source_identity = source_baseline.get("guard_snapshot", {}).get("database_identity", {}) + catalog_report = report.get("catalog") or {} + catalog_checks = catalog_report.get("checks") or {} + operation_purposes = {receipt.get("purpose") for receipt in executor.receipts} + gcp_lifecycle_results = { + name: value is True + for name, value in lifecycle_checks.items() + if name not in UNSUPPORTED_INHERITED_LIFECYCLE_CHECKS + } + report["lifecycle_gcp_evaluation"] = { + "unsupported_inherited_checks": sorted(UNSUPPORTED_INHERITED_LIFECYCLE_CHECKS), + "required_checks": gcp_lifecycle_results, + "passed": bool(gcp_lifecycle_results) + and all(gcp_lifecycle_results.values()) + and not lifecycle_report.get("errors"), + } + report["checks"] = { + "catalog_status_and_all_safety_checks_pass": catalog_gate_pass(catalog_report), + "composition_gcp_non_tautological_checks_pass": report["composition_gcp_evaluation"]["passed"] is True, + "source_baseline_is_independently_retained_and_physically_distinct": bool(source_baseline.get("sha256")) + and source_identity.get("system_identifier") + and str(source_identity.get("system_identifier")) != str(before_identity.get("system_identifier")), + "proposal_lifecycle_exact_under_gcp_supported_checks": report["lifecycle_gcp_evaluation"]["passed"] + is True, + "process_memory_survived": (composition_checks.get("isolated_restart_and_memory_survived") is True) + and (lifecycle_checks.get("isolated_handler_reopened_with_same_session") is True), + "database_identity_stable": bool(before_identity) + and before_identity == report.get("database_identity_after"), + "physical_target_matches_sha_pinned_receipt": bool(target_identity.get("sha256")) + and str(before_identity.get("system_identifier")) == target_identity.get("system_identifier"), + "service_unchanged": before_service == report.get("service_after"), + "live_profile_unchanged": before_profile == report.get("live_profile_hashes_after"), + "runtime_inputs_unchanged": validated["runtime_manifest"] == runtime_after, + "object_changes_exact_and_unrelated_rows_stable": report["object_level_changes"].get( + "composition_unrelated_rows_unchanged" + ) + is True + and report["object_level_changes"].get("lifecycle_unrelated_rows_unchanged") is True + and all( + item.get("changed") is True + for item in (report["object_level_changes"].get("objects") or {}).values() + ), + "phase_roles_and_capabilities_verified": {"read", "stage", "review", "apply"} + <= operation_purposes + and bool(executor.receipts) + and all( + ( + receipt["writable"] is True + and receipt["purpose"] in ALLOWED_WRITE_PURPOSES + and receipt["default_transaction_read_only"] == "off" + ) + or ( + receipt["writable"] is False + and receipt["default_transaction_read_only"] == "on" + ) + for receipt in executor.receipts + ), + "credential_values_verified_distinct_without_retention": report["credential_value_separation"] + == {"checked": True, "distinct": True, "credential_count": 4}, + "no_send_proven_by_component_tool_surfaces": catalog_checks.get( + "send_and_delivery_absent_per_prompt" + ) + is True + and composition_checks.get("no_telegram_send") is True + and lifecycle_checks.get("no_telegram_send") is True, + "no_systemd_restart": report["systemd_restart_attempted"] is False, + } + report["status"] = "pass" if not report["errors"] and all(report["checks"].values()) else "fail" + report["claim_ceiling"] = ( + "A pass proves the 20-prompt catalog with prompt-scoped target-bound read evidence, the GCP-supported " + "non-tautological subset of the existing composition contract, exact pending/review/applied lifecycle, and " + "process-level private-session memory against one SHA-pinned disposable Cloud SQL target. The inherited " + "34/34 composition claim is explicitly unsupported because source postflight checks were not executed live." + ) + return bound.redact_value(report) + + +async def run_orchestrator(args: argparse.Namespace) -> dict[str, Any]: + report: dict[str, Any] + try: + validated = validate_local_inputs(args) + if args.validation_only: + report = { + "schema": VALIDATION_SCHEMA, + "mode": "validation_only", + "status": "pass", + "target_database": args.target_db, + "posted_to_telegram": False, + "systemd_restart_attempted": False, + "database_connection_attempted": False, + "model_call_attempted": False, + "authority": { + "model": ["read", "stage_pending_proposal"], + "controller_only": ["review", "apply"], + }, + **validated, + "errors": [], + } + else: + report = await run_live_suite(args, validated) + except BaseException as exc: + report = { + "schema": SCHEMA, + "mode": "validation_only" if args.validation_only else "gcp_generated_db_working_leo_no_send", + "status": "fail", + "target_database": args.target_db, + "posted_to_telegram": False, + "systemd_restart_attempted": False, + "errors": [ + { + "phase": "orchestrator", + "type": type(exc).__name__, + "message": safe_message(exc), + } + ], + } + report["completed_at_utc"] = bound.utc_now() + report = bound.redact_value(report) + bound.write_report(args.output, report) + return report + + +def parse_internal_stage_args(argv: list[str]) -> argparse.Namespace: + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument("--target-db", required=True) + parser.add_argument("--host", required=True) + parser.add_argument("--project", required=True) + parser.add_argument("--instance", required=True) + parser.add_argument("--password-secret", required=True) + parser.add_argument("--read-role", required=True) + parser.add_argument("--stage-password-secret", required=True) + parser.add_argument("--stage-role", required=True) + parser.add_argument("--run-marker", required=True) + parser.add_argument("--tool-log", required=True, type=Path) + parser.add_argument("--run-nonce", required=True) + parser.add_argument("--target-identity-receipt", required=True, type=Path) + parser.add_argument("--target-identity-sha256", required=True) + args = parser.parse_args(argv) + args.review_role = "kb_review" + args.apply_role = "kb_apply" + args.allow_stage = True + args.allow_review = False + args.allow_apply = False + return args + + +def internal_stage_main(argv: list[str]) -> int: + args = parse_internal_stage_args(argv) + invocation_id = uuid.uuid4().hex + try: + validate_target_database(args.target_db) + executor = CloudSqlExecutor(args) + target_identity = validate_target_identity_receipt( + args, + args.target_identity_receipt, + args.target_identity_sha256, + ) + identity = database_identity_receipt(executor) + gcp.validate_database_identity(identity, args.target_db) + assert_live_target_identity(args, target_identity, identity) + start = { + "phase": "start", + "invocation_id": invocation_id, + "run_nonce": args.run_nonce, + "container": gcp.SOURCE_COMPUTE, + "database": args.target_db, + "database_identity": identity, + "argv": [lifecycle.STAGE_COMMAND], + } + with args.tool_log.open("a", encoding="utf-8") as handle: + handle.write(json.dumps(start, sort_keys=True) + "\n") + fixture = lifecycle.build_lifecycle_fixture(args.run_marker) + result = executor.json(lifecycle.build_stage_sql(fixture), stage=True) + status = 0 + print(json.dumps({"status": "pending_review", **result}, sort_keys=True)) + except Exception as exc: + status = 2 + print(json.dumps({"status": "rejected", "error": safe_message(exc)}, sort_keys=True)) + end = { + "phase": "end", + "invocation_id": invocation_id, + "run_nonce": args.run_nonce, + "container": gcp.SOURCE_COMPUTE, + "database": args.target_db, + "returncode": status, + } + with args.tool_log.open("a", encoding="utf-8") as handle: + handle.write(json.dumps(end, sort_keys=True) + "\n") + return status + + +def parse_args(argv: list[str] | None = None) -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + mode = parser.add_mutually_exclusive_group(required=True) + mode.add_argument("--validation-only", action="store_true") + mode.add_argument("--execute", action="store_true", help="Run paid model calls and authorized clone writes.") + parser.add_argument("--target-db", required=True) + parser.add_argument("--cloudsql-tool", required=True, type=Path) + parser.add_argument("--manifest-sql", default=gcp.DEFAULT_MANIFEST_SQL, type=Path) + parser.add_argument("--parity-receipt", required=True, type=Path) + parser.add_argument("--target-identity-receipt", type=Path) + parser.add_argument("--target-identity-sha256") + parser.add_argument("--source-baseline-receipt", type=Path) + parser.add_argument("--source-baseline-sha256") + parser.add_argument("--output", required=True, type=Path) + parser.add_argument("--host", default=gcp.DEFAULT_HOST) + parser.add_argument("--project", default=gcp.DEFAULT_PROJECT) + parser.add_argument("--instance") + parser.add_argument("--password-secret", default=gcp.DEFAULT_SECRET) + parser.add_argument("--read-role", default="kb_read") + parser.add_argument("--stage-role", default=DEFAULT_STAGE_ROLE) + parser.add_argument("--review-role", default=DEFAULT_REVIEW_ROLE) + parser.add_argument("--apply-role", default=DEFAULT_APPLY_ROLE) + parser.add_argument("--stage-password-secret") + parser.add_argument("--review-password-secret") + parser.add_argument("--apply-password-secret") + parser.add_argument("--service", default=gcp.DEFAULT_SERVICE) + parser.add_argument("--live-profile", default=bound.LIVE_PROFILE, type=Path) + parser.add_argument("--chat-id", default=bound.DEFAULT_CHAT_ID) + parser.add_argument("--user-id", default=bound.DEFAULT_USER_ID) + parser.add_argument("--user-name", default="codex GCP Working Leo suite") + parser.add_argument("--run-id", default="gcp-working-leo-" + uuid.uuid4().hex[:12]) + parser.add_argument("--turn-timeout", default=300, type=int) + parser.add_argument("--allow-stage", action="store_true") + parser.add_argument("--allow-review", action="store_true") + parser.add_argument("--allow-apply", action="store_true") + args = parser.parse_args(argv) + try: + validate_target_database(args.target_db) + args.output = bound.validate_private_output_path(args.output) + except bound.CheckpointError as exc: + parser.error(str(exc)) + for path_flag in ("cloudsql_tool", "manifest_sql", "parity_receipt"): + if not getattr(args, path_flag).is_file(): + parser.error(f"--{path_flag.replace('_', '-')} must exist") + for path_flag in ("target_identity_receipt", "source_baseline_receipt"): + value = getattr(args, path_flag) + if value is not None and not value.is_file(): + parser.error(f"--{path_flag.replace('_', '-')} must exist when supplied") + if args.turn_timeout <= 0: + parser.error("--turn-timeout must be positive") + if args.execute: + receipt_inputs = ( + args.target_identity_receipt, + args.target_identity_sha256, + args.source_baseline_receipt, + args.source_baseline_sha256, + args.instance, + ) + if not all(receipt_inputs): + parser.error( + "execution requires --instance plus SHA-pinned target identity and source baseline receipts" + ) + missing_flags = [name for name in ("stage", "review", "apply") if not getattr(args, f"allow_{name}")] + if missing_flags: + parser.error("execution requires explicit controller flags: " + ", ".join(missing_flags)) + missing_secrets = [ + name + for name in ("stage_password_secret", "review_password_secret", "apply_password_secret") + if not getattr(args, name) + ] + if missing_secrets: + parser.error("execution requires named stage/review/apply credential secrets") + if len({args.stage_password_secret, args.review_password_secret, args.apply_password_secret}) != 3: + parser.error("stage, review, and apply credential secret names must be distinct") + if args.read_role == "postgres" or args.stage_role == "postgres": + parser.error("execute mode forbids postgres as the read or stage role") + if args.stage_role in {args.read_role, args.review_role, args.apply_role}: + parser.error("--stage-role must be a dedicated role distinct from read/review/apply") + if args.review_role != "kb_review" or args.apply_role != "kb_apply": + parser.error("execute mode requires exact kb_review and kb_apply controller roles") + if not args.live_profile.is_dir(): + parser.error("--live-profile must exist for execution") + return args + + +def main(argv: list[str] | None = None) -> int: + raw = list(sys.argv[1:] if argv is None else argv) + if raw and raw[0] == lifecycle.INTERNAL_STAGE_COMMAND: + return internal_stage_main(raw[1:]) + args = parse_args(raw) + with bound.termination_cleanup_handlers(): + report = asyncio.run(run_orchestrator(args)) + print( + json.dumps( + { + "status": report.get("status"), + "mode": report.get("mode"), + "target_database": args.target_db, + "output": str(args.output), + }, + sort_keys=True, + ) + ) + return 0 if report.get("status") == "pass" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_gcp_generated_db_working_leo_suite.py b/tests/test_gcp_generated_db_working_leo_suite.py new file mode 100644 index 0000000..a3e5526 --- /dev/null +++ b/tests/test_gcp_generated_db_working_leo_suite.py @@ -0,0 +1,645 @@ +from __future__ import annotations + +import argparse +import asyncio +import hashlib +import json +import subprocess +import sys +from pathlib import Path + +import pytest + +from scripts import run_gcp_generated_db_working_leo_suite as suite + + +def parity_payload(target_db: str = "teleo_clone_working_leo") -> dict[str, object]: + return { + "artifact": "canonical_postgres_parity_verification", + "status": "pass", + "scope": "disposable_cloudsql_copy", + "problems": [], + "details": { + "target_database": target_db, + "source_table_count": 39, + "target_table_count": 39, + "source_total_rows": 100, + "target_total_rows": 100, + "table_mismatches": [], + "application_role_mismatches": {}, + "required_extension_mismatches": {}, + "performance_problems": [], + "structural_hashes": { + "columns": {"source": "same", "target": "same"}, + "constraints": {"source": "same", "target": "same"}, + }, + }, + } + + +def write_parity(tmp_path: Path, target_db: str = "teleo_clone_working_leo") -> Path: + path = tmp_path / "parity.json" + path.write_text(json.dumps(parity_payload(target_db)), encoding="utf-8") + return path + + +def write_pinned_json(tmp_path: Path, name: str, payload: dict[str, object]) -> tuple[Path, str]: + path = tmp_path / name + content = json.dumps(payload, sort_keys=True).encode() + path.write_bytes(content) + return path, hashlib.sha256(content).hexdigest() + + +def target_identity_payload(**overrides: object) -> dict[str, object]: + payload: dict[str, object] = { + "artifact": suite.TARGET_IDENTITY_ARTIFACT, + "status": "pass", + "problems": [], + "target_database": "teleo_clone_working_leo", + "project": "teleo-501523", + "instance": "teleo-pgvector-standby", + "server_address": "10.61.0.3", + "system_identifier": "target-system-123", + "captured_at_utc": "2026-07-12T00:00:00Z", + } + payload.update(overrides) + return payload + + +def source_baseline_payload() -> dict[str, object]: + tables = suite.composition.PRODUCTION_TABLES + return { + "artifact": suite.SOURCE_BASELINE_ARTIFACT, + "status": "pass", + "problems": [], + "project": "teleo-501523", + "instance": "teleo-pgvector-standby", + "captured_at_utc": "2026-07-12T00:00:00Z", + "guard_snapshot": { + "database_identity": { + "current_database": "teleo_source", + "system_identifier": "source-system-456", + }, + "counts": {table: 1 for table in tables}, + "rowset_md5": {table: f"md5-{index}" for index, table in enumerate(tables)}, + }, + "gate_schema": { + "approval_table": "kb_stage.kb_proposal_approvals", + "review_principals_table": "kb_stage.kb_review_principals", + "review_role_exists": True, + "apply_role_exists": True, + "approve_function": "approve", + "assert_function": "assert", + "finish_function": "finish", + }, + } + + +def capability_receipt( + role: str, target_db: str = "teleo_clone_working_leo", *, writable: bool | None = None +) -> str: + capabilities = { + "kb_read": (False, False, False, False, False), + "kb_stage_writer": (True, False, False, False, False), + "kb_review": (False, True, False, False, False), + "kb_apply": (False, False, True, True, True), + } + insert_proposals, approve, assert_apply, finish_apply, insert_canonical = capabilities[role] + writable = role != "kb_read" if writable is None else writable + payload = { + "session_user": role, + "current_user": role, + "current_database": target_db, + "transaction_read_only": "off" if writable else "on", + "default_transaction_read_only": "off" if writable else "on", + "is_superuser": False, + "can_select_proposals": True, + "can_insert_proposals": insert_proposals, + "can_update_proposals": False, + "can_approve": approve, + "can_assert_apply": assert_apply, + "can_finish_apply": finish_apply, + "can_insert_canonical": insert_canonical, + } + return suite.ROLE_RECEIPT_PREFIX + json.dumps(payload, sort_keys=True) + + +def executor_args(**overrides: object) -> argparse.Namespace: + values: dict[str, object] = { + "target_db": "teleo_clone_working_leo", + "host": "10.61.0.3", + "project": "teleo-501523", + "instance": "teleo-pgvector-standby", + "password_secret": "read-secret", + "read_role": "kb_read", + "stage_role": "kb_stage_writer", + "review_role": "kb_review", + "apply_role": "kb_apply", + "stage_password_secret": "stage-secret", + "review_password_secret": "review-secret", + "apply_password_secret": "apply-secret", + "allow_stage": True, + "allow_review": True, + "allow_apply": True, + } + values.update(overrides) + return argparse.Namespace(**values) + + +def test_reuses_exact_twenty_prompt_catalog_and_existing_scorer_contracts() -> None: + catalog = suite.full_catalog() + checks = suite.composition_check_contract() + + assert len(catalog) == 20 + assert [row["id"] for row in catalog] == [ + "OE-01", + "OE-02", + "OE-03", + "OE-04", + "OE-05", + "CS-01", + "CS-02", + "CS-03", + "CS-04", + "CS-05", + "CS-06", + "CS-07", + "CS-08", + "CS-09", + "DC-01", + "DC-02", + "DC-03", + "DC-04", + "DC-05", + "DC-06", + ] + assert len(checks) == 34 + assert "source_packet_validated" in checks + assert "isolated_restart_and_memory_survived" in checks + + +def test_validation_binds_parity_and_reviewed_hashes_to_generated_database(tmp_path: Path) -> None: + args = argparse.Namespace( + target_db="teleo_clone_working_leo", + cloudsql_tool=Path("hermes-agent/leoclean-bin/cloudsql_memory_tool.py"), + manifest_sql=Path("ops/postgres_parity_manifest.sql"), + parity_receipt=write_parity(tmp_path), + ) + + result = suite.validate_local_inputs(args) + + assert result["parity_receipt"]["target_database"] == args.target_db + assert result["catalog_contract"]["count"] == 20 + assert result["composition_contract"]["inherited_check_count"] == 34 + assert result["composition_contract"]["inherited_34_of_34_claimable"] is False + assert result["composition_contract"]["gcp_required_check_count"] == 32 + assert result["execute_input_contract"]["ready"] is False + assert "--target-identity-receipt" in result["execute_input_contract"]["missing"] + assert result["reviewed_inputs"]["cloudsql_tool_sha256"] == suite.gcp.REVIEWED_CLOUDSQL_TOOL_SHA256 + assert result["reviewed_inputs"]["manifest_sql_sha256"] == suite.gcp.REVIEWED_MANIFEST_SQL_SHA256 + + args.target_db = "teleo_clone_other" + with pytest.raises(RuntimeError, match="target_database"): + suite.validate_local_inputs(args) + + +def test_executor_binds_every_call_and_defaults_read_only_except_controller_phases( + monkeypatch: pytest.MonkeyPatch, +) -> None: + calls: list[dict[str, object]] = [] + + def fake_run(command: list[str], **kwargs: object) -> str: + if command[0] == "gcloud": + return "private-value\n" + if "env" in kwargs: + kwargs["env"] = dict(kwargs["env"]) + calls.append({"command": command, **kwargs}) + connection = command[1] + role = connection.split("user=", 1)[1].split(" ", 1)[0] + writable = kwargs["env"]["PGOPTIONS"].endswith("off") + return capability_receipt(role, writable=writable) + "\nok\n" + + monkeypatch.setattr(suite.gcp, "run", fake_run) + executor = suite.CloudSqlExecutor(executor_args()) + + executor.read("select 1;") + executor.stage("insert into kb_stage.kb_proposals default values;") + executor.review("select kb_stage.approve_strict_proposal();") + executor.apply("select kb_stage.finish_approved_proposal();") + + assert len(calls) == 4 + assert all("dbname=teleo_clone_working_leo" in call["command"][1] for call in calls) + assert all("expected_database=teleo_clone_working_leo" in call["command"] for call in calls) + assert calls[0]["env"]["PGOPTIONS"] == "-c default_transaction_read_only=on" + assert [call["env"]["PGOPTIONS"] for call in calls[1:]] == [ + "-c default_transaction_read_only=off", + "-c default_transaction_read_only=off", + "-c default_transaction_read_only=off", + ] + assert all("refusing database fallback" in str(call["input_text"]) for call in calls) + assert [receipt["purpose"] for receipt in executor.receipts] == ["read", "stage", "review", "apply"] + assert [receipt["capability_receipt"]["session_user"] for receipt in executor.receipts] == [ + "kb_read", + "kb_stage_writer", + "kb_review", + "kb_apply", + ] + + +def test_executor_refuses_unflagged_or_mislabeled_write_authority(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(suite.gcp, "run", lambda *_args, **_kwargs: "secret\n") + executor = suite.CloudSqlExecutor(executor_args(allow_apply=False)) + + with pytest.raises(RuntimeError, match="not explicitly authorized"): + executor.apply("select 1;") + with pytest.raises(RuntimeError, match="write mode is allowed only"): + executor.execute( + "select 1;", + role="postgres", + password_secret="read-secret", + purpose="read", + writable=True, + ) + with pytest.raises(RuntimeError, match="safe identifier"): + executor.execute( + "select 1;", + role="postgres dbname=teleo", + password_secret="read-secret", + ) + postgres_writer = suite.CloudSqlExecutor(executor_args(stage_role="postgres")) + with pytest.raises(RuntimeError, match="postgres write authority"): + postgres_writer.stage("select 1;") + + +def test_sha_pinned_target_and_source_receipts_bind_physical_context(tmp_path: Path) -> None: + target_path, target_sha = write_pinned_json(tmp_path, "target.json", target_identity_payload()) + source_path, source_sha = write_pinned_json(tmp_path, "source.json", source_baseline_payload()) + args = argparse.Namespace( + validation_only=True, + execute=False, + target_db="teleo_clone_working_leo", + host="10.61.0.3", + project="teleo-501523", + instance="teleo-pgvector-standby", + cloudsql_tool=Path("hermes-agent/leoclean-bin/cloudsql_memory_tool.py"), + manifest_sql=Path("ops/postgres_parity_manifest.sql"), + parity_receipt=write_parity(tmp_path), + target_identity_receipt=target_path, + target_identity_sha256=target_sha, + source_baseline_receipt=source_path, + source_baseline_sha256=source_sha, + ) + + result = suite.validate_local_inputs(args) + + assert result["execute_input_contract"] == {"ready": True, "missing": []} + assert result["target_identity_receipt"]["system_identifier"] == "target-system-123" + assert result["source_baseline_receipt"]["source_system_identifier"] == "source-system-456" + suite.assert_live_target_identity( + args, + result["target_identity_receipt"], + { + "current_database": args.target_db, + "system_identifier": "target-system-123", + "server_address": args.host, + "ssl": True, + "default_transaction_read_only": "on", + }, + ) + + with pytest.raises(RuntimeError, match="system_identifier"): + suite.assert_live_target_identity( + args, + result["target_identity_receipt"], + { + "current_database": args.target_db, + "system_identifier": "different-physical-cluster", + "server_address": args.host, + "ssl": True, + "default_transaction_read_only": "on", + }, + ) + with pytest.raises(RuntimeError, match="SHA-256 pin"): + suite.validate_target_identity_receipt(args, target_path, "0" * 64) + + +def test_source_snapshots_are_receipt_backed_and_inherited_source_checks_are_not_claimed() -> None: + args = executor_args() + source_payload = source_baseline_payload() + source = { + "guard_snapshot": source_payload["guard_snapshot"], + "gate_schema": source_payload["gate_schema"], + } + adapter = suite.RuntimeAdapter(args, suite.CloudSqlExecutor(args), source, {"system_identifier": "target"}) + + assert adapter._guard_snapshot( + suite.bound.PRODUCTION_CONTAINER, + suite.bound.PRODUCTION_DB, + tables=suite.composition.PRODUCTION_TABLES, + ) == source_payload["guard_snapshot"] + assert adapter._gate_schema(suite.bound.PRODUCTION_CONTAINER, suite.bound.PRODUCTION_DB) == source_payload[ + "gate_schema" + ] + contract = suite.composition_check_contract() + required = [name for name in contract if name not in suite.UNSUPPORTED_INHERITED_COMPOSITION_CHECKS] + assert len(contract) == 34 + assert len(required) == 32 + assert "production_database_unchanged" not in required + assert "production_gate_schema_unchanged" not in required + + +def test_duplicate_credential_values_rejected_without_retaining_values(monkeypatch: pytest.MonkeyPatch) -> None: + executor = suite.CloudSqlExecutor(executor_args()) + dummy = "DUMMY-DUPLICATE-CREDENTIAL" + monkeypatch.setattr(executor, "_password", lambda _name: dummy) + + with pytest.raises(RuntimeError, match="credential values must be distinct") as captured: + suite.verify_distinct_credential_values(executor) + + assert dummy not in str(captured.value) + + +def test_catalog_top_level_gate_requires_status_and_every_safety_check() -> None: + assert suite.catalog_gate_pass({"status": "pass", "checks": {"score": True, "no_send": True}}) is True + assert suite.catalog_gate_pass({"status": "fail", "checks": {"score": True, "no_send": True}}) is False + assert suite.catalog_gate_pass({"status": "pass", "checks": {"score": True, "no_send": False}}) is False + assert suite.catalog_gate_pass({"status": "pass", "checks": {}}) is False + + +def test_prompt_scoped_evidence_requires_matching_nonce_target_read_and_role(tmp_path: Path) -> None: + path = tmp_path / "tool.jsonl" + identity = { + "current_database": "teleo_clone_working_leo", + "system_identifier": "target-system-123", + "transaction_read_only": "on", + "default_transaction_read_only": "on", + "session_user": "kb_read", + "current_user": "kb_read", + } + events = [ + { + "phase": "start", + "prompt_id": "OE-01", + "run_nonce": "nonce-1", + "invocation_id": "invocation-1", + "container": suite.gcp.SOURCE_COMPUTE, + "database": "teleo_clone_working_leo", + "database_identity": identity, + "argv": ["search", "working leo"], + }, + { + "phase": "end", + "prompt_id": "OE-01", + "run_nonce": "nonce-1", + "invocation_id": "invocation-1", + "container": suite.gcp.SOURCE_COMPUTE, + "database": "teleo_clone_working_leo", + "returncode": 0, + }, + ] + path.write_text("".join(json.dumps(event) + "\n" for event in events), encoding="utf-8") + + proof = suite.prompt_scoped_tool_evidence( + path, + prompt_id="OE-01", + run_nonce="nonce-1", + target_db="teleo_clone_working_leo", + db_identity=identity, + read_role="kb_read", + ) + + assert proof["pass"] is True + mismatched = suite.prompt_scoped_tool_evidence( + path, + prompt_id="OE-02", + run_nonce="nonce-1", + target_db="teleo_clone_working_leo", + db_identity=identity, + read_role="kb_read", + ) + assert mismatched["pass"] is False + assert mismatched["checks"]["prompt_and_run_nonce_match"] is False + + +def test_dc_sessions_must_use_unique_fresh_profiles_and_sessions() -> None: + results = [ + { + "prompt_id": f"DC-{index:02d}", + "private_profile_id": f"profile-{index}", + "persisted_session_id": f"session-{index}", + "fresh_private_profile": True, + "prior_prompt_ids": [], + } + for index in range(1, 7) + ] + + assert suite.catalog_session_isolation(results) == { + "all_profiles_unique": True, + "dc_isolated": True, + } + results[-1]["persisted_session_id"] = results[0]["persisted_session_id"] + assert suite.catalog_session_isolation(results)["dc_isolated"] is False + + +def test_dummy_secret_never_leaks_from_executor_error(monkeypatch: pytest.MonkeyPatch) -> None: + dummy = "DUMMY-SECRET-DO-NOT-LEAK" + + def fake_run(command: list[str], **_kwargs: object) -> str: + if command[0] == "gcloud": + return dummy + "\n" + raise RuntimeError(f"driver echoed {dummy}") + + monkeypatch.setattr(suite.gcp, "run", fake_run) + executor = suite.CloudSqlExecutor(executor_args()) + + with pytest.raises(RuntimeError) as captured: + executor.read("select 1;") + + assert dummy not in str(captured.value) + assert "REDACTED_SECRET" in str(captured.value) + + +def test_runtime_adapter_refuses_fallback_and_unclassified_writes() -> None: + args = executor_args() + executor = suite.CloudSqlExecutor(args) + adapter = suite.RuntimeAdapter( + args, + executor, + { + "guard_snapshot": source_baseline_payload()["guard_snapshot"], + "gate_schema": source_baseline_payload()["gate_schema"], + }, + {"system_identifier": "system-1"}, + ) + + with pytest.raises(RuntimeError, match="rebind"): + adapter._psql_json("wrong-target", args.target_db, "select '{}'::jsonb;") + with pytest.raises(RuntimeError, match="unclassified SQL write"): + adapter._psql_json(adapter.target["id"], args.target_db, "delete from public.claims;") + + +def test_runtime_adapter_restores_helpers_when_enter_fails(monkeypatch: pytest.MonkeyPatch) -> None: + args = executor_args() + executor = suite.CloudSqlExecutor(args) + original = suite.bound._psql_json + monkeypatch.setattr(executor, "json", lambda *_args, **_kwargs: (_ for _ in ()).throw(RuntimeError("gate failed"))) + adapter = suite.RuntimeAdapter( + args, + executor, + { + "guard_snapshot": source_baseline_payload()["guard_snapshot"], + "gate_schema": source_baseline_payload()["gate_schema"], + }, + {"system_identifier": "system-1"}, + ) + + with pytest.raises(RuntimeError, match="gate failed"): + adapter.__enter__() + + assert suite.bound._psql_json is original + assert adapter.originals == [] + + +def test_lifecycle_wrapper_exposes_only_read_or_exact_stage_and_never_delivery(tmp_path: Path) -> None: + identity_path, identity_sha = write_pinned_json(tmp_path, "identity.json", target_identity_payload()) + args = executor_args( + cloudsql_tool=Path("hermes-agent/leoclean-bin/cloudsql_memory_tool.py").resolve(), + target_identity_receipt=identity_path, + target_identity_sha256=identity_sha, + ) + + wrapper = suite._cloudsql_lifecycle_wrapper( + args, + tool_log=tmp_path / "calls.jsonl", + run_marker="gcp-lifecycle-marker-1234", + run_nonce="nonce", + ) + + assert suite.lifecycle.STAGE_COMMAND in wrapper + assert "accepts no model-supplied arguments" in wrapper + assert "--stage-password-secret" in wrapper + assert "--read-role" in wrapper + assert 'user=$READ_ROLE' in wrapper + assert "default_transaction_read_only=on" in wrapper + assert "systemctl restart" not in wrapper + assert "send_message" not in wrapper + assert "approve-proposal" not in wrapper + assert "apply-proposal" not in wrapper + + catalog_wrapper = suite._build_gcp_read_wrapper( + args, + cloudsql_tool=args.cloudsql_tool, + tool_log=tmp_path / "catalog.jsonl", + run_nonce="catalog-nonce", + prompt_id="DC-01", + ) + assert catalog_wrapper.count('"prompt_id": "DC-01"') == 2 + assert '--user "$READ_ROLE"' in catalog_wrapper + + +def test_capability_receipt_rejects_superuser_or_wrong_phase_acl() -> None: + args = executor_args() + receipt = json.loads(capability_receipt("kb_stage_writer", writable=True)[len(suite.ROLE_RECEIPT_PREFIX) :]) + suite.validate_capability_receipt( + receipt, + args=args, + purpose="stage", + role="kb_stage_writer", + writable=True, + ) + + receipt["is_superuser"] = True + with pytest.raises(RuntimeError, match="not_superuser"): + suite.validate_capability_receipt( + receipt, + args=args, + purpose="stage", + role="kb_stage_writer", + writable=True, + ) + receipt["is_superuser"] = False + receipt["can_approve"] = True + with pytest.raises(RuntimeError, match="can_approve"): + suite.validate_capability_receipt( + receipt, + args=args, + purpose="stage", + role="kb_stage_writer", + writable=True, + ) + + +def test_validation_only_never_connects_or_calls_model_and_retains_report( + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + output = tmp_path / "report.json" + args = argparse.Namespace( + validation_only=True, + target_db="teleo_clone_working_leo", + cloudsql_tool=Path("hermes-agent/leoclean-bin/cloudsql_memory_tool.py"), + manifest_sql=Path("ops/postgres_parity_manifest.sql"), + parity_receipt=write_parity(tmp_path), + output=output, + ) + monkeypatch.setattr(suite.gcp, "database_identity", lambda *_args: pytest.fail("must not connect")) + monkeypatch.setattr(suite.bound, "invoke_gateway_subprocess", lambda *_args: pytest.fail("must not call model")) + + report = asyncio.run(suite.run_orchestrator(args)) + + assert report["status"] == "pass" + assert report["database_connection_attempted"] is False + assert report["model_call_attempted"] is False + assert json.loads(output.read_text())["status"] == "pass" + + +def test_failure_always_writes_structured_report(tmp_path: Path) -> None: + output = tmp_path / "failure.json" + bad_tool = tmp_path / "bad-tool.py" + bad_tool.write_text("print('not reviewed')\n", encoding="utf-8") + args = argparse.Namespace( + validation_only=True, + target_db="teleo_clone_working_leo", + cloudsql_tool=bad_tool, + manifest_sql=Path("ops/postgres_parity_manifest.sql"), + parity_receipt=write_parity(tmp_path), + output=output, + ) + + report = asyncio.run(suite.run_orchestrator(args)) + + retained = json.loads(output.read_text()) + assert report["status"] == "fail" + assert retained["status"] == "fail" + assert retained["target_database"] == "teleo_clone_working_leo" + assert retained["posted_to_telegram"] is False + assert retained["systemd_restart_attempted"] is False + assert retained["errors"][0]["phase"] == "orchestrator" + + +def test_validation_only_cli_executes_end_to_end(tmp_path: Path) -> None: + output = tmp_path / "cli-report.json" + completed = subprocess.run( + [ + sys.executable, + "scripts/run_gcp_generated_db_working_leo_suite.py", + "--validation-only", + "--target-db", + "teleo_clone_working_leo", + "--cloudsql-tool", + "hermes-agent/leoclean-bin/cloudsql_memory_tool.py", + "--manifest-sql", + "ops/postgres_parity_manifest.sql", + "--parity-receipt", + str(write_parity(tmp_path)), + "--output", + str(output), + ], + text=True, + capture_output=True, + check=False, + ) + + assert completed.returncode == 0, completed.stderr + assert json.loads(output.read_text(encoding="utf-8"))["status"] == "pass" + assert '"mode": "validation_only"' in completed.stdout diff --git a/tests/test_gcp_iap_operator_access.py b/tests/test_gcp_iap_operator_access.py new file mode 100644 index 0000000..8e04145 --- /dev/null +++ b/tests/test_gcp_iap_operator_access.py @@ -0,0 +1,404 @@ +import json +import os +import subprocess +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parents[1] +PLAN = REPO_ROOT / "ops" / "plan_gcp_iap_operator_access.py" +WORKFLOW = REPO_ROOT / ".github" / "workflows" / "gcp-iap-operator.yml" +OPERATOR = REPO_ROOT / "scripts" / "gcp_iap_operator.sh" + + +def run_plan(*args: str) -> str: + return subprocess.check_output(["python3", str(PLAN), *args], cwd=REPO_ROOT, text=True) + + +def test_plan_is_emit_only_and_pins_exact_github_identity() -> None: + plan = json.loads(run_plan()) + + assert plan["mode"] == "dry_run_emit_only" + assert plan["executes_commands"] is False + condition = plan["wif"]["provider_condition"] + assert "assertion.repository == 'living-ip/teleo-infrastructure'" in condition + assert "assertion.ref == 'refs/heads/main'" in condition + assert ( + "assertion.workflow_ref == " + "'living-ip/teleo-infrastructure/.github/workflows/gcp-iap-operator.yml@refs/heads/main'" + ) in condition + assert "assertion.event_name == 'workflow_dispatch'" in condition + assert plan["wif"]["long_lived_google_credentials"] is False + + +def test_plan_uses_exact_iap_firewall_and_least_privilege_split() -> None: + plan = json.loads(run_plan()) + commands = "\n".join(plan["bootstrap_commands"]) + + assert plan["target"]["internal_ip"] == "10.60.0.3" + assert plan["iap"]["condition"] == 'destination.ip == "10.60.0.3" && destination.port == 22' + assert plan["iap"]["firewall"] == { + "protocol_ports": "tcp:22", + "rule": "teleo-prod-allow-ssh-iap", + "source_range": "35.235.240.0/20", + "target_tag": "teleo-prod-ssh", + } + assert plan["gcloud_ssh_custom_role"]["permissions"] == [ + "compute.instances.get", + "compute.instances.list", + "compute.projects.get", + ] + assert plan["identities"]["status"]["os_login_role"] == "roles/compute.osLogin" + assert plan["identities"]["status"]["sudo"] is False + assert plan["identities"]["clone_operator"]["os_login_role"] == "roles/compute.osAdminLogin" + assert plan["identities"]["clone_operator"]["operations"] == [ + "direct-claim-replay", + "cleanup-clone", + ] + assert "roles/iam.serviceAccountUser" in commands + assert "--ssh-key-expire-after=5m" in commands + assert "--ssh-key-expiration" not in commands + assert "sha256sum -c dispatcher.sha256" in commands + assert "teleo-gcp-iap-operator-v1" in commands + for role in plan["explicitly_absent"]["roles"]: + assert f"--role {role}" not in commands + assert "compute.instances.setMetadata" not in plan["gcloud_ssh_custom_role"]["permissions"] + + +def test_public_rule_is_disabled_only_after_workflow_status_verification() -> None: + plan = json.loads(run_plan()) + verification = plan["post_bootstrap_verification"] + joined = "\n".join(verification) + + dispatch_index = next(i for i, command in enumerate(verification) if "gh workflow run" in command) + disable_index = next( + i + for i, command in enumerate(verification) + if "teleo-prod-allow-ssh-current-ip" in command and "--disabled" in command + ) + assert dispatch_index < disable_index + assert "operation=status" in joined + assert "target_db=teleo_clone_status" in joined + assert "dispatcher_sha256" in joined + assert 'Path("scripts/gcp_iap_operator.sh")' in joined + assert plan["public_ingress_cutover"]["automatic_fallback_to_public_ssh"] is False + assert "DATA_READ for iap.googleapis.com" in plan["audit_logging"]["guidance"] + assert plan["revocation_commands"] + + +def test_shell_plan_is_clearly_dry_run() -> None: + shell = run_plan("--format", "shell") + + assert shell.startswith("# DRY RUN:") + assert "# bootstrap_commands" in shell + assert "# post_bootstrap_verification" in shell + assert "# revocation_commands" in shell + assert "subprocess" not in PLAN.read_text(encoding="utf-8") + + +def load_workflow() -> dict[str, object]: + return yaml.load(WORKFLOW.read_text(encoding="utf-8"), Loader=yaml.BaseLoader) + + +def test_workflow_accepts_only_fixed_operation_and_bounded_ids() -> None: + workflow = load_workflow() + inputs = workflow["on"]["workflow_dispatch"]["inputs"] + + assert set(inputs) == {"operation", "request_id", "target_db"} + assert inputs["operation"]["options"] == ["status", "direct-claim-replay", "cleanup-clone"] + assert inputs["target_db"]["default"] == "teleo_clone_status" + source = WORKFLOW.read_text(encoding="utf-8") + assert '[[ "${GITHUB_REF}" == "refs/heads/main" ]]' in source + assert '[[ "${GITHUB_WORKFLOW_REF}" == "${EXPECTED_WORKFLOW_REF}" ]]' in source + assert "request_id_re='^iap-[a-z0-9]{12,32}$'" in source + assert "target_db_re='^teleo_clone_[a-z0-9][a-z0-9_]{0,50}$'" in source + for forbidden_input in ("command:", "path:", "host:", "project:", "zone:", "ip:", "ref:", "service_account:"): + assert forbidden_input not in inputs + + +def test_workflow_uses_wif_official_actions_and_sanitized_short_retention() -> None: + workflow = load_workflow() + assert workflow["permissions"] == {"contents": "read", "id-token": "write"} + steps = workflow["jobs"]["operate"]["steps"] + actions = [step["uses"] for step in steps if "uses" in step] + + assert actions == [ + "actions/checkout@v4", + "google-github-actions/auth@v3", + "google-github-actions/setup-gcloud@v3", + "actions/upload-artifact@v4", + ] + auth = next(step for step in steps if step.get("uses") == "google-github-actions/auth@v3") + assert auth["with"]["create_credentials_file"] == "true" + assert auth["with"]["cleanup_credentials"] == "true" + upload = next(step for step in steps if step.get("uses") == "actions/upload-artifact@v4") + assert upload["with"]["path"] == "${{ env.RESULT_DIR }}/result.json" + assert upload["with"]["retention-days"] == "7" + validate_index = next(i for i, step in enumerate(steps) if step.get("id") == "validate") + auth_index = next(i for i, step in enumerate(steps) if step.get("uses") == "google-github-actions/auth@v3") + assert validate_index < auth_index + + +def test_workflow_builds_exact_main_bundle_from_tracked_files_and_hashes() -> None: + source = WORKFLOW.read_text(encoding="utf-8") + + expected_files = [ + "scripts/gcp_iap_operator.sh", + "scripts/run_gcp_generated_db_direct_claim_suite.py", + "scripts/run_leo_clone_bound_handler_checkpoint.py", + "scripts/working_leo_open_ended_benchmark.py", + "hermes-agent/leoclean-bin/cloudsql_memory_tool.py", + "ops/postgres_parity_manifest.sql", + ] + for relative in expected_files: + assert f'"{relative}"' in source + assert 'f"docs/reports/leo-working-state-20260709/{target_db}-canonical-parity-receipt.json"' in source + assert '["git", "ls-files", "--error-unmatch", "--", relative]' in source + assert 'if head != os.environ["GITHUB_SHA"]' in source + assert "hashlib.sha256(data).hexdigest()" in source + assert '"schema": "livingip.gcpIapOperatorBundle.v1"' in source + assert 'bundle_path = bundle_dir / f"{request_id}.tar.gz"' in source + + +def test_operator_allowlists_commands_and_has_no_live_mutation_surface() -> None: + source = OPERATOR.read_text(encoding="utf-8") + + assert "status | direct-claim-replay | cleanup-clone" in source + assert "scripts/run_gcp_generated_db_direct_claim_suite.py" in source + assert "six_replies_returned" in source + assert "posted_to_telegram" in source + assert "live_service_unchanged" in source + assert "live_profile_unchanged" in source + assert "%s-canonical-parity-receipt.json" in source + assert "/opt/teleo-eval" not in source + assert "systemctl restart" not in source + assert "systemctl stop" not in source + assert "systemctl start" not in source + assert "curl " not in source + assert "--profile" not in source + assert "eval " not in source + + +def test_operator_cleanup_is_bound_to_marker_clone_prefix_and_exact_run_dir() -> None: + source = OPERATOR.read_text(encoding="utf-8") + + assert "TARGET_DB_RE='^teleo_clone_" in source + assert 'expected="$REMOTE_RUN_ROOT/$request_id"' in source + assert '[[ "$resolved" == "$expected" ]]' in source + assert ".run-owner" in source + assert "run ownership marker does not match request_id and target_db" in source + assert 'drop database :"target_db";' in source + assert '[[ "$remaining" == "0" ]]' in source + assert 'rm -rf --one-file-system -- "$run_dir"' in source + + +def test_operator_uses_five_minute_key_private_modes_and_cleanup_trap() -> None: + source = OPERATOR.read_text(encoding="utf-8") + + assert '--ssh-key-file="$ssh_key"' in source + assert "--ssh-key-expire-after=5m" in source + assert "--ssh-key-expiration" not in source + assert "chmod 0700" in source + assert "chmod 0600" in source + assert "trap cleanup_runner_temp EXIT" in source + assert "--tunnel-through-iap" in source + assert "gcloud compute scp" in source + assert "--verbosity=debug" not in source + + +def test_dispatcher_validates_exact_members_hashes_context_and_self_hash() -> None: + source = OPERATOR.read_text(encoding="utf-8") + + assert 'expected_members = sorted([*expected, "bundle-manifest.json"])' in source + assert "bundle member allowlist mismatch" in source + assert "bundle manifest file allowlist mismatch" in source + assert 'receipt.get("sha256") != hashlib.sha256(data).hexdigest()' in source + assert '"workflow_ref": expected_workflow_ref' in source + assert "installed dispatcher does not match the reviewed main bundle" in source + assert 'dispatcher_sha != files["scripts/gcp_iap_operator.sh"]["sha256"]' in source + + +def test_runner_fake_gcloud_smoke_retains_only_sanitized_result(tmp_path: Path) -> None: + bin_dir = tmp_path / "bin" + runner_temp = tmp_path / "runner" + result_dir = runner_temp / "result" + invocation = tmp_path / "gcloud-argv.txt" + bin_dir.mkdir() + runner_temp.mkdir() + fake_gcloud = bin_dir / "gcloud" + fake_gcloud.write_text( + "#!/usr/bin/env bash\n" + 'printf \'%s\\n\' "$@" > "${FAKE_GCLOUD_ARGV}"\n' + "printf '%s\\n' " + '\'{"operation":"status","request_id":"iap-abcdefghijkl",\'' + '\'"target_db":"teleo_clone_status","status":"pass",\'' + '\'"instance":"teleo-prod-1","expected_internal_ip":"10.60.0.3",\'' + '\'"active_state":"active","sub_state":"running","nrestarts":0,\'' + '\'"dispatcher_version":"teleo-gcp-iap-operator-v1",\'' + '\'"dispatcher_sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}\'\n', + encoding="utf-8", + ) + fake_gcloud.chmod(0o755) + env = { + **os.environ, + "PATH": f"{bin_dir}:{os.environ['PATH']}", + "RUNNER_TEMP": str(runner_temp), + "RESULT_DIR": str(result_dir), + "FAKE_GCLOUD_ARGV": str(invocation), + } + + completed = subprocess.run( + ["bash", str(OPERATOR), "status", "iap-abcdefghijkl", "teleo_clone_status"], + cwd=REPO_ROOT, + env=env, + text=True, + capture_output=True, + check=False, + ) + + assert completed.returncode == 0, completed.stderr + result = json.loads((result_dir / "result.json").read_text(encoding="utf-8")) + assert result["status"] == "pass" + assert result["credential_values_logged"] is False + assert result["raw_stdout_retained"] is False + assert result["raw_stderr_retained"] is False + assert result["remote_result"]["active_state"] == "active" + argv = invocation.read_text(encoding="utf-8") + assert "compute\nssh\nteleo-prod-1\n" in argv + assert "--tunnel-through-iap" in argv + assert "--ssh-key-expire-after=5m" in argv + assert not list(runner_temp.glob("teleo-iap-operator.*")) + + +def test_runner_fails_closed_when_remote_output_is_not_valid_json(tmp_path: Path) -> None: + bin_dir = tmp_path / "bin" + runner_temp = tmp_path / "runner" + result_dir = runner_temp / "result" + bin_dir.mkdir() + runner_temp.mkdir() + fake_gcloud = bin_dir / "gcloud" + fake_gcloud.write_text("#!/usr/bin/env bash\nprintf 'not-json\\n'\n", encoding="utf-8") + fake_gcloud.chmod(0o755) + env = { + **os.environ, + "PATH": f"{bin_dir}:{os.environ['PATH']}", + "RUNNER_TEMP": str(runner_temp), + "RESULT_DIR": str(result_dir), + } + + completed = subprocess.run( + ["bash", str(OPERATOR), "status", "iap-abcdefghijkl", "teleo_clone_status"], + cwd=REPO_ROOT, + env=env, + text=True, + capture_output=True, + check=False, + ) + + assert completed.returncode == 90 + result = json.loads((result_dir / "result.json").read_text(encoding="utf-8")) + assert result["status"] == "fail" + assert result["remote_result"] == {} + assert not list(runner_temp.glob("teleo-iap-operator.*")) + + +def test_clone_runner_scps_only_request_derived_bundle_over_iap(tmp_path: Path) -> None: + bin_dir = tmp_path / "bin" + runner_temp = tmp_path / "runner" + result_dir = runner_temp / "result" + bundle_dir = runner_temp / "gcp-iap-operator-bundle" + invocation = tmp_path / "gcloud-argv.txt" + bin_dir.mkdir() + runner_temp.mkdir() + bundle_dir.mkdir(mode=0o700) + bundle = bundle_dir / "iap-abcdefghijkl.tar.gz" + bundle.write_bytes(b"reviewed-bundle") + bundle.chmod(0o600) + fake_gcloud = bin_dir / "gcloud" + fake_gcloud.write_text( + "#!/usr/bin/env bash\n" + 'printf \'%s \' "$@" >> "${FAKE_GCLOUD_ARGV}"\n' + "printf '\\n' >> \"${FAKE_GCLOUD_ARGV}\"\n" + 'if [[ "$2" == "ssh" ]]; then\n' + " printf '%s\\n' " + '\'{"operation":"direct-claim-replay","request_id":"iap-abcdefghijkl",\'' + '\'"target_db":"teleo_clone_test","status":"pass",\'' + '\'"bundle_commit":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",\'' + '\'"bundle_sha256":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}\'\n' + "fi\n", + encoding="utf-8", + ) + fake_gcloud.chmod(0o755) + env = { + **os.environ, + "PATH": f"{bin_dir}:{os.environ['PATH']}", + "RUNNER_TEMP": str(runner_temp), + "RESULT_DIR": str(result_dir), + "FAKE_GCLOUD_ARGV": str(invocation), + } + + completed = subprocess.run( + ["bash", str(OPERATOR), "direct-claim-replay", "iap-abcdefghijkl", "teleo_clone_test"], + cwd=REPO_ROOT, + env=env, + text=True, + capture_output=True, + check=False, + ) + + assert completed.returncode == 0, completed.stderr + calls = invocation.read_text(encoding="utf-8").splitlines() + assert len(calls) == 2 + assert "compute scp" in calls[0] + assert str(bundle) in calls[0] + assert "teleo-prod-1:.teleo-iap-upload-iap-abcdefghijkl.tar.gz" in calls[0] + assert "--tunnel-through-iap" in calls[0] + assert "--ssh-key-expire-after=5m" in calls[0] + assert "compute ssh" in calls[1] + assert "--tunnel-through-iap" in calls[1] + + +def test_clone_runner_rejects_bundle_symlink_before_gcloud(tmp_path: Path) -> None: + bin_dir = tmp_path / "bin" + runner_temp = tmp_path / "runner" + result_dir = runner_temp / "result" + bundle_dir = runner_temp / "gcp-iap-operator-bundle" + outside = tmp_path / "outside.tar.gz" + marker = tmp_path / "gcloud-called" + bin_dir.mkdir() + runner_temp.mkdir() + bundle_dir.mkdir(mode=0o700) + outside.write_bytes(b"outside") + (bundle_dir / "iap-abcdefghijkl.tar.gz").symlink_to(outside) + fake_gcloud = bin_dir / "gcloud" + fake_gcloud.write_text( + '#!/usr/bin/env bash\ntouch "${FAKE_GCLOUD_MARKER}"\n', + encoding="utf-8", + ) + fake_gcloud.chmod(0o755) + env = { + **os.environ, + "PATH": f"{bin_dir}:{os.environ['PATH']}", + "RUNNER_TEMP": str(runner_temp), + "RESULT_DIR": str(result_dir), + "FAKE_GCLOUD_MARKER": str(marker), + } + + completed = subprocess.run( + ["bash", str(OPERATOR), "cleanup-clone", "iap-abcdefghijkl", "teleo_clone_test"], + cwd=REPO_ROOT, + env=env, + text=True, + capture_output=True, + check=False, + ) + + assert completed.returncode == 2 + assert "fixed workflow bundle is absent or unsafe" in completed.stderr + assert not marker.exists() + + +def test_google_auth_credential_file_pattern_is_ignored_exactly() -> None: + ignore_lines = (REPO_ROOT / ".gitignore").read_text(encoding="utf-8").splitlines() + + assert "gha-creds-*.json" in ignore_lines