package config import ( "fmt" "os" "strconv" ) // Config holds runtime configuration. All values come from env. type Config struct { Port int DatabaseURL string KeycloakIssuer string // e.g. https://auth.gosec.cloud/realms/gosecCloud KeycloakDiscovery string // optional in-cluster discovery URL override KeycloakAudCSV string // comma-separated allowed audiences (client_id values) CacheTTLSeconds int // for client-side Cache-Control hint CORSAllowOrigins string // comma-separated; empty = allow *.gosec.internal/*.gosec.cloud } func Load() (*Config, error) { c := &Config{ Port: 8080, KeycloakIssuer: os.Getenv("KEYCLOAK_ISSUER"), KeycloakDiscovery: os.Getenv("KEYCLOAK_DISCOVERY_URL"), KeycloakAudCSV: os.Getenv("KEYCLOAK_AUDIENCES"), DatabaseURL: os.Getenv("DATABASE_URL"), CacheTTLSeconds: 60, CORSAllowOrigins: os.Getenv("CORS_ORIGINS"), } if p := os.Getenv("PORT"); p != "" { v, err := strconv.Atoi(p) if err != nil { return nil, fmt.Errorf("PORT: %w", err) } c.Port = v } if t := os.Getenv("CACHE_TTL_SECONDS"); t != "" { v, err := strconv.Atoi(t) if err != nil { return nil, fmt.Errorf("CACHE_TTL_SECONDS: %w", err) } c.CacheTTLSeconds = v } if c.DatabaseURL == "" { return nil, fmt.Errorf("DATABASE_URL is required") } if c.KeycloakIssuer == "" { return nil, fmt.Errorf("KEYCLOAK_ISSUER is required") } return c, nil }