Skip to Content
InfrastructureSecrets & Config

Secrets and Configuration

Configuration lives in three planes, split by where the code runs — because a secret should only ever exist in the runtime that consumes it: AWS-side secrets in SSM Parameter Store, Cloudflare-side secrets on the Workers themselves, and non-secret deploy-time choices in CDK context and environment variables. This page describes the mechanisms and paths only — never the values.

SSM Parameter Store (/opsdna/<env>/...)

All apps/api (Lambda) secrets and config live under one path convention:

/opsdna/<env>/<service>/<key> # env = dev | staging | prod

Two parameter types, matching what the value actually is:

  • SecureString — real secrets (API keys, webhook signing secrets), KMS-encrypted with the dedicated secrets CMK from SecurityStack.
  • String — non-secret config (client IDs, environment names).

They’re written by infra/scripts/put-secrets.sh, which is idempotent (--overwrite: same value = no-op, new value = version bump) and organized as per-service conditional blocks — it only writes the services whose env vars you set:

# Set just one provider's values in one invocation: PLAID_CLIENT_ID=... PLAID_SECRET=... bash infra/scripts/put-secrets.sh staging # Or source a gitignored env file: source .env.prod.local && bash infra/scripts/put-secrets.sh prod

The AppApiStack passes the env’s ssmPathPrefix (from infra/lib/env.ts) to the Lambdas, so the same code reads /opsdna/prod/... or /opsdna/staging/... purely by which stack set it’s deployed in.

Cloudflare Worker secrets

The portal is edge-stateless and bearer-only, so its session/auth secrets are Worker secrets, not SSM: WORKOS_API_KEY, OPSDNA_SESSION_COOKIE_PASSWORD, AUTHKIT_STATE_HMAC_KEY, IDENTITY_RESOLVE_SERVICE_TOKEN (and HONEYCOMB_API_KEY for tracing — see Observability). Set them out of band:

wrangler secret put WORKOS_API_KEY # prod worker wrangler secret put WORKOS_API_KEY --name opsdna-portal-staging # staging worker

IDENTITY_RESOLVE_SERVICE_TOKEN must match the corresponding API-side SSM value at /opsdna/<env>/identity-resolve/service-token — the portal’s auth callback forwards the WorkOS code to the API’s /internal/identity/resolve route authenticated with it.

Non-secret portal config (WorkOS client ID, redirect URI, API_BASE_URL, the CSRF allowed-origins list) lives in plain vars blocks in apps/portal/wrangler.jsonc, with a separate env.staging block for the staging worker.

Database role credentials

infra/scripts/bootstrap-app-role.sh sets the DB role passwords, grants, and rds_iam memberships (see Database operations for the role model). Like SSM secrets, this is run-once operational state per environment.

Some prod state is operational, not code. A cdk deploy reproduces infrastructure — it does not reproduce role passwords (bootstrap-app-role.sh), SSM parameters (put-secrets.sh), Worker secrets (wrangler secret put), or rds_iam role memberships. A fresh environment (the one-time staging bring-up, or a prod rebuild) must run those scripts explicitly; the runbook (docs/runbooks/prod-redeploy-and-db-rebuild.md §3/§7) has the ordered checklist. Forgetting this is the classic “deploy succeeded, nothing works” failure.

CDK context and env vars

Deploy-time configuration is deliberately tiny:

  • --context env=prod|staging — selects the stack set; no context means prod, preserving the exact historical synth (see Environments).
  • API_CUSTOM_DOMAIN / MCP_CUSTOM_DOMAIN — override the API custom domains. Prod defaults API_CUSTOM_DOMAIN on (api.opsdna.ai) so a bare deploy can’t tear the live domain down; staging defaults to none.
  • CI itself holds no AWS secret: GitHub Actions assumes the github-ci-opsdna role via OIDC. Only the Cloudflare account ID and API token are stored, as GitHub environment secrets (staging / production).

Never commit secrets, .env files, or paste secret values into docs, ADRs, PRs, or CI logs. This site documents paths and mechanisms only. The gates CI job includes data-safety scans, but they are a backstop, not permission.

See also Getting started for local .env conventions and /api for how the Lambdas consume this config at runtime.

Last updated on