Snapshots

Every key sits in Secret Manager; nobody owns rotation yet

Waiting on you
  • Services read secrets by name at deploy; production has its own copies of four.
  • Never rotate the vault key by adding a version alone; it would break stored sign-ins.
  • Staging, production and the demo share the Anthropic, OpenAI and treg keys.
  • The default compute account has project-wide secret access, reaching about 15 services.
  • No source names an owner for any secret, so all of them are unowned today.
15Services the default account reaches
Your call

Who rotates which secret, and do production keys get split from staging's?

Ops and infrastructure · 8.6

Secrets and keys

DraftLast checked Mon Oct 26Owner Param

Answer

Every key Brew uses lives in Google Secret Manager in project squareup-492617 and reaches a service only by name at deploy time, production has its own copies of the four that protect sign-ins and data, and this page names each secret and where it is used, never its value; who rotates what is still Param's call.

Today

Where secrets live

  • Google Secret Manager, project squareup-492617. Services read them as NAME:latest at deploy. brew/deploy.sh:42 brew/tools/prod-env.sh:10
  • Production runs as brew-prod-runtime@; staging, the demo and the ops jobs run as brew-runtime@ and cannot read production's secrets or data. brew/tools/prod-env.sh:5-6
  • Brands' own sign-ins are not secrets in Secret Manager: they are encrypted with the vault key and stored per brand under private/vault/. brew/db/connector-ping.mjs:11 memory brew-storage
  • Locally, keys come from ../ugc-factory/.env, or the file named in ENV_FILE. brew/README.md

Secrets by site

Secret What it is for Staging Production Demo Source
BREW_STAGING_DATABASE_URL Staging Postgres yes brew/deploy.sh:42
BREW_PROD_DATABASE_URL Production Postgres yes brew/tools/prod-env.sh:10
FEED_SESSION_SECRET / _PROD Signs sign-in sessions plain _PROD plain brew/deploy.sh:42 brew/tools/prod-env.sh:10 brew/tools/deploy-demo.sh:17
HANDOFF_SECRET / _PROD Signs hand-off links plain _PROD plain brew/deploy.sh:42 brew/tools/prod-env.sh:10
FEED_VAULT_KEY / _PROD Encrypts each brand's stored sign-ins plain _PROD brew/deploy.sh:42 brew/tools/prod-env.sh:10
ANTHROPIC_API_KEY Claude yes yes yes brew/deploy.sh:42 brew/tools/prod-env.sh:10
OPENAI_API_KEY OpenAI fallback for media yes yes yes brew/deploy.sh:42 brew/tools/prod-env.sh:10
TREG_TOKEN treg data calls yes yes yes brew/deploy.sh:42 brew/tools/prod-env.sh:10
YOUTUBE_API_KEY YouTube comments for listening yes yes yes brew/deploy.sh:42 brew/tools/prod-env.sh:10
PAGESPEED_API_KEY Site speed reads yes yes yes brew/deploy.sh:42 brew/tools/prod-env.sh:10
RESEND_API_KEY Team emails yes yes brew/deploy.sh:42 brew/tools/prod-env.sh:10
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET Google sign-in yes yes yes brew/deploy.sh:42 brew/tools/prod-env.sh:10
GOOGLE_ADS_OAUTH_CLIENT_ID, _SECRET, GOOGLE_ADS_DEVELOPER_TOKEN Google Ads and GA4 connect yes yes brew/deploy.sh:42 brew/tools/prod-env.sh:10
META_ADS_OAUTH_CLIENT_ID, _SECRET Meta connect yes yes brew/deploy.sh:42 brew/tools/prod-env.sh:10
FEED_SHOPIFY_CLIENT_ID, _SECRET Shopify app yes yes brew/deploy.sh:42 brew/tools/prod-env.sh:10
SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_SIGNING_SECRET Slack app yes yes brew/deploy.sh:42 brew/tools/prod-env.sh:10
APIFY_TOKEN Apify, added only if the secret exists yes yes brew/deploy.sh:38 brew/tools/prod-env.sh:11
HF_API_KEY_ID, HF_API_KEY_SECRET Higgsfield, added only if both exist yes yes brew/deploy.sh:37 brew/tools/prod-env.sh:12
CLOUDFLARE_DNS_ONBOARDING_TOKEN Cloudflare DNS changes by script ops only memory brew-demo-mode

