Trust

What happens when the content is hostile.

An agent that can send an email, change a ticket and run a command is a different risk from a chat box, and the question worth asking is not whether you can misuse it. It is what happens when the instruction arrives inside a document, an email, or a tool result that you did not write. Here is what stands in the way, what does not, and which parts you can check.

Where hostile content gets in

Every one of these is treated as untrusted by default, with its own strictness. Naming them is the first honest step: a product that cannot list its untrusted inputs has not thought about this.

Tool results
Email bodies, search results, documents, and API responses pulled in by a connector. This is the largest surface and the one an attacker can most easily write into: sending you an email is enough.
Descriptions from external MCP servers
A tool's own name, description and schema arrive from whichever server publishes it. A description is prose the model reads, so a server you added can attempt to instruct the model through it.
Persistent user memory
Entries that survive a session. Anything written once is read back into later context, so a single successful injection could otherwise persist.
Session summaries and history
Model-generated summaries of earlier turns, and the roles attached to stored messages. A forged role marker is an attempt to impersonate a message boundary rather than to argue with the model.

The six questions a security team actually asks

These are the questions we get, in the words we get them. Where the answer is partly no, it says so.

Can an agent be tricked by instructions inside a document or an email?
In principle, yes, and any vendor who tells you otherwise is describing a product that has not been tested. A model that reads attacker-controlled text can be talked into asking for the wrong thing. Bolt's design assumes that. The controls that matter sit <strong>after</strong> the model, on the path between a tool request and the action, because those do not depend on recognising the attack. Content filtering runs as well, and is the weaker half.
Are commands allowlisted, or merely approval-gated?
Both, and the distinction is enforced by provenance. A command a person types into the terminal is never governed. A command Bolt injects (an agent action, a recipe, a tmux send, a terminal created with an explicit program) crosses one native chokepoint that returns allow, deny, or confirm. Deny is a refusal, not a prompt a tired user can click through. An allowed command that carries a live credential is escalated to confirm, so a secret never reaches a command line without someone consciously agreeing.
Can a prompt cause exfiltration through a connector we already permitted?
This is the sharpest question on the list, because the connector is authorised and the data is in scope. Two answers. First, whether a call leaves the trust boundary is derived from a per-tool <em>declaration</em> of what that tool reads, changes and can send, not from its name. That matters because a name is a poor guide: a terminal that can run curl, a file write into a synced folder and a mail compose all leave the boundary regardless of what they are called. Second, every user-supplied or admin-supplied URL is validated by one outbound guard before a connection opens, which resolves the host and revalidates every address it resolves to.
Are approvals tied to the exact action?
A grant is scoped to named tools, records where it came from, and is created at the moment a person set the job up, not at the moment the model decided it wanted something. That provenance is written on every allow and every refusal, so an audit answers who authorised this with an artifact rather than with the absence of an objection. We do not claim the approval is cryptographically bound to the specific arguments of a specific call. It is not, and if that is your requirement you should ask us about it directly.
Can a user tell a read from a change?
The declaration that drives confirmation separates what a tool pulls into context from what it changes and whether it can send. Reading is ranked, and the top of that ranking is the class that matters most for injection: ambient capture, meaning whatever happens to be on the clipboard, on screen, or in the accessibility tree at that moment. The user never chose to hand that over, which is precisely why an injected instruction reaches for it, and why it prompts by default.
What happens when no human is watching?
Nothing that needs authority runs. There are three authorities and no unset value: a human reachable on a live channel, an unattended job acting inside a scope a person granted when they created it, or unattended with no grant on record. The last one refuses. The default is refusal, and the function that builds a tool's execution context requires an authority to be named, so a new automated path cannot be added without stating which of the three it is.

Four controls, and why they are in that order

The first three do not depend on recognising an attack, which is why they carry the weight. The fourth does, which is why it is last.

Authority is named, never inferred

This is the control we are most confident in, because it cannot be reached by accident. Authority is a type with no unset value: every construction names one of three decisions, and absence is not one of them. The function that builds a tool's execution context requires an authority to be named, so a path that fails to state which of the three it is does not compile rather than defaulting to permission. Forgetting produces refusal. That includes the paths a reviewer worries about most, because the scheduler, background runs and the federation server each have to answer the same question a person at a keyboard does.

Behaviour is declared, not guessed from the name

