Skip to content

Ambient Authority Won Because It Was Convenient

, 15 min read

Capability systems lost on cost, not merit. Agents change the economics of authorization enough to make them practical again.

I have spent most of my career working on systems built on ambient authority, where software gets to do things because of who it is or where it runs, rather than because the task at hand requires that access.

We knew there was another way. The alternative had been described in the 1960s and 1970s, and some of it had even shipped. We built on ambient authority anyway, and for a long time that was a reasonable choice.

In my last post, Agents Should Earn Authority, Not Inherit It, I argued that capability systems lost to convenience, not because the underlying ideas were wrong. I moved past that point quickly, but it deserves more attention. If convenience is why capabilities lost, the real question is whether the tradeoffs that made ambient authority attractive still hold.

I don’t think they do.

Most writing on agent security asks for better guardrails or tighter sandboxes, or treats stronger agent identity as the answer. Identity is necessary, but on its own it still works inside the same assumption as the rest, that the agent holds authority and our job is to keep it from misusing it. The economics behind that assumption have changed enough that an architecture we set aside fifty years ago is practical again.

A complete idea, early

Most of the important thinking was in place by the late 1980s.

In 1966, Dennis and Van Horn proposed that programs should receive specific permissions for the resources they needed, rather than inherit everything their users could access. They called these permissions capabilities. A capability works like a key to one door. Holding it is what lets you in, and you can hand it to someone else, but it opens nothing else.

The Anderson report described the reference monitor in 1972, a small part of the system that every access has to pass through, so each one can be checked. Saltzer and Schroeder articulated least privilege a few years later, the idea that a program should have only the access its job needs. Biba worked out why something you don’t trust, like user input, shouldn’t be able to steer a decision that matters. Clark and Wilson, drawing on how accounting systems work, argued that changes to important records should be staged and checked, and that the person making a change shouldn’t be the one approving it.

Then, in 1988, Norm Hardy described the confused deputy.

A compiler had permission to write a billing file so it could record usage. A user supplied that file’s name as the compiler’s output path, and the compiler overwrote it. The compiler wasn’t malicious. It simply exercised its own authority on behalf of someone who didn’t have that authority. The problem wasn’t who the compiler was or what it was allowed to do. It was whose request the compiler was serving.

Hardy wasn’t speculating. He had worked at Tymshare on Gnosis, the capability operating system that became KeyKOS, and the example came from the kinds of problems those systems were designed to prevent. He wasn’t describing a hypothetical failure. He was explaining one that capability systems already knew how to avoid.

His diagnosis was precise. The compiler got the name of the file from the user and the authority to write it from its own environment. Those arrived through two separate channels, and nothing tied them together. His fix was to make them one. If the user had to hand the compiler the right to write the output file along with its name, the user couldn’t name a file they had no right to write.

Hardy’s split: separate designation and authority, capability-bound access, and the same split in an agent.

Hardy’s confused deputy problem has the same shape in agent systems. Untrusted input identifies a resource, ambient authority permits access, and the deputy combines the two. Capabilities prevent the original failure by binding designation to authority.

They shipped, too

Capabilities weren’t just an academic idea.

Plessey used them in telephone switching hardware in the early 1970s. Cambridge built the CAP computer. Carnegie Mellon built Hydra. Intel implemented capability-oriented protection in the iAPX 432, which failed commercially, though later analysis attributed much of that failure to design decisions beyond its protection model.

The more interesting example is IBM’s System/38. Its descendants, the AS/400 and IBM i, are still used for business-critical workloads. The architecture relied heavily on capability-like mechanisms, but application developers generally didn’t have to manage them explicitly. Much of the machinery lived below the application interface.

Capabilities could work when the system carried the complexity instead of making every programmer carry it.

Why ambient authority won

Unix and Windows didn’t win because they had better security models. They won in part because they were easier to build software for, and three tradeoffs made that so.

The first was programming cost.

