Managing multiple GitHub accounts for AI agents

Work and personal GitHub identities, both reachable from the same shell, with the right one picked per task. The actual mechanics, not the wishful version.

May 4, 20267 min read

Managing multiple GitHub accounts for AI agents.

Most developers have at least two GitHub identities. The work one that touches the company's repos. The personal one that owns side projects. The usual fix is some combination of .gitconfig includes, SSH config blocks, and gh auth switch. It works, badly, for git operations on the command line.

It doesn't work for AI agents at all. An agent that hits the GitHub REST API reads GITHUB_TOKEN from its environment. Env vars are singular. You have multiple identities. Something has to give.

This post is about how to keep multiple GitHub identities and give the right one to each agent run, with the actual authsome commands rather than the version I wish existed.

The shape of the problem

Your shell rc probably has something like this, written six months ago and never updated:

bash
export GITHUB_TOKEN="ghp_personal_account_token_from_january"

A work PAT lives somewhere else. Maybe 1Password, maybe a sticky note. When you launch any AI agent (Claude Code, a custom Python script, a LangChain workflow), it reads GITHUB_TOKEN and gets the personal one. Sometimes that's fine. Sometimes it's wrong in subtle ways:

  • The agent creates an issue in a work repo. The author shows up as your personal account.
  • The agent tries to read a private work repo. 404. Looks like the repo doesn't exist.
  • The agent merges a PR using your personal credentials. The merge commit gets attributed to a personal account on company infrastructure.

The right answer is "the agent should use the right identity for what it's doing". The wrong answer is "I'll switch tokens manually before each task". The most common answer is "I'll set up some .zshrc magic". .zshrc magic stops scaling at exactly two identities.

What gh auth switch actually does

GitHub's CLI has a multi-account flow: gh auth login adds an account, gh auth switch swaps the active one. But this only updates the CLI's stored token. It does not update GITHUB_TOKEN in your shell, because env vars don't reach back up into a parent process.

bash
$ gh auth switch
✓ Switched active account for github.com to work-account
$ echo $GITHUB_TOKEN
ghp_personal_account_token_from_january   # ← still the old one
$ claude
# Claude reads GITHUB_TOKEN and uses the personal account.

The gh CLI behaves correctly because it reads its own stored creds. Anything else that reads GITHUB_TOKEN from the environment gets the stale value. Every agent framework I know reads GITHUB_TOKEN from the environment.

What authsome calls them: connections

In authsome, multiple accounts on the same provider are modeled as named connections. The default connection is called default. You can have as many additional connections as you want, each scoped by name.

Set up two identities:

bash
authsome login github                          # creates the 'default' connection
authsome login github --connection work
authsome login github --connection personal

Each invocation:

  1. Opens the browser to the GitHub OAuth flow.
  2. You sign in with the right account (use a separate browser profile or private window if you need to).
  3. The flow returns a refresh-able access token tied to that account.
  4. Authsome stores the token under the named connection.

The connections share the OAuth client_id / client_secret you configured for the provider, but each gets an independent access token bound to its own scopes.

Verify:

bash
authsome list
authsome inspect github       # shows per-connection status, expiry, scopes

Read from a specific connection

Every read command takes --connection:

bash
authsome get github --connection work
authsome get github --connection personal --field status
authsome export github --connection work --format env

Without --connection, the command uses default.

The proxy picks the default connection

This is the part the docs are clearest about, and where I want to set expectations honestly.

authsome run injects credentials from each provider's default connection. Per-request connection selection inside authsome run is on the roadmap; it doesn't exist yet. If you want the agent to use a non-default connection, the documented pattern is to flip the default first:

bash
authsome login github --connection work --force
authsome run -- claude

The --force flag overwrites the existing default slot, so work becomes default and the proxy uses it for every request in this run.

There's also a newer set-default command in the CLI:

bash
authsome set-default github work
authsome run -- claude

Both achieve the same result. --force is what the multi-connection guide documents as the supported path today; set-default is what the binary's help text exposes. Pick whichever you find more readable.

Warning

The proxy uses one default per provider at a time. If you switch the default to work mid-session, every subsequent authsome run uses the work identity until you switch it back. There is no per-directory or per-request override yet. Build your scripts around explicit switching.

When the agent embeds the library directly

If your agent is Python and uses authsome as a library rather than going through authsome run, you can pass connection= explicitly per call. This is the only way to get per-call connection selection today:

python
from authsome.server.dependencies import create_auth_service

auth = create_auth_service()
work_token = auth.get_access_token("github", connection="work")
personal_token = auth.get_access_token("github", connection="personal")

A library-based agent can switch on the fly. A proxy-launched agent picks one identity per run.

Total isolation: profiles, not connections

A connection lets two GitHub identities sit side by side, both readable from the same context. Sometimes that's what you want.

Sometimes you want the opposite: credentials that never see each other. Personal Slack creds in one profile, work tooling in another, and switching the active profile switches the entire credential set. For that, use profiles.

bash
authsome init                            # initial profile created on install
authsome profile create --handle work    # second profile
authsome profile use work                # switch to it
authsome login slack                     # this credential lives only in the 'work' profile
authsome profile use default             # switch back

The rule of thumb the docs use:

If credentials should never see each other, use profiles. If both should be reachable in the same context, use connections within one profile.

Two GitHub identities in the same shell? Connections. Personal vs work credential sets that should be air-gapped? Profiles. The Profiles vs connections doc has the full model.

Day-to-day workflow

What it actually looks like for me, working across personal and work GitHub from the same laptop:

bash
# Morning. Working on a personal side project.
authsome run -- claude
# Default connection is 'personal'. Agent reads personal repos, writes
# personal commits. Nothing else touches work credentials.

# Afternoon. Switching to a work task.
authsome login github --connection work --force
authsome run -- claude
# Default is now 'work'. Agent talks to work repos with the work account.

# End of day. Switching back is the same command in the other direction.
authsome login github --connection personal --force

Not glamorous, but reliable. No shell magic, no stale env vars, no merge commits attributed to the wrong account.

What this doesn't fix

A few things to be honest about:

  • Git CLI authentication. Authsome handles the API side. Pushing code with git push origin main uses your SSH key or git's stored credential helper, not authsome. The two coexist; they don't interfere.
  • GitHub App identities. If you want an agent to act as a GitHub App rather than a user account, that's a separate auth model. Authsome v1 doesn't have GitHub App support built in; you'd register your own provider definition (authsome register) or use a PAT minted by the App.
  • Per-task connection switching inside a single agent process. The library path supports it. The proxy path doesn't yet.
  • Cross-organization SSO. If your org requires SAML SSO, each OAuth token has to be authorized in the GitHub UI before it can hit org repos. This is a GitHub policy thing, not an authsome thing, and applies to every connection individually.

The shorter version

Multiple GitHub identities is a known-hard problem because the standard GITHUB_TOKEN env var models exactly one. Authsome's named connections model N. The proxy uses one at a time and you switch which one by either set-default or re-login with --force. For total isolation between credential sets, switch to profiles instead of connections.

The setup is about ten minutes. After that, every agent invocation uses the right account and you stop having merge commits authored by the wrong person.

Priyansh Khodiyar

Priyansh Khodiyar

Maintainer

Works on authsome and the agentr.dev tooling.