Commit Graph
12 Commits
Author SHA1 Message Date
Andy Dixon 1c590f47ff feat: encrypted profile sync through a shared folder
Profiles can be kept in step across machines through any folder they
share (cloud drive, network share, git checkout): one file,
nexus-profiles.nexus, in the encrypted-export format under a sync
password kept in the keychain/vault. Each sync merges profile by profile
(newest updated_at wins), propagates deletions as dated tombstones (an
edit after a deletion brings the profile back; tombstones expire after
180 days), validates every incoming profile, writes the file only when
it changed, and never syncs credentials.

Settings → Security → Profile sync picks the folder and sets the
password; syncs run at launch, 3 s after a profile change, every 10
minutes and on Sync Now (automatic runs stay quiet unless something
changed). An end-to-end test drives create, edit and delete between two
data directories sharing a folder.
2026-10-12 19:20:12 +01:00
Andy Dixon 0fc9451583 feat: SSH certificates and known_hosts import/export
- Host certificates: certificate authorities are trusted per OpenSSH host
  pattern (stored as @cert-authority known-hosts rows, matched against
  'host' or '[host]:port' as OpenSSH does, with *, ? and !). For a host a
  CA covers, the client asks for a certificate; one signed by a matching
  CA, of host type, naming the host and in date is trusted without a
  first-use prompt, and anything else is refused. Hosts no CA covers keep
  the existing key checks. Applies to jump hosts and tunnels too.
- User certificates: <key>-cert.pub next to the configured private key is
  used for publickey login (falling back to the plain key).
- Settings → Known Hosts: import ~/.ssh/known_hosts (hashed names and
  @revoked lines skipped and counted), export in OpenSSH format, and a
  Trust a CA form.

Verified against OpenSSH sshd with ssh-keygen-issued certificates: host
cert accepted without prompting and user-cert login with only
TrustedUserCAKeys; a CA for other hosts falls back to the key check; a
different CA is refused.
2026-10-12 19:02:59 +01:00
Andy Dixon 1990fb5da9 feat: restore tabs and splits at launch
The tab layout (tab order and names, pins, the active tab, split trees
with their ratios) is saved whenever it changes — checked once a second,
written only on change, so it survives a crash — and reopened at the
next launch: saved profiles reconnect and local shells start in the
directory they were in (on Linux the live /proc cwd, so it isn't one
command behind). Quick Connect tabs and placeholder panes are skipped.
A General setting turns it off; it never runs when launched with
--connect, --run-local or --open, and screenshot runs never save.

Verified: a local tab left in /usr/share came back there with a
'Restored 1 tab(s)' toast.
2026-10-12 18:53:00 +01:00
Andy Dixon 4bd12bfb03 test: replace the Go compatibility fixtures with native round-trip tests
The Go-generated link.db, profiles.link and rec1.cast fixtures and the
tests that read them are removed. Their coverage is kept by tests on
data this build writes: a fresh database round-trips SSH profiles (env
vars, login scripts, forwards), serial profiles, IRC room state, known
hosts, snippets and preferences, and encrypted exports round-trip and
reject a wrong password. The import dialog no longer offers .link files
and the docs stop mentioning them.
2026-10-12 10:10:08 +01:00
Andy Dixon 88c5cca021 refactor: drop the AlchementaryLink keychain fallback and Go's Windows target format
Keychain reads no longer fall back to the AlchementaryLink service and
copy entries forward, and Windows Credential Manager entries use
keyring's own target naming instead of go-keyring's 'service:user'.
Credentials saved by Alchementary Link, or by earlier builds on Windows,
are no longer found and need re-entering.
2026-10-12 10:06:38 +01:00
Andy Dixon 672c71e26f fix: proxy passwords and TOTP seeds live only in the secret store
SshConfig no longer has proxyPassword or totpSecret fields, so neither
can sit in clear in nexus.db or travel in an export. The proxy password
is a SecretKind (profile:<id>:proxy-password) edited like other saved
credentials and purged with the profile; the session manager hands both
it and the TOTP seed to the SSH client from the keychain/vault at
connect time.

This drops compatibility with Go-era profiles and .link/.nexus files
that carry these values inline: they are ignored on read and must be
re-entered (SEC-20261012-011).
2026-10-12 10:02:08 +01:00
Andy Dixon bd1dc54e20 fix: validate Quick Connect and ~/.ssh/config imports; unknown enum values no longer reset a profile's config
- Quick Connect runs the same validation as saved profiles, so an SSH
  connection without a username fails up front with a clear message (F5).
- ~/.ssh/config imports fill a missing User (and jump-host user) with the
  local user name, as ssh does, and skip blocks that still don't validate,
  reporting how many (F6).
- An unrecognised hostKeyPolicy/lineEnding/parity/flowControl/direction in
  a stored config falls back to that field's default instead of failing
  the whole JSON column and silently resetting every other setting; parse
  failures that remain are logged (F7).
2026-10-12 09:19:03 +01:00
Andy Dixon 3b490fa6c3 fix: redact scripted login passwords from secret-free exports; secret kinds are an enum
- A JSON export without secrets now blanks login-script 'send' steps that
  answer a password-like 'wait' prompt (SSH and Telnet), not just the
  proxy password and inline TOTP seed (SEC-20261012-011, partial).
- Credential kinds were string literals in the session manager, profile
  editor and secret store; they are now SecretKind with one ALL list, so
  the profile-delete purge can't miss a kind (F19).

Moving proxyPassword out of the profile record is left open: it changes
.link/.nexus compatibility with the Go app and needs an owner decision.
2026-10-12 09:16:14 +01:00
Andy Dixon 0a18f7e578 fix: generate SSH keys in-process, verify loopback tunnel peer, bound SSH command output
- SSH key pairs are generated with ssh-key (Ed25519/ECDSA P-256/RSA) and
  encrypted in-process, so the passphrase no longer appears as an ssh-keygen
  command-line argument visible in the process list.
- open_tunnel's loopback bridge checks the accepted peer is our own client
  socket, so a local process racing for the ephemeral port can't be spliced
  into the IRC/VNC SSH tunnel.
- run_command caps buffered output at 1 MiB; local-forward abort handles are
  pruned as connections finish; SOCKS5 host/credential lengths over 255 bytes
  are rejected instead of being truncated into a malformed request.
2026-10-12 07:22:31 +01:00
Andy Dixon 2bb12c3a58 rust: merge UI feature modules; fix Ctrl+key on Linux/Windows (egui command_only shadowing); clippy clean; rustfmt config 2026-10-11 17:40:48 +01:00
Andy Dixon 328507c57b rust: packaging (Linux desktop entry, macOS bundle), cargo release script with update manifests, CI 2026-10-11 16:53:19 +01:00
Andy Dixon d654671bc1 rust: Service facade (profiles, credentials, TOTP, import/export, SSH keys, SFTP, updates) 2026-10-11 16:36:37 +01:00