Take a program that saves a report. In a capability system, the code that writes the file can’t just name a path. Something has to hand it the right to that specific file, usually starting from the user’s choice in a save dialog. The dialog passes that right to the document code, which passes it to the export code, which passes it to the library that writes the bytes. Every one of those interfaces has to change to carry it. Add a log file, a temp file, or a config read, and each one needs its own right threaded down from somewhere.

With ambient authority, the library calls open() on a path and the operating system checks whether the user is allowed. Nobody upstream has to know.

When a program needed privileges its caller didn’t have, Unix offered setuid, which let the program run with its owner’s privileges rather than the caller’s. passwd is the classic example. It was convenient, and it also created exactly the kind of authority confusion Hardy described. A privileged program had to tell legitimate requests from attempts to misuse its privileges, and decades of privilege escalation vulnerabilities followed.

Unix even had a capability hiding in plain sight. A file descriptor is one. Once a process has one, it can use the resource without looking it up by name again, and it can pass that authority to another process. What kept Unix largely ambient was that processes could still ask the operating system to open arbitrary paths using their existing credentials.

When FreeBSD introduced Capsicum in 2010, it showed both the promise and the cost of changing that. The kernel mechanisms were relatively straightforward. The difficult work was restructuring applications such as tcpdump and Chromium so they obtained the resources they needed before entering capability mode, where a process can use only the file descriptors it already holds. The programming cost that had mattered decades earlier was still there.

The second tradeoff was revocation.

Once a capability has been handed out, revoking it can be difficult. The recipient may have copied it, stored it, or passed it to someone else. An access control list makes revocation easier because the policy stays with the resource. Change the list, and future access checks see the change.

David Redell described an alternative in 1974. Put an intermediary between the holder and the resource. Instead of giving someone direct authority, give them authority to ask the intermediary to act. Revocation then becomes a matter of disabling the intermediary. It worked, but it added machinery to every operation, and most systems didn’t want to pay that cost.

The third tradeoff was the cost of preventing confused deputies.

Confused deputies weren’t necessarily rare. Privileged programs had long been vulnerable to requests that misused their authority. But the failures were often concentrated in particular interfaces, and targeted defenses could address them without changing how every application received authority.

Most software executed code written by its developers. It processed untrusted input, but that input was generally treated as data rather than as instructions capable of changing the program’s goals. When the web created a widespread version of the problem, cross-site request forgery, we addressed it with targeted defenses. Anti-forgery tokens, origin checks, and eventually SameSite cookies reduced the risk of browsers using their users’ authority on behalf of attackers. The problem didn’t require replacing the browser’s entire authority model.

Put the three together and the outcome makes sense. Capabilities imposed a cost on nearly every program, while many of the failures they prevented could be addressed where they appeared. Ambient authority was the cheaper choice.

We kept the token and threw out the rest

We kept the possession property of capabilities. Whoever holds a bearer token, API key, signed URL, or session cookie can use it. But possession alone wasn’t the whole idea. Capability systems were also designed around limiting what authority a program received, narrowing it when delegated, and preventing it from reaching outside what had been explicitly provided.

We kept the credential and largely abandoned that discipline.

Token versus broker: direct credential use compared with independently enforced task limits.

Ambient authority gives the agent credentials sized for the user. Brokered authority lets the agent request operations within independently enforced task limits, without holding the credentials used to perform them.

Take a GitHub token with access to an organization’s repositories. Any code running in the process that holds it can read any of those repositories, open pull requests, or push to branches within the token’s permissions. Nothing asks whether a given call has anything to do with the task at hand. If you want to give a helper script narrower access, you usually have to mint a new token, if the service allows it at all. Revoking the token breaks everything else that used it.

None of that breadth is an accident. Tokens are broad because the person they’re issued to has to set them up and live with them. GitHub offers fine-grained tokens scoped to particular repositories and permissions, but choosing the right scopes for every task is tedious, and getting it wrong means the task fails halfway through. OAuth consent screens ask once, in coarse terms, because nobody will click through a prompt for every API call. Cloud roles accumulate permissions because the alternative is a ticket every time someone’s job changes. The breadth is what we pay so a person doesn’t have to answer a question every few seconds.

