Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 55 additions & 13 deletions chart/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,16 @@ Secret name used to store sensitive environment variables.
{{- end }}

{{/*
Determine if an environment variable name should be treated as a secret.
Determine if an environment variable should be treated as a secret.
A key is a secret if it exists in openopsEnvSecrets or its value references openopsEnvSecrets.
Expected dict: { "root": $, "key": "ENV_VAR", "value": "some-value" }
*/}}
{{- define "openops.isSecretKey" -}}
{{- $key := upper . -}}
{{- if or (contains "PASSWORD" $key) (contains "SECRET" $key) (contains "KEY" $key) (contains "LOGZIO_TOKEN" $key) -}}
{{- $root := .root -}}
{{- $key := .key -}}
{{- $value := .value | default "" | toString -}}
{{- $secrets := default (dict) $root.Values.openopsEnvSecrets -}}
{{- if or (hasKey $secrets $key) (contains ".Values.openopsEnvSecrets." $value) -}}
true
{{- else -}}
false
Expand All @@ -162,12 +167,13 @@ Expected dict: { "root": $, "key": "ENV", "value": "value" }
{{- $root := .root -}}
{{- $key := .key -}}
{{- $value := .value -}}
{{- if eq (include "openops.isSecretKey" $key) "true" -}}
{{- if eq (include "openops.isSecretKey" (dict "root" $root "key" $key "value" $value)) "true" -}}
- name: {{ $key }}
valueFrom:
secretKeyRef:
name: {{ include "openops.secretName" $root }}
key: {{ $key }}
optional: true
{{- else -}}
- name: {{ $key }}
value: {{ tpl (tpl $value $root) $root | quote }}
Expand All @@ -189,7 +195,43 @@ Expected dict: { "root": $, "env": dict }
{{- end }}

{{/*
Render deployment strategy
Resolve the AWS Secrets Manager property name for a secret key.
For standalone keys (in openopsEnvSecrets), the property is the key itself.
For derived keys (in tables/analytics/etc), the value is a template ref like
"{{ .Values.openopsEnvSecrets.OPS_POSTGRES_PASSWORD }}" - extract the referenced key name.
Expected dict: { "key": "DATABASE_PASSWORD", "value": "{{ .Values.openopsEnvSecrets.OPS_POSTGRES_PASSWORD }}" }
*/}}
{{- define "openops.secretPropertyName" -}}
{{- $key := .key -}}
{{- $value := .value | toString -}}
{{- if contains ".Values.openopsEnvSecrets." $value -}}
{{- $value | trimPrefix "{{" | trimPrefix " " | trimSuffix "}}" | trimSuffix " " | trimPrefix ".Values.openopsEnvSecrets." -}}
{{- else -}}
{{- $key -}}
{{- end -}}
{{- end }}
Comment on lines +207 to +212
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

openops.secretPropertyName is not robust for the actual value strings used in values.yaml (they are typically quoted, e.g. "{{ .Values.openopsEnvSecrets.X }}"). Because the function only trims {{/}} and spaces, it will often fail to strip leading quotes/whitespace and may emit an invalid remoteRef.property like the full template string. Consider extracting the referenced key with a regex (capture group on \.Values\.openopsEnvSecrets\.([A-Z0-9_]+)) and falling back to $key when there is no match.

Suggested change
{{- if contains ".Values.openopsEnvSecrets." $value -}}
{{- $value | trimPrefix "{{" | trimPrefix " " | trimSuffix "}}" | trimSuffix " " | trimPrefix ".Values.openopsEnvSecrets." -}}
{{- else -}}
{{- $key -}}
{{- end -}}
{{- end }}
{{- $extracted := regexReplaceAll ".*\\.Values\\.openopsEnvSecrets\\.([A-Z0-9_]+).*" $value "$1" -}}
{{- if ne $extracted "" -}}
{{- $extracted -}}
{{- else -}}
{{- $key -}}
{{- end }}
{{- end }}

Copilot uses AI. Check for mistakes.

