Approvals
Approvals are Tyrum’s durable mechanism for gating risky or side-effecting actions behind explicit operator consent. They are created by policy checks and by the execution engine when a workflow step requires confirmation.
Approvals are enforcement, not prompt guidance.
What approvals are used for
- Spending / commitments
- External messaging to new parties
- Accessing sensitive scopes (filesystem, shell, secrets, remote nodes)
- Executing side-effecting workflow steps (playbooks)
- Human takeover handoffs (pause-and-drive)
- Device/node pairing (when required)
Approval lifecycle
- Requested: the gateway persists an approval request and emits an event.
- Resolved: an operator approves/denies (or it expires).
- Applied: the waiting workflow/run resumes, cancels, or escalates.
Approvals must be safe to process more than once (idempotent resolution handling).
Cluster notes
Approvals are durable records in the StateStore and should behave correctly when multiple gateway instances (and multiple operator clients) are active:
- Any gateway edge instance can serve the approval queue (read from the StateStore) and accept resolution requests.
- Atomic resolution: apply
pending → approved|denied|expiredtransitions in a single durable write so double-submission is safe. - At-least-once events:
approval.requested/approval.resolvedevents may be delivered more than once; clients should dedupe using event ids.
Interfaces
Approvals are exposed over:
- WebSocket requests/responses plus server-push events (for real-time operator clients)
- HTTP APIs (for automation and operational tooling)
Scoping
Approvals are scoped to durable identifiers, including tenant_id, approval_id, and execution scope (run_id, step_id, attempt_id) plus agent/session identifiers where applicable.
Approval request shape
An approval request should be explicit about impact and traceability:
approval_idprompt(operator-facing)kind(spend,pii,workflow_step,pairing, …)scope(agent/session/run/step identifiers)riskandestimated_cost(when applicable)items_preview(capped, optional)suggested_overrides(optional; bounded list of safe “approve always” patterns for tool-policy approvals)expires_atresume_token(when the approval gates a paused workflow)
Resolution
Resolutions are durable records:
outcome(approved,denied, orexpired)resolved_atresolved_by(tenant membership / user identity / client device identity)- optional
reason(operator-provided) - optional
mode(onceoralways, only meaningful whenoutcome=approved) - optional
policy_override_id(whenmode=alwayscreates a durable policy override)
Expired approvals behave like denial unless explicitly configured otherwise.
Approve once vs approve always
Approvals should support three operator outcomes:
- Approve once: resolve the pending approval and resume/cancel the waiting run as normal. No standing authorization is created.
- Approve always: resolve the pending approval and also create a durable policy override that allows future matching actions without prompting. “Always” should be offered only when the approval includes
suggested_overrides(or when a domain-specific durable authorization exists, such as node pairing). - Reject: deny the pending approval (or let it expire), and cancel/keep paused according to policy.
“Approve always” is enforcement, not convenience: the override is a durable, auditable record with explicit scope and revocation. See Policy overrides.
Suggested overrides (pattern suggestions)
For tool-policy approvals, the gateway should include suggested_overrides so operator clients can offer an “always” option without free-form rule authoring.
Each suggested override is a tool-specific wildcard pattern over a well-defined match target (see Tools). Suggestions are conservative:
- narrow scope by default (agent and workspace scoped where applicable)
- prefer prefix patterns over broad wildcards
- never suggest a rule that would bypass an explicit
deny
Integration with workflows (pause/resume)
When a workflow step requires approval:
- The execution engine pauses the run.
- An approval request is created with a resume token referencing the paused state.
- On approval resolution, the execution engine resumes/cancels the run without re-running completed steps.
Events
Approvals should be observable via gateway-emitted events:
approval.requestedapproval.resolvedpolicy_override.created(whenmode=alwayscreates an override)policy_override.revoked/policy_override.expiredrun.paused(with reason: approval)run.resumedrun.cancelled(when denied/expired)
Client/UI expectations
The control panel should expose:
- An approval queue (filterable by agent/run/kind)
- Approval details (prompt, preview, linked evidence/artifacts)
- Approve once / approve always / deny with clear consequences
- “Always” UI that presents the bounded
suggested_overrideslist (scope + match target + pattern) and requires selecting one or more suggestions - A policy override inventory (list/describe/revoke) with links back to the approvals and runs that created each override
- Deep links from notifications into the approval detail view