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

Surface Resource Notes
Branch main on Phenom-earth/phenom-backend Pushes trigger layer-3-deploy-worker-prod + layer-3-deploy-pages-prod in .github/workflows/nest-ci.yml.
Pages project phenom-backend-prod (production_branch = main) Custom domain: nest.thephenom.app. Subdomain: phenom-backend-prod.pages.dev.
Worker script nest-api-prod Deployed via wrangler deploy --env production from admin_sandbox/nest-api/wrangler.toml’s [env.production] block.
Worker routes nest.thephenom.app/api/*, nest.thephenom.app/healthnest-api-prod Bound via Cloudflare API.
DNS CNAME nest.thephenom.appphenom-backend-prod.pages.dev, proxied Managed via Cloudflare API. Not yet in Terraform.
CF Access none The React app’s own Cognito sign-in is the sole auth gate. No Cloudflare Access app in front.
Cognito pool us-east-1_knEL7cqS3 (phenom-prod), client 8uun49ru7f3fdvmlc12vqig3a Managed in phenom-infra/environments/production/cognito.tf.
Hasura api.thephenom.app/v1/graphql (ALB → phenom-prod-graphql ECS service) Managed in phenom-infra/environments/production/.
Cognito triggers phenom-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 bucket phenom-prod-media-storage Worker 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).
Email Cognito → SES via noreply@thephenom.app Set 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:

Secret Source
AWS_ACCESS_KEY_ID Access 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_ID CF 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

  • nest-prod.thephenom.app: Parallel-deploy URL formerly used to verify the prod-infra build. Custom domain, DNS, Worker routes, and CF Access app have been deleted. The backing Pages project (phenom-backend-prod) and Worker (nest-api-prod) were re-pointed at nest.thephenom.app.
  • The legacy phenom-backend Pages project (staging-backed) lost its custom domain. The project is orphaned and pending deletion.