From 04512ade1663651c525e43c1da3ae02ba4c8aa01 Mon Sep 17 00:00:00 2001 From: Jeff McCollum Date: Mon, 13 Oct 2025 15:35:37 -0500 Subject: [PATCH 1/6] add support for run as specific user --- braintrust/templates/api-deployment.yaml | 4 ++++ .../templates/brainstore-reader-deployment.yaml | 4 ++++ .../templates/brainstore-writer-deployment.yaml | 4 ++++ braintrust/values.yaml | 15 +++++++++++++++ 4 files changed, 27 insertions(+) diff --git a/braintrust/templates/api-deployment.yaml b/braintrust/templates/api-deployment.yaml index ddf42e3..a5c9f8d 100644 --- a/braintrust/templates/api-deployment.yaml +++ b/braintrust/templates/api-deployment.yaml @@ -32,6 +32,10 @@ spec: {{- end }} spec: serviceAccountName: {{ .Values.api.serviceAccount.name }} + {{- with .Values.api.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} {{- with .Values.api.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/braintrust/templates/brainstore-reader-deployment.yaml b/braintrust/templates/brainstore-reader-deployment.yaml index 45fb7fb..0b21ef6 100644 --- a/braintrust/templates/brainstore-reader-deployment.yaml +++ b/braintrust/templates/brainstore-reader-deployment.yaml @@ -38,6 +38,10 @@ spec: {{- end }} spec: serviceAccountName: {{ .Values.brainstore.serviceAccount.name }} + {{- with .Values.brainstore.reader.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} {{- if or .Values.brainstore.reader.nodeSelector (and (eq .Values.cloud "google") (eq .Values.google.mode "autopilot")) }} nodeSelector: {{- with .Values.brainstore.reader.nodeSelector }} diff --git a/braintrust/templates/brainstore-writer-deployment.yaml b/braintrust/templates/brainstore-writer-deployment.yaml index 196f65d..6a5a56d 100644 --- a/braintrust/templates/brainstore-writer-deployment.yaml +++ b/braintrust/templates/brainstore-writer-deployment.yaml @@ -38,6 +38,10 @@ spec: {{- end }} spec: serviceAccountName: {{ .Values.brainstore.serviceAccount.name }} + {{- with .Values.brainstore.writer.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} {{- if or .Values.brainstore.writer.nodeSelector (and (eq .Values.cloud "google") (eq .Values.google.mode "autopilot")) }} nodeSelector: {{- with .Values.brainstore.writer.nodeSelector }} diff --git a/braintrust/values.yaml b/braintrust/values.yaml index b9ee788..b13a887 100644 --- a/braintrust/values.yaml +++ b/braintrust/values.yaml @@ -88,6 +88,11 @@ api: limits: cpu: "4" memory: "8Gi" + # Optional: Pod-level security context + # podSecurityContext: + # runAsUser: 1000 + # runAsGroup: 1000 + # fsGroup: 1000 # Allow running user generated code functions (e.g. scorers/tools) allowCodeFunctionExecution: true # Brainstore backfill configuration. These defaults are fine for most cases. @@ -150,6 +155,11 @@ brainstore: limits: cpu: "8" memory: "16Gi" + # Optional: Pod-level security context + # podSecurityContext: + # runAsUser: 1000 + # runAsGroup: 1000 + # fsGroup: 1000 cacheDir: "/mnt/tmp/brainstore" objectStoreCacheMemoryLimit: "1Gi" objectStoreCacheFileSize: "50Gi" @@ -186,6 +196,11 @@ brainstore: limits: cpu: "16" memory: "32Gi" + # Optional: Pod-level security context + # podSecurityContext: + # runAsUser: 1000 + # runAsGroup: 1000 + # fsGroup: 1000 cacheDir: "/mnt/tmp/brainstore" objectStoreCacheMemoryLimit: "1Gi" objectStoreCacheFileSize: "50Gi" From cd856b72113ebfd076d455b315633cc9a41d978b Mon Sep 17 00:00:00 2001 From: Jeff McCollum Date: Mon, 13 Oct 2025 15:35:50 -0500 Subject: [PATCH 2/6] add drop all --- braintrust/templates/api-deployment.yaml | 4 ++++ .../templates/brainstore-reader-deployment.yaml | 4 ++++ .../templates/brainstore-writer-deployment.yaml | 4 ++++ braintrust/values.yaml | 15 +++++++++++++++ 4 files changed, 27 insertions(+) diff --git a/braintrust/templates/api-deployment.yaml b/braintrust/templates/api-deployment.yaml index a5c9f8d..fd0e92c 100644 --- a/braintrust/templates/api-deployment.yaml +++ b/braintrust/templates/api-deployment.yaml @@ -52,6 +52,10 @@ spec: - name: api image: "{{ .Values.api.image.repository }}:{{ .Values.api.image.tag }}" imagePullPolicy: {{ .Values.api.image.pullPolicy }} + {{- with .Values.api.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} ports: - containerPort: {{ .Values.api.service.port }} resources: diff --git a/braintrust/templates/brainstore-reader-deployment.yaml b/braintrust/templates/brainstore-reader-deployment.yaml index 0b21ef6..c7e9877 100644 --- a/braintrust/templates/brainstore-reader-deployment.yaml +++ b/braintrust/templates/brainstore-reader-deployment.yaml @@ -65,6 +65,10 @@ spec: - name: brainstore-reader image: "{{ .Values.brainstore.image.repository }}:{{ .Values.brainstore.image.tag }}" imagePullPolicy: {{ .Values.brainstore.image.pullPolicy }} + {{- with .Values.brainstore.reader.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} command: ["brainstore"] args: ["web"] ports: diff --git a/braintrust/templates/brainstore-writer-deployment.yaml b/braintrust/templates/brainstore-writer-deployment.yaml index 6a5a56d..ad20c76 100644 --- a/braintrust/templates/brainstore-writer-deployment.yaml +++ b/braintrust/templates/brainstore-writer-deployment.yaml @@ -65,6 +65,10 @@ spec: - name: brainstore-writer image: "{{ .Values.brainstore.image.repository }}:{{ .Values.brainstore.image.tag }}" imagePullPolicy: {{ .Values.brainstore.image.pullPolicy }} + {{- with .Values.brainstore.writer.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} command: ["brainstore"] args: ["web"] ports: diff --git a/braintrust/values.yaml b/braintrust/values.yaml index b13a887..50d4ad3 100644 --- a/braintrust/values.yaml +++ b/braintrust/values.yaml @@ -93,6 +93,11 @@ api: # runAsUser: 1000 # runAsGroup: 1000 # fsGroup: 1000 + # Optional: Container-level security context + # securityContext: + # capabilities: + # drop: + # - ALL # Allow running user generated code functions (e.g. scorers/tools) allowCodeFunctionExecution: true # Brainstore backfill configuration. These defaults are fine for most cases. @@ -160,6 +165,11 @@ brainstore: # runAsUser: 1000 # runAsGroup: 1000 # fsGroup: 1000 + # Optional: Container-level security context + # securityContext: + # capabilities: + # drop: + # - ALL cacheDir: "/mnt/tmp/brainstore" objectStoreCacheMemoryLimit: "1Gi" objectStoreCacheFileSize: "50Gi" @@ -201,6 +211,11 @@ brainstore: # runAsUser: 1000 # runAsGroup: 1000 # fsGroup: 1000 + # Optional: Container-level security context + # securityContext: + # capabilities: + # drop: + # - ALL cacheDir: "/mnt/tmp/brainstore" objectStoreCacheMemoryLimit: "1Gi" objectStoreCacheFileSize: "50Gi" From 23c5cb651dd180f1ce44385588ea5e0fbcff8a1e Mon Sep 17 00:00:00 2001 From: Jeff McCollum Date: Tue, 14 Oct 2025 08:21:14 -0500 Subject: [PATCH 3/6] add optional realtime service, disabled by default --- braintrust/templates/realtime-deployment.yaml | 64 +++++++++++++++++++ braintrust/templates/realtime-service.yaml | 24 +++++++ braintrust/values.yaml | 47 ++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 braintrust/templates/realtime-deployment.yaml create mode 100644 braintrust/templates/realtime-service.yaml diff --git a/braintrust/templates/realtime-deployment.yaml b/braintrust/templates/realtime-deployment.yaml new file mode 100644 index 0000000..0586ecf --- /dev/null +++ b/braintrust/templates/realtime-deployment.yaml @@ -0,0 +1,64 @@ +{{- if .Values.realtime.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.realtime.name }} + namespace: {{ include "braintrust.namespace" . }} + {{- with (merge .Values.global.labels .Values.realtime.labels) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.realtime.annotations.deployment }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.realtime.replicas }} + selector: + matchLabels: + app: {{ .Values.realtime.name }} + template: + metadata: + labels: + app: {{ .Values.realtime.name }} + {{- with (merge .Values.global.labels .Values.realtime.labels) }} + {{- toYaml . | nindent 8 }} + {{- end }} + annotations: + {{- with .Values.realtime.annotations.pod }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.realtime.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.realtime.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.realtime.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.realtime.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: realtime + image: "{{ .Values.realtime.image.repository }}:{{ .Values.realtime.image.tag }}" + imagePullPolicy: {{ .Values.realtime.image.pullPolicy }} + {{- with .Values.realtime.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - containerPort: {{ .Values.realtime.service.port }} + resources: + {{- toYaml .Values.realtime.resources | nindent 12 }} + {{- if .Values.realtime.extraEnvVars }} + env: + {{- toYaml .Values.realtime.extraEnvVars | nindent 12 }} + {{- end }} +{{- end }} diff --git a/braintrust/templates/realtime-service.yaml b/braintrust/templates/realtime-service.yaml new file mode 100644 index 0000000..7eec203 --- /dev/null +++ b/braintrust/templates/realtime-service.yaml @@ -0,0 +1,24 @@ +{{- if .Values.realtime.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.realtime.service.name | default .Values.realtime.name }} + namespace: {{ include "braintrust.namespace" . }} + {{- with (merge .Values.global.labels .Values.realtime.labels) }} + labels: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.realtime.annotations.service }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + app: {{ .Values.realtime.name }} + ports: + - name: {{ .Values.realtime.service.portName }} + protocol: TCP + port: {{ .Values.realtime.service.port }} + targetPort: {{ .Values.realtime.service.port }} + type: {{ .Values.realtime.service.type }} +{{- end }} diff --git a/braintrust/values.yaml b/braintrust/values.yaml index 50d4ad3..d712d22 100644 --- a/braintrust/values.yaml +++ b/braintrust/values.yaml @@ -235,6 +235,53 @@ brainstore: tolerations: [] affinity: {} +# Realtime configuration (disabled by default) +realtime: + # Set to true to enable the realtime service + enabled: false + name: "braintrust-realtime" + labels: {} + annotations: + deployment: {} + service: {} + pod: {} + replicas: 1 + image: + repository: public.ecr.aws/braintrust/standalone-realtime + tag: v1.1.23 + pullPolicy: IfNotPresent + service: + # Optional name for service object. If not specified (empty), the realtime.name + # is used. + name: "" + type: ClusterIP + port: 8788 + portName: http + resources: + requests: + cpu: "2" + memory: "4Gi" + limits: + cpu: "4" + memory: "8Gi" + # Optional: Pod-level security context + # podSecurityContext: + # runAsUser: 1000 + # runAsGroup: 1000 + # fsGroup: 1000 + # Optional: Container-level security context + # securityContext: + # capabilities: + # drop: + # - ALL + extraEnvVars: [] + # Example: + # - name: MY_ENV_VAR + # value: "my-value" + nodeSelector: {} + tolerations: [] + affinity: {} + # Optional Azure Key Vault CSI configuration for syncing secrets from Azure Key Vault azureKeyVaultCSI: # Set to true to enable Azure Key Vault CSI secret syncing From dd21825d5985dc6f8dd6a0eab90eb02e4df48270 Mon Sep 17 00:00:00 2001 From: Hedi Daoud <150018939+hdaoud23@users.noreply.github.com> Date: Thu, 30 Oct 2025 21:32:07 +0100 Subject: [PATCH 4/6] feat(helm): add optional custom TLS CA support via braintrust-secrets (#39) * Update Chart version to 2.1.0 * Pull Request Proposal for Braintrust Helm Chart: Redis TLS CA support for API (GCP Memorystore) * update redis tls to be a global setting, enabled only for google, and enabled for brainstore only when locking is set to redis. * add realtime url to api and add redis secret * simplify value and support any cloud * add BRAINSTORE_REDIS_URL (#40) * rename customRedisTLSCABundle to customTLSCABundle * rename --------- Co-authored-by: Braintrust Bot <215900051+braintrust-bot[bot]@users.noreply.github.com> Co-authored-by: Jeff McCollum Co-authored-by: Ken Jiang <39507362+knjiang@users.noreply.github.com> --- braintrust/README.md | 1 + braintrust/templates/api-deployment.yaml | 31 +++++++++++++++++-- .../brainstore-reader-deployment.yaml | 19 ++++++++++++ .../brainstore-writer-deployment.yaml | 19 ++++++++++++ braintrust/templates/realtime-deployment.yaml | 9 ++++-- braintrust/values.yaml | 7 +++++ 6 files changed, 82 insertions(+), 4 deletions(-) diff --git a/braintrust/README.md b/braintrust/README.md index 252389a..08f4b76 100644 --- a/braintrust/README.md +++ b/braintrust/README.md @@ -14,6 +14,7 @@ The `braintrust-secrets` secret must contain the following keys: | `PG_URL` | PostgreSQL connection URL | `postgres://:@:/` (append `?sslmode=require` if using TLS) | | `BRAINSTORE_LICENSE_KEY` | Brainstore license key | Valid Brainstore license key from the Braintrust Data Plane settings page | | `FUNCTION_SECRET_KEY` | Random string for encrypting function secrets | Random string | +| `CA_PEM` | Custom TLS CA bundle | Full PEM bundle as a multiline string (BEGIN/END blocks). Only required if `customTLSCABundle: true`. | | `AZURE_STORAGE_CONNECTION_STRING` | Azure storage connection string | Valid Azure storage connection string (only required if `cloud` is `azure`) | | `GCS_ACCESS_KEY_ID` | Google HMAC Access ID string | Valid S3 API Key Id (only required if `cloud` is `google`) | | `GCS_SECRET_ACCESS_KEY` | Google HMAC Secret string | Valid S3 Secret string (only required if `cloud` is `google`) | diff --git a/braintrust/templates/api-deployment.yaml b/braintrust/templates/api-deployment.yaml index fd0e92c..ad9218d 100644 --- a/braintrust/templates/api-deployment.yaml +++ b/braintrust/templates/api-deployment.yaml @@ -106,19 +106,46 @@ spec: {{- if .Values.api.extraEnvVars }} {{- toYaml .Values.api.extraEnvVars | nindent 12 }} {{- end }} - {{- if .Values.azureKeyVaultCSI.enabled }} + {{- if .Values.realtime.enabled }} + - name: REALTIME_URL + value: "http://{{ .Values.realtime.service.name | default .Values.realtime.name }}:{{ .Values.realtime.service.port }}" + {{- end }} + {{- if .Values.customTLSCABundle }} + - name: NODE_EXTRA_CA_CERTS + value: "/etc/braintrust/tls/ca-bundle.pem" + {{- end }} + {{- if or .Values.azureKeyVaultCSI.enabled .Values.customTLSCABundle }} volumeMounts: + {{- if .Values.azureKeyVaultCSI.enabled }} - name: secrets-store-inline mountPath: "/mnt/secrets-store" readOnly: true + {{- end }} + {{- if .Values.customTLSCABundle }} + - name: tls-ca + mountPath: "/etc/braintrust/tls" + readOnly: true + {{- end }} {{- end }} - {{- if .Values.azureKeyVaultCSI.enabled }} + {{- if or .Values.azureKeyVaultCSI.enabled .Values.customTLSCABundle }} volumes: + {{- if .Values.customTLSCABundle }} + - name: tls-ca + projected: + sources: + - secret: + name: "braintrust-secrets" + items: + - key: "CA_PEM" + path: "ca-bundle.pem" + {{- end }} + {{- if .Values.azureKeyVaultCSI.enabled }} - name: secrets-store-inline csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: {{ .Values.azureKeyVaultCSI.name }} + {{- end }} {{- end }} diff --git a/braintrust/templates/brainstore-reader-deployment.yaml b/braintrust/templates/brainstore-reader-deployment.yaml index c7e9877..5aef704 100644 --- a/braintrust/templates/brainstore-reader-deployment.yaml +++ b/braintrust/templates/brainstore-reader-deployment.yaml @@ -117,6 +117,10 @@ spec: {{- if .Values.brainstore.reader.extraEnvVars }} {{- toYaml .Values.brainstore.reader.extraEnvVars | nindent 12 }} {{- end }} + {{- if .Values.customTLSCABundle }} + - name: SSL_CERT_FILE + value: "/etc/braintrust/tls/ca-bundle.pem" + {{- end }} volumeMounts: - name: cache-volume mountPath: {{ .Values.brainstore.reader.cacheDir }} @@ -125,6 +129,11 @@ spec: mountPath: "/mnt/secrets-store" readOnly: true {{- end }} + {{- if .Values.customTLSCABundle }} + - name: tls-ca + mountPath: "/etc/braintrust/tls" + readOnly: true + {{- end }} volumes: - name: cache-volume emptyDir: {} @@ -136,3 +145,13 @@ spec: volumeAttributes: secretProviderClass: {{ .Values.azureKeyVaultCSI.name }} {{- end }} + {{- if .Values.customTLSCABundle }} + - name: tls-ca + projected: + sources: + - secret: + name: "braintrust-secrets" + items: + - key: "CA_PEM" + path: "ca-bundle.pem" + {{- end }} diff --git a/braintrust/templates/brainstore-writer-deployment.yaml b/braintrust/templates/brainstore-writer-deployment.yaml index ad20c76..cd1db76 100644 --- a/braintrust/templates/brainstore-writer-deployment.yaml +++ b/braintrust/templates/brainstore-writer-deployment.yaml @@ -117,6 +117,10 @@ spec: {{- if .Values.brainstore.writer.extraEnvVars }} {{- toYaml .Values.brainstore.writer.extraEnvVars | nindent 12 }} {{- end }} + {{- if .Values.customTLSCABundle }} + - name: SSL_CERT_FILE + value: "/etc/braintrust/tls/ca-bundle.pem" + {{- end }} volumeMounts: - name: cache-volume mountPath: {{ .Values.brainstore.writer.cacheDir }} @@ -125,6 +129,11 @@ spec: mountPath: "/mnt/secrets-store" readOnly: true {{- end }} + {{- if .Values.customTLSCABundle }} + - name: tls-ca + mountPath: "/etc/braintrust/tls" + readOnly: true + {{- end }} volumes: - name: cache-volume emptyDir: {} @@ -136,3 +145,13 @@ spec: volumeAttributes: secretProviderClass: {{ .Values.azureKeyVaultCSI.name }} {{- end }} + {{- if .Values.customTLSCABundle }} + - name: tls-ca + projected: + sources: + - secret: + name: "braintrust-secrets" + items: + - key: "CA_PEM" + path: "ca-bundle.pem" + {{- end }} diff --git a/braintrust/templates/realtime-deployment.yaml b/braintrust/templates/realtime-deployment.yaml index 0586ecf..c6d27d2 100644 --- a/braintrust/templates/realtime-deployment.yaml +++ b/braintrust/templates/realtime-deployment.yaml @@ -57,8 +57,13 @@ spec: - containerPort: {{ .Values.realtime.service.port }} resources: {{- toYaml .Values.realtime.resources | nindent 12 }} - {{- if .Values.realtime.extraEnvVars }} env: + - name: REDIS_URL + valueFrom: + secretKeyRef: + name: braintrust-secrets + key: REDIS_URL + {{- if .Values.realtime.extraEnvVars }} {{- toYaml .Values.realtime.extraEnvVars | nindent 12 }} - {{- end }} + {{- end }} {{- end }} diff --git a/braintrust/values.yaml b/braintrust/values.yaml index d712d22..8c3f728 100644 --- a/braintrust/values.yaml +++ b/braintrust/values.yaml @@ -23,6 +23,13 @@ global: # Cloud provider configuration cloud: "google" # or "azure" or "aws" +# Custom TLS CA Bundle configuration +# When enabled, appends custom CA certificates to the system trust store for secure connections +# This is useful for private CAs, self-signed certificates, or custom certificate chains +# The custom CA bundle is added in addition to the default system CA certificates +# Requires CA_PEM secret to be set (see README for details) +customTLSCABundle: false + # Optional: Google Cloud specific configuration google: # GKE mode: "autopilot" or "standard" From 6b2d3674b07db74d7de94be5dada3a7030acb806 Mon Sep 17 00:00:00 2001 From: Jeff McCollum Date: Mon, 15 Dec 2025 10:35:30 -0600 Subject: [PATCH 5/6] optional tlscabundle --- braintrust/values.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/braintrust/values.yaml b/braintrust/values.yaml index 3509542..f832af2 100644 --- a/braintrust/values.yaml +++ b/braintrust/values.yaml @@ -362,3 +362,7 @@ azureKeyVaultDriver: - keyVaultSecretName: "azure-storage-connection-string" keyVaultSecretType: "secret" kubernetesSecretKey: "AZURE_STORAGE_CONNECTION_STRING" + # Optional: Only needed if customTLSCABundle is enabled + # - keyVaultSecretName: "ca-pem" + # keyVaultSecretType: "secret" + # kubernetesSecretKey: "CA_PEM" From 31b2b8c230b9fb4896f7a3f26ef260361184c9a9 Mon Sep 17 00:00:00 2001 From: Jeff McCollum Date: Mon, 15 Dec 2025 10:41:54 -0600 Subject: [PATCH 6/6] update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 62d2ec7..264b2ea 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ Chart.lock **/secrets.yml values-local.yaml values-local.yml +values-*.yaml +values-*.yml # Helm output and temporary files *.tmp