< BACK_TO_INDEX
[AGENT SECURITY]2026.08.08 // 12 MIN READ

Your Prompt Isn't the Security Boundary

In the last couple of articles I've focused mostly on identity: how we identify an agent, what it should be authorized to access, and what identity follows it when it calls organizational resources.

While building with coding agents, I kept running into a different problem.

Authorization can tell me what an agent may do. It doesn't necessarily tell me what happens after I give that agent a shell.

A coding agent can read a repository, change files, run tests, install dependencies, call external services, inspect the result, and try another approach. That ability to find a path to the goal is exactly what makes these systems useful. It also makes me uncomfortable relying on a sentence in a prompt as the thing keeping execution safe.

That pushed the security boundary down a layer—from what I tell the model to what the harness actually allows it to do.

The four boundaries of harness safety: context, capability, execution, and evidence

The harness is part of the security architecture

I use Pi as my coding-agent harness. One of the reasons I like it is that it gives me a relatively small core and enough extensibility to shape the environment around the model. I can load skills, add extensions, intercept tool calls, and decide where I want human approval.

That flexibility also means I have to decide where the boundaries should be.

The model I've found useful has four parts: context, capability, execution, and evidence. I don't have all four solved. In fact, some of the interesting parts of this model came from noticing what was missing in my own setup.

1. Context: give the agent enough to understand the job

For anything more complicated than a short instruction, I usually prompt with voice-to-text. I didn't start doing this as a security technique. I started because I can explain a complicated idea much faster than I can type it.

That changes the prompt. Instead of writing refactor the authentication flow, I'll usually explain what is wrong with the current flow, what shouldn't change, what I'm worried about, and where there is an existing pattern worth looking at.

The agent gets more of the reasoning behind the task and has less to infer.

I use the repository in a similar way. I don't want one enormous AGENT.md or CLAUDE.md carrying everything an agent might ever need to know. I keep the top-level instructions small and put more specific context close to the code it applies to.

/
├── AGENT.md
├── docs/
│   ├── architecture.md
│   ├── authentication.md
│   ├── security.md
│   └── deployment.md
├── frontend/
│   ├── AGENT.md
│   └── ...
├── backend/
│   ├── AGENT.md
│   └── ...
└── infrastructure/
    ├── AGENT.md
    └── ...

A backend/AGENT.md might tell the agent to read docs/authentication.md before changing authentication, follow the existing middleware pattern, and run a particular test suite. The detailed explanation stays in the documentation instead of turning the instruction file into a book.

I do the same with skills. Some skills follow me between projects because they describe how I like to work. Others only make sense for a particular codebase or workflow.

There is an important limit here, though. Good context can reduce bad decisions, but it doesn't prevent them.

A skill can explain exactly how a production deployment works. That doesn't mean the agent should have credentials capable of performing one.

The distinction I use is roughly:

AGENT.md  → what you should know
docs/     → what you can learn
skills    → how you should perform something
tools     → what you can do
hooks     → what you are permitted to do
sandbox   → what you can actually affect

That last half is where things get more interesting.

2. Capability: Bash is not just another tool

It's easy for a coding harness to accumulate tools. Read, Write, GitHub, browsers, MCP servers, cloud CLIs, Docker, Terraform, databases, and whatever custom tooling the project needs can all end up available to the same agent.

I try to start smaller. If a project needs Read, Write, Search, and Git, that's what it should get. If it needs GitHub, add GitHub. If it needs infrastructure tooling, make that a deliberate choice.

Bash complicates this model because Bash is effectively a capability multiplier.

Give an agent a shell and it may be able to invoke Git, curl, Python, package managers, Docker, Terraform, kubectl, SSH, database clients, and anything else installed in the environment. So saying "Bash is allowed" doesn't tell me very much about what the agent can actually do.

My first instinct, like a lot of security controls, was to think in terms of dangerous commands. Block things such as rm -rf, sudo, git reset --hard, terraform apply, or kubectl delete.

The problem is that blocking the string rm -rf doesn't remove the ability to delete a directory when Python, Node, or another utility is sitting beside it. A capable model can find another route.

That pushed me toward a different question: what effect can this execution have?

Allow lists are better, but they have a similar context problem. npm test sounds harmless until a project's test script starts containers, loads credentials, runs migrations, or calls a development API.

For Bash, I increasingly want the decision to include more than the command:

command + arguments + working directory + resolved paths
+ environment + credentials + network + project

This is one of the reasons I'm interested in project-aware execution. terraform plan inside an infrastructure directory is not the same capability as npm test inside a frontend package, even though both arrive through Bash.

Project context changes the risk of the same execution capability

A gap in my own setup: network access

I don't currently maintain explicit outbound network policies for my personal coding agents. If Bash can reach the Internet from my environment, the agent can generally reach it too.

Once I started treating Bash as a capability rather than simply another tool, that began to bother me.

Filesystem access and network access are different privileges. An agent may legitimately need GitHub and a package registry without needing arbitrary outbound connectivity. I want to explore that properly rather than bolt a superficial domain list onto this article, so network policy deserves its own follow-up.

3. Execution: put important rules somewhere other than the prompt

Suppose I tell an agent:

Never modify production infrastructure without approval.

I absolutely want that instruction in the agent's context. But if the same environment gives it Terraform, production credentials, network access, and a shell, the instruction hasn't removed the capability.

For boundaries I really care about, I want something programmatic between the model's decision and execution.

Pi extensions and hooks are useful here because they let me intercept actions and make a decision in the harness. A path rule, for example, can resolve where an agent is trying to write and reject anything outside the project. The model can know the rule, but the hook is what enforces it.

