Production N.E.S.T.

nest.thephenom.app is the production N.E.S.T. surface, served by the phenom-backend-prod Cloudflare Pages project + the nest-api-prod Cloudflare Worker, built against the real production infrastructure in phenom-infra/environments/production/ (api.thephenom.app + phenom-prod Cognito pool + phenom-prod-media-storage).

TL;DR. Two N.E.S.T. surfaces today — nest.thephenom.app (production, this page) and nest-firebase.thephenom.app (frozen pre-migration snapshot, see firebase-main snapshot). The nest-prod.thephenom.app parallel URL was retired on 2026-05-16 when its backing stack was merged into nest.thephenom.app. dev-nest.thephenom.app exists for the develop branch; see the dev-nest section in Infrastructure.

Topology

graph LR
    Branch(["github.com/Phenom-earth/phenom-backend<br/>branch: main"])
    Pages(["CF Pages<br/>phenom-backend-prod"])
    Worker(["CF Worker<br/>nest-api-prod"])
    Domain(["nest.thephenom.app"])
    Cognito(["AWS Cognito<br/>phenom-prod<br/>us-east-1_knEL7cqS3"])
    Hasura(["Hasura GraphQL<br/>api.thephenom.app"])
    Bucket(["S3<br/>phenom-prod-media-storage"])
    Lambdas(["Cognito triggers<br/>phenom-prod-hasura-cognito-trigger<br/>phenom-prod-hasura-cognito-sync-users"])

    Branch -->|"push triggers layer-3 jobs"| Pages
    Branch -->|"push triggers layer-3 jobs"| Worker
    Domain -->|"DNS proxied CNAME → phenom-backend-prod.pages.dev"| Pages
    Domain -->|"routes /api/* + /health"| Worker
    Worker -->|"Bearer JWT (verified against Cognito JWKS)"| Hasura
    Worker -->|"AWS SigV4 presigned GETs"| Bucket
    Worker -.->|"JWT issuer validation"| Cognito
    Cognito -->|"pre-token / post-auth / post-confirm"| Lambdas
    Lambdas -->|"GraphQL mutations via aws_lb.main"| Hasura

Resource map

SurfaceResourceNotes
Branchmain on Phenom-earth/phenom-backendPushes trigger layer-3-deploy-worker-prod + layer-3-deploy-pages-prod in .github/workflows/nest-ci.yml.
Pages projectphenom-backend-prod (production_branch = main)Custom domain: nest.thephenom.app. Subdomain: phenom-backend-prod.pages.dev.
Worker scriptnest-api-prodDeployed via wrangler deploy --env production from admin_sandbox/nest-api/wrangler.toml’s [env.production] block.
Worker routesnest.thephenom.app/api/*, nest.thephenom.app/healthnest-api-prodBound via Cloudflare API.
DNSCNAME nest.thephenom.appphenom-backend-prod.pages.dev, proxiedManaged via Cloudflare API (NOT in TF yet — follow-up).
CF AccessnoneThe React app’s own Cognito sign-in is the sole auth gate. No Cloudflare Access app in front.
Cognito poolus-east-1_knEL7cqS3 (phenom-prod), client 8uun49ru7f3fdvmlc12vqig3aManaged in phenom-infra/environments/production/cognito.tf.
Hasuraapi.thephenom.app/v1/graphql (ALB → phenom-prod-graphql ECS service)Managed in phenom-infra/environments/production/.
Cognito triggersphenom-prod-hasura-cognito-trigger (JWT-claim shaper) + phenom-prod-hasura-cognito-sync-users (writes user rows to prod Hasura via aws_lb.main)Managed in phenom-infra/environments/production/cognito.tf.
Media bucketphenom-prod-media-storageWorker signs presigned GETs against this bucket. nest-api-prod’s IAM principal is phenom-nest-api-prod-s3 (managed in phenom-infra/environments/production/nest-api-media.tf).
EmailCognito → SES via noreply@thephenom.appSet in phenom-infra/environments/production/cognito.tf. See Cognito Email via SES.

CI deploy jobs

Pushes to main trigger two parallel jobs after the Layer 1 + 2 gates pass:

  • layer-3-deploy-worker-prodwrangler deploy --env production from admin_sandbox/nest-api/. Re-deploys the nest-api-prod Worker.
  • layer-3-deploy-pages-prodvite build from admin_sandbox/phenom/ with hardcoded prod env vars (VITE_HASURA_GRAPHQL_URL=https://api.thephenom.app/v1/graphql, VITE_COGNITO_USER_POOL_ID=us-east-1_knEL7cqS3, VITE_COGNITO_CLIENT_ID=8uun49ru7f3fdvmlc12vqig3a, VITE_CHAT_SERVER_URL=https://chat.thephenom.app), then wrangler pages deploy dist --project-name=phenom-backend-prod --branch=main.

Both jobs gate on github.ref == 'refs/heads/main'.

Why prod Cognito IDs are hardcoded in CI, not GH secrets

The Cognito pool ID and public client ID are not secrets — they’re addressable identifiers that end up in the bundled JS regardless. Hardcoding them in the workflow makes the configuration self-documenting and avoids the “wait, which env’s secret is this set to?” confusion the staging path has historically had. If you ever want to flip the pool, change the literal in the workflow + open a PR.

Worker secrets (write-only, not in TF)

nest-api-prod’s four write-only Worker secrets:

SecretSource
AWS_ACCESS_KEY_IDAccess key on aws_iam_user.nest_api_prod_s3 (managed in phenom-infra/environments/production/nest-api-media.tf) — scoped to phenom-prod-media-storage only.
AWS_SECRET_ACCESS_KEY(same)
CF_ACCESS_DOCS_CLIENT_IDCF Access service token “nest-prod docs proxy” — authorized on the int-docs Access policy.
CF_ACCESS_DOCS_CLIENT_SECRET(same)

To rotate: regenerate the underlying credential (IAM access key or CF Access service token), then bunx wrangler secret put <NAME> --name nest-api-prod from admin_sandbox/nest-api/.

Cognito Lambda triggers — where they write

phenom-prod-hasura-cognito-sync-users runs on post_authentication and post_confirmation. Its env: GRAPHQL_DOMAIN = aws_lb.main.dns_name (the prod ALB internal DNS, routing inside the VPC straight to the phenom-prod-graphql ECS service) + SECRETS_ARN = aws_secretsmanager_secret.app_secrets.arn (the prod admin-secret bag). So writes from the Lambda land in phenom-prod-postgres via the prod Hasura instance — not in staging.

phenom-prod-hasura-cognito-trigger runs on pre_token_generation and is purely a JWT-claim shaper — no env, no Hasura call.

Both Lambdas are wired only to aws_cognito_user_pool.main (the prod pool) via aws_lambda_permission — the staging pool’s Lambdas live in a separate environment and cannot invoke these.

Retired surfaces (historical)

  • nest-prod.thephenom.app (2026-05-14 → 2026-05-16): parallel-deploy URL used to verify the prod-infra build before pointing user traffic at it. Custom domain, DNS, Worker routes, and CF Access app deleted on 2026-05-16. The backing Pages project (phenom-backend-prod) and Worker (nest-api-prod) were kept and re-pointed at nest.thephenom.app.
  • The legacy phenom-backend Pages project (staging-backed) lost its custom domain on 2026-05-16. The project itself is orphaned and pending deletion — blocked on a deployments-cleanup pass.