Share Work, Not Access:
Control & Identity for Multiplayer Agents

Nick Taylor · @nickytonline

Whose permissions does the agent use?

Nick Taylor @nickytonline

One session used to mean one user

  • Chat, Copilot, Claude.ai: the session is you
  • A Slack channel is not a user
  • Incident rooms are not a user either
Nick Taylor @nickytonline

The session is now a room

  • Several people tagging the same agent
  • Developers, operators, security, in one thread
  • The identity model still binds one identity per session
Nick Taylor @nickytonline

Channel identity is a common current pattern

Claude Tag is today's clearest example.

Nick Taylor @nickytonline

What the agent can do is a property of the room

  • Access is attached to the channel, not the person who asked
  • Claude Tag: configure once, everyone in the channel inherits that capability
  • The usual advice: scope the channel to the least-privileged member
Nick Taylor @nickytonline

Least privilege of the room is not least privilege

  • Trust the channel as far as its most restricted member
  • Then the on-call cannot pull the logs the incident needs
  • Skip the scoping, and everyone inherits production
Nick Taylor @nickytonline

A channel member without repo access can still ask the agent to read the repo

Nick Taylor @nickytonline

Mixed rooms are the interesting case

  • Developer, on-call, security, in the same incident thread
  • The agent is the shared pair of hands
  • Channel identity has to pick one permission set for all of them
Nick Taylor @nickytonline

Channel identity has to pick one permission set

  • Union: collaboration is privilege escalation
  • Intersection: the on-call cannot do the job
  • Shared service account: a fourth identity everyone can wield
Nick Taylor @nickytonline

Combining permissions is privilege escalation

Nick cannot read production logs. Denis can. They share a thread.

Nick Taylor @nickytonline

The failure is subtle, which is worse

Attribution. Stickiness. Disclosure.

Nick Taylor @nickytonline

Audit says the agent did it

  • GitHub, the warehouse, the log system: the service account
  • "Who asked?" is Slack archaeology
  • A forty-message thread is not an audit trail
Nick Taylor @nickytonline

Joining the channel is the grant

  • Access lasts as long as you are in the room
  • People forget to leave incident channels
  • Invite a contractor to help: they now wield whatever access the channel has
Nick Taylor @nickytonline

The answer may already be in the thread

  • Bind the fetch to Denis, who has log access, and Nick still cannot call the tool
  • Once Denis's result is posted, Nick can read the message
  • Sharing work still writes into a shared context
Nick Taylor @nickytonline

The field is already moving toward per-user identity

ChatGPT, Copilot Studio, even Claude Tag.

Nick Taylor @nickytonline

The person is only half of run identity

Necessary. Not sufficient.

Nick Taylor @nickytonline

What's an identity-aware proxy?

  • Checks identity and policy on every request, not just once at login
  • No VPN, no trust based on which network you're on
  • Already used to secure internal apps, SSH, and as an MCP gateway in front of tool calls
  • This talk: the same enforcement point, extended to agent runs
Nick Taylor @nickytonline

Run identity

The primitive for identity-aware agents: you govern policy. Pomerium enforces it.

An agentic run connects:

  1. The person who approved it
  2. One attested executor pod

The run can carry the prompt and execution context. Routes still decide which resources and credentials that run may use.

Nick Taylor @nickytonline

Drop either binding and the model gets weaker

  • Person without executor: any pod can wield that person's approval
  • Executor without person: a service account again
  • Prompt without policy: the model is being asked to authorize itself
Nick Taylor @nickytonline

Channel identity vs run identity

Dimension Channel identity Run identity
Bound to The room The action
Who can wield it Anyone in the channel The approved run, on its executor
Audit The agent's account Person + executor + run
When it ends Leave the channel Token/run expiry (revocation designed, not yet wired)

That audit row isn't new work: an identity-aware proxy logs it already.

Nick Taylor @nickytonline

Even Claude Tag keeps the credential out of the sandbox

The gap: nothing ties it to an attested executor pod.

Nick Taylor @nickytonline

One sandbox per run

Nick Taylor @nickytonline

Good enough to ship wins by default

Not because people ignore security. Ad hoc already works, so it ships.

Nick Taylor @nickytonline

The pitch is not "be more secure"

  • The fork: ad hoc service account, or identity-aware from the start
  • Identity-aware doesn't cost you more code. It costs you less
  • Air-gapping the agent, and binding it to a real user: the two hard parts, already built
