How Open Notebook Implements Fallback Logic for Credential Providers: Database-First with Environment Variable Fallback

Open Notebook uses a database-first credential system that automatically falls back to environment variables when database entries are missing, ensuring backward compatibility while centralizing secret management in SurrealDB.

Open Notebook is a privacy-first AI research assistant built with FastAPI and SurrealDB. Its fallback logic for credential providers allows teams to store AI provider secrets securely in the database while maintaining compatibility with legacy code that expects traditional environment variables. This architecture ensures seamless migration from env-var-only setups to a managed credential system without breaking existing integrations.

Architecture Overview

The credential system spans three layers. The Credential domain model (open_notebook/domain/credential.py) defines encrypted storage for API keys and provider configurations. The key provider module (open_notebook/ai/key_provider.py) implements the fallback logic that bridges database records and environment variables. Finally, the API service layer (api/credentials_service.py) handles migrations and model discovery, ensuring that both database credentials and environment variables can coexist.

The Credential Model: Database as Source of Truth

The Credential class in open_notebook/domain/credential.py serves as the single source of truth for all AI provider authentication. Key implementation details include:

  • Dynamic configuration: The CONFIG_EXTRAS mechanism mirrors top-level fields (e.g., num_ctx) into a flexible config dictionary, allowing new provider settings without database migrations.
  • Encryption at rest: The api_key field is encrypted before storage; decryption occurs during read operations in _from_db_row, get, and get_all methods.
  • Esperanto integration: The to_esperanto_config() method builds the configuration dictionary required by the Esperanto library's AIFactory, pulling values from both the model and its config bag.

Fallback Logic Implementation

The fallback mechanism is centralized in open_notebook/ai/key_provider.py through the provision_provider_keys() function. This module implements a database-first, environment-variable-second strategy.

Database-First Lookup

The _get_default_credential(provider) function queries SurrealDB for the first credential record matching the requested provider. If found, the system decrypts the API key and prepares it for use.

Environment Variable Fallback

When no database credential exists, get_api_key(provider) consults the PROVIDER_CONFIG mapping—a dictionary that maps provider names to their traditional environment variable names (e.g., OPENAI_API_KEY, GROQ_API_KEY). The function returns the env-var value if the database lookup fails.

Provisioning Environment Variables

The _provision_simple_provider() function bridges the gap by copying database credentials into os.environ. This ensures that downstream libraries like Esperanto—which often read standard environment variables—work transparently whether the credential originated from the database or the environment. For complex providers like Azure or Vertex AI, dedicated provisioning helpers set mode-specific variables such as AZURE_OPENAI_ENDPOINT_LLM.

Provider Availability and Model Discovery

The fallback logic determines provider availability in api/routers/models.py. The system considers a provider available if either condition is true:

has_cred = await _check_provider_has_credential(provider)
has_env = os.environ.get(env_var) is not None
provider_status[provider] = has_cred or has_env

Before executing model discovery, the router calls await provision_provider_keys(provider) to populate environment variables from the database. The discover_with_config function in api/credentials_service.py then builds requests using cred.to_esperanto_config(), enabling seamless operation regardless of whether credentials are stored in the database or environment.

Migration Strategies for Legacy Credentials

Open Notebook provides two migration commands in api/credentials_service.py for teams transitioning from environment-variable-based setups:

  • migrate_from_provider_config(): Migrates legacy singleton ProviderConfig entries into individual Credential rows while preserving existing model links.
  • migrate_from_env(): Scans PROVIDER_ENV_CONFIG for configured environment variables, creates encrypted Credential records for each provider, and links unassigned models.

Both migrations invoke require_encryption_key() to ensure OPEN_NOTEBOOK_ENCRYPTION_KEY is present before storing any secrets.

Practical Implementation Examples

Provision Provider Keys from Database

Use provision_provider_keys to load database credentials into the environment before calling AI libraries:

from open_notebook.ai.key_provider import provision_provider_keys
import os

# Load OpenAI credentials from DB into os.environ

await provision_provider_keys("openai")

# Verify the key is available

print(os.getenv("OPENAI_API_KEY"))  # Decrypted value from database

Discover Models with Fallback Support

This pattern is used in api/routers/models.py to handle discovery requests:

from open_notebook.ai.key_provider import provision_provider_keys
from open_notebook.ai.model_discovery import discover_provider_models

async def discover_models(provider: str):
    # Ensures env-vars are set from DB or falls back to existing env-vars

    await provision_provider_keys(provider)
    
    # Calls external API using configured credentials

    models = await discover_provider_models(provider)
    return [{"name": m.name, "type": m.model_type} for m in models]

Migrate Environment Variables to Database

Run the migration utility to convert existing env-vars to encrypted database records:


# Requires OPEN_NOTEBOOK_ENCRYPTION_KEY to be set

uv run python -m api.credentials_service migrate_from_env

This command creates Credential records from values found in PROVIDER_ENV_CONFIG, encrypts the API keys, and associates orphaned models with the new credentials.

Summary

  • Database-first storage: All provider credentials live in SurrealDB with encryption at rest, managed through the Credential model in open_notebook/domain/credential.py.
  • Transparent fallback: The provision_provider_keys function in open_notebook/ai/key_provider.py queries the database first, then falls back to environment variables via PROVIDER_CONFIG.
  • Legacy compatibility: Migration utilities in api/credentials_service.py allow zero-downtime transitions from environment-variable setups to database-managed credentials.
  • Unified interface: The to_esperanto_config() method ensures the Esperanto AI library receives consistent configuration regardless of credential source.

Frequently Asked Questions

How does Open Notebook prioritize between database credentials and environment variables?

The system always checks the SurrealDB database first via _get_default_credential(). Only if no credential exists for the provider does it fall back to reading the environment variable defined in PROVIDER_CONFIG. When database credentials are found, they are copied into os.environ to ensure downstream libraries see the most recent values.

Is the fallback mechanism secure for multi-tenant deployments?

Yes. The fallback logic in open_notebook/ai/key_provider.py operates within the application's memory space and does not persist database credentials to external configuration files. Credentials are decrypted only when needed and loaded into the process environment temporarily. For multi-tenant scenarios, each request operates with isolated credential contexts.

Can I use the fallback logic to test providers without adding them to the database?

Absolutely. If you set the standard environment variable (e.g., OPENAI_API_KEY) in your shell or container configuration, Open Notebook will use it when no database entry exists. This allows testing and development without running migrations, while production deployments can rely on database-managed credentials for better security and auditing.

What encryption method protects API keys stored in the database?

API keys are encrypted using the key derived from OPEN_NOTEBOOK_ENCRYPTION_KEY before storage in SurrealDB. The Credential class handles decryption transparently during read operations in methods like _from_db_row and get_all, ensuring that plaintext values never persist in the database and are only available in memory during runtime.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →