GuruCrate
Switch Theme
Beautybeginner1 week

StrandAI - Your Personal Hair Intelligence

AI-powered hair and makeup assistant app that analyzes a user’s selfie to detect face shape, hair type, and skin tone, then generates personalized hairstyle and hair color try-ons, customized hair care routines, product recommendations, event-based styling suggestions, and ongoing hair health tracking to help users confidently choose, maintain, and improve their look before visiting a salon.

Potential MCP Stack

Opportunity Score46.2/100

Total Volume (Monthly)

163,900

Avg CPC

$1.12

Avg Competition

0.47

KeywordVolumeCPCComp.
best hairstyle for my face4,400$1.250.37
hairstyle simulator8,100$1.040.30
hair color simulator2,900$1.020.41
virtual hair5,400$1.030.32
hair care110,000$1.240.98

StrandAI is a mobile-first AI hair and makeup assistant that turns a user selfie into actionable, personalized styling outputs. The app performs on-device or server-side computer vision analysis to infer face shape, hair attributes (curl pattern, density, porosity proxies, frizz, length), and skin tone/undertone, then uses these signals to:

  • Generate hairstyle try-ons (2D/3D overlay) and hair color try-ons (segmentation + recolor) with realistic blending.
  • Recommend hair care routines (wash cadence, conditioning, heat protection, scalp care) and product suggestions mapped to hair type + goals.
  • Provide event-based styling suggestions (e.g., wedding guest, interview) with step-by-step instructions.
  • Track hair health over time using periodic selfies + user inputs (breakage, shedding, dryness), producing trend insights.

Positioning: a consumer app that reduces uncertainty before salon visits and improves at-home maintenance. The core differentiator is a closed loop: analyze → simulate → recommend → track, with personalization improving as the user logs outcomes.

Existing alternatives

  • Salon consultation: high quality but time-bound, subjective, and not scalable.
  • Generic hairstyle apps / AR filters: fun try-ons but limited personalization; often ignore hair type constraints and maintenance.
  • Beauty e-commerce quizzes: basic questionnaires; no visual analysis; recommendations feel generic.
  • YouTube/TikTok tutorials: abundant content but hard to map to a user’s specific face shape, hair type, and skill level.

Why they are insufficient

  • Try-on apps often fail at hair segmentation, realistic color blending, and matching styles to face shape + hair texture.
  • Product recommendations are frequently affiliate-driven rather than evidence-based and personalized.
  • Users lack a way to track hair health and correlate routine/product changes with outcomes.

Pain points

  • Fear of making an irreversible choice (cut/color) without seeing a realistic preview.
  • Confusion about hair type/porosity/needs; trial-and-error product spending.
  • Inconsistent routines; no feedback loop.
  • Difficulty communicating desired outcomes to stylists.

Market inefficiencies

  • High return rates and dissatisfaction in beauty purchases due to poor fit.
  • Salons spend time on repetitive education/consultation that could be pre-qualified.
  • Consumers lack a standardized “hair profile” they can reuse across brands/salons.

Primary persona

Consumer (18–40), style-conscious, selfie-native

  • Goals: choose flattering cuts/colors, reduce bad salon outcomes, improve hair health.
  • Behaviors: searches inspiration, saves looks, buys hair products online, open to subscriptions if value is clear.
  • Constraints: wants fast results; low tolerance for complex onboarding.

Secondary persona

Salon client power-user / frequent colorer

  • Goals: maintain color, reduce damage, plan appointments, communicate with stylist.
  • Behaviors: spends more on products/services; values tracking and reminders.

Technical maturity

  • End users: low-to-medium; must be guided.
  • Internal team: needs strong CV/ML + mobile/graphics competence.

Buying behavior (consumer SaaS)

  • Freemium entry via try-ons.
  • Conversion triggers: unlimited try-ons, premium styles, routine personalization, product bundles, exportable “salon brief”.

MVP scope (buildable in 8–12 weeks with a small team)

1) Account + onboarding

  • Email/Apple/Google sign-in.
  • Consent flow for biometric processing + privacy settings.
  • Guided selfie capture (lighting, angle) with quality checks (blur, face visibility).

