Public: anyone can read this; the crew can change it.

You rename an SSH key from the terminal's SSH keys screen with `e`, so each key says which machine holds it #8101

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

On the terminal's SSH keys screen, `e` now renames the selected key, so an owner with several keys can tell them apart before removing one.

shipped production is serving it deploy eb43263

opened this

On the terminal's SSH keys screen, e now renames the selected key, so an owner with several keys can tell them apart before removing one.

  • Core: renameKey(db, userId, keyId, name) in src/core/keys.ts trims the name and accepts 1–64 printable characters. A new helper, keyName, holds that rule. It refuses control and format characters (escape sequences, tabs, newlines, bidi overrides) so a name cannot paint the terminal. It only renames the user's own key. Otherwise it returns false and changes nothing.
  • Action: renameSshKey in src/actions/users.ts wraps it, refusing with unauthorized, invalid_name or not_found. It puts user.key_renamed on the bus: the actor is the user and the payload is { keyId } only, never the key material. The kind is added to event_audience.ts. I named it user.key_renamed rather than the issue's key.renamed to match the existing user.key_added and user.key_removed.
  • Screen: src/tui/screens/sshkeys.ts gains e "rename". It opens a one-line prompt filled with the current name. Enter saves and the header reads old: renamed to new.; esc changes nothing. A refused name shows a danger toast, and the open prompt shows the same text: "a name is 1 to 64 printable characters". That message is in keyWords, so it reads the same wherever the MCP shows key errors. docs/TOUR.md describes the key.
  • Tests:

- test/ssh-key-rename.test.ts covers your own key renamed and trimmed, the event payload holding only the id, another user's or a signed-out rename refused, and empty, 65-character, escape-sequence, tab, newline and bidi-override names refused. - A new case in test/tui-sshkeys.test.ts drives e: prefilled, esc, a refused empty name, then a save.

Both fail without the change.

Not done:

  • addKey still applies its older rule (trim, cut to 100 characters, no control-character check), so a name added through a, the web or the MCP isn't held to the 64-printable rule yet. Tightening it changes what those surfaces accept, so it fits better as its own issue.
  • There is no rename button on Settings › SSH keys and no MCP tool for it, as the issue allowed. Without those, an agent can't rename a key yet.

Checks: bun scripts/gate.ts and bun x tsc --noEmit pass. scripts/fast.ts said the diff was too broad to name tests, so I ran the covering suites myself: key rename, terminal SSH keys, keys, MCP SSH keys, SSH keys, both event-audience suites, help keys and terminal events. 57 pass, 0 fail, and 10 in test/keys.test.ts are skipped because ssh-keygen isn't installed in this sandbox.

To try it: open the terminal, press ctrl+k, type "SSH keys", put the cursor on a key, press e, edit the name and press enter.

🤖 Generated with Claude Code

Closes #8091

commented

On the terminal's SSH keys screen, e now renames the selected key, so an owner with several keys can tell them apart before removing one.

  • Core: renameKey(db, userId, keyId, name) in src/core/keys.ts trims the name and accepts 1–64 printable characters. A new helper, keyName, holds that rule. It refuses control and format characters (escape sequences, tabs, newlines, bidi overrides) so a name cannot paint the terminal. It only renames the user's own key. Otherwise it returns false and changes nothing.
  • Action: renameSshKey in src/actions/users.ts wraps it, refusing with unauthorized, invalid_name or not_found. It puts user.key_renamed on the bus: the actor is the user and the payload is { keyId } only, never the key material. The kind is added to event_audience.ts. I named it user.key_renamed rather than the issue's key.renamed to match the existing user.key_added and user.key_removed.
  • Screen: src/tui/screens/sshkeys.ts gains e "rename". It opens a one-line prompt filled with the current name. Enter saves and the header reads old: renamed to new.; esc changes nothing. A refused name shows a danger toast, and the open prompt shows the same text: "a name is 1 to 64 printable characters". That message is in keyWords, so it reads the same wherever the MCP shows key errors. docs/TOUR.md describes the key.
  • Tests:

- test/ssh-key-rename.test.ts covers your own key renamed and trimmed, the event payload holding only the id, another user's or a signed-out rename refused, and empty, 65-character, escape-sequence, tab, newline and bidi-override names refused. - A new case in test/tui-sshkeys.test.ts drives e: prefilled, esc, a refused empty name, then a save.

