Secrets & configuration
There are three secret stores in play:
- Cloudflare Workers secret store — runtime API secrets.
- GitHub Actions secrets — CI/CD.
- Local dev files —
apps/api/.dev.vars,apps/mobile/.env.local.
Cross-reference: architecture/auth § Secrets summary is the canonical list of what each secret is for.
Cloudflare Workers (API runtime)
Set with wrangler secret put <NAME>:
| Name | Purpose |
|---|---|
SESSION_SECRET | HS256 key for signing our own session JWTs. 32+ bytes of randomness (openssl rand -hex 32). |
PII_ENCRYPTION_KEY | Key used with pgcrypto to encrypt sensitive columns at rest. 32+ bytes. See privacy. |
INFINIA_API_USERNAME | HTTP Basic username for Infinia API |
INFINIA_API_PASSWORD | HTTP Basic password for Infinia API |
INFINIA_WEBHOOK_SECRET | Path secret matched against POST /webhooks/infinia/:secret. Also registered as the callback_url on every Infinia resource we create. |
NEON_DB_URL | Postgres URL. Production Neon branch (pinned to an EU region) |
Public vars (wrangler.toml [vars] — not secrets)
| Name | Purpose |
|---|---|
APPLE_BUNDLE_ID | Expected aud claim on Apple id_tokens. Must match the iOS app's bundle id. |
GOOGLE_OAUTH_CLIENT_ID | Expected aud claim on Google id_tokens. |
INFINIA_COMPANY_ID | Our tenant id at Infinia, sent as x-company-id header on every request. |
INFINIA_BASE_URL | https://app2test.infiniaweb.com/infinia_api in sandbox; production URL when Infinia issues one. |
Generating strong secrets
# 32-byte random hex (SESSION_SECRET, PII_ENCRYPTION_KEY, INFINIA_WEBHOOK_SECRET)
openssl rand -hex 32
GitHub Actions secrets
See deployment § Required GitHub secrets for the full list.
Local development
apps/api/.dev.vars (git-ignored — .env* in root .gitignore):
SESSION_SECRET=<openssl rand -hex 32>
PII_ENCRYPTION_KEY=<openssl rand -hex 32>
INFINIA_API_USERNAME=<sandbox username>
INFINIA_API_PASSWORD=<sandbox password>
INFINIA_WEBHOOK_SECRET=<openssl rand -hex 32>
INFINIA_COMPANY_ID=<sandbox company id>
INFINIA_BASE_URL=https://app2test.infiniaweb.com/infinia_api
APPLE_BUNDLE_ID=com.gringopay.dev
GOOGLE_OAUTH_CLIENT_ID=<dev OAuth client id>
NEON_DB_URL=postgresql://.../neondb?sslmode=require
Wrangler picks .dev.vars up automatically for wrangler dev. Tests use a
local Postgres (cd apps/api && docker compose up -d --wait), not .dev.vars —
Vitest defaults TEST_DATABASE_URL to the container; set it only to override.
apps/mobile/.env.local (git-ignored):
EXPO_PUBLIC_API_BASE_URL=http://localhost:8787
EXPO_PUBLIC_APPLE_BUNDLE_ID=com.gringopay.dev
EXPO_PUBLIC_GOOGLE_OAUTH_CLIENT_ID=<dev OAuth client id>
Only EXPO_PUBLIC_* vars are readable at runtime in the RN bundle.
Rotation
SESSION_SECRET — rotating logs everyone out (their existing JWTs stop verifying). Do this only when required (suspected compromise). Steps:
openssl rand -hex 32→ new valuewrangler secret put SESSION_SECRET- Users re-authenticate on next request
PII_ENCRYPTION_KEY — rotating is a data-migration event because existing encrypted columns are keyed to the old value. Do not rotate casually. Proper rotation involves:
- Introduce
PII_ENCRYPTION_KEY_NEXTas a second secret - Ship code that writes with
KEY_NEXTand reads with either key - Backfill: re-encrypt every existing encrypted column with
KEY_NEXT - Once complete, promote
KEY_NEXT→SESSION_SECRET; drop the old key - Ship code that only knows
SESSION_SECRET
Only rotate on suspected compromise. Otherwise treat as static.
INFINIA_* secrets — rotate in the Infinia dashboard first, then wrangler secret put. For INFINIA_WEBHOOK_SECRET you must also update the callback_url on every active Infinia resource (owners, accounts, payouts, transfers) to include the new value — otherwise callbacks 401 at the path check.
NEON_DB_URL — rotate the DB user password in Neon, generate a new connection string, wrangler secret put.
Secret changes are picked up on the next request — no redeploy required.
What's not a secret (deliberately)
- Apple / Google OAuth client IDs — public identifiers. Live in
[vars]. - Infinia company id — public identifier for our tenant. Live in
[vars]. - Infinia base URL — public. Live in
[vars].
Public vars are safe in wrangler.toml and in the mobile bundle. Do not put anything with _SECRET / _PASSWORD / _KEY / _TOKEN in [vars] — those are secrets by definition.