Identity & Access

Replacing Hermes Agent .env Files with HashiCorp Vault

October 10, 2026

AI agents can help IT and security professionals work more efficiently. They can query cloud environments, manage infrastructure, review logs, document changes, and handle repetitive work across several platforms.

One obvious problem: useful agents need credentials.

A Hermes profile may need API keys for cloud platforms, firewalls, monitoring systems, or Kubernetes. Those credentials normally live in the profile’s .env file. That is convenient, but it also puts several valuable credentials together in one plaintext file. If someone gains unauthorized access to the system running the agent, that file is a good place to start looking.

We can move those values into HashiCorp Vault and give each Hermes profile its own AppRole. The profile still receives the environment variables it needs, but the actual application credentials are stored in Vault instead of its local .env file.

I covered the basic Vault and KV setup in Managing Secrets with HashiCorp Vault. This post assumes Vault is already running and focuses only on connecting it to Hermes.

How it works

Hermes supports a generic command secret source. When a profile starts, Hermes runs a helper command and reads KEY=VALUE records from its standard output.[1]

The helper:

  1. Reads the profile’s Vault RoleID and SecretID.
  2. Authenticates through AppRole.
  3. Reads that profile’s KV v2 path.
  4. Prints the retrieved values in dotenv format for Hermes.
Hermes profile
      |
      v
secrets.command
      |
      v
Vault AppRole login
      |
      v
Profile-specific KV path
      |
      v
KEY=VALUE records returned to Hermes

For this example, I have two profiles:

Each profile has its own Vault path and AppRole. The general profile cannot retrieve the Kubernetes credentials, and the k3s profile cannot retrieve the general IT credentials.

Create a Vault policy and AppRole

The two logical secret paths are:

secret/hermes/profiles/general
secret/hermes/profiles/k3s

Because this is a KV v2 mount, the policy uses the data/ API path.[3]

# hermes-general.hcl
path "secret/data/hermes/profiles/general" {
  capabilities = ["read"]
}

Write the policy and create the AppRole:

vault policy write hermes-general hermes-general.hcl

vault write auth/approle/role/hermes-general \
  token_policies=hermes-general \
  token_type=service \
  token_ttl=24h \
  token_max_ttl=168h \
  secret_id_ttl=0 \
  secret_id_num_uses=0

AppRole lets an automated workload authenticate with a RoleID and SecretID, then receive a Vault token with the policies assigned to that role.[2]

Repeat the same pattern for hermes-k3s, changing the policy path to:

path "secret/data/hermes/profiles/k3s" {
  capabilities = ["read"]
}

Store each profile’s environment variables at its matching KV path. The keys should use the same variable names the profile previously loaded from .env.

Save the AppRole credentials on the Hermes host

The host needs the RoleID and SecretID used to authenticate to Vault. You can keep them in a separate protected directory for each profile:

~/.config/hermes-vault/
├── general/
│   ├── role_id
│   └── secret_id
└── k3s/
    ├── role_id
    └── secret_id

Create the files for the general profile:

mkdir -p ~/.config/hermes-vault/general
chmod 0700 ~/.config/hermes-vault/general

vault read -field=role_id \
  auth/approle/role/hermes-general/role-id \
  > ~/.config/hermes-vault/general/role_id

vault write -f -field=secret_id \
  auth/approle/role/hermes-general/secret-id \
  > ~/.config/hermes-vault/general/secret_id

chmod 0400 ~/.config/hermes-vault/general/role_id
chmod 0400 ~/.config/hermes-vault/general/secret_id

Repeat that for the k3s AppRole and directory. The SecretID is still a credential, so it should not be placed in config.yaml, Git, shell history, or documentation.

Use a profile-aware Vault helper

I use one helper at /usr/local/bin/hermes-vault-env. It has an allowlist that maps each Hermes profile to a fixed credential directory and Vault path:

PROFILE_CONFIG = {
    "general": {
        "credential_dir": Path.home() / ".config/hermes-vault/general",
        "vault_path": "secret/data/hermes/profiles/general",
    },
    "k3s": {
        "credential_dir": Path.home() / ".config/hermes-vault/k3s",
        "vault_path": "secret/data/hermes/profiles/k3s",
    },
}

The helper reads the selected RoleID and SecretID, logs in through auth/approle/login, retrieves the KV document, and prints only valid KEY=VALUE records. Tokens and secret values are never written to logs.

This article includes a complete sanitized version of the helper. It uses the installed Vault CLI, passes the AppRole login document through standard input, rejects unknown profile names, and returns a nonzero exit code if authentication or retrieval fails.

Point Hermes at the helper

Configure the general profile:

HERMES_HOME="$HOME/.hermes/profiles/general" \
  hermes config set secrets.command.enabled true

HERMES_HOME="$HOME/.hermes/profiles/general" \
  hermes config set secrets.command.command \
  'HERMES_VAULT_PROFILE=general /usr/local/bin/hermes-vault-env'

HERMES_HOME="$HOME/.hermes/profiles/general" \
  hermes config set secrets.command.helper_timeout_seconds 10

HERMES_HOME="$HOME/.hermes/profiles/general" \
  hermes config set secrets.command.override_existing true

For the Kubernetes profile, use the same settings with HERMES_HOME pointing to the k3s profile and HERMES_VAULT_PROFILE=k3s in the helper command.

override_existing: true makes the command source authoritative if the same variable already exists in the environment.[1]

Verify the profile boundary

First, confirm that Hermes can load each profile:

HERMES_HOME="$HOME/.hermes/profiles/general" hermes config check
HERMES_HOME="$HOME/.hermes/profiles/k3s" hermes config check

Then test the Vault policy. A token issued to the general AppRole should produce:

vault token capabilities secret/data/hermes/profiles/general
# read

vault token capabilities secret/data/hermes/profiles/k3s
# deny

Run the reverse test with the Kubernetes AppRole. The key result is not only that each profile can retrieve its credentials, but that it cannot retrieve another profile’s credentials.

Once the checks pass, the profile .env no longer needs to contain the application credentials. Hermes retrieves them from Vault whenever the profile starts.

The fundamentals still apply

There is more to protecting AI agents than credential storage. Prompt injection, unsafe tool use, approval boundaries, and the accuracy of generated changes all deserve attention.

Infrastructure-wise, the starting points have not changed. Know what is on your network and which risks apply to those assets. Use segmentation where systems do not need to trust each other. Give identities only the access required for their work, and verify that the denied paths are actually denied.

AI agents can help experienced IT and security professionals cover more ground. Giving them access to more systems makes identity, access control, segmentation, inventory, and review more important, not less.

Sources

[1] https://hermes-agent.nousresearch.com/docs/user-guide/secrets/command [2] https://developer.hashicorp.com/vault/docs/auth/approle [3] https://developer.hashicorp.com/vault/docs/secrets/kv/kv-v2