Manage multiple GitHub SSH identities on Linux, macOS & Windows — securely, scalably, and without external dependencies.
When you work with multiple GitHub accounts (personal, work, freelance, open-source orgs), SSH key and config management becomes error-prone and tedious. Manually editing ~/.ssh/config, juggling key files, and remembering host aliases is fragile at scale.
gh-accounts automates this entirely — creating keys, configuring SSH, testing auth, and maintaining backups — while enforcing correct permissions and providing clear diagnostics.
- Create SSH key pairs and config entries in one command
- List all managed accounts with status indicators
- Delete accounts and clean up keys + config
- Update account email addresses
- Test SSH authentication against GitHub
- Backup and restore your entire SSH configuration
- Doctor — diagnostics for permissions, agent, duplicates, integrity
- Agent management — clean, reset, load, and inspect SSH agent keys
- Per-agent worktree isolation — dedicated Git worktrees for AI coding agents
- Harden — prevent agent pollution with
IdentitiesOnly yes - Split mode — optional per-account config files for team/org setups
- Merge / split configs between unified and split modes
- Export accounts as structured JSON
- Zero external dependencies — only native tools (Linux: coreutils, grep, sed; Windows: PowerShell + OpenSSH)
git clone https://github.com/noejunior299/gh-accounts.git
cd gh-accounts
sudo bash install.shcurl -fsSL https://raw.githubusercontent.com/noejunior299/gh-accounts/main/install.sh | sudo bashRequires PowerShell 5.1+ and OpenSSH Client (built-in on Windows 10 1809+).
git clone https://github.com/noejunior299/gh-accounts.git
cd gh-accounts
PowerShell -ExecutionPolicy Bypass -File install.ps1Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force; $s=(New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/noejunior299/gh-accounts/main/install.ps1'); iex $sNote: Run PowerShell as Administrator for the installer. The script copies files to
%ProgramFiles%\gh-accounts\and adds thebin\folder to your user PATH.
# Check if OpenSSH Client is installed
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Client*'
# Install if missing (requires Administrator)
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0sudo bash uninstall.shOr if installed remotely:
sudo rm -f /usr/local/bin/gh-accounts
sudo rm -rf /usr/local/share/gh-accountsPowerShell -ExecutionPolicy Bypass -File uninstall.ps1gh-accounts create work work@company.com
gh-accounts create personal me@gmail.comThis will:
- Generate an
ed25519SSH key pair at~/.ssh/github-<name> - Add a
Host github-<name>block to~/.ssh/config - Print the public key for you to add to GitHub
Note: Keys are no longer auto-loaded into the agent. Use
agent-loadto load a key on demand.
gh-accounts listgh-accounts test workgh-accounts update work --email new@company.comgh-accounts delete personalgh-accounts backup
gh-accounts restoregh-accounts doctorgh-accounts export --jsongh-accounts agent-status # Show loaded keys and GitHub identity count
gh-accounts agent-load work # Load a specific account key into the agent
gh-accounts agent-clean # Remove all GitHub keys from the agent
gh-accounts agent-reset # Same as clean — reset agent to a clean state
gh-accounts harden # Add 'IdentitiesOnly yes' to SSH config (persistent fix)agent-status shows how many keys are loaded, which belong to GitHub accounts, and warns if more than one is active (which can cause auth failures on non-GitHub hosts).
harden writes a global Host * block with IdentitiesOnly yes so SSH only uses the key specified in each Host block — even if the agent has many keys loaded. This is the permanent fix for agent pollution.
Each AI coding agent (Claude Code, Codex, Cursor, etc.) gets its own isolated Git worktree, tied to a gh-accounts identity. This lets parallel agents work on the same repo without colliding on branches or committing under the wrong identity.
gh-accounts agent create work . --account work # worktree + per-worktree identity
gh-accounts agent list # list agent worktrees and identities
gh-accounts agent run work -- <command> # run inside the worktree (--rm to clean up)
gh-accounts agent destroy work # remove the worktreeWorktrees live in a sibling directory (../<repo>--<agent>) using native git worktree, and identity is isolated per worktree via git config --worktree — the main repo and its identity stay untouched. agent run --rm removes the worktree automatically when the command finishes.
gh-accounts split-mode enable # Per-account files in ~/.ssh/gh-accounts/
gh-accounts split-mode disable # Remove Include directive
gh-accounts merge-configs # Merge split files back into unified configgh-accounts switch work # sets user.name + user.email in the current repo
gh-accounts switch personal --global # sets them globally (~/.gitconfig)This runs git config user.name and git config user.email so your commits are attributed to the correct account — no manual editing needed.
gh-accounts set-default workThis adds a Host github.com block to your SSH config, so you can clone repos without a prefix:
git clone git@github.com:user/repo.git # uses the default accountOther accounts still work with their prefixes:
git clone git@github-work:org/repo.gitNote: Only one account can be default at a time. Setting a new default replaces the previous one.
git clone git@github-work:org/repo.git
git clone git@github-personal:user/repo.gitgh-accounts/
├── bin/
│ ├── gh-accounts # CLI entry point (Bash — Linux/macOS)
│ ├── gh-accounts.ps1 # CLI entry point (PowerShell — Windows)
│ └── gh-accounts.cmd # Wrapper (Windows, created by install.ps1)
├── lib/
│ ├── utils.sh # Colors, logging, validation, constants (Bash)
│ ├── config.sh # SSH config read/write (Bash)
│ ├── account.sh # Account CRUD, test, export (Bash)
│ ├── agent.sh # SSH agent management (Bash)
│ ├── agent-worktree.sh # Per-agent Git worktree isolation (Bash)
│ ├── backup.sh # Backup and restore (Bash)
│ ├── doctor.sh # Diagnostic checks (Bash)
│ ├── GhAccounts.psm1 # PowerShell module (all functions)
│ └── GhAccounts.psd1 # PowerShell module manifest
├── install.sh # System-wide installer (Linux/macOS)
├── uninstall.sh # Clean uninstaller (Linux/macOS)
├── install.ps1 # System-wide installer (Windows)
├── uninstall.ps1 # Clean uninstaller (Windows)
├── VERSION # Semantic version
├── LICENSE # MIT
└── README.md
Each module is independently sourced by the CLI. The entry point (bin/gh-accounts) resolves the library directory whether run from source or from /usr/local/.
All account host blocks live in a single file:
~/.ssh/config
# gh-accounts :: work <work@company.com>
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/github-work
IdentitiesOnly yes
# gh-accounts :: personal <me@gmail.com>
Host github-personal
HostName github.com
User git
IdentityFile ~/.ssh/github-personal
IdentitiesOnly yesEach account gets its own file under ~/.ssh/gh-accounts/:
~/.ssh/gh-accounts/github-work
~/.ssh/gh-accounts/github-personal
The main config includes them via:
Include ~/.ssh/gh-accounts/*Split mode is useful when:
- You share config snippets across machines via dotfiles
- You want per-account version control
- You manage a large number of identities
Switch freely between modes — merge-configs and split-mode enable/disable handle the migration.
- Private keys are generated with
ed25519(modern, fast, secure) - Permissions are enforced automatically:
~/.ssh/→700- Private keys →
600 - Public keys →
644 - Config files →
600
- Automatic backups are created before any destructive operation
- ssh-agent is validated and started if needed
- Duplicate detection prevents alias collisions
- No secrets are ever printed or logged (only public keys)
Loading multiple SSH keys into ssh-agent globally causes agent pollution — OpenSSH offers all loaded keys to every host, which can trigger Too many authentication failures on non-GitHub servers.
gh-accounts prevents this by:
- Not auto-loading keys —
createno longer runsssh-add. Load keys explicitly withagent-load. hardencommand — addsHost * / IdentitiesOnly yesto your SSH config, ensuring each connection only uses the key specified in itsIdentityFiledirective.agent-clean— removes GitHub keys from the agent when you need a clean slate.doctor— detects agent pollution and suggests remediation.
Note: Desktop environments using GNOME Keyring as the SSH agent may re-inject keys after removal. Running
gh-accounts hardenis the permanent fix in those environments.
| Requirement | Supported |
|---|---|
| Linux (any distro) | ✅ |
| macOS | ✅ |
| Windows 10 1809+ | ✅ (native, via PowerShell + OpenSSH Client) |
| Bash 4.3+ / 5.x | ✅ |
| Fish shell | ✅ (CLI is bash, works from any shell) |
| PowerShell 5.1+ | ✅ |
| OpenSSH | ✅ (ssh, ssh-keygen, ssh-agent, ssh-add) |
| External deps | None — only OS-native tools (Linux: coreutils, grep, sed, awk / Windows: PowerShell + OpenSSH) |
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Commit with clear messages
- Open a Pull Request against
main
- Keep modules under 300 lines
- Use functions — no inline logic
- Add color-coded output for user-facing messages
- Validate all inputs
- Fail safely — never leave config in a broken state
- Test on Ubuntu 22.04+ with bash 5.x
-
gh-accounts switch <name> [--global]— setuser.nameanduser.emailfor the current repo (default) or globally, so commits are attributed to the correct identity -
gh-accounts set-default <name>— set an account as the default forgithub.com(no prefix needed for clones) -
gh-accounts agent-*/harden— SSH agent management and pollution prevention -
gh-accounts agent create|list|run|destroy— per-agent Git worktree isolation -
gh-accounts agent runauto-detection via env vars (future sugar) -
gh-accounts import— import existing SSH keys into management -
gh-accounts config— interactive setup wizard - Shell completions for bash and fish
- Man page generation
- Optional GPG-signed key metadata
- CI/CD pipeline with ShellCheck and BATS tests
- AUR / PPA / Snap packaging
gh-accounts — One machine. Many GitHub identities. Zero friction.