# GCP Observatory Read Adapter

## Definition of Working

- Working target: an authenticated GCP staging API returns one canonical claim
  with source/evidence provenance and a separately labeled proposal ledger.
- Operator path: `GET /v1/claims/sample` or `GET /v1/claims/{uuid}` with the
  server-only `X-Api-Key` value.
- Done means: the response names `teleo_canonical`, the dedicated IAM database
  principal and `kb_observatory_read`, reports a read-only transaction and
  denied write privileges, and live anonymous/write HTTP attempts return
  `401`/`405`.
- Not done: the VPS `teleo-pg`, a public Cloud SQL address, a browser-visible
  key, a mock-only receipt, or a production Vercel repoint.
- Required tier: `T3_live_readonly`.

## Existing Connector Boundary

The current `living-ip/livingip-web` main branch reads claims through
`src/lib/api.ts`. It selects `NEXT_PUBLIC_API_URL` and otherwise falls back to
`http://77.42.65.182:8081`, then calls the legacy `/api/claims` routes. That is
the VPS-local/public Observatory path. It is not safe to replace that public
environment variable with this protected API: the response shape differs and
the adapter key must never enter a `NEXT_PUBLIC_` variable or browser bundle.

The existing Teleo route `GET /api/kb/claims` is the compatibility reference
for route-scoped authentication, but its default database/container settings
still describe the VPS-local process. This service is a separate GCP runtime;
it does not change Argus or PR #148's Leo runtime files.

## Runtime Contract

- Cloud Run service: `observatory-read-adapter-staging` in `europe-west6`.
- Direct VPC egress: `teleo-staging-net` / `teleo-staging-europe-west6`.
- Cloud SQL: `teleo-501523:europe-west6:teleo-pgvector-standby`, private IP.
- Database: `teleo_canonical` only.
- Build identity: `sa-artifact-builder@teleo-501523.iam.gserviceaccount.com`;
  image build/push only.
- Staging deploy/canary identity:
  `sa-observatory-deployer@teleo-501523.iam.gserviceaccount.com`; an exact
  workflow/main-only WIF provider, a five-permission Cloud Run custom role,
  repository read, exact runtime `actAs`, and exact secret access. Never grant
  deploy permissions to the artifact builder.
- Runtime identity: `sa-observatory-read-adapter@teleo-501523.iam.gserviceaccount.com`.
- IAM database user: `sa-observatory-read-adapter@teleo-501523.iam`.
- Database authorization role: `kb_observatory_read` (`NOLOGIN`).
- API authentication: route-scoped key from Secret Manager secret
  `observatory-read-api-key-staging`; the value is not in Git or deploy logs.
- No mutation routes exist. Authenticated unsupported methods return `405`.

The Cloud SQL Python Connector uses Application Default Credentials from the
Cloud Run service identity, automatic IAM database authentication, and private
IP. Every request starts a read-only transaction and refuses the response if
the database, IAM principal, authorization membership, required reads, or
effective write-denial checks drift.

## One-Time Staging Bootstrap

These are staging mutations. Run them only with an authenticated GCP operator;
they do not route production traffic.

1. Reauthenticate the project operator, then run the first full direct check:

   ```bash
   gcloud projects describe teleo-501523 --format=value(projectId)
   python3 ops/check_gcp_infra_readiness.py
   ```

2. Review the exact dry-run packet. It enables only required APIs, creates
   separate staging deploy/runtime identities, creates the dedicated
   `observatory-read-adapter-main` WIF provider, keeps the artifact builder
   narrow, scopes Cloud SQL connect/login to `teleo-pgvector-standby`, and
   scopes secret access to `observatory-read-api-key-staging`:

   ```bash
   python3 ops/plan_observatory_read_adapter_gcp.py
   python3 ops/plan_observatory_read_adapter_gcp.py --format shell
   ```

   The shell output is unexecuted and uses `set -euo pipefail`. Apply it only
   after review with an authenticated `teleo-501523` administrator. The
   deployer custom role contains only `run.services.create`,
   `run.services.update`, `run.services.get`, `run.services.setIamPolicy`, and
   `run.operations.get`; it contains no delete permission. Do not add
   `run.admin`, `run.developer`, `cloudsql.admin`, `secretmanager.admin`,
   `compute.admin`, Editor, or Owner to either lane identity.
   The deployer binding uses the provider-unique
   `attribute.observatory_workflow` principal set. A pool-wide subject binding
   or the general `living-ip-github` provider is diagnostic-only and must not
   authenticate the preflight or deploy jobs. The preflight lists every
   provider in `github-actions`, including soft-deleted providers, and blocks
   if any non-dedicated provider maps that attribute.
3. Add the dedicated runtime service account as a Cloud SQL IAM
   service-account user on `teleo-pgvector-standby`.
4. From the existing private GCP VM database-admin path, run
   `ops/observatory_read_role.sql` against `teleo_canonical` with
   `OBSERVATORY_DB_IAM_USER=sa-observatory-read-adapter@teleo-501523.iam`.
   Do not read, copy, or retain the administrator password.
