Import sources

Goal: import from more than one Terraform state (or Atmos manifest set, or any other external source) into a single project, and keep each source's data addressable without the two colliding.

An import source is a project-scoped record — a URI plus a kind (tf-state / atmos-manifests) and a reconciliation policy — that every entity is imported under. The catalog — your entity types, cells, relationship types, and Action definitions — is shared across every source in a project, so there's nothing to keep in sync between sources. The graph data is not: an entity's identity is (entity type, name, source), so two sources in the same project can each hold a Server named web-01 without colliding.

Shared across sources (project-scoped)Partitioned per source
Entity types, cells, relationship typesEntities
Action definitions—
apply of the catalog (schema)Relationships still resolve project-wide — an edge can span two sources

Step 1 — Register a source

Sources are project-scoped. The CLI registers one automatically on your first import terraform / import atmos against a given state — you rarely need to register one by hand:

terrantula import terraform --state prod.tfstate --config terrantula.yaml
terrantula import-sources list

Registration is idempotent, keyed on (project, source URI). A second import against the same URI refreshes the existing source rather than creating a duplicate; a re-import that omits labels leaves whatever labels are already on file untouched (only an explicit clear removes them).

Step 2 — Address entities with?source=

Every write against /entities names the source explicitly — there is no default and nothing is auto-created. A write with no source fails closed:

curl -X POST "$TERRANTULA_API_URL/<org>/<project>/entities?source=terraform:prod.tfstate" \
  -H "Authorization: Bearer $TERRANTULA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind":"Entity","entityType":"Server","name":"web-01","state":"active"}'

Reads are project-wide by default — GET /<org>/<project>/entities returns every entity across every source in the project — and source narrows a read to one source when you need it:

terrantula entities list
terrantula entities list --source terraform:prod.tfstate

Step 3 — Handle a name that exists in more than one source

Two sources in one project can each register an entity of the same type and name — that's the whole point of source-partitioned identity. Direct entity routes (GET/PUT/DELETE /entities/:name, /actions, /metrics, …) always require ?source=, so they can never be ambiguous. A relationship's from / to is different: an edge's two ends legitimately live under different sources (R3), so a Relationship item in an apply bundle names its endpoints by bare entity name, resolved project-wide. If that name matches rows in more than one source, the apply raises 409 AMBIGUOUS_ENTITY_NAME rather than wiring the edge to whichever row Postgres happened to return first:

# terrantula.yaml — ambiguous if two sources both have a Cluster named "web-1"
kind: Relationship
relationshipType: tenant-placement
from: tenant-alpha
to: web-1          # 409 AMBIGUOUS_ENTITY_NAME if two sources both own "web-1"

Address the endpoint by its surrogate id instead once you hit this — the error message names every colliding type@source candidate so you know which id to use.

Step 4 — Delete a source (optional)

Deleting a source destroys every entity whose identity partitions by it, and — through those entities — every relationship and cell membership that references one of them:

terrantula import-sources delete --id <importSourceId>

An edge cascades off either endpoint: a relationship from an entity on the deleted source to an entity on a still-live source is destroyed too, even though the surviving entity is untouched. Re-import recreates the source and its entities from scratch.

Result

One project, one shared schema, and as many import sources as you have external states — each with its own partition of the entity graph, addressed explicitly rather than picked implicitly. Edit the model once and apply once; every source's entities are governed by it.

Caveats

Schema is shared and unversioned across sources

There's no per-source schema. An apply of the catalog changes the model for every source in the project simultaneously; you don't (and can't) applyagainst a single source. This is deliberate — it's what keeps two sources' entities from drifting onto different definitions of the same entity type.

A write with no

source is a 400, never a default There is no "current source" a write falls back to. Every write against /entities (and instance-scoped routes under it — metrics, actions, relationships) requires ?source= naming a source already registered in the project. Register the source first (import-sources register, or let import terraform / import atmosdo it for you) before writing entities against it.