2) Hair/face analysis (v1)

  • Face detection + landmarks.
  • Face shape classification (e.g., oval/round/square/heart/diamond/oblong) with confidence.
  • Skin tone estimation (Fitzpatrick-like scale + undertone heuristic).
  • Hair segmentation mask (head hair region) + basic hair attributes:
    • Curl pattern bucket (straight/wavy/curly/coily)
    • Length bucket (short/medium/long)
    • Density proxy (low/med/high) from mask area + texture cues

3) Try-ons (v1)

  • Hair color try-on: recolor within hair mask using physically-plausible blending (preserve highlights/shadows).
  • Hairstyle try-on (2D overlay): curated set of PNG/alpha hairstyle assets aligned to landmarks; limited but reliable.
  • Save/share results.

4) Recommendations (rules + LLM)

  • Routine generator using deterministic rules + templated content:
    • wash frequency, conditioner, deep conditioning, leave-in, heat protectant, scalp care
  • Product recommendations (initially curated catalog + tagging):
    • tags: hair type, concerns (frizz/dryness/damage), budget, ingredients to avoid
  • Event-based styling suggestions: choose event + time available + skill level → steps.

5) Hair health tracking (v1)

  • Weekly check-in: selfie + 5-question survey (dryness, frizz, shedding, breakage, scalp itch).
  • Trend charts (simple): score over time + notes.

6) “Salon brief” export

  • Generate a one-page summary:
    • face shape, hair type, chosen style/color, maintenance routine, reference images
  • Export as PDF/share link.

Non-goals for MVP

  • Full 3D hair simulation.
  • Real-time AR video try-on.
  • Medical/dermatology claims.

High-level components

  • Mobile app (iOS/Android): selfie capture, try-on rendering, results browsing, tracking UI.
  • API backend: auth, user profiles, recommendation orchestration, product catalog, analytics.
  • ML inference service: face landmarks, segmentation, classification; can be server-side initially.
  • Asset service/CDN: hairstyle overlays, color palettes, product images.
  • Observability: logs, metrics, tracing.

Processing approach

  • MVP: server-side inference for faster iteration.
  • Optional: on-device for privacy/performance later (CoreML/TF Lite).

Deployment

  • Containerized services on AWS (ECS/Fargate) or GCP (Cloud Run).
  • Postgres managed (RDS/Cloud SQL).
  • Object storage (S3/GCS) for user images (encrypted) and generated renders.

Mermaid diagram

flowchart LR
  A[Mobile App] -->|HTTPS| B[API Gateway]
  B --> C[Backend API]
  C --> D[(Postgres)]
  C --> E[Object Storage: S3]
  C --> F[Product Catalog Service]
  C --> G[Recommendation Engine]
  G --> H[LLM Provider]
  C --> I[ML Inference Service]
  I --> J[CV Models: face/landmarks/segmentation]
  C --> K[CDN for Assets]
  C --> L[Analytics/Events]

Key implementation notes

  • Generated try-on images stored as objects with short-lived signed URLs.
  • Keep raw selfies optional; default to storing derived embeddings/metadata + user-approved images.
  • Use a job queue for heavier inference (BullMQ/SQS) if latency becomes an issue.

Core tables (Postgres)

users

create table users (
  id uuid primary key,
  email text unique,
  auth_provider text not null,
  created_at timestamptz not null default now()
);

user_profiles

create table user_profiles (
  user_id uuid primary key references users(id) on delete cascade,
  display_name text,
  birth_year int,
  hair_goals text[], -- e.g. {"grow_length","reduce_frizz"}
  consent_biometric boolean not null default false,
  updated_at timestamptz not null default now()
);
create index on user_profiles using gin (hair_goals);

selfies

Stores user-uploaded images and metadata.

create table selfies (
  id uuid primary key,
  user_id uuid not null references users(id) on delete cascade,
  image_object_key text not null,
  captured_at timestamptz not null default now(),
  quality_score numeric,
  source text -- onboarding, weekly_checkin
);
create index on selfies(user_id, captured_at desc);

analyses

One analysis per selfie per model version.

create table analyses (
  id uuid primary key,
  selfie_id uuid not null references selfies(id) on delete cascade,
  model_version text not null,
  face_shape text,
  face_shape_conf numeric,
  skin_tone text,
  undertone text,
  hair_type text,
  hair_length text,
  hair_density text,
  hair_mask_object_key text,
  created_at timestamptz not null default now(),
  unique(selfie_id, model_version)
);
create index on analyses(selfie_id);

tryons

