PluginBench
Skill
Review
Audit score 70

multi-tenant-architecture

mblode/agent-skills

How to install multi-tenant-architecture

npx skills add https://github.com/mblode/agent-skills --skill multi-tenant-architecture
Claude Code
Cursor
Windsurf
Cline
Full instructions (SKILL.md)

Source of truth, from mblode/agent-skills.


name: multi-tenant-architecture description: Provides architecture guidance for multi-tenant SaaS platforms on Cloudflare or Vercel. Covers platform choice, domain strategy, tenant identification and isolation, subdomain routing, custom domains and SSL, white-label setup, tenant context propagation, PSL submission, and mapping platform limits to pricing plans. Use when building a multi-tenant application or asking "how do I support multiple tenants", "build a white-label platform", "add custom domains", "route tenants by subdomain", or "map limits to plans". For general app folder structure use define-architecture; for scaffolding a new Next.js repo use scaffold-nextjs.

Multi-Tenant Platform Architecture (Cloudflare or Vercel)

  • IS: domain strategy, tenant identification and isolation, subdomain routing, custom domains, white-label setup, and plan/limit mapping on Cloudflare or Vercel.
  • IS NOT: general app folder structure or module boundaries (use define-architecture), or scaffolding a new repo (use scaffold-nextjs).

Contents

  • Platform dispatch (decide first)
  • Workflow (order matters)
  • Gotchas
  • Output schema
  • Pre-commit checklist
  • Related skills

Platform dispatch (decide first)

SignalsPlatformLoad
Tenants run untrusted or per-tenant code; need code-level isolation; edge-first compute on D1/KV/Durable ObjectsCloudflare (Workers for Platforms, dispatch namespaces)cloudflare-platform.md
All tenants share one Next.js codebase; need ISR, React Server Components, managed deploysVercel (App Router + Middleware)vercel-platform.md, then vercel-domains.md for domains
  • Pick one platform and commit; never mix hosting (hybrid routing complexity compounds).
  • Load only the chosen platform's references unless explicitly comparing.
  • Load psl.md when deciding domain strategy (step 1).
  • Load limits-and-quotas.md before mapping limits to pricing (step 8).
  • agents/openai.yaml is launcher metadata for external runners only; do not load it in normal use.

Workflow (order matters)

  1. Choose domain strategy
  • Dedicated tenant domain, separate from the brand domain, for all subdomains and custom hostnames. Reputation does not isolate: a phishing site on random.acme.com damages the whole domain.
  • Register a separate TLD for tenant workloads (e.g. acme.app for tenants, acme.com for brand).
  • Untrusted content on sibling subdomains: choose PSL submission, record owner plus timeline. Otherwise record No PSL with the cookie-isolation reason. See psl.md.
  • Start PSL early; review takes weeks.
  1. Choose tenant identification strategy (pick one primary; offer custom domain as upgrade path)
  • Subdomain-based: tenant.yourdomain.com. Requires wildcard DNS. Simplest at scale.
  • Custom domain: tenant CNAMEs their own domain to your platform. Best for serious/paying tenants.
  • Path-based: yourdomain.com/tenant-slug. No per-tenant DNS/SSL, but limits branding and complicates cookie isolation.
  1. Define isolation model
  • Cloudflare: per-tenant Workers via dispatch namespaces for untrusted code. Avoid shared-tenant branching unless you fully control code and data.
  • Vercel: shared Next.js app with tenant_id scoping. Middleware resolves tenant from hostname; every query includes tenant context. Postgres RLS for defence-in-depth.
  1. Route traffic deterministically (tenants never control routing or see each other)
  • Cloudflare: platform Worker owns routing: hostname -> tenant id -> dispatch namespace -> tenant Worker. 404 when no mapping.
  • Vercel: Middleware extracts hostname, rewrites to a /domains/[domain] segment; Edge Config for sub-millisecond lookups. 404 when no mapping.
  1. Pass tenant context through the stack (single authority: Middleware or platform Worker; never trust client-supplied identity)
  • Cloudflare: platform Worker resolves the tenant, injects headers/bindings before dispatching to the tenant Worker.
  • Vercel: Middleware sets x-tenant-id, x-tenant-slug, x-tenant-plan on forwarded request headers (not the response). Server Components read via headers(); API routes read from request headers:
    // middleware.ts
    import { NextRequest, NextResponse } from "next/server";
    export function middleware(request: NextRequest) {
      const hostname = request.headers.get("host") ?? "";
      const tenant = hostname.split(".")[0]; // resolve from Edge Config/DB in production
      const requestHeaders = new Headers(request.headers);
      requestHeaders.set("x-tenant-id", tenant);
      return NextResponse.next({ request: { headers: requestHeaders } });
    }
    
  1. Bind only what is needed
  • Cloudflare: least-privilege bindings per tenant (DB/storage/limited platform API), no shared global state. New bindings are explicit changes; redeploy to grant access.
  • Vercel: Edge Config for tenant config (domain mappings, feature flags, plan info). @vercel/sdk for domain management. DB connections scoped by tenant_id, or database-per-tenant (Neon).
  1. Support custom domains and per-tenant static files
  • Provide a DNS target, verify ownership, store the mapping, route by hostname.
  • Cloudflare: Cloudflare for SaaS custom hostnames with managed certs. See cloudflare-platform.md.
  • Vercel: @vercel/sdk for domain CRUD plus automatic Let's Encrypt SSL; wildcard subdomains require Vercel nameservers. See vercel-domains.md.
  • Custom domains shift reputation to the tenant and create natural user segments (casual on platform domain, serious on their own).
  • robots.txt, sitemap.xml, llms.txt must vary by tenant; never serve from /public. Cloudflare: generate in the tenant Worker. Vercel: route handlers under the domain segment (see vercel-platform.md).
  1. Surface limits as plans
  • Map platform limits to pricing tiers; expose in API and UI.
  • No long jobs in requests; use queues or workflows.
  • See limits-and-quotas.md; re-check official docs before final architecture or pricing decisions.
  1. Make the API the product
  • Everything works over HTTP; the UI is for ops, incidents, billing.
  • Platform logic stays in the routing layer (dispatch Worker or Middleware); tenant content serves requests.
  • If it only works in the UI, the platform is leaking.
  1. Extend without breaking boundaries
  • Add queues, workflows, or containers as optional modes. Keep routing explicit and isolation intact.