{{/*
Collect ExternalSecret data entries for all secret keys in an env map.
Emits YAML list items for keys that are secrets (in openopsEnvSecrets or referencing it).
Expected dict: { "root": $, "env": dict, "secretName": "my-secret" }
*/}}
{{- define "openops.collectSecretEntries" -}}
{{- $root := .root -}}
{{- $env := .env -}}
{{- $secretName := .secretName -}}
{{- range $k := keys $env | sortAlpha -}}
{{- $v := index $env $k -}}
{{- if eq (include "openops.isSecretKey" (dict "root" $root "key" $k "value" ($v | toString))) "true" }}
- secretKey: {{ $k }}
remoteRef:
key: {{ $secretName }}
property: {{ include "openops.secretPropertyName" (dict "key" $k "value" ($v | toString)) }}
{{- end -}}
{{- end -}}
{{- end }}

{{/*
*/}}
{{- define "openops.deploymentStrategy" -}}
{{- if .Values.global.strategy }}
Expand Down Expand Up @@ -293,26 +335,26 @@ Validate that required secrets are configured - ALWAYS ENFORCED
{{- /* Skip validation if using an external secret manager */ -}}
{{- $usingExistingSecret := and .Values.secretEnv .Values.secretEnv.existingSecret (not .Values.secretEnv.create) -}}
{{- if not $usingExistingSecret -}}
{{- $encKey := .Values.openopsEnv.OPS_ENCRYPTION_KEY -}}
{{- $encKey := .Values.openopsEnvSecrets.OPS_ENCRYPTION_KEY -}}
{{- if not $encKey -}}
{{- fail "ERROR: OPS_ENCRYPTION_KEY is required. Generate with: openssl rand -hex 32" -}}
{{- fail "ERROR: OPS_ENCRYPTION_KEY is required. Generate with: openssl rand -hex 16" -}}
{{- end -}}
{{- if ne (len $encKey) 32 -}}
Comment on lines +338 to 342
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateSecrets now reads required secrets from .Values.openopsEnvSecrets (e.g., OPS_ENCRYPTION_KEY, OPS_JWT_SECRET). The repo’s example overlays still document/set these under openopsEnv (not openopsEnvSecrets), so following the provided examples will now fail chart rendering. Update the example values files (and/or add a temporary backward-compat shim) to keep the bundled overlays/docs consistent with this new convention.

Copilot uses AI. Check for mistakes.
{{- fail "ERROR: OPS_ENCRYPTION_KEY must be exactly 32 hex characters" -}}
{{- end -}}
Comment on lines +338 to 344
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In openops.validateSecrets, the error message recommends openssl rand -hex 32, but the validation enforces len(OPS_ENCRYPTION_KEY) == 32. openssl rand -hex 32 produces 64 hex chars, so users following the error message will still fail validation. Update the guidance (e.g., -hex 16) or adjust the required length to match the recommended command.

Copilot uses AI. Check for mistakes.
{{- if not .Values.openopsEnv.OPS_JWT_SECRET -}}
{{- fail "ERROR: OPS_JWT_SECRET is required. Generate with: openssl rand -hex 32" -}}
{{- if not .Values.openopsEnvSecrets.OPS_JWT_SECRET -}}
{{- fail "ERROR: OPS_JWT_SECRET is required. Generate with: openssl rand -hex 16" -}}
{{- end -}}
{{- if not .Values.openopsEnv.OPS_OPENOPS_ADMIN_PASSWORD -}}
{{- if not .Values.openopsEnvSecrets.OPS_OPENOPS_ADMIN_PASSWORD -}}
{{- fail "ERROR: OPS_OPENOPS_ADMIN_PASSWORD is required. Use a strong password" -}}
{{- end -}}
{{- if not .Values.openopsEnv.OPS_POSTGRES_PASSWORD -}}
{{- if not .Values.openopsEnvSecrets.OPS_POSTGRES_PASSWORD -}}
{{- fail "ERROR: OPS_POSTGRES_PASSWORD is required. Use a strong password" -}}
{{- end -}}
{{- if not .Values.openopsEnv.OPS_ANALYTICS_ADMIN_PASSWORD -}}
{{- if not .Values.openopsEnvSecrets.OPS_ANALYTICS_ADMIN_PASSWORD -}}
{{- fail "ERROR: OPS_ANALYTICS_ADMIN_PASSWORD is required. Use a strong password" -}}
{{- end -}}
{{- if not .Values.openopsEnv.ANALYTICS_POWERUSER_PASSWORD -}}
{{- if not .Values.openopsEnvSecrets.ANALYTICS_POWERUSER_PASSWORD -}}
{{- fail "ERROR: ANALYTICS_POWERUSER_PASSWORD is required. Use a strong password" -}}
{{- end -}}
{{- end -}}
Comment on lines 359 to 360
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUPERSET_SECRET_KEY is referenced in analytics.env, but openops.validateSecrets doesn’t validate that it’s set (and ideally not equal to any placeholder/default). Consider adding a required check here so installs fail fast instead of starting analytics with an empty/known secret key.

