Skip to content

Multi-region

Regions represent geographic or logical deployment zones. Each region can have its own database URL, control plane URL, and lifecycle status. Tenants can be migrated between regions to satisfy compliance requirements or optimize latency.

interface Region {
id: string;
display_name: string; // "EU West (Frankfurt)"
slug: string; // "eu_west"
control_plane_url: string; // "https://eu.stratum.example.com" (http or https, without credentials)
database_url: string; // stored but never exposed in API responses
is_primary: boolean;
status: "active" | "draining" | "inactive";
metadata: Record<string, unknown>;
}
active ──> draining ──> inactive
Status Description
active Accepting new tenants, serving traffic
draining No new tenants, existing tenants being migrated out
inactive Empty, can be safely deleted
const eu = await stratum.createRegion({
display_name: "EU West (Frankfurt)",
slug: "eu_west",
control_plane_url: "https://eu.stratum.example.com",
is_primary: false,
metadata: { provider: "aws", az: "eu-west-1" },
});
const us = await stratum.createRegion({
display_name: "US East (Virginia)",
slug: "us_east",
is_primary: true,
});

Via the API:

curl -X POST http://localhost:3001/api/v1/regions \
-H "X-API-Key: YOUR_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"display_name": "EU West (Frankfurt)",
"slug": "eu_west",
"control_plane_url": "https://eu.stratum.example.com",
"is_primary": false,
"metadata": {"provider": "aws", "az": "eu-west-1"}
}'

Move a tenant to a different region:

await stratum.migrateRegion("tenant-uuid", eu.id);

Via the API:

curl -X POST http://localhost:3001/api/v1/tenants/TENANT_UUID/migrate-region \
-H "X-API-Key: YOUR_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"region_id": "EU_REGION_UUID"}'

The migration checks:

  • Tenant exists and is not archived
  • Target region exists
  • Target region status is active (not draining or inactive)

If the target region is not active, the migration throws RegionNotActiveError. The control plane answers 409 with the code REGION_NOT_ACTIVE.

curl http://localhost:3001/api/v1/regions \
-H "X-API-Key: YOUR_KEY"

Drain a region before decommissioning:

curl -X PATCH http://localhost:3001/api/v1/regions/REGION_UUID \
-H "X-API-Key: YOUR_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "draining"}'

If active tenants are still assigned to the region, the deletion fails with RegionInUseError. The control plane answers 409 with the code REGION_IN_USE:

curl -X DELETE http://localhost:3001/api/v1/regions/REGION_UUID \
-H "X-API-Key: YOUR_ADMIN_KEY"

For GDPR data residency compliance, create regions per jurisdiction and assign tenants accordingly:

// Create jurisdiction-specific regions
const eu = await stratum.createRegion({
display_name: "EU (Frankfurt)",
slug: "eu_frankfurt",
});
const us = await stratum.createRegion({
display_name: "US East (Virginia)",
slug: "us_east",
is_primary: true,
});
// EU customer data stays in EU
await stratum.migrateRegion("eu-tenant-uuid", eu.id);
// US customer data stays in US
await stratum.migrateRegion("us-tenant-uuid", us.id);

Region slugs follow the same rules as tenant slugs:

  • Lowercase letters, digits, and underscores only
  • Must start with a letter
  • Maximum 63 characters
  • Pattern: ^[a-z][a-z0-9_]{0,62}$
Route Required Scope
GET /api/v1/regions read
POST /api/v1/regions write
PATCH /api/v1/regions/:id write
DELETE /api/v1/regions/:id write
POST /api/v1/tenants/:id/migrate-region admin

The database_url field is stored internally but never exposed in API responses. This prevents accidental leakage of database credentials through the API.