create table tryons (
  id uuid primary key,
  user_id uuid not null references users(id) on delete cascade,
  analysis_id uuid not null references analyses(id) on delete cascade,
  type text not null, -- color|style
  params jsonb not null, -- {color:"chestnut"} or {style_id:"bob_01"}
  result_object_key text not null,
  created_at timestamptz not null default now()
);
create index on tryons(user_id, created_at desc);
create index on tryons using gin (params);

styles

Curated hairstyle assets.

create table styles (
  id text primary key,
  name text not null,
  gender_tag text,
  length text,
  compatible_hair_types text[],
  compatible_face_shapes text[],
  asset_object_key text not null,
  created_at timestamptz not null default now()
);
create index on styles(length);
create index on styles using gin (compatible_hair_types);
create index on styles using gin (compatible_face_shapes);

products

create table products (
  id text primary key,
  brand text,
  name text not null,
  category text, -- shampoo, conditioner, mask, oil
  tags text[],
  price_cents int,
  affiliate_url text,
  image_url text
);
create index on products(category);
create index on products using gin (tags);

recommendations

create table recommendations (
  id uuid primary key,
  user_id uuid not null references users(id) on delete cascade,
  analysis_id uuid references analyses(id) on delete set null,
  type text not null, -- routine|products|event
  payload jsonb not null,
  created_at timestamptz not null default now()
);
create index on recommendations(user_id, created_at desc);
create index on recommendations(type);

checkins

create table checkins (
  id uuid primary key,
  user_id uuid not null references users(id) on delete cascade,
  selfie_id uuid references selfies(id) on delete set null,
  dryness int,
  frizz int,
  shedding int,
  breakage int,
  scalp_irritation int,
  notes text,
  created_at timestamptz not null default now()
);
create index on checkins(user_id, created_at desc);

Auth

  • OAuth (Apple/Google) + email magic link.
  • Backend issues JWT access token (15m) + refresh token (30d).
  • All endpoints require Authorization: Bearer <token> unless noted.

Core endpoints (REST)

Upload selfie

POST /v1/selfies

  • Multipart upload or pre-signed URL flow.

Option A: presigned

  1. POST /v1/selfies/presign
{ "contentType": "image/jpeg", "source": "onboarding" }

Response:

{ "selfieId": "uuid", "uploadUrl": "https://...", "objectKey": "..." }
  1. Client PUTs to uploadUrl.
  2. POST /v1/selfies/{selfieId}/complete

Run analysis

POST /v1/analyses

{ "selfieId": "uuid" }

Response:

{
  "analysisId": "uuid",
  "faceShape": {"label":"oval","confidence":0.82},
  "hairType":"wavy",
  "hairLength":"medium",
  "skinTone":"medium",
  "undertone":"neutral"
}

List styles

GET /v1/styles?faceShape=oval&hairType=wavy&length=medium Response:

{ "items": [{"id":"bob_01","name":"Soft Bob","assetUrl":"https://cdn/..."}] }

Create try-on (color)

POST /v1/tryons/color

{ "analysisId": "uuid", "color": "chestnut_brown" }

Response:

{ "tryonId": "uuid", "resultUrl": "https://signed-url..." }

Create try-on (style)

POST /v1/tryons/style

{ "analysisId": "uuid", "styleId": "bob_01" }

Get routine recommendation

POST /v1/recommendations/routine

{ "analysisId": "uuid", "goals": ["reduce_frizz","shine"] }

Response:

{ "id":"uuid", "routine": [{"step":"Wash","frequency":"2-3x/week"}] }

Product recommendations

POST /v1/recommendations/products

{ "analysisId": "uuid", "budget": "mid", "avoid": ["sulfates"] }

Event styling

POST /v1/recommendations/event

{ "analysisId": "uuid", "event": "interview", "timeMinutes": 15, "skill": "beginner" }

Weekly check-in

POST /v1/checkins

{
  "selfieId": "uuid",
  "dryness": 3,
  "frizz": 4,
  "shedding": 2,
  "breakage": 3,
  "scalpIrritation": 1,
  "notes": "Used new leave-in"
}

Salon brief

POST /v1/salon-briefs

{ "analysisId": "uuid", "tryonIds": ["uuid"], "notes": "No bleach" }

Response:

{ "pdfUrl": "https://signed-url...", "shareUrl": "https://app/.../b/abc" }

Recommended stack (MVP)