Gotchas

  • Tenant headers go on the Middleware request, not the response: headers() in Server Components reads forwarded request headers, so use NextResponse.next({ request: { headers } }) or the tenant id never arrives.
  • Don't start path-based if custom domains are on the roadmap: migrating later means URL rewrites, cookie changes, and DNS migration.
  • Never share DB connections across tenants without RLS or tenant_id scoping: one missing WHERE clause leaks another tenant's data.
  • Never block /.well-known/acme-challenge/* with Middleware or redirects: Let's Encrypt HTTP-01 validation fails and custom-domain SSL never issues.
  • Edge Config writes are not instant: propagation takes up to 10 seconds, so a "domain connected" UI reading Edge Config immediately shows stale state.

Output schema

# Multi-tenant architecture

## Platform decision
- Platform: Cloudflare | Vercel
- Why this platform:
- Rejected platform and reason:

## Domain map
- Brand domain:
- Tenant domain:
- Tenant subdomains:
- Custom domains:
- PSL decision: Submit | No PSL
- PSL owner/timeline or No PSL reason:

## Routing matrix
| Host pattern | Resolver | Destination | Unknown tenant behavior |
|---|---|---|---|

## Tenant context flow
- Authority: Middleware | platform Worker
- Propagation:
- Server read path:
- Database/API scoping:

## Isolation model
- Compute isolation:
- Data isolation:
- Config/binding isolation:

## Custom-domain lifecycle
1. DNS target:
2. Ownership verification:
3. Certificate provisioning:
4. Routing activation:
5. Removal/failure path:

## Limits-to-plan table
| Limit | Source URL/date | Free | Pro | Enterprise | Enforcement point |
|---|---|---:|---:|---:|---|

## Validation evidence
| Check | Command/source | Expected result | Result |
|---|---|---|---|

Pre-commit checklist

  • Platform chosen with documented rationale
  • Tenant workloads off the brand domain; PSL decision and timeline set
  • Tenant identification strategy chosen; custom-domain upgrade path defined
  • Isolation model defined: per-tenant Workers (Cloudflare) or shared-app plus RLS (Vercel)
  • Routing authoritative and tenant-blind; dispatch or Middleware handles all traffic
  • Tenant context flows through Middleware/platform Worker only; no client-supplied identity trusted
  • Custom-domain onboarding defined: DNS target, verification, cert provisioning
  • Per-tenant static files (robots.txt, sitemap.xml, llms.txt) served dynamically
  • Limits tied to billing; API parity with UI
  • Limits snapshot refreshed from official docs and dated in planning notes

Evidence commands (run or mark N/A):

CheckEvidence
Tenant context exists at the boundary`rg "x-tenant-id
Tenant routing workscurl -sI -H "Host: tenant.example.com" <local-or-preview-url>
Per-tenant static files are dynamiccurl -s -H "Host: tenant.example.com" <url>/robots.txt and curl -s -H "Host: tenant.example.com" <url>/sitemap.xml
Custom-domain verification path existsAPI route, SDK call, or platform config path in the plan
Platform limits are up to dateofficial Cloudflare/Vercel URLs with access date in the Limits-to-plan table

Related skills

  • define-architecture: folder structure, module contracts, and middleware pipelines for the application itself.
  • scaffold-nextjs: bootstrap the Next.js turborepo before applying these tenancy patterns.
  • optimise-seo: per-tenant sitemaps, canonical URLs, and structured data once routing works.

Related skills

More from mblode/agent-skills and the wider catalog.