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

  1. First attempt: gateway creates an approvals row with status pending, inserts an install row with result='pending_approval', and emits approval.requested.
  2. Duplicate attempts while pending return the same approval id (no pile-up of duplicate rows).
  3. An admin approves or denies in the UI at /[org]/approvals or via a Slack button. Slack clicks verify the Slack signing secret plus an embedded state token (defense-in-depth against replay).
  4. On retry: gateway finds the decided approval and either serves the artifact (approved → install.result='allowed' with approvalId stamped 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 — installed
  • 2 — hard deny (blocklist, allowlist miss, previously-denied approval)
  • 3 — pending approval (re-run after an admin decides)