Skip to content
Eric Kochen edited this page May 16, 2026 · 1 revision

Keys

A dedicated top-level tab for every SSH key in ~/.ssh/. Reachable from the Host List with Tab (cycles Hosts -> Tunnels -> Containers -> Keys). Inspect, copy and push your keys without leaving the TUI.

What you see

The tab is a three-zone layout, top to bottom:

  1. Vault SSH cert strip at the top (only when at least one host has a Vault SSH role configured). One row per active cached cert with alias, role, a colour-coded TTL bar (green >= 5 min, amber >= 2 min, red below) and remaining time. Expired certs read expired.
  2. Hero panel with three side-by-side cards:
    • Keys list on the left (shown only when there are at least two keys, or while search is active). Name-only rows with the selection highlighter and a N/total counter in the card title.
    • Randomart card in the middle. A drunken-bishop visualisation of the key's fingerprint with a gentle twinkle animation. Two grid sizes: 17x9 on shorter terminals, 25x13 once the terminal reaches 30 rows.
    • Info card on the right with a vertical strength gauge, kv-rows for security, identity and usage, and a multi-line activity sparkline at the bottom that auto-scales its time window from 5 days out to a full year as the per-key history accrues.
  3. Linked hosts panel below the hero. Every host that references the highlighted key with status glyph, alias, hostname and last-connect age. Recently-connected hosts cluster at the top sorted by recency; never-connected hosts sit below a centred divider with the muted label never connected flanked by horizontal rule characters, sorted alphabetically. The list overflows with a ... N more footer when there is no room for everything.

On narrow terminals the hero collapses to a single stacked card: name, type, strength bar, last-touch, linked-hosts count and the randomart fingerprint below.

Keybindings

See Keybindings#Keys-Tab for the full table. Quick reference:

Key Action
j / k / arrows Navigate keys
Enter / c Copy the highlighted .pub to the clipboard
p Push the highlighted key to a multi-host picker
V Bulk-sign Vault SSH certs (shared handler with the Host List)
/ Search keys by name
: Jump
n What's new overlay
? Help
Esc Cancel an in-flight push (otherwise the one-shot quit-hint toast)
q Quit
Tab / Shift+Tab Cycle top tabs

Push a key to multiple hosts

Press p on the highlighted key. A picker opens with every host in ~/.ssh/config. Use Space to toggle hosts, a to flip every eligible host at once, Enter to commit, Esc to cancel.

Hosts that use Vault SSH (either a # purple:vault-ssh role comment or a CertificateFile inside ~/.purple/certs/) render as [-] and are skipped automatically. Pressing Space on one of those hosts surfaces a brief toast and the host stays unselected. This is intentional: signed-cert hosts already authenticate via short-lived certs and should not also carry the static key.

After commit, a destructive confirm dialog names the key and the host count. Press y to start the push.

What the push does on the remote

For each selected host purple spawns a single ssh -F <config_path> -T <alias> and pipes the .pub over stdin to a small shell snippet that:

  • creates ~/.ssh with mode 700 if missing
  • creates ~/.ssh/authorized_keys with mode 600 if missing
  • normalises CRLF on both the incoming key and the file before comparing, so a Windows-edited authorized_keys does not produce duplicate appends
  • skips the append when the key is already present (exact-line match)
  • writes the new key with a trailing newline when it is not

Per-host outcome is one of Appended, AlreadyPresent or Failed(<scrubbed stderr>). The picker's toast and sticky status summarise the run.

Safety rails

The local pubkey is validated before any host is touched:

  • Algorithm allowlist: ssh-rsa, ssh-ed25519, ssh-dss, ecdsa-sha2-nistp256 / 384 / 521, sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com. Cert tokens like ssh-ed25519-cert-v01@openssh.com are rejected: pushing a certificate as a static key would bypass its TTL.
  • Multi-line input is rejected so a command="..." clause smuggled on a second line cannot land in authorized_keys.
  • Base64 body validated so a malformed key never reaches the wire.
  • 16 KiB cap on the .pub file (OpenSSH RSA-8192 keys serialise to ~3 KiB; the cap leaves headroom for comments).
  • O_NOFOLLOW on the open so a symlink at the .pub path errors out instead of dereferencing into a log file or /dev/urandom.

Cancelling a run

Press Esc while the picker is in-flight. The cancel flag is observed before each host's ssh spawn, so the worker short-circuits at its next iteration. A toast names the per-host progress at the moment of cancel: Push cancelled after 3 of 12 host(s). Re-run to finish the rest.

Activity log

Every successful connect (TUI or CLI) records a (alias, timestamp) event to ~/.purple/key_activity.json. The info card's sparkline and the Used label in the kv-list both read from this log. Retention is 90 days; older events are pruned on load and on every record. Writes are atomic (temp file + rename) and suppressed in --demo mode.

The log is per alias, not per fingerprint, so a key push followed by a connect does not have to attribute the connect to a specific key file. The linked-hosts panel pivots the log through SshKeyInfo::linked_hosts at render time.

Search

Press / to filter the key list by name. The query is modeless: keystrokes refine, Backspace removes one character (an empty query stays open), Enter copies the highlighted match and exits search, Esc closes search outright. Tab and Shift+Tab exit search and then cycle top tabs.

Where keys come from

purple scans ~/.ssh/ for every .pub file. A private key without a matching .pub sibling is not listed; the tab is driven by the public keys it can read. The list refreshes after every key push and after host-list reloads, so there is no manual refresh keybinding.

How this relates to the legacy K overlay

The Host List's K key still opens the read-only key list overlay introduced in earlier releases. The Keys tab is the multi-action surface: copy, push and Vault SSH sign all live there. Both views read the same key discovery; pick whichever fits the moment.

Related

Clone this wiki locally