compliance.tf

Promotion and Gates

  • A snapshot is a resolved compliance posture, frozen at publication. Its content is immutable; only its approval status changes afterwards.
  • A binding points one project environment at one snapshot. That binding is what the registry serves.
  • Promotion is the act of moving a snapshot into an environment. Protected environments run it past a gate first.
  • Waivers are exceptions with a stated expiry. Approved, unexpired waivers are frozen into the binding at promotion time — after which editing, revoking, or outliving the expiry does not change what production is already serving.
  • Rollback repoints an environment at the previous snapshot. It moves the snapshot only — the frozen waiver set is left exactly as the last promotion built it.

Why promotion exists

A compliance posture that can be edited in place has no defensible history. If an auditor asks what production enforced on a given date, "the current configuration" is not an answer — it changes every time someone edits it.

compliance.tf separates authoring from what is live. You edit a draft freely. Publishing freezes that draft into a snapshot that can never be modified. An environment is then bound to a specific snapshot. Production changes only when someone deliberately promotes a different snapshot to it, and that act is recorded.

The objects

ObjectMutable?What it is
ConfigYesA named workspace holding a draft posture: which frameworks are enforced, which controls are added or forbidden, which operational rules apply.
DraftYesThe editable content of a config. Concurrent edits are detected rather than silently merged.
SnapshotNoA published, fully resolved posture — frameworks expanded to a concrete control set. Only its approval status can change after publication.
BindingYesThe pointer from one project environment to one snapshot, plus the waivers frozen at the moment of binding.
WaiverYesAn approved exception to one control or rule, scoped to one environment, carrying a stated expiry.

Environments

Every project has exactly three environments: dev, staging, and prod. They are not created or named — they exist for every project, and a record for one appears the first time you act on it, whether that is binding a snapshot or proposing a waiver.

An environment is either protected or not. Protection is what turns a bind into a gated promotion. By default prod is protected and the other two are not.

  • Unprotected environment. Binding a snapshot is a direct action: no gate runs. Authorization still applies — you need permission to bind — but none of the gate conditions below are evaluated. This is deliberate; dev should be fast to iterate against.
  • Protected environment. Every attempt runs the promotion gate first, and the gate can refuse.

The lifecycle

From draft to a bound environment State diagram: a draft is edited and published into a published snapshot, approval makes it approved; an approved snapshot is bound to an unprotected environment or promoted to a protected one, and a protected environment can roll back to the previous snapshot. EDIT PUBLISH APPROVE BIND PROMOTE ROLLBACK Drafteditable Snapshot: publishedimmutable Snapshot: approvedgate passed Unprotected envdev · staging Protected envprod Previous snapshotrestored binding LEGENDMutable / bindingImmutable snapshotGated promotionRollback outcome

Publishing and approving are separate steps on purpose. Publishing says "this posture is finished". Approving says "someone accountable has reviewed it". A protected environment requires the second signal, not just the first.

Promoting

A promotion names a snapshot and a target environment. On a protected environment the gate evaluates the request and returns a list of blockers. Each blocker has a severity: a block refuses the promotion, a warn records a concern and lets it through.

You do not have to attempt a promotion to find out how the gate will judge it. The preview endpoint runs the same gate without changing anything, and returns the blockers alongside a diff of what would change and which waivers would carry over. It reports the gate's verdict on the state at the moment you ask, and reserves nothing: anything that changes afterwards is evaluated afresh when you promote.

Waivers

A waiver records a decision to accept a specific gap for a limited time. It names the control or rule, an expiry, and a justification. Waivers are proposed and approved as separate acts.

The important property is when a waiver takes effect on an environment, and the matching question of when its expiry takes effect.

A waiver sitting on an environment is only a proposal until a promotion happens. At promotion time, waivers that are both approved and unexpired are copied into the binding and frozen there.

The expiry is checked at that moment — the freeze — and not afterwards. Once a waiver is frozen into a binding, it applies to what the registry serves for as long as that binding stands, whether or not its stated expiry has since passed. An expiry is therefore a rule about which waivers are eligible to be frozen, not a timer that withdraws a live exception.

To clear an expired waiver from an environment, promote a different snapshot to it — a bind or promote that actually writes a new binding is what re-runs the eligibility check and rebuilds the frozen set. Two things that look like they would work do not: re-promoting the snapshot already bound is a no-change write, and a rollback repoints the snapshot without rebuilding the frozen set. Neither refreshes anything. If you need to audit this across an estate, read each environment's frozen waiver set and compare the expiry dates against today rather than assuming lapsed entries have fallen out.

Consequences worth understanding:

  • Approving a waiver does not change what production serves. The next promotion does.
  • Revoking a waiver does not change what production serves either. The frozen copy stays in effect until a bind or promote writes a new binding and drops it.
  • A waiver that expires stops being carried into new bindings, but the already-frozen copy stays in force until a bind or promote writes a new binding for that environment. Rollback is not such an operation: it repoints the snapshot on its own code path, without rebuilding the frozen waiver set.

This is what makes a binding reproducible. What the registry serves for an environment is fully determined by the binding, not by the current state of an editable waiver list.

Operating the gate

The rest of this page is the operator's half: every gate blocker and what raises it, the guarantees that hold under concurrent promotions and partial failure, and the permission each action requires.

Audit trail

Every promotion, bind, rollback and waiver decision is recorded as an append-only event, written in the same atomic operation as the change it describes. Reading the trail requires the audit:read permission.

The audit trail covers what each record contains, how to read a window of it, what it cannot tell you, and the retention caveat that matters if you intend to rely on it as evidence.

On this page

Ask AI about this

Help improve this page