engineering· May 22, 2026· 4 min read

The CLI re-auth tax: scope your credentials per project, not per machine

Switching repos shouldn't mean re-authing five CLIs. The fix is scoping gh, gcloud, kubectl, aws, and ssh-agent state to each project — turning context-switch from an 8-minute parade into a cd.

Nobody puts CLI re-authentication on their timesheet. That's why nobody fixes it.

You finish a thing in your work repo, switch to the open-source side project you've been pecking at, and your terminal greets you with a slow, predictable parade: gh: authentication required, gcloud: re-run gcloud auth login, kubectl: Unable to connect, aws: The config profile (...) could not be found, doctl: Unable to authenticate you. Eight minutes later you've reauthed five CLIs and forgotten what you opened the terminal to do.

It does not feel like a meaningful cost in the moment. It adds up to days across a quarter. And the worst part is that the friction never shows up in any retro — because it's never the thing that blocked a deploy, just the thing that wore you down before the deploy.

What changed

The fix isn't another credential manager. It's giving each project its own scoped credential context, so switching repos doesn't trigger a CLI-by-CLI re-auth scramble. The CLIs are fine. The model of "one global credential per CLI per developer" is what's broken.

A workspace-per-project model — where each repo's context owns its own gh, gcloud, kubectl, aws, and ssh-agent state — turns "switch project" from a multi-minute setup into a directory change.

How it works in practice

Stop treating CLIs as machine-global

The default install of every major dev CLI assumes you are one person, working on one company's stuff, with one set of credentials. gh auth login writes to ~/.config/gh/hosts.yml and overwrites whatever was there. gcloud auth login swaps the active account. kubectl reads one ~/.kube/config.

That mental model worked when most engineers had one job and one cloud provider. It breaks the instant you have a day job, a side project, an open-source contribution, and a client engagement — all touching different orgs.

Use scoped config dirs, not global state

Every major CLI supports redirecting its config dir via an env var. Pin them per project:

  • GH_CONFIG_DIR=$PWD/.cli/gh
  • GOOGLE_APPLICATION_CREDENTIALS=$PWD/.cli/gcp/key.json
  • KUBECONFIG=$PWD/.cli/kube/config
  • AWS_CONFIG_FILE=$PWD/.cli/aws/config, AWS_SHARED_CREDENTIALS_FILE=$PWD/.cli/aws/credentials
  • DOCTL_CONFIG=$PWD/.cli/doctl/config.yaml

A direnv .envrc per repo (or your shell's equivalent) sets these on cd. Now gh pr list in your work repo and gh pr list in your side-project repo are talking to different accounts with no manual switch.

The credentials still live in real files — but they're scoped to the project that owns them. Wandering into the wrong repo can't accidentally hit the wrong cluster.

Run one ssh-agent per project, not one per machine

ssh-agent is the worst offender because the friction is silent — wrong key, host refuses, you retry, eventually figure it out. The fix is the same shape:

# .envrc per repo
export SSH_AUTH_SOCK=$PWD/.cli/ssh-agent.sock
ssh-agent -a "$SSH_AUTH_SOCK" >/dev/null 2>&1 || true
ssh-add ~/.ssh/<project>_id_ed25519 2>/dev/null

Now ssh production-bastion in client A's repo offers client A's key first. In client B's repo, client B's. The keys never cross-pollute.

Quarantine the credential, not just the CLI

Re-auth friction is half the cost. The other half is the risk — accidentally pushing to the wrong remote, running terraform apply against the wrong account, deleting a bucket that doesn't belong to this client. Scoped credentials prevent those, too. A CLI that doesn't have the credential can't perform the destructive action by accident.

This is the part you can't quantify on a timesheet, but is the actual reason senior engineers end up doing it.

Before vs after

Without scoped workspace With scoped workspace
cd into a different repo 5–10 min of re-auth Zero — direnv exports the right env
Likelihood of wrong-account command Real (you've done it) Near zero — wrong creds aren't loaded
Setting up the next machine Bookmark a doc, hope Clone repos + direnv allow, done
ssh-agent confusion "Why is it offering my work key?" Scoped agent per project
Cost of taking a week off the client Re-auth tour on return Same cd, same context

Who benefits most

Engineers running multiple cloud accounts. If you have AWS profiles for two clients and a personal account, the scoped-config-dir pattern alone saves the mental load of constantly checking which AWS_PROFILE is exported.

Open-source contributors with a day job. The gh clash between your personal account and your work account is the canonical example. Scoping the gh config dir per repo eliminates it for good — and gives you back the ability to push to a personal fork without first logging out of your work account.

Anyone using AI coding agents that issue CLI commands. When an agent has the ability to run gh, kubectl, or terraform, scoping the credential context to the repo it's working in is a hard safety boundary. The agent literally cannot touch a cluster it doesn't have config for.

The migration is incremental

You don't have to refactor your whole machine. Start with the project where re-auth bites you most. Add a .envrc, redirect one CLI's config dir, run gh auth login inside that directory. Confirm the credential ends up in the scoped dir, not your home. Repeat for the next CLI when it next annoys you.

After three or four passes, the workspace pattern feels normal — and your terminal stops greeting you with errors every time you change directories. The hour you got back on the first day pays for the setup. Every hour after that is the dividend.

cli-workflowmulti-projectauth-frictioncredential-managementdirenvcontext-switchingssh-agent