package config import ( "nofx/telemetry" "nofx/mcp" "os" "strconv" "strings" ) // Global configuration instance var global *Config // Config is the global configuration (loaded from .env) // Only contains truly global config, trading related config is at trader/strategy level type Config struct { // Service configuration APIServerPort int JWTSecret string // Database configuration DBType string // sqlite or postgres DBPath string // SQLite database file path DBHost string // PostgreSQL host DBPort int // PostgreSQL port DBUser string // PostgreSQL user DBPassword string // PostgreSQL password DBName string // PostgreSQL database name DBSSLMode string // PostgreSQL SSL mode // Security configuration // TransportEncryption enables browser-side encryption for API keys // Requires HTTPS or localhost. Set to false for HTTP access via IP. TransportEncryption bool // Experience improvement (anonymous usage statistics) // Helps us understand product usage and improve the experience // Set EXPERIENCE_IMPROVEMENT=false to disable ExperienceImprovement bool // Market data provider API keys AlpacaAPIKey string // Alpaca API key for US stocks AlpacaSecretKey string // Alpaca secret key TwelveDataKey string // TwelveData API key for forex & metals } // Init initializes global configuration (from .env) func Init() { cfg := &Config{ APIServerPort: 8080, ExperienceImprovement: true, // Default: enabled to help improve the product // Database defaults DBType: "sqlite", DBPath: "data/data.db", DBHost: "localhost", DBPort: 5432, DBUser: "postgres", DBName: "nofx", DBSSLMode: "disable", } // Load from environment variables if v := os.Getenv("JWT_SECRET"); v != "" { cfg.JWTSecret = strings.TrimSpace(v) } if cfg.JWTSecret == "" { cfg.JWTSecret = "default-jwt-secret-change-in-production" } if v := os.Getenv("API_SERVER_PORT"); v != "" { if port, err := strconv.Atoi(v); err == nil && port > 0 { cfg.APIServerPort = port } } // Transport encryption: default false for easier deployment // Set TRANSPORT_ENCRYPTION=true to enable (requires HTTPS or localhost) if v := os.Getenv("TRANSPORT_ENCRYPTION"); v != "" { cfg.TransportEncryption = strings.ToLower(v) == "true" } // Experience improvement: anonymous usage statistics // Default enabled, set EXPERIENCE_IMPROVEMENT=false to disable if v := os.Getenv("EXPERIENCE_IMPROVEMENT"); v != "" { cfg.ExperienceImprovement = strings.ToLower(v) != "false" } // Market data provider API keys cfg.AlpacaAPIKey = os.Getenv("ALPACA_API_KEY") cfg.AlpacaSecretKey = os.Getenv("ALPACA_SECRET_KEY") cfg.TwelveDataKey = os.Getenv("TWELVEDATA_API_KEY") // Database configuration if v := os.Getenv("DB_TYPE"); v != "" { cfg.DBType = strings.ToLower(v) } if v := os.Getenv("DB_PATH"); v != "" { cfg.DBPath = v } if v := os.Getenv("DB_HOST"); v != "" { cfg.DBHost = v } if v := os.Getenv("DB_PORT"); v != "" { if port, err := strconv.Atoi(v); err == nil && port > 0 { cfg.DBPort = port } } if v := os.Getenv("DB_USER"); v != "" { cfg.DBUser = v } if v := os.Getenv("DB_PASSWORD"); v != "" { cfg.DBPassword = v } if v := os.Getenv("DB_NAME"); v != "" { cfg.DBName = v } if v := os.Getenv("DB_SSLMODE"); v != "" { cfg.DBSSLMode = v } global = cfg // Initialize experience improvement (installation ID will be set after database init) telemetry.Init(cfg.ExperienceImprovement, "") // Set up AI token usage tracking callback mcp.TokenUsageCallback = func(usage mcp.TokenUsage) { telemetry.TrackAIUsage(telemetry.AIUsageEvent{ ModelProvider: usage.Provider, ModelName: usage.Model, Channel: usage.Channel(), InputTokens: usage.PromptTokens, OutputTokens: usage.CompletionTokens, }) } } // Get returns the global configuration func Get() *Config { if global == nil { Init() } return global }