Skip to main content

Secrets Management

Control Zero offers two ways to handle LLM provider keys (OpenAI, Anthropic, Google, Cohere, and others). You choose the approach that fits your security requirements.

Two Modes

Mode 1: Policy-Only (Default)

You manage your own provider keys. Control Zero only handles policy enforcement. Your keys never touch our servers.

from controlzero import Client
import openai

# You manage the OpenAI key yourself (env var, vault, etc.)
openai_client = openai.OpenAI(api_key=os.environ["OPENAI_API_KEY"])

# Control Zero only enforces policies
cz = Client(api_key="cz_live_...")

# Check policy before calling OpenAI
cz.guard("llm", method="generate", args={"model": "gpt-5.4"})
response = openai_client.chat.completions.create(model="gpt-5.4", messages=[...])

This is the default. No additional configuration is needed.

Mode 2: Secrets Vault (Optional)

Store your provider keys in Control Zero's encrypted vault. The SDK retrieves and decrypts them at runtime. No plaintext keys in your codebase or environment variables.

from controlzero import Client

# Enable secrets. The SDK fetches encrypted keys during initialize()
cz = Client(api_key="cz_live_...")

# Retrieve the decrypted key from memory
# Secrets are managed separately via the Control Zero dashboard

How the Vault Works

Storing a Secret

  1. Open the Control Zero dashboard and navigate to your project.
  2. In the Secrets Vault section, click Add Secret.
  3. Select the provider (OpenAI, Anthropic, Google, Cohere, or Custom).
  4. Paste your API key and give it a name (e.g., "Production OpenAI Key").
  5. Click Store Secret.

The plaintext value is shown exactly once. After that, only a masked prefix is displayed. The key is encrypted before it reaches our database.

Retrieving a Secret in the SDK

When secrets_enabled is set, initialize() fetches the encrypted secret bundle alongside the policy bundle. This is a single network call. No additional requests.

After initialization, get_secret() is a zero-latency in-memory lookup. It does not make network calls and adds no overhead to your application's hot path.

Python

from controlzero import Client

cz = Client(api_key="cz_live_...")

# By provider type
# Secrets retrieved via dashboard

# By name (as shown in the dashboard)
# Secrets retrieved via dashboard by name

Go

Not available in the Go SDK

The secrets vault API is not yet available in the Go SDK (v1.4.0). The Go SDK ships core policy enforcement only. Use the Python or Node SDK for secret retrieval, or read provider keys directly from your own environment / vault and keep Control Zero in policy-only mode.

Node.js

const client = new Client({
apiKey: 'cz_live_...',
policyFile: './controlzero.yaml',
});

// By provider type
const openaiKey = client.getSecret('openai');

// By name
const key = client.getSecretByName('Production OpenAI Key');

Security Model

Encryption

  • At rest: Secrets are encrypted before storage. The encryption key is derived from your project API key. Control Zero servers never store plaintext secret values.
  • In transit: All communication between SDKs and the Control Zero API uses TLS 1.3.
  • In memory: The SDK decrypts secrets into process memory only. Decrypted values are never written to disk, logged, or included in error messages.

Key Derivation

The encryption key used for secrets is derived from your project API key, not stored separately. This means:

  • Only holders of the project API key can decrypt the secrets.
  • Rotating your project API key automatically invalidates the derived key.
  • The derivation uses a cryptographic key derivation function with a fixed context string, ensuring the derived key is cryptographically independent from the API key itself.

Access Control

  • Secrets are scoped to a single project. They cannot be accessed across projects.
  • Storing and deleting secrets requires dashboard access or an organization-level API token.
  • SDK access to secrets requires the project API key and secrets_enabled to be set.

What We Cannot See

  • Plaintext secret values are never stored on our servers.
  • Our database contains only the encrypted ciphertext.
  • Our staff cannot decrypt your secrets without your project API key.

Performance Impact

The vault is designed to avoid per-call network overhead:

OperationWhen It RunsNetwork Call
initialize()Once at startupYes (bundled with policy fetch)
get_secret()Per usageNo (in-memory lookup)
guard()Per actionNo (local evaluation)

Secrets are fetched once during initialization as part of the same policy bundle request. After that, all operations are in-memory with no network round-trips.

Supported Providers

ProviderType StringExample Key Prefix
OpenAIopenaisk-proj-...
Anthropicanthropicsk-ant-...
GooglegoogleAIza...
Coherecohereco-...
CustomcustomAny format

Choosing Between Modes

ConsiderationPolicy-Only (Default)Secrets Vault
Provider keys on your serversYesNo
Provider keys in Control ZeroNoYes (encrypted)
Environment variable managementYou handle itNot needed
Key rotationYou handle itVia dashboard
Setup complexityNoneOne-time per key
SDK configurationDefaultAdd secrets_enabled=True
Compliance (key centralization)Keys in your infraKeys in one encrypted vault

Next Steps