How we keep brand data safe today, sign in, sessions, secrets, least access, the network edge and the audits already done, and what is still open before brands beyond the five partners arrive.
Today
Sign in and sessions
Google sign in with PKCE and a nonce; the id token's audience, issuer, expiry, nonce and verified email are checked. brew/auth.js:4-5, 221-239
Sessions are an HMAC signed cookie, HttpOnly, SameSite Lax, Secure on Cloud Run, for 30 days. brew/auth.js:54-64, 66, 245
Every token carries its type, so a connect state never works as a session. brew/auth.js:56-64
Each sign in has a session row; sign out ends it on every instance, and a root admin can end every session of one email. brew/sessions.js:1-4, 27-32
Redirects after sign in go only to our own paths. brew/auth.js:73
Local test sign in exists only off Cloud Run. brew/auth.js:250
Secrets
All keys come from Secret Manager; production uses its own *_PROD secrets for the session, vault and share links. brew/tools/prod-env.sh:10brew/docs/production.md:14
Connected account keys are AES-256-GCM in the vault, never sent to a page and never logged. brew/vault.js:1-3
On production, share links are signed with HANDOFF_SECRET only, never the AI key. brew/handoff.js:18handbook content/engineering/changelog.md:895
Without a session secret on Cloud Run, sign in turns itself off instead of using a default. brew/auth.js:46-47
Least access
Production runs as brew-prod-runtime@; staging's account cannot read production's secrets or data. brew/tools/prod-env.sh:4-6memory brew-prod-backend
Web ingress is the load balancer only; the worker is internal; the database has a private IP and deletion protection. brew/docs/production.md:9-15memory brew-prod-backend
Cloud Armor limits 3000 and 6000 requests a minute; load balancer logging is on; the CDN is off. memory brew-prod-backend
Every answer not marked public is private, so a cache never serves one viewer's data to another. handbook content/engineering/changelog.md:97
The only root admin on production is param@joinsquareup.com; every address at the two staff domains, joinsquareup.com and shopos.ai, gets full staff access. brew/tools/prod-env.sh:9
Outside input
Every fetch of a pasted address resolves DNS and refuses private, loopback and metadata ranges on each redirect. brew/reader/util.js:10-40
Headless Chromium runs with --no-sandbox in the reader and the PDF renderer. brew/reader/stages/site.js:103brew/pdf/render.js:26
The demo site wraps fetch so it can never call a sending host or write to an account. brew/docs/handover/engineering/09-security-and-access.md:48
Release and recovery
Only tools/promote.sh releases production: backup, migrations, a clean gate that refuses test data, dummies and wrong settings, then traffic, with rollback on failure. brew/docs/production.md:25-33memory prod-clean-only
Point in time recovery for 7 days, 14 daily backups, a nightly export to a separate bucket; a restore test passed on 25 Oct. brew/docs/production.md:35-45
Alerts email param@ and hello@: server errors, slow requests, worker lease lost, release failed, disk, connections, CPU, export failed, site down. brew/docs/production.md:112-115
Since 26 Oct GitHub Actions runs the whole test suite on every push and pull request, with every AI call mocked, so no key is needed in CI. brew/.github/workflows/test.yml:1-16
A data request deletion is a dry run first, refuses partners and protected brands, never revokes a platform grant and leaves one audit row. brew/db/brand-wipe.js:7-9, 24-32, 187-195
Audits done
Date
Audit
Outcome
Source
15 Oct
Handover audit E: accounts, demo, security; 11 findings S-1 to S-11 and 5 fix-now items N-1 to N-5
Shared links, team notes, mic, facts, PDF path, service account and share secret fixed the same day
Onboarding stream and Play titles made team only; notify takes only your own address; photo fetches through the guard; Slack actions and GA4 by property flagged
memory brew-audit-2026-10-15
20 Oct
QA audit, 4 auditors, about 86 findings
P0s and most P1s live
memory brew-qa-audit-2026-10-20
25 Oct
Three independent reviews on storage, roles and security, about 45 findings, then QV rounds 2 and 3
All fixed with tests; sign out ends sessions; Arata guard no longer bypassable
memory brew-prod-backend
Still open
The default compute service account keeps project wide secret access and run.admin, which affects about 15 other services. memory brew-prod-backend
STAFF_REQUIRE_HD is not set on production. brew/auth.js:240-242brew/tools/prod-env.sh:9
Claiming by email domain is weak proof of ownership. brew/docs/handover/engineering/09-security-and-access.md:40
Chromium without its sandbox. brew/reader/stages/site.js:103
Tomorrow
Item
Version
Tag
Strip the default compute account to what Cloud Run needs
v1
New, proposed
STAFF_REQUIRE_HD=1 and named staff on production
v1
New, proposed
A security review before every new power, v2 to v4
v1
New, proposed
Store admin or DNS proof for claims
v2
New
Write scopes held in their own table, used only after a person's yes
v2
New
Chromium in its own sandboxed job
v2
New, proposed
Ad rules with spend limits checked on the server
v4
New
How we do it
Never print, log or send a key to a page. New keys go to Secret Manager and tools/prod-env.sh.
Every new route goes through sign in, genOk and the closed gate; test the outsider paths.
Run the security and access tests before every deploy:
cd /Users/param/Documents/ChatGPT/brew && AI_MOCK=1 node --test test/security-fixes.test.js test/access.test.js test/qv1-roles-security.test.js test/qa-final-access.test.js
Release only with tools/promote.sh; never edit it while it runs.
Run the restore test monthly, as written in docs/production.md.
After any leak of a key in chat or a file, rotate it the same day.
Numbers
Metric
Definition
Where measured
Status
Open security findings
Items in Still open
This page
4 on 26 Oct
Days since restore test
Days since the last passed restore test
docs/production.md
1 on 26 Oct
Clean gate refusals
Promotes the gate stopped
brew-prod-clean job logs
Proposed
Open
Fix the default compute account now? It touches other company services. Proposal: yes, this week, one service at a time, each moved to its own account first.
An outside penetration test? Proposal: one after v1 ships to the first 20 brands, not before.
Rotate the keys pasted in chat on 28 Sep? The SquareUp tracking keys share this Google project. Proposal: rotate them now. memory squareup-tracking-structure