Tenant Architecture
Multi-Tenant Design
Section titled “Multi-Tenant Design”Each tenant is an isolated Medusa instance with its own:
- Docker containers (backend + storefront + Redis)
- PostgreSQL database (
magic_b2b_{tenant}) - Sales channel and publishable API key
- Branding (colors, logo, favicon)
- Domain and Nginx routing
They all share:
- The same source codebase (from
magic_development) - The same PostgreSQL server instance
- The same PIM product data (read-only mount)
- The same product images (
/mnt/data/pim_data/)
Brand System
Section titled “Brand System”The storefront detects the brand from the domain and applies per-brand configuration:
interface BrandConfig { slug: string // "brinxx", "moxz", etc. domain: string // Primary domain type: 'owner' | 'brand' | 'whitelabel' logo: string // Logo image path primaryColor: string // Brand accent color salesChannelId: string // Medusa sales channel publishableKey: string // Medusa API key // ... contact info, social, SEO}Configured Brands
Section titled “Configured Brands”| Brand | Domain | Primary Color | Type |
|---|---|---|---|
| Brinxx | brinxx.magiceverse.online | #CF0D65 (pink) | owner |
| Magiceverse | development.magiceverse.online | #8338B6 (purple) | owner |
| Default | default.magiceverse.online | #4caf50 (green) | owner |
| MOXZ | moxz.magiceverse.online | #CF0D65 (pink) | brand |
| Promotionalz | promotionalz.magiceverse.online | #e91e63 (pink) | brand |
Domain Detection Order
Section titled “Domain Detection Order”- Exact domain match (highest priority)
- Check
additionalDomainsarray - Domain starts with brand slug
- Fallback to magiceverse (demo store)
CSS Variable Injection
Section titled “CSS Variable Injection”The BrandProvider injects CSS custom properties at runtime:
:root { --brand-primary: #CF0D65; --brand-secondary: #1a1a1a; --brand-accent: #e91e63; --brand-header-bg: #CF0D65; --brand-header-text: #ffffff; --brand-footer-bg: #1a1a1a; --brand-footer-text: #ffffff;}Theme System
Section titled “Theme System”Three built-in themes loaded via NEXT_PUBLIC_THEME:
src/themes/├── base/variables.css # Base CSS variables├── brinxx/override.css # Pink/magenta theme└── default/override.css # Indigo/green themePer-Tenant Docker Configuration
Section titled “Per-Tenant Docker Configuration”Each tenant’s docker-compose.yml follows this pattern:
services: redis: image: redis:7-alpine ports: ["63XX:6379"] healthcheck: redis-cli ping
backend: build: args: MEDUSA_BACKEND_URL: https://admin-{tenant}.magiceverse.online ports: ["40XX:3002", "70XX:7992"] environment: DATABASE_URL: postgres://...@host.docker.internal:5432/magic_b2b_{tenant} APLT_DB_NAME: magic_b2b_{tenant} MEDUSA_BACKEND_URL: https://admin-{tenant}.magiceverse.online volumes: - /mnt/data/pim_data:/mnt/data/pim_data:ro - /mnt/data/magic_omniverse:/mnt/data/magic_omniverse extra_hosts: - "host.docker.internal:host-gateway"
storefront: ports: ["100XX:9002"] environment: NEXT_PUBLIC_THEME: {tenant} NEXT_PUBLIC_MEDUSA_BACKEND_URL: https://admin-{tenant}.magiceverse.online NEXT_PUBLIC_BASE_URL: https://{tenant}.magiceverse.onlineCORS Configuration
Section titled “CORS Configuration”Each tenant whitelists its specific domains. Example for Brinxx:
STORE_CORS=http://localhost:10040,https://brinxx.magiceverse.online, https://admin-brinxx.magiceverse.online, https://moxz.magiceverse.online, https://promotionalz.magiceverse.onlineAuthentication & Login
Section titled “Authentication & Login”Admin Login URLs
Section titled “Admin Login URLs”| Tenant | Admin Login URL | Database |
|---|---|---|
| PIM (Master) | https://magic-pimadmin.magiceverse.online/app/login | magic_pim |
| Development | https://admin-development.magiceverse.online/app/login | magic_b2b_development |
| Default | https://admin-default.magiceverse.online/app/login | magic_b2b_default |
| Demo | https://admin-demo.magiceverse.online/app/login | magic_b2b_demo |
| Brinxx | https://admin-brinxx.magiceverse.online/app/login | magic_b2b_brinxx |
| Spranz | https://admin-spranz.magiceverse.online/app/login | magic_b2b_spranz |
| Jodasign | https://admin-jodasign.magiceverse.online/app/login | magic_b2b_jodasign |
| Logohorloge | https://admin-logohorloge.magiceverse.online/app/login | magic_b2b_logohorloge |
| De Sluis | https://admin-desluis.magiceverse.online/app/login | magic_b2b_desluis |
| Bovisales | https://admin-bovisales.magiceverse.online/app/login | magic_b2b_bovisales |
| Master | https://admin-master.magiceverse.online/app/login | master_magic |
How Authentication Works
Section titled “How Authentication Works”- Each Medusa instance stores user credentials in its own
auth_identity/provider_identitytables - The
emailpassprovider handles email + password login - Password reset tokens are JWT tokens signed with the tenant’s
JWT_SECRET - The reset email link points to the same tenant’s admin URL (via
MEDUSA_BACKEND_URL) - A user can exist in multiple tenant databases with different passwords
Password Reset Flow
Section titled “Password Reset Flow”User clicks "Forgot password" on admin-{tenant}.magiceverse.online → POST /auth/user/emailpass/reset-password { identifier: email } → generateResetPasswordTokenWorkflow creates JWT token → Email sent with link to admin-{tenant}/app/reset-password?token=... → User clicks link, enters new password → POST /auth/user/emailpass/update { password: "..." } (with JWT in header) → Password updated in tenant's provider_identity table → User logs in at admin-{tenant}/app/loginDatabase Isolation
Section titled “Database Isolation”Each tenant connects to the same PostgreSQL server but uses a separate database:
Connection Pattern:postgres://postgres:<your-db-password>@host.docker.internal:5432/magic_b2b_{tenant}
Databases:├── magic_b2b_development├── magic_b2b_brinxx├── magic_b2b_default├── magic_b2b_demo├── magic_b2b_desluis├── magic_b2b_jodasign├── magic_b2b_logohorloge├── magic_b2b_bovisales├── magic_b2b_spranz└── master_magicEach database contains:
- Standard Medusa tables (products, carts, orders, customers)
- APLT tables (quotations, invoices, brands, CMS settings, access control)