Przejdź do treści
Biblioteka skilli
DevOps i chmura · Automatyzacja

ssh-config

/ssh-config

Edytuje konfigurację SSH: dodaje, zmienia i przenosi hosty, wiąże klucze i porządkuje pliki config oraz multipleksowanie połączeń.

Licencja
MIT
Ostatnia zmiana repozytorium
27.10.2026

Ocena i bezpieczeństwo

100/ 100Klasa: dobry
Format25 / 25
Frontmatter z nazwą i opisem, poprawna nazwa, opis do 1024 znaków, nazwa zgodna z folderem.
Odkrywalność25 / 25
Opis mówi, kiedy użyć skilla, ma sensowną długość i jest w 3. osobie.
Treść35 / 35
Co najmniej 150 słów i do 500 linii, nagłówki, listy kroków, przykłady albo kod.
Higiena15 / 15
Bez zaślepek typu TODO, bez ścieżek Windows, z licencją.
Ryzykobrak sygnałów w skanie
Pliki w paczce
3
Skrypty
2
Komentarze HTML
0
Adresy URL w SKILL.md
0
Przestarzałość

Brak sygnałów. Repozytorium zmienione 27.10.2026, bez odwołań do wycofanych modeli i API.

Licencja

MIT, otwarta: można używać i zmieniać, z zachowaniem informacji o autorze.

Co sprawdzić przed instalacją
  • 2 skrypty w paczce. Przejrzyj je przed instalacją, bo Claude może je uruchomić.

Skrypty: scripts/sshcfg.py, tests/test_sshcfg.py

Co robi i kiedy

Edytuje konfigurację SSH: dodaje, zmienia i przenosi hosty, wiąże klucze i porządkuje pliki config oraz multipleksowanie połączeń.

Wyzwalacze z opisu

make ssh fasterkeep connection alivereuse the connection

Argumenty

/ssh-config <add|delete|move|sort|list|new-site|speedup> [args]
Oryginalny opis (EN)

Use when someone asks to update/edit/add/delete/move an SSH host or site, wire up an SSH key (identityfile) for a host, modify ~/.ssh/config or ~/.ssh/config.d, manage SSH sites (cypress, df-austin, ed-house, brian), sort SSH config files, set up SSH access to a new machine, **speed up SSH connections** (ControlMaster / multiplexing / "make ssh faster" / "keep connection alive" / "reuse the connection"), or invokes /ssh-config.

Jak działa

  1. Layout
  2. The helper: scripts/sshcfg.py
  3. Arguments
  4. Conventions
  5. Workflow
  6. Common scenarios
  7. Guardrails
  8. What NOT to do
Sekcje z nagłówków SKILL.md, w oryginale · 349 linii, 2 845 słów
Podgląd SKILL.md
---
name: ssh-config
description: Use when someone asks to update/edit/add/delete/move an SSH host or site, wire up an SSH key (identityfile) for a host, modify ~/.ssh/config or ~/.ssh/config.d, manage SSH sites (cypress, df-austin, ed-house, brian), sort SSH config files, set up SSH access to a new machine, **speed up SSH connections** (ControlMaster / multiplexing / "make ssh faster" / "keep connection alive" / "reuse the connection"), or invokes /ssh-config.
argument-hint: <add|delete|move|sort|list|new-site|speedup> [args]
---

# ssh-config

Manages per-site SSH config files in `~/.ssh/config.d/`. Each file is one **site** (a physical location or logical grouping). Each `Host` block within a file is one **node**.

Adds nodes, removes nodes, moves nodes between sites, and keeps host blocks alphabetical within each site — while preserving the ASCII banner header at the top of each file.

## Layout

