Types & values

Five value types, all of which round-trip to JSON.

String

Double-quoted. Supports the escape sequences \", \\, \n, \r, \t. Strings cannot contain a raw newline; use \n.

name = "api"
greeting = "hello\nworld"

A string may also contain one or more ${<ref>} placeholders, replaced at config-load time with the resolved value of the reference. \${ escapes a literal ${. See String interpolation.

db_url = "postgresql://app:${secret.pg.value}@${host.primary.addr}:5432/app"
escaped = "literal \${HOME}"   # -> "literal ${HOME}"

Number

A signed decimal, optionally with a fractional part. Lexed as f64. When emitting JSON, stratum prefers a JSON integer if the number is finite and whole (port = 4000 becomes 4000, not 4000.0); otherwise it emits a JSON float. Non-finite floats serialize as null.

port    = 4000      # -> JSON 4000
timeout = 1.5       # -> JSON 1.5

This matters for diff: changing 4000 to 4000.0 is a no-op, since both render as the integer 4000.

Bool

Bare true or false. These are lexed as keywords, not identifiers.

tls = true

List

Square-bracketed, comma-separated. Trailing commas are allowed. Items can be any value type (including refs and other lists/maps).

ports   = ["8080:80", "8443:443"]
mixed   = [1, "two", true]
nested  = [[1, 2], [3, 4]]
list ::= "[" ( value ( "," value )* ","? )? "]"

Map

Brace-delimited. Each entry is <key> = <value>. Entries are separated by whitespace — no commas. Keys may be either bare identifiers or quoted strings; the string form is required for dotted keys like Traefik labels.

env = {
  NODE_ENV = "production"
  PORT     = 4000
}

labels = {
  "traefik.enable"                          = "true"
  "traefik.http.routers.api.rule"           = "Host(`api.example.com`)"
}
map ::= "{" ( ( ident | string ) "=" value )* "}"

Reference

See References & scope.

host = host.prod.addr

Identifiers

Identifiers start with an ASCII letter or _, then continue with letters, digits, _, or -. They are used for block kinds, attribute keys, map keys, and ref segments.

content_file on system_file

The system_file resource (see providers/system) accepts a special attribute, content_file, which inlines a local file's bytes into content at config-load time.

resource "system_file" "traefik-config" {
  host         = host.primary.addr
  path         = "/etc/traefik/traefik.yml"
  content_file = "files/traefik.yml"
  mode         = "0644"
}

Semantics:

  • The value is a path relative to the .strat file's directory (not the current working directory).
  • The file is read at config-load time. Its bytes become the content attribute the provider sees. content_file itself is stripped — providers never see it.
  • The file must contain valid UTF-8 (it's loaded with std::fs::read_to_string).

Errors:

conditionerror variant
Both content and content_file on the same system_fileEvalError::ContentConflict
The referenced file does not exist or is unreadableEvalError::ContentFileMissing
Using content_file via stratum_config::load_str (no base)EvalError::ContentFileNoBaseDir

The third case only matters if you're embedding stratum-config in another program and calling load_str directly. The stratum CLI always uses load_file, so content_file always works in CLI flows.

This attribute is specific to system_file. The ssh_file resource only supports inline content. Use system_file if you want to load a file from disk.

source_dir on system_dir

The system_dir resource (see providers/system) accepts an optional source_dir attribute pointing at a local directory. Same base-dir rule as content_file:

resource "system_dir" "book" {
  host       = host.primary.addr
  source_dir = "../book/book"
  path       = "/srv/stratum-book"
}

Semantics:

  • The value is a path relative to the .strat file's directory.
  • At config-load time the path is joined with the base dir and std::fs::canonicalize'd. The provider sees the canonical absolute path. The original relative form is not preserved.
  • The directory must exist and be a directory.
  • Omitting source_dir is valid — the resource enters empty-dir mode, where only mkdir -p + chown + chmod run on the host.

Errors:

conditionerror variant
source_dir points at a missing path or non-directoryEvalError::SourceDirMissing
Using source_dir via stratum_config::load_str (no base dir)EvalError::SourceDirNoBaseDir

Unlike content_file, the contents are not inlined into state at config-load time — system_dir builds a fresh manifest from source_dir every plan and only ships bytes during apply.