Suggested change
{{- end -}}
{{- end -}}
{{- end -}}
{{- if not .Values.openopsEnvSecrets.SUPERSET_SECRET_KEY -}}
{{- fail "ERROR: SUPERSET_SECRET_KEY is required. Generate with: openssl rand -hex 32" -}}
{{- end -}}
{{- end -}}

Copilot uses AI. Check for mistakes.
Expand Down
1 change: 1 addition & 0 deletions chart/templates/deployment-app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ spec:
- name: OPS_COMPONENT
value: app
{{ include "openops.renderEnv" (dict "root" . "env" .Values.openopsEnv) | nindent 12 }}
{{ include "openops.renderEnv" (dict "root" . "env" .Values.openopsEnvSecrets) | nindent 12 }}
Comment on lines 76 to +77
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rendering .Values.openopsEnv and .Values.openopsEnvSecrets as two separate env: blocks can produce duplicate environment variable names if a user (or an overlay file) sets the same key in both maps, which Kubernetes rejects. Consider rendering a single merged map with a deterministic precedence (e.g., secrets overriding non-secrets) or filtering keys from openopsEnv that also exist in openopsEnvSecrets before rendering.

Suggested change
{{ include "openops.renderEnv" (dict "root" . "env" .Values.openopsEnv) | nindent 12 }}
{{ include "openops.renderEnv" (dict "root" . "env" .Values.openopsEnvSecrets) | nindent 12 }}
{{- $openopsEnv := .Values.openopsEnv | default (dict) }}
{{- $openopsEnvSecrets := .Values.openopsEnvSecrets | default (dict) }}
{{- $mergedEnv := merge $openopsEnv $openopsEnvSecrets }}
{{ include "openops.renderEnv" (dict "root" . "env" $mergedEnv) | nindent 12 }}

Copilot uses AI. Check for mistakes.
ports:
- containerPort: 80
name: http
Expand Down
1 change: 1 addition & 0 deletions chart/templates/deployment-engine.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ spec:
- name: OPS_COMPONENT
value: engine
{{ include "openops.renderEnv" (dict "root" . "env" .Values.openopsEnv) | nindent 12 }}
{{ include "openops.renderEnv" (dict "root" . "env" .Values.openopsEnvSecrets) | nindent 12 }}
Comment on lines 59 to +60
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rendering .Values.openopsEnv and .Values.openopsEnvSecrets separately can lead to duplicate env entries when the same key is defined in both maps (common during migrations/overrides). Kubernetes requires env var names to be unique, so this can break deployments. Prefer rendering a single merged env map (with openopsEnvSecrets taking precedence) or exclude overlapping keys from openopsEnv.

Suggested change
{{ include "openops.renderEnv" (dict "root" . "env" .Values.openopsEnv) | nindent 12 }}
{{ include "openops.renderEnv" (dict "root" . "env" .Values.openopsEnvSecrets) | nindent 12 }}
{{- $mergedEnv := dict -}}
{{- if .Values.openopsEnv }}
{{- $mergedEnv = merge $mergedEnv .Values.openopsEnv }}
{{- end }}
{{- if .Values.openopsEnvSecrets }}
{{- $mergedEnv = merge $mergedEnv .Values.openopsEnvSecrets }}
{{- end }}
{{ include "openops.renderEnv" (dict "root" . "env" $mergedEnv) | nindent 12 }}

