Interpolation is how an Action passes
fleet-specific values into the external work it triggers. Every
interpolation-aware field accepts {{ ... }} expressions: trigger URLs, headers,
auth values, webhook payloads, PR titles and bodies, branch names, file contents,
labels, reviewers, and the name/properties of cascade-created entities and
relationships. The same syntax resolves identically across all six trigger types
(webhook, pull-request, terraform-cloud, atmos-workflow, atlantis, noop) and
inside cascade operations.
This is the lookup reference. There is one expression form per resolution root — Terrantula's interpolation is variable substitution, not a function language: there are no filters, pipes, conditionals, or calls. Each root below is documented man-page style: purpose → syntax → example → notes.
A trigger is resolved in two passes, in this order:
{{ secrets.<name> }} is replaced with the named
Secret's decrypted value, loaded from the project's encrypted store at
dispatch time. This runs first so secret values are in place before anything
else looks at the field. On a pull-request trigger it runs over the
credential fields only — see Published fields.{{ ... }} expression — parameters,
entity, recommendations, run, operation — is substituted from the
run's context.The expression grammar is intentionally small: a {{, optional whitespace, a
dotted path (word characters, dots, and hyphens), optional whitespace, a }}.
Anything else is left untouched.
An unresolved variable renders as its own literal, not an empty string. If a
blueprint uses {{ entity.name }} but the run has no entity in context (a
collection-scoped Action, say), the field keeps the literal text
{{ entity.name }} rather than collapsing to "". This is deliberate: a missing
value surfaces loudly in the PR or payload instead of silently vanishing. The one
exception is {{ secrets.<name> }} — a missing or value-less secret fails the
run (see secretsnotes).
Path segments named __proto__, constructor, or prototype never resolve —
they short-circuit to the unresolved literal. This prototype-pollution guard
applies to every nested-path root (parameters, entity.properties,
entity.labels, recommendations.*.properties).
secretsPurpose. Inject a decrypted Secret value — a TFC API token, a GitHub token, an Atlantis API secret, a webhook HMAC secret — into a trigger's auth field, header, or payload. The catalog only ever names the credential; the value is set out-of-band and never lands in YAML.
Syntax.
<name> is the Secret's name. Names may contain word characters, dots, hyphens,
and colons.
Example.
Notes.
terrantula secrets set-value <name> --value ….A pull-request trigger writes most of its fields into somebody else's git
repository: files[].content and files[].patch.value become blobs, title
becomes the commit message as well as the PR title, and body, head,
labels, reviewers and the rest become pull-request fields. A git blob is a
permanent object, replicated to every clone — deleting the branch does not
remove it.
So those fields may name a credential and never carry one. Both expressions that carry a credential value are refused there, at apply and again at dispatch:
The credential fields are the exception, and they are the only one: auth.token
and webhookSecret on the trigger (and on an Atlantis trigger's pullRequest
block) still take either expression. Their rendered values become an
Authorization header and an HMAC comparison — neither is repository content.
What a published field may say about a credential is its name:
{{ target.<name>.credentialRef }} renders the Secret's name, and a Secret's own
name is just a string. Fetch the value in the workflow that receives the PR, from
your own store.
A GitHub Actions expression is spelled ${{ secrets.MY_TOKEN }}, which contains
{{ secrets.MY_TOKEN }} — so a workflow file committed through
files[].content is refused by this fence. Scaffold workflow files into the repo
directly (terrantula spin, or a one-time commit) rather than through a
pull-request trigger.
parametersPurpose. Read an input collected when the Action was triggered. Parameters
are declared on the Action's parameters field and supplied per run (by the
operator in the UI/CLI, or by a cascade rule).
Syntax.
<path> may be nested (parameters.network.cidr) for object-valued parameters.
Example.
Notes.
entityPurpose. Reference the entity the Action is firing against — its name,
declared properties, and labels. Available on instance-scoped Actions
(associatedWith.scope: instance) and on cascade operations, where it refers to
the entity the cascade is acting on (for a create-entity operation, the
just-created entity).
Syntax.
Example.
Notes.
instance-scoped Actions and cascade operations. On a
collection-scoped Action there is no entity in context, so entity.*
references render the literal.{{ entity.id }} is deprecated: entities are now keyed by name, not a
UUID. Prefer {{ entity.name }} for new blueprints.entity.properties.* and entity.labels.* accept nested paths and are
prototype-pollution-guarded.recommendationsPurpose. Read a cell-ranked placement suggestion surfaced at trigger time.
When an Action declares recommendations, Terrantula ranks candidate
Cells and exposes the chosen target so the
trigger can wire the entity to the right pen — the core of the tenant-placement
cattle workflow.
Syntax.
<name> is the recommendation's declared name.
Example.
Notes.
<name>. An unresolved recommendation renders the literal.{{ recommendations.<name>.id }} is deprecated for the same reason as
entity.id; prefer .name..properties.* accepts nested paths and is prototype-pollution-guarded.runPurpose. Reference the firing ActionRun itself — its identifier and the callback channel an external workflow uses to report completion back to Terrantula.
Syntax.
Example.
Notes.
{{ run.id }} — the ActionRun UUID. Always safe to embed in PR bodies,
commit messages, or logs: it identifies a run for human reference but cannot
authorize anything on its own.{{ run.callbackUrl }} — the endpoint your external workflow POSTs to when it
finishes, so Terrantula can mark the run succeeded or failed.{{ run.callbackToken }} — the per-run token your workflow presents to that
endpoint in the X-Terrantula-Callback-Token header. It is one of two
credentials the callback takes: Authorization: Bearer … carries a project
API token holding at least data:read, and this header carries the HMAC that
authorizes completing this particular run. A request presenting only one of
the two is refused with 401.
Format: {orgId}:{runId}:{expiresAtMs}:{hmac}. The HMAC-SHA256
signature is computed with a key derived from the server encryption key and
binds the token to this organization, this run and this expiry
(the Action's timeout, default 60 minutes). The callback endpoint verifies
it in constant time and guards on run status, so a token issued for one run
cannot be replayed against another, and the organization it names is the
tenant the callback is served in.
Because the binding — not the string's secrecy — is what protects it, the token
is safe to embed in a PR body or payload.operationPurpose. Inside a cascade createRelationship on a create-entity operation,
reference the entity the parent operation just created. This lets one Action
create an entity and wire its relationship atomically, without a second call.
Syntax.
Example.
Notes.
createRelationship block on a create-entity
operation. Anywhere else it renders the literal.createRelationship can point at it before the entity exists by any other
reference.| Expression | Available when | Pass | Miss behavior |
|---|---|---|---|
{{ secrets.<name> }} | anywhere but a published field | secrets | fails the run |
{{ target.<name>.credential }} | anywhere but a published field | variable | renders literal |
{{ target.<name>.credentialRef }} | always, published fields included | variable | renders literal |
{{ parameters.<path> }} | run supplied the parameter | variable | renders literal |
{{ entity.name }} / .properties.<p> / .labels.<p> | instance scope or cascade operation | variable | renders literal |
{{ entity.id }} | (deprecated — use entity.name) | variable | renders literal |
{{ recommendations.<name>.name }} / .properties.<p> | Action declares recommendations | variable | renders literal |
{{ recommendations.<name>.id }} | (deprecated — use .name) | variable | renders literal |
{{ run.id }} / {{ run.callbackUrl }} / {{ run.callbackToken }} | always | variable | renders literal |
{{ operation.entity.id }} | createRelationship on a create-entity op | variable | renders literal |
A single instance-scoped Action wiring every root together. Terrantula opens
this PR; your CI applies it — Terrantula never runs terraform apply itself.
{{ secrets.* }} surface and
how values are set out-of-band.