add_ssh_key generate=true returns the minted public_key once, in the response to the generating call (security/sshKeyOperations.ts L249). Nothing persists it, so if that one response is lost the public half cannot be retrieved.
The recovery paths are all closed:
get_ssh_key returns the key file's contents (L283), which is the sealed enc:v1: envelope. That is deliberate — it keeps the private key off the wire on the clone path — but it means the public half is not derivable by the caller.
- Retrying
add_ssh_key returns Key already exists.
update_ssh_key requires a key you supply; it cannot mint one.
So a client that times out, disconnects, or crashes after the node has committed the key is left with a key it cannot register anywhere. The only way out is delete_ssh_key followed by a fresh add_ssh_key generate=true, which is exactly what the docs currently tell users to do (HarperFast/documentation#599).
That is a rough edge for automation in particular: the deploy-by-reference flow (#570) has scripts calling this, and a transient network fault mid-call forces a delete/regenerate cycle rather than a safe retry.
Suggested fix
Per @kriszyp's review on #594, either:
- Persist the public key. It is not secret, so it can sit next to the key file (e.g.
ssh/<name>.key.pub) or in the config block, and be returned by get_ssh_key. This also makes list_ssh_keys able to show which public key each entry corresponds to.
- Make generated adds idempotent. A repeated
add_ssh_key name=X generate=true returns the existing entry's public_key instead of Key already exists — though this needs care so it does not mask a genuine name collision between two different intended keys.
(1) is the smaller change and composes with (2) if we later want it. Both interact with the reservation work in #693, so they are probably worth doing together.
If we do neither, the docs note in HarperFast/documentation#599 stays accurate and this is a known limitation rather than a defect — the decision is whether automation should be able to retry safely.
Provenance
Raised by @kriszyp reviewing #594 (approved; flagged as follow-up, not a blocker).
add_ssh_key generate=truereturns the mintedpublic_keyonce, in the response to the generating call (security/sshKeyOperations.tsL249). Nothing persists it, so if that one response is lost the public half cannot be retrieved.The recovery paths are all closed:
get_ssh_keyreturns the key file's contents (L283), which is the sealedenc:v1:envelope. That is deliberate — it keeps the private key off the wire on the clone path — but it means the public half is not derivable by the caller.add_ssh_keyreturnsKey already exists.update_ssh_keyrequires a key you supply; it cannot mint one.So a client that times out, disconnects, or crashes after the node has committed the key is left with a key it cannot register anywhere. The only way out is
delete_ssh_keyfollowed by a freshadd_ssh_key generate=true, which is exactly what the docs currently tell users to do (HarperFast/documentation#599).That is a rough edge for automation in particular: the deploy-by-reference flow (#570) has scripts calling this, and a transient network fault mid-call forces a delete/regenerate cycle rather than a safe retry.
Suggested fix
Per @kriszyp's review on #594, either:
ssh/<name>.key.pub) or in the config block, and be returned byget_ssh_key. This also makeslist_ssh_keysable to show which public key each entry corresponds to.add_ssh_key name=X generate=truereturns the existing entry'spublic_keyinstead ofKey already exists— though this needs care so it does not mask a genuine name collision between two different intended keys.(1) is the smaller change and composes with (2) if we later want it. Both interact with the reservation work in #693, so they are probably worth doing together.
If we do neither, the docs note in HarperFast/documentation#599 stays accurate and this is a known limitation rather than a defect — the decision is whether automation should be able to retry safely.
Provenance
Raised by @kriszyp reviewing #594 (approved; flagged as follow-up, not a blocker).