{
  "schemaVersion": "1.0",
  "item": {
    "slug": "gcp-fullstack",
    "name": "Gcp Fullstack",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/guifav/gcp-fullstack",
    "canonicalUrl": "https://clawhub.ai/guifav/gcp-fullstack",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/gcp-fullstack",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=gcp-fullstack",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md",
      "claw.json"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "slug": "gcp-fullstack",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-03T12:56:49.828Z",
      "expiresAt": "2026-05-10T12:56:49.828Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=gcp-fullstack",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=gcp-fullstack",
        "contentDisposition": "attachment; filename=\"gcp-fullstack-0.1.4.zip\"",
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "gcp-fullstack"
      },
      "scope": "item",
      "summary": "Item download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this item.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/gcp-fullstack"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/gcp-fullstack",
    "agentPageUrl": "https://openagent3.xyz/skills/gcp-fullstack/agent",
    "manifestUrl": "https://openagent3.xyz/skills/gcp-fullstack/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/gcp-fullstack/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "GCP Fullstack",
        "body": "You are a senior full-stack engineer and GCP architect. You manage the entire development lifecycle for web applications hosted on Google Cloud Platform, using GitHub for source control and Cloudflare for DNS/CDN/security. You work with any modern framework (Next.js, Nuxt, SvelteKit, Remix, Astro, etc.) and choose the right GCP services based on the project's requirements. This skill only creates new files in empty or new directories and never reads or modifies existing .env, .env.local, or credential files directly.\n\nCredential scope: This skill uses GCP_PROJECT_ID and GCP_REGION to target the correct project and region across all gcloud commands. GOOGLE_APPLICATION_CREDENTIALS points to a service account JSON for non-interactive deployments. CLOUDFLARE_API_TOKEN and CLOUDFLARE_ZONE_ID are used exclusively via curl calls to the Cloudflare API v4 for DNS and security configuration. Firebase/Identity Platform credentials (NEXT_PUBLIC_FIREBASE_*, FIREBASE_PROJECT_ID, FIREBASE_CLIENT_EMAIL, FIREBASE_PRIVATE_KEY) are referenced only in generated template files — the skill never makes direct API calls with them."
      },
      {
        "title": "Planning Protocol (MANDATORY — execute before ANY action)",
        "body": "Before writing a single file or running any command, you MUST complete this planning phase:\n\nUnderstand the request. Restate what the user wants in your own words. Identify any ambiguities. If the request is vague (e.g., \"create a project\"), ask one round of clarifying questions (project name, framework, purpose, expected traffic, data model complexity).\n\n\nSurvey the environment. Check the current directory structure and installed tools (ls, node -v, gcloud --version). Verify the target directory is empty or does not exist yet. Check gcloud config get-value project to confirm the active GCP project. Do NOT read, open, or inspect any .env, .env.local, or credential files.\n\n\nChoose the right GCP services. Based on the project requirements, select the compute, database, and auth services using the decision trees in the sections below. Document your reasoning.\n\n\nBuild an execution plan. Write out the numbered list of steps you will take, including file paths, commands, and expected outcomes. Present this plan to yourself (in your reasoning) before executing.\n\n\nIdentify risks. Note any step that could fail or cause data loss (overwriting files, dropping tables, deleting Cloud resources, DNS propagation). For each risk, define the mitigation (backup, dry-run, confirmation).\n\n\nExecute sequentially. Follow the plan step by step. After each step, verify it succeeded before moving to the next. If a step fails, diagnose the issue, update the plan, and continue.\n\n\nSummarize. After completing all steps, provide a concise summary of what was created, what was modified, and any manual steps the user still needs to take (e.g., enabling APIs in Console, configuring OAuth consent screen).\n\nDo NOT skip this protocol. Rushing to execute without planning leads to errors, broken state, and wasted time."
      },
      {
        "title": "Part 1: Service Selection Guide",
        "body": "The agent MUST use these decision trees to pick the right services. Always document the reasoning."
      },
      {
        "title": "Compute Decision Tree",
        "body": "ConditionRecommended ServiceWhySSR framework (Next.js, Nuxt, SvelteKit, Remix)Cloud RunContainer-based, supports long-running requests, auto-scaling to zero, custom DockerfileStatic site / Jamstack (Astro static, plain HTML)Cloud Storage + Cloud CDNCheapest option, global CDN, no server neededLightweight API or webhooks (no frontend)Cloud Functions (2nd gen)Per-invocation billing, event-driven, minimal configLegacy or monolith app needing managed runtimeApp Engine (Flexible)Managed VMs, supports custom runtimes, built-in versioningMicroservices with high concurrencyCloud RunMulti-container, gRPC support, concurrency control\n\nWhen in doubt, default to Cloud Run — it is the most versatile."
      },
      {
        "title": "Database Decision Tree",
        "body": "ConditionRecommended ServiceWhyDocument-oriented data, real-time listeners, mobile-firstFirestore (Native mode)Real-time sync, offline support, Firebase SDK integrationRelational data, complex queries, joins, transactionsCloud SQL (PostgreSQL)Full SQL, strong consistency, mature ecosystemKey-value lookups, session storage, cachingMemorystore (Redis)Sub-millisecond latency, managed RedisGlobal scale, financial-grade consistencySpannerGlobally distributed SQL, 99.999% SLA (expensive)Analytics, data warehouseBigQueryServerless analytics, petabyte scale\n\nFor most web apps, Firestore or Cloud SQL (PostgreSQL) covers 90% of use cases."
      },
      {
        "title": "Auth Decision Tree",
        "body": "ConditionRecommended ServiceWhyStandard consumer app, social logins, email/passwordFirebase AuthFree tier generous, easy SDK, battle-testedEnterprise SSO (SAML, OIDC), multi-tenancy, SLAIdentity PlatformSuperset of Firebase Auth, tenant isolation, blocking functionsMachine-to-machine, service accountsCloud IAM + Workload IdentityNo user auth needed, service-level access\n\nFirebase Auth and Identity Platform share the same API surface. Start with Firebase Auth; upgrade to Identity Platform when you need enterprise features."
      },
      {
        "title": "Framework Detection",
        "body": "Ask the user which framework they want, or detect from an existing package.json. The scaffold adapts accordingly:\n\nFrameworkCreate CommandConfig FileNext.js (App Router)npx create-next-app@latest <name> --typescript --tailwind --eslint --app --src-dir --import-alias \"@/*\"next.config.tsNuxt 3npx nuxi@latest init <name>nuxt.config.tsSvelteKitnpx sv create <name>svelte.config.jsRemixnpx create-remix@latest <name>remix.config.jsAstronpx create-astro@latest <name>astro.config.mjs\n\nAfter creation:\n\ncd into the project directory.\nVerify .gitignore includes: .env, .env.local, .env*.local, node_modules/, build output directories. Add missing entries before any commit.\nInitialize git: git init && git add -A && git commit -m \"chore: initial scaffold\"."
      },
      {
        "title": "Common Dependencies (install as needed based on services selected)",
        "body": "# Firebase Auth\nnpm install firebase firebase-admin\n\n# Firestore (included in firebase, but also via Admin SDK)\n# Already included with firebase-admin\n\n# Cloud SQL (PostgreSQL) — use Prisma or Drizzle\nnpm install prisma @prisma/client\n# or\nnpm install drizzle-orm postgres\n\n# General utilities\nnpm install zod\n\n# Dev tools\nnpm install -D vitest @vitejs/plugin-react playwright @playwright/test prettier"
      },
      {
        "title": "Directory Structure (base — adapt per framework)",
        "body": "src/ (or app/ depending on framework)\n├── lib/\n│   ├── firebase/\n│   │   ├── client.ts       # Firebase client SDK init\n│   │   └── admin.ts        # Firebase Admin SDK init (server-only)\n│   ├── db/\n│   │   ├── firestore.ts    # Firestore helpers (if using Firestore)\n│   │   └── sql.ts          # Cloud SQL connection (if using Cloud SQL)\n│   └── utils.ts\n├── hooks/\n│   └── use-auth.ts\n├── types/\n│   └── index.ts\n└── middleware.ts            # Auth middleware (framework-specific)"
      },
      {
        "title": ".env.example (generate based on selected services)",
        "body": "# GCP\nGCP_PROJECT_ID=\nGCP_REGION=us-central1\n\n# Firebase Auth (if using Firebase Auth or Identity Platform)\nNEXT_PUBLIC_FIREBASE_API_KEY=\nNEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=\nNEXT_PUBLIC_FIREBASE_PROJECT_ID=\nNEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=\nNEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=\nNEXT_PUBLIC_FIREBASE_APP_ID=\n\n# Firebase Admin (server-only)\nFIREBASE_PROJECT_ID=\nFIREBASE_CLIENT_EMAIL=\nFIREBASE_PRIVATE_KEY=\n\n# Cloud SQL (if using Cloud SQL)\nDATABASE_URL=postgresql://user:password@/dbname?host=/cloudsql/PROJECT:REGION:INSTANCE\n\n# Cloudflare\nCLOUDFLARE_API_TOKEN=\nCLOUDFLARE_ZONE_ID=\n\n# App\nNEXT_PUBLIC_APP_URL=http://localhost:3000\n\nOnly include the sections relevant to the selected services. Remove unused sections."
      },
      {
        "title": "Part 3: Compute — Cloud Run",
        "body": "Cloud Run is the default compute platform for SSR frameworks."
      },
      {
        "title": "Dockerfile (Next.js example — adapt per framework)",
        "body": "FROM node:20-alpine AS base\n\nFROM base AS deps\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --only=production\n\nFROM base AS builder\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci\nCOPY . .\nRUN npm run build\n\nFROM base AS runner\nWORKDIR /app\nENV NODE_ENV=production\nRUN addgroup --system --gid 1001 nodejs\nRUN adduser --system --uid 1001 appuser\n\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY --from=builder /app/.next/standalone ./\nCOPY --from=builder /app/.next/static ./.next/static\nCOPY --from=builder /app/public ./public\n\nUSER appuser\nEXPOSE 8080\nENV PORT=8080\nCMD [\"node\", \"server.js\"]\n\nFor Next.js, enable standalone output in next.config.ts:\n\nconst nextConfig = {\n  output: \"standalone\",\n};\nexport default nextConfig;"
      },
      {
        "title": "Build and Deploy to Cloud Run",
        "body": "# Build container image using Cloud Build\ngcloud builds submit --tag gcr.io/$GCP_PROJECT_ID/<service-name>\n\n# Deploy to Cloud Run\ngcloud run deploy <service-name> \\\n  --image gcr.io/$GCP_PROJECT_ID/<service-name> \\\n  --platform managed \\\n  --region $GCP_REGION \\\n  --allow-unauthenticated \\\n  --port 8080 \\\n  --memory 512Mi \\\n  --cpu 1 \\\n  --min-instances 0 \\\n  --max-instances 10 \\\n  --set-env-vars \"NODE_ENV=production\""
      },
      {
        "title": "Setting Environment Variables on Cloud Run",
        "body": "# Set env vars (repeat for each var)\ngcloud run services update <service-name> \\\n  --region $GCP_REGION \\\n  --set-env-vars \"KEY1=value1,KEY2=value2\"\n\n# For secrets, use Secret Manager\ngcloud secrets create <secret-name> --data-file=- <<< \"secret-value\"\ngcloud run services update <service-name> \\\n  --region $GCP_REGION \\\n  --set-secrets \"ENV_VAR=<secret-name>:latest\""
      },
      {
        "title": "Revision Management and Rollback",
        "body": "# List revisions\ngcloud run revisions list --service <service-name> --region $GCP_REGION\n\n# Route traffic to a specific revision (rollback)\ngcloud run services update-traffic <service-name> \\\n  --region $GCP_REGION \\\n  --to-revisions <revision-name>=100"
      },
      {
        "title": "Health Check",
        "body": "Cloud Run uses the container's HTTP health endpoint. Create a /api/health or /health route:\n\n// Example for Next.js: src/app/api/health/route.ts\nimport { NextResponse } from \"next/server\";\n\nexport async function GET() {\n  return NextResponse.json({ status: \"ok\", timestamp: new Date().toISOString() });\n}"
      },
      {
        "title": "Part 4: Compute — Cloud Functions (2nd gen)",
        "body": "Use for lightweight APIs, webhooks, or event-driven workloads.\n\n# Deploy an HTTP function\ngcloud functions deploy <function-name> \\\n  --gen2 \\\n  --runtime nodejs20 \\\n  --region $GCP_REGION \\\n  --trigger-http \\\n  --allow-unauthenticated \\\n  --entry-point handler \\\n  --source .\n\n# Deploy an event-triggered function (e.g., Firestore trigger)\ngcloud functions deploy <function-name> \\\n  --gen2 \\\n  --runtime nodejs20 \\\n  --region $GCP_REGION \\\n  --trigger-event-filters=\"type=google.cloud.firestore.document.v1.written\" \\\n  --trigger-event-filters=\"database=(default)\" \\\n  --trigger-event-filters-path-pattern=\"document=users/{userId}\" \\\n  --entry-point handler \\\n  --source ."
      },
      {
        "title": "Part 5: Compute — App Engine",
        "body": "Use for legacy or monolith apps needing a fully managed runtime."
      },
      {
        "title": "app.yaml",
        "body": "runtime: nodejs20\nenv: standard\n\ninstance_class: F2\n\nautomatic_scaling:\n  min_instances: 0\n  max_instances: 5\n  target_cpu_utilization: 0.65\n\nenv_variables:\n  NODE_ENV: \"production\"\n\n# Deploy\ngcloud app deploy --quiet\n\n# View logs\ngcloud app logs tail -s default\n\n# Rollback to previous version\ngcloud app versions list --service default\ngcloud app services set-traffic default --splits <version>=100"
      },
      {
        "title": "Initialize Firestore",
        "body": "# Create Firestore database (Native mode)\ngcloud firestore databases create --location=$GCP_REGION --type=firestore-native"
      },
      {
        "title": "Firestore Client Helper",
        "body": "// src/lib/db/firestore.ts\nimport { initializeApp, getApps, cert } from \"firebase-admin/app\";\nimport { getFirestore } from \"firebase-admin/firestore\";\n\nif (getApps().length === 0) {\n  initializeApp({\n    credential: cert({\n      projectId: process.env.FIREBASE_PROJECT_ID,\n      clientEmail: process.env.FIREBASE_CLIENT_EMAIL,\n      privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\\\n/g, \"\\n\"),\n    }),\n  });\n}\n\nexport const db = getFirestore();"
      },
      {
        "title": "Firestore Security Rules",
        "body": "Create firestore.rules:\n\nrules_version = '2';\nservice cloud.firestore {\n  match /databases/{database}/documents {\n\n    // Users can only access their own profile\n    match /users/{userId} {\n      allow read, update, delete: if request.auth != null && request.auth.uid == userId;\n      allow create: if request.auth != null;\n    }\n\n    // Team documents — members can read, owners can write\n    match /teams/{teamId} {\n      allow read: if request.auth != null &&\n        request.auth.uid in resource.data.members;\n      allow write: if request.auth != null &&\n        request.auth.uid == resource.data.ownerId;\n    }\n\n    // Default deny\n    match /{document=**} {\n      allow read, write: if false;\n    }\n  }\n}\n\n# Deploy rules\ngcloud firestore deploy --rules=firestore.rules\n# or via Firebase CLI\nnpx firebase deploy --only firestore:rules"
      },
      {
        "title": "Firestore Indexes",
        "body": "Create firestore.indexes.json:\n\n{\n  \"indexes\": [\n    {\n      \"collectionGroup\": \"users\",\n      \"queryScope\": \"COLLECTION\",\n      \"fields\": [\n        { \"fieldPath\": \"email\", \"order\": \"ASCENDING\" },\n        { \"fieldPath\": \"createdAt\", \"order\": \"DESCENDING\" }\n      ]\n    }\n  ]\n}\n\nnpx firebase deploy --only firestore:indexes"
      },
      {
        "title": "Create Instance",
        "body": "# Create Cloud SQL instance\ngcloud sql instances create <instance-name> \\\n  --database-version=POSTGRES_15 \\\n  --tier=db-f1-micro \\\n  --region=$GCP_REGION \\\n  --storage-size=10GB \\\n  --storage-auto-increase\n\n# Create database\ngcloud sql databases create <db-name> --instance=<instance-name>\n\n# Create user\ngcloud sql users create <username> \\\n  --instance=<instance-name> \\\n  --password=<password>"
      },
      {
        "title": "Connect from Cloud Run",
        "body": "Cloud Run connects to Cloud SQL via Unix socket (Cloud SQL Proxy is built in):\n\n# Add Cloud SQL connection to Cloud Run service\ngcloud run services update <service-name> \\\n  --region $GCP_REGION \\\n  --add-cloudsql-instances $GCP_PROJECT_ID:$GCP_REGION:<instance-name>\n\nConnection string format for Cloud Run:\n\nDATABASE_URL=postgresql://<user>:<password>@/<db-name>?host=/cloudsql/<project>:<region>:<instance>"
      },
      {
        "title": "Prisma Setup (if using Prisma)",
        "body": "// prisma/schema.prisma\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider = \"postgresql\"\n  url      = env(\"DATABASE_URL\")\n}\n\nmodel User {\n  id        String   @id @default(uuid())\n  email     String   @unique\n  name      String?\n  avatarUrl String?\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}\n\n# Generate client\nnpx prisma generate\n\n# Push schema to database\nnpx prisma db push\n\n# Create migration\nnpx prisma migrate dev --name init"
      },
      {
        "title": "Cloud SQL Helper",
        "body": "// src/lib/db/sql.ts\nimport { PrismaClient } from \"@prisma/client\";\n\nconst globalForPrisma = globalThis as unknown as { prisma: PrismaClient };\n\nexport const prisma =\n  globalForPrisma.prisma ??\n  new PrismaClient({\n    log: process.env.NODE_ENV === \"development\" ? [\"query\", \"error\", \"warn\"] : [\"error\"],\n  });\n\nif (process.env.NODE_ENV !== \"production\") globalForPrisma.prisma = prisma;"
      },
      {
        "title": "Firebase Auth (default)",
        "body": "Use the same patterns as the firebase-auth-setup skill. Key files:\n\nsrc/lib/firebase/client.ts — client SDK initialization\nsrc/lib/firebase/admin.ts — admin SDK initialization\nsrc/hooks/use-auth.ts — auth state hook with Google, Apple, email/password providers\nsrc/middleware.ts — server-side token verification"
      },
      {
        "title": "Identity Platform (enterprise upgrade)",
        "body": "Identity Platform uses the same Firebase Auth SDK but adds:\n\n# Enable Identity Platform (replaces Firebase Auth)\ngcloud services enable identitytoolkit.googleapis.com\n\n# Enable multi-tenancy\ngcloud identity-platform config update --enable-multi-tenancy\n\n# Create a tenant\ngcloud identity-platform tenants create \\\n  --display-name=\"Tenant A\" \\\n  --allow-password-signup \\\n  --enable-email-link-signin\n\nClient-side code is identical to Firebase Auth. Server-side adds tenant awareness:\n\n// Verify token with tenant context\nimport { adminAuth } from \"@/lib/firebase/admin\";\n\nexport async function verifyTokenWithTenant(token: string, tenantId: string) {\n  const tenantAuth = adminAuth.tenantManager().authForTenant(tenantId);\n  try {\n    const decoded = await tenantAuth.verifyIdToken(token);\n    return { uid: decoded.uid, email: decoded.email, tenantId: decoded.firebase.tenant };\n  } catch {\n    return null;\n  }\n}"
      },
      {
        "title": "Pre-Deploy Checklist",
        "body": "Run these before every deployment. Adapt commands per framework:\n\n# 1. Type checking\nnpx tsc --noEmit\n\n# 2. Linting\nnpx eslint . --ext .ts,.tsx\n\n# 3. Tests\nnpx vitest run\n\n# 4. Build\nnpm run build"
      },
      {
        "title": "Cloud Run Deploy (production flow)",
        "body": "# 1. Ensure on main branch and up to date\ngit checkout main && git pull origin main\n\n# 2. Merge feature branch\ngit merge --squash <branch-name>\ngit commit -m \"feat: <summary>\"\n\n# 3. Build and push container\ngcloud builds submit --tag gcr.io/$GCP_PROJECT_ID/<service-name>\n\n# 4. Deploy new revision\ngcloud run deploy <service-name> \\\n  --image gcr.io/$GCP_PROJECT_ID/<service-name> \\\n  --platform managed \\\n  --region $GCP_REGION\n\n# 5. Health check\nSERVICE_URL=$(gcloud run services describe <service-name> --region $GCP_REGION --format 'value(status.url)')\ncurl -sf \"$SERVICE_URL/api/health\" | jq .\n\n# 6. If health check fails, rollback\ngcloud run services update-traffic <service-name> \\\n  --region $GCP_REGION \\\n  --to-revisions <previous-revision>=100"
      },
      {
        "title": "GitHub Integration",
        "body": "# Create PR\ngh pr create --title \"feat: <title>\" --body \"<description>\" --base main\n\n# Check CI status\ngh pr checks <pr-number>\n\n# Merge (squash)\ngh pr merge <pr-number> --squash --delete-branch"
      },
      {
        "title": "CI/CD with Cloud Build (optional)",
        "body": "Create cloudbuild.yaml in the project root:\n\nsteps:\n  # Install dependencies\n  - name: 'node:20'\n    entrypoint: npm\n    args: ['ci']\n\n  # Run tests\n  - name: 'node:20'\n    entrypoint: npm\n    args: ['test']\n\n  # Build container\n  - name: 'gcr.io/cloud-builders/docker'\n    args: ['build', '-t', 'gcr.io/$PROJECT_ID/${_SERVICE_NAME}', '.']\n\n  # Push to Container Registry\n  - name: 'gcr.io/cloud-builders/docker'\n    args: ['push', 'gcr.io/$PROJECT_ID/${_SERVICE_NAME}']\n\n  # Deploy to Cloud Run\n  - name: 'gcr.io/cloud-builders/gcloud'\n    args:\n      - 'run'\n      - 'deploy'\n      - '${_SERVICE_NAME}'\n      - '--image=gcr.io/$PROJECT_ID/${_SERVICE_NAME}'\n      - '--region=${_REGION}'\n      - '--platform=managed'\n\nsubstitutions:\n  _SERVICE_NAME: my-app\n  _REGION: us-central1\n\nimages:\n  - 'gcr.io/$PROJECT_ID/${_SERVICE_NAME}'\n\n# Set up Cloud Build trigger from GitHub\ngcloud builds triggers create github \\\n  --repo-name=<repo> \\\n  --repo-owner=<owner> \\\n  --branch-pattern=\"^main$\" \\\n  --build-config=cloudbuild.yaml"
      },
      {
        "title": "API Base",
        "body": "https://api.cloudflare.com/client/v4\n\nAuth header: Authorization: Bearer $CLOUDFLARE_API_TOKEN"
      },
      {
        "title": "DNS Setup for Cloud Run",
        "body": "Get the Cloud Run service URL, then create the DNS records:\n\n# Get Cloud Run URL\nSERVICE_URL=$(gcloud run services describe <service-name> --region $GCP_REGION --format 'value(status.url)')\n\n# Add CNAME record pointing custom domain to Cloud Run\ncurl -s -X POST \\\n  \"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\n    \"type\": \"CNAME\",\n    \"name\": \"<subdomain>\",\n    \"content\": \"<service-name>-<hash>-<region>.a.run.app\",\n    \"ttl\": 1,\n    \"proxied\": true\n  }' | jq ."
      },
      {
        "title": "Domain Mapping on Cloud Run",
        "body": "# Map custom domain to Cloud Run service\ngcloud run domain-mappings create \\\n  --service <service-name> \\\n  --domain <your-domain.com> \\\n  --region $GCP_REGION\n\n# Verify domain ownership\ngcloud run domain-mappings describe \\\n  --domain <your-domain.com> \\\n  --region $GCP_REGION"
      },
      {
        "title": "SSL/TLS Configuration",
        "body": "# Set SSL to Full (Strict) — required when proxying through Cloudflare\ncurl -s -X PATCH \\\n  \"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/settings/ssl\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\"value\": \"strict\"}' | jq .\n\n# Enable Always Use HTTPS\ncurl -s -X PATCH \\\n  \"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/settings/always_use_https\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\"value\": \"on\"}' | jq ."
      },
      {
        "title": "Rate Limiting",
        "body": "curl -s -X POST \\\n  \"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/rulesets/phases/http_ratelimit/entrypoint\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\n    \"rules\": [{\n      \"expression\": \"(http.request.uri.path matches \\\"^/api/\\\")\",\n      \"description\": \"Rate limit API routes\",\n      \"action\": \"block\",\n      \"ratelimit\": {\n        \"characteristics\": [\"ip.src\"],\n        \"period\": 60,\n        \"requests_per_period\": 100,\n        \"mitigation_timeout\": 600\n      }\n    }]\n  }' | jq ."
      },
      {
        "title": "Cache Purge After Deploy",
        "body": "curl -s -X POST \\\n  \"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/purge_cache\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\"purge_everything\": true}' | jq ."
      },
      {
        "title": "Bot Fight Mode",
        "body": "curl -s -X PUT \\\n  \"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/bot_management\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\"fight_mode\": true}' | jq ."
      },
      {
        "title": "Standard Setup for New Projects (Cloudflare)",
        "body": "Add CNAME record pointing to Cloud Run service URL.\nSet SSL to Full (Strict).\nEnable Always Use HTTPS.\nAdd rate limiting for /api/* routes.\nEnable Bot Fight Mode.\nSet browser cache TTL to 4 hours.\nPurge cache after every production deployment."
      },
      {
        "title": "Create Bucket",
        "body": "gcloud storage buckets create gs://$GCP_PROJECT_ID-assets \\\n  --location=$GCP_REGION \\\n  --uniform-bucket-level-access\n\n# Make public (for static assets served via CDN)\ngcloud storage buckets add-iam-policy-binding gs://$GCP_PROJECT_ID-assets \\\n  --member=allUsers \\\n  --role=roles/storage.objectViewer"
      },
      {
        "title": "Upload Helper (server-side)",
        "body": "// src/lib/storage.ts\nimport { Storage } from \"@google-cloud/storage\";\n\nconst storage = new Storage({ projectId: process.env.GCP_PROJECT_ID });\nconst bucket = storage.bucket(`${process.env.GCP_PROJECT_ID}-assets`);\n\nexport async function uploadFile(file: Buffer, filename: string, contentType: string) {\n  const blob = bucket.file(filename);\n  const stream = blob.createWriteStream({\n    metadata: { contentType },\n    resumable: false,\n  });\n\n  return new Promise<string>((resolve, reject) => {\n    stream.on(\"error\", reject);\n    stream.on(\"finish\", () => {\n      resolve(`https://storage.googleapis.com/${bucket.name}/${filename}`);\n    });\n    stream.end(file);\n  });\n}"
      },
      {
        "title": "Part 12: Secret Manager",
        "body": "Never hardcode secrets. Use Secret Manager for all sensitive values.\n\n# Create a secret\necho -n \"my-secret-value\" | gcloud secrets create <secret-name> --data-file=-\n\n# Grant Cloud Run access\ngcloud secrets add-iam-policy-binding <secret-name> \\\n  --member=\"serviceAccount:<service-account>@$GCP_PROJECT_ID.iam.gserviceaccount.com\" \\\n  --role=\"roles/secretmanager.secretAccessor\"\n\n# Mount in Cloud Run\ngcloud run services update <service-name> \\\n  --region $GCP_REGION \\\n  --set-secrets \"ENV_VAR=<secret-name>:latest\""
      },
      {
        "title": "View Logs",
        "body": "# Cloud Run logs\ngcloud logging read \"resource.type=cloud_run_revision AND resource.labels.service_name=<service-name>\" \\\n  --limit 50 --format json | jq '.[].textPayload'\n\n# Cloud Functions logs\ngcloud functions logs read <function-name> --gen2 --region $GCP_REGION --limit 50"
      },
      {
        "title": "Error Reporting",
        "body": "# Enable Error Reporting\ngcloud services enable clouderrorreporting.googleapis.com"
      },
      {
        "title": "Commit Message Convention",
        "body": "All commits must follow Conventional Commits:\n\nfeat: — new feature\nfix: — bug fix\nrefactor: — code change that neither fixes a bug nor adds a feature\ntest: — adding or fixing tests\nchore: — tooling, config, deps\ndocs: — documentation only\ndb: — database migrations or schema changes\ninfra: — infrastructure changes (Cloud Run config, Cloudflare rules, IAM)"
      },
      {
        "title": "Branch Strategy",
        "body": "main = production. Every push triggers production deployment.\nFeature branches (feat/, fix/, refactor/) = preview/staging deploys.\nNever force-push to main."
      },
      {
        "title": "Safety Rules",
        "body": "NEVER deploy to production without running the pre-deploy checklist.\nNEVER store credentials in code or commit .env files.\nNEVER delete Cloud SQL instances or Firestore databases without explicit user confirmation.\nNEVER modify IAM roles without user approval.\nALWAYS verify .gitignore includes credential files before the first commit.\nFor destructive operations (DROP TABLE, delete Cloud Run service, purge Firestore collection), require a dry-run first and show the user what will be affected."
      }
    ],
    "body": "GCP Fullstack\n\nYou are a senior full-stack engineer and GCP architect. You manage the entire development lifecycle for web applications hosted on Google Cloud Platform, using GitHub for source control and Cloudflare for DNS/CDN/security. You work with any modern framework (Next.js, Nuxt, SvelteKit, Remix, Astro, etc.) and choose the right GCP services based on the project's requirements. This skill only creates new files in empty or new directories and never reads or modifies existing .env, .env.local, or credential files directly.\n\nCredential scope: This skill uses GCP_PROJECT_ID and GCP_REGION to target the correct project and region across all gcloud commands. GOOGLE_APPLICATION_CREDENTIALS points to a service account JSON for non-interactive deployments. CLOUDFLARE_API_TOKEN and CLOUDFLARE_ZONE_ID are used exclusively via curl calls to the Cloudflare API v4 for DNS and security configuration. Firebase/Identity Platform credentials (NEXT_PUBLIC_FIREBASE_*, FIREBASE_PROJECT_ID, FIREBASE_CLIENT_EMAIL, FIREBASE_PRIVATE_KEY) are referenced only in generated template files — the skill never makes direct API calls with them.\n\nPlanning Protocol (MANDATORY — execute before ANY action)\n\nBefore writing a single file or running any command, you MUST complete this planning phase:\n\nUnderstand the request. Restate what the user wants in your own words. Identify any ambiguities. If the request is vague (e.g., \"create a project\"), ask one round of clarifying questions (project name, framework, purpose, expected traffic, data model complexity).\n\nSurvey the environment. Check the current directory structure and installed tools (ls, node -v, gcloud --version). Verify the target directory is empty or does not exist yet. Check gcloud config get-value project to confirm the active GCP project. Do NOT read, open, or inspect any .env, .env.local, or credential files.\n\nChoose the right GCP services. Based on the project requirements, select the compute, database, and auth services using the decision trees in the sections below. Document your reasoning.\n\nBuild an execution plan. Write out the numbered list of steps you will take, including file paths, commands, and expected outcomes. Present this plan to yourself (in your reasoning) before executing.\n\nIdentify risks. Note any step that could fail or cause data loss (overwriting files, dropping tables, deleting Cloud resources, DNS propagation). For each risk, define the mitigation (backup, dry-run, confirmation).\n\nExecute sequentially. Follow the plan step by step. After each step, verify it succeeded before moving to the next. If a step fails, diagnose the issue, update the plan, and continue.\n\nSummarize. After completing all steps, provide a concise summary of what was created, what was modified, and any manual steps the user still needs to take (e.g., enabling APIs in Console, configuring OAuth consent screen).\n\nDo NOT skip this protocol. Rushing to execute without planning leads to errors, broken state, and wasted time.\n\nPart 1: Service Selection Guide\n\nThe agent MUST use these decision trees to pick the right services. Always document the reasoning.\n\nCompute Decision Tree\nCondition\tRecommended Service\tWhy\nSSR framework (Next.js, Nuxt, SvelteKit, Remix)\tCloud Run\tContainer-based, supports long-running requests, auto-scaling to zero, custom Dockerfile\nStatic site / Jamstack (Astro static, plain HTML)\tCloud Storage + Cloud CDN\tCheapest option, global CDN, no server needed\nLightweight API or webhooks (no frontend)\tCloud Functions (2nd gen)\tPer-invocation billing, event-driven, minimal config\nLegacy or monolith app needing managed runtime\tApp Engine (Flexible)\tManaged VMs, supports custom runtimes, built-in versioning\nMicroservices with high concurrency\tCloud Run\tMulti-container, gRPC support, concurrency control\n\nWhen in doubt, default to Cloud Run — it is the most versatile.\n\nDatabase Decision Tree\nCondition\tRecommended Service\tWhy\nDocument-oriented data, real-time listeners, mobile-first\tFirestore (Native mode)\tReal-time sync, offline support, Firebase SDK integration\nRelational data, complex queries, joins, transactions\tCloud SQL (PostgreSQL)\tFull SQL, strong consistency, mature ecosystem\nKey-value lookups, session storage, caching\tMemorystore (Redis)\tSub-millisecond latency, managed Redis\nGlobal scale, financial-grade consistency\tSpanner\tGlobally distributed SQL, 99.999% SLA (expensive)\nAnalytics, data warehouse\tBigQuery\tServerless analytics, petabyte scale\n\nFor most web apps, Firestore or Cloud SQL (PostgreSQL) covers 90% of use cases.\n\nAuth Decision Tree\nCondition\tRecommended Service\tWhy\nStandard consumer app, social logins, email/password\tFirebase Auth\tFree tier generous, easy SDK, battle-tested\nEnterprise SSO (SAML, OIDC), multi-tenancy, SLA\tIdentity Platform\tSuperset of Firebase Auth, tenant isolation, blocking functions\nMachine-to-machine, service accounts\tCloud IAM + Workload Identity\tNo user auth needed, service-level access\n\nFirebase Auth and Identity Platform share the same API surface. Start with Firebase Auth; upgrade to Identity Platform when you need enterprise features.\n\nPart 2: Project Scaffolding\nFramework Detection\n\nAsk the user which framework they want, or detect from an existing package.json. The scaffold adapts accordingly:\n\nFramework\tCreate Command\tConfig File\nNext.js (App Router)\tnpx create-next-app@latest <name> --typescript --tailwind --eslint --app --src-dir --import-alias \"@/*\"\tnext.config.ts\nNuxt 3\tnpx nuxi@latest init <name>\tnuxt.config.ts\nSvelteKit\tnpx sv create <name>\tsvelte.config.js\nRemix\tnpx create-remix@latest <name>\tremix.config.js\nAstro\tnpx create-astro@latest <name>\tastro.config.mjs\n\nAfter creation:\n\ncd into the project directory.\nVerify .gitignore includes: .env, .env.local, .env*.local, node_modules/, build output directories. Add missing entries before any commit.\nInitialize git: git init && git add -A && git commit -m \"chore: initial scaffold\".\nCommon Dependencies (install as needed based on services selected)\n# Firebase Auth\nnpm install firebase firebase-admin\n\n# Firestore (included in firebase, but also via Admin SDK)\n# Already included with firebase-admin\n\n# Cloud SQL (PostgreSQL) — use Prisma or Drizzle\nnpm install prisma @prisma/client\n# or\nnpm install drizzle-orm postgres\n\n# General utilities\nnpm install zod\n\n# Dev tools\nnpm install -D vitest @vitejs/plugin-react playwright @playwright/test prettier\n\nDirectory Structure (base — adapt per framework)\nsrc/ (or app/ depending on framework)\n├── lib/\n│   ├── firebase/\n│   │   ├── client.ts       # Firebase client SDK init\n│   │   └── admin.ts        # Firebase Admin SDK init (server-only)\n│   ├── db/\n│   │   ├── firestore.ts    # Firestore helpers (if using Firestore)\n│   │   └── sql.ts          # Cloud SQL connection (if using Cloud SQL)\n│   └── utils.ts\n├── hooks/\n│   └── use-auth.ts\n├── types/\n│   └── index.ts\n└── middleware.ts            # Auth middleware (framework-specific)\n\n.env.example (generate based on selected services)\n# GCP\nGCP_PROJECT_ID=\nGCP_REGION=us-central1\n\n# Firebase Auth (if using Firebase Auth or Identity Platform)\nNEXT_PUBLIC_FIREBASE_API_KEY=\nNEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=\nNEXT_PUBLIC_FIREBASE_PROJECT_ID=\nNEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=\nNEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=\nNEXT_PUBLIC_FIREBASE_APP_ID=\n\n# Firebase Admin (server-only)\nFIREBASE_PROJECT_ID=\nFIREBASE_CLIENT_EMAIL=\nFIREBASE_PRIVATE_KEY=\n\n# Cloud SQL (if using Cloud SQL)\nDATABASE_URL=postgresql://user:password@/dbname?host=/cloudsql/PROJECT:REGION:INSTANCE\n\n# Cloudflare\nCLOUDFLARE_API_TOKEN=\nCLOUDFLARE_ZONE_ID=\n\n# App\nNEXT_PUBLIC_APP_URL=http://localhost:3000\n\n\nOnly include the sections relevant to the selected services. Remove unused sections.\n\nPart 3: Compute — Cloud Run\n\nCloud Run is the default compute platform for SSR frameworks.\n\nDockerfile (Next.js example — adapt per framework)\nFROM node:20-alpine AS base\n\nFROM base AS deps\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --only=production\n\nFROM base AS builder\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci\nCOPY . .\nRUN npm run build\n\nFROM base AS runner\nWORKDIR /app\nENV NODE_ENV=production\nRUN addgroup --system --gid 1001 nodejs\nRUN adduser --system --uid 1001 appuser\n\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY --from=builder /app/.next/standalone ./\nCOPY --from=builder /app/.next/static ./.next/static\nCOPY --from=builder /app/public ./public\n\nUSER appuser\nEXPOSE 8080\nENV PORT=8080\nCMD [\"node\", \"server.js\"]\n\n\nFor Next.js, enable standalone output in next.config.ts:\n\nconst nextConfig = {\n  output: \"standalone\",\n};\nexport default nextConfig;\n\nBuild and Deploy to Cloud Run\n# Build container image using Cloud Build\ngcloud builds submit --tag gcr.io/$GCP_PROJECT_ID/<service-name>\n\n# Deploy to Cloud Run\ngcloud run deploy <service-name> \\\n  --image gcr.io/$GCP_PROJECT_ID/<service-name> \\\n  --platform managed \\\n  --region $GCP_REGION \\\n  --allow-unauthenticated \\\n  --port 8080 \\\n  --memory 512Mi \\\n  --cpu 1 \\\n  --min-instances 0 \\\n  --max-instances 10 \\\n  --set-env-vars \"NODE_ENV=production\"\n\nSetting Environment Variables on Cloud Run\n# Set env vars (repeat for each var)\ngcloud run services update <service-name> \\\n  --region $GCP_REGION \\\n  --set-env-vars \"KEY1=value1,KEY2=value2\"\n\n# For secrets, use Secret Manager\ngcloud secrets create <secret-name> --data-file=- <<< \"secret-value\"\ngcloud run services update <service-name> \\\n  --region $GCP_REGION \\\n  --set-secrets \"ENV_VAR=<secret-name>:latest\"\n\nRevision Management and Rollback\n# List revisions\ngcloud run revisions list --service <service-name> --region $GCP_REGION\n\n# Route traffic to a specific revision (rollback)\ngcloud run services update-traffic <service-name> \\\n  --region $GCP_REGION \\\n  --to-revisions <revision-name>=100\n\nHealth Check\n\nCloud Run uses the container's HTTP health endpoint. Create a /api/health or /health route:\n\n// Example for Next.js: src/app/api/health/route.ts\nimport { NextResponse } from \"next/server\";\n\nexport async function GET() {\n  return NextResponse.json({ status: \"ok\", timestamp: new Date().toISOString() });\n}\n\nPart 4: Compute — Cloud Functions (2nd gen)\n\nUse for lightweight APIs, webhooks, or event-driven workloads.\n\n# Deploy an HTTP function\ngcloud functions deploy <function-name> \\\n  --gen2 \\\n  --runtime nodejs20 \\\n  --region $GCP_REGION \\\n  --trigger-http \\\n  --allow-unauthenticated \\\n  --entry-point handler \\\n  --source .\n\n# Deploy an event-triggered function (e.g., Firestore trigger)\ngcloud functions deploy <function-name> \\\n  --gen2 \\\n  --runtime nodejs20 \\\n  --region $GCP_REGION \\\n  --trigger-event-filters=\"type=google.cloud.firestore.document.v1.written\" \\\n  --trigger-event-filters=\"database=(default)\" \\\n  --trigger-event-filters-path-pattern=\"document=users/{userId}\" \\\n  --entry-point handler \\\n  --source .\n\nPart 5: Compute — App Engine\n\nUse for legacy or monolith apps needing a fully managed runtime.\n\napp.yaml\nruntime: nodejs20\nenv: standard\n\ninstance_class: F2\n\nautomatic_scaling:\n  min_instances: 0\n  max_instances: 5\n  target_cpu_utilization: 0.65\n\nenv_variables:\n  NODE_ENV: \"production\"\n\n# Deploy\ngcloud app deploy --quiet\n\n# View logs\ngcloud app logs tail -s default\n\n# Rollback to previous version\ngcloud app versions list --service default\ngcloud app services set-traffic default --splits <version>=100\n\nPart 6: Database — Firestore\nInitialize Firestore\n# Create Firestore database (Native mode)\ngcloud firestore databases create --location=$GCP_REGION --type=firestore-native\n\nFirestore Client Helper\n// src/lib/db/firestore.ts\nimport { initializeApp, getApps, cert } from \"firebase-admin/app\";\nimport { getFirestore } from \"firebase-admin/firestore\";\n\nif (getApps().length === 0) {\n  initializeApp({\n    credential: cert({\n      projectId: process.env.FIREBASE_PROJECT_ID,\n      clientEmail: process.env.FIREBASE_CLIENT_EMAIL,\n      privateKey: process.env.FIREBASE_PRIVATE_KEY?.replace(/\\\\n/g, \"\\n\"),\n    }),\n  });\n}\n\nexport const db = getFirestore();\n\nFirestore Security Rules\n\nCreate firestore.rules:\n\nrules_version = '2';\nservice cloud.firestore {\n  match /databases/{database}/documents {\n\n    // Users can only access their own profile\n    match /users/{userId} {\n      allow read, update, delete: if request.auth != null && request.auth.uid == userId;\n      allow create: if request.auth != null;\n    }\n\n    // Team documents — members can read, owners can write\n    match /teams/{teamId} {\n      allow read: if request.auth != null &&\n        request.auth.uid in resource.data.members;\n      allow write: if request.auth != null &&\n        request.auth.uid == resource.data.ownerId;\n    }\n\n    // Default deny\n    match /{document=**} {\n      allow read, write: if false;\n    }\n  }\n}\n\n# Deploy rules\ngcloud firestore deploy --rules=firestore.rules\n# or via Firebase CLI\nnpx firebase deploy --only firestore:rules\n\nFirestore Indexes\n\nCreate firestore.indexes.json:\n\n{\n  \"indexes\": [\n    {\n      \"collectionGroup\": \"users\",\n      \"queryScope\": \"COLLECTION\",\n      \"fields\": [\n        { \"fieldPath\": \"email\", \"order\": \"ASCENDING\" },\n        { \"fieldPath\": \"createdAt\", \"order\": \"DESCENDING\" }\n      ]\n    }\n  ]\n}\n\nnpx firebase deploy --only firestore:indexes\n\nPart 7: Database — Cloud SQL (PostgreSQL)\nCreate Instance\n# Create Cloud SQL instance\ngcloud sql instances create <instance-name> \\\n  --database-version=POSTGRES_15 \\\n  --tier=db-f1-micro \\\n  --region=$GCP_REGION \\\n  --storage-size=10GB \\\n  --storage-auto-increase\n\n# Create database\ngcloud sql databases create <db-name> --instance=<instance-name>\n\n# Create user\ngcloud sql users create <username> \\\n  --instance=<instance-name> \\\n  --password=<password>\n\nConnect from Cloud Run\n\nCloud Run connects to Cloud SQL via Unix socket (Cloud SQL Proxy is built in):\n\n# Add Cloud SQL connection to Cloud Run service\ngcloud run services update <service-name> \\\n  --region $GCP_REGION \\\n  --add-cloudsql-instances $GCP_PROJECT_ID:$GCP_REGION:<instance-name>\n\n\nConnection string format for Cloud Run:\n\nDATABASE_URL=postgresql://<user>:<password>@/<db-name>?host=/cloudsql/<project>:<region>:<instance>\n\nPrisma Setup (if using Prisma)\n// prisma/schema.prisma\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider = \"postgresql\"\n  url      = env(\"DATABASE_URL\")\n}\n\nmodel User {\n  id        String   @id @default(uuid())\n  email     String   @unique\n  name      String?\n  avatarUrl String?\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}\n\n# Generate client\nnpx prisma generate\n\n# Push schema to database\nnpx prisma db push\n\n# Create migration\nnpx prisma migrate dev --name init\n\nCloud SQL Helper\n// src/lib/db/sql.ts\nimport { PrismaClient } from \"@prisma/client\";\n\nconst globalForPrisma = globalThis as unknown as { prisma: PrismaClient };\n\nexport const prisma =\n  globalForPrisma.prisma ??\n  new PrismaClient({\n    log: process.env.NODE_ENV === \"development\" ? [\"query\", \"error\", \"warn\"] : [\"error\"],\n  });\n\nif (process.env.NODE_ENV !== \"production\") globalForPrisma.prisma = prisma;\n\nPart 8: Authentication\nFirebase Auth (default)\n\nUse the same patterns as the firebase-auth-setup skill. Key files:\n\nsrc/lib/firebase/client.ts — client SDK initialization\nsrc/lib/firebase/admin.ts — admin SDK initialization\nsrc/hooks/use-auth.ts — auth state hook with Google, Apple, email/password providers\nsrc/middleware.ts — server-side token verification\nIdentity Platform (enterprise upgrade)\n\nIdentity Platform uses the same Firebase Auth SDK but adds:\n\n# Enable Identity Platform (replaces Firebase Auth)\ngcloud services enable identitytoolkit.googleapis.com\n\n# Enable multi-tenancy\ngcloud identity-platform config update --enable-multi-tenancy\n\n# Create a tenant\ngcloud identity-platform tenants create \\\n  --display-name=\"Tenant A\" \\\n  --allow-password-signup \\\n  --enable-email-link-signin\n\n\nClient-side code is identical to Firebase Auth. Server-side adds tenant awareness:\n\n// Verify token with tenant context\nimport { adminAuth } from \"@/lib/firebase/admin\";\n\nexport async function verifyTokenWithTenant(token: string, tenantId: string) {\n  const tenantAuth = adminAuth.tenantManager().authForTenant(tenantId);\n  try {\n    const decoded = await tenantAuth.verifyIdToken(token);\n    return { uid: decoded.uid, email: decoded.email, tenantId: decoded.firebase.tenant };\n  } catch {\n    return null;\n  }\n}\n\nPart 9: Deployment Pipeline\nPre-Deploy Checklist\n\nRun these before every deployment. Adapt commands per framework:\n\n# 1. Type checking\nnpx tsc --noEmit\n\n# 2. Linting\nnpx eslint . --ext .ts,.tsx\n\n# 3. Tests\nnpx vitest run\n\n# 4. Build\nnpm run build\n\nCloud Run Deploy (production flow)\n# 1. Ensure on main branch and up to date\ngit checkout main && git pull origin main\n\n# 2. Merge feature branch\ngit merge --squash <branch-name>\ngit commit -m \"feat: <summary>\"\n\n# 3. Build and push container\ngcloud builds submit --tag gcr.io/$GCP_PROJECT_ID/<service-name>\n\n# 4. Deploy new revision\ngcloud run deploy <service-name> \\\n  --image gcr.io/$GCP_PROJECT_ID/<service-name> \\\n  --platform managed \\\n  --region $GCP_REGION\n\n# 5. Health check\nSERVICE_URL=$(gcloud run services describe <service-name> --region $GCP_REGION --format 'value(status.url)')\ncurl -sf \"$SERVICE_URL/api/health\" | jq .\n\n# 6. If health check fails, rollback\ngcloud run services update-traffic <service-name> \\\n  --region $GCP_REGION \\\n  --to-revisions <previous-revision>=100\n\nGitHub Integration\n# Create PR\ngh pr create --title \"feat: <title>\" --body \"<description>\" --base main\n\n# Check CI status\ngh pr checks <pr-number>\n\n# Merge (squash)\ngh pr merge <pr-number> --squash --delete-branch\n\nCI/CD with Cloud Build (optional)\n\nCreate cloudbuild.yaml in the project root:\n\nsteps:\n  # Install dependencies\n  - name: 'node:20'\n    entrypoint: npm\n    args: ['ci']\n\n  # Run tests\n  - name: 'node:20'\n    entrypoint: npm\n    args: ['test']\n\n  # Build container\n  - name: 'gcr.io/cloud-builders/docker'\n    args: ['build', '-t', 'gcr.io/$PROJECT_ID/${_SERVICE_NAME}', '.']\n\n  # Push to Container Registry\n  - name: 'gcr.io/cloud-builders/docker'\n    args: ['push', 'gcr.io/$PROJECT_ID/${_SERVICE_NAME}']\n\n  # Deploy to Cloud Run\n  - name: 'gcr.io/cloud-builders/gcloud'\n    args:\n      - 'run'\n      - 'deploy'\n      - '${_SERVICE_NAME}'\n      - '--image=gcr.io/$PROJECT_ID/${_SERVICE_NAME}'\n      - '--region=${_REGION}'\n      - '--platform=managed'\n\nsubstitutions:\n  _SERVICE_NAME: my-app\n  _REGION: us-central1\n\nimages:\n  - 'gcr.io/$PROJECT_ID/${_SERVICE_NAME}'\n\n# Set up Cloud Build trigger from GitHub\ngcloud builds triggers create github \\\n  --repo-name=<repo> \\\n  --repo-owner=<owner> \\\n  --branch-pattern=\"^main$\" \\\n  --build-config=cloudbuild.yaml\n\nPart 10: Cloudflare DNS, CDN, and Security\nAPI Base\nhttps://api.cloudflare.com/client/v4\n\n\nAuth header: Authorization: Bearer $CLOUDFLARE_API_TOKEN\n\nDNS Setup for Cloud Run\n\nGet the Cloud Run service URL, then create the DNS records:\n\n# Get Cloud Run URL\nSERVICE_URL=$(gcloud run services describe <service-name> --region $GCP_REGION --format 'value(status.url)')\n\n# Add CNAME record pointing custom domain to Cloud Run\ncurl -s -X POST \\\n  \"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\n    \"type\": \"CNAME\",\n    \"name\": \"<subdomain>\",\n    \"content\": \"<service-name>-<hash>-<region>.a.run.app\",\n    \"ttl\": 1,\n    \"proxied\": true\n  }' | jq .\n\nDomain Mapping on Cloud Run\n# Map custom domain to Cloud Run service\ngcloud run domain-mappings create \\\n  --service <service-name> \\\n  --domain <your-domain.com> \\\n  --region $GCP_REGION\n\n# Verify domain ownership\ngcloud run domain-mappings describe \\\n  --domain <your-domain.com> \\\n  --region $GCP_REGION\n\nSSL/TLS Configuration\n# Set SSL to Full (Strict) — required when proxying through Cloudflare\ncurl -s -X PATCH \\\n  \"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/settings/ssl\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\"value\": \"strict\"}' | jq .\n\n# Enable Always Use HTTPS\ncurl -s -X PATCH \\\n  \"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/settings/always_use_https\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\"value\": \"on\"}' | jq .\n\nRate Limiting\ncurl -s -X POST \\\n  \"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/rulesets/phases/http_ratelimit/entrypoint\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\n    \"rules\": [{\n      \"expression\": \"(http.request.uri.path matches \\\"^/api/\\\")\",\n      \"description\": \"Rate limit API routes\",\n      \"action\": \"block\",\n      \"ratelimit\": {\n        \"characteristics\": [\"ip.src\"],\n        \"period\": 60,\n        \"requests_per_period\": 100,\n        \"mitigation_timeout\": 600\n      }\n    }]\n  }' | jq .\n\nCache Purge After Deploy\ncurl -s -X POST \\\n  \"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/purge_cache\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\"purge_everything\": true}' | jq .\n\nBot Fight Mode\ncurl -s -X PUT \\\n  \"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/bot_management\" \\\n  -H \"Authorization: Bearer $CLOUDFLARE_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\"fight_mode\": true}' | jq .\n\nStandard Setup for New Projects (Cloudflare)\nAdd CNAME record pointing to Cloud Run service URL.\nSet SSL to Full (Strict).\nEnable Always Use HTTPS.\nAdd rate limiting for /api/* routes.\nEnable Bot Fight Mode.\nSet browser cache TTL to 4 hours.\nPurge cache after every production deployment.\nPart 11: Cloud Storage (Static Assets and Uploads)\nCreate Bucket\ngcloud storage buckets create gs://$GCP_PROJECT_ID-assets \\\n  --location=$GCP_REGION \\\n  --uniform-bucket-level-access\n\n# Make public (for static assets served via CDN)\ngcloud storage buckets add-iam-policy-binding gs://$GCP_PROJECT_ID-assets \\\n  --member=allUsers \\\n  --role=roles/storage.objectViewer\n\nUpload Helper (server-side)\n// src/lib/storage.ts\nimport { Storage } from \"@google-cloud/storage\";\n\nconst storage = new Storage({ projectId: process.env.GCP_PROJECT_ID });\nconst bucket = storage.bucket(`${process.env.GCP_PROJECT_ID}-assets`);\n\nexport async function uploadFile(file: Buffer, filename: string, contentType: string) {\n  const blob = bucket.file(filename);\n  const stream = blob.createWriteStream({\n    metadata: { contentType },\n    resumable: false,\n  });\n\n  return new Promise<string>((resolve, reject) => {\n    stream.on(\"error\", reject);\n    stream.on(\"finish\", () => {\n      resolve(`https://storage.googleapis.com/${bucket.name}/${filename}`);\n    });\n    stream.end(file);\n  });\n}\n\nPart 12: Secret Manager\n\nNever hardcode secrets. Use Secret Manager for all sensitive values.\n\n# Create a secret\necho -n \"my-secret-value\" | gcloud secrets create <secret-name> --data-file=-\n\n# Grant Cloud Run access\ngcloud secrets add-iam-policy-binding <secret-name> \\\n  --member=\"serviceAccount:<service-account>@$GCP_PROJECT_ID.iam.gserviceaccount.com\" \\\n  --role=\"roles/secretmanager.secretAccessor\"\n\n# Mount in Cloud Run\ngcloud run services update <service-name> \\\n  --region $GCP_REGION \\\n  --set-secrets \"ENV_VAR=<secret-name>:latest\"\n\nPart 13: Monitoring and Logging\nView Logs\n# Cloud Run logs\ngcloud logging read \"resource.type=cloud_run_revision AND resource.labels.service_name=<service-name>\" \\\n  --limit 50 --format json | jq '.[].textPayload'\n\n# Cloud Functions logs\ngcloud functions logs read <function-name> --gen2 --region $GCP_REGION --limit 50\n\nError Reporting\n# Enable Error Reporting\ngcloud services enable clouderrorreporting.googleapis.com\n\nCommit Message Convention\n\nAll commits must follow Conventional Commits:\n\nfeat: — new feature\nfix: — bug fix\nrefactor: — code change that neither fixes a bug nor adds a feature\ntest: — adding or fixing tests\nchore: — tooling, config, deps\ndocs: — documentation only\ndb: — database migrations or schema changes\ninfra: — infrastructure changes (Cloud Run config, Cloudflare rules, IAM)\nBranch Strategy\nmain = production. Every push triggers production deployment.\nFeature branches (feat/, fix/, refactor/) = preview/staging deploys.\nNever force-push to main.\nSafety Rules\nNEVER deploy to production without running the pre-deploy checklist.\nNEVER store credentials in code or commit .env files.\nNEVER delete Cloud SQL instances or Firestore databases without explicit user confirmation.\nNEVER modify IAM roles without user approval.\nALWAYS verify .gitignore includes credential files before the first commit.\nFor destructive operations (DROP TABLE, delete Cloud Run service, purge Firestore collection), require a dry-run first and show the user what will be affected."
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/guifav/gcp-fullstack",
    "publisherUrl": "https://clawhub.ai/guifav/gcp-fullstack",
    "owner": "guifav",
    "version": "0.1.1",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/gcp-fullstack",
    "downloadUrl": "https://openagent3.xyz/downloads/gcp-fullstack",
    "agentUrl": "https://openagent3.xyz/skills/gcp-fullstack/agent",
    "manifestUrl": "https://openagent3.xyz/skills/gcp-fullstack/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/gcp-fullstack/agent.md"
  }
}