Secrets & Configuration
Where every credential, secret, and config value lives across the platform — and how to rotate each one.
This page inventories every secret store so the repo is self-contained: a new operator can find (and rotate) any credential without tribal knowledge. No live secret values are copied here — instead each entry says exactly where the value lives and how it's generated/rotated.
A live credential is committed in this repo
.claude/settings.local.json contains a real sandbox Postgres password in plaintext (inside pre-approved PGPASSWORD=… psql -h 94.130.137.222 … allow-list entries), and frontend/grasshopper/wrangler.jsonc commits several sandbox worker secrets under env.sandbox.vars. These are documented here deliberately (so the repo is self-contained), but they should be rotated and future secrets should never be committed. See rotation.
The secret stores at a glance
1. Backend env files (/etc/octopus{,-sandbox}/octopus.env)
Generated once by deploy/setup-octopus-service.sh (prod) / setup-octopus-sandbox-service.sh (sandbox), which mint random secrets with openssl. File perms 640, dir root:octopus 750. update-octopus.sh appends any missing keys ("drift-fix") but never overwrites existing values.
| Key | Purpose | Source / rotation |
|---|---|---|
JWT_SECRET | Signs API + portal + G2A JWTs | openssl rand -base64 64 at setup. Rotating invalidates all live tokens. |
APP_KEY | AES key for encrypting Shopify tokens & voucher data | openssl rand -base64 32. Rotating breaks decryption of existing encrypted DB values — re-encrypt first. |
PG_PASS | Postgres password (octopus user) | openssl rand -base64 24 at setup; must match the local_postgres container. |
VALKEY_PASS | Valkey auth (often empty locally) | set at setup if used. |
OTEL_EXPORTER_OTLP_TOKEN | signoz-access-token for telemetry ingest | from SigNoz; paste into env. |
SENDGRID_API_KEY / SMTP_* | Email delivery | added manually to the env file (not written by setup); from SendGrid. |
WEBAUTHN_RP_ID / _ORIGINS | Passkey relying-party config | domain-derived; sandbox uses octopuscards.io. |
Other (non-secret) keys in the same file: APP_ENV, PORT (:8081/:8082), PG_HOST/PORT/USER/DB/SSLMODE, CACHE_TYPE, CACHE_PREFIX (sandbox:), VALKEY_ADDR, QUEUE_* (sandbox: app.sandbox), FEATURE_*_ENABLED, OTEL_ENABLED, OTEL_EXPORTER_OTLP_ENDPOINT, INSECURE_MODE, COOKIE_DOMAIN, CLIENT_PORTAL_ORIGINS.
2. Client-portal env files (/etc/octopus{,-sandbox}/octopus-client.env)
All keys are NEXT_PUBLIC_* and inlined at build time (changing one needs a rebuild via update-octopus-client.sh): NEXT_PUBLIC_API_URL, NEXT_PUBLIC_APP_NAME, NEXT_PUBLIC_BRAND (default/octopus-cards), NEXT_PUBLIC_ASSET_BASE_URL, NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN, NEXT_PUBLIC_OTEL_*, NEXT_PUBLIC_DEBUG_LOGS. The only real secret here is the Mapbox token (public-scoped, but rotate via Mapbox). The docs site has an analogous /etc/octopus/octopus-docs.env with APP_*/FEATURE_* build vars.
3. Docker data-plane credentials (root docker-compose.yaml)
- Postgres:
POSTGRES_USER=octopus,POSTGRES_PASSWORD=${PG_PASS:-octopus},REPLICATION_PASSWORD=${REPLICATION_PASSWORD:-replicator}. - RabbitMQ:
octopus/octopus(mgmt UI on:15672). - MySQL: root
rootpassword, db/user/passoctopus.
These default to weak values (octopus/octopus, rootpassword) and the ports are published on the public interface — security relies entirely on the host firewall. In prod, PG_PASS/REPLICATION_PASSWORD come from the env; set strong values and lock the firewall.
4. Vendor credentials — vendor_attributes table
All upstream vendor API credentials live in the database, not env. Rows are (vendor_id, key, value); factories map host/username/password/api_key/callback_url and pass everything else through Extra/Credentials. To rotate a vendor key, update the vendor_attributes row (via the admin UI / a migration — never an ad-hoc SQL write on prod). See Vendors → credentials.
Channel-specific credential stores:
- Shopify —
clients.ShopifyAccessToken, AES-encrypted withAPP_KEY, decrypted per-tenant at client build. - G2A —
g2a_credentialstable: bcrypt-hashed client secret + webhook secret, per client.
5. Cloudflare secrets
- grasshopper (prod) — real Wrangler secrets (
wrangler secret put):OCTOPUS_API_URL,OCTOPUS_CLIENT_USERNAME,OCTOPUS_CLIENT_PASSWORD,ENCRYPTION_KEY(AES-256-GCM PII field encryption — the worker crashes without it),ADMIN_TOKEN,CLAIM_NONCE_SECRET,TURNSTILE_SECRET_KEY,OTEL_INGEST_TOKEN. - grasshopper (sandbox) — the same names are committed as plaintext in
wrangler.jsoncenv.sandbox.vars. Rotate these. - KV
octopus_token— runtime cache of the Octopus JWT (not a managed secret; self-heals on expiry). - Cloudflare API token — used by CI + local
wrangler; issue a scoped token from the CF dashboard. - Local dev —
frontend/grasshopper/.dev.vars(gitignored; template.dev.vars.example).
6. GitHub Actions secrets
CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID (used by every deploy-*.yml). Rotate the token in the CF dashboard and update the repo secret. The backend test workflow uses only static, non-sensitive test values.
7. AI-agent config — .claude/settings.local.json
A Claude Code permission allow-list. Contains a leaked sandbox Postgres password (see the top-of-page warning). It should list command shapes, never literal secrets.
Rotation checklist
Rotating APP_KEY or JWT_SECRET has blast radius — plan it.
- Sandbox Postgres password (leaked in
.claude/settings.local.json): change theoctopusDB password on94.130.137.222, update/etc/octopus-sandbox/octopus.env+ the container, and scrub thePGPASSWORD=entries from the settings file. Because it's in git history, treat it as compromised regardless. - grasshopper sandbox secrets (in
wrangler.jsonc): move to realwrangler secret putvalues, delete the committed ones. JWT_SECRET: rotating logs everyone out (all tokens invalid). Do it during a maintenance window.APP_KEY: do not rotate blindly — it decrypts Shopify tokens & voucher data. Re-encrypt existing values with the new key first, or you'll lose access to them.- Vendor keys: update the
vendor_attributesrow via admin UI / migration; no redeploy needed. - Cloudflare API token: rotate in dashboard → update GitHub secret + local wrangler.
Cloudflare Setup
Live inventory of the Cloudflare account — DNS, Pages, Workers, KV, D1 — reconciled against wrangler configs and deploy scripts.
Environment Variables
Every environment variable the backend reads — what it controls, its default/fallback, where it's read, and PROD vs SANDBOX differences. Plus the insecure fallbacks, drift risks, and dead vars.