# cjm-fasthtml-byok
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
## Install
``` bash
pip install cjm_fasthtml_byok
```
## Project Structure
nbs/
├── components/ (2)
│ ├── alerts.ipynb # FastHTML alert and notification components for user feedback
│ └── forms.ipynb # FastHTML form components for API key input and management
├── core/ (3)
│ ├── security.ipynb # Encryption and security utilities for API key management
│ ├── storage.ipynb # Storage backends for API keys (session and database)
│ └── types.ipynb # Type definitions and protocols for the BYOK system
├── middleware/ (1)
│ └── beforeware.ipynb # FastHTML beforeware for API key management
└── utils/ (1)
└── helpers.ipynb # Helper functions for BYOK system
Total: 7 notebooks across 4 directories
## Module Dependencies
``` mermaid
graph LR
components_alerts[components.alerts<br/>Alerts]
components_forms[components.forms<br/>Forms]
core_security[core.security<br/>Security]
core_storage[core.storage<br/>Storage]
core_types[core.types<br/>Types]
middleware_beforeware[middleware.beforeware<br/>Beforeware]
utils_helpers[utils.helpers<br/>Helpers]
components_forms --> core_security
components_forms --> utils_helpers
core_security --> core_types
core_storage --> core_types
core_storage --> core_security
middleware_beforeware --> core_security
middleware_beforeware --> core_types
middleware_beforeware --> core_storage
utils_helpers --> core_security
```
*9 cross-module dependencies detected*
## CLI Reference
No CLI commands found in this project.
## Module Overview
Detailed documentation for each module in the project:
### Alerts (`alerts.ipynb`)
> FastHTML alert and notification components for user feedback
#### Import
``` python
from cjm_fasthtml_byok.components.alerts import (
InfoIcon,
SuccessIcon,
WarningIcon,
ErrorIcon,
Alert,
SecurityAlert,
KeyStatusNotification,
ToastContainer,
Toast,
ValidationMessage,
AlertStack
)
```
#### Functions
``` python
def InfoIcon(
size: str = "6" # Size of the icon (matches Tailwind h-{size} and w-{size} classes)
) -> FT: # SVG element for the info icon
"Create an info icon SVG."
```
``` python
def SuccessIcon(
size: str = "6" # Size of the icon (matches Tailwind h-{size} and w-{size} classes)
) -> FT: # SVG element for the success icon
"Create a success/check icon SVG."
```
``` python
def WarningIcon(
size: str = "6" # Size of the icon (matches Tailwind h-{size} and w-{size} classes)
) -> FT: # SVG element for the warning icon
"Create a warning/exclamation icon SVG."
```
``` python
def ErrorIcon(
size: str = "6" # Size of the icon (matches Tailwind h-{size} and w-{size} classes)
) -> FT: # SVG element for the error icon
"Create an error/X icon SVG."
```
``` python
def Alert(
message: str, # The alert message
kind: Literal["info", "success", "warning", "error"] = "info",
title: Optional[str] = None, # Optional title for the alert
dismissible: bool = False, # Whether the alert can be dismissed
show_icon: bool = True, # Whether to show an icon
style: Optional[str] = None, # Alert style ("soft", "outline", or None for default)
id: Optional[str] = None # HTML ID for the alert element
) -> FT: # Alert component
"Create an alert component for displaying messages."
```
``` python
def SecurityAlert(
message: str, # Security alert message
severity: Literal["low", "medium", "high", "critical"] = "medium",
action_url: Optional[str] = None, # Optional URL for remediation action
action_text: str = "Fix Now" # Text for the action button
) -> FT: # Security alert component
"Create a security-focused alert with severity levels."
```
``` python
def KeyStatusNotification(
provider: str, # Provider name
status: Literal["added", "updated", "deleted", "expired", "invalid"],
masked_key: Optional[str] = None, # Masked version of the key
auto_dismiss: bool = True, # Whether to auto-dismiss
dismiss_after: int = 5000 # Milliseconds before auto-dismiss
) -> FT: # Key status notification component
"Create a notification for API key status changes."
```
``` python
def ToastContainer(
position: Literal["top", "middle", "bottom"] = "top",
align: Literal["start", "center", "end"] = "end",
id: str = "toast-container" # HTML ID for the container
) -> FT: # Toast container component
"Create a container for toast notifications."
```
``` python
def Toast(
message: str, # Toast message
kind: Literal["info", "success", "warning", "error"] = "info",
duration: int = 3000 # Duration in milliseconds
) -> FT: # Toast notification component
"Create a toast notification."
```
``` python
def ValidationMessage(
message: str, # Validation message
is_valid: bool = False, # Whether the validation passed
show_icon: bool = True # Whether to show an icon
) -> FT: # Validation message component
"Create an inline validation message for form fields."
```
``` python
def AlertStack(
alerts: list, # List of alert components
max_visible: int = 3, # Maximum number of visible alerts
spacing: str = "4" # Gap between alerts
) -> FT: # Alert stack component
"Create a stack of alerts with optional limit."
```
### Beforeware (`beforeware.ipynb`)
> FastHTML beforeware for API key management
#### Import
``` python
from cjm_fasthtml_byok.middleware.beforeware import (
create_byok_beforeware,
require_api_key,
require_any_api_key,
SecurityCheckBeforeware,
CleanupBeforeware,
setup_byok
)
```
#### Functions
``` python
def create_byok_beforeware(
byok_manager: BYOKManager # The BYOK manager instance
)
"Create a FastHTML beforeware handler for BYOK functionality."
```
``` python
def require_api_key(
provider: str, # The provider name to check for
user_id_func: Optional[Callable] = None # Optional function to get user_id from request Usage: @rt @require_api_key("openai") def chat(request): byok = request.scope['byok'] api_key = byok.get_key(request, "openai") # Use the API key...
)
"Decorator that requires an API key to be present for a route."
```
``` python
def require_any_api_key(
providers: List[str], # List of provider names to check
user_id_func: Optional[Callable] = None # Optional function to get user_id from request Usage: @rt @require_any_api_key(["openai", "anthropic", "google"]) def chat(request): # Use whichever API key is available pass
)
"Decorator that requires at least one of the specified API keys."
```
``` python
def setup_byok(
secret_key: str, # Application secret key
db: Optional[Any] = None, # Optional database for persistent storage
user_id_func: Optional[Callable] = None, # Optional function to get user_id from request
enable_security_checks: bool = True, # Enable HTTPS checking
enable_cleanup: bool = True # Enable automatic cleanup of expired keys
)
"Complete setup helper for BYOK with FastHTML. Returns beforeware functions and the BYOK manager."
```
#### Classes
``` python
class SecurityCheckBeforeware:
def __init__(
self,
require_https: bool = True, # Whether to require HTTPS in production
is_production: Optional[bool] = None # Whether running in production (auto-detected if None)
)
"Beforeware that performs security checks."
def __init__(
self,
require_https: bool = True, # Whether to require HTTPS in production
is_production: Optional[bool] = None # Whether running in production (auto-detected if None)
)
"Initialize security check beforeware with HTTPS requirements"
```
``` python
class CleanupBeforeware:
def __init__(
self,
byok_manager: BYOKManager, # The BYOK manager instance
user_id_func: Optional[Callable] = None # Optional function to get user_id from request
)
"Beforeware that cleans up expired keys."
def __init__(
self,
byok_manager: BYOKManager, # The BYOK manager instance
user_id_func: Optional[Callable] = None # Optional function to get user_id from request
)
"Initialize cleanup beforeware with BYOK manager"
```
### Forms (`forms.ipynb`)
> FastHTML form components for API key input and management
#### Import
``` python
from cjm_fasthtml_byok.components.forms import (
KeyInputForm,
MultiProviderKeyForm,
KeyManagementCard,
KeyManagerDashboard,
InlineKeyInput
)
```
#### Functions
``` python
def KeyInputForm(
provider: str, # The API provider identifier
action: Optional[str] = None, # Form action URL (defaults to /api/keys/{provider})
method: str = "post", # HTTP method (default: "post")
show_help: bool = True, # Whether to show help text
custom_placeholder: Optional[str] = None, # Custom placeholder text
extra_fields: Optional[List[tuple]] = None, # Additional form fields as [(name, type, placeholder, required), ...]
provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration
) -> FT: # FastHTML Form component
"Create a form for inputting an API key with improved design."
```
``` python
def MultiProviderKeyForm(
providers: List[str], # List of provider identifiers
action: str = "/api/keys", # Form action URL
method: str = "post", # HTTP method
default_provider: Optional[str] = None, # Initially selected provider
provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration
) -> FT: # FastHTML Form component with provider selection
"Create a form that allows selecting from multiple providers with enhanced UX."
```
``` python
def KeyManagementCard(
provider: str, # Provider identifier
has_key: bool, # Whether a key is stored
masked_key: Optional[str] = None, # Masked version of the key for display
created_at: Optional[str] = None, # When the key was stored
expires_at: Optional[str] = None, # When the key expires
delete_action: Optional[str] = None, # URL for delete action
update_action: Optional[str] = None, # URL for update action
provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration
) -> FT: # Card component for key management
"Create a card component for managing a stored API key with enhanced design."
```
``` python
def KeyManagerDashboard(
request, # FastHTML request object
providers: List[str], # List of provider identifiers to manage
byok_manager = None,
user_id: Optional[str] = None, # Optional user ID for database storage
base_url: str = "/api/keys", # Base URL for API endpoints
provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration
) -> FT: # Dashboard component with all provider cards
"Create a complete dashboard for managing multiple API keys with improved layout."
```
``` python
def InlineKeyInput(
provider: str, # Provider identifier
input_id: Optional[str] = None, # HTML ID for the input element
on_save: Optional[str] = None, # JavaScript to execute on save (or hx-post URL for HTMX)
compact: bool = True, # Whether to use compact styling
provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration
) -> FT: # Inline input component
"Create a compact inline key input component with polished design."
```
### Helpers (`helpers.ipynb`)
> Helper functions for BYOK system
#### Import
``` python
from cjm_fasthtml_byok.utils.helpers import (
get_provider_info,
format_provider_name,
format_key_age,
format_expiration,
get_key_summary,
get_env_key,
import_from_env
)
```
#### Functions
``` python
def get_provider_info(
provider: str, # Provider identifier
provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration
) -> Dict[str, Any]: # Provider info dict with defaults
"Get provider information from config or generate defaults."
```
``` python
def format_provider_name(
provider: str, # Provider identifier
provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration
) -> str: # Formatted provider name
"Format provider name for display."
```
``` python
def format_key_age(
created_at: datetime # When the key was created
) -> str: # Human-readable age string
"Format the age of a key for display."
```
``` python
def format_expiration(
expires_at: Optional[datetime] # Expiration datetime
) -> str: # Human-readable expiration string
"Format expiration time for display."
```
``` python
def get_key_summary(
byok_manager, # BYOK manager instance
request, # FastHTML request
user_id: Optional[str] = None, # Optional user ID
provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration
) -> Dict[str, Any]: # Summary dictionary with provider info
"Get a summary of all stored keys."
```
``` python
def get_env_key(
provider: str, # Provider name
env_prefix: str = "API_KEY_" # Environment variable prefix
) -> Optional[str]: # API key from environment or None
"Get an API key from environment variables."
```
``` python
def import_from_env(
byok_manager, # BYOK manager instance
request, # FastHTML request
providers: List[str], # List of providers to check
user_id: Optional[str] = None, # Optional user ID
env_prefix: str = "API_KEY_" # Environment variable prefix
) -> Dict[str, bool]: # Dict of provider: success status
"Import API keys from environment variables."
```
### Security (`security.ipynb`)
> Encryption and security utilities for API key management
#### Import
``` python
from cjm_fasthtml_byok.core.security import (
generate_encryption_key,
get_or_create_app_key,
KeyEncryptor,
check_https,
validate_environment,
mask_key,
get_key_fingerprint
)
```
#### Functions
``` python
def generate_encryption_key(
password: Optional[str] = None, # Optional password to derive key from
salt: Optional[bytes] = None # Optional salt for key derivation (required if password provided)
) -> bytes: # 32-byte encryption key suitable for Fernet
"Generate or derive an encryption key."
```
``` python
def get_or_create_app_key(
"Get or create an app-specific encryption key derived from the app's secret key."
```
``` python
def check_https(
request # FastHTML/Starlette request object
) -> bool: # True if using HTTPS, False otherwise
"Check if the request is using HTTPS."
```
``` python
def validate_environment(
request, # FastHTML/Starlette request object
require_https: bool = True, # Whether to require HTTPS
is_production: bool = None # Whether running in production (auto-detected if None)
) -> None
"Validate the security environment."
```
``` python
def mask_key(
key: str, # The API key to mask
visible_chars: int = 4 # Number of characters to show at start and end
) -> str: # Masked key like 'sk-a...xyz'
"Mask an API key for display purposes."
```
``` python
def get_key_fingerprint(
key: str # The API key
) -> str: # SHA256 fingerprint of the key (first 16 chars)
"Generate a fingerprint for an API key (for logging/tracking without exposing the key)."
```
#### Classes
``` python
class KeyEncryptor:
def __init__(
self,
encryption_key: Optional[bytes] = None # Encryption key to use. If None, generates a new one
)
"Handles encryption and decryption of API keys."
def __init__(
self,
encryption_key: Optional[bytes] = None # Encryption key to use. If None, generates a new one
)
"Initialize the encryptor.
Args:
encryption_key: Encryption key to use. If None, generates a new one."
def encrypt(
self,
value: str # Plain text API key to encrypt
) -> bytes: # Encrypted bytes
"Encrypt an API key value.
Args:
value: Plain text API key
Returns:
Encrypted bytes
Raises:
EncryptionError: If encryption fails"
def decrypt(
self,
encrypted_value: bytes # Encrypted bytes to decrypt
) -> str: # Decrypted plain text API key
"Decrypt an API key value.
Args:
encrypted_value: Encrypted bytes
Returns:
Decrypted API key
Raises:
EncryptionError: If decryption fails"
def rotate_key(
self,
new_key: bytes, # New encryption key to use
encrypted_value: bytes # Value encrypted with current key
) -> bytes: # Value re-encrypted with new key
"Re-encrypt a value with a new key.
Args:
new_key: New encryption key
encrypted_value: Value encrypted with current key
Returns:
Value encrypted with new key"
```
### Storage (`storage.ipynb`)
> Storage backends for API keys (session and database)
#### Import
``` python
from cjm_fasthtml_byok.core.storage import (
SessionStorage,
DatabaseStorage,
HybridStorage,
BYOKManager
)
```
#### Classes
``` python
class SessionStorage:
def __init__(
self,
config: BYOKConfig # BYOK configuration object
)
"""
Session-based storage for API keys.
Keys are stored in the user's session and expire with the session.
"""
def __init__(
self,
config: BYOKConfig # BYOK configuration object
)
"Initialize session storage with configuration"
def store(
self,
request: Any, # FastHTML/Starlette request object with session
key: APIKey # API key object to store
) -> None
"Store an API key in the session"
def retrieve(
self,
request: Any, # FastHTML/Starlette request object with session
provider: str, # Provider name to retrieve key for
user_id: Optional[str] = None # User ID (unused in session storage)
) -> Optional[APIKey]: # API key object if found and valid, None otherwise
"Retrieve an API key from the session"
def delete(
self,
request: Any, # FastHTML/Starlette request object with session
provider: str, # Provider name to delete key for
user_id: Optional[str] = None # User ID (unused in session storage)
) -> None
"Delete an API key from the session"
def list_providers(
self,
request: Any, # FastHTML/Starlette request object with session
user_id: Optional[str] = None # User ID (unused in session storage)
) -> List[str]: # List of provider names with stored keys
"List all providers with stored keys"
def clear_all(
self,
request: Any, # FastHTML/Starlette request object with session
user_id: Optional[str] = None # User ID (unused in session storage)
) -> None
"Clear all API keys from the session"
```
``` python
class DatabaseStorage:
def __init__(
self,
config: BYOKConfig, # BYOK configuration object
db_url: str = "sqlite:///byok_keys.db" # Database URL (defaults to SQLite)
)
"""
Database-backed storage for API keys using SQLAlchemy 2.0+.
Keys persist across sessions and devices.
"""
def __init__(
self,
config: BYOKConfig, # BYOK configuration object
db_url: str = "sqlite:///byok_keys.db" # Database URL (defaults to SQLite)
)
"Initialize database storage with SQLAlchemy."
def store(
self,
request: Any, # FastHTML/Starlette request object (unused but kept for interface consistency)
key: APIKey # API key object to store in database
) -> None
"Store an API key in the database"
def retrieve(
self,
request: Any, # FastHTML/Starlette request object (unused but kept for interface consistency)
provider: str, # Provider name to retrieve key for
user_id: Optional[str] = None # User ID to retrieve key for (required for database)
) -> Optional[APIKey]: # API key object if found and valid, None otherwise
"Retrieve an API key from the database"
def delete(
self,
request: Any, # FastHTML/Starlette request object (unused but kept for interface consistency)
provider: str, # Provider name to delete key for
user_id: Optional[str] = None # User ID to delete key for (required for database)
) -> None
"Delete an API key from the database"
def list_providers(
self,
request: Any, # FastHTML/Starlette request object (unused but kept for interface consistency)
user_id: Optional[str] = None # User ID to list providers for (required for database)
) -> List[str]: # List of provider names with stored keys
"List all providers with stored keys for a user"
def clear_all(
self,
request: Any, # FastHTML/Starlette request object (unused but kept for interface consistency)
user_id: Optional[str] = None # User ID to clear keys for (required for database)
) -> None
"Clear all API keys for a user"
```
``` python
class HybridStorage:
def __init__(
self,
config: BYOKConfig, # BYOK configuration object
db_url: Optional[str] = None # Optional database URL for persistent storage
)
"""
Hybrid storage using both session and database.
Session acts as a cache, database provides persistence.
"""
def __init__(
self,
config: BYOKConfig, # BYOK configuration object
db_url: Optional[str] = None # Optional database URL for persistent storage
)
"Initialize hybrid storage with session and optional database backends"
def store(
self,
request: Any, # FastHTML/Starlette request object with session
key: APIKey # API key object to store
) -> None
"Store in both session and database"
def retrieve(
self,
request: Any, # FastHTML/Starlette request object with session
provider: str, # Provider name to retrieve key for
user_id: Optional[str] = None # User ID for database lookup
) -> Optional[APIKey]: # API key object if found, None otherwise
"Retrieve from session first, then database"
def delete(
self,
request: Any, # FastHTML/Starlette request object with session
provider: str, # Provider name to delete key for
user_id: Optional[str] = None # User ID for database deletion
) -> None
"Delete from both storages"
def list_providers(
self,
request: Any, # FastHTML/Starlette request object with session
user_id: Optional[str] = None # User ID for database lookup
) -> List[str]: # Combined list of providers from both storages
"List providers from both storages"
def clear_all(
self,
request: Any, # FastHTML/Starlette request object with session
user_id: Optional[str] = None # User ID for database clearing
) -> None
"Clear from both storages"
```
``` python
class BYOKManager:
def __init__(
self,
secret_key: str, # Application secret key for encryption
db_url: Optional[str] = None, # Optional database URL for persistent storage (e.g., "sqlite:///keys.db")
config: Optional[BYOKConfig] = None # Optional configuration (uses defaults if not provided)
)
"""
Main manager for the BYOK system.
Handles encryption and storage coordination.
"""
def __init__(
self,
secret_key: str, # Application secret key for encryption
db_url: Optional[str] = None, # Optional database URL for persistent storage (e.g., "sqlite:///keys.db")
config: Optional[BYOKConfig] = None # Optional configuration (uses defaults if not provided)
)
"Initialize the BYOK manager."
def set_key(
self,
request: Any, # FastHTML/Starlette request object
provider: str, # Provider name (e.g., 'openai', 'anthropic')
api_key: str, # The API key to store
user_id: Optional[str] = None, # Optional user ID for database storage
ttl: Optional[timedelta] = None # Optional time-to-live for the key
) -> None
"Store an API key."
def get_key(
self,
request: Any, # FastHTML/Starlette request object
provider: str, # Provider name
user_id: Optional[str] = None # Optional user ID for database lookup
) -> Optional[str]: # Decrypted API key or None if not found
"Retrieve and decrypt an API key."
def delete_key(
self,
request: Any, # FastHTML/Starlette request object
provider: str, # Provider name
user_id: Optional[str] = None # Optional user ID
) -> None
"Delete an API key."
def list_providers(
self,
request: Any, # FastHTML/Starlette request object
user_id: Optional[str] = None # Optional user ID
) -> List[str]: # List of provider names
"List all providers with stored keys."
def clear_keys(
self,
request: Any, # FastHTML/Starlette request object
user_id: Optional[str] = None # Optional user ID
) -> None
"Clear all stored API keys."
def has_key(
self,
request: Any, # FastHTML/Starlette request object
provider: str, # Provider name
user_id: Optional[str] = None # Optional user ID
) -> bool: # True if key exists, False otherwise
"Check if a key exists for a provider."
```
### Types (`types.ipynb`)
> Type definitions and protocols for the BYOK system
#### Import
``` python
from cjm_fasthtml_byok.core.types import (
StorageBackend,
APIKey,
KeyStorage,
BYOKConfig,
UserAPIKey,
BYOKException,
EncryptionError,
StorageError,
KeyNotFoundError,
SecurityWarning
)
```
#### Classes
``` python
class StorageBackend(Enum):
"Available storage backends for API keys"
```
``` python
@dataclass
class APIKey:
"Represents an encrypted API key with metadata"
provider: str # e.g., 'openai', 'anthropic', 'google'
encrypted_value: bytes # Encrypted key value
created_at: datetime = field(...)
expires_at: Optional[datetime]
user_id: Optional[str] # For database storage
def is_expired(
self
) -> bool: # True if key has expired, False otherwise
"Check if the key has expired"
def to_dict(
self
) -> Dict[str, Any]: # Dictionary representation for serialization
"Convert to dictionary for storage"
def from_dict(
cls, # The APIKey class
data: Dict[str, Any] # Dictionary containing serialized key data
) -> 'APIKey': # Reconstructed APIKey instance
"Create from dictionary"
```
``` python
@runtime_checkable
class KeyStorage(Protocol):
"Protocol for key storage implementations"
def store(
self,
request: Any, # FastHTML/Starlette request object
key: APIKey # API key object to store
) -> None
"Store an API key"
def retrieve(
self,
request: Any, # FastHTML/Starlette request object
provider: str, # Provider name to retrieve key for
user_id: Optional[str] = None # Optional user ID for database lookup
) -> Optional[APIKey]: # API key object if found, None otherwise
"Retrieve an API key for a provider"
def delete(
self,
request: Any, # FastHTML/Starlette request object
provider: str, # Provider name to delete key for
user_id: Optional[str] = None # Optional user ID for database deletion
) -> None
"Delete an API key"
def list_providers(
self,
request: Any, # FastHTML/Starlette request object
user_id: Optional[str] = None # Optional user ID for database lookup
) -> list[str]: # List of provider names with stored keys
"List all stored providers"
def clear_all(
self,
request: Any, # FastHTML/Starlette request object
user_id: Optional[str] = None # Optional user ID for database clearing
) -> None
"Clear all stored keys"
```
``` python
@dataclass
class BYOKConfig:
"Configuration for the BYOK system"
storage_backend: StorageBackend = StorageBackend.SESSION
encryption_key: Optional[bytes] # If None, will be generated
default_ttl: Optional[timedelta] = timedelta(hours=24) # Default key expiration
session_key_prefix: str = 'byok_' # Prefix for session storage keys
db_table_name: str = 'user_api_keys' # Database table name
auto_cleanup: bool = True # Auto-cleanup expired keys
require_https: bool = True # Warn if not using HTTPS in production
```
``` python
class UserAPIKey:
"Database schema for persistent API key storage (for use with fastsql)"
```
``` python
class BYOKException(Exception):
"Base exception for BYOK errors"
```
``` python
class EncryptionError(BYOKException):
"Error during encryption/decryption"
```
``` python
class StorageError(BYOKException):
"Error during storage operations"
```
``` python
class KeyNotFoundError(BYOKException):
"Requested key not found"
```
``` python
class SecurityWarning(BYOKException):
"Security-related warning"
```
Raw data
{
"_id": null,
"home_page": "https://github.com/cj-mills/cjm-fasthtml-byok",
"name": "cjm-fasthtml-byok",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "nbdev jupyter notebook python",
"author": "Christian J. Mills",
"author_email": "9126128+cj-mills@users.noreply.github.com",
"download_url": "https://files.pythonhosted.org/packages/fd/3b/a66d6cd9073533ff1d285febd2e9bdf750cc2237372b690f8c39b7813585/cjm_fasthtml_byok-0.0.3.tar.gz",
"platform": null,
"description": "# cjm-fasthtml-byok\n\n\n<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->\n\n## Install\n\n``` bash\npip install cjm_fasthtml_byok\n```\n\n## Project Structure\n\n nbs/\n \u251c\u2500\u2500 components/ (2)\n \u2502 \u251c\u2500\u2500 alerts.ipynb # FastHTML alert and notification components for user feedback\n \u2502 \u2514\u2500\u2500 forms.ipynb # FastHTML form components for API key input and management\n \u251c\u2500\u2500 core/ (3)\n \u2502 \u251c\u2500\u2500 security.ipynb # Encryption and security utilities for API key management\n \u2502 \u251c\u2500\u2500 storage.ipynb # Storage backends for API keys (session and database)\n \u2502 \u2514\u2500\u2500 types.ipynb # Type definitions and protocols for the BYOK system\n \u251c\u2500\u2500 middleware/ (1)\n \u2502 \u2514\u2500\u2500 beforeware.ipynb # FastHTML beforeware for API key management\n \u2514\u2500\u2500 utils/ (1)\n \u2514\u2500\u2500 helpers.ipynb # Helper functions for BYOK system\n\nTotal: 7 notebooks across 4 directories\n\n## Module Dependencies\n\n``` mermaid\ngraph LR\n components_alerts[components.alerts<br/>Alerts]\n components_forms[components.forms<br/>Forms]\n core_security[core.security<br/>Security]\n core_storage[core.storage<br/>Storage]\n core_types[core.types<br/>Types]\n middleware_beforeware[middleware.beforeware<br/>Beforeware]\n utils_helpers[utils.helpers<br/>Helpers]\n\n components_forms --> core_security\n components_forms --> utils_helpers\n core_security --> core_types\n core_storage --> core_types\n core_storage --> core_security\n middleware_beforeware --> core_security\n middleware_beforeware --> core_types\n middleware_beforeware --> core_storage\n utils_helpers --> core_security\n```\n\n*9 cross-module dependencies detected*\n\n## CLI Reference\n\nNo CLI commands found in this project.\n\n## Module Overview\n\nDetailed documentation for each module in the project:\n\n### Alerts (`alerts.ipynb`)\n\n> FastHTML alert and notification components for user feedback\n\n#### Import\n\n``` python\nfrom cjm_fasthtml_byok.components.alerts import (\n InfoIcon,\n SuccessIcon,\n WarningIcon,\n ErrorIcon,\n Alert,\n SecurityAlert,\n KeyStatusNotification,\n ToastContainer,\n Toast,\n ValidationMessage,\n AlertStack\n)\n```\n\n#### Functions\n\n``` python\ndef InfoIcon(\n size: str = \"6\" # Size of the icon (matches Tailwind h-{size} and w-{size} classes)\n) -> FT: # SVG element for the info icon\n \"Create an info icon SVG.\"\n```\n\n``` python\ndef SuccessIcon(\n size: str = \"6\" # Size of the icon (matches Tailwind h-{size} and w-{size} classes)\n) -> FT: # SVG element for the success icon\n \"Create a success/check icon SVG.\"\n```\n\n``` python\ndef WarningIcon(\n size: str = \"6\" # Size of the icon (matches Tailwind h-{size} and w-{size} classes)\n) -> FT: # SVG element for the warning icon\n \"Create a warning/exclamation icon SVG.\"\n```\n\n``` python\ndef ErrorIcon(\n size: str = \"6\" # Size of the icon (matches Tailwind h-{size} and w-{size} classes)\n) -> FT: # SVG element for the error icon\n \"Create an error/X icon SVG.\"\n```\n\n``` python\ndef Alert(\n message: str, # The alert message\n kind: Literal[\"info\", \"success\", \"warning\", \"error\"] = \"info\",\n title: Optional[str] = None, # Optional title for the alert\n dismissible: bool = False, # Whether the alert can be dismissed\n show_icon: bool = True, # Whether to show an icon\n style: Optional[str] = None, # Alert style (\"soft\", \"outline\", or None for default)\n id: Optional[str] = None # HTML ID for the alert element\n) -> FT: # Alert component\n \"Create an alert component for displaying messages.\"\n```\n\n``` python\ndef SecurityAlert(\n message: str, # Security alert message\n severity: Literal[\"low\", \"medium\", \"high\", \"critical\"] = \"medium\",\n action_url: Optional[str] = None, # Optional URL for remediation action\n action_text: str = \"Fix Now\" # Text for the action button\n) -> FT: # Security alert component\n \"Create a security-focused alert with severity levels.\"\n```\n\n``` python\ndef KeyStatusNotification(\n provider: str, # Provider name\n status: Literal[\"added\", \"updated\", \"deleted\", \"expired\", \"invalid\"],\n masked_key: Optional[str] = None, # Masked version of the key\n auto_dismiss: bool = True, # Whether to auto-dismiss\n dismiss_after: int = 5000 # Milliseconds before auto-dismiss\n) -> FT: # Key status notification component\n \"Create a notification for API key status changes.\"\n```\n\n``` python\ndef ToastContainer(\n position: Literal[\"top\", \"middle\", \"bottom\"] = \"top\",\n align: Literal[\"start\", \"center\", \"end\"] = \"end\",\n id: str = \"toast-container\" # HTML ID for the container\n) -> FT: # Toast container component\n \"Create a container for toast notifications.\"\n```\n\n``` python\ndef Toast(\n message: str, # Toast message\n kind: Literal[\"info\", \"success\", \"warning\", \"error\"] = \"info\",\n duration: int = 3000 # Duration in milliseconds\n) -> FT: # Toast notification component\n \"Create a toast notification.\"\n```\n\n``` python\ndef ValidationMessage(\n message: str, # Validation message\n is_valid: bool = False, # Whether the validation passed\n show_icon: bool = True # Whether to show an icon\n) -> FT: # Validation message component\n \"Create an inline validation message for form fields.\"\n```\n\n``` python\ndef AlertStack(\n alerts: list, # List of alert components\n max_visible: int = 3, # Maximum number of visible alerts\n spacing: str = \"4\" # Gap between alerts\n) -> FT: # Alert stack component\n \"Create a stack of alerts with optional limit.\"\n```\n\n### Beforeware (`beforeware.ipynb`)\n\n> FastHTML beforeware for API key management\n\n#### Import\n\n``` python\nfrom cjm_fasthtml_byok.middleware.beforeware import (\n create_byok_beforeware,\n require_api_key,\n require_any_api_key,\n SecurityCheckBeforeware,\n CleanupBeforeware,\n setup_byok\n)\n```\n\n#### Functions\n\n``` python\ndef create_byok_beforeware(\n byok_manager: BYOKManager # The BYOK manager instance\n)\n \"Create a FastHTML beforeware handler for BYOK functionality.\"\n```\n\n``` python\ndef require_api_key(\n provider: str, # The provider name to check for\n user_id_func: Optional[Callable] = None # Optional function to get user_id from request Usage: @rt @require_api_key(\"openai\") def chat(request): byok = request.scope['byok'] api_key = byok.get_key(request, \"openai\") # Use the API key...\n)\n \"Decorator that requires an API key to be present for a route.\"\n```\n\n``` python\ndef require_any_api_key(\n providers: List[str], # List of provider names to check\n user_id_func: Optional[Callable] = None # Optional function to get user_id from request Usage: @rt @require_any_api_key([\"openai\", \"anthropic\", \"google\"]) def chat(request): # Use whichever API key is available pass\n)\n \"Decorator that requires at least one of the specified API keys.\"\n```\n\n``` python\ndef setup_byok(\n secret_key: str, # Application secret key\n db: Optional[Any] = None, # Optional database for persistent storage\n user_id_func: Optional[Callable] = None, # Optional function to get user_id from request\n enable_security_checks: bool = True, # Enable HTTPS checking\n enable_cleanup: bool = True # Enable automatic cleanup of expired keys\n)\n \"Complete setup helper for BYOK with FastHTML. Returns beforeware functions and the BYOK manager.\"\n```\n\n#### Classes\n\n``` python\nclass SecurityCheckBeforeware:\n def __init__(\n self,\n require_https: bool = True, # Whether to require HTTPS in production\n is_production: Optional[bool] = None # Whether running in production (auto-detected if None)\n )\n \"Beforeware that performs security checks.\"\n \n def __init__(\n self,\n require_https: bool = True, # Whether to require HTTPS in production\n is_production: Optional[bool] = None # Whether running in production (auto-detected if None)\n )\n \"Initialize security check beforeware with HTTPS requirements\"\n```\n\n``` python\nclass CleanupBeforeware:\n def __init__(\n self,\n byok_manager: BYOKManager, # The BYOK manager instance\n user_id_func: Optional[Callable] = None # Optional function to get user_id from request\n )\n \"Beforeware that cleans up expired keys.\"\n \n def __init__(\n self,\n byok_manager: BYOKManager, # The BYOK manager instance\n user_id_func: Optional[Callable] = None # Optional function to get user_id from request\n )\n \"Initialize cleanup beforeware with BYOK manager\"\n```\n\n### Forms (`forms.ipynb`)\n\n> FastHTML form components for API key input and management\n\n#### Import\n\n``` python\nfrom cjm_fasthtml_byok.components.forms import (\n KeyInputForm,\n MultiProviderKeyForm,\n KeyManagementCard,\n KeyManagerDashboard,\n InlineKeyInput\n)\n```\n\n#### Functions\n\n``` python\ndef KeyInputForm(\n provider: str, # The API provider identifier\n action: Optional[str] = None, # Form action URL (defaults to /api/keys/{provider})\n method: str = \"post\", # HTTP method (default: \"post\")\n show_help: bool = True, # Whether to show help text\n custom_placeholder: Optional[str] = None, # Custom placeholder text\n extra_fields: Optional[List[tuple]] = None, # Additional form fields as [(name, type, placeholder, required), ...]\n provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration\n) -> FT: # FastHTML Form component\n \"Create a form for inputting an API key with improved design.\"\n```\n\n``` python\ndef MultiProviderKeyForm(\n providers: List[str], # List of provider identifiers\n action: str = \"/api/keys\", # Form action URL\n method: str = \"post\", # HTTP method\n default_provider: Optional[str] = None, # Initially selected provider\n provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration\n) -> FT: # FastHTML Form component with provider selection\n \"Create a form that allows selecting from multiple providers with enhanced UX.\"\n```\n\n``` python\ndef KeyManagementCard(\n provider: str, # Provider identifier\n has_key: bool, # Whether a key is stored\n masked_key: Optional[str] = None, # Masked version of the key for display\n created_at: Optional[str] = None, # When the key was stored\n expires_at: Optional[str] = None, # When the key expires\n delete_action: Optional[str] = None, # URL for delete action\n update_action: Optional[str] = None, # URL for update action\n provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration\n) -> FT: # Card component for key management\n \"Create a card component for managing a stored API key with enhanced design.\"\n```\n\n``` python\ndef KeyManagerDashboard(\n request, # FastHTML request object\n providers: List[str], # List of provider identifiers to manage\n byok_manager = None,\n user_id: Optional[str] = None, # Optional user ID for database storage\n base_url: str = \"/api/keys\", # Base URL for API endpoints\n provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration\n) -> FT: # Dashboard component with all provider cards\n \"Create a complete dashboard for managing multiple API keys with improved layout.\"\n```\n\n``` python\ndef InlineKeyInput(\n provider: str, # Provider identifier\n input_id: Optional[str] = None, # HTML ID for the input element\n on_save: Optional[str] = None, # JavaScript to execute on save (or hx-post URL for HTMX)\n compact: bool = True, # Whether to use compact styling\n provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration\n) -> FT: # Inline input component\n \"Create a compact inline key input component with polished design.\"\n```\n\n### Helpers (`helpers.ipynb`)\n\n> Helper functions for BYOK system\n\n#### Import\n\n``` python\nfrom cjm_fasthtml_byok.utils.helpers import (\n get_provider_info,\n format_provider_name,\n format_key_age,\n format_expiration,\n get_key_summary,\n get_env_key,\n import_from_env\n)\n```\n\n#### Functions\n\n``` python\ndef get_provider_info(\n provider: str, # Provider identifier\n provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration\n) -> Dict[str, Any]: # Provider info dict with defaults\n \"Get provider information from config or generate defaults.\"\n```\n\n``` python\ndef format_provider_name(\n provider: str, # Provider identifier\n provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration\n) -> str: # Formatted provider name\n \"Format provider name for display.\"\n```\n\n``` python\ndef format_key_age(\n created_at: datetime # When the key was created\n) -> str: # Human-readable age string\n \"Format the age of a key for display.\"\n```\n\n``` python\ndef format_expiration(\n expires_at: Optional[datetime] # Expiration datetime\n) -> str: # Human-readable expiration string\n \"Format expiration time for display.\"\n```\n\n``` python\ndef get_key_summary(\n byok_manager, # BYOK manager instance\n request, # FastHTML request\n user_id: Optional[str] = None, # Optional user ID\n provider_config: Optional[Dict[str, Any]] = None # Optional provider configuration\n) -> Dict[str, Any]: # Summary dictionary with provider info\n \"Get a summary of all stored keys.\"\n```\n\n``` python\ndef get_env_key(\n provider: str, # Provider name\n env_prefix: str = \"API_KEY_\" # Environment variable prefix\n) -> Optional[str]: # API key from environment or None\n \"Get an API key from environment variables.\"\n```\n\n``` python\ndef import_from_env(\n byok_manager, # BYOK manager instance\n request, # FastHTML request\n providers: List[str], # List of providers to check\n user_id: Optional[str] = None, # Optional user ID\n env_prefix: str = \"API_KEY_\" # Environment variable prefix\n) -> Dict[str, bool]: # Dict of provider: success status\n \"Import API keys from environment variables.\"\n```\n\n### Security (`security.ipynb`)\n\n> Encryption and security utilities for API key management\n\n#### Import\n\n``` python\nfrom cjm_fasthtml_byok.core.security import (\n generate_encryption_key,\n get_or_create_app_key,\n KeyEncryptor,\n check_https,\n validate_environment,\n mask_key,\n get_key_fingerprint\n)\n```\n\n#### Functions\n\n``` python\ndef generate_encryption_key(\n password: Optional[str] = None, # Optional password to derive key from\n salt: Optional[bytes] = None # Optional salt for key derivation (required if password provided)\n) -> bytes: # 32-byte encryption key suitable for Fernet\n \"Generate or derive an encryption key.\"\n```\n\n``` python\ndef get_or_create_app_key(\n \"Get or create an app-specific encryption key derived from the app's secret key.\"\n```\n\n``` python\ndef check_https(\n request # FastHTML/Starlette request object\n) -> bool: # True if using HTTPS, False otherwise\n \"Check if the request is using HTTPS.\"\n```\n\n``` python\ndef validate_environment(\n request, # FastHTML/Starlette request object\n require_https: bool = True, # Whether to require HTTPS\n is_production: bool = None # Whether running in production (auto-detected if None)\n) -> None\n \"Validate the security environment.\"\n```\n\n``` python\ndef mask_key(\n key: str, # The API key to mask\n visible_chars: int = 4 # Number of characters to show at start and end\n) -> str: # Masked key like 'sk-a...xyz'\n \"Mask an API key for display purposes.\"\n```\n\n``` python\ndef get_key_fingerprint(\n key: str # The API key\n) -> str: # SHA256 fingerprint of the key (first 16 chars)\n \"Generate a fingerprint for an API key (for logging/tracking without exposing the key).\"\n```\n\n#### Classes\n\n``` python\nclass KeyEncryptor:\n def __init__(\n self,\n encryption_key: Optional[bytes] = None # Encryption key to use. If None, generates a new one\n )\n \"Handles encryption and decryption of API keys.\"\n \n def __init__(\n self,\n encryption_key: Optional[bytes] = None # Encryption key to use. If None, generates a new one\n )\n \"Initialize the encryptor.\n\nArgs:\n encryption_key: Encryption key to use. If None, generates a new one.\"\n \n def encrypt(\n self,\n value: str # Plain text API key to encrypt\n ) -> bytes: # Encrypted bytes\n \"Encrypt an API key value.\n\nArgs:\n value: Plain text API key\n\nReturns:\n Encrypted bytes\n\nRaises:\n EncryptionError: If encryption fails\"\n \n def decrypt(\n self,\n encrypted_value: bytes # Encrypted bytes to decrypt\n ) -> str: # Decrypted plain text API key\n \"Decrypt an API key value.\n\nArgs:\n encrypted_value: Encrypted bytes\n\nReturns:\n Decrypted API key\n\nRaises:\n EncryptionError: If decryption fails\"\n \n def rotate_key(\n self,\n new_key: bytes, # New encryption key to use\n encrypted_value: bytes # Value encrypted with current key\n ) -> bytes: # Value re-encrypted with new key\n \"Re-encrypt a value with a new key.\n\nArgs:\n new_key: New encryption key\n encrypted_value: Value encrypted with current key\n\nReturns:\n Value encrypted with new key\"\n```\n\n### Storage (`storage.ipynb`)\n\n> Storage backends for API keys (session and database)\n\n#### Import\n\n``` python\nfrom cjm_fasthtml_byok.core.storage import (\n SessionStorage,\n DatabaseStorage,\n HybridStorage,\n BYOKManager\n)\n```\n\n#### Classes\n\n``` python\nclass SessionStorage:\n def __init__(\n self,\n config: BYOKConfig # BYOK configuration object\n )\n \"\"\"\n Session-based storage for API keys.\n Keys are stored in the user's session and expire with the session.\n \"\"\"\n \n def __init__(\n self,\n config: BYOKConfig # BYOK configuration object\n )\n \"Initialize session storage with configuration\"\n \n def store(\n self,\n request: Any, # FastHTML/Starlette request object with session\n key: APIKey # API key object to store\n ) -> None\n \"Store an API key in the session\"\n \n def retrieve(\n self,\n request: Any, # FastHTML/Starlette request object with session\n provider: str, # Provider name to retrieve key for\n user_id: Optional[str] = None # User ID (unused in session storage)\n ) -> Optional[APIKey]: # API key object if found and valid, None otherwise\n \"Retrieve an API key from the session\"\n \n def delete(\n self,\n request: Any, # FastHTML/Starlette request object with session\n provider: str, # Provider name to delete key for\n user_id: Optional[str] = None # User ID (unused in session storage)\n ) -> None\n \"Delete an API key from the session\"\n \n def list_providers(\n self,\n request: Any, # FastHTML/Starlette request object with session\n user_id: Optional[str] = None # User ID (unused in session storage)\n ) -> List[str]: # List of provider names with stored keys\n \"List all providers with stored keys\"\n \n def clear_all(\n self,\n request: Any, # FastHTML/Starlette request object with session\n user_id: Optional[str] = None # User ID (unused in session storage)\n ) -> None\n \"Clear all API keys from the session\"\n```\n\n``` python\nclass DatabaseStorage:\n def __init__(\n self,\n config: BYOKConfig, # BYOK configuration object\n db_url: str = \"sqlite:///byok_keys.db\" # Database URL (defaults to SQLite)\n )\n \"\"\"\n Database-backed storage for API keys using SQLAlchemy 2.0+.\n Keys persist across sessions and devices.\n \"\"\"\n \n def __init__(\n self,\n config: BYOKConfig, # BYOK configuration object\n db_url: str = \"sqlite:///byok_keys.db\" # Database URL (defaults to SQLite)\n )\n \"Initialize database storage with SQLAlchemy.\"\n \n def store(\n self,\n request: Any, # FastHTML/Starlette request object (unused but kept for interface consistency)\n key: APIKey # API key object to store in database\n ) -> None\n \"Store an API key in the database\"\n \n def retrieve(\n self,\n request: Any, # FastHTML/Starlette request object (unused but kept for interface consistency)\n provider: str, # Provider name to retrieve key for\n user_id: Optional[str] = None # User ID to retrieve key for (required for database)\n ) -> Optional[APIKey]: # API key object if found and valid, None otherwise\n \"Retrieve an API key from the database\"\n \n def delete(\n self,\n request: Any, # FastHTML/Starlette request object (unused but kept for interface consistency)\n provider: str, # Provider name to delete key for\n user_id: Optional[str] = None # User ID to delete key for (required for database)\n ) -> None\n \"Delete an API key from the database\"\n \n def list_providers(\n self,\n request: Any, # FastHTML/Starlette request object (unused but kept for interface consistency)\n user_id: Optional[str] = None # User ID to list providers for (required for database)\n ) -> List[str]: # List of provider names with stored keys\n \"List all providers with stored keys for a user\"\n \n def clear_all(\n self,\n request: Any, # FastHTML/Starlette request object (unused but kept for interface consistency)\n user_id: Optional[str] = None # User ID to clear keys for (required for database)\n ) -> None\n \"Clear all API keys for a user\"\n```\n\n``` python\nclass HybridStorage:\n def __init__(\n self,\n config: BYOKConfig, # BYOK configuration object\n db_url: Optional[str] = None # Optional database URL for persistent storage\n )\n \"\"\"\n Hybrid storage using both session and database.\n Session acts as a cache, database provides persistence.\n \"\"\"\n \n def __init__(\n self,\n config: BYOKConfig, # BYOK configuration object\n db_url: Optional[str] = None # Optional database URL for persistent storage\n )\n \"Initialize hybrid storage with session and optional database backends\"\n \n def store(\n self,\n request: Any, # FastHTML/Starlette request object with session\n key: APIKey # API key object to store\n ) -> None\n \"Store in both session and database\"\n \n def retrieve(\n self,\n request: Any, # FastHTML/Starlette request object with session\n provider: str, # Provider name to retrieve key for\n user_id: Optional[str] = None # User ID for database lookup\n ) -> Optional[APIKey]: # API key object if found, None otherwise\n \"Retrieve from session first, then database\"\n \n def delete(\n self,\n request: Any, # FastHTML/Starlette request object with session\n provider: str, # Provider name to delete key for\n user_id: Optional[str] = None # User ID for database deletion\n ) -> None\n \"Delete from both storages\"\n \n def list_providers(\n self,\n request: Any, # FastHTML/Starlette request object with session\n user_id: Optional[str] = None # User ID for database lookup\n ) -> List[str]: # Combined list of providers from both storages\n \"List providers from both storages\"\n \n def clear_all(\n self,\n request: Any, # FastHTML/Starlette request object with session\n user_id: Optional[str] = None # User ID for database clearing\n ) -> None\n \"Clear from both storages\"\n```\n\n``` python\nclass BYOKManager:\n def __init__(\n self,\n secret_key: str, # Application secret key for encryption\n db_url: Optional[str] = None, # Optional database URL for persistent storage (e.g., \"sqlite:///keys.db\")\n config: Optional[BYOKConfig] = None # Optional configuration (uses defaults if not provided)\n )\n \"\"\"\n Main manager for the BYOK system.\n Handles encryption and storage coordination.\n \"\"\"\n \n def __init__(\n self,\n secret_key: str, # Application secret key for encryption\n db_url: Optional[str] = None, # Optional database URL for persistent storage (e.g., \"sqlite:///keys.db\")\n config: Optional[BYOKConfig] = None # Optional configuration (uses defaults if not provided)\n )\n \"Initialize the BYOK manager.\"\n \n def set_key(\n self,\n request: Any, # FastHTML/Starlette request object\n provider: str, # Provider name (e.g., 'openai', 'anthropic')\n api_key: str, # The API key to store\n user_id: Optional[str] = None, # Optional user ID for database storage\n ttl: Optional[timedelta] = None # Optional time-to-live for the key\n ) -> None\n \"Store an API key.\"\n \n def get_key(\n self,\n request: Any, # FastHTML/Starlette request object\n provider: str, # Provider name\n user_id: Optional[str] = None # Optional user ID for database lookup\n ) -> Optional[str]: # Decrypted API key or None if not found\n \"Retrieve and decrypt an API key.\"\n \n def delete_key(\n self,\n request: Any, # FastHTML/Starlette request object\n provider: str, # Provider name\n user_id: Optional[str] = None # Optional user ID\n ) -> None\n \"Delete an API key.\"\n \n def list_providers(\n self,\n request: Any, # FastHTML/Starlette request object\n user_id: Optional[str] = None # Optional user ID\n ) -> List[str]: # List of provider names\n \"List all providers with stored keys.\"\n \n def clear_keys(\n self,\n request: Any, # FastHTML/Starlette request object\n user_id: Optional[str] = None # Optional user ID\n ) -> None\n \"Clear all stored API keys.\"\n \n def has_key(\n self,\n request: Any, # FastHTML/Starlette request object\n provider: str, # Provider name\n user_id: Optional[str] = None # Optional user ID\n ) -> bool: # True if key exists, False otherwise\n \"Check if a key exists for a provider.\"\n```\n\n### Types (`types.ipynb`)\n\n> Type definitions and protocols for the BYOK system\n\n#### Import\n\n``` python\nfrom cjm_fasthtml_byok.core.types import (\n StorageBackend,\n APIKey,\n KeyStorage,\n BYOKConfig,\n UserAPIKey,\n BYOKException,\n EncryptionError,\n StorageError,\n KeyNotFoundError,\n SecurityWarning\n)\n```\n\n#### Classes\n\n``` python\nclass StorageBackend(Enum):\n \"Available storage backends for API keys\"\n```\n\n``` python\n@dataclass\nclass APIKey:\n \"Represents an encrypted API key with metadata\"\n \n provider: str # e.g., 'openai', 'anthropic', 'google'\n encrypted_value: bytes # Encrypted key value\n created_at: datetime = field(...)\n expires_at: Optional[datetime]\n user_id: Optional[str] # For database storage\n \n def is_expired(\n self\n ) -> bool: # True if key has expired, False otherwise\n \"Check if the key has expired\"\n \n def to_dict(\n self\n ) -> Dict[str, Any]: # Dictionary representation for serialization\n \"Convert to dictionary for storage\"\n \n def from_dict(\n cls, # The APIKey class\n data: Dict[str, Any] # Dictionary containing serialized key data\n ) -> 'APIKey': # Reconstructed APIKey instance\n \"Create from dictionary\"\n```\n\n``` python\n@runtime_checkable\nclass KeyStorage(Protocol):\n \"Protocol for key storage implementations\"\n \n def store(\n self,\n request: Any, # FastHTML/Starlette request object\n key: APIKey # API key object to store\n ) -> None\n \"Store an API key\"\n \n def retrieve(\n self,\n request: Any, # FastHTML/Starlette request object\n provider: str, # Provider name to retrieve key for\n user_id: Optional[str] = None # Optional user ID for database lookup\n ) -> Optional[APIKey]: # API key object if found, None otherwise\n \"Retrieve an API key for a provider\"\n \n def delete(\n self,\n request: Any, # FastHTML/Starlette request object\n provider: str, # Provider name to delete key for\n user_id: Optional[str] = None # Optional user ID for database deletion\n ) -> None\n \"Delete an API key\"\n \n def list_providers(\n self,\n request: Any, # FastHTML/Starlette request object\n user_id: Optional[str] = None # Optional user ID for database lookup\n ) -> list[str]: # List of provider names with stored keys\n \"List all stored providers\"\n \n def clear_all(\n self,\n request: Any, # FastHTML/Starlette request object\n user_id: Optional[str] = None # Optional user ID for database clearing\n ) -> None\n \"Clear all stored keys\"\n```\n\n``` python\n@dataclass\nclass BYOKConfig:\n \"Configuration for the BYOK system\"\n \n storage_backend: StorageBackend = StorageBackend.SESSION\n encryption_key: Optional[bytes] # If None, will be generated\n default_ttl: Optional[timedelta] = timedelta(hours=24) # Default key expiration\n session_key_prefix: str = 'byok_' # Prefix for session storage keys\n db_table_name: str = 'user_api_keys' # Database table name\n auto_cleanup: bool = True # Auto-cleanup expired keys\n require_https: bool = True # Warn if not using HTTPS in production\n```\n\n``` python\nclass UserAPIKey:\n \"Database schema for persistent API key storage (for use with fastsql)\"\n```\n\n``` python\nclass BYOKException(Exception):\n \"Base exception for BYOK errors\"\n```\n\n``` python\nclass EncryptionError(BYOKException):\n \"Error during encryption/decryption\"\n```\n\n``` python\nclass StorageError(BYOKException):\n \"Error during storage operations\"\n```\n\n``` python\nclass KeyNotFoundError(BYOKException):\n \"Requested key not found\"\n```\n\n``` python\nclass SecurityWarning(BYOKException):\n \"Security-related warning\"\n```\n",
"bugtrack_url": null,
"license": "Apache Software License 2.0",
"summary": "Secure API key management for FastHTML applications with encrypted storage, session/database persistence, and built-in UI components.",
"version": "0.0.3",
"project_urls": {
"Homepage": "https://github.com/cj-mills/cjm-fasthtml-byok"
},
"split_keywords": [
"nbdev",
"jupyter",
"notebook",
"python"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "f82cc08dab4bed11c1ecead1dfab706de0c21510b26a29b7f107627b7f02ece7",
"md5": "ed52f745160bb85330f151b639f76e77",
"sha256": "6a1d9477e645a9ce1e31d60d9c735edc8863fe31ffb683a86274dc8059899e17"
},
"downloads": -1,
"filename": "cjm_fasthtml_byok-0.0.3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "ed52f745160bb85330f151b639f76e77",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 35976,
"upload_time": "2025-10-24T21:46:07",
"upload_time_iso_8601": "2025-10-24T21:46:07.032404Z",
"url": "https://files.pythonhosted.org/packages/f8/2c/c08dab4bed11c1ecead1dfab706de0c21510b26a29b7f107627b7f02ece7/cjm_fasthtml_byok-0.0.3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "fd3ba66d6cd9073533ff1d285febd2e9bdf750cc2237372b690f8c39b7813585",
"md5": "9e1dba4c7b92a67d00174432d5fd0820",
"sha256": "55f03ccdb2cac8e65e04b011e5e72a7a399d992e1f113b3bd7998895b1c77781"
},
"downloads": -1,
"filename": "cjm_fasthtml_byok-0.0.3.tar.gz",
"has_sig": false,
"md5_digest": "9e1dba4c7b92a67d00174432d5fd0820",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 38425,
"upload_time": "2025-10-24T21:46:08",
"upload_time_iso_8601": "2025-10-24T21:46:08.440135Z",
"url": "https://files.pythonhosted.org/packages/fd/3b/a66d6cd9073533ff1d285febd2e9bdf750cc2237372b690f8c39b7813585/cjm_fasthtml_byok-0.0.3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-24 21:46:08",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "cj-mills",
"github_project": "cjm-fasthtml-byok",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "cjm-fasthtml-byok"
}