# MotoVaultPro Architecture Overview ## Architecture Summary MotoVaultPro is a single-tenant vehicle management application built with a **9-container Docker-first architecture** (6 application + 3 logging). All development and deployment occurs in production-configured containers with no local installation dependencies. ### Application Containers (6) 1. **Traefik** - Reverse proxy and service discovery 2. **Frontend** - React SPA with Vite (Node.js 20 + nginx) 3. **Backend** - Node.js API with Fastify (Node.js 20) - includes platform feature module 4. **OCR** - Python-based document text extraction 5. **PostgreSQL** - Primary database (PostgreSQL 18) 6. **Redis** - Caching layer (Redis 8) ### Logging Containers (3) 7. **Loki** - Centralized log storage (30-day retention) 8. **Alloy** - Log collection and forwarding 9. **Grafana** - Log querying and visualization ### Key Architectural Principles - **Production-Only**: All services use production builds and configuration - **Docker-First**: Zero local installations, all work happens in containers - **Single-Tenant**: Application serves a single user/tenant - **User-Scoped Data**: All application data isolated by `user_id` - **Feature Capsule Organization**: Self-contained feature modules in `backend/src/features/` ## Container Interaction Diagram ``` Internet/Users | v +----------------+------------------+ | Traefik | | (Reverse Proxy & TLS) | | Ports: 80, 443, 8080 | +----------------+------------------+ | +---------------------+---------------------+ | | v v +------------------+ +--------------------+ | Frontend | | Backend | | React + Vite | | Node.js + Fastify | | Port: 3000 | | Port: 3001 | +------------------+ +--------------------+ (frontend network) (backend network) | | +---------------------------------+ | | v v +---------------+ +-------------+ | PostgreSQL | | Redis | | Port: 5432 | | Port: 6379 | +---------------+ +-------------+ (database network - internal) ``` ## Network Topology ### Frontend Network - **Purpose**: Public traffic routing - **Type**: Bridge (non-internal) - **Connected Services**: Traefik, Frontend - **Access**: External (for Traefik public endpoints) ### Backend Network - **Purpose**: API service communication - **Type**: Bridge (non-internal) - **Connected Services**: Traefik, Backend - **Access**: External (requires Auth0 JWT validation) ### Database Network - **Purpose**: Data layer isolation - **Type**: Bridge (internal) - **Connected Services**: Backend, PostgreSQL, Redis - **Access**: Internal only, no external exposure ## Request Flow ### User Request Flow ``` 1. User → HTTPS Request 2. Traefik → TLS Termination + Route Selection - Domain: motovaultpro.com or www.motovaultpro.com - Path: / → Frontend (priority 10) - Path: /api → Backend (priority 20) - Path: /api/platform/* → Backend (platform feature module) 3. Frontend/Backend → Process Request 4. Response → Traefik → User ``` ### Authentication Flow ``` 1. User → Frontend → Auth0 (Login) 2. Auth0 → JWT Token → Frontend 3. Frontend → Backend (with Authorization: Bearer ) 4. Backend → Validate JWT via Auth0 JWKS 5. Backend → Extract user_id from JWT (request.user.sub) 6. Backend → Execute user-scoped queries 7. Backend → Response → Frontend ``` ### Backend Platform Flow ``` 1. Backend Feature → Calls platform domain services (TypeScript in-process) 2. Platform module → Query PostgreSQL (vehicles schema) 3. Platform module → Check/Update Redis cache 4. Platform response → Returned to caller (vehicles, frontend proxies, etc.) ``` ### Data Access Flow ``` 1. Backend/Platform → PostgreSQL Query - Database: motovaultpro - Connection: Internal via database network 2. Backend/Platform → Redis Cache Check - Connection: Internal via database network 3. Response → Backend → Frontend ``` ## Service Details ### Traefik (Reverse Proxy) - **Image**: traefik:v3.0 - **Purpose**: Service discovery, TLS termination, load balancing - **Ports**: - 80 (HTTP) - 443 (HTTPS) - 8080 (Dashboard) - **Networks**: frontend, backend - **Health Check**: `traefik healthcheck` (30s interval) - **Configuration**: - `/etc/traefik/traefik.yml` - Main config - `/etc/traefik/middleware.yml` - Middleware rules - Dynamic routing via Docker labels ### Frontend (React SPA) - **Technology**: React 18 + Vite + TypeScript - **Build**: Multi-stage Dockerfile (node:lts-alpine → nginx:alpine) - **Port**: 3000 (internal) - **Networks**: frontend - **Dependencies**: Backend (API calls) - **Health Check**: `curl https://motovaultpro.com` (30s interval) - **Environment Variables**: - `VITE_AUTH0_DOMAIN` - Auth0 tenant - `VITE_AUTH0_CLIENT_ID` - Auth0 application ID - `VITE_AUTH0_AUDIENCE` - API identifier - `VITE_API_BASE_URL` - Backend API path (/api) - **Routing**: Traefik routes non-/api paths to frontend ### Backend (Node.js API) - **Technology**: Node.js 20 + Fastify + TypeScript - **Build**: Production build in node:lts-alpine - **Port**: 3001 (internal) - **Networks**: backend, database - **Dependencies**: PostgreSQL, Redis, Platform - **Health Check**: `https://motovaultpro.com/api/health` (30s interval) - **Configuration**: - `/app/config/production.yml` - Main config - `/app/config/shared.yml` - Shared config - `/run/secrets/*` - Secret files (K8s pattern) - **Secrets**: - `postgres-password` - Database password - `platform-vehicles-api-key` - Platform service auth - `auth0-client-secret` - Auth0 M2M secret - `google-maps-api-key` - Maps API key - **Storage**: `/app/data/documents` - Document uploads ### PostgreSQL (Primary Database) - **Image**: postgres:15-alpine - **Database**: motovaultpro - **Port**: 5432 (exposed for development) - **Networks**: database (internal) - **User**: postgres - **Password**: File-based secret (`/run/secrets/postgres-password`) - **Health Check**: `pg_isready -U postgres` (10s interval) - **Storage**: Named volume `mvp_postgres_data` - **Schemas**: - `public` - Application data (user-scoped) - `vehicles` - Platform vehicle data (shared) ### Redis (Cache Layer) - **Image**: redis:7-alpine - **Port**: 6379 (exposed for development) - **Networks**: database (internal) - **Persistence**: AOF enabled (`--appendonly yes`) - **Health Check**: `redis-cli ping` (10s interval) - **Storage**: Named volume `mvp_redis_data` - **Usage**: - Vehicle data caching (year-based hierarchy) - Session storage (future) - Rate limiting (future) ### Platform Module (Vehicle Data) - **Technology**: TypeScript feature capsule inside `backend/src/features/platform/` - **Runtime**: Shares the `mvp-backend` Fastify container - **Dependencies**: PostgreSQL, Redis (accessed via backend connection pool) - **Deployment**: Bundled with backend build; no separate container or port - **Health**: Covered by backend `/health` endpoint and feature-specific logs - **Configuration**: - `backend/src/features/platform/domain/*.ts` - Business logic - `backend/src/features/platform/data/*.ts` - Database queries - **Secrets**: - Reuses backend secrets (PostgreSQL, Auth0, etc.) - **Purpose**: - Vehicle hierarchical data (years, makes, models, trims, engines) - VIN decoding (Planned/Future - not yet implemented) - Standardized vehicle information ## Platform Module Integration ### Current Architecture Platform capabilities now run **in-process** within the backend as a feature capsule. This delivers: - **Shared Language & Tooling**: TypeScript across the stack - **Centralized Caching**: Reuses backend Redis helpers for year-based lookups - **Simplified Operations**: No extra container to build, scale, or monitor ### Shared Infrastructure - **Database**: Uses the backend connection pool against `mvp-postgres` - **Cache**: Shares the backend Redis client (`mvp-redis`) - **Runtime**: Packaged with the `mvp-backend` container and Fastify plugins - **Secrets**: Relies on the same secret files as the backend (PostgreSQL, Auth0, etc.) ### Communication Pattern ```typescript // Vehicles feature pulling platform data via domain services const platformService = new PlatformVehicleDataService({ db: postgresPool, cache: redisClient }); const makes = await platformService.getMakes({ year: 2024 }); ``` ### Ongoing Work - Continue migrating remaining FastAPI utilities into TypeScript where needed - Expand `/api/platform/*` endpoints as new lookup types are added - Monitor backend logs for `platform` namespace entries to verify health ## Feature Capsule Architecture ### Organization Pattern Features are **self-contained modules** within the backend service at `backend/src/features/[name]/`: ``` backend/src/features/ ├── admin/ # Admin role management ├── auth/ # User authentication ├── backup/ # Database backup/restore ├── documents/ # Document storage ├── fuel-logs/ # Fuel tracking ├── maintenance/ # Service records and schedules ├── notifications/ # Email and toast notifications ├── onboarding/ # User onboarding flow ├── platform/ # Vehicle data + VIN decoding ├── stations/ # Gas station locations ├── terms-agreement/ # T&C acceptance audit ├── user-export/ # GDPR data export ├── user-preferences/ # User settings ├── user-profile/ # User profile management └── vehicles/ # Vehicle management ``` ### Feature Structure Each feature follows a standardized structure: ``` feature-name/ ├── README.md # Feature documentation ├── api/ # Route handlers ├── domain/ # Business logic ├── data/ # Data access layer ├── migrations/ # Database migrations ├── tests/ # Unit and integration tests ├── events/ # Event handlers (optional) └── external/ # External service integrations (optional) ``` ### Current Features (15) #### Admin - **Purpose**: Admin role management and oversight - **Tables**: `admin_users`, `admin_audit_logs` - **Endpoints**: Admin CRUD, catalog management, station oversight #### Auth - **Purpose**: User authentication and signup - **Tables**: None (uses Auth0) - **Integration**: Auth0 for identity management - **Endpoints**: Signup, verify email, resend verification #### Backup - **Purpose**: Database and document backup/restore - **Tables**: `backup_schedules`, `backup_history`, `backup_settings` - **Storage**: `/app/data/backups/` - **Endpoints**: Admin backup CRUD, restore, schedules #### Documents - **Purpose**: Document storage and retrieval - **Tables**: `documents` (user-scoped) - **Storage**: Filesystem (`/app/data/documents/`) - **Endpoints**: Upload + download + delete #### Fuel Logs - **Purpose**: Fuel purchase and efficiency tracking - **Tables**: `fuel_logs` (user-scoped) - **Integration**: Stations feature for location data - **Endpoints**: CRUD + analytics #### Maintenance - **Purpose**: Service and maintenance record tracking - **Tables**: `maintenance_logs`, `maintenance_schedules` (user-scoped) - **Integration**: Vehicles feature, notifications - **Endpoints**: CRUD + schedules + reminders #### Notifications - **Purpose**: Email and toast notifications - **Tables**: `email_templates`, `notification_logs` - **Integration**: Resend API for email delivery - **Endpoints**: User summary, admin template management #### Onboarding - **Purpose**: User onboarding after signup - **Tables**: Uses `user_profiles`, `user_preferences` - **Integration**: User profile and preferences features - **Endpoints**: Preferences, complete, status #### Platform - **Purpose**: Vehicle data and VIN decoding - **Tables**: `engines`, `transmissions`, `vehicle_options` - **Integration**: Backend dropdown cascade queries - **Endpoints**: Years, makes, models, trims, engines #### Stations - **Purpose**: Gas station location and pricing - **Tables**: `stations`, `community_stations` - **Integration**: Google Maps API - **Endpoints**: Search + favorites + community reports #### Terms Agreement - **Purpose**: Legal audit trail for T&C acceptance - **Tables**: `terms_agreements` - **Integration**: Created during signup flow - **Endpoints**: None (internal use) #### User Export - **Purpose**: GDPR-compliant data export - **Tables**: None (reads from all user data) - **Storage**: Temporary TAR.GZ archives - **Endpoints**: Export download #### User Preferences - **Purpose**: User preference management - **Tables**: `user_preferences` (user-scoped) - **Endpoints**: GET/PUT preferences #### User Profile - **Purpose**: User profile management - **Tables**: `user_profiles` (user-scoped) - **Endpoints**: GET/PUT profile #### Vehicles - **Purpose**: Vehicle inventory management - **Tables**: `vehicles` (user-scoped) - **Integration**: Platform service for make/model/trim data - **Endpoints**: CRUD + dropdown data + images ## Data Flow ### Authentication via Auth0 JWT ``` 1. Frontend → Auth0 Login 2. Auth0 → JWT Token (contains user_id in 'sub' claim) 3. Frontend → Backend (Authorization: Bearer ) 4. Backend → Validate JWT: - Verify signature via Auth0 JWKS endpoint - Check issuer: https://{AUTH0_DOMAIN}/ - Check audience: {AUTH0_AUDIENCE} 5. Backend → Extract user_id from token.sub 6. Backend → Execute user-scoped queries ``` ### User ID Extraction ```typescript // From auth.plugin.ts await request.jwtVerify(); // Validates and populates request.user const userId = request.user.sub; // Auth0 user ID (e.g., "auth0|123456") ``` ### User-Scoped Data Access All application data is isolated by `user_id`: ```sql -- Example: Get user's vehicles SELECT * FROM vehicles WHERE user_id = $1; -- Example: Create fuel log (user_id inserted) INSERT INTO fuel_logs (user_id, vehicle_id, gallons, cost, ...) VALUES ($1, $2, $3, $4, ...); -- Example: Soft delete (preserves audit trail) UPDATE vehicles SET deleted_at = NOW() WHERE id = $1 AND user_id = $2; ``` ### Data Isolation Strategy - **Foreign Keys**: All user data tables have `user_id` foreign key - **Query Scoping**: All queries filtered by authenticated user's ID - **Soft Deletes**: `deleted_at` timestamp for audit trail - **No Cascade Deletes**: Prevents accidental data loss - **Index Strategy**: Composite indexes on (`user_id`, primary key) ### Cross-Feature Data Flow ``` 1. User creates vehicle (Vehicles feature) 2. User logs fuel purchase (Fuel Logs feature) - References vehicle_id - References station_id (Stations feature) 3. User adds maintenance record (Maintenance feature) - References vehicle_id - Can attach documents (Documents feature) ``` ## Configuration Management ### Kubernetes-Style Patterns The application uses Kubernetes-inspired configuration patterns: **ConfigMaps** (YAML files): - `/app/config/production.yml` - Service-specific config - `/app/config/shared.yml` - Cross-service config **Secrets** (File-based): - `/run/secrets/postgres-password` - Database credentials - `/run/secrets/auth0-client-secret` - OAuth credentials - `/run/secrets/google-maps-api-key` - External API keys ### Environment Variables Used for service references and runtime configuration: ```yaml NODE_ENV: production CONFIG_PATH: /app/config/production.yml SECRETS_DIR: /run/secrets DATABASE_HOST: mvp-postgres REDIS_HOST: mvp-redis ``` ## Development Workflow ### Container Commands (via Makefile) ```bash make start # Start all containers make stop # Stop all containers make restart # Restart all containers make rebuild # Rebuild and restart containers make logs # View all container logs make logs-backend # View backend logs only make clean # Remove containers and volumes ``` ### Health Monitoring ```bash # Check container health docker ps # View specific service logs docker logs mvp-backend -f docker logs mvp-postgres -f # Test health endpoints (via Traefik) curl https://motovaultpro.com/api/health # Backend (includes platform module) # Or from within backend container docker compose exec mvp-backend curl https://motovaultpro.com/api/health ``` ### Database Access ```bash # PostgreSQL shell make db-shell-app # Execute SQL docker exec -it mvp-postgres psql -U postgres -d motovaultpro # View Redis data docker exec -it mvp-redis redis-cli ``` ## Security Architecture ### Authentication & Authorization - **Provider**: Auth0 (OAuth 2.0 / OpenID Connect) - **Token Type**: JWT (JSON Web Token) - **Validation**: JWKS-based signature verification - **Token Cache**: 1 hour TTL for Auth0 public keys - **Issuer Validation**: Prevents token spoofing - **Audience Validation**: API-specific tokens only ### Protected Resources All API endpoints require valid JWT except: - `/health` - Health check (priority routing) - `/api/health` - API health check (bypass auth) ### Data Security - **VIN Handling**: Check digit validation, no VIN in logs - **User Isolation**: Queries filtered by authenticated `user_id` - **Soft Deletes**: Audit trail preservation - **No Cascading Deletes**: Manual data cleanup only - **Encrypted Connections**: PostgreSQL SSL/TLS ### Infrastructure Security - **Non-root Containers**: All services run as non-root users - **Network Isolation**: Internal database network - **Secret Management**: File-based secrets (K8s pattern) - **No Hardcoded Credentials**: Environment and file injection only ## Deployment Strategy ### Production Environment - **Container Orchestration**: Docker Compose (current) → Kubernetes (future) - **Service Discovery**: Traefik with Docker provider - **TLS**: Automatic certificate management via Traefik - **Scaling**: Single instance per service (MVP phase) - **Monitoring**: Container health checks + log aggregation ### Zero-Downtime Deployment ```bash # Build new images make rebuild # Traefik handles traffic routing during restart # Health checks ensure services are ready before traffic ``` ## Observability ### Logging Strategy - **Format**: Structured JSON logging - **Levels**: ERROR, WARN, INFO, DEBUG - **Context**: Request ID, User ID (truncated), Service name - **Aggregation**: Docker logs → Future: ELK/Loki stack ### Health Checks Every service exposes health endpoints: - **Backend**: `GET /health` - Database + Redis connectivity - **Platform**: `GET /health` - Database + Redis connectivity - **Frontend**: Nginx status check - **PostgreSQL**: `pg_isready` - **Redis**: `redis-cli ping` ### Metrics (Future) - Request rate and latency - Database query performance - Cache hit rates - Error rates by endpoint ## Performance Considerations ### Caching Strategy - **Vehicle Data**: Year-based hierarchical caching in Redis - **JWT Keys**: 1-hour cache for Auth0 public keys - **Database Queries**: Indexed by (`user_id`, primary key) ### Database Optimization - **Connection Pooling**: Fastify PostgreSQL plugin - **Prepared Statements**: SQL injection prevention + performance - **Indexes**: Composite indexes on frequently queried columns - **Query Scoping**: All queries filtered by `user_id` for data locality ### Horizontal Scaling (Future) - **Stateless Services**: Backend and Platform can scale horizontally - **Shared State**: PostgreSQL and Redis as centralized state - **Load Balancing**: Traefik distributes traffic across instances ## Related Documentation - Feature Documentation: `backend/src/features/{feature}/README.md` - Platform Architecture: `docs/PLATFORM-SERVICES.md` - Security Details: `docs/SECURITY.md` - Database Schema: `docs/DATABASE-SCHEMA.md` - Testing Guide: `docs/TESTING.md` - Vehicles API: `docs/VEHICLES-API.md` - Development Commands: `Makefile`