Approvals & separation of duties
Hold risky actions for a second person — the requester can never approve their own.
When a policy returns require approval, the action doesn't run. It's stored as a
pending approval and the execute call returns 202 with an approval_id. A reviewer then approves or
denies it — and separation of duties guarantees that reviewer isn't the person who requested it.
The flow
- An action hits a
require_approvalpolicy → a pending Approval is created. Its payload is stored encrypted (AES-256-GCM) and a hash of the arguments is kept for integrity. - A reviewer lists pending approvals and decides:
- On approve, Nolixan decrypts the original payload and re-executes the action (the policy gate is not re-applied — the approval is the authorization). On deny, it's marked denied and never runs.
- Pending approvals expire after a TTL (24h by default); an expired approval can't be acted on.
Separation of duties
The user who triggered an action cannot approve or deny it. If the requester tries, the call returns
403:
You requested this action and cannot approve or deny it yourself. It needs a different approver.
Crucially, that blocked attempt is itself written to the audit log (outcome
self_approval_blocked) — so the separation-of-duties enforcement is visible in the trail, not silent.
Every decision — approved, denied, self-approval-blocked, or expired — is recorded against the approval and the original trace.