# services/hermes/chat-sandbox.yaml apiVersion: v1 kind: Service metadata: name: hermes-chat-sandbox namespace: hermes labels: app: hermes-chat-sandbox spec: clusterIP: None publishNotReadyAddresses: false selector: app: hermes-chat-sandbox ports: - name: http port: 9080 targetPort: http --- apiVersion: apps/v1 kind: StatefulSet metadata: name: hermes-chat-sandbox namespace: hermes labels: app: hermes-chat-sandbox spec: serviceName: hermes-chat-sandbox replicas: 4 podManagementPolicy: Parallel persistentVolumeClaimRetentionPolicy: whenDeleted: Retain whenScaled: Retain selector: matchLabels: app: hermes-chat-sandbox template: metadata: labels: app: hermes-chat-sandbox annotations: ai.bstein.dev/role: isolated-user-computation ai.bstein.dev/isolation: one credential-free sandbox per chat tenant spec: automountServiceAccountToken: false enableServiceLinks: false securityContext: fsGroup: 20000 fsGroupChangePolicy: OnRootMismatch seccompProfile: type: RuntimeDefault affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/arch operator: In values: [arm64] - key: node-role.kubernetes.io/worker operator: In values: ["true"] - key: kubernetes.io/hostname operator: NotIn values: [titan-05, titan-08, titan-13, titan-14, titan-17, titan-18] preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 preference: matchExpressions: - key: hardware operator: In values: [rpi5] - weight: 40 preference: matchExpressions: - key: hardware operator: In values: [rpi4] podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: hermes-chat-sandbox topologyKey: kubernetes.io/hostname containers: - name: sandbox image: registry.bstein.dev/bstein/hermes-chat-sandbox@sha256:17ee62b8e61c08573a3a8cca903b38ec43800cb44ec29340e1bc095176544bca imagePullPolicy: IfNotPresent ports: - {name: http, containerPort: 9080, protocol: TCP} volumeMounts: - {name: workspace, mountPath: /workspace} - {name: tmp, mountPath: /tmp} readinessProbe: httpGet: {path: /health, port: http} initialDelaySeconds: 3 periodSeconds: 10 timeoutSeconds: 3 livenessProbe: httpGet: {path: /health, port: http} initialDelaySeconds: 10 periodSeconds: 30 timeoutSeconds: 3 securityContext: allowPrivilegeEscalation: false capabilities: drop: [ALL] readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 20000 runAsGroup: 20000 seccompProfile: type: RuntimeDefault resources: requests: {cpu: 50m, memory: 128Mi} limits: {cpu: "1", memory: 1Gi} volumes: - name: tmp emptyDir: sizeLimit: 256Mi volumeClaimTemplates: - metadata: name: workspace labels: app: hermes-chat-sandbox spec: accessModes: [ReadWriteOnce] storageClassName: astreae resources: requests: storage: 2Gi