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")
}