raxit
Capabilities

Approvals & warrants

How deferred calls route to human approval, and how warrants skip the wait.

When a rule defers, the agent process blocks and prints a token:

[raxit] waiting for human approval (up to 120s)… approval id dt_1373bb3e0bcf139d

[raxit PENDING] 'post_to_webhook' deferred — waiting for human approval (up to 120s).
  Token:   dt_1373bb3e0bcf139d
  Approve: raxit approvals approve dt_1373bb3e0bcf139d
  Deny:    raxit approvals deny dt_1373bb3e0bcf139d

From another terminal:

$ raxit approvals approve dt_1373bb3e0bcf139d --reason "docs fact pack approval"
dt_1373bb3e0bcf139d: approved (approver=operator)

The first terminal resumes on its own — no re-run needed:

[raxit] approval approved (id dt_28930b3bf7da664a)

>>> agent calls post_to_webhook({'url': 'https://example.com/hook', 'payload': 'hi'})
[webhook] would POST to https://example.com/hook: hi
<<< result: ok

If nobody approves in time (default 120s, RAXIT_DEFER_TIMEOUT), the call resolves to a hard deny instead:

[raxit] approval expired (id dt_1373bb3e0bcf139d)

<<< [raxit BLOCKED] 'post_to_webhook' denied (reason: deny.approval_expired). The operation was
    not executed. Details: raxit explain d867c63661454e8497369a08ebaec35d

An explicit denial produces deny.approval_denied instead — same terminal shape, different reason code.

RaxitDeferred (Python SDK)

Since 0.0.3, a call that stays deferred — the human approval times out, or the control plane goes unreachable while waiting — raises RaxitDeferred, a subclass of RaxitDenied, instead of collapsing into an indistinguishable denial. Every existing except RaxitDenied: site still catches it and fails closed (the tool body never runs); the subclass only lets a caller that owns a retry loop tell "still pending" apart from "a human said no" — a RaxitDeferred carries the approval token, so the same bound call can be resubmitted later instead of minting a fresh pending approval.

  • RAXIT_DEFER_TIMEOUT=0 returns the raw, unresolved defer immediately — no inline approval poll — reported with the policy's own defer reason code.
  • A positive timeout that elapses without resolution reports defer.timeout.
  • An explicit human denial still raises the terminal deny.approval_denied (plain RaxitDenied, not RaxitDeferred — that's a "no," not a pending retry).

raxit approvals

$ raxit approvals list
TOKEN                  AGENT            RESOURCE               STATUS
dt_1373bb3e0bcf139d    default-agent    post_to_webhook        pending
dt_28930b3bf7da664a    default-agent    post_to_webhook        pending

$ raxit approvals approve dt_28930b3bf7da664a --reason "..."
dt_28930b3bf7da664a: approved (approver=operator)

$ raxit approvals deny dt_1373bb3e0bcf139d --reason "..."
dt_1373bb3e0bcf139d: denied (approver=operator)

raxit approvals authenticates to the daemon automatically using a local, 0600-permission token file at .raxit/var/approvals.token — no --token flag needed on a local box. Denying an already-expired token still succeeds; it just records the operator's explicit denial on top of the automatic expiry.

Warrants — standing pre-approvals that skip the wait

A warrant is a time- and scope-bounded pre-approval: once issued, a matching defer upgrades straight to permit (permit.warrant) without a per-call human loop. You declare the shape a warrant may take in security.yaml:

warrants:
  templates:
    - name: refund-approval
      tool: "stripe/refund"
      max_ttl: 1h
      max_uses: 5

This is an envelope, never an issuance path — writing this block does not grant anything, and an attacker who can edit security.yaml still cannot self-issue a warrant. Warrants are minted separately, through the same human-approval flow, and are consumed exactly once per call id.

Warrants are in-memory only — there is no persistence across a daemon restart. This is distinct from the session-store file backend (--session-store file), which persists taint, budget, rate, and other session state across restarts but does not cover warrants. A restarted daemon has zero warrants regardless of session-store configuration; any standing approvals must be re-issued.

Where approvals route

Beyond the CLI/terminal flow shown above, a deferred call can also route out via webhooks: (Slack or a generic webhook) — see Advanced. Whatever the channel, the resolution is the same: permit.approved on approval, deny.approval_denied on denial, deny.approval_expired on timeout.

On this page