# Multi-Tenant Patterns

## Billing by Plan

```typescript
interface Env {
  DISPATCHER: DispatchNamespace
  CUSTOMERS_KV: KVNamespace
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const userWorkerName = new URL(request.url).hostname.split('.')[0]
    const customerPlan = await env.CUSTOMERS_KV.get(userWorkerName)

    const plans = {
      enterprise: { cpuMs: 50, subRequests: 50 },
      pro: { cpuMs: 20, subRequests: 20 },
      free: { cpuMs: 10, subRequests: 5 },
    }
    const limits = plans[customerPlan as keyof typeof plans] || plans.free

    const userWorker = env.DISPATCHER.get(userWorkerName, {}, { limits })
    return await userWorker.fetch(request)
  },
}
```

## Resource Isolation

**Complete isolation:** Create unique resources per customer

- KV namespace per customer
- D1 database per customer
- R2 bucket per customer

```typescript
const bindings = [
  {
    type: 'kv_namespace',
    name: 'USER_KV',
    namespace_id: `customer-${customerId}-kv`,
  },
]
```

## Hostname Routing

### Wildcard Route (Recommended)

Configure `*/*` route on SaaS domain → dispatch Worker

**Benefits:**

- Supports subdomains + custom vanity domains
- No per-route limits (regular Workers limited to 100 routes)
- Programmatic control
- Works with any DNS proxy settings

**Setup:**

1. Cloudflare for SaaS custom hostnames
2. Fallback origin (dummy `A 192.0.2.0` if Worker is origin)
3. DNS CNAME to SaaS domain
4. `*/*` route → dispatch Worker
5. Routing logic in dispatch Worker

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const hostname = new URL(request.url).hostname
    const hostnameData = await env.ROUTING_KV.get(`hostname:${hostname}`, { type: 'json' })

    if (!hostnameData?.workerName) {
      return new Response('Hostname not configured', { status: 404 })
    }

    const userWorker = env.DISPATCHER.get(hostnameData.workerName)
    return await userWorker.fetch(request)
  },
}
```

### Subdomain-Only

1. Wildcard DNS: `*.saas.com` → origin
2. Route: `*.saas.com/*` → dispatch Worker
3. Extract subdomain for routing

### Orange-to-Orange (O2O) Behavior

When customers use Cloudflare and CNAME to your Workers domain:

| Scenario                                | Behavior               | Route Pattern             |
| --------------------------------------- | ---------------------- | ------------------------- |
| Customer not on Cloudflare              | Standard routing       | `*/*` or `*.domain.com/*` |
| Customer on Cloudflare (proxied CNAME)  | Invokes Worker at edge | `*/*` required            |
| Customer on Cloudflare (DNS-only CNAME) | Standard routing       | Any route works           |

**Recommendation:** Always use `*/*` wildcard for consistent O2O behavior.

### Custom Metadata Routing

For Cloudflare for SaaS: Store worker name in custom hostname `custom_metadata`, retrieve in dispatch worker to route requests. Requires custom hostnames as subdomains of your domain.

## Observability

### Logpush

- Enable on dispatch Worker → captures all user Worker logs
- Filter by `Outcome` or `Script Name`

### Tail Workers

- Real-time logs with custom formatting
- Receives HTTP status, `console.log()`, exceptions, diagnostics

### Analytics Engine

```typescript
// Track violations
env.ANALYTICS.writeDataPoint({
  indexes: [customerName],
  blobs: ['cpu_limit_exceeded'],
})
```

### GraphQL

```graphql
query {
  viewer {
    accounts(filter: { accountTag: $accountId }) {
      workersInvocationsAdaptive(filter: { dispatchNamespaceName: "production" }) {
        sum {
          requests
          errors
          cpuTime
        }
      }
    }
  }
}
```

## Use Case Implementations

### AI Code Execution

```typescript
async function deployGeneratedCode(name: string, code: string) {
  const file = new File([code], `${name}.mjs`, { type: 'application/javascript+module' })
  await client.workersForPlatforms.dispatch.namespaces.scripts.update('production', name, {
    account_id: accountId,
    metadata: { main_module: `${name}.mjs`, tags: [name, 'ai-generated'] },
    files: [file],
  })
}

// Short limits for untrusted code
const userWorker = env.DISPATCHER.get(sessionId, {}, { limits: { cpuMs: 5, subRequests: 3 } })
```

**VibeSDK:** For AI-powered code generation + deployment platforms, see [VibeSDK](https://github.com/cloudflare/vibesdk) - handles AI generation, sandbox execution, live preview, and deployment.

Reference: [AI Vibe Coding Platform Architecture](https://developers.cloudflare.com/reference-architecture/diagrams/ai/ai-vibe-coding-platform/)

### Edge Functions Platform

```typescript
// Route: /customer-id/function-name
const [customerId, functionName] = new URL(request.url).pathname.split('/').filter(Boolean)
const workerName = `${customerId}-${functionName}`
const userWorker = env.DISPATCHER.get(workerName)
```

### Website Builder

- Deploy static assets + Worker code
- See [api.md](./api.md#static-assets) for full implementation
- Salt hashes for asset isolation

## Best Practices

### Architecture

- One namespace per environment (production, staging)
- Platform logic in dispatch Worker (auth, rate limiting, validation)
- Isolation automatic (no shared cache, untrusted mode)

### Routing

- Use `*/*` wildcard routes
- Store mappings in KV
- Handle missing Workers gracefully

### Limits & Security

- Set custom limits by plan
- Track violations with Analytics Engine
- Use outbound Workers for egress control
- Sanitize responses

### Tags

- Tag all Workers: customer ID, plan, environment
- Enable bulk operations
- Filter efficiently

See [README.md](./README.md), [configuration.md](./configuration.md), [api.md](./api.md), [gotchas.md](./gotchas.md)
