208 lines
7.3 KiB
Markdown
208 lines
7.3 KiB
Markdown
# 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
|
|
|
|
Debian/Ubuntu (amd64):
|
|
|
|
```bash
|
|
sudo apt install ./sshkeeper_0.4.0-1_amd64.deb
|
|
```
|
|
|
|
Fedora/RHEL-family (x86_64):
|
|
|
|
```bash
|
|
sudo dnf install ./sshkeeper-0.4.0-1.x86_64.rpm
|
|
```
|
|
|
|
ARM64 packages (`arm64.deb` / `aarch64.rpm`) and the original tar.gz archives
|
|
are published alongside them. Package dependencies pull in the distro OpenSSH
|
|
client; user config, database and vault files are not owned or modified by the
|
|
package.
|
|
|
|
Verify downloads against `checksums.txt`. Linux and macOS are the primary
|
|
release targets. Windows remains experimental and requires OpenSSH Client
|
|
(`ssh.exe`) in `PATH`.
|