5. Create `observatory-read-api-key-staging`, add a generated whitespace-free
   32-byte-or-longer value through stdin only when no enabled version exists,
   and grant that secret's accessor role only to the runtime service account
   and the dedicated staging deploy/canary identity. Deployment pins the exact
   enabled version; it does not inject `latest` into Cloud Run.
6. Dispatch the read-only preflight from merged `main`:

   ```bash
   gh workflow run gcp-observatory-read-adapter.yml \
     --repo living-ip/teleo-infrastructure \
     --ref main \
     -f action=preflight_staging
   ```

   Require a passing `observatory-read-adapter-preflight` artifact before a
   bounded `action=deploy_staging` dispatch. The preflight verifies the exact
   WIF condition, custom-role permission sets, project/resource IAM bindings,
   Cloud Run service agent, private VPC/subnet relationship, private-only Cloud
   SQL network and IAM authentication, trimmed IAM database user, exact secret
   policy/value shape, and the run-unique immutable image. It also rejects any
   artifact-builder deploy, runtime `actAs`, secret, or service binding.

The workflow always builds a run-unique immutable image, deploys at most two Cloud Run
instances, and retains the positive response plus anonymous `401` and
authenticated POST `405` receipts. The retained positive receipt contains
identity/provenance, counts, and proposal/live state only; claim text, source
URLs, excerpts, unknown future provenance fields, and secret values are
removed by an explicit tested allowlist. The live gate binds the response to
the exact project, private instance, database principal, Git revision, Cloud
Run image digest, runtime identity, and pinned secret version. It does not
modify Vercel. The app-level `X-Api-Key` remains the staging authentication
boundary, so anonymous Cloud Run invocation reaches the adapter and returns
the required `401` instead of a platform-generated status.

## Current T2 Receipt And Exact T3 Gate

GitHub Actions run `29389090997` proves the existing `living-ip-github` WIF
provider can federate as the artifact builder, run the checker, upload its
artifact, and clean up credentials. The checker reported `8` pass, `10`
blocked, `0` fail. Eight blocks are missing inventory permissions on the
artifact-builder identity; two are missing restore/replication proof. This is
partial OIDC/T2 evidence, not T3 and not authorization to deploy with that
identity.

The exact external action is for `billy@livingip.xyz` to reauthenticate gcloud
and ADC, prove `gcloud projects describe teleo-501523`, run
`python3 ops/check_gcp_infra_readiness.py`, review and execute the planner's
shell output as a project IAM/API administrator, then apply
`ops/observatory_read_role.sql` from the existing private database-admin path.
The general readiness identity and artifact-builder identity are not deployer
substitutes. After those actions, dispatch `preflight_staging`; only a passing
artifact permits `deploy_staging`. A passing preflight remains T2 setup proof:
the fail-closed live claim and negative receipts are what establish T3.

The IAM basis is the Google Cloud deployment contract for Cloud Run (Cloud Run
access, Artifact Registry repository read, and exact runtime `actAs`), Secret
Manager resource-level accessor grants, Direct VPC access through the default
Cloud Run service agent, and conditional Cloud SQL client access to one named
instance:

- https://docs.cloud.google.com/run/docs/deploying
- https://docs.cloud.google.com/run/docs/authenticating/public
- https://docs.cloud.google.com/run/docs/configuring/vpc-direct-vpc
- https://docs.cloud.google.com/iam/docs/workload-identity-federation-with-deployment-pipelines
- https://docs.cloud.google.com/secret-manager/docs/manage-access-to-secrets
- https://docs.cloud.google.com/sql/docs/postgres/iam-conditions

## Unexecuted Cutover Packet

Production cutover requires a separate exact authorization and a
`living-ip/livingip-web` change. Do not execute these steps from this lane.

1. Add server-only Vercel secrets `OBSERVATORY_CANONICAL_API_URL` and
   `OBSERVATORY_CANONICAL_API_KEY` to a protected preview environment. The URL
   targets the GCP service; neither setting may use `NEXT_PUBLIC_`.
2. Add a Next.js server route or server action that calls the GCP API, validates
   `livingip.observatory-canonical-claim.v1`, and maps it to the Observatory UI.
   The browser calls only the same-origin Next.js route.
3. Prove preview Deployment Protection, anonymous denial, source/evidence
   rendering, proposal/live labels, and no browser key exposure.
4. Obtain exact production-repoint authorization naming the reviewed
   `livingip-web` commit and Vercel project before changing production values.

## Unexecuted Rollback Packet

1. Revert the authorized `livingip-web` connector commit or disable its
   server-side feature flag.
2. Restore the prior production Vercel values and redeploy the last accepted
   production revision.
3. Verify `/claims` again uses the prior `/api/claims` contract.
4. Leave the private Cloud SQL instance and adapter role intact while receipts
   are reviewed; scale the staging Cloud Run service to zero if isolation is
   required.
5. Delete the staging service/role/secret only under a separate cleanup
   decision. Rollback never adds a Cloud SQL public IP and never redirects the
   adapter to VPS `teleo-pg`.
