A Rule interprets one managed or data resource instance in a plan JSON or state JSON. Its match decides eligibility; as assigns an optional Concept; emissions and composition add further architecture meaning. The Rule name alone creates no Concept or fact.
For the mental model, see Read a Rule.
dialect "example" { version = "0.1.0"
provider "hashicorp/aws" { version = ">= 6.0.0, < 7.0.0" }}
rule "bucket" { match { type = "aws_s3_bucket" }
as = rf.concept.object-storage-container
identity { attributes = ["bucket"] scope = "provider" }
endpoint { attributes = ["id", "bucket", "arn"] }}The official AWS Dialect uses this shape for buckets. bucket is an instance identity attribute; id, bucket, and arn are endpoint paths that a verified saved-plan traversal may name. Neither block publishes its values. See Fact emissions for target resolution.
A Rule must have as, at least one emission, or a nonempty composition. A match-only Rule fails validation with RULE_NO_ARCHITECTURE. The Rule has no description attribute. See Member resolution for per-instance evidence and Unresolved members for what remains when one member cannot be established.
match { kind = "resource" type = "aws_s3_bucket" where = source.bucket == "logs"}match has no label or nested block. where reads the instance's available source.* values. An unknown or sensitive operand can make the predicate indeterminate; it cannot make a candidate false. A bare traversal such as where = source.enabled is not a Boolean predicate and produces PREDICATE_UNRESOLVED during validation.
The language keeps a closed 15-value set. Plan/state analysis provides only the first two populations:
The other values remain accepted language syntax, but plan and state inputs contain no instances of those kinds, so a Rule that matches one never applies. type never glob-matches or follows the Rule name.
These attributes are declared on a target Rule, not inferred from a Concept. A target with an unavailable provider alias remains a possible candidate, so Rootform cannot force a unique value match around it. scope = "global" changes candidate eligibility, not the meaning of an identity value. With --plan-file, a direct reference to a declared endpoint can establish the exact instance even if its evaluated value is unknown or shared. Traversals and scope gives the supported expression syntax variants.
as assigns one Concept to an interpreted instance. A Rule can instead emit facts or compose members without a Concept. There is no Concept inheritance or automatic classification from resource type. The built-in RF Vocabulary supplies shared Concepts; a Dialect can declare local ones.
For each instance, Rootform checks, in order:
- mode (
resourceordata); - exact resource type;
- provider source address;
- optional
wherepredicate.
The provider source address determines Dialect binding. A shorthand such as
hashicorp/aws binds its corresponding Terraform and OpenTofu public-registry
addresses; a fully qualified host binds only that host. An unbound provider is
reported with PROVIDER_UNBOUND. The Dialect's provider version constraint
declares its compatibility envelope, but Rule selection does not compare it
with an observed exact provider version. A version constraint cannot
distinguish two Rules for the same instance.
If a resource type could match but no selected Dialect binds its provider address, interpretation fails with PROVIDER_UNBOUND before Rule selection. There is no priority by file order, Dialect origin, or Rule name. A managed or data instance with no matching Rule still has a Representation in the Form. It has no invented classification or emissions. Policy selection can also include an instance whose possible Rule is indeterminate or failed, producing an indeterminate policy evaluation; see Evaluation. Rule selection places this step in the analysis pipeline.
dialect "example" { version = "0.1.0"
provider "hashicorp/aws" { version = ">= 6.0.0" }}
rule "no-architecture" { match { type = "aws_s3_bucket" }}rootform validate dialects reports RULE_NO_ARCHITECTURE. Add an as, an emission, or a nonempty composition only when it expresses intended meaning. Test and validate shows how to check a Rule against a plan fixture.