Fix SSH Key Password (keyPassword) Field Naming Mismatch Between Frontend and Backend
🐛 Problem
The application was experiencing authentication failures when using encrypted SSH private keys with passwords. Users would see the error:
Authentication failed: Failed to connect to host: Cannot parse privateKey:
Encrypted private OpenSSH key detected, but no passphrase given
Additionally, the "Server Metrics" feature would fail to load with "Failed to fetch server metrics" error.
Root Cause
There was a field naming inconsistency between the frontend and backend:
Frontend: Sending and expecting keyPassword (camelCase)
Backend: Expecting and returning key_password (snake_case)
Database: Column named key_password (snake_case)
This mismatch caused the SSH key password to not be passed correctly to the SSH2 client, resulting in connection failures.
✅ Solution
Implemented a comprehensive field naming standardization across the entire codebase to use camelCase (keyPassword) in the API layer while maintaining snake_case (key_password) in the database layer.
✅Fixed tab switching behavior: Removed field clearing when switching between authentication methods
✅ Form now preserves all auth field values when switching tabs (password/key/credential)
✅ Backend properly handles only submitting fields relevant to selected authType
✅ Prevents data loss when users switch between authentication tabs without saving
3. Data Flow
Frontend (keyPassword)
↓
API Routes (accepts keyPassword)
↓
Database (stores as key_password)
↓
Database Query (returns key_password)
↓
API Response (converts to keyPassword)
↓
Frontend/SSH Client (uses keyPassword as passphrase)
🐛 Additional Bug Fix
Stale Data Override Prevention
Fixed a critical bug in the resolveHostCredentials() function where a host using a credential ID could have its credential's keyPassword incorrectly overwritten by a stale key_password property from the host object itself.
Scenario:
Host originally created with inline credentials (had key_password)
Host later updated to use a credential ID
Old key_password remained in the host record
When fetching, the stale key_password would override the credential's keyPassword
Solution:
// Only use the inline key_password if keyPassword hasn't been set by credential resolution
if(result.keyPassword===undefined){result.keyPassword=host.key_password;}// Always remove the snake_case version to standardize the output
deleteresult.key_password;
This ensures credential-resolved keyPassword always takes precedence over stale inline values.
🧪 Tested Scenarios
✅ Creating new host with encrypted SSH key + password
✅ Updating existing host with encrypted SSH key + password
✅ Terminal connection with encrypted SSH key
✅ File Manager connection with encrypted SSH key
✅ Server Stats/Metrics with encrypted SSH key
✅ Hosts using credential IDs
✅ Hosts using inline credentials
✅ Hosts switching from inline to credential-based auth (stale data handling)
✅ Auto-start tunnel functionality
✅ Switching between authentication tabs (password/key/credential) without data loss
✅ Export functionality preserving keyPassword field
🔧 Technical Details
Field Conversion Strategy
API Input: Accept keyPassword (camelCase) from frontend
Database Storage: Store as key_password (snake_case) in database columns
API Output: Convert key_password → keyPassword before sending to frontend
SSH Connection: Use keyPassword as passphrase for SSH2 client
Precedence: Credential-resolved values > inline values (prevents stale data override)
Files Modified
src/backend/database/routes/ssh.ts
Line ~229: Changed parameter destructuring from key_password to keyPassword (POST)
Line ~281: Changed assignment to use keyPassword instead of key_password
Line ~410: Changed parameter destructuring from key_password to keyPassword (PUT)
Line ~467: Changed condition and assignment to use keyPassword
Line ~94: Fixed internal autostart endpoint to return keyPassword
Line ~154: Fixed internal all hosts endpoint to return keyPassword
Line ~1243-1252: Added smart conversion logic in resolveHostCredentials() to prevent stale data override
src/backend/ssh/server-stats.ts
Line ~480: Updated addLegacyCredentials() to convert key_password to keyPassword
Backward Compatibility
✅ Handles both key_password and keyPassword during reads (fallback logic)
✅ Existing encrypted data continues to work
✅ No database migration required
✅ Terminal, File Manager, and Credentials routes already had proper handling
✅ Gracefully handles stale data from configuration changes
📊 Impact
Before: Users couldn't connect to hosts with encrypted SSH keys that require a password
After: Full support for encrypted SSH keys with passwords across all features
Before: Stale inline credentials could override credential-based auth
After: Credential-based auth always takes precedence, preventing data corruption
🎯 Fixes
SSH key password field now saves correctly
Encrypted SSH keys with passwords work in Terminal
Encrypted SSH keys with passwords work in File Manager
Server Metrics now loads correctly for all hosts
Consistent API contract between frontend and backend
Stale data no longer overrides credential-resolved values
Proper precedence when hosts switch authentication methods
📸 Related Issues
Fixes: Authentication failures with encrypted SSH keys
Fixes: "Failed to fetch server metrics" error
Fixes: Stale inline credentials overriding credential-based auth
Breaking Changes: None
Rollback Plan: Revert commits in this PR if issues arise
Testing: Manual testing completed for all SSH connection scenarios including edge cases
# Fix SSH Key Password (keyPassword) Field Naming Mismatch Between Frontend and Backend
## 🐛 Problem
The application was experiencing authentication failures when using encrypted SSH private keys with passwords. Users would see the error:
```
Authentication failed: Failed to connect to host: Cannot parse privateKey:
Encrypted private OpenSSH key detected, but no passphrase given
```
Additionally, the "Server Metrics" feature would fail to load with "Failed to fetch server metrics" error.
### Root Cause
There was a field naming inconsistency between the frontend and backend:
- **Frontend**: Sending and expecting `keyPassword` (camelCase)
- **Backend**: Expecting and returning `key_password` (snake_case)
- **Database**: Column named `key_password` (snake_case)
This mismatch caused the SSH key password to not be passed correctly to the SSH2 client, resulting in connection failures.
## ✅ Solution
Implemented a comprehensive field naming standardization across the entire codebase to use **camelCase** (`keyPassword`) in the API layer while maintaining **snake_case** (`key_password`) in the database layer.
## 📝 Changes Made
### 1. **SSH Routes** (`src/backend/database/routes/ssh.ts`)
- ✅ Updated POST `/ssh/db/host` to accept `keyPassword` from frontend
- ✅ Updated PUT `/ssh/db/host/:id` to accept `keyPassword` from frontend
- ✅ Modified `resolveHostCredentials()` to convert `key_password` → `keyPassword` when returning data
- ✅ Fixed internal endpoints to return `keyPassword` instead of `key_password`
- ✅ Ensured both credential-based and inline credential hosts properly convert field names
- ✅ **Fixed critical bug**: Prevented stale `key_password` from overwriting credential-resolved `keyPassword`
- ✅ Fixed export endpoint to use `keyPassword` instead of deleted `key_password` field
### 2. **Server Stats** (`src/backend/ssh/server-stats.ts`)
- ✅ Updated `addLegacyCredentials()` to read `key_password` from database and convert to `keyPassword`
- ✅ Fixed field resolution to check `host.key_password || host.keyPassword`
### 3. **Host Manager UI** (`src/ui/Desktop/Apps/Host Manager/HostManagerEditor.tsx`)
- ✅ **Fixed tab switching behavior**: Removed field clearing when switching between authentication methods
- ✅ Form now preserves all auth field values when switching tabs (password/key/credential)
- ✅ Backend properly handles only submitting fields relevant to selected `authType`
- ✅ Prevents data loss when users switch between authentication tabs without saving
### 3. **Data Flow**
```
Frontend (keyPassword)
↓
API Routes (accepts keyPassword)
↓
Database (stores as key_password)
↓
Database Query (returns key_password)
↓
API Response (converts to keyPassword)
↓
Frontend/SSH Client (uses keyPassword as passphrase)
```
## 🐛 Additional Bug Fix
### Stale Data Override Prevention
Fixed a critical bug in the `resolveHostCredentials()` function where a host using a credential ID could have its credential's `keyPassword` incorrectly overwritten by a stale `key_password` property from the host object itself.
**Scenario:**
1. Host originally created with inline credentials (had `key_password`)
2. Host later updated to use a credential ID
3. Old `key_password` remained in the host record
4. When fetching, the stale `key_password` would override the credential's `keyPassword`
**Solution:**
```typescript
// Only use the inline key_password if keyPassword hasn't been set by credential resolution
if (result.keyPassword === undefined) {
result.keyPassword = host.key_password;
}
// Always remove the snake_case version to standardize the output
delete result.key_password;
```
This ensures credential-resolved `keyPassword` always takes precedence over stale inline values.
## 🧪 Tested Scenarios
- ✅ Creating new host with encrypted SSH key + password
- ✅ Updating existing host with encrypted SSH key + password
- ✅ Terminal connection with encrypted SSH key
- ✅ File Manager connection with encrypted SSH key
- ✅ Server Stats/Metrics with encrypted SSH key
- ✅ Hosts using credential IDs
- ✅ Hosts using inline credentials
- ✅ Hosts switching from inline to credential-based auth (stale data handling)
- ✅ Auto-start tunnel functionality
- ✅ Switching between authentication tabs (password/key/credential) without data loss
- ✅ Export functionality preserving keyPassword field
## 🔧 Technical Details
### Field Conversion Strategy
- **API Input**: Accept `keyPassword` (camelCase) from frontend
- **Database Storage**: Store as `key_password` (snake_case) in database columns
- **API Output**: Convert `key_password` → `keyPassword` before sending to frontend
- **SSH Connection**: Use `keyPassword` as passphrase for SSH2 client
- **Precedence**: Credential-resolved values > inline values (prevents stale data override)
### Files Modified
1. `src/backend/database/routes/ssh.ts`
- Line ~229: Changed parameter destructuring from `key_password` to `keyPassword` (POST)
- Line ~281: Changed assignment to use `keyPassword` instead of `key_password`
- Line ~410: Changed parameter destructuring from `key_password` to `keyPassword` (PUT)
- Line ~467: Changed condition and assignment to use `keyPassword`
- Line ~94: Fixed internal autostart endpoint to return `keyPassword`
- Line ~154: Fixed internal all hosts endpoint to return `keyPassword`
- Line ~1243-1252: Added smart conversion logic in `resolveHostCredentials()` to prevent stale data override
2. `src/backend/ssh/server-stats.ts`
- Line ~480: Updated `addLegacyCredentials()` to convert `key_password` to `keyPassword`
### Backward Compatibility
- ✅ Handles both `key_password` and `keyPassword` during reads (fallback logic)
- ✅ Existing encrypted data continues to work
- ✅ No database migration required
- ✅ Terminal, File Manager, and Credentials routes already had proper handling
- ✅ Gracefully handles stale data from configuration changes
## 📊 Impact
- **Before**: Users couldn't connect to hosts with encrypted SSH keys that require a password
- **After**: Full support for encrypted SSH keys with passwords across all features
- **Before**: Stale inline credentials could override credential-based auth
- **After**: Credential-based auth always takes precedence, preventing data corruption
## 🎯 Fixes
- SSH key password field now saves correctly
- Encrypted SSH keys with passwords work in Terminal
- Encrypted SSH keys with passwords work in File Manager
- Server Metrics now loads correctly for all hosts
- Consistent API contract between frontend and backend
- Stale data no longer overrides credential-resolved values
- Proper precedence when hosts switch authentication methods
## 📸 Related Issues
Fixes: Authentication failures with encrypted SSH keys
Fixes: "Failed to fetch server metrics" error
Fixes: Stale inline credentials overriding credential-based auth
---
**Breaking Changes**: None
**Rollback Plan**: Revert commits in this PR if issues arise
**Testing**: Manual testing completed for all SSH connection scenarios including edge cases
Hello @thorved, I'm Gemini Code Assist1 ! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request resolves a critical issue preventing the use of encrypted SSH keys with passwords and causing server metrics to fail. The core problem was a naming inconsistency for the SSH key password field between the frontend's camelCase expectation and the backend/database's snake_case usage. The solution standardizes the API contract to use 'keyPassword' for all interactions, implementing robust conversion logic to seamlessly translate between the API and database formats. This ensures that all SSH-related features now correctly handle encrypted keys, restoring full functionality and consistency.
Highlights
Problem Identification: The application experienced authentication failures with encrypted SSH private keys and 'Server Metrics' failing to load due to a field naming mismatch: frontend expected 'keyPassword' (camelCase), while the backend/database used 'key_password' (snake_case).
Solution Implemented: A comprehensive standardization was implemented to use 'keyPassword' (camelCase) in the API layer for both input and output, with conversion logic to store and retrieve the value as 'key_password' (snake_case) in the database.
Key Changes: Updates were made across SSH routes (POST/PUT) to accept 'keyPassword', modifications to 'resolveHostCredentials()' to convert 'key_password' to 'keyPassword' for API responses, and adjustments in 'addLegacyCredentials()' for server stats to correctly handle the field.
Extensive Testing: The fix was thoroughly tested across various scenarios including creating/updating hosts with encrypted keys, terminal/file manager connections, server stats, credential IDs, inline credentials, and auto-start tunnels, ensuring full functionality.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.
Feature
Command
Description
Code Review
/gemini review
Performs a code review for the current pull request in its current state.
Pull Request Summary
/gemini summary
Provides a summary of the current pull request in its current state.
Comment
@gemini-code-assist
Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help
/gemini help
Displays a list of available commands.
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
## Summary of Changes
Hello @thorved, I'm Gemini Code Assist[^1]! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request resolves a critical issue preventing the use of encrypted SSH keys with passwords and causing server metrics to fail. The core problem was a naming inconsistency for the SSH key password field between the frontend's camelCase expectation and the backend/database's snake_case usage. The solution standardizes the API contract to use 'keyPassword' for all interactions, implementing robust conversion logic to seamlessly translate between the API and database formats. This ensures that all SSH-related features now correctly handle encrypted keys, restoring full functionality and consistency.
### Highlights
* **Problem Identification**: The application experienced authentication failures with encrypted SSH private keys and 'Server Metrics' failing to load due to a field naming mismatch: frontend expected 'keyPassword' (camelCase), while the backend/database used 'key_password' (snake_case).
* **Solution Implemented**: A comprehensive standardization was implemented to use 'keyPassword' (camelCase) in the API layer for both input and output, with conversion logic to store and retrieve the value as 'key_password' (snake_case) in the database.
* **Key Changes**: Updates were made across SSH routes (POST/PUT) to accept 'keyPassword', modifications to 'resolveHostCredentials()' to convert 'key_password' to 'keyPassword' for API responses, and adjustments in 'addLegacyCredentials()' for server stats to correctly handle the field.
* **Extensive Testing**: The fix was thoroughly tested across various scenarios including creating/updating hosts with encrypted keys, terminal/file manager connections, server stats, credential IDs, inline credentials, and auto-start tunnels, ensuring full functionality.
<details>
<summary><b>Using Gemini Code Assist</b></summary>
<br>
The full guide for Gemini Code Assist can be found on our [documentation page](https://developers.google.com/gemini-code-assist/docs/review-github-code), here are some quick tips.
<b>Invoking Gemini</b>
You can request assistance from Gemini at any point by creating a comment using either `/gemini <command>` or `@gemini-code-assist <command>`. Below is a summary of the supported commands on the current page.
Feature | Command | Description
--- | --- | ---
Code Review | `/gemini review` | Performs a code review for the current pull request in its current state.
Pull Request Summary | `/gemini summary` | Provides a summary of the current pull request in its current state.
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help | `/gemini help` | Displays a list of available commands.
<b>Customization</b>
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a `.gemini/` folder in the base of the repository. Detailed instructions can be found [here](https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github).
<b>Limitations & Feedback</b>
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with :thumbsup: and :thumbsdown: on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up [here](https://google.qualtrics.com/jfe/form/SV_2cyuGuTWsEw84yG).
<b>You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the [Gemini Code Assist IDE Extension](https://cloud.google.com/products/gemini/code-assist).</b>
</details>
[^1]: Review the [Privacy Notices](https://policies.google.com/privacy), [Generative AI Prohibited Use Policy](https://policies.google.com/terms/generative-ai/use-policy), [Terms of Service](https://policies.google.com/terms), and learn how to configure Gemini Code Assist in GitHub [here](https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github). Gemini can make mistakes, so double check it and [use code with caution](https://support.google.com/legal/answer/13505487).
gemini-code-assist[bot]
(Migrated from github.com)
reviewed 2025-10-08 07:33:05 +11:00
gemini-code-assist[bot]
(Migrated from github.com)
left a comment
Copy Link
Copy Source
Code Review
This is a great pull request that addresses a critical bug with SSH key passwords. The description is very clear and detailed, which is much appreciated. The changes to standardize on keyPassword at the API layer while keeping key_password at the DB layer are well-implemented across the board. I've found one potential issue in the credential resolution logic, which I've detailed in a specific comment. Otherwise, the changes look solid.
## Code Review
This is a great pull request that addresses a critical bug with SSH key passwords. The description is very clear and detailed, which is much appreciated. The changes to standardize on `keyPassword` at the API layer while keeping `key_password` at the DB layer are well-implemented across the board. I've found one potential issue in the credential resolution logic, which I've detailed in a specific comment. Otherwise, the changes look solid.
There's a potential bug in this conversion logic. If a host object uses a credentialId but also has a stale key_password property from a previous configuration, this logic will incorrectly overwrite the keyPassword from the credential with the stale key_password from the host object.
This happens because the credential resolution logic merges credential properties into the host object, but the original host.key_password is preserved. This block then unconditionally uses host.key_password if it exists.
To fix this, we should only perform the conversion from key_password to keyPassword if keyPassword hasn't already been set by the credential resolution logic.
constresult={...host};if(host.key_password!==undefined){// If keyPassword is not already set by credential resolution, use the inline one.
if(result.keyPassword===undefined){result.keyPassword=host.key_password;}// Always remove the snake_case version to standardize the output.
deleteresult.key_password;}returnresult;

There's a potential bug in this conversion logic. If a `host` object uses a `credentialId` but also has a stale `key_password` property from a previous configuration, this logic will incorrectly overwrite the `keyPassword` from the credential with the stale `key_password` from the host object.
This happens because the credential resolution logic merges credential properties into the `host` object, but the original `host.key_password` is preserved. This block then unconditionally uses `host.key_password` if it exists.
To fix this, we should only perform the conversion from `key_password` to `keyPassword` if `keyPassword` hasn't already been set by the credential resolution logic.
```typescript
const result = { ...host };
if (host.key_password !== undefined) {
// If keyPassword is not already set by credential resolution, use the inline one.
if (result.keyPassword === undefined) {
result.keyPassword = host.key_password;
}
// Always remove the snake_case version to standardize the output.
delete result.key_password;
}
return result;
```
thorved
(Migrated from github.com)
reviewed 2025-10-08 07:51:17 +11:00
This document summarizes the small but critical fix that prevents stale inline key_password values from overwriting credential-resolved keyPassword values when resolving SSH host credentials.
constresult={...host};if(host.key_password!==undefined){// Only use the inline key_password if keyPassword hasn't been set by credential resolution
if(result.keyPassword===undefined){result.keyPassword=host.key_password;}// Always remove the snake_case version to standardize the output
deleteresult.key_password;}
Why this matters
Hosts can be migrated from inline credentials to credential-backed configurations. If the old key_password remains on the host row, it must not overwrite the keyPassword supplied by the referenced credential.
The fix correctly:
Checks if keyPassword is already set by credential resolution (line 1245 in src/backend/database/routes/ssh.ts).
Only uses host.key_password if keyPassword is undefined (line 1246).
Always removes the snake_case version (key_password) to maintain a clean, camelCase API contract (line 1249).
Result: This prevents stale inline credentials from overwriting credential-resolved values and ensures proper precedence when building the API response.
# Key Password Precedence Fix
This document summarizes the small but critical fix that prevents stale inline `key_password` values from overwriting credential-resolved `keyPassword` values when resolving SSH host credentials.
```typescript
const result = { ...host };
if (host.key_password !== undefined) {
// Only use the inline key_password if keyPassword hasn't been set by credential resolution
if (result.keyPassword === undefined) {
result.keyPassword = host.key_password;
}
// Always remove the snake_case version to standardize the output
delete result.key_password;
}
```
Why this matters
- Hosts can be migrated from inline credentials to credential-backed configurations. If the old `key_password` remains on the host row, it must not overwrite the `keyPassword` supplied by the referenced credential.
The fix correctly:
1. Checks if `keyPassword` is already set by credential resolution (line 1245 in `src/backend/database/routes/ssh.ts`).
2. Only uses `host.key_password` if `keyPassword` is `undefined` (line 1246).
3. Always removes the snake_case version (`key_password`) to maintain a clean, camelCase API contract (line 1249).
Result: This prevents stale inline credentials from overwriting credential-resolved values and ensures proper precedence when building the API response.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fix SSH Key Password (keyPassword) Field Naming Mismatch Between Frontend and Backend
🐛 Problem
The application was experiencing authentication failures when using encrypted SSH private keys with passwords. Users would see the error:
Additionally, the "Server Metrics" feature would fail to load with "Failed to fetch server metrics" error.
Root Cause
There was a field naming inconsistency between the frontend and backend:
keyPassword(camelCase)key_password(snake_case)key_password(snake_case)This mismatch caused the SSH key password to not be passed correctly to the SSH2 client, resulting in connection failures.
✅ Solution
Implemented a comprehensive field naming standardization across the entire codebase to use camelCase (
keyPassword) in the API layer while maintaining snake_case (key_password) in the database layer.📝 Changes Made
1. SSH Routes (
src/backend/database/routes/ssh.ts)/ssh/db/hostto acceptkeyPasswordfrom frontend/ssh/db/host/:idto acceptkeyPasswordfrom frontendresolveHostCredentials()to convertkey_password→keyPasswordwhen returning datakeyPasswordinstead ofkey_passwordkey_passwordfrom overwriting credential-resolvedkeyPasswordkeyPasswordinstead of deletedkey_passwordfield2. Server Stats (
src/backend/ssh/server-stats.ts)addLegacyCredentials()to readkey_passwordfrom database and convert tokeyPasswordhost.key_password || host.keyPassword3. Host Manager UI (
src/ui/Desktop/Apps/Host Manager/HostManagerEditor.tsx)authType3. Data Flow
🐛 Additional Bug Fix
Stale Data Override Prevention
Fixed a critical bug in the
resolveHostCredentials()function where a host using a credential ID could have its credential'skeyPasswordincorrectly overwritten by a stalekey_passwordproperty from the host object itself.Scenario:
key_password)key_passwordremained in the host recordkey_passwordwould override the credential'skeyPasswordSolution:
This ensures credential-resolved
keyPasswordalways takes precedence over stale inline values.🧪 Tested Scenarios
🔧 Technical Details
Field Conversion Strategy
keyPassword(camelCase) from frontendkey_password(snake_case) in database columnskey_password→keyPasswordbefore sending to frontendkeyPasswordas passphrase for SSH2 clientFiles Modified
src/backend/database/routes/ssh.tskey_passwordtokeyPassword(POST)keyPasswordinstead ofkey_passwordkey_passwordtokeyPassword(PUT)keyPasswordkeyPasswordkeyPasswordresolveHostCredentials()to prevent stale data overridesrc/backend/ssh/server-stats.tsaddLegacyCredentials()to convertkey_passwordtokeyPasswordBackward Compatibility
key_passwordandkeyPasswordduring reads (fallback logic)📊 Impact
🎯 Fixes
📸 Related Issues
Fixes: Authentication failures with encrypted SSH keys
Fixes: "Failed to fetch server metrics" error
Fixes: Stale inline credentials overriding credential-based auth
Breaking Changes: None
Rollback Plan: Revert commits in this PR if issues arise
Testing: Manual testing completed for all SSH connection scenarios including edge cases
Summary of Changes
Hello @thorved, I'm Gemini Code Assist1 ! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request resolves a critical issue preventing the use of encrypted SSH keys with passwords and causing server metrics to fail. The core problem was a naming inconsistency for the SSH key password field between the frontend's camelCase expectation and the backend/database's snake_case usage. The solution standardizes the API contract to use 'keyPassword' for all interactions, implementing robust conversion logic to seamlessly translate between the API and database formats. This ensures that all SSH-related features now correctly handle encrypted keys, restoring full functionality and consistency.
Highlights
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point by creating a comment using either
/gemini <command>or@gemini-code-assist <command>. Below is a summary of the supported commands on the current page./gemini review/gemini summary/gemini helpCustomization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a
.gemini/folder in the base of the repository. Detailed instructions can be found here.Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩︎
Code Review
This is a great pull request that addresses a critical bug with SSH key passwords. The description is very clear and detailed, which is much appreciated. The changes to standardize on
keyPasswordat the API layer while keepingkey_passwordat the DB layer are well-implemented across the board. I've found one potential issue in the credential resolution logic, which I've detailed in a specific comment. Otherwise, the changes look solid.@@ -1234,12 +1234,27 @@ async function resolveHostCredentials(host: any): Promise<any> {authType: credential.auth_type || credential.authType,There's a potential bug in this conversion logic. If a
hostobject uses acredentialIdbut also has a stalekey_passwordproperty from a previous configuration, this logic will incorrectly overwrite thekeyPasswordfrom the credential with the stalekey_passwordfrom the host object.This happens because the credential resolution logic merges credential properties into the
hostobject, but the originalhost.key_passwordis preserved. This block then unconditionally useshost.key_passwordif it exists.To fix this, we should only perform the conversion from
key_passwordtokeyPasswordifkeyPasswordhasn't already been set by the credential resolution logic.@@ -1234,12 +1234,27 @@ async function resolveHostCredentials(host: any): Promise<any> {authType: credential.auth_type || credential.authType,Key Password Precedence Fix
This document summarizes the small but critical fix that prevents stale inline
key_passwordvalues from overwriting credential-resolvedkeyPasswordvalues when resolving SSH host credentials.Why this matters
key_passwordremains on the host row, it must not overwrite thekeyPasswordsupplied by the referenced credential.The fix correctly:
keyPasswordis already set by credential resolution (line 1245 insrc/backend/database/routes/ssh.ts).host.key_passwordifkeyPasswordisundefined(line 1246).key_password) to maintain a clean, camelCase API contract (line 1249).Result: This prevents stale inline credentials from overwriting credential-resolved values and ensures proper precedence when building the API response.