Skip to content

Secrets Management

How TapPass manages API keys, encryption keys, and credentials — and how to integrate with your enterprise secret manager.

TapPass handles two categories of secrets with different trust models:

SecretWho uses itWhere it livesWho manages it
TapPass API keys (tp_, tp_dev_)Developers → TapPassIssued by TapPass, stored by developerCISO / Admin
LLM provider keys (OpenAI, Anthropic, etc.)TapPass → LLM providerServer-side environment variablePlatform team
Vault encryption keyTapPass internalEnv var or KMSPlatform team

The key design principle: developers never see the real LLM API key. They authenticate to TapPass with a tp_ key. TapPass authenticates to the LLM provider with the real key. The developer’s key is revocable, auditable, and scoped. The LLM key is centrally managed.

Developer's machine TapPass (your infra) LLM Provider
┌──────────────┐ ┌──────────────────┐ ┌──────────┐
│ Claude Code │── tp_dev_xxx ────────▶│ Authenticate │ │ │
│ Cursor │ (TapPass key) │ 49-step pipeline │── sk-xxx ──▶│ OpenAI │
│ CrewAI │ │ Audit trail │ (real key) │ Anthropic│
└──────────────┘ └──────────────────┘ └──────────┘
Developer never LLM key lives here Developer
sees sk-xxx (env var or KMS) never sees

Part 1: TapPass API Keys (Developer → TapPass)

Section titled “Part 1: TapPass API Keys (Developer → TapPass)”

Each developer gets their own key, mapped to their email in the audit trail:

Terminal window
# Admin creates a key for Alice (expires in 90 days)
curl -X POST https://tappass.company.com/agents/research-bot/developer-keys \
-H "Authorization: Bearer $ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"developer_email": "alice@company.com", "expires_days": 90}'
# Response (shown ONCE — store securely):
# {
# "api_key": "tp_dev_aBcDeFgHiJkLmNoPqRsTuVwXyZ...",
# "key_id": "dk_abc123",
# "expires_at": "2026-06-14T10:00:00Z"
# }
Terminal window
# Alice uses it (two env vars, no code changes):
export OPENAI_BASE_URL=https://tappass.company.com/v1
export OPENAI_API_KEY=tp_dev_aBcDeFgHiJkLmNoPqRsTuVwXyZ...
OperationHowWho
CreatePOST /agents/{id}/developer-keysAdmin, developer
ListGET /agents/{id}/developer-keysAuditor+
RevokeDELETE /agents/{id}/developer-keys/{key_id}Admin
ExpiryAutomatic (1–365 days, configurable)System
RotationCreate new key → distribute → revoke old keyAdmin

For production agents (not humans in IDEs), TapPass supports SPIFFE/SPIRE:

  • No API keys at all — certificate-based mutual TLS
  • SPIRE issues short-lived X.509 certs (1h TTL, auto-rotated)
  • Identity: spiffe://company.com/agent/research-bot
  • See Identity docs

Part 2: LLM Provider Keys (TapPass → OpenAI/Anthropic)

Section titled “Part 2: LLM Provider Keys (TapPass → OpenAI/Anthropic)”

The real LLM API keys live server-side only. Five ways to get them there, from simple to enterprise:

Terminal window
# .env file or Docker env
OPENAI_API_KEY=sk-proj-xxx
ANTHROPIC_API_KEY=sk-ant-xxx

Simple but the key is in plaintext on disk. Fine for development, not for production.

Option B: Kubernetes Secret (basic production)

Section titled “Option B: Kubernetes Secret (basic production)”
Terminal window
kubectl create secret generic tappass-llm-secrets -n tappass \
--from-literal=OPENAI_API_KEY=sk-proj-xxx \
--from-literal=ANTHROPIC_API_KEY=sk-ant-xxx

Then reference in the Helm values:

secrets:
llmSecret: "tappass-llm-secrets"

The secret is mounted as env vars in the TapPass pod. Better than plaintext .env, but the key is stored in etcd (encrypted at rest if you configured it).

Section titled “Option C: External Secrets Operator (recommended)”

ESO syncs secrets from your vault into Kubernetes Secrets automatically. Ready-to-use templates in deploy/examples/secrets/:

Secret ManagerTemplate
HashiCorp Vaulthashicorp-vault.yaml
Azure Key Vaultazure-keyvault.yaml
AWS Secrets Manageraws-secrets-manager.yaml
CyberArk Conjurcyberark-conjur.yaml

Example for HashiCorp Vault:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: tappass-llm-keys
spec:
refreshInterval: 1h # ← auto-syncs rotated keys from Vault
secretStoreRef:
name: hashicorp-vault
kind: ClusterSecretStore
target:
name: tappass-llm-secrets
data:
- secretKey: OPENAI_API_KEY
remoteRef:
key: secret/data/tappass/llm
property: openai-api-key

