feat(helm): add copilot (#1833)

* Add helm for copilot

* Remove otel and log level

* Change repo name

* improvement(helm): enhance copilot chart with HA support and validation

* refactor(helm): consolidate copilot secrets and fix postgres volume mount
This commit is contained in:
Siddharth Ganesan
2025-11-08 17:36:48 -08:00
committed by GitHub
parent 7c6e6d1603
commit 142d3aadb8
10 changed files with 1014 additions and 1 deletions

View File

@@ -0,0 +1,129 @@
# Enable the copilot service
copilot:
enabled: true
# Server configuration
server:
image:
repository: simstudioai/copilot
tag: latest
pullPolicy: Always
replicaCount: 2
# Node scheduling (OPTIONAL)
# By default, copilot runs on the same nodes as the main Sim platform
nodeSelector: {}
# nodeSelector:
# workload-type: copilot
resources:
limits:
memory: "2Gi"
cpu: "1000m"
requests:
memory: "1Gi"
cpu: "500m"
# Required secrets (set via values or provide your own secret)
env:
PORT: "8080"
SERVICE_NAME: "copilot"
ENVIRONMENT: "production"
AGENT_API_DB_ENCRYPTION_KEY: "" # openssl rand -hex 32
INTERNAL_API_SECRET: "" # reuse Sim INTERNAL_API_SECRET
LICENSE_KEY: "" # Provided by Sim team
OPENAI_API_KEY_1: "" # At least one provider key required
ANTHROPIC_API_KEY_1: "" # Optional secondary provider
SIM_BASE_URL: "https://sim.example.com" # Base URL for Sim deployment
SIM_AGENT_API_KEY: "" # Must match SIM-side COPILOT_API_KEY
REDIS_URL: "redis://default:password@redis:6379"
# Optional configuration
LOG_LEVEL: "info"
CORS_ALLOWED_ORIGINS: "https://sim.example.com"
OTEL_EXPORTER_OTLP_ENDPOINT: ""
# Create a Secret from the values above. Set create=false to reference an existing secret instead.
secret:
create: true
name: ""
annotations: {}
extraEnv: []
extraEnvFrom: []
service:
type: ClusterIP
port: 8080
targetPort: 8080
# Internal PostgreSQL database (disable to use an external database)
postgresql:
enabled: true
image:
repository: postgres
tag: 16-alpine
pullPolicy: IfNotPresent
auth:
username: copilot
password: "" # REQUIRED - set via --set copilot.postgresql.auth.password
database: copilot
nodeSelector: {}
# nodeSelector:
# workload-type: copilot
resources:
limits:
memory: "1Gi"
cpu: "500m"
requests:
memory: "512Mi"
cpu: "250m"
persistence:
enabled: true
size: 10Gi
# External database configuration (only used when postgresql.enabled=false)
database:
existingSecretName: ""
secretKey: DATABASE_URL
url: ""
# Migration job
migrations:
enabled: true
resources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "100m"
# Optional: Configure ingress to expose copilot service
# Uncomment if you need external access to copilot
# ingress:
# enabled: true
# className: nginx
# annotations:
# cert-manager.io/cluster-issuer: letsencrypt-prod
# nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
# copilot:
# host: copilot.yourdomain.com
# paths:
# - path: /
# pathType: Prefix
# tls:
# enabled: true
# secretName: copilot-tls-secret
# If using private Docker Hub repository
# global:
# imagePullSecrets:
# - name: dockerhub-secret

View File

@@ -300,4 +300,69 @@ Affinity
affinity:
{{- toYaml .affinity | nindent 2 }}
{{- end }}
{{- end }}
{{/*
Copilot environment secret name
*/}}
{{- define "sim.copilot.envSecretName" -}}
{{- if and .Values.copilot.server.secret.name (ne .Values.copilot.server.secret.name "") -}}
{{- .Values.copilot.server.secret.name -}}
{{- else -}}
{{- printf "%s-copilot-env" (include "sim.fullname" .) -}}
{{- end -}}
{{- end }}
{{/*
Copilot database secret name
*/}}
{{- define "sim.copilot.databaseSecretName" -}}
{{- if .Values.copilot.postgresql.enabled -}}
{{- printf "%s-copilot-postgresql-secret" (include "sim.fullname" .) -}}
{{- else if and .Values.copilot.database.existingSecretName (ne .Values.copilot.database.existingSecretName "") -}}
{{- .Values.copilot.database.existingSecretName -}}
{{- else -}}
{{- printf "%s-copilot-database-secret" (include "sim.fullname" .) -}}
{{- end -}}
{{- end }}
{{/*
Copilot database secret key
*/}}
{{- define "sim.copilot.databaseSecretKey" -}}
{{- default "DATABASE_URL" .Values.copilot.database.secretKey -}}
{{- end }}
{{/*
Validate Copilot configuration
*/}}
{{- define "sim.copilot.validate" -}}
{{- if .Values.copilot.enabled -}}
{{- if and (not .Values.copilot.server.secret.create) (or (not .Values.copilot.server.secret.name) (eq .Values.copilot.server.secret.name "")) -}}
{{- fail "copilot.server.secret.name must be provided when copilot.server.secret.create=false" -}}
{{- end -}}
{{- if .Values.copilot.server.secret.create -}}
{{- $env := .Values.copilot.server.env -}}
{{- $required := list "AGENT_API_DB_ENCRYPTION_KEY" "INTERNAL_API_SECRET" "LICENSE_KEY" "SIM_BASE_URL" "SIM_AGENT_API_KEY" "REDIS_URL" -}}
{{- range $key := $required -}}
{{- if not (and $env (index $env $key) (ne (index $env $key) "")) -}}
{{- fail (printf "copilot.server.env.%s is required when copilot is enabled" $key) -}}
{{- end -}}
{{- end -}}
{{- $hasOpenAI := and $env (ne (default "" (index $env "OPENAI_API_KEY_1")) "") -}}
{{- $hasAnthropic := and $env (ne (default "" (index $env "ANTHROPIC_API_KEY_1")) "") -}}
{{- if not (or $hasOpenAI $hasAnthropic) -}}
{{- fail "Set at least one of copilot.server.env.OPENAI_API_KEY_1 or copilot.server.env.ANTHROPIC_API_KEY_1" -}}
{{- end -}}
{{- end -}}
{{- if .Values.copilot.postgresql.enabled -}}
{{- if or (not .Values.copilot.postgresql.auth.password) (eq .Values.copilot.postgresql.auth.password "") -}}
{{- fail "copilot.postgresql.auth.password is required when copilot.postgresql.enabled=true" -}}
{{- end -}}
{{- else -}}
{{- if and (or (not .Values.copilot.database.existingSecretName) (eq .Values.copilot.database.existingSecretName "")) (or (not .Values.copilot.database.url) (eq .Values.copilot.database.url "")) -}}
{{- fail "Provide copilot.database.existingSecretName or copilot.database.url when copilot.postgresql.enabled=false" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end }}

View File

@@ -0,0 +1,109 @@
{{- if .Values.copilot.enabled }}
{{- include "sim.copilot.validate" . }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "sim.fullname" . }}-copilot
namespace: {{ .Release.Namespace }}
labels:
{{- include "sim.labels" . | nindent 4 }}
app.kubernetes.io/component: copilot
spec:
type: {{ .Values.copilot.server.service.type }}
ports:
- port: {{ .Values.copilot.server.service.port }}
targetPort: {{ .Values.copilot.server.service.targetPort }}
protocol: TCP
name: http
selector:
app.kubernetes.io/name: {{ include "sim.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: copilot
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "sim.fullname" . }}-copilot
namespace: {{ .Release.Namespace }}
labels:
{{- include "sim.labels" . | nindent 4 }}
app.kubernetes.io/component: copilot
spec:
replicas: {{ .Values.copilot.server.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "sim.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: copilot
template:
metadata:
annotations:
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
app.kubernetes.io/name: {{ include "sim.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: copilot
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.copilot.server.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.copilot.server.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: copilot
image: {{ include "sim.image" (dict "context" . "image" .Values.copilot.server.image) }}
imagePullPolicy: {{ .Values.copilot.server.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.copilot.server.service.targetPort }}
protocol: TCP
envFrom:
- secretRef:
name: {{ include "sim.copilot.envSecretName" . }}
- secretRef:
name: {{ include "sim.copilot.databaseSecretName" . }}
{{- with .Values.copilot.server.extraEnvFrom }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.copilot.server.extraEnv }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if .Values.copilot.server.livenessProbe }}
livenessProbe:
{{- toYaml .Values.copilot.server.livenessProbe | nindent 12 }}
{{- end }}
{{- if .Values.copilot.server.readinessProbe }}
readinessProbe:
{{- toYaml .Values.copilot.server.readinessProbe | nindent 12 }}
{{- end }}
{{- with .Values.copilot.server.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.copilot.server.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- end }}

View File

@@ -21,6 +21,9 @@ spec:
{{- if .Values.realtime.enabled }}
- {{ .Values.ingress.realtime.host }}
{{- end }}
{{- if and .Values.copilot.enabled .Values.ingress.copilot }}
- {{ .Values.ingress.copilot.host }}
{{- end }}
secretName: {{ .Values.ingress.tls.secretName }}
{{- end }}
rules:
@@ -52,4 +55,19 @@ spec:
number: {{ $.Values.realtime.service.port }}
{{- end }}
{{- end }}
{{- if and .Values.copilot.enabled .Values.ingress.copilot }}
# Copilot service ingress rule
- host: {{ .Values.ingress.copilot.host }}
http:
paths:
{{- range .Values.ingress.copilot.paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "sim.fullname" $ }}-copilot
port:
number: {{ $.Values.copilot.server.service.port }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,83 @@
{{- if and .Values.copilot.enabled .Values.copilot.migrations.enabled }}
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "sim.fullname" . }}-copilot-migrations
namespace: {{ .Release.Namespace }}
labels:
{{- include "sim.labels" . | nindent 4 }}
app.kubernetes.io/component: copilot-migrations
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation
spec:
backoffLimit: {{ .Values.copilot.migrations.backoffLimit }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "sim.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: copilot-migrations
spec:
{{- with .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
restartPolicy: {{ .Values.copilot.migrations.restartPolicy }}
{{- with .Values.copilot.migrations.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.copilot.server.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.copilot.postgresql.enabled }}
initContainers:
- name: wait-for-postgres
image: postgres:16-alpine
command:
- /bin/sh
- -c
- |
until pg_isready -h {{ include "sim.fullname" . }}-copilot-postgresql -p {{ .Values.copilot.postgresql.service.port }} -U {{ .Values.copilot.postgresql.auth.username }}; do
echo "Waiting for Copilot PostgreSQL to be ready..."
sleep 2
done
echo "Copilot PostgreSQL is ready!"
envFrom:
- secretRef:
name: {{ include "sim.fullname" . }}-copilot-postgresql-secret
{{- with .Values.copilot.migrations.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- end }}
containers:
- name: migrations
image: {{ include "sim.image" (dict "context" . "image" .Values.copilot.migrations.image) }}
imagePullPolicy: {{ .Values.copilot.migrations.image.pullPolicy }}
command: ["/usr/local/bin/migrate"]
envFrom:
- secretRef:
name: {{ include "sim.copilot.envSecretName" . }}
- secretRef:
name: {{ include "sim.copilot.databaseSecretName" . }}
{{- with .Values.copilot.server.extraEnvFrom }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.copilot.server.extraEnv }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.copilot.migrations.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.copilot.migrations.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- end }}

View File

@@ -50,3 +50,32 @@ spec:
{{- include "sim.realtime.selectorLabels" $ | nindent 6 }}
{{- end }}
{{- end }}
{{- if and .Values.copilot.enabled .Values.copilot.server.podDisruptionBudget.enabled }}
{{- with .Values.copilot.server.podDisruptionBudget }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ include "sim.fullname" $ }}-copilot-pdb
namespace: {{ $.Release.Namespace }}
labels:
{{- include "sim.labels" $ | nindent 4 }}
app.kubernetes.io/component: copilot
spec:
{{- if .minAvailable }}
minAvailable: {{ .minAvailable }}
{{- else if .maxUnavailable }}
maxUnavailable: {{ .maxUnavailable }}
{{- else }}
maxUnavailable: 1
{{- end }}
{{- if .unhealthyPodEvictionPolicy }}
unhealthyPodEvictionPolicy: {{ .unhealthyPodEvictionPolicy }}
{{- end }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "sim.name" $ }}
app.kubernetes.io/instance: {{ $.Release.Name }}
app.kubernetes.io/component: copilot
{{- end }}
{{- end }}