Copilot uses AI. Check for mistakes.
{{ include "openops.renderEnv" (dict "root" . "env" .Values.engine.env) | nindent 12 }}
ports:
- containerPort: 3005
Expand Down
97 changes: 10 additions & 87 deletions chart/templates/external-secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,91 +41,14 @@ spec:
name: {{ .Values.secretEnv.existingSecret | default "openops-env" }}
creationPolicy: Owner
data:
- secretKey: OPS_ENCRYPTION_KEY
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_ENCRYPTION_KEY
- secretKey: OPS_JWT_SECRET
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_JWT_SECRET
- secretKey: OPS_POSTGRES_PASSWORD
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_POSTGRES_PASSWORD
- secretKey: OPS_OPENOPS_ADMIN_PASSWORD
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_OPENOPS_ADMIN_PASSWORD
- secretKey: OPS_ANALYTICS_ADMIN_PASSWORD
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_ANALYTICS_ADMIN_PASSWORD
- secretKey: ANALYTICS_POWERUSER_PASSWORD
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: ANALYTICS_POWERUSER_PASSWORD
- secretKey: OPS_SLACK_APP_SIGNING_SECRET
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_SLACK_APP_SIGNING_SECRET
- secretKey: OPS_LOGZIO_TOKEN
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_LOGZIO_TOKEN
- secretKey: OPS_OPENOPS_ADMIN_PASSWORD_SALT
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_OPENOPS_ADMIN_PASSWORD_SALT
- secretKey: OPS_LANGFUSE_PUBLIC_KEY
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_LANGFUSE_PUBLIC_KEY
- secretKey: OPS_LANGFUSE_SECRET_KEY
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_LANGFUSE_SECRET_KEY
- secretKey: OPS_SSO_FRONTEGG_PUBLIC_KEY
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_SSO_FRONTEGG_PUBLIC_KEY
# Tables derived keys
- secretKey: LOGZIO_TOKEN
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_LOGZIO_TOKEN
- secretKey: OPENOPS_ADMIN_PASSWORD_SALT
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_OPENOPS_ADMIN_PASSWORD_SALT
# Tables (Baserow) derived keys
- secretKey: BASEROW_ADMIN_PASSWORD
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_OPENOPS_ADMIN_PASSWORD
- secretKey: BASEROW_JWT_SIGNING_KEY
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_JWT_SECRET
- secretKey: SECRET_KEY
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_ENCRYPTION_KEY
- secretKey: DATABASE_PASSWORD
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_POSTGRES_PASSWORD
# Analytics (Superset) derived keys
- secretKey: ADMIN_PASSWORD
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: OPS_ANALYTICS_ADMIN_PASSWORD
- secretKey: POWERUSER_PASSWORD
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: ANALYTICS_POWERUSER_PASSWORD
- secretKey: SUPERSET_SECRET_KEY
remoteRef:
key: {{ .Values.externalSecrets.secretName }}
property: SUPERSET_SECRET_KEY
{{- $allEnv := dict -}}
{{- range $k, $v := .Values.openopsEnvSecrets }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}
{{- range $k, $v := .Values.openopsEnv }}{{ if not (hasKey $allEnv $k) }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}{{ end }}
{{- range $k, $v := .Values.tables.env }}{{ if not (hasKey $allEnv $k) }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}{{ end }}
Comment on lines 43 to +47
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The templating whitespace trimming here is likely to generate invalid YAML under spec.data:. In particular, {{- $allEnv := dict -}} trims both the preceding and following newlines, which can collapse data: and the first list item onto the same line. Remove the trailing - (and consider using include ... | nindent 2/nindent 4 for the emitted list) to ensure data: is followed by a newline and properly indented - secretKey entries.

Copilot uses AI. Check for mistakes.
{{- range $k, $v := .Values.analytics.env }}{{ if not (hasKey $allEnv $k) }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}{{ end }}
{{- range $k, $v := .Values.postgres.env }}{{ if not (hasKey $allEnv $k) }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}{{ end }}
{{- if .Values.engine }}{{- if .Values.engine.env }}
{{- range $k, $v := .Values.engine.env }}{{ if not (hasKey $allEnv $k) }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}{{ end }}
Comment on lines +47 to +51
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The env merge into $allEnv currently uses if not (hasKey $allEnv $k) which prevents later env maps (e.g. analytics.env) from overriding keys defined earlier (e.g. tables.env). This differs from templates/secret-env.yaml, which overwrites on collisions, and it can block legitimate per-component overrides (notably DATABASE_PASSWORD exists in both tables and analytics env by default). Consider using overwrite semantics (later sources win) to match existing behavior.