- Each file `~/.ssh/config.d/<site>` holds the nodes for one site.
- The main `~/.ssh/config` already does `Include ~/.ssh/config.d/*`, so **every file in `config.d/` is live configuration.** A backup, an editor copy or a temp file there applies its Host blocks too, and options such as `LocalForward` accumulate across matching blocks. Nothing but site files belongs in `config.d/`.
- Backups and staged writes live beside it, in `~/.ssh/config.d-backups/`, outside the glob.
- Banner header at the top of a file (lines starting with `#`, often ASCII art) is **preserved verbatim** on any sort or rewrite.
- Host blocks start with `Host <name>` (column 0), followed by indented option lines until the next `Host` line or EOF.

### Known sites

| File | Description |
|---|---|
| `cypress` | Local home (Travis's house). No banner. |
| `df-austin` | Izuma Data Foundry site in Austin. ASCII banner: "the site in austin". |
| `ed-house` | Ed's house / Ed's garage. ASCII banner: "ya' know, ed's grarrage". |
| `brian` | Brian's house. Banner to be created on first `new-site brian`. |

### Files the skill never edits

The skill never edits, moves or deletes these, but ssh still loads every one of them:

- Zero-byte files (`1`, `octa`) — they load nothing.
- `gke`, `incus-uc-vms` — specialized non-site groupings; touch them only when the user names them.
- Backup, editor, temp and sync copies: `*.bak`, `*.bak.*`, `*~`, `*.tmp`, `*.swp`, `*.orig`, `*.sync-conflict-*`, and `old`. These load as live config, so `verify` flags each one and the report lists them with the command that moves it out (`mv ~/.ssh/config.d/<file> ~/.ssh/config.d-backups/`). Moving them is the user's call.

When in doubt about whether a filename is a site, ask once.

## The helper: `scripts/sshcfg.py`

Every write, backup, check and duplicate report goes through the helper in this skill's directory (`H="python3 <skill dir>/scripts/sshcfg.py"`). It reads `~/.ssh/config` unless `--config <path>` names another, and flags parse in any position.

| Command | Does |
|---|---|
| `$H tempfile <site>` | Creates an empty staging file in `~/.ssh/config.d-backups/` and prints its path. Build the new site file there, never inside `config.d/`. |
| `$H write <site> <staged file> --verify <host>` | Backs up the current site file to `~/.ssh/config.d-backups/<site>.<UTC>` (newest 20 kept per site), installs the staged file atomically, then runs `ssh -G <host>`. If ssh cannot parse the result it restores the backup and exits 3. |
| `$H verify <host>` | Runs `ssh -G -vvv <host>`, names every file that applied options to it, and lists every non-empty file the glob loads that is a backup, editor or temp copy. Exit 0 clean, 1 flagged files, 3 ssh cannot parse. |
| `$H dupes [<site>]` | Lists Host names defined twice within a site (with line numbers) and names defined in more than one site, on separate `DUPLICATE` and `CROSS-SITE` lines. |
| `$H restore <site> [<backup>]` | Reinstalls the newest (or the named) backup. |
| `$H migrate` | Moves `*.bak.skill` files left in `config.d/` into `config.d-backups/`. |
| `$H sites` | Lists the live site files. |

## Arguments

The first argument is a subcommand. Subsequent arguments are subcommand-specific.

| Subcommand | Form | Purpose |
|---|---|---|
| `add` | `/ssh-config add <site> <host-name>` | Add a new `Host` block to a site file |
| `delete` | `/ssh-config delete <site> <host-name>` | Remove a `Host` block from a site file |
| `move` | `/ssh-config move <host-name> <from-site> <to-site>` | Move a node between sites |
| `sort` | `/ssh-config sort [site]` | Alphabetize host blocks within a site (or all sites if omitted) |
| `list` | `/ssh-config list [site]` | Print host names in a site (or every site if omitted) |
| `new-site` | `/ssh-config new-site <name> [description]` | Create a new empty site file with a banner |
| `speedup` | `/ssh-config speedup <host-name> [<host-name> ...]` | Add SSH ControlMaster multiplexing to one or more existing host blocks (first connection opens a socket, subsequent reuse it = near-instant) |

If `$ARGUMENTS` is empty or the subcommand is unknown, print a usage line listing the subcommands above and exit.

## Conventions

**Indentation:** 4 spaces inside a Host block. Option names are lowercase (`hostname`, `user`, `port`, `identityfile`, `proxyjump`, `localforward`, etc.).

**Identity files:** Use the **path to the public key** (`~/.ssh/to_xxx.pub` or absolute) so the agent can match against the corresponding private key. If the user gives only a private key path, suggest its `.pub` sibling and confirm.

**Jump hosts:** When a host needs to traverse a jump, set `proxyjump <jumphost-name>`. The jump host itself must be defined as its own `Host` block somewhere (often in the same site file).

**Standard options** to include on most hosts unless the user says otherwise:

```
    identitiesonly yes
    forwardagent yes
    stricthostkeychecking no
    userknownhostsfile /dev/null
    passwordauthentication no
    serveraliveinterval 200
```

These mirror the patterns already in the existing site files. Match the surrounding style of the file being edited — e.g., `cypress` uses lowercase `port`, `df-austin` uses lowercase, `ed-house` mixes `User`/`HostName` casing in some blocks. Stay consistent with what's already in the target file.

## Workflow

### Step 1: Parse args and locate file

1. Read `$1` as subcommand. Reject if unknown.
2. Resolve site name → file path: `~/.ssh/config.d/<site>`.
3. Verify the file exists (or, for `new-site`, that it does not).
4. Before any write, run `$H migrate` once so no skill backup is left inside the glob.

### Step 2: Parse the file into structured blocks

Read the file. Split it into:

- **Header** — the contiguous run of comment/blank lines at the top, up to (but not including) the first `Host ` line.
- **Blocks** — each `Host <name>` line and all following lines up to the next `Host ` line or EOF.

Treat the block name as the entire token after `Host ` (case-sensitive). Empty lines and comments inside a block belong to that block.

### Step 3: Apply the subcommand

#### `add <site> <host-name>`

If the host name already exists in any site file, refuse and report which file already has it (don't silently overwrite).

Prompt the user via AskUserQuestion for the minimum fields:

1. **hostname** (IP, FQDN, or `localhost`)
2. **user**
3. **port** (default `22`)
4. **identityfile** — suggest `~/.ssh/to_<something>.pub` based on the site (e.g., `to_cypressPro.pub` for `cypress` site, `to_generic.pub` for most others). Show recent identityfiles seen in the target file as quick options.
5. **proxyjump** (optional) — list existing host names from the same site file as candidates; allow "(none)".
6. **localforward / remoteforward** (optional, can be multiple)

Compose the new Host block using the standard options listed above. Build the new file (current contents plus the block, sorted per Step 4) in a `$H tempfile <site>` path, then `$H write <site> <path> --verify <host-name>`.

#### `delete <site> <host-name>`

Find the block. Confirm with the user showing the block's contents before removing (one round, default yes). Drop the block plus its trailing blank line in a `$H tempfile <site>` copy, then `$H write <site> <path> --verify <a remaining host>`. No sort needed (deletion preserves order).

#### `move <host-name> <from-site> <to-site>`

1. Extract the block from `from-site`.
2. Verify `host-name` does not already exist in `to-site`. If it does, refuse.
3. Remove the block from `from-site`: stage and `$H write` it.
4. Append the block to `to-site`, sort it, stage and `$H write <to-site> <path> --verify <host-name>`.

If `to-site` doesn't exist, suggest running `new-site <to-site>` first; don't auto-create here.

#### `sort [site]`

For each target file:

1. Parse into header + blocks (Step 2).
2. Sort blocks by host name (case-insensitive ASCII, stable).
3. Rejoin: header (verbatim) + sorted blocks separated by a single blank line, into a `$H tempfile <site>` path.
4. `$H write <site> <path> --verify <first host>` installs it atomically; nothing temporary ever sits in `config.d/`.

If `[site]` is omitted, run sort on every live site file (`$H sites`). Then print `$H dupes` (or `$H dupes <site>`) under the report.

#### `list [site]`

Print each site's host names. Plain text, one per line, grouped by site:

```
cypress (35 hosts):
  bryanHouse
  chopper2-host
  cypress-dryden
  ...

df-austin (52 hosts):
  ...
```

Then print the duplicate report from `$H dupes` (or `$H dupes <site>`): each `DUPLICATE` line names a Host defined twice in one site with both line numbers, and each `CROSS-SITE` line a Host defined in more than one site. Both accumulate options in ssh. `list` writes nothing.

#### `new-site <name> [description]`

1. Refuse if `~/.ssh/config.d/<name>` already exists.
2. Create the file with a minimal banner. Use the figlet-style block style already in `df-austin` and `ed-house` for visual consistency, but if `figlet` is not installed, fall back to a simple hash-bar banner. Generate via:

   ```bash
   if command -v figlet >/dev/null 2>&1; then
       figlet -f standard "<name>"
   fi
   ```

   Wrap each line with `# ... #` to match the existing 103-column box style.
3. Banner template (simple fallback if no figlet):

   ```
   #-----------------------------------------------------------------------------------------------------#
   #                                                                                                     #
   #                                          <name>                                                     #
   #                                          <description>                                              #
   #-----------------------------------------------------------------------------------------------------#

   ```

   (One trailing blank line so the first added host has spacing.)
   Write the banner into a `$H tempfile <name>` path and install it with `$H write <name> <path>`.
4. Report the file path so the user can immediately follow up with `add`.

#### `speedup <host-name> [<host-name> ...]`

Adds SSH ControlMaster multiplexing to one or more existing host blocks. First connection opens a master socket; subsequent connections to the same host reuse the socket — multi-second cold connects become sub-second warm ones.

For each `<host-name>` in the argument list, run the per-host flow below:

1. **Locate the block.** Search all non-skipped files in `~/.ssh/config.d/`. Resolve to (filename, block-start-line, block-end-line).
   - Not found → refuse: "host '<name>' not found in `~/.ssh/config.d/*`. Try `/ssh-config list` to see what's registered." Continue with the next host arg.
   - Found in multiple files → refuse: "host '<name>' found in multiple files: <list>. Fix the duplicate first." Continue with the next host arg.

2. **Inspect existing controlmaster state.** Parse the option lines inside the block (case-insensitive):
   - If `controlmaster auto` AND `controlpath` AND `controlpersist` are ALL already present → report "already sped up: controlmaster=<value>, controlpath=<value>, controlpersist=<value>. Skipping." No write. Continue with next host.
   - If any of the three is present but not all (partial state) → ask via AskUserQuestion: "Partial speedup config exists. Replace with defaults?" Default yes. On yes: remove the existing controlmaster/controlpath/controlpersist lines (Step 4 will re-insert). On no: skip this host.
   - If none present → proceed.

3. **Stage the edit** in a `$H tempfile <site>` copy of the file; `$H write` takes the backup (Step 6).

4. **Edit the block in the staged copy.** Insert these three option lines inside the `Host <name>` block, between the last existing option line and the next `Host ` line (or EOF). Use 4-space indentation to match the block's existing style:
   ```
       controlmaster auto
       controlpath ~/.ssh/cm-%C
       controlpersist 10m
   ```
   The `%C` is OpenSSH's connection-hash token (resolves to a hash of `user@host:port` — collision-free across hosts, no manual naming needed).
   
   If you removed partial state in Step 2: insert at the position where the old `controlmaster` line was, so the block stays grouped sensibly. Otherwise: append after the last existing option line.

5. **Verify** with:
   ```bash
   ssh -G <name> 2>&1 | grep -iE '^(controlmaster|controlpath|controlpersist) '
   ```
   after `$H write <site> <path> --verify <name>`. Expect all three lines: `controlmaster auto`, `controlpath <expanded-absolute-path>`, `controlpersist 600`. If any missing or wrong: **`$H restore <site>`** and report the parse error.

6. **Measure the speedup** (informative; skip on unreachable host):
   ```bash
   T1=$( { time ssh -o ConnectTimeout=5 -o BatchMode=yes <name> true 2>/dev/null; } 2>&1 | awk '/real/ {print $2}' )
   T2=$( { time ssh -o ConnectTimeout=5 -o BatchMode=yes <name> true 2>/dev/null; } 2>&1 | awk '/real/ {print $2}' )
   ```
   `BatchMode=yes` prevents interactive password prompts on unreachable hosts. If the first ssh fails (non-zero exit), skip measurement and report "host unreachable — config applied, couldn't measure live."

7. **Report** in the standard format:
   ```
   ssh-config: speedup done
   
   File:           ~/.ssh/config.d/<site>
   Host modified:  <name>
   Added:          controlmaster auto
                   controlpath ~/.ssh/cm-%C
                   controlpersist 10m
   Backup:         ~/.ssh/config.d-backups/<site>.<UTC>
   Verified with:  ssh -G <name>
   
   Speedup measured:
     1st ssh (cold, opens master): <T1>
     2nd ssh (warm, reuses):       <T2>
   ```

After ALL hosts in the args list are processed, append a single caveat block (once, not per-host):

```
Caveat — controlpersist holds the master for 10 minutes after the last child exits. If your laptop sleeps or your IP changes, the next `ssh <name>` may hang trying to use a dead socket. Fix with:
  ssh -O exit <name>
If that becomes routine, lower controlpersist (or set `controlpersist no` to close immediately after each child).
```

No sort needed (in-place edit doesn't change block order).

### Step 4: Sort after mutations (add / move)

After any `add` or `move`, always run the sort routine on the modified file(s). This is the invariant: **on disk, every site file is alphabetical**.

### Step 5: Verify

After any write, run `$H verify <one-affected-host>`:

- **Exit 3** — ssh cannot parse the configuration. `$H write --verify` has already restored the site file; report the parse error. Don't leave the file broken.
- **Exit 1** — a file other than a live site file loads as live configuration (or applied options to the host). The report says `Verify: FAILED` and lists each `APPLIES` / `LOADED` line with the `mv` that moves the file into `~/.ssh/config.d-backups/`. The skill moves only backups it made itself (`$H migrate`); a user's file is the user's to move.
- **Exit 0** — options come only from live site files.

### Step 6: Backup before destructive ops

`$H write` backs up the current site file to `~/.ssh/config.d-backups/<site>.<UTC>` before installing the new one, keeping the newest 20 per site; `$H backup <site>` takes one by hand. Backups never land in `config.d/`, where ssh would load them. `$H restore <site>` reinstalls the newest.

Don't commit backups anywhere — they're working scratch only.

### Step 7: Report

Terse summary on stdout:

```
ssh-config: <subcommand> done

File: ~/.ssh/config.d/<site>
Hosts before: 35
Hosts after: 36
Added: bryanLab (proxyjump fieldstone)

Backup: ~/.ssh/config.d-backups/cypress.20261022T214954420475Z
Verify: ok (sshcfg.py verify bryanLab)
Sorted: yes
Duplicates: none
```

A failed check reads `Verify: FAILED` followed by the helper's `APPLIES` / `LOADED` lines.

## Common scenarios

### Spinning up the new "brian" site and moving Bryan's host out of cypress

```
/ssh-config new-site brian "bryan's house"
/ssh-config move bryanHouse cypress brian
```

After this, `bryanHouse` lives only in `brian`, as its first (only) block, sorted alphabetically.

### Adding a node to an existing site

```
/ssh-config add df-austin newserver
```

Skill prompts for hostname/user/identityfile/proxyjump, composes the block, appends, sorts df-austin.

### Just alphabetize everything

```
/ssh-config sort
```

Sorts every site file. Banners preserved.

### Speed up a slow ssh host (or two)

```
/ssh-config speedup darkfactory
/ssh-config speedup darkfactory kilroyfactor
```

Adds ControlMaster multiplexing to each named host's block. First ssh opens a master socket; subsequent ssh sessions to the same host reuse it (sub-second). Reports the timing diff and a one-line caveat about stale sockets after laptop sleep.

## Guardrails

- **Never overwrite an existing host block silently.** Refuse with a pointer to where the name already lives.
- **Never delete a file** in `~/.ssh/config.d/` even on `new-site` collision — refuse and report.
- **Always verify with `$H verify`** after writing. A broken file breaks every shell that touches SSH.
- **Every write goes through `$H write`**, which backs up to `~/.ssh/config.d-backups/` first. Nothing but site files is ever written into `config.d/`.
- **Preserve banner headers verbatim.** Don't reformat, re-wrap, or strip comments above the first `Host` line.
- **Don't touch `~/.ssh/config` itself** — only files in `config.d/`. The main config's `Include` line already wires everything together.
- **Never edit the files** listed in "Files the skill never edits". If the user names one explicitly, ask once whether to proceed.
- **Lowercase host names** are preferred when adding (`bryanlab` not `BryanLab`), but if the user gives a specific case, honor it.
- **Don't rewrite a block's option style** when moving — preserve original indentation, casing, and option ordering. Sort operates at the block level, not inside blocks.

## What NOT to do

- Don't merge or dedupe option lines inside a block — preserve what the user wrote
- Don't change the file's existing host blocks just because they don't follow the "standard options" convention — only new blocks added via `add` follow that template
- Don't follow symlinks if any file in `config.d/` is a symlink — leave it alone, ask user
- Don't `chmod` or `chown` — leave permissions alone
- Don't run `ssh-add` or modify the ssh-agent — this skill edits config files and runs read-only `ssh -G` (verification) + `ssh <name> true` (speedup timing). It does NOT touch agent state, keys, known_hosts, or remote files.
Pliki w paczce (3)
  • SKILL.md 19 KB
  • scripts/sshcfg.py 14 KB
  • tests/test_sshcfg.py 10 KB

Wersje

W bazie nie ma innych skilli o nazwie ssh-config.

Migawka bazy z 28.10.2026 · wersja paczki b208ea262dee (skrót SHA-256 całej paczki)

Pobrania w ostatnich 30 dniach: 0 · ranking

Zgłoś problem z tym skillem: szkodliwa treść, błędny opis albo prośba autora o usunięcie.

Podobne skille

  • 95glab-ssh-key
    Dodaje, wypisuje i usuwa klucze SSH na koncie GitLab, gdy konfigurujesz uwierzytelnianie przez SSH i Git.
  • 90ssh-remote-operations
    Wykonuje operacje DevOps na zdalnych serwerach przez konfigurację SSH w katalogu projektu, bez ujawniania kluczy i haseł.
  • 95glab-deploy-key
    Dodaje, wypisuje i usuwa klucze SSH deploy dla projektów GitLab, na potrzeby CI/CD z dostępem tylko do odczytu.
  • 93mt-ssh
    Pomaga skonfigurować połączenie SSH z Matlantis: klucze, ProxyCommand, skrypty ssh_matlantis i VSCode Remote-SSH.
  • 100ssh-key-manager
    Zarządza kluczami SSH w ramach automatycznie aktywowanej kategorii DevOps Basics, reagując na frazy związane z menedżerem kluczy SSH.
  • 100ssh-remote-connection
    Łączy się przez SSH ze zdalnymi serwerami, aby wykonywać polecenia, sprawdzać logi, restartować usługi i zarządzać kontenerami Docker.

Uwagi i zgłoszenia

Wiadomość trafi do Bartosza Sokolińskiego. Adres e-mail jest potrzebny tylko do odpowiedzi. Szczegóły w polityce prywatności.