Skip to main content
Environment variables are the most common way to pass secrets to AI agents, but they’re shared across all processes in a container, visible in logs, and manually rotated. Hexr Vault replaces them with identity-scoped secrets: each secret is encrypted at rest with AES-256-GCM, and access is gated by the requesting agent’s SPIFFE identity. A researcher role cannot read secrets scoped to a writer role — even if they’re in the same agent. This guide shows you how to store, retrieve, and scope secrets using the Hexr Vault client.

The problem with environment variables

# Avoid this pattern — secrets visible in logs, shared across agents
import os
OPENAI_KEY = os.environ["OPENAI_API_KEY"]

Steps

1

Use VaultClient to store and retrieve secrets

Replace environment variable lookups with VaultClient. Secrets are encrypted at rest and scoped to your agent’s SPIFFE identity:
my_agent.py
from hexr import hexr_agent
from hexr.vault import VaultClient

@hexr_agent(name="my-agent", tenant="acme-corp")
def main():
    vault = VaultClient()

    # Store a secret (encrypted at rest, scoped to this agent)
    vault.put("OPENAI_API_KEY", "sk-...")

    # Retrieve it
    key = vault.get("OPENAI_API_KEY")
2

Inject secrets as function parameters (optional)

Use the @secrets_batch decorator to inject secrets directly into your function signature — no VaultClient calls needed in your business logic:
my_agent.py
from hexr import hexr_agent
from hexr.vault import secrets_batch

@hexr_agent(name="my-agent", tenant="acme-corp")
@secrets_batch(["OPENAI_API_KEY", "ANTHROPIC_API_KEY"])
def main(OPENAI_API_KEY: str, ANTHROPIC_API_KEY: str):
    # Secrets injected as function parameters
    print(f"OpenAI key starts with: {OPENAI_API_KEY[:8]}...")
3

Scope secrets to specific roles (optional)

In multi-role agents, you can restrict secrets to the role that needs them. A role that doesn’t own a secret receives an access denied error:
scoped_secrets.py
# Agent-level: all processes in this agent can access
vault.put("SHARED_KEY", "value")
# Stored with scope: spiffe://hexr.cloud/agent/acme/my-agent/*

# Process-level: only the researcher process can access
vault.put("RESEARCH_DB_KEY", "value", scope="researcher")
# Stored with scope: spiffe://hexr.cloud/agent/acme/my-agent/researcher
A writer process trying to read RESEARCH_DB_KEY will receive an access denied error.

Full example

Here’s a complete agent that stores two secrets, uses one for an LLM call, and lists its stored keys:
smart_agent.py
from hexr import hexr_agent, hexr_llm
from hexr.vault import VaultClient

@hexr_agent(name="smart-agent", tenant="acme-corp")
def main():
    vault = VaultClient()

    # Store secrets during setup
    vault.put("OPENAI_API_KEY", "sk-proj-...")
    vault.put("SLACK_WEBHOOK", "https://hooks.slack.com/...")

    # Use them
    response = hexr_llm(
        provider="openai",
        model="gpt-4o",
        prompt="Generate a status report",
    )

    # List all secrets (keys only, not values)
    keys = vault.list()
    print(f"Stored secrets: {keys}")
    # → ["OPENAI_API_KEY", "SLACK_WEBHOOK"]

How Hexr Vault compares to environment variables

FeatureEnvironment variablesHexr Vault
Encryption at restPlaintextAES-256-GCM
Per-agent isolationSharedSPIFFE-scoped
Per-process isolationNoRole-level scoping
Audit trailNoEvery access logged
RotationManualSDK-managed
Visible in logsYesNever
Never log or print secret values returned from vault.get(). The Vault client intentionally withholds values from traces and spans, but your application code is responsible for not exposing them.

Next steps

Multi-cloud tools

Combine Vault-managed secrets with identity-based cloud access for a fully credential-free agent.

LLM observability

See how secret-backed LLM calls appear in cost and latency traces.

Multi-framework agents

Scope secrets to individual CrewAI or AutoGen roles.

SDK reference

Full reference for VaultClient, secrets_batch, and scoping options.