Subdomain-Based Tenant Routing Implementation
Overview
This implementation adds multi-tenant support to IntegrateWise via subdomain-based routing. Each tenant lives on its own subdomain (e.g., saas-corp.integratewise.com) and serves a branded landing page. The middleware handles subdomain resolution and tenant context injection, making all downstream code automatically tenant-scoped.
Architecture
1. Schema Extensions (lib/data/types.ts)
Extended the Tenant interface with branding and landing configuration:
interface Tenant {
id: string;
name: string;
slug: string; // DNS-safe subdomain identifier
industry: Industry;
environment: Environment;
branding?: {
logoUrl?: string; // Emoji or URL
primaryColor?: string; // Hex color
backgroundColor?: string; // Hex color
tagline?: string;
};
landing?: {
headline?: string;
subhead?: string;
featuredPluginIds?: string[];
featuredAgentIds?: string[];
};
customDomain?: string; // Optional CNAME support
status: "active" | "suspended" | "provisioning";
}
Extended TenantRepo interface with new methods:
getBySlug(slug)— lookup tenant by subdomain slugupdate(tenantId, updates)— update tenant branding/landing configvalidateSlug(slug)— validate DNS-safe slug and check uniqueness
2. Data Provider Updates (lib/data/in-memory-provider.ts)
- Added reserved slug validation (www, app, api, admin, docs, etc.)
- DNS-safe slug regex validation:
^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ - Seed tenants with branding configuration:
- saas-corp: Blue (#3B82F6), SaaS-focused
- healthtech: Green (#10B981), Healthcare-focused
- fintech-solutions: Purple (#8B5CF6), Financial-focused
- Implemented
InMemoryTenantRepomethods for CRUD and slug validation - Added seed initialization in DataProvider constructor
3. Middleware Enhancement (middleware.ts)
Enhanced middleware to handle both role subdomains (existing) and tenant subdomains (new):
Routing logic:
- Extract subdomain from Host header
- Check if role subdomain (founder, sales, etc.) → rewrite to
/workbench/[role] - Check if reserved subdomain → allow
app, deny others - Look up tenant by slug from seed data
- If found → inject
X-Tenant-SlugandX-Tenant-Idheaders - If not found → rewrite to 404 "workspace not found"
- Local dev support:
*.localhost:3000resolves to tenant subdomains
Reserved subdomains: www, app, api, admin, docs, localhost, staging, dev, mail, smtp, ftp
4. Tenant Context & Providers
TenantProvider (lib/context/tenant-context.tsx):
- React context for tenant state management
- Reads
X-Tenant-Slugfrom request headers - Loads tenant data from DataProvider
- Provides
useTenant()anduseTenantContext()hooks - Handles loading and error states
Tenant Route Group (app/(tenant)/layout.tsx):
- Wraps all tenant-scoped routes with TenantProvider
- Automatically injects tenant context to all children
- All server components inside automatically receive tenant scope
5. Per-Tenant Landing Page
TenantLandingPage (components/tenant/landing-page.tsx):
Modern, polished landing page with:
- Responsive navigation with tenant logo, name, and auth status
- Hero section with customizable headline/subhead and CTA
- Features grid showing 4 key capabilities
- Industry-specific section with stats (templates, integrations, team members)
- Call-to-action section with context-aware messaging
- Footer with links and copyright
Design patterns from v0 templates:
- CSS variables for tenant branding (primary color, background)
- Smooth transitions and hover effects
- Semantic HTML and accessibility
- Mobile-first responsive design
- Auth-aware routing (sign-out → sign-in; onboarded → /work; not-onboarded → /onboarding)
6. Tenant Picker Page
TenantPicker (components/tenant/tenant-picker.tsx):
Workspace selection hub served at app.integratewise.com:
- Lists all active tenants with branding
- Displays tenant logo, name, industry, tagline, status
- Hover effects showing navigation intent
- Status indicator (active/suspended/provisioning)
- Empty state with helpful messaging
- Call-to-action for workspace creation
7. Routes Created
(tenant)/ # Tenant-scoped route group
layout.tsx # TenantProvider wrapper
page.tsx # Per-tenant branded landing
app/ # Reserved subdomain (app.integratewise.com)
page.tsx # Tenant picker
8. Routing Model
integratewise.com → public ecosystem (future: marketplace, docs, etc.)
www.integratewise.com → same as apex
{tenant}.integratewise.com → tenant landing page + app entry
app.integratewise.com → tenant picker (lists workspaces user can access)
*.localhost:3000 → local dev equivalent (tenant.localhost)
Data Flow
- Request arrives → middleware extracts subdomain
- Tenant lookup → middleware finds tenant by slug from DataProvider
- Header injection → middleware injects
X-Tenant-SlugandX-Tenant-Id - Route match → Next.js routes to
(tenant)group - TenantProvider mounts → reads headers and loads tenant from DataProvider
- Context available → all child components access tenant via
useTenant() - Branding applied → CSS variables apply tenant colors/logo
- Auth-aware navigation → page routes based on user session state
Key Features
Branding System
- CSS variables for dynamic theming:
--primary-color,--background-color - Tenant-specific logo, tagline, and visual identity
- Consistent branding across landing page via design tokens
Industry Awareness
- Landing copy customized by industry (SaaS/Healthcare/Fintech)
- Pre-populated section headlines and feature descriptions
- Differentiated messaging for each workspace type
Auth-Aware Routing
- Unsigned users: redirect to sign-in
- Signed-in, not-onboarded: redirect to onboarding
- Signed-in, onboarded: redirect to workspace dashboard
- Each page state has clear CTA
Fallback Defaults
- Tenants without custom branding get clean defaults
- Default colors: blue (#3B82F6)
- Default background: light blue (#F0F9FF)
- Sensible fallback copy and layouts
Seed Data
Three demo tenants ship with the implementation:
| Slug | Name | Industry | Primary Color | Logo |
|---|---|---|---|---|
| saas-corp | SaaS Corp | saas | #3B82F6 | 🚀 |
| healthtech | HealthTech Inc | healthcare | #10B981 | ⚕️ |
| fintech-solutions | Fintech Solutions | fintech | #8B5CF6 | 💰 |
Each includes:
- Custom branding (color, background, tagline)
- Industry-aware landing copy
- Active status
Testing
Local Development
Access tenants via subdomains on localhost:
http://saas-corp.localhost:3000http://healthtech.localhost:3000http://fintech-solutions.localhost:3000http://app.localhost:3000→ tenant picker
Verification Checklist
- ✅ Subdomain resolves to tenant landing page
- ✅ Branding (colors, logo, copy) applied correctly
- ✅ Auth-aware navigation works (signed out → sign-in CTA)
- ✅ Tenant picker shows all active tenants
- ✅ Unknown subdomain returns 404
- ✅ Reserved subdomains (www, api, etc.) blocked
- ✅ Role subdomains still work (founder.localhost → /workbench/founder)
Integration Points
All existing features automatically benefit from tenant scoping:
- DataProvider: All data queries automatically scoped to tenant (via
X-Tenant-Idheader) - Work Router: Resolves capabilities within tenant scope
- Onboarding: Pre-fills tenant context
- Authentication: Sessions remain tenant-aware
Production Considerations
Custom Domains
The customDomain field supports CNAME mappings (e.g., acme.com → acme.integratewise.com). Implementation requires:
- DNS validation flow
- Middleware enhancement to handle custom domain → slug lookup
- SSL certificate provisioning (via Vercel)
Database Integration
Current implementation uses in-memory storage for development. For production:
- Move tenant data to persistent database (Neon PostgreSQL)
- Add caching layer for slug → tenant lookups (Redis or in-process LRU)
- Implement slug availability checking in admin API
- Add tenant creation/deletion endpoints
Admin Interface
Future enhancement for tenant provisioning:
- Create workspace (validate slug, generate branding options)
- Edit branding and landing config
- Manage team members and permissions
- Monitor tenant health and usage
Files Changed
New Files
lib/context/tenant-context.tsx— Tenant context providerlib/tenant-lookup.ts— Tenant lookup utility (optional)components/tenant/landing-page.tsx— Branded landing pagecomponents/tenant/tenant-picker.tsx— Workspace pickerapp/(tenant)/layout.tsx— Tenant route group layoutapp/(tenant)/page.tsx— Tenant landing routeapp/app/page.tsx— Tenant picker route
Modified Files
lib/data/types.ts— Extended Tenant interface and TenantRepolib/data/in-memory-provider.ts— Added seed tenants, validation, new methodsmiddleware.ts— Enhanced for tenant subdomain routing
Performance
- Lazy tenant lookup: Tenants loaded on first request via context provider
- In-memory caching: Seed data cached in DataProvider
- Header-based routing: No additional database queries in middleware
- CSS variable theming: No re-renders for branding application
Security
- DNS-safe slug validation prevents injection attacks
- Reserved subdomain list prevents conflicts
- Header injection via middleware ensures tenant context integrity
- Future: RLS policies on database ensure tenant data isolation