Nick Taylor @nickytonline

Configured for the room. Not for the run.

Claude Tag's channel bundle, Agentforce's permission set: same shape.

Nick Taylor @nickytonline

Outside the agent is the easy part now

Outside the room, bound to the run, is what's still missing.

Nick Taylor @nickytonline

Instantiating a harness client

const client = new HarnessClient({
  transport: createNodeHarnessTransport({
    baseUrl,
    // Projected ServiceAccount token, re-read per request since it
    // rotates under a running process.
    tokenFile: process.env.HARNESS_TOKEN_FILE,
  }),
})
Nick Taylor @nickytonline

Demo

Kubernetes today. Work in progress.

Nick Taylor @nickytonline

Deterministic, and scoped to the run

Dimension In the prompt Scoped to the room Scoped to the run
Who checks it The model The proxy, at config time The proxy, at request time
Grant covers Nothing enforced The channel or agent user The approved person + executor
Survives a prompt injection No Yes Yes
Tells Nick from Denis No No Yes
Nick Taylor @nickytonline

Share work, not access

The primitives for identity-aware, multiplayer agents.

Nick Taylor @nickytonline

Thank You!

Nick Taylor · @nickytonline

Introduce yourself. Slot 11:30-11:55, hard cap 25 min including demo. Questions after. Minute map: title ~1 · argument ~14 · demo ~5 · wrap ~4 · closer ~1. Thesis: people collaborating through agents, not the agent becoming a shared key.

The whole talk hangs on this. Single-player AI: obvious. Your tokens, your repos, your Drive. Second person joins the thread. Three people tagging the same agent. Whose access?

This is the assumption most agent auth still makes: one human, one identity (Claude.ai chat, Copilot session, coding agent on your laptop). Multiplayer agents break that. The "session" is a room: developers, operators, security, all steering the same agent.

Multiplayer is the product; the auth model didn't get the memo. Adding the on-call to the thread doesn't open a new identity boundary, it just adds them. Channel identity: the old session object with a Slack channel stuffed into it.

Not a dunk, Claude Tag makes the room/action tradeoff explicit: in a channel, Claude acts as itself using connections configured for that channel, not as the person who tagged it. Anyone in the channel gets the same capability.

Anthropic documents this: configure the channel once, connections don't change based on who asked. The mitigation, treat the channel as trusted as its most-restricted member, only works if you actually do it, and it punishes whoever has the access the incident needs. Source: Anthropic, "How agent identity works," Claude Tag docs, accessed Sept 2026. Scope the claim to the documented channel-connection model, not every Claude deployment.