Suggested change
{{- range $k, $v := .Values.tables.env }}{{ if not (hasKey $allEnv $k) }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}{{ end }}
{{- range $k, $v := .Values.analytics.env }}{{ if not (hasKey $allEnv $k) }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}{{ end }}
{{- range $k, $v := .Values.postgres.env }}{{ if not (hasKey $allEnv $k) }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}{{ end }}
{{- if .Values.engine }}{{- if .Values.engine.env }}
{{- range $k, $v := .Values.engine.env }}{{ if not (hasKey $allEnv $k) }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}{{ end }}
{{- range $k, $v := .Values.tables.env }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}
{{- range $k, $v := .Values.analytics.env }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}
{{- range $k, $v := .Values.postgres.env }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}
{{- if .Values.engine }}{{- if .Values.engine.env }}
{{- range $k, $v := .Values.engine.env }}{{ $_ := set $allEnv $k ($v | toString) }}{{ end }}

Copilot uses AI. Check for mistakes.
{{- end }}{{- end }}
{{- include "openops.collectSecretEntries" (dict "root" $ "env" $allEnv "secretName" .Values.externalSecrets.secretName) }}
{{- end }}
4 changes: 2 additions & 2 deletions chart/templates/secret-env.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
{{- $existingSecret := default "" $secretSettings.existingSecret -}}
{{- if and $create (not $existingSecret) -}}
{{- $autoSecretData := dict -}}
{{- $envSources := list .Values.openopsEnv .Values.engine.env .Values.tables.env .Values.analytics.env .Values.postgres.env -}}
{{- $envSources := list .Values.openopsEnvSecrets .Values.openopsEnv .Values.engine.env .Values.tables.env .Values.analytics.env .Values.postgres.env -}}
{{- range $env := $envSources }}
{{- if $env }}
{{- range $k, $v := $env }}
{{- if eq (include "openops.isSecretKey" $k) "true" }}
{{- if eq (include "openops.isSecretKey" (dict "root" $root "key" $k "value" ($v | toString))) "true" }}
{{- /* Double-tpl allows recursive variable resolution, e.g., "{{ .Values.foo }}" can contain "{{ .Values.bar }}" */ -}}
{{- $_ := set $autoSecretData $k (tpl (tpl $v $root) $root) }}
{{- end }}
Expand Down
2 changes: 1 addition & 1 deletion chart/values.ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ global:
enabled: false

# CI test secrets (not for production)
openopsEnv:
openopsEnvSecrets:
OPS_ENCRYPTION_KEY: "0123456789abcdef0123456789abcdef"
OPS_JWT_SECRET: "ci-jwt-secret-not-for-production"
OPS_OPENOPS_ADMIN_PASSWORD: "ci-admin123"
Expand Down
2 changes: 2 additions & 0 deletions chart/values.overrides-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ global:

openopsEnv:
OPS_OPENOPS_ADMIN_EMAIL: openops@example.com

openopsEnvSecrets:
OPS_OPENOPS_ADMIN_PASSWORD: 9wqhK7jehmxAQlre
OPS_ENCRYPTION_KEY: 7de996f020438444dc2c79e9b843805d
OPS_JWT_SECRET: M72yCOp9riLjIDws
Expand Down
45 changes: 27 additions & 18 deletions chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,8 @@ openopsEnv:
OPS_OPENOPS_TABLES_VERSION: "{{ .Values.tables.tag }}"
OPS_ANALYTICS_VERSION: "{{ .Values.analytics.tag }}"

# Authentication - REQUIRED, NO DEFAULTS
# Generate secure values with: openssl rand -hex 32
OPS_ENCRYPTION_KEY: "" # REQUIRED: 32-character hex string
OPS_JWT_SECRET: "" # REQUIRED: Random secret string
# Authentication
OPS_OPENOPS_ADMIN_EMAIL: admin@openops.com
OPS_OPENOPS_ADMIN_PASSWORD: "" # REQUIRED: Strong password

# Telemetry
OPS_LOG_LEVEL: info
Expand All @@ -125,7 +121,6 @@ openopsEnv:
OPS_POSTGRES_HOST: '{{ include "openops.postgresHost" . }}'
OPS_POSTGRES_PORT: '{{ include "openops.postgresPort" . }}'
OPS_POSTGRES_USERNAME: postgres
OPS_POSTGRES_PASSWORD: "" # REQUIRED: Strong password