Two security decisions turn on what a tool actually does: whether it prompts by default, and whether calling it leaves the trust boundary. Both derive from a per-tool declaration of what that tool reads, changes and can send, because a name is not a behaviour. The declaration is combined with a keyword check by OR, so the set of tools that prompt can only grow, never shrink, as declarations are added.

Injected commands cross a chokepoint, typed ones do not

Every Bolt-injected shell command routes through one native gate before its bytes can reach a terminal. The classification and decision live in a shared policy crate and the native process is the single place that calls it, so no injected path can skip the gate. The raw verdict is deliberately private, because a call site that branches only on deny would silently downgrade confirm to allow. A test reads the source of every registered injection site and fails the build if one reaches past the gate, and a companion test fails the build when a new shell-spawning file is added without being registered. Two paths are recorded in that same list as knowingly ungoverned rather than quietly missed: running the raw Exec line out of a Linux desktop entry, and one AppleScript execution tool that is instead blocked in every sandbox mode.

Untrusted content is fenced before the model reads it

Content from the sources above is wrapped in boundary markers so the model can tell data from instruction, known override and role-impersonation patterns are neutered, and stored message roles are validated. One detail is worth stating because it shows the limit being taken seriously: a tool schema's functional fields can legitimately contain a marker-shaped literal, so rewriting them would change what a tool call means. Those fields get the narrower rule and only prose gets the full one.

What these controls do not cover

Pattern matching is not a proof
The content filter recognises a known set of phrasings. A new phrasing will not match it. We treat it as the weaker half of the defence for exactly that reason, and it is why the load-bearing controls are the ones that never have to recognise an attack: a refusal that comes from having no authority does not care how the request was worded.
A confirmation is only as good as the person reading it
Every prompt-based control assumes attention. A user who approves by reflex has approved. This is why deny exists as a separate outcome from confirm, and why the ambient-capture class prompts rather than relying on a policy nobody reads. It is not a solved problem and we do not present it as one.
One documented residual in the outbound guard
The guard resolves a hostname and revalidates every address before allowing a connection, then the HTTP client resolves again when it actually connects. A DNS rebind inside that window still wins. Closing it needs a connector that dials the validated address while preserving TLS SNI. Until that exists this is a known, accepted and written-down gap rather than one we have quietly rounded off.
Data-policy enforcement is not yet blanket
The framework for enforcing policy at every display, copy and share boundary ships, and the boundaries are being wired one at a time. Do not read that as complete coverage today, and do not let anyone sell it to you as such. Where a boundary is not yet wired, it is not enforced.
Free-text name detection is off by default
The structured floor, meaning identifiers, financial values and secrets, is always on at the model boundary. The model that finds names in ordinary prose is a separate engine that ships bundled on Apple Silicon macOS only, runs as a deployed scanner elsewhere, and is off until an administrator enables it. Enable it where it cannot run and the API reports the engine as degraded rather than silently doing nothing. See /trust/when-we-do-not-know.

What you can check yourself

Three of these are observable on your own machine, without taking our word for any of it.

Provenance is real, not a label. Type a command into Bolt's terminal and it runs ungoverned, because you typed it. Ask Bolt to run the same command and it crosses the gate. The difference in behaviour is the control.

Ambient capture prompts. Ask an agent to do something that would read your clipboard or your screen without you naming what it should read. That class prompts by default, and it is the class an injected instruction reaches for first.

Refusals are recorded, not just allows. An unattended path with no grant on record refuses, and the refusal is written down with the reason. An audit trail that only records successes cannot answer the question you will actually be asked after an incident.

More of this kind of thing, including every port Bolt binds and every command it can spawn, is at what Bolt does on your endpoint and prove it yourself.

If you find something

We would rather hear it than not, including about this page. If a control described here does not behave as written, that is as much a finding as a bug is. Our disclosure policy commits to a one-business-day acknowledgement, a 90-day coordinated window, and a good-faith safe harbor.

This page describes mechanisms and their limits. It deliberately does not publish working bypasses, our internal test corpus, or the specific phrasings a filter matches, because that is a recipe rather than a disclosure. If you are evaluating Bolt and need more depth than this, ask us under NDA and we will go further than a public page can.

Reviewing Bolt?

The reviewer packet states what we have and what we do not, on one page, written to be forwarded without edits.