Deployment¶
Deploying Scribe is Terraform apply in the selected workspace. Terraform resolves API, frontend, Triplet, and OCR GAR tags to immutable digests. The deployment has no VM, Cloud Compose, Traefik, or cloud Vault dependency.
| Environment | Workspace | Public service | Images | Database |
|---|---|---|---|---|
| Production | prod | scribe | main | Regional Cloud SQL MySQL 8.4 |
| Development | dev | scribe-dev | main or image_tag | Isolated zonal Cloud SQL |
| Preview | pr-N | scribe-pr-N | PR API/frontend; main Triplet/OCR | Isolated zonal Cloud SQL |
Bootstrap secrets¶
While the existing dev/prod Vault service is still available, authenticate with gcloud and keyless Application Default Credentials, then copy its four application credential maps:
GCLOUD_PROJECT=your-project make secret-manager-secrets WORKSPACE=dev
GCLOUD_PROJECT=your-project make secret-manager-secrets WORKSPACE=prod
The command discovers the corresponding Vault service, uses the same operator login as make vault-secrets, and writes google_oauth, openai, gemini, and database/app to deterministic deployment-scoped Secret Manager names. It verifies every map after writing, preserves opaque values, and prints only the logical secret names. An unchanged value does not create another version. Missing optional provider credentials become empty maps; missing OAuth or database credentials fail. VAULT_ADDR, VAULT_TOKEN, and VAULT_ADMIN_TOKEN may be supplied explicitly for a locally accessible Vault.
Terraform adopts these exact bootstrap secrets with declarative imports. The copied database password initializes the new Cloud SQL user. The schema jobs initialize fresh databases. Preview credentials are independently generated by Terraform; previews cannot read dev/prod credentials or create provider secrets.
Apply¶
The foundation root owns enabled project APIs and the shared Artifact Registry. Apply it before application workspaces. It contains no Cloud Compose module.
terraform -chdir=terraform/foundation init -backend-config=bucket=YOUR_STATE_BUCKET -backend-config=prefix=scribe-foundation
terraform -chdir=terraform/foundation apply -var=project_id=YOUR_PROJECT
GCLOUD_PROJECT=YOUR_PROJECT make tf-dev ACTION=apply
GCLOUD_PROJECT=YOUR_PROJECT make tf-prod ACTION=apply
GCLOUD_PROJECT=YOUR_PROJECT make tf-preview PR=23 ACTION=apply
The protected main workflow runs CI, builds API/frontend/Triplet images in GAR, refreshes OCR images when needed, applies foundation, and invokes make tf-prod. Preview image builds run without credentials. The protected publisher promotes OCI artifacts to GAR without executing PR code; Terraform runs trusted source. Fork PRs receive CI only.
Terraform creates two finite Cloud Run migration jobs. Each starts a keyless Private Service Connect Cloud SQL proxy, applies its schema, and stops the proxy before exit. A straight Terraform provisioner sequence executes both jobs with --wait before creating API or worker revisions. A failed migration fails the apply.
The API service runs frontend, API, Triplet, PDF, and SQL-proxy containers.
The private worker service runs a request-driven worker, an API for source
reads, Triplet, and a SQL proxy. worker_min_instances defaults to zero and
request-based billing allows idle workers to scale to zero. Authenticated
Pub/Sub pushes wake workers for transcription; Cloud Scheduler invokes bounded
maintenance every thirty minutes for outboxes, retention, and recovery of missed
publishes or expired leases. The invocation identity has no data access.
Committed application events also push maintenance wake-ups, so outbox delivery
does not normally wait for the schedule. The thirty-minute fallback leaves an
idle window for scale-down; a lost wake-up can delay recovery until that pass.
The Scheduler job runs in us-east4, a supported Scheduler location, and invokes the
worker in its configured runtime region; it carries no application payload.
Transcription attempts cancel after nine minutes, before Pub/Sub's ten-minute
push deadline, and follow the existing fenced, bounded retry policy. Maintenance
requests finish within four minutes. No background work runs between requests.
Only the frontend and worker request ports
are ingress ports; all other listeners stay inside their instance.
When services or images change, Terraform executes backend_readiness_job and ocr_readiness_job. The backend job checks the deployed API and worker image digests, canonical origin, and database readiness over HTTPS. The OCR job sends a real image through the registered private OCR endpoints. These jobs have no database, upload, or secret access. A failed probe fails deployment.
Configuration and destruction¶
Allowed ingress CIDRs are enforced by the Cloud Run frontend. It accepts only the reviewed direct run.app forwarding topology, establishes external HTTPS, and forwards one validated client address to the loopback API. Custom load balancers require a separate reviewed topology. Production requires nonempty allowed_ips before apply. Runtime quotas are decoded from config.yaml and may be overridden with the bounded Terraform variables.
Production Cloud SQL and serving services have deletion protection. Preview resources have independent names, buckets, queues, credentials, and database instances, and can be removed with make tf-preview PR=N ACTION=destroy. No preview teardown requires a shared Vault namespace.
See configuration, troubleshooting, and backup and restore.
SQL connectivity uses a deployment-owned Private Service Connect endpoint and private Cloud DNS record. It creates no producer VPC peering to retain after a preview is deleted. Terraform removes the endpoint and DNS records before removing its SQL instance and application network.