Skip to main content

How it works

Belay is a local-first security layer that sits at the tool-call boundary of an AI coding agent. Every action an agent tries to take — reading a file, running a shell command, calling an MCP tool — passes a deterministic policy check before it is allowed to happen. This page explains that boundary, the decision engine behind it, and the integration styles that plug into it.

When to read this

Read this to build the mental model behind everything else in the docs: why Belay is deterministic and fast, why it fails closed, and how it wires into different agents. Once this clicks, Verdicts and Audit & tamper-evidence follow naturally.

The tool-call boundary

An AI coding agent doesn't touch your machine directly with its "thoughts" — it acts through tool calls: file reads and writes, shell commands, MCP tool invocations. That boundary is the one place where every consequential action is visible and interceptable.

Belay inserts itself exactly there. Before a tool call executes, the call is handed to Belay, which returns a verdict. A single agent misstep — or a prompt injection that hijacks the agent — cannot silently leak a .env file, dump SSH keys, run rm -rf /, pipe curl | sh, open a reverse shell, or exfiltrate data to a known sink, because that action has to clear the boundary first.

Belay runs as your user, never root, and does not phone home by default.

Detect → Block → Notify

The product operates on three verbs:

  1. Detect. Belay auto-detects the AI coding agents installed on the machine — Claude Code, Codex, Cursor, Cline, Roo, Gemini CLI, Goose, OpenClaw, Hermes, Antigravity, opencode, and more — and how to intercept each one, then evaluates every tool call against a deterministic rule catalog.
  2. Block. Dangerous actions are denied outright. Ambiguous-but-risky actions are escalated to a human for an explicit Allow / Deny.
  3. Notify. You are informed — through the desktop app's privacy-safe notifications, the local live feed, and optional chat channels — of what was blocked or is awaiting a decision.

The decision engine

The engine that produces each verdict is built around four properties:

  • Deterministic. The same tool call always yields the same verdict. Rules are compiled into the binary, and the outcome doesn't depend on a model's mood.
  • No LLM on the hot path. There is no language-model call in the gating path. This is a policy engine, not a classifier that might hallucinate.
  • Sub-100ms. Because it's deterministic and in-process, the check adds negligible latency to the agent's workflow.
  • Fail-closed. If anything goes wrong — an internal error, a timeout, an unanswered escalation — the result is a deny, never an accidental allow.

The engine combines several signals into a single verdict: matches against the rule catalog (secrets, egress, destructive, RCE, supply-chain, persistence, privilege-escalation, recon, config-tamper), plus session correlation that watches for an "arm" step followed by a "sink" step, and the "lethal-trifecta" pattern across a session. Precedence is strict: Deny > Ask > Allow. A deny is never downgraded — not even by the allowlist that exists to wave through benign developer-toolchain activity. The full model is in Verdicts.

Every rule in the catalog is tagged with the OWASP Agentic Security Initiative (ASI) Top 10, the OWASP LLM Top 10, and MITRE ATLAS techniques.

note

The rule catalog is deterministic and local. A separate, optional static scanner (belay scan) can consult an LLM cascade to filter false positives before you install a package — but that is off the enforcement hot path. Runtime gating is always LLM-free.

Explain & advise

Every verdict carries a plain-English explanation of why it is risky and what to do — a curated summary / what / why_risky / normal_use / suggested_action written per rule. You never get a bare rule ID.

An optional AI explainer can add a second, richer explanation. It is off by default and feature-gated, bring-your-own-key: point it at a local Ollama model or a cloud provider (Anthropic, OpenAI, Gemini, xAI, DeepSeek, Mistral, Groq, Cohere, Perplexity, Together, OpenRouter, MiniMax) with a per-provider model picker. The cloud key is pasted in-app (stored owner-only 0600 at ~/.belay/ai_key, write-only, never logged) or supplied via BELAY_AI_KEY. Secrets and host paths are redacted before anything is sent, cloud mode requires explicit consent, and the AI text is advisory only — it never makes or changes a decision. Any error falls back silently to the curated explanation.

Integration styles

Different agents expose different extension points, so Belay meets each one where it lives. At a concept level there are a few styles, and you don't pick them by hand: belay detect reports how each agent will be intercepted, and belay protect <agent> wires in the right one (belay unprotect <agent> cleanly removes it).

Hook

For agents with native pre/post tool-call hooks (such as Claude Code and Codex), Belay registers a hook. It gates the call before it runs, and on the post-call side it redacts secret-shaped strings from tool output so a credential that did get read isn't echoed back into the transcript. The hook — belay hook pretooluse / belay hook posttooluse, installed into the agent's settings.json — talks to the resident daemon over a local socket.

Gate

The gate style is a one-shot verdict: the tool call is passed in on stdin as JSON, belay gate returns a decision on stdout, and the process exits. It's the same policy engine in a pipe-friendly form, for integrations that prefer a stateless call over a persistent socket client.

Agent-correct interception

Beyond generic hooks, Belay installs the interception each agent actually supports: Claude Code and Codex get JSON hooks in settings.json; Cursor gets Cursor hooks; Hermes gets its YAML pre_tool_call hooks; OpenClaw gets an exec-policy; and MCP-server-driven agents get the mcp-proxy gate below.

MCP proxy

For agents driven through the Model Context Protocol, Belay runs as an MCP stdio proxy that wraps any real MCP server:

belay mcp-proxy -- <server-cmd> [args...]

Every tools/call request flows through the gate, so tool calls made through MCP servers are checked too — not just the agent's built-in tools. The proxy is fail-closed: anything that isn't an explicit allow (an unanswered ask, or any error) becomes a deny.

What Belay does not do

Belay runs unprivileged, as the invoking user, never root — that's a deliberate design choice. True syscall-level blocking (denying an operation in the kernel before it happens) requires CAP_SYS_ADMIN/root and is out of scope by design. Belay enforces at the tool-call boundary and, on Linux with the opt-in privileged eBPF sensor, can detect and react to out-of-band tampering — but it does not claim kernel-level pre-write prevention. See Audit & tamper-evidence for the honest limits of self-protection.