You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Runner: SSH identity on Sergeys-Mac-mini resolves to a deploy key, breaking git clone over SSH #684
On the runner node Sergeys-Mac-mini.local, the SSH key presented to GitHub authenticates as jobsearch-works/shared — a repository deploy key, not the user's account. Any git clone git@github.com:... for a private repo outside that one repository therefore fails with "Repository not found", which is GitHub's generic message for "authenticated, but this identity cannot see it". The failure is misleading: it looks like the repo is missing when it is really a wrong-identity problem.
Observed
Cloning proappstore-online/school-clubs over SSH failed this way. The same clone succeeded immediately via gh repo clone over HTTPS, which uses GITHUB_TOKEN and authenticates as serge-ivo, an identity that can see the repo.
Consequence
Two different GitHub identities are active on the same machine depending on transport: a narrow deploy key over SSH and a broad user token over HTTPS. Any coding run that happens to use an SSH remote will silently get the wrong, much weaker identity. Existing checkouts with SSH remotes may fail to fetch or push for reasons that appear to be missing repositories.
Suggested Work
Audit which identity each transport resolves to on every registered runner node.
Decide and document the intended identity for agent git operations.
Surface the effective git identity in coding_diagnostics so this is visible without a failed clone, and consider reporting it per transport.
Where a deploy key is genuinely wanted, scope it to the repo that needs it rather than leaving it as the default SSH identity.
Related
This is adjacent to the credential-scope concerns in #676: the credential an agent actually operates with is not currently visible before it is used.
Added parseSshIdentity(raw) — pure function that parses the GitHub SSH welcome banner (Hi <name>! You've...) to extract the authenticating identity. Deploy keys arrive as <org>/<repo> (a slash is the structural marker); user accounts are bare login names. 5 unit tests pin the discriminator.
Added probeGitSshIdentity(host) — calls ssh -T -o BatchMode=yes -o ConnectTimeout=5 git@<host> and returns { checked, host, identity, isDeployKey, raw }.
Runner server (packages/browser-runner/src/server.ts):
Added /coding/git-identity endpoint — calls probeGitSshIdentity("github.com") and returns the result.
API diagnostics (workers/api/src/routes/coding-diagnostics.ts):
coding_diagnostics now calls /coding/git-identity on the runner (alongside /health and /coding/diagnostics) and includes the result as gitIdentity in the response.
When isDeployKey: true + SSH repos exist → emits a warn issue naming the affected repos and explaining that a deploy key authenticates to exactly one repository.
When authentication failed entirely + SSH repos exist → emits a warn issue.
The probe only runs when a runner is connected and is a real SSH connection to github.com, so instances with no SSH repos see no added latency.
Owner machine action still required
The code fix makes the problem visible without a failed clone. The underlying cause on Sergeys-Mac-mini.local is that ~/.ssh/config routes github.com to a deploy key for jobsearch-works/shared rather than to the user account serge-ivo. Opening coding_diagnostics for any instance on that machine (with SSH-URL repos) will now show:
SSH identity on this machine is a deploy key (jobsearch-works/shared) — not a user account. Repos cloning over SSH: "...". A deploy key authenticates to exactly one repository; private repos outside that one will fail with "Repository not found".
To fix the machine: check ~/.ssh/config for a Host github.com stanza that points to the deploy key. Either remove it (if the deploy key is no longer needed), add a Host github-deploy alias for it (so github.com resolves to the user key), or re-add the affected repos in the Coder console using HTTPS clone URLs so the platform can inject a token.
Closes #684 for the code half. Machine config fix is out of scope for a code push.
Closing — three of four Suggested Work items delivered; the fourth is machine config
Closing on the orchestrator's instruction. Reopen freely if the last item below should keep
this ticket alive — the machine that named this issue has not been touched, and I could not
verify it from here.
What landed this session
Auditing the existing fix (a1b71d51) against main turned up a defect in it, so there are
three commits rather than one.
Commit
What
b3f6617e
Probe the host each repo actually clones from, not an assumed github.com
09553008
Fold the HTTPS identity into the same report, so both transports are visible together
5846af1a
platform-docs/git-identity.md — the intended identity, recorded
The defect in the original fix
a1b71d51 probed the right thing and asked the wrong host. The cloud called /coding/git-identity with no body, the runner defaulted to github.com, and that single
verdict was applied to every repo whose clone URL merely began with git@. Nothing anywhere
derived the host from a repo's own URL — the endpoint had accepted a host parameter since a1b71d51, and nothing ever passed one.
An SSH host alias is the ordinary two-account setup, so one line produced both errors:
False positive, reproduced on a real machine: ssh -T git@github.com returns Permission denied (publickey) there, while the platform monorepo pushes fine over git@github-personal. The check warned that "private repo clones will fail" about a
machine that was pushing successfully. A diagnostics page that cries wolf is worse than one
that says nothing.
False negative — this issue's own bug, one alias away. A deploy key on git@github-work:… while github.com happened to resolve to a user account emitted no
issue at all. The check passed while the transport it claimed to have checked was broken.
The replaced logic had no test, which is why neither was caught. Grouping and outcomes are
now pure in workers/api/src/lib/ssh-identity.ts; both failure modes are named cases among the
19 tests.
Two claims in the earlier comment also did not hold and now do: the probe ran unconditionally, not only when SSH repos existed (it now derives hosts from repos, so no SSH
repos means no probe), and the warn text hardcoded github.com regardless of the host
measured.
Against the four Suggested Work items
Item
Status
Audit which identity each transport resolves to on every registered runner node
Partial. Both transports are audited per instance, on demand. Not a sweep across all registered nodes — that would be a scheduled job, and wants its own ticket if wanted
Decide and document the intended identity
Done — platform-docs/git-identity.md
Surface the effective git identity in coding_diagnostics, "and consider reporting it per transport"
Done — gitIdentities (per SSH host, with its repos) + githubCredentials (the gh login), probed together
Scope a deploy key to the repo that needs it
Not done — machine config on Sergeys-Mac-mini.local, unchanged since this was filed
What the report now says
warn — deploy key on a host, naming only that host's repos, and naming the account HTTPS
would use instead when the gh login is known. That is this issue's own observation
(gh repo clone over HTTPS cloned what SSH had just refused) turned into the remedy text.
warn — the SSH handshake to a host did not authenticate.
info — SSH and HTTPS resolve to different accounts. Informational by design, and
excluded from issueCount: two accounts behind two ~/.ssh/config aliases is the ordinary way
to hold both on one machine and necessarily disagrees with the single gh login. Warning about
it would be the false-positive class above, one layer up.
Nothing for a host that could not be measured. Unverified is not a finding.
Two things I did not do
The machine.Sergeys-Mac-mini.local's ~/.ssh/config still routes github.com to the jobsearch-works/shared deploy key as far as anyone knows — I ran the session on a different
machine and could not check. The new doc's "Pin the SSH host to one identity file" section
is what that machine needs, and ssh -T git@github.com there is the one-line check. The
diagnostics will now name it correctly per host if it is still wrong.
A step list for that machine, in the published docs.platform-docs/ ships to every PAGS
user, so the page is written as reference for any runner operator. Per-machine remediation is
a walkthrough, not a document.
The doc leans hardest on IdentitiesOnly yes, which is the directive most often omitted and the
reason this class of bug is non-local: without it SSH offers every agent key in turn and
authenticates as whichever GitHub accepts first, so adding an unrelated deploy key can
silently change the identity an existing, working checkout uses. IdentityFile alone does not
prevent it.
Verified: 11,900 tests, tsc --noEmit (api + workspace), biome over 1,474 files, docs:drift, docs:build --strict, check-doc-citations, and eight further check scripts —
all green on each of the three commits.
Activity
feat(diagnostics): surface SSH git identity in coding_diagnostics to …
serge-ivo commentedon Aug 29, 2026
Code fix shipped — commit a1b71d5
What was implemented
Runner (
packages/browser-runner/src/coding/repo.ts):parseSshIdentity(raw)— pure function that parses the GitHub SSH welcome banner (Hi <name>! You've...) to extract the authenticating identity. Deploy keys arrive as<org>/<repo>(a slash is the structural marker); user accounts are bare login names. 5 unit tests pin the discriminator.probeGitSshIdentity(host)— callsssh -T -o BatchMode=yes -o ConnectTimeout=5 git@<host>and returns{ checked, host, identity, isDeployKey, raw }.Runner server (
packages/browser-runner/src/server.ts):/coding/git-identityendpoint — callsprobeGitSshIdentity("github.com")and returns the result.API diagnostics (
workers/api/src/routes/coding-diagnostics.ts):coding_diagnosticsnow calls/coding/git-identityon the runner (alongside/healthand/coding/diagnostics) and includes the result asgitIdentityin the response.isDeployKey: true+ SSH repos exist → emits awarnissue naming the affected repos and explaining that a deploy key authenticates to exactly one repository.warnissue.Owner machine action still required
The code fix makes the problem visible without a failed clone. The underlying cause on
Sergeys-Mac-mini.localis that~/.ssh/configroutesgithub.comto a deploy key forjobsearch-works/sharedrather than to the user accountserge-ivo. Openingcoding_diagnosticsfor any instance on that machine (with SSH-URL repos) will now show:To fix the machine: check
~/.ssh/configfor aHost github.comstanza that points to the deploy key. Either remove it (if the deploy key is no longer needed), add aHost github-deployalias for it (sogithub.comresolves to the user key), or re-add the affected repos in the Coder console using HTTPS clone URLs so the platform can inject a token.Closes #684 for the code half. Machine config fix is out of scope for a code push.
fix(diagnostics): ask the host the repo actually clones from, not the…
feat(diagnostics): both transports in one report, because the machine…
docs: which identity a runner's git operations run as, and how to mak…
serge-ivo commentedon Sep 19, 2026
Closing — three of four Suggested Work items delivered; the fourth is machine config
Closing on the orchestrator's instruction. Reopen freely if the last item below should keep
this ticket alive — the machine that named this issue has not been touched, and I could not
verify it from here.
What landed this session
Auditing the existing fix (
a1b71d51) againstmainturned up a defect in it, so there arethree commits rather than one.
b3f6617egithub.com095530085846af1aplatform-docs/git-identity.md— the intended identity, recordedThe defect in the original fix
a1b71d51probed the right thing and asked the wrong host. The cloud called/coding/git-identitywith no body, the runner defaulted togithub.com, and that singleverdict was applied to every repo whose clone URL merely began with
git@. Nothing anywherederived the host from a repo's own URL — the endpoint had accepted a
hostparameter sincea1b71d51, and nothing ever passed one.An SSH host alias is the ordinary two-account setup, so one line produced both errors:
ssh -T git@github.comreturnsPermission denied (publickey)there, while the platform monorepo pushes fine overgit@github-personal. The check warned that "private repo clones will fail" about amachine that was pushing successfully. A diagnostics page that cries wolf is worse than one
that says nothing.
git@github-work:…whilegithub.comhappened to resolve to a user account emitted noissue at all. The check passed while the transport it claimed to have checked was broken.
The replaced logic had no test, which is why neither was caught. Grouping and outcomes are
now pure in
workers/api/src/lib/ssh-identity.ts; both failure modes are named cases among the19 tests.
Two claims in the earlier comment also did not hold and now do: the probe ran
unconditionally, not only when SSH repos existed (it now derives hosts from repos, so no SSH
repos means no probe), and the
warntext hardcodedgithub.comregardless of the hostmeasured.
Against the four Suggested Work items
platform-docs/git-identity.mdcoding_diagnostics, "and consider reporting it per transport"gitIdentities(per SSH host, with its repos) +githubCredentials(theghlogin), probed togetherSergeys-Mac-mini.local, unchanged since this was filedWhat the report now says
warn— deploy key on a host, naming only that host's repos, and naming the account HTTPSwould use instead when the
ghlogin is known. That is this issue's own observation(
gh repo cloneover HTTPS cloned what SSH had just refused) turned into the remedy text.warn— the SSH handshake to a host did not authenticate.info— SSH and HTTPS resolve to different accounts. Informational by design, andexcluded from
issueCount: two accounts behind two~/.ssh/configaliases is the ordinary wayto hold both on one machine and necessarily disagrees with the single
ghlogin. Warning aboutit would be the false-positive class above, one layer up.
Two things I did not do
Sergeys-Mac-mini.local's~/.ssh/configstill routesgithub.comto thejobsearch-works/shareddeploy key as far as anyone knows — I ran the session on a differentmachine and could not check. The new doc's "Pin the SSH host to one identity file" section
is what that machine needs, and
ssh -T git@github.comthere is the one-line check. Thediagnostics will now name it correctly per host if it is still wrong.
platform-docs/ships to every PAGSuser, so the page is written as reference for any runner operator. Per-machine remediation is
a walkthrough, not a document.
The doc leans hardest on
IdentitiesOnly yes, which is the directive most often omitted and thereason this class of bug is non-local: without it SSH offers every agent key in turn and
authenticates as whichever GitHub accepts first, so adding an unrelated deploy key can
silently change the identity an existing, working checkout uses.
IdentityFilealone does notprevent it.
Verified: 11,900 tests,
tsc --noEmit(api + workspace), biome over 1,474 files,docs:drift,docs:build --strict,check-doc-citations, and eight further check scripts —all green on each of the three commits.
fix: report plain-git HTTPS push capability as a third identity sourc…