The gate
Every sensitive route delegates torunGuardedAction(req, { actionType, payload }):
- Resolve the subject (
tenant_user+ Account Kit) and the actor. resolveApprovalRequirement(kit, actionType)decides if approval is needed: start from the built-inDEFAULT_APPROVAL_ACTIONSset, then apply the per-primitiverequiresApprovaloverride on the kit.- Agent + required → freeze the request as a
pendingrow, return202 { status: "pending_approval", approval_id }. - Human session, or not required → execute immediately.
mcpGuard) so an agent can’t bypass it by
switching transports.
Deferred replay (single execution path)
There is one executor registry mappingaction_type → service call
(cards.create → createCard, domains.purchase → purchaseDomain, …). Both the
immediate path and the post-approval path dispatch through it, so a frozen
action runs exactly as it would have live.
Data
Pending and resolved approvals live in theapprovals table, scoped to a
tenant_user: action_type, primitive, the frozen payload,
requested_by_*, status (pending | executed | failed | denied | expired),
result / error, and resolved_by_*. Creating, approving, and denying each
write an activity_events row (approval.requested, approval.approved, …)
and publish a live event for dashboards.
Approvers
Anyone authenticated for the company can resolve an approval: operators via the dashboard or CLI (any of their tenant users), and end-users via your app (scoped to their owntenant_user). PII-heavy payloads (e.g. KYC members) are
stored minimally and re-fetched at execution time.