Skip to content

A Dialect is one named and versioned interpretation unit. Its Rules add architecture meaning to observed plan or state instances.

Definitions and Rules are top-level siblings of the dialect block. Only provider blocks are nested inside dialect.

Complete layout

example-dialect/dialect.rf.hcl RF
dialect "example" {
version = "0.1.0"
provider "hashicorp/example" {
version = ">= 1.0.0, < 2.0.0"
}
}
concept "application" {
description = "A deployable application service."
}
context "runtime" {
description = "The runtime selected for an application."
}
relation "calls" {
description = "A declared application dependency."
}

A source root may split these blocks across any number of .rf.hcl and .rf.json files.

dialect block

PropertyContract
PlacementTop level of a Dialect source root
CardinalityExactly one per source root
LabelsExactly one required Dialect owner
Attributesversion
Nested blocksprovider only

Parameters

NameTypeRequiredDefaultConstraints
LabelIdentifierYesNoneLowercase kebab case, 1-64 bytes; rf forbidden
versionStatic stringYesNoneExact MAJOR.MINOR.PATCH

Canonical unit identity combines label and version, for example example@0.1.0.

A Dialect has no authored dependency list. It may reference its own symbols and rf.* only. Compiler derives RF Vocabulary dependency when used.

provider block

provider envelopes RF
dialect "example" {
version = "0.1.0"
provider "hashicorp/example" {
version = ">= 1.4.0, < 2.0.0"
}
provider "registry.example.com/acme/platform" {
version = "~> 3.2.0"
}
}
PropertyContract
PlacementInside dialect
CardinalityZero or more; at least one when source root declares any Rule
LabelsExactly one required provider source
Attributesversion
Nested blocksNone

Parameters

NameTypeRequiredDefaultConstraints
LabelProvider sourceYesNonenamespace/type or host/namespace/type
versionStatic stringYesNoneOne or more comma-separated clauses

Accepted clause operators are =, !=, >, >=, <, <=, and ~>. Every clause must include an operator and an exact three-component version:

EBNF
provider-constraint = clause, { ",", clause } ;
clause = operator, version ;
operator = "=" | "!=" | ">" | ">=" | "<" | "<=" | "~>" ;

All clauses combine with logical AND against one known exact provider version.

OperatorVersion test
= 3.2.0Equal to 3.2.0
!= 3.2.0Not equal to 3.2.0
> 3.2.0Greater than 3.2.0
>= 3.2.0Greater than or equal to 3.2.0
< 3.2.0Less than 3.2.0
<= 3.2.0Less than or equal to 3.2.0
~> 3.2.0Greater than or equal to 3.2.0 and less than 3.3.0

Whitespace around clauses is normalized. Duplicate clauses are removed and remaining clauses are sorted in compiled output. A bare version such as "1.4.0" is invalid.

Two-part provider sources bind the equivalent Terraform and OpenTofu public registry addresses for that namespace and type. Three-part sources require exact host, namespace, and type. Duplicate normalized provider sources produce DUPLICATE_ID.

Provider source binding participates in Rule eligibility; plan/state selection does not compare an observed exact provider version to the declared envelope:

Every Rule in the Dialect uses this shared provider list. Rules do not declare their own provider selector.

  • a fully qualified provider address binds that exact registry host;
  • a public-registry shorthand binds equivalent Terraform and OpenTofu public registry addresses for the same namespace and type;
  • an observed provider without a selected binding produces PROVIDER_UNBOUND and leaves interpretation unsettled;
  • an unavailable provider alias is uncertain, not proof of a different provider.

Semantic definition blocks

concept, top-level context, and top-level relation share one shape:

definitions.rf.hcl RF
concept "database" {
description = "A database service."
}
context "runtime" {
description = "The runtime selected for a service."
}
relation "reads-from" {
description = "A declared read dependency."
}
PropertyContract
PlacementTop level of a Dialect source root
CardinalityZero or more of each kind
LabelsExactly one required name
AttributesOptional description
Nested blocksNone

Parameters

NameTypeRequiredDefaultConstraints
LabelIdentifierYesNoneLowercase kebab case, 1-64 bytes; unique within symbol kind
descriptionStatic stringNoEmptyValid UTF-8, at most 1,024 bytes; empty allowed

Meaning

BlockDefinesDoes not define
conceptOptional nominal classificationShape, inheritance, properties, or structural coverage
contextNamed directed placement dimensionA fact by itself
relationNamed directed relation predicateA fact by itself

Rules emit concrete Contexts, Relations, and Contributions. Concepts classify a representation only when a Rule uses as. A Concept is optional and never inferred from a Rule name or source type.

Descriptions are editorial metadata. They affect exact content identity but are excluded from Dialect semantic digest.

Labeled Rule emissions may introduce a local Context or Relation without a separate top-level definition. Top-level definition remains useful for a description. Concepts always require explicit definition.

Allowed references

A Dialect may resolve:

Reference syntaxScope
concept.name, context.name, relation.name, rule.nameCurrent Dialect
owner.kind.name where owner equals current DialectCurrent Dialect
Supported rf.* IDEmbedded RF Vocabulary

Foreign Dialect references are invalid. File location never changes scope. See Symbols and references for complete resolution rules.

Rejected syntax

invalid-provider.rf.hcl RF
dialect "example" {
version = "0.1.0"
provider "example" {
version = "1.4.0"
}
}

Provider source has too few slash-separated segments and version clause has no operator. Compiler reports PROVIDER_INVALID.

A requires block, nested concept, unknown attribute, or second dialect block also fails closed.