2026-08-08 17:59:45 -03:00
# services/hermes/agent-deployment.yaml
apiVersion : apps/v1
kind : Deployment
metadata :
name : hermes-agent
namespace : hermes
labels :
app : hermes-agent
spec :
replicas : 1
revisionHistoryLimit : 2
progressDeadlineSeconds : 2700
strategy :
type : Recreate
selector :
matchLabels :
app : hermes-agent
template :
metadata :
labels :
app : hermes-agent
annotations :
ai.bstein.dev/role : project-coordinator
2026-08-11 16:33:53 -03:00
ai.bstein.dev/router-wire-contract : ollama-numeric-keepalive
2026-08-10 16:31:06 -03:00
ai.bstein.dev/execution : Hermes Kanban with durable direct Codex and Claude Code CLI workers
2026-08-09 02:42:11 -03:00
ai.bstein.dev/model-policy : Jetson-assisted AUTO routing, low through xhigh, cross-provider fallback
2026-08-23 10:45:51 -03:00
ai.bstein.dev/placement : titan-08 rpi5; storage-backbone nodes excluded
2026-08-24 14:13:35 -03:00
ai.bstein.dev/config-rev : "20260824-claude-fable-quota"
2026-08-16 05:13:20 -03:00
prometheus.io/scrape : "true"
prometheus.io/path : /metrics
prometheus.io/port : "9010"
2026-08-08 17:59:45 -03:00
vault.hashicorp.com/agent-inject : "true"
vault.hashicorp.com/role : hermes-agent
2026-08-22 17:32:30 -03:00
vault.hashicorp.com/agent-inject-containers : stage-runtime-access
2026-08-15 17:58:47 -03:00
vault.hashicorp.com/agent-inject-secret-agent-api-key : kv/data/atlas/hermes/agent-tokens
vault.hashicorp.com/agent-inject-template-agent-api-key : |
2026-08-08 17:59:45 -03:00
{{- with secret "kv/data/atlas/hermes/agent-tokens" -}}
2026-08-15 17:58:47 -03:00
{{ .Data.data.agent_api_key }}
{{- end }}
vault.hashicorp.com/agent-inject-secret-claude-credentials : kv/data/atlas/hermes/agent-tokens
vault.hashicorp.com/agent-inject-template-claude-credentials : |
{{- with secret "kv/data/atlas/hermes/agent-tokens" -}}
{{ .Data.data.claude_credentials_json }}
{{- end }}
2026-08-22 17:32:30 -03:00
vault.hashicorp.com/agent-inject-secret-claude-oauth-token : kv/data/atlas/hermes/agent-tokens
vault.hashicorp.com/agent-inject-template-claude-oauth-token : |
{{- with secret "kv/data/atlas/hermes/agent-tokens" -}}
{{ .Data.data.claude_oauth_token }}
{{- end }}
2026-08-15 17:58:47 -03:00
vault.hashicorp.com/agent-inject-secret-codex-auth : kv/data/atlas/hermes/agent-tokens
vault.hashicorp.com/agent-inject-template-codex-auth : |
{{- with secret "kv/data/atlas/hermes/agent-tokens" -}}
{{ .Data.data.codex_auth_json }}
2026-08-08 17:59:45 -03:00
{{- end }}
2026-08-16 20:04:17 -03:00
vault.hashicorp.com/agent-inject-secret-jenkins-image-build-token : kv/data/atlas/hermes/developer-jenkins
vault.hashicorp.com/agent-inject-template-jenkins-image-build-token : |
{{- with secret "kv/data/atlas/hermes/developer-jenkins" -}}
{{ .Data.data.build_token }}
2026-08-08 17:59:45 -03:00
{{- end }}
2026-08-15 13:22:01 -03:00
vault.hashicorp.com/agent-inject-secret-node-ssh-private-key : kv/data/atlas/hermes/developer-ssh
vault.hashicorp.com/agent-inject-template-node-ssh-private-key : |
{{- with secret "kv/data/atlas/hermes/developer-ssh" -}}
{{ .Data.data.private_key }}
{{- end }}
2026-08-15 13:58:42 -03:00
vault.hashicorp.com/agent-inject-secret-node-ssh-config : kv/data/atlas/hermes/developer-ssh
vault.hashicorp.com/agent-inject-template-node-ssh-config : |
{{- with secret "kv/data/atlas/hermes/developer-ssh" -}}
{{ .Data.data.config }}
{{- end }}
vault.hashicorp.com/agent-inject-secret-node-ssh-known-hosts : kv/data/atlas/hermes/developer-ssh
vault.hashicorp.com/agent-inject-template-node-ssh-known-hosts : |
{{- with secret "kv/data/atlas/hermes/developer-ssh" -}}
{{ .Data.data.known_hosts }}
{{- end }}
2026-08-11 01:14:46 -03:00
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 }}
2026-08-10 21:08:42 -03:00
vault.hashicorp.com/agent-inject-secret-oidc-config : kv/data/atlas/hermes/agent-oidc
vault.hashicorp.com/agent-inject-template-oidc-config : |
{{- with secret "kv/data/atlas/hermes/agent-oidc" -}}
client_id = "{{ .Data.data.client_id }}"
client_secret = "{{ .Data.data.client_secret }}"
cookie_secret = "{{ .Data.data.cookie_secret }}"
{{- end -}}
2026-08-08 17:59:45 -03:00
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-agent
2026-08-16 20:53:22 +00:00
enableServiceLinks : false
2026-08-08 17:59:45 -03:00
automountServiceAccountToken : true
securityContext :
seccompProfile :
type : RuntimeDefault
affinity :
nodeAffinity :
requiredDuringSchedulingIgnoredDuringExecution :
nodeSelectorTerms :
- matchExpressions :
- key : kubernetes.io/arch
operator : In
2026-08-23 10:45:51 -03:00
values : [ arm64]
2026-08-08 17:59:45 -03:00
- key : node-role.kubernetes.io/worker
operator : In
values : [ "true" ]
- key : kubernetes.io/hostname
2026-08-25 09:29:21 -03:00
operator : NotIn
values : [ titan-04, titan-06, titan-08, titan-13, titan-14, titan-17, titan-18, titan-19]
2026-08-08 17:59:45 -03:00
preferredDuringSchedulingIgnoredDuringExecution :
- weight : 100
preference :
matchExpressions :
2026-08-23 10:45:51 -03:00
- key : hardware
2026-08-08 17:59:45 -03:00
operator : In
2026-08-23 10:45:51 -03:00
values : [ rpi5]
2026-08-08 17:59:45 -03:00
initContainers :
- name : init-config
image : busybox:1.37
imagePullPolicy : IfNotPresent
command :
- sh
- -c
- |
set -eu
env_file=/opt/data/.env
2026-08-16 01:10:47 -03:00
profile_file=/opt/data/home/.profile
2026-08-08 17:59:45 -03:00
mkdir -p \
/opt/data/home/.claude \
/opt/data/home/.codex \
2026-08-09 14:38:23 -03:00
/opt/data/home/.kube \
2026-08-15 13:22:01 -03:00
/opt/data/home/.ssh \
2026-08-10 16:31:06 -03:00
/opt/data/cli-lanes \
2026-08-08 17:59:45 -03:00
/opt/data/logs \
2026-08-12 23:08:21 -03:00
/opt/data/provider-health \
2026-08-08 17:59:45 -03:00
/opt/data/tools/bin \
/opt/data/workspace/coordinator \
/opt/data/workspace/projects \
/opt/data/workspace/skills
2026-08-09 01:35:38 -03:00
if [ ! -e /opt/data/home/.hermes ]; then
ln -s /opt/data /opt/data/home/.hermes
fi
2026-08-15 14:45:27 -03:00
# OpenSSH resolves ~/.ssh from the account's passwd home
# (/opt/data), not the HOME override used by Hermes. Keep one
# Vault-populated directory visible through both paths.
if [ ! -e /opt/data/.ssh ] && [ ! -L /opt/data/.ssh ]; then
ln -s home/.ssh /opt/data/.ssh
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
cp /config/START-HERE.md /opt/data/workspace/START-HERE.md
2026-08-15 17:58:47 -03:00
rm -f \
/opt/data/auth.json \
/opt/data/home/.claude/.credentials.json \
/opt/data/home/.codex/auth.json \
2026-08-15 13:22:01 -03:00
/opt/data/home/.ssh/config \
2026-08-15 17:58:47 -03:00
/opt/data/home/.ssh/known_hosts \
/opt/data/home/.ssh/id_ed25519_atlas_nodes
ln -s /runtime-access/node-ssh-config /opt/data/home/.ssh/config
ln -s /runtime-access/node-ssh-known-hosts /opt/data/home/.ssh/known_hosts
ln -s /runtime-access/node-ssh-private-key /opt/data/home/.ssh/id_ed25519_atlas_nodes
2026-08-15 14:45:27 -03:00
chmod 0700 /opt/data/home/.ssh
2026-08-08 17:59:45 -03:00
touch "${env_file}"
upsert_env() {
key="$1"
value="$2"
{ grep -v "^${key}=" "${env_file}" || true; printf '%s=%s\n' "${key}" "${value}"; } > "${env_file}.tmp"
mv "${env_file}.tmp" "${env_file}"
}
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}=" "${env_file}" > "${env_file}.tmp" || true
mv "${env_file}.tmp" "${env_file}"
done
for profile_env in /opt/data/profiles/*/.env; do
[ -f "${profile_env}" ] || continue
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}=" "${profile_env}" > "${profile_env}.tmp" || true
mv "${profile_env}.tmp" "${profile_env}"
done
chmod 0600 "${profile_env}"
chown 10000:10000 "${profile_env}"
done
2026-08-08 17:59:45 -03:00
upsert_env GIT_TERMINAL_PROMPT 0
2026-08-16 20:41:14 -03:00
upsert_env HERMES_SCM_BROKER_URL http://hermes-scm-broker.hermes-scm.svc.cluster.local:9081
2026-08-16 07:59:35 -03:00
upsert_env JENKINS_BASE_URL http://jenkins.jenkins.svc.cluster.local:8080
upsert_env ARIADNE_BASE_URL http://ariadne.maintenance.svc.cluster.local
upsert_env VICTORIA_METRICS_URL http://victoria-metrics-single-server.monitoring.svc.cluster.local:8428
upsert_env GRAFANA_BASE_URL https://metrics.bstein.dev
2026-08-08 17:59:45 -03:00
chmod 0600 "${env_file}"
2026-08-16 01:10:47 -03:00
touch "${profile_file}"
if ! grep -qxF '# Hermes managed operator PATH.' "${profile_file}"; then
printf '%s\n' \
'' \
'# Hermes managed operator PATH.' \
'case ":${PATH}:" in' \
' *":/opt/data/tools/bin:"*) ;;' \
' *) PATH="/opt/coordinator:/opt/data/tools/bin:/opt/hermes/.venv/bin:/opt/hermes/bin:${PATH}" ;;' \
'esac' \
'export PATH' >> "${profile_file}"
fi
chmod 0644 "${profile_file}"
2026-08-10 23:28:35 -03:00
# Existing owner data is already written as uid/gid 10000. A
# recursive chown made every routine rollout walk the full 20Gi
# workspace while the dashboard had no endpoint. Own only the
# paths this init container creates or updates.
chown 10000:10000 \
/opt/data \
/opt/data/home \
/opt/data/home/.claude \
/opt/data/home/.codex \
/opt/data/home/.kube \
2026-08-15 13:22:01 -03:00
/opt/data/home/.ssh \
2026-08-10 23:28:35 -03:00
/opt/data/cli-lanes \
/opt/data/logs \
2026-08-12 23:08:21 -03:00
/opt/data/provider-health \
2026-08-10 23:28:35 -03:00
/opt/data/tools \
/opt/data/tools/bin \
/opt/data/workspace \
/opt/data/workspace/coordinator \
/opt/data/workspace/projects \
/opt/data/workspace/skills \
/opt/data/config.yaml \
/opt/data/SOUL.md \
/opt/data/workspace/AGENTS.md \
/opt/data/workspace/START-HERE.md \
2026-08-16 01:10:47 -03:00
"${profile_file}" \
2026-08-10 23:28:35 -03:00
"${env_file}"
2026-08-08 17:59:45 -03:00
securityContext :
allowPrivilegeEscalation : false
runAsUser : 0
runAsGroup : 0
seccompProfile :
type : RuntimeDefault
volumeMounts :
- name : home
mountPath : /opt/data
- name : config
mountPath : /config
readOnly : true
resources :
requests : {cpu: 25m, memory : 32Mi}
limits : {cpu: 100m, memory : 64Mi}
2026-08-24 13:54:27 -03:00
# Worker HUX identity provisioning. Mirrors the chat tenants'
# init-hux-runtime, adapted to the single-replica Deployment: the
# subtree name is the fixed literal "hux" (Deployment pod names churn,
# so ${HOSTNAME} would break identity persistence across restarts) on
# the durable hermes-agent-home claim, and the slot is pinned to
# slot-100 — outside the chat tenants' slot-0..slot-3 range. Only a
# worker transport key is provisioned: the Worker has no relay or
# human browser surface, so relay trust stays fail-closed by absence.
- name : init-hux-runtime
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy : IfNotPresent
command : [ /bin/sh, -ec]
args :
- |
umask 077
worker_root="/hux-data/hux"
export HUX_INIT_ROOT="${worker_root}"
mkdir -p \
"${worker_root}/binding" \
"${worker_root}/context" \
"${worker_root}/store" \
/hux-worker
chown 10000:10000 \
"${worker_root}" \
"${worker_root}/binding" \
"${worker_root}/context" \
"${worker_root}/store" \
/hux-worker
chmod 0700 \
"${worker_root}" \
"${worker_root}/binding" \
"${worker_root}/context" \
"${worker_root}/store" \
/hux-worker
if [ ! -e "${worker_root}/context/context-key" ]; then
dd if=/dev/urandom of="${worker_root}/context/.context-key.tmp" bs=32 count=1 2>/dev/null
chown 10000:10000 "${worker_root}/context/.context-key.tmp"
chmod 0600 "${worker_root}/context/.context-key.tmp"
mv "${worker_root}/context/.context-key.tmp" "${worker_root}/context/context-key"
fi
test "$(wc -c < "${worker_root}/context/context-key")" -eq 32
chown 10000:10000 "${worker_root}/context/context-key"
chmod 0600 "${worker_root}/context/context-key"
HUX_INIT_SLOT="slot-100" \
/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 "${worker_root}/context/redaction-canary" ]; then
dd if=/dev/urandom bs=32 count=1 2>/dev/null \
| sha256sum | cut -d ' ' -f 1 \
> "${worker_root}/context/.redaction-canary.tmp"
chown 10000:10000 "${worker_root}/context/.redaction-canary.tmp"
chmod 0400 "${worker_root}/context/.redaction-canary.tmp"
mv "${worker_root}/context/.redaction-canary.tmp" "${worker_root}/context/redaction-canary"
fi
target=/hux-worker/worker-key
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}"
securityContext :
allowPrivilegeEscalation : false
capabilities :
drop : [ ALL]
add : [ CHOWN, DAC_OVERRIDE, FOWNER]
runAsUser : 0
runAsGroup : 0
seccompProfile :
type : RuntimeDefault
volumeMounts :
- {name: home, mountPath : /hux-data}
- {name: hux-worker-key, mountPath : /hux-worker}
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
- agent
securityContext :
allowPrivilegeEscalation : false
runAsUser : 0
runAsGroup : 0
seccompProfile :
type : RuntimeDefault
volumeMounts :
2026-08-22 17:32:30 -03:00
- {name: claude-oauth-access, mountPath : /claude-oauth-access}
2026-08-15 17:58:47 -03:00
- {name: home, mountPath : /opt/data}
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
2026-08-22 17:32:30 -03:00
- {name: oauth2-config, mountPath : /oauth2-config}
2026-08-15 17:58:47 -03:00
- {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 : install-agent-tools
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 :
- sh
- -c
- |
set -eu
tools=/opt/data/tools
mkdir -p "${tools}/bin"
2026-08-08 22:05:39 -03:00
ttyd_version="$("${tools}/bin/ttyd" --version 2>/dev/null || true)"
case "${ttyd_version}" in *1.7.7*) ttyd_ready=1 ;; *) ttyd_ready=0 ;; esac
if [ "${ttyd_ready}" != "1" ]; then
curl -fsSL -o "${tools}/bin/ttyd.tmp" https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.aarch64
printf '%s %s\n' b38acadd89d1d396a0f5649aa52c539edbad07f4bc7348b27b4f4b7219dd4165 "${tools}/bin/ttyd.tmp" | sha256sum -c -
chmod 0755 "${tools}/bin/ttyd.tmp"
mv "${tools}/bin/ttyd.tmp" "${tools}/bin/ttyd"
fi
2026-08-08 17:59:45 -03:00
if [ ! -f "${tools}/.cli-versions-0.147.0-2.1.226" ]; then
npm install --global --omit=dev --no-audit --no-fund --prefix "${tools}" \
@openai/codex@0.147.0 \
@anthropic-ai/claude-code@2.1.226
touch "${tools}/.cli-versions-0.147.0-2.1.226"
fi
2026-08-09 14:38:23 -03:00
kubectl_version="$("${tools}/bin/kubectl" version --client --output=json 2>/dev/null || true)"
case "${kubectl_version}" in *\"gitVersion\":\"v1.33.3\"*) kubectl_ready=1 ;; *) kubectl_ready=0 ;; esac
if [ "${kubectl_ready}" != "1" ]; then
curl -fsSL -o "${tools}/bin/kubectl.tmp" https://dl.k8s.io/release/v1.33.3/bin/linux/arm64/kubectl
printf '%s %s\n' 3d514dbae5dc8c09f773df0ef0f5d449dfad05b3aca5c96b13565f886df345fd "${tools}/bin/kubectl.tmp" | sha256sum -c -
chmod 0755 "${tools}/bin/kubectl.tmp"
mv "${tools}/bin/kubectl.tmp" "${tools}/bin/kubectl"
fi
2026-08-10 17:05:14 -03:00
/bin/sh /opt/coordinator/install_agent_tools.sh
2026-08-16 07:59:35 -03:00
HOME=/opt/data/home git config --global user.name "Hermes Agent"
HOME=/opt/data/home git config --global user.email "hermes@bstein.dev"
2026-08-08 17:59:45 -03:00
securityContext :
allowPrivilegeEscalation : false
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- name : home
mountPath : /opt/data
2026-08-10 17:05:14 -03:00
- name : coordinator
mountPath : /opt/coordinator
readOnly : true
2026-08-08 17:59:45 -03:00
resources :
requests : {cpu: 100m, memory : 256Mi}
limits : {cpu : "1" , memory : 1Gi}
2026-08-15 03:35:26 -03:00
- name : repair-cassandra-kanban
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy : IfNotPresent
command :
- /opt/hermes/.venv/bin/python
- /opt/coordinator/repair_cassandra_kanban.py
- --database
- /opt/data/kanban/boards/cassandra/kanban.db
securityContext :
allowPrivilegeEscalation : false
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- {name: home, mountPath : /opt/data}
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
resources :
requests : {cpu: 25m, memory : 64Mi}
limits : {cpu: 250m, memory : 256Mi}
2026-08-15 07:07:21 -03:00
- name : recover-cassandra-workers
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy : IfNotPresent
command :
- /opt/hermes/.venv/bin/python
- /opt/coordinator/recover_cassandra_workers.py
securityContext :
allowPrivilegeEscalation : false
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- {name: home, mountPath : /opt/data}
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
resources :
requests : {cpu: 25m, memory : 64Mi}
limits : {cpu: 250m, memory : 256Mi}
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-10 21:08:42 -03:00
- name : patch-tui-gateway
2026-08-11 20:22:26 -03:00
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
2026-08-10 21:08:42 -03:00
imagePullPolicy : IfNotPresent
command :
- /opt/hermes/.venv/bin/python
- /opt/coordinator/patch_tui_gateway.py
- /opt/hermes/tui_gateway/server.py
- /patched/server.py
securityContext :
allowPrivilegeEscalation : false
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- name : coordinator
mountPath : /opt/coordinator
readOnly : true
- name : tui-gateway-patch
mountPath : /patched
resources :
requests : {cpu: 25m, memory : 64Mi}
limits : {cpu: 100m, memory : 128Mi}
2026-08-12 23:08:21 -03:00
- name : patch-api-server-sessions
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy : IfNotPresent
command :
- /opt/hermes/.venv/bin/python
- /opt/coordinator/patch_api_server_sessions.py
- /opt/hermes/gateway/platforms/api_server.py
- /patched/api_server.py
securityContext :
allowPrivilegeEscalation : false
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- 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-web-session-activity
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy : IfNotPresent
command :
- /opt/hermes/.venv/bin/python
- /opt/coordinator/patch_web_session_activity.py
- /opt/hermes/hermes_cli/web_server.py
- /patched/web_server.py
securityContext :
allowPrivilegeEscalation : false
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- name : coordinator
mountPath : /opt/coordinator
readOnly : true
- name : web-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}
2026-08-10 22:39:51 -03:00
- name : patch-codex-runtime
2026-08-11 20:22:26 -03:00
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
2026-08-10 22:39:51 -03:00
imagePullPolicy : IfNotPresent
command :
- /opt/hermes/.venv/bin/python
- /opt/coordinator/patch_codex_runtime.py
- /opt/hermes/hermes_cli/runtime_provider.py
- /patched/runtime_provider.py
- /opt/hermes/agent/transports/codex_app_server_session.py
- /patched/codex_app_server_session.py
- /opt/hermes/agent/codex_runtime.py
- /patched/codex_runtime.py
2026-08-10 23:19:42 -03:00
- /opt/hermes/agent/chat_completion_helpers.py
- /patched/chat_completion_helpers.py
- /opt/hermes/agent/conversation_loop.py
- /patched/conversation_loop.py
2026-08-11 00:41:08 -03:00
- /opt/hermes/agent/auxiliary_client.py
- /patched/auxiliary_client.py
2026-08-10 22:39:51 -03:00
securityContext :
allowPrivilegeEscalation : false
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- name : coordinator
mountPath : /opt/coordinator
readOnly : true
- name : codex-runtime-patch
mountPath : /patched
resources :
requests : {cpu: 25m, memory : 64Mi}
limits : {cpu: 100m, memory : 128Mi}
2026-08-08 17:59:45 -03:00
- name : bootstrap-coordinator
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/hermes_coordinator.py
- --once
env :
- {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-15 17:58:47 -03:00
- {name: CODEX_HOME, value : /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value : /runtime-access/claude}
2026-08-08 17:59:45 -03:00
- {name: PYTHONPATH, value : /opt/hermes}
2026-08-11 20:22:26 -03:00
- {name: HERMES_ROUTING_CATALOG_PATH, value : /routing-catalog/catalog.json}
2026-08-12 08:30:01 -03:00
- {name: HERMES_CASSANDRA_ACTIVE_WORKTREE, value : /opt/data/workspace/projects/cassandra-hermes-v69}
2026-08-24 17:22:30 +00:00
- {name: PATH, value : '/opt/coordinator:/opt/data/tools/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin' }
2026-08-08 17:59:45 -03:00
securityContext :
allowPrivilegeEscalation : false
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
2026-08-22 17:32:30 -03:00
- {name: claude-oauth-access, mountPath: /claude-oauth-access, readOnly : true }
2026-08-08 17:59:45 -03:00
- {name: home, mountPath : /opt/data}
2026-08-15 17:58:47 -03:00
- {name: runtime-access, mountPath : /runtime-access}
2026-08-08 17:59:45 -03:00
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath : auth.py}
2026-08-11 20:22:26 -03:00
- {name: routing-catalog, mountPath : /routing-catalog}
2026-08-08 17:59:45 -03:00
resources :
requests : {cpu: 50m, memory : 128Mi}
limits : {cpu: 500m, memory : 512Mi}
2026-08-10 16:31:06 -03:00
- name : configure-agent-clients
2026-08-11 20:22:26 -03:00
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
2026-08-09 01:35:38 -03:00
imagePullPolicy : IfNotPresent
command :
- sh
- -ec
- |
2026-08-10 22:39:51 -03:00
set -a
. /opt/data/.env
set +a
2026-08-10 02:15:34 -03:00
/opt/hermes/.venv/bin/python /opt/coordinator/configure_agent_clients.py
2026-08-10 16:31:06 -03:00
/opt/hermes/.venv/bin/python /opt/coordinator/migrate_herdr_state.py
2026-08-12 23:08:21 -03:00
/opt/hermes/.venv/bin/python /opt/coordinator/migrate_api_session_lineage.py
2026-08-10 23:44:56 -03:00
# Client configuration restores the persisted Codex CLI login.
# Refresh routing afterwards so AUTO sees the app-server lane on
# the first request instead of waiting for the hourly steward.
/opt/hermes/.venv/bin/python /opt/coordinator/hermes_coordinator.py --once
2026-08-09 01:35:38 -03:00
env :
- {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-09 01:35:38 -03:00
- {name: HOME, value : /opt/data/home}
2026-08-15 17:58:47 -03:00
- {name: CODEX_HOME, value : /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value : /runtime-access/claude}
2026-08-10 23:44:56 -03:00
- {name: PYTHONPATH, value : /opt/hermes}
2026-08-11 20:22:26 -03:00
- {name: HERMES_ROUTING_CATALOG_PATH, value : /routing-catalog/catalog.json}
2026-08-12 08:30:01 -03:00
- {name: HERMES_CASSANDRA_ACTIVE_WORKTREE, value : /opt/data/workspace/projects/cassandra-hermes-v69}
2026-08-24 17:22:30 +00:00
- {name: PATH, value : '/opt/coordinator:/opt/data/tools/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin' }
2026-08-09 01:35:38 -03:00
securityContext :
allowPrivilegeEscalation : false
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
2026-08-22 17:32:30 -03:00
- {name: claude-oauth-access, mountPath: /claude-oauth-access, readOnly : true }
2026-08-09 01:35:38 -03:00
- {name: home, mountPath : /opt/data}
2026-08-15 17:58:47 -03:00
- {name: runtime-access, mountPath : /runtime-access}
2026-08-10 02:15:34 -03:00
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
2026-08-10 23:44:56 -03:00
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath : auth.py}
2026-08-11 20:22:26 -03:00
- {name: routing-catalog, mountPath : /routing-catalog}
2026-08-09 01:35:38 -03:00
resources :
2026-08-12 23:17:47 -03:00
requests : {cpu: 25m, memory : 128Mi}
limits : {cpu: 500m, memory : 512Mi}
2026-08-09 15:52:06 -03:00
- name : prepare-ttyd-index
2026-08-11 20:22:26 -03:00
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
2026-08-09 15:52:06 -03:00
imagePullPolicy : IfNotPresent
command :
- /opt/hermes/.venv/bin/python
- /opt/coordinator/patch_ttyd_index.py
- /opt/data/tools/bin/ttyd
- /ttyd-index/index.html
securityContext :
allowPrivilegeEscalation : false
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- {name: home, mountPath : /opt/data}
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
- {name: ttyd-index, mountPath : /ttyd-index}
resources :
requests : {cpu: 25m, memory : 32Mi}
limits : {cpu: 250m, 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-15 03:44:00 -03:00
command : [ /bin/sh, -ec]
args :
- |
set -a
. /opt/data/.env
set +a
2026-08-15 17:58:47 -03:00
API_SERVER_KEY="$(tr -d '\r\n' < /runtime-access/agent-api-key)"
test -n "${API_SERVER_KEY}"
export API_SERVER_KEY
2026-08-15 03:44:00 -03:00
exec /init /opt/hermes/docker/main-wrapper.sh gateway run
2026-08-08 17:59:45 -03:00
ports :
2026-08-08 19:18:33 -03:00
- {name: api, containerPort: 8642, protocol : TCP}
2026-08-10 21:27:24 -03:00
- {name: dashboard, containerPort: 9119, protocol : TCP}
2026-08-08 17:59:45 -03:00
env :
- {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-15 17:58:47 -03:00
- {name: CODEX_HOME, value : /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value : /runtime-access/claude}
2026-08-22 17:32:30 -03:00
- {name: HERMES_CLAUDE_BIN, value : /opt/coordinator/claude}
2026-08-09 14:38:23 -03:00
- {name: KUBECONFIG, value : /opt/data/home/.kube/config}
2026-08-24 17:22:30 +00:00
- {name: PATH, value : '/opt/coordinator:/opt/data/tools/bin:/opt/data/home/.local/bin:/opt/hermes/.venv/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-10 21:27:24 -03:00
- {name: HERMES_DASHBOARD, value : "1" }
- {name: HERMES_DASHBOARD_HOST, value : 127.0 .0 .1 }
- {name: HERMES_DASHBOARD_PORT, value : "9119" }
- {name: HERMES_DASHBOARD_FILES_ROOT, value : /opt/data/workspace}
2026-08-24 17:22:30 +00:00
- {name: HERMES_DASHBOARD_PUBLIC_URL, value : 'https://worker.bstein.dev' }
2026-08-10 21:27:24 -03:00
- {name: HERMES_TUI_AGENT_INIT_TIMEOUT_S, value : "180" }
2026-08-15 13:58:42 -03:00
- {name: HERMES_STREAM_STALE_TIMEOUT, value : "600" }
- {name: HERMES_API_CALL_STALE_TIMEOUT, value : "600" }
2026-08-08 19:18:33 -03:00
- {name: API_SERVER_ENABLED, value : "true" }
- {name: API_SERVER_HOST, value : 0.0 .0 .0 }
- {name: API_SERVER_PORT, value : "8642" }
refactor(hermes): rename the owner agent host to worker.bstein.dev
Introduce worker.bstein.dev as the canonical hostname for the owner-only
Hermes coordinator, previously agent.hermes.bstein.dev.
The rename is additive, matching the shape #38 restored for chat and triage.
CoreDNS, both agent Ingresses and the hermes-sites certificate now serve BOTH
names, so merging this cannot take away the endpoint the operator uses to
reach the coordinator. Retiring agent.hermes.bstein.dev is a separate,
separately scheduled change. No redirect middleware is added.
What switches to the new host:
- HERMES_DASHBOARD_PUBLIC_URL and the oauth2-proxy --redirect-url
- the Keycloak agent proxy rootUrl
- operator docs, skills, the ZAP baseline target and the triage monitor default
What stays dual-homed until retirement:
- CoreDNS hosts entry, both agent Ingress rules, certificate SANs
- API_SERVER_CORS_ORIGINS (now a comma-separated pair)
- the Keycloak redirect URIs, web origins and post-logout origins, so a
rollback only needs the oauth2-proxy --redirect-url reverted and does not
require re-running the ensure job
The agent client passes its legacy origin through the optional fourth argument
#38 added to ensure_proxy_client, so no second mechanism is introduced. The
immutable ensure Job goes -11 -> -12 because #38 already consumed -11 and that
run has completed; without a further bump this change would never be applied.
Login on the new host fails until the -12 Job completes.
Because the session and CSRF cookies use the __Host- prefix they are bound to
one origin, so a fresh login must start on worker.bstein.dev and existing
sessions do not carry over -- re-login is required after rollout.
#38's public-host continuity test now covers the agent proxy's dual origins
rather than asserting the agent surface was untouched by the rename.
Knowledge catalogs and diagrams regenerated with `make knowledge`.
2026-08-21 10:29:46 +00:00
# Comma-separated; the legacy origin stays until agent.hermes.bstein.dev is retired.
- {name: API_SERVER_CORS_ORIGINS, value : "https://worker.bstein.dev,https://agent.hermes.bstein.dev" }
2026-08-10 16:31:06 -03:00
- {name: HERMES_MEDIA_DELIVERY_STRICT, value : "1" }
- {name: HERMES_MEDIA_ALLOW_DIRS, value : /opt/data/workspace}
2026-08-11 16:22:19 -03:00
- {name: HERMES_AUTO_ROUTER_PROFILE, value : agent}
2026-08-12 23:48:06 -03:00
- {name: HERMES_ROUTING_CATALOG_PATH, value : /routing-catalog/catalog.json}
- {name: HERMES_CODEX_HEALTH_PATH, value : /opt/data/provider-health/codex.json}
- {name: HERMES_CLAUDE_HEALTH_PATH, value : /opt/data/provider-health/claude.json}
2026-08-15 17:58:47 -03:00
- {name: HERMES_IMAGE_BROKER_KEY_FILE, value : /runtime-access/chat-relay-key}
2026-08-24 13:54:27 -03:00
# Worker HUX hook wiring (observe-only). The hux-runtime plugin is
# mounted but stays OUT of plugins.enabled until the activation
# commit (docs/hux/WORKER-PLAN.md), so this environment is inert
# until that deliberate flip. Enforcement is pinned to 0: the hook
# observes and emits, it never gates worker tools in this stage.
- {name: HUX_BASE_URL, value : 'http://127.0.0.1:8790' }
- {name: HUX_RUNTIME_ENABLED, value : "1" }
- {name: HUX_TOOL_ENFORCEMENT, value : "0" }
- {name: HUX_TENANT_SLOT, value : slot-100}
- {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 :
2026-08-22 17:32:30 -03:00
- {name: claude-oauth-access, mountPath: /claude-oauth-access, readOnly : true }
2026-08-08 17:59:45 -03:00
- {name: home, mountPath : /opt/data}
2026-08-15 17:58:47 -03:00
- {name: runtime-access, mountPath : /runtime-access}
2026-08-08 17:59:45 -03:00
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath : auth.py}
2026-08-15 17:58:47 -03:00
- {name: web-server-patch, mountPath: /opt/hermes/hermes_cli/web_server.py, subPath : web_server.py}
2026-08-10 22:39:51 -03:00
- {name: codex-runtime-patch, mountPath: /opt/hermes/hermes_cli/runtime_provider.py, subPath : runtime_provider.py}
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/transports/codex_app_server_session.py, subPath : codex_app_server_session.py}
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/codex_runtime.py, subPath : codex_runtime.py}
2026-08-10 23:19:42 -03:00
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/chat_completion_helpers.py, subPath : chat_completion_helpers.py}
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/conversation_loop.py, subPath : conversation_loop.py}
2026-08-11 00:41:08 -03:00
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/auxiliary_client.py, subPath : auxiliary_client.py}
2026-08-10 21:27:24 -03:00
- {name: tui-gateway-patch, mountPath: /opt/hermes/tui_gateway/server.py, subPath : server.py}
2026-08-12 23:08:21 -03:00
- {name: api-server-patch, mountPath: /opt/hermes/gateway/platforms/api_server.py, subPath : api_server.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-09 14:38:23 -03:00
- {name: kubeconfig, mountPath: /opt/data/home/.kube/config, subPath: config, readOnly : true }
2026-08-09 02:42:11 -03:00
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly : true }
2026-08-16 19:54:41 -03:00
- {name: atlas-pr-skill, mountPath: /opt/data/workspace/skills/manage-atlas-pull-requests, readOnly : true }
2026-08-16 20:41:14 -03:00
- {name: scm-boundary, mountPath: /opt/scm, readOnly : true }
2026-08-12 23:48:06 -03:00
- {name: routing-catalog, mountPath: /routing-catalog, readOnly : true }
2026-08-10 21:27:24 -03:00
- {name: tmp, mountPath : /tmp}
2026-08-24 13:54:27 -03:00
- {name: hux-runtime-plugin, mountPath: /opt/data/plugins/hux-runtime, readOnly : true }
- {name: hux-worker-key, mountPath: /run/hermes-hux-worker, readOnly : true }
- {name: home, mountPath: /run/hermes-hux-context, subPath: hux/context, readOnly : true }
- {name: home, mountPath: /run/hermes-hux-subject, subPath: hux/binding, readOnly : true }
2026-08-08 19:05:17 -03:00
startupProbe :
2026-08-10 21:38:39 -03:00
exec :
2026-08-24 17:22:30 +00:00
command : [ curl, -fsS, 'http://127.0.0.1:9119/api/status']
2026-08-08 19:05:17 -03:00
periodSeconds : 10
timeoutSeconds : 5
failureThreshold : 60
2026-08-08 17:59:45 -03:00
readinessProbe :
2026-08-10 21:38:39 -03:00
exec :
2026-08-24 17:22:30 +00:00
command : [ curl, -fsS, 'http://127.0.0.1:9119/api/status']
2026-08-08 17:59:45 -03:00
initialDelaySeconds : 30
periodSeconds : 10
timeoutSeconds : 5
livenessProbe :
2026-08-10 21:38:39 -03:00
exec :
2026-08-24 17:22:30 +00:00
command : [ curl, -fsS, 'http://127.0.0.1:9119/api/status']
2026-08-08 17:59:45 -03:00
initialDelaySeconds : 90
periodSeconds : 30
2026-08-25 09:29:21 -03:00
timeoutSeconds : 15
failureThreshold : 5
2026-08-08 17:59:45 -03:00
securityContext :
allowPrivilegeEscalation : false
2026-08-10 21:27:24 -03:00
runAsUser : 0
runAsGroup : 0
2026-08-08 19:18:33 -03:00
seccompProfile :
type : RuntimeDefault
resources :
2026-08-22 15:55:30 -03:00
requests : {cpu: 125m, memory : 320Mi}
2026-08-10 21:27:24 -03:00
limits : {cpu : "3" , memory : 6Gi}
2026-08-10 21:08:42 -03:00
- name : oauth2-proxy
image : quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
imagePullPolicy : IfNotPresent
args :
- --provider=oidc
2026-08-22 17:32:30 -03:00
- --config=/oauth2-config/oidc-config
refactor(hermes): rename the owner agent host to worker.bstein.dev
Introduce worker.bstein.dev as the canonical hostname for the owner-only
Hermes coordinator, previously agent.hermes.bstein.dev.
The rename is additive, matching the shape #38 restored for chat and triage.
CoreDNS, both agent Ingresses and the hermes-sites certificate now serve BOTH
names, so merging this cannot take away the endpoint the operator uses to
reach the coordinator. Retiring agent.hermes.bstein.dev is a separate,
separately scheduled change. No redirect middleware is added.
What switches to the new host:
- HERMES_DASHBOARD_PUBLIC_URL and the oauth2-proxy --redirect-url
- the Keycloak agent proxy rootUrl
- operator docs, skills, the ZAP baseline target and the triage monitor default
What stays dual-homed until retirement:
- CoreDNS hosts entry, both agent Ingress rules, certificate SANs
- API_SERVER_CORS_ORIGINS (now a comma-separated pair)
- the Keycloak redirect URIs, web origins and post-logout origins, so a
rollback only needs the oauth2-proxy --redirect-url reverted and does not
require re-running the ensure job
The agent client passes its legacy origin through the optional fourth argument
#38 added to ensure_proxy_client, so no second mechanism is introduced. The
immutable ensure Job goes -11 -> -12 because #38 already consumed -11 and that
run has completed; without a further bump this change would never be applied.
Login on the new host fails until the -12 Job completes.
Because the session and CSRF cookies use the __Host- prefix they are bound to
one origin, so a fresh login must start on worker.bstein.dev and existing
sessions do not carry over -- re-login is required after rollout.
#38's public-host continuity test now covers the agent proxy's dual origins
rather than asserting the agent surface was untouched by the rename.
Knowledge catalogs and diagrams regenerated with `make knowledge`.
2026-08-21 10:29:46 +00:00
# Single canonical callback. The __Host- cookie prefix below binds both the
# session and the CSRF cookie to one host, so a fresh login must start on
# worker.bstein.dev; agent.hermes.bstein.dev keeps serving already
# authenticated sessions until it is retired separately.
- --redirect-url=https://worker.bstein.dev/oauth2/callback
2026-08-10 21:08:42 -03:00
- --oidc-issuer-url=https://sso.bstein.dev/realms/atlas
- --user-id-claim=sub
- --oidc-groups-claim=groups
- --allowed-group=/hermes-owner
- --code-challenge-method=S256
- --scope=openid profile email
- --email-domain=*
- --authenticated-emails-file=/etc/oauth2-proxy/allowed-emails
2026-08-13 02:49:41 -03:00
- --custom-templates-dir=/etc/oauth2-proxy/templates
2026-08-10 21:08:42 -03:00
- --set-xauthrequest=true
- --pass-user-headers=true
- --pass-basic-auth=false
- --pass-host-header=false
- --proxy-websockets=true
- --cookie-name=__Host-hermes_agent
- --cookie-path=/
- --cookie-secure=true
- --cookie-samesite=lax
- --cookie-csrf-expire=10m
- --cookie-csrf-per-request=true
- --cookie-csrf-per-request-limit=8
2026-08-11 01:51:30 -03:00
- --cookie-refresh=19m
2026-08-10 21:08:42 -03:00
- --cookie-expire=8h
2026-08-11 01:51:30 -03:00
- --session-store-type=redis
- --redis-connection-url=redis://hermes-oauth-sessions.hermes.svc.cluster.local:6379/0
2026-08-10 21:08:42 -03:00
- --upstream=http://127.0.0.1:7681/terminal/
- --upstream=http://127.0.0.1:9119/
- --http-address=0.0.0.0:4180
- --skip-provider-button=true
- --reverse-proxy=true
2026-08-11 02:23:15 -03:00
- --api-route=^/api/
2026-08-10 21:08:42 -03:00
- --trusted-proxy-ip=10.42.0.0/16
ports :
- {name: auth-http, containerPort: 4180, protocol : TCP}
readinessProbe :
httpGet : {path: /ping, port : auth-http}
initialDelaySeconds : 5
periodSeconds : 10
livenessProbe :
httpGet : {path: /ping, port : auth-http}
initialDelaySeconds : 20
periodSeconds : 20
securityContext :
allowPrivilegeEscalation : false
capabilities :
drop : [ ALL]
readOnlyRootFilesystem : true
runAsNonRoot : true
seccompProfile :
type : RuntimeDefault
resources :
requests : {cpu: 25m, memory : 64Mi}
limits : {cpu: 250m, memory : 256Mi}
volumeMounts :
- {name: allowlist, mountPath: /etc/oauth2-proxy, readOnly : true }
2026-08-22 17:32:30 -03:00
- {name: oauth2-config, mountPath: /oauth2-config, readOnly : true }
2026-08-13 02:49:41 -03:00
- {name: oauth-templates, mountPath: /etc/oauth2-proxy/templates, readOnly : true }
2026-08-10 21:08:42 -03:00
- {name: oauth-tmp, mountPath : /tmp}
2026-08-10 16:31:06 -03:00
- name : terminal
2026-08-11 20:22:26 -03:00
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
2026-08-08 22:05:39 -03:00
imagePullPolicy : IfNotPresent
command : [ /bin/sh, -ec]
args :
- |
set -a
. /opt/data/.env
set +a
exec /opt/data/tools/bin/ttyd \
--writable \
2026-08-10 16:31:06 -03:00
--base-path /terminal \
2026-08-08 22:05:39 -03:00
--interface 0.0.0.0 \
--port 7681 \
--cwd /opt/data/workspace \
--terminal-type xterm-256color \
2026-08-09 15:52:06 -03:00
--index /ttyd-index/index.html \
2026-08-09 16:17:31 -03:00
--client-option disableReconnect=true \
2026-08-10 05:45:36 -03:00
--client-option "titleFixed=Hermes Agent" \
2026-08-08 22:05:39 -03:00
--client-option fontSize=15 \
2026-08-10 05:45:36 -03:00
/usr/bin/tmux new-session -A \
-s hermes-coordinator \
-c /opt/data/workspace \
/bin/sh -lc '
while true; do
/opt/hermes/.venv/bin/hermes \
--continue \
--tui \
--yolo \
2026-08-10 12:06:31 -03:00
--accept-hooks
2026-08-10 05:45:36 -03:00
exit_code=$?
printf "\r\nHermes exited (exit %s); resuming in 2 seconds...\r\n" "${exit_code}"
sleep 2
done
'
2026-08-08 22:05:39 -03:00
ports :
2026-08-10 16:31:06 -03:00
- {name: terminal, containerPort: 7681, protocol : TCP}
2026-08-08 22:05:39 -03:00
env :
- {name: HOME, value : /opt/data/home}
2026-08-10 05:45:36 -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}
- {name: CODEX_HOME, value : /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value : /runtime-access/claude}
2026-08-22 17:32:30 -03:00
- {name: HERMES_CLAUDE_BIN, value : /opt/coordinator/claude}
2026-08-10 05:45:36 -03:00
- {name: KUBECONFIG, value : /opt/data/home/.kube/config}
- {name: PYTHONPATH, value : /opt/hermes}
2026-08-24 17:22:30 +00:00
- {name: PATH, value : '/opt/coordinator:/opt/data/tools/bin:/opt/data/home/.local/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin' }
2026-08-10 05:45:36 -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-10 21:08:42 -03:00
- {name: HERMES_TUI_AGENT_INIT_TIMEOUT_S, value : "180" }
2026-08-11 16:22:19 -03:00
- {name: HERMES_AUTO_ROUTER_PROFILE, value : agent}
2026-08-12 23:48:06 -03:00
- {name: HERMES_ROUTING_CATALOG_PATH, value : /routing-catalog/catalog.json}
- {name: HERMES_CODEX_HEALTH_PATH, value : /opt/data/provider-health/codex.json}
- {name: HERMES_CLAUDE_HEALTH_PATH, value : /opt/data/provider-health/claude.json}
2026-08-08 22:05:39 -03:00
volumeMounts :
- {name: home, mountPath : /opt/data}
2026-08-15 17:58:47 -03:00
- {name: runtime-access, mountPath : /runtime-access}
2026-08-22 17:32:30 -03:00
- {name: claude-oauth-access, mountPath: /claude-oauth-access, readOnly : true }
2026-08-10 02:15:34 -03:00
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
2026-08-10 05:45:36 -03:00
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath : auth.py}
2026-08-10 22:39:51 -03:00
- {name: codex-runtime-patch, mountPath: /opt/hermes/hermes_cli/runtime_provider.py, subPath : runtime_provider.py}
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/transports/codex_app_server_session.py, subPath : codex_app_server_session.py}
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/codex_runtime.py, subPath : codex_runtime.py}
2026-08-10 23:19:42 -03:00
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/chat_completion_helpers.py, subPath : chat_completion_helpers.py}
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/conversation_loop.py, subPath : conversation_loop.py}
2026-08-11 00:41:08 -03:00
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/auxiliary_client.py, subPath : auxiliary_client.py}
2026-08-10 21:08:42 -03:00
- {name: tui-gateway-patch, mountPath: /opt/hermes/tui_gateway/server.py, subPath : server.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-10 05:45:36 -03:00
- {name: kubeconfig, mountPath: /opt/data/home/.kube/config, subPath: config, readOnly : true }
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly : true }
2026-08-16 19:54:41 -03:00
- {name: atlas-pr-skill, mountPath: /opt/data/workspace/skills/manage-atlas-pull-requests, readOnly : true }
2026-08-16 20:41:14 -03:00
- {name: scm-boundary, mountPath: /opt/scm, readOnly : true }
2026-08-12 23:48:06 -03:00
- {name: routing-catalog, mountPath: /routing-catalog, readOnly : true }
2026-08-08 22:05:39 -03:00
- {name: tmp, mountPath : /tmp}
2026-08-09 15:52:06 -03:00
- {name: ttyd-index, mountPath: /ttyd-index, readOnly : true }
2026-08-08 22:05:39 -03:00
startupProbe :
2026-08-10 16:31:06 -03:00
tcpSocket : {port : terminal}
2026-08-08 22:05:39 -03:00
periodSeconds : 5
timeoutSeconds : 3
failureThreshold : 60
readinessProbe :
2026-08-10 16:31:06 -03:00
tcpSocket : {port : terminal}
2026-08-08 22:05:39 -03:00
periodSeconds : 10
timeoutSeconds : 3
livenessProbe :
2026-08-10 16:31:06 -03:00
tcpSocket : {port : terminal}
2026-08-08 22:05:39 -03:00
initialDelaySeconds : 30
periodSeconds : 30
timeoutSeconds : 5
securityContext :
allowPrivilegeEscalation : false
capabilities :
drop : [ ALL]
readOnlyRootFilesystem : true
runAsNonRoot : true
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
resources :
2026-08-22 15:55:30 -03:00
requests : {cpu: 25m, memory : 32Mi}
2026-08-08 22:05:39 -03:00
limits : {cpu: 500m, memory : 512Mi}
2026-08-10 16:31:06 -03:00
- name : cli-lane-runner
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-10 16:31:06 -03:00
command : [ /bin/sh, -ec]
args :
2026-08-08 17:59:45 -03:00
- |
set -a
. /opt/data/.env
set +a
2026-08-10 16:31:06 -03:00
exec /opt/hermes/.venv/bin/python /opt/coordinator/cli_lane_runner.py
2026-08-17 20:30:53 -03:00
ports :
- {name: lane-metrics, containerPort: 9011, protocol : TCP}
2026-08-08 17:59:45 -03:00
env :
2026-08-08 22:05:39 -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-15 17:58:47 -03:00
- {name: CODEX_HOME, value : /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value : /runtime-access/claude}
2026-08-22 17:32:30 -03:00
- {name: HERMES_CLAUDE_BIN, value : /opt/coordinator/claude}
2026-08-09 14:38:23 -03:00
- {name: KUBECONFIG, value : /opt/data/home/.kube/config}
2026-08-08 22:05:39 -03:00
- {name: PYTHONPATH, value : /opt/hermes}
2026-08-16 20:53:22 +00:00
- {name: HERMES_CLI_LANE_CONCURRENCY, value : "2" }
2026-08-17 15:03:50 -03:00
- {name: HERMES_CLI_HEALTH_MAX_AGE_SECONDS, value : "60" }
2026-08-17 20:30:53 -03:00
- {name: HERMES_CLI_LANE_METRICS_PORT, value : "9011" }
- {name: HERMES_CLI_QUOTA_METRICS_URL, value : "http://127.0.0.1:9010/metrics" }
2026-08-11 16:22:19 -03:00
- {name: HERMES_AUTO_ROUTER_PROFILE, value : agent}
2026-08-24 17:22:30 +00:00
- {name: PATH, value : '/opt/coordinator:/opt/data/tools/bin:/opt/data/home/.local/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin' }
2026-08-08 17:59:45 -03:00
securityContext :
allowPrivilegeEscalation : false
2026-08-10 16:31:06 -03:00
capabilities :
drop : [ ALL]
readOnlyRootFilesystem : true
runAsNonRoot : true
2026-08-08 17:59:45 -03:00
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- {name: home, mountPath : /opt/data}
2026-08-15 17:58:47 -03:00
- {name: runtime-access, mountPath : /runtime-access}
2026-08-22 17:32:30 -03:00
- {name: claude-oauth-access, mountPath: /claude-oauth-access, readOnly : true }
2026-08-08 22:05:39 -03:00
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath : auth.py}
2026-08-09 14:38:23 -03:00
- {name: kubeconfig, mountPath: /opt/data/home/.kube/config, subPath: config, readOnly : true }
2026-08-09 02:42:11 -03:00
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly : true }
2026-08-16 19:54:41 -03:00
- {name: atlas-pr-skill, mountPath: /opt/data/workspace/skills/manage-atlas-pull-requests, readOnly : true }
2026-08-16 20:41:14 -03:00
- {name: scm-boundary, mountPath: /opt/scm, readOnly : true }
2026-08-10 16:31:06 -03:00
- {name: tmp, mountPath : /tmp}
2026-08-17 15:03:50 -03:00
readinessProbe :
2026-08-23 10:37:01 -03:00
httpGet :
path : /healthz
port : lane-metrics
2026-08-17 15:03:50 -03:00
initialDelaySeconds : 2
periodSeconds : 5
2026-08-17 15:20:43 -03:00
timeoutSeconds : 5
failureThreshold : 3
2026-08-08 17:59:45 -03:00
resources :
2026-08-22 15:55:30 -03:00
requests : {cpu: 25m, memory : 96Mi}
2026-08-16 20:53:22 +00:00
limits : {cpu : "2" , memory : 6Gi}
2026-08-08 17:59:45 -03:00
- name : model-steward
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/hermes_coordinator.py, --loop, --interval, "3600"]
env :
- {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-15 17:58:47 -03:00
- {name: CODEX_HOME, value : /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value : /runtime-access/claude}
2026-08-08 17:59:45 -03:00
- {name: PYTHONPATH, value : /opt/hermes}
2026-08-11 20:22:26 -03:00
- {name: HERMES_ROUTING_CATALOG_PATH, value : /routing-catalog/catalog.json}
2026-08-12 08:30:01 -03:00
- {name: HERMES_CASSANDRA_ACTIVE_WORKTREE, value : /opt/data/workspace/projects/cassandra-hermes-v69}
2026-08-24 17:22:30 +00:00
- {name: PATH, value : '/opt/coordinator:/opt/data/tools/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin' }
2026-08-08 17:59:45 -03:00
securityContext :
allowPrivilegeEscalation : false
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- {name: home, mountPath : /opt/data}
2026-08-15 17:58:47 -03:00
- {name: runtime-access, mountPath : /runtime-access}
2026-08-22 17:32:30 -03:00
- {name: claude-oauth-access, mountPath: /claude-oauth-access, readOnly : true }
2026-08-08 17:59:45 -03:00
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath : auth.py}
2026-08-11 20:22:26 -03:00
- {name: routing-catalog, mountPath : /routing-catalog}
2026-08-08 17:59:45 -03:00
resources :
2026-08-22 15:55:30 -03:00
requests : {cpu: 25m, memory : 32Mi}
2026-08-08 17:59:45 -03:00
limits : {cpu: 250m, memory : 512Mi}
2026-08-18 06:28:49 -03:00
- name : kanban-supervisor
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy : IfNotPresent
# Autonomous cross-card review->repair->re-review driver. Reads/writes
# only the local Kanban DB under /opt/data; creates Kanban cards that
# route through the existing subscription lanes. No runtime-access
# mount and no provider client: it holds no metered/API-key path. Inert
# until kanban.auto_supervise is set true in the deployed config.
command : [ /opt/hermes/.venv/bin/python, /opt/coordinator/kanban_supervisor.py]
env :
- {name: HERMES_HOME, value : /opt/data}
- {name: HOME, value : /opt/data/home}
- {name: PYTHONPATH, value : /opt/hermes}
- {name: PYTHONDONTWRITEBYTECODE, value : "1" }
securityContext :
allowPrivilegeEscalation : false
capabilities :
drop : [ ALL]
readOnlyRootFilesystem : true
runAsNonRoot : true
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- {name: home, mountPath : /opt/data}
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath : auth.py}
resources :
2026-08-22 15:55:30 -03:00
requests : {cpu: 10m, memory : 32Mi}
2026-08-18 06:28:49 -03:00
limits : {cpu: 250m, memory : 256Mi}
2026-08-15 17:58:47 -03:00
- name : credential-sync
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy : IfNotPresent
command :
- /opt/hermes/.venv/bin/python
- /opt/coordinator/sync_runtime_credentials.py
env :
- {name: HERMES_CREDENTIAL_SYNC_VAULT_ROLE, value : hermes-credential-sync}
- {name: HERMES_CREDENTIAL_SYNC_INTERVAL, value : "60" }
securityContext :
allowPrivilegeEscalation : false
capabilities :
drop : [ ALL]
readOnlyRootFilesystem : true
runAsNonRoot : true
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
2026-08-22 17:32:30 -03:00
# Reconcile provider rotations to Vault and restore runtime files if a
# client leaves an incomplete credential behind after refresh.
- {name: runtime-access, mountPath: /runtime-access/claude, subPath : claude}
- {name: runtime-access, mountPath: /runtime-access/codex, subPath : codex}
2026-08-15 17:58:47 -03:00
resources :
requests : {cpu: 10m, memory : 32Mi}
limits : {cpu: 100m, memory : 128Mi}
2026-08-16 05:13:20 -03:00
- name : ai-usage-exporter
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy : IfNotPresent
command :
- /opt/hermes/.venv/bin/python
- /opt/coordinator/ai_usage_exporter.py
ports :
- {name: ai-metrics, containerPort: 9010, protocol : TCP}
env :
- {name: HOME, value : /tmp}
- {name: CODEX_HOME, value : /runtime-access/codex}
- {name: ATLAS_AI_CODEX_BIN, value : /opt/data/tools/bin/codex}
2026-08-23 00:14:58 -03:00
- {name: ATLAS_AI_CODEX_QUERY_TIMEOUT_SECONDS, value : "45" }
2026-08-24 14:13:35 -03:00
- {name: ATLAS_AI_CLAUDE_CREDENTIALS, value : /runtime-access/claude/.credentials.json}
2026-08-23 00:42:39 -03:00
- {name: ATLAS_AI_CLAUDE_OAUTH_TOKEN_FILE, value : /claude-oauth-access/token}
- {name: ATLAS_AI_CLAUDE_QUERY_TIMEOUT_SECONDS, value : "30" }
- {name: ATLAS_AI_CLAUDE_QUOTA_MODEL, value : claude-haiku-4-5-20251001}
2026-08-24 14:13:35 -03:00
- {name: ATLAS_AI_CLAUDE_USAGE_URL, value : 'https://api.anthropic.com/api/oauth/usage' }
2026-08-23 02:52:42 -03:00
- {name: ATLAS_AI_AUTHENTICATION_GRACE_SECONDS, value : "1200" }
2026-08-22 17:32:30 -03:00
- {name: ATLAS_AI_PROVIDER_HEALTH_ROOT, value : /provider-health}
2026-08-16 05:13:20 -03:00
- {name: ATLAS_AI_USAGE_INTERVAL_SECONDS, value : "300" }
- {name: ATLAS_AI_USAGE_PORT, value : "9010" }
readinessProbe :
httpGet : {path: /healthz, port : ai-metrics}
initialDelaySeconds : 5
periodSeconds : 10
livenessProbe :
httpGet : {path: /healthz, port : ai-metrics}
initialDelaySeconds : 30
periodSeconds : 30
securityContext :
allowPrivilegeEscalation : false
capabilities :
drop : [ ALL]
readOnlyRootFilesystem : true
runAsNonRoot : true
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
2026-08-23 00:42:39 -03:00
- {name: claude-oauth-access, mountPath: /claude-oauth-access, readOnly : true }
2026-08-16 05:13:20 -03:00
- {name: home, mountPath: /opt/data/tools, subPath: tools, readOnly : true }
2026-08-22 17:32:30 -03:00
- {name: home, mountPath: /provider-health, subPath: provider-health, readOnly : true }
2026-08-16 05:13:20 -03:00
- {name: runtime-access, mountPath: /runtime-access/claude, subPath : claude}
- {name: runtime-access, mountPath: /runtime-access/codex, subPath : codex}
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
- {name: tmp, mountPath : /tmp}
resources :
requests : {cpu: 10m, memory : 32Mi}
limits : {cpu: 250m, memory : 192Mi}
2026-08-11 01:14:46 -03:00
- name : image-broker
2026-08-11 20:22:26 -03:00
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
2026-08-11 01:14:46 -03:00
imagePullPolicy : IfNotPresent
command : [ /bin/sh, -ec]
args :
- |
exec /opt/hermes/.venv/bin/python /opt/coordinator/image_broker.py
ports :
- {name: image-broker, containerPort: 9002, protocol : TCP}
env :
- {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-11 01:14:46 -03:00
- {name: HOME, value : /opt/data/home}
2026-08-15 17:58:47 -03:00
- {name: CODEX_HOME, value : /runtime-access/codex}
2026-08-11 01:14:46 -03:00
- {name: PYTHONPATH, value : /opt/hermes}
2026-08-11 05:20:18 -03:00
- {name: HERMES_IMAGE_BROKER_DEFAULT_MODEL, value : atlas-image-auto-high}
2026-08-11 01:22:16 -03:00
- {name: HERMES_IMAGE_BROKER_LISTEN_PORT, value : "9002" }
2026-08-15 17:58:47 -03:00
- {name: HERMES_IMAGE_BROKER_KEY_FILE, value : /runtime-access/chat-relay-key}
2026-08-24 17:22:30 +00:00
- {name: HERMES_LOCAL_IMAGE_URL, value : 'http://hermes-local-image.hermes.svc.cluster.local:9004' }
2026-08-11 05:20:18 -03:00
- {name: HERMES_IMAGE_POLICY_PATH, value : /etc/hermes-image-policy/policy.json}
2026-08-11 01:14:46 -03:00
readinessProbe :
tcpSocket : {port : image-broker}
initialDelaySeconds : 5
periodSeconds : 10
livenessProbe :
tcpSocket : {port : image-broker}
initialDelaySeconds : 30
periodSeconds : 30
securityContext :
allowPrivilegeEscalation : false
capabilities :
drop : [ ALL]
readOnlyRootFilesystem : true
runAsNonRoot : true
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- {name: home, mountPath : /opt/data}
2026-08-15 17:58:47 -03:00
- {name: runtime-access, mountPath : /runtime-access}
2026-08-11 01:14:46 -03:00
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/auxiliary_client.py, subPath : auxiliary_client.py}
2026-08-11 05:20:18 -03:00
- {name: image-policy, mountPath: /etc/hermes-image-policy, readOnly : true }
2026-08-11 01:14:46 -03:00
- {name: tmp, mountPath : /tmp}
resources :
2026-08-18 16:05:50 -03:00
requests : {cpu: 25m, memory : 64Mi}
2026-08-11 01:14:46 -03:00
limits : {cpu : "1" , memory : 1Gi}
2026-08-11 03:15:34 -03:00
- name : codex-broker
2026-08-11 20:22:26 -03:00
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
2026-08-11 03:15:34 -03:00
imagePullPolicy : IfNotPresent
command : [ /bin/sh, -ec]
args :
- |
exec /opt/hermes/.venv/bin/python /opt/coordinator/codex_broker.py
ports :
- {name: codex-broker, containerPort: 9003, protocol : TCP}
env :
- {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-11 03:15:34 -03:00
- {name: HOME, value : /opt/data/home}
2026-08-15 17:58:47 -03:00
- {name: CODEX_HOME, value : /runtime-access/codex}
2026-08-11 03:15:34 -03:00
- {name: PYTHONPATH, value : /opt/hermes}
- {name: HERMES_CODEX_BROKER_LISTEN_PORT, value : "9003" }
2026-08-15 17:58:47 -03:00
- {name: HERMES_CODEX_BROKER_KEY_FILE, value : /runtime-access/chat-relay-key}
2026-08-12 23:48:06 -03:00
- {name: HERMES_CODEX_HEALTH_PATH, value : /opt/data/provider-health/codex.json}
2026-08-11 20:22:26 -03:00
- {name: HERMES_ROUTING_CATALOG_PATH, value : /routing-catalog/catalog.json}
2026-08-11 03:15:34 -03:00
readinessProbe :
tcpSocket : {port : codex-broker}
initialDelaySeconds : 5
periodSeconds : 10
livenessProbe :
tcpSocket : {port : codex-broker}
initialDelaySeconds : 30
periodSeconds : 30
securityContext :
allowPrivilegeEscalation : false
capabilities :
drop : [ ALL]
readOnlyRootFilesystem : true
runAsNonRoot : true
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- {name: home, mountPath : /opt/data}
2026-08-15 17:58:47 -03:00
- {name: runtime-access, mountPath : /runtime-access}
2026-08-11 03:15:34 -03:00
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/auxiliary_client.py, subPath : auxiliary_client.py}
- {name: tmp, mountPath : /tmp}
2026-08-11 20:22:26 -03:00
- {name: routing-catalog, mountPath: /routing-catalog, readOnly : true }
2026-08-11 03:15:34 -03:00
resources :
2026-08-18 16:05:50 -03:00
requests : {cpu: 25m, memory : 64Mi}
2026-08-11 03:15:34 -03:00
limits : {cpu : "1" , memory : 1Gi}
2026-08-12 23:08:21 -03:00
- name : claude-broker
image : registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy : IfNotPresent
command : [ /bin/sh, -ec]
args :
- |
unset ANTHROPIC_API_KEY CLAUDE_API_KEY
exec /opt/hermes/.venv/bin/python /opt/coordinator/claude_oauth_broker.py
ports :
- {name: claude-broker, containerPort: 9006, protocol : TCP}
env :
- {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-12 23:08:21 -03:00
- {name: HOME, value : /opt/data/home}
2026-08-15 17:58:47 -03:00
- {name: CODEX_HOME, value : /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value : /runtime-access/claude}
2026-08-12 23:08:21 -03:00
- {name: PYTHONPATH, value : /opt/hermes}
2026-08-24 17:22:30 +00:00
- {name: PATH, value : '/opt/coordinator:/opt/data/tools/bin:/opt/data/home/.local/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin' }
2026-08-12 23:08:21 -03:00
- {name: HERMES_CLAUDE_BIN, value : /opt/coordinator/claude}
- {name: HERMES_CLAUDE_BROKER_PORT, value : "9006" }
2026-08-15 17:58:47 -03:00
- {name: HERMES_CLAUDE_BROKER_KEY_FILE, value : /runtime-access/chat-relay-key}
2026-08-12 23:08:21 -03:00
- {name: HERMES_CLAUDE_BROKER_READ_TIMEOUT, value : "1800" }
2026-08-15 22:15:06 -03:00
- {name: HERMES_CLAUDE_BROKER_CONCURRENCY, value : "2" }
2026-08-23 10:37:01 -03:00
- {name: HERMES_CLAUDE_HEALTH_POLL_SECONDS, value : "300" }
2026-08-12 23:08:21 -03:00
- {name: HERMES_CLAUDE_HEALTH_PATH, value : /opt/data/provider-health/claude.json}
- {name: HERMES_ROUTING_CATALOG_PATH, value : /routing-catalog/catalog.json}
readinessProbe :
2026-08-15 22:15:06 -03:00
tcpSocket : {port : claude-broker}
2026-08-12 23:08:21 -03:00
initialDelaySeconds : 5
periodSeconds : 10
livenessProbe :
2026-08-15 22:15:06 -03:00
tcpSocket : {port : claude-broker}
2026-08-12 23:08:21 -03:00
initialDelaySeconds : 30
periodSeconds : 30
securityContext :
allowPrivilegeEscalation : false
capabilities :
drop : [ ALL]
readOnlyRootFilesystem : true
runAsNonRoot : true
runAsUser : 10000
runAsGroup : 10000
seccompProfile :
type : RuntimeDefault
volumeMounts :
- {name: home, mountPath : /opt/data}
2026-08-15 17:58:47 -03:00
- {name: runtime-access, mountPath : /runtime-access}
2026-08-22 17:32:30 -03:00
- {name: claude-oauth-access, mountPath: /claude-oauth-access, readOnly : true }
2026-08-12 23:08:21 -03:00
- {name: coordinator, mountPath: /opt/coordinator, readOnly : true }
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath : auth.py}
- {name: tmp, mountPath : /tmp}
- {name: routing-catalog, mountPath: /routing-catalog, readOnly : true }
resources :
2026-08-18 16:05:50 -03:00
requests : {cpu: 50m, memory : 128Mi}
2026-08-12 23:08:21 -03:00
limits : {cpu : "3" , memory : 3Gi}
2026-08-24 13:54:27 -03:00
# Worker HUX service: the same reviewed WebUI image line as the chat
# tenants (one artifact, one Flux image policy), loopback-only, with
# foundation as the only enabled card until the Worker lifecycle gates
# in docs/hux/WORKER-PLAN.md pass on this instance.
- 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 13:54:27 -03:00
imagePullPolicy : IfNotPresent
command : [ /opt/hermes/.venv/bin/python, -m, hux.server]
ports :
- {name: hux-loopback, containerPort: 8790, protocol : TCP}
env :
- {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_TENANT_SLOT, value : slot-100}
- {name: HUX_DATA_ROOT, value : /var/lib/hux/store}
- {name: HUX_FLAGS, value : hux.foundation}
- {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
2026-08-25 04:43:23 +00:00
value : git-5b1f8320729ebd1514ce83b7e695a02938e5cef8-build-36-release # {"$imagepolicy": "hermes:hermes-webui-release:tag"}
2026-08-24 13:54:27 -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 13:54:27 -03:00
- {name: HUX_READS_PER_MINUTE, value : "600" }
- {name: HUX_WRITES_PER_MINUTE, value : "120" }
- {name: HUX_REQUEST_TIMEOUT_SECONDS, value : "10" }
volumeMounts :
- {name: home, mountPath: /var/lib/hux, subPath : hux}
- {name: hux-worker-key, mountPath: /run/hermes-hux-worker, readOnly : true }
- {name: hux-tmp, mountPath : /tmp}
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}
2026-08-08 17:59:45 -03:00
volumes :
- name : home
persistentVolumeClaim :
claimName : hermes-agent-home
2026-08-15 17:58:47 -03:00
- name : runtime-access
emptyDir :
medium : Memory
sizeLimit : 128Mi
2026-08-22 17:32:30 -03:00
- name : claude-oauth-access
emptyDir :
medium : Memory
sizeLimit : 1Mi
2026-08-11 20:22:26 -03:00
- name : routing-catalog
persistentVolumeClaim :
claimName : hermes-routing-catalog
2026-08-08 17:59:45 -03:00
- name : config
configMap :
name : hermes-agent-config
- name : coordinator
configMap :
name : hermes-coordinator
defaultMode : 0555
2026-08-09 14:38:23 -03:00
- name : kubeconfig
configMap :
name : hermes-agent-kubeconfig
defaultMode : 0444
2026-08-08 17:59:45 -03:00
- name : auth-patch
emptyDir : {}
2026-08-10 21:08:42 -03:00
- name : tui-gateway-patch
emptyDir : {}
2026-08-12 23:08:21 -03:00
- name : api-server-patch
emptyDir : {}
2026-08-15 17:58:47 -03:00
- name : web-server-patch
emptyDir : {}
- name : subprocess-secret-patch
emptyDir : {}
2026-08-10 22:39:51 -03:00
- name : codex-runtime-patch
emptyDir : {}
2026-08-09 02:42:11 -03:00
- name : auto-router-plugin
configMap :
name : hermes-auto-router-plugin
2026-08-12 02:23:31 -03:00
items :
- {key: __init__.py, path : __init__.py}
- {key: provider_status.py, path : provider_status.py}
- {key: plugin.yaml, path : plugin.yaml}
- {key: dashboard-manifest.json, path : dashboard/manifest.json}
- {key: dashboard-api.py, path : dashboard/plugin_api.py}
- {key: dashboard-index.js, path : dashboard/dist/index.js}
- {key: dashboard-style.css, path : dashboard/dist/style.css}
2026-08-16 19:54:41 -03:00
- name : atlas-pr-skill
configMap :
name : hermes-atlas-pr-skill
items :
- {key: SKILL.md, path : SKILL.md}
- {key: openai.yaml, path : agents/openai.yaml}
2026-08-16 20:41:14 -03:00
- name : scm-boundary
configMap :
2026-08-17 16:31:15 +00:00
name : hermes-scm-boundary-v2
2026-08-16 20:41:14 -03:00
defaultMode : 0555
2026-08-11 05:20:18 -03:00
- name : image-policy
configMap :
name : hermes-image-policy
2026-08-08 19:18:33 -03:00
- name : tmp
emptyDir :
2026-08-10 17:05:14 -03:00
sizeLimit : 4Gi
2026-08-10 21:08:42 -03:00
- name : oauth-tmp
emptyDir :
sizeLimit : 64Mi
2026-08-22 17:32:30 -03:00
- name : oauth2-config
emptyDir :
medium : Memory
sizeLimit : 1Mi
2026-08-10 21:08:42 -03:00
- name : allowlist
configMap :
name : hermes-owner-allowlist
2026-08-13 02:49:41 -03:00
- name : oauth-templates
configMap :
name : hermes-chat-oauth-templates
2026-08-09 15:52:06 -03:00
- name : ttyd-index
emptyDir :
sizeLimit : 2Mi
2026-08-24 13:54:27 -03:00
- name : hux-worker-key
emptyDir :
medium : Memory
sizeLimit : 1Mi
- name : hux-tmp
emptyDir :
sizeLimit : 64Mi
- 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}