Aegis Orchestrator
Deployment

Secrets Management

OpenBao Keymaster Pattern, AppRole authentication, KV and dynamic secrets, Transit Engine, and multi-tenant namespaces.

Secrets Management

AEGIS uses OpenBao (an Apache 2.0 fork of HashiCorp Vault) as its centralized secrets store. The Keymaster Pattern ensures that API keys and credentials are held exclusively by the orchestrator — agent containers never receive raw secrets.


The Keymaster Pattern

OpenBao

  │  Only the Orchestrator host communicates with OpenBao directly.

AEGIS Daemon (Orchestrator)
  ├── Resolves API keys for LLM providers
  ├── Resolves credentials for MCP Tool Servers
  ├── Issues SMCP SecurityToken JWTs via Transit Engine
  └── Never passes raw secrets to agent containers


Agent Containers (bootstrap.py)
  No OpenBao access. Receives a time-limited SecurityToken JWT only.

Authentication: AppRole

The AEGIS daemon authenticates to OpenBao using the AppRole method:

  1. Role ID — a static identifier bound to the AppRole (non-secret, stored in config).
  2. Secret ID — a rotatable credential injected at deployment time (secret, sourced from environment).
secrets:
  openbao:
    addr: "https://openbao.internal:8200"
    auth:
      role_id: "env:OPENBAO_ROLE_ID"
      secret_id: "env:OPENBAO_SECRET_ID"
    renewal_interval: 1800s    # renew token every 30 minutes
    kv_mount: "aegis-system"

On startup, the AEGIS daemon authenticates via AppRole and receives a renewable Vault token. A background task renews it every renewal_interval seconds.


KV Engine: Static Secrets

Use the KV v2 engine for long-lived secrets (API keys, OAuth tokens):

# Enable KV v2 at the aegis-system mount
bao secrets enable -path=aegis-system kv-v2

# Write a secret
bao kv put aegis-system/tools/search-api-key value="sk-..."

# Read a secret
bao kv get aegis-system/tools/search-api-key

Reference in aegis-config.yaml:

tools:
  mcp_servers:
    - name: web-search
      env:
        SEARCH_API_KEY: "vault:aegis-system/tools/search-api-key"

The credential resolver expands vault: paths at runtime via the OpenBao KV API.


Dynamic Secrets: Database Credentials

For database tools, use dynamic secrets to generate short-lived credentials on-demand:

# Enable the database secrets engine
bao secrets enable database

# Configure a PostgreSQL connection
bao write database/config/aegis-db \
  plugin_name=postgresql-database-plugin \
  connection_url="postgresql://{{username}}:{{password}}@localhost:5432/mydb" \
  allowed_roles="agent-role" \
  username="vault" \
  password="vault-password"

# Create a role with a 1 hour TTL
bao write database/roles/agent-role \
  db_name=aegis-db \
  creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';" \
  default_ttl="1h" \
  max_ttl="24h"

Reference in agent manifests or tool server config:

env:
  DB_CREDENTIALS: "vault:db/creds/agent-role"

The AEGIS credential resolver fetches a fresh credential from database/creds/agent-role each time, never caching it longer than the credential's TTL.


Transit Engine: Encryption-as-a-Service

The Transit Engine provides cryptographic operations without distributing keys. AEGIS uses it for:

  • SMCP SecurityToken signing: The orchestrator signs agent JWTs using a Transit key, so the private key never leaves OpenBao memory.
  • Blackboard encryption at rest: Workflow Blackboard state can be encrypted before writing to PostgreSQL.
# Enable Transit
bao secrets enable transit

# Create a signing key for SMCP tokens
bao write transit/keys/smcp-signing-key type=ed25519

# Create an encryption key for Blackboard data
bao write transit/keys/blackboard-encryption type=aes256-gcm96

AEGIS configuration:

secrets:
  openbao:
    transit_keys:
      smcp_signing: smcp-signing-key
      blackboard: blackboard-encryption

Multi-Tenant Namespaces

For multi-tenant deployments, each tenant gets an isolated OpenBao namespace. Namespaces map 1:1 to Keycloak realms:

OpenBao Namespaces:
  aegis-system/     ← platform-level secrets (LLM keys, SMCP keys)
  tenant-acme/      ← secrets for tenant acme
  tenant-enterprise-b/ ← secrets for another tenant

Each tenant's secrets are isolated — an AEGIS daemon operating in tenant context cannot access another tenant's secrets.


SensitiveString: Preventing Secret Leakage

All secrets resolved from OpenBao or environment variables are wrapped in a SensitiveString type in the Rust domain model. This type implements Debug and Display as [REDACTED], preventing accidental secret leakage into:

  • Structured log output
  • Prometheus metric labels
  • Panic messages and stack traces
  • gRPC error responses

Secrets are only unwrapped in the specific code paths that need the raw value (e.g., constructing an HTTP Authorization header for an LLM provider call).


AppRole Policy

The AEGIS AppRole should be bound to a minimal policy. Example aegis-policy.hcl:

# KV read access for tool server credentials
path "aegis-system/data/*" {
  capabilities = ["read"]
}

# Dynamic database credentials
path "database/creds/agent-role" {
  capabilities = ["read"]
}

# Transit signing and encryption
path "transit/sign/smcp-signing-key" {
  capabilities = ["update"]
}

path "transit/encrypt/blackboard-encryption" {
  capabilities = ["update"]
}

path "transit/decrypt/blackboard-encryption" {
  capabilities = ["update"]
}

Apply with:

bao policy write aegis-policy aegis-policy.hcl

bao auth enable approle
bao write auth/approle/role/aegis-daemon \
  policies=aegis-policy \
  token_ttl=1h \
  token_max_ttl=24h

# Get credentials for AEGIS daemon deployment
bao read auth/approle/role/aegis-daemon/role-id
bao write -force auth/approle/role/aegis-daemon/secret-id

On this page