How Open Notebook Manages AI Provider Credentials: Architecture and Security
Open Notebook stores AI provider credentials as individual encrypted records in SurrealDB, exposing them via a FastAPI router while never returning raw API keys in API responses.
The lfnovo/open-notebook project implements a secure, domain-driven approach to AI provider credential management that replaces global configuration files with encrypted database records. Each credential is stored as a first-class entity that integrates with the Esperanto multi-provider library, supporting automatic encryption at rest and granular control over authentication lifecycle. This architecture ensures that sensitive API keys remain encrypted while remaining accessible to the application for model discovery and inference.
Core Architecture of AI Provider Credential Management
The Credential Domain Model
The Credential class in open_notebook/domain/credential.py defines the central data structure for AI provider authentication. As a concrete SurrealDB record, it encapsulates the provider name, API key, optional base URLs, and provider-specific configuration options like num_ctx. The model implements to_esperanto_config(), which constructs the configuration dictionary that the Esperanto library expects, enabling seamless integration with any supported AI provider.
Encryption and Security Implementation
Security relies on Fernet symmetric encryption via the OPEN_NOTEBOOK_ENCRYPTION_KEY environment variable defined in open_notebook/utils/encryption.py. Before persistence, the _prepare_save_data method encrypts the API key using encrypt_value, while retrieval methods including get, get_all, and _from_db_row automatically decrypt the value using decrypt_value. The API key is wrapped in pydantic.SecretStr to prevent accidental logging or serialization of plaintext secrets.
Legacy Migration from ProviderConfig
The legacy singleton pattern in open_notebook/domain/provider_config.py previously aggregated multiple ProviderCredential objects per provider. The system provides a migration path via the /credentials/migrate-from-provider-config endpoint, which reads the legacy configuration, decrypts any stored keys, and creates individual Credential records in the SurrealDB table.
API Layer for Credential Management
CRUD Operations via FastAPI
The api/routers/credentials.py module exposes REST endpoints for full lifecycle management of AI provider credentials. The POST /credentials endpoint validates endpoint URLs, ensures the encryption key is present via require_encryption_key, and wraps the raw key in SecretStr before calling await cred.save(). Listing operations return sanitized CredentialResponse objects that exclude the raw API key, while PUT /credentials/{id} supports partial updates where None values clear optional fields.
Discovery and Model Registration
After credential creation, the /credentials/{id}/discover endpoint uses the decrypted key to query the provider for available models. The get_linked_models() method queries the model table for records where the credential field matches the credential ID, enabling the UI to display model counts and relationships. When deleting credentials, the system supports cascading reassignment of linked models via the migrate_to query parameter, with fallback to raw database operations if decryption fails due to encryption key rotation.
Using AI Provider Credentials in Practice
Creating Credentials Programmatically
You can instantiate and save credentials directly using the domain model:
from open_notebook.domain.credential import Credential
from pydantic import SecretStr
cred = Credential(
name="Prod‑OpenAI",
provider="openai",
modalities=["language", "embedding"],
api_key=SecretStr("sk‑my‑secret‑key"),
base_url=None,
endpoint=None,
api_version="2023‑08‑15",
num_ctx=16384,
)
await cred.save()
The save() call automatically encrypts the API key before persistence.
Integrating with the Esperanto Library
To use a stored credential for inference, convert it to the Esperanto configuration format:
from open_notebook.domain.credential import Credential
cred = await Credential.get("<credential‑id>")
config = cred.to_esperanto_config() # Returns dict with decrypted key
# Pass to Esperanto factory or your model initialization
This approach bypasses environment variable lookups in favor of the stored credential configuration.
Listing Credentials via REST API
Retrieve all credentials without exposing sensitive data:
curl -X GET http://localhost:5055/credentials \
-H "Authorization: Bearer <token>"
The response includes metadata fields like id, name, provider, and model_count, but never the raw API key.
Migrating from Legacy Configuration
Convert existing provider configurations to the new credential system:
curl -X POST http://localhost:5055/credentials/migrate-from-provider-config
This endpoint returns a summary of migrated credentials and their new record IDs.
Summary
- Encrypted Storage: API keys are encrypted at rest using Fernet encryption via
OPEN_NOTEBOOK_ENCRYPTION_KEYand wrapped inpydantic.SecretStr. - Domain Model: The
Credentialclass inopen_notebook/domain/credential.pyhandles encryption, decryption, and conversion to Esperanto configuration formats. - Secure API: The FastAPI router in
api/routers/credentials.pynever returns raw API keys in responses, filtering sensitive fields throughCredentialResponse. - Model Linkage: Credentials maintain relationships with AI models through the
get_linked_models()method, enabling cascade operations during deletion. - Migration Support: Legacy configurations from
provider_config.pymigrate to individualCredentialrecords via dedicated API endpoints.
Frequently Asked Questions
How are API keys encrypted in open-notebook?
API keys are encrypted using Fernet symmetric encryption from the cryptography library, implemented in open_notebook/utils/encryption.py. The system requires the OPEN_NOTEBOOK_ENCRYPTION_KEY environment variable; if absent, keys are stored in plain text with a security warning. All encryption operations happen transparently through the Credential model's _prepare_save_data and _from_db_row methods.
What happens if the server encryption key changes?
If the encryption key changes, existing credentials cannot be decrypted normally. The deletion and migration endpoints in api/routers/credentials.py implement fallback logic that uses raw database queries to retrieve and delete records when decryption fails, allowing administrators to clean up or migrate credentials even after key rotation.
How do I migrate from the old provider configuration?
Use the /credentials/migrate-from-provider-config endpoint, which reads the legacy singleton from open_notebook/domain/provider_config.py, decrypts any stored authentication data, and creates new individual Credential records for each provider configuration. This process preserves the original credentials while upgrading to the encrypted record-based architecture.
Can a single credential support multiple AI providers?
No, each Credential record is tied to a specific provider name (e.g., "openai", "anthropic"). However, you can create multiple Credential records for the same provider with different configurations, and the system tracks which models are linked to each credential via the get_linked_models() method.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →