# How to Integrate Credential Management with Key Provider for API Key Lookup in Open Notebook

> Securely integrate Open Notebook credential management with your key provider for encrypted API key lookup. Learn how to authenticate AI providers with runtime injection.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: how-to-guide
- Published: 2026-06-07

---

**Open Notebook integrates credential management with the key provider by storing encrypted API keys in the `Credential` domain model and injecting them into the process environment at runtime via the key provider module, enabling secure AI provider authentication.**

Open Notebook solves the challenge of securely managing AI provider credentials by combining a database-backed credential store with a runtime key provisioning system. When you integrate credential management with the key provider for API key lookup, encrypted secrets are retrieved from SurrealDB and mapped to environment variables that the underlying Esperanto AI library expects. This architecture ensures that sensitive API keys remain encrypted at rest while remaining accessible to the models that need them.

## Understanding the Architecture

### The Credential Domain Model

Located in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py), the `Credential` class persists provider-specific secrets in SurrealDB. Each record stores the provider name, encrypted API key, and optional configuration fields such as `base_url` or `endpoint`. The model automatically encrypts the `api_key` field on save via `_prepare_save_data` and decrypts it on read using `get_secret_value()`, ensuring that sensitive data never exists in plaintext within the database.

### The Key Provider Module

The [`open_notebook/ai/key_provider.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/key_provider.py) module serves as the bridge between stored credentials and the AI libraries. It exposes `provision_provider_keys()`, which normalizes provider names and routes to specialized provisioning functions like `_provision_simple_provider`, `_provision_azure`, and `_provision_vertex`. These functions query the database via `Credential.get_by_provider()`, decrypt the API key, and inject it into the current process environment using `os.environ`.

### Provider Configuration Mapping

The architecture relies on a centralized `PROVIDER_CONFIG` mapping that associates provider names with their corresponding environment variable names. This prevents hardcoded strings throughout the codebase and ensures that when you integrate credential management with the key provider for API key lookup, the correct environment variables (such as `OPENAI_API_KEY` or `AZURE_OPENAI_ENDPOINT`) are populated consistently.

## Implementation Flow

### Creating and Storing Credentials

Before keys can be provisioned, they must be stored securely. The system exposes REST endpoints in [`api/routers/credentials.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/credentials.py) for CRUD operations, or you can interact with the domain model directly. When creating a credential, the API key is wrapped in `SecretStr` and encrypted before persistence.

```python
from open_notebook.domain.credential import Credential
from pydantic import SecretStr

async def create_openai_credential():
    cred = Credential(
        name="My OpenAI Key",
        provider="openai",
        modalities=["language", "embedding"],
        api_key=SecretStr("sk-REPLACE_WITH_REAL_KEY"),
        base_url=None,
    )
    await cred.save()

```

### Provisioning Keys at Runtime

When an AI model is instantiated, the application calls `await provision_provider_keys("provider_name")` before initializing the Esperanto client. The function fetches the first matching credential from the database, decrypts the API key using `cred.api_key.get_secret_value()`, and sets the appropriate environment variable. If no database record exists, the system falls back to existing environment variables, maintaining backward compatibility with containerized deployments.

```python
from open_notebook.ai.key_provider import provision_provider_keys

async def load_openai_key():
    success = await provision_provider_keys("openai")
    if success:
        print("OpenAI key loaded from Credential")
    else:
        print("Falling back to existing env var")

```

### Complex Provider Configurations

Different AI providers require distinct environment variable schemas. For **Azure OpenAI**, `_provision_azure` extracts `api_key`, `endpoint`, and `api_version` to populate `AZURE_OPENAI_API_KEY`, `AZURE_OPENAI_ENDPOINT`, and related variables. For **Google Vertex AI**, `_provision_vertex` handles `project`, `location`, and `GOOGLE_APPLICATION_CREDENTIALS` file paths. Simple URL-based providers like Ollama utilize `_provision_simple_provider` to map the `base_url` field to `<PROVIDER>_API_BASE`.

## Practical Code Examples

### Using Provisioned Keys with Esperanto Models

After provisioning, the Esperanto factory automatically reads the environment variables to authenticate with the provider:

```python
from open_notebook.ai.models import Model
from open_notebook.ai.key_provider import provision_provider_keys

async def create_gpt4_model():
    await provision_provider_keys("openai")
    model = Model.create_language(model_name="gpt-4")
    return model

```

### Bulk Provisioning at Startup

While per-provider provisioning is preferred to avoid stale credentials, you can load all keys at once using `provision_all_keys()`:

```python
from open_notebook.ai.key_provider import provision_all_keys

async def initialize_all_providers():
    results = await provision_all_keys()
    print("Provisioned:", results)

```

### Retrieving Specific API Keys

For direct access without setting environment variables, use `get_api_key()`:

```python
from open_notebook.ai.key_provider import get_api_key

async def fetch_key_directly():
    api_key = await get_api_key("openai")
    if api_key:
        print(f"Retrieved key: {api_key.get_secret_value()}")

```

## Summary

- **Database-first storage**: Credentials live encrypted in SurrealDB via the `Credential` model in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py), enabling UI-driven management and audit trails.
- **Lazy environment injection**: The key provider in [`open_notebook/ai/key_provider.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/key_provider.py) injects secrets into `os.environ` only when `provision_provider_keys()` is called, minimizing exposure windows.
- **Provider-specific routing**: Functions like `_provision_azure` and `_provision_vertex` handle complex multi-variable configurations for enterprise AI providers.
- **Zero-downtime fallback**: When no database credential exists, the system transparently falls back to existing environment variables, ensuring compatibility with existing deployment scripts.

## Frequently Asked Questions

### How does Open Notebook encrypt API keys before storing them?

The `Credential` model in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py) uses the `_prepare_save_data` method to encrypt the `api_key` field before persistence. The encryption key is sourced from the application configuration in [`open_notebook/config.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/config.py), ensuring that only encrypted values are written to SurrealDB. When retrieved, the model decrypts the value automatically, exposing it only as a `SecretStr` object that reveals the plaintext only when explicitly requested via `get_secret_value()`.

### What happens if a credential exists in the database and in an environment variable?

When `provision_provider_keys()` is called, it checks the database first via `Credential.get_by_provider()`. If a database record exists, its decrypted value overwrites the environment variable for the current process. If no record exists, the function preserves the existing environment variable value. This database-first approach ensures that user-managed credentials take precedence over system-level configuration while maintaining fallback compatibility.

### Can I use the key provider with self-hosted or custom AI endpoints?

Yes. For providers like Ollama or other URL-based services, store the base URL in the `base_url` field of the `Credential` record. The `_provision_simple_provider` function maps this value to `<PROVIDER>_API_BASE` in the environment. This allows the Esperanto library to connect to custom endpoints without modifying the core application code, as implemented in [`open_notebook/ai/key_provider.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/key_provider.py).

### When should I use `provision_all_keys()` versus `provision_provider_keys()`?

Use `provision_provider_keys("provider_name")` when you need to integrate credential management with the key provider for API key lookup for a specific AI model, as it prevents stale environment variables from lingering after credentials are deleted. Reserve `provision_all_keys()` only for startup initialization scenarios where multiple providers are required simultaneously, as it may populate environment variables for providers that are never used, slightly increasing the memory footprint of sensitive data.