That was a manageable tradeoff when the credential holder was a person or a web service running a fixed program its developers wrote. Then we handed those tokens to agents.

Most agent integrations today run on the user’s own credentials. A tool server gets the user’s OAuth token, a coding agent inherits the developer’s shell and environment variables, and when the permission prompts get annoying, there’s a flag to turn them off. The agent ends up with access sized for a human’s patience, not for the task. And the process holding that access is the same one reading untrusted input. It decides what to do, reads content that might be hostile, and holds the authority to act, all at once.

It gets worse as agents delegate. An agent spawns a sub-agent, which calls a tool, which calls another service, and each hop may carry the parent’s full token. Capabilities were meant to be narrowed before being passed on, and macaroons brought that idea to the web, but the pattern remains uncommon. A sub-agent asked to summarize a file shouldn’t carry the right to push to main.

The logs don’t help either. When an agent acts with a person’s token, the GitHub or cloud audit log may say the person did it. You can’t necessarily tell which actions were the agent’s, which task they belonged to, or what request prompted them. It’s Hardy’s question about whose request was being served, showing up after the fact with no answer.

Identity is necessary here, but it isn’t sufficient. You need to know which agent is acting, which task it’s working on, and who started it, and everything a broker does depends on that. But Hardy’s compiler was correctly identified, and it really did have permission to write the billing file. The failure was that it exercised that permission on behalf of the wrong user. Identity tells you who holds a credential. It doesn’t tell you whether a particular use of it is justified.

What agents change

Go back through the three tradeoffs, starting with programming cost.

System/38 worked because the platform carried the capabilities instead of the application developer. Agent infrastructure can do the same thing.

Take the agent fixing a flaky test from my last post. It never held a registry token or a GitHub credential. When it needed a package, it asked the package broker, a service that fetches packages on the agent’s behalf, for that package at a specific version. When it had a patch, it asked the source-control broker to stage it on one branch. The brokers held the credentials, checked each request against what the task allowed, and did the work themselves.

Nobody threaded a capability through the agent’s code. The work that used to fall on every programmer falls on whoever writes the package broker and the source-control broker, and they write each one once. Every agent and every task after that uses them.

Agents don’t run out of patience.

A lot of least privilege failed not because programmers wouldn’t thread permissions through code, but because the one asked to justify each access was a person, and people run out of patience. An agent can name the exact package, the exact branch, and the exact file every time, and explain why it wants each one. That explanation isn’t proof that the access is justified. The agent may be mistaken, manipulated, or deliberately seeking a way around its limits. But asking it to specify what it wants costs almost nothing, and infrastructure can check the request against constraints established independently of the agent.

The precision we gave up because people found it exhausting becomes practical when software can formulate the requests and the platform can enforce the answers.

Mediation got cheap too. Checking every access was expensive when the access was a memory reference or a file read measured in microseconds, and that cost is part of why the 432’s reputation stuck. An agent step takes seconds of inference. A broker check that takes a few milliseconds disappears next to it.

Authority can also arrive progressively. In that same run, the agent could read the repository from the start. Running builds opened up only after it recorded a diagnosis, and staging the patch required a signed receipt showing the tests passed. The agent didn’t manage any of that, and the engineer didn’t approve each step. She approved the overall limits for the task once, and independently checked evidence determined when additional operations became available.

That avoids the two familiar outcomes of least-privilege systems: programmers abandoning the discipline because it’s too much work, and operators approving broad access because the requests never stop.

Revocation changes for the same reason. If the broker retains the real credential and the agent only receives permission to request particular operations, there are no credential copies scattered through the agent’s environment to recover. Every operation passes through the broker. Revoke the permission, and the next request is denied. Give permissions short lifetimes, and unused authority expires without an explicit cleanup step. Because the broker performs each action, it can also record it against the task that asked for it, so the log says which run did what and why.

