Engineering
11 min

What Keys to Give an Agent

The frightening question about an agent isn't whether it can write code, but what it's allowed to do. An agent with a shell, your credentials and internet access is a new kind of privileged actor. Which keys you hand it decides whether you get a useful assistant or a self-inflicted breach.

The wrong question

When teams talk about AI agents, almost everything is about capability. Can it build the feature? Does it understand the codebase? Will it find the bug? Those are the questions that impress in demos and get shared in Slack threads. They are also the wrong questions, or at least the harmless ones.

The question that ought to keep you up at night is a different one: what is this agent actually allowed to do? Not what it can do — what it's permitted to do. Because an agent that writes code is a pleasant colleague. An agent that has a shell, knows your production credentials and is allowed to phone out to the open internet is something else entirely: a new kind of privileged actor that acts with your authority, in seconds, without tiring — and that can be steered by any line of text that finds its way into its context.

The shift in thinking that most teams still have ahead of them is this: an agent is not a tool you use. It's an actor you permit. And the discipline that decides whether it becomes a useful assistant or a self-inflicted breach is as old as it is dull: least privilege. Who gets which keys.

The confused deputy: acting with someone else's authority

There's a security problem that has had a name since the seventies and that comes roaring back with agents: the confused deputy. The basic figure is simple. One actor holds a permission. Another, who doesn't hold it, gets the first to exercise it on their behalf. The deputy acts with its own, legitimate authority — only at someone else's instruction, and without realising it's being used.

An agent is the perfect deputy. It has your credentials. It has tools. And it processes text from sources that aren't you: a GitHub issue it's asked to read, the response from an API, the contents of a web page, a comment in a file, a ticket in the support system. Any of those sources can contain an instruction. And the agent is structurally poor at telling the difference between "my operator told me to do this" and "this was in the data I was asked to process".

This borders on prompt injection, but it's a broader problem. Prompt injection describes the malicious input — the trick by which someone smuggles the instruction into the context. The confused deputy describes why that input is dangerous in the first place: because the agent holds the authority and the tools to translate it into harm. You can never fully prevent the injection — but you can make sure that the deputy, even when confused, can barely cause any damage. That's what permission design is about. Not stopping the agent from being confused. Keeping the blast radius of its confusion small.

Least privilege for agents

The principle is old and applies to people and services alike: give everyone exactly the rights they need for their job — and not one more. With agents, sound advice becomes hard necessity, because an agent acts faster, wider and more autonomously than any human user you'd hand the same key to.

Concretely, that means a few things you shouldn't negotiate away:

  • Read-only as the default. The overwhelming majority of what an agent does — reading, analysing, suggesting — needs no write access. The default isn't "may do everything unless we forbid it", but "may read, and anything beyond that is a deliberate decision".
  • Scoped tokens instead of God-mode keys. An API token that can see only one repository is a different world from a personal access token with org-wide admin rights. If the agent only needs to read the calendar, give it a "read calendar" token — not the key that happens to do calendars and can also empty the mailbox on the side.
  • No standing prod credentials. The most dangerous combination is an agent with permanent, unsupervised access to production. Credentials that sit permanently in an agent's context or environment are credentials that are permanently exfiltratable.
  • Ephemeral per-task credentials. The right mode is a key issued for this one task, minimally scoped, short-lived, and worthless afterwards. A leaked token that expires in five minutes and could only read one repo is a nuisance. A leaked God-mode key is a reportable incident.

The same holds at the tool layer. When an agent talks to the world through MCP or a similar tool interface, every enabled tool is a right. An MCP server that offers "query the database" shouldn't quietly also be able to "drop the database table" just because both happen to live in the same server. The permission belongs at the level of the individual action, not at the level of "agent talks to database, yes or no".

The blast radius of a shell

There's one permission that dwarfs all the others, and it sounds so innocent: bash. The moment an agent is allowed to run a shell, it doesn't have one tool — it has all the tools. A shell isn't a feature, it's a skeleton key. What the agent can do with it is no longer measured by what you gave it, but by what's reachable on the machine.

Three examples that aren't science fiction, but simply what a shell does:

  • rm. A confused or badly briefed agent asked to "tidy up" a directory can fire an rm -rf at the wrong path. No malice required — a plausible-sounding path, a wrong assumption about the working directory, and hours of work are gone.
  • curl for exfiltration. This is the one that hurts. An agent allowed to read and to phone out to the network can send everything it reads to a foreign host. A .env file, an SSH key, a customer database — a single curl -X POST to an address that was sitting in a doctored issue, and the data is out. The agent didn't "hack" anything. It read and it sent, and it was allowed to do both.
  • git push. An agent with write access to the remote can push code to branches that get deployed. "Write me the fix" becomes, one confused-deputy chain later, "push this to main" — and if the pipeline deploys on push, the road from a doctored instruction into production is shorter than anyone would like.

