package config import ( "fmt" "os" "strconv" "strings" ) // Environment represents the deployment environment. type Environment string const ( EnvDev Environment = "dev" EnvStaging Environment = "staging" EnvProd Environment = "prod" ) // TemporalConfig holds Temporal cluster configuration. type TemporalConfig struct { HostPort string // env: TEMPORAL_HOSTPORT Namespace string // env: TEMPORAL_NAMESPACE TLSCert string // env: TEMPORAL_TLS_CERT (file path) TLSKey string // env: TEMPORAL_TLS_KEY (file path) TaskQueue string // env: TEMPORAL_TASK_QUEUE WorkerCount int // env: TEMPORAL_WORKER_COUNT } // MemoryServiceConfig holds memory service connection settings. type MemoryServiceConfig struct { URL string // env: MEMORY_SERVICE_URL JWTToken string // env: MEMORY_SERVICE_JWT_TOKEN } // LLMConfig holds LLM provider settings. type LLMConfig struct { BaseURL string // env: LOCAL_LLM_BASE_URL AnthropicKey string // env: ANTHROPIC_API_KEY AuthToken string // env: LLM_AUTH_TOKEN } // AppConfig holds all application configuration. type AppConfig struct { Env Environment Temporal TemporalConfig MemoryService MemoryServiceConfig LLM LLMConfig LogLevel string // env: LOG_LEVEL } // LoadConfig loads configuration from environment variables with validation. func LoadConfig() (AppConfig, error) { cfg := AppConfig{ Env: parseEnv(getEnvOrDefault("APP_ENV", "dev")), Temporal: TemporalConfig{ HostPort: addDefaultPort(getEnvOrDefault("TEMPORAL_HOSTPORT", defaultTemporalHost())), Namespace: getEnvOrDefault("TEMPORAL_NAMESPACE", "poimen-harness"), TLSCert: os.Getenv("TEMPORAL_TLS_CERT"), TLSKey: os.Getenv("TEMPORAL_TLS_KEY"), TaskQueue: getEnvOrDefault("TEMPORAL_TASK_QUEUE", "poimen-taskqueue"), WorkerCount: getEnvIntOrDefault("TEMPORAL_WORKER_COUNT", 10), }, MemoryService: MemoryServiceConfig{ URL: os.Getenv("MEMORY_SERVICE_URL"), JWTToken: os.Getenv("MEMORY_SERVICE_JWT_TOKEN"), }, LLM: LLMConfig{ BaseURL: os.Getenv("LOCAL_LLM_BASE_URL"), AnthropicKey: os.Getenv("ANTHROPIC_API_KEY"), AuthToken: os.Getenv("LLM_AUTH_TOKEN"), }, LogLevel: getEnvOrDefault("LOG_LEVEL", "info"), } if err := cfg.Validate(); err != nil { return AppConfig{}, err } return cfg, nil } // Validate checks required fields and consistency. func (c *AppConfig) Validate() error { if c.Temporal.HostPort == "" { return fmt.Errorf("TEMPORAL_HOSTPORT is required") } if c.Temporal.Namespace == "" { return fmt.Errorf("TEMPORAL_NAMESPACE is required") } // TLS: both or neither hasCert := c.Temporal.TLSCert != "" hasKey := c.Temporal.TLSKey != "" if hasCert != hasKey { return fmt.Errorf("TEMPORAL_TLS_CERT and TEMPORAL_TLS_KEY must both be set or both empty") } // Validate TLS files exist if specified if hasCert { if _, err := os.Stat(c.Temporal.TLSCert); err != nil { return fmt.Errorf("TEMPORAL_TLS_CERT file not found: %s", c.Temporal.TLSCert) } if _, err := os.Stat(c.Temporal.TLSKey); err != nil { return fmt.Errorf("TEMPORAL_TLS_KEY file not found: %s", c.Temporal.TLSKey) } } // Prod requires LLM key if c.Env == EnvProd { if c.LLM.AnthropicKey == "" && c.LLM.AuthToken == "" { return fmt.Errorf("prod requires ANTHROPIC_API_KEY or LLM_AUTH_TOKEN") } } return nil } // IsProd returns true if running in production. func (c *AppConfig) IsProd() bool { return c.Env == EnvProd } // IsDevOrStaging returns true if running in dev or staging. func (c *AppConfig) IsDevOrStaging() bool { return c.Env == EnvDev || c.Env == EnvStaging } func defaultTemporalHost() string { // In-cluster default vs local if os.Getenv("KUBERNETES_SERVICE_HOST") != "" { return "temporal-frontend.temporal.svc.cluster.local:7233" } return "127.0.0.1:7233" } func parseEnv(s string) Environment { switch strings.ToLower(s) { case "prod", "production": return EnvProd case "staging", "stage": return EnvStaging default: return EnvDev } } func getEnvOrDefault(key, defaultVal string) string { if val := os.Getenv(key); val != "" { return val } return defaultVal } func getEnvIntOrDefault(key string, defaultVal int) int { if val := os.Getenv(key); val != "" { if i, err := strconv.Atoi(val); err == nil { return i } } return defaultVal } func addDefaultPort(hostPort string) string { if !strings.Contains(hostPort, ":") { return hostPort + ":7233" } return hostPort }