Key rotation flow: rotate the key in your vault → ESO syncs within refreshInterval → new K8s Secret created → TapPass pod picks up new env var on next restart (or use a reloader like Stakater Reloader for zero-downtime rotation).

Option D: HashiCorp Vault Agent Injector (Vault-native orgs)

Section titled “Option D: HashiCorp Vault Agent Injector (Vault-native orgs)”

If you run Vault Agent as a sidecar:

# Pod annotations for Vault Agent Injector
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "tappass"
vault.hashicorp.com/agent-inject-secret-llm: "secret/data/tappass/llm"
vault.hashicorp.com/agent-inject-template-llm: |
{{- with secret "secret/data/tappass/llm" -}}
export OPENAI_API_KEY="{{ .Data.data.openai_api_key }}"
export ANTHROPIC_API_KEY="{{ .Data.data.anthropic_api_key }}"
{{- end -}}
.gitlab-ci.yml
deploy:
script:
- kubectl create secret generic tappass-llm-secrets -n tappass \
--from-literal=OPENAI_API_KEY=$OPENAI_API_KEY \
--from-literal=ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
--dry-run=client -o yaml | kubectl apply -f -

Where $OPENAI_API_KEY is a GitLab CI/CD masked variable. Or better: GitLab → HashiCorp Vault → ESO (see Option C).


Part 3: Vault Encryption Key (TapPass Internal)

Section titled “Part 3: Vault Encryption Key (TapPass Internal)”

TapPass encrypts credentials at rest (OAuth tokens, provider secrets) using AES-256-GCM. The encryption key can come from multiple sources.

Terminal window
# Generate
python -c "import secrets,base64; print(base64.b64encode(secrets.token_bytes(32)).decode())"
# Set
TAPPASS_VAULT_KEY="your-base64-key-here"

The raw encryption key never appears in env vars. Instead, TapPass fetches or unwraps it from your KMS at startup.

Terminal window
# AWS KMS — envelope encryption (DEK wrapped by KMS)
TAPPASS_VAULT_KEY_KMS="aws_kms://arn:aws:kms:eu-west-1:123456789:key/key-id"
# GCP Cloud KMS — envelope encryption
TAPPASS_VAULT_KEY_KMS="gcp_kms://projects/my-project/locations/europe-west1/keyRings/tappass/cryptoKeys/vault-key"
# Azure Key Vault — key stored as secret
TAPPASS_VAULT_KEY_KMS="azure_kv://tappass-kv/vault-encryption-key"
# HashiCorp Vault KV v2 — key stored in Vault
TAPPASS_VAULT_KEY_KMS="vault://secret/data/tappass/vault-key"
# HashiCorp Vault Transit — envelope encryption via Transit engine
TAPPASS_VAULT_KEY_KMS="vault_transit://tappass-encryption-key"

Uses envelope encryption: KMS wraps/unwraps a Data Encryption Key (DEK). The DEK is stored in TAPPASS_VAULT_DEK (auto-generated on first run).

Terminal window
TAPPASS_VAULT_KEY_KMS="aws_kms://key-id-or-arn"
TAPPASS_VAULT_DEK="" # auto-generated, store after first run
AWS_REGION="eu-west-1" # or set via IAM role on EKS

Requires: pip install tappass[kms] (installs boto3)

Same envelope encryption pattern as AWS KMS.

Terminal window
TAPPASS_VAULT_KEY_KMS="gcp_kms://projects/my-project/locations/europe-west1/keyRings/tappass-kr/cryptoKeys/vault-key"
TAPPASS_VAULT_DEK="" # auto-generated, store after first run
GOOGLE_APPLICATION_CREDENTIALS="/path/to/sa-key.json" # or workload identity on GKE

Requires: pip install tappass[kms] (installs google-cloud-kms)

Stores the encryption key as a secret in Azure Key Vault. Uses DefaultAzureCredential (managed identity on AKS, or service principal).

Terminal window
TAPPASS_VAULT_KEY_KMS="azure_kv://tappass-kv/vault-encryption-key"
AZURE_TENANT_ID="your-tenant-id" # not needed with managed identity
AZURE_CLIENT_ID="your-client-id" # not needed with managed identity
AZURE_CLIENT_SECRET="your-secret" # not needed with managed identity

Store the key in Azure Key Vault:

Terminal window
KEY=$(python -c "import secrets,base64; print(base64.b64encode(secrets.token_bytes(32)).decode())")
az keyvault secret set --vault-name tappass-kv --name vault-encryption-key --value "$KEY"