Ops jobs

  • brew-partner-copy runs as its own service account and reads both vault keys and both database URLs; it refuses to run if the two vault keys are equal. brew/tools/partner-copy.sh:13-16 brew/db/migrate-to-prod.mjs:19-20
  • brew-prod-migrate and brew-prod-clean read only BREW_PROD_DATABASE_URL. brew/tools/promote.sh:65-66 brew/tools/promote.sh:85-86
  • brew-backup has a custom export-only role. memory brew-prod-backend

Known key history

When What Source
20 Oct New Anthropic key, ANTHROPIC_API_KEY version 3; the key ran out of credit the same morning memory brew-pro-switch-and-flush
about 23 Oct Old treg key died with 401s; TREG_TOKEN version 2 is an agent key brew-live on team squareup, capped at 20k calls a day; version 1 disabled memory brew-apify-watch
23 Oct APIFY_TOKEN added with the Apify Starter plan memory brew-apify-watch
25 Oct Production got its own session, vault and hand-off secrets memory brew-prod-backend

The handbook's own

  • Deploys of the docs and the handbook use the person's own gcloud sign-in token, nothing stored. brand-directions/_tools/deploy-handbook.py:61-62
  • The comments service checks Cloudflare Access's signed identity; it needs CF_TEAM_DOMAIN, CF_ACCESS_AUD and ALLOWED_EMAILS, which are settings, not secrets. It reads Firestore as its runtime account. handbook/comments/server.mjs:1-15
  • Claude's side reads the same Firestore through Google sign-in on the Mac. handbook/tools/comments.mjs:1-13

A known gap

The default compute service account has project-wide secret access and Cloud Run admin, which reaches about 15 other services. Raised as open for Param on 25 Oct. memory brew-prod-backend

Tomorrow

Item Version Tag
Secret Manager, names only in code, production's own copies of the four v1 Stays
One named owner and a rotation date per secret v1 New
Separate production keys for Anthropic, OpenAI and treg, so spend and leaks are told apart v2 New
Default compute account stripped of project-wide secret access v1 Changes

How we do it

  1. Never paste a secret value in chat, code, the handbook or a log. Refer to it by name.
  2. Add a new key:
printf '%s' "<value>" | gcloud secrets create <NAME> --data-file=- --project=squareup-492617
  1. Rotate a key: add a version, deploy staging, promote production, then disable the old version.
printf '%s' "<new value>" | gcloud secrets versions add <NAME> --data-file=- --project=squareup-492617
gcloud secrets versions disable <old version> --secret=<NAME> --project=squareup-492617
  1. Never rotate FEED_VAULT_KEY or FEED_VAULT_KEY_PROD by adding a version alone: every stored brand sign-in is encrypted with it. A vault key change needs a re-encrypt job like the partner copy, and Arata's sign-ins must survive it. brew/db/migrate-to-prod.mjs:19-20 brew/connectors/protected.js:1-4
  2. Rotating FEED_SESSION_SECRET_PROD signs everyone out of production; tell the partners first.

Numbers

Metric Definition Where measured
Secrets without an owner Names in the table with no owner set This page, Proposed; all of them today
Oldest secret version in use Days since the serving version was made gcloud secrets versions list, Proposed

Open

  1. Who rotates which secret? No source names anyone. Proposal: Param owns every secret for now; Claude may add versions only when Param asks in chat; a review every quarter starting January 2027.
  2. Should production have its own AI and data keys? Today staging, production and the demo share ANTHROPIC_API_KEY, OPENAI_API_KEY and TREG_TOKEN. Proposal: yes for treg and Anthropic before the first paying partner, so a staging leak or runaway test cannot spend production's balance.
  3. Fix the default compute account's access? Proposal: yes, remove project-wide secret accessor and give each of the 15 services its own account, in one planned session.
Sources
  • brew origin/main a559de6: deploy.sh:37-42, tools/prod-env.sh:5-12, tools/deploy-demo.sh:15-17, tools/partner-copy.sh:13-16, tools/promote.sh:65-66, db/migrate-to-prod.mjs:19-20, connectors/protected.js:1-4, README.md 'Run it locally'
  • handbook/comments/server.mjs:1-15, handbook/tools/comments.mjs:1-13, brand-directions/_tools/deploy-handbook.py:61-62
  • memory brew-prod-backend, brew-apify-watch, brew-pro-switch-and-flush, brew-real-connectors, brew-demo-mode, brew-storage, brand-directions-2026-10-25

Source file content/ops-and-infrastructure/secrets-and-keys.md · For agents: Markdown · JSON