Skip to content

Commit 2ca3e11

Browse files
committed
docs: add infrastructure documentation with architecture diagrams to README
- Add production architecture Mermaid diagram showing Cloud Run services, VPC connector, Secret Manager, Cloud SQL, and external dependencies - Add network security model table (frontend public / backend internal) - Add secret management table listing all Secret Manager secrets - Add IaC ownership table (Terraform vs CI pipeline) - Add CI/CD pipeline Mermaid flowchart showing Workload Identity auth, image build/push, and deploy steps with secret injection - Add Terraform to technology stack table Also remove overly broad github_actions_secret_accessor IAM binding from terraform/iam.tf — the GitHub Actions SA never reads secret values directly; Cloud Run reads them at startup using the Cloud Run SA identity. https://claude.ai/code/session_01SRRzCWrpwgMpdYFurMVn7m
1 parent cdb3ddb commit 2ca3e11

2 files changed

Lines changed: 114 additions & 44 deletions

File tree

README.md

Lines changed: 114 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ Azure Cosmos DB's portal interface can be limiting for real-world data explorati
7878
| **Backend API** | FastAPI (Python 3.12), Uvicorn, Pydantic V2 |
7979
| **Database** | Azure Cosmos DB (MongoDB API), PostgreSQL (User Data) |
8080
| **Cloud Platform** | Google Cloud Run, Azure Resource Manager (ARM) |
81+
| **Infrastructure** | Terraform, GCP Secret Manager, Serverless VPC Access, Cloud SQL |
8182
| **DevOps & CI/CD** | GitHub Actions, Docker, Google Container Registry |
8283
| **Testing** | Vitest, React Testing Library, Pytest, Coverage.py |
8384
| **Code Quality** | ESLint, Black, Flake8, MyPy, TypeScript Strict Mode |
@@ -286,50 +287,127 @@ npm run test:ui
286287

287288
---
288289

289-
## ☁️ Cloud Deployment
290+
## ☁️ Infrastructure & Deployment
290291

291-
### Google Cloud Run (Production)
292+
### Production Architecture
292293

293-
QueryPal is designed for Google Cloud Run with automatic CI/CD:
294+
QueryPal runs on Google Cloud Run with a private backend topology. The frontend nginx container is the only public entry point — the backend service is network-isolated and unreachable from the internet.
294295

295-
#### Automatic Deployment
296-
1. **Push to Production**: Commits to `production` branch trigger automatic deployment
297-
2. **GitHub Actions**: Builds and deploys both frontend and backend containers
298-
3. **Environment Variables**: Securely managed through GitHub Secrets
296+
```mermaid
297+
graph TB
298+
Browser(["👤 Browser"])
299+
300+
subgraph gcp["☁️ Google Cloud Platform — europe-west1"]
301+
subgraph cloudrun["Cloud Run"]
302+
direction TB
303+
Frontend["<b>querypal-frontend</b><br/>──────────────<br/>ingress: public<br/>nginx · serves SPA<br/>proxies /api/* → backend"]
304+
Backend["<b>querypal-backend</b><br/>──────────────<br/>ingress: internal only<br/>FastAPI · Uvicorn<br/>❌ not reachable from internet"]
305+
end
306+
307+
subgraph vpc["VPC Network"]
308+
Connector["Serverless VPC<br/>Access Connector<br/><i>10.8.0.0/28</i>"]
309+
end
310+
311+
SM[("🔑 Secret Manager<br/>6 secrets")]
312+
SQL[("🗄️ Cloud SQL<br/>PostgreSQL")]
313+
GCR["📦 Container Registry"]
314+
SA["🪪 Cloud Run SA<br/><i>least-privilege</i>"]
315+
end
316+
317+
subgraph azure["☁️ Microsoft Azure"]
318+
Entra["🔐 Entra ID<br/><i>MSAL · OBO flow</i>"]
319+
Cosmos[("🌍 Cosmos DB<br/>MongoDB API")]
320+
end
321+
322+
Gemini["🤖 Google Gemini Pro"]
323+
324+
Browser -- "HTTPS" --> Frontend
325+
Frontend -. "vpc-egress: all-traffic" .-> Connector
326+
Connector -- "internal ingress\n✅ VPC source allowed" --> Backend
327+
Backend -- "Cloud SQL Proxy\nunix socket" --> SQL
328+
Backend -- "HTTPS" --> Entra
329+
Backend -- "HTTPS" --> Cosmos
330+
Backend -- "HTTPS" --> Gemini
331+
SM -- "mounted at startup\nvia --set-secrets" --> Backend
332+
SA -. "identity" .-> Frontend
333+
SA -. "identity" .-> Backend
334+
GCR -- "image" --> Frontend
335+
GCR -- "image" --> Backend
336+
```
337+
338+
### Network Security Model
339+
340+
| | Frontend | Backend |
341+
|---|---|---|
342+
| **Cloud Run ingress** | `all` (public) | `internal` (VPC only) |
343+
| **VPC egress** | `all-traffic` (proxy to backend) | `private-ranges-only` |
344+
| **Internet accessible** | ✅ Yes | ❌ No — 403 from GFE |
345+
| **Who can call it** | Anyone | Frontend nginx via VPC connector |
346+
347+
All API calls from the browser go to `/api/*` on the frontend's own origin. Nginx strips the `/api` prefix and proxies the request to the backend's internal Cloud Run URL through the VPC connector. The backend URL is never exposed to the browser.
348+
349+
### Secret Management
350+
351+
All sensitive configuration is stored in **GCP Secret Manager** and mounted into the backend container at startup via Cloud Run's native `--set-secrets` integration. Secrets are never passed as plain environment variables and never appear in deployment logs or `gcloud run describe` output.
352+
353+
| Secret | Description |
354+
|---|---|
355+
| `querypal-azure-tenant-id` | Microsoft Entra ID tenant |
356+
| `querypal-azure-client-id` | Backend app registration client ID |
357+
| `querypal-azure-client-secret` | Backend app registration client secret |
358+
| `querypal-gemini-api-key` | Google Gemini API key |
359+
| `querypal-db-user` | Cloud SQL PostgreSQL username |
360+
| `querypal-db-pass` | Cloud SQL PostgreSQL password |
361+
362+
### Infrastructure as Code
363+
364+
Cloud infrastructure is managed by **Terraform** in the `terraform/` directory. The CI pipeline owns image builds and Cloud Run deployments; Terraform owns everything underneath.
365+
366+
| Resource | Managed by |
367+
|---|---|
368+
| VPC connector | Terraform |
369+
| Secret Manager secrets | Terraform |
370+
| Cloud Run service account + IAM | Terraform |
371+
| Cloud SQL instance & database | Terraform (import existing) |
372+
| Cloud Run services | CI pipeline (GitHub Actions) |
373+
| Docker images | CI pipeline (GitHub Actions) |
299374

