A platform for vehicle owners in the Sri Lankan market to manage their vehicles and see the
complete, verified service history in one place — garages and service centers log service records
against a vehicle, and owners can share that history with dealers so used and refurbished vehicles
can be sold without the usual paperwork hassle. Architected as a multi-tenant SaaS. This is
Milestone 1: Core Domain MVP — tenancy, auth, Postgres Row-Level Security, vehicles, service
records, inventory, cross-tenant consent, quotes/invoices/payments, a type/condition-aware vehicle
taxonomy console, and an internal ops (/platform) section. See
Roadmap below for what's intentionally out of scope for this milestone.
Further reading: docs/ARCHITECTURE.md (system overview & module map),
docs/ER_DIAGRAM.md (full data model), docs/GAP_ANALYSIS.md
(verified gaps for future development), docs/adr/ (architectural decision records),
NEXT.md (product roadmap toward a vehicle passport).
- apps/api — NestJS (TypeScript), Prisma 7 (driver adapters,
@prisma/adapter-pg), PostgreSQL 15 with Row-Level Security, JWT auth (Argon2id), XState v5 statecharts for every lifecycle entity. - apps/web — Next.js 14 (App Router), TanStack Query, react-hook-form + zod, Tailwind. Includes
the internal ops console at
/platform(tenant search/management, vehicle-taxonomy console), gated by aRoleScope.PLATFORMrole rather than a separate app — see ADR 0006. - packages/shared — zod DTOs, permission catalog, XState machines, country pack — imported by
both the API (enforcement) and the web app (UI
can(state, event)checks), so they can never disagree.
- Node.js 20+, npm 10+
- Docker (Postgres 15 + Redis via
docker-compose.yml; the full stack can also run entirely in Docker — see Running fully in Docker below)
# 1. Install dependencies (workspace root)
npm install
# 2. Start Postgres + Redis
docker compose up -d
# 3. Copy env files
cp apps/api/.env.example apps/api/.env # already has working local-dev defaults
# 4. Build packages/shared (apps/api and apps/web import its compiled output, not raw source)
npm run build --workspace packages/shared
# 5. Apply migrations (creates the carcare_app RLS role, RLS policies, partitions, etc.)
cd apps/api
npx prisma migrate deploy
# 6. Seed demo data (run ONCE against a freshly migrated, empty database — see the doc-comment
# at the top of apps/api/prisma/seed.ts; it is not safe to re-run against a populated DB)
npm run db:seed
# 7. Back at the repo root, start everything
cd ../..
npm run dev- API: http://localhost:3000 (
/healthz, Swagger at/docs) - Web: http://localhost:8080 — internal ops lives at
/platformwithin this same app, visible in the sidebar for any account that's been granted aRoleScope.PLATFORMrole (see ADR 0005 for the auth model and ADR 0006 for why it's part ofapps/web, not a separate app):The dev seed already grantscd apps/api npm run db:sync-permissions # creates the platform_admin/platform_support built-in roles npm run db:grant-platform-role -- <email> platform_admin
platform_admintoadmin@autocare.lk(below) after running the two commands above.
| Role | Workspace | |
|---|---|---|
admin@autocare.lk |
station_admin (+ platform_admin — see above) |
AutoCare Group — Colombo 05 |
advisor@autocare.lk |
service_advisor | AutoCare Group — Colombo 05 |
tech@autocare.lk |
technician | AutoCare Group — Colombo 05 |
admin@primeauto.lk |
dealer_admin | Prime Auto Traders |
owner1@example.com |
owner_admin | My Vehicles (Corolla, Alto) |
owner2@example.com |
owner_admin | My Vehicles (Bajaj Pulsar) |
For a quick end-to-end run without installing Node locally, docker-compose.yml also builds and
runs api and web (in addition to db/redis):
docker compose up -d --build- API: http://localhost:3000 (
/healthz, Swagger at/docs) - Web: http://localhost:8080 (includes
/platform)
The api container's entrypoint runs prisma migrate deploy on every start (idempotent — safe to
re-run), then starts the server. Migrations, RLS policies, and partitions are applied automatically;
seeding is not run automatically (seed.ts is not idempotent — see its doc-comment), so seed
once explicitly:
docker compose run --rm api npm run db:seedNotes:
webis built withNEXT_PUBLIC_API_URL=http://localhost:3000baked in at image build time (Next.js inlinesNEXT_PUBLIC_*vars duringnext build), since that variable is read by the browser, not by the container — it must be a host-reachable URL, not the Docker-internalhttp://api:3000. Server-to-server config (DATABASE_URL,APP_DATABASE_URL,REDIS_URL) uses the Docker-internal service names (db,redis) instead.- This is a from-source build (multi-stage
Dockerfileper app, fullnode_modulescopied into the runtime image — not a pruned/standalone Next.js output), intended for local end-to-end testing and demos, not hardened for production deployment. - The local
npm run devworkflow above is still the faster loop for active development (hot reload); use whichever fits — both point at the samedb/rediscontainers, so don't run both against the same Postgres volume with the app-level (non-Docker)api/webat the same time as their Docker equivalents (port collisions on 3000/8080).
cd apps/api
npm test # unit tests — includes tests that hit the real Postgres instanceDocker must be up and migrated (steps 2 + 5 above) before running tests that exercise RLS, document numbering concurrency, or the stock ledger against a real database.
apps/
api/ NestJS backend
prisma/
schema.prisma Prisma-managed models (normalized, no JSON columns)
migrations/ baseline + hand-written SQL layered on top (RLS, partitioning,
generated columns, materialized view — see docs/adr/0002)
seed.ts demo data (run once, see doc-comment)
src/
core/ cross-cutting: prisma, tenant context, auth, rbac, state-machine,
audit, documents (numbering/tax)
modules/ one directory per bounded context (see docs/adr/0003)
web/ Next.js customer-facing app — also hosts /platform, the internal ops
section (see docs/adr/0006), gated by a RoleScope.PLATFORM role, not a
separate app
packages/
shared/ zod DTOs, XState machines, permission catalog, country pack
ui/ design-system package (not yet populated this milestone)
infrastructure/ reserved for future Terraform/Helm — not used yet, see its README
docs/
ARCHITECTURE.md system overview, module map, request lifecycle
ER_DIAGRAM.md full data model (all 43 Prisma models, grouped by bounded context)
GAP_ANALYSIS.md verified gaps for future development
adr/ architecture decision records
This milestone intentionally does not include (see docs/adr/* "Consequences" sections for the
reasoning behind several of these):
- SSO (OIDC/SAML), SCIM, MFA/TOTP, device/session management UI
- Terraform/Helm/Kubernetes manifests; CI beyond basic lint/test/migrate-check
- Stripe billing/plan enforcement; the feature-flag management subsystem (no
feature_flagstable yet — it'll be added when the feature is actually built, not stubbed ahead of time) - Webhook delivery infrastructure (HMAC signing/retry) and API key management (same reasoning —
no
webhooks/api_keystables yet) - Branded PDF rendering (S3 + headless Chromium/react-pdf) for quotes/invoices/credit
notes/receipts — they have a nullable
pdf_urlcolumn ready for it - Broader job-queue coverage —
@nestjs/bullmq(against the same Redisdocker-compose.ymlprovisions) is already wired for outbound mail (core/mail/) and the debouncedworkspace_revenue_summaryrefresh (core/revenue-summary/; seedocs/PERFORMANCE.md§1.1), but most other side effects (notifications, audit) still happen synchronously in the same DB transaction as the triggering mutation. Partition maintenance (npm run db:ensure-partitions --workspace apps/api) is a script meant for a cron, not a live in-process scheduler. - ClickHouse/DuckDB analytics sink, report builder, scheduled report emails, CSV/XLSX export
- PgBouncer, read replicas; partitioning is limited to
service_recordsandaudit_events(state_transitionsstays a normal BRIN-indexed table) - next-intl i18n wiring (folder structure left ready; only the
LKcountry pack / English copy exists) - The exhaustive worldwide vehicle catalog from the original spec — the seed loads a representative ~12-make subset spanning the type-branches (car/motorcycle/three-wheeler/van/EV)
- A real internal-ops identity system beyond individual
RoleScope.PLATFORMaccounts (SSO, audited impersonation, an audit trail for admin actions) — seedocs/adr/0005-platform-role-scope.md - Tenant-level (cross-workspace) admin roles are modeled in the permission catalog but not yet
independently assignable — see
docs/adr/0001-tenancy-model.md
This list is the maintainers' own curated "intentionally out of scope" set. For a fuller,
codebase-verified inventory (including things not intentionally deferred — e.g. wide-open CORS,
missing security headers, zero frontend test coverage, unaudited admin actions — that should be
closed rather than tracked as roadmap), see docs/GAP_ANALYSIS.md.