Mobile

  • React Native (Expo)
    • Fast iteration, good camera integrations, cross-platform.
    • Use expo-camera, expo-image-manipulator, react-native-skia (optional) for rendering overlays.

Backend API

  • Node.js + TypeScript + NestJS (or Fastify)
    • Strong structure, validation, DI, easy REST.
  • Alternative: Python FastAPI if team is ML-heavy.

ML inference

  • Python FastAPI service with PyTorch/ONNX Runtime
    • Keep ML isolated; version models independently.
    • Use ONNX for faster CPU inference.

Database

  • PostgreSQL (managed)
    • Relational core + JSONB for flexible recommendation payloads.

Storage/CDN

  • S3 + CloudFront (or GCS + Cloud CDN)
    • Signed URLs for private user images.

Queue (optional)

  • SQS (AWS) or BullMQ + Redis
    • For async try-on generation if needed.

Observability

  • Sentry (mobile + backend)
  • OpenTelemetry + managed logs (CloudWatch)

Why this stack

  • Separates concerns: mobile UX, API orchestration, ML inference.
  • Minimizes time-to-MVP while keeping a path to on-device inference later.

Authentication

  • OAuth (Apple/Google) + email magic link.
  • JWT access tokens + rotating refresh tokens.
  • Device binding for refresh tokens (store in Keychain/Keystore).

Authorization

  • Row-level authorization: every resource scoped by user_id.
  • Signed URLs for S3 objects; never expose raw object keys publicly.

Data protection

  • Encrypt at rest (RDS, S3 SSE-KMS).
  • TLS everywhere.
  • Store selfies only with explicit consent; allow deletion.
  • Separate PII (email) from biometric-derived attributes where possible.

Rate limiting / abuse

  • IP + user-based rate limits on upload and inference endpoints.
  • Quotas for free tier (e.g., 5 analyses/day).

Threat considerations

  • Prompt injection (if using LLM): restrict to templated outputs; never allow tool execution from model output.
  • Model inversion / biometric sensitivity: minimize retention; avoid storing embeddings unless necessary.
  • Content safety: prevent generating harmful advice; disclaimers for non-medical guidance.

Compliance posture (pragmatic)

  • Provide: data export, deletion, consent logs.
  • If targeting EU/CA: GDPR/CCPA readiness (DPA with vendors, privacy policy, RoPA-lite).

Pricing model

  • Freemium
    • Free: limited analyses/try-ons per day, basic routine.
    • Pro ($7.99–$12.99/mo): unlimited try-ons, premium styles/colors, advanced tracking, salon brief exports.

Revenue streams

  • Subscription (primary).
  • Affiliate revenue on product recommendations (secondary).
  • Sponsored placements (later; must be clearly labeled).

Expansion pricing

  • Salon partner tier (B2B): $49–$199/mo per seat for pre-consult intake + shareable briefs.
  • One-time packs: “Color Pack”, “Wedding Styles Pack”.

Unit economics assumptions (initial)

  • Inference cost: optimize with ONNX + batching; target <$0.01–$0.05 per analysis at scale.
  • Storage: keep only user-approved images; lifecycle policies to reduce cost.
  • LLM: use small models or templating; reserve LLM for summarization.

Distribution strategy

  • Mobile app stores (iOS/Android) with strong ASO around “hairstyle try on”, “hair color simulator”, “face shape haircuts”.

Channels

  • Creator partnerships: hairstylists and beauty creators demonstrating before/after try-ons.
  • UGC loop: shareable try-on images with subtle watermark.
  • SEO landing pages for style guides (web) that deep-link into the app.

Launch plan

  1. Private beta (200–500 users): focus on segmentation quality + recommendation relevance.
  2. Public launch with limited style packs.
  3. Add “Salon brief” sharing to drive referrals.

Growth loops

  • Share try-on → recipient installs → runs analysis → shares.
  • Weekly check-in streaks → retention.
  • Seasonal/event prompts (prom, weddings) → reactivation.

Validate before full build

  • Landing page + waitlist with 3 value props: try-on realism, routine personalization, salon brief.
  • Run paid ads to measure CTR and waitlist conversion.

MVP testing approach

  • Start with 20–30 curated styles and 10–15 color options.
  • A/B test:
    • Onboarding flow (questionnaire-first vs selfie-first)
    • Try-on result presentation (grid vs swipe)
    • Routine format (short checklist vs detailed steps)

