diff --git a/pkg/cli/production/install/flags.go b/pkg/cli/production/install/flags.go index 51e9610..6fb0b90 100644 --- a/pkg/cli/production/install/flags.go +++ b/pkg/cli/production/install/flags.go @@ -59,7 +59,7 @@ func ParseFlags(args []string) (*Flags, error) { // Cluster join flags fs.StringVar(&flags.JoinAddress, "join", "", "Join an existing cluster (e.g. 1.2.3.4:7001)") fs.StringVar(&flags.ClusterSecret, "cluster-secret", "", "Cluster secret for IPFS Cluster (required if joining)") - fs.StringVar(&flags.SwarmKey, "swarm-key", "", "IPFS Swarm key (required if joining)") + fs.StringVar(&flags.SwarmKey, "swarm-key", "", "IPFS Swarm key hex (64 chars, last line of swarm.key)") fs.StringVar(&flags.PeersStr, "peers", "", "Comma-separated list of bootstrap peer multiaddrs") // IPFS/Cluster specific info for Peering configuration diff --git a/pkg/cli/production/install/validator.go b/pkg/cli/production/install/validator.go index 3a02325..12be366 100644 --- a/pkg/cli/production/install/validator.go +++ b/pkg/cli/production/install/validator.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/DeBrosOfficial/network/pkg/cli/utils" + "github.com/DeBrosOfficial/network/pkg/config/validate" "github.com/DeBrosOfficial/network/pkg/environments/production/installers" ) @@ -99,8 +100,9 @@ func (v *Validator) SaveSecrets() error { if err := os.MkdirAll(secretsDir, 0755); err != nil { return fmt.Errorf("failed to create secrets directory: %w", err) } - // Convert 64-hex key to full swarm.key format - swarmKeyContent := fmt.Sprintf("/key/swarm/psk/1.0.0/\n/base16/\n%s\n", strings.ToUpper(v.flags.SwarmKey)) + // Extract hex only (strips headers if user passed full file content) + hexKey := strings.ToUpper(validate.ExtractSwarmKeyHex(v.flags.SwarmKey)) + swarmKeyContent := fmt.Sprintf("/key/swarm/psk/1.0.0/\n/base16/\n%s\n", hexKey) swarmKeyPath := filepath.Join(secretsDir, "swarm.key") if err := os.WriteFile(swarmKeyPath, []byte(swarmKeyContent), 0600); err != nil { return fmt.Errorf("failed to save swarm key: %w", err) diff --git a/pkg/config/validate/validators.go b/pkg/config/validate/validators.go index 19dc223..8195ec9 100644 --- a/pkg/config/validate/validators.go +++ b/pkg/config/validate/validators.go @@ -167,9 +167,26 @@ func ExtractTCPPort(multiaddrStr string) string { return "" } +// ExtractSwarmKeyHex extracts just the 64-char hex portion from a swarm key input. +// Handles both raw hex ("ABCD...") and full file content ("/key/swarm/psk/1.0.0/\n/base16/\nABCD...\n"). +func ExtractSwarmKeyHex(input string) string { + input = strings.TrimSpace(input) + // If it contains the swarm key header, extract the last non-empty line (the hex) + if strings.Contains(input, "/key/swarm/") || strings.Contains(input, "/base16/") { + lines := strings.Split(input, "\n") + for i := len(lines) - 1; i >= 0; i-- { + line := strings.TrimSpace(lines[i]) + if line != "" && !strings.HasPrefix(line, "/") { + return line + } + } + } + return input +} + // ValidateSwarmKey validates that a swarm key is 64 hex characters. func ValidateSwarmKey(key string) error { - key = strings.TrimSpace(key) + key = ExtractSwarmKeyHex(key) if len(key) != 64 { return fmt.Errorf("swarm key must be 64 hex characters (32 bytes), got %d", len(key)) } diff --git a/pkg/environments/production/config.go b/pkg/environments/production/config.go index b224111..a503dec 100644 --- a/pkg/environments/production/config.go +++ b/pkg/environments/production/config.go @@ -325,10 +325,25 @@ func (sg *SecretGenerator) EnsureSwarmKey() ([]byte, error) { return nil, fmt.Errorf("failed to set secrets directory permissions: %w", err) } - // Try to read existing key + // Try to read existing key — validate and auto-fix if corrupted (e.g. double headers) if data, err := os.ReadFile(swarmKeyPath); err == nil { - if strings.Contains(string(data), "/key/swarm/psk/1.0.0/") { - return data, nil + content := string(data) + if strings.Contains(content, "/key/swarm/psk/1.0.0/") { + // Extract hex and rebuild clean file + lines := strings.Split(strings.TrimSpace(content), "\n") + hexKey := "" + for i := len(lines) - 1; i >= 0; i-- { + line := strings.TrimSpace(lines[i]) + if line != "" && !strings.HasPrefix(line, "/") { + hexKey = line + break + } + } + clean := fmt.Sprintf("/key/swarm/psk/1.0.0/\n/base16/\n%s\n", hexKey) + if clean != content { + _ = os.WriteFile(swarmKeyPath, []byte(clean), 0600) + } + return []byte(clean), nil } } diff --git a/pkg/installer/installer.go b/pkg/installer/installer.go index 60e1c7d..c9d19f2 100644 --- a/pkg/installer/installer.go +++ b/pkg/installer/installer.go @@ -11,6 +11,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/DeBrosOfficial/network/pkg/config" + "github.com/DeBrosOfficial/network/pkg/config/validate" "github.com/DeBrosOfficial/network/pkg/installer/discovery" "github.com/DeBrosOfficial/network/pkg/installer/steps" "github.com/DeBrosOfficial/network/pkg/installer/validation" @@ -232,7 +233,7 @@ func (m *Model) handleEnter() (tea.Model, tea.Cmd) { m.setupStepInput() case StepSwarmKey: - swarmKey := strings.TrimSpace(m.textInput.Value()) + swarmKey := validate.ExtractSwarmKeyHex(m.textInput.Value()) if err := config.ValidateSwarmKey(swarmKey); err != nil { m.err = err return m, nil diff --git a/pkg/installer/steps/swarm_key.go b/pkg/installer/steps/swarm_key.go index 85711cd..7161ca2 100644 --- a/pkg/installer/steps/swarm_key.go +++ b/pkg/installer/steps/swarm_key.go @@ -29,8 +29,8 @@ func NewSwarmKey() *SwarmKey { func (s *SwarmKey) View() string { var sb strings.Builder sb.WriteString(titleStyle.Render("IPFS Swarm Key") + "\n\n") - sb.WriteString("Enter the swarm key from an existing node:\n") - sb.WriteString(subtitleStyle.Render("Get it with: cat ~/.orama/secrets/swarm.key | tail -1") + "\n\n") + sb.WriteString("Enter the hex key from an existing node (last line of swarm.key):\n") + sb.WriteString(subtitleStyle.Render("Get it with: tail -1 ~/.orama/secrets/swarm.key") + "\n\n") sb.WriteString(s.Input.View()) if s.Error != nil {