SSH Known Hosts Management in Tabby: Implementation and Configuration Guide
Tabby implements SSH known hosts management through an Angular injectable service that persists host-key fingerprints in the user configuration file and triggers interactive prompts when server keys are unknown or mismatched.
Tabby, the open-source terminal emulator by Eugeny, provides built-in SSH known hosts management to prevent man-in-the-middle attacks during remote connections. The implementation stores verified host-key digests in the application's config store and automatically validates subsequent connections against these fingerprints. This guide examines the source code architecture behind Tabby's SSH security model, covering configuration storage, verification workflows, and programmatic APIs.
Configuration Storage Structure
Tabby defines the default SSH configuration structure in tabby-ssh/src/config.ts within the SSHConfigProvider class. The implementation uses two critical properties for host key management:
knownHosts: An array that stores objects containing host identifiers and their corresponding key fingerprintsverifyHostKeys: A boolean flag that globally enables or disables host key verification
// https://github.com/Eugeny/tabby/blob/master/tabby-ssh/src/config.ts#L12-L14
knownHosts: [],
verifyHostKeys: true,
When persisted to disk, these values are written to the user's configuration file (typically located at ~/.config/Tabby/config.json on Linux systems). The knownHosts array maintains the trust anchor for all previously accepted SSH servers.
The SSHKnownHostsService API
The core logic for reading and writing known host entries resides in tabby-ssh/src/services/sshKnownHosts.service.ts. The SSHKnownHostsService is an injectable singleton that operates directly on config.store.ssh.knownHosts and provides a type-safe interface for host key retrieval and persistence.
The service exposes two primary methods:
getFor(selector: KnownHostSelector): Retrieves a stored host entry matching the provided host, port, and algorithm type. Returnsundefinedif no matching entry exists.store(selector: KnownHostSelector, digest: string): Updates an existing entry or appends a new one to the configuration array, then persists changes viaconfig.save().
// https://github.com/Eugeny/tabby/blob/master/tabby-ssh/src/services/sshKnownHosts.service.ts#L20-L31
getFor (selector) { … }
async store (selector, digest) { … }
The KnownHostSelector interface requires three properties: host (string), port (number), and type (string representing the algorithm name such as 'ssh-rsa' or 'ssh-ed25519').
Host Key Verification Flow
When establishing an SSH connection, SSHSession.verifyHostKey() in tabby-ssh/src/session/ssh.ts validates each host key presented by the server. The method implements the following security checks:
- Digest Computation: Calculates a SHA-256 fingerprint of the raw key bytes received from the server
- Global Bypass: If
verifyHostKeysis set tofalse, verification is skipped entirely - Selector Construction: Creates a selector object containing the target host, port, and key algorithm
- Lookup: Queries
SSHKnownHostsService.getFor()to find a matching stored entry
// https://github.com/Eugeny/tabby/blob/master/tabby-ssh/src/session/ssh.ts#L55-L71
const selector = { host, port, type: key.algorithm() }
const knownHost = this.knownHosts.getFor(selector)
If the lookup returns no result, or if the stored digest differs from the computed fingerprint, the session triggers the user interface prompt to resolve the discrepancy.
User Interaction and Key Prompting
When verification fails or finds no existing entry, Tabby instantiates HostKeyPromptModalComponent from tabby-ssh/src/components/hostKeyPromptModal.component.ts. This modal displays the new host key fingerprint and provides options to accept temporarily or permanently trust the key.
The modal component implements acceptAndSave(), which persists the new fingerprint to configuration:
// https://github.com/Eugeny/tabby/blob/master/tabby-ssh/src/components/hostKeyPromptModal.component.ts#L31-L38
async acceptAndSave () {
await this.knownHosts.store(this.selector, this.digest)
this.accept()
}
Once the user confirms acceptance, the fingerprint is permanently stored in the configuration file, enabling automatic verification for all future connections to that specific host, port, and algorithm combination.
Programmatic Access Examples
You can interact with Tabby's SSH known hosts management system programmatically through the service APIs.
Retrieve a Known Host Entry
import { SSHKnownHostsService, KnownHostSelector } from 'tabby-ssh/src/services/sshKnownHosts.service';
import { Injector } from '@angular/core';
const injector = Injector.create({providers: [{provide: SSHKnownHostsService, useClass: SSHKnownHostsService, deps: []}]});
const knownHosts = injector.get(SSHKnownHostsService);
const selector: KnownHostSelector = {
host: 'example.com',
port: 22,
type: 'ssh-rsa',
};
const entry = knownHosts.getFor(selector);
if (entry) {
console.log(`Stored fingerprint: ${entry.digest}`);
} else {
console.log('Host not known yet');
}
Add or Update a Host Entry
import { SSHKnownHostsService, KnownHostSelector } from 'tabby-ssh/src/services/sshKnownHosts.service';
import { Injector } from '@angular/core';
const injector = Injector.create({providers: [{provide: SSHKnownHostsService, useClass: SSHKnownHostsService, deps: []}]});
const knownHosts = injector.get(SSHKnownHostsService);
const selector: KnownHostSelector = {
host: 'my.server',
port: 2222,
type: 'ssh-ed25519',
};
const fingerprint = 'SHA256:abcdef...';
await knownHosts.store(selector, fingerprint);
console.log('Host key stored');
Disable Host Key Verification Globally
Edit the configuration file directly:
{
"ssh": {
"verifyHostKeys": false
}
}
Or modify the setting programmatically:
import { ConfigService } from 'tabby-core';
const config = injector.get(ConfigService);
config.store.ssh.verifyHostKeys = false;
config.save();
Summary
- Storage Location: Host fingerprints are stored in the
ssh.knownHostsarray within Tabby's user configuration file, defined intabby-ssh/src/config.ts. - Service Layer:
SSHKnownHostsServiceprovides the primary API for lookups (getFor) and persistence (store), operating directly on the config store. - Verification Logic:
SSHSession.verifyHostKey()intabby-ssh/src/session/ssh.tscomputes SHA-256 digests and validates against stored entries, respecting theverifyHostKeysboolean flag. - User Interface:
HostKeyPromptModalComponenthandles unknown or changed keys, callingstore()to persist user-approved fingerprints viaacceptAndSave(). - Security Model: The implementation prevents man-in-the-middle attacks by maintaining a persistent trust-on-first-use (TOFU) database with interactive confirmation for new or modified host keys.
Frequently Asked Questions
Where does Tabby store SSH known host fingerprints?
Tabby stores SSH host-key fingerprints in the user configuration file, typically located at ~/.config/Tabby/config.json on Linux systems or equivalent paths on other platforms. The fingerprints reside in the ssh.knownHosts array as objects containing host, port, algorithm type, and digest properties, as defined in tabby-ssh/src/config.ts.
How do I disable host key verification in Tabby?
Set the verifyHostKeys property to false in the SSH configuration section. You can edit the configuration JSON file directly or access config.store.ssh.verifyHostKeys through the ConfigService API in tabby-core. When disabled, SSHSession.verifyHostKey() bypasses all fingerprint validation checks.
What hashing algorithm does Tabby use for host key verification?
Tabby uses SHA-256 to compute digests of raw host key bytes during the verification process in SSHSession.verifyHostKey(). The resulting fingerprint is compared against previously stored entries in the knownHosts array, with mismatches triggering the modal prompt for user confirmation.
Can extensions programmatically manage known hosts in Tabby?
Yes, extensions can inject SSHKnownHostsService from tabby-ssh/src/services/sshKnownHosts.service.ts to programmatically retrieve entries via getFor(selector) or persist new fingerprints using store(selector, digest). Both methods accept a KnownHostSelector object specifying the host, port, and algorithm type, enabling automated host key management for specialized workflows.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →