# Email and OAuth configuration contracts # # SecretValue enforces that sensitive fields never contain plaintext: # they must be empty, a '${VAR}' substitution token, or an '@blob' AES-GCM # encrypted value. The Rust side resolves substitutions and decrypts blobs # after loading — never write real secrets in version-controlled files. let SecretValue | doc "Sensitive value: must be empty, '${ENV_VAR}', or '@encrypted' — no plaintext" = fun label value => if std.is_string value then if value == "" || std.string.is_match "^\\$\\{[A-Za-z_][A-Za-z0-9_]*\\}$" value || std.string.is_match "^@" value then value else std.contract.blame_with_message "Sensitive field must be empty, '${ENV_VAR}', or '@encrypted' — no plaintext" label else std.contract.blame_with_message "Expected a String" label in { SecretValue = SecretValue, EmailConfig = { enabled | Bool | default = true, provider | String | doc "Email backend: 'console', 'smtp', or 'sendgrid'" | default = "console", smtp_host | String | default = "smtp.gmail.com", smtp_port | Number | default = 587, smtp_username | String | doc "SMTP user — prefer '${SMTP_USERNAME}'" | default = "", smtp_password | String | doc "SMTP password — prefer '${SMTP_PASSWORD}' or '@encrypted'" | default = "", smtp_use_tls | Bool | default = false, smtp_use_starttls | Bool | default = true, sendgrid_api_key | String | doc "SendGrid API key — prefer '${SENDGRID_API_KEY}'" | default = "", sendgrid_endpoint | String | default = "https://api.sendgrid.com/v3/mail/send", from_email | String | doc "Sender address — prefer '${EMAIL_FROM}'" | default = "", from_name | String | doc "Sender display name — prefer '${EMAIL_FROM_NAME}'" | default = "", template_dir | String | optional, email_enabled | Bool | optional, }, OAuthProvider = { client_id | String | default = "", client_secret | String | default = "", redirect_uri | String | default = "", }, OAuthConfig = { enabled | Bool | default = false, google | OAuthProvider | optional, github | OAuthProvider | optional, }, }