Metrics

  • Activation: % users completing selfie + first try-on within 5 minutes.
  • Try-on satisfaction: thumbs up/down + “looks realistic” rating.
  • Retention: D7/D30, weekly check-in completion.
  • Conversion: free→paid, paywall view→purchase.
  • Recommendation engagement: product click-through, routine saves.

Phase 1 (MVP) — 8–12 weeks

  • Auth + onboarding + selfie capture quality checks.
  • Server-side analysis (face shape, skin tone, hair segmentation, basic hair type).
  • Color try-on + 2D style overlays.
  • Routine + product recommendations (rules + curated catalog).
  • Weekly check-ins + basic trends.
  • Salon brief PDF.

Phase 2 — 3–4 months

  • Improve segmentation (edge quality, occlusions) + model versioning.
  • Personalized style ranking using implicit feedback.
  • Better color rendering (multi-tone, balayage presets).
  • Add reminders (wash day, mask day) + calendar integration.
  • Web viewer for salon briefs.

Phase 3 — 6–12 months

  • On-device inference for privacy + cost.
  • Real-time AR video try-on (select devices).
  • Stylist marketplace / booking integrations.
  • Personalized product bundles + checkout.
  • Longitudinal hair health insights with correlations (routine changes vs scores).

Technical risks

  • Hair segmentation quality across lighting, backgrounds, and hair textures.
  • Try-on realism: uncanny results reduce trust.
  • Latency/cost of server inference at scale.

Market risks

  • Users may treat as entertainment rather than utility; low willingness to pay.
  • Crowded space with filter apps and beauty platforms.

Legal risks

  • Biometric data handling requirements; consent and deletion must be robust.
  • Avoid medical claims (hair loss, scalp conditions) without clinical backing.

Competitive risks

  • Large platforms (Snap, TikTok, beauty retailers) can replicate try-ons.
  • Differentiation must come from tracking + salon brief + personalization loop.

Product expansions

  • Advanced hair diagnostics: split ends detection proxy, shine/frizz scoring, curl definition scoring.
  • Ingredient-based product matching (avoid allergens, CGM-friendly).
  • “Stylist mode”: intake form + client profile + recommended service plan.
  • Community: share routines and results with similar hair profiles.
  • Localization: region-specific products and hair norms.

Scalability direction

  • Move inference to edge (CoreML/TF Lite) for common tasks; keep heavy generation server-side.
  • Build a feature store for user hair profile signals.
  • Introduce offline-first caching for assets and previous try-ons.
You are a senior full-stack engineer building the MVP for “StrandAI” (AI hair and makeup assistant). Implement a production-ready but minimal system with a React Native (Expo) mobile app, a NestJS (Node/TypeScript) backend API, and a Python FastAPI ML inference service. Use PostgreSQL and S3-compatible object storage.

GOALS (MVP)
1) User can sign in (Apple/Google/email magic link acceptable as stub), upload a selfie, run analysis, and receive:
   - face shape label + confidence
   - hair type bucket + length + density
   - skin tone + undertone
   - hair segmentation mask (stored as image)
2) User can generate try-ons:
   - hair color recolor within mask
   - hairstyle overlay using curated PNG assets aligned to face landmarks
3) User can receive:
   - routine recommendation (rules-based + optional LLM summarization)
   - product recommendations from curated catalog
   - event-based styling steps
4) User can do weekly check-ins and see a simple trend chart.
5) User can export a “salon brief” PDF with analysis + selected try-ons.

TECH STACK
- Mobile: React Native (Expo), TypeScript, expo-camera, react-query, Zustand (or Redux Toolkit), react-native-svg (optional), victory-native (charts).
- Backend: NestJS + TypeScript, PostgreSQL (Prisma ORM), S3 (AWS SDK v3), JWT auth.
- ML service: Python FastAPI, ONNX Runtime (preferred) or PyTorch; OpenCV; mediapipe (optional) for landmarks.
- Infra: Docker Compose for local dev; deploy via AWS ECS Fargate (or Render/Fly.io as simpler alternative).

DELIVERABLES
A) Monorepo with /apps/mobile, /apps/api, /apps/ml, /packages/shared.
B) Docker Compose that runs: postgres, api, ml.
C) Database migrations (Prisma) matching schema below.
D) REST API endpoints implemented with validation and auth.
E) Minimal mobile UI flows:
   - Sign in (can be mocked with email+code for MVP)
   - Onboarding selfie capture with quality checks
   - Results screen (analysis + recommended styles)
   - Try-on screen (color palette + style list)
   - Tracking screen (weekly check-in + chart)
   - Salon brief export/share