Requires: pip install tappass[kms] (installs azure-keyvault-secrets, azure-identity)

Reads the encryption key from Vault’s KV v2 secrets engine.

Terminal window
TAPPASS_VAULT_KEY_KMS="vault://secret/data/tappass/vault-key"
VAULT_ADDR="https://vault.internal:8200"

Store the key in Vault:

Terminal window
KEY=$(python -c "import secrets,base64; print(base64.b64encode(secrets.token_bytes(32)).decode())")
vault kv put secret/tappass/vault-key key="$KEY"

Authentication (checked in order):

MethodEnv varsRecommended for
TokenVAULT_TOKENDev, CI/CD
AppRoleVAULT_ROLE_ID + VAULT_SECRET_IDProduction
KubernetesVAULT_K8S_ROLEKubernetes workloads

Requires: pip install tappass[kms] (installs hvac)

Uses Vault’s Transit engine for envelope encryption — same concept as AWS KMS, but with Vault. The Transit engine performs crypto operations without exposing the master key.

Terminal window
TAPPASS_VAULT_KEY_KMS="vault_transit://tappass-encryption-key"
TAPPASS_VAULT_DEK="" # auto-generated, store after first run
VAULT_ADDR="https://vault.internal:8200"
VAULT_ROLE_ID="role-id"
VAULT_SECRET_ID="secret-id"

Setup:

Terminal window
vault secrets enable transit
vault write transit/keys/tappass-encryption-key type=aes256-gcm96

Requires: pip install tappass[kms] (installs hvac)


Terminal window
# 1. Create new key for Alice
POST /agents/research-bot/developer-keys
{"developer_email": "alice@company.com", "expires_days": 90}
# 2. Alice updates her env var
# 3. Revoke old key
DELETE /agents/research-bot/developer-keys/dk_old_key_id
# Audit trail shows both keys and the rotation event
MethodRotation flowDowntime
Env varUpdate .env → restart TapPassBrief restart
K8s SecretUpdate secret → rolling restartZero (with rollout)
ESO + VaultRotate in vault → ESO syncs → Reloader triggers rolloutZero
Vault AgentRotate in vault → Agent re-renders → App reloadsZero

TapPass uses versioned ciphertext (v1: prefix) to support key rotation:

  1. Update the key in your KMS / env var
  2. Call POST /admin/vault/rotate (invalidates the in-memory key cache)
  3. New data is encrypted with the new key
  4. Old data remains readable (TapPass tries current key first, falls back to previous)

Part 5: Architecture Decision — Why a Reverse Proxy?

Section titled “Part 5: Architecture Decision — Why a Reverse Proxy?”

The most common question: “Why not just let each developer use their own OpenAI key?”

Each dev has their own keyCentralized via TapPass
Key sprawl50 developers × 3 providers = 150 keys3 keys total
RevocationMust revoke 150 keys on breachRevoke 3 keys (or revoke tp_dev_ keys individually)
AuditNo centralized audit trailEvery call logged with developer identity
GovernanceNo PII detection, no injection blocking49-step pipeline on every call
Cost controlNo per-developer budget limitsPer-agent, per-developer cost tracking
ComplianceCannot prove to auditors what happenedTamper-evident audit trail with hash chain
RotationMust coordinate with 50 developersRotate once, server-side

The reverse proxy pattern is the same architecture used by API gateways (Kong, Apigee) — but specialized for AI governance.


VariableRequiredDescription
TAPPASS_VAULT_KEYYes*Raw AES-256 encryption key
TAPPASS_VAULT_KEY_KMSYes*KMS URI (alternative to VAULT_KEY)
TAPPASS_VAULT_DEKIf KMSWrapped DEK (auto-generated on first run)
VAULT_ADDRIf HashiCorpVault server URL
VAULT_TOKENIf HC token authVault token
VAULT_ROLE_IDIf HC AppRoleAppRole role ID
VAULT_SECRET_IDIf HC AppRoleAppRole secret ID
VAULT_K8S_ROLEIf HC K8s authKubernetes auth role name
VAULT_KV_MOUNTOptionalVault KV mount point (default: secret)
VAULT_TRANSIT_MOUNTOptionalVault Transit mount point (default: transit)

* One of TAPPASS_VAULT_KEY or TAPPASS_VAULT_KEY_KMS is required when a database is configured.

Terminal window
pip install tappass[kms]
# Installs: hvac, boto3, google-cloud-kms, azure-keyvault-secrets, azure-identity

Or install only what you need:

Terminal window
pip install hvac # HashiCorp Vault only
pip install boto3 # AWS KMS only
pip install google-cloud-kms # GCP Cloud KMS only
pip install azure-keyvault-secrets azure-identity # Azure Key Vault only