Deployment and Infrastructure
Reference for c2fit frontend deployment and backend infrastructure.
AWS account: 564141170168 · Region: ap-southeast-1 — same account/region digital-thai-moca's braindi-backend infrastructure runs in.
Repositories & Deploy Targets
| Repository | Deploy target | Runtime |
|---|---|---|
| c2fit-app-frontend ↗ | Dev/study: Firebase App Distribution. Prod: App Store Connect and Google Play Console. | Flutter mobile app, 3 flavors |
| c2fit-bff ↗ | ECS (c2fit-backend cluster) | Go API/gateway — port 8080 in every environment, per task definition (APP_PORT/containerPort) |
c2fit-assessment-result ↗, cmd/assessment | ECS (c2fit-backend cluster) | Go API — port 8080 (not 8081 as config.yml's local default suggests; ECS overrides via APP_PORT env var) |
c2fit-assessment-result ↗, cmd/result | ECS (c2fit-backend cluster) | Go API — port 8080 (not 8082 as config.yml's local default suggests, same correction as above) |
| c2fit-tmt-generate-question ↗ | AWS Amplify (Open Amplify app d1bde3g4q8xsru ↗) | Trail Making Test question-generation tool. Open the deployed tool ↗. |
c2fit-assessment-dashboard is a local-only content-authoring tool with no CI or deploy target. Open the project overview → or open the repository map → for details.
Two more ECR repos/services exist in this AWS account under the c2fit- prefix with no matching source repo among the AIMET siblings checked — c2fit-lakehouse and c2fit-aiservice-aicontroller. Out of scope for this doc set; tracked in unknown.md for the data team to fill in.
Topology
Dev and prod+study are split into separate diagrams because prod and study share most of the same AWS resources (cluster, ALB) — folding all 3 environments into one diagram makes that sharing hard to read.
Dev
Prod + Study
Inter-service calls (BFF → Assessment/Result) go over AWS Cloud Map / ECS Service Connect private DNS (http://c2fit-result-dev, http://c2fit-assessment-prod, etc. — confirmed from task-definition env vars), not through the public ALB. Route53 has matching private-looking hosted zones (c2fit-backend-dev., c2fit-backend-prod., c2fit-backend-study., global-asr-service.) that back this Service Connect namespace.
Frontend deployment
The mobile app has separate dev, study, and production flavors. Release actions are manually started in GitHub Actions; a branch push does not publish a mobile build automatically.
CI/CD workflows
"Manual" means selecting the workflow in GitHub Actions and choosing Run workflow.
| Workflow | Trigger | What it does |
|---|---|---|
lint.yaml | Automatic on push or pull request to main | Checks the Flutter code. |
unit-test.yaml | Automatic on push or pull request to main | Runs the Flutter unit tests. |
create_tag.yml | Manual. Enter the new version. | Creates the release tag and GitHub release. Run this before study and production releases. |
deploy-firebase-app-distribution-dev.yml | Manual. Enter a commit SHA. | Builds dev iOS and Android apps and sends them to Firebase App Distribution. |
deploy-firebase-app-distribution-study.yml | Manual after creating the study tag. Enter that tag in the commit_sha field. | Builds study iOS and Android apps and sends them to Firebase App Distribution. |
upload-to-playstore.yaml | Manual. Enter the production tag and Play Store track. | Builds the production Android app and uploads it to Google Play Console. |
CI release settings are stored in GitHub Actions Variables. Credentials and signing material are stored in GitHub Actions Secrets. Local Xcode builds use the ignored local .env file, which must contain production values for a production archive.
Environment and destination reference
| Environment | Flutter flavor | App identifier | Firebase project | Current distribution route |
|---|---|---|---|---|
| Dev | releaseDev | tech.aimet.c2fit.dev | Open c2fit-app-dev in Firebase ↗ | Firebase App Distribution for iOS and Android |
| Study | releaseStudy | tech.aimet.c2fit.study | Open c2fit-app-study in Firebase ↗ | Firebase App Distribution for iOS and Android |
| Prod | releaseProd | tech.aimet.c2fit | c2fit-app | App Store Connect/TestFlight for iOS; Google Play Console for Android |
Dev release
- Choose the commit SHA to test.
- Manually run
deploy-firebase-app-distribution-dev.yml. - The reusable deployment workflow builds the dev iOS and Android apps and uploads both to the dev Firebase App Distribution project.
Study release
- Manually run
create_tag.ymlto create the study release tag. - Run
deploy-firebase-app-distribution-study.ymlwith that tag/ref. - CI builds the
releaseStudyiOS and Android apps and uploads both to the study Firebase App Distribution project.
Study versions use the x.y.z-study-N form in pubspec.yaml. Fastlane converts that value to x.y.z.N when packaging the apps.
Production release
Start both platform releases by manually running create_tag.yml for the production commit on main.
iOS
- On a developer machine, select the
releaseProdconfiguration and create the production archive in Xcode. - Upload the archive to App Store Connect from Xcode.
- Hand-test the uploaded build through TestFlight.
- If the build passes, submit it for App Store review.
Android
- Run
upload-to-playstore.yamlwith the production tag. - CI builds the signed
releaseProdAndroid App Bundle and uploads it to the Google Play Console track used for closed testing. The workflow currently exposesalpha,beta, andproductiontrack inputs; use the team's configured closed-testing track rather than uploading directly to production. - Hand-test the closed-testing build.
- If the build passes, promote it to the production release in Play Console.
Frontend services
| Service | Purpose |
|---|---|
| Firebase | Per-flavor project configuration, analytics, and App Distribution for dev and study. |
| App Store Connect / TestFlight | Production iOS testing, review, and release. |
| Google Play Console | Production Android closed testing and release promotion. |
| Sentry | Mobile error tracking. |
| Smartlook | Mobile session replay. |
Backend deployment
CI/CD
The backend repositories build container images in GitHub Actions and store them in Amazon ECR. Dev deployment is automated; production and study ECS rollout is manual.
| Repository/workflow | Trigger | Result |
|---|---|---|
c2fit-bff/development.yml | Push to dev | Runs tests, builds and pushes c2fit-bff:<commit-sha>, then updates ECS service c2fit-bff-dev. |
c2fit-bff/development.yml | Push to main | Runs tests and pushes the commit-SHA image to ECR. The deploy job does not run. |
c2fit-bff/acceptance_test.yml | Pull request to dev/main, or manual run | Runs unit and acceptance tests with WireMock, Redis, and MongoDB service containers; it does not deploy. |
c2fit-bff/tag-version.yml | Manual, with commit and version | Creates the Git tag and retags the existing ECR image with the requested version; it does not update ECS. |
c2fit-assessment-result/development.yml | Push to dev | Runs tests, builds and pushes separate Assessment and Result images, then updates both dev ECS services. |
c2fit-assessment-result/development.yml | Push to main | Runs tests and pushes both commit-SHA images to ECR. The deploy jobs do not run. |
c2fit-assessment-result/tag-version.yml | Manual, with service, commit, and version | Creates a service-scoped Git tag and retags the selected Assessment or Result ECR image; it does not update ECS. |
For production or study, the image must first exist in ECR under the intended version tag. A maintainer then updates the matching ECS service manually in the AWS console. No c2fit CodePipeline or CodeDeploy pipeline performs this rollout. Read-only AWS checks found no CodePipeline pipelines; the available CodeBuild projects are generic GitHub Actions runners or unrelated projects, and the CodeDeploy applications are test/POC resources.
Deploy IAM roles:
arn:aws:iam::564141170168:role/c2fit-backend-actions-runnerforc2fit-bffarn:aws:iam::564141170168:role/c2fit-assessment-result-actions-runnerforc2fit-assessment-result
c2fit-assessment-dashboard has no .github/workflows/ directory and no deployment target.
DNS
Confirmed via route53 list-hosted-zones / list-resource-record-sets on the c2fit.aimet.tech public zone:
| Hostname | Points to | Purpose |
|---|---|---|
c2fit.aimet.tech | CloudFront d2g98tuobnmnvd.cloudfront.net → S3 c2fit-app-public-asset | Intermediate web page that redirects to the AIMET C2Fit page ↗, mainly to support iOS/Android universal links; the same bucket also serves the Cognito OAuth2 redirect landing page (middleware-web.html) |
api.c2fit.aimet.tech | ALB c2fit-backend-alb (dualstack) | Prod API — default rule on the HTTPS listener |
api.study.c2fit.aimet.tech | Same ALB, host-header routed | Study API |
api.dev.c2fit.aimet.tech | ALB dev-alb | Dev API |
auth.c2fit.aimet.tech / dev.auth.c2fit.aimet.tech | Cognito Hosted UI custom domains (prod / dev respectively, Cognito serves these via its own CloudFront) | Login/OAuth2 hosted UI |
speech.dev.c2fit.aimet.tech, global-asr.c2fit.aimet.tech, google.c2fit.aimet.tech | dev-alb / CloudFront | Dev-only ASR-related endpoints — not individually traced back to a repo |
Open tmt-generate-question.c2fit.aimet.tech ↗ | AWS Amplify | Serves c2fit-tmt-generate-question ↗ (Trail Making Test question-generation tool), Amplify app d1bde3g4q8xsru — not a dangling record. |
SES DKIM records (*._domainkey.c2fit.aimet.tech) | dkim.amazonses.com | Confirms c2fit.aimet.tech is the SES-verified sending domain referenced in "Messaging" below |
ECS
All 9 services confirmed via ecs list-services/describe-services on cluster c2fit-backend (desiredCount=runningCount=1 for each):
| Cluster | Service | Environment | ALB / target group | Notes |
|---|---|---|---|---|
c2fit-backend | c2fit-bff-dev | Dev | dev-alb / c2fit-bff-dev | Default VPC (vpc-ae7685c8), public IP enabled |
c2fit-backend | c2fit-assessment-dev | Dev | dev-alb / c2fit-assessment-dev | Same VPC as above |
c2fit-backend | c2fit-result-dev | Dev | dev-alb / c2fit-result-dev | Same VPC as above |
c2fit-backend | c2fit-bff-prod | Prod | c2fit-backend-alb / c2fit-bff-service-prod | Shared prod VPC (vpc-00b2df00a723fbfe9), no public IP — not documented in any CI/CD workflow file read, confirmed only via AWS CLI |
c2fit-backend | c2fit-assessment-prod | Prod | c2fit-backend-alb / c2fit-assessment-service-prod | Same as above |
c2fit-backend | c2fit-result-prod | Prod | c2fit-backend-alb / c2fit-result-service-prod | Same as above |
c2fit-backend | c2fit-bff-study | Study | c2fit-backend-alb / c2fit-bff-service-study | Shared prod VPC, no public IP. It is a dedicated study service. Open the project overview →. |
c2fit-backend | c2fit-assessment-study | Study | c2fit-backend-alb / c2fit-assessment-service-study | Same as above |
c2fit-backend | c2fit-result-study | Study | c2fit-backend-alb / c2fit-result-service-study | Same as above |
For c2fit-bff and c2fit-assessment-result, a push to main builds the Docker image and pushes it to ECR only — rolling that image out to the running prod/study ECS service is a separate manual step in the AWS console, not an automated deploy. Only the dev branch has an automated build-and-deploy workflow.
ASR integration
c2fit-bff and Result Service both consume global-asr-service — the same shared ASR platform documented in digital-thai-moca's infrastructure doc, on the same AWS account. Confirmed real endpoints from task-definition env vars:
| Mode | Path | Use case |
|---|---|---|
| Real-time (streaming) | Mobile app --WSS--> c2fit-bff /ws/v1/speech --WS reverse proxy--> SPEECH_SERVICE_BASE_URL | Live captions during Digit Span Speak / Verbal Memory recording. Prod: ws://global-asr-api-server-prod.global-asr-service:8080 (cross-cluster Service Connect). Dev: ws://dev-alb-...elb.amazonaws.com:8080 (routed through the dev ALB, not Service Connect). |
| Async (submission-time) | Result Service → ASR (inline call during submission scoring) | Digit Span Speak / Verbal Memory scoring from submitted audio. Prod ASR_SERVICE_BASE_URL: http://global-asr-api-server-prod.global-asr-service:8080. Dev: routed through dev-alb on port 8080. |
No Kafka or other message broker exists in c2fit-assessment-result, unlike digital-thai-moca's Kafka-based async AI pipeline — the Result Service → ASR call is a direct, blocking call through global-asr-service's Go client, made inline inside the scoring function.
ECR
Confirmed via ecr describe-repositories:
| Repository | Image example |
|---|---|
c2fit-bff | 564141170168.dkr.ecr.ap-southeast-1.amazonaws.com/c2fit-bff:<tag> — dev tags are commit SHAs, prod tags are semver (v1.0.0-pre-release-2 seen on the currently-running prod task def) |
c2fit-assessment | 564141170168.dkr.ecr.ap-southeast-1.amazonaws.com/c2fit-assessment:<tag> — prod running v1.0.1 |
c2fit-result | 564141170168.dkr.ecr.ap-southeast-1.amazonaws.com/c2fit-result:<tag> — prod running v1.0.1 |
(c2fit-lakehouse and c2fit-aiservice-aicontroller repos also exist in this account — see the note under "Repositories & Deploy Targets" above; out of scope, no matching source repo found.)
Data stores
Confirmed via task-definition environment values (plaintext, non-secret) and elasticache/s3 CLI queries:
| Service | Resource | Notes |
|---|---|---|
| MongoDB Atlas | dmind-prescreening-back.1k8q9bs.mongodb.net, db c2fit-bff / c2fit-assessment-result | Dev + study environments — this cluster is shared with dmind's dev/prescreening environment (dmind-prescreening-back), not c2fit-dedicated. Auth via MONGODB-AWS (IAM-based), no embedded credentials in the connection string. |
| MongoDB Atlas | c2fit-backend-prod.wqbwgq.mongodb.net, db c2fit-bff / c2fit-assessment-result | Prod only — dedicated cluster, same MONGODB-AWS auth pattern. |
| MongoDB (collections) | db c2fit-bff | Collections: user_profile, delete_account_request, journey, streak, bundle, app_version. |
| MongoDB (collections) | db assessment-result (c2fit-assessment-result env var name) | 15 collections split between question_*/symbol_set_*/assessment_path (reference/content) and result_* (per-user results). |
| Redis | general-instance.dev.internal:6379 (REDIS_ENDPOINT, dev) | Shared dev-environment Redis instance (name suggests cross-product reuse, not c2fit-dedicated) — OTP sessions (TTL 5m), forgot-password sessions (TTL 10m), journey TTL 24h per config/bff/config.yml. |
| Redis | c2fit-bff-prod.3olnvy.clustercfg.apse1.cache.amazonaws.com:6379 (prod) | Dedicated ElastiCache Valkey replication group c2fit-bff-prod, cache.t4g.micro, confirmed via elasticache describe-replication-groups. Study's Redis endpoint was not individually re-verified. |
| S3 | c2fit-bff-dev / c2fit-bff-prod / c2fit-bff-study (AWS_S3_BUCKET_NAME for BFF, per-env) | BFF's own file storage — separate bucket per environment, not traced beyond bucket existence. |
| S3 | c2fit-result-service-filestore (dev) / c2fit-result-service-filestore-prod / c2fit-result-service-filestore-study (AWS_S3_BUCKET_NAME for Result Service, per-env) | Resolved by reading internal/pkg/engine/result_eng.go's StorePhaseFile: generic per-phase file store for all 6 assessments, hit via POST /result-service/api/v1/journey/result/phase-file. Holds audio (Digit Span Speak, Verbal Memory) and drawing/path-replay data (Drawing Memory) alike, keyed userId/journeyId/assessment/version/task/phase/filename via adaptor.ToS3Key. |
| S3 | c2fit-app-public-asset | Public static assets, served via CloudFront at c2fit.aimet.tech; also hosts the Cognito OAuth2 redirect landing page middleware-web.html. |
A handful of other S3 buckets exist under c2fit-adjacent names (c2fit-app-build-artifacts, aimet-c2fit-interview-record, c2fit-fine-tuning-audio-data, speech.dev.c2fit.aimet.tech) but don't trace to any of the 4 repos studied here — out of scope for this doc set, tracked in unknown.md for the data team to pick up.
Messaging
| Component | Purpose |
|---|---|
| AWS SES | Transactional email (NotificationRepository.SendEmail, direct SDK call — no queue). |
| AWS SNS | SMS (NotificationRepository.SendSMS, direct SDK call). |
WebSocket (/ws/v1/speech) | Real-time speech proxy to global-asr-service, not a message queue. |
No Kafka/SQS/SNS-topic-based async pipeline was found in c2fit-bff or c2fit-assessment-result — contrast with digital-thai-moca's Kafka input.asr/output.ai.braindi topics.
Secrets Manager
Confirmed via secretsmanager list-secrets (names/ARNs only — this profile's IAM policy explicitly denies GetSecretValue, so no secret contents were read):
| Secret name | Used by |
|---|---|
c2fit-bff-dev, c2fit-bff-prod, c2fit-bff-study | One secret per environment, referenced by the matching ECS task definition's secrets block (e.g. prod's APP_SECRET_KEY and AWS_COGNITO_CLIENT_SECRET resolve from c2fit-bff-prod). Interesting: dev's google.GOOGLE_CREDENTIALS_JSON key for Result Service is also stored inside c2fit-bff-dev, not a dedicated result-service secret. |
dev/c2fit/federation, prod/c2fit/federation, study/c2fit/federation | Naming ({env}/c2fit/federation) suggests SSO/identity federation config, not yet cross-referenced against a task definition's secrets block — purpose not fully confirmed. |
c2fit-prod-android | The secret exists, but its consumer and purpose have not been confirmed. Frontend CI uses GitHub Actions secrets for Android signing. |
So the convention is c2fit-bff-{env} for the BFF's own secret and {env}/c2fit/{purpose} for others — no fully unified prefix scheme.
External and managed services
| Service | Purpose |
|---|---|
| AWS Cognito | Identity/auth (c2fit-bff) — same AWS account, called out separately as a managed identity service rather than app infra. |
| MongoDB Atlas | Dev/study and production database clusters described under "Data stores". |
| Sentry | Backend error tracking for c2fit-bff. |
global-asr-service | Shared speech-recognition platform consumed by the BFF and Result Service, described under "ASR integration". |