Skip to main contentArrow Right
How We Built Accountable Identity For Shuni, Our AI Coding Agent

Table of Contents

Summarize with AI

Don't have the time to read the entire post? Our human writers will be sad, but we understand. Summarize the post with your preferred LLM here instead.

One of Descope’s members of technical staff, Tal Aharoni, has a dog named Shuni.

Shuni is a very good girl and the namesake of an internal coding agent we built at Descope. Much like Dog Shuni, Coding Agent Shuni is eager, loves fetching, and needs a leash to stay on track. 

This is the story of how we solved the identity problem AI agents like Shuni pose. We’ll walk through the four identity decisions every Shuni run makes, the credential model behind them, and the design principle that ties the whole system together: every action the agent takes is attributable to, and scoped by, the human who triggered it. Engineers remain accountable for the code Shuni produces because the delegation runs through their own OAuth grant, not a shared bot credential. 

If you're building any agent that takes actions in third-party systems on behalf of users, the architecture we landed on should translate directly.

How Shuni works

Shuni lives inside our GitHub org. Engineers invoke it by mentioning @shuni on an issue or assigning the issue to it. A few minutes later, it opens a pull request: branch created, code written, tests run, PR description filled in. The commit author is the engineer who invoked it, not a generic bot account.

Shuni review comments and accountability screenshot
Fig: This screenshot shows that, while Shuni posted the review comments, the commit for the changes was done on a human's behalf

Three pieces run under the hood:

  • A GitHub App that listens for @shuni mentions and assignments and dispatches work

  • A GitHub Action that runs the Claude Agent SDK with a set of internal Model Context Protocol (MCP) tools

  • A memory service that lets Shuni recall context across repos and runs

We’re going to focus on the identity layer that sits between those pieces. Every cross-boundary call inside Shuni's pipeline raises the same three questions: who is this for, what scopes does it need, and how do we record it.

Identity options we ruled out

Before building the identity layer, we ruled out the obvious options.

  • A single shared bot account. Easy to deploy. Every action looks like it came from the bot regardless of who triggered it. Attribution is lost, scopes have to be broad enough to cover any caller, and revocation is all-or-nothing.

  • The triggering user's PAT, stored on the agent side. Attribution works, but a long-lived, broadly scoped credential is now sitting inside an autonomous process. If the token leaks, the blast radius is the user's full GitHub permissions.

  • Per-user service principals managed in CI. Better than the previous two. Operationally, you've signed up to provision, rotate, and offboard credentials forever, and the cost grows linearly with headcount.

What we wanted was this: the developer authenticates once, we store their GitHub grant securely, and every time they invoke Shuni, the agent acts as them against GitHub with their org membership, repo access, and branch protections.

End-to-end identity flow for our coding agent

Here's what actually happens when a developer mentions @shuni on an issue:

Fig: Diagram illustrating the identity flow when invoking the Descope coding agent Shuni
Fig: Diagram illustrating the identity flow when invoking the Descope coding agent Shuni

The user-facing path is short. First time: click a link, sign in, come back, mention Shuni again. Every time after: just mention Shuni. Everything else happens between Descope, the GitHub App, and the GitHub Action.

The four identity decisions per request

Every Shuni run involves four distinct identity decisions, each handled by a corresponding capability in Descope's Agentic Identity Hub.

1. Authenticate the human who triggered the agent

The first thing the GitHub App does when it sees a @shuni mention is verify that the GitHub user who wrote it is a known, authorized Descope user. If they aren't, Shuni replies on the issue with a Descope sign-up link. No work begins until the user is authenticated.

Agents need a front door, not just a back door. If the only system that knows who can invoke the agent is the agent itself, the audit trail starts inside the agent rather than at the boundary.

2. Issue a scoped credential at request time

Once the user is authenticated, Shuni needs to act as that user: create a branch, push commits, comment on the issue. We don't store GitHub credentials inside Shuni. When an engineer signs up for Shuni access, they connect their GitHub account through Descope, which stores the OAuth grant. At request time, Shuni asks Descope for a scoped GitHub token tied to that user.

The response format:

type DescopeTokenResult =
  | {
      authorized: true;
      token: string;        // short-lived GitHub OAuth token
      scopes: string[];     // exact GitHub scopes granted
      expiresAt: string;    // hard expiration
    }
  | {
      authorized: false;
      authUrl: string;      // sign-up / consent URL
    };

The token is short-lived and isn't cached across runs. Scopes are explicit and inspectable: Shuni logs what it was granted on every run and rejects work that would exceed the grant. There is no shared-bot fallback. If Descope returns authorized: false, the agent stops and replies on the issue with the authUrl (the authorization URL). The user clicks through, signs in via single sign-on (SSO), authorizes GitHub, and comes back. Shuni picks up where it left off.

This is the role Descope Connections plays for outbound agent flows. Connections is a credential vault for third-party services with two properties that matter for agent design. It handles both OAuth tokens and API keys: GitHub happens to use OAuth, but if Shuni needed to call a service that authenticates with a static key (Linear, Stripe, an internal admin API), the same primitive stores that credential and keeps it out of the agent's runtime. 

And credentials can be scoped at the user level or the tenant level. User-scoped credentials are what Shuni uses: each engineer's GitHub grant is tied to that engineer. Tenant-scoped credentials cover agents that act on behalf of a customer org rather than a specific person.

