HCL schema reference#

Every block and attribute Orchard’s parser accepts. The two authoring contexts are scenario files and component files.

Scenario files#

A scenario file contains exactly one scenario block.

scenario#

scenario "<name>" {
  description = "..."           # optional
  tags        = ["a", "b"]      # optional

  required_providers { ... }
  variable "<name>"  { ... }
  provider "<name>"  { ... }
  component "<type>" "<name>" { ... }
  action "<type>" "<name>"    { ... }
  output "<name>"    { ... }
}

A single HCL file may contain multiple scenario blocks. When it does, orchard run and orchard plan require --scenario <name> to pick one. Single-scenario files work without the flag.

AttributeTypeRequiredDescription
descriptionstringnoFree-text summary. Shown by orchard list.
tagslist(string)noCategorisation labels. Shown by orchard list in violet brackets.
BlockCardinalityPurpose
required_providers0..1Declare which providers the scenario uses.
variable0..nScenario inputs.
provider0..nProvider configurations.
component0..nInstances of reusable components.
action0..nExecutable units.
output0..nValues exposed to the caller.

required_providers#

required_providers {
  postgres = { source = "builtin/postgres" }
  stripe   = { source = "exec:./bin/orchard-stripe" }
}

A flat attribute set, one entry per provider. Each entry is an object with:

AttributeTypeRequiredDescription
sourcestringyesProvider source: builtin/<name> or exec:<path>.

variable#

variable "dsn" {
  type        = string
  default     = "postgres://localhost/dev"
  description = "Database connection string"
}
AttributeTypeRequiredDescription
typetype expressionnoDeclared type. Defaults to dynamic (any) if omitted.
defaultanynoDefault value. Used when no CLI override is given.
descriptionstringnoFree-text documentation.

Variables without a default must be supplied via --var at runtime.

provider#

provider "postgres" {
  dsn = var.dsn
}

The label selects which provider in required_providers is being configured. The body is provider-specific — see the providers reference.

Provider configuration is evaluated with variables in scope; it cannot reference action or component outputs.

component#

component "merchant" "acme" {
  source = "../components/merchant.hcl"
  inputs = {
    name = "Acme Corp"
  }
}
LabelPurpose
FirstComponent type (must match component "<type>" { } in the source file).
SecondInstance name, unique within the scenario.
AttributeTypeRequiredDescription
sourcestringyesPath to the component file. Resolved relative to this scenario file.
inputsobjectValues for the component’s declared variables. Required if the component has any required variables.

action#

action "postgres_query" "create_merchant" {
  query = "INSERT INTO ..."
}
LabelPurpose
FirstAction type (must match an action declared by some provider).
SecondAction name, unique within the scenario or component.

Body attributes are defined by the provider’s schema. In addition, Orchard reserves a few attributes for engine-level behavior:

AttributeTypeDescription
providerstringExplicit provider alias. Required only when two providers offer the same action type.
depends_onlist(reference)Explicit dependency edges. Each entry is a reference to another action or component node.
timeoutstringGo-style duration (e.g. "30s", "5m"). Wraps the provider’s Execute call.
retryblockRetry policy. See below.
lifecycleblockTeardown configuration. See below.

retry block#

action "http_request" "flaky" {
  url = "https://example.com"
  retry {
    max_attempts = 5
    delay        = "1s"
    backoff      = "exponential"
    max_delay    = "30s"
  }
}
AttributeTypeDescription
max_attemptsnumberMaximum number of attempts including the first.
delaystringInitial delay between attempts, as a Go duration.
backoffstring"constant" or "exponential".
max_delaystringCap for exponential backoff.

lifecycle block#

Declares a teardown operation for the action. Read by orchard teardown <record> when walking the graph in reverse.

action "postgres_query" "create_merchant" {
  query = "INSERT INTO merchants (name) VALUES ('${var.name}') RETURNING id, name"

  lifecycle {
    teardown {
      query = "DELETE FROM merchants WHERE id = ${self.id}"
    }
  }
}

The teardown block’s body uses the same schema as the outer action’s provider. A sql action’s teardown declares query; an http action’s declares method + url; an exec action’s declares command + args.

Inside the teardown body, three reference namespaces are in scope:

  • var.<name> — scenario variables (from the record).
  • self.<field> — this action’s own recorded outputs. Use it to target the resources this action created, without restating the node name.
  • action.<type>.<name>.<field> — any other action’s recorded outputs, for teardowns that reference upstream state.

See lifecycle for authoring guidance and the self namespace under expressions.

output#

output "merchant_id" {
  value = action.postgres_query.create_merchant.id
}
AttributeTypeRequiredDescription
valueany expressionyesExpression to evaluate after the scenario completes.

Component files#

A component file contains exactly one component block.

component#

component "merchant" {
  description = "..."           # optional
  tags        = ["seed"]        # optional

  variable "<name>" { ... }
  action "<type>" "<name>" { ... }
  output "<name>"   { ... }
}

The label is the component’s type. Component files may not declare required_providers or provider blocks — components inherit providers from the enclosing scenario.

AttributeTypeRequiredDescription
descriptionstringnoFree-text summary.
tagslist(string)noCategorisation labels.

Inside a component, variable, action, and output have the same schema as in a scenario.

Components reference their own variables as var.<name>, their own actions as action.<type>.<name>.<output>, and cannot reach scenario-level actions or components.

Evaluation order#

  1. Parse — decode scenario and component files structurally. Action bodies and output expressions are captured but not evaluated.
  2. Resolve variables — apply defaults and CLI overrides.
  3. Evaluate provider configs — with variables in scope.
  4. Build graph — derive edges from expr.Variables() traversals plus depends_on lists.
  5. Topological sort, then execute each node:
    • For an action: evaluate its body, call provider Execute, record outputs.
    • For a component: resolve inputs, execute internal actions, resolve outputs.
  6. Evaluate scenario outputs with all node results in scope.