# How to Configure AI Provider Credentials in the Database Instead of Environment Variables

> Securely store AI provider credentials in your database with Open Notebook. Learn how to configure database secrets instead of environment variables for enhanced security and flexibility.

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

---

**Open Notebook stores AI provider authentication data in SurrealDB's `credential` table, automatically decrypting and injecting these secrets into environment variables at runtime to override static env-var configurations.**

Open Notebook provides a flexible, database-first approach to managing AI provider credentials. Rather than relying solely on static environment variables, you can configure AI provider credentials in the database, enabling runtime updates without service restarts while maintaining backward compatibility with existing env-var setups.

## Understanding the Database-First Credential Architecture

Open Notebook uses **SurrealDB** as the backing store for credential management, implementing a secure encryption layer and a runtime injection system. When a model is provisioned, the system queries the database first, decrypts the stored secrets, and populates the appropriate environment variables dynamically.

This architecture ensures that **database-stored credentials take precedence** over environment variables, while still allowing legacy env-var configurations to function as fallbacks.

### The Credential Model and Encryption

The `Credential` model in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py) defines the schema for storing provider authentication data. Before saving, the `_prepare_save_data` method encrypts the `api_key` field to ensure secrets are never stored in plaintext. The model also includes a flexible `config` object (added in Migration 15) that preserves provider-specific settings such as `num_ctx`, `base_url`, or custom endpoints.

Key fields include:
- **`provider`** – The AI provider name (e.g., `openai`, `azure_openai`, `ollama`)
- **`api_key`** – Encrypted secret key
- **`modalities`** – Supported model types (language, embedding, image)
- **`base_url`** – Custom endpoint URL for self-hosted providers

### 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 handles runtime credential resolution. The `provision_provider_keys` function retrieves the default credential for a specified provider and injects the decrypted values into `os.environ` using provider-specific variable names defined in `PROVIDER_CONFIG`.

## Storing Credentials in SurrealDB

You can populate the credential table either through the REST API or directly via Python code. Both methods automatically encrypt sensitive data before persistence.

### Using the Credentials API

The Credentials API router ([`api/routers/credentials.py`](https://github.com/lfnovo/open-notebook/blob/main/api/routers/credentials.py)) exposes CRUD endpoints for managing credentials. Create a new credential by POSTing to the credentials endpoint:

```bash
curl -X POST http://localhost:5055/credentials \
  -H "Content-Type: application/json" \
  -d '{
        "name": "OpenAI Production",
        "provider": "openai",
        "modalities": ["language", "embedding"],
        "api_key": "sk-REDACTED",
        "config": {"num_ctx": 16384}
      }'

```

### Programmatic Credential Creation

For self-hosted providers like Ollama, you can create credentials directly in Python:

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

await Credential(
    name="Local Ollama",
    provider="ollama",
    api_key="dummy",            # Ollama may not require a key

    base_url="http://localhost:11434"
).save()

```

## Runtime Loading and Environment Variable Injection

When provisioning models, Open Notebook automatically loads database credentials before initializing the AI client. This process ensures that any runtime updates to the credential table are immediately available without restarting the service.

### How provision_provider_keys Works

The workflow occurs in [`open_notebook/ai/models.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/models.py) when `AIFactory.create_language()` is invoked:

1. **`provision_provider_keys`** calls `_get_default_credential` internally
2. **`_get_default_credential`** executes `SELECT * FROM credential WHERE provider = $provider` via `Credential.get_by_provider`
3. **Decryption** – The `decrypt_value` method decrypts the stored API key
4. **Injection** – The key and configuration are written to `os.environ` (e.g., `OPENAI_API_KEY`, `AZURE_OPENAI_ENDPOINT`)

### Provider-Specific Configuration Mapping

The `PROVIDER_CONFIG` dictionary maps provider names to their corresponding environment variable names. When provisioning, the system copies not only the `api_key` but also provider-specific fields like `base_url`, `endpoint`, or `project` into the appropriate env vars.

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

# Load DB-stored OpenAI credentials into env vars

await provision_provider_keys("openai")

# Create model using the dynamically loaded credentials

model = AIFactory.create_language(model_name="gpt-4", provider="openai")

```

## Reading Credentials for Custom Logic

If you need to access credentials directly for custom implementations, use the `Credential` domain model:

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

# Get all stored OpenAI credentials

openai_creds = await Credential.get_by_provider("openai")
for cred in openai_creds:
    print(cred.name, cred.api_key.get_secret_value())

```

## Summary

- **Database-First Approach**: Open Notebook queries SurrealDB's `credential` table before checking environment variables, allowing runtime credential updates without service restarts.
- **Automatic Encryption**: The `Credential` model in [`open_notebook/domain/credential.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/domain/credential.py) encrypts API keys via `_prepare_save_data` before storage.
- **Runtime Injection**: The `provision_provider_keys` function in [`open_notebook/ai/key_provider.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/ai/key_provider.py) decrypts credentials and injects them into `os.environ` during model provisioning.
- **Flexible Configuration**: The `config` field supports provider-specific settings like `base_url`, `num_ctx`, and custom endpoints.
- **Backward Compatibility**: If no database credential exists, the system falls back to existing environment variables.

## Frequently Asked Questions

### How are API keys secured when stored in the database?

API keys are encrypted before storage using the `_prepare_save_data` method in the `Credential` model. When retrieved, the `decrypt_value` method decrypts the secret only at runtime, ensuring plaintext keys never persist in the database.

### Can I use multiple credentials for the same provider?

Yes, the `Credential.get_by_provider` method returns all credentials for a given provider. However, `_get_default_credential` selects the first matching record, so the system typically uses the earliest created credential unless you implement custom selection logic.

### What happens if no credential exists in the database?

If `provision_provider_keys` finds no matching credential in SurrealDB, the function exits without modifying environment variables. The application then relies on existing environment variables (e.g., `OPENAI_API_KEY`), maintaining backward compatibility with traditional configuration methods.

### Which environment variables are set when provisioning credentials?

The specific variables depend on the provider configuration. For OpenAI, the system sets `OPENAI_API_KEY`. For Azure OpenAI, it sets `AZURE_OPENAI_API_KEY`, `AZURE_OPENAI_ENDPOINT`, and related project fields. Self-hosted providers like Ollama receive `OLLAMA_API_BASE` mapped from the `base_url` field.