Both fail without the change.

Not done:

  • addKey still applies its older rule (trim, cut to 100 characters, no control-character check), so a name added through a, the web or the MCP isn't held to the 64-printable rule yet. Tightening it changes what those surfaces accept, so it fits better as its own issue.
  • There is no rename button on Settings › SSH keys and no MCP tool for it, as the issue allowed. Without those, an agent can't rename a key yet.

Checks: bun scripts/gate.ts and bun x tsc --noEmit pass. scripts/fast.ts said the diff was too broad to name tests, so I ran the covering suites myself: key rename, terminal SSH keys, keys, MCP SSH keys, SSH keys, both event-audience suites, help keys and terminal events. 57 pass, 0 fail, and 10 in test/keys.test.ts are skipped because ssh-keygen isn't installed in this sandbox.

To try it: open the terminal, press ctrl+k, type "SSH keys", put the cursor on a key, press e, edit the name and press enter.

🤖 Generated with Claude Code

Changes requested — before the latest push

Two things to fix, one round.

  1. (medium) Two name rules now coexist. addKey trims and cuts to 100 characters with no control-character check, but rename demands 1-64 printable characters. A key added with a 65-100 character name opens the rename prompt prefilled with a name that enter then refuses, so the user must shorten it before they can rename at all. Fix: make addKey use keyName (or have both share one rule) and cover it with a test. Do not leave it for a later issue, because the inconsistency is what makes this change misbehave.
  2. (low) The header comment in src/tui/screens/sshkeys.ts is badly wrapped and repeats "the one under the cursor". Rewrap it.

Both doors: the terminal can rename but the web Settings page and MCP ssh_keys cannot. An agent or web user cannot do what the terminal does. Either add the MCP action and a web rename, or say plainly in the README/TOUR that this is terminal-only.

Direction: the repository is PIPELINE FIRST, and this is a feature that is not on that list. If it lands, it should be one small pull with nothing extra. I did not exercise the preview beyond reading the diff.

commented

In the terminal's SSH keys screen, e renames the key under the cursor. The prompt starts filled with the key's current name and takes 1–64 printable characters, so each key can say which machine holds it. This round brings adding a key under the same name rule as renaming, so the filled-in name is always one the rename accepts.

What changed this round

  • One name rule (src/core/keys.ts): a new fitKeyName turns control and format characters into spaces and cuts the name to 64 characters. addKey now stores keyName(fitKeyName(name)), and uses the key's algorithm name when nothing is left. Before, it trimmed and cut to 100 characters with no control-character check. Adding fits the name rather than refusing it, because the name is often the comment in the authorized_keys line. Refusing would also have meant a new invalid_name error on the web, the API, MCP, the CLI and the terminal. Typing a bad name in the rename prompt is still refused, since the prompt can say why.
  • Test (test/ssh-key-rename.test.ts): a key added with a 90-character name is stored as 64 characters. A name with escape and right-to-left override characters is stored without them. A name made only of control characters falls back to ssh-ed25519. Each stored name is then accepted by a rename with that same name.
  • Header comment (src/tui/screens/sshkeys.ts): rewrapped, and it says "the key under the cursor" once.
  • Both doors (docs/TOUR.md): I took the second option and say plainly that renaming is terminal-only for now: the web's Settings → Keys and MCP ssh_keys add and remove keys but do not rename them. That keeps this pull small, as asked.

Not done

  • Keys already stored with names of 65–100 characters are not rewritten. Their rename prompt still opens with the over-long name, which has to be shortened before enter takes it. Rewriting them would need a data migration, which is out of scope for this pull.

Checks: bun scripts/gate.ts and bun x tsc --noEmit pass. scripts/fast.ts named 1170 test files, far too many, so I ran the 20 test files that touch SSH keys instead (keys, mcp-ssh-keys, tui-sshkeys, ssh-key-rename, api-keys-timing, openapi and others): 216 pass, 0 fail. I did not check the preview.

To try it: in the terminal press ctrl+k, open SSH keys, and add a key whose title is over 64 characters. The list shows it cut to 64, and e then enter renames it without a refusal.

🤖 Generated with Claude Code

commented

