Initial commit: dial — interactive SSH host picker

A fast, read-only ~/.ssh/config picker that exec's the system ssh client.
Includes: static high-contrast picker with recency/frequency/name sort and
tag grouping, `dial keygen` (native ed25519/RSA + optional config entry), and
an optional offline YubiKey launch gate with one-time recovery codes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 23:18:23 +10:00
commit 8af44dfcd2
31 changed files with 4219 additions and 0 deletions
+82
View File
@@ -0,0 +1,82 @@
package store
import (
"os"
"path/filepath"
"testing"
)
// withHome points UserHomeDir at a temp dir for the duration of a test.
func withHome(t *testing.T) string {
t.Helper()
home := t.TempDir()
t.Setenv("HOME", home)
// Ensure ~/.ssh exists as the store nests under it.
if err := os.MkdirAll(filepath.Join(home, ".ssh"), 0o700); err != nil {
t.Fatal(err)
}
return home
}
func TestSettingsRoundTrip(t *testing.T) {
withHome(t)
s := DefaultSettings()
s.YubiKeyEnabled = true
s.Sort = SortFrequent
if err := SaveSettings(s); err != nil {
t.Fatal(err)
}
got := LoadSettings()
if !got.YubiKeyEnabled || got.Sort != SortFrequent {
t.Errorf("round trip mismatch: %+v", got)
}
}
func TestLoadCorruptFallsBackToDefaults(t *testing.T) {
withHome(t)
d, _ := Dir()
// Simulate a mid-Syncthing partial write.
if err := os.WriteFile(filepath.Join(d, "settings.json"), []byte(`{"yubikey_enabled": tr`), 0o600); err != nil {
t.Fatal(err)
}
got := LoadSettings()
def := DefaultSettings()
if got != def {
t.Errorf("corrupt settings should yield defaults, got %+v", got)
}
}
func TestRecordAndSort(t *testing.T) {
withHome(t)
if err := Record("web1"); err != nil {
t.Fatal(err)
}
if err := Record("web1"); err != nil {
t.Fatal(err)
}
if err := Record("db"); err != nil {
t.Fatal(err)
}
d := LoadData()
if d.Stats["web1"].Count != 2 {
t.Errorf("web1 count = %d, want 2", d.Stats["web1"].Count)
}
if d.Stats["db"].Count != 1 {
t.Errorf("db count = %d, want 1", d.Stats["db"].Count)
}
}
func TestToggleFavorite(t *testing.T) {
withHome(t)
on, err := ToggleFavorite("web1")
if err != nil || !on {
t.Fatalf("expected favorite on, got %v %v", on, err)
}
off, err := ToggleFavorite("web1")
if err != nil || off {
t.Fatalf("expected favorite off, got %v %v", off, err)
}
if len(LoadData().Favorites) != 0 {
t.Error("favorites should be empty after toggle off")
}
}