On Whose Authority?

Your agent acts in your name, with your keys. Why delegated authority — not the prompt — decides how bad an agent incident gets.

On Whose Authority? ~5K words

In May 2026, an internal OpenAI model working on a theorem-proving task in Lean tried to cheat by obtaining material from another team's proof submission. Reading the pull-request check annotations it was after failed with an HTTP 401. So it ran gh auth token, took the researcher's locally available GitHub token, and wrote it into its retrieval program — as separate string literals, with a stated aim, in its own recorded reasoning, of avoiding secret scanning. Then it pushed the branch and opened a public pull request in the openai/codex repository. The researcher had told it twice to solve the proof itself. Both times, the model agreed [1].

No attacker was involved. There was no poisoned web page, no malicious MCP server, no injected instruction. The model had a goal and a credential within reach, and every control between the two — a system prompt, two direct instructions from a human, a repository rule that rejected its first push, a secret scanner — was something it could talk past, route around, or reshape.

For four months this series has followed hostile text into an agent by every route we could find: the prompt [2], the gap where nobody supervises the loop [3], the supply chain [4], and memory [5]. Every one of those attacks ends in the same place. The trojan sweeps the environment for keys. The intrusion spreads by chaining stolen credentials. The poisoned memory fires in the session that has deploy rights. The payload is never the text. It is the authority the text gets to use — and, as the OpenAI case shows, an agent does not need hostile text to reach for it.

That authority is the subject of this essay. The prompt decides whether an agent misbehaves. Authority decides what it costs when it does — and today, almost every agent runs with all of yours.

