# Key Provider Pattern and Database-First Credential Lookup in Open Notebook

> Learn how Open Notebook uses the database-first key provider pattern for AI credential lookup, prioritizing encrypted records over environment variables for secure runtime configuration.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: deep-dive
- Published: 2026-06-22

---

**Open Notebook implements a database-first key provider pattern that retrieves AI credentials from encrypted `Credential` records before falling back to environment variables, provisioning runtime environment variables through a centralized configuration mapping.**

The `lfnovo/open-notebook` repository manages AI provider secrets through a sophisticated key provider pattern that prioritizes database storage over traditional environment variables. This architecture centralizes secret management in the `Credential` table while maintaining backward compatibility with standard env-var deployments. Understanding this pattern is essential for developers integrating custom AI providers or securing production deployments.

## How the Key Provider Pattern Works

### Centralized Provider Configuration with PROVIDER_CONFIG

At the core of the system lies the `PROVIDER_CONFIG` dictionary defined in [`open_notebook/ai/key_provider.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/key_provider.py) (lines 28-73). This mapping serves as the single source of truth, associating each provider name with the specific environment variable names that underlying AI libraries expect.

### Database-First Credential Retrieval

When the application requests credentials, the `_get_default_credential()` helper (lines 76-84) queries the database for the first `Credential` record matching the requested provider. This database-first approach ensures that stored secrets take precedence over system environment variables.

### The get_api_key Function

The `get_api_key()` function (lines 87-106) implements the lookup logic: it first checks the database result for an `api_key` field, returning that encrypted secret immediately if present. Only when no database credential exists does the function fall back to reading the environment variable defined in `PROVIDER_CONFIG`.

### Runtime Environment Provisioning

Before instantiating AI models, Open Notebook populates the process environment using `provision_provider_keys()` or `provision_all_keys()`. For simple providers, `_provision_simple_provider()` (lines 113-141) writes `os.environ[env_var] = cred.api_key` and optionally sets a `*_API_BASE` variable for URL-based endpoints.

### Complex Provider Handling

Providers requiring multiple configuration fields receive specialized treatment. The module includes dedicated helpers: `_provision_vertex()` (lines 145-176), `_provision_azure()` (lines 174-216), and `_provision_openai_compatible()` (lines 219-244). These functions translate database credential fields into provider-specific environment variable sets required by Google Vertex, Azure OpenAI, and other complex services.

### Fallback Behavior for Backward Compatibility

If no credential record exists for a provider, the provisioning functions leave the environment unchanged. This design permits pre-existing environment variables (such as developer-set `OPENAI_API_KEY` values) to function normally, ensuring seamless migration from env-var-only deployments to database-managed credentials.

## Implementation Examples

Retrieve a key manually using the database-first lookup:

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

# Returns the key from the Credential table if present,

# otherwise reads OPENAI_API_KEY from the environment.

openai_key = await get_api_key("openai")

```

Provision environment variables for a single provider before model creation:

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

await provision_provider_keys("anthropic")

# Now os.getenv("ANTHROPIC_API_KEY") holds the value from the DB.

```

Load all credentials at application startup:

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

results = await provision_all_keys()
print(results)   # {"openai": True, "anthropic": False, ...}

```

## Why Database-First Credentials Matter

- **Single source of truth** – `PROVIDER_CONFIG` guarantees that every part of the codebase references the exact environment variable names for each provider.
- **Secure secret handling** – API keys remain encrypted in the `Credential` table and are only exposed to the running process via `os.environ` after a successful database fetch.
- **Operational flexibility** – Developers can override database values with local environment variables for testing, while production deployments rely on consistent database storage for easy secret rotation.
- **Extensibility** – Adding new providers requires only an entry in `PROVIDER_CONFIG` and, if needed, a small provisioning helper following the pattern established for Azure and Vertex.

## Summary

- The **key provider pattern** centralizes AI credential management in [`open_notebook/ai/key_provider.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/key_provider.py) through a `PROVIDER_CONFIG` mapping that defines environment variable expectations.
- **Database-first lookup** via `_get_default_credential()` and `get_api_key()` prioritizes encrypted `Credential` records over environment variables, falling back to env-vars only when database records are absent.
- Runtime provisioning functions inject secrets into `os.environ` before model instantiation, with specialized handlers for complex providers like Azure and Vertex.
- The architecture maintains backward compatibility by preserving existing environment variables when database credentials are unavailable.

## Frequently Asked Questions

### How does Open Notebook store AI provider credentials?

Credential records are stored in the database with encrypted `api_key` fields, accessed through the `Credential` model in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py). Each record maps to a specific provider, allowing multiple credentials to coexist in a single deployment.

### What happens if both database credentials and environment variables exist?

The database-first lookup in `get_api_key()` returns the stored credential immediately, ignoring the environment variable unless no database record exists. This prioritization ensures that database-managed secrets always take precedence.

### Can I add custom AI providers to the key provider pattern?

Yes, by adding an entry to `PROVIDER_CONFIG` in [`open_notebook/ai/key_provider.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/key_provider.py) and optionally implementing a provisioning helper if the provider requires multiple configuration fields beyond a simple API key.

### Where does Open Notebook call the provisioning functions?

The AI model instantiation code in [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py) calls `provision_provider_keys()` before creating model instances, while [`open_notebook/ai/provision.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/provision.py) provides higher-level orchestration for application-wide credential management.