package config import ( "crypto/rand" "encoding/base64" "fmt" "os" "path/filepath" "gopkg.in/yaml.v3" "kvs/types" ) // Default configuration func Default() *types.Config { hostname, _ := os.Hostname() return &types.Config{ NodeID: hostname, BindAddress: "127.0.0.1", Port: 8080, DataDir: "./data", SeedNodes: []string{}, ReadOnly: false, LogLevel: "info", GossipIntervalMin: 60, // 1 minute GossipIntervalMax: 120, // 2 minutes SyncInterval: 300, // 5 minutes CatchupInterval: 120, // 2 minutes BootstrapMaxAgeHours: 720, // 30 days ThrottleDelayMs: 100, FetchDelayMs: 50, // Default compression settings CompressionEnabled: true, CompressionLevel: 3, // Balance between performance and compression ratio // Default TTL and size limit settings DefaultTTL: "0", // No default TTL MaxJSONSize: 1048576, // 1MB default max JSON size // Default rate limiting settings RateLimitRequests: 100, // 100 requests per window RateLimitWindow: "1m", // 1 minute window // Default tamper-evident logging settings TamperLogActions: []string{"data_write", "user_create", "auth_failure"}, // Default backup system settings BackupEnabled: true, BackupSchedule: "0 0 * * *", // Daily at midnight BackupPath: "./backups", BackupRetention: 7, // Keep backups for 7 days // Default feature toggle settings (all enabled by default) AuthEnabled: true, TamperLoggingEnabled: true, ClusteringEnabled: true, RateLimitingEnabled: true, RevisionHistoryEnabled: true, // Default anonymous access settings (both disabled by default for security) AllowAnonymousRead: false, AllowAnonymousWrite: false, // Default cluster authentication settings (Issue #13) ClusterSecret: generateClusterSecret(), ClusterTLSEnabled: false, ClusterTLSCertFile: "", ClusterTLSKeyFile: "", ClusterTLSSkipVerify: false, } } // generateClusterSecret generates a cryptographically secure random cluster secret func generateClusterSecret() string { // Generate 32 bytes (256 bits) of random data randomBytes := make([]byte, 32) if _, err := rand.Read(randomBytes); err != nil { // Fallback to a warning - this should never happen in practice fmt.Fprintf(os.Stderr, "Warning: Failed to generate secure cluster secret: %v\n", err) return "" } // Encode as base64 for easy configuration file storage return base64.StdEncoding.EncodeToString(randomBytes) } // Load configuration from file or create default func Load(configPath string) (*types.Config, error) { config := Default() if _, err := os.Stat(configPath); os.IsNotExist(err) { // Create default config file if err := os.MkdirAll(filepath.Dir(configPath), 0755); err != nil { return nil, fmt.Errorf("failed to create config directory: %v", err) } data, err := yaml.Marshal(config) if err != nil { return nil, fmt.Errorf("failed to marshal default config: %v", err) } if err := os.WriteFile(configPath, data, 0644); err != nil { return nil, fmt.Errorf("failed to write default config: %v", err) } fmt.Printf("Created default configuration at %s\n", configPath) return config, nil } data, err := os.ReadFile(configPath) if err != nil { return nil, fmt.Errorf("failed to read config file: %v", err) } if err := yaml.Unmarshal(data, config); err != nil { return nil, fmt.Errorf("failed to parse config file: %v", err) } // Generate cluster secret if not provided and clustering is enabled (Issue #13) if config.ClusteringEnabled && config.ClusterSecret == "" { config.ClusterSecret = generateClusterSecret() fmt.Printf("Warning: No cluster_secret configured. Generated a random secret.\n") fmt.Printf(" To share this secret with other nodes, add it to your config:\n") fmt.Printf(" cluster_secret: %s\n", config.ClusterSecret) } return config, nil }