atlas-iac/services/hermes/agent-deployment.yaml
2026-08-17 10:43:17 +00:00

1210 lines
58 KiB
YAML

# 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
ai.bstein.dev/router-wire-contract: ollama-numeric-keepalive
ai.bstein.dev/execution: Hermes Kanban with durable direct Codex and Claude Code CLI workers
ai.bstein.dev/model-policy: Jetson-assisted AUTO routing, low through xhigh, cross-provider fallback
ai.bstein.dev/placement: rpi5 preferred; Jetson deferred until state storage is available
ai.bstein.dev/config-rev: "20260816-auth-health-quarantine-v6"
prometheus.io/scrape: "true"
prometheus.io/path: /metrics
prometheus.io/port: "9010"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: hermes-agent
vault.hashicorp.com/agent-inject-secret-agent-api-key: kv/data/atlas/hermes/agent-tokens
vault.hashicorp.com/agent-inject-template-agent-api-key: |
{{- with secret "kv/data/atlas/hermes/agent-tokens" -}}
{{ .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 }}
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 }}
{{- end }}
vault.hashicorp.com/agent-inject-secret-gitea-token: kv/data/atlas/hermes/developer-gitea
vault.hashicorp.com/agent-inject-template-gitea-token: |
{{- with secret "kv/data/atlas/hermes/developer-gitea" -}}
{{ .Data.data.token }}
{{- end }}
vault.hashicorp.com/agent-inject-secret-gitea-username: kv/data/atlas/hermes/developer-gitea
vault.hashicorp.com/agent-inject-template-gitea-username: |
{{- with secret "kv/data/atlas/hermes/developer-gitea" -}}
{{ .Data.data.username }}
{{- end }}
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 }}
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 }}
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-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 -}}
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
enableServiceLinks: false
automountServiceAccountToken: true
securityContext:
fsGroup: 10000
fsGroupChangePolicy: OnRootMismatch
seccompProfile:
type: RuntimeDefault
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/arch
operator: In
values: [arm64]
- key: node-role.kubernetes.io/worker
operator: In
values: ["true"]
- key: kubernetes.io/hostname
operator: NotIn
values: [titan-04, titan-08, titan-13, titan-14, titan-17, titan-18, titan-19]
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: hardware
operator: In
values: [rpi5]
initContainers:
- name: init-config
image: busybox:1.37
imagePullPolicy: IfNotPresent
command:
- sh
- -c
- |
set -eu
env_file=/opt/data/.env
profile_file=/opt/data/home/.profile
mkdir -p \
/opt/data/home/.claude \
/opt/data/home/.codex \
/opt/data/home/.kube \
/opt/data/home/.ssh \
/opt/data/cli-lanes \
/opt/data/logs \
/opt/data/provider-health \
/opt/data/tools/bin \
/opt/data/workspace/coordinator \
/opt/data/workspace/projects \
/opt/data/workspace/skills
if [ ! -e /opt/data/home/.hermes ]; then
ln -s /opt/data /opt/data/home/.hermes
fi
# 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
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
rm -f \
/opt/data/auth.json \
/opt/data/home/.claude/.credentials.json \
/opt/data/home/.codex/auth.json \
/opt/data/home/.ssh/config \
/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
chmod 0700 /opt/data/home/.ssh
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}"
}
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
upsert_env GIT_ASKPASS /opt/coordinator/gitea_askpass.sh
upsert_env GIT_TERMINAL_PROMPT 0
upsert_env GITEA_BASE_URL https://scm.bstein.dev
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
chmod 0600 "${env_file}"
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}"
# 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 \
/opt/data/home/.ssh \
/opt/data/cli-lanes \
/opt/data/logs \
/opt/data/provider-health \
/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 \
"${profile_file}" \
"${env_file}"
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}
- 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:
- {name: home, mountPath: /opt/data}
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
- {name: runtime-access, mountPath: /runtime-access}
resources:
requests: {cpu: 25m, memory: 32Mi}
limits: {cpu: 100m, memory: 64Mi}
- name: install-agent-tools
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy: IfNotPresent
command:
- sh
- -c
- |
set -eu
tools=/opt/data/tools
mkdir -p "${tools}/bin"
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
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
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
/bin/sh /opt/coordinator/install_agent_tools.sh
HOME=/opt/data/home git config --global user.name "Hermes Agent"
HOME=/opt/data/home git config --global user.email "hermes@bstein.dev"
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: 100m, memory: 256Mi}
limits: {cpu: "1", memory: 1Gi}
- 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}
- 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}
- name: patch-auth
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy: IfNotPresent
command:
- /opt/hermes/.venv/bin/python
- /opt/coordinator/patch_hermes_auth.py
- /opt/hermes/hermes_cli/auth.py
- /patched/auth.py
securityContext:
allowPrivilegeEscalation: false
runAsUser: 10000
runAsGroup: 10000
seccompProfile:
type: RuntimeDefault
volumeMounts:
- name: coordinator
mountPath: /opt/coordinator
readOnly: true
- name: auth-patch
mountPath: /patched
resources:
requests: {cpu: 25m, memory: 64Mi}
limits: {cpu: 100m, memory: 128Mi}
- name: patch-tui-gateway
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
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}
- 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}
- 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}
- name: patch-codex-runtime
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
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
- /opt/hermes/agent/chat_completion_helpers.py
- /patched/chat_completion_helpers.py
- /opt/hermes/agent/conversation_loop.py
- /patched/conversation_loop.py
- /opt/hermes/agent/auxiliary_client.py
- /patched/auxiliary_client.py
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}
- name: bootstrap-coordinator
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy: IfNotPresent
command:
- /opt/hermes/.venv/bin/python
- /opt/coordinator/hermes_coordinator.py
- --once
env:
- {name: HERMES_HOME, value: /opt/data}
- {name: HERMES_AUTH_FILE, value: /runtime-access/hermes-auth.json}
- {name: HOME, value: /opt/data/home}
- {name: CODEX_HOME, value: /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value: /runtime-access/claude}
- {name: PYTHONPATH, value: /opt/hermes}
- {name: HERMES_ROUTING_CATALOG_PATH, value: /routing-catalog/catalog.json}
- {name: HERMES_CASSANDRA_ACTIVE_WORKTREE, value: /opt/data/workspace/projects/cassandra-hermes-v69}
- {name: PATH, value: /opt/coordinator:/opt/data/tools/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin}
securityContext:
allowPrivilegeEscalation: false
runAsUser: 10000
runAsGroup: 10000
seccompProfile:
type: RuntimeDefault
volumeMounts:
- {name: home, mountPath: /opt/data}
- {name: runtime-access, mountPath: /runtime-access}
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
- {name: routing-catalog, mountPath: /routing-catalog}
resources:
requests: {cpu: 50m, memory: 128Mi}
limits: {cpu: 500m, memory: 512Mi}
- name: configure-agent-clients
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy: IfNotPresent
command:
- sh
- -ec
- |
set -a
. /opt/data/.env
set +a
/opt/hermes/.venv/bin/python /opt/coordinator/configure_agent_clients.py
/opt/hermes/.venv/bin/python /opt/coordinator/migrate_herdr_state.py
/opt/hermes/.venv/bin/python /opt/coordinator/migrate_api_session_lineage.py
# 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
env:
- {name: HERMES_HOME, value: /opt/data}
- {name: HERMES_AUTH_FILE, value: /runtime-access/hermes-auth.json}
- {name: HOME, value: /opt/data/home}
- {name: CODEX_HOME, value: /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value: /runtime-access/claude}
- {name: PYTHONPATH, value: /opt/hermes}
- {name: HERMES_ROUTING_CATALOG_PATH, value: /routing-catalog/catalog.json}
- {name: HERMES_CASSANDRA_ACTIVE_WORKTREE, value: /opt/data/workspace/projects/cassandra-hermes-v69}
- {name: PATH, value: /opt/coordinator:/opt/data/tools/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin}
securityContext:
allowPrivilegeEscalation: false
runAsUser: 10000
runAsGroup: 10000
seccompProfile:
type: RuntimeDefault
volumeMounts:
- {name: home, mountPath: /opt/data}
- {name: runtime-access, mountPath: /runtime-access}
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
- {name: routing-catalog, mountPath: /routing-catalog}
resources:
requests: {cpu: 25m, memory: 128Mi}
limits: {cpu: 500m, memory: 512Mi}
- name: prepare-ttyd-index
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
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}
containers:
- name: hermes
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy: IfNotPresent
command: [/bin/sh, -ec]
args:
- |
set -a
. /opt/data/.env
set +a
API_SERVER_KEY="$(tr -d '\r\n' < /runtime-access/agent-api-key)"
test -n "${API_SERVER_KEY}"
export API_SERVER_KEY
exec /init /opt/hermes/docker/main-wrapper.sh gateway run
ports:
- {name: api, containerPort: 8642, protocol: TCP}
- {name: dashboard, containerPort: 9119, protocol: TCP}
env:
- {name: HERMES_HOME, value: /opt/data}
- {name: HERMES_AUTH_FILE, value: /runtime-access/hermes-auth.json}
- {name: HOME, value: /opt/data/home}
- {name: CODEX_HOME, value: /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value: /runtime-access/claude}
- {name: KUBECONFIG, value: /opt/data/home/.kube/config}
- {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}
- {name: PLAYWRIGHT_BROWSERS_PATH, value: /opt/hermes/.playwright}
- {name: AGENT_BROWSER_EXECUTABLE_PATH, value: /opt/hermes/.playwright/chromium_headless_shell-1228/chrome-linux/headless_shell}
- {name: AGENT_BROWSER_ARGS, value: "--no-sandbox,--disable-dev-shm-usage"}
- {name: HERMES_DASHBOARD, value: "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}
- {name: HERMES_DASHBOARD_PUBLIC_URL, value: https://agent.hermes.bstein.dev}
- {name: HERMES_TUI_AGENT_INIT_TIMEOUT_S, value: "180"}
- {name: HERMES_STREAM_STALE_TIMEOUT, value: "600"}
- {name: HERMES_API_CALL_STALE_TIMEOUT, value: "600"}
- {name: API_SERVER_ENABLED, value: "true"}
- {name: API_SERVER_HOST, value: 0.0.0.0}
- {name: API_SERVER_PORT, value: "8642"}
- {name: API_SERVER_CORS_ORIGINS, value: https://agent.hermes.bstein.dev}
- {name: HERMES_MEDIA_DELIVERY_STRICT, value: "1"}
- {name: HERMES_MEDIA_ALLOW_DIRS, value: /opt/data/workspace}
- {name: HERMES_AUTO_ROUTER_PROFILE, value: agent}
- {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}
- {name: HERMES_IMAGE_BROKER_KEY_FILE, value: /runtime-access/chat-relay-key}
volumeMounts:
- {name: home, mountPath: /opt/data}
- {name: runtime-access, mountPath: /runtime-access}
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
- {name: web-server-patch, mountPath: /opt/hermes/hermes_cli/web_server.py, subPath: web_server.py}
- {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}
- {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}
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/auxiliary_client.py, subPath: auxiliary_client.py}
- {name: tui-gateway-patch, mountPath: /opt/hermes/tui_gateway/server.py, subPath: server.py}
- {name: api-server-patch, mountPath: /opt/hermes/gateway/platforms/api_server.py, subPath: api_server.py}
- {name: subprocess-secret-patch, mountPath: /opt/hermes/tools/environments/local.py, subPath: local.py}
- {name: subprocess-secret-patch, mountPath: /opt/hermes/tools/process_registry.py, subPath: process_registry.py}
- {name: kubeconfig, mountPath: /opt/data/home/.kube/config, subPath: config, readOnly: true}
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly: true}
- {name: routing-catalog, mountPath: /routing-catalog, readOnly: true}
- {name: tmp, mountPath: /tmp}
startupProbe:
exec:
command: [curl, -fsS, http://127.0.0.1:9119/api/status]
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 60
readinessProbe:
exec:
command: [curl, -fsS, http://127.0.0.1:9119/api/status]
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
livenessProbe:
exec:
command: [curl, -fsS, http://127.0.0.1:9119/api/status]
initialDelaySeconds: 90
periodSeconds: 30
timeoutSeconds: 10
securityContext:
allowPrivilegeEscalation: false
runAsUser: 0
runAsGroup: 0
seccompProfile:
type: RuntimeDefault
resources:
requests: {cpu: 300m, memory: 768Mi}
limits: {cpu: "3", memory: 6Gi}
- name: oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.3@sha256:10a1165743a192e1940b4708fb9647027185ce11a681a1c5519b442ff7f1f561
imagePullPolicy: IfNotPresent
args:
- --provider=oidc
- --config=/vault/secrets/oidc-config
- --redirect-url=https://agent.hermes.bstein.dev/oauth2/callback
- --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
- --custom-templates-dir=/etc/oauth2-proxy/templates
- --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
- --cookie-refresh=19m
- --cookie-expire=8h
- --session-store-type=redis
- --redis-connection-url=redis://hermes-oauth-sessions.hermes.svc.cluster.local:6379/0
- --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
- --api-route=^/api/
- --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}
- {name: oauth-templates, mountPath: /etc/oauth2-proxy/templates, readOnly: true}
- {name: oauth-tmp, mountPath: /tmp}
- name: terminal
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy: IfNotPresent
command: [/bin/sh, -ec]
args:
- |
set -a
. /opt/data/.env
set +a
exec /opt/data/tools/bin/ttyd \
--writable \
--base-path /terminal \
--interface 0.0.0.0 \
--port 7681 \
--cwd /opt/data/workspace \
--terminal-type xterm-256color \
--index /ttyd-index/index.html \
--client-option disableReconnect=true \
--client-option "titleFixed=Hermes Agent" \
--client-option fontSize=15 \
/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 \
--accept-hooks
exit_code=$?
printf "\r\nHermes exited (exit %s); resuming in 2 seconds...\r\n" "${exit_code}"
sleep 2
done
'
ports:
- {name: terminal, containerPort: 7681, protocol: TCP}
env:
- {name: HOME, value: /opt/data/home}
- {name: HERMES_HOME, value: /opt/data}
- {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}
- {name: KUBECONFIG, value: /opt/data/home/.kube/config}
- {name: PYTHONPATH, value: /opt/hermes}
- {name: PATH, value: /opt/coordinator:/opt/data/tools/bin:/opt/data/home/.local/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin}
- {name: PLAYWRIGHT_BROWSERS_PATH, value: /opt/hermes/.playwright}
- {name: AGENT_BROWSER_EXECUTABLE_PATH, value: /opt/hermes/.playwright/chromium_headless_shell-1228/chrome-linux/headless_shell}
- {name: AGENT_BROWSER_ARGS, value: "--no-sandbox,--disable-dev-shm-usage"}
- {name: HERMES_TUI_AGENT_INIT_TIMEOUT_S, value: "180"}
- {name: HERMES_AUTO_ROUTER_PROFILE, value: agent}
- {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}
volumeMounts:
- {name: home, mountPath: /opt/data}
- {name: runtime-access, mountPath: /runtime-access}
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
- {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}
- {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}
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/auxiliary_client.py, subPath: auxiliary_client.py}
- {name: tui-gateway-patch, mountPath: /opt/hermes/tui_gateway/server.py, subPath: server.py}
- {name: subprocess-secret-patch, mountPath: /opt/hermes/tools/environments/local.py, subPath: local.py}
- {name: subprocess-secret-patch, mountPath: /opt/hermes/tools/process_registry.py, subPath: process_registry.py}
- {name: kubeconfig, mountPath: /opt/data/home/.kube/config, subPath: config, readOnly: true}
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly: true}
- {name: routing-catalog, mountPath: /routing-catalog, readOnly: true}
- {name: tmp, mountPath: /tmp}
- {name: ttyd-index, mountPath: /ttyd-index, readOnly: true}
startupProbe:
tcpSocket: {port: terminal}
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 60
readinessProbe:
tcpSocket: {port: terminal}
periodSeconds: 10
timeoutSeconds: 3
livenessProbe:
tcpSocket: {port: terminal}
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 5
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 10000
runAsGroup: 10000
seccompProfile:
type: RuntimeDefault
resources:
requests: {cpu: 25m, memory: 64Mi}
limits: {cpu: 500m, memory: 512Mi}
- name: cli-lane-runner
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy: IfNotPresent
command: [/bin/sh, -ec]
args:
- |
set -a
. /opt/data/.env
set +a
exec /opt/hermes/.venv/bin/python /opt/coordinator/cli_lane_supervisor.py -- \
/opt/hermes/.venv/bin/python /opt/coordinator/cli_lane_runner.py
env:
- {name: HERMES_HOME, value: /opt/data}
- {name: HERMES_AUTH_FILE, value: /runtime-access/hermes-auth.json}
- {name: HOME, value: /opt/data/home}
- {name: CODEX_HOME, value: /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value: /runtime-access/claude}
- {name: KUBECONFIG, value: /opt/data/home/.kube/config}
- {name: PYTHONPATH, value: /opt/hermes}
- {name: HERMES_CLI_LANE_CONCURRENCY, value: "2"}
- {name: HERMES_AUTO_ROUTER_PROFILE, value: agent}
- {name: PATH, value: /opt/coordinator:/opt/data/tools/bin:/opt/data/home/.local/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin}
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 10000
runAsGroup: 10000
seccompProfile:
type: RuntimeDefault
volumeMounts:
- {name: home, mountPath: /opt/data}
- {name: runtime-access, mountPath: /runtime-access}
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
- {name: kubeconfig, mountPath: /opt/data/home/.kube/config, subPath: config, readOnly: true}
- {name: auto-router-plugin, mountPath: /opt/data/plugins/auto-router, readOnly: true}
- {name: tmp, mountPath: /tmp}
resources:
requests: {cpu: 100m, memory: 256Mi}
limits: {cpu: "2", memory: 6Gi}
- name: model-steward
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
imagePullPolicy: IfNotPresent
command: [/opt/hermes/.venv/bin/python, /opt/coordinator/hermes_coordinator.py, --loop, --interval, "3600"]
env:
- {name: HERMES_HOME, value: /opt/data}
- {name: HERMES_AUTH_FILE, value: /runtime-access/hermes-auth.json}
- {name: HOME, value: /opt/data/home}
- {name: CODEX_HOME, value: /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value: /runtime-access/claude}
- {name: PYTHONPATH, value: /opt/hermes}
- {name: HERMES_ROUTING_CATALOG_PATH, value: /routing-catalog/catalog.json}
- {name: HERMES_CASSANDRA_ACTIVE_WORKTREE, value: /opt/data/workspace/projects/cassandra-hermes-v69}
- {name: PATH, value: /opt/coordinator:/opt/data/tools/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin}
securityContext:
allowPrivilegeEscalation: false
runAsUser: 10000
runAsGroup: 10000
seccompProfile:
type: RuntimeDefault
volumeMounts:
- {name: home, mountPath: /opt/data}
- {name: runtime-access, mountPath: /runtime-access}
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
- {name: auth-patch, mountPath: /opt/hermes/hermes_cli/auth.py, subPath: auth.py}
- {name: routing-catalog, mountPath: /routing-catalog}
resources:
requests: {cpu: 25m, memory: 64Mi}
limits: {cpu: 250m, memory: 512Mi}
- 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}
- {name: runtime-access, mountPath: /runtime-access/claude, subPath: claude, readOnly: true}
- {name: runtime-access, mountPath: /runtime-access/codex, subPath: codex, readOnly: true}
resources:
requests: {cpu: 10m, memory: 32Mi}
limits: {cpu: 100m, memory: 128Mi}
- 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}
- {name: ATLAS_AI_CLAUDE_CREDENTIALS, value: /runtime-access/claude/.credentials.json}
- {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:
- {name: home, mountPath: /opt/data/tools, subPath: tools, readOnly: true}
- {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}
- name: image-broker
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
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}
- {name: HERMES_AUTH_FILE, value: /runtime-access/hermes-auth.json}
- {name: HOME, value: /opt/data/home}
- {name: CODEX_HOME, value: /runtime-access/codex}
- {name: PYTHONPATH, value: /opt/hermes}
- {name: HERMES_IMAGE_BROKER_DEFAULT_MODEL, value: atlas-image-auto-high}
- {name: HERMES_IMAGE_BROKER_LISTEN_PORT, value: "9002"}
- {name: HERMES_IMAGE_BROKER_KEY_FILE, value: /runtime-access/chat-relay-key}
- {name: HERMES_LOCAL_IMAGE_URL, value: http://hermes-local-image.hermes.svc.cluster.local:9004}
- {name: HERMES_IMAGE_POLICY_PATH, value: /etc/hermes-image-policy/policy.json}
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}
- {name: runtime-access, mountPath: /runtime-access}
- {name: coordinator, mountPath: /opt/coordinator, readOnly: true}
- {name: codex-runtime-patch, mountPath: /opt/hermes/agent/auxiliary_client.py, subPath: auxiliary_client.py}
- {name: image-policy, mountPath: /etc/hermes-image-policy, readOnly: true}
- {name: tmp, mountPath: /tmp}
resources:
requests: {cpu: 50m, memory: 128Mi}
limits: {cpu: "1", memory: 1Gi}
- name: codex-broker
image: registry.bstein.dev/bstein/hermes-agent@sha256:81970563e542f0720773e72297810b3a844b83e381e278f25c0916c78d930107
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}
- {name: HERMES_AUTH_FILE, value: /runtime-access/hermes-auth.json}
- {name: HOME, value: /opt/data/home}
- {name: CODEX_HOME, value: /runtime-access/codex}
- {name: PYTHONPATH, value: /opt/hermes}
- {name: HERMES_CODEX_BROKER_LISTEN_PORT, value: "9003"}
- {name: HERMES_CODEX_BROKER_KEY_FILE, value: /runtime-access/chat-relay-key}
- {name: HERMES_CODEX_HEALTH_PATH, value: /opt/data/provider-health/codex.json}
- {name: HERMES_ROUTING_CATALOG_PATH, value: /routing-catalog/catalog.json}
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}
- {name: runtime-access, mountPath: /runtime-access}
- {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}
- {name: routing-catalog, mountPath: /routing-catalog, readOnly: true}
resources:
requests: {cpu: 50m, memory: 128Mi}
limits: {cpu: "1", memory: 1Gi}
- 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}
- {name: HERMES_AUTH_FILE, value: /runtime-access/hermes-auth.json}
- {name: HOME, value: /opt/data/home}
- {name: CODEX_HOME, value: /runtime-access/codex}
- {name: CLAUDE_CONFIG_DIR, value: /runtime-access/claude}
- {name: PYTHONPATH, value: /opt/hermes}
- {name: PATH, value: /opt/coordinator:/opt/data/tools/bin:/opt/data/home/.local/bin:/opt/hermes/.venv/bin:/usr/local/bin:/usr/bin:/bin}
- {name: HERMES_CLAUDE_BIN, value: /opt/coordinator/claude}
- {name: HERMES_CLAUDE_BROKER_PORT, value: "9006"}
- {name: HERMES_CLAUDE_BROKER_KEY_FILE, value: /runtime-access/chat-relay-key}
- {name: HERMES_CLAUDE_BROKER_READ_TIMEOUT, value: "1800"}
- {name: HERMES_CLAUDE_BROKER_CONCURRENCY, value: "2"}
- {name: HERMES_CLAUDE_HEALTH_PATH, value: /opt/data/provider-health/claude.json}
- {name: HERMES_ROUTING_CATALOG_PATH, value: /routing-catalog/catalog.json}
readinessProbe:
tcpSocket: {port: claude-broker}
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket: {port: claude-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}
- {name: runtime-access, mountPath: /runtime-access}
- {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:
requests: {cpu: 100m, memory: 256Mi}
limits: {cpu: "3", memory: 3Gi}
volumes:
- name: home
persistentVolumeClaim:
claimName: hermes-agent-home
- name: runtime-access
emptyDir:
medium: Memory
sizeLimit: 128Mi
- name: routing-catalog
persistentVolumeClaim:
claimName: hermes-routing-catalog
- name: config
configMap:
name: hermes-agent-config
- name: coordinator
configMap:
name: hermes-coordinator
defaultMode: 0555
- name: kubeconfig
configMap:
name: hermes-agent-kubeconfig
defaultMode: 0444
- name: auth-patch
emptyDir: {}
- name: tui-gateway-patch
emptyDir: {}
- name: api-server-patch
emptyDir: {}
- name: web-server-patch
emptyDir: {}
- name: subprocess-secret-patch
emptyDir: {}
- name: codex-runtime-patch
emptyDir: {}
- name: auto-router-plugin
configMap:
name: hermes-auto-router-plugin
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}
- name: image-policy
configMap:
name: hermes-image-policy
- name: tmp
emptyDir:
sizeLimit: 4Gi
- name: oauth-tmp
emptyDir:
sizeLimit: 64Mi
- name: allowlist
configMap:
name: hermes-owner-allowlist
- name: oauth-templates
configMap:
name: hermes-chat-oauth-templates
- name: ttyd-index
emptyDir:
sizeLimit: 2Mi