docs: add v0.4.0 workflow guide and release notes

This commit is contained in:
mirivlad 2026-09-05 21:00:21 +08:00
parent db25f9011b
commit 0151076ca9
3 changed files with 265 additions and 32 deletions

View File

@ -22,11 +22,11 @@ port forwarding management.
- Password and key-passphrase auth through a PTY prompt handler, without putting
secrets in command-line arguments.
- Key, SSH-agent, password, and key+passphrase auth modes.
- **Routes / ProxyJump**manage bastion hosts and jump chains with human-readable display.
- **Routes / ProxyJump**ordered bastion chains with stable references to sshkeeper profiles; profile renames do not break routes.
- **Port forwarding** — named local/remote/SOCKS forwards with type selector, validation, and OpenSSH preview.
- **Tunnel management** — start/stop/list background tunnels, PID tracking, runtime state.
- **Tunnel vs Forward** — clear separation: forward = saved rule, tunnel = running SSH process.
- Groups, tags, command templates, search by metadata/routes/forward ports, and OpenSSH config generation.
- First-class groups, multi-select tags, command templates, search by metadata/routes/forward ports, and OpenSSH config generation.
- Import from `~/.ssh/config` and simple tab-separated export.
## Install
@ -66,11 +66,11 @@ Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
- Primary public repository: [github.com/mirivlad/sshkeeper](https://github.com/mirivlad/sshkeeper)
- Self-hosted mirror: `git@git.mirv.top:mirivlad/sshkeeper`
### Install from release (after v0.2.0 publication)
### Install from release
```bash
tar -xzf sshkeeper_v0.2.0_linux_amd64.tar.gz
sudo install -m 0755 sshkeeper_v0.2.0_linux_amd64/sshkeeper /usr/local/bin/sshkeeper
tar -xzf sshkeeper_v0.4.0_linux_amd64.tar.gz
sudo install -m 0755 sshkeeper_v0.4.0_linux_amd64/sshkeeper /usr/local/bin/sshkeeper
sshkeeper
```
@ -140,7 +140,8 @@ it to DEL (in xterm, `backarrowKey: false`).
| Ctrl+E | Edit server |
| Ctrl+F | Search |
| Ctrl+W | Manage port forwards for selected server |
| Ctrl+X | Action menu (connect, tunnels, forwards, route, test, edit, delete, import/export, vault actions) |
| Ctrl+X | Server actions (connect, tunnels, forwards, route, test, edit, delete) |
| m | Manage groups, tags, command templates, running tunnels, import/export, and vault |
| Ins | Select / deselect a server |
| ? | Quick help (hotkeys) |
| Ctrl+H | Full documentation |
@ -157,12 +158,25 @@ In add/edit forms:
|-----|--------|
| Tab / Down | Next field |
| Shift+Tab / Up | Previous field |
| `/` on Auth Method or Group | Pick from list |
| `/` on Auth, Identity File, Route, Group, Startup Command, or Tags | Open the relevant picker/editor |
| Enter | Move to action / activate |
| Esc | Back |
## Routes, Tunnels, and Port Forwards
Routes are stored as ordered hops. If a hop matches an existing sshkeeper profile,
sshkeeper stores a stable reference to that profile ID, not the mutable alias. The
connection planner resolves the profile's real host/user/port/key and writes a
temporary OpenSSH config for the session, so a sshkeeper bastion does **not** need
a matching `Host` entry in `~/.ssh/config`.
Use `profile:<alias>` to require a profile reference and `raw:<target>` to require
a literal OpenSSH jump target. An unprefixed exact known alias is treated as a
profile; any other value remains a raw target.
In the TUI, `/` on Route opens the ordered route editor: `Enter` adds a profile,
`x`/Delete removes a hop, and `[`/`]` moves it.
### Jump host (single bastion)
```bash
@ -248,11 +262,11 @@ key-passphrase authentication so the PTY prompt handler can provide the secret.
| Action | Command | TUI | Description |
|--------|---------|-----|-------------|
| Connect | `sshkeeper connect <alias>` | `Enter` | Standard SSH session, no port forwards |
| Connect with tunnels | `sshkeeper tunnel <alias>` | Action menu → Connect with tunnels | SSH session with all enabled forwards active |
| Start tunnels only | `sshkeeper tunnel <alias> --forward-only` | Action menu → Start tunnels only | Foreground tunnel, no shell |
| Start tunnels in background | `sshkeeper tunnel <alias> --background` | Action menu → Start tunnels in background | Detached tunnel process with PID tracking |
| Manage port forwards | `sshkeeper forward` | Action menu → Manage port forwards | Add/edit/delete forward rules |
| Manage tunnels | `sshkeeper tunnel list/stop/stop-all` | Action menu → Manage tunnels | View running tunnels and stop them |
| Connect with tunnels | `sshkeeper tunnel <alias>` | Server Actions → Connect with tunnels | SSH session with all enabled forwards active |
| Start tunnels only | `sshkeeper tunnel <alias> --forward-only` | Server Actions → Start tunnels only | Foreground tunnel, no shell |
| Start tunnels in background | `sshkeeper tunnel <alias> --background` | Server Actions → Start tunnels in background | Detached tunnel process with PID tracking |
| Port forwards | `sshkeeper forward` | Server Actions → Port forwards (or `Ctrl+W`) | Add/edit/enable/delete forward rules |
| Running tunnels | `sshkeeper tunnel list/stop/stop-all` | `m` → Running tunnels | View tracked/running tunnels and stop them |
## Vault

View File

@ -166,7 +166,8 @@ Auth: agent Group: - Status: ?
| `Enter` | Подключиться к серверу |
| `Ctrl+A` | Добавить сервер |
| `Ctrl+E` | Редактировать сервер |
| `Ctrl+X` | Меню действий |
| `Ctrl+X` | Действия выбранного сервера |
| `m` | Глобальное меню Manage |
| `Ctrl+F` | Поиск |
| `Ins` | Выбрать/снять выбор |
| `?` | Краткая справка по клавишам |
@ -201,7 +202,8 @@ sshkeeper — Quick Help
Ctrl+A Add server
Ctrl+E Edit server
Ctrl+F Search
Ctrl+X Action menu
Ctrl+X Server actions
m Manage
Ins Select / deselect
Port forwards
@ -252,7 +254,8 @@ sshkeeper — Full Help
Ctrl+A Add server
Ctrl+E Edit server
Ctrl+F Search
Ctrl+X Action menu
Ctrl+X Server actions
m Manage
Ins Select / deselect
...
@ -287,7 +290,7 @@ Add Server
User: root
Auth Method: key
Identity File: ~/.ssh/id_ed25519
Route hops: bastion
Route: profile:bastion
Group: KP
Notes: Main mail server
Startup Command: tmux attach -t ops
@ -311,8 +314,12 @@ Add Server
|---------|----------|
| `Tab` или `↓` | Следующее поле |
| `Shift+Tab` или `↑` | Предыдущее поле |
| `/` на Auth Method | Выбрать из списка (password/key/key_passphrase/agent) |
| `/` на Group | Выбрать из существующих групп |
| `/` на Auth Method | Выбрать password/key/key_passphrase/agent |
| `/` на Identity File | Выбрать приватный ключ из `~/.ssh` |
| `/` на Route | Открыть редактор цепочки бастионов |
| `/` на Group | Выбрать существующую группу |
| `/` на Startup Command | Скопировать команду из глобального шаблона |
| `/` на Tags | Multi-select существующих тегов; новые можно ввести вручную |
| `Enter` на Test | Проверить подключение |
| `Enter` на Save | Сохранить |
| `Esc` | Назад; при изменённых данных сначала запросить подтверждение сброса |
@ -378,9 +385,16 @@ ROUTE: ⇒ bastion → dmz-gw → … → root@secure.internal:22
### Настройка маршрута
**Через TUI:**
1. Добавьте/редактируйте сервер или выберите `Ctrl+X` → "Manage route"
2. В поле "Route hops" введите бастионы через запятую: `bastion,dmz-gw`
3. Или введите адрес напрямую: `user@bastion.example.com:2222`
1. Добавьте/редактируйте сервер или выберите `Ctrl+X`**Route**.
2. Нажмите `/` на поле Route — откроется список существующих профилей.
3. `Enter` добавляет выбранный профиль в ordered chain, `x`/Delete удаляет hop, `[`/`]` меняет порядок.
4. Для произвольной OpenSSH-цели оставьте escape hatch в поле: `raw:user@bastion.example.com:2222`.
Ссылка на профиль хранится по стабильному ID. Переименование alias бастиона не
ломает зависимые маршруты, а удалить используемый бастион sshkeeper не даст,
пока он присутствует в route других серверов. Во время подключения profile-hop
разрешается из БД sshkeeper; отдельная запись с тем же alias в `~/.ssh/config`
не требуется.
**Через CLI:**
@ -413,7 +427,7 @@ sshkeeper route clear web
### Управление forwards через TUI
1. Выберите сервер на главном экране
2. Нажмите `Ctrl+X`"Manage port forwards"
2. Нажмите `Ctrl+X`**Port forwards**
3. Откроется список forwards:
```
@ -426,7 +440,7 @@ Selected
Port 127.0.0.1:15432 on this machine will be forwarded through web to 127.0.0.1:5432.
ssh -L 127.0.0.1:15432:127.0.0.1:5432
Ctrl+A: add | Ctrl+E/Enter: edit | Ctrl+D: delete | Esc: back
Ctrl+A: add | Ctrl+E/Enter: edit | Space: enable/disable | Ctrl+D: delete | Esc: back
```
Строки и пояснение выбранного forward сокращаются по экранным ячейкам, а не
@ -438,6 +452,7 @@ Selected
|---------|----------|
| `Ctrl+A` | Добавить forward |
| `Enter` или `Ctrl+E` | Редактировать выбранный |
| `Space` | Включить/выключить правило |
| `Ctrl+D` | Удалить (с подтверждением) |
| `Esc` | Назад |
@ -514,7 +529,7 @@ forward и сейчас поддерживает только `key` или `agen
### Управление туннелями
**Через TUI:**
1. Нажмите `Ctrl+X` → "Manage tunnels"
1. Нажмите `m` → **Running tunnels**
2. Список запущенных туннелей:
```
@ -730,7 +745,7 @@ sshkeeper connect secure
```bash
# В TUI: выбрать несколько серверов (Ins), затем:
# Ctrl+X → Run template → выбрать шаблон
# Ctrl+R → выбрать шаблон
# Команда выполнится на всех выбранных серверах
```
@ -746,13 +761,14 @@ sshkeeper connect secure
| `Ctrl+A` | Добавить сервер |
| `Ctrl+E` | Редактировать сервер |
| `Ctrl+F` | Поиск |
| `Ctrl+X` | Меню действий |
| `Ctrl+X` | Действия выбранного сервера |
| `m` | Глобальное меню Manage |
| `Ins` | Выбрать/снять выбор |
| `?` | Краткая справка по клавишам |
| `Ctrl+H` | Полная справка по приложению |
| `Ctrl+Q` | Выход |
### Меню действий (Ctrl+X)
### Действия сервера (Ctrl+X)
| Действие | Описание |
|----------|----------|
@ -760,13 +776,21 @@ sshkeeper connect secure
| Connect with tunnels | SSH + все активные forwards |
| Start tunnels only | Туннель без shell |
| Start tunnels in background | Фоновый туннель |
| Manage port forwards | Управление forwards |
| Manage tunnels | Список туннелей |
| Manage route | Настройка маршрута |
| Port forwards | Управление forwards выбранного сервера |
| Route | Ordered chain бастионов выбранного сервера |
| Test connection | Проверка подключения |
| Edit | Редактирование сервера |
| Delete | Удаление (с подтверждением) |
| Import | Импорт из `~/.ssh/config` и обновление списка |
### Manage (`m`)
| Действие | Описание |
|----------|----------|
| Groups | Создание, переименование и удаление групп с количеством серверов |
| Tags | Управление тегами |
| Command templates | Глобальные шаблоны команд |
| Running tunnels | Список и остановка фоновых туннелей |
| Import SSH config | Импорт из `~/.ssh/config` |
| Export | Выход в терминал и печать экспорта |
| Vault: lock | Заблокировать vault в текущем процессе |
| Vault: change password | Выход в терминал и смена master password |
@ -777,7 +801,7 @@ sshkeeper connect secure
|---------|----------|
| `Tab` / `↓` | Следующее поле |
| `Shift+Tab` / `↑` | Предыдущее поле |
| `/` | Выбрать из списка (Auth Method, Group) |
| `/` | Открыть picker/editor для Auth, key, Route, Group, Startup Command или Tags |
| `Enter` | Действие / переход |
| `Esc` | Назад / отмена |

195
docs/releases/v0.4.0.md Normal file
View File

@ -0,0 +1,195 @@
# sshkeeper v0.4.0 — Model & Workflow Cleanup
v0.4.0 is a structural release. The main goal is to make sshkeeper's data model
match what the user sees: server profiles are real reusable objects, routes
reference those objects instead of mutable text, and the TUI offers pickers for
things that already exist instead of asking you to retype them.
Existing databases and vaults are migrated automatically. No manual conversion
is required.
## Routes are now real relationships
The old implementation stored both `ProxyJump` text and a second route
representation. Different commands wrote different fields, and runtime SSH used
one in preference to the other. That made it possible for `edit --proxy-jump` to
say "Saved" while the old structured route was still used.
v0.4.0 makes `Route` canonical and stores profile hops in a normalized
`server_route_hops` table with foreign keys to stable server IDs.
- Renaming a bastion no longer breaks dependent routes.
- Deleting a profile that is still used as a route hop is rejected and names
the dependent profiles.
- Self references, duplicate hops and route cycles are rejected.
- `proxy_jump` and legacy `route_hops` remain compatibility projections for old
databases/tools, not competing sources of truth.
- Existing `group_name` strings are likewise migrated to first-class `groups`
with stable IDs.
### Profile hops no longer depend on `~/.ssh/config`
A route hop that references a sshkeeper profile now resolves that profile's real
host, user, port and identity file from SQLite. sshkeeper generates a temporary
OpenSSH config for the connection and passes it with `ssh -F`.
This fixes the old accidental requirement that a sshkeeper alias such as
`bastion-prod` also had to exist as a matching `Host` in the user's OpenSSH
config.
CLI route syntax is explicit when needed:
```bash
# exact known aliases are profile references
sshkeeper route set prod --jumps bastion,dmz-gw
# force the interpretation
sshkeeper route set prod --jumps profile:bastion,raw:ops@external-gw:2222
```
An unprefixed value matching an existing sshkeeper alias becomes a profile hop;
an unknown value remains a raw OpenSSH target.
Password or key-passphrase authentication on an *intermediate* profile hop is
rejected with a clear error for now. The current PTY secret flow can safely feed
the target profile, but it cannot reliably route different vault secrets to
multiple OpenSSH prompts in a jump chain. Key/agent bastions are supported.
## Route editor in the TUI
`Ctrl+X`**Route**, or `/` while the Route field is focused, opens an ordered
route editor.
- `Enter` adds an existing server profile as a hop.
- `x` / Delete removes a hop.
- `[` / `]` moves a hop up/down in the chain.
- `/` filters the available profiles.
- `raw:<target>` remains available in the editable field for arbitrary OpenSSH
targets.
The server form no longer claims that a picker exists while still requiring a
comma-separated string.
## Server Actions vs Manage
`Ctrl+X` is now strictly about the selected server:
- Connect
- Connect with tunnels
- Start tunnels only / in background
- Port forwards
- Route
- Test connection
- Edit
- Delete
Press **`m`** for global management:
- Groups
- Tags
- Command templates
- Running tunnels
- Import SSH config
- Export
- Vault lock / password change
Groups now have their own manager with server counts, create/rename/delete
operations, and safe delete consequences.
## Context-aware server form
The form now exposes only authentication fields that matter:
| Auth | Fields |
|------|--------|
| password | Password |
| key | Identity File |
| key_passphrase | Identity File + Key passphrase |
| agent | no credential fields |
`/` opens context-specific pickers:
- Auth method → auth list
- Identity File → detected private keys in `~/.ssh`
- Route → profile/chain editor
- Group → existing groups
- Startup Command → global command templates (the command is copied, not linked)
- Tags → multi-select existing tags; new tags can still be typed manually
## CLI consistency fixes
All writers now go through the same route semantics.
- `add`, `edit`, `route set`, TUI save and SSH config import all create the same
canonical route model.
- SSH config import is two-pass: profiles are created first, then `ProxyJump`
aliases are resolved against the complete imported/existing profile set.
- `edit` uses Cobra's `Changed()` state, so `--group ''`, `--notes ''`,
`--startup-command ''`, `--identity-file ''`, `--proxy-jump ''` and
`--tags ''` can actually clear values.
- An empty server `User` no longer produces the invalid target `@host`; OpenSSH
is allowed to choose its configured/current user.
- Shared server/auth/route validation replaced duplicated partial checks.
## Vault identity follows the server, not its alias
New and migrated server secrets are stored under stable server IDs. Renaming a
profile no longer requires a risky copy/delete of secrets keyed by alias.
Legacy `server:<alias>:<type>` records remain readable and are lazily migrated
when used. `vault list` understands both legacy and stable-ID records and
resolves stable IDs back to current aliases when the database is available.
TUI server save now commits the database change before rewriting vault state;
if the vault step fails, the profile/tags are rolled back instead of leaving the
database and vault disagreeing.
## Port forward fixes
Two concrete TUI bugs are fixed:
- Editing a disabled forward no longer silently re-enables it.
- Remote-forward preview now uses the same semantic builder as validation/save,
so listen/target endpoints cannot be shown reversed.
The editor has an explicit Enabled toggle and the forward list supports `Space`
to enable/disable a rule quickly.
## Tunnel fixes
Profile routes work consistently for foreground and background tunnels.
Background tunnels keep their generated temporary SSH config for the lifetime of
the process and remove it on stop/stop-all.
Tunnel Manager now reports `running` separately from `tracked`; stale tracked
state is no longer counted as a running process.
## Cleanup
- Removed the stale per-server `CommandTemplate.ServerID` field; command
templates are global.
- Removed an unused legacy `model.Secret` type; the encrypted vault has its own
actual storage model.
- Added regression coverage for stable route references, deletion protection,
cycle detection, stable-ID vault metadata, contextual auth fields, Manage,
route/identity/tag/startup pickers, and forward semantics.
## Upgrade notes
On first start, v0.4.0 automatically creates `groups` and
`server_route_hops`, links existing group names, and converts existing route
text/JSON. The old columns are retained for compatibility.
As always, keep a copy of `~/.local/share/sshkeeper/` before a major upgrade if
the data matters to you.
## Install
```bash
tar -xzf sshkeeper_v0.4.0_linux_amd64.tar.gz
sudo install -m 0755 sshkeeper_v0.4.0_linux_amd64/sshkeeper /usr/local/bin/sshkeeper
```
Verify downloads against `checksums.txt`. Linux and macOS are the primary
release targets. Windows remains experimental and requires OpenSSH Client
(`ssh.exe`) in `PATH`.