Auth for BYOC apps, part two: when the client is an agent
In our post on authentication for BYOC apps we argued that identity belongs in the control plane. Users sign into your own auth system, the control plane mints short-lived JWTs scoped to a specific deployment, and the data plane verifies them locally against a public key that ships as config. Nothing to call back to on the request path, no stateful identity server to patch in a hundred customer accounts.
We still think that is the right default. It answers the case we built it for, which is a person opening a browser and reaching a deployment that lives somewhere we do not control.
A different case is showing up, and control plane auth does not answer it.
More of what vendors ship into customer environments now includes software that acts on its own. Not the deployment agent we wrote about in The agent nobody operates, which installs your product and reports back. Something further along: a process that reads a queue, decides what to do, and does it. When that process needs to talk to a service outside the deployment, the question stops being whether this browser may reach this data plane, and becomes what this thing is and who is responsible for it.
Why neither existing answer fits
Identity infrastructure has two mature models and an agent inside a customer's perimeter falls between them.
Workload identity answers which piece of software this is. SPIFFE, X.509 SVIDs, IAM roles bound to an instance. It works inside a single trust domain, which is exactly what you have when the agent talks to a database in the same VPC, and it is the right tool there. It stops at the edge of that domain. No SaaS login page accepts an SVID.
User identity answers which person this is. OIDC, SAML, the control plane JWT in our own design. It works when there is a person to authenticate. The agent is not a user in your IdP, because you never provisioned it. It is not a user in the customer's IdP either, because nobody at the customer created a seat and a mailbox for a process your software started.
So the agent borrows, and every way of borrowing costs something.
A shared service account across deployments loses attribution the moment there is more than one agent, and makes revocation all or nothing. Disabling one misbehaving agent in one customer's environment disables every agent in every environment.
An employee's credentials drag that person's one-time code and second factor into the path of every action, which removes the reason the agent exists. It also hands a process standing access to a mailbox full of things that are none of its business, in an environment your security review already promised to keep narrow.
An API key works where the far side is an API and does nothing where the far side is a login page, which is most of the services a customer will ask you to integrate with.
The failure is quiet, which is why it is worth naming
Three things break and none of them page anyone.
The agent cannot complete a signup, because signup wants an email address and it does not have one. So somebody wires in an operations mailbox and moves on.
Anything the service sends back goes somewhere the agent cannot read. Verification codes, rate limit warnings, invoices, deprecation notices, the answer to the support ticket the agent opened last week. In a BYOC deployment this is worse than usual, because the mailbox is on your side of the line and the agent is on the customer's.
And the receiving service cannot tell an agent from a human. It cannot enforce per-person limits, it cannot attribute usage, and it has nobody to contact when something goes wrong. Its rational response is to refuse what it cannot identify. Gmail already bans accounts created and operated by agents, which is why giving a fleet consumer mailboxes stopped being viable.
None of that shows up as an error in your telemetry. It shows up as a workflow that silently never completes, weeks later, in one customer's environment.
The shape of the answer
The model that resolves this treats the agent as a principal rather than a delegate. It holds its own account, its own credentials and its own address, and the human behind it is a claim the receiving service can read rather than the account the agent is borrowing.
AgentID is the implementation we would point at today. It is a standard OpenID Connect provider, which matters more than it sounds: a service that already accepts Google or GitHub adds it as another provider with an issuer and a client_id, so adoption on the far side is configuration rather than engineering. A sign-in returns a stable identifier for the agent and a verified email address belonging to it, and a registered service can read the accountable human's email separately. The sign-in completes in a browser, and a headless one is enough, so it runs inside the customer's environment with nobody present. The address in that identity is also where replies land, which is the part that closes the receiving problem, and is what AgentMail provides as an inbox per agent.
We have no integration with any of this. We are pointing at it because it is the only approach we have seen that survives the constraints we already design around.
Why those constraints are the interesting part
Look at what this arrangement does not require, and compare it to the list in our first post.
No inbound ports. The agent initiates, which is the same posture we take for remote commands, and for the same reason: a security review that has already blocked inbound traffic will block it again.
No long-lived secret sitting in an environment you do not control. Whatever the agent holds is scoped to the agent and does not unlock anything else, which is the difference between a finding and a non-finding when somebody audits the deployment.
No dependency on the customer's IdP. This is the one we would weigh most heavily. The fallback we described in part two of the original post, federating per deployment to the customer's Okta or Entra, multiplies configurations, credential rotations and support load by the number of customers. An identity the agent brings with it does not.
And no human in the path, which is the assumption that makes control plane auth work for people and stop working for agents.
Where this leaves the original argument
Unchanged, for what it covered. Keep identity in the control plane for your users. Ship the public key as config. Do not run Keycloak in a hundred customer accounts.
Add a second answer for the clients that are not people. Control plane auth establishes that a request may reach a deployment. It says nothing about what a process inside that deployment is when it talks to the outside world, and increasingly something inside there is doing exactly that.
BYOC settled where the data lives. What the software does once it is there, and whether the far side can tell who it is acting for, is the next thing to design rather than discover.