# services/hermes/chat-statefulset.yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: hermes-chat-tenant namespace: hermes labels: app: hermes-chat-tenant annotations: kustomize.toolkit.fluxcd.io/force: enabled spec: serviceName: hermes-chat-tenant replicas: 4 podManagementPolicy: Parallel persistentVolumeClaimRetentionPolicy: whenDeleted: Retain whenScaled: Retain updateStrategy: type: RollingUpdate selector: matchLabels: app: hermes-chat-tenant template: metadata: labels: app: hermes-chat-tenant annotations: ai.bstein.dev/role: isolated-user-chat ai.bstein.dev/router-wire-contract: ollama-numeric-keepalive ai.bstein.dev/isolation: one Hermes process and PVC per Keycloak subject ai.bstein.dev/model-policy: uniform automatic policy with per-user overrides ai.bstein.dev/config-rev: "20260913-provider-model-catalog-v2" ai.bstein.dev/hux-config-rev: "20260824-hux-v1" vault.hashicorp.com/agent-inject: "true" vault.hashicorp.com/role: hermes-chat vault.hashicorp.com/agent-inject-secret-chat-relay-key: kv/data/atlas/hermes/chat-telegram vault.hashicorp.com/agent-inject-template-chat-relay-key: | {{- with secret "kv/data/atlas/hermes/chat-telegram" -}} {{ .Data.data.relay_key }} {{- end }} vault.hashicorp.com/agent-pre-populate-only: "true" vault.hashicorp.com/agent-init-first: "true" vault.hashicorp.com/agent-requests-cpu: 25m vault.hashicorp.com/agent-requests-mem: 32Mi vault.hashicorp.com/agent-limits-cpu: 100m vault.hashicorp.com/agent-limits-mem: 128Mi spec: serviceAccountName: hermes-chat automountServiceAccountToken: true securityContext: fsGroup: 10000 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: node-role.kubernetes.io/storage-backbone operator: DoesNotExist - key: kubernetes.io/hostname operator: NotIn values: [titan-05, titan-13, titan-14, titan-17, titan-18, titan-19] preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 preference: matchExpressions: - key: node-role.kubernetes.io/storage-backbone operator: DoesNotExist - 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-tenant topologyKey: kubernetes.io/hostname initContainers: - name: init-config image: busybox:1.37 imagePullPolicy: IfNotPresent command: - sh - -c - | set -eu mkdir -p /opt/data/home/.local/bin /opt/data/logs /opt/data/workspace if [ ! -e /opt/data/workspace/.hermes-workspace-v1 ]; then if [ -d /legacy-home/workspace ]; then cp -a /legacy-home/workspace/. /opt/data/workspace/ fi touch /opt/data/workspace/.hermes-workspace-v1 fi cp /config/config.yaml /opt/data/config.yaml cp /config/SOUL.md /opt/data/SOUL.md cp /config/AGENTS.md /opt/data/workspace/AGENTS.md touch /opt/data/.env for key in ANTHROPIC_API_KEY API_SERVER_KEY CLAUDE_API_KEY CLAUDE_CODE_OAUTH_TOKEN GITEA_TOKEN GITEA_USERNAME HERMES_IMAGE_BROKER_KEY OPENAI_API_KEY; do grep -v "^${key}=" /opt/data/.env > /opt/data/.env.tmp || true mv /opt/data/.env.tmp /opt/data/.env done rm -f /opt/data/auth.json chmod 0600 /opt/data/.env chown 10000:10000 \ /opt/data \ /opt/data/home \ /opt/data/home/.local \ /opt/data/home/.local/bin \ /opt/data/logs \ /opt/data/workspace \ /opt/data/config.yaml \ /opt/data/SOUL.md \ /opt/data/workspace/AGENTS.md \ /opt/data/.env securityContext: allowPrivilegeEscalation: false runAsUser: 0 runAsGroup: 0 seccompProfile: type: RuntimeDefault volumeMounts: - {name: home, mountPath: /opt/data} - {name: home, mountPath: /legacy-home, readOnly: true} - {name: workspace, mountPath: /opt/data/workspace} - {name: config, mountPath: /config, readOnly: true} resources: requests: {cpu: 25m, memory: 32Mi} limits: {cpu: 100m, memory: 64Mi} - name: init-hux-runtime image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107 imagePullPolicy: IfNotPresent command: [/bin/sh, -ec] args: - | umask 077 tenant_root="/hux-data/${HOSTNAME}" export HUX_INIT_ROOT="${tenant_root}" mkdir -p \ "${tenant_root}/binding" \ "${tenant_root}/context" \ "${tenant_root}/store" \ /hux-relay \ /hux-worker chown 10000:10000 \ "${tenant_root}" \ "${tenant_root}/binding" \ "${tenant_root}/context" \ "${tenant_root}/store" \ /hux-relay \ /hux-worker chmod 0700 \ "${tenant_root}" \ "${tenant_root}/binding" \ "${tenant_root}/context" \ "${tenant_root}/store" \ /hux-relay \ /hux-worker if [ ! -e "${tenant_root}/context/context-key" ]; then dd if=/dev/urandom of="${tenant_root}/context/.context-key.tmp" bs=32 count=1 2>/dev/null chown 10000:10000 "${tenant_root}/context/.context-key.tmp" chmod 0600 "${tenant_root}/context/.context-key.tmp" mv "${tenant_root}/context/.context-key.tmp" "${tenant_root}/context/context-key" fi test "$(wc -c < "${tenant_root}/context/context-key")" -eq 32 chown 10000:10000 "${tenant_root}/context/context-key" chmod 0600 "${tenant_root}/context/context-key" ordinal="${HOSTNAME##*-}" HUX_INIT_SLOT="slot-${ordinal}" \ /opt/hermes/.venv/bin/python - <<'PY' import hashlib import hmac import os import stat from pathlib import Path root = Path(os.environ["HUX_INIT_ROOT"]) key = (root / "context/context-key").read_bytes() slot = os.environ["HUX_INIT_SLOT"] subject = "usr_" + hmac.new( key, b"hux.subject.id.v1\0" + slot.encode("ascii"), hashlib.sha256, ).hexdigest() target = root / "binding/subject" expected = (subject + "\n").encode("ascii") flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_NOFOLLOW", 0) try: descriptor = os.open(target, flags, 0o440) except FileExistsError: info = target.lstat() if ( not stat.S_ISREG(info.st_mode) or info.st_uid != 10000 or stat.S_IMODE(info.st_mode) != 0o440 or info.st_nlink != 1 or target.read_bytes() != expected ): raise SystemExit("persistent HUX subject binding is unsafe") else: try: os.write(descriptor, expected) os.fchown(descriptor, 10000, 10000) os.fchmod(descriptor, 0o440) os.fsync(descriptor) finally: os.close(descriptor) PY if [ ! -e "${tenant_root}/context/redaction-canary" ]; then dd if=/dev/urandom bs=32 count=1 2>/dev/null \ | sha256sum | cut -d ' ' -f 1 \ > "${tenant_root}/context/.redaction-canary.tmp" chown 10000:10000 "${tenant_root}/context/.redaction-canary.tmp" chmod 0400 "${tenant_root}/context/.redaction-canary.tmp" mv "${tenant_root}/context/.redaction-canary.tmp" "${tenant_root}/context/redaction-canary" fi for target in /hux-relay/relay-key /hux-worker/worker-key; do if [ ! -e "${target}" ]; then dd if=/dev/urandom bs=32 count=1 2>/dev/null \ | sha256sum | cut -d ' ' -f 1 > "${target}.tmp" chown 10000:10000 "${target}.tmp" chmod 0400 "${target}.tmp" mv "${target}.tmp" "${target}" fi test "$(wc -c < "${target}")" -eq 65 chown 10000:10000 "${target}" chmod 0400 "${target}" done securityContext: allowPrivilegeEscalation: false capabilities: drop: [ALL] add: [CHOWN, DAC_OVERRIDE, FOWNER] runAsUser: 0 runAsGroup: 0 seccompProfile: type: RuntimeDefault volumeMounts: - {name: hux-data, mountPath: /hux-data} - {name: hux-relay-key, mountPath: /hux-relay} - {name: hux-worker-key, mountPath: /hux-worker} resources: requests: {cpu: 10m, memory: 16Mi} limits: {cpu: 50m, memory: 32Mi} - name: stage-hux-evidence image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107 imagePullPolicy: IfNotPresent command: [/bin/sh, -ec] args: - | # HUX-12 enablement is fail-closed: until the Vault annotation # projects hux-evidence-key, nothing is staged and the evidence # capability stays off. The key is staged only for the hux # service and producer containers, never for hermes or webui. if [ -s /vault/secrets/hux-evidence-key ]; then umask 077 tr -d '\r\n' < /vault/secrets/hux-evidence-key > /hux-evidence/.evidence-key.tmp chown 10000:10000 /hux-evidence/.evidence-key.tmp chmod 0400 /hux-evidence/.evidence-key.tmp mv /hux-evidence/.evidence-key.tmp /hux-evidence/evidence-key fi securityContext: allowPrivilegeEscalation: false capabilities: drop: [ALL] add: [CHOWN, DAC_OVERRIDE, FOWNER] runAsUser: 0 runAsGroup: 0 seccompProfile: type: RuntimeDefault volumeMounts: - {name: hux-evidence-key, mountPath: /hux-evidence} resources: requests: {cpu: 10m, memory: 16Mi} limits: {cpu: 50m, memory: 32Mi} - name: stage-runtime-access image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107 imagePullPolicy: IfNotPresent command: - /opt/hermes/.venv/bin/python - /opt/coordinator/stage_runtime_access.py - chat securityContext: allowPrivilegeEscalation: false runAsUser: 0 runAsGroup: 0 seccompProfile: type: RuntimeDefault volumeMounts: - {name: coordinator, mountPath: /opt/coordinator, readOnly: true} - {name: runtime-access, mountPath: /runtime-access} resources: requests: {cpu: 25m, memory: 32Mi} limits: {cpu: 100m, memory: 64Mi} - name: patch-auth image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107 imagePullPolicy: IfNotPresent command: - /opt/hermes/.venv/bin/python - /opt/coordinator/patch_hermes_auth.py - /opt/hermes/hermes_cli/auth.py - /patched/auth.py securityContext: allowPrivilegeEscalation: false runAsUser: 10000 runAsGroup: 10000 seccompProfile: type: RuntimeDefault volumeMounts: - {name: coordinator, mountPath: /opt/coordinator, readOnly: true} - {name: auth-patch, mountPath: /patched} resources: requests: {cpu: 25m, memory: 64Mi} limits: {cpu: 100m, memory: 128Mi} - name: patch-stream-recovery image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107 imagePullPolicy: IfNotPresent command: - /opt/hermes/.venv/bin/python - /opt/coordinator/patch_stream_recovery.py - /opt/hermes/agent/conversation_loop.py - /patched/conversation_loop.py securityContext: allowPrivilegeEscalation: false runAsUser: 10000 runAsGroup: 10000 seccompProfile: type: RuntimeDefault volumeMounts: - {name: coordinator, mountPath: /opt/coordinator, readOnly: true} - {name: stream-recovery-patch, mountPath: /patched} resources: requests: {cpu: 25m, memory: 64Mi} limits: {cpu: 100m, memory: 128Mi} - name: patch-api-server-sessions image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107 imagePullPolicy: IfNotPresent command: [/bin/sh, -ec] args: - | /opt/hermes/.venv/bin/python /opt/coordinator/patch_api_server_sessions.py \ /opt/hermes/gateway/platforms/api_server.py /patched/api_server.py grep -Fq 'conversation_history = compact_telegram_history(conversation_history)' /patched/api_server.py grep -Fq 'full_history = compact_telegram_history(full_history)' /patched/api_server.py grep -Fq 'conversation_history_snapshot = compact_telegram_history(' /patched/api_server.py /opt/hermes/.venv/bin/python /opt/coordinator/migrate_telegram_api_sessions.py \ /opt/data/state.db /opt/data/response_store.db securityContext: allowPrivilegeEscalation: false runAsUser: 10000 runAsGroup: 10000 seccompProfile: type: RuntimeDefault volumeMounts: - {name: home, mountPath: /opt/data} - {name: coordinator, mountPath: /opt/coordinator, readOnly: true} - {name: api-server-patch, mountPath: /patched} resources: requests: {cpu: 25m, memory: 64Mi} limits: {cpu: 100m, memory: 128Mi} - name: patch-subprocess-secret-boundary image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107 imagePullPolicy: IfNotPresent command: - /bin/sh - -ec - | /opt/hermes/.venv/bin/python /opt/coordinator/patch_subprocess_secret_boundary.py \ /opt/hermes/tools/environments/local.py /patched/local.py /opt/hermes/.venv/bin/python /opt/coordinator/patch_process_output_redaction.py \ /opt/hermes/tools/process_registry.py /patched/process_registry.py securityContext: allowPrivilegeEscalation: false runAsUser: 10000 runAsGroup: 10000 seccompProfile: type: RuntimeDefault volumeMounts: - {name: coordinator, mountPath: /opt/coordinator, readOnly: true} - {name: subprocess-secret-patch, mountPath: /patched} resources: requests: {cpu: 25m, memory: 64Mi} limits: {cpu: 100m, memory: 128Mi} containers: - name: hermes image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107 imagePullPolicy: IfNotPresent command: [/bin/sh, -ec] args: - | ordinal="${HOSTNAME##*-}" export HERMES_CODE_SANDBOX_URL="http://hermes-chat-sandbox-${ordinal}.hermes-chat-sandbox.hermes.svc.cluster.local:9080/v1/execute" set -a . /opt/data/.env set +a API_SERVER_KEY="$(tr -d '\r\n' < /runtime-access/chat-relay-key)" test -n "${API_SERVER_KEY}" export API_SERVER_KEY export HUX_TENANT_SLOT="slot-${ordinal}" exec /opt/hermes/.venv/bin/hermes gateway run ports: - {name: api, containerPort: 8642, protocol: TCP} env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - {name: HERMES_HOME, value: /opt/data} - {name: HERMES_AUTH_FILE, value: /runtime-access/hermes-auth.json} - {name: HOME, value: /opt/data/home} - {name: TERMINAL_CWD, value: /opt/data/workspace} - {name: HERMES_WRITE_SAFE_ROOT, value: /opt/data/workspace} - {name: PATH, value: '/opt/data/home/.local/bin:/opt/hermes/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'} - {name: PLAYWRIGHT_BROWSERS_PATH, value: /opt/hermes/.playwright} - {name: AGENT_BROWSER_EXECUTABLE_PATH, value: /opt/hermes/.playwright/chromium_headless_shell-1228/chrome-linux/headless_shell} - {name: AGENT_BROWSER_ARGS, value: "--no-sandbox,--disable-dev-shm-usage"} - {name: HERMES_DASHBOARD, value: "0"} - {name: API_SERVER_ENABLED, value: "true"} - {name: API_SERVER_HOST, value: 0.0.0.0} - {name: API_SERVER_PORT, value: "8642"} - {name: API_SERVER_CORS_ORIGINS, value: 'https://chat.bstein.dev'} - {name: HERMES_IMAGE_BROKER_URL, value: 'http://hermes-image-broker.hermes.svc.cluster.local:9002'} - {name: HERMES_IMAGE_BROKER_KEY_FILE, value: /runtime-access/chat-relay-key} - {name: HERMES_AUTO_ROUTER_PROFILE, value: chat} - {name: HUX_BASE_URL, value: 'http://127.0.0.1:8790'} - {name: HUX_RUNTIME_ENABLED, value: "1"} # First rollout is observe-only until approval parking/resume is # connected to the upstream tool loop and proven live. - {name: HUX_TOOL_ENFORCEMENT, value: "0"} - {name: HUX_WORKER_KEY_FILE, value: /run/hermes-hux-worker/worker-key} - {name: HUX_SUBJECT_FILE, value: /run/hermes-hux-subject/subject} - {name: HUX_CONTEXT_KEY_FILE, value: /run/hermes-hux-context/context-key} - {name: HUX_PROJECT_SOURCE, value: 'profile:default'} - {name: HUX_TIMEOUT_SECONDS, value: "3"} volumeMounts: - {name: home, mountPath: /opt/data} - {name: workspace, mountPath: /opt/data/workspace} - {name: runtime-access, mountPath: /runtime-access} - {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py} - {name: api-server-patch, mountPath: /opt/hermes/gateway/platforms/api_server.py, subPath: api_server.py} - {name: coordinator, mountPath: /opt/hermes/gateway/platforms/telegram_continuity.py, subPath: migrate_telegram_api_sessions.py, readOnly: true} - {name: stream-recovery-patch, mountPath: /opt/hermes/agent/conversation_loop.py, subPath: conversation_loop.py} - {name: subprocess-secret-patch, mountPath: /opt/hermes/tools/environments/local.py, subPath: local.py} - {name: subprocess-secret-patch, mountPath: /opt/hermes/tools/process_registry.py, subPath: process_registry.py} - {name: image-plugin, mountPath: /opt/hermes/plugins/image_gen/atlas-broker, readOnly: true} - {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly: true} - {name: hux-runtime-plugin, mountPath: /opt/data/plugins/hux-runtime, readOnly: true} - {name: cluster-read-plugin, mountPath: /opt/data/plugins/cluster-read, readOnly: true} - {name: hux-worker-key, mountPath: /run/hermes-hux-worker, readOnly: true} - {name: hux-data, mountPath: /run/hermes-hux-context, subPathExpr: $(POD_NAME)/context, readOnly: true} - {name: hux-data, mountPath: /run/hermes-hux-subject, subPathExpr: $(POD_NAME)/binding, readOnly: true} readinessProbe: tcpSocket: {port: api} initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 livenessProbe: tcpSocket: {port: api} initialDelaySeconds: 90 periodSeconds: 30 timeoutSeconds: 10 securityContext: allowPrivilegeEscalation: false runAsUser: 10000 runAsGroup: 10000 seccompProfile: type: RuntimeDefault resources: requests: {cpu: 250m, memory: 256Mi} limits: {cpu: "1", memory: 2Gi} - name: webui image: registry.bstein.dev/bstein/hermes-webui:git-2c91aea01d6b874e247c2fc3528e5b4bb580ffe4-build-39-release@sha256:4f60fae01efb8ffc00b6af78865c975417446904beb5c447d11e39c1ecc1438c # {"$imagepolicy": "hermes:hermes-webui-release"} imagePullPolicy: IfNotPresent command: [/bin/sh, -ec] args: - | api_key="$(tr -d '\r\n' < /runtime-access/chat-relay-key)" test -n "${api_key}" export API_SERVER_KEY="${api_key}" export HERMES_WEBUI_GATEWAY_API_KEY="${api_key}" exec /opt/hermes/.venv/bin/python /opt/hermes-webui/server.py ports: - {name: webui, containerPort: 8787, protocol: TCP} env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - {name: HERMES_HOME, value: /opt/data} - {name: HERMES_AUTH_FILE, value: /runtime-access/hermes-auth.json} - {name: HOME, value: /opt/data/home} - {name: HERMES_WEBUI_AGENT_DIR, value: /opt/hermes} - {name: HERMES_WEBUI_HOST, value: 0.0.0.0} - {name: HERMES_WEBUI_PORT, value: "8787"} - {name: HERMES_WEBUI_STATE_DIR, value: /opt/data/webui} - {name: HERMES_WEBUI_DEFAULT_WORKSPACE, value: /opt/data/workspace} - {name: HERMES_WEBUI_CHAT_BACKEND, value: gateway} - {name: HERMES_WEBUI_GATEWAY_BASE_URL, value: 'http://127.0.0.1:8642'} - {name: HERMES_WEBUI_GATEWAY_USE_RUNS_API, value: "true"} - {name: HERMES_WEBUI_SKIP_ONBOARDING, value: "1"} - {name: HERMES_WEBUI_SECURE, value: "1"} - {name: HERMES_WEBUI_COOKIE_NAME, value: hermes_chat_session} - {name: HERMES_WEBUI_PROFILE_COOKIE_NAME, value: hermes_chat_profile} - {name: HERMES_WEBUI_TRUSTED_AUTH_HEADER, value: X-Hermes-Tenant-Identity} # NetworkPolicy admits this port only from hermes-chat-router; the # CIDR lets the WebUI validate that router's changing pod address. - {name: HERMES_WEBUI_TRUSTED_PROXY_CIDRS, value: 10.42.0.0/16} - {name: HERMES_WEBUI_ALLOWED_ORIGINS, value: 'https://chat.bstein.dev'} - {name: HERMES_WEBUI_TRUST_FORWARDED_HOST, value: "1"} - {name: HERMES_WEBUI_TRUST_FORWARDED_PROTO, value: "1"} - {name: HERMES_ROUTER_PROFILE, value: chat} - {name: HERMES_STT_URL, value: 'http://hermes-stt.hermes.svc.cluster.local:9000/v1/audio/transcriptions'} - {name: HERMES_WEBUI_ATLAS_STT_STREAM_URL, value: 'http://hermes-stt.hermes.svc.cluster.local:9000/v1/audio/transcriptions/stream'} - {name: HERMES_LOCAL_STT_COMMAND, value: "/opt/hermes/.venv/bin/python /opt/coordinator/hermes_stt_client.py {input_path} --output-dir {output_dir} --language {language} --model {model}"} - {name: HERMES_WEBUI_ATLAS_TTS_URL, value: 'http://hermes-tts.hermes.svc.cluster.local:9001/v1/audio/speech'} - {name: HERMES_WEBUI_ATLAS_TTS_STREAM_URL, value: 'http://hermes-tts.hermes.svc.cluster.local:9001/v1/audio/speech/stream'} - {name: HUX_CONTEXT_KEY_FILE, value: /run/hermes-hux-context/context-key} - {name: HUX_PROJECT_SOURCE, value: 'profile:default'} volumeMounts: - {name: home, mountPath: /opt/data} - {name: workspace, mountPath: /opt/data/workspace} - {name: runtime-access, mountPath: /runtime-access, readOnly: true} - {name: coordinator, mountPath: /opt/coordinator, readOnly: true} - {name: tmp, mountPath: /tmp} - {name: hux-relay-key, mountPath: /run/hermes-webui-hux, readOnly: true} - {name: hux-data, mountPath: /run/hermes-hux-context, subPathExpr: $(POD_NAME)/context, readOnly: true} readinessProbe: httpGet: {path: /health, port: webui} initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 5 livenessProbe: httpGet: {path: /health, port: webui} initialDelaySeconds: 30 periodSeconds: 30 timeoutSeconds: 10 securityContext: allowPrivilegeEscalation: false capabilities: drop: [ALL] readOnlyRootFilesystem: true runAsUser: 10000 runAsGroup: 10000 seccompProfile: type: RuntimeDefault resources: requests: {cpu: 100m, memory: 224Mi} limits: {cpu: 750m, memory: 1Gi} - name: hux image: registry.bstein.dev/bstein/hermes-webui:git-2c91aea01d6b874e247c2fc3528e5b4bb580ffe4-build-39-release@sha256:4f60fae01efb8ffc00b6af78865c975417446904beb5c447d11e39c1ecc1438c # {"$imagepolicy": "hermes:hermes-webui-release"} imagePullPolicy: IfNotPresent command: [/bin/sh, -ec] args: - | ordinal="${HOSTNAME##*-}" export HUX_TENANT_SLOT="slot-${ordinal}" exec /opt/hermes/.venv/bin/python -m hux.server ports: - {name: hux-loopback, containerPort: 8790, protocol: TCP} env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - {name: PYTHONPATH, value: /opt/hermes-hux} - {name: PYTHONDONTWRITEBYTECODE, value: "1"} - {name: HOME, value: /tmp} - {name: HUX_BIND, value: 127.0.0.1} - {name: HUX_PORT, value: "8790"} - {name: HUX_DATA_ROOT, value: /var/lib/hux/store} - {name: HUX_FLAGS, value: 'hux.foundation,hux.activity_timeline,hux.projects,hux.privacy,hux.memory_control,hux.artifacts,hux.research,hux.friendly_modes,hux.multimodal,hux.onboarding,hux.autonomy'} - {name: HUX_RELAY_KEY_FILE, value: /run/hermes-webui-hux/relay-key} - {name: HUX_WORKER_KEY_FILE, value: /run/hermes-hux-worker/worker-key} - {name: HUX_SUBJECT_BINDING_FILE, value: /var/lib/hux/binding/subject} - {name: HUX_CONTEXT_KEY_FILE, value: /var/lib/hux/context/context-key} - {name: HUX_CANARY_FILE, value: /var/lib/hux/context/redaction-canary} - name: HUX_IMAGE_TAG value: git-2c91aea01d6b874e247c2fc3528e5b4bb580ffe4-build-39-release # {"$imagepolicy": "hermes:hermes-webui-release:tag"} - name: HUX_IMAGE_DIGEST value: sha256:4f60fae01efb8ffc00b6af78865c975417446904beb5c447d11e39c1ecc1438c # {"$imagepolicy": "hermes:hermes-webui-release:digest"} - {name: HUX_SWITCHYARD_ROUTE_CATALOG, value: 'atlas/manual/codex/luna,atlas/manual/codex/terra,atlas/manual/codex/sol,atlas/manual/claude/haiku,atlas/manual/claude/fable,atlas/manual/claude/sonnet,atlas/manual/claude/opus,atlas/manual/local/qwen-14b'} - {name: HUX_RELEASE_EVIDENCE_KEY_FILE, value: /run/hermes-hux-evidence/evidence-key} - {name: HUX_RELEASE_EVIDENCE_POLICY_FILE, value: /etc/hux-evidence/policy.json} - {name: HUX_READS_PER_MINUTE, value: "600"} - {name: HUX_WRITES_PER_MINUTE, value: "120"} - {name: HUX_REQUEST_TIMEOUT_SECONDS, value: "10"} volumeMounts: - {name: hux-data, mountPath: /var/lib/hux, subPathExpr: $(POD_NAME)} - {name: hux-relay-key, mountPath: /run/hermes-webui-hux, readOnly: true} - {name: hux-worker-key, mountPath: /run/hermes-hux-worker, readOnly: true} - {name: hux-tmp, mountPath: /tmp} - {name: hux-evidence-key, mountPath: /run/hermes-hux-evidence, readOnly: true} - {name: hux-evidence-policy, mountPath: /etc/hux-evidence, readOnly: true} readinessProbe: exec: command: - /opt/hermes/.venv/bin/python - -c - "import json,urllib.request; body=json.load(urllib.request.urlopen('http://127.0.0.1:8790/healthz', timeout=2)); assert body['status']=='ok'" initialDelaySeconds: 2 periodSeconds: 5 timeoutSeconds: 5 failureThreshold: 12 livenessProbe: exec: command: - /opt/hermes/.venv/bin/python - -c - "import json,urllib.request; body=json.load(urllib.request.urlopen('http://127.0.0.1:8790/healthz', timeout=2)); assert body['status']=='ok'" initialDelaySeconds: 20 periodSeconds: 20 timeoutSeconds: 5 securityContext: allowPrivilegeEscalation: false capabilities: drop: [ALL] readOnlyRootFilesystem: true runAsUser: 10000 runAsGroup: 10000 seccompProfile: type: RuntimeDefault resources: requests: {cpu: 25m, memory: 64Mi} limits: {cpu: 250m, memory: 256Mi} - name: hux-evidence-producer image: registry.bstein.dev/bstein/hermes-webui:git-2c91aea01d6b874e247c2fc3528e5b4bb580ffe4-build-39-release@sha256:4f60fae01efb8ffc00b6af78865c975417446904beb5c447d11e39c1ecc1438c # {"$imagepolicy": "hermes:hermes-webui-release"} imagePullPolicy: IfNotPresent command: [/bin/sh, -ec] args: - | ordinal="${HOSTNAME##*-}" export HUX_TENANT_SLOT="slot-${ordinal}" while true; do if [ -s /run/hermes-hux-evidence/evidence-key ] \ && [ -s /run/hermes-hux-subject/subject ] \ && [ -n "${HUX_PRODUCER_PROJECT_ID:-}" ] \ && [ -n "${HUX_PRODUCER_CONVERSATION_ID:-}" ]; then HUX_PRODUCER_SUBJECT="$(tr -d '\r\n' < /run/hermes-hux-subject/subject)" \ /opt/hermes/.venv/bin/python -c \ 'from hux_producer import run_once; run_once()' || true fi sleep 60 done env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - {name: PYTHONPATH, value: /opt/hermes-hux} - {name: PYTHONDONTWRITEBYTECODE, value: "1"} - {name: HOME, value: /tmp} - {name: HUX_BASE_URL, value: 'http://127.0.0.1:8790'} - {name: HUX_PRODUCER_WORKLOAD, value: hermes-webui} - {name: HUX_PRODUCER_NAMESPACE, value: hermes} - {name: HUX_PRODUCER_POD_SELECTOR, value: 'app=hermes-chat-tenant'} - {name: HUX_PRODUCER_WORKLOAD_KIND, value: statefulset} - {name: HUX_PRODUCER_WORKLOAD_NAME, value: hermes-chat-tenant} - {name: HUX_PRODUCER_TIMEOUT_SECONDS, value: "10"} - {name: HUX_RELEASE_EVIDENCE_KEY_FILE, value: /run/hermes-hux-evidence/evidence-key} - {name: HUX_RELEASE_EVIDENCE_POLICY_FILE, value: /etc/hux-evidence/policy.json} envFrom: - configMapRef: name: hermes-hux-evidence-scope optional: true volumeMounts: - {name: hux-evidence-key, mountPath: /run/hermes-hux-evidence, readOnly: true} - {name: hux-evidence-policy, mountPath: /etc/hux-evidence, readOnly: true} - {name: hux-data, mountPath: /run/hermes-hux-subject, subPathExpr: $(POD_NAME)/binding, readOnly: true} - {name: hux-tmp, mountPath: /tmp} securityContext: allowPrivilegeEscalation: false capabilities: drop: [ALL] readOnlyRootFilesystem: true runAsUser: 10000 runAsGroup: 10000 seccompProfile: type: RuntimeDefault resources: requests: {cpu: 10m, memory: 48Mi} limits: {cpu: 100m, memory: 128Mi} - name: telegram-media image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107 imagePullPolicy: IfNotPresent command: [/opt/hermes/.venv/bin/python, /opt/coordinator/telegram_media_server.py] ports: - {name: telegram-media, containerPort: 8788, protocol: TCP} env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - {name: HERMES_MEDIA_RELAY_KEY_FILE, value: /runtime-access/chat-relay-key} volumeMounts: - {name: home, mountPath: /opt/data, readOnly: true} - {name: workspace, mountPath: /opt/data/workspace, readOnly: true} - {name: coordinator, mountPath: /opt/coordinator, readOnly: true} - {name: runtime-access, mountPath: /runtime-access, readOnly: true} readinessProbe: httpGet: {path: /healthz, port: telegram-media} initialDelaySeconds: 2 periodSeconds: 10 livenessProbe: httpGet: {path: /healthz, port: telegram-media} initialDelaySeconds: 10 periodSeconds: 20 securityContext: allowPrivilegeEscalation: false capabilities: drop: [ALL] readOnlyRootFilesystem: true runAsUser: 10000 runAsGroup: 10000 seccompProfile: type: RuntimeDefault resources: requests: {cpu: 10m, memory: 24Mi} limits: {cpu: 100m, memory: 64Mi} volumes: - name: runtime-access emptyDir: medium: Memory sizeLimit: 2Mi - name: config configMap: name: hermes-chat-config - name: coordinator configMap: name: hermes-coordinator defaultMode: 0555 - name: auth-patch emptyDir: {} - name: api-server-patch emptyDir: {} - name: stream-recovery-patch emptyDir: {} - name: subprocess-secret-patch emptyDir: {} - name: auto-router-plugin configMap: name: hermes-auto-router-plugin - name: image-plugin configMap: name: hermes-chat-image-plugin - name: cluster-read-plugin configMap: name: hermes-cluster-read-plugin - name: hux-runtime-plugin configMap: name: hermes-hux-runtime-plugin items: - {key: __init__.py, path: __init__.py} - {key: context_ids.py, path: context_ids.py} - {key: emitters.py, path: emitters.py} - {key: runtime.py, path: runtime.py} - {key: tool_policy.py, path: tool_policy.py} - {key: plugin.yaml, path: plugin.yaml} - {key: hux-hook-init.py, path: hux_hook/__init__.py} - {key: hux-hook-client.py, path: hux_hook/client.py} - {key: hux-hook-hooks.py, path: hux_hook/hooks.py} - name: tmp emptyDir: sizeLimit: 256Mi - name: hux-relay-key emptyDir: medium: Memory sizeLimit: 1Mi - name: hux-worker-key emptyDir: medium: Memory sizeLimit: 1Mi - name: hux-tmp emptyDir: sizeLimit: 64Mi - name: hux-evidence-key emptyDir: medium: Memory sizeLimit: 1Mi - name: hux-evidence-policy configMap: name: hermes-hux-evidence-policy defaultMode: 0444 - name: hux-data persistentVolumeClaim: claimName: hermes-chat-hux-data volumeClaimTemplates: - metadata: name: home labels: app: hermes-chat-tenant spec: accessModes: [ReadWriteOnce] storageClassName: astreae resources: requests: storage: 10Gi - metadata: name: workspace labels: app: hermes-chat-tenant ai.bstein.dev/data: user-workspace spec: accessModes: [ReadWriteMany] storageClassName: astreae resources: requests: storage: 10Gi