§ Reference · Lookup
FAQ
Short, factual answers to the questions asked first.
On this page
| Question | Answer in one line |
|---|---|
| Does pravo need OpenTofu? | No. |
| Which providers work? | Those that speak plugin protocol 5 or 6. |
| Which registry is used? | registry.opentofu.org, or the host in the import path. |
| Can pravo read my OpenTofu state? | Once, by conversion. |
| Is there remote state? | Yes: S3 and compatible stores. |
| Do I need to split a large estate into several states? | Not to keep plans short. |
| How do I pass a variable? | -t name=value. |
| How do I pass a secret? | pravo.#SecretEnv. |
| How do I enforce rules on plans? | --policy FILE. |
Does pravo need OpenTofu?
No. pravo downloads provider binaries and starts them itself. The tofu binary is never run. pravo finds an already unpacked provider in its own store, then in the plugin cache that TF_PLUGIN_CACHE_DIR names or in ~/.terraform.d/plugin-cache.
Which providers work?
Providers that serve plugin protocol 5 or 6 are started and called for the operations that Compatibility lists. Ephemeral resources, write-only attributes and provider-defined functions are supported.
Which registry is used?
registry.opentofu.org. A package imported from pravo.dev/providers/hashicorp/random resolves there. To use another registry, put its host in the import path: pravo.dev/providers/registry.example.com/hashicorp/random. Any registry that serves /.well-known/terraform.json and the /v1/providers/... endpoints works, and so does an OCI registry.
Do I have to declare providers?
Yes, one line each: $random: rnd.#Provider & {$version: "~> 3.6"}. A provider that needs no configuration is $local: loc.#Provider. Every resource is built through a declared provider. See Providers.
Can pravo read my OpenTofu state?
pravo plan and pravo apply never read terraform.tfstate, and a state.path that names it is an error. pravo state import-tfstate FILE converts a state file of format version 4 into the state graph, once. It prints the renamed instances and the records it skips. See Not in pravo.
Is there remote state?
Yes. The state graph can live in an S3 bucket or in an S3-compatible store that honours conditional writes (If-None-Match: * and If-Match). S3-compatible stores says how to test a store. The layout is the same as the local one. See Settings and State format.
Do I need to split a large estate into several states?
Not to keep plans and applies short. The state keeps one record for each resource, with its dependencies. A plan reads and plans what a change reaches, a refresh age keeps it from reading a record again within that age, and an apply locks only the nodes it writes. What a command does for a change follows the change, whatever the number of records. Evaluating the specification and opening the records grow with the estate, and a change that reaches every resource costs what the estate costs. Split a state for ownership, permissions or blast radius. See Cost follows the change.
What do "skipped", "not read" and "within refresh age" mean?
A plan walks the part of the graph a change reaches. A resource whose configuration hash matches the state, and whose provider and schema versions match, is not validated or planned. Its record is not read from the provider either, unless the plan reaches a resource that reads it, the resource sets a refresh age of its own that has run out, or the record has a mark. The Graph: line counts what was planned, skipped, refreshed, within its refresh age and not read. --full plans every resource, -refresh=expired reads every record that is past its refresh age and -refresh=all reads every record. See Plans and The plan document.
Does a plan find drift in every resource?
No. A plan finds drift in the resources it looks at: those it reaches and those they read. pravo drift reads every record, and pravo drift --due --limit N reads the oldest N records that are past their refresh age. See Refresh age and CI.
Does a plan change anything?
A plan is not read-only. It installs providers, writes the lock file, refreshes the records it looks at whose refresh age ran out, marks a record whose object differs or is gone, renames records for $from, adopts the objects named by $import, reads data sources and opens ephemeral resources. It creates, updates and destroys no infrastructure. See Before the verdict.
Where are moved and import blocks?
They are fields of the resource they describe. $from: "old.address" is a move, and $import: "id" adopts an object. See Moves and imports.
Where are provisioners, workspaces and -target?
They are absent. Not in pravo lists each with its replacement.
How do I pass a variable?
With -t name=value on any command that evaluates the specification. It sets the field tagged @tag(name). Defaults and constraints are CUE on the field. -T injects the system tags. There is no variables file: a .cue file of the package sets a set of values. See Tags and variables.
How do I pass a secret?
Not with -t: a tag value is written into saved plans. Read it from the environment with pravo.#SecretEnv and use it in provider configuration, in a write-only attribute or in another ephemeral resource. pravo does not let it reach anything that is recorded. See Environment and remote outputs.
How does one root use the outputs of another?
With pravo.#RemoteOutputs. Only the other root's outputs.json is read, so read access to that one object is enough. Apply the producer first. See Environment and remote outputs.
How do I enforce rules on plans?
With a plan policy: one CUE file that whoever runs pravo names with --policy FILE on plan, apply, destroy or mcp. A plan that violates it is printed and refused with exit code 5. A run that omits the flag is not checked. See Policies overview and the trust anchor.
How do I see what pravo is doing?
Run the command with -v. It logs the load, the provider versions and where they came from, the state it opened and, for each resource, the action and the reasons. -vv adds the providers' own log. pravo why ADDR explains one plan. OTEL_TRACES_EXPORTER=console prints traces. See Telemetry.
What is the address of a resource?
The CUE path at which its struct sits: pet, api.config, servers[0], pets.eu. pravo state list prints the addresses, and --replace, pravo import and pravo why take them. See Resources.
Do I have to run pravo init before pravo validate?
When the specification imports a generated package, yes. pravo init installs the providers, writes the lock file, and writes the packages under cue.mod/gen/. A directory of plain CUE with no import validates without it.
package demo
size: >=1 & <=20 | *5
name: "web\(size)"
Success! 0 resources, 0 data sources, 0 ephemeral, 0 function calls, 0 outputs across 0 providers.
Does pravo eval write anything?
No. It reads the state of the root to fill in the values that are recorded there. When the state does not exist, eval reads an empty one and creates nothing.
size: 5
name: "web5"
no state directory
How do I upgrade a provider?
pravo init --upgrade ignores the lock file and selects the newest version that matches $version. It rewrites pravo.lock.cue and the generated package. See init.
Does pravo send telemetry?
No. pravo exports OpenTelemetry traces and metrics only when the OTEL_* variables configure it. See Telemetry.
What is the license?
Apache-2.0. The protocol definitions under internal/tfplugin/proto are licensed MPL-2.0. Providers keep their own licenses, which the registry shows for each provider.
How do I install it?
Install a release with the script at https://pravo.systems/install.sh (macOS and Linux) or install.ps1 (Windows), or download an archive from the releases page. To build it instead, use a checkout of the repository with Go 1.26 or newer: go build -o bin/pravo ./cmd/pravo. See the quick start.
Which version is this?
pravo version prints the version of pravo and of the CUE it embeds. The footer of each page names the versions it was written against. See Compatibility.
See also
- Not in pravo lists the features that pravo does not have.
- Error catalogue lists the messages pravo prints.