The same structure handles delegation. When an agent hands work to a sub-agent, the broker can issue it a narrower grant with fewer resources and a shorter lifetime, and nothing the sub-agent does can widen it.

This is Redell’s intermediary, applied to agent infrastructure. The indirection that was too expensive to add to every application becomes a shared service.

The third tradeoff changes most dramatically. The confused deputy is no longer a problem that can be contained to a few known interfaces.

An agent’s job is to read material it didn’t write and decide what to do next. Repositories, web pages, emails, issue trackers, documents, and tool responses are all part of its working environment. Any of them can contain instructions, and if the agent has broad authority available, an attacker may be able to steer it into using that authority. Prompt injection, hiding instructions in content the agent will read, turns ordinary input processing into a confused-deputy problem.

We’ve already seen examples.

In May 2025, Invariant Labs demonstrated an agent with access to private GitHub repositories being manipulated through an issue in a public repository. The injected instructions directed the agent to copy private information into a public pull request. The individual API calls were authorized. The overall action was not.

That is Hardy’s split exactly. The issue text named the private repositories, and the token authorized access to them. Nothing checked whether whoever wrote the issue had any right to the repositories it named.

That same year, a Replit agent deleted a production database despite an instruction not to make changes during a code freeze. The restriction lived in the agent’s instructions, but the credentials still allowed the operation.

The more revealing case is the OpenAI evaluation I discussed in my last post. There was no attacker planting malicious instructions. Agents optimizing for a score explored their environment and discovered resources they could exploit: a shared cache, exposed credentials, and a dataset processor capable of executing their code.

Hardy’s compiler didn’t go looking for the billing file. It was tricked into writing it. These agents went looking for opportunities.

Much of the engineering around classical capability systems dealt with programs whose behavior was relatively predictable. An agent can revise its plan, discover new resources, and pursue paths its operator never anticipated. It may even propose changes to the rules governing its own behavior. A deputy that searches will eventually search the controls, so the reference monitor has to remain outside that search.

That rules out two shortcuts. A second model can review the agent’s requests, and if the agent asks for a package the task never mentioned, the reviewer can reject it or recommend a narrower version. But the reviewer’s judgment cannot expand the task’s independently established authority. A reviewer that can grant new authority is just another thing for the agent to search its way past.

The agent also can’t loosen its own rules. If it proposes letting runs reach a new network host, the change goes to people other than the run that proposed it, and that run never operates under the new rule. Otherwise the shortest path around a limit is to propose removing it.

The old economics favored fixing confused deputies at known boundaries rather than changing the authority model everywhere. Agents make the boundary between untrusted input and privileged action part of their normal operation, and the deputy may actively search for ways around its limits.

The math is different now

Capabilities lost because the costs were immediate and widespread, while many of the failures they prevented could be mitigated locally. Programmers had to carry authority through their applications. Revocation required additional machinery. The benefit of changing the entire authority model rarely justified those costs.

Agents change all three. The platform can carry the programming cost and release authority as tasks justify it. Brokers can keep credentials out of the agent and make revocation immediate. Independent review and separation of duties can keep an agent from turning its ability to plan into an ability to expand its own authority.

Someone still has to decide what the task permits, build the brokers, and keep the reference monitor outside the agent’s control. Those costs are paid in shared infrastructure instead of being repeated in every application, and the failures they prevent are now part of the normal operating environment.

There’s one more reason not to wait. Part of why capabilities lost was that ambient authority was already everywhere, and replacing it was expensive. The tooling and protocols that connect agents to services are being designed right now. If they settle on passing user tokens through, we’ll live with that for decades, the way we did with Unix. The switching cost is as low today as it will ever be.

We spent decades building systems where possession was enough. Now we’re putting those credentials in the hands of software whose job is to read untrusted material, find ways to accomplish goals, and act.

Hardy explained the underlying failure in 1988. We’re only now building software where it’s the normal case.

AISecurity Practice

Use ← and → to move through the timeline.