This also doesn't mean every action needs an approval dialog. Too many prompts just train us to click yes.

I have an agent that helps with IAM operations such as creating groups and changing group rules. I let it do the work of understanding the request, determining the operation, and preparing the change. The actual IAM mutation still requires my approval in the harness.

That experience has made reversibility a useful signal for deciding where I want a human involved.

Editing a source file is easy to undo. Sending an external email isn't. An IAM change may be reversible technically, but the effect of a bad rule can happen before I roll it back. Production data might not be recoverable at all.

As actions become harder to reverse, the authorization boundary should become stronger

Action Recoverability Possible posture
Edit source file Easy Autonomous
Git commit Easy Autonomous
Install dependency Moderate Context dependent
Push branch Moderate Context dependent
Create IAM group Consequential Approval
Modify IAM rule Potentially high impact Approval
Send external email Irreversible Approval
Delete production data Potentially irreversible Strong approval or deny

The table isn't meant to be universal policy. The useful question is: if the agent gets this wrong, how hard is it to get back?

The harder the answer is, the less comfortable I am leaving authorization entirely to the agent.

Policy and sandboxing solve different problems

There is another hole if we stop at command policy.

If I allow npm test, I'm allowing project code to execute. That code can spawn another process, which can execute something else. At some point parsing the original command stops telling me what the process can affect.

That's where I separate policy from containment. Policy decides whether something should start. A sandbox limits what the resulting process can touch.

I don't have a comprehensive sandbox model across my projects today. That's deliberate unfinished work, and I think it deserves a separate article alongside network policy.

Extensions deserve scrutiny too

Pi's extension model is one of the reasons I use it, but extensions sit in a privileged position. They can observe or intercept tool calls, add tools, introduce approvals, and change how execution works.

I treat them more like privileged dependencies than convenience plugins.

There is an especially interesting case when an agent helps write the extension that will govern its own future execution. That's useful, but I want a much stronger review posture for that change than I do for an ordinary application edit.

4. Evidence: I want to know what actually happened

I could watch Pi work, inspect Git afterward, and read the session transcript, but I couldn't easily answer a question that felt like it should be simple:

What exactly happened during this session?

Shell history gives me one piece. The agent transcript gives me another. Git gives me another. None of them gives me the whole execution story on its own.

That gap led me to start building BashGuard, an open-source, local-first companion for Pi. I'm using it to explore how much of a session's execution story we can reconstruct from the evidence Pi and the surrounding development environment actually give us.

The project is deliberately observation-first right now. Rather than turn this article into a moving list of BashGuard features and limitations, I'm keeping the BashGuard tool page as the living overview and the GitHub repository as the detailed source of truth.

The part that matters for this safety model is what building it has exposed: observability is not the same thing as provenance. Putting a prompt, tool call, command, file event, and Git diff next to each other can be useful evidence without proving that one caused the other.

BashGuard connects agent intent to execution evidence and recovery context

I want the evidence layer to make those gaps visible. If the relationship is strong, show why. If it is partial, missing, or only temporal, say that too. A confident story built on incomplete evidence is worse than an honest gap.

That also exposed a second problem: seeing what happened isn't the same as being able to undo it.

Git is a great recovery mechanism for source code. It doesn't unsend an email, restore a deleted external resource, reverse an IAM decision that already had an effect, or necessarily put a database back where it was.

So recovery feeds back into authorization. If I don't have a credible way to undo an action, I should probably be more conservative about allowing it in the first place.

Learn before writing hundreds of policies

Building the evidence side has also changed my instinct around enforcement.

As a security engineer, my reflex is to define policy and enforce it. With coding agents, I'm not convinced we always understand normal execution well enough to start there.

I'd rather observe first.

If a project has run npm run test:auth dozens of times and the observed impact has consistently stayed inside expected paths, that is useful evidence for suggesting a project rule. The same applies if I keep approving the same seed command in one repository.

The important part is that observation can produce a recommendation, not a silent permission grant. I still want the human to change the policy.

The workflow I'm experimenting toward is closer to:

observe → understand → recommend → review → enforce

That feels more useful than starting with a generic list of supposedly dangerous Bash commands and hoping it maps cleanly to every project.

Orchestration makes these boundaries harder to ignore

Most of my examples here are about one developer working with one agent. The same questions become more important when one agent delegates work to others.

A research agent may only need repository read access, documentation, and the web. A coding agent may need write access and Bash. A reviewer may need git diff and test results but no ability to change the repository.

They don't need to inherit the same tool surface just because they are part of the same workflow.

With an interactive coding agent I can still watch a lot of what is happening. That gets harder with scheduled agents, event-driven workflows, longer-running tasks, and multiple agents handing work to each other. At that point context, capability, execution, and evidence stop feeling like optional safety features. They become part of the orchestration layer.

Where I've landed so far

None of this feels like a new branch of security. It's mostly familiar ideas showing up around a new actor: least privilege, scoped access, runtime enforcement, human authorization, containment, audit, and recovery.

What has changed for me is where I apply them.

I'm fairly mature in how I give coding agents context and project-specific skills. I use human approval for some consequential workflows such as IAM changes. I'm building out the evidence side with BashGuard.

I still have gaps. I don't consistently restrict outbound network destinations. My sandboxing approach isn't where I want it to be. Project-aware Bash policy and recovery are still areas I'm exploring.

This isn't a finished reference architecture, and I don't want to present it as one. It's the model I'm using to decide where I trust the model, where I trust the harness, and where I still want a human in the path.

As agents get better at finding their own execution paths, I expect that distinction to matter more.

A good prompt helps the agent understand what we want.

The harness still decides what we're willing to let happen.