Appearance
Tenant Isolation
Cyclone does not authenticate which application principal owns a run. The runtime database role can mutate the entire Cyclone schema. A run ID, node id, or signal target is therefore not an authorization boundary. Any caller who can reach a client's API, or the database role, can read outputs, signal, cancel, or retry any run in that schema. Treat the whole schema as one trust domain.
The Boundary Is the Schema, Not the Run
For mutually untrusted tenants, isolate at one of these levels:
- Separate databases give the strongest isolation. They separate connection budgets, roles, and blast radius. Use one Cyclone client for each tenant database.
- Separate schemas in one database: each tenant gets its own Cyclone schema (
search_path-pinned pool) and its own migration run. This costs less than separate databases. It is still a hard boundary at the role level if you grant each tenant's runtime role only its schema. - One schema plus an application-owned authorization layer is acceptable only when your application mediates every Cyclone call. Map your tenant identity onto run IDs before you call
GetRun,Output,Signal,Cancel,Retry, or the DLQ APIs. Never accept a raw run ID from an untrusted caller. PostgreSQL RLS on the Cyclone tables can back this up, but Cyclone's own queries are not tenant-aware. RLS predicates must key on something your session sets (for example,SET app.tenant).
Role Hygiene
- Give the runtime role only DML on the Cyclone schema (
SELECT,INSERT,UPDATE,DELETE) plusLISTEN/NOTIFY. It does not need DDL. - Reserve DDL for the migration role. Use that role only for
cyclone.Migrateorcyclone migrate, on a direct or session-mode connection. - Do not share the Cyclone runtime role with application code that must not mutate workflow state.
Do Not Expose Raw APIs to Untrusted Callers
Signal, Cancel, Retry, RetryAll, RetryExhausted, and the enqueue and submit APIs are operator surfaces and application surfaces. A direct exposure lets one tenant drive another tenant's workflows. One example is an HTTP endpoint that calls Signal with a caller-supplied run ID. Always resolve tenant to run ownership in your application first. Payload size limits (see Payload Limits) bound resource consumption. They do not authenticate callers.
If First-Class Multi-Tenancy Is Ever Needed
Adding tenant identity to Cyclone itself would put a tenant key on every primary-key path and every foreign-key path. Those paths are cyclone_runs, cyclone_steps, cyclone_signals, cyclone_schedules, uniqueness constraints, and every claim query and advancement query. Until that exists, schema separation or database separation is the supported pattern.