| 1 | --- |
| 2 | name: "gh-auth-isolation" |
| 3 | description: "Safely manage multiple GitHub identities (EMU + personal) in agent workflows" |
| 4 | domain: "security, github-integration, authentication, multi-account" |
| 5 | confidence: "high" |
| 6 | source: "earned (production usage across 50+ sessions with EMU corp + personal GitHub accounts)" |
| 7 | tools: |
| 8 | - name: "gh" |
| 9 | description: "GitHub CLI for authenticated operations" |
| 10 | when: "When accessing GitHub resources requiring authentication" |
| 11 | --- |
| 12 | |
| 13 | ## Context |
| 14 | |
| 15 | Many developers use GitHub through an Enterprise Managed User (EMU) account at work while maintaining a personal GitHub account for open-source contributions. AI agents spawned by Squad inherit the shell's default `gh` authentication — which is usually the EMU account. This causes failures when agents try to push to personal repos, create PRs on forks, or interact with resources outside the enterprise org. |
| 16 | |
| 17 | This skill teaches agents how to detect the active identity, switch contexts safely, and avoid mixing credentials across operations. |
| 18 | |
| 19 | ## Patterns |
| 20 | |
| 21 | ### Detect Current Identity |
| 22 | |
| 23 | Before any GitHub operation, check which account is active: |
| 24 | |
| 25 | ```bash |
| 26 | gh auth status |
| 27 | ``` |
| 28 | |
| 29 | Look for: |
| 30 | - `Logged in to github.com as USERNAME` — the active account |
| 31 | - `Token scopes: ...` — what permissions are available |
| 32 | - Multiple accounts will show separate entries |
| 33 | |
| 34 | ### Extract a Specific Account's Token |
| 35 | |
| 36 | When you need to operate as a specific user (not the default): |
| 37 | |
| 38 | ```bash |
| 39 | # Get the personal account token (by username) |
| 40 | gh auth token --user personaluser |
| 41 | |
| 42 | # Get the EMU account token |
| 43 | gh auth token --user corpalias_enterprise |
| 44 | ``` |
| 45 | |
| 46 | **Use case:** Push to a personal fork while the default `gh` auth is the EMU account. |
| 47 | |
| 48 | ### Push to Personal Repos from EMU Shell |
| 49 | |
| 50 | The most common scenario: your shell defaults to the EMU account, but you need to push to a personal GitHub repo. |
| 51 | |
| 52 | ```bash |
| 53 | # 1. Extract the personal token |
| 54 | $token = gh auth token --user personaluser |
| 55 | |
| 56 | # 2. Push using token-authenticated HTTPS |
| 57 | git push https://personaluser:$token@github.com/personaluser/repo.git branch-name |
| 58 | ``` |
| 59 | |
| 60 | **Why this works:** `gh auth token --user` reads from `gh`'s credential store without switching the active account. The token is used inline for a single operation and never persisted. |
| 61 | |
| 62 | ### Create PRs on Personal Forks |
| 63 | |
| 64 | When the default `gh` context is EMU but you need to create a PR from a personal fork: |
| 65 | |
| 66 | ```bash |
| 67 | # Option 1: Use --repo flag (works if token has access) |
| 68 | gh pr create --repo upstream/repo --head personaluser:branch --title "..." --body "..." |
| 69 | |
| 70 | # Option 2: Temporarily set GH_TOKEN for one command |
| 71 | $env:GH_TOKEN = $(gh auth token --user personaluser) |
| 72 | gh pr create --repo upstream/repo --head personaluser:branch --title "..." |
| 73 | Remove-Item Env:\GH_TOKEN |
| 74 | ``` |
| 75 | |
| 76 | ### Config Directory Isolation (Advanced) |
| 77 | |
| 78 | For complete isolation between accounts, use separate `gh` config directories: |
| 79 | |
| 80 | ```bash |
| 81 | # Personal account operations |
| 82 | $env:GH_CONFIG_DIR = "$HOME/.config/gh-public" |
| 83 | gh auth login # Login with personal account (one-time setup) |
| 84 | gh repo clone personaluser/repo |
| 85 | |
| 86 | # EMU account operations (default) |
| 87 | Remove-Item Env:\GH_CONFIG_DIR |
| 88 | gh auth status # Back to EMU account |
| 89 | ``` |
| 90 | |
| 91 | **Setup (one-time):** |
| 92 | ```bash |
| 93 | # Create isolated config for personal account |
| 94 | mkdir ~/.config/gh-public |
| 95 | $env:GH_CONFIG_DIR = "$HOME/.config/gh-public" |
| 96 | gh auth login --web --git-protocol https |
| 97 | ``` |
| 98 | |
| 99 | ### Shell Aliases for Quick Switching |
| 100 | |
| 101 | Add to your shell profile for convenience: |
| 102 | |
| 103 | ```powershell |
| 104 | # PowerShell profile |
| 105 | function ghp { $env:GH_CONFIG_DIR = "$HOME/.config/gh-public"; gh @args; Remove-Item Env:\GH_CONFIG_DIR } |
| 106 | function ghe { gh @args } # Default EMU |
| 107 | |
| 108 | # Usage: |
| 109 | # ghp repo clone personaluser/repo # Uses personal account |
| 110 | # ghe issue list # Uses EMU account |
| 111 | ``` |
| 112 | |
| 113 | ```bash |
| 114 | # Bash/Zsh profile |
| 115 | alias ghp='GH_CONFIG_DIR=~/.config/gh-public gh' |
| 116 | alias ghe='gh' |
| 117 | |
| 118 | # Usage: |
| 119 | # ghp repo clone personaluser/repo |
| 120 | # ghe issue list |
| 121 | ``` |
| 122 | |
| 123 | ## Examples |
| 124 | |
| 125 | ### ✓ Correct: Agent pushes blog post to personal GitHub Pages |
| 126 | |
| 127 | ```powershell |
| 128 | # Agent needs to push to personaluser.github.io (personal repo) |
| 129 | # Default gh auth is corpalias_enterprise (EMU) |
| 130 | |
| 131 | $token = gh auth token --user personaluser |
| 132 | git remote set-url origin https://personaluser:$token@github.com/personaluser/personaluser.github.io.git |
| 133 | git push origin main |
| 134 | |
| 135 | # Clean up — don't leave token in remote URL |
| 136 | git remote set-url origin https://github.com/personaluser/personaluser.github.io.git |
| 137 | ``` |
| 138 | |
| 139 | ### ✓ Correct: Agent creates a PR from personal fork to upstream |
| 140 | |
| 141 | ```powershell |
| 142 | # Fork: personaluser/squad, Upstream: bradygaster/squad |
| 143 | # Agent is on branch contrib/fix-docs in the fork clone |
| 144 | |
| 145 | git push origin contrib/fix-docs # Pushes to fork (may need token auth) |
| 146 | |
| 147 | # Create PR targeting upstream |
| 148 | gh pr create --repo bradygaster/squad --head personaluser:contrib/fix-docs ` |
| 149 | --title "docs: fix installation guide" ` |
| 150 | --body "Fixes #123" |
| 151 | ``` |
| 152 | |
| 153 | ### ✗ Incorrect: Blindly pushing with wrong account |
| 154 | |
| 155 | ```bash |
| 156 | # BAD: Agent assumes default gh auth works for personal repos |
| 157 | git push origin main |
| 158 | # ERROR: Permission denied — EMU account has no access to personal repo |
| 159 | |
| 160 | # BAD: Hardcoding tokens in scripts |
| 161 | git push https://personaluser:ghp_xxxxxxxxxxxx@github.com/personaluser/repo.git main |
| 162 | # SECURITY RISK: Token exposed in command history and process list |
| 163 | ``` |
| 164 | |
| 165 | ### ✓ Correct: Check before you push |
| 166 | |
| 167 | ```bash |
| 168 | # Always verify which account has access before operations |
| 169 | gh auth status |
| 170 | # If wrong account, use token extraction: |
| 171 | $token = gh auth token --user personaluser |
| 172 | git push https://personaluser:$token@github.com/personaluser/repo.git main |
| 173 | ``` |
| 174 | |
| 175 | ## Anti-Patterns |
| 176 | |
| 177 | - ❌ **Hardcoding tokens** in scripts, environment variables, or committed files. Use `gh auth token --user` to extract at runtime. |
| 178 | - ❌ **Assuming the default `gh` auth works** for all repos. EMU accounts can't access personal repos and vice versa. |
| 179 | - ❌ **Switching `gh auth login`** globally mid-session. This changes the default for ALL processes and can break parallel agents. |
| 180 | - ❌ **Storing personal tokens in `.env`** or `.squad/` files. These get committed by Scribe. Use `gh`'s credential store. |
| 181 | - ❌ **Ignoring token cleanup** after inline HTTPS pushes. Always reset the remote URL to avoid persisting tokens. |
| 182 | - ❌ **Using `gh auth switch`** in multi-agent sessions. One agent switching affects all others sharing the shell. |
| 183 | - ❌ **Mixing EMU and personal operations** in the same git clone. Use separate clones or explicit remote URLs per operation. |