Verdicts: Allow / Ask / Deny
Every tool call Belay evaluates resolves to exactly one of three verdicts: Allow, Ask, or Deny. This page explains what each means, how precedence is resolved when signals disagree, and the guarantees that make the outcome safe.
When to read this
Read this alongside How it works. Once you understand the tool-call boundary, this is the rulebook for what happens at it.
The three verdicts
Allow
The tool call proceeds normally. Either nothing in the rule catalog matched, or what matched was benign and the call is safe to run. Belay still records the decision in the audit log.
Ask
The call is ambiguous but risky — plausibly legitimate, plausibly dangerous. Instead of guessing, Belay pauses the agent and escalates to a human for an explicit Allow / Deny. The agent stays blocked until the decision comes back. Approvals surface in the terminal, in the desktop app's approval queue, and — if you configure them — in a chat channel (see below).
Timeout equals deny. An ask that is never answered resolves to a deny,
not an allow. Belay fails closed — an unattended escalation cannot become an
accidental approval. Prompts auto-expire; a stale prompt is auto-denied.
Deny
The call is dangerous and is blocked outright — no human is asked. This covers
things like reading secret-shaped files (.env, API keys, SSH keys), destructive
commands (rm -rf /), curl | sh, reverse shells, and exfiltration to known
sinks. The agent is prevented from taking the action, and the block is recorded.
Every verdict — allow, ask, or deny — carries a curated plain-English explanation
(summary / what / why_risky / normal_use / suggested_action), so you
always know why a call was gated and what to do next.
Precedence: Deny > Ask > Allow
A single tool call can trip more than one signal at once — a rule match, a session-correlation pattern, an allowlist entry. Belay resolves them with a strict, unambiguous priority:
Deny > Ask > Allow
If any signal says deny, the verdict is deny. If none say deny but at least one says ask, the verdict is ask. Only when nothing raises a concern is the verdict allow. There is no averaging, scoring-to-threshold, or "mostly fine" — the most restrictive applicable signal wins.
The allowlist can never downgrade a Deny
Belay ships an allowlist so that ordinary developer-toolchain activity isn't
constantly flagged. That allowlist can only ever downgrade non-deny
decisions — it can quiet an ask down to an allow for genuinely benign work.
It can never touch a deny. A DENY is final: the allowlist cannot upgrade
a blocked action into an allowed one. This is a deliberate one-way rule so that a
crafted "looks-like-dev-tooling" wrapper around a dangerous command can't launder
its way past the boundary.
This is why precedence and the allowlist are described together: the allowlist sits below deny in priority by construction. Deny always wins.
Session correlation feeds the verdict
Beyond matching a single call in isolation, the engine correlates activity across
a session. It watches for an arm → sink shape (a step that stages something
dangerous, followed by a step that acts on it) and for the "lethal-trifecta"
combination that, taken together, indicates a genuine exfiltration risk even when
each individual step looked harmless. When correlation fires, it raises the
verdict through the same Deny > Ask > Allow precedence.
Approving an Ask from a chat channel
When a verdict is ask, Belay can send the approval prompt to a chat channel and
take your Allow / Deny reply back. Two-way channels: Telegram, Discord,
WhatsApp, Matrix, Mattermost, and Slack (Block Kit buttons). Notify-only
channels: ntfy, Microsoft Teams, WeCom, a generic webhook, and the terminal.
Approval is default-deny and owner-gated: only enrolled principals can approve.
Enrollment is interactive — the app shows a one-time code, and the approver DMs
pair <code> to the bot to become an authorized approver. Prompts auto-expire, so
an unanswered prompt in a chat channel denies just like any other timeout.
Configure channels in the setup wizard (belay setup) or the desktop Messaging
tab.
--observe: dry-run before you enforce
Before you enforce, run in observe mode:
belay protect claude-code --observe
In observe mode the engine computes the exact same verdicts and logs them, but
nothing is ever blocked — every decision is log-only. It's the recommended way
to see what would be denied or asked, tune out false positives, and build
confidence, before you re-run protect without the flag to enforce for real.
Observe mode changes nothing about how verdicts are computed — only whether they're enforced. What you see logged in observe mode is exactly what will be enforced when you drop the flag.
Fail-closed, everywhere
The verdict model is safe by default at every failure point:
- An internal engine error resolves to deny.
- A timeout resolves to deny.
- An unanswered
ask(including in the headless MCP proxy) resolves to deny.
There is no path where a failure quietly becomes an allow.
Related docs
- How it works — the boundary and the deterministic engine.
- Audit & tamper-evidence — how each verdict is recorded and protected.
- Quickstart — observe, then enforce.