package main import ( "fmt" "os" "strconv" "strings" "time" ) type Config struct { ListenAddr string DBPath string AllowedOrigin string MemberPollIntervalSec int IntentTTL time.Duration QuoteTTL time.Duration WalletSessionTTL time.Duration RequireWalletSession bool InstallTokenTTL time.Duration LeaseTTL time.Duration OfflineRenewTTL time.Duration ChainID int64 DomainName string VerifyingContract string MembershipContract string EntitlementContract string MintCurrency string MintAmountAtomic string MintDecimals int ChainRPCURL string RequireOnchainTxVerify bool GovernanceRuntimeVersion string GovernancePackageURL string GovernancePackageHash string GovernancePackageSig string GovernanceSignerKeyID string GovernancePolicyHash string GovernanceRolloutChannel string } func loadConfig() Config { return Config{ ListenAddr: env("SECRET_API_LISTEN_ADDR", ":8080"), DBPath: env("SECRET_API_DB_PATH", "./secret.db"), AllowedOrigin: env("SECRET_API_ALLOWED_ORIGIN", "https://edut.ai"), MemberPollIntervalSec: envInt("SECRET_API_MEMBER_POLL_INTERVAL_SECONDS", 30), IntentTTL: time.Duration(envInt("SECRET_API_INTENT_TTL_SECONDS", 900)) * time.Second, QuoteTTL: time.Duration(envInt("SECRET_API_QUOTE_TTL_SECONDS", 900)) * time.Second, WalletSessionTTL: time.Duration(envInt("SECRET_API_WALLET_SESSION_TTL_SECONDS", 2592000)) * time.Second, RequireWalletSession: envBool("SECRET_API_REQUIRE_WALLET_SESSION", false), InstallTokenTTL: time.Duration(envInt("SECRET_API_INSTALL_TOKEN_TTL_SECONDS", 900)) * time.Second, LeaseTTL: time.Duration(envInt("SECRET_API_LEASE_TTL_SECONDS", 3600)) * time.Second, OfflineRenewTTL: time.Duration(envInt("SECRET_API_OFFLINE_RENEW_TTL_SECONDS", 2592000)) * time.Second, ChainID: int64(envInt("SECRET_API_CHAIN_ID", 84532)), DomainName: env("SECRET_API_DOMAIN_NAME", "EDUT Designation"), VerifyingContract: strings.ToLower(env("SECRET_API_VERIFYING_CONTRACT", "0x0000000000000000000000000000000000000000")), MembershipContract: strings.ToLower(env("SECRET_API_MEMBERSHIP_CONTRACT", "0x0000000000000000000000000000000000000000")), EntitlementContract: strings.ToLower(env("SECRET_API_ENTITLEMENT_CONTRACT", "0x0000000000000000000000000000000000000000")), MintCurrency: strings.ToUpper(env("SECRET_API_MINT_CURRENCY", "USDC")), MintAmountAtomic: env("SECRET_API_MINT_AMOUNT_ATOMIC", "100000000"), MintDecimals: envInt("SECRET_API_MINT_DECIMALS", 6), ChainRPCURL: env("SECRET_API_CHAIN_RPC_URL", ""), RequireOnchainTxVerify: envBool("SECRET_API_REQUIRE_ONCHAIN_TX_VERIFICATION", false), GovernanceRuntimeVersion: env("SECRET_API_GOV_RUNTIME_VERSION", "0.1.0"), GovernancePackageURL: env("SECRET_API_GOV_PACKAGE_URL", "https://cdn.edut.ai/governance/edutd-0.1.0.tar.gz"), GovernancePackageHash: strings.ToLower(env("SECRET_API_GOV_PACKAGE_HASH", "sha256:pending")), GovernancePackageSig: env("SECRET_API_GOV_PACKAGE_SIGNATURE", "pending"), GovernanceSignerKeyID: env("SECRET_API_GOV_SIGNER_KEY_ID", "edut-signer-1"), GovernancePolicyHash: strings.ToLower(env("SECRET_API_GOV_POLICY_HASH", "sha256:pending")), GovernanceRolloutChannel: strings.ToLower(env("SECRET_API_GOV_ROLLOUT_CHANNEL", "stable")), } } func (c Config) Validate() error { if c.ChainID <= 0 { return fmt.Errorf("SECRET_API_CHAIN_ID must be positive") } if c.WalletSessionTTL <= 0 { return fmt.Errorf("SECRET_API_WALLET_SESSION_TTL_SECONDS must be positive") } if c.RequireOnchainTxVerify && strings.TrimSpace(c.ChainRPCURL) == "" { return fmt.Errorf("SECRET_API_REQUIRE_ONCHAIN_TX_VERIFICATION requires SECRET_API_CHAIN_RPC_URL") } return nil } func env(key, fallback string) string { if v := strings.TrimSpace(os.Getenv(key)); v != "" { return v } return fallback } func envInt(key string, fallback int) int { raw := strings.TrimSpace(os.Getenv(key)) if raw == "" { return fallback } value, err := strconv.Atoi(raw) if err != nil { return fallback } return value } func envBool(key string, fallback bool) bool { raw := strings.ToLower(strings.TrimSpace(os.Getenv(key))) if raw == "" { return fallback } switch raw { case "1", "true", "yes", "on": return true case "0", "false", "no", "off": return false default: return fallback } }