Approvals
A require_approval policy gates installs on human review. The gateway returns HTTP 202 with a problem+json body identifying the approval; the CLI exits 3 so scripts can detect pending state.
Lifecycle
- First attempt: gateway creates an
approvalsrow with statuspending, inserts an install row withresult='pending_approval', and emitsapproval.requested. - Duplicate attempts while pending return the same approval id (no pile-up of duplicate rows).
- An admin approves or denies in the UI at
/[org]/approvalsor via a Slack button. Slack clicks verify the Slack signing secret plus an embedded state token (defense-in-depth against replay). - On retry: gateway finds the decided approval and either serves the artifact (approved →
install.result='allowed'withapprovalIdstamped in metadata) or returns 403 policy-violation (denied).
TTL
Pending approvals expire after 24 hours. An expired approval is effectively "no approval" — the next install attempt creates a fresh one.
Slack integration
Set CAVALRY_SLACK_CLIENT_ID, CAVALRY_SLACK_CLIENT_SECRET, and CAVALRY_SLACK_SIGNING_SECRET, then "Add to Slack" from /[org]/settings/integrations. The worker posts approval requests to the channel you configure; Approve/Deny buttons drive the same approval.decide path as the UI.
Exit codes
0— installed2— hard deny (blocklist, allowlist miss, previously-denied approval)3— pending approval (re-run after an admin decides)