예제
각 예제는 부분(partial) values 파일이며, 차트의 기본 values.yaml 위에 함께 적용합니다.
values-a2a.yaml¶
charts/hermes-agent/values-a2a.yaml
# values-a2a.yaml
#
# Runs Hermes' A2A (Agent-to-Agent) inbound listener behind an explicit
# ClusterIP Service port, so another A2A-compatible agent (another Hermes,
# LangChain, CrewAI, Google ADK, or anything on the official a2a-sdk) can
# discover and drive this one.
#
# Unlike the API server and webhook receiver, A2A has no dedicated chart
# values or env var toggle: upstream's only on-switch is the
# `gateway.platforms.a2a` block in config.yaml, so this overlay sets it
# directly through the chart's existing free-form `config:` passthrough
# (see the "A2A (Agent-to-Agent) listener" section in README.md).
#
# The external Secret below must contain OPENAI_API_KEY and A2A_BEARER_TOKEN.
# Do not commit real keys.
#
# kubectl create secret generic hermes-agent-a2a-secrets \
# --namespace hermes-agent \
# --from-literal=OPENAI_API_KEY='sk-<real>' \
# --from-literal=A2A_BEARER_TOKEN='<long-random-token>'
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-a2a.yaml --wait
config:
model:
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
gateway:
platforms:
a2a:
enabled: true
extra:
port: 9900
extraEnv:
# Upstream defaults A2A_HOST to 127.0.0.1; a Kubernetes Service needs a
# non-loopback address. Upstream itself only widens the bind once an auth
# token (A2A_BEARER_TOKEN or A2A_PEER_TOKENS) is configured - set below.
- name: A2A_HOST
value: "0.0.0.0"
- name: A2A_PORT
value: "9900"
extraEnvFrom:
- secretRef:
name: hermes-agent-a2a-secrets
service:
enabled: true
type: ClusterIP
ports:
- name: a2a
port: 9900
values-anthropic-and-discord.yaml¶
charts/hermes-agent/values-anthropic-and-discord.yaml
# values-anthropic-and-discord.yaml
#
# Hermes Agent using Anthropic (Claude) as the model provider AND running as a
# Discord bot: both wired in one file.
#
# All secrets below are DUMMY placeholders. Do NOT commit real keys: override
# them at install time (--set-string) or inject via a SealedSecret + extraEnvFrom
# (see examples/argocd/).
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-anthropic-and-discord.yaml \
# --set-string env.ANTHROPIC_API_KEY='sk-ant-<real>' \
# --set-string env.DISCORD_BOT_TOKEN='<real-bot-token>' --wait
config:
model:
provider: anthropic
# A current Claude model id: see https://docs.anthropic.com for the list
# (e.g. claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5).
default: claude-sonnet-4-6
terminal:
backend: local
env:
# --- Model provider (Anthropic) -----------------------------------------
ANTHROPIC_API_KEY: "sk-ant-DUMMY_replace_me_0000000000000000000000"
# Unused by the anthropic provider; overrides the chart's OpenAI placeholder.
OPENAI_API_KEY: "unused"
# --- Discord bot (secret bits) ------------------------------------------
# Setting the token is enough to auto-enable Discord: no config.yaml change.
# Create the bot at https://discord.com/developers/applications, enable the
# "Message Content Intent", and invite it to your server.
DISCORD_BOT_TOKEN: "MTA0DUMMYtoken000000000000.DUMMY.replace_me_with_a_real_token"
# Non-secret Discord knobs go here (plain env, not the Secret).
extraEnv:
- name: DISCORD_HOME_CHANNEL # channel id for cron / notification delivery
value: "000000000000000000" # DUMMY - your channel id (18 digits)
- name: DISCORD_ALLOWED_USERS # comma-separated user ids allowed to talk
value: "111111111111111111" # DUMMY - your Discord user id
- name: DISCORD_ALLOW_ALL_USERS # true only for throwaway/dev bots
value: "false"
values-anthropic.yaml¶
charts/hermes-agent/values-anthropic.yaml
# values-anthropic.yaml
#
# Hermes Agent using Anthropic (Claude) as the model provider.
# Dummy key: override at install time.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-anthropic.yaml \
# --set-string env.ANTHROPIC_API_KEY='sk-ant-<real>' --wait
config:
model:
provider: anthropic
# A current Claude model id: see https://docs.anthropic.com for the list
# (e.g. claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5).
default: claude-sonnet-4-6
terminal:
backend: local
env:
ANTHROPIC_API_KEY: "sk-ant-DUMMY_replace_me_0000000000000000000000"
# Unused by the anthropic provider; overrides the chart's OpenAI placeholder.
OPENAI_API_KEY: "unused"
values-api-server-and-webhook.yaml¶
charts/hermes-agent/values-api-server-and-webhook.yaml
# values-api-server-and-webhook.yaml
#
# Runs Hermes' OpenAI-compatible API server and generic webhook receiver behind
# explicit ClusterIP Service ports. The external Secret below must contain
# OPENAI_API_KEY, API_SERVER_KEY, and WEBHOOK_SECRET. Do not commit real keys.
#
# kubectl create secret generic hermes-agent-listener-secrets \
# --namespace hermes-agent \
# --from-literal=OPENAI_API_KEY='sk-<real>' \
# --from-literal=API_SERVER_KEY='<long-random-token>' \
# --from-literal=WEBHOOK_SECRET='<long-random-secret>'
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-api-server-and-webhook.yaml --wait
config:
model:
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
extraEnvFrom:
- secretRef:
name: hermes-agent-listener-secrets
apiServer:
enabled: true
host: 0.0.0.0
port: 8642
corsOrigins: "https://chat.example.com"
webhook:
enabled: true
port: 8644
service:
enabled: true
type: ClusterIP
ports:
- name: api-server
port: 8642
- name: webhook
port: 8644
values-bitwarden.yaml¶
charts/hermes-agent/values-bitwarden.yaml
# values-bitwarden.yaml
#
# Hermes Agent with Bitwarden Secrets Manager as the source of provider keys.
# The Kubernetes Secret contains only the Bitwarden machine-account token;
# provider keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, Discord bot tokens, etc.)
# stay in the selected Bitwarden project and Hermes fetches them at startup.
#
# 1. Create a Bitwarden Secrets Manager machine account with read access to a
# project whose secret names are the environment variables Hermes expects.
# 2. Create the bootstrap-token Secret (do not commit the token):
# kubectl create secret generic bitwarden-bootstrap \
# --namespace hermes-agent \
# --from-literal=BWS_ACCESS_TOKEN='0.<machine-account-token>'
# 3. Install this overlay:
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-bitwarden.yaml --wait
#
# On first startup Hermes downloads its checksum-verified `bws` CLI into the
# persistent HERMES_HOME volume. The pod therefore needs egress to Bitwarden
# and GitHub Releases; subsequent starts reuse the binary.
config:
model:
provider: openai-api
default: gpt-4o-mini
secrets:
bitwarden:
enabled: true
# Bitwarden Secrets Manager project UUID containing provider keys.
project_id: "REPLACE_ME_BITWARDEN_PROJECT_UUID"
# Optional endpoint override (upstream default: US Cloud).
# server_url: "https://vault.bitwarden.com"
cache_ttl_seconds: 300
# Bitwarden is the source of truth for the provider keys it supplies.
override_existing: true
terminal:
backend: local
env:
# Bitwarden replaces this chart placeholder with OPENAI_API_KEY from its
# project. Keep it only to override the chart's default OpenAI placeholder.
OPENAI_API_KEY: "unused"
# The bootstrap token is an externally managed Kubernetes Secret, not a Helm
# value. Hermes protects BWS_ACCESS_TOKEN from replacement by Bitwarden.
extraEnvFrom:
- secretRef:
name: bitwarden-bootstrap
values-deepinfra.yaml¶
charts/hermes-agent/values-deepinfra.yaml
# values-deepinfra.yaml
#
# Hermes Agent using DeepInfra's built-in OpenAI-compatible provider.
# Dummy key: override at install time. Use a model currently listed by
# DeepInfra's /v1/openai/models endpoint for config.model.default.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-deepinfra.yaml \
# --set-string env.DEEPINFRA_API_KEY='<real>' --wait
config:
model:
provider: deepinfra
default: deepseek-ai/DeepSeek-V4-Flash
terminal:
backend: local
env:
DEEPINFRA_API_KEY: "DUMMY_replace_me_0000000000000000000000"
# Optional endpoint override (upstream default: https://api.deepinfra.com/v1/openai).
# DEEPINFRA_BASE_URL: "https://api.deepinfra.com/v1/openai"
# Unused by the deepinfra provider; overrides the chart's OpenAI placeholder.
OPENAI_API_KEY: "unused"
values-fireworks.yaml¶
charts/hermes-agent/values-fireworks.yaml
# values-fireworks.yaml
#
# Hermes Agent using Fireworks AI's built-in OpenAI-compatible provider.
# Dummy key: override at install time.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-fireworks.yaml \
# --set-string env.FIREWORKS_API_KEY='fw-<real>' --wait
config:
model:
provider: fireworks
# Fireworks model IDs use its native accounts/fireworks/models/... form.
default: accounts/fireworks/models/glm-5p2
terminal:
backend: local
env:
FIREWORKS_API_KEY: "fw-DUMMY_replace_me_000000000000000000000000"
# Unused by the fireworks provider; overrides the chart's OpenAI placeholder.
OPENAI_API_KEY: "unused"
values-gemini.yaml¶
charts/hermes-agent/values-gemini.yaml
# values-gemini.yaml
#
# Hermes Agent using Google Gemini as the model provider.
# Dummy key: override at install time.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-gemini.yaml \
# --set-string env.GOOGLE_API_KEY='<real>' --wait
config:
model:
provider: gemini
default: gemini-2.5-flash
terminal:
backend: local
env:
GOOGLE_API_KEY: "DUMMY_replace_me_0000000000000000000000"
# Unused by the gemini provider; overrides the chart's OpenAI placeholder.
OPENAI_API_KEY: "unused"
values-github-copilot.yaml¶
charts/hermes-agent/values-github-copilot.yaml
# values-github-copilot.yaml
#
# Hermes Agent backed by GitHub Copilot, authenticated at startup via the OAuth
# 2.0 Device Authorization Grant (RFC 8628): no API key to paste. The
# "auth-device-login" init container surfaces a verification link + code to your
# Discord home channel, waits for you to approve it on github.com (phone is
# fine), then persists the resulting token to HERMES_HOME/.env where Hermes
# reads it natively. The token lives on the persistent volume, so restarts are
# fast; re-login only happens when it is missing or revoked.
#
# Copilot's token API rejects PATs: a device-flow `gho_`/`ghu_` token is
# required, which is exactly what this flow produces.
#
# All secrets below are DUMMY placeholders. Do NOT commit real keys: override
# them at install time (--set-string) or inject via a SealedSecret + extraEnvFrom
# (see examples/argocd/).
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-github-copilot.yaml \
# --set-string env.DISCORD_BOT_TOKEN='<real-bot-token>' --wait
#
# # then watch the login init container for the verification prompt:
# kubectl logs deploy/hermes-agent -n hermes-agent -c auth-device-login -f
config:
model:
# Hermes' built-in GitHub Copilot provider (calls the Copilot token API).
provider: copilot
# Any model your Copilot subscription can reach. Examples: gpt-4o, gpt-4.1,
# claude-sonnet-4.5, gemini-2.5-pro, gpt-5.
default: gpt-4o
terminal:
backend: local
# Authenticate the Copilot credential via the OAuth device flow at startup.
auth:
deviceFlow:
enabled: true
provider: github-copilot
# Deliver the verification link + code to the agent's Discord home channel
# (reuses DISCORD_BOT_TOKEN + DISCORD_HOME_CHANNEL). It is always also
# printed to the init container logs as a fallback.
notify: discord
env:
# The chart's default placeholder is for OpenAI; this deployment doesn't use
# it (the Copilot token is fetched at runtime via device flow). Set to a clear
# sentinel so no real OpenAI key is implied.
OPENAI_API_KEY: "unused"
# --- Discord bot (secret bits) ------------------------------------------
# Setting the token is enough to auto-enable Discord: no config.yaml change.
# The login init container reuses this same bot to post the verification link.
# Create the bot at https://discord.com/developers/applications, enable the
# "Message Content Intent", and invite it to your server.
DISCORD_BOT_TOKEN: "MTA0DUMMYtoken000000000000.DUMMY.replace_me_with_a_real_token"
# Non-secret Discord knobs go here (plain env, not the Secret). The login init
# container also reads DISCORD_HOME_CHANNEL from here to know where to post.
extraEnv:
- name: DISCORD_HOME_CHANNEL # channel id for cron / notification / login delivery
value: "000000000000000000" # DUMMY - your channel id (18 digits)
- name: DISCORD_ALLOWED_USERS # comma-separated user ids allowed to talk
value: "111111111111111111" # DUMMY - your Discord user id
- name: DISCORD_ALLOW_ALL_USERS # true only for throwaway/dev bots
value: "false"
# Persistence is required for device-flow login: the token is written here so it
# survives restarts (otherwise you would re-approve on every restart). Empty
# storageClass = cluster default; on a Raspberry Pi cluster that is typically
# local-path (k3s) or microk8s-hostpath: both ReadWriteOnce, which is exactly
# what this single-writer workload wants.
persistence:
enabled: true
storageClass: ""
accessModes:
- ReadWriteOnce
size: 5Gi
# Defaults are already tuned for small arm64 nodes; shown here for visibility.
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
values-google-chat.yaml¶
charts/hermes-agent/values-google-chat.yaml
# values-google-chat.yaml
#
# Hermes Agent as a Google Chat bot, receiving events over a Cloud Pub/Sub
# PULL subscription. Pub/Sub pull means outbound connections only: no inbound
# Service, Ingress or public endpoint is needed, so the listeners stay off.
# Requires hermes-agent >= v2026.9.14, whose published image ships the
# google-chat dependencies (no first-boot install).
#
# Prerequisites (upstream guide, "Google Chat Setup"):
# https://hermes-agent.nousresearch.com/docs/user-guide/messaging/google_chat
# - A Google Workspace Chat app whose connection setting is Cloud Pub/Sub.
# - A Pub/Sub topic. On the TOPIC, grant `Pub/Sub Publisher` to
# chat-api-push@system.gserviceaccount.com, or Chat can never deliver.
# - A pull subscription. On the SUBSCRIPTION, grant your own service account
# `Pub/Sub Subscriber` AND `Pub/Sub Viewer` (Hermes checks the subscription
# at startup). Do not grant project-level Pub/Sub roles.
#
# The service-account JSON is a FILE, mounted from a Secret you create:
#
# kubectl create secret generic google-chat-sa \
# --namespace hermes-agent \
# --from-file=sa.json=/path/to/key.json
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-google-chat.yaml \
# --set-string env.OPENAI_API_KEY='sk-<real>' --wait
#
# Everything below is a placeholder. Never commit real project IDs, emails,
# space IDs or keys.
config:
model:
# Any provider works; OpenAI is only the example. `openai` is NOT valid
# here (it aliases to OpenRouter), the built-in key is `openai-api`.
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
env:
OPENAI_API_KEY: "sk-DUMMY_replace_me_000000000000000000000000"
# Google Chat settings are not secret, so they go in as plain env vars. The
# only credential is the mounted service-account file.
extraEnv:
- name: GOOGLE_CHAT_PROJECT_ID
value: "REPLACE_ME_GCP_PROJECT"
- name: GOOGLE_CHAT_SUBSCRIPTION_NAME
value: "projects/REPLACE_ME_GCP_PROJECT/subscriptions/hermes-chat-events-sub"
# Path inside the container to the JSON mounted below.
- name: GOOGLE_CHAT_SERVICE_ACCOUNT_JSON
value: /var/run/secrets/google-chat/sa.json
# Explicit allowlist of Workspace emails that may talk to the bot. Keep it
# narrow: this bot can run commands inside the pod.
- name: GOOGLE_CHAT_ALLOWED_USERS
value: "you@example.com"
# Optional: default destination for cron job output.
# - name: GOOGLE_CHAT_HOME_CHANNEL
# value: "spaces/REPLACE_ME"
# The Secret volume's default mode (0644) lets the Hermes runtime user
# (uid 10000) read the key. If you set a tighter defaultMode such as 0440,
# also give the pod `podSecurityContext.fsGroup: 10000`.
extraVolumes:
- name: google-chat-sa
secret:
secretName: google-chat-sa
extraVolumeMounts:
- name: google-chat-sa
mountPath: /var/run/secrets/google-chat
readOnly: true
# Native file attachments need a separate, per-user OAuth setup
# (`/setup-files` in chat, upstream guide Step 10). Plain text messaging
# works without it, and this example does not configure it.
values-google-vertex.yaml¶
charts/hermes-agent/values-google-vertex.yaml
# values-google-vertex.yaml
#
# Hermes Agent using Google Vertex AI as the model provider (Gemini models via
# Vertex's OpenAI-compatible endpoint). Requires hermes-agent >= v2026.7.1.
#
# Vertex has NO static API key: every request needs a short-lived OAuth2 access
# token, which Hermes mints and auto-refreshes from a service-account JSON (or
# Application Default Credentials). So unlike the other provider examples, the
# credential here is a FILE mounted into the pod, not an env var:
#
# 1. Create a GCP service account with the "Vertex AI User" role and download
# its JSON key.
# 2. Put the JSON into a Kubernetes Secret:
# kubectl create secret generic vertex-sa \
# --namespace hermes-agent \
# --from-file=sa.json=/path/to/key.json
# 3. Install:
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-google-vertex.yaml \
# --set-string config.vertex.project_id='<your-gcp-project>' --wait
config:
model:
provider: vertex
default: google/gemini-2.5-flash
# Non-secret Vertex settings live in config.yaml; only the credential file
# path goes through the environment (VERTEX_CREDENTIALS_PATH below).
vertex:
project_id: "REPLACE_ME_GCP_PROJECT"
# "global" uses the global endpoint; set a specific region (e.g.
# us-central1) to pin data residency / regional capacity.
region: "global"
terminal:
backend: local
env:
# No Vertex API key exists: tokens are minted from the mounted SA JSON.
# This only overrides the chart's OpenAI placeholder.
OPENAI_API_KEY: "unused"
extraEnv:
# Path (inside the container) to the service-account JSON mounted below.
# Omit it to fall back to ADC (GOOGLE_APPLICATION_CREDENTIALS / metadata
# server, e.g. GKE Workload Identity).
- name: VERTEX_CREDENTIALS_PATH
value: /var/run/secrets/vertex/sa.json
# The vertex provider needs `google-auth`, which upstream ships as an opt-in
# extra handled by its lazy-install mechanism: it is NOT baked into the
# image, and the image disables lazy installs by default. Re-enable them so
# the first Vertex call can install it into HERMES_LAZY_INSTALL_TARGET on
# the persistent volume (one-time, needs network egress; survives restarts).
- name: HERMES_DISABLE_LAZY_INSTALLS
value: "0"
# Mount the service-account Secret created in step 2.
extraVolumes:
- name: vertex-sa
secret:
secretName: vertex-sa
extraVolumeMounts:
- name: vertex-sa
mountPath: /var/run/secrets/vertex
readOnly: true
# NOTE: as of a recent Hermes security hardening, VERTEX_CREDENTIALS_PATH and
# GOOGLE_APPLICATION_CREDENTIALS are stripped from the environment of
# subprocesses the agent spawns (terminal, execute_code, browser,
# computer_use): they still work for the model's own API calls above. If a
# tool call in your session needs Vertex credentials too (e.g. shelling out to
# `gcloud`), re-allow it explicitly:
#
# config:
# tools:
# env_passthrough: ["VERTEX_CREDENTIALS_PATH"]
values-hardened.yaml¶
charts/hermes-agent/values-hardened.yaml
# values-hardened.yaml
#
# Pod Security Standards `restricted`-compliant overlay. CI-verified against
# the pinned image (see the "investigation" section of the issue that added
# this file): non-root and a read-only rootfs both start cleanly once /run
# and /tmp are writable+executable tmpfs mounts - s6-overlay's own init
# binary lives under /run and needs to exec from there.
#
# kubectl create namespace hermes-agent
# kubectl label namespace hermes-agent \
# pod-security.kubernetes.io/enforce=restricted \
# pod-security.kubernetes.io/enforce-version=latest
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent \
# -f charts/hermes-agent/values-hardened.yaml \
# --set-string env.OPENAI_API_KEY='sk-...' --wait
#
# Caveat: some CNI/container-runtime combinations mount tmpfs volumes
# `noexec` by default, which breaks s6-overlay's own init the same way a
# read-only rootfs without an execable /run does. Kubernetes' own `emptyDir`
# does not set `noexec` unless the node/runtime does something unusual - if
# this overlay fails to start, check that first.
#
# Do NOT stack this file with another example that also sets `extraVolumes`/
# `extraVolumeMounts` (e.g. values-google-vertex.yaml, values-shared-knowledge.yaml)
# via `-f values-hardened.yaml -f values-other.yaml`: Helm replaces array
# values wholesale rather than merging them, so whichever file is listed last
# silently wins and the other's mounts vanish - here, that means losing the
# /run and /tmp tmpfs mounts with no error, and the pod fails to boot under a
# read-only rootfs exactly like the unmounted case this file's own
# investigation documented. Copy this file's `extraVolumes`/`extraVolumeMounts`
# entries into the other values file (merging the lists yourself) instead.
config:
model:
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
podSecurityContext:
runAsNonRoot: true
runAsUser: 10000
runAsGroup: 10000
fsGroup: 10000
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
# The rootfs itself is read-only; s6-overlay needs /run writable+executable
# to boot, and the agent's own tooling needs a writable /tmp. HERMES_HOME
# (persistence.mountPath) is already a separate PVC, not rootfs, so it
# doesn't need to be listed here.
# sizeLimit bounds these - a medium:Memory emptyDir is RAM-backed and
# uncapped by default, which would otherwise leave a hardening profile with
# an unbounded memory-exhaustion vector.
extraVolumes:
- name: run-tmpfs
emptyDir:
medium: Memory
sizeLimit: 16Mi
- name: tmp-tmpfs
emptyDir:
medium: Memory
sizeLimit: 64Mi
extraVolumeMounts:
- name: run-tmpfs
mountPath: /run
- name: tmp-tmpfs
mountPath: /tmp
team:
sharedVolume:
permissions:
# The ownership-preparation init container needs root (chown across
# arbitrary storage backends) - incompatible with `restricted`. Leave
# disabled and rely on `podSecurityContext.fsGroup` above instead, when
# the storage backend honours fsGroup.
enabled: false
# auth.deviceFlow's login init container can also run non-root under
# `restricted`, but only once its target uid already owns (or matches
# tokenOwner, making the internal chown a same-uid no-op) the destination
# path - left disabled here since it needs a real interactive login, which
# CI cannot perform. To harden it too, set the same restricted-required
# fields as the main securityContext above, not just runAsUser/runAsGroup:
#
# auth:
# deviceFlow:
# enabled: true
# securityContext:
# runAsUser: 10000
# runAsGroup: 10000
# allowPrivilegeEscalation: false
# capabilities:
# drop: [ALL]
values-httproute.yaml¶
charts/hermes-agent/values-httproute.yaml
# values-httproute.yaml
#
# Routes Hermes' API server and generic webhook receiver through a Gateway API
# HTTPRoute. The cluster must already have the Gateway API CRD and a Gateway
# named hermes-gateway. Create hermes-agent-listener-secrets with
# OPENAI_API_KEY, API_SERVER_KEY, and WEBHOOK_SECRET before installing. Do not
# commit real credentials.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-httproute.yaml --wait
config:
model:
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
extraEnvFrom:
- secretRef:
name: hermes-agent-listener-secrets
apiServer:
enabled: true
host: 0.0.0.0
port: 8642
corsOrigins: "https://chat.example.com"
webhook:
enabled: true
port: 8644
service:
enabled: true
ports:
- name: api-server
port: 8642
- name: webhook
port: 8644
httpRoute:
enabled: true
parentRefs:
- name: hermes-gateway
sectionName: https
hostnames:
- api.example.com
- webhooks.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /v1
backendRefs:
- port: 8642
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- port: 8644
values-ingress-listeners.yaml¶
charts/hermes-agent/values-ingress-listeners.yaml
# values-ingress-listeners.yaml
#
# Routes Hermes' API server and generic webhook receiver through different
# Ingress hosts and Service ports. Create hermes-agent-listener-secrets with
# OPENAI_API_KEY, API_SERVER_KEY, and WEBHOOK_SECRET before installing. Do not
# commit real credentials.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-ingress-listeners.yaml --wait
config:
model:
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
extraEnvFrom:
- secretRef:
name: hermes-agent-listener-secrets
apiServer:
enabled: true
host: 0.0.0.0
port: 8642
corsOrigins: "https://chat.example.com"
webhook:
enabled: true
port: 8644
service:
enabled: true
ports:
- name: api-server
port: 8642
- name: webhook
port: 8644
ingress:
enabled: true
className: nginx
hosts:
- host: api.example.com
paths:
- path: /v1
pathType: Prefix
port: 8642
- host: webhooks.example.com
paths:
- path: /
pathType: Prefix
port: 8644
values-ingress-oauth.yaml¶
charts/hermes-agent/values-ingress-oauth.yaml
# values-ingress-oauth.yaml
#
# Exposes the Hermes management dashboard through an Ingress and signs users in
# with Nous Portal OAuth. Upstream recommends this provider for a dashboard that
# is reachable over the public internet; the bundled username/password provider
# (values-ingress.yaml) is meant for a trusted network or a VPN.
#
# 1. Register the dashboard in the Nous Portal ("Local Dashboards" page, or
# `hermes dashboard register` where a Nous login exists) and copy the client
# id it returns (shape `agent:<id>`). In the portal, set Base URL to the
# dashboard's external origin, https://hermes-agent.example.com, with no path:
# the portal appends /auth/callback itself. (`hermes dashboard register` takes
# the full `--redirect-uri https://hermes-agent.example.com/auth/callback`.)
# A personal-account registration limits sign-in to its owner ("Only you" in
# the portal), so, unlike the OIDC example, no extra access control is needed
# for that case.
# 2. Put that client id in `config.dashboard.oauth.client_id` below. The client
# id is not a secret. The template fails at render time when it is missing;
# if you register from inside the pod instead (the CLI writes
# HERMES_DASHBOARD_OAUTH_CLIENT_ID into the persistent .env), set
# `dashboard.auth.provider: external` so the render-time check is skipped.
# 3. Install, using your own host and ingress controller CIDR:
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-ingress-oauth.yaml \
# --set-string env.OPENAI_API_KEY='sk-<real>' --wait
#
# After sign-in `GET /api/auth/me` reports `provider: nous`. Observed with a
# personal account: `email` and `display_name` come back empty.
#
# Troubleshooting, as observed against the portal: a dashboard whose external
# origin differs from the registered Base URL is rejected by the PORTAL, not by
# the dashboard, with "Authorization failed ... Error: redirect_uri_mismatch:
# redirect_uri does not match the agent's canonical URL or localhost carve-out".
# The portal also requires PKCE; the dashboard sends it, so a hand-built
# authorize request without a code_challenge fails with "invalid_request".
#
# The external origin must match the registered Base URL exactly. It is
# derived from the first Ingress host (https once `ingress.tls` is set); set
# `dashboard.publicUrl` explicitly when it differs. Dummy values below: replace
# them, and never commit real credentials.
config:
model:
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
dashboard:
oauth:
# Client id issued by the Nous Portal for this dashboard.
client_id: "agent:REPLACE_ME"
dashboard:
enabled: true
# Only listed peers may supply X-Forwarded-Proto / X-Forwarded-For. Put your
# ingress controller's pod CIDR (or its exact pod IP) here; without it the
# session cookies are not marked Secure behind a TLS-terminating Ingress.
trustedProxies:
- "10.244.0.0/16"
auth:
provider: oauth
env:
OPENAI_API_KEY: "sk-DUMMY_replace_me_000000000000000000000000"
service:
enabled: true
ingress:
enabled: true
className: nginx
annotations: {}
# cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: hermes-agent.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: hermes-agent-tls
hosts:
- hermes-agent.example.com
values-ingress-oidc.yaml¶
charts/hermes-agent/values-ingress-oidc.yaml
# values-ingress-oidc.yaml
#
# Exposes the Hermes management dashboard through an Ingress and signs users in
# against your own OpenID Connect identity provider (Keycloak, Authentik, Auth0,
# Okta, Google, ...). No Nous Portal is involved. Upstream recommends an
# OAuth/OIDC provider for a dashboard reachable over the public internet; the
# bundled username/password provider (values-ingress.yaml) is meant for a
# trusted network or a VPN.
#
# In your identity provider, register a PUBLIC client (no client secret) that
# uses the authorization-code flow with PKCE (S256), and allow the redirect URI
# https://hermes-agent.example.com/auth/callback
# Then set the issuer and client id below. The issuer must be HTTPS (loopback
# http is accepted only for local development) and serve
# `<issuer>/.well-known/openid-configuration`. Neither value is a secret. The
# template fails at render time when either is missing; use
# `dashboard.auth.provider: external` when they come from `extraEnvFrom` or an
# ExternalSecret instead.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-ingress-oidc.yaml \
# --set-string env.OPENAI_API_KEY='sk-<real>' --wait
#
# ACCESS CONTROL IS YOURS TO SET. The dashboard has no allowlist of its own:
# every identity your provider issues a token to for this client can sign in,
# and the dashboard shows API keys to whoever is signed in. Restrict the
# application or client at the identity provider (a group or policy binding)
# to the people who should reach the dashboard.
#
# Troubleshooting, as observed behind ingress-nginx:
# - A wrong issuer makes GET /auth/login answer 503. The dashboard's own body
# names the reason ("Provider unreachable: OIDC discovery returned 404 for
# ..."), but a proxy with custom error pages can replace it with a generic
# 503, so query the Service directly or read the response body to see it.
# A trailing-slash difference in the issuer is tolerated.
# - A redirect URI that is not registered fails at the identity provider (for
# example "Unregistered redirect_uri"), not on the dashboard.
#
# The external origin must match the registered redirect URI exactly. It is
# derived from the first Ingress host (https once `ingress.tls` is set); set
# `dashboard.publicUrl` explicitly when it differs. Dummy values below: replace
# them, and never commit real credentials.
config:
model:
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
dashboard:
oauth:
self_hosted:
issuer: "https://auth.example.com/application/o/hermes/"
client_id: "hermes-dashboard"
# scopes: "openid profile email"
dashboard:
enabled: true
# Only listed peers may supply X-Forwarded-Proto / X-Forwarded-For. Put your
# ingress controller's pod CIDR (or its exact pod IP) here; without it the
# session cookies are not marked Secure behind a TLS-terminating Ingress.
trustedProxies:
- "10.244.0.0/16"
auth:
provider: oidc
env:
OPENAI_API_KEY: "sk-DUMMY_replace_me_000000000000000000000000"
service:
enabled: true
ingress:
enabled: true
className: nginx
annotations: {}
# cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: hermes-agent.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: hermes-agent-tls
hosts:
- hermes-agent.example.com
values-ingress.yaml¶
charts/hermes-agent/values-ingress.yaml
# values-ingress.yaml
#
# Exposes the Hermes management dashboard (service.port, default 9119) via an
# Ingress.
#
# The dashboard is an s6 service inside the image and stays DOWN until
# `dashboard.enabled` (HERMES_DASHBOARD=1) is set. In-container it binds 0.0.0.0, and on any
# non-loopback bind upstream's auth gate is mandatory: without an auth
# provider the dashboard fails closed and never listens, so an Ingress in
# front of it would answer 502/503. This example uses the bundled
# username/password provider. The deprecated `--insecure` /
# HERMES_DASHBOARD_INSECURE escape hatch is a no-op upstream.
#
# The dashboard shows API keys to whoever is logged in, and a logged-in user
# can also create shell hooks and use the Chat tab (shell access to the pod).
# Upstream says the
# username/password provider is for a trusted network or a VPN, NOT for public
# internet exposure. For a public host use values-ingress-oauth.yaml (Nous
# Portal) or values-ingress-oidc.yaml (your own identity provider) instead; on
# a private network you can still add a second auth layer at the proxy
# (oauth2-proxy, an ingress basic-auth annotation, ...) as defence in depth.
#
# Dummy values: override at install time. Never commit real credentials.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-ingress.yaml \
# --set-string env.OPENAI_API_KEY='sk-<real>' \
# --set-string env.HERMES_DASHBOARD_BASIC_AUTH_PASSWORD='<strong password>' \
# --set-string env.HERMES_DASHBOARD_BASIC_AUTH_SECRET="$(openssl rand -base64 32)" \
# --wait
#
# Or reference an externally managed Secret holding those keys through
# `extraEnvFrom` instead of `env` (see the README's "Secret provisioning
# strategies").
config:
model:
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
dashboard:
enabled: true
# External origin the dashboard is reached at. Left empty it is derived from
# the first ingress host below (https once ingress.tls is set).
# publicUrl: "https://hermes-agent.example.com"
# Only listed peers may supply X-Forwarded-Proto / X-Forwarded-For. Put your
# ingress controller's pod CIDR (or its exact pod IP) here; Hermes rejects
# unbounded entries such as 0.0.0.0/0. Without this, a TLS-terminating
# ingress is not trusted and cookies are not marked Secure.
trustedProxies:
- "10.244.0.0/16"
auth:
# The template fails when this provider's keys are missing from env.
provider: basic
# Secrets rendered into the chart's env Secret.
env:
OPENAI_API_KEY: "sk-DUMMY_replace_me_000000000000000000000000"
HERMES_DASHBOARD_BASIC_AUTH_USERNAME: "admin"
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD: "DUMMY_replace_me"
# 32+ random bytes signing the provider's session tokens; set it so logins
# survive pod restarts (blank = a fresh random key per process).
HERMES_DASHBOARD_BASIC_AUTH_SECRET: "DUMMY_replace_me_with_openssl_rand_base64_32"
service:
enabled: true
type: ClusterIP
port: 9119
ingress:
enabled: true
className: nginx
annotations: {}
hosts:
- host: hermes-agent.example.com
paths:
- path: /
pathType: Prefix
tls: []
# - secretName: hermes-agent-tls
# hosts:
# - hermes-agent.example.com
values-litellm-k8s.yaml¶
charts/hermes-agent/values-litellm-k8s.yaml
# values-litellm-k8s.yaml
#
# Hermes Agent talking to a LiteLLM proxy deployed in the SAME Kubernetes
# cluster (e.g. via the upstream berriai/litellm-helm chart), reached over
# in-cluster Service DNS: no Ingress/TLS needed. For a proxy reachable over
# the network instead, see values-litellm.yaml.
#
# Adjust `base_url` to match your LiteLLM Service name/namespace:
# http://<service>.<namespace>.svc.cluster.local:<port>/v1
#
# Dummy key: override at install time.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-litellm-k8s.yaml \
# --set-string env.OPENAI_API_KEY='sk-<your-litellm-proxy-key>' --wait
config:
# Register the in-cluster proxy as a custom OpenAI-compatible provider. The
# key ("litellm") is the provider id referenced by model.provider below.
providers:
litellm:
# DUMMY: service "litellm" in namespace "litellm", default chart port.
base_url: http://litellm.litellm.svc.cluster.local:4000/v1
key_env: OPENAI_API_KEY # env var that holds the proxy key
discover_models: true # populate the model picker from /v1/models
model:
provider: litellm
# Must match a model name exposed by the proxy (see /v1/models).
default: openai/gpt-oss-120b
terminal:
backend: local
env:
OPENAI_API_KEY: "sk-DUMMY_replace_me_000000000000000000000000"
values-litellm.yaml¶
charts/hermes-agent/values-litellm.yaml
# values-litellm.yaml
#
# Hermes Agent talking to a LiteLLM proxy reachable over the network (outside
# the cluster, or via an Ingress/LoadBalancer): one key, many upstream models.
# For a proxy running inside the same cluster, see values-litellm-k8s.yaml.
#
# Dummy key: override at install time.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-litellm.yaml \
# --set-string env.OPENAI_API_KEY='sk-<your-litellm-proxy-key>' --wait
config:
# Register the proxy as a custom OpenAI-compatible provider. The key
# ("litellm") is the provider id referenced by model.provider below.
providers:
litellm:
base_url: https://litellm.example.com/v1
key_env: OPENAI_API_KEY # env var that holds the proxy key
discover_models: true # populate the model picker from /v1/models
# Optional: extra HTTP headers on every request to this provider: for
# a WAF/Cloudflare Access-gated proxy, a corporate gateway needing a
# second auth header, or request tracing. Values may hold secrets
# (e.g. CF-Access-Client-Secret); prefer ${ENV_VAR} substitution over
# a literal here so nothing sensitive lands in the ConfigMap.
# extra_headers:
# CF-Access-Client-Id: "xxxx.access"
# CF-Access-Client-Secret: "${CF_ACCESS_SECRET}"
model:
provider: litellm
# Must match a model name exposed by the proxy (see /v1/models).
default: openai/gpt-oss-120b
terminal:
backend: local
env:
OPENAI_API_KEY: "sk-DUMMY_replace_me_000000000000000000000000"
values-moa.yaml¶
charts/hermes-agent/values-moa.yaml
# values-moa.yaml
#
# Hermes Agent using Mixture-of-Agents (MoA): reference models run in
# parallel and an aggregator model synthesizes their output, instead of a
# single model answering directly. MoA is a virtual provider (image
# v2026.7.1+): `config.model.provider: moa` + `config.model.default: <preset
# name>` selects a named preset defined under `config.moa.presets`.
#
# The example preset below fans out to two OpenRouter reference models and
# aggregates with a third: swap provider/model ids for whichever you use,
# and supply each provider's own API key under `env` (a preset can mix
# providers freely).
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-moa.yaml \
# --set-string env.OPENROUTER_API_KEY='sk-or-<real>' --wait
config:
model:
provider: moa
default: default # preset name under config.moa.presets
moa:
presets:
default:
reference_models:
- provider: openrouter
model: deepseek/deepseek-v4-pro
- provider: openrouter
model: qwen/qwen3-max
aggregator:
provider: openrouter
model: anthropic/claude-opus-4.8
enabled: true
# Persist full turn traces (each reference's input/output + the
# aggregator's input/output) to HERMES_HOME/moa-traces/<session_id>.jsonl.
# Off by default; turn on to audit/improve preset behavior.
save_traces: false
terminal:
backend: local
env:
OPENROUTER_API_KEY: "sk-or-DUMMY_replace_me_0000000000000000000000"
# Unused by the moa provider; overrides the chart's OpenAI placeholder.
OPENAI_API_KEY: "unused"
values-multi-agent-collab.yaml¶
charts/hermes-agent/values-multi-agent-collab.yaml
# values-multi-agent-collab.yaml
#
# One half of a COLLABORATING PAIR of Hermes agents that hand the conversation
# to each other by @mention in a shared Discord channel. This file is the
# "planner" role; copy it to a "builder" (swap the role text and the partner's
# user ID) and deploy both as separate releases into the same channel.
#
# Full walkthrough: handoff protocol, the loop brake, mixed backends, and an
# ApplicationSet that templates a whole roster: is in docs/advanced/teams/collaboration.md.
#
# Two instances collaborate when:
# 1. they share ONE channel (same DISCORD_HOME_CHANNEL + DISCORD_ALLOWED_USERS),
# 2. each is told its partner's Discord user ID via config.agent.environment_hint,
# 3. the four loop-brake env vars below are set so a partner fires ONLY on an
# explicit <@id> in the message body (Hermes has no bot-to-bot turn limiter).
#
# All secrets below are DUMMY placeholders. Do NOT commit real keys: override at
# install time (--set-string) or inject via a SealedSecret + extraEnvFrom (see
# examples/argocd/hermes-collab-pair.yaml).
#
# helm upgrade --install hermes-planner ./charts/hermes-agent \
# --namespace hermes-team --create-namespace \
# -f charts/hermes-agent/values-multi-agent-collab.yaml \
# --set-string env.DISCORD_BOT_TOKEN='<planner-bot-token>' --wait
fullnameOverride: hermes-planner
config:
# This pair uses the shared LiteLLM proxy for the planner; the builder could
# just as well use Copilot device-flow (see docs/advanced/teams/collaboration.md → mixed
# backends). Collaboration does NOT require a shared backend.
providers:
litellm:
base_url: https://litellm.example.com/v1
key_env: OPENAI_API_KEY # env var that holds the proxy key
discover_models: true
model:
provider: litellm
default: openai/gpt-oss-120b # must match a model the proxy exposes
terminal:
backend: local
# A collaboration thread contains messages from the human and both bots.
# Use one transcript for all senders and backfill visible thread messages
# that arrived while this bot was not mentioned.
group_sessions_per_user: false
discord:
require_mention: true
thread_require_mention: true
history_backfill: true
history_backfill_limit: 50
agent:
# The handoff protocol. Names the PARTNER's Discord user ID and tells this
# agent how to hand over (explicit <@id> in the BODY) and: critically - how
# to STOP (address the human, drop the mention) when a topic is done. The
# closing sentences are the prompt half of the loop brake; without them the
# two bots ping-pong forever.
environment_hint: |
You are "planner", one of two collaborating Hermes agents in this Discord
channel. Your job is to scope and plan the work. Your partner is "builder",
Discord user ID <BUILDER_BOT_USER_ID>. To hand the conversation to builder,
put an explicit <@BUILDER_BOT_USER_ID> mention in the BODY of your message.
Only mention builder when you have something substantive to say or genuinely
need their input. When a topic reaches a natural conclusion, do NOT mention
builder - address the human instead and end your turn, so the exchange
stops. Never send a filler or "let me know if you need anything" message
that mentions builder; that only restarts the loop.
env:
# Real proxy key comes from --set-string or a SealedSecret. Setting
# DISCORD_BOT_TOKEN is enough to auto-enable Discord (one bot per agent).
OPENAI_API_KEY: "set-via-extraEnvFrom-or-set-string"
DISCORD_BOT_TOKEN: "MTA0DUMMYtoken000000000000.DUMMY.replace_me_with_a_real_token"
# Non-secret Discord knobs. The first two are SHARED by every agent in the team
# (same channel, same allowed users); the four loop-brake knobs are also shared
# and MUST be set on every collaborating agent. See docs/advanced/teams/collaboration.md for the
# full rationale of each loop-brake var.
extraEnv:
- name: DISCORD_HOME_CHANNEL # the ONE shared channel (context bus)
value: "000000000000000000" # DUMMY - your channel id (18 digits)
- name: DISCORD_ALLOWED_USERS # shared - who may talk to the team
value: "111111111111111111" # DUMMY - comma-separated user ids
# --- loop brake: a partner fires ONLY on an explicit <@id> in the body -------
- name: DISCORD_ALLOW_BOTS # respond to a bot only when it @mentions us
value: "mentions"
- name: DISCORD_THREAD_REQUIRE_MENTION # in shared threads, fire only when mentioned
value: "true"
- name: DISCORD_REPLY_TO_MODE # don't attach a reply-reference (auto-ping)
value: "off"
- name: DISCORD_ALLOW_MENTION_REPLIED_USER # never treat an auto reply-ping as a mention
value: "false"
# Persistence: empty storageClass = cluster default. On a Raspberry Pi cluster
# that is typically local-path (k3s) or microk8s-hostpath: both ReadWriteOnce,
# which is exactly what this single-writer workload wants.
persistence:
enabled: true
storageClass: ""
accessModes:
- ReadWriteOnce
size: 5Gi
# Defaults are already tuned for small arm64 nodes; shown here for visibility.
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
values-networkpolicy-dashboard.yaml¶
charts/hermes-agent/values-networkpolicy-dashboard.yaml
# values-networkpolicy-dashboard.yaml
#
# The dashboard behind an Ingress while the chart's NetworkPolicy is on.
#
# The NetworkPolicy denies all inbound traffic by default, so turning it on
# silently cuts the Ingress off from the dashboard: the pod stays Ready (the
# kubelet probe is not affected) but the controller cannot reach it. This
# overlay adds the one rule that is needed, for the dashboard port only.
#
# What to allow depends on how the controller reaches the pod. Measured on
# MicroK8s with Calico (VXLAN) and a host-network ingress-nginx, one controller
# per node (the dashboard pod on one of them):
#
# rule controller on controller on
# the same node another node
# no rule allowed BLOCKED
# podSelector/namespaceSelector of the ingress allowed BLOCKED
# ipBlock: the node network only allowed BLOCKED
# ipBlock: the node network + the pod network allowed allowed
#
# A host-network controller is not a pod as far as the policy is concerned, so a
# selector never matches it. Traffic from another node reaches the dashboard from
# that node's tunnel address, which is inside the pod network. Allow BOTH
# networks, and set the same two CIDRs in `dashboard.trustedProxies`, or the
# session cookies lose `Secure` depending on which node the request enters.
#
# Replace the two CIDRs below with your own:
# node network: the addresses of the nodes running the controller, for
# example `kubectl get nodes -o wide`
# pod network: `kubectl get ippools.crd.projectcalico.org` (Calico) or
# `kubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}'`
# A bounded CIDR is preferred over a pod IP, which changes when the controller
# pod is recreated. A controller that runs as an ordinary pod (not host-network)
# can be matched with a namespaceSelector and podSelector instead, which is
# narrower; that variant was not measured here.
#
# Dummy values: override at install time. Never commit real credentials.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-networkpolicy-dashboard.yaml \
# --set-string env.OPENAI_API_KEY='sk-<real>' \
# --set-string env.HERMES_DASHBOARD_BASIC_AUTH_PASSWORD='<strong password>' \
# --set-string env.HERMES_DASHBOARD_BASIC_AUTH_SECRET="$(openssl rand -base64 32)" \
# --wait
#
# Changing `dashboard.*` or the hosts later needs `--set bootstrap.overwrite=true`
# for that upgrade (see the README, "Expose the dashboard").
config:
model:
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
dashboard:
enabled: true
# Same two networks as the NetworkPolicy rule below.
trustedProxies:
- "10.0.4.0/24"
- "10.1.0.0/16"
auth:
provider: basic
env:
OPENAI_API_KEY: "sk-DUMMY_replace_me_000000000000000000000000"
HERMES_DASHBOARD_BASIC_AUTH_USERNAME: "admin"
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD: "DUMMY_replace_me"
HERMES_DASHBOARD_BASIC_AUTH_SECRET: "DUMMY_replace_me_with_openssl_rand_base64_32"
service:
enabled: true
ingress:
enabled: true
className: nginx
annotations: {}
# cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: hermes-agent.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: hermes-agent-tls
hosts:
- hermes-agent.example.com
networkPolicy:
enabled: true
extraIngress:
- from:
- ipBlock:
cidr: "10.0.4.0/24"
- ipBlock:
cidr: "10.1.0.0/16"
ports:
- protocol: TCP
port: 9119
values-networkpolicy-litellm.yaml¶
charts/hermes-agent/values-networkpolicy-litellm.yaml
# values-networkpolicy-litellm.yaml
#
# Egress-locked install talking to an in-cluster LiteLLM proxy (see
# values-litellm-k8s.yaml). blockPrivateEgress denies the rest of RFC1918 and
# the cloud metadata endpoint while still permitting the LiteLLM Service
# specifically, via extraEgress - a precise allowlist instead of opening all
# of RFC1918 just to reach one in-cluster Service.
#
# Adjust the namespaceSelector/podSelector below to match your LiteLLM
# install; the labels here match the upstream berriai/litellm-helm chart's
# defaults (Service "litellm" in namespace "litellm", port 4000).
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-networkpolicy-litellm.yaml \
# --set-string env.OPENAI_API_KEY='sk-<your-litellm-proxy-key>' --wait
config:
providers:
litellm:
base_url: http://litellm.litellm.svc.cluster.local:4000/v1
key_env: OPENAI_API_KEY
discover_models: true
model:
provider: litellm
default: openai/gpt-oss-120b
terminal:
backend: local
env:
OPENAI_API_KEY: "sk-DUMMY_replace_me_000000000000000000000000"
networkPolicy:
enabled: true
blockPrivateEgress: true
extraEgress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: litellm
podSelector:
matchLabels:
app.kubernetes.io/name: litellm
ports:
- protocol: TCP
port: 4000
values-nous.yaml¶
charts/hermes-agent/values-nous.yaml
# values-nous.yaml
#
# Hermes Agent on the Nous free tier: zero external API keys required.
# Nous Research's account service mints an anonymous identity at boot that
# provides one free model (nous/welcome) plus a token for the managed
# connector tools (web search, browser, Gmail, Linear, and other third-party
# integrations). No sign-up, no provider key.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-nous.yaml --wait
#
# The free tier is meant as a zero-setup starting point (or a source of
# connector tools alongside your own provider key, see the
# HERMES_GUEST_ONBOARDING note in README.md), not a production inference
# path: `nous/welcome` is a single shared model with no SLA. Switch to a
# provider-specific values file (values-openai.yaml, values-openrouter.yaml,
# ...) once you have your own key.
config:
model:
provider: nous
default: nous/welcome
terminal:
backend: local
env:
# Turns on the free-tier bootstrap at boot; unset (the chart default)
# leaves this feature off entirely, matching pre-v2026.9.11 behavior.
HERMES_GUEST_ONBOARDING: "1"
# Unused with the `nous` provider; overrides the chart's OpenAI placeholder
# so no dummy key is required.
OPENAI_API_KEY: "unused"
values-nvidia-nim-and-buzz.yaml¶
charts/hermes-agent/values-nvidia-nim-and-buzz.yaml
# values-nvidia-nim-and-buzz.yaml
#
# Hermes Agent using NVIDIA NIM as the model provider AND running as a Buzz
# bot: both wired in one file. Buzz is Block's open-source human+agent
# collaboration platform built on Nostr; the adapter ships bundled with the
# image (same category as Telegram/Discord/WhatsApp), so setting the two
# required env vars below is enough to enable it: no config.yaml change.
#
# All secrets below are DUMMY placeholders. Do NOT commit real keys: override
# them at install time (--set-string) or inject via a SealedSecret + extraEnvFrom
# (see examples/argocd/).
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-nvidia-nim-and-buzz.yaml \
# --set-string env.NVIDIA_API_KEY='nvapi-<real>' \
# --set-string env.BUZZ_PRIVATE_KEY='<real-nostr-nsec-or-hex>' --wait
config:
model:
# Built-in provider key for NVIDIA NIM (build.nvidia.com).
provider: nvidia
# NIM model id: pick one your account can reach from build.nvidia.com.
default: nvidia/nemotron-3-nano-omni-30b-a3b-reasoning
terminal:
backend: local
env:
# --- Model provider (NVIDIA NIM) ----------------------------------------
NVIDIA_API_KEY: "nvapi-DUMMY_replace_me_0000000000000000000000"
# The chart's default placeholder is for OpenAI; this deployment doesn't use
# it. Set to a clear sentinel so no real OpenAI key is implied.
OPENAI_API_KEY: "unused"
# --- Buzz bot (secret bits) ----------------------------------------------
# The Nostr private key (nsec or hex) for the agent's Buzz identity: the
# only Buzz secret. Create a dedicated identity for the bot; do not reuse a
# personal Nostr key.
BUZZ_PRIVATE_KEY: "DUMMY_replace_me_with_a_real_nsec_or_hex_key"
# Non-secret Buzz knobs go here (plain env, not the Secret).
extraEnv:
- name: BUZZ_RELAY_URL # required - base URL of your Buzz community relay
value: "https://DUMMY.communities.buzz.xyz"
- name: BUZZ_HOME_CHANNEL # channel UUID for cron / notification delivery
value: "" # DUMMY - empty defaults to the first watched channel
- name: BUZZ_ALLOWED_USERS # comma-separated npubs or hex pubkeys allowed to talk
value: "" # DUMMY - your Nostr pubkey
- name: BUZZ_ALLOW_ALL_USERS # true only for throwaway/dev bots
value: "false"
values-nvidia-nim-and-discord.yaml¶
charts/hermes-agent/values-nvidia-nim-and-discord.yaml
# values-nvidia-nim-and-discord.yaml
#
# Hermes Agent on a Raspberry Pi cluster (arm64), talking to NVIDIA NIM for the
# model AND running as a Discord bot: both wired in one file.
#
# All secrets below are DUMMY placeholders. Do NOT commit real keys: override
# them at install time (--set-string) or inject via a SealedSecret + extraEnvFrom
# (see examples/argocd/).
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-nvidia-nim-and-discord.yaml \
# --set-string env.NVIDIA_API_KEY='nvapi-<real>' \
# --set-string env.DISCORD_BOT_TOKEN='<real-bot-token>' --wait
config:
model:
# Built-in provider key for NVIDIA NIM (build.nvidia.com).
provider: nvidia
# NIM model id: pick one your account can reach from build.nvidia.com.
default: nvidia/nemotron-3-nano-omni-30b-a3b-reasoning
terminal:
backend: local
env:
# --- Model provider (NVIDIA NIM) ----------------------------------------
NVIDIA_API_KEY: "nvapi-DUMMY_replace_me_0000000000000000000000"
# The chart's default placeholder is for OpenAI; this deployment doesn't use
# it. Set to a clear sentinel so no real OpenAI key is implied.
OPENAI_API_KEY: "unused"
# --- Discord bot (secret bits) ------------------------------------------
# Setting the token is enough to auto-enable Discord: no config.yaml change.
# Create the bot at https://discord.com/developers/applications, enable the
# "Message Content Intent", and invite it to your server.
DISCORD_BOT_TOKEN: "MTA0DUMMYtoken000000000000.DUMMY.replace_me_with_a_real_token"
# Non-secret Discord knobs go here (plain env, not the Secret).
extraEnv:
- name: DISCORD_HOME_CHANNEL # channel id for cron / notification delivery
value: "000000000000000000" # DUMMY - your channel id (18 digits)
- name: DISCORD_ALLOWED_USERS # comma-separated user ids allowed to talk
value: "111111111111111111" # DUMMY - your Discord user id
- name: DISCORD_ALLOW_ALL_USERS # true only for throwaway/dev bots
value: "false"
# Persistence: empty storageClass = cluster default. On a Raspberry Pi cluster
# that is typically local-path (k3s) or microk8s-hostpath: both ReadWriteOnce,
# which is exactly what this single-writer workload wants.
persistence:
enabled: true
storageClass: ""
accessModes:
- ReadWriteOnce
size: 5Gi
# Defaults are already tuned for small arm64 nodes; shown here for visibility.
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
values-openai-and-telegram.yaml¶
charts/hermes-agent/values-openai-and-telegram.yaml
# values-openai-and-telegram.yaml
#
# Hermes Agent using OpenAI (api.openai.com) as the model provider AND running
# as a Telegram bot: both wired in one file.
#
# All secrets below are DUMMY placeholders. Do NOT commit real keys: override
# them at install time (--set-string) or inject via a SealedSecret + extraEnvFrom
# (see examples/argocd/).
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-openai-and-telegram.yaml \
# --set-string env.OPENAI_API_KEY='sk-<real>' \
# --set-string env.TELEGRAM_BOT_TOKEN='<real-bot-token>' --wait
config:
model:
# NOTE: the built-in key is `openai-api` (api.openai.com).
# `openai` is NOT valid here: it aliases to OpenRouter.
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
env:
# --- Model provider (OpenAI) --------------------------------------------
OPENAI_API_KEY: "sk-DUMMY_replace_me_000000000000000000000000"
# --- Telegram bot (secret bits) -----------------------------------------
# Setting the token is enough to auto-enable Telegram: no config.yaml change.
# Create the bot via https://t.me/BotFather.
TELEGRAM_BOT_TOKEN: "0000000000:DUMMY-replace_me_with_a_real_token"
# Non-secret Telegram knobs go here (plain env, not the Secret).
extraEnv:
- name: TELEGRAM_HOME_CHANNEL # chat id for cron / notification delivery
value: "000000000" # DUMMY - your chat id
- name: TELEGRAM_ALLOWED_USERS # comma-separated user ids allowed to talk
value: "111111111" # DUMMY - your Telegram user id
values-openai-codex.yaml¶
charts/hermes-agent/values-openai-codex.yaml
# values-openai-codex.yaml
#
# Hermes Agent backed by the account-authenticated OpenAI Codex provider. The
# auth init container sends a verification URL + one-time code to Discord,
# waits for approval, and stores the refreshable credential in
# HERMES_HOME/auth.json through Hermes' native auth-store helper.
#
# This is distinct from the `openai-api` provider and does not use an OpenAI API
# key. Your ChatGPT plan and the live Codex catalog determine model access.
#
# All secrets below are DUMMY placeholders. Do not commit real tokens.
#
# helm upgrade --install hermes-codex ./charts/hermes-agent \
# --namespace hermes-codex --create-namespace \
# -f charts/hermes-agent/values-openai-codex.yaml \
# --set-string env.DISCORD_BOT_TOKEN='<real-bot-token>' --wait
#
# kubectl logs deploy/hermes-codex-hermes-agent -n hermes-codex \
# -c auth-device-login -f
config:
model:
provider: openai-codex
# Listed in the pinned Hermes Codex catalog. Your plan decides whether the
# account can use it.
# Use /model in Discord to select another model available to your account.
default: gpt-5.6-terra
# Optional larger context window. The Codex route advertises 272K for these
# models. Hermes can use about 900K when you opt in with a `-900k` suffix,
# for example:
# default: gpt-6-luna-900k
# Notes (checked against Hermes v2026.9.24, the chart's pinned image):
# - `-900k` is a Hermes alias, not an OpenAI model name. It is removed
# from the model id sent to OpenAI, and Hermes caps the window at the
# account catalog's maximum. Hermes verified the larger window live; it
# is not an OpenAI-published guarantee, so drop the suffix if a request
# is rejected.
# - Only some models qualify (the gpt-5.6 and gpt-6 families, among
# others). The suffix on any other model is not a valid alias. This
# chart's live check used gpt-6-luna-900k; other models are untested.
# - It is opt-in because a large context uses your subscription quota
# faster.
# - The behavior is still evolving upstream (several fixes landed between
# 2026-09-19 and 2026-10-01), so re-check it after an image bump.
terminal:
backend: local
# Compaction fires at the lower of the ratio trigger and this absolute cap.
# The default cap is 256000, so a 900K window would still compact at 256K.
# Raise it (or set it to null for the ratio only) when you use `-900k`.
# compression:
# threshold_tokens: 700000
auth:
deviceFlow:
enabled: true
provider: openai-codex
notify: discord
env:
# Unused by openai-codex; overrides the chart's OpenAI placeholder.
OPENAI_API_KEY: "unused"
DISCORD_BOT_TOKEN: "MTA0DUMMYtoken000000000000.DUMMY.replace_me_with_a_real_token"
extraEnv:
- name: DISCORD_HOME_CHANNEL
value: "000000000000000000" # DUMMY - channel id for login delivery
- name: DISCORD_ALLOWED_USERS
value: "111111111111111111" # DUMMY - allowed Discord user id
- name: DISCORD_ALLOW_ALL_USERS
value: "false"
# Required so auth.json and its refresh token survive Pod replacement.
persistence:
enabled: true
storageClass: ""
accessModes:
- ReadWriteOnce
size: 5Gi
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
values-openai.yaml¶
charts/hermes-agent/values-openai.yaml
# values-openai.yaml
#
# Hermes Agent using OpenAI (api.openai.com) as the model provider.
# Dummy key: override at install time.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-openai.yaml \
# --set-string env.OPENAI_API_KEY='sk-<real>' --wait
config:
model:
# NOTE: the built-in key is `openai-api` (api.openai.com).
# `openai` is NOT valid here: it aliases to OpenRouter.
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
env:
OPENAI_API_KEY: "sk-DUMMY_replace_me_000000000000000000000000"
values-openrouter.yaml¶
charts/hermes-agent/values-openrouter.yaml
# values-openrouter.yaml
#
# Hermes Agent using OpenRouter (one key, many upstream models).
# Dummy key: override at install time.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-openrouter.yaml \
# --set-string env.OPENROUTER_API_KEY='sk-or-<real>' --wait
config:
model:
provider: openrouter
# OpenRouter model id (vendor/model): see https://openrouter.ai/models.
default: openai/gpt-4o-mini
terminal:
backend: local
env:
OPENROUTER_API_KEY: "sk-or-DUMMY_replace_me_0000000000000000000000"
# Unused by the openrouter provider; overrides the chart's OpenAI placeholder.
OPENAI_API_KEY: "unused"
values-shared-knowledge.yaml¶
charts/hermes-agent/values-shared-knowledge.yaml
# values-shared-knowledge.yaml
#
# Example: multiple Hermes agents with private HERMES_HOME PVCs plus a separate
# common knowledge base on one ReadWriteMany (RWX) PVC.
#
# Use case: A team of Hermes agents (planner, builder, researcher, etc.) that all
# read from and write to the same durable knowledge repository: curated notes,
# reference documents, or vector indices. Every agent mounts the SAME knowledge
# PVC at /opt/data/shared-knowledge while retaining its own config, memory,
# sessions, and identity on a private HERMES_HOME PVC.
#
# REQUIREMENT: The PVC must:
# - Use a StorageClass that supports ReadWriteMany (RWX) access mode
# (e.g., NFS, CephFS, Longhorn, Azure Files, GCE Persistent Disk with
# appropriate access modes). Most cloud providers' default StorageClass
# is ReadWriteOnce (RWO) and will NOT work for multiple writers.
# - Have accessModes: [ReadWriteMany]
# - Be created BEFORE deploying the agents (this chart only references it)
# - Be readable and writable by the Hermes uid/gid 10000
#
# Example PVC manifest (create this once, then reference it below):
#
# apiVersion: v1
# kind: PersistentVolumeClaim
# metadata:
# name: hermes-shared-knowledge
# namespace: hermes-team
# spec:
# accessModes:
# - ReadWriteMany
# storageClassName: nfs-client # or your RWX-capable StorageClass
# resources:
# requests:
# storage: 10Gi
#
# All secrets below are DUMMY placeholders. Do NOT commit real keys: override
# them at install time (--set-string) or inject via a SealedSecret + extraEnvFrom
# (see examples/argocd/).
#
# # Deploy the planner agent
# helm upgrade --install hermes-planner ./charts/hermes-agent \
# --namespace hermes-team --create-namespace \
# -f charts/hermes-agent/values-shared-knowledge.yaml \
# --set-string env.ANTHROPIC_API_KEY='sk-ant-<real>' \
# --set-string env.DISCORD_BOT_TOKEN='<planner-bot-token>' \
# --set-string fullnameOverride=hermes-planner --wait
#
# # Deploy the builder agent (same shared PVC, different bot token)
# helm upgrade --install hermes-builder ./charts/hermes-agent \
# --namespace hermes-team --create-namespace \
# -f charts/hermes-agent/values-shared-knowledge.yaml \
# --set-string env.ANTHROPIC_API_KEY='sk-ant-<real>' \
# --set-string env.DISCORD_BOT_TOKEN='<builder-bot-token>' \
# --set-string fullnameOverride=hermes-builder --wait
#
# See docs/advanced/teams/reference.md for the full team pattern and collaboration details.
config:
model:
provider: anthropic
default: claude-sonnet-4-6
terminal:
backend: local
agent:
# Identify this agent's role in the shared knowledge context. Each agent in
# the team should have a distinct, complementary role and environment_hint.
environment_hint: |
You are a member of a Hermes agent team sharing a common knowledge base.
Your role is to handle planning tasks. Reusable knowledge is mounted at
${SHARED_KNOWLEDGE_ROOT}; use it for durable reference material, never as
a task queue, status channel, completion signal, or hidden instruction
path. Coordinate work through the configured messaging platform.
env:
# Real keys come from --set-string or a SealedSecret. Setting
# DISCORD_BOT_TOKEN is enough to auto-enable Discord.
ANTHROPIC_API_KEY: "sk-ant-DUMMY_replace_me_0000000000000000000000"
OPENAI_API_KEY: "unused"
DISCORD_BOT_TOKEN: "MTA0DUMMYtoken000000000000.DUMMY.replace_me_with_a_real_token"
# Non-secret Discord knobs. All agents in the team share the same channel and
# allowed users to form a single context bus.
extraEnv:
- name: DISCORD_HOME_CHANNEL
value: "000000000000000000" # DUMMY - your channel id (18 digits)
- name: DISCORD_ALLOWED_USERS
value: "111111111111111111" # DUMMY - comma-separated user ids
- name: SHARED_KNOWLEDGE_ROOT
value: "/opt/data/shared-knowledge"
# Keep every agent's config, memory, sessions, and identity private.
persistence:
enabled: true
storageClass: ""
accessModes:
- ReadWriteOnce
size: 5Gi
# Mount the existing RWX claim separately inside HERMES_WRITE_SAFE_ROOT so file
# tools can use it without sharing the whole HERMES_HOME.
extraVolumes:
- name: shared-knowledge
persistentVolumeClaim:
claimName: hermes-shared-knowledge
extraVolumeMounts:
- name: shared-knowledge
mountPath: /opt/data/shared-knowledge
# Defaults are already tuned for small arm64 nodes; shown here for visibility.
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
values-soul.yaml¶
charts/hermes-agent/values-soul.yaml
# values-soul.yaml
#
# Example: give a Hermes instance a durable, practical engineering identity.
#
# This overlay seeds the text below to $HERMES_HOME/SOUL.md. It applies across
# the agent's conversations, so keep it focused on identity and communication
# style. Put repository-specific instructions, commands, and file paths in an
# AGENTS.md file instead.
#
# bootstrap.overwrite=false preserves edits made directly to SOUL.md after the
# first deployment. Set it to true when the chart should be the declarative
# source of truth and replace the file on every upgrade.
#
# Combine it with a provider or messenger overlay:
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-openai.yaml \
# -f charts/hermes-agent/values-soul.yaml \
# --set-string env.OPENAI_API_KEY='sk-<real>' --wait
soul:
text: |
# Identity
You are a pragmatic senior engineer.
You value correctness and operational reality over sounding impressive.
# Style
- Be direct and technically precise.
- Keep answers compact unless complexity requires depth.
- State uncertainty and tradeoffs clearly.
# Avoid
- Hype language.
- Sycophancy.
- Overexplaining obvious points.
bootstrap:
overwrite: false
values-team-leader.yaml¶
charts/hermes-agent/values-team-leader.yaml
# values-team-leader.yaml
#
# Chart-native LEADER for a Discord star team. Install one release per agent,
# define the complete public roster once in an ApplicationSet, and supply
# Discord IDs via environment variables from a Secret/SealedSecret. The leader
# owns the shared skill ConfigMap and one RWX knowledge PVC; members reference
# both resources read-only.
#
# The cluster default StorageClass must support ReadWriteMany, or override:
#
# --set-string team.sharedVolume.storageClass='<rwx-storage-class>'
#
# All values below are public dummy examples. Do not commit real bot tokens or
# Discord IDs. See examples/argocd/hermes-team.yaml for Secret references.
fullnameOverride: hermes-august
config:
model:
provider: nvidia
default: z-ai/glm-5.2
terminal:
backend: local
display:
tool_progress: "off"
agent:
# Keep skills enabled: team.enabled injects the role-specific roster skill.
disabled_toolsets:
- browser
- clarify
- code_execution
- cronjob
- delegation
- discord
- discord_admin
- messaging
- session_search
- terminal
- todo
- web
team:
enabled: true
name: hermes-team
role: leader
identity: august
leader:
name: august
mentionEnv: AUGUST_BOT_USER_ID
members:
- name: may
role: Research and evidence gathering
mentionEnv: MAY_BOT_USER_ID
capabilities: [research, source-review]
- name: march
role: Implementation and verification
mentionEnv: MARCH_BOT_USER_ID
capabilities: [implementation, testing]
protocol:
maxHandoffs: 6
skill:
enabled: true
create: true
name: hermes-team-roster
configMapName: hermes-team-skill
extraInstructions: ""
sharedVolume:
enabled: true
create: true
claimName: hermes-team-knowledge
mountPath: /opt/data/team-knowledge
storageClass: ""
accessModes: [ReadWriteMany]
size: 10Gi
retain: true
permissions:
enabled: true
image: busybox:1.38
uid: 10000
gid: 10000
env:
NVIDIA_API_KEY: "nvapi-DUMMY_replace_me_0000000000000000000000"
OPENAI_API_KEY: "unused"
DISCORD_BOT_TOKEN: "MTA0DUMMYtoken000000000000.DUMMY.replace_me_with_a_real_token"
extraEnv:
- name: DISCORD_HOME_CHANNEL
value: "000000000000000000"
- name: DISCORD_ALLOWED_USERS
value: "111111111111111111"
- name: MAY_BOT_USER_ID
value: "000000000000000000"
- name: MARCH_BOT_USER_ID
value: "000000000000000000"
- name: AUGUST_BOT_USER_ID
value: "000000000000000000"
extraInitContainers:
- name: init-private-home
image: busybox:1.38
command: ["sh", "-c", "chown -R 10000:10000 /home"]
volumeMounts:
- name: data
mountPath: /home
persistence:
enabled: true
storageClass: ""
accessModes: [ReadWriteOnce]
size: 5Gi
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
values-team-member.yaml¶
charts/hermes-agent/values-team-member.yaml
# values-team-member.yaml
#
# One MEMBER of the chart-native Discord team. Deploy one release per member,
# set `team.identity` to the matching roster entry, and reference the shared
# skill ConfigMap and claim created by the leader release. Both are mounted
# read-only.
fullnameOverride: hermes-may
config:
model:
provider: nvidia
default: z-ai/glm-5.2
terminal:
backend: local
display:
tool_progress: "off"
agent:
# Keep skills enabled: team.enabled injects the role-specific roster skill.
disabled_toolsets:
- browser
- clarify
- code_execution
- cronjob
- delegation
- discord
- discord_admin
- messaging
- session_search
- terminal
- todo
- web
team:
enabled: true
name: hermes-team
role: member
identity: may
leader:
name: august
mentionEnv: AUGUST_BOT_USER_ID
members:
- name: may
role: Research and evidence gathering
mentionEnv: MAY_BOT_USER_ID
capabilities: [research, source-review]
- name: march
role: Implementation and verification
mentionEnv: MARCH_BOT_USER_ID
capabilities: [implementation, testing]
protocol:
maxHandoffs: 6
skill:
enabled: true
create: false
name: hermes-team-roster
configMapName: hermes-team-skill
extraInstructions: ""
sharedVolume:
enabled: true
create: false
claimName: hermes-team-knowledge
mountPath: /opt/data/team-knowledge
storageClass: ""
accessModes: [ReadWriteMany]
size: 10Gi
retain: true
permissions:
enabled: false
image: busybox:1.38
uid: 10000
gid: 10000
env:
NVIDIA_API_KEY: "nvapi-DUMMY_replace_me_0000000000000000000000"
OPENAI_API_KEY: "unused"
DISCORD_BOT_TOKEN: "MTA0DUMMYtoken000000000000.DUMMY.replace_me_with_a_real_token"
extraEnv:
- name: DISCORD_HOME_CHANNEL
value: "000000000000000000"
- name: DISCORD_ALLOWED_USERS
value: "111111111111111111"
- name: AUGUST_BOT_USER_ID
value: "000000000000000000"
extraInitContainers:
- name: init-private-home
image: busybox:1.38
command: ["sh", "-c", "chown -R 10000:10000 /home"]
volumeMounts:
- name: data
mountPath: /home
persistence:
enabled: true
storageClass: ""
accessModes: [ReadWriteOnce]
size: 5Gi
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
values-telegram-team-assistant.yaml¶
charts/hermes-agent/values-telegram-team-assistant.yaml
# values-telegram-team-assistant.yaml
#
# ONE Hermes Telegram bot shared by several people: a team assistant, not a
# multi-bot team. For several collaborating bots, see
# values-telegram-team-leader.yaml / values-telegram-team-member.yaml.
#
# BotFather setup, once per bot:
# - /newbot, then keep the token secret (revoke with /revoke if it leaks).
# - In groups the bot sees only commands and replies to it while Group
# Privacy is ON. Either make the bot a group admin or turn privacy off
# (Bot Settings -> Group Privacy); after changing privacy, remove and
# re-add the bot to the group.
#
# Access control, pick what you need:
# - TELEGRAM_ALLOWED_USERS: people allowed everywhere (DMs and groups).
# - TELEGRAM_GROUP_ALLOWED_USERS: people allowed in groups only.
# - TELEGRAM_GROUP_ALLOWED_CHATS: whole groups; any member is allowed.
# - DM pairing: an unknown user who DMs the bot gets a one-time code; the
# operator approves it with
# kubectl exec -n hermes-agent deploy/hermes-agent -c hermes-agent -- \
# hermes pairing approve telegram <CODE>
# Approvals are stored in HERMES_HOME, so they survive restarts.
#
# Everything below is a placeholder. Never commit real tokens or IDs; put the
# token in a Secret and reference it with extraEnvFrom in production.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-telegram-team-assistant.yaml \
# --set-string env.OPENAI_API_KEY='sk-<real>' \
# --set-string env.TELEGRAM_BOT_TOKEN='<real-bot-token>' --wait
config:
model:
provider: openai-api
default: gpt-4o-mini
terminal:
backend: local
telegram:
# In groups, answer only when mentioned (@botname), replied to, or sent a
# /command@botname. Ordinary group chatter is ignored.
require_mention: true
# Each person in a group keeps their own conversation with the bot.
group_sessions_per_user: true
env:
OPENAI_API_KEY: "sk-DUMMY_replace_me_000000000000000000000000"
TELEGRAM_BOT_TOKEN: "000000000:DUMMY_replace_me_with_a_real_bot_token"
extraEnv:
# Numeric Telegram user IDs of the people who may use the bot anywhere.
- name: TELEGRAM_ALLOWED_USERS
value: "111111111,222222222"
# The team group's chat ID (negative). Any member of it may use the bot there.
- name: TELEGRAM_GROUP_ALLOWED_CHATS
value: "-1001234567890"
# Where cron job output is delivered.
- name: TELEGRAM_HOME_CHANNEL
value: "-1001234567890"
values-telegram-team-leader.yaml¶
charts/hermes-agent/values-telegram-team-leader.yaml
# values-telegram-team-leader.yaml
#
# Chart-native LEADER for a Telegram star team: one Helm release per bot, all
# bots in one Telegram group (or one forum topic). Handoffs are ordinary bot
# messages: the leader @mentions one member, the member answers with the
# leader's @username, and the leader's final answer to the human mentions no
# bot, which ends the exchange.
#
# Team mode sets these gates on every release (and rejects them in extraEnv):
# TELEGRAM_ALLOW_BOTS=mentions accept another bot only when it
# TELEGRAM_BOTS_REQUIRE_MENTION=true explicitly @mentions this bot, so a
# quote-reply cannot start a loop
# TELEGRAM_REQUIRE_MENTION=true group messages need a mention at all
# TELEGRAM_REPLY_TO_MODE=off replies carry no reply reference
# and defaults telegram.exclusive_bot_mentions=true (only the named bots act)
# and telegram.mention_patterns=[] (no shared wake word). Hermes' own bot loop
# guard (gateway.bot_loop_guard) stays on as a second line of defence.
#
# BotFather, for EVERY bot in the team:
# - Create a separate bot. Never reuse one token across releases: Telegram
# rejects concurrent polling for the same token.
# - Enable Bot-to-Bot Communication, so the bots can see each other.
# - Make the bot a group admin, or turn Group Privacy off and re-add it.
#
# The cluster default StorageClass must support ReadWriteMany, or override:
# --set-string team.sharedVolume.storageClass='<rwx-storage-class>'
#
# Placeholders only. See examples/argocd/hermes-team-telegram.yaml for the
# ApplicationSet with per-bot Secrets.
fullnameOverride: hermes-august
config:
model:
provider: nvidia
default: z-ai/glm-5.2
terminal:
backend: local
display:
tool_progress: "off"
agent:
# Keep skills enabled: team.enabled injects the role-specific roster skill.
disabled_toolsets:
- browser
- clarify
- code_execution
- cronjob
- delegation
- messaging
- session_search
- terminal
- todo
- web
team:
enabled: true
platform: telegram
name: hermes-team
role: leader
identity: august
leader:
name: august
username: hermes_august_bot
members:
- name: may
role: Research and evidence gathering
username: hermes_may_bot
capabilities: [research, source-review]
- name: march
role: Implementation and verification
username: hermes_march_bot
capabilities: [implementation, testing]
protocol:
maxHandoffs: 6
skill:
enabled: true
create: true
name: hermes-team-roster
configMapName: hermes-team-skill
extraInstructions: ""
sharedVolume:
enabled: true
create: true
claimName: hermes-team-knowledge
mountPath: /opt/data/team-knowledge
storageClass: ""
accessModes: [ReadWriteMany]
size: 10Gi
retain: true
permissions:
enabled: true
image: busybox:1.38
uid: 10000
gid: 10000
env:
NVIDIA_API_KEY: "nvapi-DUMMY_replace_me_0000000000000000000000"
OPENAI_API_KEY: "unused"
TELEGRAM_BOT_TOKEN: "000000000:DUMMY_replace_me_with_the_leader_token"
extraEnv:
# Humans allowed to start work (numeric IDs). Other team bots do not need to
# be listed: TELEGRAM_ALLOW_BOTS=mentions admits them when they mention us.
- name: TELEGRAM_ALLOWED_USERS
value: "111111111"
# The shared team group; also where cron output goes.
- name: TELEGRAM_GROUP_ALLOWED_CHATS
value: "-1001234567890"
- name: TELEGRAM_HOME_CHANNEL
value: "-1001234567890"
persistence:
enabled: true
storageClass: ""
accessModes: [ReadWriteOnce]
size: 5Gi
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
values-telegram-team-member.yaml¶
charts/hermes-agent/values-telegram-team-member.yaml
# values-telegram-team-member.yaml
#
# Chart-native MEMBER for a Telegram star team: one Helm release per bot, all
# bots in one Telegram group (or one forum topic). Handoffs are ordinary bot
# messages: the leader @mentions one member, the member answers with the
# leader's @username, and the leader's final answer to the human mentions no
# bot, which ends the exchange.
#
# Team mode sets these gates on every release (and rejects them in extraEnv):
# TELEGRAM_ALLOW_BOTS=mentions accept another bot only when it
# TELEGRAM_BOTS_REQUIRE_MENTION=true explicitly @mentions this bot, so a
# quote-reply cannot start a loop
# TELEGRAM_REQUIRE_MENTION=true group messages need a mention at all
# TELEGRAM_REPLY_TO_MODE=off replies carry no reply reference
# and defaults telegram.exclusive_bot_mentions=true (only the named bots act)
# and telegram.mention_patterns=[] (no shared wake word). Hermes' own bot loop
# guard (gateway.bot_loop_guard) stays on as a second line of defence.
#
# BotFather, for EVERY bot in the team:
# - Create a separate bot. Never reuse one token across releases: Telegram
# rejects concurrent polling for the same token.
# - Enable Bot-to-Bot Communication, so the bots can see each other.
# - Make the bot a group admin, or turn Group Privacy off and re-add it.
#
# The cluster default StorageClass must support ReadWriteMany, or override:
# --set-string team.sharedVolume.storageClass='<rwx-storage-class>'
#
# Placeholders only. See examples/argocd/hermes-team-telegram.yaml for the
# ApplicationSet with per-bot Secrets.
fullnameOverride: hermes-may
config:
model:
provider: nvidia
default: z-ai/glm-5.2
terminal:
backend: local
display:
tool_progress: "off"
agent:
# Keep skills enabled: team.enabled injects the role-specific roster skill.
disabled_toolsets:
- browser
- clarify
- code_execution
- cronjob
- delegation
- messaging
- session_search
- terminal
- todo
- web
team:
enabled: true
platform: telegram
name: hermes-team
role: member
identity: may
leader:
name: august
username: hermes_august_bot
members:
- name: may
role: Research and evidence gathering
username: hermes_may_bot
capabilities: [research, source-review]
- name: march
role: Implementation and verification
username: hermes_march_bot
capabilities: [implementation, testing]
protocol:
maxHandoffs: 6
skill:
enabled: true
create: false
name: hermes-team-roster
configMapName: hermes-team-skill
extraInstructions: ""
sharedVolume:
enabled: true
create: false
claimName: hermes-team-knowledge
mountPath: /opt/data/team-knowledge
storageClass: ""
accessModes: [ReadWriteMany]
size: 10Gi
retain: true
permissions:
enabled: false
image: busybox:1.38
uid: 10000
gid: 10000
env:
NVIDIA_API_KEY: "nvapi-DUMMY_replace_me_0000000000000000000000"
OPENAI_API_KEY: "unused"
TELEGRAM_BOT_TOKEN: "000000000:DUMMY_replace_me_with_this_members_token"
extraEnv:
# Humans allowed to start work (numeric IDs). Other team bots do not need to
# be listed: TELEGRAM_ALLOW_BOTS=mentions admits them when they mention us.
- name: TELEGRAM_ALLOWED_USERS
value: "111111111"
# The shared team group.
- name: TELEGRAM_GROUP_ALLOWED_CHATS
value: "-1001234567890"
# Same group as the home channel. Without it Hermes posts a "no home channel
# is set" notice into the group on the member's first message, and the device
# login and ready messages (auth.deviceFlow.notify / readyNotify) have nowhere
# to go.
- name: TELEGRAM_HOME_CHANNEL
value: "-1001234567890"
persistence:
enabled: true
storageClass: ""
accessModes: [ReadWriteOnce]
size: 5Gi
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
values-upstage.yaml¶
charts/hermes-agent/values-upstage.yaml
# values-upstage.yaml
#
# Hermes Agent using Upstage Solar's built-in OpenAI-compatible provider.
# Dummy key: override at install time.
#
# helm upgrade --install hermes-agent ./charts/hermes-agent \
# --namespace hermes-agent --create-namespace \
# -f charts/hermes-agent/values-upstage.yaml \
# --set-string env.UPSTAGE_API_KEY='<real>' --wait
config:
model:
provider: upstage
# Hosted/business-tier model. For Upstage's open-weight flagship instead
# (250B-A15B MoE, https://huggingface.co/upstage/Solar-Open2-250B),
# use "solar-open2": same provider, no other change needed. Verified
# working end-to-end: https://github.com/jyje/pilot-upstage-solar-open2/tree/main/02-hermes-agent-solar-open2
default: solar-pro3
terminal:
backend: local
env:
UPSTAGE_API_KEY: "DUMMY_replace_me_0000000000000000000000"
# Optional endpoint override (upstream default: https://api.upstage.ai/v1).
# UPSTAGE_BASE_URL: "https://api.upstage.ai/v1"
# Unused by the upstage provider; overrides the chart's OpenAI placeholder.
OPENAI_API_KEY: "unused"