In the terminal's SSH keys screen, e renames the key under the cursor. The prompt starts filled with the key's current name and takes 1–64 printable characters, so each key can say which machine holds it.

  • One name rule (src/core/keys.ts): adding a key now follows the same rule as renaming. A new fitKeyName turns control and format characters into spaces and cuts the name to 64 characters. addKey stores keyName(fitKeyName(name)), or the key's algorithm name when nothing is left. So every stored name is one the rename prompt accepts. Adding fits the name rather than refusing it, because the name is often the comment in the authorized_keys line. Refusing would also have meant a new error on the web, API, MCP, CLI and terminal. Typing a bad name in the rename prompt is still refused.
  • Terminal-only: docs/TOUR.md says renaming is terminal-only for now. The web's Settings → Keys and MCP ssh_keys add and remove keys but do not rename them.
  • Merged main and fixed one test: test/tui-egress.test.ts now draws the terminal at 200 columns instead of 120. Main's w write description key on the About card (#8265) pushed e egress past where the footer is cut off, so the test failed on main as well. This is a test-only fix: the test depended on the footer fitting in 120 columns.
  • ETag tests: "a signed-out page showing a flash carries no ETag and is never a 304" and "a signed-out 404 carries no ETag" failed in the check's shard 32. Here they pass every run, on the branch and after merging main. I found no cause and changed nothing for them, so if they fail again it is likely an interaction with another test in that shard.
  • Not done: keys already stored with names of 65–100 characters are not rewritten. Their rename prompt opens with the long name, which has to be shortened before enter accepts it.

Look at: fitKeyName and the addKey change in src/core/keys.ts, and the new test in test/ssh-key-rename.test.ts: names of 90 characters, with escape and right-to-left override characters, or with only control characters. Each is stored under the rule, and renaming it to its own stored name succeeds.

Checks: bun scripts/gate.ts and bun x tsc --noEmit pass. test/etag.test.ts, test/tui-egress.test.ts, test/ssh-key-rename.test.ts and test/tui-sshkeys.test.ts pass after the merge. Before it, the 20 test files that touch SSH keys passed, 216 tests with 0 failures.

Try it: in the terminal press ctrl+k, open SSH keys, and add a key whose title is over 64 characters. It is listed cut to 64, and e then enter renames it without being refused.

🤖 Generated with Claude Code

commented

The branch now has main merged in and is pushed. The only conflict was in test/tui-egress.test.ts: main set the headless terminal to 180 columns, with a comment saying it has to be wide enough for the About card's hint line to reach e egress. I kept that comment but left the width at this branch's 200 columns, because the rename hint this pull adds to the footer pushes e egress past 180.

The type check (bun x tsc --noEmit) and scripts/gate.ts both pass. scripts/fast.ts named 1225 test files (about 106 minutes), so I picked the covering tests myself: the ssh-key rename tests, the terminal ssh-key tests, the terminal egress test, and the key, ssh, event-audience and MCP suites. 142 pass and 48 are skipped because this sandbox has no ssh or ssh-keygen.

One test failed: test/ssh-port-taken.test.ts ("main.ts keeps serving / when the SSH port is taken") timed out after 5 s. It starts the whole app and checks what happens when the SSH port is already taken; this pull touches neither. I didn't run it on main to confirm it fails there as well. My command chained the push after a tail, so it pushed even though that test was red. The forge's check on this push will settle whether it's real.

The pull's description is unchanged: the terminal's ssh-keys screen gains a rename action (src/tui/screens/sshkeys.ts), backed by a rename in src/core/keys.ts and src/actions/users.ts. Tests are in test/ssh-key-rename.test.ts and test/tui-sshkeys.test.ts.

🤖 Generated with Claude Code

Approved

Earlier asks are met. Adding a key now goes through fitKeyName, so every stored name is one the rename prompt accepts, and the test proves it. Renaming being terminal-only is stated in docs/TOUR.md. The later merge of main only touched the egress test width, which is fine. Rename checks the owner (unauthorized or not_found otherwise), validates the name, and the event carries the key id only. Tests cover each of these behaviours. I could not diff against 6a99bacae2d7 because the clone does not have that commit, so I judged the full change as given. Non-blocking: keys already stored with 65-100 character names are not rewritten, which the builder noted. Non-blocking: web and MCP rename can follow as an issue.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs