> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://rivet.chrisboulton.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://rivet.chrisboulton.com/_mcp/server.

# Values and expressions

> Strict values, operators, for expressions, splats, references, and fact traversal.

Rivet evaluates expressions while compiling. The host only receives the results.

| Value         | Example                      | Notes                                                                                                                                                        |
| ------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| String        | `"hello"`                    | No implicit boolean or number-to-string conversion for resource fields.                                                                                      |
| Boolean       | `true`                       | Used for actual boolean attributes.                                                                                                                          |
| Number        | `1001`, `1.5`                | Integers use signed 64-bit storage; fractional expressions use finite double precision. Resource integer fields and external number inputs reject fractions. |
| Null          | `null`                       | Accepted only where a field's null policy permits it.                                                                                                        |
| List          | `["app", "ops"]`             | Element types are checked by the consuming resource.                                                                                                         |
| Object        | `{ APP_ENV = "production" }` | Used for maps such as command environments.                                                                                                                  |
| Handle        | `file.config`                | Refers to a declaration and carries dependency provenance.                                                                                                   |
| Fact          | `facts.processors.count`     | Snapshot values retain their JSON types.                                                                                                                     |
| Trusted claim | `trusted.node`               | Credential-derived values from authenticated [service compilation](/language/trusted-facts).                                                                 |

Parentheses group expressions. Declared types include scalars, resource handles, and nested list/set/map/object/tuple types. Collections can be inputs, locals, module arguments, or outputs. See [types](/language/variables-and-outputs#types).

## Interpolation

```hcl
locals {
  directory = "/var/lib/example"
  filename  = "message.txt"
}

file "message" {
  path    = "${local.directory}/${local.filename}"
  content = "Target: ${facts.distribution} ${facts.version}\n"
}
```

Strings, finite numbers, and booleans can be interpolated. Null, collections, and handles require explicit conversion or encoding. Numeric facts support interpolation and numeric outputs; ordinary resource parameters retain strict types. [Templates](/language/templates) support heredocs, directives, and whitespace stripping. Expression nesting is limited to 128.

## Operators

```hcl
variable "environment" {
  type    = string
  default = "production"
}

locals {
  production = var.environment == "production"
  workers    = facts.processors.count * 2
}

output "replicas" {
  type  = number
  value = local.production && local.workers >= 4 ? local.workers : 1
}
```

| Operators               | Operands     | Result                                                                                                               |
| ----------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------- |
| `-x`, `!x`              | number, bool | Negation and boolean not.                                                                                            |
| `*`, `/`, `%`, `+`, `-` | numbers      | Integer results stay exact and fail on overflow; inexact `/` yields a fraction. Division or remainder by zero fails. |
| `<`, `<=`, `>`, `>=`    | numbers      | Strings and other values are errors; nothing is converted.                                                           |
| `==`, `!=`              | any values   | Structural equality without conversion: `1 == 1.0` but `"1" != 1`.                                                   |
| `&&`, `\|\|`            | bools        | Short-circuit: the right side is not evaluated when the left side decides the result.                                |

Precedence follows HCL: unary, multiplicative, additive, comparison, equality, `&&`, `||`, then the conditional. A result carries the dependencies and sensitivity of every operand that was evaluated.

## For expressions

```hcl
variable "users" {
  type = map(object({ admin = bool, shell = string }))
  default = {
    alice = { admin = true, shell = "/bin/bash" }
    bob   = { admin = false, shell = "/bin/sh" }
  }
}

locals {
  admins = [for name, user in var.users : name if user.admin]
  shells = { for name, user in var.users : user.shell => name... }
}

file "admins" {
  path    = "/etc/rivet-admins"
  content = "${join("\n", local.admins)}\n"
}
```

`[for v in collection : value]` builds a list; `{for k, v in collection : key => value}` builds a map. Lists bind index and element, maps bind key (in sorted order) and value, and sets bind each element as both. An optional `if` filter drops elements. Duplicate map keys fail unless the value ends in `...`, which groups values into lists. Keys convert strings, numbers, and bools to strings.

Loop variables are visible only within the expression and may shadow other names. Each element keeps the annotations it actually read. A sensitive filter or collection shape makes the result sensitive, and a sensitive key makes the map's keys sensitive. Template loops and `for` expressions share a compilation-wide 100,000-iteration budget.

## Splats

```hcl
file "copies" {
  count = 2
  path  = "/srv/copy-${count.index}"
}

output "copy_paths" {
  type  = list(string)
  value = file.copies[*].path
}
```

`list[*].attr` applies every following traversal step to each element and returns a list. `list.*.attr` applies only the attribute lookups immediately after it, so `list.*.attr[0]` indexes the resulting list. Splatting `null` yields `[]`, and any other non-list value becomes a one-element list.

## References

Use `var.name`, `local.name`, `file.label`, or an exported static property such as `service.app.name`. Local values can refer forward to other locals or declarations. Cycles are errors.

A handle is declared work, not an apply result. You can read only supported static attributes; omitted optional attributes cannot be discovered from the host. See [resource conventions](/resources/conventions) for the export list.

Resource provenance survives locals and interpolation. Referring to `file.config.path` in a consuming resource makes the config a prerequisite. An independently written equal path string does not create that relationship.

## Fact paths

Use dot traversal, map keys, array indices, splats, and `for` expressions:

```hcl
output "root_filesystem" {
  type  = string
  value = facts.mounts["/"].filesystem
}

output "cpu_count" {
  type  = number
  value = facts.processors.count
}

output "ext4_mounts" {
  type  = list(string)
  value = [for path, mount in facts.mounts : path if mount.filesystem == "ext4"]
}
```

These examples need a collected snapshot with the relevant fields. Baseline-only assumptions contain just the four [legacy fact aliases](/language/facts). Indices may be evaluated expressions. Missing keys and invalid indices fail; integer facts can supply numeric indices/count. Optional inventory fields may be absent; use `try` or `can` for guarded access. Decoded collections such as `jsondecode(file("assets/config.json"))` support computed traversal too.

## Conditionals and collection functions

```hcl
variable "enabled" {
  type    = bool
  default = true
}

locals {
  names   = toset(["app", "ops", "app"])
  message = var.enabled ? "enabled" : "disabled"
}

output "name_count" {
  type  = number
  value = length(local.names)
}

file "summary" {
  path    = "/etc/rivet-summary"
  content = "${local.message}: ${length(local.names)} names\n"
}
```

A conditional requires a boolean condition and evaluates only the selected branch. Condition dependencies and sensitivity propagate into selected values. `toset` accepts a list or set, unifies compatible primitive elements, and deduplicates deterministically. `length` accepts a collection or string; string length counts Unicode grapheme clusters.

Use `each.key`/`each.value` and `count.index` only inside the appropriate expanded instance. Module outputs use `module.name.output`; `path.module` is the canonical declaring directory. All [built-in functions](/language/functions) and [template directives](/language/templates) are available. Final argument expansion is supported for ordinary functions, for example `max([1, 2, 3]...)`; `try` and `can` take explicit expressions.