Goal: run the same fleet across dev, staging, and prod without
duplicating your model or letting the schemas drift apart.
An environment is a named slice of a project. The catalog — your entity types, cells, relationship types, and Action definitions — is shared across all envs, so there's nothing to keep in sync. The data is not: entities, relationships, secrets, action runs, and access are scoped per env. That split is the whole point: one model, isolated data.
| Shared across envs (project-scoped) | Scoped per env |
|---|---|
| Entity types, cells, relationship types | Entities, relationships |
| Action definitions | Action runs |
apply of the catalog (schema) | Secrets |
| Import-source registrations | Access (env-scoped tokens/members) |
Every project starts with a default env, so if you never think about
environments, everything lands there and the CLI just works.
Environments are project-scoped. Names are lowercase letters, digits, and hyphens (max 31 chars):
--envEvery env-scoped command takes a global --env flag. Omit it and you get the
default env. You can also set TERRANTULA_ENV_NAME for CI:
Under the hood these hit /<org>/<project>/envs/<env>/…. The schema commands
(entity-types, cells, relationship-types, actions, apply) sit above the
env segment because they're shared — you don't pass --env to them.
Secrets are env-scoped, so the same secret name holds a different value per
env — test keys in staging, live keys in prod, no naming gymnastics:
Your Action references it by name as {{ secrets.stripe-key }}; the value resolved
at run time is the one for the env the run fires in. Values are encrypted at rest
and never appear in the catalog, version control, or logs.
The Action definition is shared, but the same Action often needs to point at a
different repo, TFC workspace, or runner per env. Declare envOverrides on the
Action — a per-env partial that is shallow-merged over the base trigger at dispatch:
When the Action fires in prod, the override wins and the run targets the prod
workspace. See Action → Per-env overrides
for the field detail.
A token or membership can be locked to a single env. A caller scoped to staging
that tries to reach /<org>/<project>/envs/prod/… is rejected — even if its
project-level role would otherwise allow the action. Use this so dev tokens can't
write prod, while a project-level token (no env lock) reaches every env.
One project, one shared schema, and N environments each with isolated data,
secrets, and access. Edit the model once and apply once; it's in effect for every
env. Per-env values and trigger targets live where they belong.
There's no per-env schema. An apply of the catalog changes the model for all
envs simultaneously; you don't (and can't) apply against a single env. This is
deliberate — it's what keeps staging and prodfrom drifting apart.
Env-scoped routes resolve the env from the URL. There's no implicit "current env"
on those routes other than the CLI's default. If you mean prod, pass --env prod
(or set TERRANTULA_ENV_NAME) — an unscoped env-scoped call goes to default.
Removing an env removes the data scoped to it (entities, relationships, secrets,
runs). The shared schema is untouched. The defaultenv is structural — keep it.
envOverrides field detail.(project, env, source URI).