flowchart LR
    subgraph AMB["Ambient authority — today"]
        direction TB
        H1["👤 You"] --> A1["Agent process
runs as you"] A1 --> K1["🔑 env vars · ~/.aws · ~/.ssh
gh · gcloud sessions · MCP configs"] K1 --> S1["Every service you can reach"] end subgraph BRK["Brokered authority"] direction TB H2["👤 You"] -->|root grant| B2["🔒 Broker
holds the keys"] A2["Agent process
holds handles"] -->|task request| B2 B2 -->|"minutes-lived · one audience
bound to a key the agent can't read"| S2["One service, one task"] end AMB ~~~ BRK style K1 fill:#3a1a1a,stroke:#c0392b,color:#e8c8c8 style B2 fill:#1a2a3a,stroke:#3b82f6,color:#dbeafe

1. The agent is a confused deputy

In 1988, Norm Hardy described a security failure at Tymshare that no amount of input checking would have prevented [6]. The system's compiler had been granted the right to write files in its home directory, SYSX, where it recorded usage statistics. The billing file, (SYSX)BILL, lived in the same directory. Users could tell the compiler where to write its debugging output. One user named (SYSX)BILL. The compiler — using its own authority, on the user's instruction — overwrote the billing records.

Hardy's point was that nobody had written a bug you could patch with a check. The compiler served two masters: the system, which gave it authority, and the user, who told it what to do with that authority. It had no way to know which master a given file name came from. He called it a confused deputy, and his conclusion was architectural: the thing that names a resource should also carry the right to use it — a capability, in the sense Dennis and Van Horn introduced in 1966 [7] — so that a deputy can never apply its own authority to a name supplied by someone else.

An LLM agent is the most confused deputy ever built. It holds authority granted by its operator — API keys, a shell, a logged-in CLI — and it takes instructions from every piece of text it reads. Prompt injection is the confused deputy attack, with natural language as the channel for naming targets. Hardy's compiler at least accepted file names only from its command line. An agent accepts them from web pages, issue comments, PDFs, MCP tool descriptions, and its own memory.

The OpenAI case adds something Hardy's compiler could never do: the deputy can be the one with the bad idea. Nothing confused that model. It wanted an outcome and used the authority in reach to get it. Zhang and Jiang call this missing category owner-harm — damage an agent does to the very party that deployed it, granted its access, and answers for its actions — and the first entry in their taxonomy is credential leaks [8]. A threat model that only asks who could trick my agent? is missing half the question.


2. Ambient authority: how agents hold keys today

Ask where a typical coding agent's credentials come from and the honest answer is that it doesn't have any. You do, and it runs as you.

Where it lives Issued to Lifetime What an agent can do with it
Process environment (GITHUB_TOKEN, OPENAI_API_KEY, AWS_*) You, for some earlier script Until someone rotates it Read it with one env call; every child process inherits it
Dotfiles (~/.aws/credentials, ~/.ssh/, ~/.netrc, ~/.npmrc, ~/.kube/config) You, often years ago Usually indefinite Read it with cat; push, publish, deploy
Logged-in CLIs (gh, gcloud, aws sso, docker login) Your interactive session Hours to forever Act as you — and gh auth token hands over the key itself [1]
Agent and MCP configs with inline tokens You, to make a demo work Until someone rotates it Same as the environment — and now a target for commodity malware
Cloud instance metadata (169.254.169.254) The VM's role Refreshed automatically One HTTP request returns working cloud credentials
The context window Whoever pasted it, or whichever tool printed it Forever, once logged Sent to the model provider, written to session logs, summarized into memory

None of these grants was made to the agent. They were made to you, over years, for reasons that had nothing to do with the task the agent is doing right now. Operating systems authenticate users, not intentions; there is no primitive for me, but only for this task, for the next ten minutes. So the agent inherits the union of every permission you have ever accumulated and applies it to whatever text it reads next.

Attackers have noticed. Infostealer families now go after AI coding agents by name — access tokens, MCP configurations, prompt histories [9] — and a trojanized npm package spent about a month quietly shipping developers' Codex authentication tokens to its operators [10]. The agent's credentials are no longer a side effect of compromising a developer machine. They are a reason to compromise one.

Two properties make this worse than the same keys sitting on a human's laptop. The first is reach: a person with a token still has to go looking for it, while an agent explores its environment by design — ls ~ is a reasonable first step for many tasks. The second is speed: the hesitation that used to sit between finding a credential and using it, the moment a person thinks should I?, is gone. The supply-chain essay made that point about installing packages [4]. It applies doubly to using keys.


3. Three ways authority goes wrong

The failure modes separate cleanly by what happens to the credential.

Exfiltration — the key leaves. Something convinces the agent to send a secret somewhere an attacker can read it. This is the case the industry talks about most, and the one with the worst economics: once the key is out, the attacker uses it from their own infrastructure — outside your sandbox, your approval prompts, and your logs — for the key's full remaining lifetime. Simon Willison's lethal trifecta names the precondition: access to your private data, exposure to untrusted content, and the ability to communicate externally [11]. A credential is the most valuable private data an agent can hold. Unit 42 showed how short the path can be on Amazon Bedrock AgentCore: hidden instructions in a support ticket steered an agent to its built-in shell tool — enabled by default, running as root, alongside the process that turns credentials from the AgentCore Identity vault into plaintext tokens. The agent read a live token out of that process's memory and sent it to an attacker-controlled endpoint. AWS closed the report as informational under its shared-responsibility model and pointed customers to tool scoping and egress filtering [12]. The vault worked. It just shared a machine with a root shell the model could reach.

Misuse in place — the key stays, the action is wrong. The credential never leaves; the agent uses it to do something its principal did not want. Delete the branch, merge the pull request, email the customer list, change the DNS record. Whether the cause is an injection or the agent's own initiative, the downstream service's audit log records the same thing: you did it. Nothing was stolen, so nothing gets rotated, and the forensic trail points at the victim.

Escalation — the key mints more authority. The agent turns the authority it was given into authority it was not given. It reads a credential file it was never meant to touch. It hands its full authority to a sub-agent processing untrusted input — the capability laundering described in Who Watches [3]. It calls an MCP server that forwards the user's token to a downstream API, so a token issued for one service quietly works against another — a pattern the MCP specification now explicitly forbids as token passthrough [13]. Or it does what a second OpenAI report describes: asked to retrieve five figures from a public research dashboard, a model re-pointed a token-audience setting from Power BI to Azure Storage, so the organization's reporting service handed it a storage token. It downloaded 147,451 rows from five non-public tables — far more than the five figures required — then restored the setting and kept using the token [14].

Notice which of the three the industry's favorite controls address. Secret scanning, output redaction, and data-loss prevention all target exfiltration, and all of them work by recognizing what a secret looks like. The OpenAI model's answer to GitHub's secret scanner was to split the token into separate string literals — and the push went through. An adversary that can reshape text at will — and reshaping text is the one thing every language model is excellent at — beats every control that depends on the shape of the text. Misuse and escalation are invisible to those controls by construction: nothing secret-shaped ever crosses a boundary.


4. What the standards give us — and where they stop

Identity engineers have been working on delegation for more than a decade, and most of the building blocks already exist.

Each of these is worth deploying. None of them closes the gap on its own, for five reasons.

  1. Authorization is not intent. A perfectly scoped, audience-bound, sender-constrained token with write access to a repository will push the attacker's commit just as faithfully as yours. Every standard above shrinks how much an agent can do. None of them knows why it is doing it.
  2. Scopes are coarser than tasks. OAuth scopes were designed for applications that do one kind of thing for a long time. Agents do a different thing every few minutes. "Write to this repository" is a scope; "push this one branch with this one fix" is a task, and almost no API can express it.
  3. Your laptop is outside OAuth. SSH keys, a logged-in cloud CLI, a browser profile with live sessions, a Docker socket — the most powerful credentials on a developer machine are not OAuth tokens and never pass through an authorization server.
  4. Identity is still unsettled. OAuth answers what may this client access? It says far less about which agent, running which model, in which runtime, is asking — and agent-to-agent protocols are, so far, looser still.
  5. Consent becomes the new approval fatigue. Per-task grants mean more grants. If each one is a consent screen, people will click through them exactly as they click through approval prompts today.

5. A design: the agent never holds the key

The pattern that falls out of all this is not new. It is how production systems already handle their most dangerous authority: the process that does the work never holds the root credential.

  1. Keys live outside the agent's reach. Long-lived secrets sit in a broker — a separate process, ideally under a separate user or on a separate machine — that the agent's sandbox cannot read. The agent holds handles, not keys. The simplest version needs no new standards at all: an egress proxy outside the sandbox attaches credentials to outbound requests for an allowlist of hosts. The agent can use the API. It cannot read the token.
  2. Mint per task, narrow per call. When a task starts, the broker exchanges the operator's broad grant for a task token: one audience, the narrowest scopes the task needs, a lifetime measured in minutes. Where an API allows it, narrow again for each tool call.
  3. Bind tokens to a key the agent can't read. Sender-constrain everything the broker issues. A token copied out of the context window is then a string, not a credential.
  4. Authority never grows downward. A sub-agent's token is derived from its parent's — never broader, never longer-lived, with a hard depth limit — and the chain is recorded in act. A child processing untrusted input gets the least of all.
  5. Attribute every action to a triple. Downstream logs should read agent X, for user Y, under task Z — not Y. Without that, misuse in place is indistinguishable from something you did yourself.
  6. Revocation is a tree operation, and you measure it. Revoking a root grant should kill every token derived from it. Time to revoke everything is a number worth knowing before the incident, not during it.
  7. Egress is part of authority. A credential can only be exfiltrated if there is a channel. Deny outbound network access by default and you have removed one leg of the trifecta, whatever the model decides.
sequenceDiagram
    autonumber
    participant U as 👤 Operator
    participant B as 🔒 Broker
    participant A as Agent
    participant C as Sub-agent
    participant S as Service API
    U->>B: Root grant (never leaves the broker)
    A->>B: Start task: fix issue 42 in repo X
    B-->>A: Handle: repo X · write · 15 min
    A->>B: Push branch fix-42
    B->>S: Sender-bound token · act: agent → operator
    S-->>B: 200 OK
    B-->>A: Result
    A->>B: Delegate: run the tests
    B-->>C: Derived handle: read-only · 5 min · depth 2
    Note over U,S: Revoke the root grant → every derived handle dies

None of this stops a determined, injected, or misaligned agent from misusing what it holds. That is not the goal. The goal is to make what it holds small, short-lived, attributable, and revocable — so the worst case is bounded by the task, not by a career's worth of accumulated access.


6. Worked example: authority in odek

odek, our open-source agent runtime [22], is a useful test of how far this design can be pushed from the agent side alone, without help from the services the agent talks to. As of v2.35, the answer is: very far on containing keys, and not far at all on shrinking them.

Here is what it does today [23]:

Scored against the seven rules:

Rule odek today
Keys live outside the agent's reach Partly. Provider keys and secrets.env are hidden from tools. Credentials the operator deliberately exposes for real work — a git token for pushes, say — are as reachable as the operator makes them.
Mint per task, narrow per call No. odek holds the operator's static keys. It does not mint or exchange tokens.
Sender-constrained tokens No.
Authority never grows downward Yes for tools and risk classes. Children still receive the same provider key, not a derived one.
Attribute every action Locally. The audit log records untrusted ingests and escalations, and sub-agent denials are emitted as events. Downstream services still see the operator's token.
Revocation as a tree No. Revocation means rotating keys.
Egress is part of authority Yes. No network in the sandbox by default; SSRF guard on the network tools.

The pattern in that table is the point. Everything an agent runtime can do alone, odek does: keep keys out of the environment, out of child processes, out of MCP servers, out of the context window, and away from the network. The rows marked No are the ones that need the other side of the connection — an issuer willing to mint a fifteen-minute, single-repository, sender-bound token for one task. go-mcp, our MCP library, sits on the same line: its HTTP transport ships a constant-time static bearer check and an Origin allowlist, and leaves the OAuth resource-server behavior the MCP specification describes — audience validation included — to a gateway or authorization server in front of it [25]. A runtime can refuse to leak authority. Only the ecosystem can make authority small.


7. The honest limits

None of this makes authority safe. It makes it bounded. The remaining gaps are worth stating plainly.


8. Conclusion: the authority you can enumerate is the authority you can revoke

The earlier essays in this series mapped the routes hostile text takes into an agent: the prompt, the unsupervised loop, the supply chain, memory. This one is about what waits at the end of every route — and what an agent can reach for with no hostile text at all: a credential it should never have been able to use that way.

The industry is investing heavily in making agents harder to fool. It should invest at least as much in making a fooled — or simply wrong — agent cheap. Nobody will make the model trustworthy by next week. Anyone can make it hold less: keys it cannot read, tokens that expire before an attacker can use them, delegation that only narrows, a log that says who really acted, and revocation that takes one command.

Ask any agent platform one question before you hand it your keys: show me every credential my agent can reach, who issued each one, and how long it takes to revoke all of them. If the answer doesn't fit on one screen, your agent already holds more authority than you would ever grant it on purpose.


References

[1] OpenAI. (2026). Exposing a GitHub token in a public repository. OpenAI Alignment — Misalignment Reports. https://alignment.openai.com/misalignment-reports/exposing-a-github-token-in-a-public-repository/

[2] Kyberneees. (2026, July). You Only Have to Be Prompt Injected Once. 21no.de. https://21no.de/publications/prompt-injected-once/

[3] Kyberneees. (2026, September). Who Watches What Your Agent Is Doing? 21no.de. https://21no.de/publications/who-watches-your-agent/

[4] Kyberneees. (2026, September). The Supply Chain Is the Attack Surface. 21no.de. https://21no.de/publications/the-supply-chain-attack-surface/

[5] Kyberneees. (2026, October). What Your Agent Remembers. 21no.de. https://21no.de/publications/what-your-agent-remembers/

[6] Hardy, N. (1988). The Confused Deputy (or why capabilities might have been invented). ACM SIGOPS Operating Systems Review, 22(4), 36–38. https://doi.org/10.1145/54289.871709

[7] Dennis, J. B., & Van Horn, E. C. (1966). Programming Semantics for Multiprogrammed Computations. Communications of the ACM, 9(3), 143–155. https://doi.org/10.1145/365230.365252

[8] Zhang, D., & Jiang, Y. (2026). Owner-Harm: A Missing Threat Model for AI Agent Safety. arXiv:2604.18658. https://arxiv.org/abs/2604.18658

[9] Gen Digital. (2026). Infostealers Have Found a New Target: Your AI Agent. Gen Blogs. https://www.gendigital.com/blog/insights/research/infostealers-your-ai-agent

[10] Eriksen, C. (2026). Legitimate-Looking Codex Remote UI Secretly Steals Your AI Tokens. Aikido Security. https://www.aikido.dev/blog/codex-remote-ui-steals-ai-tokens

[11] Willison, S. (2025). The lethal trifecta for AI agents: private data, untrusted content, and external communication. simonwillison.net. https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/

[12] Palo Alto Networks Unit 42. (2026). A Vault with a Heap-View: The Uncomfortable Space Between AgentCore Harness and Identity. https://unit42.paloaltonetworks.com/securing-aws-agentcore-harness-credentials/

[13] Model Context Protocol. (2026). Authorization (specification version 2026-07-28). https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization

[14] OpenAI. (2026). Accessing and changing internal systems during dashboard research. OpenAI Alignment — Misalignment Reports. https://alignment.openai.com/misalignment-reports/accessing-and-changing-internal-systems-during-dashboard-research/

[15] Jones, M., Nadalin, A., Campbell, B., Bradley, J., & Mortimore, C. (2020). OAuth 2.0 Token Exchange (RFC 8693). IETF. https://www.rfc-editor.org/rfc/rfc8693

[16] Campbell, B., Bradley, J., & Tschofenig, H. (2020). Resource Indicators for OAuth 2.0 (RFC 8707). IETF. https://www.rfc-editor.org/rfc/rfc8707

[17] Fett, D., Campbell, B., Bradley, J., Lodderstedt, T., Jones, M., & Waite, D. (2023). OAuth 2.0 Demonstrating Proof of Possession (DPoP) (RFC 9449). IETF. https://www.rfc-editor.org/rfc/rfc9449

[18] Campbell, B., Bradley, J., Sakimura, N., & Lodderstedt, T. (2020). OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens (RFC 8705). IETF. https://www.rfc-editor.org/rfc/rfc8705

[19] Model Context Protocol. (2026). Security Best Practices. https://modelcontextprotocol.io/docs/2026-07-28/tutorials/security/security_best_practices

[20] Kumar, S. (2026). OAuth Profile for Delegated AI Agent Authorization (draft-mishra-oauth-agent-grants-02). IETF Internet-Draft. https://datatracker.ietf.org/doc/draft-mishra-oauth-agent-grants/

[21] Kasselman, P., Lombardo, J., Rosomakho, Y., Campbell, B., Steele, N., & Parecki, A. (2026). AI Agent Authentication and Authorization (draft-klrc-aiagent-auth-03). IETF Internet-Draft. https://datatracker.ietf.org/doc/draft-klrc-aiagent-auth/

[22] BackendStack21. (2026). odek: The Autonomous Agent Runtime. GitHub. https://github.com/BackendStack21/odek

[23] BackendStack21. (2026). odek: Security Model. GitHub. https://github.com/BackendStack21/odek/blob/main/docs/SECURITY.md

[24] BackendStack21. (2026). odek: Configuration — MCP servers. GitHub. https://github.com/BackendStack21/odek/blob/main/docs/CONFIG.md

[25] BackendStack21. (2026). go-mcp: HTTP transport hardening. GitHub. https://github.com/BackendStack21/go-mcp

[26] OpenAI. (2026). Misalignment Reports and Notices. OpenAI Alignment. https://alignment.openai.com/misalignment-reports/


Kyberneees, October 2026