Table of Contents

I’ve been working on Agent Identity lately.

My mental model started out pretty simple: to send an agent off to do one specific thing, you need answers to at least these questions:

  • Which agent / workload is this?
  • Who does it represent right now?
  • What did that person actually authorize it to do?
  • When does that authorization expire?
  • Is this particular tool call allowed?

So I spent a while building that whole call path out: the credential can be verified, the delegation chain can be walked, the scope can be checked, the expiry can be checked, and authorization runs again before every single tool call. Different refusal reasons stay distinguishable too.

As the flow came together, my test cases piled up, and in the end I just had Claude fill them out to fifty-odd. All green, no surprise there (ha). I was honestly pretty pleased with myself. Then during code review I accidentally deleted the line that calls authorization. Ran the tests again. Still all green. I sat there for a second: crap, the tests are garbage.

But the more I thought about it, the less that held up. If this were an ordinary library, sure, you could call it a test coverage problem. You ship an authorize(), the application developer forgets to call it, that’s an integration bug. Except what I was building isn’t a use-it-if-you-feel-like-it helper. It’s a security boundary the agent is not, in theory, supposed to be able to get around. If application code can delete that one line and the agent still calls the tool, what is all that beautiful authorization actually worth?

That’s the first time I got genuinely stuck. I had been framing the problem as:

What’s still missing from identity?

And the question suddenly became:

Who guarantees identity gets enforced at all?

Those are completely different questions.


I had runtime wrong

When I used to see the term ā€œAgent Runtimeā€, honestly, it didn’t land. The runtime in my head was closer to:

ā€œthe thing that runs the agent.ā€

LangGraph, some SDK, a graph executor, a planner loop, roughly that category. So I thought of runtime as a framework-side concern. Identity was the security feature. Build it properly, wire it in, done.

But the moment I pulled the authorization call out and the tests stayed green, I thought for the first time that the model might be wrong at the root. Because if a security check can be selectively bypassed by application code, it isn’t a boundary, it’s an API.

This reminded me of a lot of my platform work. SELinux matters, and not because it has a policy file. Android permissions matter, and not because there’s a permission table somewhere in the framework.

What actually matters is this:

the last path to the resource cannot get around enforcement.

If an app can say:

I’d rather not go through this permission check today.

then there’s nothing left to discuss. So I went back and started checking my own work with one question:

Pull this security check out. Can the system still reach the tool normally?

If it can, the boundary isn’t finished. I’ve built a library.


Then I found the front door had no lock

The funnier part is that I hadn’t finished patching the first hole before I saw the second. All my checks were answering:

Which workload is this?

Is the credential it presented valid?

Does it hold a delegation from Alice?

Is this action inside the scope?

Looks complete. But then I went back and asked something extremely basic:

Hang on, who exactly is connecting right now?

And I couldn’t answer it. Say Alice tells the agent in Slack:

Restart all of device group A for me.

What I had drawn in my head was this:

Alice -> Agent -> Tool

That flow looks reasonable. Except it quietly hides two completely different things:

The first:

Is the person talking to me right now actually Alice?

The second:

Did Alice authorize this agent to do this?

Before I actually started building agent identity, I drew those two as one line. Because delegation can be done very rigorously. The document can say:

delegator = Alice
actor     = Agent A
action    = restart
resource  = group_A
expires   = 16:00

Check every field and they all come back correct. But if the name ā€œAliceā€ came from a session, from conversation state, or from anywhere a model can touch in the first place, then all the authorization downstream is very strictly verifying a name that might be fake. This is where a few things have to come apart:

1. Which workload identity is running this code right now?
2. Which user is making this request right now?
3. Did that user delegate the authority this call needs?
4. Can this runtime / agent workload use that delegation for this action?

These four answer different layers:

  • SPIFFE / SVID helps me answer the first.
  • OAuth / user authentication handles the second.
  • A delegation record handles the third.
  • Authorization policy handles the fourth.

Before I got into identity, I couldn’t quite tell these apart:

SPIFFE or OAuth Token Exchange?

Now I understand they’re answering questions at different boundaries.


The problem I actually hit wasn’t ā€œhow to do identityā€

It was around here that I slowly realized the problem I’d hit wasn’t:

