Orivael Firewall ·Docs ·Dashboard ·Sign up

Slack one-click approvals

When the firewall holds an irreversible or high-risk action for a human, it can post an interactive Slack message with Approve / Deny buttons. One click resolves it — no custom webhook handler required, and every decision is HMAC-signed for audit.

 held action ──▶ Slack: "✋ Approval needed  [✓ Approve] [✗ Deny]"
                        │ click
                        ▼
   POST /v1/slack/interactions  ──▶ verify Slack signature (v0 HMAC, 5-min replay window)
                                 ──▶ record SIGNED decision (approve/deny + who + when)
                                 ──▶ drive stop-authority gate (resume / halt)  [optional]
                                 ──▶ replace message: "✅ Approved by @alice"

Setup (~5 minutes)

  1. Create a Slack appIncoming Webhooks: add a webhook for the channel your approvers watch. Copy the webhook URL — that's where approval requests are posted.
  2. In the same app → Interactivity & Shortcuts: turn Interactivity on and set the Request URL to: https://<your-firewall-host>/v1/slack/interactions
  3. Copy the app's Signing Secret (Basic Information → App Credentials) and export it where the firewall runs: bash export AXIOM_SLACK_SIGNING_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Without it, /v1/slack/interactions returns 503 (fail-closed) — the endpoint never trusts an unverified click.

Sending an approval request

The decision logic is a small pure module (axiom_firewall/approvals.py), so you create and post an approval from your block/hold path:

from axiom_firewall import approvals

pa = approvals.create_approval(
    tenant_id="acme",
    action_id="wire.transfer.4821",
    summary="Wire $4,000 to vendor *Globex* (flagged: new payee)",
    approver="cfo@acme.com",            # a NAMED approver — fail-closed if empty
)
approvals.post_approval_request(SLACK_WEBHOOK_URL, pa)   # interactive Approve/Deny message

create_approval binds the action to a named approver and signs the record; the action stays held until the click resolves it.

Wiring the decision to your action (stop-authority)

Register a gate callback once at startup; it fires exactly once per decision:

from axiom_firewall import dashboard

def gate(decision, approval):           # 'approve' | 'deny'
    if decision == "approve":
        stop_registry.resume(approval.action_id)   # let the held action proceed
    else:
        stop_registry.halt(approval.action_id)     # fail-closed: it never runs

dashboard.set_approval_gate(gate)

The gate is where the approval meets axiom_stop_authority's fail-closed halt/resume. If you don't register one, decisions are still recorded and signed — you just resolve the action yourself off the audit record.

Security properties

Not using Slack?

create_approval + the signed decision record are channel-agnostic. Point post_approval_request at any incoming-webhook-compatible endpoint, or read the pending approvals and drive Approve/Deny from your own dashboard/API — the same decide_approval core backs every path.