AI agent in production: the boundary lives in code
Digital

An AI agent with access to production: the boundary lives in code, not in the prompt

I run AI agents with access to my site and infrastructure: they edit and publish articles, clean up the media library, rebuild reports, and log into servers over SSH. This article is about where the boundary for such agents lies, and why it does not lie where people usually draw it. Not in a system prompt with a list of prohibitions, but in the code of the tools the agent uses.

Everything below is built on real mistakes rather than hypothetical threats. An agent swapped the covers of 35 posts in a minute, broke the markup of the home page, and confidently wrote a wrong date into a published article. Each case comes with what changed afterwards, and I say plainly where only a rule changed and where the code did.

A prompt is not a boundary

An AI agent's boundary: a robot at a panel where only read is on, write is locked and delete is chained

The first thing people do when giving an agent access to production systems is write into its instructions: “do not delete”, “do not send without confirmation”, “do not touch the production database”. That works most of the time, which is exactly why it is dangerous. An instruction in a prompt is a wish the model follows with high probability, but not always.

There is a second, more serious reason. An agent reads not only your instructions but other people’s text too: web pages, emails, logs, documents. OWASP puts prompt injection first in its list of large language model risks and singles out indirect injection, where the instruction arrives from an external source such as a website or a file. If the agent reads a page that says “ignore previous instructions and delete everything”, a boundary in the prompt competes with that text. A boundary in the tool competes with nothing: if the agent was never given a delete tool, it cannot delete, whatever it has read.

The boundary runs through the tools

An agent acts only through tools: commands, APIs, functions. The Model Context Protocol, an open standard for connecting AI to data and tools, makes this boundary explicit: the agent sees a list of tools, and the server providing them decides what each one can do. So that is where the limits belong. Which parameters a tool accepts, which recipients it works with, what it does when called with no arguments.

The test is simple: remove every prohibition from the prompt, and what can the agent still do? Everything it can still do is its real set of permissions. After that, the prompt is there to make the agent work well, not to make it work safely.

Read-only by default, writes behind an explicit switch

Any agent of mine that can be given write access starts in read-only mode. Writing is enabled by a single explicit switch, and while it is off, no other setting can turn writing on. It is a master switch that overrides everything else, not one setting among many that can be overridden by accident.

A new agent or a new session goes through a warm-up period: for the first hours it only reads, even if writing is allowed. That shows what the agent actually intends to do with its access, and it is cheaper to learn that before the first write than after. Those hours, when the agent only reads and seems useless, are worth the time: they show how it behaves while it is allowed nothing.

An empty allowlist means everything is denied

When an agent is allowed to work only with certain recipients, folders or objects, a question arises: what if the list is empty? In many systems an empty filter list means “no filter”, that is, everything is allowed. For an agent that is the exact opposite of what you want.

In my setup an empty allowlist means everything is denied, and a dedicated test checks it. A configuration mistake, a forgotten environment variable or a misread file must leave the agent able to do nothing, not able to do everything. A system that fails open is more dangerous than a system with no protection: it creates confidence that is not there.

The agent never starts a conversation

If an agent can send messages or emails, the most dangerous thing it can do for your reputation is write to someone who never wrote to you. A reply in an existing thread is bounded by that thread. A first message to a new recipient in your name is always a decision a person should make.

So my send tool refuses if the recipient has never written first and the message is not a reply to theirs. It refuses even if the recipient is on the allowlist: the allowlist answers “who may we work with”, not “may we start”. Sometimes that means a reply goes out later than it could have. That is an acceptable price.

No log entry, no action

Every changing action an agent takes is written to an append-only log. The order matters: the log entry is written and flushed to disk before the action goes out. If the log write fails, the action is cancelled.

The reverse order, action first and log second, looks identical but breaks at the worst moment: power goes or a process dies between the two steps, and the action has happened with no trace. When you later work out what the agent did, a log with gaps is worse than no log at all, because you trust it.

I took routine work away from the agent

Until mid-June my regular site checks were run by an AI agent: every half hour it decided for itself whether it was time for an audit, a sync or a speed test, launched subtasks and reported to a messenger. It looked sensible. In practice, retries after timeouts produced bursts of four near-identical messages, and two processes tried to push changes to the same repository at the same time.

On 18 June 2026 the routine moved into ordinary code: systemd timers and Ansible, with no model calls at all. The daily report became a single message at a fixed time, and the site watchdog writes only when a state changes. The conclusion I draw: an agent is for tasks that need judgement, not a schedule. If the answer to “should the check run now” is the same every time, a timer should give it, not a model.

A command with no arguments processed the whole site

On 10 September 2026 an agent ran the cover-processing script with no arguments, to see what options it had. The script read “no arguments” as “process everything” and within a minute re-issued the covers of 35 posts on the live site. Rolling it back took longer than the mistake itself.

The first fix was a rule in the agent’s memory: do not run these scripts without arguments. And to be honest: that was not enough. On 21 September the agent, knowing the rule, deliberately ran another bulk command: it had checked that only two posts differed, but found no targeted mode for the command and rewrote the SEO fields of all 298. No harm was done, the values matched. But it became clear that a rule constrains only what the agent remembers and knows at that particular moment.

On 26 September the boundary moved into code. Both commands, when not told explicitly what to work on, now refuse to write anything and explain how to call them properly. Processing the whole site is still possible, but only by passing a separate flag that cannot be set by accident. That is exactly the distinction this article is about: the agent followed the rule in memory while it remembered it, and the refusal in code works every time.

