Bagtyyar

AutoTM

Active build

Turkmenistan's vehicle marketplace — rebuilt from the ground up as a Turborepo monorepo with React Native, NestJS bounded contexts, and fully air-gapped deployment.

  • Expo SDK 55
  • React Native 0.83
  • NativeWind v4
  • NestJS 11
  • Fastify
  • Prisma 7
  • PostgreSQL 16
  • Redis 7
  • BullMQ
  • Next.js 16
  • Tailwind CSS v4
  • shadcn/ui
  • MinIO
  • Sharp
  • Zod
  • Socket.IO
  • Turborepo
  • Docker
  • Caddy

Outcome

Ground-up rewrite of Turkmenistan's vehicle marketplace. Shipped S1–S3 (Turborepo scaffold, OTP auth, trilingual catalog) in 5 days with 63 API tests and full CI coverage. S4 Listings CRUD in progress. Built for air-gapped deployment with zero cloud dependencies.

Context

AutoTM is a vehicle listing and transaction platform built specifically for the Turkmenistan market. The original version was a Flutter mobile app with a stock NestJS API. This is a ground-up rewrite with a new architecture, new stack, and a focus on production discipline.

Engineering Decisions

  • Monorepo: Turborepo + pnpm workspaces with 7 apps and 5 shared packages. Shared Prisma schema, Zod contracts, and UI tokens consumed by every client.
  • Mobile: Expo SDK 55 + React Native 0.83.6 with NativeWind v4 and React Native Reusables, replacing Flutter for better ecosystem access and team velocity.
  • API: NestJS 11 on Fastify with Level 2 bounded contexts — pure TypeScript domain layer, one use-case per file, ports and adapters for cross-context communication.
  • ORM: Prisma 7 with explicit migrations and a single schema file in packages/db, replacing Sequelize for type safety and reliable migrations.
  • Web: Next.js 16 + Tailwind CSS v4 + shadcn/ui for both public site (auto.tm) and admin dashboard (admin.auto.tm).
  • Media pipeline: MinIO (S3-compatible, self-hosted) + Sharp for variant generation, replacing Firebase Storage. Client-side compression is mandatory before upload.
  • Real-time: Socket.IO 4 with Redis adapter for horizontal scaling, planned for S7 (buyer-seller chat).
  • Auth: Phone OTP via a custom SMS gateway fleet (5–20 Android phones running a Kotlin agent) + JWT access tokens + bcrypt-hashed refresh tokens. Multi-device sessions capped at 10 with FIFO eviction.
  • Job queue: BullMQ + Redis + dedicated NestJS worker app for async processing.
  • Hosting: Fully air-gapped inside Turkmenistan — Docker Compose on self-hosted Ubuntu, Caddy reverse proxy with auto-TLS. No cloud dependencies.
  • Deployment: Multi-service Docker images built via GitHub Actions (self-hosted runner), bundled into tarballs, and transferred to Turkmenistan via SCP/USB.

Current State

Phase 1 — Marketplace MVP

  • S1 (Scaffold): 🟢 Shipped — Turborepo structure, CI pipeline, Docker Compose dev environment.
  • S2 (Identity): 🟢 Shipped — Phone OTP login, JWT sessions, multi-device cap, rate limiting, full test coverage.
  • S3 (Catalog): 🟢 Shipped — Trilingual catalog seed (Brand, Model, Color, BodyType, Region, City, EngineType, Transmission, DriveType), read endpoints, FX rates.
  • S4 (Listings CRUD): 🟡 In Progress — Prisma schema migrated, mobile 7-step listing wizard with upload state machine, API use-cases being wired. Known gaps documented honestly: autosave edge cases, orphan cleanup, public listing-detail route pending.
  • S5–S10: Pending — Search/filters, garage/dealership, chat, notifications, admin dashboard, polish, soft launch.

Testing: 63 API test files, 7 mobile test files, 4 SMS-gateway test files. All passing in CI.

Documentation: 26 Architecture Decision Records, CONTEXT.md per workspace, sprint files with Definition of Done, and agent skill docs for mobile and TypeScript runtime boundaries.

Milestones

  1. M1 — Hello stack

    OK

    Turborepo monorepo scaffold, CI pipeline, Docker Compose dev environment, and local Postgres/Redis/MinIO services.

    Completed May 2026

  2. M2 — I can log in

    OK

    Phone OTP authentication via custom SMS gateway, JWT access tokens, bcrypt-hashed refresh tokens, multi-device session cap with FIFO eviction, and rate limiting.

    Completed May 2026

  3. M3 — I can browse cars

    Active

    Listings CRUD with mobile 7-step wizard, media upload state machine, catalog integration, and public listing discovery.

    Target May 2026

  4. M4 — I can search + save

    Active

    Full-text search, advanced filters, saved searches, and garage/dealership profiles.

    Target Jun 2026

  5. M5 — I can contact the seller

    Active

    Socket.IO real-time chat between buyers and sellers, message persistence, and read receipts.

    Target Jun 2026

  6. M6 — I get notified

    Draft

    Push notifications via FCM/APNS, in-app notification center, and notification preferences.

    Target Jul 2026

  7. M7 — Admins run the place

    Draft

    Admin dashboard with moderation queues, user management, analytics, and content administration.

    Target Jul 2026

  8. M8 — Soft launch

    Draft

    App store submissions, production hardening, load testing, monitoring, and beta release to Turkmenistan market.

    Target Aug 2026

