The Problem
Developers and operators need a single, cross‑platform terminal that can host SSH, serial, and Telnet sessions, keep tabs, and support split panes. Existing tools are either platform‑locked (e.g., Windows Terminal) or require multiple binaries (PuTTY + screen). Maintaining separate clients adds configuration drift and hampers automation.
What This Does
tabby bundles a modern terminal emulator with built‑in SSH, serial, and Telnet clients. The core UI lives in tabby-core, while platform‑specific code is in tabby-electron (desktop shell) and tabby-web (self‑hosted web app). Themes and color schemes are provided by tabby-community-color-schemes.
Key entry points:
app/lib/app.ts– constructs the main Electron application, registers global hotkeys, and creates windows.tabby-core/src/cli.ts– parses CLI flags and dispatches commands (e.g.,new-window,list-profiles).tabby-core/src/services/commands.service.ts– implements the command registry used by the CLI and UI.
Configuration is loaded via app/lib/config.ts, which merges user settings with defaults from tabby-core/src/services/config.service.ts. SSH sessions are created by tabby-ssh/src/session/ssh.ts, which also performs the random‑bytes generation for channel IDs.
How It Is Wired
Execution starts in app/lib/app.ts (init at line 111). The flow is:
initreads the config file (retrieveFile→fs.readFile).- It creates the main window (
newWindow) which loads the Angular‑style UI bundle built by Webpack (webpack.config.mjs). - UI components subscribe to the
subscribeutility (used in 28 places) to react to events such asgetAllTabs(22 callers) andshowMessageBox(19 callers). - When a user opens an SSH profile,
tabby-core/src/services/profiles.service.tsresolves the profile and hands it totabby-ssh/src/session/ssh.ts. That module callscrypto.randomBytes(16).toString(cryptographic operation) to generate a channel ID, then opens the SSH channel viaopenShellChannel. - Data from the remote host flows back through
tabby-terminal/src/api/baseTerminalTab.component.ts, which exposesoutput$andbinaryOutput$streams consumed by the UI renderer.
The most connected modules are tabby-core/src/services/config.service (16 inbound, 6 outbound imports) and tabby-core/src/components/baseTab.component (17 inbound, 3 outbound). Both sit in a circular import cycle, meaning a change to either can trigger wide recompilation and potential runtime surprises. Deep nesting (up to 9 levels) appears in webpack.plugin.config.mjs, config.service.ts, and ssh.ts, making the control flow hard to follow.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/tabby
cd tabby
# Install dependencies (yarn is the lockfile manager)
yarn install # installs workspace packages
yarn workspace app build # builds the Electron app
yarn workspace app start # runs the desktop client
Configuration files live under app/lib/config.ts and are merged with user settings in $HOME/.config/tabby/config.json. No additional environment variables are required beyond the optional .env (which currently contains a secret and must be removed).
Real‑World Use
A CI/CD pipeline can embed Tabby as a headless SSH client to run remote commands on heterogeneous hosts. Example:
import { ProfilesService } from 'tabby-core/src/services/profiles.service';
import { SSHSession } from 'tabby-ssh/src/session/ssh';
async function runRemote(host: string, cmd: string) {
const profile = await ProfilesService.instance.getProfileByName('default');
const sess = new SSHSession(profile);
await sess.connect();
await sess.exec(cmd);
}
The call chain traverses ProfilesService → SSHSession → openShellChannel → crypto.randomBytes, touching only a handful of modules, which simplifies tracing and debugging.
Code Health & Issues
- High – Pin GitHub Actions –
.github/workflows/*.ymlusesmarvinpinto/action-automatic-releases@latest. Pin to a commit SHA to avoid supply‑chain risk. - High – Committed
.env– File contains live credentials; remove from history, add to.gitignore, and provide a template (.env.example). - High – Eval over runtime value –
app/lib/app.tsexecutesexec()on a computed string; replace with safe parsing. - High – No test suite – 322 source files lack tests; add unit tests for each public entry point and integrate them into CI.
- Medium – Least‑privilege GITHUB_TOKEN – Workflows lack explicit
permissions; declarecontents: readat minimum. - Medium – Dependency‑vulnerability gate – Add
dependency-review-actionorosv-scannerto CI. - Medium – Large binaries –
tabby-web-demo/data/v86state.bin(23 MB) should be stored in Git LFS or fetched at build time. - Medium – Generated output in VCS – Remove the
build/directory from the repo and ignore it. - Medium – Persist‑credentials false – Set
persist-credentials: falseon the checkout step to avoid exposing the token to post‑install scripts.
The Bottom Line
tabby delivers a feature‑rich, cross‑platform terminal with integrated SSH/serial/Telnet support, but the monorepo’s circular imports, deep nesting, and several high‑severity health issues make it risky for production use without remediation. It is best suited for teams willing to invest in refactoring and adding proper tests, or for developers who need a quick‑start UI for remote session management.