3. Preserve the delegation chain and keep engineers accountable

Because Shuni operates under the requesting user's GitHub OAuth token, every PR is attributed to that user. Reviewers see Alice's commit. Branch protection rules apply as if Alice pushed. The engineer who invoked Shuni remains accountable for the output, not because of a policy we wrote, but because the OAuth delegation model makes it a structural property of the system. The same mechanism that grants the agent access is the mechanism that records who authorized it.

When a PR turns out to be wrong, three independent systems agree on what happened:

  • GitHub records the human as the actor on the commit and PR

  • Descope records the OAuth token grant tied to that human and that agent run

  • Shuni's own logs record the inputs, the tools invoked, and the model output. 

No system has to guess. This is where most traditional IAM models break down. They treat human and non-human principals as separate categories. Agentic identity has to track both at once: the human who delegated and the agent that acted on their behalf.

4. Revocation in a single action

When someone leaves the company, we revoke their GitHub connection in Descope. That single action removes Shuni's ability to act on their behalf immediately, across all repos, with no token rotation and no orphaned service accounts to track down.

An engineer left last quarter. By the time their laptop hit the offboarding queue, Shuni had already lost the ability to push to anything they could touch. No security ticket, no scavenger hunt through CI configs. The agent's surface area shrank in lockstep with the human's. It looks unremarkable on a slide. In operations, it's the single largest reduction in offboarding overhead we've seen from this project.

Why our solution for Shuni applies broadly

Take Shuni out of the picture and the problem is the same everywhere agents are being deployed: an autonomous process needs to take actions in an external system, and you want those actions tied to a real user with the right scope.

Consider these examples of agents facing similar identity challenges:

  • A support agent that reads tickets in Zendesk, posts updates to Slack, and writes notes back to Salesforce. Three services, three auth schemes, all needing to act as the support rep who triggered the workflow. 

  • A finance copilot that pulls invoices from Stripe and pushes journal entries into a customer's accounting system, where each customer's credentials need to stay isolated and the copilot can't hold raw API keys in memory. 

  • A data ops agent that runs queries against Snowflake on behalf of analysts, where row-level security needs to apply per-user rather than giving the agent blanket access.

In every case, the model is the same: the agent fetches a scoped credential at request time from a vault that handles refresh, expiration, and revocation. The user's permissions in the downstream service govern what the agent can do. The only variables are the credential type (OAuth or API key) and whether it's scoped to a user or a tenant.

Recommendations for AI agent identity

Shuni’s properties generalize to any AI agent that takes actions on behalf of users. Running Shuni on the Agentic Identity Hub gives us:

  • No long-lived shared credentials. There is no broadly scoped PAT to leak, rotate, or vault.

  • Inherited least privilege. Shuni can only act within the scopes the requesting user holds. If the user cannot merge to main, neither can the agent.

  • Provable attribution. Every change Shuni makes is signed by a specific human at a specific time, traceable across three systems.

  • Policy enforcement at the identity layer. Repo allowlists, sensitive-path gating, and rate limits can be applied through Descope Policies instead of rewritten into the agent.

  • Offboarding parity. Removing a user removes the agent's reach for that user in the same step.

With these principles in mind, the design rules we'd recommend are:

  1. Don't give agents their own privileged service accounts. Borrow user-scoped credentials through an identity provider that supports agentic flows. Service-account sprawl is the next iteration of the secret-sprawl problem.

  2. Separate authentication from authorization. "Who triggered this agent" is a different gate than "what is this agent allowed to do." Treat them as independent checks.

  3. Make tokens short-lived by default. Token refresh and rotation belong in the identity provider, not in the agent and not in a developer's runbook.

  4. Log identity, not just actions. Tie every agent operation back to a token grant and the human principal who delegated it. Action logs without identity logs are incomplete forensics.

  5. Plan for revocation on day one. If you can't kill an agent's access to a user's resources in one operation, the agent is a liability that will outlive the user.

What’s next for our coding agent

The next step is exposing Shuni's internal MCP tools through Descope-secured MCP servers, so other agents inside the org can call them with the same OAuth 2.1 and Dynamic Client Registration flow we already use for inbound clients. After that, we plan to layer per-repo policies through the Hub's policy engine: read everywhere, write only to repos explicitly tagged as shuni-allowed.

Securing AI agents with Descope

Building secure agent infrastructure is largely an identity problem in disguise. The Descope Agentic Identity Hub provides the building blocks Shuni relies on: Connections for outbound third-party credentials (OAuth tokens and API keys, user-scoped or tenant-scoped), MCP Auth SDKs for agent-facing OAuth 2.1 flows, scope-based policies, and full audit logging across credential grants and agent invocations.

Start securing your AI agents that act on behalf of users by signing up for a Free Forever Descope account, joining the AuthTown dev community, or reaching out to our auth experts to talk through your architecture.

Of course, we couldn’t close out a blog about Coding Agent Shuni until we shared a cute pic of Dog Shuni. For her part, the original Shuni remains eager, still loves to fetch, and has never once held GitHub credentials.

Fig: The four-legged version of Shuni who, while not being much of a coder, is a very good girl
Fig: The four-legged version of Shuni who, while not being much of a coder, is a very good girl