package config import ( "fmt" "io/ioutil" "os" "strings" "time" "gopkg.in/yaml.v2" ) // SecurityConfig holds cluster security and election configuration type SecurityConfig struct { // Admin key sharing AdminKeyShares ShamirShare `yaml:"admin_key_shares" json:"admin_key_shares"` ElectionConfig ElectionConfig `yaml:"election_config" json:"election_config"` // Key management KeyRotationDays int `yaml:"key_rotation_days,omitempty" json:"key_rotation_days,omitempty"` AuditLogging bool `yaml:"audit_logging" json:"audit_logging"` AuditPath string `yaml:"audit_path,omitempty" json:"audit_path,omitempty"` } // Config represents the complete configuration for a Bzzz agent type Config struct { HiveAPI HiveAPIConfig `yaml:"hive_api"` Agent AgentConfig `yaml:"agent"` GitHub GitHubConfig `yaml:"github"` P2P P2PConfig `yaml:"p2p"` Logging LoggingConfig `yaml:"logging"` HCFS HCFSConfig `yaml:"hcfs"` Slurp SlurpConfig `yaml:"slurp"` V2 V2Config `yaml:"v2"` // BZZZ v2 protocol settings UCXL UCXLConfig `yaml:"ucxl"` // UCXL protocol settings Security SecurityConfig `yaml:"security"` // Cluster security and elections } // HiveAPIConfig holds Hive system integration settings type HiveAPIConfig struct { BaseURL string `yaml:"base_url"` APIKey string `yaml:"api_key"` Timeout time.Duration `yaml:"timeout"` RetryCount int `yaml:"retry_count"` } // CollaborationConfig holds role-based collaboration settings type CollaborationConfig struct { PreferredMessageTypes []string `yaml:"preferred_message_types"` AutoSubscribeToRoles []string `yaml:"auto_subscribe_to_roles"` AutoSubscribeToExpertise []string `yaml:"auto_subscribe_to_expertise"` ResponseTimeoutSeconds int `yaml:"response_timeout_seconds"` MaxCollaborationDepth int `yaml:"max_collaboration_depth"` EscalationThreshold int `yaml:"escalation_threshold"` CustomTopicSubscriptions []string `yaml:"custom_topic_subscriptions"` } // AgentConfig holds agent-specific configuration type AgentConfig struct { ID string `yaml:"id"` Capabilities []string `yaml:"capabilities"` PollInterval time.Duration `yaml:"poll_interval"` MaxTasks int `yaml:"max_tasks"` Models []string `yaml:"models"` Specialization string `yaml:"specialization"` ModelSelectionWebhook string `yaml:"model_selection_webhook"` DefaultReasoningModel string `yaml:"default_reasoning_model"` SandboxImage string `yaml:"sandbox_image"` // Role-based configuration from Bees-AgenticWorkers Role string `yaml:"role"` SystemPrompt string `yaml:"system_prompt"` ReportsTo []string `yaml:"reports_to"` Expertise []string `yaml:"expertise"` Deliverables []string `yaml:"deliverables"` // Role-based collaboration settings CollaborationSettings CollaborationConfig `yaml:"collaboration"` } // GitHubConfig holds GitHub integration settings type GitHubConfig struct { TokenFile string `yaml:"token_file"` UserAgent string `yaml:"user_agent"` Timeout time.Duration `yaml:"timeout"` RateLimit bool `yaml:"rate_limit"` Assignee string `yaml:"assignee"` } // P2PConfig holds P2P networking configuration type P2PConfig struct { ServiceTag string `yaml:"service_tag"` BzzzTopic string `yaml:"bzzz_topic"` HmmmTopic string `yaml:"hmmm_topic"` DiscoveryTimeout time.Duration `yaml:"discovery_timeout"` // Human escalation settings EscalationWebhook string `yaml:"escalation_webhook"` EscalationKeywords []string `yaml:"escalation_keywords"` ConversationLimit int `yaml:"conversation_limit"` } // LoggingConfig holds logging configuration type LoggingConfig struct { Level string `yaml:"level"` Format string `yaml:"format"` Output string `yaml:"output"` Structured bool `yaml:"structured"` } // V2Config holds BZZZ v2 protocol configuration type V2Config struct { // Enable v2 protocol features Enabled bool `yaml:"enabled" json:"enabled"` // Protocol version ProtocolVersion string `yaml:"protocol_version" json:"protocol_version"` // URI resolution settings URIResolution URIResolutionConfig `yaml:"uri_resolution" json:"uri_resolution"` // DHT settings DHT DHTConfig `yaml:"dht" json:"dht"` // Semantic addressing SemanticAddressing SemanticAddressingConfig `yaml:"semantic_addressing" json:"semantic_addressing"` // Feature flags FeatureFlags map[string]bool `yaml:"feature_flags" json:"feature_flags"` } // URIResolutionConfig holds URI resolution settings type URIResolutionConfig struct { CacheTTL time.Duration `yaml:"cache_ttl" json:"cache_ttl"` MaxPeersPerResult int `yaml:"max_peers_per_result" json:"max_peers_per_result"` DefaultStrategy string `yaml:"default_strategy" json:"default_strategy"` ResolutionTimeout time.Duration `yaml:"resolution_timeout" json:"resolution_timeout"` } // DHTConfig holds DHT-specific configuration type DHTConfig struct { Enabled bool `yaml:"enabled" json:"enabled"` BootstrapPeers []string `yaml:"bootstrap_peers" json:"bootstrap_peers"` Mode string `yaml:"mode" json:"mode"` // "client", "server", "auto" ProtocolPrefix string `yaml:"protocol_prefix" json:"protocol_prefix"` BootstrapTimeout time.Duration `yaml:"bootstrap_timeout" json:"bootstrap_timeout"` DiscoveryInterval time.Duration `yaml:"discovery_interval" json:"discovery_interval"` AutoBootstrap bool `yaml:"auto_bootstrap" json:"auto_bootstrap"` } // SemanticAddressingConfig holds semantic addressing settings type SemanticAddressingConfig struct { EnableWildcards bool `yaml:"enable_wildcards" json:"enable_wildcards"` DefaultAgent string `yaml:"default_agent" json:"default_agent"` DefaultRole string `yaml:"default_role" json:"default_role"` DefaultProject string `yaml:"default_project" json:"default_project"` EnableRoleHierarchy bool `yaml:"enable_role_hierarchy" json:"enable_role_hierarchy"` } // UCXLConfig holds UCXL protocol configuration type UCXLConfig struct { // Enable UCXL protocol Enabled bool `yaml:"enabled" json:"enabled"` // UCXI server configuration Server UCXIServerConfig `yaml:"server" json:"server"` // Address resolution settings Resolution UCXLResolutionConfig `yaml:"resolution" json:"resolution"` // Storage settings Storage UCXLStorageConfig `yaml:"storage" json:"storage"` // P2P integration settings P2PIntegration UCXLP2PConfig `yaml:"p2p_integration" json:"p2p_integration"` } // UCXIServerConfig holds UCXI server settings type UCXIServerConfig struct { Port int `yaml:"port" json:"port"` BasePath string `yaml:"base_path" json:"base_path"` Enabled bool `yaml:"enabled" json:"enabled"` } // UCXLResolutionConfig holds address resolution settings type UCXLResolutionConfig struct { CacheTTL time.Duration `yaml:"cache_ttl" json:"cache_ttl"` EnableWildcards bool `yaml:"enable_wildcards" json:"enable_wildcards"` MaxResults int `yaml:"max_results" json:"max_results"` } // UCXLStorageConfig holds storage settings type UCXLStorageConfig struct { Type string `yaml:"type" json:"type"` // "filesystem", "memory" Directory string `yaml:"directory" json:"directory"` MaxSize int64 `yaml:"max_size" json:"max_size"` // in bytes } // UCXLP2PConfig holds P2P integration settings type UCXLP2PConfig struct { EnableAnnouncement bool `yaml:"enable_announcement" json:"enable_announcement"` EnableDiscovery bool `yaml:"enable_discovery" json:"enable_discovery"` AnnouncementTopic string `yaml:"announcement_topic" json:"announcement_topic"` DiscoveryTimeout time.Duration `yaml:"discovery_timeout" json:"discovery_timeout"` } // HCFSConfig holds HCFS integration configuration type HCFSConfig struct { // API settings APIURL string `yaml:"api_url" json:"api_url"` APITimeout time.Duration `yaml:"api_timeout" json:"api_timeout"` // Workspace settings MountPath string `yaml:"mount_path" json:"mount_path"` WorkspaceTimeout time.Duration `yaml:"workspace_timeout" json:"workspace_timeout"` // FUSE settings FUSEEnabled bool `yaml:"fuse_enabled" json:"fuse_enabled"` FUSEMountPoint string `yaml:"fuse_mount_point" json:"fuse_mount_point"` // Cleanup settings IdleCleanupInterval time.Duration `yaml:"idle_cleanup_interval" json:"idle_cleanup_interval"` MaxIdleTime time.Duration `yaml:"max_idle_time" json:"max_idle_time"` // Storage settings StoreArtifacts bool `yaml:"store_artifacts" json:"store_artifacts"` CompressArtifacts bool `yaml:"compress_artifacts" json:"compress_artifacts"` // Enable/disable HCFS integration Enabled bool `yaml:"enabled" json:"enabled"` } // LoadConfig loads configuration from file, environment variables, and defaults func LoadConfig(configPath string) (*Config, error) { // Start with defaults config := getDefaultConfig() // Load from file if it exists if configPath != "" && fileExists(configPath) { if err := loadFromFile(config, configPath); err != nil { return nil, fmt.Errorf("failed to load config file: %w", err) } } // Override with environment variables if err := loadFromEnv(config); err != nil { return nil, fmt.Errorf("failed to load environment variables: %w", err) } // Validate configuration if err := validateConfig(config); err != nil { return nil, fmt.Errorf("invalid configuration: %w", err) } return config, nil } // getDefaultConfig returns the default configuration func getDefaultConfig() *Config { return &Config{ HiveAPI: HiveAPIConfig{ BaseURL: "https://hive.home.deepblack.cloud", Timeout: 30 * time.Second, RetryCount: 3, }, Agent: AgentConfig{ Capabilities: []string{"general", "reasoning", "task-coordination"}, PollInterval: 30 * time.Second, MaxTasks: 3, Models: []string{"phi3", "llama3.1"}, Specialization: "general_developer", ModelSelectionWebhook: "https://n8n.home.deepblack.cloud/webhook/model-selection", DefaultReasoningModel: "phi3", SandboxImage: "registry.home.deepblack.cloud/tony/bzzz-sandbox:latest", }, GitHub: GitHubConfig{ TokenFile: "/home/tony/chorus/business/secrets/gh-token", UserAgent: "Bzzz-P2P-Agent/1.0", Timeout: 30 * time.Second, RateLimit: true, Assignee: "anthonyrawlins", }, P2P: P2PConfig{ ServiceTag: "bzzz-peer-discovery", BzzzTopic: "bzzz/coordination/v1", HmmmTopic: "hmmm/meta-discussion/v1", DiscoveryTimeout: 10 * time.Second, EscalationWebhook: "https://n8n.home.deepblack.cloud/webhook-test/human-escalation", EscalationKeywords: []string{"stuck", "help", "human", "escalate", "clarification needed", "manual intervention"}, ConversationLimit: 10, }, Logging: LoggingConfig{ Level: "info", Format: "text", Output: "stdout", Structured: false, }, HCFS: HCFSConfig{ APIURL: "http://localhost:8000", APITimeout: 30 * time.Second, MountPath: "/tmp/hcfs-workspaces", WorkspaceTimeout: 2 * time.Hour, FUSEEnabled: false, FUSEMountPoint: "/mnt/hcfs", IdleCleanupInterval: 15 * time.Minute, MaxIdleTime: 1 * time.Hour, StoreArtifacts: true, CompressArtifacts: false, Enabled: true, }, Slurp: GetDefaultSlurpConfig(), UCXL: UCXLConfig{ Enabled: false, // Disabled by default Server: UCXIServerConfig{ Port: 8081, BasePath: "/bzzz", Enabled: true, }, Resolution: UCXLResolutionConfig{ CacheTTL: 5 * time.Minute, EnableWildcards: true, MaxResults: 50, }, Storage: UCXLStorageConfig{ Type: "filesystem", Directory: "/tmp/bzzz-ucxl-storage", MaxSize: 100 * 1024 * 1024, // 100MB }, P2PIntegration: UCXLP2PConfig{ EnableAnnouncement: true, EnableDiscovery: true, AnnouncementTopic: "bzzz/ucxl/announcement/v1", DiscoveryTimeout: 30 * time.Second, }, }, Security: SecurityConfig{ AdminKeyShares: ShamirShare{ Threshold: 3, TotalShares: 5, }, ElectionConfig: ElectionConfig{ HeartbeatTimeout: 5 * time.Second, DiscoveryTimeout: 30 * time.Second, ElectionTimeout: 15 * time.Second, MaxDiscoveryAttempts: 6, DiscoveryBackoff: 5 * time.Second, MinimumQuorum: 3, ConsensusAlgorithm: "raft", SplitBrainDetection: true, ConflictResolution: "highest_uptime", }, KeyRotationDays: 90, AuditLogging: true, AuditPath: ".bzzz/security-audit.log", }, V2: V2Config{ Enabled: false, // Disabled by default for backward compatibility ProtocolVersion: "2.0.0", URIResolution: URIResolutionConfig{ CacheTTL: 5 * time.Minute, MaxPeersPerResult: 5, DefaultStrategy: "best_match", ResolutionTimeout: 30 * time.Second, }, DHT: DHTConfig{ Enabled: false, // Disabled by default BootstrapPeers: []string{}, Mode: "auto", ProtocolPrefix: "/bzzz", BootstrapTimeout: 30 * time.Second, DiscoveryInterval: 60 * time.Second, AutoBootstrap: false, }, SemanticAddressing: SemanticAddressingConfig{ EnableWildcards: true, DefaultAgent: "any", DefaultRole: "any", DefaultProject: "any", EnableRoleHierarchy: true, }, FeatureFlags: map[string]bool{ "uri_protocol": false, "semantic_addressing": false, "dht_discovery": false, "advanced_resolution": false, }, }, } } // loadFromFile loads configuration from a YAML file func loadFromFile(config *Config, filePath string) error { data, err := ioutil.ReadFile(filePath) if err != nil { return fmt.Errorf("failed to read config file: %w", err) } if err := yaml.Unmarshal(data, config); err != nil { return fmt.Errorf("failed to parse YAML config: %w", err) } return nil } // loadFromEnv loads configuration from environment variables func loadFromEnv(config *Config) error { // Hive API configuration if url := os.Getenv("BZZZ_HIVE_API_URL"); url != "" { config.HiveAPI.BaseURL = url } if apiKey := os.Getenv("BZZZ_HIVE_API_KEY"); apiKey != "" { config.HiveAPI.APIKey = apiKey } // Agent configuration if agentID := os.Getenv("BZZZ_AGENT_ID"); agentID != "" { config.Agent.ID = agentID } if capabilities := os.Getenv("BZZZ_AGENT_CAPABILITIES"); capabilities != "" { config.Agent.Capabilities = strings.Split(capabilities, ",") } if specialization := os.Getenv("BZZZ_AGENT_SPECIALIZATION"); specialization != "" { config.Agent.Specialization = specialization } if modelWebhook := os.Getenv("BZZZ_MODEL_SELECTION_WEBHOOK"); modelWebhook != "" { config.Agent.ModelSelectionWebhook = modelWebhook } // GitHub configuration if tokenFile := os.Getenv("BZZZ_GITHUB_TOKEN_FILE"); tokenFile != "" { config.GitHub.TokenFile = tokenFile } // P2P configuration if webhook := os.Getenv("BZZZ_ESCALATION_WEBHOOK"); webhook != "" { config.P2P.EscalationWebhook = webhook } // Logging configuration if level := os.Getenv("BZZZ_LOG_LEVEL"); level != "" { config.Logging.Level = level } // SLURP configuration if slurpURL := os.Getenv("BZZZ_SLURP_URL"); slurpURL != "" { config.Slurp.BaseURL = slurpURL } if slurpKey := os.Getenv("BZZZ_SLURP_API_KEY"); slurpKey != "" { config.Slurp.APIKey = slurpKey } if slurpEnabled := os.Getenv("BZZZ_SLURP_ENABLED"); slurpEnabled == "true" { config.Slurp.Enabled = true } // UCXL protocol configuration if ucxlEnabled := os.Getenv("BZZZ_UCXL_ENABLED"); ucxlEnabled == "true" { config.UCXL.Enabled = true } if ucxiPort := os.Getenv("BZZZ_UCXI_PORT"); ucxiPort != "" { // Would need strconv.Atoi but keeping simple for now // In production, add proper integer parsing } // V2 protocol configuration if v2Enabled := os.Getenv("BZZZ_V2_ENABLED"); v2Enabled == "true" { config.V2.Enabled = true } if dhtEnabled := os.Getenv("BZZZ_DHT_ENABLED"); dhtEnabled == "true" { config.V2.DHT.Enabled = true } if dhtMode := os.Getenv("BZZZ_DHT_MODE"); dhtMode != "" { config.V2.DHT.Mode = dhtMode } if bootstrapPeers := os.Getenv("BZZZ_DHT_BOOTSTRAP_PEERS"); bootstrapPeers != "" { config.V2.DHT.BootstrapPeers = strings.Split(bootstrapPeers, ",") } return nil } // validateConfig validates the configuration values func validateConfig(config *Config) error { // Validate required fields if config.HiveAPI.BaseURL == "" { return fmt.Errorf("hive_api.base_url is required") } // Note: Agent.ID can be empty - it will be auto-generated from node ID in main.go if len(config.Agent.Capabilities) == 0 { return fmt.Errorf("agent.capabilities cannot be empty") } if config.Agent.PollInterval <= 0 { return fmt.Errorf("agent.poll_interval must be positive") } if config.Agent.MaxTasks <= 0 { return fmt.Errorf("agent.max_tasks must be positive") } // Validate GitHub token file exists if specified if config.GitHub.TokenFile != "" && !fileExists(config.GitHub.TokenFile) { return fmt.Errorf("github token file does not exist: %s", config.GitHub.TokenFile) } // Validate SLURP configuration if err := ValidateSlurpConfig(config.Slurp); err != nil { return fmt.Errorf("slurp configuration invalid: %w", err) } return nil } // SaveConfig saves the configuration to a YAML file func SaveConfig(config *Config, filePath string) error { data, err := yaml.Marshal(config) if err != nil { return fmt.Errorf("failed to marshal config to YAML: %w", err) } if err := ioutil.WriteFile(filePath, data, 0644); err != nil { return fmt.Errorf("failed to write config file: %w", err) } return nil } // GetGitHubToken reads the GitHub token from the configured file func (c *Config) GetGitHubToken() (string, error) { if c.GitHub.TokenFile == "" { return "", fmt.Errorf("no GitHub token file configured") } tokenBytes, err := ioutil.ReadFile(c.GitHub.TokenFile) if err != nil { return "", fmt.Errorf("failed to read GitHub token: %w", err) } return strings.TrimSpace(string(tokenBytes)), nil } // fileExists checks if a file exists func fileExists(filePath string) bool { _, err := os.Stat(filePath) return err == nil } // GenerateDefaultConfigFile creates a default configuration file func GenerateDefaultConfigFile(filePath string) error { config := getDefaultConfig() return SaveConfig(config, filePath) }