The lesson isn't "never a shell". The lesson is that bash is the permission with by far the largest blast radius, and it deserves to be treated as one: not a checkbox you tick once during setup, but a deliberate, bounded, monitored decision.

Humans on the doors that don't close again

Not every action is equal. Reading a file is reversible and inconsequential. Dropping a database is neither. The decisive distinction isn't "important" versus "unimportant", but reversible versus irreversible — and for everything irreversible, a human belongs on the door.

Four classes of action deserve a hard human gate, no matter how reliable the agent otherwise seems:

  1. Deploys. Anything that carries code into production. The step from "tested" to "live" is the moment a mistake stops being yours and starts being your users'.
  2. Deletion. Resources, data, infrastructure. Tipping over a table, rolling back a migration, terminating an instance — anything that has no undo.
  3. Spending money. Provisioning resources, taking out a subscription, triggering a payment. An agent allowed to spin up infrastructure unchecked is also an agent that can produce a bill for thousands of pounds.
  4. Sending outward. Emails, messages, API calls to third parties, anything that leaves your boundary and lands in someone else's systems. A sent email can't be recalled, and one to the wrong list is a small breach in its own right.

Human-in-the-loop here doesn't mean "a human glances over it at the end". It means: the agent prepares the action, lays it out, and the final, triggering move is made by a person who understands what happens when they click. For destructive operations the confirmation isn't a ritual, it's the actual safety catch. Everything you'd regret after the click is something you should have seen before it.

Sandboxing, egress and the ratchet

Two technical guardrails make the difference between a mistake and a catastrophe. The first is sandboxing: the agent runs in an isolated environment — a container, a throwaway VM, a walled-off directory — where even an unleashed rm only destroys what was ephemeral anyway. The machine the agent rampages on should never be the machine where something valuable sits irrecoverably.

The second, and this one is chronically underrated, is egress control. The confused deputy only turns dangerous through sending. An agent that's allowed to read but physically can't phone out — because its network can reach only a handful of permitted destinations — cannot exfiltrate, however cleverly it's manipulated. Most setups regulate meticulously what the agent may do and leave the network exit wide open. Yet the egress filter is often the last wall standing between a confused instruction and your data in someone else's hands.

And then there's the dynamic that slowly hollows all of this out: the ratchet. It sounds reasonable and it's treacherous. The agent gets tightly scoped rights. It works. Encouraged, you give it more access so it can get more done. It works again. You give it still more. Every single step is justified by real usefulness, and none feels like crossing a line. The rights only ever move in one direction — up, never back — and at some point there stands an agent with standing prod credentials and an open shell, and nobody can say precisely which decision got it there. "It worked, so we gave it more access" is the sentence with which least privilege quietly privileges itself to death. Rights should be rolled back regularly, not only granted.

The honest other side: a paralysed agent is no answer either

Now the flip side, before the lesson is stretched too far. You can lock an agent down so hard that it becomes useless — and a useless agent isn't a safe agent, it's no agent at all. An assistant that has to ask permission for every single file it reads is worse than no assistant: it costs attention, delivers nothing, and the person at the other end learns in record time to click the confirmation dialogs away blind. At which point the guardrail isn't just useless but harmful, because it trains the one thing you never wanted to train: reflexive approval.

The goal is not zero capability. The goal is to match privilege to blast radius — the same calibration that makes good code review. Read-only and low risk? Let it run, without asking. Reading a file in the project directory, a query against a replica, writing an analysis — none of that needs a gate, and every gate there is wasted attention. Irreversible, expensive or outward-facing? Gate it, every time. The art isn't in blocking everything, but in steering the scarce resource of human attention to where a mistake genuinely hurts.

And here's why over-restriction is not merely inefficient but dangerous: it drives people to switch the guardrails off entirely. A team wrestling with an agent that begs at every step will reliably find the global switch that disables all confirmations — and from then on a God-mode agent runs with no gate at all, because the fine-grained version was unbearable. Too much safety, as so often, ends up producing less of it. The only safety catch that holds is the one you actually leave on.

Conclusion

The frightening question about an agent was never whether it can write code. It can, and it will do so better. The question is what it's allowed to do: which keys sit in its context, which tools are enabled, how far its network reaches, and what stands between its intent and your production. An agent with a shell, credentials and internet is a privileged actor, and you treat it like one — not like an autocomplete.

Least privilege here isn't red tape, it's the one discipline that reliably holds the line between a useful assistant and a self-inflicted breach. Scoped, ephemeral credentials instead of God-mode keys. Read-only as the default. Sandbox and egress filter as walls that hold even when the agent is confused. And a human on every door that doesn't close again.

That's exactly how we run our agents at NH Labs. They get the rights their task needs and not one more; their credentials are scoped and short-lived; they run isolated, with a controlled network exit. And on everything irreversible or outward-facing — deploy, delete, spend, send — sits a human gate no agent gets around. Not because we distrust the capability, but because we know that capability without a bounded blast radius isn't progress. It's just a bigger risk with better PR.