Honest mitigation, but mixed rooms break it: starve Denis (who has access) or over-grant Nick (who doesn't). Fine for peer channels with equal clearance; incident threads aren't peer channels.

Paraphrase of Anthropic's own writeup, not a gotcha: the agent is allowed to do what the channel is allowed to do, the asking human isn't in the authorization path. Fine for peer channels with equal clearance; not fine once the room is mixed.

This is how people actually work: no new identity boundary opens every time a specialist joins the investigation.

There is no fourth option inside channel identity. That is why mixed rooms matter. Union: Nick asked, the agent could, so Nick got Denis's reach. Intersection: you scoped to Nick, Denis cannot pull prod logs. Service account: you invented a principal none of them hold, and anyone in the thread can wield it.

Classic confused deputy, but helpful and sitting in Slack. Nick didn't get Denis's laptop, he got Denis's access, mediated by the agent.

Not a Hollywood breach, three quiet failures: attribution, stickiness, disclosure. Plus a fourth: the tools don't end when the question does.

Security asks who pulled customer logs: the warehouse says the agent's account, Slack says a thread. "Just paste what it said" isn't provenance, the identity bound to the tool call is the agent's.

Stickiness: membership is the ACL. Leaving might be the revoke, but incident channels linger. Contractor example is illustrative, not a real incident.

Run identity governs the action, not Slack history. What gets pasted back is still shared context, that's sharing work, that's the point.

Not just Pomerium: ChatGPT's Slack app defaults to per-user permission inheritance, Copilot Studio offers per-user OAuth in Teams, Claude Tag added personal connectors on top of its channel default. Run identity goes one step further: bind the executor too, not just the person. Source: OpenAI, "Using Slack in ChatGPT"; Microsoft, Copilot Studio docs, "Configure user authentication"; Anthropic, "Personal connectors in channels," Claude Tag docs (accessed Sept 2026).

Binding to the approving person is what the industry already does. Alone it stops nothing: the right person's credential replayed from the wrong workload still gets through if nothing checks which pod uses it. Run identity adds the second binding: one attested executor pod.

Quick grounding beat, not a new argument. Some of the room won't have this term loaded; the rest of the talk leans on it hard (audit row later, "Pomerium enforces it" next slide). IAP in one line: identity and policy checked per request, at the proxy, before traffic reaches the app. Contrast with network-perimeter trust (VPN, "you're on the LAN so you're trusted") and session-based trust (log in once, trusted for the rest of the session). Always verify, never assume trust just because a request arrived. Existing use cases the room may already know Pomerium for: fronting internal web apps/APIs, SSH access, and as an MCP gateway mediating tool calls. This talk pushes the same enforcement point one layer further, onto the agent run itself, that's the bridge into "Run identity" next. Keep this short, it's context for what's coming, not a beat that needs its own reaction.

The concept the talk introduces: not a session, not a channel, the action. Person: the IdP subject that approved the run. Executor: the one attested pod that can act (Nick's Slack run isn't interchangeable with a headless cron agent). Prompt/context: only a useful authorization input when the harness/route carries and enforces it, a prompt is not a grant. This states the target design, both bindings. The demo proves the person half live; executor-pod attestation is the next milestone, not yet wired into AgentOps (see architecture slide, demo caveat). Don't imply the executor binding is already enforced in the running demo. Source: "Agentic Run Identity" reference design PDF, pp. 1-2 (confirm page numbers before citing live). Hard binding is person + executor; route policy supplies the resource/credential boundary.

First two are the run's core binding. Context/task can narrow policy only when the system carries them as trusted inputs, conversation text alone can't do that.

Channel identity answers "what connections are available in this room?" Run identity answers "which person approved this execution, and which executor is acting?" Audit row: not something to build. Pomerium as an IAP already logs human access out of the box; binding the run to person + executor gets agent traffic the same audit trail for free, ties to the "two hard parts, already built" framing later. Source: "Agentic Run Identity," pp. 1-2 and 15-20 (confirm page numbers before citing live). A run isn't itself a route grant, a route must accept run tokens and its policy decides access. Demo is the mixed-room version: Nick then Denis, same investigation, different runs. It proves the person half live (tells Nick from Denis); don't claim the executor half was demonstrated, it wasn't wired into the running demo.

Anthropic's architecture: the channel session sandbox holds no credential, Agent Proxy injects it at the network boundary, same shape as the sidecar coming up. The gap isn't isolation, it's that neither binding (channel default or personal connector) ties the credential to an attested executor pod. Source: Anthropic, "How agent identity works," Claude Tag docs, accessed Sept 2026.

Two independent Slack threads/runs, two sandboxes, not one per channel. AgentOps binds a thread to one multi-turn session, only its starter drives it. The agent container doesn't hold the upstream credential. The sidecar presents the person's own delegated OAuth token (per-user connect); Pomerium holds and brokers the credentials, not AgentOps. The pod authenticates to Pomerium via its own Kubernetes service account, and the access layer enforces what that person can do. Run identity's next step: bind the executor pod itself, not just the person. Warm pools may cut startup cost, but the security boundary is the executor pod attached to this run.

Denis's framing: "do people really care that much?" Depends where they are. Most builders go ad hoc (service account first, security later) not because they weighed and rejected identity, but because they were tasked with shipping, not designing an authorization model, and ad hoc already clears the bar of "it works." Same adoption question as Pomerium generally: being the technically better security solution isn't, by itself, why people show up. Meet people at "this already ships," don't scold them for not prioritizing security.

What you've got probably works fine for now. Getting to something more secure isn't the uphill climb you'd expect, most of the hard part's already done. Denis, verbatim in spirit: "it does not limit you, and it will also let you skip writing quite a bit of code and debugging it." That's the pitch, not "you should care more about security." Two hard parts he named: air-gap the agent (sandbox + sidecar, architecture slide) and bind it to a real user (delegated OAuth token, same slide). Both take real time to build yourself; we already did both. If you already run Kubernetes and want the agent substrate, adding Pomerium on top is not a big lift, per Denis.

Industry didn't stop at "put the rule in the system prompt": Claude Tag scopes tools/credentials per channel via an admin bundle, Agentforce scopes actions via permission sets and a dedicated agent user record. Both moved the check into a config layer the model can't rewrite, real progress. Both still resolve the check once, for the room or service user, not per run. Same gap as Claude Tag's credential-outside-the-sandbox point, one level up: the grant is scoped to who's in the room, not the run inside it. Source: Anthropic, "Introducing Claude Tag," blog post, https://www.anthropic.com/news/introducing-claude-tag; Salesforce Help, "Configure Service Agent Access," Agentforce permission-set docs (accessed Sept 2026).

Wrap starts here. Demo next (5 min), then ~1 min through the table and closer. Getting the check out of the model and into config is mostly done industry-wide. Still missing: binding that check to the specific approved run, not the channel, workspace, or service user it's acting through. The check belongs on the way out: the tool call carries the approving person and attested executor as metadata, a policy decision outside the model evaluates the request. Any identity-aware proxy can enforce this, the pattern isn't proprietary.

Real snippet from sdk/ts/examples/hello-session.ts in AgentOps as of the 2026-09-22 call. Confirm the path and parameter names still match before presenting, Denis restructured the ~50k LOC repo toward fresh experimental Pomerium images right after that call, so it may have moved. Deliberately just the client construction, not the session/event-loop code that follows (that's `client.createSession({ template, conversationRef, approvalPrompt })`, then subscribing to the event feed). Point of showing this: not much code, and token handling (a rotated, projected ServiceAccount token, not a static secret) is already done for you.

Live Slack. Same investigation, separate threads. Two runs, two sandboxes. github.com/pomerium/agentops, work in progress. Five minutes. Kubernetes-only for now. One sandbox per run in the thread. The agent container doesn't receive the upstream credential, the sidecar and access layer handle the protected connection. Reality check: the demo proves the person half of run identity (per-user delegated OAuth, enforced live), not executor-pod attestation, that binding is designed but not yet wired into AgentOps (see architecture slide). If asked in Q&A: today's binding is person-to-token, attested-executor is the next milestone. The Nick/Denis distinction still holds since it only needs the person binding. Nick: no production log access. Denis: on-call, has it. Nick tags the bot in a thread, asks for production logs. Starts Nick's run/sandbox with Nick's approval. Denied. Denis (D-E-N-I-S), same channel/investigation, separate thread and run, doesn't inherit Nick's run. Allowed, logs come back under Denis's authority. Nick can see whatever's posted to the shared channel but didn't inherit the grant. Nick retries: still denied. Shared the investigation, not the credential. Caveat: if Denis's result is sitting in the thread, Nick can read the message, that's a data-in-the-room problem, not a tool-call problem. Source: Pomerium AgentOps repo, github.com/pomerium/agentops. Confirm current Kubernetes/Slack demo behavior before presenting, it's WIP. If the demo flakes, this slide is the story, don't narrate a success that didn't happen. Same scene under channel identity: if the channel has a production-logs connection, Nick's ask succeeds; if scoped to Nick, Denis can't do the job. Either way, sharing access or blocking work.

This slide plus the last two punches are the wrap. If demo ran long, skip straight to the closer. Middle column: where most shipping products sit (Claude Tag's channel bundle, Copilot Studio's app registration, Agentforce's permission set). Real enforcement, wrong grain, answers "is this room allowed to," not "did this person, on this executor, get approved." Right column: the target model, person + executor, checked per request. The demo (Nick denied, Denis allowed) showed the person half live; don't credit it with proving the executor half, that's Pomerium core's concept (union of user + workload identity), not yet wired into this demo. "Scoped to the run" means the tool call carries the approving person and attested executor as metadata the infrastructure reads, not model-written text. A policy engine outside the model decides. Today the demo's tool calls carry the person; executor metadata is next to wire in. Pomerium is how I work on this; the pattern isn't proprietary. If the check stops at the room, two different people in it look identical to the policy.

Multiplayer is the point, keep the shared thread and investigation. Bind each action to a run identity (approving person + attested executor), let route policy decide the resource/credential boundary. Ties back to the "Run identity" slide's thesis (identity-aware agents, primitives, not something you build yourself) and the title. The future of multiplayer agents is people collaborating, not a group-sized service account. End of talk, target ~11:50 for a few minutes of Q&A. QR slide is for lingering.

Four codes: Pomerium, agentops (the Kubernetes Slack bot from the demo, WIP), the deck, nickyt.co. Find-me slide, not a second ending, the last line of the talk happens on the previous punch.