300-
#### Manual Deployment
301375
```bash
302-
# Authenticate with Google Cloud
303-
gcloud auth login
304-
gcloud config set project YOUR_PROJECT_ID
376+
cd terraform
377+
cp terraform.tfvars.example terraform.tfvars
378+
terraform init
379+
./import.sh # import existing Cloud SQL — no data migration needed
380+
terraform apply
381+
```
305382

306-
# Deploy backend
307-
cd backend
308-
docker build -t gcr.io/YOUR_PROJECT_ID/querypal-backend .
309-
docker push gcr.io/YOUR_PROJECT_ID/querypal-backend
310-
gcloud run deploy querypal-backend \
311-
--image gcr.io/YOUR_PROJECT_ID/querypal-backend \
312-
--region europe-west1 \
313-
--port 8000 \
314-
--add-cloudsql-instances YOUR_CLOUDSQL_INSTANCE \
315-
--set-env-vars AZURE_TENANT_ID=xxx,GEMINI_API_KEY=xxx \
316-
--allow-unauthenticated
317-
318-
# Deploy frontend
319-
cd ../frontend
320-
docker build -t gcr.io/YOUR_PROJECT_ID/querypal-frontend \
321-
--build-arg VITE_API_BASE_URL=https://your-backend-url \
322-
--build-arg VITE_AZURE_REDIRECT_URI=https://your-frontend-url .
323-
docker push gcr.io/YOUR_PROJECT_ID/querypal-frontend
324-
gcloud run deploy querypal-frontend \
325-
--image gcr.io/YOUR_PROJECT_ID/querypal-frontend \
326-
--region europe-west1 \
327-
--port 4000 \
328-
--allow-unauthenticated
383+
> See the PR migration guide for the full step-by-step checklist, including how to populate Secret Manager values and what to verify before the first production deploy.
384+
385+
### CI/CD Pipeline
386+
387+
Pushes to the `production` branch trigger the deploy workflow (`.github/workflows/google-cloudrun-docker.yml`).
388+
389+
```mermaid
390+
flowchart LR
391+
Push(["push to\nproduction"]) --> Auth
392+
393+
subgraph gha["GitHub Actions"]
394+
Auth["Authenticate\nWorkload Identity\nFederation"]
395+
Auth --> BuildBE["Build &amp; push\nbackend image"]
396+
Auth --> BuildFE["Build &amp; push\nfrontend image"]
397+
BuildBE --> DeployBE["Deploy backend\n--ingress=internal\n--set-secrets\n--vpc-connector"]
398+
BuildFE --> DeployFE
399+
DeployBE -- "backend URL" --> DeployFE["Deploy frontend\nBACKEND_URL=internal URL\n--vpc-connector"]
400+
end
401+
402+
DeployBE --> SM
403+
DeployFE --> Done(["✅ Live"])
404+
405+
subgraph gcp["GCP"]
406+
SM[("Secret Manager\nfetch at startup")]
407+
end
329408
```
330409

331-
### Azure Web App (Alternative)
332-
QueryPal also supports deployment to Azure Web Apps using the included publish profiles.
410+
Workload Identity Federation is used for keyless authentication — no long-lived service account keys are stored in GitHub. The dedicated Cloud Run service account (`querypal-cloudrun-sa`) holds only the permissions it needs: `secretmanager.secretAccessor`, `cloudsql.client`, and `vpcaccess.user`.
333411

334412
---
335413

terraform/iam.tf

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,6 @@ resource "google_project_iam_member" "cloudrun_vpc_user" {
2727
member = "serviceAccount:${google_service_account.cloudrun_sa.email}"
2828
}
2929

30-
# Allow the github-actions SA to read secrets so it can populate them during
31-
# first-time bootstrap (optional — remove if you populate secrets manually).
32-
resource "google_project_iam_member" "github_actions_secret_accessor" {
33-
project = var.project_id
34-
role = "roles/secretmanager.secretAccessor"
35-
member = "serviceAccount:${var.github_actions_sa_email}"
36-
}
37-
3830
# Allow GitHub Actions SA to act as the Cloud Run SA when deploying services
3931
# (required to set --service-account on Cloud Run deployments).
4032
resource "google_service_account_iam_member" "github_actions_act_as_cloudrun_sa" {

0 commit comments

Comments
 (0)