Skip to content

Gates & confirmations

One contract — Gate.confirm(request) → decision — several ways to pause.

mode gate what happens on ask
console ConsoleGate() (default) terminal prompt y / n / e; a non-interactive stdin rejects
block StoreGate(wait=True, notifiers=[…]) request persisted; Slack card / webhook sent; call blocks until decided or timeout_s
pending StoreGate(wait=False) raises ActionPending(resume_token); fn.resume(token) later
interrupt InterruptGate() LangGraph interrupt(); Command(resume=…) continues
cloud Attest.cloud(...) the cloud persists + notifies; SDK polls or resumes
tests / CI AutoGate("approved"), ATTEST_AUTO_APPROVE=1 deterministic
from attest.gate.slack import SlackNotifier
from attest.gate.webhook import WebhookNotifier
at = Attest(gate=StoreGate(PendingStore(".attest/ledger.sqlite"), notifiers=[
    SlackNotifier("xoxb-…", "#agent-approvals", inbox_url="http://localhost:8321"),
    WebhookNotifier("https://your.app/attest", secret="…")]))

Where decisions come from

  • Slack — Approve / Reject buttons on the card; the approver's Slack identity lands in the ledger. Wire the interaction URL to POST /slack/interact on attest serve or Attest Cloud (signature-verified).
  • Web inboxattest serve (local) or the dashboard (cloud): approve, reject, or approve with JSON edits.
  • Webhook — your UI receives the signed request and answers POST /confirm/{id}.
  • Microsoft TeamsTeamsNotifier(webhook_url, inbox_url=…, link_secret=…) posts an Adaptive Card with signed one-click Approve / Reject links (Teams webhooks cannot call back) and a link to the inbox.
  • EmailEmailNotifier(to, sender=…, inbox_url=…, link_secret=…, smtp_host=…) mails the approver group (or a fallback list) the same one-click links. Set ATTEST_LINK_SECRET on attest serve.
  • CLIattest confirm <id> approve --edits '{"subject": "…"}'.

Digests and anomaly flags

attest digest --since 24 [--slack-token … --slack-channel …] (cloud: GET /v1/digest) summarises actions by agent, system, level and decision, lists every unverified row, and flags anomalies against the previous seven days: new_action, volume_spike, unverified_spike, off_hours, external_burst.

Edits change what runs. First decision wins. Timeouts expire the request, which counts as a rejection. Every decision records status, approver, channel, decided_at, edits.