cmd/sketch: keep public keys separate per skaband

Change the layering a bit.
Always create a public key, for better telemetry.
Keep separate things separate.
This could use a touch more refactoring,
but good enough for today.
diff --git a/cmd/sketch/main.go b/cmd/sketch/main.go
index 5015278..c97ed60 100644
--- a/cmd/sketch/main.go
+++ b/cmd/sketch/main.go
@@ -447,16 +447,15 @@
 	}
 
 	// Get credentials and connect to skaband if needed
-	var pubKey, modelURL, apiKey string
+	privKey, err := skabandclient.LoadOrCreatePrivateKey(skabandclient.DefaultKeyPath(flags.skabandAddr))
+	if err != nil {
+		return err
+	}
+	pubKey, modelURL, apiKey, err := skabandclient.Login(os.Stdout, privKey, flags.skabandAddr, flags.sessionID, flags.modelName)
+	if err != nil {
+		return err
+	}
 	if flags.skabandAddr != "" {
-		privKey, err := skabandclient.LoadOrCreatePrivateKey(skabandclient.DefaultKeyPath())
-		if err != nil {
-			return err
-		}
-		pubKey, modelURL, apiKey, err = skabandclient.Login(os.Stdout, privKey, flags.skabandAddr, flags.sessionID, flags.modelName)
-		if err != nil {
-			return err
-		}
 		flags.mcpServers = append(flags.mcpServers, skabandMcpConfiguration(flags))
 	} else {
 		// When not using skaband, get API key from environment or flag
@@ -570,10 +569,17 @@
 // runInUnsafeMode handles execution on the host machine without Docker.
 // This mode is used when the -unsafe flag is provided.
 func runInUnsafeMode(ctx context.Context, flags CLIFlags, logFile *os.File) error {
-	// Check if we need to get the API key from environment
-	var apiKey, antURL, pubKey string
+	privKey, err := skabandclient.LoadOrCreatePrivateKey(skabandclient.DefaultKeyPath(flags.skabandAddr))
+	if err != nil {
+		return err
+	}
+	pubKey, antURL, apiKey, err := skabandclient.Login(os.Stdout, privKey, flags.skabandAddr, flags.sessionID, flags.modelName)
+	if err != nil {
+		return err
+	}
 
 	if flags.skabandAddr == "" {
+		// When not using skaband, get API key from environment or flag
 		envName := "ANTHROPIC_API_KEY"
 		if flags.modelName == "gemini" {
 			envName = gem.GeminiAPIKeyEnv
@@ -583,17 +589,6 @@
 			return fmt.Errorf("%s environment variable is not set, -llm-api-key flag not provided", envName)
 		}
 	} else {
-		// Connect to skaband
-		privKey, err := skabandclient.LoadOrCreatePrivateKey(skabandclient.DefaultKeyPath())
-		if err != nil {
-			return err
-		}
-		pubKey, antURL, apiKey, err = skabandclient.Login(os.Stdout, privKey, flags.skabandAddr, flags.sessionID, flags.modelName)
-		if err != nil {
-			return err
-		}
-
-		// Add MCP Server for skaband
 		flags.mcpServers = append(flags.mcpServers, skabandMcpConfiguration(flags))
 	}
 
diff --git a/skabandclient/skabandclient.go b/skabandclient/skabandclient.go
index 6e2aef5..85f9cbf 100644
--- a/skabandclient/skabandclient.go
+++ b/skabandclient/skabandclient.go
@@ -5,6 +5,7 @@
 	"context"
 	"crypto/ed25519"
 	crand "crypto/rand"
+	"crypto/sha256"
 	"crypto/tls"
 	"crypto/x509"
 	"encoding/hex"
@@ -198,14 +199,20 @@
 	return key, nil
 }
 
+// Login connects to skaband and authenticates the user.
+// If skabandAddr is empty, it returns the public key without contacting a server.
+// It is the caller's responsibility to set the API URL and key in this case.
 func Login(stdout io.Writer, privKey ed25519.PrivateKey, skabandAddr, sessionID, model string) (pubKey, apiURL, apiKey string, err error) {
 	sig := ed25519.Sign(privKey, []byte(sessionID))
+	pubKey = hex.EncodeToString(privKey.Public().(ed25519.PublicKey))
+	if skabandAddr == "" {
+		return pubKey, "", "", nil
+	}
 
 	req, err := http.NewRequest("POST", skabandAddr+"/authclient", nil)
 	if err != nil {
 		return "", "", "", err
 	}
-	pubKey = hex.EncodeToString(privKey.Public().(ed25519.PublicKey))
 	req.Header.Set("Public-Key", pubKey)
 	req.Header.Set("Session-ID", sessionID)
 	req.Header.Set("Session-ID-Sig", hex.EncodeToString(sig))
@@ -233,12 +240,16 @@
 	return pubKey, apiURL, apiKey, nil
 }
 
-func DefaultKeyPath() string {
+func DefaultKeyPath(skabandAddr string) string {
 	homeDir, err := os.UserHomeDir()
 	if err != nil {
 		panic(err)
 	}
 	cacheDir := filepath.Join(homeDir, ".cache", "sketch")
+	if skabandAddr != "https://sketch.dev" { // main server gets "root" cache dir, for backwards compatibility
+		h := sha256.Sum256([]byte(skabandAddr))
+		cacheDir = filepath.Join(cacheDir, hex.EncodeToString(h[:8]))
+	}
 	os.MkdirAll(cacheDir, 0o777)
 	return filepath.Join(cacheDir, "sketch.ed25519")
 }