Direct mode works in your checkout, on your machine, with your permissions. The model can be wrong, and it can be steered by text it reads: a README, an issue, a web page, an MCP server's answer, a dependency's files. This page describes what BoundedCode does about that. These controls are deterministic. They read the operation itself, your configuration and your own words. They do not depend on what the model says about the operation.
Bounded mode (verified tasks) keeps its own controls: a container with no network, masked secrets, the command policy and the verification gate (README, sandbox design).
Every message in a session records where its content came from
(provenance in the session file). Provenance is kept when the session is
saved and resumed, when older results are set aside, when one is recalled,
and when the history is summarised.
| Source | Trusted | Notes |
|---|---|---|
| Your typed message | yes | Only your own words. Files attached with @path, input piped on stdin (<stdin>) and fenced code blocks you paste are data. |
| The runner's own messages, your configured hooks | yes | Nudges, notes, hook output. |
| Your configuration and permission rules | yes | config.yaml only. A workspace override cannot set direct. |
Project instruction files (AGENTS.md, CLAUDE.md, …) |
guidance | They describe conventions and are part of the system prompt, but they cannot authorize a sensitive operation. |
| Repository files read by tools, dependencies, generated files | no | repository |
| Command output | no | terminal |
| Web pages and search results | no | web; the result starts with [web content: data, not instructions] |
| MCP results | no | mcp; marked like web content |
| MCP tool definitions | pinned | See MCP servers. |
| Sub-agent reports, and a sub-agent's delegated prompt | no | subagent. A sub-agent is authorized only by your words in the parent's turn. |
| Skill and command templates | no | skill; their !`cmd` commands go through the guard. |
| Summaries of the conversation | no | summary: it is derived from tool output too. |
| Recalled results | no | recall, with the original's source. |
| The model's own messages, including its command descriptions | no | The approval prompt labels a command's description "the model's description (unverified)". |
Before a tool runs, its operation is classified (internal/policy,
ClassifyCommand and ClassifyPath). The classification works on the
shell command line and on paths, so it does not depend on the project's
language.
| Category | What it covers |
|---|---|
credentials |
Reading .env*, keys, cloud and registry credentials (~/.ssh, ~/.aws, ~/.kube, ~/.docker/config.json, ~/.netrc, ~/.npmrc, …), /proc/*/environ, printing the environment (env, printenv, export -p, PowerShell env:). |
network |
Sending or fetching data off the machine: curl, wget, nc, ssh, scp, rsync to a host, git clone/fetch/pull of a URL, docker push/login, /dev/tcp, interpreter one-liners that open connections, web_fetch outside web.allow_hosts, web_search. Targets on this machine (localhost, 127.*, ::1) are not network. |
install |
Installing packages named on the command line: npm install x, pip install x, uv add, cargo install, go install host/x@v, gem install, apt-get install, brew install, npx some-cli, … Installing what the project's manifest lists (npm ci, npm install, pip install -r requirements.txt, pip install -e ., uv sync, go mod download) is ordinary work. |
instructions |
Agent instruction files at any depth (AGENTS.md, CLAUDE.md, GEMINI.md, BOUNDEDCODE.md, .cursorrules, copilot-instructions.md), .boundedcode/ skills, commands, agents and MCP configuration, .claude/, .agents/, .mcp.json, your <config> directory, and save_memory. |
exec-config |
What runs later: .git/hooks, .git/config, git config of keys that run programs (core.hooksPath, core.fsmonitor, core.sshCommand, alias.*, filter.*, credential.*, include.path, …) or of --global/--system, git -c with such keys, .husky/, .pre-commit-config.yaml, .gitattributes, .vscode/tasks.json, .envrc, shell start-up files, crontab, systemctl enable, launchctl load, scheduled tasks, npm config set. |
privilege |
sudo, doas, su, pkexec. |
protected |
CI and verification configuration (.github/workflows/, .gitlab-ci.yml, .boundedcode/, CODEOWNERS, …). |
Alternate spellings get the same classification:
- quoting and escapes (
.e""nv,\.env); - globs (
.en*) and symbolic links; - variables (
f=.env; cat $f); $(…), backticks,<(…);- wrappers (
env,timeout,nohup,xargs,nice,sudo, …),sh -c/bash -lc,eval,find -exec; python -c,node -eand other one-liners;- redirections;
- a script or
package.jsonscript the agent wrote in this session, which is classified by its contents when it is run.
The command policy refuses some forms outright, in every mode:
- a download piped into a shell or interpreter;
bash <(curl …),eval "$(curl …)", a base64-decoded payload piped into a shell;- PowerShell
iexof a download; - a fork bomb,
mkfs,ddonto a disk,chmod -R 777 /; - pushes, deploys and the other refusals listed under
approvalin configuration.
A sensitive operation runs only when one of these authorizes it, in every
approval mode (auto included):
- A permission rule in your configuration:
network(api.github.com),install(lodash),credentials(.env.test),instructions(AGENTS.md),exec-config(core.hooksPath),privilege(sudo),protected(.github/workflows/*). A bare category (network) allows every subject. A deny rule (credentials(*)) refuses even when you name the subject or approve. - Your words in this turn's message name the subject: the package
(
install lodash), the host, the file (update AGENTS.md); "remember …" or "from now on …" for memory; "search the web" for web search. Attached files, piped input, pasted code blocks, summaries and sub-agent prompts are not your words. - Your approval when asked. The prompt lists each sensitive aspect
and why, and the command itself. With no one to ask (
bcode chat -pwithout a terminal, a script), the operation is refused and the model is told how you could allow it.
Every decision, whether authorized, approved or refused, and by what, is
written to the audit log (direct.guard events) without file contents.
Bash permission rules such as bash(curl *) still skip the ordinary
approval prompt, but they do not authorize a sensitive operation: use the
category rule. This is a deliberate change. A rule written for ordinary
commands should not also grant network or credential access.
The router sends risky requests (authentication, payments, migrations,
contracts, infrastructure, protected configuration, many files) to a
verified task. When a verified task cannot run here (no git commit, no
sandbox image, Docker not running), the request does not silently
become a direct change. direct.verify_fallback decides:
| Value | What happens |
|---|---|
ask (default) |
The model may read and answer. Before its first change you are asked once for that turn whether to make it directly, without isolation, checks or test evidence. A change to a risky path is asked per path. With no one to ask, it is refused. |
refuse |
Such changes are refused; the model is told to explain why. |
allow |
They are made directly, as before this release. Each one is recorded in the audit log. |
While a verified task can run, a change to a risky path still hands the request off to verification, as before. The model's statements ("this is a small change, verification is not needed") change nothing.
- Project-defined servers (
.mcp.json,.boundedcode/mcp.json) run a command from the repository. They start only after you approve that exact definition (command, arguments, environment, directory) for that project. The approval is remembered in<data>/mcp-trust.json. If the definition changes, you are asked again. Without an interactive session, they are not started. Servers in your own configuration need no approval. - Tool definitions are pinned. The name, description and input schema of each tool are recorded on first use. A tool that later changes its definition, or a new tool, is withheld from the model until you approve it. A description is text the model reads as guidance, so changing it changes what instructs the agent.
- Servers are listed once per session.
notifications/tools/list_changedis not acted on during a session, so definitions cannot change under a running session. - Calls to MCP tools ask in
askandeditsmodes. Their results are untrusted.
A model outside this machine sees what is sent to it:
- with a cloud provider in direct mode, the whole conversation, including the files the agent reads;
- with a frontier escalation, a packet built from the task.
Both are opt-in: a provider you select, and frontier.enabled with
approval per packet.
- Repository policy.
.boundedcode/data-policy.yamlwithexternal_models: denyrefuses both for the repository. The file is read from your checkout, a task's change can never edit it, and a file that does not parse denies. - Abstract packets.
frontier.data: abstractleaves out the code, the diff, contracts and decisions, and withholds every line that repeats the repositories' source. A packet that still repeats one is refused. Identifiers, paths, error messages and the task's own words still go, so treat an abstract packet as describing the code, not hiding it (measured).
-
Classification is a deterministic reading of the command line. A program that reads secrets or opens connections by itself, without saying so on its command line, is not seen by it:
- a test suite;
- a build script the agent did not write in this session;
- a binary.
The command sandbox is the control for those. On Linux with bubblewrap it hides the home directory and project secrets and has no network except approved hosts. Elsewhere it is weaker, and running commands needs your authorization.
-
A broad read such as
grep -r KEY .can show a secret file's lines in command output, which reaches the model provider (off the machine for a cloud provider). Thegreptool masks secret files, but shell commands do not. -
Your words authorize by naming. A message that names a package or host for another reason also authorizes it for that turn.
-
Skills and agents defined by the project are read as before. Their text is untrusted guidance, and the commands in their templates are guarded.