Why Infrena exists

Infrastructure tools got good at describing resources and stayed bad at everything around them. Environments became a wrapper you build yourself. Wiring mistakes waited until an apply was half done to announce themselves. Adopting infrastructure you already ran meant writing configuration and hoping the first plan came back empty.

Infrena is an attempt at the parts that were left to you.

Four decisions that shaped everything else

Environments belong to the project

Not to a directory you copied, and not to a wrapper script. dev, staging and production are declared in one file, each with its own state and its own lock. What differs between them is variable values, resolved in a fixed and documented order. Removing an environment proposes a teardown rather than silently orphaning it.

environments: dev, staging, production

The provider's schema is the type system

Because a provider declares what each attribute means, pointing a field at the wrong kind of resource is a compile error. Not an API rejection after four resources already exist. This is also why you write vpc: ${vpc} and never have to remember whether that field wants an id, a name or an ARN.

$ infrena validate

Every value remembers where it came from

A plan can say whether you wrote a value, a module defaulted it, an environment overrode it or the provider supplied it. That provenance is not a debugging aid bolted on afterwards. It is carried through the whole engine, which is why the plan can print it at all.

size: 10 [default, from provider default]

The core stays small

Providers and state backends run as separate processes and ship separately, so the engine never grows an SDK. The engine has three third-party dependencies: a CLI library, a YAML parser, and one for reading a passphrase without echoing it. The AWS SDK lives in the AWS provider.

providers: - plugin: aws

Rules the project enforces on itself

These are not aspirations in a contributing guide. Each one is checked, and the checks are the reason they have survived contact with a deadline.

  1. A diagnostic must be worth reading

    Every message the tool prints has to say what is wrong, where it is, what was expected, and an action you can actually take. A message that fails any of those four is a defect, not a rough edge.

    infrena validate
    Error: fake.network has no attribute "nme"
      at infra.yml:14:5
    
      ${net.nme} reads an attribute that does not exist.
      Attributes of fake.network:
        cidr
        id
    
      Suggested action:
        Correct the attribute name.
  2. Every backend must lock, with no unsafe fallback

    The S3 backend proves your object store honours conditional writes before it serves anything: it writes a throwaway object twice and requires the second write to be refused. A store that ignores the header reports success both times, which looks exactly like a lock that never locks.

    So the bucket is refused when the project loads, rather than during an apply that is already under way. There is no flag to turn this off, because the failure it prevents is a silent one.

    tested against real stores
    AWS S3     passes the full live suite
    MinIO      passes the full live suite
    Backblaze  fails the check, and is refused
    R2         expected to work, not yet tested
    Spaces     expected to work, not yet tested

    An untested store costs you a clear message at load time rather than a lock that silently does nothing.

  3. Credentials are never configuration

    infra.yml gets committed. So there is deliberately nowhere in the backend: block to put a secret, and writing one there is an error that points you at profile: instead.

    Accepting it would have worked perfectly and put a long-lived key in a repository.

    infra.yml
    backend:
      plugin: s3
      bucket: my-infrena-state
      profile: ops          # a named profile, not a key
  4. Layout is organisation, never meaning

    One infra.yml and the same project split across directories produce byte-identical plans. That is pinned by a test, so moving files around is a layout change and can never become a behaviour change.

    A directory scopes variables and templates. It never scopes names, so moving a file renames nothing and destroys nothing.

    the equivalence test
    single-file project  ─┐
                          ├─→ identical plan
    directory project    ─┘

What stays free, and why that line cannot move

The boundary between the free CLI and a future commercial platform was written down while the project had no revenue and nothing to gain from blurring it. A boundary decided under pressure is a boundary that moves.

Infrena the CLI is free and open source, forever. Everything a single operator or a CI runner needs to manage infrastructure is core, and stays core.

The commercial platform sells what an organisation needs: coordination between people, and history over time.

Free, in the CLI, forever

  • Plan, apply, destroy, refresh, and the whole reconcile loop
  • Local and S3 remote state, locking included
  • Provider plugins, and the ability to write your own
  • Discovery and import of infrastructure you already run
  • Modules, environments, variables and the precedence rules
  • Drift detection on demand
  • prevent_destroy, retain and require_approval
  • Machine-readable NDJSON output
  • Running anywhere you like, including your own CI

State locking is explicitly free, including on S3. A free backend that could not enforce it would ship a corrupted-state hazard under this project's name. The platform sells coordination among many operators, never the mutex itself.

Six pairs that share a name

Several platform features have a counterpart that already ships in the CLI. They are additions, not replacements, and every row's left column is free forever.

In the CLI, freeWhat a platform would add
refresh detects drift on demandContinuous monitoring, and drift history over time
prevent_destroy, retain, require_approvalPolicy-as-code across many projects at once
Sensitive values redacted, never written to generated filesA managed secret store
--output writes an NDJSON report of a runRetained, queryable audit across an organisation
plan and apply in your own GitHub ActionsWatching a repository and triggering runs itself
State locking per environment, including on S3Coordination among many concurrent operators

Why this is not the Terraform story

Terraform was open source for a decade while Terraform Cloud was proprietary, and that arrangement was fine. What broke trust was changing the licence of the open tool itself, after people had built on the promise that it would not. Two things make that harder here, deliberately.

Apache 2.0 is irrevocable for anything published

A future licence change could only affect future releases. Every version that has ever shipped stays usable under the licence it shipped with, no matter what the project does later.

Moving a feature from free to paid is not allowed

Adding to the paid list is fine when the addition is genuinely new. Moving something the other way is not, and no commercial argument makes it so. If that ever seems necessary, the honest move is to say the promise is being broken, not to redefine the words.

Infrena also gives away more than Terraform did at the equivalent point. Remote state was the hook into Terraform Cloud; here S3 state, locking included, is free and always will be.

There is no platform code in the repository, no hooks for one, and no feature in the CLI shaped to leave room for one. The usual way open core goes wrong is not a dramatic relicensing. It is a slow accumulation of seams and stubs inside the free tool, until the free tool is shaped around a product its users cannot see. Anything in the repository that only makes sense if you know about a platform should be treated as a defect.

Where it is today

Maturity

Pre-1.0 and under active development, currently at 0.14.0. The engine is complete and tested: create, plan, apply, drift detection, refresh, import and destroy run end to end, with six correctness invariants asserted explicitly rather than assumed.

Licence

Apache 2.0. Binaries ship for macOS, Linux and Windows on Intel and ARM, each release with a SHA256SUMS file.

Clouds

One provider plugin covers AWS (0.7.0), serving every resource type Cloud Control API supports. A fake provider (0.5.0) keeps its cloud in a local JSON file so the whole lifecycle can be exercised with no account and no network.

Getting involved

Development is open, with a visible roadmap. Issues and discussions are on GitHub, and the documentation covers the configuration language end to end.