Architecture Decisions

Level 2 bounded contexts with use-cases

NestJS on Fastify with pure TypeScript domain layer, one use-case per file, and ports/adapters for cross-context communication.

Stock NestJS feature folders led to bloated services and tight coupling. Bounded contexts with explicit domain/application/infrastructure/presentation layers enforce clear boundaries between identity, catalog, listings, subscriptions, conversations, notifications, content, reports, and admin.

Stack selection: NestJS + Prisma + Next.js + Expo

NestJS API, Prisma ORM, Next.js web, and Expo mobile. Replaced Flutter, Sequelize, and Firebase.

Prisma provides type-safe migrations and a single schema source of truth. Expo + React Native gives better ecosystem access than Flutter for the team. Next.js handles both public site and admin dashboard.

Turborepo + pnpm workspaces

Monorepo with 7 apps and 5 shared packages. Rejected Nx, Lerna, and multi-repo.

Shared packages for Prisma schema (db), Zod contracts, UI tokens, tsconfig presets, and ESLint config. All clients consume the same types and validation rules.

Fully air-gapped hosting in Turkmenistan

Self-hosted Ubuntu servers with Docker Compose, Caddy, and no cloud dependencies. Docker tarballs shipped via SCP/USB.

Internet connectivity inside Turkmenistan is unreliable and foreign cloud providers have latency and compliance issues. Topology C: build on CI, bundle images, transfer to local servers.

Phone OTP + JWT auth with custom SMS gateway

OTP via fleet of Android phones running a Kotlin agent. JWT access tokens + bcrypt-hashed refresh tokens. No password database.

Local SMS providers are expensive and unreliable. A fleet of 5–20 Android phones with SIM cards provides a cost-effective, controllable OTP delivery system. Multi-device sessions capped at 10 with FIFO eviction.

MinIO + Sharp media pipeline

Self-hosted MinIO for S3-compatible object storage. Sharp for server-side variant generation. Client-side compression mandatory.

Replaces Firebase Storage. MinIO runs locally alongside the API. Sharp generates thumbnails and compressed variants on upload. Client compresses before upload to save bandwidth.

Pipeline Evidence

Mobile test suite — 7 files passing

Testing

Expo mobile app unit and integration tests covering components, hooks, and API client behavior.

API test suite — 63 specs passing

Testing

Domain, application, and e2e layers tested. NestJS service layer, controller, and utility tests pass consistently in CI.

Docker multi-stage build — 5 services

Docker

API, web, admin, worker, and SMS-gateway containers build successfully with production optimizations and multi-stage Dockerfiles.

CI pipeline — GitHub Actions with self-hosted runner

CI/CD

Lint, typecheck, test, and build steps run on every push and PR via self-hosted runner labeled tm-proxy.

Prisma schema — 8 migrations applied

architecture

Type-safe Prisma client generated from a single schema in packages/db. 8 migrations applied covering identity, catalog, listings, media, and exchange rates.

AutoTM

Listings mobile wizard — 7-step flow with upload state machine

Expo mobile app listing creation wizard with step validation, media upload staging, and catalog integration. Prisma schema extended with ListingDraft, ListingMedia, and ExchangeRate. Known gaps documented: autosave edge cases, orphan cleanup, public listing-detail route pending.

AutoTM

Catalog context with trilingual seed data

Read endpoints for Brand, Model, Color, BodyType, Region, City, EngineType, Transmission, and DriveType. Seed data in Turkmen, Russian, and English. FX rates table for currency conversion.

AutoTM

Identity context shipped — OTP login, JWT sessions, multi-device cap

Phone OTP authentication via custom SMS gateway with 5-device fleet. JWT access tokens and bcrypt-hashed refresh tokens. Multi-device session cap at 10 with FIFO eviction. Rate limiting on OTP endpoints. Full domain + application + e2e test coverage.

AutoTM

Prisma schema and 8 migrations applied

Single schema file in packages/db covering identity, catalog, listings, media, exchange rates, and subscriptions. Type-safe client generated and consumed by API, worker, and SMS-gateway apps.

AutoTM

Turborepo monorepo scaffold with 7 apps and 5 packages

Turbo.json pipeline configured with lint, typecheck, test, and build stages. Shared packages established for Prisma schema, Zod contracts, UI tokens, tsconfig, and ESLint config. Docker Compose dev environment with Postgres 16, Redis 7, and MinIO.