Getting started with Nivis
A hands-on walkthrough using the in-repo fake providers. Everything here runs offline: no provider registry, no cloud account, no credentials. You need Go 1.22+ and Nix.
1. Build the binaries
go build -o bin/provider-alpha ./cmd/provider-alpha
go build -o bin/provider-beta ./cmd/provider-beta
go build -o bin/nivis ./cmd/nivis
provider-alpha and provider-beta are minimal tfprotov6 providers used as a
hermetic test substrate. Their outputs are a deterministic function of inputs (a
per-process counter seeded by TERRAE_NIVIS_FAKE_COUNTER, default 0), so every run is
reproducible.
Prefer Nix? The flake builds the CLIs from source:
nix run .#nivis -- …andnix run .#nivis-gen -- …(ornix build .#nivis). Everywhere below,./bin/niviscan be read asnix run .#nivis --. You still build the fake providers withgo build(they aren't packaged as apps).
2. The example configuration
The flake's nivis.plan (in nix/example/) describes three resources and a
consumer, wired so each hop crosses the Nix boundary:
alpha_token.A (alpha) -- no inputs; A.value computed at apply
└─ name = "rec-" + A.value (a __derived value)
beta_record.B (beta) from = name -- B.endpoint computed at apply
└─ final = B.endpoint + "::" + A.value (a __derived on BOTH providers)
alpha_token.C (alpha) label = final
systemConfig (a Nix consumer) reads:
recordEndpoint = B.endpoint # from beta
tokenValue = A.value # from alpha
combined = final # from both
Because name and final are values Nix computes from provider outputs, they
can't be known until those outputs exist and Nix is re-evaluated, which is what
forces multiple phases.
3. Plan and apply
./bin/nivis plan
+ alpha.alpha_token.A (alpha_token)
+ beta.beta_record.B (beta_record)
+ alpha.alpha_token.C (alpha_token)
3 resource(s) to resolve across phases. Run `nivis apply`.
./bin/nivis apply
Applied 3 resource(s) across 3 phase(s):
✓ alpha.alpha_token.A
✓ beta.beta_record.B
✓ alpha.alpha_token.C
Three phases, not one: phase 1 applies A (nothing else is ready); re-evaluating
with A.value known unlocks B; re-evaluating with B.endpoint known unlocks C.
The loop halts at a fixpoint once nothing new resolves.
4. Inspect the round trip
./bin/nivis state list
./bin/nivis state show alpha.alpha_token.C
alpha.alpha_token.C (alpha_token)
id = alpha-1
label = beta://rec-alpha::0::alpha::0
value = alpha:beta://rec-alpha::0::alpha::0:1
C.label is final: a string Nix built from both B.endpoint (beta) and
A.value (alpha). That value only became concrete after both providers applied
and Nix re-evaluated. That is the round trip.
5. Refresh and destroy
./bin/nivis refresh # reconciles state via ReadResource; no changes here
./bin/nivis destroy # tears down in reverse dependency order
Destroyed 3 resource(s):
- alpha.alpha_token.C
- beta.beta_record.B
- alpha.alpha_token.A
6. Generate constructors from a provider schema
nivis gen turns any provider's schema into typed Nix constructors:
go run ./cmd/nivis gen -- --provider ./bin/provider-alpha --out ./generated
cat ./generated/alpha/alpha_token.nix
The generated constructor requires the provider's required inputs (throwing a
named error if missing), passes optional inputs through, omits computed-only
attributes (they're outputs), and accepts an overrides argument so you can
adjust the generated output.
7. A real provider (AWS)
Everything above is offline against the fakes. The same nivis commands drive
real providers: nivis resolves a provider by address from the OpenTofu
registry, downloads and checksum-verifies the binary, negotiates the plugin
protocol (AWS speaks v5), configures it, and runs plan/apply/destroy. The example
nix/example/aws.nix (flake attr nivis.aws) declares the hashicorp/aws
provider with mkProvider and one aws_s3_bucket.
⚠️ This creates a real resource in your AWS account: one (free-tier) S3 bucket, then destroys it. The provider's
regionlives in the Nix config; only credentials come from the environment (the AWS SDK default chain), so setAWS_PROFILE(orAWS_ACCESS_KEY_ID/…). The first run downloads the ~900 MB AWS provider (cached afterwards).
For the full, hand-held walkthrough (prerequisites, writing the config line by
line, plan/apply/inspecting state/destroy, and troubleshooting) follow the
AWS S3 tutorial.
Where to go next
IR-CONTRACT.md+ir-schema.json: the IR, the stable contract between the Nix frontend and the Go executor.TESTING.md: the test layers and the headline two-provider e2e.DESIGN.md: why the architecture is the way it is (spawn-not-link, batch-not-live, phased re-eval to a fixpoint).
The core test suite is hermetic (fakes, no network/credentials); real-provider support (registry download + checksum verification, tfprotov5/6) is proven against AWS as shown above.