deploy-key add silently converts an existing user key, and removing it deletes the key #54

closed cmc opened this on 2026-08-30 17:48 UTC

Discussion

cmc 2026-08-30 17:48 UTC

repo deploy-key add on a key that is already registered as a user key does not fail and does not add a second registration. It converts the existing one, and removing the deploy key then deletes the key outright.

What happened on a live instance: the CI runner's key was registered as a user key with full scope. Registering that same public key as a repository deploy key re-scoped it, and the runner stopped:

runner: claiming build: exit status 4 (this key's scope (deploy:26:rw) does not allow control commands

Removing the deploy key to undo it did not restore the user key — it unregistered the key entirely:

runner: claiming build: exit status 4 (this key is not registered here. Create an account with:
  ssh <host> register --username <name> --email <address>

Recovering needed keys add --scope full from a second machine that still had a working key. An instance whose only registered key was the one converted this way would have no way back in over SSH.

Two things would each have prevented it: refusing deploy-key add for a key that is already registered, naming the conflict; or scoping the two registrations independently so that removing a deploy key restores nothing but the deploy grant.

cmc 2026-08-30 18:09 UTC

Not a bug — I misread my own outage.

ssh_keys.fingerprint is NOT NULL UNIQUE globally, AddSSHKey is a plain INSERT with no upsert, and runDeployKeyAdd already maps the conflict to ErrDuplicateKey. Adding a deploy key for an already-registered fingerprint is refused, not converted.

What actually happened: the ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 I ran as ci-runner overwrote the key the runner was already authenticating with. The new key was unregistered, so deploy-key add succeeded on it with no conflict, and the runner — now presenting that key — got deploy:26:rw scope and could no longer call control commands. Removing the deploy key then left the new key unregistered entirely.

Both symptoms follow from the overwrite. Closing.