package licensing import ( "bytes" "encoding/json" "fmt" "net/http" "time" ) const ( DefaultKachingURL = "https://kaching.chorus.services" LicenseTimeout = 30 * time.Second ) // LicenseConfig holds licensing information type LicenseConfig struct { Email string LicenseKey string ClusterID string } // Validator handles license validation with KACHING type Validator struct { config LicenseConfig kachingURL string client *http.Client } // NewValidator creates a new license validator func NewValidator(config LicenseConfig) *Validator { return &Validator{ config: config, kachingURL: DefaultKachingURL, client: &http.Client{ Timeout: LicenseTimeout, }, } } // Validate performs license validation with KACHING license authority // CRITICAL: CHORUS will not start without valid license validation func (v *Validator) Validate() error { if v.config.Email == "" || v.config.LicenseKey == "" { return fmt.Errorf("license email and key are required") } // Prepare validation request request := map[string]interface{}{ "email": v.config.Email, "license_key": v.config.LicenseKey, "cluster_id": v.config.ClusterID, "product": "CHORUS", "version": "0.1.0-dev", "container": true, // Flag indicating this is a container deployment } requestBody, err := json.Marshal(request) if err != nil { return fmt.Errorf("failed to marshal license request: %w", err) } // Call KACHING license authority licenseURL := fmt.Sprintf("%s/v1/license/validate", v.kachingURL) resp, err := v.client.Post(licenseURL, "application/json", bytes.NewReader(requestBody)) if err != nil { // FAIL-CLOSED: No network = No license = No operation return fmt.Errorf("unable to contact license authority: %w", err) } defer resp.Body.Close() // Parse response var licenseResponse map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&licenseResponse); err != nil { return fmt.Errorf("invalid license authority response: %w", err) } // Check validation result if resp.StatusCode != http.StatusOK { message := "license validation failed" if msg, ok := licenseResponse["message"].(string); ok { message = msg } return fmt.Errorf("license validation failed: %s", message) } // License is valid return nil } // ValidateBackground performs background license validation (for runtime checks) // This is used for periodic license validation during operation func (v *Validator) ValidateBackground() error { // Similar to Validate() but with longer timeout and retry logic // Implementation would include retry logic and graceful degradation return v.Validate() }