GCP Load Balancer & SuperTokens Infrastructure Setup
This document describes the complete infrastructure setup for ChainAlign's production environment on GCP, including Cloud Load Balancing with path-based routing and SuperTokens authentication.
Overview
ChainAlign uses a multi-layer architecture:
Browser → Cloudflare (CDN/Proxy)
→ GCP External Load Balancer
→ Cloud Run Services (Frontend/Backend)
→ SuperTokens Core (Auth)
→ Supabase PostgreSQL (Data)
1. Domain & DNS Configuration
Domain: d135.chainalign.com
- DNS Provider: Cloudflare
- Record Type: CNAME/Proxy
- Target: GCP Load Balancer IP (136.110.204.68)
- Cloudflare Proxy: Enabled (orange cloud)
- SSL: Full (strict) between Cloudflare and GCP origin
Verify DNS:
dig d135.chainalign.com +short
# Returns Cloudflare proxy IPs (104.21.x.x, 172.67.x.x)
2. GCP Load Balancer Architecture
2.1 External IP Address
gcloud compute forwarding-rules list --project=opspilot-865d9
# NAME IP_ADDRESS TARGET
# d135-frontend-https-rule 136.110.204.68 d135-frontend-https-proxy
2.2 HTTPS Proxy
gcloud compute target-https-proxies describe d135-frontend-https-proxy \
--project=opspilot-865d9
SSL Certificate: cloudflare-origin-cert
- Type: Self-managed
- Valid until: 2040-11-07
- Used for Cloudflare-to-GCP communication (Full SSL)
2.3 URL Map (Path-Based Routing)
Name: d135-frontend-lb
gcloud compute url-maps describe d135-frontend-lb \
--project=opspilot-865d9 --format=yaml
Routing Rules:
| Path Pattern | Backend Service | Target |
|---|---|---|
/api/* | chainalign-backend-bs | Backend Cloud Run |
/auth/* | chainalign-backend-bs | Backend Cloud Run (SuperTokens) |
/* (default) | d135-frontend-backend | Frontend Cloud Run |
2.4 Backend Services
Frontend Backend Service
gcloud compute backend-services describe d135-frontend-backend \
--global --project=opspilot-865d9
- Protocol: HTTP
- NEG:
d135-frontend-neg - Cloud Run Service: chainalign-frontend
Backend API Service
gcloud compute backend-services describe chainalign-backend-bs \
--global --project=opspilot-865d9
- Protocol: HTTP
- NEG:
chainalign-backend-neg - Cloud Run Service: chainalign-backend
2.5 Network Endpoint Groups (NEGs)
gcloud compute network-endpoint-groups list --project=opspilot-865d9
# NAME LOCATION ENDPOINT_TYPE SIZE
# d135-frontend-neg us-central1 SERVERLESS 0
# chainalign-backend-neg us-central1 SERVERLESS 0
Both are Serverless NEGs that automatically connect to Cloud Run services.
3. Cloud Run Configuration
3.1 Backend Service
Service URL: https://chainalign-backend-657144924568.us-central1.run.app
Ingress Settings:
gcloud run services describe chainalign-backend \
--region=us-central1 --project=opspilot-865d9 \
--format="value(metadata.annotations['run.googleapis.com/ingress'])"
# Output: internal-and-cloud-load-balancing
IAM Policy:
gcloud run services get-iam-policy chainalign-backend \
--region=us-central1 --project=opspilot-865d9
# Binding: allUsers → roles/run.invoker
This allows:
- ✅ Load Balancer to invoke the service
- ✅ Internal GCP services to invoke
- ❌ Direct public access (blocked by ingress setting)
Environment Variables:
SUPERTOKENS_CONNECTION_URI=https://chainalign-supertokens-tvfzibzoyq-uc.a.run.app
SUPERTOKENS_API_DOMAIN=https://d135.chainalign.com
SUPERTOKENS_WEBSITE_DOMAIN=https://d135.chainalign.com
3.2 Frontend Service
Service URL: https://chainalign-frontend-tvfzibzoyq-uc.a.run.app
Ingress: internal-and-cloud-load-balancing
3.3 SuperTokens Core Service
Service URL: https://chainalign-supertokens-tvfzibzoyq-uc.a.run.app
Database Connection:
postgresql://postgres:<password>@db.vflqqrqbmjzncwkuvqvc.supabase.co:5432/postgres?currentSchema=supertokens
API Key: Stored in GCP Secret Manager as SUPERTOKENS_API_KEY
4. SuperTokens Authentication Setup
4.1 Architecture
Frontend Browser
↓ (HTTPS)
Load Balancer (/auth/*)
↓
Backend Cloud Run (SuperTokens SDK)
↓ (HTTPS)
SuperTokens Core (Cloud Run)
↓ (PostgreSQL)
Supabase Database (supertokens schema)
4.2 Frontend Configuration
File: frontend/src/config/supertokens.js
const superTokensConfig = {
appInfo: {
appName: 'ChainAlign',
apiDomain: 'https://d135.chainalign.com', // Load Balancer URL
websiteDomain: 'https://d135.chainalign.com', // Same origin
apiBasePath: '/auth',
websiteBasePath: '/auth'
},
recipeList: [
Session.init(),
ThirdPartyEmailPassword.init(),
],
};
4.3 Backend Configuration
File: backend/src/services/ChainAlignAuth.js
supertokens.init({
framework: 'express',
supertokens: {
connectionURI: process.env.SUPERTOKENS_CONNECTION_URI,
apiKey: process.env.SUPERTOKENS_API_KEY
},
appInfo: {
appName: 'ChainAlign',
apiDomain: process.env.SUPERTOKENS_API_DOMAIN, // d135.chainalign.com
websiteDomain: process.env.SUPERTOKENS_WEBSITE_DOMAIN,
apiBasePath: '/auth',
websiteBasePath: '/auth'
},
// ... recipes
});
4.4 Database Schema (Supabase)
Schema: supertokens (33 tables)
Key Tables:
emailpassword_users- User credentialsall_auth_recipe_users- All users across recipessession_info- Active sessionsemailpassword_user_to_tenant- Multi-tenant user mapping
Application Tables (public schema):
tenant_users- ChainAlign tenant-user mapping with rolestenants- ChainAlign tenants
4.5 Creating a New User
Method 1: Via Frontend UI
Navigate to https://d135.chainalign.com/auth/signup
Method 2: Direct Database Insert
DO $$
DECLARE
v_user_id VARCHAR(36) := gen_random_uuid()::text;
v_email VARCHAR(255) := 'user@example.com';
v_password_hash VARCHAR(255) := '$2b$10$...'; -- bcrypt hash
v_time_joined BIGINT := EXTRACT(EPOCH FROM NOW())::BIGINT * 1000;
v_tenant_uuid UUID := '11111111-1111-1111-1111-111111111111';
BEGIN
-- 1. SuperTokens app_id_to_user_id
INSERT INTO supertokens.app_id_to_user_id (app_id, user_id, recipe_id)
VALUES ('public', v_user_id, 'emailpassword');
-- 2. SuperTokens all_auth_recipe_users
INSERT INTO supertokens.all_auth_recipe_users
(app_id, tenant_id, user_id, recipe_id, time_joined)
VALUES ('public', 'public', v_user_id, 'emailpassword', v_time_joined);
-- 3. SuperTokens emailpassword_users
INSERT INTO supertokens.emailpassword_users
(app_id, user_id, email, password_hash, time_joined)
VALUES ('public', v_user_id, v_email, v_password_hash, v_time_joined);
-- 4. SuperTokens emailpassword_user_to_tenant
INSERT INTO supertokens.emailpassword_user_to_tenant
(app_id, tenant_id, user_id, email)
VALUES ('public', 'public', v_user_id, v_email);
-- 5. ChainAlign tenant_users (with roles)
INSERT INTO public.tenant_users
(supertokens_user_id, tenant_id, user_email, roles)
VALUES (v_user_id, v_tenant_uuid, v_email, ARRAY['admin']);
END $$;
Generate bcrypt hash:
node -e "console.log(require('bcrypt').hashSync('your-password', 10))"
5. Security Considerations
5.1 Network Security
| Service | Ingress | IAM Policy | Direct Access |
|---|---|---|---|
| Backend | internal-and-cloud-load-balancing | allUsers (invoker) | ❌ Blocked |
| Frontend | internal-and-cloud-load-balancing | allUsers (invoker) | ❌ Blocked |
| SuperTokens | internal | None (via service account) | ❌ Blocked |
5.2 Authentication Flow
- User visits
d135.chainalign.com - Cloudflare terminates HTTPS, proxies to GCP LB
- Load Balancer routes
/auth/*to backend - Backend SuperTokens SDK validates credentials
- SuperTokens Core checks against Supabase DB
- Session tokens returned via HTTP-only cookies
- Subsequent requests include session tokens automatically
5.3 CORS Configuration
Since frontend and backend share the same origin (d135.chainalign.com), CORS is not an issue. This is a major benefit of the Load Balancer setup.
6. Maintenance & Operations
6.1 Viewing Logs
Backend:
gcloud logging read 'resource.type="cloud_run_revision" AND \
resource.labels.service_name="chainalign-backend"' \
--project=opspilot-865d9 --limit=50
SuperTokens:
gcloud logging read 'resource.type="cloud_run_revision" AND \
resource.labels.service_name="chainalign-supertokens"' \
--project=opspilot-865d9 --limit=50
6.2 Updating Services
Update backend environment variables:
gcloud run services update chainalign-backend \
--region=us-central1 \
--project=opspilot-865d9 \
--update-env-vars "KEY=value"
Update ingress (emergency public access):
gcloud run services update chainalign-backend \
--region=us-central1 \
--project=opspilot-865d9 \
--ingress=all # DANGER: Opens to public
6.3 Monitoring Backend Health
# Via Load Balancer
curl -s -o /dev/null -w "%{http_code}" https://d135.chainalign.com/api/health
# Should return 200 or 401 (auth required) - not 404/502/503
6.4 Rotating SuperTokens API Key
- Generate new key in SuperTokens Core
- Update GCP Secret Manager:
SUPERTOKENS_API_KEY - Redeploy backend service to pick up new secret
7. Troubleshooting
7.1 "Something went wrong" on login page
Cause: Frontend can't reach backend API
Check:
- Load Balancer routing:
curl https://d135.chainalign.com/auth/signin - Backend ingress allows LB: Should be
internal-and-cloud-load-balancing - Backend IAM has
allUsers:gcloud run services get-iam-policy...
7.2 401 Unauthorized on API calls
Expected for: Unauthenticated requests (healthy sign!)
Check for public endpoints:
// backend/server.js
const publicAuthPaths = [
'/api/internal/v1/auth/exchange',
'/api/internal/v1/auth/signup',
'/api/internal/v1/auth/signin'
];
7.3 SuperTokens Core not responding
Check service status:
gcloud run services describe chainalign-supertokens \
--region=us-central1 --project=opspilot-865d9 \
--format="yaml(status.conditions)"
Check database connectivity:
gcloud logging read '... AND "Pool stats"' --limit=5
# Should show: total=10, active=0, idle=10
7.4 CloudBuild not updating services
Verify trigger:
gcloud builds list --project=opspilot-865d9 --region=us-central1 --limit=5
Check image digest matches:
gcloud run revisions describe <revision-name> \
--region=us-central1 --project=opspilot-865d9 \
--format="value(spec.containers[0].image)"
8. Important URLs & Credentials
Production Environment (d135)
| Resource | URL/Value |
|---|---|
| Frontend | https://d135.chainalign.com |
| API | https://d135.chainalign.com/api/* |
| Auth | https://d135.chainalign.com/auth/* |
| Load Balancer IP | 136.110.204.68 |
| Supabase Host | db.vflqqrqbmjzncwkuvqvc.supabase.co |
| SuperTokens Schema | supertokens |
| App Schema | public |
GCP Resources
| Resource | Name | Region |
|---|---|---|
| URL Map | d135-frontend-lb | global |
| HTTPS Proxy | d135-frontend-https-proxy | global |
| Frontend Backend Service | d135-frontend-backend | global |
| Backend API Service | chainalign-backend-bs | global |
| Frontend NEG | d135-frontend-neg | us-central1 |
| Backend NEG | chainalign-backend-neg | us-central1 |
| SSL Certificate | cloudflare-origin-cert | global |
Secret Manager Keys
SUPERTOKENS_API_KEY- SuperTokens Core API keySUPABASE_PASSWORD- Database passwordGEMINI_API_KEY- AI service key
9. Future Considerations
9.1 Identity-Aware Proxy (IAP)
Current setup uses SuperTokens for app-level auth. For additional security layer:
# Enable IAP on backend service
gcloud iap web enable \
--resource-type=compute \
--service=chainalign-backend-bs \
--project=opspilot-865d9
# Add authorized users
gcloud iap web add-iam-policy-binding \
--resource-type=compute \
--service=chainalign-backend-bs \
--member="user:admin@chainalign.com" \
--role="roles/iap.httpsResourceAccessor" \
--project=opspilot-865d9
9.2 Rate Limiting
Configure via Cloud Armor security policies attached to backend services.
9.3 Custom Domains
To add more domains (e.g., app.chainalign.com):
- Add SSL certificate
- Update URL map host rules
- Configure Cloudflare DNS
10. Change Log
| Date | Change | Author |
|---|---|---|
| 2025-11-17 | Initial Load Balancer setup with backend routing | Claude Code |
| 2025-11-17 | SuperTokens Core configured with Supabase | Claude Code |
| 2025-11-17 | Path-based routing for /api/* and /auth/* | Claude Code |
Document Version: 1.0 Last Updated: 2025-11-17 Environment: D135 (Pre-production)