Skip to content

A Policy Pack is an independent source unit. It owns a name, version, and Policies. This guide uses the public baseline Pack with the Azure commerce plan, which contains a Kubernetes cluster and a managed database. A Policy observes architectural facts; it never creates them. Exact RF Vocabulary and Dialect identities are derived when the Pack is linked.

Start with one source root

Policy Pack source Files
baseline/
├── pack.rf.hcl
├── policies/
│ ├── cluster-network-context.rf.hcl
│ └── managed-database-network-context.rf.hcl
├── LICENSE
└── NOTICE

Rootform discovers .rf.hcl and .rf.json recursively. Exactly one policy_pack declaration owns every top-level policy below this root.

policy-packs/baseline/pack.rf.hcl RF
policy_pack "baseline" {
version = "0.1.0"
}
policy-packs/baseline/policies/cluster-network-context.rf.hcl RF
policy "cluster-network-context" {
target {
concept = rf.concept.kubernetes-cluster
}
assert = (
exists(contexts(rf.context.network, rf.concept.virtual-network)) ||
exists(contexts(rf.context.network, rf.concept.subnet))
)
message = "Kubernetes clusters must belong to a network context."
}
policy-packs/baseline/policies/managed-database-network-context.rf.hcl RF
policy "managed-database-network-context" {
target {
concept = rf.concept.managed-database
}
assert = (
exists(contexts(rf.context.network, rf.concept.virtual-network)) ||
exists(contexts(rf.context.network, rf.concept.subnet))
)
message = "Managed databases must belong to a network context."
}

These two Policy blocks reproduce the baseline source.

For the evidence model behind an assertion, read Policies over facts.

  1. Name and version the Policy Pack

    policy_pack "baseline" establishes source identity. Policy IDs use owner-first syntax, for example baseline.policy.cluster-network-context. Names use lowercase kebab case. Version is exact MAJOR.MINOR.PATCH.

    No requires block exists. Policies use qualified references only. Linking resolves each referenced owner and symbol against the Form, then records exact versions and digests in the compiled Pack.

  2. Define target

    Each Policy has one target block. The baseline targets shared Concepts, so they can select matching Rules from different provider Dialects. The commerce walkthrough uses Azure. The target below is an AWS-only variant of the cluster Policy; keep its provider filters separate from the baseline used in this walkthrough.

    AWS-only target variant RF
    target {
    concept = rf.concept.kubernetes-cluster
    rules = [aws.rule.eks-cluster]
    dialects = ["aws"]
    }

    At least concept or rules is required. Values within each list are OR; present dimensions combine with AND. dialects filters owner of applied Rule. One selected Representation is evaluated once. Base Representation without matching Concept or applied Rule is not selected.

  3. Evaluate locally

    From the Rootform repository root, run the Azure commerce plan against the baseline Pack. Its saved plan pairs with the plan JSON and supplies the traversals needed to decide these network contexts. Plan inputs shows how to export both files from your own project with terraform or tofu. Saved plans and plan JSON can contain secrets in clear text; keep yours out of Git and public artifacts. Rootform reads them locally and keeps sensitive values out of its outputs.

    rootform run examples/playground/commerce-platform/head/plan.json \
    --plan-file examples/playground/commerce-platform/head/plan.tfplan \
    --no-serve -o analysis.json
    rootform check analysis.json --policy-pack ./policy-packs/baseline --color always
    rootform list policies --policy-pack ./policy-packs/baseline
    rootform show policy baseline.policy.cluster-network-context \
    --policy-pack ./policy-packs/baseline
    Shell
    Passing result, excerpt OUTPUT
    Policy check completed
    Policies 2 selected
    Evaluations 2
    Passed 2
    Verdict PASSED

    The check summary reports both selected targets passing and exits 0. A violation exits 1; indeterminate evidence or no selected decision exits 3. The latter two are not passes. list names both qualified Policies, while show prints the target and assertion without evaluating it. If a context is indeterminate, inspect the instance closure and confirm that the saved plan matches the JSON. The local override lasts one command and leaves rootform.lock unchanged.

    rootform check resultStatusWhat to do
    PASSED0All selected Policies passed. Confirm that at least one target was evaluated.
    VIOLATED1Read the named target and Policy message, then explain that Policy.
    INDETERMINATE3Inspect its closure reason; missing or unknown evidence cannot prove a pass.
    NO DECISION3At least one selected Policy had no target, or no Policy was selected, and no violation or indeterminate result takes priority. Another Policy passing does not change this. Check the Pack target and selected plan stage.

    The check walkthrough shows violations, indeterminate closures, and no-target results on small plans.

    Save the linked Pack against the Form when replay must use that exact semantic selection. analysis.json came from the preceding run:

    rootform compile policy-pack ./policy-packs/baseline --semantics analysis.json \
    --output baseline.compiled.json
    rootform check analysis.json --policy-pack baseline.compiled.json --color always
    Shell

    The compile command prints the Pack, semantic-pin count and destination. The check loads the Form without recompiling the plan and reports Policy outcomes with the check exit status. The compiled artifact records the authored content digest, linked digest, language version, and exact semantic identities. A mismatch fails closed. When the project should retain the source Pack, use rootform add policy-packs ./policy-packs/baseline from that project root and commit the Pack source with rootform.lock.

  4. Package and publish a Policy Pack

    Packaging is local and offline:

    rootform package policy-packs ./policy-packs/baseline \
    --to ./artifacts/policies \
    --source-url https://example.com/team/policies \
    --documentation-url https://example.com/team/policies/docs \
    --licenses Apache-2.0
    Shell

    The result names the Pack and local destination. Record the reviewed source revision with --revision when your publication process requires that provenance. Packaging itself sends nothing to a registry. Publication is separate and generic:

    rootform publish policy-packs ./artifacts/policies \
    --to registry.example.com/team/policy-packs
    Shell

    V0 has no mutable Policy Pack index. Existing version tag with different digest is rejected.

  5. Use published Policy Pack

    From the project root, add the published reference, then prepare its exact selection:

    cd ./infra
    rootform add policy-packs \
    registry.example.com/team/policy-packs:policy-pack-baseline-0.1.0
    rootform init . --locked --no-input
    Shell

    After exporting a plan for this project, evaluate the selected Pack with rootform check plan.json --locked.

    The registry reference is illustrative; replace it with the published one you reviewed. add records digests without hand editing the lock. Set DOCKER_CONFIG before acquisition if the registry needs credentials. See External content storage for paths.

See Policy Pack reference, evaluation, and compiled-policy-pack.schema.json.