d4c65d5857
Architecture: intelligence stays on Legion; only compiled artifacts cross to GCP. Source code and Neuron's knowledge base never leave the system. Artifact Registry: - neuron-marketing, neuron-accounts, neuron-api repos in us-central1 - Keep-last-10 cleanup policy; ci-pusher SA with writer access - Legion CI runners authenticate via GCP_SA_KEY Gitea secret Cloud SQL (cloud-sql.tf): - postgres-15 on db-g1-small, us-central1 (scale up to REGIONAL HA at 1k users) - Point-in-time recovery, 14-day backup retention - Accounts DB + user; password generated and stored in Secret Manager - JWT signing key in Secret Manager (shared by accounts + api) - Cloud Run connects via built-in Auth Proxy (Unix socket volume mount) Accounts Cloud Run (cloud-run-accounts.tf): - 3 regions (us-central1, europe-west1, asia-northeast1), min:1 max:50 - Cloud SQL proxy volume mount; secrets via Secret Manager - Stripe + JWT env vars; health probe on /health API Cloud Run (cloud-run-api.tf): - 3 regions, min:1 max:100, cpu_idle=false (always-hot) - Validates JWTs from accounts service; no direct DB connection - License admin token from Secret Manager Load balancer (host-based routing): - Same global anycast IP for all three services - URL map routes by Host: neurontechnologies.ai→marketing, api.neurontechnologies.ai→api, accounts.neurontechnologies.ai→accounts - New managed SSL certs for api.* and accounts.* added to HTTPS proxy - Cloud Armor (WAF + rate limit) applied to all backends Service accounts + IAM: - neuron-accounts-sa: secretmanager.secretAccessor + cloudsql.client - neuron-api-sa: secretmanager.secretAccessor - allUsers invoker on all prod Cloud Run services (LB health checks) bootstrap.sh: - One-shot setup: pulls Stripe secrets from Vault → Secret Manager, creates CI SA JSON key, prints DNS + next-step instructions
98 lines
4.3 KiB
Bash
Executable File
98 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ── GCP Bootstrap — run once after terraform apply ────────────────────────────
|
|
# Sets up secrets and credentials that Terraform can't create automatically.
|
|
#
|
|
# Prerequisites:
|
|
# - GOOGLE_APPLICATION_CREDENTIALS or `gcloud auth application-default login`
|
|
# - GCP project access
|
|
# - Vault running (for reading existing secrets)
|
|
# - terraform apply has run successfully (creates SA, secrets)
|
|
#
|
|
# Usage:
|
|
# cd servers/gcp
|
|
# terraform apply
|
|
# bash bootstrap.sh
|
|
|
|
set -euo pipefail
|
|
|
|
PROJECT="neuron-785695"
|
|
VAULT_ADDR="${VAULT_ADDR:-https://vault.neuralplatform.ai}"
|
|
|
|
echo "==> Bootstrapping GCP secrets and CI credentials"
|
|
|
|
# ── 1. Populate Stripe secrets in GCP Secret Manager ─────────────────────────
|
|
echo "--> Pulling Stripe secrets from Vault..."
|
|
MARKETING_SECRETS=$(curl -sf \
|
|
-H "X-Vault-Token: ${VAULT_TOKEN}" \
|
|
"${VAULT_ADDR}/v1/secret/data/neuron-technologies/marketing")
|
|
|
|
populate_secret() {
|
|
local secret_id="$1"
|
|
local vault_key="$2"
|
|
local value
|
|
value=$(echo "${MARKETING_SECRETS}" | python3 -c "
|
|
import json,sys; d=json.load(sys.stdin)['data']['data']; print(d.get('${vault_key}',''))
|
|
")
|
|
if [ -z "${value}" ]; then
|
|
echo " WARN: vault key '${vault_key}' not found — skipping ${secret_id}"
|
|
return
|
|
fi
|
|
# Check if secret already has a version
|
|
if gcloud secrets versions list "${secret_id}" --project="${PROJECT}" \
|
|
--format="value(name)" 2>/dev/null | grep -q .; then
|
|
echo " SKIP: ${secret_id} already has a version"
|
|
else
|
|
echo "${value}" | gcloud secrets versions add "${secret_id}" \
|
|
--project="${PROJECT}" \
|
|
--data-file=-
|
|
echo " OK: ${secret_id}"
|
|
fi
|
|
}
|
|
|
|
populate_secret "stripe-secret-key" "stripe_secret_key"
|
|
populate_secret "stripe-webhook-secret" "stripe_webhook_secret"
|
|
populate_secret "stripe-price-professional" "stripe_price_professional"
|
|
populate_secret "stripe-price-founding" "stripe_price_founding"
|
|
|
|
# ── 2. License admin token ────────────────────────────────────────────────────
|
|
echo "--> Populating license-admin-token..."
|
|
LICENSE_TOKEN=$(curl -sf \
|
|
-H "X-Vault-Token: ${VAULT_TOKEN}" \
|
|
"${VAULT_ADDR}/v1/secret/data/neuron-technologies/marketing" \
|
|
| python3 -c "import json,sys; print(json.load(sys.stdin)['data']['data'].get('license_admin_token',''))")
|
|
if [ -n "${LICENSE_TOKEN}" ]; then
|
|
echo "${LICENSE_TOKEN}" | gcloud secrets versions add "license-admin-token" \
|
|
--project="${PROJECT}" --data-file=- 2>/dev/null || echo " SKIP: already exists"
|
|
echo " OK: license-admin-token"
|
|
fi
|
|
|
|
# ── 3. Create CI service account JSON key ────────────────────────────────────
|
|
CI_SA_EMAIL=$(terraform output -raw ci_pusher_email)
|
|
echo "--> Creating CI SA key for: ${CI_SA_EMAIL}"
|
|
KEY_FILE="/tmp/neuron-ci-pusher.json"
|
|
gcloud iam service-accounts keys create "${KEY_FILE}" \
|
|
--iam-account="${CI_SA_EMAIL}" \
|
|
--project="${PROJECT}"
|
|
echo " SA key written to: ${KEY_FILE}"
|
|
echo ""
|
|
echo " !! Add this as a Gitea secret GCP_SA_KEY in the neuron-technologies/neuron repo:"
|
|
echo " tea secret set --repo neuron-technologies/neuron GCP_SA_KEY < ${KEY_FILE}"
|
|
echo " Then delete the key file:"
|
|
echo " rm ${KEY_FILE}"
|
|
echo ""
|
|
|
|
# ── 4. Summary ────────────────────────────────────────────────────────────────
|
|
echo "==> Bootstrap complete"
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. Set Gitea secret: tea secret set --repo neuron-technologies/neuron GCP_SA_KEY < /tmp/neuron-ci-pusher.json"
|
|
echo " 2. Delete key file: rm /tmp/neuron-ci-pusher.json"
|
|
echo " 3. Point DNS at: $(terraform output -raw prod_lb_ip)"
|
|
echo " A records (proxied=true in Cloudflare):"
|
|
echo " neurontechnologies.ai → prod_lb_ip"
|
|
echo " www.neurontechnologies.ai → prod_lb_ip"
|
|
echo " api.neurontechnologies.ai → prod_lb_ip"
|
|
echo " accounts.neurontechnologies.ai → prod_lb_ip"
|
|
echo " 4. Run a marketing CI build on main to push first image to GCP AR"
|
|
echo " 5. Check SSL cert provisioning: gcloud compute ssl-certificates list --project=${PROJECT}"
|