콘텐츠로 이동

예제

각 예제는 부분(partial) values 파일이며, 차트의 기본 values.yaml 위에 함께 적용합니다.

values-a2a.yaml

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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

Open raw 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"