Skip to content

EMmind MTL infrastructure and deployment ​

Evidence snapshot: 2026-08-26. Repository evidence uses the commits listed in the project overview. Live resources for public EMmind and EMmind MTL were checked read-only in AWS accounts 050752624503 and 564141170168, region ap-southeast-1.

This page maps both infrastructure boundaries where they meet. The detailed runtime map is for MTL. The public stack is recorded separately because it owns the original Studio Twist build and deployment pipeline referenced by the MTL repository workflows.

Infrastructure boundary ​

The live application runs on EC2, not ECS. Each environment registers the same EC2 instance with three instance-type target groups. HTTPS host and path rules select the core, employee web, or CMS target. The three application repositories describe the containers, service dependencies, image publication, and deployment-event contract, but contain no definitions for these live compute, load-balancer, DNS, database, cache, bucket, or secret-delivery resources. The repository or team that owns those definitions was not established.

Environment resource map ​

ResourceDevelopmentProduction
Employee application and API hostdev.mtl.emmind.aimet.techmtl.emmind.aimet.tech
CMS hostdev.cms.mtl.emmind.aimet.techcms.mtl.emmind.aimet.tech
Application Load Balancerdev-albemmind-mtl-prod
EC2 hostemmind-mtl-dev (i-062e6284f3b20bdff)emmind-mtl-prod (i-0fd95a3c2cede9828)
VPCDefault VPC vpc-ae7685c8dmind-prod-vpc (vpc-00b2df00a723fbfe9)
PostgreSQL RDSdev-db, port 5432emmind-mtl-prod, port 5432
System/assets bucketemmind-mtl-assets-devemmind-mtl-assets
User-content bucketemmind-mtl-usercontent-devemmind-mtl-usercontent
EC2 instance role/profileemmind-mtl-dev-ec2emmind-mtl-prod-ec2
RedisLive placement not establishedLive placement not established

Security-group relationships allow each application host to reach its environment's RDS instance on TCP 5432. EC2 role policies establish access to the listed S3 buckets. The production RDS endpoint is emmind-mtl-prod.cax9kybodeyp.ap-southeast-1.rds.amazonaws.com; use the configured DSN rather than embedding this address in application code.

