Conversation
Encode the OpenSSH private-key envelope and verify displayed public-key equality with native ssh-keygen. Ultraworked with [omo](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
There was a problem hiding this comment.
2 issues found across 2 files
Confidence score: 4/5
tests/e2e/ssh-key-generator-interop.spec.jshard-depends on the externalssh-keygenbinary without an availability guard, so environments without OpenSSH can fail the entire E2E suite—skip or conditionally run the test when the dependency is unavailable.tests/e2e/ssh-key-generator-interop.spec.jsdoes not explicitly select Ed25519, so a future default change could make the test validate a different key type—choose the Ed25519 option before asserting interop.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="tests/e2e/ssh-key-generator-interop.spec.js">
<violation number="1" location="tests/e2e/ssh-key-generator-interop.spec.js:10">
P3: The test asserts Ed25519 interop but never selects the Ed25519 radio, depending on the current checked default in the route. If the default key type changes, this test silently starts validating a different key type. Click the ed25519 radio explicitly (and assert it) so the test's coverage matches its name and intent.</violation>
<violation number="2" location="tests/e2e/ssh-key-generator-interop.spec.js:46">
P2: The test hard-depends on the external `ssh-keygen` binary with no availability guard, so any machine running `npm run test:e2e` without the OpenSSH client (a dev box, a non-ubuntu runner) fails the whole suite rather than skipping this interop check. Detect the binary and `test.skip` cleanly when it is absent, e.g. `spawnSync("ssh-keygen", ["-V"])` up front and skip on error, while still failing hard when present but key parsing fails.</violation>
</file>
Shadow auto-approve: would not auto-approve because issues were found.
Re-trigger cubic
| try { | ||
| await writeFile(keyPath, `${generated.privateKey}\n`, { mode: 0o600 }); | ||
| await chmod(keyPath, 0o600); | ||
| const derived = spawnSync("ssh-keygen", ["-y", "-f", keyPath], { |
There was a problem hiding this comment.
P2: The test hard-depends on the external ssh-keygen binary with no availability guard, so any machine running npm run test:e2e without the OpenSSH client (a dev box, a non-ubuntu runner) fails the whole suite rather than skipping this interop check. Detect the binary and test.skip cleanly when it is absent, e.g. spawnSync("ssh-keygen", ["-V"]) up front and skip on error, while still failing hard when present but key parsing fails.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/e2e/ssh-key-generator-interop.spec.js, line 46:
<comment>The test hard-depends on the external `ssh-keygen` binary with no availability guard, so any machine running `npm run test:e2e` without the OpenSSH client (a dev box, a non-ubuntu runner) fails the whole suite rather than skipping this interop check. Detect the binary and `test.skip` cleanly when it is absent, e.g. `spawnSync("ssh-keygen", ["-V"])` up front and skip on error, while still failing hard when present but key parsing fails.</comment>
<file context>
@@ -0,0 +1,60 @@
+ try {
+ await writeFile(keyPath, `${generated.privateKey}\n`, { mode: 0o600 });
+ await chmod(keyPath, 0o600);
+ const derived = spawnSync("ssh-keygen", ["-y", "-f", keyPath], {
+ encoding: "utf8",
+ stdio: ["ignore", "pipe", "pipe"],
</file context>
| test("Ed25519 private output is loadable and matches its displayed public key", async ({ | ||
| page, | ||
| }) => { | ||
| await page.goto("/ssh-key-generator", { waitUntil: "domcontentloaded" }); |
There was a problem hiding this comment.
P3: The test asserts Ed25519 interop but never selects the Ed25519 radio, depending on the current checked default in the route. If the default key type changes, this test silently starts validating a different key type. Click the ed25519 radio explicitly (and assert it) so the test's coverage matches its name and intent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/e2e/ssh-key-generator-interop.spec.js, line 10:
<comment>The test asserts Ed25519 interop but never selects the Ed25519 radio, depending on the current checked default in the route. If the default key type changes, this test silently starts validating a different key type. Click the ed25519 radio explicitly (and assert it) so the test's coverage matches its name and intent.</comment>
<file context>
@@ -0,0 +1,60 @@
+test("Ed25519 private output is loadable and matches its displayed public key", async ({
+ page,
+}) => {
+ await page.goto("/ssh-key-generator", { waitUntil: "domcontentloaded" });
+
+ const generated = await page.evaluate(() =>
</file context>
Summary
fix(ssh): emit interoperable Ed25519 OpenSSH keys. This is logical change 6/11 in the dependency-ordered product-audit release stack.
Refs #969
What changed
2091ca61d6a7a4ac928c3edc8072164f9727967e; validated tree:c471025df8dcc0326cde39203b8fce487aa340be.Why
Emit Ed25519 private keys that OpenSSH accepts and that match the public key shown to the user.
Verification
bun run buildpassed on this exact candidate tree.npm test -- --maxWorkers=2passed on this exact candidate tree.A real browser-generated Ed25519 key was accepted by ssh-keygen and derived the exact displayed public blob; temporary key files were removed.
Final combined tree additionally passed 801 unit tests and all 293 Playwright tests with retries disabled, plus all 48 primary tool workflows at desktop and mobile. The exploratory Color Converter exact-HEX boundary remains a documented pre-existing defect; its runtime is unchanged by this stack.
Risk & rollback
Summary by cubic
Fixes the SSH key generator so Ed25519 private keys are accepted by OpenSSH. Previously the generator exported PKCS#8 private keys that
ssh-keygenrejected; now they're encoded in the OpenSSH private key format and match the displayed public key.ssh-keygenand confirms it derives the displayed public key.Written for commit 2091ca6. Summary will update on new commits.