# Bilko Stage Environment — Cloud Run Services (Phase 2)

# Overview

**MC:** #10177 Phase 2 | **Deployed:** 2026-04-30 | **Git SHA:** `1f48fdc` | **Status:** LIVE, healthy

**GCP Project:** tribal-sign-487920-k0 | **Region:** europe-north1

> **WARNING — TD-3 PROD CUTOVER BLOCKER (MC #10241):** `bilko-staging-db` uses public IP (0.0.0.0/0 authorized network, requireSsl=false). Acceptable for stage only. MUST NOT be replicated to production. Production deploy is blocked until Cloud SQL private IP + VPC connector is configured.

## Live Services

<table id="bkmrk-serviceurlimagemin%2Fm"><thead><tr><th>Service</th><th>URL</th><th>Image</th><th>Min/Max</th><th>Memory</th><th>Status</th></tr></thead><tbody><tr><td>`bilko-api-stage`</td><td>[bilko-api-stage](https://bilko-api-stage-dh4m46blja-lz.a.run.app)</td><td>`bilko/api:stage-1f48fdc`</td><td>0/2</td><td>512Mi, CPU 1</td><td>LIVE</td></tr><tr><td>`bilko-web-stage`</td><td>[bilko-web-stage](https://bilko-web-stage-dh4m46blja-lz.a.run.app)</td><td>`bilko/web:stage-1f48fdc`</td><td>0/2</td><td>512Mi, CPU 1</td><td>LIVE</td></tr></tbody></table>

Full Artifact Registry prefix: `europe-north1-docker.pkg.dev/tribal-sign-487920-k0/`

## bilko-api-stage Detail

<table id="bkmrk-fieldvalue-dockerfil"><thead><tr><th>Field</th><th>Value</th></tr></thead><tbody><tr><td>Dockerfile</td><td>`Dockerfile.api-kotlin` (Kotlin/Ktor, port 4001)</td></tr><tr><td>JAVA\_OPTS</td><td>HikariCP connection pool tuned</td></tr><tr><td>Cloud SQL</td><td>`tribal-sign-487920-k0:europe-north1:bilko-staging-db` via direct TCP 35.228.33.112:5432 (TD-2 + TD-3)</td></tr><tr><td>Secrets</td><td>`bilko-staging-db-password`, `bilko-jwt-secret`, `bilko-jwt-refresh-secret`, `bilko-staging-field-encryption-key` (NEW, ADR-014), `bilko-staging-field-hmac-key` (NEW, ADR-014)</td></tr><tr><td>SA</td><td>`bilko-api-stage-sa@tribal-sign-487920-k0.iam.gserviceaccount.com`</td></tr><tr><td>SA roles</td><td>`cloudsql.client`, `secretmanager.secretAccessor`</td></tr><tr><td>Smoke</td><td>`GET /api/v1/health` → 200 `{"status":"ok","service":"bilko-api","version":"1.0.0"}`</td></tr><tr><td>Revision</td><td>`bilko-api-stage-00001-5x8` (100% traffic)</td></tr></tbody></table>

## bilko-web-stage Detail

<table id="bkmrk-fieldvalue-dockerfil-1"><thead><tr><th>Field</th><th>Value</th></tr></thead><tbody><tr><td>Dockerfile</td><td>`apps/web/Dockerfile` (Next.js 15)</td></tr><tr><td>NEXT\_PUBLIC\_API\_URL</td><td>`https://bilko-api-stage-dh4m46blja-lz.a.run.app/api/v1`</td></tr><tr><td>NEXT\_PUBLIC\_APP\_ENV</td><td>`stage`</td></tr><tr><td>Smoke</td><td>`GET /` → 200 (HTML, lang=sr-Latn)</td></tr><tr><td>Revision</td><td>`bilko-web-stage-00001-c45` (100% traffic)</td></tr><tr><td>Build note</td><td>Fresh npm install (no lockfile) — workaround TD-1 MC #10239</td></tr></tbody></table>

## Smoke Test Commands

```
# API health (expected: {"status":"ok","service":"bilko-api","version":"1.0.0"})
curl -s https://bilko-api-stage-dh4m46blja-lz.a.run.app/api/v1/health

# Web root (expected: HTTP 200)
curl -s -o /dev/null -w "HTTP %{http_code}" https://bilko-web-stage-dh4m46blja-lz.a.run.app
```

## Stage Rollback

```
# List revisions
gcloud run revisions list --service bilko-api-stage --project=tribal-sign-487920-k0 --region=europe-north1

# Route to prior revision
gcloud run services update-traffic bilko-api-stage --project=tribal-sign-487920-k0 --region=europe-north1 --to-revisions=REVISION_NAME=100
```

## Stage Redeploy (image update only)

```
gcloud run services update bilko-api-stage --project=tribal-sign-487920-k0 --region=europe-north1 --image=europe-north1-docker.pkg.dev/tribal-sign-487920-k0/bilko/api:NEW_TAG
gcloud run services update bilko-web-stage --project=tribal-sign-487920-k0 --region=europe-north1 --image=europe-north1-docker.pkg.dev/tribal-sign-487920-k0/bilko/web:NEW_TAG
```

## Phase 2 Tech Debt Tracker

<table id="bkmrk-idmcdescriptionsever"><thead><tr><th>ID</th><th>MC</th><th>Description</th><th>Severity</th><th>Blocks</th></tr></thead><tbody><tr><td>TD-1</td><td>\#10239</td><td>package-lock.json macOS arm64 missing linux-x64 native bins — fresh npm install workaround</td><td>Medium</td><td>Clean stage re-deploys</td></tr><tr><td>TD-2</td><td>\#10240</td><td>postgres-socket-factory not in build.gradle.kts — Kotlin API uses direct TCP public IP</td><td>Medium</td><td>Secure DB connectivity</td></tr><tr><td>**TD-3**</td><td>**\#10241**</td><td>**bilko-staging-db: 0.0.0.0/0 + requireSsl=false — STAGE ONLY, NEVER replicate to prod**</td><td>**BLOCKER**</td><td>**PROD CUTOVER Phase 5**</td></tr></tbody></table>

## Key Learnings

1. Lockfile drift macOS/linux: fresh npm install required per build until TD-1 fixed
2. Kotlin Cloud SQL TCP via public IP works for stage, NOT prod (TD-2 + TD-3)
3. --no-traffic flag invalid on new service creation — route 100% on first deploy
4. Field encryption/HMAC keys are random per env (stage isolated from prod — ADR-014)
5. HikariCP socketPath URL param silently ignored — always use explicit host:port for direct TCP

## References

- Phase 1 Cloud SQL: Bilko Stage Environment — Cloud SQL &amp; IAM (Phase 1)
- MC #10177 (parent), #10239 / #10240 / #10241 (TD items)
- ADR-014 (field encryption), ADR-021 (blueprint Section 15)
- DEPLOY-MAP.md section: Cloud Run Stage Services
- RUNBOOK.md section: 7a Stage Cloud Run Services Access