Files
sim/helm/sim/values.schema.json
T
Waleed 9dc828f36a fix(email): greet SMTP relays with a qualified hostname instead of [127.0.0.1] (#6799)
* fix(email): greet SMTP relays with a qualified hostname instead of [127.0.0.1]

Nodemailer derives the EHLO greeting from os.hostname() and substitutes the
address literal [127.0.0.1] whenever that name contains no dot. Kubernetes pod
hostnames never contain one, so every k8s deployment introduced itself to the
relay as loopback and strict relays refused the session before any mail moved.

Send the domain the app is served from instead, as RFC 5321 4.1.4 asks, with
SMTP_EHLO_NAME to override it for relays that expect a different identity.

* fix(email): parse EHLO address literals and drop a port from the app domain

Review round 1. The bracketed branch matched a character class rather than an
address, so [::::] and [13] reached the relay as a greeting it would refuse.
Parse the address with node:net instead, which also admits the RFC 5321
IPv6: form.

getEmailDomain reports a URL host, so a deployment served on a non-default
port failed the qualified-name check and fell back to nodemailer's default —
[127.0.0.1] again on Kubernetes, the exact failure this change exists to fix.
Strip the port before validating.

* fix(email): accept any casing of the IPv6 literal tag, and stop owning SMTP_EHLO_NAME in setup

Review round 2. RFC 5321 tags the IPv6 address-literal form, and RFC 5234
makes ABNF string literals case-insensitive, so [ipv6:2001:db8::1] is as valid
as [IPv6:...]. The exact-prefix check routed it to isIPv4 and discarded it.

Drop SMTP_EHLO_NAME from the email capability's optional fields. SMTP_SECURE,
the same kind of optional transport knob on the same provider, is not modelled
there either, and claiming the field obliged the setup wizard to prompt for it
— a field whose entire purpose is to stay unset now that the default is right.
2026-08-17 18:09:02 -07:00

1366 lines
46 KiB
JSON

{
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["app", "realtime"],
"properties": {
"global": {
"type": "object",
"properties": {
"imageRegistry": {
"type": "string",
"description": "Global Docker image registry (host[:port][/path]). Supports plain hosts (registry.example.com), host:port (registry.example.com:5000), and host+path forms used by Artifactory virtual repos, Harbor projects, GCR (gcr.io/project-id), and ECR-with-namespace."
},
"useRegistryForAllImages": {
"type": "boolean",
"description": "Use registry for all images, not just simstudioai/* images"
},
"imagePullSecrets": {
"type": "array",
"items": {
"type": "object"
},
"description": "Global image pull secrets"
},
"storageClass": {
"type": "string",
"description": "Global storage class for PVCs"
}
}
},
"app": {
"type": "object",
"required": ["enabled"],
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable the main application"
},
"replicaCount": {
"type": "integer",
"minimum": 1,
"description": "Number of app replicas"
},
"resources": {
"type": "object",
"properties": {
"limits": {
"type": "object",
"properties": {
"memory": {
"type": "string",
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti|Pi|Ei|m|k|M|G|T|P|E)?$",
"description": "Memory limit (e.g., 4Gi, 2048Mi)"
},
"cpu": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?m?$",
"description": "CPU limit (e.g., 2000m, 2.0)"
},
"nvidia.com/gpu": {
"type": "string",
"pattern": "^[0-9]+$",
"description": "GPU limit"
}
}
},
"requests": {
"type": "object",
"properties": {
"memory": {
"type": "string",
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti|Pi|Ei|m|k|M|G|T|P|E)?$",
"description": "Memory request (e.g., 2Gi, 1024Mi)"
},
"cpu": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?m?$",
"description": "CPU request (e.g., 1000m, 1.0)"
}
}
}
}
},
"secrets": {
"type": "object",
"description": "Secret management configuration",
"properties": {
"existingSecret": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Use an existing secret instead of creating one"
},
"name": {
"type": "string",
"description": "Name of the existing Kubernetes secret"
}
}
}
}
},
"env": {
"type": "object",
"properties": {
"BETTER_AUTH_SECRET": {
"type": "string",
"anyOf": [{ "minLength": 32 }, { "const": "" }],
"description": "Auth secret (minimum 32 characters required when not using existingSecret; leave empty when the value comes from existingSecret/ESO instead)"
},
"ENCRYPTION_KEY": {
"type": "string",
"anyOf": [{ "minLength": 32 }, { "const": "" }],
"description": "Encryption key (minimum 32 characters required when not using existingSecret; leave empty when the value comes from existingSecret/ESO instead)"
},
"NEXT_PUBLIC_APP_URL": {
"type": "string",
"anyOf": [{ "format": "uri" }, { "const": "" }],
"description": "Public application URL (default http://localhost:3000 via envDefaults; set explicitly here to override in Secret)"
},
"INTERNAL_API_BASE_URL": {
"type": "string",
"anyOf": [
{
"format": "uri"
},
{
"const": ""
}
],
"description": "Optional server-side internal base URL for internal /api self-calls (must include http:// or https://); defaults to NEXT_PUBLIC_APP_URL when unset"
},
"BETTER_AUTH_URL": {
"type": "string",
"anyOf": [{ "format": "uri" }, { "const": "" }],
"description": "Authentication service URL (default http://localhost:3000 via envDefaults; set explicitly here to override in Secret)"
},
"SOCKET_SERVER_URL": {
"type": "string",
"format": "uri",
"description": "Socket server URL"
},
"NEXT_PUBLIC_SOCKET_URL": {
"type": "string",
"anyOf": [
{
"format": "uri"
},
{
"const": ""
}
],
"description": "Public socket URL; leave empty to default to the page origin (assumes reverse proxy routes /socket.io)"
},
"TRUSTED_ORIGINS": {
"type": "string",
"description": "Comma-separated additional public origins to trust for auth (e.g. 'https://app.example.com,https://www.example.com'). Merged into Better Auth trustedOrigins."
},
"AUTH_TRUSTED_PROXIES": {
"type": "string",
"description": "Comma-separated reverse-proxy IPs or CIDR ranges in front of the app (e.g. '10.0.0.0/16'). Better Auth walks x-forwarded-for right to left, skips these hops, and uses the first untrusted address as the client IP."
},
"SSO_TRUSTED_PROVIDER_IDS": {
"type": "string",
"description": "Comma-separated SSO provider IDs to trust for automatic account linking when an SSO sign-in matches an existing account's email. Only needed for IdPs that do not assert email_verified. Merged into Better Auth accountLinking.trustedProviders."
},
"NODE_ENV": {
"type": "string",
"enum": ["development", "test", "production"],
"description": "Runtime environment"
},
"NEXT_TELEMETRY_DISABLED": {
"type": "string",
"description": "Disable Next.js telemetry"
},
"RESEND_API_KEY": {
"type": "string",
"description": "Resend API key for transactional emails"
},
"FROM_EMAIL_ADDRESS": {
"type": "string",
"description": "Complete from address (e.g., \"Sim <noreply@domain.com>\" or \"DoNotReply@domain.com\")"
},
"EMAIL_DOMAIN": {
"type": "string",
"description": "Domain for sending emails (fallback when FROM_EMAIL_ADDRESS not set)"
},
"AWS_SES_REGION": {
"type": "string",
"description": "AWS region for SES (e.g., 'us-east-1'). Credentials are resolved via the standard AWS provider chain (env vars, IRSA, EC2/ECS task role, SSO)."
},
"AZURE_ACS_CONNECTION_STRING": {
"type": "string",
"description": "Azure Communication Services connection string (email provider — used when Resend/SES/SMTP are not configured)."
},
"SMTP_HOST": {
"type": "string",
"description": "SMTP server hostname. When set together with SMTP_PORT, enables the SMTP mail provider."
},
"SMTP_PORT": {
"type": "string",
"description": "SMTP server port. 465 = implicit TLS, 587 = STARTTLS, 25 = plain."
},
"SMTP_USER": {
"type": "string",
"description": "SMTP username (optional — leave empty for unauthenticated relays like MailHog)."
},
"SMTP_PASS": {
"type": "string",
"description": "SMTP password (optional — leave empty for unauthenticated relays)."
},
"SMTP_SECURE": {
"type": "string",
"description": "Set to 'true' to force TLS on connect. Defaults to true when SMTP_PORT=465."
},
"SMTP_EHLO_NAME": {
"type": "string",
"description": "Hostname sent in the SMTP EHLO greeting. Defaults to the domain the app is served from; set only when the relay expects a different identity."
},
"GMAIL_CREDENTIALS_JSON": {
"type": "string",
"description": "Inline Google service-account JSON with domain-wide delegation for the Gmail API mail provider."
},
"GMAIL_SENDER": {
"type": "string",
"description": "Google Workspace user the Gmail service account impersonates when sending (e.g., noreply@yourdomain.com)."
},
"GOOGLE_CLIENT_ID": {
"type": "string",
"description": "Google OAuth client ID"
},
"GOOGLE_CLIENT_SECRET": {
"type": "string",
"description": "Google OAuth client secret"
},
"GITHUB_CLIENT_ID": {
"type": "string",
"description": "GitHub OAuth client ID"
},
"GITHUB_CLIENT_SECRET": {
"type": "string",
"description": "GitHub OAuth client secret"
},
"DISABLE_GOOGLE_AUTH": {
"type": "string",
"description": "Set to 'true' to hide Google OAuth login even when credentials are configured"
},
"DISABLE_GITHUB_AUTH": {
"type": "string",
"description": "Set to 'true' to hide GitHub OAuth login even when credentials are configured"
},
"MSHIP_SYSPROMPT_OVERRIDE": {
"type": "string",
"description": "Optional enterprise-only highest-priority system prompt override forwarded to Mothership"
},
"OPENAI_API_KEY": {
"type": "string",
"description": "Primary OpenAI API key"
},
"OPENAI_API_KEY_1": {
"type": "string",
"description": "Additional OpenAI API key for load balancing"
},
"OPENAI_API_KEY_2": {
"type": "string",
"description": "Additional OpenAI API key for load balancing"
},
"OPENAI_API_KEY_3": {
"type": "string",
"description": "Additional OpenAI API key for load balancing"
},
"MISTRAL_API_KEY": {
"type": "string",
"description": "Mistral AI API key"
},
"ANTHROPIC_API_KEY_1": {
"type": "string",
"description": "Primary Anthropic Claude API key"
},
"ANTHROPIC_API_KEY_2": {
"type": "string",
"description": "Additional Anthropic API key for load balancing"
},
"ANTHROPIC_API_KEY_3": {
"type": "string",
"description": "Additional Anthropic API key for load balancing"
},
"OLLAMA_URL": {
"type": "string",
"description": "Ollama local LLM server URL (leave empty if not using Ollama)"
},
"ELEVENLABS_API_KEY": {
"type": "string",
"description": "ElevenLabs API key for text-to-speech in deployed chat"
},
"KB_OPENAI_MODEL_NAME": {
"type": "string",
"description": "Azure deployment name serving the configured KB embedding model (used only when AZURE_OPENAI_* credentials are set)."
},
"WAND_OPENAI_MODEL_NAME": {
"type": "string",
"description": "Wand generation model deployment name (works with both regular OpenAI and Azure OpenAI)."
},
"OCR_AZURE_ENDPOINT": {
"type": "string",
"description": "Azure Mistral OCR service endpoint."
},
"OCR_AZURE_MODEL_NAME": {
"type": "string",
"description": "Azure Mistral OCR model name."
},
"OCR_AZURE_API_KEY": {
"type": "string",
"description": "Azure Mistral OCR API key."
},
"RATE_LIMIT_WINDOW_MS": {
"type": "string",
"description": "Rate limit window duration in milliseconds"
},
"RATE_LIMIT_FREE_SYNC": {
"type": "string",
"description": "Free tier sync API executions per minute"
},
"RATE_LIMIT_PRO_SYNC": {
"type": "string",
"description": "Pro tier sync API executions per minute"
},
"RATE_LIMIT_TEAM_SYNC": {
"type": "string",
"description": "Team tier sync API executions per minute"
},
"RATE_LIMIT_ENTERPRISE_SYNC": {
"type": "string",
"description": "Enterprise tier sync API executions per minute"
},
"RATE_LIMIT_FREE_ASYNC": {
"type": "string",
"description": "Free tier async API executions per minute"
},
"RATE_LIMIT_FREE_API_ENDPOINT": {
"type": "string",
"description": "Free tier v1 API endpoint requests per minute"
},
"RATE_LIMIT_PRO_ASYNC": {
"type": "string",
"description": "Pro tier async API executions per minute"
},
"RATE_LIMIT_TEAM_ASYNC": {
"type": "string",
"description": "Team tier async API executions per minute"
},
"RATE_LIMIT_ENTERPRISE_ASYNC": {
"type": "string",
"description": "Enterprise tier async API executions per minute"
},
"BILLING_CONCURRENCY_LIMIT_FREE": {
"type": "string",
"description": "In-flight executions per free billing account"
},
"BILLING_CONCURRENCY_LIMIT_PRO": {
"type": "string",
"description": "In-flight executions per Pro-tier billing account (Pro and Pro for Teams)"
},
"BILLING_CONCURRENCY_LIMIT_TEAM": {
"type": "string",
"description": "In-flight executions per Max-tier billing account (Max and Max for Teams)"
},
"BILLING_CONCURRENCY_LIMIT_ENTERPRISE": {
"type": "string",
"description": "Default in-flight executions per Enterprise billing account"
},
"MANUAL_EXECUTION_LIMIT": {
"type": "string",
"description": "Manual execution bypass value"
},
"NEXT_PUBLIC_BRAND_NAME": {
"type": "string",
"description": "Custom brand name"
},
"NEXT_PUBLIC_BRAND_LOGO_URL": {
"type": "string",
"description": "Custom logo URL (leave empty for default)"
},
"NEXT_PUBLIC_BRAND_FAVICON_URL": {
"type": "string",
"description": "Custom favicon URL (leave empty for default)"
},
"NEXT_PUBLIC_CUSTOM_CSS_URL": {
"type": "string",
"description": "Custom stylesheet URL (leave empty for none)"
},
"NEXT_PUBLIC_SUPPORT_EMAIL": {
"type": "string",
"anyOf": [{ "format": "email" }, { "const": "" }],
"description": "Support email address (default help@sim.ai via envDefaults; set explicitly here to override in Secret)"
},
"NEXT_PUBLIC_DOCUMENTATION_URL": {
"type": "string",
"description": "Documentation URL"
},
"NEXT_PUBLIC_TERMS_URL": {
"type": "string",
"description": "Terms of service URL"
},
"NEXT_PUBLIC_PRIVACY_URL": {
"type": "string",
"description": "Privacy policy URL"
},
"ALLOWED_LOGIN_EMAILS": {
"type": "string",
"description": "Comma-separated list of allowed email addresses for login"
},
"ALLOWED_LOGIN_DOMAINS": {
"type": "string",
"description": "Comma-separated list of allowed email domains for login"
}
}
}
}
},
"realtime": {
"type": "object",
"required": ["enabled"],
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable the realtime service"
},
"replicaCount": {
"type": "integer",
"minimum": 1,
"description": "Number of realtime replicas"
},
"resources": {
"type": "object",
"properties": {
"limits": {
"type": "object",
"properties": {
"memory": {
"type": "string",
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti|Pi|Ei|m|k|M|G|T|P|E)?$",
"description": "Memory limit (e.g., 2Gi, 1024Mi)"
},
"cpu": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?m?$",
"description": "CPU limit (e.g., 1000m, 1.0)"
}
}
},
"requests": {
"type": "object",
"properties": {
"memory": {
"type": "string",
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti|Pi|Ei|m|k|M|G|T|P|E)?$",
"description": "Memory request (e.g., 1Gi, 512Mi)"
},
"cpu": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?m?$",
"description": "CPU request (e.g., 500m, 0.5)"
}
}
}
}
},
"env": {
"type": "object",
"properties": {
"BETTER_AUTH_SECRET": {
"type": "string",
"anyOf": [{ "minLength": 32 }, { "const": "" }],
"description": "Auth secret (minimum 32 characters required when not using existingSecret; leave empty when the value comes from existingSecret/ESO instead)"
},
"NEXT_PUBLIC_APP_URL": {
"type": "string",
"anyOf": [{ "format": "uri" }, { "const": "" }],
"description": "Public application URL (default http://localhost:3000 via envDefaults; set explicitly here to override in Secret)"
},
"BETTER_AUTH_URL": {
"type": "string",
"anyOf": [{ "format": "uri" }, { "const": "" }],
"description": "Authentication service URL (default http://localhost:3000 via envDefaults; set explicitly here to override in Secret)"
},
"ALLOWED_ORIGINS": {
"type": "string",
"description": "CORS allowed origins"
},
"NODE_ENV": {
"type": "string",
"enum": ["development", "test", "production"],
"description": "Runtime environment"
}
}
}
}
},
"redis": {
"type": "object",
"description": "In-cluster Redis for pub/sub, the Socket.IO adapter, and the idempotency/progress stores. Disable and set app.env.REDIS_URL to use a managed instance.",
"properties": {
"enabled": {
"type": "boolean",
"description": "Deploy the bundled Redis. Skipped automatically when app.env.REDIS_URL is set or a secret-manager mode is active."
},
"image": {
"type": "object",
"properties": {
"repository": { "type": "string" },
"tag": { "type": "string" },
"digest": { "type": "string" },
"pullPolicy": { "type": "string", "enum": ["Always", "IfNotPresent", "Never"] }
}
},
"maxmemory": { "type": "string", "description": "Redis maxmemory value, e.g. 512mb" },
"maxmemoryPolicy": {
"type": "string",
"enum": [
"noeviction",
"allkeys-lru",
"allkeys-lfu",
"allkeys-random",
"volatile-lru",
"volatile-lfu",
"volatile-random",
"volatile-ttl"
],
"description": "Redis eviction policy. An invalid value makes Redis exit at startup, so it is enumerated."
},
"resources": { "type": "object" },
"nodeSelector": { "type": "object" },
"podSecurityContext": { "type": "object" },
"securityContext": { "type": "object" }
}
},
"postgresql": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable internal PostgreSQL"
},
"resources": {
"type": "object",
"properties": {
"limits": {
"type": "object",
"properties": {
"memory": {
"type": "string",
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti|Pi|Ei|m|k|M|G|T|P|E)?$",
"description": "Memory limit (e.g., 2Gi, 1024Mi)"
},
"cpu": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?m?$",
"description": "CPU limit"
}
}
},
"requests": {
"type": "object",
"properties": {
"memory": {
"type": "string",
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti|Pi|Ei|m|k|M|G|T|P|E)?$",
"description": "Memory request (e.g., 1Gi, 512Mi)"
},
"cpu": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?m?$",
"description": "CPU request (e.g., 500m, 0.5)"
}
}
}
}
},
"persistence": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable persistent storage"
},
"size": {
"type": "string",
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti|Pi|Ei)$",
"description": "Storage size (e.g., 10Gi, 50Gi)"
}
}
},
"auth": {
"type": "object",
"properties": {
"username": {
"type": "string",
"minLength": 1,
"description": "PostgreSQL username"
},
"database": {
"type": "string",
"minLength": 1,
"description": "PostgreSQL database name"
},
"password": {
"type": "string",
"anyOf": [{ "minLength": 8 }, { "const": "" }],
"description": "PostgreSQL password (minimum 8 characters when not using existingSecret; leave empty when the value comes from existingSecret/ESO instead)"
},
"existingSecret": {
"type": "object",
"description": "Use an existing secret for PostgreSQL credentials",
"properties": {
"enabled": {
"type": "boolean",
"description": "Use an existing secret instead of creating one"
},
"name": {
"type": "string",
"description": "Name of the existing Kubernetes secret"
}
}
}
}
}
}
},
"externalDatabase": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Use external database"
},
"host": {
"type": "string",
"format": "hostname",
"description": "External database host"
},
"port": {
"type": "integer",
"minimum": 1,
"maximum": 65535,
"description": "External database port"
},
"username": {
"type": "string",
"description": "External database username"
},
"password": {
"type": "string",
"description": "External database password"
},
"database": {
"type": "string",
"description": "External database name"
},
"sslMode": {
"type": "string",
"enum": ["disable", "allow", "prefer", "require", "verify-ca", "verify-full"],
"description": "SSL mode for database connection"
},
"existingSecret": {
"type": "object",
"description": "Use an existing secret for external database credentials",
"properties": {
"enabled": {
"type": "boolean",
"description": "Use an existing secret instead of creating one"
},
"name": {
"type": "string",
"description": "Name of the existing Kubernetes secret"
}
}
}
},
"if": {
"properties": {
"enabled": {
"const": true
}
}
},
"then": {
"required": ["host", "username", "password", "database"]
}
},
"autoscaling": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable autoscaling"
},
"minReplicas": {
"type": "integer",
"minimum": 1,
"description": "Minimum replicas"
},
"maxReplicas": {
"type": "integer",
"minimum": 1,
"description": "Maximum replicas"
}
},
"if": {
"properties": {
"enabled": {
"const": true
}
}
},
"then": {
"required": ["minReplicas", "maxReplicas"]
}
},
"ollama": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable Ollama service"
},
"replicaCount": {
"type": "integer",
"minimum": 1,
"description": "Number of Ollama replicas"
},
"resources": {
"type": "object",
"properties": {
"limits": {
"type": "object",
"properties": {
"memory": {
"type": "string",
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti|Pi|Ei|m|k|M|G|T|P|E)?$",
"description": "Memory limit (e.g., 8Gi, 4096Mi)"
},
"cpu": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?m?$",
"description": "CPU limit"
},
"nvidia.com/gpu": {
"type": "string",
"pattern": "^[0-9]+$",
"description": "GPU limit"
}
}
},
"requests": {
"type": "object",
"properties": {
"memory": {
"type": "string",
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti|Pi|Ei|m|k|M|G|T|P|E)?$",
"description": "Memory request (e.g., 4Gi, 2048Mi)"
},
"cpu": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?m?$",
"description": "CPU request (e.g., 1000m, 1.0)"
}
}
}
}
},
"persistence": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable persistent storage"
},
"size": {
"type": "string",
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti|Pi|Ei)$",
"description": "Storage size (e.g., 100Gi, 500Gi)"
}
}
},
"gpu": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable GPU support"
},
"count": {
"type": "integer",
"minimum": 1,
"description": "Number of GPUs"
}
}
}
}
},
"pii": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable the Presidio PII redaction service"
},
"replicaCount": {
"type": "integer",
"minimum": 1,
"description": "Number of PII service replicas"
},
"image": {
"type": "object",
"properties": {
"repository": { "type": "string" },
"tag": { "type": "string" },
"digest": { "type": "string" },
"pullPolicy": { "type": "string", "enum": ["Always", "IfNotPresent", "Never"] }
}
},
"resources": {
"type": "object",
"properties": {
"limits": {
"type": "object",
"properties": {
"memory": {
"type": "string",
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti|Pi|Ei|m|k|M|G|T|P|E)?$",
"description": "Memory limit (e.g., 8Gi, 4096Mi)"
},
"cpu": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?m?$",
"description": "CPU limit"
}
}
},
"requests": {
"type": "object",
"properties": {
"memory": {
"type": "string",
"pattern": "^[0-9]+(Ki|Mi|Gi|Ti|Pi|Ei|m|k|M|G|T|P|E)?$",
"description": "Memory request (e.g., 4Gi, 2048Mi)"
},
"cpu": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?m?$",
"description": "CPU request (e.g., 1000m, 1.0)"
}
}
}
}
},
"service": {
"type": "object",
"properties": {
"type": { "type": "string" },
"port": { "type": "integer" },
"targetPort": { "type": "integer" }
}
},
"env": { "type": "object" },
"nodeSelector": { "type": "object" },
"topologySpreadConstraints": { "type": "array", "items": { "type": "object" } },
"podSecurityContext": { "type": "object" },
"securityContext": { "type": "object" },
"startupProbe": { "type": "object" },
"livenessProbe": { "type": "object" },
"readinessProbe": { "type": "object" },
"extraVolumes": { "type": "array", "items": { "type": "object" } },
"extraVolumeMounts": { "type": "array", "items": { "type": "object" } }
}
},
"telemetry": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable telemetry collection"
},
"replicaCount": {
"type": "integer",
"minimum": 1,
"description": "Number of telemetry collector replicas"
}
}
},
"tests": {
"type": "object",
"description": "Helm test hook configuration (helm test)",
"properties": {
"enabled": { "type": "boolean" },
"image": {
"type": "object",
"properties": {
"repository": { "type": "string" },
"tag": { "type": "string" },
"pullPolicy": { "type": "string", "enum": ["Always", "IfNotPresent", "Never"] },
"pullSecrets": { "type": "array", "items": { "type": "object" } }
}
},
"timeoutSeconds": { "type": "integer", "minimum": 1 },
"resources": { "type": "object" }
}
},
"sharedStorage": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable shared storage"
},
"volumes": {
"type": "array",
"items": {
"type": "object",
"required": ["name", "size"],
"properties": {
"name": {
"type": "string",
"minLength": 1,
"description": "Volume name"
},
"size": {
"type": "string",
"pattern": "^[0-9]+[KMGT]i$",
"description": "Volume size (e.g., 100Gi)"
}
}
}
}
}
},
"copilot": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable the Copilot microservice"
},
"server": {
"type": "object",
"properties": {
"replicaCount": {
"type": "integer",
"minimum": 1,
"description": "Number of Copilot replicas"
},
"image": {
"type": "object",
"properties": {
"repository": {
"type": "string",
"description": "Copilot image repository"
},
"tag": {
"type": "string",
"description": "Copilot image tag"
},
"pullPolicy": {
"type": "string",
"enum": ["Always", "IfNotPresent", "Never"],
"description": "Image pull policy"
}
}
},
"resources": {
"type": "object",
"properties": {
"limits": { "type": "object" },
"requests": { "type": "object" }
}
},
"nodeSelector": {
"type": "object",
"additionalProperties": { "type": "string" }
},
"env": {
"type": "object",
"additionalProperties": { "type": "string" },
"description": "Environment variables for Copilot"
},
"extraEnv": {
"type": "array",
"items": { "type": "object" },
"description": "Additional environment variable definitions"
},
"extraEnvFrom": {
"type": "array",
"items": { "type": "object" },
"description": "Additional envFrom sources"
},
"secret": {
"type": "object",
"properties": {
"create": {
"type": "boolean",
"description": "Whether to create a secret from copilot.server.env"
},
"name": {
"type": "string",
"description": "Override name for the Copilot secret"
},
"annotations": {
"type": "object",
"additionalProperties": { "type": "string" },
"description": "Annotations added to the Copilot secret"
}
}
},
"service": {
"type": "object",
"properties": {
"type": { "type": "string" },
"port": { "type": "integer" },
"targetPort": { "type": "integer" }
}
},
"podDisruptionBudget": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable PodDisruptionBudget for Copilot server"
},
"minAvailable": {
"type": "integer",
"description": "Minimum number of available pods"
},
"maxUnavailable": {
"type": "integer",
"description": "Maximum number of unavailable pods"
}
}
}
}
},
"postgresql": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Deploy an internal PostgreSQL instance for Copilot"
},
"auth": {
"type": "object",
"properties": {
"username": { "type": "string" },
"password": { "type": "string" },
"database": { "type": "string" }
}
},
"persistence": {
"type": "object",
"properties": {
"enabled": { "type": "boolean" },
"size": { "type": "string" },
"storageClass": { "type": "string" }
}
}
}
},
"database": {
"type": "object",
"properties": {
"existingSecretName": {
"type": "string",
"description": "Existing secret containing the Copilot DATABASE_URL"
},
"secretKey": {
"type": "string",
"description": "Key name inside the database secret"
},
"url": {
"type": "string",
"description": "External database connection string"
}
}
},
"migrations": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable the Copilot migration job"
},
"image": {
"type": "object",
"properties": {
"repository": { "type": "string" },
"tag": { "type": "string" },
"pullPolicy": { "type": "string" }
}
},
"resources": {
"type": "object",
"properties": {
"limits": { "type": "object" },
"requests": { "type": "object" }
}
},
"backoffLimit": {
"type": "integer",
"minimum": 0
},
"restartPolicy": {
"type": "string",
"enum": ["Never", "OnFailure"]
}
}
}
}
},
"externalSecrets": {
"type": "object",
"description": "External Secrets Operator integration",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable External Secrets Operator integration"
},
"apiVersion": {
"type": "string",
"enum": ["v1", "v1beta1"],
"description": "ESO API version - use v1 for ESO v0.17+ (recommended), v1beta1 for older versions"
},
"refreshInterval": {
"type": "string",
"description": "How often to sync secrets from external store"
},
"secretStoreRef": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of the SecretStore or ClusterSecretStore"
},
"kind": {
"type": "string",
"enum": ["SecretStore", "ClusterSecretStore"],
"description": "Kind of the store"
}
}
},
"remoteRefs": {
"type": "object",
"description": "Remote key paths in external secret store",
"properties": {
"app": {
"type": "object",
"additionalProperties": { "type": "string" }
},
"postgresql": {
"type": "object",
"properties": {
"password": { "type": "string" }
}
},
"externalDatabase": {
"type": "object",
"properties": {
"password": { "type": "string" }
}
}
}
}
}
},
"ingress": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable ingress"
},
"className": {
"type": "string",
"description": "Ingress class name"
},
"app": {
"type": "object",
"properties": {
"host": {
"type": "string",
"format": "hostname",
"description": "Main application hostname"
}
}
},
"realtime": {
"type": "object",
"properties": {
"host": {
"type": "string",
"format": "hostname",
"description": "Realtime service hostname"
}
}
},
"copilot": {
"type": "object",
"properties": {
"host": {
"type": "string",
"format": "hostname",
"description": "Copilot service hostname"
},
"paths": {
"type": "array",
"items": {
"type": "object",
"properties": {
"path": { "type": "string" },
"pathType": { "type": "string" }
}
},
"description": "Ingress paths for Copilot service"
}
}
},
"tls": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"description": "Enable TLS"
},
"secretName": {
"type": "string",
"minLength": 1,
"description": "TLS secret name"
}
}
}
}
},
"nameOverride": {
"type": "string",
"description": "Override the chart name used in resource names"
},
"fullnameOverride": {
"type": "string",
"description": "Override the fully qualified release name used in resource names"
},
"affinity": {
"type": "object",
"description": "Pod affinity/anti-affinity rules applied to app and realtime pods"
},
"branding": {
"type": "object",
"description": "Whitelabel branding configuration (logos, names, colors)"
},
"certManager": {
"type": "object",
"description": "Chart-managed cert-manager issuers (self-signed bootstrap + CA) for postgresql.tls"
},
"cronjobs": {
"type": "object",
"description": "Scheduled jobs (schedule execution, polling, reconciliation)"
},
"extraEnvVars": {
"type": "array",
"description": "Additional environment variables added to app and realtime containers"
},
"extraVolumeMounts": {
"type": "array",
"description": "Additional volume mounts added to app and realtime containers"
},
"extraVolumes": {
"type": "array",
"description": "Additional volumes added to app and realtime pods"
},
"ingressInternal": {
"type": "object",
"description": "Secondary internal ingress configuration"
},
"migrations": {
"type": "object",
"description": "Database migrations init-container configuration"
},
"monitoring": {
"type": "object",
"description": "Prometheus Operator ServiceMonitor configuration"
},
"networkPolicy": {
"type": "object",
"description": "NetworkPolicy toggles and custom ingress/egress rules"
},
"podAnnotations": {
"type": "object",
"description": "Annotations added to app and realtime pods"
},
"podDisruptionBudget": {
"type": "object",
"description": "PodDisruptionBudget configuration for app and realtime"
},
"podLabels": {
"type": "object",
"description": "Labels added to app and realtime pods"
},
"serviceAccount": {
"type": "object",
"description": "ServiceAccount creation, name, and annotations (e.g. Workload Identity)"
},
"tolerations": {
"type": "array",
"description": "Tolerations applied to app and realtime pods"
}
},
"additionalProperties": false,
"allOf": [
{
"if": {
"properties": {
"postgresql": {
"properties": {
"enabled": {
"const": false
}
}
}
},
"anyOf": [
{
"properties": {
"app": {
"properties": {
"enabled": {
"const": true
}
}
}
}
},
{
"properties": {
"realtime": {
"properties": {
"enabled": {
"const": true
}
}
}
}
}
]
},
"then": {
"properties": {
"externalDatabase": {
"properties": {
"enabled": {
"const": true
}
},
"required": ["enabled"]
}
}
}
}
]
}