- Updated pod.spec.securityContext and all container/initContainer securityContext to be fully compliant with restricted:latest (runAsNonRoot: true, allowPrivilegeEscalation: false, runAsUser: 1000, capabilities drop ALL, seccomp RuntimeDefault, fsGroup) - Changed initContainer from root chown to non-root mkdir/chmod relying on fsGroup (avoids PSA violation) - Updated default model to grok-4.20-0309-reasoning (per xAI switch note) - Added automountServiceAccountToken: false and imagePullPolicy for best practices (matches openclaw deployment pattern) - hermes-webui now runs as non-root with WANTED_UID matching This should resolve the container not starting. Leave PR open for review before merge.
216 lines
No EOL
6.1 KiB
YAML
216 lines
No EOL
6.1 KiB
YAML
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: hermes-agent
|
|
namespace: customer1
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: hermes-agent
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: hermes-agent
|
|
spec:
|
|
automountServiceAccountToken: false
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
runAsUser: 1000
|
|
runAsGroup: 1000
|
|
fsGroup: 1000
|
|
seccompProfile:
|
|
type: RuntimeDefault
|
|
initContainers:
|
|
- name: fix-webui-perms
|
|
image: busybox:1.37
|
|
imagePullPolicy: IfNotPresent
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
runAsUser: 1000
|
|
runAsGroup: 1000
|
|
allowPrivilegeEscalation: false
|
|
capabilities:
|
|
drop:
|
|
- ALL
|
|
seccompProfile:
|
|
type: RuntimeDefault
|
|
command:
|
|
- sh
|
|
- -c
|
|
args:
|
|
- |
|
|
mkdir -p /data/.hermes/webui /data/.cache /data/.config /data/bin
|
|
chmod -R g+rwX,o-rwx /data
|
|
echo "✅ Hermes data permissions fixed (non-root with fsGroup)"
|
|
volumeMounts:
|
|
- name: hermes-data
|
|
mountPath: /data
|
|
containers:
|
|
- name: hermes-agent
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
runAsUser: 1000
|
|
runAsGroup: 1000
|
|
allowPrivilegeEscalation: false
|
|
readOnlyRootFilesystem: false
|
|
capabilities:
|
|
drop:
|
|
- ALL
|
|
seccompProfile:
|
|
type: RuntimeDefault
|
|
image: nousresearch/hermes-agent:latest
|
|
command: ["/bin/bash", "-c"]
|
|
args:
|
|
- |
|
|
set -euo pipefail
|
|
echo "Hermes Agent starting (Telegram polling + full config persistence)..."
|
|
|
|
HERMES_BIN="/opt/hermes/.venv/bin/hermes"
|
|
|
|
if [ ! -x "$HERMES_BIN" ]; then
|
|
echo "❌ hermes binary NOT FOUND at $HERMES_BIN"
|
|
echo "Searching filesystem..."
|
|
find / -name hermes -type f 2>/dev/null | head -10 || true
|
|
exit 1
|
|
fi
|
|
|
|
echo "✅ Found hermes at: $HERMES_BIN"
|
|
|
|
# Copy config
|
|
cp -f /tmp/hermes/config.yaml /opt/data/config.yaml || true
|
|
|
|
# Create symlink in a writable location (PVC) so it works when you exec in
|
|
mkdir -p /opt/data/bin
|
|
ln -sf "$HERMES_BIN" /opt/data/bin/hermes || true
|
|
export PATH="/opt/data/bin:$PATH"
|
|
|
|
echo "🚀 Starting hermes gateway..."
|
|
exec "$HERMES_BIN" gateway run
|
|
env:
|
|
# === Hermes Home - REQUIRED for config persistence ===
|
|
- name: HERMES_HOME
|
|
value: "/opt/data"
|
|
- name: HOME
|
|
value: "/opt/data"
|
|
- name: XDG_CACHE_HOME
|
|
value: "/opt/data/.cache"
|
|
- name: XDG_CONFIG_HOME
|
|
value: "/opt/data/.config"
|
|
|
|
# === Telegram Configuration (polling only - no public exposure) ===
|
|
- name: TELEGRAM_BOT_TOKEN
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: hermes-secrets
|
|
key: TELEGRAM_BOT_TOKEN
|
|
- name: XAI_API_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: xai-apikey
|
|
key: XAI_API_KEY
|
|
|
|
- name: TELEGRAM_ALLOWED_USERS
|
|
value: "7528130947"
|
|
|
|
# === Local vLLM (OpenAI-compatible) ===
|
|
- name: OPENAI_BASE_URL
|
|
value: "http://openclaw-brain-service.customer1.svc.cluster.local:8000/v1" # ← adjust if your service name differs
|
|
|
|
- name: HERMES_MODEL_PROVIDER
|
|
value: xai
|
|
|
|
- name: HERMES_MODEL
|
|
value: grok-4.20-0309-reasoning
|
|
|
|
- name: OPENAI_API_KEY
|
|
value: "dummy" # vLLM ignores this
|
|
|
|
# === API Server (for external OpenAI-compatible clients) ===
|
|
- name: API_SERVER_ENABLED
|
|
value: "true"
|
|
- name: API_SERVER_HOST
|
|
value: "0.0.0.0"
|
|
- name: API_SERVER_PORT
|
|
value: "8642"
|
|
- name: API_SERVER_KEY
|
|
value: "" # empty = no auth (tailnet-only, private)
|
|
- name: API_SERVER_MODEL_NAME
|
|
value: "hermes-agent"
|
|
|
|
# === Optional ===
|
|
# - name: LOG_LEVEL
|
|
# value: "INFO"
|
|
volumeMounts:
|
|
- name: hermes-data
|
|
mountPath: /opt/data
|
|
- name: hermes-configmap
|
|
mountPath: /tmp/hermes/config.yaml
|
|
subPath: config.yaml
|
|
readOnly: true
|
|
resources:
|
|
requests:
|
|
memory: 2Gi
|
|
cpu: "1"
|
|
limits:
|
|
memory: 4Gi
|
|
cpu: "2"
|
|
|
|
- name: hermes-webui
|
|
image: ghcr.io/nesquena/hermes-webui:latest
|
|
ports:
|
|
- containerPort: 8787
|
|
env:
|
|
- name: HERMES_HOME
|
|
value: "/home/hermeswebui/.hermes"
|
|
- name: HERMES_WEBUI_HOST
|
|
value: "0.0.0.0"
|
|
- name: HERMES_WEBUI_PORT
|
|
value: "8787"
|
|
- name: HERMES_WEBUI_STATE_DIR
|
|
value: "/home/hermeswebui/.hermes/webui"
|
|
- name: WANTED_UID
|
|
value: "1000"
|
|
- name: WANTED_GID
|
|
value: "1000"
|
|
volumeMounts:
|
|
- name: hermes-data
|
|
mountPath: /home/hermeswebui/.hermes
|
|
|
|
resources:
|
|
requests:
|
|
memory: 256Mi
|
|
cpu: "100m"
|
|
limits:
|
|
memory: 512Mi
|
|
cpu: "500m"
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
runAsUser: 1000
|
|
runAsGroup: 1000
|
|
allowPrivilegeEscalation: false
|
|
readOnlyRootFilesystem: false
|
|
capabilities:
|
|
drop:
|
|
- ALL
|
|
seccompProfile:
|
|
type: RuntimeDefault
|
|
volumes:
|
|
- name: hermes-configmap
|
|
configMap:
|
|
name: hermes-config
|
|
- name: hermes-data
|
|
persistentVolumeClaim:
|
|
claimName: hermes-agent-pvc
|
|
---
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: hermes-agent-pvc
|
|
namespace: customer1
|
|
spec:
|
|
accessModes:
|
|
- ReadWriteOnce
|
|
resources:
|
|
requests:
|
|
storage: 25Gi |