feat(git-identity): import an existing SSH key and sign commits with it
Settings → Git gains Commit signing: one picker signs every commit (terminal, Changes panel, agents) with one of the account's keys — gpg.format ssh, user.signingkey = the key's private file, commit.gpgsign true in its own ~/.gitconfig; a signing key the user set up themselves is replaced only after a confirm (EFOREIGNSIGN without `replace`). Import a key you have takes a key file or paste (OpenSSH or PEM, ed25519 or RSA >= 2048) into ~/.config/agenthub/ssh/ as a named key, written like a generated one (member: write-as, 0600), and by default signs with it in the same step. A passphrase key, a .pub, an ecdsa key, a mismatched paste and the same key twice are refused; only the public half ever comes back. Deleting the signing key stops signing so commits keep working. The SSH tab is renamed "Git (GitHub, GitLab, Gitea, Backlog…)" with the Gitea and Backlog steps, and the File Explorer's ESECRET toast now says that top-level dot folders of a home are edited in a terminal, and where the SSH keys live instead.
This commit is contained in:
@@ -1529,6 +1529,8 @@ cannot collide on `index.lock`.
|
||||
GET /api/me/git { account: "member"|"host", writable, ssh: { type, publicKey, fingerprint, terminal: "on"|"off"|"foreign" } | null (the first key), sshKeys: [{ id, type, name, publicKey, fingerprint }] (default ed25519, default rsa, then named keys by name; `name` null for a default key, `id` = its file name), sshTerminal, sshUsers: [{ host, user, alias, key }], https: [{ protocol, host, username, hasToken }], author: { name, email }, defaultEmail }
|
||||
POST /api/me/git/ssh-key Make an SSH key — Body: { replace?, type?: "ed25519" | "rsa", name? } — without `name` the DEFAULT key of that type (`id_ed25519`/`id_rsa`), with `name` (lowercase letters, digits, `-`, ≤ 32; 400 EBADKEYNAME) one more beside it, `id_<type>_<name>` — for another AWS account or a second GitHub user. 409 EEXISTS when that key exists without replace, or the name is taken by a key of the other type; 409 ETOOMANYKEYS past 10 keys → 201, same shape
|
||||
DELETE /api/me/git/ssh-key Delete one key — `?id=<key id>` (400 EBADKEYNAME for a malformed id), or the default key of `?type=ed25519|rsa` — or every key without either (404 ENOTFOUND, 400 EBADKEYTYPE). A login naming a deleted key goes back to every key; our terminal `core.sshCommand` follows the keys and goes with the last one
|
||||
POST /api/me/git/ssh-key/import Import a private key the user already has — Body: { privateKey, name?, sign?, replace? } — OpenSSH (`BEGIN OPENSSH PRIVATE KEY`) or PEM (PKCS#1/PKCS#8) text, ed25519 or RSA ≥ 2048; saved as a NAMED key (`name`, else `signing`, `signing-2`…) written like a generated one, so it serves clones too; only `imported` (its public half) comes back. 400 EBADKEY (not a key, a `.pub`, halves that do not match), EKEYPASSPHRASE (an encrypted key — a dashboard commit has nowhere to type the passphrase), EBADKEYTYPE; 409 EEXISTS (the same key already imported, or the name taken), ETOOMANYKEYS. With `sign: true` commits are signed with it at once; a refusal of that step comes back as `signError` (the key stays saved) → 201
|
||||
PUT /api/me/git/signing Sign commits — Body: { key: <key id> | null, replace? } — writes `gpg.format ssh`, `user.signingkey <the key's PRIVATE file>` (ssh-keygen signs with it; a `.pub` would need an ssh-agent) and `commit.gpgsign true` to the account's `~/.gitconfig`, so terminal, dashboard and agent commits are all signed; `null` removes the three only when the key is ours. 409 EFOREIGNSIGN over a signing key the user set themselves, unless `replace: true`; deleting the signing key stops signing. `GET` answers `signing: { state: "on"|"off"|"foreign", key, path }` (`path` = the user's own key file when foreign)
|
||||
PUT /api/me/git/ssh-terminal Use the keys for git typed in the account's own terminal — Body: { on } — sets/unsets `core.sshCommand` in its `~/.gitconfig` (every AgentHub key first, then `~/.ssh/id_*`; rewritten as keys are made or deleted while on); on by default when a member makes a key; 409 EFOREIGNSSH over one the user wrote, which is never touched
|
||||
PUT /api/me/git/ssh-user An SSH login — Body: { host, user, alias?, key?, allRegions? } — `allRegions: true` (a CodeCommit host, no alias — else 400 EBADHOST) writes the rule for EVERY CodeCommit region at once (the list is `codecommitRegions` in `GET /api/me/git`), replacing each region's own; `DELETE /api/me/git/ssh-user/<a CodeCommit host>?allRegions=1&user=<key ID>` removes them all. Otherwise it writes `url."ssh://<user>@<host>/".insteadOf "ssh://<from>/"` to the account's `~/.gitconfig`, `<from>` = `alias` or the host itself (replacing ours for that host/alias), so `ssh://<from>/…` logs in as `user` (AWS CodeCommit: the IAM SSH key ID `APKA…`). `alias` (lowercase letters, digits, `-`, NO dot — 400 EBADALIAS) is one more account on the same host: two AWS accounts clone as `ssh://aws-prod/v1/repos/x` and `ssh://aws-dev/v1/repos/x`. `key` (a key id, 400 ENOKEY) is stored as `agenthub.<from>.sshKey` there: a clone of a `ssh://<from>/…` URL then offers ONLY that key (`IdentitiesOnly`) and persists it into the clone's config; without it every key is offered. An IAM SSH key ID logs in to every region of its AWS account, so a Clone of `ssh://git-codecommit.<region>.amazonaws.com/…` tries every CodeCommit login saved — the region's own first, then other regions' and every named key's (another AWS account), each with its own key: the first the remote accepts (`git ls-remote`), or the only one, and the clone's `origin` then names that user. So a second account's key clones from a plain AWS URL too, with no `ssh://<alias>/` rewrite. 400 EBADHOST / EBADSSHUSER
|
||||
DELETE /api/me/git/ssh-user/:from Drop the rule of that host or alias and its key link (404 ENOTFOUND); any other `insteadOf` the user wrote is never touched
|
||||
|
||||
Reference in New Issue
Block a user