IaC Bazaar

Install Vizier Local

Vizier Local is a free download. It runs OpenTofu or Terraform for you, and refuses to run a module whose provenance it cannot check. This page takes you from an empty directory to a proven apply, then to running your own modules.

Install

On macOS and Linux the installer resolves the latest release, downloads the right archive for your machine, and refuses to install if the checksum does not match. A tool whose job is checking what you run has no business installing itself unverified.

macOS, Linux
URL=https://raw.githubusercontent.com/CyberCoreSystems/vizier/main/install.sh

curl -fsSL "$URL" | sh                        # the latest release
VIZIER_VERSION=1.6.0 curl -fsSL "$URL" | sh   # or pin a version
VIZIER_BIN_DIR=~/.local/bin curl -fsSL "$URL" | sh

On Windows, take the zip from the releases page and check it yourself. The installer stops with that instruction rather than guessing.

Windows (PowerShell)
$v = "1.6.0"
$base = "https://github.com/CyberCoreSystems/vizier/releases/download/v$v"
Invoke-WebRequest "$base/vizier_${v}_windows_amd64.zip" -OutFile vizier.zip
Invoke-WebRequest "$base/checksums.txt" -OutFile checksums.txt

# compare against the published checksum before you unpack it
(Get-FileHash vizier.zip -Algorithm SHA256).Hash.ToLower()
Select-String "windows_amd64.zip" checksums.txt

Expand-Archive vizier.zip -DestinationPath .   # put vizier.exe on your PATH

Confirm it is there:

$ vizier version
vizier 1.6.0

The engine

Vizier does not replace OpenTofu or Terraform - it runs one of them. Install OpenTofu (or Terraform) and make sure it is on your PATH. vizier info reports which one it found, which is the answer to most "it works on my machine" questions.

To pin a specific engine, pass --tofu-bin or set VIZIER_TOFU_BIN. A named engine is a requirement, not a preference: if it cannot be run, Vizier stops and says so rather than falling back to whatever is on PATH, which would not be the engine you asked for.

Your first module

Ask the catalog for a module and it prints the exact source line to paste. A catalog source always names a version: an unpinned module would change under your tree without your tree changing, which is the drift Vizier exists to catch.

find a module
$ vizier catalog list
aws-acm                    live_tested   signed   ACM Certificate (DNS-validated)
aws-s3-bucket              live_tested   signed   AWS S3 Bucket (hardened)
...
183 module(s). `vizier catalog show <slug>` prints the source line to paste.

$ vizier catalog show aws-s3-bucket
slug:     aws-s3-bucket
version:  1.1.0
status:   live_tested
signed:   true (catalog claim; `vizier verify` checks it)
sha256:   87b4ffc46b3964c6c7c004ebf75de5c5e9c0e509c82cc8af2254bb3282d3a005
source:   iacbazaar://[email protected]

Put that source in a vizier.hcl, in its own directory. That directory is a unit: one module, one state, one set of inputs.

vizier.hcl
terraform {
  source = "iacbazaar://[email protected]"
}

inputs = {
  bucket_name = "acme-artifacts-eu-north-1"
}

Now check it before running anything. verify touches no cloud and needs no credentials: it asks the catalog what was published, checks the signature against a key compiled into the binary, and reports what it found.

$ vizier verify
[ok] . - [email protected] proven (live_tested, signature verified)

signature verified means the signature was checked, not that the catalog claims one exists. live_tested means that module was really applied against a live cloud account, verified, and destroyed again before it was published - what that does and does not claim.

vizier scaffold iacbazaar://aws-s3-bucketwrites the same file for you and fills in the current version. A module's input list is part of its published contract, so run vizier login first if you want the inputs filled in too.

Plan, apply, destroy

These are the commands you already know. Vizier verifies first, materialises the module, writes the backend if the unit declares one, and then runs the engine.

vizier plan            # inside the unit directory
vizier apply
vizier destroy

vizier run-all plan    # every unit in the tree, in dependency order
vizier verify          # proof status only, no engine, no cloud

Apply and destroy prompt for confirmation. Where there is no terminal, they refuse rather than assume: CI has to pass --auto-approve and say so out loud, because silence is not consent.

Your own modules

Most teams have modules of their own. Vizier runs them, but it will not pretend to have checked something it has not: a module we did not publish has no signature from us. There are three ways forward, in descending order of evidence, and with none of them Vizier stops.

$ vizier plan
[BLOCK] . - git::https://github.com/acme/infra.git//vpc is not an
        IaC Bazaar module, so there is nothing to verify it against.
        Pin it with verify { source_sha256 = "..." }, allow-list its
        prefix with allow_unverified, or set allow_any_source = true
        to run unpinned code deliberately

1. Pin the digest. You declare what the code should be, and Vizier checks what it fetched against that. This is real provenance, supplied by you rather than by us, and it needs no catalog and no signing key. Run it once unpinned and Vizier prints the digest to paste.

vizier.hcl
terraform {
  source = "git::https://github.com/acme/infra.git//vpc?ref=v2.4.0"
}

verify {
  source_sha256 = "9f2c...the digest vizier printed..."
}

If the tree ever differs from that digest, the run stops and shows both values. A tag can be moved; a digest cannot.

2. Allow-list a prefix. allow_unverified = ["git::https://github.com/acme/"] covers a whole repository or organisation. Admitted, and recorded in the receipt as unverified.

3. Say so for the whole tree. allow_any_source = true means this tree runs code from anywhere. It is explicit, and the receipt says so.

Public registry modules

Modules from the public Terraform registry work, in both the plain and the tfr:// spellings. Pin the version in the source: a registry module with no version means whatever was published this morning.

terraform { source = "terraform-aws-modules/vpc/aws?version=5.1.0" }
terraform { source = "tfr:///terraform-aws-modules/vpc/aws?version=5.1.0" }

# git, an archive over HTTPS, S3, GCS and a local path all work too
terraform { source = "../../modules/vpc" }
A query parameter a registry module does not understand - ?ref=main, say - is refused rather than ignored. An ignored pin is worse than a rejected one, because the run looks pinned while running code nobody chose.

Where state lives

A fetched module is a cache, unpacked into .vizier-module beside the unit, and it can be thrown away and fetched again. Your state is not a cache. With a local backend the engine writes terraform.tfstate next to the configuration, so it sits inside that same directory - and --source-update is careful to move it aside and put it back rather than delete it with the rest.

vizier plan --source-update    # re-fetch the module, keep the state beside it
No changes. Your infrastructure matches the configuration.

For anything shared, declare a remote_state block and let Vizier write the backend file. vizier backend creates and moves the state stores a tree writes to.

In CI

There is a GitHub Action, and worked examples for Jenkins, Azure Pipelines, AWS CodeBuild, Atlassian and Spacelift in the public repository. Pin a version in CI rather than taking the latest.

.github/workflows/infra.yml
- uses: CyberCoreSystems/[email protected]
  with:
    action: plan
    dir: envs/prod
    verify-mode: enforce

Leave verify-mode on enforce. warn still runs modules that failed the check and records the bypass in the receipt, which is a different thing from a pipeline that stops.

For a team

Everything above is one operator on one machine, answerable to the policy written in the tree. When the policy needs to belong to the organisation instead - roles, an evidence floor per environment, review before an apply, shared state locking and durable history - that is Vizier Central, which you host yourself.