# Change control

Every artefact that reaches a host is versioned: plans by revision, config templates by numbered version with rollback, and git-managed plans by the repository.

Source: https://opafra.com/docs/governance/change-control

---

Three kinds of artefact reach your hosts, and each carries its own version history. What
ran, which version of it ran, and who let it through are all answerable after the fact.

## Plan revisions

Every edit to a plan increments its revision. The number is not decoration: it is what
makes an approval specific.

When a run stops at an [approval gate](/docs/running/approval-gates), Opafra records the
plan's revision at the moment approval was **requested**, and again at the moment it was
**decided**. Both go into the audit record, along with whether they differed.

```text title="audit entry for an approval"
approval.continue    ana@example.com    requested 3, approved 4, revision_changed true
```

Without that pair, an audit trail can say a run was approved but not what was approved.
Someone could approve revision 3 and revision 5 could run.

> **Important**
>
> A revision change is reported, not blocked. Opafra shows the approver that the plan moved
> under them and lets them decide. Blocking would be the wrong default: the edit is often
> the fix the approver asked for.

## Config template versions

A config template is the file content that gets rendered and deployed to a host. Every
save creates a numbered version holding the full content, a hash of it, and an optional
change note.

Versions are listed per template, any version can be read back, and any version can be
restored:

```text title="template versions"
v4   2026-09-08   tighten worker_connections   a1b2c3d
v3   2026-09-01   add gzip block               9f2a41c
v2   2026-08-22   initial nginx.conf           4d1e77b
```

Rolling back creates a new version rather than deleting the ones after it. History only
grows, which is what makes it evidence.

The hash matters at run time as well as in review. `template.verify` checks that a
deployed file still matches the version that was deployed, which is how drift is caught
rather than assumed away.

## Git-managed plans

A plan owned by a [git source](/docs/security/connect-a-repository) is read-only in the
builder. Its version history is the repository's history, and a change reaches production
by pull request and a sync rather than by an edit box.

This is the strongest of the three for teams that already review code, because the review
happens in the tool the team already uses, before the change exists in Opafra at all.

Opafra records which commit each sync brought in, so "which revision of the repository is
live" has an exact answer.

## What is not versioned

Stated plainly, because a change-control page that overstates is worse than none:

- **Servers and environments** keep no version history. A change to a host's address or an
  environment's variables is recorded in the [audit log](/docs/governance/audit-log) as an
  event, with actor and timestamp, but there is no previous value to restore.
- **Scripts** synced from git carry the repository's history; scripts created in Opafra do
  not carry their own version list.
- **Secrets** are references, so their values live in your provider and their history is
  whatever your provider keeps.

## Next steps

- [Audit log](/docs/governance/audit-log) for what each change records
- [Connect a git repository](/docs/security/connect-a-repository) for repository-owned plans