# Tables
OPS_OPENOPS_TABLES_DATABASE_NAME: tables
Expand All @@ -139,18 +134,32 @@ openopsEnv:
# Analytics
OPS_ANALYTICS_PUBLIC_URL: '{{ include "openops.publicUrl" . }}'
OPS_ANALYTICS_PRIVATE_URL: '{{ include "openops.analyticsServiceUrl" . }}'
OPS_ANALYTICS_ADMIN_PASSWORD: "" # REQUIRED: Strong password
ANALYTICS_POWERUSER_PASSWORD: "" # REQUIRED: Strong password
ANALYTICS_ALLOW_ADHOC_SUBQUERY: "true"

# AWS
OPS_AWS_ENABLE_IMPLICIT_ROLE: "false"

# Blocks custom settings
OPS_CODE_BLOCK_MEMORY_LIMIT_IN_MB: "256"
OPS_SLACK_APP_SIGNING_SECRET: ""
OPS_SLACK_ENABLE_INTERACTIONS: "true"

# Secret environment variables
# Any var in this section is treated as a secret (stored in K8s Secret, referenced via secretKeyRef).
# Generate secure values with: openssl rand -hex 16
openopsEnvSecrets:
OPS_ENCRYPTION_KEY: "" # REQUIRED: 32-character hex string (openssl rand -hex 16)
Comment on lines +146 to +150
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the new openopsEnvSecrets convention, the shipped overlay/example files and docs that still instruct placing secrets under openopsEnv (e.g. values.production.yaml) are now misleading. Update those examples/docs to reference openopsEnvSecrets for secret inputs so users don’t accidentally render secrets as plain env values or omit them entirely.

Copilot uses AI. Check for mistakes.
OPS_JWT_SECRET: "" # REQUIRED: Random secret string
OPS_OPENOPS_ADMIN_PASSWORD: "" # REQUIRED: Strong password
OPS_POSTGRES_PASSWORD: "" # REQUIRED: Strong password
OPS_ANALYTICS_ADMIN_PASSWORD: "" # REQUIRED: Strong password
ANALYTICS_POWERUSER_PASSWORD: "" # REQUIRED: Strong password
OPS_SLACK_APP_SIGNING_SECRET: ""
SUPERSET_SECRET_KEY: "thisISaSECRET_1234"
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

openopsEnvSecrets.SUPERSET_SECRET_KEY has a hard-coded default value ("thisISaSECRET_1234"). Shipping a real secret (even a placeholder) in default values is insecure and also defeats the goal of forcing explicit configuration. Make this empty by default and add validation (similar to the other required secrets) so installs fail fast unless a unique key is provided.

Suggested change
SUPERSET_SECRET_KEY: "thisISaSECRET_1234"
SUPERSET_SECRET_KEY: "" # REQUIRED: Random secret string

Copilot uses AI. Check for mistakes.
OPS_LOGZIO_TOKEN: ""
OPS_LANGFUSE_PUBLIC_KEY: ""
OPS_LANGFUSE_SECRET_KEY: ""
OPS_SSO_FRONTEGG_PUBLIC_KEY: ""

