Step-local vars
A vars { ... } block declares step-local variables that are evaluated once, in source order, before the provider runs. They exist for the lifetime of the step and are not propagated to other steps.
vars is the right tool for:
- Timestamps and signing material that must stay stable across
request/expect/capture(now_unix()would otherwise be re-evaluated on each call). - JSON payloads built once and then hashed for HMAC signing.
- Generated values you want to reference twice in the same step (a password and its confirmation field, for example).
Syntax
Section titled “Syntax”step "http" "signed_call" { vars { ts = now_unix() body = jsonencode({ id = "evt-1", type = "ping" }) sig = hmac_sha256_hex(config.webhook_secret, "${vars.ts}.${vars.body}") }
request { method = "POST" url = "${config.base_url}/webhook" headers = { X-Signature = "t=${vars.ts},v1=${vars.sig}" } body { raw = vars.body } }
expect { status = 200 }}Evaluation rules
Section titled “Evaluation rules”- Source order is preserved. Later vars can reference earlier ones via
vars.<name>. - One evaluation per step.
varsare computed once, before the provider runs. Subsequentrequest/expect/capturesee the same value. - Step-scoped. A var declared in step A is invisible to step B. To share values across steps, use
capture. - Visible to
request,expect,captureof the same step. They are not visible towhen,skip_if, orskip_unless, those are evaluated before the step body runs.
Why vars and not inline expressions?
Section titled “Why vars and not inline expressions?”You could inline now_unix() directly in request.headers. But:
now_unix()reads the wall clock at each evaluation. The header value and the body signed with that header would not match.- Computing
jsonencode(payload)andhmac_sha256_hex(secret, payload)separately would computejsonencodetwice, once for the body, once nested inside the HMAC, risking the two values diverging if the payload contained any non-determinism.
vars makes the “compute once, use many times” pattern explicit and unambiguous.
Forbidden references
Section titled “Forbidden references”Tales validates the body of when, skip_if, and skip_unless at load time. Any vars.<name> reference there produces a parse error (exit code 2) pointing at the offending block.
step "http" "bad" { vars { id = "evt-1" }
when = can(vars.id) // ERROR: vars are not visible in `when` // ...}The fix: capture the value upstream or compute it inside an expression that runs before the step body (config, result.<earlier_step>, host, env(...), or an expression already free of vars).
Composing vars
Section titled “Composing vars”A common pattern: compute base values first, derive composite values from them:
vars { ts = now_unix() body = jsonencode({ id = "evt-${result.upstream.id}" type = "notarization.completed" }) signature = hmac_sha256_hex(config.webhook_secret, "${vars.ts}.${vars.body}") header = "t=${vars.ts},v1=${vars.signature}"}
request { headers = { X-Signature = vars.header } body { raw = vars.body }}The full webhook signing recipe is in the Signing webhooks guide.