# FourIA Cloudflare Capacity Report

**Scope:** Tenant provisioning pipeline (`apps/lerma/lib/lerma/provisioning/tenant_provisioner.ex`), Cloudflare adapters (`apps/lerma/lib/lerma/cloudflare/`), IaC (`apps/fouria/iac/`, `apps/lerma/iac/tf/`), cloud/local E2E suites (`apps/fouria/test/e2e/`, `apps/lerma/test/e2e/`), and live account state.

**Method:** Code inspection of the provisioning and E2E paths, Cloudflare documentation cross-check (docs current on 2026-08-17), and a **read-only** live inventory of account `35afea16440634aa2350331d2a736eec` (Paso4) via the Cloudflare API/MCP suite.

**Inventory timestamp:** 2026-08-17 ~20:31 UTC. **No mutations were performed.**

**Security note:** `apps/fouria/test/e2e/.e2e.vars` contains what appear to be live credentials (API token, R2 keys, AI Gateway key). These are committed in the working tree and should be rotated and removed from version control.

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [Per-Tenant Provisioning Footprint](#2-per-tenant-provisioning-footprint)
3. [Documented Cloudflare Limits](#3-documented-cloudflare-limits)
4. [Capacity Model](#4-capacity-model)
5. [Live Account Inventory](#5-live-account-inventory)
6. [Actual Provisioning Capacity (Current State)](#6-actual-provisioning-capacity-current-state)
7. [Container Capacity](#7-container-capacity)
8. [E2E Resource Lifecycle & Leak Analysis](#8-e2e-resource-lifecycle--leak-analysis)
9. [E2E Cleanup Debt (Live)](#9-e2e-cleanup-debt-live)
10. [Services That Should Be Grouped](#10-services-that-should-be-grouped)
11. [Required Operational Fixes](#11-required-operational-fixes)
12. [Live-State Discrepancies vs Repository Assumptions](#12-live-state-discrepancies-vs-repository-assumptions)
13. [Final Answer](#13-final-answer)

---

## 1. Executive Summary

The exact number of clients FourIA can provision is bounded by **Cloudflare AI Gateway account limits**, not by R2, WFP user Workers, or Access applications. The current implementation creates **one AI Gateway per client environment**, and the account already holds **5 gateways** against a documented cap of **10 (Free) / 20 (Paid)**.

With the account's current inventory:

| Scenario                                                 | Free AI Gateway plan | Paid AI Gateway plan |
| -------------------------------------------------------- | -------------------: | -------------------: |
| **New production-only clients**                          |                    5 |               **15** |
| **New clients with production + dev**                    |                    2 |                **7** |
| **New clients with production + dev + staging (3 envs)** |                    1 |                    5 |

The account reports `free_tier=false` and `num_allowed_scripts=500` (Workers Paid / Workers for Platforms), so the **Paid = 20** gateway column is the most likely correct figure. The AI Gateway subscription itself could not be independently verified (subscriptions endpoint was permission-denied), so both columns are provided.

After grouping AI Gateways (one gateway per platform stage or billing tier instead of one per client), the AI Gateway quota stops bounding the client count and the next limiting quota becomes **Access groups**:

- **254 additional clients** under the current one-group-per-client model (300 limit − 46 used).
- More if Access applications/groups are also consolidated.

The largest E2E cleanup lever is **27 retained `moltbot-e2e-*` service tokens** — only 22 service-token slots remain, and each additional failed/cancelled cloud E2E run can consume one for a full year.

---

## 2. Per-Tenant Provisioning Footprint

The pipeline is defined in `tenant_provisioner.ex:52-63`. Each step creates the following resources:

| #   | Step                     | Resource created                                                                                  | Ref                                                                                       |
| --- | ------------------------ | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| 1   | `:ai_gateway`            | AI Gateway `fouria-{routable_slug}`                                                               | `tenant_provisioner.ex:390-404`, `ai_gateway.ex:22-37`                                    |
| 2   | `:access_policy`         | Per-instance Access application `FourIA - {hostname}` + closed group `Client Users - {hostname}`  | `tenant_provisioner.ex`, `access_policy.ex`                                               |
| 3   | `:r2_bucket`             | R2 bucket `fouria-backup-{routable_slug}`                                                         | `tenant_provisioner.ex:426-439`, `r2.ex:18-41`                                            |
| 4   | `:fetch_bundle`          | None (R2/registry reads only)                                                                     | `worker_bundle.ex`                                                                        |
| 5   | `:dispatch_deploy`       | WFP user Worker script + Container application `<script>-sandbox` + SQLite `Sandbox` DO namespace | `tenant_provisioner.ex:465-510`, `dispatch_namespace.ex:111-182`, `containers.ex:372-415` |
| 6   | `:worker_tags`           | Tags on the script (`customer_id`, `plan`, `env`, `environment` — 4 of the 8 allowed)             | `tenant_provisioner.ex:512-527`, `tags.ex:19-51`                                          |
| 7   | `:create_instance`       | DB record only                                                                                    | `tenant_provisioner.ex:529-537`                                                           |
| 8   | `:invalidate_cache`      | KV entry invalidation (HTTP, not CF API)                                                          | `tenant_provisioner.ex:770-784`                                                           |
| 9   | `:health_poll`           | Public HTTPS probe (not CF API)                                                                   | `health_poller.ex`                                                                        |
| 10  | `:production_transition` | DB status flip                                                                                    | `tenant_provisioner.ex:575-582`                                                           |

### Resource count per client

| Resource                 | Production tenant | Dev tenant (additional) | Per client (prod + dev) |
| ------------------------ | ----------------: | ----------------------: | ----------------------: |
| AI Gateway               |                 1 |                       1 |                   **2** |
| Access application       |                 1 |                       1 |                   **2** |
| Access group             |                 1 |                       1 |                   **2** |
| R2 backup bucket         |                 1 |                       1 |                   **2** |
| WFP user Worker script   |                 1 |                       1 |                   **2** |
| Durable Object namespace |                 1 |                       1 |                   **2** |
| Container application    |                 1 |                       1 |                   **2** |
| Worker tags              |                 4 |                       4 |                       8 |
| Worker bindings          |            ~13–22 |                  ~13–22 |                  ~26–44 |
| DB records               |                1+ |                      1+ |                      2+ |
| DNS records              |                 0 |                       0 |    0 (wildcard routing) |

Key details:

- **AI Gateway is per-environment.** `routable_slug` includes the `-dev` environment suffix and the platform suffix (`-pre`/`-staging`), so production, dev, and staging each get a distinct gateway (`tenant_provisioner.ex:1042-1044`).
- **Access is per-environment.** Production uses `{slug}.fouria.io`; dev uses `{slug}-dev.fouria.io`. Each has a separate application and validated-user group, preventing production invitations from granting dev access and vice versa.
- **R2 is per-environment** (`fouria-backup-{slug}` vs `fouria-backup-{slug}-dev`).
- **WFP scripts are unlimited** by Cloudflare documentation — the ordinary 500-Worker account limit does **not** apply to scripts deployed into dispatch namespaces.
- Bindings per Worker are well under the 128/env-var limit (Workers Paid) and are not a constraint (`tenant_provisioner.ex:788-900`).

---

## 3. Documented Cloudflare Limits

All limits below are from official Cloudflare documentation current on 2026-08-17.

| Service                 | Documented limit                                                                                             | Effect on FourIA                                                        | Source                                                                                                                             |
| ----------------------- | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| AI Gateway gateways     | 10/account (Free), 20/account (Paid)                                                                         | **Immediate client-count bottleneck**                                   | [AI Gateway limits](https://developers.cloudflare.com/ai-gateway/reference/limits/)                                                |
| AI Gateway metadata     | 5 custom metadata entries/request                                                                            | Shared gateways can identify tenants, but dimensions are bounded        | [AI Gateway limits](https://developers.cloudflare.com/ai-gateway/reference/limits/)                                                |
| AI Gateway spend limits | 20 rules/gateway                                                                                             | Per-tenant budget rules on a shared gateway are capped at 20            | [Spend limits](https://developers.cloudflare.com/ai-gateway/features/spend-limits/)                                                |
| AI Gateway log storage  | 100,000/account (Free); 10M/gateway (Paid); 500 logs/s/gateway                                               | Cost telemetry should live in Analytics Engine, not gateway logs        | [AI Gateway limits](https://developers.cloudflare.com/ai-gateway/reference/limits/)                                                |
| Access applications     | 500/account                                                                                                  | Second-order bottleneck (~478 remaining live)                           | [Cloudflare One account limits](https://developers.cloudflare.com/cloudflare-one/account-limits/)                                  |
| Access rule groups      | 300/account                                                                                                  | First Access bottleneck (~254 remaining live)                           | [Cloudflare One account limits](https://developers.cloudflare.com/cloudflare-one/account-limits/)                                  |
| Access service tokens   | 50/account                                                                                                   | **Acute E2E risk** — only 22 remaining live                             | [Cloudflare One account limits](https://developers.cloudflare.com/cloudflare-one/account-limits/)                                  |
| R2 buckets              | 1,000,000/account; unlimited objects & storage per bucket                                                    | Not a practical limit                                                   | [R2 limits](https://developers.cloudflare.com/r2/platform/limits/)                                                                 |
| R2 API                  | 50 bucket-mgmt ops/s; REST API 1,200 req/5 min                                                               | Bulk provisioning/cleanup must be paced                                 | [R2 limits](https://developers.cloudflare.com/r2/platform/limits/)                                                                 |
| WFP scripts             | **Unlimited**                                                                                                | The per-tenant WFP model is correct for scale                           | [Workers for Platforms limits](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/reference/limits/) |
| WFP DO namespaces       | No limit                                                                                                     | —                                                                       | [Workers for Platforms limits](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/reference/limits/) |
| WFP tags                | 8/script                                                                                                     | Current 4 tags are fine; keep taxonomy bounded                          | [Workers for Platforms limits](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/reference/limits/) |
| Ordinary Workers        | 100 (Free) / 500 (Paid)                                                                                      | Applies to standalone platform/E2E Workers, **not** WFP user scripts    | [Workers limits](https://developers.cloudflare.com/workers/platform/limits/)                                                       |
| Containers concurrent   | 1,500 vCPU / 6 TiB mem / 30 TB disk                                                                          | Active-instance capacity, not application count                         | [Containers limits](https://developers.cloudflare.com/containers/platform-details/limits/)                                         |
| Container image storage | 50 GB/account total                                                                                          | Old image tags must be pruned                                           | [Containers limits](https://developers.cloudflare.com/containers/platform-details/limits/)                                         |
| Durable Objects         | 10 GB per SQLite DO; no WFP namespace-count limit                                                            | Per-tenant state must be bounded; R2 is the bulk layer                  | [DO SQLite GA changelog](https://developers.cloudflare.com/changelog/post/2025-04-07-sqlite-in-durable-objects-ga/)                |
| Browser Run             | Free: 3 concurrent; Paid: 120 concurrent, 1 new/s                                                            | Shared runtime concurrency, not a provisioning resource                 | [Browser Run limits](https://developers.cloudflare.com/browser-run/limits/)                                                        |
| Analytics Engine        | 20 blobs + 20 doubles + 1 index per datapoint; 250 datapoints/invocation; 16 KB blobs; **3-month retention** | Shared dataset is correct; retention caps cost dashboards               | [Analytics Engine limits](https://developers.cloudflare.com/analytics/analytics-engine/limits/)                                    |
| Secrets Store           | 1 store/account; **100 secrets/account**                                                                     | Must not be used per-tenant                                             | [Secrets Store changelog](https://developers.cloudflare.com/changelog/post/2025-05-19-paygo-updates/)                              |
| Cloudflare API          | 1,200 req/5 min per user/account token; 50 user / 500 account tokens                                         | ~13–18 management calls per new tenant; ~66–85 tenants per 5-min window | [Error 429 docs](https://developers.cloudflare.com/support/troubleshooting/http-status-codes/4xx-client-error/error-429/)          |

---

## 4. Capacity Model

```text
usable_clients =
  min(
    remaining_ai_gateways / gateways_per_client,
    remaining_access_apps,
    remaining_access_groups,
    remaining_r2_buckets / buckets_per_client,
    active_container_capacity,
    account-specific limits
  )
```

Definitions:

```text
G  = 10 (Free) or 20 (Paid) AI Gateways
G0 = current AI Gateways (live: 5)
A0 = current Access apps (live: 22)
R0 = current Access groups (live: 46)
B0 = current R2 buckets (live: 25)
e  = number of distinct per-client environments (1, 2, or 3)
```

```text
N_gateway    = floor((G - G0) / e)
N_access_app = 500 - A0
N_access_grp = 300 - R0
N_r2         = floor((1,000,000 - B0) / e)
```

The current Access implementation uses one app + one group for production and dev because `access_policy.ex:235` hardcodes the base slug domain. If Access is made environment-specific, the app/group terms would also need division by `e`.

---

## 5. Live Account Inventory

Read-only inventory of account `35afea16440634aa2350331d2a736eec` (Paso4). All endpoints below returned `200 OK` unless noted.

| Resource                  |                                         Used |                    Remaining | Notes                                                                             |
| ------------------------- | -------------------------------------------: | ---------------------------: | --------------------------------------------------------------------------------- |
| AI Gateway gateways       |                                            5 |             5 Free / 15 Paid | 1 shared `moltworker` + 4 tenant/staging                                          |
| Access applications       |                                           22 |                          478 | 4 platform + 18 tenant (`FourIA - *`)                                             |
| Access groups             |                                           46 |                          254 | 1 root + 45 `Client Users - *`                                                    |
| Access service tokens     |                                           28 |                           22 | 1 platform + 27 `moltbot-e2e-*` (valid until 2027)                                |
| R2 buckets                |                                           25 |                      999,975 | 5 platform + 18 `fouria-backup-*` + 2 empty E2E                                   |
| WFP dispatch namespaces   |                                            4 |                    Unlimited | `fouria-tenants` (3 scripts), `fouria-tenants-staging` (6 scripts), 2 stray empty |
| WFP dispatch scripts      |                                            9 |                    Unlimited | Unlimited per Cloudflare docs                                                     |
| Ordinary Workers          |                                            5 |                   495 (Paid) | `fouria-dispatch*`, `mila-gemini-proxy`, 2 E2E                                    |
| Container applications    |                                           13 | No published app-count limit | 11 tenant + 2 E2E; all `max_instances=3`                                          |
| Durable Object namespaces |                                           11 |           No WFP count limit | 9 tenant + 2 E2E                                                                  |
| Secrets Store             |                       1 store, 0/100 secrets |                          100 | `default_secrets_store`                                                           |
| Analytics Engine          |              `fouria_cost_metrics` confirmed |                       Shared | ~191 rows/24h (adaptive sample)                                                   |
| Browser Run usage         |                                  Unavailable |                            — | No read-only endpoint exposed                                                     |
| Workers plan tier         | `free_tier=false`, `num_allowed_scripts=500` |                            — | Workers Paid / WFP inferred                                                       |
| AI Gateway subscription   |                                      Unknown |                            — | Subscriptions endpoint permission-denied                                          |

### Gateway classification (live)

| Gateway                    | Kind                |
| -------------------------- | ------------------- |
| `moltworker`               | Platform / shared   |
| `fouria-arua-staging`      | Staging tenant      |
| `fouria-tio-staging`       | Staging tenant      |
| `fouria-nocf-8`            | Test/E2E-looking    |
| `fouria-test2-dev-staging` | Test/staging tenant |

### Container instance-type mix (live)

| Instance type | vCPU / Mem / Disk     |            Apps |
| ------------- | --------------------- | --------------: |
| lite          | 1/16 / 256 MiB / 2 GB |               7 |
| basic         | 1/4 / 1 GiB / 4 GB    | 4 (incl. 2 E2E) |
| standard-1    | 1/2 / 4 GiB / 8 GB    |               1 |
| standard-2    | 1 / 6 GiB / 12 GB     |               1 |

---

## 6. Actual Provisioning Capacity (Current State)

### New production-only clients

Each new production client consumes **1** AI Gateway.

| AI Gateway plan    | Available | New clients |
| ------------------ | --------: | ----------: |
| Free (10 − 5 used) |         5 |       **5** |
| Paid (20 − 5 used) |        15 |      **15** |

### New clients with production + dev

Each client consumes **2** gateways (prod + dev), but shares one Access app/group.

| AI Gateway plan | New clients |
| --------------- | ----------: |
| Free (5 / 2)    |       **2** |
| Paid (15 / 2)   |       **7** |

### New clients with production + dev + staging (3 distinct environments)

| AI Gateway plan | New clients |
| --------------- | ----------: |
| Free (5 / 3)    |       **1** |
| Paid (15 / 3)   |       **5** |

**Answer (likely Paid plan):** the current account can fully provision **15 additional production-only clients**, or **7 additional clients with both production and dev**, before the AI Gateway quota is exhausted. This is the current-state limit; the theoretical earlier figures of 20/10 did not subtract the 5 gateways already in the account.

If the two E2E-looking gateways (`fouria-nocf-8`, `fouria-test2-dev-staging`) are confirmed test-only and cleaned, capacity rises to **17 new production-only (Paid)** and **8 new prod+dev (Paid)** clients.

### After grouping AI Gateways

Routing all tenant traffic through one shared tenant gateway consumes 2 gateway slots total (platform + shared tenant). Gateway count then stops bounding the client count, and the next constraints become:

- **Access groups: 254 remaining clients** under the current one-group-per-client model.
- Access apps: 478 remaining.
- Containers: effectively unlimited at current sizing.
- The new per-gateway constraints are log storage (10M logs/gateway on Paid), 20 spend-limit rules/gateway, 10 datasets/gateway, 5 metadata entries/request.

---

## 7. Container Capacity

The provisioner defaults to `lite` and `max_instances=3` (`tenant_provisioner.ex:974,1011-1021`). Container applications start at `instances: 0`, so **provisioning an application does not reserve** its full `max_instances` capacity; these figures are worst-case simultaneous-live-instance ceilings.

| Instance type | One environment per client | Prod + dev (worst case) |
| ------------- | -------------------------: | ----------------------: |
| `lite`        |                      5,000 |                   2,500 |
| `basic`       |                      2,000 |                   1,000 |
| `standard-1`  |                        512 |                     256 |
| `standard-2`  |                        341 |                     170 |

Live account maximum configured envelope (13 apps × `max_instances=3`, by type):

| Dimension | Max configured | Account limit |  Headroom |
| --------- | -------------: | ------------: | --------: |
| vCPU      |           ~8.8 |         1,500 |    ~1,491 |
| Memory    |        ~47 GiB |         6 TiB | ~5.95 TiB |
| Disk      |        ~150 GB |         30 TB | ~29.85 TB |

Container capacity is **orders of magnitude above** the AI Gateway bottleneck. The more immediate container concern is the **50 GB shared image-storage limit**: all tenant applications should reuse a small number of shared image tags and prune old tags.

---

## 8. E2E Resource Lifecycle & Leak Analysis

### Lerma dashboard E2E (`apps/lerma/test/e2e`)

`client_provisioning.txt` runs one real provisioning through the full pipeline. **Not run in CI**; run with `--sequential` (`mix test.e2e`).

A successful run creates: 1 AI Gateway, 1 Access app, 1 Access group, 1 R2 bucket, 1 WFP script, 1 DO namespace, 1 container app.

**Teardown is incomplete.** `DELETE /api/clients/:id` is a plain `Repo.delete` (`clients.ex`), and `decommission_instance/1` deletes only the dispatch script (`tenant_deprovisioner.ex:144-163`). The full `decommission/1` (Access + AI Gateway + R2 + archive, `tenant_deprovisioner.ex:28-52`) is **never called** by production code. Therefore every successful manual Lerma E2E run permanently leaks:

- AI Gateway
- Access application + group
- R2 bucket
- Container application
- DO namespace

Each leaked Lerma E2E run consumes one AI Gateway slot permanently — on a 20-gateway cap this is the fastest path to quota exhaustion.

### Fouria cloud E2E (`apps/fouria/test/e2e`)

Runs on **develop pushes only** in CI. A fresh run creates: 1 ordinary Worker, 1 container app, 1 R2 bucket, 1 Access service token (8760h), 1 DO namespace, plus a retained registry image. The Access application is intentionally skipped (workers.dev hostname + `DEV_MODE`).

Cleanup behavior:

| Resource              | Cleaned on success?                                                                                                                 |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| Worker                | Yes                                                                                                                                 |
| Container application | Yes                                                                                                                                 |
| Service token         | Yes                                                                                                                                 |
| R2 bucket             | **Usually no** — `wrangler r2 bucket delete` fails on non-empty buckets; `terraform-destroy` (which empties first) is never invoked |
| Failed/cancelled runs | **Leak everything** (worker + container + bucket + token)                                                                           |
| Cache eviction        | Removes only the local cache JSON, never the Cloudflare resources                                                                   |

Local E2E (`E2E_LOCAL=true`) uses `wrangler dev`, Docker, Miniflare, and local R2 emulation — **no equivalent Cloudflare tenant-resource quota consumed**.

### Dispatch and staging suites

- `provision_and_dispatch.txt`: creates/deletes one Supabase `clients` row, **no Cloudflare tenant resources**.
- `staging_redeploy/worker_redeploy.txt`: re-deploys the persistent `test2` instance in place, **0 new resources**.

### Parallelism & caching

- Lerma E2E: sequential.
- Fouria cloud E2E: one suite, files sequential; cache forced **off** on develop merges, so CI provisions fresh each push.
- No `concurrency` group exists in the workflows, so overlapping develop pushes can provision several temporary Workers concurrently (unique run-IDs prevent collisions, but quotas stack).
- Local cloud runs can preserve cached Workers/containers/buckets/tokens indefinitely.

---

## 9. E2E Cleanup Debt (Live)

Retained test/E2E-prefixed resources found in the live inventory (none cleaned — read-only pass):

| Resource                              | Retained | Risk                                                                       |
| ------------------------------------- | -------: | -------------------------------------------------------------------------- |
| Access service tokens `moltbot-e2e-*` |       27 | **Only 22 slots left (limit 50); each leaked run consumes one for a year** |
| Test-pattern Access apps              |       16 | Applies pressure on the 500-app limit                                      |
| Test-pattern Access groups            |      ~43 | Applies pressure on the 300-group limit                                    |
| E2E-looking AI Gateways               |        2 | `fouria-nocf-8`, `fouria-test2-dev-staging`                                |
| E2E R2 buckets                        |        2 | Both **empty** — safe deletion candidates                                  |
| E2E container applications            |        2 | `moltbot-sandbox-e2e-*`                                                    |
| Standalone E2E Workers                |        2 | `moltbot-sandbox-e2e-*`                                                    |
| E2E DO namespaces                     |        2 | `moltbot-sandbox-e2e-*_Sandbox`                                            |
| Stray dispatch namespaces             |        2 | `fouria-test-client-322`, `test-namespace` (empty)                         |

---

## 10. Services That Should Be Grouped

### 1. AI Gateway — group immediately

The current one-gateway-per-client-environment design is the binding constraint (5/10 or 5/20 used). Recommended model:

- One gateway for production, one for dev/staging (or a small pool by billing/security tier).
- Route tenants through custom metadata (`tenant_id`, `environment`, `plan`, `request_id` — up to 5 entries/request supported).
- Keep cost attribution in Analytics Engine / lerma Postgres.
- Note: only **20 spend-limit rules/gateway** — per-tenant budgets on a shared gateway are not unbounded.

### 2. Access applications — consolidate

One app per client reaches the 500 limit before the container platform does. Evaluate one wildcard application per platform stage with authorization enforced in the dispatch layer and database policy. Requires a security review — do not remove per-tenant authorization until tenant isolation is enforced elsewhere.

### 3. Access groups — reduce cardinality

300-group limit is the next wall after grouping gateways (~254 clients remaining). Group by organization/plan/role where security allows; otherwise plan tenant-specific groups carefully.

### 4. E2E service tokens — eliminate or reuse

Cloud E2E already runs with `DEV_MODE` and skips Access. The Terraform service token is unnecessary for the normal path. Options: remove it from the fixture, reuse one short-lived token, use a separate E2E-only account, and add stale-token cleanup.

### 5. WFP namespaces — keep as-is (already grouped)

Current model is correct: one namespace per platform stage, many tenant scripts per namespace, one shared bundle, 4 of 8 tags used. Do not create per-tenant namespaces.

### 6. Analytics Engine — keep shared

Shared dataset with tenant dimensions is correct. Do not create per-client datasets. Audit the staging dataset selection (multiple dataset names exist in code; staging binds the production dataset due to an env-var mismatch).

### 7. Secrets Store — keep account-level and sparse

The F4E1-encrypted-R2 + `secret_text` binding design correctly avoids the 100-secret account cap. Do not move to per-tenant Secrets Store entries.

### 8. R2 — grouping optional

Per-tenant buckets are fine quota-wise (1M limit). Grouping into one bucket with tenant prefixes would reduce bucket-management overhead but requires changes to backup paths, encryption boundaries, bindings, cleanup, and isolation tests. Fix cleanup first.

### 9. Containers — keep tenant-scoped, manage images

Container applications stay tied to tenant Workers/DOs. Reuse shared image tags, freeze inactive tenants, prune old registry images, and watch the shared **50 GB** registry limit.

### 10. Browser Run — pool runtime sessions

Limit is concurrent browsers (120 on Paid), not bindings. Reuse sessions, call `browser.close()`, use queues for bursts, and cap per-plan concurrency.

---

## 11. Required Operational Fixes

1. Wire full tenant deprovisioning (`TenantDeprovisioner.decommission/1`) into the client deletion path so Lerma E2E runs stop leaking AI Gateways, Access apps/groups, R2 buckets, and container apps. **(Fixed — `DELETE /api/clients/:id` now decommissions and archives the client; see Lerma issue #207.)**
2. Delete container applications when deleting tenant Worker scripts (`decommission_instance/1` only deletes the script). **(Fixed — `decommission_instance/1` deletes the container application alongside the script.)**
3. Make deprovisioning environment-aware (prod/dev/staging suffixes). **(Fixed — decommissioning derives all names from `Lerma.Instances.Naming` and iterates the client's instances across environments; shared pool gateways are never deleted.)**
4. Drain R2 objects before deleting E2E buckets (invoke the existing `terraform-destroy` empty-then-delete path).
5. Add a scheduled cleanup job for stale `moltbot-e2e-*` / `moltbot-sandbox-e2e-*` resources.
6. Add a GitHub Actions `concurrency` group for the cloud E2E job.
7. Remove the unnecessary E2E service token from the Terraform fixture or shorten its lifetime.
8. Fix Access domains/policies for dev/staging isolation (currently shared base-slug app breaks dev-hostname auth; see §12).
9. Ensure `CLOUDFLARE_AI_GATEWAY_API_KEY` is sourced from Lerma's environment as a real AI-scoped runtime credential; the provisioner must never generate a random value for it.
10. Audit `CF_ENVIRONMENT=pre` vs `"staging"` config branches and the Analytics dataset env-var mismatch.
11. Rotate the live-looking credentials committed in `apps/fouria/test/e2e/.e2e.vars` and remove them from version control.

---

## 12. Live-State Discrepancies vs Repository Assumptions

1. **`arua` and `tio` are orphaned.** They have Access apps, AI Gateways, backup buckets, and container apps, but **no `tenant-arua`/`tenant-tio` WFP script** and no matching DO namespace (11 tenant containers vs 9 WFP scripts). Appear abandoned/legacy.
2. **`CONFIG_BUCKET` buckets don't exist live.** `fourai-moltlazy-config[-dev]` are documented in AGENTS.md but absent; code comments confirm CONFIG_BUCKET was removed.
3. **Worker naming:** live account has `fouria-dispatch` / `fouria-dispatch-staging` (matches `workers/fouria-dispatch/`), while `wrangler.jsonc` top-level name is `fouria` / `fouria-staging` (local-dev only or stale).
4. **Per-tenant backup buckets are undocumented:** live shows 18 `fouria-backup-<slug>` buckets while docs describe a single `fouria-data` bucket.
5. **Repository declares public health Access apps** (4, from IaC `access.tf`), but only 4 recognizable platform apps were found live — IaC and account state should be reconciled.
6. **Analytics dataset drift:** repository code references `fourai_cost_metrics`, `fouria_cost_metrics_pre`, and `fouria_cost_metrics` across different files; live confirms `fouria_cost_metrics` exists. Staging tenants may be binding the production dataset.
7. Everything else matched: `moltworker` gateway, `fouria-tenants[-staging]` namespaces, `fouria-data[-staging]`, `fouria_cost_metrics`, `FourIA Platform (*.fouria.io)`, `fouria-dashboard-local-ci` token.

---

## 13. Final Answer

**Current state (with live inventory, likely Paid AI Gateway plan):**

- **15 additional production-only clients** (or 5 on the Free plan).
- **7 additional clients with production + dev** (or 2 on the Free plan).
- **5 additional clients with production + dev + staging** (or 1 on the Free plan).
- These assume 5 gateways currently in use; if the 2 E2E-looking gateways are cleaned, add 2/1 production-only / prod+dev clients respectively.

**After grouping AI Gateways** (recommended, immediate action):

- The gateway quota stops bounding the client count.
- Next limiting quota: **Access groups → 254 additional clients** under the current model.
- Access apps → 478; containers and R2 are effectively unbounded at current sizing.

**The immediate operational action** is an ownership-reviewed cleanup of the 27 E2E service tokens, ~43 test Access groups, 16 test Access apps, 2 E2E gateways, the orphaned `arua`/`tio` chains, and stale E2E containers/DOs — followed by fixing the Lerma teardown path so future E2E runs stop leaking quota.

---

## Appendix: Reproduce the Inventory

```bash
# AI Gateway
curl -s "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/ai-gateway/gateways" -H "Authorization: Bearer $CF_API_TOKEN"

# Access
curl -s "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/access/apps" -H "Authorization: Bearer $CF_API_TOKEN"
curl -s "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/access/groups" -H "Authorization: Bearer $CF_API_TOKEN"
curl -s "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/access/service_tokens" -H "Authorization: Bearer $CF_API_TOKEN"

# R2
curl -s "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/r2/buckets" -H "Authorization: Bearer $CF_API_TOKEN"

# WFP
curl -s "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/workers/dispatch/namespaces" -H "Authorization: Bearer $CF_API_TOKEN"

# Containers
curl -s "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/containers/applications" -H "Authorization: Bearer $CF_API_TOKEN"

# Secrets Store
curl -s "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/secrets_store/quota" -H "Authorization: Bearer $CF_API_TOKEN"
```