secretEnv:
create: true
existingSecret: ""
Expand Down Expand Up @@ -276,10 +285,10 @@ tables:
BASEROW_PUBLIC_URL: "{{ .Values.openopsEnv.OPS_OPENOPS_TABLES_PUBLIC_URL }}"
BASEROW_PRIVATE_URL: "{{ .Values.openopsEnv.OPS_OPENOPS_TABLES_API_URL }}"
BASEROW_EXTRA_ALLOWED_HOSTS: '*'
SECRET_KEY: "{{ .Values.openopsEnv.OPS_ENCRYPTION_KEY }}"
BASEROW_JWT_SIGNING_KEY: "{{ .Values.openopsEnv.OPS_JWT_SECRET }}"
SECRET_KEY: "{{ .Values.openopsEnvSecrets.OPS_ENCRYPTION_KEY }}"
BASEROW_JWT_SIGNING_KEY: "{{ .Values.openopsEnvSecrets.OPS_JWT_SECRET }}"
BASEROW_ADMIN_USERNAME: "{{ .Values.openopsEnv.OPS_OPENOPS_ADMIN_EMAIL }}"
BASEROW_ADMIN_PASSWORD: "{{ .Values.openopsEnv.OPS_OPENOPS_ADMIN_PASSWORD }}"
BASEROW_ADMIN_PASSWORD: "{{ .Values.openopsEnvSecrets.OPS_OPENOPS_ADMIN_PASSWORD }}"
BASEROW_REFRESH_TOKEN_LIFETIME_HOURS: "{{ .Values.openopsEnv.OPS_JWT_TOKEN_LIFETIME_HOURS }}"
BASEROW_ACCESS_TOKEN_LIFETIME_MINUTES: "{{ .Values.openopsEnv.OPS_TABLES_TOKEN_LIFETIME_MINUTES }}"
SYNC_TEMPLATES_ON_STARTUP: 'false'
Expand All @@ -289,7 +298,7 @@ tables:
DATABASE_HOST: "{{ .Values.openopsEnv.OPS_POSTGRES_HOST }}"
DATABASE_PORT: "{{ .Values.openopsEnv.OPS_POSTGRES_PORT }}"
DATABASE_USER: "{{ .Values.openopsEnv.OPS_POSTGRES_USERNAME }}"
DATABASE_PASSWORD: "{{ .Values.openopsEnv.OPS_POSTGRES_PASSWORD }}"
DATABASE_PASSWORD: "{{ .Values.openopsEnvSecrets.OPS_POSTGRES_PASSWORD }}"
REDIS_URL: '{{ include "openops.redisUrl" . }}'
storage:
size: 10Gi
Expand Down Expand Up @@ -336,17 +345,17 @@ analytics:
port: 8088
path: /health
env:
ADMIN_PASSWORD: "{{ .Values.openopsEnv.OPS_ANALYTICS_ADMIN_PASSWORD }}"
POWERUSER_PASSWORD: "{{ .Values.openopsEnv.ANALYTICS_POWERUSER_PASSWORD }}"
ADMIN_PASSWORD: "{{ .Values.openopsEnvSecrets.OPS_ANALYTICS_ADMIN_PASSWORD }}"
POWERUSER_PASSWORD: "{{ .Values.openopsEnvSecrets.ANALYTICS_POWERUSER_PASSWORD }}"
GUNICORN_LOGLEVEL: 'debug'
DATABASE_DIALECT: 'postgresql'
DATABASE_DB: 'analytics'
DATABASE_HOST: "{{ .Values.openopsEnv.OPS_POSTGRES_HOST }}"
DATABASE_PORT: "{{ .Values.openopsEnv.OPS_POSTGRES_PORT }}"
DATABASE_USER: "{{ .Values.openopsEnv.OPS_POSTGRES_USERNAME }}"
DATABASE_PASSWORD: "{{ .Values.openopsEnv.OPS_POSTGRES_PASSWORD }}"
DATABASE_PASSWORD: "{{ .Values.openopsEnvSecrets.OPS_POSTGRES_PASSWORD }}"
DATABASE_HOST_ALT: "{{ .Values.openopsEnv.OPS_OPENOPS_TABLES_DB_HOST }}"
SUPERSET_SECRET_KEY: "{{ .Values.openopsEnv.OPS_ENCRYPTION_KEY }}"
SUPERSET_SECRET_KEY: "{{ .Values.openopsEnvSecrets.SUPERSET_SECRET_KEY }}"
SUPERSET_FEATURE_ALLOW_ADHOC_SUBQUERY: '"{{ .Values.openopsEnv.ANALYTICS_ALLOW_ADHOC_SUBQUERY }}"'
REDIS_HOST: "{{ .Values.openopsEnv.OPS_REDIS_HOST }}"
REDIS_PORT: "{{ .Values.openopsEnv.OPS_REDIS_PORT }}"
Expand Down Expand Up @@ -391,7 +400,7 @@ postgres:
env:
maxConnections: "300"
POSTGRES_USER: "{{ .Values.openopsEnv.OPS_POSTGRES_USERNAME }}"
POSTGRES_PASSWORD: "{{ .Values.openopsEnv.OPS_POSTGRES_PASSWORD }}"
POSTGRES_PASSWORD: "{{ .Values.openopsEnvSecrets.OPS_POSTGRES_PASSWORD }}"
POSTGRES_DB: "{{ .Values.openopsEnv.OPS_POSTGRES_DATABASE }}"
# Authentication configuration
auth:
Expand Down