Subdomain Provisioning
Each tenant gets a dedicated subdomain per assigned app. Provisioning is handled exclusively by dot-portal-api.
Subdomain Pattern
{tenant-slug}.{app-base-domain}
Examples:
acme-corp.foot-factory.dotevolve.net
acme-corp.cos.dotevolve.net
The Demo Tenant (dotevolve) uses the App Base Domain directly and is never provisioned.
Provisioning Flow
flowchart TD
A[Tenant created / payment confirmed] --> B[provisionTenantApps]
B --> C{For each app slug}
C --> D[Vercel API\nRegister subdomain]
C --> E[Cloudflare API\nCreate CNAME record]
D --> F{Both succeed?}
E --> F
F -->|Yes| G[Add origin to app CORS\nSet domain_status: active]
F -->|No| H[Set domain_status: pending\nReturn 207 Partial Content]
G --> I[Create alert: subdomain_provisioned]
H --> J[Create alert: subdomain_failed]
Retry
A pending subdomain can be retried from the Admin Portal:
POST /api/v1/tenants/:tenantId/apps/:appSlug/retry-domain
Deprovisioning
When a tenant is deleted, all subdomains are deprovisioned:
- Remove subdomain from Vercel project
- Delete Cloudflare CNAME record
- Remove origin from app CORS policy
- Failures are logged but do not abort the deletion
- 404 responses from Vercel/Cloudflare are treated as already-removed
App Registry
The App Registry is an environment-level config in dot-portal-api:
{
'foot-factory': {
slug: 'foot-factory',
baseDomain: 'foot-factory.dotevolve.net',
vercelProjectId: process.env.FOOT_FACTORY_VERCEL_PROJECT_ID,
corsEndpoint: process.env.FOOT_FACTORY_CORS_ENDPOINT,
allowSelfSignup: true,
},
'dot-cos': {
slug: 'dot-cos',
baseDomain: 'cos.dotevolve.net',
vercelProjectId: process.env.DOT_COS_VERCEL_PROJECT_ID,
corsEndpoint: process.env.DOT_COS_CORS_ENDPOINT,
allowSelfSignup: true,
},
}
CORS Origin Management
dot-portal-api exposes an internal endpoint that returns all active domains for an app:
GET /api/v1/internal/cors-origins/:appSlug
x-service-secret: <secret>
dot-foot-factory-api fetches this list on startup and refreshes it periodically, replacing the old MongoDB GlobalConfig collection.