Secrets Audit Product

Secret Version History: Browse, Restore, and Audit Every Change

By Sean · June 10, 2026 · 7 min read

CertLocker now keeps a version history for every shared and private secret. Every change increments a version counter, the full history is browsable from the secret's detail drawer, and restoring a previous version is one click.

CertLocker Private Secrets list showing four secrets — Production DB Password at version 3, GitHub Deploy PAT at version 2, SMTP Relay Password at version 1, and Personal SSH Key at version 0

Open full-size screenshot

Every private secret now carries a version counter. Version 3 on the Production DB Password means the secret has been modified three times since it was created.

Before this change, modifying a secret in CertLocker was destructive. The old value was replaced immediately and there was no record of what had been there. For most day-to-day operations that is fine — you rotate a password and move on. But for compliance audits, rotation tracking, or recovering from an accidental change, having no history was a gap.

Version history closes that gap for both secret types: shared secrets (visible to groups) and private secrets (user-scoped).

How versioning works

The model is straightforward. Every secret starts at version 0 when it is created. Every subsequent modify call increments the version counter by one and archives the previous state before writing the new value. The current secret record always reflects the latest version, and the history table holds the full chain from version 0 to current minus one.

There is no branching, no diff storage, and no merge concept. Each archived version is a complete snapshot of the secret at that point in time: name, description, type, and the actual secret value. You can retrieve any snapshot directly without having to reconstruct it from a sequence of deltas.

This approach trades storage efficiency for operational simplicity. The history table grows proportionally to the number of modifications, but each version can be read back directly without any reconstruction logic.

The UI flow

In the CertLocker frontend, version history is surfaced from the secret detail drawer. Open any private secret from the Private Secrets page and you will see a Versions tab alongside the existing detail view.

The Versions tab shows a table of all archived versions — version number, timestamp, and description. Click a row to open that version's snapshot: the full secret value at that point, masked by default with a reveal control. If the snapshot represents a password you want to compare against the current value, both are visible in the same session without any API work on your side.

Below the snapshot, a Restore button promotes that version back to the current value. Restoring does not delete the history — it creates a new version entry with the restored value and increments the version counter again. You do not lose the record of what the value was between the original version and the restoration.

Shared secrets

Shared secrets — passwords and PEM files visible to groups — also participate in versioning. The version number is included in the /secret/detail/{id} response and increments on every /secret/modify call.

# Shared secrets also track versions
# version increments on every /secret/modify call
POST /rest/secret/detail/{id}

# Response includes current version
{
  "id": 5,
  "name": "prod-smtp-password",
  "version": 3,
  "secret": "CurrentValue",
  "secret_type": "PASSWORD"
}

Full history browsing and restore are currently exposed in the UI for private secrets. Shared secrets carry the version field in every API response; extended history browsing for shared secrets will follow in a subsequent release.

CertLocker private secret detail drawer open on the Versions tab, showing a history table with versions 0, 1, and 2 of the Production DB Password secret

Open full-size screenshot

The Versions tab in the secret detail drawer. Each row is a complete snapshot — click any row to view the secret value at that point and restore it with one click.
CertLocker showing version 0 of the Production DB Password secret — the initial provisioning value is visible and a Restore button is present at the bottom

Open full-size screenshot

Viewing version 0 — the original provisioning value from October 2025. The Restore button will promote this value back to the current secret without deleting the version history between them.

The API

For integrations and automation, the version history is available via two API endpoints on private secrets.

# Retrieve the full version history for a private secret
POST /rest/private-secret/get-versions/{id}
Authorization: Bearer 

# Response
{
  "history": [
    { "version": 0, "name": "db-password", "secret_type": "PASSWORD",
      "description": "initial", "created_at": "2026-06-01T10:00:00Z" },
    { "version": 1, "name": "db-password", "secret_type": "PASSWORD",
      "description": "rotated after Q2 audit", "created_at": "2026-06-08T14:30:00Z" }
  ]
}

# Retrieve a specific version snapshot
POST /rest/private-secret/get-version/{id}/{version}
Authorization: Bearer 

# Response — full secret state at that version
{
  "id": 12,
  "version": 0,
  "name": "db-password",
  "secret": "OldPasswordHere!",
  "secret_type": "PASSWORD",
  "description": "initial"
}

get-versions/{id} returns the full history array with metadata for each archived version. get-version/{id}/{version} returns the complete snapshot for a specific version number, including the secret value.

Both endpoints respect the same access controls as the parent secret. You need a valid session token and appropriate permissions for the secret to access its history.

When this is useful

Compliance auditing

A common audit requirement is demonstrating that credentials are rotated on a schedule and that no value has been in use longer than the rotation policy allows. The version history provides a timestamped trail that satisfies that requirement without any additional tooling. The audit question "when was this credential last changed?" has a direct answer in the history table.

Recovering from an accidental change

The most immediate use case is recovery. A wrong value is entered, or a rotation script uses an incorrect secret, and now something is broken. Previously, recovery meant digging through logs, Ansible history, or whoever's memory retained the old value. Now it means opening the secret, going to the Versions tab, and restoring the previous version.

Rotation tracking for certificates and PEM files

CertLocker stores PEM keys as well as passwords. Certificate private keys get rotated when certificates are renewed, and tracking which key was active during which period matters for incident investigation. The version history provides that record for PEM secrets using the same mechanism as password secrets.

Debugging deployment failures after a rotation

A service starts failing after a scheduled secret rotation. The version history shows the exact value before and after the rotation without requiring access to the deployment logs or the person who ran the rotation. You can compare old and new values side by side in the detail drawer, confirm whether the rotation introduced the change that broke the service, and decide whether to roll back or fix forward.

Migration and existing secrets

Version history is forward-looking. Secrets that existed before the feature shipped start at version 0, and that version 0 record reflects the current value at the time the migration ran. There is no retroactive history reconstructed from modification events before this release.

The database migration added version columns to both the shared and private secret tables, with a fixer pass that sets all existing records to version 0. From the point the migration runs forward, every modify call produces a new archived version.

E2E test coverage

The version history flow has full end-to-end coverage in the CertLocker test suite. The spec creates a shared and a private secret, modifies both to produce a version increment, then walks the private secret through the UI:

  • Confirms the version counter incremented after modify via the detail API
  • Opens the Versions tab in the detail drawer
  • Clicks the archived version row
  • Reveals the masked password and confirms it matches the original value
  • Restores the archived version
  • Confirms the current secret value reverted via API and UI

The test also cleans up after itself using the delete and revoke endpoints, so it runs cleanly on a shared environment without leaving test data behind.

What is next

The current release ships versioning for private secrets with full UI history and restore, and versioning for shared secrets with the version counter in API responses. The immediate next steps are:

  • Full history UI for shared secrets, matching the private secrets experience
  • Version annotations — the ability to add a note to a version explaining why the change was made, visible in the history table alongside the timestamp
  • Audit log integration — version changes already generate audit events; surfacing those events in the version history view alongside the value snapshots is the natural next step

The goal is a complete picture of a secret's lifecycle: what it was, when it changed, who changed it, and why. The version counter and history table are the foundation; the richer features build on that.

Secrets with a full audit trail

CertLocker gives infrastructure teams a central vault for credentials and PEM files, with version history, access controls, groups, and audit events built in.