Skip to content

Approval flows

Sensitive changes — custom policy publication, certain PKI operations, sometimes high-risk rollout configurations — require a four-eyes approval: a different operator must approve before the change goes live.

Where approvals are required

By default, approvals are required for:

  • Custom policy publication. A policy draft must be approved by another Admin before it goes Active.
  • Custom policy edits to an Active policy. Any edit that is not a metadata-only change reverts the policy to Draft and re-requires approval.
  • Signing-key activation. Promoting a Draft signing key to Active is a tenant-wide event with broad blast radius; a second Admin must approve.
  • Federation peer creation and edits. Adding or editing the trust relationship to another Ampora server is high-impact.

The list above is the default policy. Tenants can extend it via custom approval rules — see below.

How approval works

  1. Operator A creates or edits the entity (policy, signing key, federation peer).
  2. The entity sits in PendingApproval.
  3. Operator A is not allowed to approve their own change. Even if they have the role, the approve action is disabled with a "must be a different operator" tooltip.
  4. Operator B (a different operator with the relevant role) opens the entity, reviews the diff, and approves or rejects.
  5. On approval, the entity moves to its terminal state (Active, Live, etc.).
  6. On rejection, it returns to Draft with the rejection reason captured in audit.

Both A and B are recorded in the audit row, with the diff and the rejection / approval reason.

What "different operator" means

The check is on the user identity (OIDC sub claim), not on the role. Two Admins are still two Admins; the same Admin viewing under two browsers is still one Admin.

In multi-tenant deployments the check is also tenant-scoped — an Admin in tenant X cannot approve an action in tenant Y.

Editing during approval

If A edits a draft that is sitting in PendingApproval, the approval state is invalidated and the entity reverts to Draft. B's prior review on the older content is no longer authoritative; B (or another Admin) must review the new draft.

This is the right tradeoff: the alternative — letting an approval carry over to an edit — would let A get approval for X and then push Y.

Custom approval rules

Tenants can extend the default approval-required list via the same DSL as policies:

require-approval where target.kind = "rollout" and rollout.target_group.name = "prod-eu"

This makes any rollout targeting prod-eu require four-eyes — useful for environments where a senior operator must sign off on every production change. The rule is itself approved like a policy.

Audit trail

Every approval cycle produces multiple audit events:

  • Entity.DraftSaved — by A.
  • Entity.ApprovalRequested — implicit on save; some integrations page operators on this.
  • Entity.Approved or Entity.Rejected — by B, with reason.
  • Entity.Activated (or equivalent terminal state) — implicit on approval.

Filter the audit log by the entity to see the full cycle in one view.

Bypassing approvals (dev only)

A development setting Approvals:RequireFourEyes=false lets a single operator approve their own work. Never enable this in production — it defeats the entire mechanism. The setting itself emits an audit event on every change; a deployment that turns it off is auditable even when its other audits are not.