Environments

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 typesEntities, relationships
Action definitionsAction runs
apply of the catalog (schema)Secrets
Import-source registrationsAccess (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.

Step 1 — Create an environment

Environments are project-scoped. Names are lowercase letters, digits, and hyphens (max 31 chars):

terrantula environments create --name staging
terrantula environments create --name prod
terrantula environments list

Step 2 — Target an env with--env

Every 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:

# entities, relationships, secrets, action runs all scope to --env
terrantula --env prod entities list
terrantula --env staging entities list --entity-type Tenant --state active

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.

Step 3 — Give each env its own secrets

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:

terrantula --env staging secrets set-value stripe-key --value "$STRIPE_TEST_KEY"
terrantula --env prod    secrets set-value stripe-key --value "$STRIPE_LIVE_KEY"

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.

Step 4 — Vary trigger config per env (optional)

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:

kind: Action
name: OnboardTenant
# ... operation, base trigger ...
trigger:
  type: terraform-cloud
  organization: my-org
  workspaceName: tenant-onboard-dev
  apiToken: "{{ secrets.tfc-api-token }}"
envOverrides:
  prod:
    workspaceName: tenant-onboard-prod
  staging:
    workspaceName: tenant-onboard-staging

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.

Step 5 — Scope access per env (optional)

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.

Result

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.

Caveats

Schema is shared and unversioned — applies hit every env at once

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.

The env segment is required on env-scoped routes — no fallback

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.

Deleting an environment removes its data

Removing an env removes the data scoped to it (entities, relationships, secrets, runs). The shared schema is untouched. The defaultenv is structural — keep it.