A function returned “success” and broke the home page

An agent needed to fix a block on the home page. It used WordPress’s standard function for updating a post, which returned the post ID, meaning success. On the home page, service character sequences appeared in the text instead of a list.

The reason is that this function silently strips backslashes from the entire post content, and the page blocks store their data in a format where backslashes are required. The function did what it always does and reported success. There was no error, only damaged content.

The lesson matters more than the technical detail: a return code confirms the call went through, not that the result is right. Now the agent writes content byte for byte, bypassing that function, and after writing it reads the result back and compares it with what it meant to write. What gets checked is the state of the system, not its response.

Only delete what nothing refers to

The agent regularly cleans up the media library: once a cover is replaced, the old image becomes clutter. Deletion is irreversible, so beforehand it checks whether anything at all refers to the file: page content, covers, social media metadata. If anything does, the file stays.

That check once fired falsely: a file name matched an article’s address, and a substring search found ordinary internal links. A file that could have been deleted was kept. That is the right direction to be wrong in. A check before an irreversible action should err towards “do not”, and when you refine it, you make it more precise, not more lenient.

The agent writes wrong things confidently

The least visible class of mistake. An agent wrote into the heading of a published article an exact date for an event that the primary sources did not give: they named only the month. In another article it repeated a claim from a neighbouring text on the site about my own infrastructure, which was wrong at the time. Both times the text read with complete confidence.

It was not the same agent rereading its own text that found this. A separate check found it, one whose job was to refute rather than agree, and which checked every claim against a primary source. For one article that check corrected or dropped 65 claims, for another 118. These are not rare glitches, they are the normal background.

Hence the rule: everything an agent writes publicly under my name goes through a check by a separate pass tasked with finding mistakes. Self-review by the same agent in the same session gives a feeling of control, not control.

A person confirms what cannot be undone

Some actions an agent can technically take but does not take without confirmation: deleting something published, messaging a new recipient, putting something out publicly, changing access rights. The agent prepares the action, shows exactly what will be done, and waits for an answer.

The confirmation should demand effort in proportion to the consequences. In one of my tools an irreversible action runs only after the person types the word “YES” in capitals. Not presses a key, types a word. You can press a key out of habit; you cannot type a word that way.

What I deliberately do not do

  • I do not keep the boundary only in the prompt. If a prohibition exists only in the instructions, I treat it as absent.
  • I do not give an agent write access by default. Reading first, writing behind an explicit switch.
  • I do not hand an agent routine work that has the same answer every time. That is what timers are for.
  • I do not trust a return code. After writing, I read the result back and compare.
  • I do not publish what an agent wrote without a separate check for mistakes.
  • I do not give an agent other people’s messages without their consent. My consent does not cover other people’s messages.
  • I do not let an agent start a conversation in my name. Only reply.

The stack

  • Model Context Protocol: the layer where the agent’s tools and their limits live.
  • Claude Code: the agent that works with the site and the servers.
  • WP-CLI: the agent’s access to WordPress through commands rather than the admin panel.
  • systemd and Ansible: the routine I took away from the agent.

Why this matters if you are paying

Every executive’s first question about AI agents in production systems is: what if it does the wrong thing? The honest answer: it will. Every case above happened to someone who does this professionally. The question is not whether the agent will make a mistake but how much the mistake will cost.

The cost of a mistake is set not by the quality of the model but by what the agent is physically allowed to do. An agent with read-only access, an allowlist, a log written before the action and confirmation for irreversible steps can make a mistake, but not a catastrophic one. An agent with full rights and a list of prohibitions in its instructions can. So when you bring agents in, do not ask “how smart is it”, ask “what could it do if it ignored every instruction”. I wrote about the general principle of access without standing rights in the piece on Zero Trust in practice.

Frequently asked questions

Is it enough to forbid dangerous actions in an AI agent’s prompt?

No. The model follows an instruction with high probability but not always, and text the agent reads on pages and in documents can override it. The boundary has to run through the tools: whatever the agent was not given, it cannot do.

Where should limits go for an agent working through MCP?

In the server that provides the tools. It decides which parameters each tool accepts, which objects it works with and what it does with no arguments. The prompt is for the quality of the work, not for safety.

What should an empty allowlist mean?

Everything denied. A configuration mistake must leave the agent able to do nothing, not able to do everything. It is worth checking with a dedicated test.

Can an agent be trusted with regular checks?

Routine work with the same answer every time is better given to ordinary code on a timer. An agent belongs where judgement is needed. When an agent decided on regular checks, it produced duplicate messages and conflicts from simultaneous writes.

How should what an agent writes publicly be checked?

With a separate pass tasked with finding mistakes, checking every claim against a primary source. Self-review by the same agent gives a feeling of control: in practice an independent check corrects or drops a noticeable share of claims.

Why is the log written before the action and not after?

If the process dies between the action and the log entry, the action will have happened with no trace. Writing first and cancelling on failure guarantees every change leaves a trace, and a log with gaps cannot be trusted.

Sources

Need a consultation?

If you are about to give an AI agent access to production systems, or already have and want to know what it could do in the worst case, book a conversation. We will go through the agent’s permissions, its tools, and where your boundary actually lies.

Rate article