View File

@@ -0,0 +1,37 @@
{{- if and .Values.copilot.enabled .Values.copilot.server.secret.create }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "sim.copilot.envSecretName" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "sim.labels" . | nindent 4 }}
app.kubernetes.io/component: copilot
{{- with .Values.copilot.server.secret.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
type: Opaque
stringData:
{{- range $key, $value := .Values.copilot.server.env }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
{{- if and .Values.copilot.enabled (not .Values.copilot.postgresql.enabled) (or (not .Values.copilot.database.existingSecretName) (eq .Values.copilot.database.existingSecretName "")) (ne .Values.copilot.database.url "") }}
---
apiVersion: v1
kind: Secret
metadata:
name: {{ include "sim.copilot.databaseSecretName" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "sim.labels" . | nindent 4 }}
app.kubernetes.io/component: copilot
{{- with .Values.copilot.server.secret.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
type: Opaque
stringData:
{{ include "sim.copilot.databaseSecretKey" . }}: {{ required "copilot.database.url is required when using an external database" .Values.copilot.database.url | quote }}
{{- end }}

View File

@@ -0,0 +1,134 @@
{{- if and .Values.copilot.enabled .Values.copilot.postgresql.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "sim.fullname" . }}-copilot-postgresql-secret
namespace: {{ .Release.Namespace }}
labels:
{{- include "sim.labels" . | nindent 4 }}
app.kubernetes.io/component: copilot-postgresql
type: Opaque
stringData:
POSTGRES_USER: {{ .Values.copilot.postgresql.auth.username | quote }}
POSTGRES_PASSWORD: {{ required "copilot.postgresql.auth.password is required when copilot is enabled" .Values.copilot.postgresql.auth.password | quote }}
POSTGRES_DB: {{ .Values.copilot.postgresql.auth.database | quote }}
DATABASE_URL: "postgresql://{{ .Values.copilot.postgresql.auth.username }}:{{ .Values.copilot.postgresql.auth.password }}@{{ include "sim.fullname" . }}-copilot-postgresql:{{ .Values.copilot.postgresql.service.port }}/{{ .Values.copilot.postgresql.auth.database }}"
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "sim.fullname" . }}-copilot-postgresql
namespace: {{ .Release.Namespace }}
labels:
{{- include "sim.labels" . | nindent 4 }}
app.kubernetes.io/component: copilot-postgresql
spec:
type: {{ .Values.copilot.postgresql.service.type }}
ports:
- port: {{ .Values.copilot.postgresql.service.port }}
targetPort: {{ .Values.copilot.postgresql.service.targetPort }}
protocol: TCP
name: postgresql
selector:
app.kubernetes.io/name: {{ include "sim.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: copilot-postgresql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "sim.fullname" . }}-copilot-postgresql
namespace: {{ .Release.Namespace }}
labels:
{{- include "sim.labels" . | nindent 4 }}
app.kubernetes.io/component: copilot-postgresql
spec:
serviceName: {{ include "sim.fullname" . }}-copilot-postgresql
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: {{ include "sim.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: copilot-postgresql
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "sim.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: copilot-postgresql
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.copilot.postgresql.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.copilot.postgresql.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: postgresql
image: {{ include "sim.image" (dict "context" . "image" .Values.copilot.postgresql.image) }}
imagePullPolicy: {{ .Values.copilot.postgresql.image.pullPolicy }}
ports:
- name: postgresql
containerPort: {{ .Values.copilot.postgresql.service.targetPort }}
protocol: TCP
envFrom:
- secretRef:
name: {{ include "sim.fullname" . }}-copilot-postgresql-secret
{{- if .Values.copilot.postgresql.livenessProbe }}
livenessProbe:
{{- toYaml .Values.copilot.postgresql.livenessProbe | nindent 12 }}
{{- end }}
{{- if .Values.copilot.postgresql.readinessProbe }}
readinessProbe:
{{- toYaml .Values.copilot.postgresql.readinessProbe | nindent 12 }}
{{- end }}
{{- with .Values.copilot.postgresql.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.copilot.postgresql.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if .Values.copilot.postgresql.persistence.enabled }}
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
subPath: pgdata
{{- end }}
{{- if .Values.copilot.postgresql.persistence.enabled }}
volumeClaimTemplates:
- metadata:
name: data
labels:
{{- include "sim.labels" . | nindent 10 }}
app.kubernetes.io/component: copilot-postgresql
spec:
accessModes:
{{- range .Values.copilot.postgresql.persistence.accessModes }}
- {{ . | quote }}
{{- end }}
{{- if .Values.copilot.postgresql.persistence.storageClass }}
{{- if (eq "-" .Values.copilot.postgresql.persistence.storageClass) }}
storageClassName: ""
{{- else }}
storageClassName: {{ .Values.copilot.postgresql.persistence.storageClass | quote }}
{{- end }}
{{- else if .Values.global.storageClass }}
storageClassName: {{ .Values.global.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.copilot.postgresql.persistence.size | quote }}
{{- end }}
{{- end }}

View File

@@ -642,6 +642,186 @@
}
}
},
"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"]
}
}
}
}
},
"ingress": {
"type": "object",
"properties": {
@@ -673,6 +853,27 @@
}
}
},
"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": {

View File

@@ -744,4 +744,212 @@ telemetry:
enabled: false
endpoint: "http://otlp-collector:4317"
tls:
enabled: false
enabled: false
# Copilot service configuration (optional microservice)
copilot:
# Enable/disable the copilot service
enabled: false
# Server deployment configuration
server:
# Image configuration
image:
repository: simstudioai/copilot
tag: latest
pullPolicy: Always
# Number of replicas
replicaCount: 1
# Resource limits and requests
resources:
limits:
memory: "2Gi"
cpu: "1000m"
requests:
memory: "1Gi"
cpu: "500m"
# Node selector for pod scheduling
# Leave empty to run on same infrastructure as main Sim platform
# Or specify labels to isolate on dedicated nodes: { "workload-type": "copilot" }
nodeSelector: {}
# Pod security context
podSecurityContext:
fsGroup: 1001
# Container security context
securityContext:
runAsNonRoot: true
runAsUser: 1001
# Environment variables (required and optional)
env:
PORT: "8080"
SERVICE_NAME: "copilot"
ENVIRONMENT: "production"
AGENT_API_DB_ENCRYPTION_KEY: ""
INTERNAL_API_SECRET: ""
LICENSE_KEY: ""
OPENAI_API_KEY_1: ""
ANTHROPIC_API_KEY_1: ""
SIM_BASE_URL: ""
SIM_AGENT_API_KEY: ""
REDIS_URL: ""
# Optional configuration
LOG_LEVEL: "info"
CORS_ALLOWED_ORIGINS: ""
OTEL_EXPORTER_OTLP_ENDPOINT: ""
# Optional: additional static environment variables
extraEnv: []
# Optional: references to existing ConfigMaps/Secrets
extraEnvFrom: []
# Secret generation configuration (set create=false to use an existing secret)
secret:
create: true
name: ""
annotations: {}
# Service configuration
service:
type: ClusterIP
port: 8080
targetPort: 8080
# Health checks
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
# Pod Disruption Budget for high availability
podDisruptionBudget:
enabled: false
minAvailable: 1
# PostgreSQL database for copilot (separate from main Sim database)
postgresql:
# Enable/disable internal PostgreSQL for copilot
enabled: true
# Image configuration
image:
repository: postgres
tag: 16-alpine
pullPolicy: IfNotPresent
# Authentication configuration
auth:
username: copilot
password: "" # REQUIRED - set via --set flag or external secret manager
database: copilot
# Node selector for database pod scheduling
# Leave empty to run on same infrastructure as main Sim platform
# Or specify labels to isolate on dedicated nodes: { "workload-type": "copilot" }
nodeSelector: {}
# Resource limits and requests
resources:
limits:
memory: "1Gi"
cpu: "500m"
requests:
memory: "512Mi"
cpu: "250m"
# Pod security context
podSecurityContext:
fsGroup: 999
# Container security context
securityContext:
runAsUser: 999
# Persistence configuration
persistence:
enabled: true
storageClass: ""
size: 10Gi
accessModes:
- ReadWriteOnce
# Service configuration
service:
type: ClusterIP
port: 5432
targetPort: 5432
# Health checks
livenessProbe:
exec:
command: ["pg_isready", "-U", "copilot", "-d", "copilot"]
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 10
readinessProbe:
exec:
command: ["pg_isready", "-U", "copilot", "-d", "copilot"]
initialDelaySeconds: 5
periodSeconds: 3
timeoutSeconds: 5
failureThreshold: 10
# External database configuration (use when connecting to a managed database)
database:
existingSecretName: ""
secretKey: DATABASE_URL
url: ""
# Migration job configuration
migrations:
# Enable/disable migrations job
enabled: true
# Image configuration (same as server)
image:
repository: simstudioai/copilot
tag: latest
pullPolicy: Always
# Resource limits and requests
resources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "100m"
# Pod security context
podSecurityContext:
fsGroup: 1001
# Container security context
securityContext:
runAsNonRoot: true
runAsUser: 1001
# Job configuration
backoffLimit: 3
restartPolicy: OnFailure