Two transports, one rule: pick by visibility, not by owner.Every repo across the org has exactly one canonical transport. The visibility of the repo decides which.
The rule
| Repo visibility | Transport | Remote URL shape | Authenticates via |
|---|---|---|---|
| Public | SSH | git@github.com:<owner>/<repo>.git | Unlocked SSH key |
| Private | HTTPS | https://github.com/<owner>/<repo>.git | GitHub token (PAT or fine-grained) |
Why split this way
The two scenarios that drive the rule:Public repos use SSH because there’s nothing to gate
A public repo’s read path needs no auth (anyone cangit clone https://…), and the write path is gated by GitHub-side branch protection and ruleset rules rather than client-side credentials. Routing public pushes through SSH:
- Skips per-command credential lookups (no token timeout, no keychain unlock).
- Keeps the GitHub PAT scope completely orthogonal to public writes. Even a read-only PAT can push to a public repo over SSH, because the SSH key, not the PAT, is what GitHub authenticates.
- Works fine on hosts where no PAT is configured at all (CI runners with deploy keys, for example).
Private repos use HTTPS because the token tier needs to gate writes
A private repo’s write path is only gated by client-side credentials. If a private repo used SSH:- An unlocked SSH key would auto-authenticate to every private repo the user can reach — no per-repo scope, no per-tier scope, no audit trail of which tool initiated the push.
- Any process with read access to
~/.ssh/id_*could push, because SSH is binary “key works” or “key doesn’t” — there is no fine-grained scope on the SSH side.
git push. That credential lookup goes through the platform’s token store (macOS keychain, git-credential-manager, OS keyring, etc.), which can hold tokens scoped narrowly — read-only on most repos, read+write on a specific set, admin on a smaller set. The same tier system that gates gh API calls now also gates git push.
Without an appropriately-scoped token in the credential store, a private push fails at the credential prompt. That failure is the point: it makes the user (or AI agent) escalate explicitly to a higher-privilege token before any write reaches the remote.
Setting the right remote URL
After cloning, verify and fix the remote if wrong:What this does not cover
- Specific token tiers, PAT scopes, or which keychain holds which token. That’s per-machine setup; it lives in private user-level documentation, not on this public site. Public docs explain that a credential lookup happens; the which credential and how is private operational detail.
- CI authentication. GitHub Actions, deploy keys, and OIDC federation use their own per-job credentials wired into the workflow. The SSH-vs-HTTPS rule is for local developer transports, not CI.
- Submodules. Treat the submodule URL the same way: public submodule → SSH, private submodule → HTTPS, regardless of how the parent repo is cloned.
What this connects to
Branch conventions
Branch names are transport-agnostic. The conventions are the same on either remote.
PR conventions
PR creation uses the
gh CLI, which respects the same token tier as a private git push.Golden laws
The non-negotiable rules around secrets, force-pushes, and admin operations.
GitHub authentication docs
Upstream reference on connecting to GitHub over SSH and HTTPS.