2026-08-08 17:59:45 -03:00
# services/hermes/chat-statefulset.yaml
apiVersion : apps/v1
kind : StatefulSet
metadata :
name : hermes-chat-tenant
namespace : hermes
labels :
app : hermes-chat-tenant
2026-08-08 21:28:33 -03:00
annotations :
kustomize.toolkit.fluxcd.io/force : enabled
2026-08-08 17:59:45 -03:00
spec :
serviceName : hermes-chat-tenant
2026-08-18 23:47:22 -03:00
replicas : 4
2026-08-08 17:59:45 -03:00
podManagementPolicy : Parallel
2026-08-08 21:28:33 -03:00
persistentVolumeClaimRetentionPolicy :
whenDeleted : Retain
whenScaled : Retain
2026-08-08 17:59:45 -03:00
updateStrategy :
type : RollingUpdate
selector :
matchLabels :
app : hermes-chat-tenant
template :
metadata :
labels :
app : hermes-chat-tenant
annotations :
ai.bstein.dev/role : isolated-user-chat
2026-08-11 16:33:53 -03:00
ai.bstein.dev/router-wire-contract : ollama-numeric-keepalive
2026-08-08 17:59:45 -03:00
ai.bstein.dev/isolation : one Hermes process and PVC per Keycloak subject
ai.bstein.dev/model-policy : uniform automatic policy with per-user overrides
2026-08-16 15:59:09 -03:00
ai.bstein.dev/config-rev : "20260816-telegram-topics"
2026-08-24 05:47:46 -03:00
ai.bstein.dev/hux-config-rev : "20260824-hux-v1"
2026-08-08 17:59:45 -03:00
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" ]
2026-08-23 10:43:42 -03:00
- key : node-role.kubernetes.io/storage-backbone
operator : DoesNotExist
2026-08-08 17:59:45 -03:00
- key : kubernetes.io/hostname
operator : NotIn
2026-08-23 10:37:01 -03:00
values : [ titan-05, titan-13, titan-14, titan-17, titan-18, titan-19]
2026-08-08 17:59:45 -03:00
preferredDuringSchedulingIgnoredDuringExecution :
2026-08-23 10:37:01 -03:00
- weight : 100
preference :
matchExpressions :
- key : node-role.kubernetes.io/storage-backbone
operator : DoesNotExist
2026-08-08 17:59:45 -03:00
- 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
2026-08-10 00:42:37 -03:00
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
2026-08-08 17:59:45 -03:00
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
2026-08-15 17:58:47 -03:00
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
2026-08-08 17:59:45 -03:00
chmod 0600 /opt/data/.env
2026-08-08 23:47:50 -03:00
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
2026-08-08 17:59:45 -03:00
securityContext :
allowPrivilegeEscalation : false
runAsUser : 0
runAsGroup : 0
seccompProfile :
type : RuntimeDefault
volumeMounts :
- {name: home, mountPath : /opt/data}
2026-08-10 00:42:37 -03:00
- {name: home, mountPath: /legacy-home, readOnly : true }
- {name: workspace, mountPath : /opt/data/workspace}
2026-08-08 17:59:45 -03:00
- {name: config, mountPath: /config, readOnly : true }
resources :
requests : {cpu: 25m, memory : 32Mi}
limits : {cpu: 100m, memory : 64Mi}
2026-08-24 05:47:46 -03:00
- 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}
2026-08-15 17:58:47 -03:00
- 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}
2026-08-08 17:59:45 -03:00
- name : patch-auth
2026-08-11 20:22:26 -03:00
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
2026-08-08 17:59:45 -03:00
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}
2026-08-12 10:41:47 -03:00
- 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}
2026-08-16 15:59:09 -03:00
- 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
2026-08-17 14:39:21 +00:00
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
2026-08-16 15:59:09 -03:00
/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}
2026-08-15 17:58:47 -03:00
- 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}
2026-08-08 17:59:45 -03:00
containers :
- name : hermes
2026-08-11 20:22:26 -03:00
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
2026-08-08 17:59:45 -03:00
imagePullPolicy : IfNotPresent
2026-08-08 23:24:58 -03:00
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"
2026-08-11 01:14:46 -03:00
set -a
. /opt/data/.env
set +a
2026-08-15 17:58:47 -03:00
API_SERVER_KEY="$(tr -d '\r\n' < /runtime-access/chat-relay-key)"
test -n "${API_SERVER_KEY}"
export API_SERVER_KEY
2026-08-24 05:47:46 -03:00
export HUX_TENANT_SLOT="slot-${ordinal}"
2026-08-08 23:24:58 -03:00
exec /opt/hermes/.venv/bin/hermes gateway run
2026-08-08 17:59:45 -03:00
ports :
- {name: api, containerPort: 8642, protocol : TCP}
env :
2026-08-24 05:47:46 -03:00
- name : POD_NAME
valueFrom :
fieldRef :
fieldPath : metadata.name
2026-08-08 17:59:45 -03:00
- {name: HERMES_HOME, value : /opt/data}
2026-08-15 17:58:47 -03:00
- {name: HERMES_AUTH_FILE, value : /runtime-access/hermes-auth.json}
2026-08-08 17:59:45 -03:00
- {name: HOME, value : /opt/data/home}
2026-08-08 21:28:33 -03:00
- {name: TERMINAL_CWD, value : /opt/data/workspace}
- {name: HERMES_WRITE_SAFE_ROOT, value : /opt/data/workspace}
2026-08-23 17:20:01 +00:00
- {name: PATH, value : '/opt/data/home/.local/bin:/opt/hermes/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' }
2026-08-09 13:19:12 -03:00
- {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" }
2026-08-08 17:59:45 -03:00
- {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" }
2026-08-23 17:20:01 +00:00
- {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' }
2026-08-15 17:58:47 -03:00
- {name: HERMES_IMAGE_BROKER_KEY_FILE, value : /runtime-access/chat-relay-key}
2026-08-11 16:22:19 -03:00
- {name: HERMES_AUTO_ROUTER_PROFILE, value : chat}
2026-08-24 05:47:46 -03:00
- {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.
2026-08-24 10:46:54 -03:00
- {name: HUX_TOOL_ENFORCEMENT, value : "0" }
2026-08-24 05:47:46 -03:00
- {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" }
2026-08-08 17:59:45 -03:00
volumeMounts :
- {name: home, mountPath : /opt/data}
2026-08-10 00:42:37 -03:00
- {name: workspace, mountPath : /opt/data/workspace}
2026-08-15 17:58:47 -03:00
- {name: runtime-access, mountPath : /runtime-access}
2026-08-08 17:59:45 -03:00
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath : auth.py}
2026-08-16 15:59:09 -03:00
- {name: api-server-patch, mountPath: /opt/hermes/gateway/platforms/api_server.py, subPath : api_server.py}
2026-08-17 14:39:21 +00:00
- {name: coordinator, mountPath: /opt/hermes/gateway/platforms/telegram_continuity.py, subPath: migrate_telegram_api_sessions.py, readOnly : true }
2026-08-12 10:41:47 -03:00
- {name: stream-recovery-patch, mountPath: /opt/hermes/agent/conversation_loop.py, subPath : conversation_loop.py}
2026-08-15 17:58:47 -03:00
- {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}
2026-08-11 01:14:46 -03:00
- {name: image-plugin, mountPath: /opt/hermes/plugins/image_gen/atlas-broker, readOnly : true }
2026-08-11 05:20:18 -03:00
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly : true }
2026-08-24 05:47:46 -03:00
- {name: hux-runtime-plugin, mountPath: /opt/data/plugins/hux-runtime, readOnly : true }
2026-08-24 13:33:48 -03:00
- {name: cluster-read-plugin, mountPath: /opt/data/plugins/cluster-read, readOnly : true }
2026-08-24 05:47:46 -03:00
- {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 }
2026-08-08 17:59:45 -03:00
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 :
2026-08-23 10:53:57 -03:00
requests : {cpu: 250m, memory : 256Mi}
2026-08-08 17:59:45 -03:00
limits : {cpu : "1" , memory : 2Gi}
- name : webui
2026-08-25 04:43:23 +00:00
image : registry.bstein.dev/bstein/hermes-webui:git-5b1f8320729ebd1514ce83b7e695a02938e5cef8-build-36-release@sha256:dfdacf006af93add1bd811637c30f3d4fd16b6f7e24d81ddc56d2989aa31210a # {"$imagepolicy": "hermes:hermes-webui-release"}
2026-08-08 17:59:45 -03:00
imagePullPolicy : IfNotPresent
command : [ /bin/sh, -ec]
args :
- |
2026-08-15 17:58:47 -03:00
api_key="$(tr -d '\r\n' < /runtime-access/chat-relay-key)"
2026-08-08 17:59:45 -03:00
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 :
2026-08-24 05:47:46 -03:00
- name : POD_NAME
valueFrom :
fieldRef :
fieldPath : metadata.name
2026-08-08 17:59:45 -03:00
- {name: HERMES_HOME, value : /opt/data}
2026-08-15 17:58:47 -03:00
- {name: HERMES_AUTH_FILE, value : /runtime-access/hermes-auth.json}
2026-08-08 17:59:45 -03:00
- {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}
2026-08-23 17:20:01 +00:00
- {name: HERMES_WEBUI_GATEWAY_BASE_URL, value : 'http://127.0.0.1:8642' }
2026-08-08 17:59:45 -03:00
- {name: HERMES_WEBUI_GATEWAY_USE_RUNS_API, value : "true" }
- {name: HERMES_WEBUI_SKIP_ONBOARDING, value : "1" }
- {name: HERMES_WEBUI_SECURE, value : "1" }
2026-08-12 01:07:50 -03:00
- {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}
2026-08-23 17:20:01 +00:00
- {name: HERMES_WEBUI_ALLOWED_ORIGINS, value : 'https://chat.bstein.dev' }
2026-08-08 17:59:45 -03:00
- {name: HERMES_WEBUI_TRUST_FORWARDED_HOST, value : "1" }
- {name: HERMES_WEBUI_TRUST_FORWARDED_PROTO, value : "1" }
2026-08-11 16:22:19 -03:00
- {name: HERMES_ROUTER_PROFILE, value : chat}
2026-08-23 17:20:01 +00:00
- {name: HERMES_STT_URL, value : 'http://hermes-stt.hermes.svc.cluster.local:9000/v1/audio/transcriptions' }
2026-08-23 18:29:02 -03:00
- {name: HERMES_WEBUI_ATLAS_STT_STREAM_URL, value : 'http://hermes-stt.hermes.svc.cluster.local:9000/v1/audio/transcriptions/stream' }
2026-08-10 00:42:37 -03:00
- {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}" }
2026-08-23 17:20:01 +00:00
- {name: HERMES_WEBUI_ATLAS_TTS_URL, value : 'http://hermes-tts.hermes.svc.cluster.local:9001/v1/audio/speech' }
2026-08-23 18:29:02 -03:00
- {name: HERMES_WEBUI_ATLAS_TTS_STREAM_URL, value : 'http://hermes-tts.hermes.svc.cluster.local:9001/v1/audio/speech/stream' }
2026-08-24 05:47:46 -03:00
- {name: HUX_CONTEXT_KEY_FILE, value : /run/hermes-hux-context/context-key}
- {name: HUX_PROJECT_SOURCE, value : 'profile:default' }
2026-08-08 17:59:45 -03:00
volumeMounts :
- {name: home, mountPath : /opt/data}
2026-08-10 00:42:37 -03:00
- {name: workspace, mountPath : /opt/data/workspace}
2026-08-15 17:58:47 -03:00
- {name: runtime-access, mountPath: /runtime-access, readOnly : true }
2026-08-10 00:42:37 -03:00
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
2026-08-08 17:59:45 -03:00
- {name: tmp, mountPath : /tmp}
2026-08-24 05:47:46 -03:00
- {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 }
2026-08-08 17:59:45 -03:00
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 :
2026-08-23 10:53:57 -03:00
requests : {cpu: 100m, memory : 224Mi}
2026-08-08 17:59:45 -03:00
limits : {cpu: 750m, memory : 1Gi}
2026-08-24 05:47:46 -03:00
- name : hux
2026-08-25 04:43:23 +00:00
image : registry.bstein.dev/bstein/hermes-webui:git-5b1f8320729ebd1514ce83b7e695a02938e5cef8-build-36-release@sha256:dfdacf006af93add1bd811637c30f3d4fd16b6f7e24d81ddc56d2989aa31210a # {"$imagepolicy": "hermes:hermes-webui-release"}
2026-08-24 05:47:46 -03:00
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}
2026-08-24 06:13:57 -03:00
- {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' }
2026-08-24 05:47:46 -03:00
- {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}
2026-08-24 09:31:45 -03:00
- name : HUX_IMAGE_TAG
2026-08-25 04:43:23 +00:00
value : git-5b1f8320729ebd1514ce83b7e695a02938e5cef8-build-36-release # {"$imagepolicy": "hermes:hermes-webui-release:tag"}
2026-08-24 09:31:45 -03:00
- name : HUX_IMAGE_DIGEST
2026-08-25 04:43:23 +00:00
value : sha256:dfdacf006af93add1bd811637c30f3d4fd16b6f7e24d81ddc56d2989aa31210a # {"$imagepolicy": "hermes:hermes-webui-release:digest"}
2026-08-24 05:47:46 -03:00
- {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
2026-08-24 09:31:45 -03:00
timeoutSeconds : 5
2026-08-24 05:47:46 -03:00
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
2026-08-24 09:31:45 -03:00
timeoutSeconds : 5
2026-08-24 05:47:46 -03:00
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
2026-08-25 04:43:23 +00:00
image : registry.bstein.dev/bstein/hermes-webui:git-5b1f8320729ebd1514ce83b7e695a02938e5cef8-build-36-release@sha256:dfdacf006af93add1bd811637c30f3d4fd16b6f7e24d81ddc56d2989aa31210a # {"$imagepolicy": "hermes:hermes-webui-release"}
2026-08-24 05:47:46 -03:00
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}
2026-08-15 00:34:08 -03:00
- 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
2026-08-15 17:58:47 -03:00
- {name: HERMES_MEDIA_RELAY_KEY_FILE, value : /runtime-access/chat-relay-key}
2026-08-15 00:34:08 -03:00
volumeMounts :
- {name: home, mountPath: /opt/data, readOnly : true }
- {name: workspace, mountPath: /opt/data/workspace, readOnly : true }
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
2026-08-15 17:58:47 -03:00
- {name: runtime-access, mountPath: /runtime-access, readOnly : true }
2026-08-15 00:34:08 -03:00
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}
2026-08-08 17:59:45 -03:00
volumes :
2026-08-15 17:58:47 -03:00
- name : runtime-access
emptyDir :
medium : Memory
sizeLimit : 2Mi
2026-08-08 17:59:45 -03:00
- name : config
configMap :
name : hermes-chat-config
- name : coordinator
configMap :
name : hermes-coordinator
defaultMode : 0555
- name : auth-patch
emptyDir : {}
2026-08-16 15:59:09 -03:00
- name : api-server-patch
emptyDir : {}
2026-08-12 10:41:47 -03:00
- name : stream-recovery-patch
emptyDir : {}
2026-08-15 17:58:47 -03:00
- name : subprocess-secret-patch
emptyDir : {}
2026-08-11 05:20:18 -03:00
- name : auto-router-plugin
configMap :
name : hermes-auto-router-plugin
2026-08-11 01:14:46 -03:00
- name : image-plugin
configMap :
name : hermes-chat-image-plugin
2026-08-24 13:33:48 -03:00
- name : cluster-read-plugin
configMap :
name : hermes-cluster-read-plugin
2026-08-24 05:47:46 -03:00
- 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}
2026-08-08 17:59:45 -03:00
- name : tmp
emptyDir :
sizeLimit : 256Mi
2026-08-24 05:47:46 -03:00
- 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
2026-08-08 17:59:45 -03:00
volumeClaimTemplates :
- metadata :
name : home
labels :
app : hermes-chat-tenant
spec :
accessModes : [ ReadWriteOnce]
storageClassName : astreae
resources :
requests :
2026-08-08 21:28:33 -03:00
storage : 10Gi
2026-08-10 00:42:37 -03:00
- metadata :
name : workspace
labels :
app : hermes-chat-tenant
ai.bstein.dev/data : user-workspace
spec :
accessModes : [ ReadWriteMany]
storageClassName : astreae
resources :
requests :
storage : 10Gi