Skip to content

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 ​

RepositoryDeploy targetRuntime
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/assessmentECS (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/resultECS (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.

WorkflowTriggerWhat it does
lint.yamlAutomatic on push or pull request to mainChecks the Flutter code.
unit-test.yamlAutomatic on push or pull request to mainRuns the Flutter unit tests.
create_tag.ymlManual. Enter the new version.Creates the release tag and GitHub release. Run this before study and production releases.
deploy-firebase-app-distribution-dev.ymlManual. Enter a commit SHA.Builds dev iOS and Android apps and sends them to Firebase App Distribution.
deploy-firebase-app-distribution-study.ymlManual 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.yamlManual. 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 ​

EnvironmentFlutter flavorApp identifierFirebase projectCurrent distribution route
DevreleaseDevtech.aimet.c2fit.devOpen c2fit-app-dev in Firebase ↗Firebase App Distribution for iOS and Android
StudyreleaseStudytech.aimet.c2fit.studyOpen c2fit-app-study in Firebase ↗Firebase App Distribution for iOS and Android
ProdreleaseProdtech.aimet.c2fitc2fit-appApp Store Connect/TestFlight for iOS; Google Play Console for Android

Dev release ​

  1. Choose the commit SHA to test.
  2. Manually run deploy-firebase-app-distribution-dev.yml.
  3. The reusable deployment workflow builds the dev iOS and Android apps and uploads both to the dev Firebase App Distribution project.

Study release ​

  1. Manually run create_tag.yml to create the study release tag.
  2. Run deploy-firebase-app-distribution-study.yml with that tag/ref.
  3. CI builds the releaseStudy iOS 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 ​

  1. On a developer machine, select the releaseProd configuration and create the production archive in Xcode.
  2. Upload the archive to App Store Connect from Xcode.
  3. Hand-test the uploaded build through TestFlight.
  4. If the build passes, submit it for App Store review.

Android ​

  1. Run upload-to-playstore.yaml with the production tag.
  2. CI builds the signed releaseProd Android App Bundle and uploads it to the Google Play Console track used for closed testing. The workflow currently exposes alpha, beta, and production track inputs; use the team's configured closed-testing track rather than uploading directly to production.
  3. Hand-test the closed-testing build.
  4. If the build passes, promote it to the production release in Play Console.

Frontend services ​

ServicePurpose
FirebasePer-flavor project configuration, analytics, and App Distribution for dev and study.
App Store Connect / TestFlightProduction iOS testing, review, and release.
Google Play ConsoleProduction Android closed testing and release promotion.
SentryMobile error tracking.
SmartlookMobile 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/workflowTriggerResult
c2fit-bff/development.ymlPush to devRuns tests, builds and pushes c2fit-bff:<commit-sha>, then updates ECS service c2fit-bff-dev.
c2fit-bff/development.ymlPush to mainRuns tests and pushes the commit-SHA image to ECR. The deploy job does not run.
c2fit-bff/acceptance_test.ymlPull request to dev/main, or manual runRuns unit and acceptance tests with WireMock, Redis, and MongoDB service containers; it does not deploy.
c2fit-bff/tag-version.ymlManual, with commit and versionCreates the Git tag and retags the existing ECR image with the requested version; it does not update ECS.
c2fit-assessment-result/development.ymlPush to devRuns tests, builds and pushes separate Assessment and Result images, then updates both dev ECS services.
c2fit-assessment-result/development.ymlPush to mainRuns tests and pushes both commit-SHA images to ECR. The deploy jobs do not run.
c2fit-assessment-result/tag-version.ymlManual, with service, commit, and versionCreates 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-runner for c2fit-bff
  • arn:aws:iam::564141170168:role/c2fit-assessment-result-actions-runner for c2fit-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:

HostnamePoints toPurpose
c2fit.aimet.techCloudFront d2g98tuobnmnvd.cloudfront.net → S3 c2fit-app-public-assetIntermediate 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.techALB c2fit-backend-alb (dualstack)Prod API — default rule on the HTTPS listener
api.study.c2fit.aimet.techSame ALB, host-header routedStudy API
api.dev.c2fit.aimet.techALB dev-albDev API
auth.c2fit.aimet.tech / dev.auth.c2fit.aimet.techCognito 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.techdev-alb / CloudFrontDev-only ASR-related endpoints — not individually traced back to a repo
Open tmt-generate-question.c2fit.aimet.tech ↗AWS AmplifyServes 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.comConfirms 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):

ClusterServiceEnvironmentALB / target groupNotes
c2fit-backendc2fit-bff-devDevdev-alb / c2fit-bff-devDefault VPC (vpc-ae7685c8), public IP enabled
c2fit-backendc2fit-assessment-devDevdev-alb / c2fit-assessment-devSame VPC as above
c2fit-backendc2fit-result-devDevdev-alb / c2fit-result-devSame VPC as above
c2fit-backendc2fit-bff-prodProdc2fit-backend-alb / c2fit-bff-service-prodShared prod VPC (vpc-00b2df00a723fbfe9), no public IP — not documented in any CI/CD workflow file read, confirmed only via AWS CLI
c2fit-backendc2fit-assessment-prodProdc2fit-backend-alb / c2fit-assessment-service-prodSame as above
c2fit-backendc2fit-result-prodProdc2fit-backend-alb / c2fit-result-service-prodSame as above
c2fit-backendc2fit-bff-studyStudyc2fit-backend-alb / c2fit-bff-service-studyShared prod VPC, no public IP. It is a dedicated study service. Open the project overview →.
c2fit-backendc2fit-assessment-studyStudyc2fit-backend-alb / c2fit-assessment-service-studySame as above
c2fit-backendc2fit-result-studyStudyc2fit-backend-alb / c2fit-result-service-studySame 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:

ModePathUse case
Real-time (streaming)Mobile app --WSS--> c2fit-bff /ws/v1/speech --WS reverse proxy--> SPEECH_SERVICE_BASE_URLLive 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:

RepositoryImage example
c2fit-bff564141170168.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-assessment564141170168.dkr.ecr.ap-southeast-1.amazonaws.com/c2fit-assessment:<tag> — prod running v1.0.1
c2fit-result564141170168.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:

ServiceResourceNotes
MongoDB Atlasdmind-prescreening-back.1k8q9bs.mongodb.net, db c2fit-bff / c2fit-assessment-resultDev + 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 Atlasc2fit-backend-prod.wqbwgq.mongodb.net, db c2fit-bff / c2fit-assessment-resultProd only — dedicated cluster, same MONGODB-AWS auth pattern.
MongoDB (collections)db c2fit-bffCollections: 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).
Redisgeneral-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.
Redisc2fit-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.
S3c2fit-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.
S3c2fit-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.
S3c2fit-app-public-assetPublic 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 ​

ComponentPurpose
AWS SESTransactional email (NotificationRepository.SendEmail, direct SDK call — no queue).
AWS SNSSMS (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 nameUsed by
c2fit-bff-dev, c2fit-bff-prod, c2fit-bff-studyOne 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/federationNaming ({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-androidThe 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 ​

ServicePurpose
AWS CognitoIdentity/auth (c2fit-bff) — same AWS account, called out separately as a managed identity service rather than app infra.
MongoDB AtlasDev/study and production database clusters described under "Data stores".
SentryBackend error tracking for c2fit-bff.
global-asr-serviceShared speech-recognition platform consumed by the BFF and Result Service, described under "ASR integration".