F) Seed script to load 20 styles and 50 products.

DATABASE SCHEMA (Prisma models)
- User(id, email, authProvider, createdAt)
- UserProfile(userId, displayName, birthYear, hairGoals[], consentBiometric, updatedAt)
- Selfie(id, userId, imageObjectKey, capturedAt, qualityScore, source)
- Analysis(id, selfieId, modelVersion, faceShape, faceShapeConf, skinTone, undertone, hairType, hairLength, hairDensity, hairMaskObjectKey, createdAt)
- Style(id, name, genderTag, length, compatibleHairTypes[], compatibleFaceShapes[], assetObjectKey, createdAt)
- Tryon(id, userId, analysisId, type, params Json, resultObjectKey, createdAt)
- Product(id, brand, name, category, tags[], priceCents, affiliateUrl, imageUrl)
- Recommendation(id, userId, analysisId?, type, payload Json, createdAt)
- Checkin(id, userId, selfieId?, dryness, frizz, shedding, breakage, scalpIrritation, notes, createdAt)

API ENDPOINTS (NestJS)
Auth
- POST /v1/auth/login (email magic link stub) -> returns access+refresh
- POST /v1/auth/refresh

Selfies
- POST /v1/selfies/presign -> {selfieId, uploadUrl, objectKey}
- POST /v1/selfies/:id/complete

Analysis
- POST /v1/analyses {selfieId} -> returns analysis payload
  - API calls ML service /infer with signed download URL or streams image bytes.

Styles
- GET /v1/styles?faceShape&hairType&length

Try-ons
- POST /v1/tryons/color {analysisId, color}
- POST /v1/tryons/style {analysisId, styleId}
  - Implement try-on generation in ML service for color; style overlay can be done in API or ML service.

Recommendations
- POST /v1/recommendations/routine {analysisId, goals[]}
- POST /v1/recommendations/products {analysisId, budget, avoid[]}
- POST /v1/recommendations/event {analysisId, event, timeMinutes, skill}

Tracking
- POST /v1/checkins
- GET /v1/checkins (list)

Salon brief
- POST /v1/salon-briefs {analysisId, tryonIds[], notes}
  - Generate PDF server-side (e.g., pdfkit) and store to S3; return signed URL.

ML SERVICE (FastAPI)
Endpoints:
- POST /infer
  Input: image (bytes) OR image_url
  Output JSON:
    - face_shape label+confidence
    - skin_tone, undertone
    - hair_type, hair_length, hair_density
    - landmarks (optional)
    - hair_mask_png (base64) OR store mask and return bytes
- POST /tryon/color
  Input: image + hair_mask + color
  Output: result PNG/JPEG
- POST /tryon/style
  Input: image + landmarks + style_asset
  Output: composited image

IMPLEMENTATION NOTES
- Start with deterministic heuristics for face shape and skin tone if needed; keep model_version field.
- Hair segmentation: use a pre-trained segmentation model (e.g., MODNet-like or a lightweight hair segmentation ONNX). If unavailable, stub with mediapipe selfie segmentation and document limitations.
- Color try-on: recolor in HSV/Lab space within mask; preserve luminance; feather edges.
- Store all images in S3 with private ACL; serve via signed URLs.
- Add lifecycle policy for selfies (e.g., delete after 30 days unless user saves).

PROJECT STRUCTURE
/apps
  /mobile
  /api
  /ml
/packages
  /shared (types, zod schemas, constants)

LOCAL DEV
- Provide docker-compose.yml with postgres, api, ml.
- Provide .env.example for each app.

DEPLOYMENT
- Provide Dockerfiles for api and ml.
- Provide minimal IaC notes (Terraform optional) or manual steps:
  - RDS Postgres
  - S3 bucket + CloudFront (optional)
  - ECS services for api and ml
  - Secrets in AWS Secrets Manager

QUALITY BAR
- Input validation with Zod/class-validator.
- Centralized error handling.
- Basic request logging + correlation IDs.
- Unit tests for recommendation rules and key endpoints.

Now implement the MVP end-to-end.
Pro Tip: Copy the content above into your favorite AI coding assistant to jumpstart your build.