How should Agent Identity be designed?

but:

Where do identity / delegation / authorization actually get enforced?

That’s where I started rethinking runtime. Although honestly, every company is currently using the term a little differently.

Some lean workflow.

Some lean sandbox.

Some count memory in.

Some mean framework + hosting.

Every company defines agent runtime around its own business needs. But from my own experience, there’s one thing I’m now very sure of:

A runtime has to own the execution boundary, at minimum.

Meaning when the agent finally produces a side effect, it has to pass through this layer. For example:

  • call API
  • restart machine
  • modify data
  • deploy
  • send message
  • change config

If the agent application can reach the tool while going around the runtime, then the authorization, sandbox, audit and policy hanging off that runtime are all just optional middleware. That distinction matters far more to me than whether the runtime is built on LangGraph.


And identity ends up somewhere else

My head used to look more like this:

Agent
+-- Planner
+-- Memory
+-- Tools
+-- Identity

Identity as one of the features.

Now I lean toward drawing it like this:

        Agent Application
                |
                |   <-- enforcement
                v
    -------------------------------
      RUNTIME BOUNDARY
    -------------------------------
      User -->  1. Authenticate user
                2. Identify workload
                3. Validate delegation
                4. Authorize action
                5. Execute tool
                6. Record audit trail
    -------------------------------
                |
                v
               Tool

This isn’t saying runtime equals identity. It’s that all of this identity work doesn’t mean much unless it’s enforced at the execution boundary. That shift only started getting clear while I was building a Slack bot agent + MCP gateway, because I really did start out treating Agent Identity as a security subsystem. Now I think of it as a set of runtime properties, or more precisely:

security properties the runtime has to enforce.


And then more questions start falling out

After getting this far, I started to understand why this area looks so messy right now. Because once an execution boundary exists, a lot of problems you’d normally look at separately suddenly tangle together. Take durable execution: a long-running task has to be able to pick up and finish after an interruption.

Say Alice authorizes the agent at 2pm:

Finish restarting device group A within two hours.

The agent gets halfway at 3pm and saves a checkpoint. At 4pm the machine dies. At 7pm the runtime resumes. Now what? The original delegation expired long ago. You can:

  • go get Alice to authorize again
  • refresh automatically
  • stop and wait for a human
  • issue a longer-lived delegation from the start

Those are all possible options. Which made me realize:

durability isn’t simply ā€œcan it resumeā€.

It runs straight into authorization lifecycle. And that made one thing very clear:

identity and execution lifecycle don’t come apart.


Then there’s audit, which I can’t leave out either. I used to think about it quite naturally:

the runtime writes an audit log entry every time it takes an action.

That’s too simple, too naive. If the runtime itself gets compromised, it’s also the author of its own history. There’s no reason to trust that audit log. Same for the sandbox. If the sandbox writes ā€œI’m one of the good ones! Trust me! I didn’t do anything badā€ from inside itself, that sounds a bit off too, right?


So what did I actually learn these past few weeks?

If you only look at the implementation code, what I did this round is Agent Identity.

  • credential
  • delegation
  • authorization
  • policy

But if you look at the questions I described, I don’t think the real learning was how to design those primitives. It was:

a security property with no enforcement boundary is just metadata.

A signed delegation doesn’t protect anything on its own. Neither does a beautiful authorization engine. They only start meaning something once the system can guarantee:

every execution with a real side effect has to pass through here

That’s also why I feel like I’m starting to understand Agent Runtime.


What’s next

I’m not going to come out and say:

Agent Runtime is X, Y and Z.

This round I only went in through the identity hole. Next I want to keep looking at how durable execution, sandbox, tool boundary and observability actually connect to the runtime, in Temporal, LangGraph, MCP, sandboxes, OpenTelemetry. You can pull plenty of recurring pieces out of those:

  • control loop
  • checkpoint / resume
  • tool boundary
  • sandbox
  • identity / authorization
  • observability
  • audit

But the only one of them I’ve dug the hole and fallen into myself is identity / authorization. The rest is still ā€œseen it, read it, thought about itā€, not ā€œI’ve run itā€. So I’d rather not rush to wrap them into a tidy taxonomy. I’ll go into them one at a time.

Happy coding!