package config import ( "bytes" "encoding/hex" "errors" "fmt" "io" "net/url" "os" "path/filepath" "strconv" "strings" "time" ) const ( EnvironmentLocal = "local" EnvironmentTest = "test" EnvironmentDev = "dev" EnvironmentStage = "stage" EnvironmentProd = "prod" ) type Config struct { Onboarding OnboardingConfig VikingResourceID string VikingCollectionName string VikingAPIKey string VikingEventType string VikingProfileType string ServiceName string Environment string LogLevel string HTTPAddr string DatabaseURL string RedisAddr string RedisPassword string RedisDB int AuthIssuerURL string AuthJWKSURL string AuthAudience string OTLPEndpoint string RateLimitRPS float64 RateLimitBurst int RateLimitUserRPS float64 RateLimitUserBurst int RateLimitTenantRPS float64 RateLimitTenantBurst int RateLimitDeviceRPS float64 RateLimitDeviceBurst int ProviderStartRPS float64 ProviderStartBurst int ProviderMaxActiveTasks int DeviceLocalSecret string DeviceCredentialMasterKey string ContentModelMasterKey string DeviceClockSkew time.Duration RTCAppID string RTCAppKey string RTCTokenIssuer string RTCTokenSecret string CursorSigningSecret string ShareTokenSecret string RTCTokenTTL time.Duration AllocationTTL time.Duration HeartbeatInterval int ProviderCallbackSecret string SubtitleCallbackURL string SubtitleCallbackSecret string SubtitlePrimary bool ProviderCallbackSkew time.Duration PlatformHealthSecret string PlatformHealthSkew time.Duration MockProviderBaseURL string MockProviderAPIKey string VoiceProvider string VolcRTCBaseURL string VolcAccessKeyID string VolcSecretAccessKey string VolcSessionToken string VolcRTCRegion string VolcRTCAPIVersion string VolcVoiceChatTemplate string VolcHardwareAgentBotID string VolcHardwareTransport string VolcHardwareMinimumSDKVersion string VolcHardwareProvisioningEnabled bool VolcHardwareInstanceID string VolcHardwareProductKey string VolcHardwareProductSecret string VolcHardwareDeviceCredentialMasterKey string VolcHardwareRegisterURL string VolcHardwareRegisterTimeout time.Duration VolcHardwareDeviceNamePrefix string ProviderRequestTimeout time.Duration ProviderFailureThreshold int ProviderCircuitCooldown time.Duration DependencyTimeout time.Duration ExternalProbeInterval time.Duration ShutdownTimeout time.Duration WorkerPollInterval time.Duration RecoveryScanInterval time.Duration RecoveryStartingAfter time.Duration RecoveryStoppingAfter time.Duration ObjectStoreEndpoint string ObjectStorePublic string ObjectStoreAccessKey string ObjectStoreSecretKey string ObjectStoreBucket string ObjectStoreRegion string ObjectStoreSecure bool ObjectStorePublicSecure bool ExportDownloadTTL time.Duration ExportObjectTTL time.Duration MediaLocalDeleteGrace time.Duration AlertWebhookURL string AlertWebhookSecret string AlertWebhookTimeout time.Duration ContentGenerator string ArkBaseURL string ArkAPIKey string ArkContentModel string ArkRequestTimeout time.Duration ContentSafetyProvider string ContentSafetyEndpoint string ContentSafetyAPIKey string ContentSafetyPolicyRef string ContentSafetyTimeout time.Duration EmbeddingProvider string ArkEmbeddingModel string MemoryToolBaseURL string MemoryToolSigningSecret string MemoryToolTokenTTL time.Duration MemoryToolRateRPS float64 MemoryToolRateBurst int ProviderMemoryMode string ProviderMemoryBaseURL string ProviderMemoryAPIKey string ProviderMemoryTimeout time.Duration } func Load(serviceName string) (Config, error) { if strings.TrimSpace(serviceName) == "" { return Config{}, errors.New("service name is required") } redisDB, err := intEnv("REDIS_DB", 0) if err != nil { return Config{}, err } dependencyTimeout, err := durationEnv("DEPENDENCY_TIMEOUT", 2*time.Second) if err != nil { return Config{}, err } externalProbeInterval, err := durationEnv("EXTERNAL_PROBE_INTERVAL", 30*time.Second) if err != nil { return Config{}, err } shutdownTimeout, err := durationEnv("SHUTDOWN_TIMEOUT", 10*time.Second) if err != nil { return Config{}, err } pollInterval, err := durationEnv("WORKER_POLL_INTERVAL", 2*time.Second) if err != nil { return Config{}, err } recoveryScanInterval, err := durationEnv("RECOVERY_SCAN_INTERVAL", 5*time.Second) if err != nil { return Config{}, err } recoveryStartingAfter, err := durationEnv("RECOVERY_STARTING_AFTER", 20*time.Second) if err != nil { return Config{}, err } recoveryStoppingAfter, err := durationEnv("RECOVERY_STOPPING_AFTER", 20*time.Second) if err != nil { return Config{}, err } deviceClockSkew, err := durationEnv("DEVICE_CLOCK_SKEW", 5*time.Minute) if err != nil { return Config{}, err } rtcTokenTTL, err := durationEnv("RTC_TOKEN_TTL", 5*time.Minute) if err != nil { return Config{}, err } allocationTTL, err := durationEnv("CONVERSATION_ALLOCATION_TTL", 30*time.Second) if err != nil { return Config{}, err } providerCallbackSkew, err := durationEnv("PROVIDER_CALLBACK_CLOCK_SKEW", 5*time.Minute) if err != nil { return Config{}, err } platformHealthSkew, err := durationEnv("PLATFORM_HEALTH_CALLBACK_CLOCK_SKEW", 5*time.Minute) if err != nil { return Config{}, err } providerRequestTimeout, err := durationEnv("PROVIDER_REQUEST_TIMEOUT", 5*time.Second) if err != nil { return Config{}, err } providerCircuitCooldown, err := durationEnv("PROVIDER_CIRCUIT_COOLDOWN", time.Minute) if err != nil { return Config{}, err } exportDownloadTTL, err := durationEnv("EXPORT_DOWNLOAD_TTL", 10*time.Minute) if err != nil { return Config{}, err } exportObjectTTL, err := durationEnv("EXPORT_OBJECT_TTL", 24*time.Hour) if err != nil { return Config{}, err } mediaLocalDeleteGrace, err := durationEnv("MEDIA_LOCAL_DELETE_GRACE", time.Hour) if err != nil { return Config{}, err } alertWebhookTimeout, err := durationEnv("ALERT_WEBHOOK_TIMEOUT", 5*time.Second) if err != nil { return Config{}, err } arkRequestTimeout, err := durationEnv("ARK_REQUEST_TIMEOUT", 30*time.Second) if err != nil { return Config{}, err } contentSafetyTimeout, err := durationEnv("CONTENT_SAFETY_TIMEOUT", 5*time.Second) if err != nil { return Config{}, err } providerMemoryTimeout, err := durationEnv("PROVIDER_MEMORY_TIMEOUT", 5*time.Second) if err != nil { return Config{}, err } memoryToolTokenTTL, err := durationEnv("MEMORY_TOOL_TOKEN_TTL", 4*time.Hour) if err != nil { return Config{}, err } volcHardwareProvisioningEnabled, err := boolEnv("VOLC_HARDWARE_PROVISIONING_ENABLED", false) if err != nil { return Config{}, err } volcHardwareRegisterTimeout, err := durationEnv("VOLC_HARDWARE_REGISTER_TIMEOUT", 10*time.Second) if err != nil { return Config{}, err } objectStoreSecure, err := boolEnv("OBJECT_STORE_SECURE", false) if err != nil { return Config{}, err } objectStorePublicSecure, err := boolEnv("OBJECT_STORE_PUBLIC_SECURE", objectStoreSecure) if err != nil { return Config{}, err } environment := stringEnv("APP_ENV", EnvironmentLocal) secretValues, err := loadSecretValues( "ONBOARDING_MASTER_KEY", "ONBOARDING_SMS_GATEWAY_TOKEN", "DATABASE_URL", "REDIS_PASSWORD", "DEVICE_LOCAL_SECRET", "DEVICE_CREDENTIAL_MASTER_KEY", "MODEL_CREDENTIAL_MASTER_KEY", "RTC_APP_KEY", "RTC_TOKEN_SIGNING_SECRET", "CURSOR_SIGNING_SECRET", "SHARE_TOKEN_SIGNING_SECRET", "PROVIDER_CALLBACK_SECRET", "PLATFORM_HEALTH_CALLBACK_SECRET", "VOLC_SUBTITLE_CALLBACK_SECRET", "VIKING_MEMORY_API_KEY", "MOCK_PROVIDER_API_KEY", "VOLC_ACCESS_KEY_ID", "VOLC_SECRET_ACCESS_KEY", "VOLC_SESSION_TOKEN", "VOLC_HARDWARE_PRODUCT_SECRET", "VOLC_HARDWARE_DEVICE_CREDENTIAL_MASTER_KEY", "OBJECT_STORE_ACCESS_KEY", "OBJECT_STORE_SECRET_KEY", "ALERT_WEBHOOK_SECRET", "ARK_API_KEY", "CONTENT_SAFETY_API_KEY", "MEMORY_TOOL_SIGNING_SECRET", "PROVIDER_MEMORY_API_KEY", ) if err != nil { return Config{}, err } deviceLocalSecret := secretValues["DEVICE_LOCAL_SECRET"] deviceCredentialMasterKey := secretValues["DEVICE_CREDENTIAL_MASTER_KEY"] contentModelMasterKey := secretValues["MODEL_CREDENTIAL_MASTER_KEY"] rtcTokenSecret := secretValues["RTC_TOKEN_SIGNING_SECRET"] cursorSigningSecret := secretValues["CURSOR_SIGNING_SECRET"] shareTokenSecret := secretValues["SHARE_TOKEN_SIGNING_SECRET"] providerCallbackSecret := secretValues["PROVIDER_CALLBACK_SECRET"] objectStoreEndpoint := strings.TrimSpace(os.Getenv("OBJECT_STORE_ENDPOINT")) objectStorePublic := strings.TrimSpace(os.Getenv("OBJECT_STORE_PUBLIC_ENDPOINT")) objectStoreAccessKey := secretValues["OBJECT_STORE_ACCESS_KEY"] objectStoreSecretKey := secretValues["OBJECT_STORE_SECRET_KEY"] objectStoreBucket := strings.TrimSpace(os.Getenv("OBJECT_STORE_EXPORT_BUCKET")) objectStoreRegion := strings.TrimSpace(os.Getenv("OBJECT_STORE_REGION")) if deviceLocalSecret == "" && serviceName == "api" && (environment == EnvironmentLocal || environment == EnvironmentTest) { deviceLocalSecret = "local-simulated-device-secret-32-bytes-minimum" } if deviceCredentialMasterKey == "" && serviceName == "api" && (environment == EnvironmentLocal || environment == EnvironmentTest) { deviceCredentialMasterKey = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" } if contentModelMasterKey == "" && (serviceName == "api" || serviceName == "worker") && (environment == EnvironmentLocal || environment == EnvironmentTest) { contentModelMasterKey = "1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100" } if rtcTokenSecret == "" && serviceName == "api" && (environment == EnvironmentLocal || environment == EnvironmentTest) { rtcTokenSecret = "local-simulated-rtc-token-secret-32-bytes-minimum" } if cursorSigningSecret == "" && serviceName == "api" && (environment == EnvironmentLocal || environment == EnvironmentTest) { cursorSigningSecret = "local-signed-pagination-cursor-secret-32-bytes-minimum" } if shareTokenSecret == "" && serviceName == "api" && (environment == EnvironmentLocal || environment == EnvironmentTest) { shareTokenSecret = "local-share-token-signing-secret-32-bytes-minimum" } memoryToolSigningSecret := secretValues["MEMORY_TOOL_SIGNING_SECRET"] if memoryToolSigningSecret == "" && (serviceName == "api" || serviceName == "worker") && (environment == EnvironmentLocal || environment == EnvironmentTest) { memoryToolSigningSecret = "local-memory-tool-signing-secret-32-bytes-minimum" } if providerCallbackSecret == "" && serviceName == "callbacks" && (environment == EnvironmentLocal || environment == EnvironmentTest) { providerCallbackSecret = "local-provider-callback-secret-32-bytes-minimum" } if (serviceName == "api" || serviceName == "worker") && (environment == EnvironmentLocal || environment == EnvironmentTest) { if objectStoreEndpoint == "" { objectStoreEndpoint = "localhost:59000" } if objectStorePublic == "" { objectStorePublic = "localhost:59000" } if objectStoreAccessKey == "" { objectStoreAccessKey = "local_minio" } if objectStoreSecretKey == "" { objectStoreSecretKey = "local_minio_password" } if objectStoreBucket == "" { objectStoreBucket = "private-exports" } if objectStoreRegion == "" { objectStoreRegion = "us-east-1" } } cfg := Config{ ServiceName: serviceName, Environment: environment, LogLevel: stringEnv("LOG_LEVEL", "info"), HTTPAddr: stringEnv("HTTP_ADDR", defaultHTTPAddr(serviceName)), DatabaseURL: secretDefault(secretValues["DATABASE_URL"], "postgres://voice:voice_dev_only@localhost:55432/voice_platform?sslmode=disable"), RedisAddr: stringEnv("REDIS_ADDR", "localhost:56379"), RedisPassword: secretValues["REDIS_PASSWORD"], RedisDB: redisDB, AuthIssuerURL: stringEnv("AUTH_ISSUER_URL", "http://localhost:58180/realms/voice-local"), AuthJWKSURL: stringEnv("AUTH_JWKS_URL", "http://localhost:58180/realms/voice-local/protocol/openid-connect/certs"), AuthAudience: stringEnv("AUTH_AUDIENCE", "voice-platform-web"), OTLPEndpoint: stringEnvAllowEmpty("OTEL_EXPORTER_OTLP_ENDPOINT", "localhost:54317"), DependencyTimeout: dependencyTimeout, ExternalProbeInterval: externalProbeInterval, ShutdownTimeout: shutdownTimeout, WorkerPollInterval: pollInterval, RecoveryScanInterval: recoveryScanInterval, RecoveryStartingAfter: recoveryStartingAfter, RecoveryStoppingAfter: recoveryStoppingAfter, DeviceLocalSecret: deviceLocalSecret, DeviceCredentialMasterKey: deviceCredentialMasterKey, ContentModelMasterKey: contentModelMasterKey, DeviceClockSkew: deviceClockSkew, RTCAppID: stringEnv("RTC_APP_ID", "local-public-rtc-app"), RTCAppKey: secretValues["RTC_APP_KEY"], RTCTokenIssuer: stringEnv("RTC_TOKEN_ISSUER", "local"), RTCTokenSecret: rtcTokenSecret, CursorSigningSecret: cursorSigningSecret, ShareTokenSecret: shareTokenSecret, RTCTokenTTL: rtcTokenTTL, AllocationTTL: allocationTTL, ProviderCallbackSecret: providerCallbackSecret, SubtitleCallbackURL: strings.TrimSpace(os.Getenv("VOLC_SUBTITLE_CALLBACK_URL")), SubtitleCallbackSecret: secretValues["VOLC_SUBTITLE_CALLBACK_SECRET"], SubtitlePrimary: os.Getenv("VOLC_SUBTITLE_PRIMARY") == "true", ProviderCallbackSkew: providerCallbackSkew, PlatformHealthSecret: secretValues["PLATFORM_HEALTH_CALLBACK_SECRET"], PlatformHealthSkew: platformHealthSkew, MockProviderBaseURL: stringEnv("MOCK_PROVIDER_BASE_URL", "http://localhost:59200"), MockProviderAPIKey: secretDefault(secretValues["MOCK_PROVIDER_API_KEY"], "local-mock-provider-key"), VoiceProvider: stringEnv("VOICE_PROVIDER", "mock"), VolcRTCBaseURL: stringEnv("VOLC_RTC_BASE_URL", "https://rtc.volcengineapi.com"), VolcAccessKeyID: secretValues["VOLC_ACCESS_KEY_ID"], VolcSecretAccessKey: secretValues["VOLC_SECRET_ACCESS_KEY"], VolcSessionToken: secretValues["VOLC_SESSION_TOKEN"], VolcRTCRegion: stringEnv("VOLC_RTC_REGION", "cn-north-1"), VolcRTCAPIVersion: stringEnv("VOLC_RTC_API_VERSION", "2024-12-01"), VolcVoiceChatTemplate: strings.TrimSpace(os.Getenv("VOLC_VOICE_CHAT_TEMPLATE_FILE")), VolcHardwareAgentBotID: strings.TrimSpace(os.Getenv("VOLC_HARDWARE_AGENT_BOT_ID")), VolcHardwareTransport: stringEnv("VOLC_HARDWARE_AGENT_TRANSPORT", "high_quality"), VolcHardwareMinimumSDKVersion: stringEnv("VOLC_HARDWARE_AGENT_MINIMUM_SDK_VERSION", "1.0.0"), VolcHardwareProvisioningEnabled: volcHardwareProvisioningEnabled, VolcHardwareInstanceID: strings.TrimSpace(os.Getenv("VOLC_HARDWARE_INSTANCE_ID")), VolcHardwareProductKey: strings.TrimSpace(os.Getenv("VOLC_HARDWARE_PRODUCT_KEY")), VolcHardwareProductSecret: secretValues["VOLC_HARDWARE_PRODUCT_SECRET"], VolcHardwareDeviceCredentialMasterKey: secretValues["VOLC_HARDWARE_DEVICE_CREDENTIAL_MASTER_KEY"], VolcHardwareRegisterURL: stringEnv("VOLC_HARDWARE_REGISTER_URL", "https://iot-cn-shanghai.iot.volces.com/2021-12-14/DynamicRegister?Action=DynamicRegister&Version=2021-12-14"), VolcHardwareRegisterTimeout: volcHardwareRegisterTimeout, VolcHardwareDeviceNamePrefix: stringEnv("VOLC_HARDWARE_DEVICE_NAME_PREFIX", "dt-"), ProviderRequestTimeout: providerRequestTimeout, ProviderCircuitCooldown: providerCircuitCooldown, ObjectStoreEndpoint: objectStoreEndpoint, ObjectStorePublic: objectStorePublic, ObjectStoreAccessKey: objectStoreAccessKey, ObjectStoreSecretKey: objectStoreSecretKey, ObjectStoreBucket: objectStoreBucket, ObjectStoreRegion: objectStoreRegion, ObjectStoreSecure: objectStoreSecure, ObjectStorePublicSecure: objectStorePublicSecure, ExportDownloadTTL: exportDownloadTTL, ExportObjectTTL: exportObjectTTL, MediaLocalDeleteGrace: mediaLocalDeleteGrace, AlertWebhookURL: strings.TrimSpace(os.Getenv("ALERT_WEBHOOK_URL")), AlertWebhookSecret: secretValues["ALERT_WEBHOOK_SECRET"], AlertWebhookTimeout: alertWebhookTimeout, ContentGenerator: stringEnv("CONTENT_GENERATOR", "local"), ArkBaseURL: stringEnv("ARK_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3"), ArkAPIKey: secretValues["ARK_API_KEY"], ArkContentModel: strings.TrimSpace(os.Getenv("ARK_CONTENT_MODEL")), ArkRequestTimeout: arkRequestTimeout, ContentSafetyProvider: stringEnv("CONTENT_SAFETY_PROVIDER", "local"), ContentSafetyEndpoint: strings.TrimSpace(os.Getenv("CONTENT_SAFETY_ENDPOINT")), ContentSafetyAPIKey: secretValues["CONTENT_SAFETY_API_KEY"], ContentSafetyPolicyRef: strings.TrimSpace(os.Getenv("CONTENT_SAFETY_POLICY_REF")), ContentSafetyTimeout: contentSafetyTimeout, EmbeddingProvider: stringEnv("EMBEDDING_PROVIDER", "local"), ArkEmbeddingModel: strings.TrimSpace(os.Getenv("ARK_EMBEDDING_MODEL")), MemoryToolBaseURL: strings.TrimSpace(os.Getenv("MEMORY_TOOL_BASE_URL")), MemoryToolSigningSecret: memoryToolSigningSecret, MemoryToolTokenTTL: memoryToolTokenTTL, VikingResourceID: strings.TrimSpace(os.Getenv("VIKING_MEMORY_RESOURCE_ID")), VikingCollectionName: strings.TrimSpace(os.Getenv("VIKING_MEMORY_COLLECTION_NAME")), VikingAPIKey: secretValues["VIKING_MEMORY_API_KEY"], VikingEventType: stringEnv("VIKING_MEMORY_EVENT_TYPE", "event_v1"), VikingProfileType: stringEnv("VIKING_MEMORY_PROFILE_TYPE", "profile_v1"), ProviderMemoryMode: stringEnv("PROVIDER_MEMORY_MODE", "disabled"), ProviderMemoryBaseURL: strings.TrimSpace(os.Getenv("PROVIDER_MEMORY_BASE_URL")), ProviderMemoryAPIKey: secretValues["PROVIDER_MEMORY_API_KEY"], ProviderMemoryTimeout: providerMemoryTimeout, } cfg.RateLimitRPS, err = floatEnv("RATE_LIMIT_RPS", 50) if err != nil { return Config{}, err } cfg.RateLimitBurst, err = intEnv("RATE_LIMIT_BURST", 100) if err != nil { return Config{}, err } cfg.RateLimitUserRPS, err = floatEnv("RATE_LIMIT_USER_RPS", 20) if err != nil { return Config{}, err } cfg.RateLimitUserBurst, err = intEnv("RATE_LIMIT_USER_BURST", 40) if err != nil { return Config{}, err } cfg.RateLimitTenantRPS, err = floatEnv("RATE_LIMIT_TENANT_RPS", 200) if err != nil { return Config{}, err } cfg.RateLimitTenantBurst, err = intEnv("RATE_LIMIT_TENANT_BURST", 400) if err != nil { return Config{}, err } cfg.RateLimitDeviceRPS, err = floatEnv("RATE_LIMIT_DEVICE_RPS", 10) if err != nil { return Config{}, err } cfg.RateLimitDeviceBurst, err = intEnv("RATE_LIMIT_DEVICE_BURST", 20) if err != nil { return Config{}, err } cfg.ProviderStartRPS, err = floatEnv("PROVIDER_START_RPS", 20) if err != nil { return Config{}, err } cfg.ProviderStartBurst, err = intEnv("PROVIDER_START_BURST", 40) if err != nil { return Config{}, err } cfg.ProviderMaxActiveTasks, err = intEnv("PROVIDER_MAX_ACTIVE_TASKS", 100) if err != nil { return Config{}, err } cfg.MemoryToolRateRPS, err = floatEnv("MEMORY_TOOL_RATE_RPS", 2) if err != nil { return Config{}, err } cfg.MemoryToolRateBurst, err = intEnv("MEMORY_TOOL_RATE_BURST", 5) if err != nil { return Config{}, err } cfg.HeartbeatInterval, err = intEnv("HEARTBEAT_INTERVAL_SECONDS", 15) if err != nil { return Config{}, err } cfg.ProviderFailureThreshold, err = intEnv("PROVIDER_FAILURE_THRESHOLD", 3) if err != nil { return Config{}, err } cfg.Onboarding, err = loadOnboardingConfig(secretValues) if err != nil { return Config{}, err } if err := cfg.Validate(); err != nil { return Config{}, err } return cfg, nil } func (c Config) Validate() error { if err := c.Onboarding.validate(c.Environment); err != nil { return err } if c.SubtitleCallbackURL != "" || c.SubtitleCallbackSecret != "" || c.SubtitlePrimary { u, err := url.Parse(c.SubtitleCallbackURL) if err != nil || u.Scheme != "https" || u.Host == "" || u.RawQuery != "" || u.Fragment != "" || u.User != nil || len(c.SubtitleCallbackSecret) < 32 { return errors.New("native subtitle callback requires an HTTPS URL without query and a dedicated secret of at least 32 bytes") } } validEnvironment := map[string]bool{ EnvironmentLocal: true, EnvironmentTest: true, EnvironmentDev: true, EnvironmentStage: true, EnvironmentProd: true, } if !validEnvironment[c.Environment] { return fmt.Errorf("APP_ENV %q is not supported", c.Environment) } if strings.TrimSpace(c.HTTPAddr) == "" { return errors.New("HTTP_ADDR is required") } if strings.TrimSpace(c.DatabaseURL) == "" { return errors.New("DATABASE_URL is required") } if strings.TrimSpace(c.RedisAddr) == "" { return errors.New("REDIS_ADDR is required") } if strings.TrimSpace(c.AuthIssuerURL) == "" || strings.TrimSpace(c.AuthJWKSURL) == "" || strings.TrimSpace(c.AuthAudience) == "" { return errors.New("AUTH_ISSUER_URL, AUTH_JWKS_URL and AUTH_AUDIENCE are required") } if c.DependencyTimeout <= 0 || c.ExternalProbeInterval <= 0 || c.ShutdownTimeout <= 0 || c.WorkerPollInterval <= 0 || c.RecoveryScanInterval <= 0 || c.RecoveryStartingAfter <= 0 || c.RecoveryStoppingAfter <= 0 { return errors.New("timeouts and worker poll interval must be positive") } if c.RateLimitRPS <= 0 || c.RateLimitBurst <= 0 || c.RateLimitUserRPS <= 0 || c.RateLimitUserBurst <= 0 || c.RateLimitTenantRPS <= 0 || c.RateLimitTenantBurst <= 0 || c.RateLimitDeviceRPS <= 0 || c.RateLimitDeviceBurst <= 0 || c.ProviderStartRPS <= 0 || c.ProviderStartBurst <= 0 || c.ProviderMaxActiveTasks <= 0 || c.MemoryToolRateRPS <= 0 || c.MemoryToolRateBurst <= 0 { return errors.New("rate limits, bursts and provider task capacity must be positive") } if c.ServiceName == "api" { decodedMasterKey, err := hex.DecodeString(c.DeviceCredentialMasterKey) if err != nil || len(decodedMasterKey) != 32 { return errors.New("DEVICE_CREDENTIAL_MASTER_KEY must be 32 bytes encoded as 64 hexadecimal characters") } } if c.ServiceName == "api" || c.ServiceName == "worker" { decodedModelKey, err := hex.DecodeString(c.ContentModelMasterKey) if err != nil || len(decodedModelKey) != 32 { return errors.New("MODEL_CREDENTIAL_MASTER_KEY must be 32 bytes encoded as 64 hexadecimal characters") } } if c.DeviceClockSkew <= 0 { return errors.New("DEVICE_CLOCK_SKEW must be positive") } if c.ServiceName == "api" { if c.RTCTokenIssuer != "local" && c.RTCTokenIssuer != "volcengine" { return errors.New("RTC_TOKEN_ISSUER must be local or volcengine") } if c.RTCTokenIssuer == "local" && len(c.RTCTokenSecret) < 32 { return errors.New("RTC_TOKEN_SIGNING_SECRET must contain at least 32 characters for the local issuer") } if c.RTCTokenIssuer == "volcengine" && len(c.RTCAppKey) < 16 { return errors.New("RTC_APP_KEY must contain at least 16 characters for the Volcengine token issuer") } if c.RTCTokenIssuer == "volcengine" && len(c.RTCAppID) != 24 { return errors.New("RTC_APP_ID must contain exactly 24 characters for the Volcengine token issuer") } } if c.ServiceName == "api" && len(c.CursorSigningSecret) < 32 { return errors.New("CURSOR_SIGNING_SECRET must contain at least 32 characters") } if c.ServiceName == "api" && len(c.ShareTokenSecret) < 32 { return errors.New("SHARE_TOKEN_SIGNING_SECRET must contain at least 32 characters") } if (c.ServiceName == "api" || c.ServiceName == "worker") && (len(c.MemoryToolSigningSecret) < 32 || c.MemoryToolTokenTTL <= 0) { return errors.New("MEMORY_TOOL_SIGNING_SECRET must contain at least 32 characters and token TTL must be positive") } if c.MemoryToolBaseURL != "" && !isAbsoluteHTTPURL(c.MemoryToolBaseURL) { return errors.New("MEMORY_TOOL_BASE_URL must be an absolute HTTP(S) URL") } if c.ProviderMemoryMode != "disabled" && c.ProviderMemoryMode != "managed_http" { return errors.New("PROVIDER_MEMORY_MODE must be disabled or managed_http") } if (c.ServiceName == "api" || c.ServiceName == "worker") && c.ProviderMemoryMode == "managed_http" && (!isAbsoluteHTTPURL(c.ProviderMemoryBaseURL) || c.ProviderMemoryAPIKey == "" || c.ProviderMemoryTimeout <= 0) { return errors.New("managed provider memory requires an HTTP(S) base URL, API key and positive timeout") } if c.RTCAppID == "" || c.RTCTokenTTL <= 0 || c.AllocationTTL <= 0 || c.HeartbeatInterval <= 0 { return errors.New("RTC app, token TTL, allocation TTL and heartbeat interval must be valid") } if c.ServiceName == "callbacks" && len(c.ProviderCallbackSecret) < 32 { return errors.New("PROVIDER_CALLBACK_SECRET must contain at least 32 characters") } if c.ServiceName == "callbacks" && len(c.PlatformHealthSecret) < 32 { return errors.New("PLATFORM_HEALTH_CALLBACK_SECRET must contain at least 32 characters") } if c.ServiceName == "worker" { if c.VoiceProvider != "mock" && c.VoiceProvider != "volcengine" { return errors.New("VOICE_PROVIDER must be mock or volcengine") } if c.VoiceProvider == "mock" && (c.MockProviderBaseURL == "" || c.MockProviderAPIKey == "") { return errors.New("MOCK_PROVIDER_BASE_URL and MOCK_PROVIDER_API_KEY are required for the mock voice provider") } if c.VoiceProvider == "volcengine" { if !isAbsoluteHTTPURL(c.VolcRTCBaseURL) || c.VolcAccessKeyID == "" || c.VolcSecretAccessKey == "" || c.VolcRTCRegion == "" || c.VolcRTCAPIVersion == "" || c.VolcVoiceChatTemplate == "" { return errors.New("Volcengine voice provider requires an RTC URL, AK/SK, region, API version and voice-chat template file") } } } if c.ProviderCallbackSkew <= 0 || c.PlatformHealthSkew <= 0 || c.ProviderRequestTimeout <= 0 || c.ProviderFailureThreshold < 1 || c.ProviderCircuitCooldown <= 0 { return errors.New("provider callback skew and request timeout must be positive") } if c.VolcHardwareTransport != "high_quality" && c.VolcHardwareTransport != "low_load" { return errors.New("VOLC_HARDWARE_AGENT_TRANSPORT must be high_quality or low_load") } if (c.ServiceName == "api" && c.VolcHardwareProvisioningEnabled) || ((c.ServiceName == "api" || c.ServiceName == "worker") && c.Onboarding.ActivationEnabled) { providerKey, err := hex.DecodeString(c.VolcHardwareDeviceCredentialMasterKey) if err != nil || len(providerKey) != 32 { return errors.New("VOLC_HARDWARE_DEVICE_CREDENTIAL_MASTER_KEY must be 32 bytes encoded as 64 hexadecimal characters") } if c.VolcHardwareInstanceID == "" || c.VolcHardwareProductKey == "" || c.VolcHardwareAgentBotID == "" || c.VolcHardwareMinimumSDKVersion == "" || len(c.VolcHardwareMinimumSDKVersion) > 80 || len(c.VolcHardwareProductSecret) < 16 || c.VolcHardwareRegisterTimeout <= 0 { return errors.New("enabled hardware provisioning requires instance ID, product key, default Bot ID, minimum SDK, product secret and positive registration timeout") } } if c.AlertWebhookTimeout <= 0 { return errors.New("ALERT_WEBHOOK_TIMEOUT must be positive") } if c.ArkRequestTimeout <= 0 { return errors.New("ARK_REQUEST_TIMEOUT must be positive") } if c.ContentSafetyTimeout <= 0 { return errors.New("CONTENT_SAFETY_TIMEOUT must be positive") } if c.ServiceName == "worker" && c.AlertWebhookURL != "" { parsed, err := url.Parse(c.AlertWebhookURL) if err != nil || parsed.Host == "" || (parsed.Scheme != "http" && parsed.Scheme != "https") { return errors.New("ALERT_WEBHOOK_URL must be an absolute HTTP(S) URL") } if len(c.AlertWebhookSecret) < 32 { return errors.New("ALERT_WEBHOOK_SECRET must contain at least 32 characters when webhook delivery is enabled") } } if c.ServiceName == "worker" { if c.ContentSafetyProvider != "local" && c.ContentSafetyProvider != "managed_http" { return errors.New("CONTENT_SAFETY_PROVIDER must be local or managed_http") } if c.ContentSafetyProvider == "managed_http" { if !isAbsoluteHTTPURL(c.ContentSafetyEndpoint) || c.ContentSafetyAPIKey == "" || c.ContentSafetyPolicyRef == "" { return errors.New("managed content safety requires an HTTP(S) endpoint, API key and policy reference") } } if c.ContentGenerator != "local" && c.ContentGenerator != "ark" { return errors.New("CONTENT_GENERATOR must be local or ark") } if c.ContentGenerator == "ark" { parsed, err := url.Parse(c.ArkBaseURL) if err != nil || parsed.Host == "" || (parsed.Scheme != "http" && parsed.Scheme != "https") { return errors.New("ARK_BASE_URL must be an absolute HTTP(S) URL") } if c.ArkAPIKey == "" || c.ArkContentModel == "" { return errors.New("ARK_API_KEY and ARK_CONTENT_MODEL are required for the Ark content generator") } } } if c.ServiceName == "api" || c.ServiceName == "worker" { if c.EmbeddingProvider != "local" && c.EmbeddingProvider != "ark" { return errors.New("EMBEDDING_PROVIDER must be local or ark") } if c.EmbeddingProvider == "ark" { parsed, err := url.Parse(c.ArkBaseURL) if err != nil || parsed.Host == "" || (parsed.Scheme != "http" && parsed.Scheme != "https") { return errors.New("ARK_BASE_URL must be an absolute HTTP(S) URL") } if c.ArkAPIKey == "" || c.ArkEmbeddingModel == "" { return errors.New("ARK_API_KEY and ARK_EMBEDDING_MODEL are required for the Ark embedding provider") } } } if c.ServiceName == "api" || c.ServiceName == "worker" { if c.ObjectStoreEndpoint == "" || c.ObjectStorePublic == "" || c.ObjectStoreAccessKey == "" || c.ObjectStoreSecretKey == "" || c.ObjectStoreBucket == "" || c.ObjectStoreRegion == "" { return errors.New("object store endpoint, public endpoint, credentials, region and export bucket are required") } if c.ExportDownloadTTL <= 0 || c.ExportDownloadTTL > 24*time.Hour || c.ExportObjectTTL <= 0 { return errors.New("export download and object TTL must be positive, and download TTL cannot exceed 24 hours") } if c.MediaLocalDeleteGrace <= 0 || c.MediaLocalDeleteGrace > 7*24*time.Hour { return errors.New("media local delete grace must be positive and cannot exceed 7 days") } } if c.Environment == EnvironmentProd { if err := c.validateProduction(); err != nil { return err } } return nil } func (c Config) validateProduction() error { if !isHTTPSURL(c.AuthIssuerURL) || !isHTTPSURL(c.AuthJWKSURL) { return errors.New("production OIDC issuer and JWKS URLs must use HTTPS") } databaseURL, err := url.Parse(c.DatabaseURL) if err != nil || databaseURL.Scheme == "" || strings.EqualFold(databaseURL.Query().Get("sslmode"), "disable") { return errors.New("production DATABASE_URL must enable transport security") } if strings.TrimSpace(c.RedisPassword) == "" || isPlaceholderSecret(c.RedisPassword) { return errors.New("production REDIS_PASSWORD must be a non-placeholder secret") } if c.ServiceName == "api" { if c.DeviceCredentialMasterKey == "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" { return errors.New("production API must use a non-default DEVICE_CREDENTIAL_MASTER_KEY") } if isPlaceholderSecret(c.CursorSigningSecret) || isPlaceholderSecret(c.ShareTokenSecret) { return errors.New("production API signing secrets must not use local or example values") } if c.CursorSigningSecret == c.ShareTokenSecret { return errors.New("production API signing secrets must be distinct") } if isPlaceholderSecret(c.MemoryToolSigningSecret) || c.MemoryToolSigningSecret == c.CursorSigningSecret || c.MemoryToolSigningSecret == c.ShareTokenSecret { return errors.New("production memory tool signing secret must be non-placeholder and distinct") } if c.MemoryToolBaseURL != "" && !isHTTPSURL(c.MemoryToolBaseURL) { return errors.New("production memory tool URL must use HTTPS") } if isLocalValue(c.RTCAppID) || c.RTCTokenIssuer != "volcengine" || isPlaceholderSecret(c.RTCAppKey) { return errors.New("production RTC credentials must use a real app ID, Volcengine token issuer and non-placeholder app key") } if c.EmbeddingProvider != "ark" || !isHTTPSURL(c.ArkBaseURL) || isPlaceholderSecret(c.ArkAPIKey) || isLocalValue(c.ArkEmbeddingModel) { return errors.New("production memory retrieval must use Ark HTTPS, a non-placeholder API key and a real embedding model ID") } } if c.ServiceName == "callbacks" && isPlaceholderSecret(c.ProviderCallbackSecret) { return errors.New("production PROVIDER_CALLBACK_SECRET must not use a local or example value") } if c.ServiceName == "callbacks" && isPlaceholderSecret(c.PlatformHealthSecret) { return errors.New("production PLATFORM_HEALTH_CALLBACK_SECRET must not use a local or example value") } if c.ServiceName == "worker" { if c.VoiceProvider != "volcengine" || len(c.RTCAppID) != 24 || isLocalValue(c.RTCAppID) || !isHTTPSURL(c.VolcRTCBaseURL) || isLocalValue(c.VolcRTCBaseURL) || isPlaceholderSecret(c.VolcAccessKeyID) || isPlaceholderSecret(c.VolcSecretAccessKey) { return errors.New("production voice provider must use signed Volcengine RTC HTTPS with non-placeholder AK/SK") } if c.AlertWebhookURL != "" && (!isHTTPSURL(c.AlertWebhookURL) || isPlaceholderSecret(c.AlertWebhookSecret)) { return errors.New("production alert webhook must use HTTPS and a non-placeholder signing secret") } if c.ContentGenerator != "ark" || !isHTTPSURL(c.ArkBaseURL) || isPlaceholderSecret(c.ArkAPIKey) || isLocalValue(c.ArkContentModel) { return errors.New("production content generation must use Ark HTTPS, a non-placeholder API key and a real model ID") } if c.ContentSafetyProvider != "managed_http" || !isHTTPSURL(c.ContentSafetyEndpoint) || isLocalValue(c.ContentSafetyEndpoint) || isPlaceholderSecret(c.ContentSafetyAPIKey) || isLocalValue(c.ContentSafetyPolicyRef) { return errors.New("production content safety must use managed HTTPS with a non-placeholder API key and policy reference") } if c.EmbeddingProvider != "ark" || isLocalValue(c.ArkEmbeddingModel) { return errors.New("production memory indexing must use a real Ark embedding model ID") } } if c.ServiceName == "api" || c.ServiceName == "worker" { if isPlaceholderSecret(c.MemoryToolSigningSecret) { return errors.New("production memory tool signing secret must be non-placeholder") } if c.ProviderMemoryMode == "managed_http" && (!isHTTPSURL(c.ProviderMemoryBaseURL) || isLocalValue(c.ProviderMemoryBaseURL) || isPlaceholderSecret(c.ProviderMemoryAPIKey)) { return errors.New("production provider memory must use managed HTTPS and a non-placeholder API key") } if !c.ObjectStoreSecure || !c.ObjectStorePublicSecure { return errors.New("production object storage must use TLS") } if isLocalValue(c.ObjectStoreEndpoint) || isLocalValue(c.ObjectStorePublic) { return errors.New("production object storage endpoints must not target localhost") } if isPlaceholderSecret(c.ObjectStoreAccessKey) || isPlaceholderSecret(c.ObjectStoreSecretKey) { return errors.New("production object storage credentials must not use local or example values") } } return nil } func isHTTPSURL(value string) bool { parsed, err := url.Parse(strings.TrimSpace(value)) return err == nil && strings.EqualFold(parsed.Scheme, "https") && parsed.Host != "" } func isAbsoluteHTTPURL(value string) bool { parsed, err := url.Parse(strings.TrimSpace(value)) return err == nil && (strings.EqualFold(parsed.Scheme, "http") || strings.EqualFold(parsed.Scheme, "https")) && parsed.Host != "" } func isLocalValue(value string) bool { normalized := strings.ToLower(strings.TrimSpace(value)) allZeroes := normalized != "" && strings.Trim(normalized, "0") == "" return strings.Contains(normalized, "localhost") || strings.Contains(normalized, "127.0.0.1") || strings.Contains(normalized, "mock-provider") || strings.Contains(normalized, "example.invalid") || strings.Contains(normalized, "replace-with") || strings.HasPrefix(normalized, "local-") || strings.HasPrefix(normalized, "local_") || allZeroes } func isPlaceholderSecret(value string) bool { normalized := strings.ToLower(strings.TrimSpace(value)) return len(normalized) < 32 || strings.Contains(normalized, "local-") || strings.Contains(normalized, "local_") || strings.Contains(normalized, "dev_only") || strings.Contains(normalized, "changeme") || strings.Contains(normalized, "example") } func loadSecretValues(names ...string) (map[string]string, error) { values := make(map[string]string, len(names)) for _, name := range names { value, err := secretEnv(name) if err != nil { return nil, err } values[name] = value } return values, nil } func secretEnv(name string) (string, error) { inline := strings.TrimSpace(os.Getenv(name)) filePath := strings.TrimSpace(os.Getenv(name + "_FILE")) if inline != "" && filePath != "" { return "", fmt.Errorf("%s and %s_FILE cannot both be configured", name, name) } if filePath == "" { return inline, nil } if !filepath.IsAbs(filePath) { return "", fmt.Errorf("%s_FILE must be an absolute path", name) } file, err := os.Open(filepath.Clean(filePath)) if err != nil { return "", fmt.Errorf("read %s_FILE: secret file is unavailable", name) } defer file.Close() info, err := file.Stat() if err != nil || !info.Mode().IsRegular() { return "", fmt.Errorf("read %s_FILE: secret path must be a regular file", name) } content, err := io.ReadAll(io.LimitReader(file, 64*1024+1)) if err != nil || len(content) > 64*1024 { return "", fmt.Errorf("read %s_FILE: secret file exceeds 64 KiB", name) } content = bytes.TrimRight(content, "\r\n") if bytes.IndexByte(content, 0) >= 0 { return "", fmt.Errorf("read %s_FILE: secret contains a NUL byte", name) } return string(content), nil } func secretDefault(value, fallback string) string { if value == "" { return fallback } return value } func defaultHTTPAddr(serviceName string) string { switch serviceName { case "callbacks": return "0.0.0.0:58081" case "worker": return "0.0.0.0:58082" default: return "0.0.0.0:58080" } } func stringEnv(key, fallback string) string { value := strings.TrimSpace(os.Getenv(key)) if value == "" { return fallback } return value } func stringEnvAllowEmpty(key, fallback string) string { value, exists := os.LookupEnv(key) if !exists { return fallback } return strings.TrimSpace(value) } func intEnv(key string, fallback int) (int, error) { value := strings.TrimSpace(os.Getenv(key)) if value == "" { return fallback, nil } parsed, err := strconv.Atoi(value) if err != nil { return 0, fmt.Errorf("%s must be an integer: %w", key, err) } return parsed, nil } func floatEnv(key string, fallback float64) (float64, error) { value := strings.TrimSpace(os.Getenv(key)) if value == "" { return fallback, nil } parsed, err := strconv.ParseFloat(value, 64) if err != nil { return 0, fmt.Errorf("%s must be a number: %w", key, err) } return parsed, nil } func boolEnv(key string, fallback bool) (bool, error) { value := strings.TrimSpace(os.Getenv(key)) if value == "" { return fallback, nil } parsed, err := strconv.ParseBool(value) if err != nil { return false, fmt.Errorf("%s must be a boolean: %w", key, err) } return parsed, nil } func durationEnv(key string, fallback time.Duration) (time.Duration, error) { value := strings.TrimSpace(os.Getenv(key)) if value == "" { return fallback, nil } parsed, err := time.ParseDuration(value) if err != nil { return 0, fmt.Errorf("%s must be a duration: %w", key, err) } return parsed, nil }