Isolation guarantees
| Boundary | Guarantee | Mechanism |
|---|---|---|
| Tenant data | API keys, trained Custom Routers, training datasets, and usage logs are never shared across userId. | Every row in PostgreSQL carries a userId foreign key. Clerk auth validates the user on every dashboard request; validate_token validates the API key on every router call. There is no shared-mutable cache between tenants. |
| Model traffic | A request dispatched to provider X is never logged into provider Y. | Per-call provider routing. No cross-provider retry happens unless you explicitly populate fallback_models. |
| Custom Routers | Trained routers are never callable across users — model_id is scoped to the training userId. | CustomRouter.userId is enforced in db_utils.can_user_create_router and get_router_model. Calling another user’s model_id returns 404, not 403 — we don’t acknowledge cross-tenant existence. |
| AgentOpt sandboxes | Each optimization iteration runs in an isolated Modal Sandbox. No filesystem state, no network state, and no in-memory state survives across iterations. | Modal Sandbox provides container-level isolation; the sandbox is destroyed and recreated per iteration. Code, dataset, and dependencies are re-mounted from your uploaded ZIP each time. |
| Provider keys (per-provider mode) | When you supply your own providers config in the Node SDK, those keys are never sent to the IronLabs routing API. | Resolution happens client-side in the SDK before the request leaves your process. The routing API receives a pre-resolved gateway URL and your IronLabs key only. |
| Prompt-Opt OpenRouter keys | Optimizer-model calls (GEPA / MIPROv2) hit OpenRouter directly using your OpenRouter key. IronLabs neither stores it long-term nor uses it for any other purpose. | The key is held in memory for the duration of the optimization job and discarded when the job finalizes. Audit trail in Langfuse. |
What this means in practice
- Cross-tenant data leak through routing. Not possible — the router’s input is a single tenant’s message; the classifier output is consumed inline and discarded; the only persisted artifact is
ModelSelectUsage, which isuserId-scoped. - A neighboring user’s bad-faith Custom Router affecting your routing. Not possible — your requests route through the pre-trained router unless you set
routerIdto one you own. - A leaked AgentOpt iteration leaving state behind. Not possible — sandboxes are torn down per iteration. The only artifact that survives is the iteration’s checkpoint row in
AgentOptRuns(youruserId). - Provider key exfiltration via a logged request. Not possible in per-provider mode — keys never traverse our backend. In gateway mode (OpenRouter, LLM Gateway), the key is stored in your account’s encrypted secrets and only injected at egress.
Architecture, in one sentence
All requests terminate at the FastAPI gateway on Modal; per-tenant state lives inuserId-scoped rows in PostgreSQL; there is no peer-to-peer state between tenants, between routers, or between optimization runs.
What we do not guarantee
Honesty matters. These are not currently guaranteed:- At-rest encryption of training datasets. Datasets you upload to a public URL for Custom Router training are downloaded into Modal Volumes and persisted in the trained model. If the dataset itself is sensitive, redact it before uploading.
- Cross-region failover. A single region outage will produce errors until that region recovers.
- Provider-level isolation. When a request is forwarded to OpenAI or Anthropic, that provider’s security model applies; we cannot guarantee what they log on their side.
Related
Routing Lifecycle
See where in the lifecycle each isolation boundary is enforced.
API Authentication
Bearer-token scheme and key rotation.
AgentOpt
How the per-iteration sandbox is mounted.
Custom Router
Where your training data lives after training.