Integrate BACKBEAT SDK and resolve KACHING license validation
Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,25 +2,28 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This is a container-adapted version of BZZZ's config system
|
||||
// This is a container-adapted version of CHORUS's config system
|
||||
// All configuration comes from environment variables instead of YAML files
|
||||
|
||||
// Config represents the complete CHORUS configuration loaded from environment variables
|
||||
type Config struct {
|
||||
Agent AgentConfig `yaml:"agent"`
|
||||
Network NetworkConfig `yaml:"network"`
|
||||
License LicenseConfig `yaml:"license"`
|
||||
AI AIConfig `yaml:"ai"`
|
||||
Logging LoggingConfig `yaml:"logging"`
|
||||
V2 V2Config `yaml:"v2"`
|
||||
UCXL UCXLConfig `yaml:"ucxl"`
|
||||
Slurp SlurpConfig `yaml:"slurp"`
|
||||
Agent AgentConfig `yaml:"agent"`
|
||||
Network NetworkConfig `yaml:"network"`
|
||||
License LicenseConfig `yaml:"license"`
|
||||
AI AIConfig `yaml:"ai"`
|
||||
Logging LoggingConfig `yaml:"logging"`
|
||||
V2 V2Config `yaml:"v2"`
|
||||
UCXL UCXLConfig `yaml:"ucxl"`
|
||||
Slurp SlurpConfig `yaml:"slurp"`
|
||||
Security SecurityConfig `yaml:"security"`
|
||||
WHOOSHAPI WHOOSHAPIConfig `yaml:"whoosh_api"`
|
||||
}
|
||||
|
||||
// AgentConfig defines agent-specific settings
|
||||
@@ -46,10 +49,9 @@ type NetworkConfig struct {
|
||||
BindAddr string `yaml:"bind_address"`
|
||||
}
|
||||
|
||||
// LicenseConfig defines licensing settings (adapted from BZZZ)
|
||||
// LicenseConfig defines licensing settings (adapted from CHORUS)
|
||||
type LicenseConfig struct {
|
||||
Email string `yaml:"email"`
|
||||
LicenseKey string `yaml:"license_key"`
|
||||
LicenseID string `yaml:"license_id"`
|
||||
ClusterID string `yaml:"cluster_id"`
|
||||
OrganizationName string `yaml:"organization_name"`
|
||||
KachingURL string `yaml:"kaching_url"`
|
||||
@@ -63,7 +65,9 @@ type LicenseConfig struct {
|
||||
|
||||
// AIConfig defines AI service settings
|
||||
type AIConfig struct {
|
||||
Ollama OllamaConfig `yaml:"ollama"`
|
||||
Provider string `yaml:"provider"`
|
||||
Ollama OllamaConfig `yaml:"ollama"`
|
||||
ResetData ResetDataConfig `yaml:"resetdata"`
|
||||
}
|
||||
|
||||
// OllamaConfig defines Ollama-specific settings
|
||||
@@ -72,13 +76,21 @@ type OllamaConfig struct {
|
||||
Timeout time.Duration `yaml:"timeout"`
|
||||
}
|
||||
|
||||
// ResetDataConfig defines ResetData LLM service settings
|
||||
type ResetDataConfig struct {
|
||||
BaseURL string `yaml:"base_url"`
|
||||
APIKey string `yaml:"api_key"`
|
||||
Model string `yaml:"model"`
|
||||
Timeout time.Duration `yaml:"timeout"`
|
||||
}
|
||||
|
||||
// LoggingConfig defines logging settings
|
||||
type LoggingConfig struct {
|
||||
Level string `yaml:"level"`
|
||||
Format string `yaml:"format"`
|
||||
}
|
||||
|
||||
// V2Config defines v2-specific settings (from BZZZ)
|
||||
// V2Config defines v2-specific settings (from CHORUS)
|
||||
type V2Config struct {
|
||||
DHT DHTConfig `yaml:"dht"`
|
||||
}
|
||||
@@ -119,6 +131,14 @@ type SlurpConfig struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
// WHOOSHAPIConfig defines WHOOSH API integration settings
|
||||
type WHOOSHAPIConfig struct {
|
||||
URL string `yaml:"url"`
|
||||
BaseURL string `yaml:"base_url"`
|
||||
Token string `yaml:"token"`
|
||||
Enabled bool `yaml:"enabled"`
|
||||
}
|
||||
|
||||
// LoadFromEnvironment loads configuration from environment variables
|
||||
func LoadFromEnvironment() (*Config, error) {
|
||||
cfg := &Config{
|
||||
@@ -127,13 +147,13 @@ func LoadFromEnvironment() (*Config, error) {
|
||||
Specialization: getEnvOrDefault("CHORUS_SPECIALIZATION", "general_developer"),
|
||||
MaxTasks: getEnvIntOrDefault("CHORUS_MAX_TASKS", 3),
|
||||
Capabilities: getEnvArrayOrDefault("CHORUS_CAPABILITIES", []string{"general_development", "task_coordination"}),
|
||||
Models: getEnvArrayOrDefault("CHORUS_MODELS", []string{"llama3.1:8b"}),
|
||||
Models: getEnvArrayOrDefault("CHORUS_MODELS", []string{"meta/llama-3.1-8b-instruct"}),
|
||||
Role: getEnvOrDefault("CHORUS_ROLE", ""),
|
||||
Expertise: getEnvArrayOrDefault("CHORUS_EXPERTISE", []string{}),
|
||||
ReportsTo: getEnvOrDefault("CHORUS_REPORTS_TO", ""),
|
||||
Deliverables: getEnvArrayOrDefault("CHORUS_DELIVERABLES", []string{}),
|
||||
ModelSelectionWebhook: getEnvOrDefault("CHORUS_MODEL_SELECTION_WEBHOOK", ""),
|
||||
DefaultReasoningModel: getEnvOrDefault("CHORUS_DEFAULT_REASONING_MODEL", "llama3.1:8b"),
|
||||
DefaultReasoningModel: getEnvOrDefault("CHORUS_DEFAULT_REASONING_MODEL", "meta/llama-3.1-8b-instruct"),
|
||||
},
|
||||
Network: NetworkConfig{
|
||||
P2PPort: getEnvIntOrDefault("CHORUS_P2P_PORT", 9000),
|
||||
@@ -142,8 +162,7 @@ func LoadFromEnvironment() (*Config, error) {
|
||||
BindAddr: getEnvOrDefault("CHORUS_BIND_ADDRESS", "0.0.0.0"),
|
||||
},
|
||||
License: LicenseConfig{
|
||||
Email: os.Getenv("CHORUS_LICENSE_EMAIL"),
|
||||
LicenseKey: os.Getenv("CHORUS_LICENSE_KEY"),
|
||||
LicenseID: getEnvOrFileContent("CHORUS_LICENSE_ID", "CHORUS_LICENSE_ID_FILE"),
|
||||
ClusterID: getEnvOrDefault("CHORUS_CLUSTER_ID", "default-cluster"),
|
||||
OrganizationName: getEnvOrDefault("CHORUS_ORGANIZATION_NAME", ""),
|
||||
KachingURL: getEnvOrDefault("CHORUS_KACHING_URL", "https://kaching.chorus.services"),
|
||||
@@ -151,10 +170,17 @@ func LoadFromEnvironment() (*Config, error) {
|
||||
GracePeriodHours: getEnvIntOrDefault("CHORUS_GRACE_PERIOD_HOURS", 72),
|
||||
},
|
||||
AI: AIConfig{
|
||||
Provider: getEnvOrDefault("CHORUS_AI_PROVIDER", "resetdata"),
|
||||
Ollama: OllamaConfig{
|
||||
Endpoint: getEnvOrDefault("OLLAMA_ENDPOINT", "http://localhost:11434"),
|
||||
Timeout: getEnvDurationOrDefault("OLLAMA_TIMEOUT", 30*time.Second),
|
||||
},
|
||||
ResetData: ResetDataConfig{
|
||||
BaseURL: getEnvOrDefault("RESETDATA_BASE_URL", "https://models.au-syd.resetdata.ai/v1"),
|
||||
APIKey: os.Getenv("RESETDATA_API_KEY"),
|
||||
Model: getEnvOrDefault("RESETDATA_MODEL", "meta/llama-3.1-8b-instruct"),
|
||||
Timeout: getEnvDurationOrDefault("RESETDATA_TIMEOUT", 30*time.Second),
|
||||
},
|
||||
},
|
||||
Logging: LoggingConfig{
|
||||
Level: getEnvOrDefault("LOG_LEVEL", "info"),
|
||||
@@ -183,6 +209,29 @@ func LoadFromEnvironment() (*Config, error) {
|
||||
Slurp: SlurpConfig{
|
||||
Enabled: getEnvBoolOrDefault("CHORUS_SLURP_ENABLED", false),
|
||||
},
|
||||
Security: SecurityConfig{
|
||||
KeyRotationDays: getEnvIntOrDefault("CHORUS_KEY_ROTATION_DAYS", 30),
|
||||
AuditLogging: getEnvBoolOrDefault("CHORUS_AUDIT_LOGGING", true),
|
||||
AuditPath: getEnvOrDefault("CHORUS_AUDIT_PATH", "/tmp/chorus-audit.log"),
|
||||
ElectionConfig: ElectionConfig{
|
||||
DiscoveryTimeout: getEnvDurationOrDefault("CHORUS_DISCOVERY_TIMEOUT", 10*time.Second),
|
||||
HeartbeatTimeout: getEnvDurationOrDefault("CHORUS_HEARTBEAT_TIMEOUT", 30*time.Second),
|
||||
ElectionTimeout: getEnvDurationOrDefault("CHORUS_ELECTION_TIMEOUT", 60*time.Second),
|
||||
DiscoveryBackoff: getEnvDurationOrDefault("CHORUS_DISCOVERY_BACKOFF", 5*time.Second),
|
||||
LeadershipScoring: &LeadershipScoring{
|
||||
UptimeWeight: 0.4,
|
||||
CapabilityWeight: 0.3,
|
||||
ExperienceWeight: 0.2,
|
||||
LoadWeight: 0.1,
|
||||
},
|
||||
},
|
||||
},
|
||||
WHOOSHAPI: WHOOSHAPIConfig{
|
||||
URL: getEnvOrDefault("WHOOSH_API_URL", "http://localhost:3000"),
|
||||
BaseURL: getEnvOrDefault("WHOOSH_API_BASE_URL", "http://localhost:3000"),
|
||||
Token: os.Getenv("WHOOSH_API_TOKEN"),
|
||||
Enabled: getEnvBoolOrDefault("WHOOSH_API_ENABLED", false),
|
||||
},
|
||||
}
|
||||
|
||||
// Validate required configuration
|
||||
@@ -195,12 +244,8 @@ func LoadFromEnvironment() (*Config, error) {
|
||||
|
||||
// Validate ensures all required configuration is present
|
||||
func (c *Config) Validate() error {
|
||||
if c.License.Email == "" {
|
||||
return fmt.Errorf("CHORUS_LICENSE_EMAIL is required")
|
||||
}
|
||||
|
||||
if c.License.LicenseKey == "" {
|
||||
return fmt.Errorf("CHORUS_LICENSE_KEY is required")
|
||||
if c.License.LicenseID == "" {
|
||||
return fmt.Errorf("CHORUS_LICENSE_ID is required")
|
||||
}
|
||||
|
||||
if c.Agent.ID == "" {
|
||||
@@ -217,16 +262,16 @@ func (c *Config) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyRoleDefinition applies role-based configuration (from BZZZ)
|
||||
// ApplyRoleDefinition applies role-based configuration (from CHORUS)
|
||||
func (c *Config) ApplyRoleDefinition(role string) error {
|
||||
// This would contain the role definition logic from BZZZ
|
||||
// This would contain the role definition logic from CHORUS
|
||||
c.Agent.Role = role
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRoleAuthority returns the authority level for a role (from BZZZ)
|
||||
// GetRoleAuthority returns the authority level for a role (from CHORUS)
|
||||
func (c *Config) GetRoleAuthority(role string) (string, error) {
|
||||
// This would contain the authority mapping from BZZZ
|
||||
// This would contain the authority mapping from CHORUS
|
||||
switch role {
|
||||
case "admin":
|
||||
return "master", nil
|
||||
@@ -278,6 +323,23 @@ func getEnvArrayOrDefault(key string, defaultValue []string) []string {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// getEnvOrFileContent reads from environment variable or file (for Docker secrets support)
|
||||
func getEnvOrFileContent(envKey, fileEnvKey string) string {
|
||||
// First try the direct environment variable
|
||||
if value := os.Getenv(envKey); value != "" {
|
||||
return value
|
||||
}
|
||||
|
||||
// Then try reading from file path specified in fileEnvKey
|
||||
if filePath := os.Getenv(fileEnvKey); filePath != "" {
|
||||
if content, err := ioutil.ReadFile(filePath); err == nil {
|
||||
return strings.TrimSpace(string(content))
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsSetupRequired checks if setup is required (always false for containers)
|
||||
func IsSetupRequired(configPath string) bool {
|
||||
return false // Containers are always pre-configured via environment
|
||||
@@ -285,5 +347,17 @@ func IsSetupRequired(configPath string) bool {
|
||||
|
||||
// IsValidConfiguration validates configuration (simplified for containers)
|
||||
func IsValidConfiguration(cfg *Config) bool {
|
||||
return cfg.License.Email != "" && cfg.License.LicenseKey != ""
|
||||
return cfg.License.LicenseID != "" && cfg.License.ClusterID != ""
|
||||
}
|
||||
|
||||
// LoadConfig loads configuration from file (for API compatibility)
|
||||
func LoadConfig(configPath string) (*Config, error) {
|
||||
// For containers, always load from environment
|
||||
return LoadFromEnvironment()
|
||||
}
|
||||
|
||||
// SaveConfig saves configuration to file (stub for API compatibility)
|
||||
func SaveConfig(cfg *Config, configPath string) error {
|
||||
// For containers, configuration is environment-based, so this is a no-op
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user