Listener selectionTarget groupTarget portLoad-balancer health path
Employee host and /api/*emmind-mtl-core-dev or emmind-mtl-core-prod3001/api/health-check
Remaining paths on employee hostemmind-mtl-webapp-dev or emmind-mtl-webapp-prod3000/liff
CMS hostemmind-mtl-cmsapp-dev or emmind-mtl-cmsapp-prod3002/health-check

The production listener's default action also forwards to the employee web target. The development listener relies on its host-based rules for the verified application hosts.

Public EMmind infrastructure reference ​

Public EMmind runs entirely in account 050752624503:

ResourcePublic deployment
VPCcbt-vpc (vpc-0e2d0a6656f56e8fe)
Application Load BalancerInternet-facing cbt-alb
EC2 application hostscbt-dev, cbt-uat, and cbt-prod
Target groupscbt-core-* on port 3001, cbt-webapp-* on port 3000, and cbt-cmsapp-* on port 3002
Production hostsemmind.aimet.tech and cms.emmind.aimet.tech
UAT hostscbt.aimet.tech and cms.cbt.aimet.tech
PostgreSQLAurora PostgreSQL cluster db-1, engine 16.11
System/assets bucketscbtapp-assets-dev and cbtapp-assets
User-content bucketscbtapp-usercontent-dev and cbtapp-usercontent

The HTTPS listener sends /api/* on the employee host to core, remaining employee-host paths to the web app, and the CMS host to the CMS target. The account also owns the emmind.aimet.tech hosted zone and delegates the mtl.emmind.aimet.tech subdomain to account 564141170168.

Runtime components ​

ComponentRuntimeContainer default in sourceLive target portRequired services
Core API and scheduled jobsNestJS on Node.js 22.12.0-alpine30003001PostgreSQL, Redis, S3, LINE APIs, Google OAuth
Employee web appRemix on Node.js 20.17.0-alpine30003000Core API, Redis session store, LINE LIFF
CMSReact Router on Node.js 22.13.0-alpine30003002Core API

All container entrypoints load environment values from /data/secrets/env. The core starts with yarn start:prod; its dbmigrate entrypoint mode runs TypeORM migrations. The employee and CMS containers run their server-rendered web servers. The AWS control plane shows the target ports but not the host's current Docker arguments or image tags, so the exact container-to-host port mappings and active image versions were not verified.

Build and image publication ​

The public and MTL editions use separate build projects and ECR namespaces.

EditionCodeBuild projectsGitHub sourceDevelopment branch and tagECR repositories
Publiccbt-core-build, cbt-webapp-build, cbt-cmsapp-buildstudiotwist/cbt-core, studiotwist/cbt-webapp, studiotwist/cbt-cmsappdevelop -> developcbt/cbt-core, cbt/cbt-webapp, cbt/cbt-cmsapp in account 050752624503
MTLemmind-core-build, emmind-webapp-build, emmind-cmsapp-buildAimet cbt-core, cbt-webapp, cbt-cmsappdev -> devemmind/emmind-core, emmind/emmind-webapp, emmind/emmind-cmsapp in account 564141170168

Public CodeBuild webhooks accept the develop branch and Git tags. The public buildspecs also tag the image as latest. On a successful develop build, they publish a deployment request to cbt-devops-deploy-events.

MTL CodeBuild webhooks accept the dev branch and Git tags. The MTL buildspecs publish the selected tag and resolved commit SHA but have the original SNS notification and automatic deployment block commented out.

The MTL ECR repositories and their development and version tags were verified in account 564141170168.

RepositoryCodeBuild projectECR imageDevelopment imageRelease image
cbt-coreemmind-core-buildemmind/emmind-core:dev from the dev branchMatching Git tag
cbt-webappemmind-webapp-buildemmind/emmind-webapp:dev from the dev branchMatching Git tag
cbt-cmsappemmind-cmsapp-buildemmind/emmind-cmsapp:dev from the dev branchMatching Git tag

Each CodeBuild project points to its matching GitHub repository, uses the root buildspec.yml, and shares the emmind-code-build service role. Active CodeBuild webhooks accept pushes to refs/heads/dev and Git tag refs. For the development branch, each buildspec sets the image tag to dev; for tag refs, it uses the Git tag. It also tags the image with CODEBUILD_RESOLVED_SOURCE_VERSION and attempts that push without failing the build if the SHA push fails.

Deployment event contract ​

Each repository has an active, manually dispatched GitHub workflow. Source configures it to assume CBTGithubDeployerRole in AWS account 050752624503 and send an SNS message to cbt-devops-deploy-events.

json
{
  "app_name": "cbt-core | cbt-webapp | cbt-cmsapp",
  "app_env": "operator input",
  "app_image_tag": "operator input"
}

The SNS topic and its consumer are part of the public Studio Twist deployment pipeline. The topic invokes Lambda CBT-Devops-Deploy in account 050752624503. That function:

  1. selects an EC2 instance in the same account using App_<app_name>=true and App_env=<environment> tags;
  2. runs an SSM shell command under /app/<app_name>;
  3. updates the image tag in docker-compose.yml, logs in to the public ECR registry, pulls the image, and runs docker compose up -d --remove-orphans;
  4. runs the core database migration on an instance tagged for migrations; and
  5. invokes CBT-Devops-PostDeploy after a successful deployment to check target-group health.

This consumer resolves cbt-* application names to the public cbt/cbt-* images and the public cbt-dev, cbt-uat, or cbt-prod hosts. It does not map requests to the MTL emmind/emmind-* images or to EC2 instances in account 564141170168.

The manual workflows in the MTL repositories still publish cbt-* names to this public topic. The reviewed source and AWS resources therefore do not establish those workflows as the mechanism that activates MTL images. MTL image publication is verified; MTL runtime activation remains a separate ownership gap.

Configuration ownership ​

Core API ​

GroupConfiguration
HTTPPORT
PostgreSQLDB_DSN or split DB_HOST, DB_PORT, DB_USER, DB_PASS, and DB_NAME settings, plus optional DB_SCHEMA and DB_SSL
RedisHost, port, and optional LINE conversation-state expiry
JWTBase64-encoded access-token public/private PEM keys and refresh-token private PEM key. A refresh public-key constant exists but is not consumed by current source.
LINELogin client ID, LIFF ID, and Messaging API channel token
GoogleOAuth client ID, client secret, redirect URI, and verification expiry
AWS and S3Region, system bucket, user-content bucket, and the AWS SDK credential chain

Employee web app ​

GroupConfiguration
SessionCookie name, signing secret, secure flag, and CSRF secret
Core APIBase URL that reaches the core's globally prefixed /api routes
LINELIFF ID, LIFF URL, and endpoint URL
RedisREDIS_DSN for server-side employee sessions
Assets and analyticsCDN base URL and Google Analytics ID
Local developmentHTTPS toggle used by the LIFF development setup

CMS ​

The CMS uses a cookie name, cookie secret, secure flag, CSRF secret, and core API base URL. Its browser monitoring reads VITE_NODE_ENV and VITE_SENTRY_DSN. A Redis service exists in the local compose file, but the current CMS session is stored in a signed cookie.

No application-specific Secrets Manager secret or Systems Manager Parameter Store name was identified in the verified account. /data/secrets/env is a container-level contract; the mechanism that creates or mounts that file on the EC2 hosts remains unknown.

Database migrations ​

cbt-core owns TypeORM entities and migrations. Runtime schema synchronization and automatic migration execution are disabled. Run the core image in migration mode for schema changes:

bash
/app/docker-entrypoint.sh dbmigrate

Migration execution belongs in the deployment sequence before application tasks that require the new schema.

Scheduled infrastructure work ​

Scheduled jobs run inside cbt-core, not in a separate worker repository.

ScheduleWork
Daily at midnightCopy recurring notified activities into the current journal
Every five minutesSend due activity and 9Q notifications through LINE
Daily at 08:00, Asia/BangkokSend streak and seven-day emotion reminders
Daily at 03:00, Asia/BangkokReset streak state and re-enable eligible emotion reminders

Health and telemetry ​

ApplicationApplication health routeALB health pathCloudWatch application log groupsTelemetry in source
CoreGET /api/health-check/api/health-check/emmind/docker/emmind-core-dev, /emmind/docker/emmind-core-prodHTTP request logging and global exception logging. The Sentry package and debug route exist, but no initialization was found.
Employee web/health-check/liff/emmind/docker/emmind-webapp-dev, /emmind/docker/emmind-webapp-prodSentry server error handling and Google Analytics when configured
CMS/health-check/health-check/emmind/docker/emmind-cmsapp-dev, /emmind/docker/emmind-cmsapp-prodBrowser Sentry in the configured production mode

MTL CodeBuild writes to /aws/codebuild/emmind-core-build, /aws/codebuild/emmind-webapp-build, and /aws/codebuild/emmind-cmsapp-build. Public CodeBuild writes to /aws/codebuild/cbt-core-build, /aws/codebuild/cbt-webapp-build, and /aws/codebuild/cbt-cmsapp-build.

qr.mtl.emmind.aimet.tech is a static QR entry page served by CloudFront distribution E3QXBPOIIJYCY from the emmind-mtl-qr-page S3 bucket. Route 53 aliases the hostname to the distribution. Its source repository and infrastructure-definition owner were not identified.

Verified infrastructure limits ​

No CBT-mapped ECS service or task family, Auto Scaling group, CodeDeploy application, CodePipeline pipeline, API Gateway API, or Cloud Map service was identified. No CloudFormation stack was mapped to the verified resources. These negative findings narrow the current operating model to EC2, ALB, RDS, S3, ECR, CodeBuild, CloudWatch, and the external deployment-event path; they do not prove that infrastructure definitions do not exist in another account or repository.

Local service map ​

RepositoryLocal serviceDefault host port
CorePostgreSQL5450
CoreRedis6364
Employee webRedis6377
CMSRedis compose service, not used by the current session code6371

The core Makefile starts PostgreSQL and Redis. Running the employee application locally also requires its Redis session store and a reachable core API.