# ssh-connection-manager: a TUI for hosts in ~/.ssh/config

LLMS index: [llms.txt](/en/llms.txt)

---

When `~/.ssh/config` holds dozens of stands, bastions, and jump hosts, memorizing aliases stops being fun. **ssh-connection-manager** is a TUI on top of ordinary OpenSSH: a host list, a filter, a connect via the system `ssh`, and a way to append a new block to the config.

The CLI command is `ssh-connect`. Repository: [gitlab.com/unsorted-projects/ssh-connection-manager](https://gitlab.com/public-projects-docff-devops/ssh-connection-manager).

## Why not another SSH client

The client already exists: the system `ssh`. What is missing is navigation over the config file.

The tool:

- reads `~/.ssh/config` or another file (`-c`);
- lists concrete `Host` entries, not wildcards such as `Host *`;
- follows `Include` (up to 16 levels);
- on `Enter` suspends the TUI and runs `ssh -F <config> <alias>`;
- after the session ends, shows the list again.

It is Python 3.11+ and [Textual](https://textual.textualize.io/). There is no custom SSH stack: keys, `ProxyJump`, and the agent stay with OpenSSH.

## What the table shows

Column names keep the alias and the address apart:

| Field | Meaning |
| --- | --- |
| HostName | alias from the `Host` directive |
| ConnectPoint | address from `HostName` in the config (IP or DNS) |
| User | user, if set |
| Port | port, or `22` by default |

The details line shows `IdentityFile` and `ProxyJump` when present. If a host has no `User` (and none comes from `Host *`), the TUI asks for a username before connecting.

The filter (`/`) matches alias and ConnectPoint.

## Keys

| Key | Action |
| --- | --- |
| `Enter` | connect |
| `a` | add a host to the config |
| `/` | filter |
| `q` | quit |

Adding a host **appends** a block; existing sections are not rewritten. If the file does not exist yet, it is created with mode `0600`. Wildcard characters are rejected in the alias: only explicit names appear in the list.

Example of what gets written:

```sshconfig
Host prod
    HostName 10.0.0.5
    User deploy
    Port 22
    IdentityFile ~/.ssh/id_ed25519
```

## Install

On Debian/Ubuntu do not install into the system Python (`externally-managed-environment`). Use a venv or pipx.

```bash
git clone git@gitlab.com:unsorted-projects/ssh-connection-manager.git
cd ssh-connection-manager
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
```

Then `ssh-connect` is available in the activated venv. To run it from any directory:

```bash
mkdir -p ~/.local/bin
ln -sf "$(pwd)/.venv/bin/ssh-connect" ~/.local/bin/ssh-connect
```

Or `pipx install -e .` — pipx isolates the environment and puts the binary in `~/.local/bin`.

An interactive TTY is required: without a terminal, Textual cannot hand the screen over to `ssh`.

```bash
ssh-connect
ssh-connect -c /path/to/other/config
```

> [!NOTE]
> The tool does not rewrite other config blocks and ignores `Match`. Only named `Host` entries without `*?[` appear in the list.

## Why this fits the job

One config file stays the source of truth. The TUI does not duplicate inventory in YAML and does not store passwords: only what already lives in OpenSSH. For a Lead DevOps that is the usual loop: bastion, prod, jump — pick a row and you are in the session.
