Database¶
Public API for async SQLite database operations with connection pooling and SQLCipher encryption. Provides a high-level interface for database operations with automatic connection management.
Tip
Pair this reference with Database for the feature guide.
Quick overview¶
AsyncDatabaseprovides the main entry point for database operationsConnectionPoolmanages connection lifecycle with health checks and retryPoolStatstracks pool usage statisticsis_sqlcipher_available()checks if SQLCipher encryption is availableEncryption keys can be sourced from passphrase, environment variable, or SOPS
All operations are async-first with context manager support
Configuration cascade¶
The module consults the loaded config for default values. A minimal config block:
db:
pool:
min_size: 2
max_size: 20
acquire_timeout: 30.0
retry:
max_attempts: 3
delay: 0.5
cipher:
enabled: false # Opt-in SQLCipher encryption
key_source: env # env | sops | passphrase
key_env: "KSTLIB_DB_KEY"
Override any of these per instance:
from kstlib.db import AsyncDatabase
db = AsyncDatabase(
"app.db",
pool_min=5,
pool_max=50,
pool_timeout=60.0
)
Usage patterns¶
Basic database operations¶
from kstlib.db import AsyncDatabase
async with AsyncDatabase(":memory:") as db:
await db.execute("CREATE TABLE test (id INTEGER)")
await db.execute("INSERT INTO test VALUES (?)", (1,))
row = await db.fetch_one("SELECT * FROM test")
Encrypted database with SOPS¶
from kstlib.db import AsyncDatabase
db = AsyncDatabase(
"secure.db",
cipher_sops="secrets.yml",
cipher_sops_key="database_key"
)
async with db:
await db.execute("SELECT * FROM sensitive_data")
Transaction with automatic rollback¶
async with db.transaction() as conn:
await conn.execute("INSERT INTO accounts VALUES (?, ?)", (1, 1000))
await conn.execute("INSERT INTO accounts VALUES (?, ?)", (2, 500))
# Commits on success, rolls back on exception
Direct connection access¶
async with db.connection() as conn:
await conn.execute("PRAGMA optimize")
Key resolution from various sources¶
from kstlib.db import resolve_cipher_key
# From passphrase
key = resolve_cipher_key(passphrase="direct-key")
# From environment variable
key = resolve_cipher_key(env_var="DB_KEY")
# From SOPS file
key = resolve_cipher_key(sops_path="secrets.yml", sops_key="db_key")
Module reference¶
Async database module for SQLite/SQLCipher.
Provides: - AsyncDatabase: High-level async interface - ConnectionPool: Connection pooling with retry - SQLCipher encryption via SOPS integration
Examples
Basic in-memory database:
>>> from kstlib.db import AsyncDatabase
>>> db = AsyncDatabase(":memory:")
Encrypted database with SOPS:
>>> db = AsyncDatabase(
... "app.db",
... cipher_sops="secrets.yml",
... cipher_sops_key="database_key"
... )
Usage as context manager:
>>> async with AsyncDatabase(":memory:") as db:
... await db.execute("CREATE TABLE test (id INTEGER)")
... await db.execute("INSERT INTO test VALUES (?)", (1,))
... row = await db.fetch_one("SELECT * FROM test")
- class kstlib.db.AsyncDatabase(path, cipher_key=None, cipher_env=None, cipher_sops=None, cipher_sops_key='db_key', pool_min=1, pool_max=10, pool_timeout=30.0, max_retries=3, retry_delay=0.5, _pool=None, _resolved_key=None)[source]
Bases:
objectAsync database interface for SQLite/SQLCipher.
Provides connection pooling, encryption, and query helpers for async database operations.
- Parameters:
path (str | Path) – Path to database file (or “:memory:” for in-memory).
cipher_key (str | None) – Direct encryption key for SQLCipher.
cipher_env (str | None) – Environment variable containing cipher key.
cipher_sops (str | Path | None) – Path to SOPS file containing cipher key.
cipher_sops_key (str) – Key name in SOPS file (default: “db_key”).
pool_min (int) – Minimum pool connections.
pool_max (int) – Maximum pool connections.
pool_timeout (float) – Acquire timeout in seconds.
max_retries (int) – Retry attempts on failure.
retry_delay (float) – Delay between retries.
Examples
Basic usage:
>>> db = AsyncDatabase(":memory:") >>> db.path ':memory:'
With encryption:
>>> db = AsyncDatabase("app.db", cipher_key="secret")
With SOPS:
>>> db = AsyncDatabase("app.db", cipher_sops="secrets.yml")
- path: str | Path
- cipher_sops_key: str = 'db_key'
- pool_min: int = 1
- pool_max: int = 10
- pool_timeout: float = 30.0
- max_retries: int = 3
- retry_delay: float = 0.5
- __post_init__(self) 'None' -> None[source]
Resolve cipher key and apply config defaults with hard limits.
- async connect(self) 'None' -> None[source]
Initialize the connection pool.
Called automatically on first operation, but can be called explicitly for eager initialization.
- async close(self) 'None' -> None[source]
Close all connections and shutdown the pool.
- async __aenter__(self) 'Self' -> Self[source]
Async context manager entry.
- async __aexit__(self, exc_type: 'type[BaseException] | None', exc_val: 'BaseException | None', exc_tb: 'object') 'None' -> None[source]
Async context manager exit.
- connection(self) 'AsyncGenerator[aiosqlite.Connection, None]' -> AsyncGenerator[aiosqlite.Connection, None][source]
Get a connection from the pool.
- Yields:
Database connection.
Examples
>>> async with db.connection() as conn: ... await conn.execute("SELECT 1")
- transaction(self) 'AsyncGenerator[aiosqlite.Connection, None]' -> AsyncGenerator[aiosqlite.Connection, None][source]
Execute operations within a transaction.
Automatically commits on success, rolls back on error.
- Yields:
Database connection within transaction.
- Raises:
TransactionError – If transaction fails.
Examples
>>> async with db.transaction() as conn: ... await conn.execute("INSERT INTO users VALUES (?)", ("alice",)) ... await conn.execute("INSERT INTO users VALUES (?)", ("bob",))
- async execute(self, sql: 'str', parameters: 'Sequence[Any] | None' = None) 'aiosqlite.Cursor' -> aiosqlite.Cursor[source]
Execute a single SQL statement.
- Parameters:
sql (str) – SQL statement to execute.
parameters (Sequence[Any] | None) – Query parameters.
- Returns:
Cursor with results.
- Return type:
aiosqlite.Cursor
Examples
>>> await db.execute("CREATE TABLE test (id INTEGER)")
- async executemany(self, sql: 'str', parameters: 'Sequence[Sequence[Any]]') 'aiosqlite.Cursor' -> aiosqlite.Cursor[source]
Execute SQL statement for multiple parameter sets.
- Parameters:
sql (str) – SQL statement to execute.
parameters (Sequence[Sequence[Any]]) – Sequence of parameter tuples.
- Returns:
Cursor with results.
- Return type:
aiosqlite.Cursor
Examples
>>> await db.executemany( ... "INSERT INTO test VALUES (?)", ... [(1,), (2,), (3,)] ... )
- async fetch_one(self, sql: 'str', parameters: 'Sequence[Any] | None' = None) 'tuple[Any, ...] | None' -> tuple[Any, ...] | None[source]
Fetch a single row.
- Parameters:
sql (str) – SQL query.
parameters (Sequence[Any] | None) – Query parameters.
- Returns:
Row tuple or None if no results.
- Return type:
tuple[Any, …] | None
Examples
>>> row = await db.fetch_one("SELECT * FROM test WHERE id=?", (1,))
- async fetch_all(self, sql: 'str', parameters: 'Sequence[Any] | None' = None) 'list[tuple[Any, ...]]' -> list[tuple[Any, ...]][source]
Fetch all rows.
- Parameters:
sql (str) – SQL query.
parameters (Sequence[Any] | None) – Query parameters.
- Returns:
List of row tuples.
- Return type:
Examples
>>> rows = await db.fetch_all("SELECT * FROM test")
- async fetch_value(self, sql: 'str', parameters: 'Sequence[Any] | None' = None) 'Any' -> Any[source]
Fetch a single value (first column of first row).
- Parameters:
sql (str) – SQL query.
parameters (Sequence[Any] | None) – Query parameters.
- Returns:
Single value or None.
- Return type:
Any
Examples
>>> count = await db.fetch_value("SELECT count(*) FROM test")
- async table_exists(self, table_name: 'str') 'bool' -> bool[source]
Check if a table exists.
Examples
>>> await db.table_exists("users") False
- property stats: PoolStats
Get connection pool statistics.
- property is_encrypted: bool
Whether database is configured for encryption.
- property pool_size: int
Current number of connections in pool.
- __init__(self, path: 'str | Path', cipher_key: 'str | None' = None, cipher_env: 'str | None' = None, cipher_sops: 'str | Path | None' = None, cipher_sops_key: 'str' = 'db_key', pool_min: 'int' = 1, pool_max: 'int' = 10, pool_timeout: 'float' = 30.0, max_retries: 'int' = 3, retry_delay: 'float' = 0.5, _pool: 'ConnectionPool | None' = None, _resolved_key: 'str | None' = None) None -> None
- class kstlib.db.ConnectionPool(db_path, min_size=1, max_size=10, acquire_timeout=30.0, max_retries=3, retry_delay=0.5, cipher_key=None, on_connect=None, _pool=<factory>, _connections=<factory>, _lock=<factory>, _closed=False, _stats=<factory>)[source]
Bases:
objectAsync connection pool for SQLite/SQLCipher databases.
Manages a pool of database connections with health checks and automatic retry on failures.
- Parameters:
db_path (str) – Path to database file.
min_size (int) – Minimum connections to maintain.
max_size (int) – Maximum connections allowed.
acquire_timeout (float) – Timeout for acquiring connection.
max_retries (int) – Retry attempts on failure.
retry_delay (float) – Delay between retries.
cipher_key (str | None) – Optional encryption key for SQLCipher.
on_connect (Any | None) – Callback after connection established.
Examples
>>> pool = ConnectionPool(":memory:", min_size=1, max_size=5) >>> pool.max_size 5
- db_path: str
- min_size: int = 1
- max_size: int = 10
- acquire_timeout: float = 30.0
- max_retries: int = 3
- retry_delay: float = 0.5
- on_connect: Any | None = None
- __post_init__(self) 'None' -> None[source]
Validate and clamp configuration values to hard limits.
- async acquire(self) 'aiosqlite.Connection' -> aiosqlite.Connection[source]
Acquire a connection from the pool.
- Returns:
Database connection.
- Raises:
PoolExhaustedError – If no connection available within timeout.
DatabaseConnectionError – If connection creation fails after retries.
- Return type:
aiosqlite.Connection
- async release(self, conn: 'aiosqlite.Connection') 'None' -> None[source]
Release a connection back to the pool.
- Parameters:
conn (aiosqlite.Connection) – Connection to release.
- connection(self) 'AsyncGenerator[aiosqlite.Connection, None]' -> AsyncGenerator[aiosqlite.Connection, None][source]
Context manager for acquiring and releasing connections.
- Yields:
Database connection.
Examples
>>> async with pool.connection() as conn: ... await conn.execute("SELECT 1")
- async close(self) 'None' -> None[source]
Close all connections and shutdown the pool.
- property stats: PoolStats
Get pool statistics.
- property size: int
Current number of connections in pool.
- property is_closed: bool
Whether the pool is closed.
- __init__(self, db_path: 'str', min_size: 'int' = 1, max_size: 'int' = 10, acquire_timeout: 'float' = 30.0, max_retries: 'int' = 3, retry_delay: 'float' = 0.5, cipher_key: 'str | None' = None, on_connect: 'Any | None' = None, _pool: 'asyncio.Queue[aiosqlite.Connection]' = <factory>, _connections: 'set[aiosqlite.Connection]' = <factory>, _lock: 'asyncio.Lock' = <factory>, _closed: 'bool' = False, _stats: 'PoolStats' = <factory>) None -> None
- exception kstlib.db.DatabaseConnectionError[source]
Bases:
DatabaseErrorFailed to establish database connection.
- exception kstlib.db.DatabaseError[source]
Bases:
KstlibErrorBase exception for database operations.
- exception kstlib.db.EncryptionError[source]
Bases:
DatabaseErrorFailed to decrypt or access encrypted database.
- exception kstlib.db.PoolExhaustedError[source]
Bases:
DatabaseErrorConnection pool exhausted, no connections available.
- class kstlib.db.PoolStats(total_connections=0, active_connections=0, idle_connections=0, total_acquired=0, total_released=0, total_timeouts=0, total_errors=0)[source]
Bases:
objectStatistics for connection pool monitoring.
- total_connections
Total connections created.
- Type:
- active_connections
Currently in-use connections.
- Type:
- idle_connections
Available connections in pool.
- Type:
- total_acquired
Total acquire operations.
- Type:
- total_released
Total release operations.
- Type:
- total_timeouts
Acquire operations that timed out.
- Type:
- total_errors
Connection errors encountered.
- Type:
Examples
>>> stats = PoolStats() >>> stats.total_connections 0
- total_connections: int = 0
- active_connections: int = 0
- idle_connections: int = 0
- total_acquired: int = 0
- total_released: int = 0
- total_timeouts: int = 0
- total_errors: int = 0
- __init__(self, total_connections: 'int' = 0, active_connections: 'int' = 0, idle_connections: 'int' = 0, total_acquired: 'int' = 0, total_released: 'int' = 0, total_timeouts: 'int' = 0, total_errors: 'int' = 0) None -> None
- exception kstlib.db.TransactionError[source]
Bases:
DatabaseErrorTransaction operation failed.
- kstlib.db.apply_cipher_key(conn: 'sqlite3.Connection', key: 'str') 'None' -> None[source]
Apply SQLCipher key to a connection.
- Parameters:
conn (sqlite3.Connection) – SQLite connection object.
key (str) – Encryption key to apply.
- Raises:
EncryptionError – If key application fails.
- kstlib.db.is_sqlcipher_available() 'bool' -> bool[source]
Check if sqlcipher3 is installed and available.
- Returns:
True if sqlcipher3 can be imported.
- Return type:
Examples
>>> is_sqlcipher_available() True
- kstlib.db.resolve_cipher_key(*, passphrase: 'str | None' = None, env_var: 'str | None' = None, sops_path: 'str | Path | None' = None, sops_key: 'str' = 'db_key') 'str' -> str[source]
Resolve encryption key from various sources.
Priority: passphrase > env_var > sops_path
- Parameters:
- Returns:
Resolved encryption key.
- Raises:
EncryptionError – If no key source provided or resolution fails.
- Return type:
Examples
>>> key = resolve_cipher_key(passphrase="my-secret-key") >>> len(key) > 0 True
Database class¶
Async database wrapper for SQLite/SQLCipher.
Provides a high-level async interface for database operations with: - Connection pooling - Automatic retry on transient failures - SQLCipher encryption support - Transaction management - Query helpers
- class kstlib.db.database.AsyncDatabase(path, cipher_key=None, cipher_env=None, cipher_sops=None, cipher_sops_key='db_key', pool_min=1, pool_max=10, pool_timeout=30.0, max_retries=3, retry_delay=0.5, _pool=None, _resolved_key=None)[source]
Bases:
objectAsync database interface for SQLite/SQLCipher.
Provides connection pooling, encryption, and query helpers for async database operations.
- Parameters:
path (str | Path) – Path to database file (or “:memory:” for in-memory).
cipher_key (str | None) – Direct encryption key for SQLCipher.
cipher_env (str | None) – Environment variable containing cipher key.
cipher_sops (str | Path | None) – Path to SOPS file containing cipher key.
cipher_sops_key (str) – Key name in SOPS file (default: “db_key”).
pool_min (int) – Minimum pool connections.
pool_max (int) – Maximum pool connections.
pool_timeout (float) – Acquire timeout in seconds.
max_retries (int) – Retry attempts on failure.
retry_delay (float) – Delay between retries.
Examples
Basic usage:
>>> db = AsyncDatabase(":memory:") >>> db.path ':memory:'
With encryption:
>>> db = AsyncDatabase("app.db", cipher_key="secret")
With SOPS:
>>> db = AsyncDatabase("app.db", cipher_sops="secrets.yml")
- path: str | Path
- cipher_sops_key: str = 'db_key'
- pool_min: int = 1
- pool_max: int = 10
- pool_timeout: float = 30.0
- max_retries: int = 3
- retry_delay: float = 0.5
- __post_init__(self) 'None' -> None[source]
Resolve cipher key and apply config defaults with hard limits.
- async connect(self) 'None' -> None[source]
Initialize the connection pool.
Called automatically on first operation, but can be called explicitly for eager initialization.
- async close(self) 'None' -> None[source]
Close all connections and shutdown the pool.
- async __aenter__(self) 'Self' -> Self[source]
Async context manager entry.
- async __aexit__(self, exc_type: 'type[BaseException] | None', exc_val: 'BaseException | None', exc_tb: 'object') 'None' -> None[source]
Async context manager exit.
- connection(self) 'AsyncGenerator[aiosqlite.Connection, None]' -> AsyncGenerator[aiosqlite.Connection, None][source]
Get a connection from the pool.
- Yields:
Database connection.
Examples
>>> async with db.connection() as conn: ... await conn.execute("SELECT 1")
- transaction(self) 'AsyncGenerator[aiosqlite.Connection, None]' -> AsyncGenerator[aiosqlite.Connection, None][source]
Execute operations within a transaction.
Automatically commits on success, rolls back on error.
- Yields:
Database connection within transaction.
- Raises:
TransactionError – If transaction fails.
Examples
>>> async with db.transaction() as conn: ... await conn.execute("INSERT INTO users VALUES (?)", ("alice",)) ... await conn.execute("INSERT INTO users VALUES (?)", ("bob",))
- async execute(self, sql: 'str', parameters: 'Sequence[Any] | None' = None) 'aiosqlite.Cursor' -> aiosqlite.Cursor[source]
Execute a single SQL statement.
- Parameters:
sql (str) – SQL statement to execute.
parameters (Sequence[Any] | None) – Query parameters.
- Returns:
Cursor with results.
- Return type:
aiosqlite.Cursor
Examples
>>> await db.execute("CREATE TABLE test (id INTEGER)")
- async executemany(self, sql: 'str', parameters: 'Sequence[Sequence[Any]]') 'aiosqlite.Cursor' -> aiosqlite.Cursor[source]
Execute SQL statement for multiple parameter sets.
- Parameters:
sql (str) – SQL statement to execute.
parameters (Sequence[Sequence[Any]]) – Sequence of parameter tuples.
- Returns:
Cursor with results.
- Return type:
aiosqlite.Cursor
Examples
>>> await db.executemany( ... "INSERT INTO test VALUES (?)", ... [(1,), (2,), (3,)] ... )
- async fetch_one(self, sql: 'str', parameters: 'Sequence[Any] | None' = None) 'tuple[Any, ...] | None' -> tuple[Any, ...] | None[source]
Fetch a single row.
- Parameters:
sql (str) – SQL query.
parameters (Sequence[Any] | None) – Query parameters.
- Returns:
Row tuple or None if no results.
- Return type:
tuple[Any, …] | None
Examples
>>> row = await db.fetch_one("SELECT * FROM test WHERE id=?", (1,))
- async fetch_all(self, sql: 'str', parameters: 'Sequence[Any] | None' = None) 'list[tuple[Any, ...]]' -> list[tuple[Any, ...]][source]
Fetch all rows.
- Parameters:
sql (str) – SQL query.
parameters (Sequence[Any] | None) – Query parameters.
- Returns:
List of row tuples.
- Return type:
Examples
>>> rows = await db.fetch_all("SELECT * FROM test")
- async fetch_value(self, sql: 'str', parameters: 'Sequence[Any] | None' = None) 'Any' -> Any[source]
Fetch a single value (first column of first row).
- Parameters:
sql (str) – SQL query.
parameters (Sequence[Any] | None) – Query parameters.
- Returns:
Single value or None.
- Return type:
Any
Examples
>>> count = await db.fetch_value("SELECT count(*) FROM test")
- async table_exists(self, table_name: 'str') 'bool' -> bool[source]
Check if a table exists.
Examples
>>> await db.table_exists("users") False
- property stats: PoolStats
Get connection pool statistics.
- property is_encrypted: bool
Whether database is configured for encryption.
- property pool_size: int
Current number of connections in pool.
- __init__(self, path: 'str | Path', cipher_key: 'str | None' = None, cipher_env: 'str | None' = None, cipher_sops: 'str | Path | None' = None, cipher_sops_key: 'str' = 'db_key', pool_min: 'int' = 1, pool_max: 'int' = 10, pool_timeout: 'float' = 30.0, max_retries: 'int' = 3, retry_delay: 'float' = 0.5, _pool: 'ConnectionPool | None' = None, _resolved_key: 'str | None' = None) None -> None
Connection pool¶
Connection pool with retry support for async SQLite.
Provides connection pooling with: - Configurable pool size - Connection health checks - Automatic retry on transient failures - Graceful shutdown
- class kstlib.db.pool.ConnectionPool(db_path, min_size=1, max_size=10, acquire_timeout=30.0, max_retries=3, retry_delay=0.5, cipher_key=None, on_connect=None, _pool=<factory>, _connections=<factory>, _lock=<factory>, _closed=False, _stats=<factory>)[source]
Bases:
objectAsync connection pool for SQLite/SQLCipher databases.
Manages a pool of database connections with health checks and automatic retry on failures.
- Parameters:
db_path (str) – Path to database file.
min_size (int) – Minimum connections to maintain.
max_size (int) – Maximum connections allowed.
acquire_timeout (float) – Timeout for acquiring connection.
max_retries (int) – Retry attempts on failure.
retry_delay (float) – Delay between retries.
cipher_key (str | None) – Optional encryption key for SQLCipher.
on_connect (Any | None) – Callback after connection established.
Examples
>>> pool = ConnectionPool(":memory:", min_size=1, max_size=5) >>> pool.max_size 5
- db_path: str
- min_size: int = 1
- max_size: int = 10
- acquire_timeout: float = 30.0
- max_retries: int = 3
- retry_delay: float = 0.5
- on_connect: Any | None = None
- __post_init__(self) 'None' -> None[source]
Validate and clamp configuration values to hard limits.
- async acquire(self) 'aiosqlite.Connection' -> aiosqlite.Connection[source]
Acquire a connection from the pool.
- Returns:
Database connection.
- Raises:
PoolExhaustedError – If no connection available within timeout.
DatabaseConnectionError – If connection creation fails after retries.
- Return type:
aiosqlite.Connection
- async release(self, conn: 'aiosqlite.Connection') 'None' -> None[source]
Release a connection back to the pool.
- Parameters:
conn (aiosqlite.Connection) – Connection to release.
- connection(self) 'AsyncGenerator[aiosqlite.Connection, None]' -> AsyncGenerator[aiosqlite.Connection, None][source]
Context manager for acquiring and releasing connections.
- Yields:
Database connection.
Examples
>>> async with pool.connection() as conn: ... await conn.execute("SELECT 1")
- async close(self) 'None' -> None[source]
Close all connections and shutdown the pool.
- property stats: PoolStats
Get pool statistics.
- property size: int
Current number of connections in pool.
- property is_closed: bool
Whether the pool is closed.
- __init__(self, db_path: 'str', min_size: 'int' = 1, max_size: 'int' = 10, acquire_timeout: 'float' = 30.0, max_retries: 'int' = 3, retry_delay: 'float' = 0.5, cipher_key: 'str | None' = None, on_connect: 'Any | None' = None, _pool: 'asyncio.Queue[aiosqlite.Connection]' = <factory>, _connections: 'set[aiosqlite.Connection]' = <factory>, _lock: 'asyncio.Lock' = <factory>, _closed: 'bool' = False, _stats: 'PoolStats' = <factory>) None -> None
- class kstlib.db.pool.PoolStats(total_connections=0, active_connections=0, idle_connections=0, total_acquired=0, total_released=0, total_timeouts=0, total_errors=0)[source]
Bases:
objectStatistics for connection pool monitoring.
- total_connections
Total connections created.
- Type:
- active_connections
Currently in-use connections.
- Type:
- idle_connections
Available connections in pool.
- Type:
- total_acquired
Total acquire operations.
- Type:
- total_released
Total release operations.
- Type:
- total_timeouts
Acquire operations that timed out.
- Type:
- total_errors
Connection errors encountered.
- Type:
Examples
>>> stats = PoolStats() >>> stats.total_connections 0
- total_connections: int = 0
- active_connections: int = 0
- idle_connections: int = 0
- total_acquired: int = 0
- total_released: int = 0
- total_timeouts: int = 0
- total_errors: int = 0
- __init__(self, total_connections: 'int' = 0, active_connections: 'int' = 0, idle_connections: 'int' = 0, total_acquired: 'int' = 0, total_released: 'int' = 0, total_timeouts: 'int' = 0, total_errors: 'int' = 0) None -> None
Cipher utilities¶
SQLCipher integration with SOPS secret resolution.
Provides secure key management for encrypted SQLite databases. Keys can be loaded from: - Direct passphrase - Environment variable - SOPS-encrypted file via kstlib.secrets
- kstlib.db.cipher.apply_cipher_key(conn: 'sqlite3.Connection', key: 'str') 'None' -> None[source]
Apply SQLCipher key to a connection.
- Parameters:
conn (sqlite3.Connection) – SQLite connection object.
key (str) – Encryption key to apply.
- Raises:
EncryptionError – If key application fails.
- kstlib.db.cipher.resolve_cipher_key(*, passphrase: 'str | None' = None, env_var: 'str | None' = None, sops_path: 'str | Path | None' = None, sops_key: 'str' = 'db_key') 'str' -> str[source]
Resolve encryption key from various sources.
Priority: passphrase > env_var > sops_path
- Parameters:
- Returns:
Resolved encryption key.
- Raises:
EncryptionError – If no key source provided or resolution fails.
- Return type:
Examples
>>> key = resolve_cipher_key(passphrase="my-secret-key") >>> len(key) > 0 True
Async SQLCipher¶
Async SQLCipher wrapper built on top of aiosqlite.
Provides async database connections with SQLCipher AES-256 encryption. This module wraps aiosqlite to use sqlcipher3 instead of standard sqlite3.
- Requirements:
pip install kstlib[db-crypto] # Installs sqlcipher3
Examples
Basic encrypted connection:
import asyncio
from kstlib.db.aiosqlcipher import connect
async def main():
async with connect(":memory:", cipher_key="secret") as db:
await db.execute("CREATE TABLE test (id INTEGER)")
asyncio.run(main())
- kstlib.db.aiosqlcipher.connect(database: 'str | Path', *, cipher_key: 'str', iter_chunk_size: 'int' = 64, **kwargs: 'Any') 'Connection' -> Connection[source]
Create an async connection to an encrypted SQLite database.
This function is a drop-in replacement for aiosqlite.connect() that uses SQLCipher for AES-256 encryption. The cipher key is applied immediately after connection using PRAGMA key.
- Parameters:
- Returns:
Async Connection object (same interface as aiosqlite.Connection).
- Raises:
EncryptionError – If sqlcipher3 is not installed or key is empty.
sqlite3.DatabaseError – If database exists but key is wrong.
- Return type:
Connection
Examples
>>> async with connect("app.db", cipher_key="secret") as db: ... await db.execute("CREATE TABLE users (id INTEGER)")
>>> # With SOPS key resolution: >>> from kstlib.db.cipher import resolve_cipher_key >>> key = resolve_cipher_key(sops_path="secrets.yml") >>> async with connect("app.db", cipher_key=key) as db: ... pass
Exceptions¶
Database module exceptions.
- exception kstlib.db.exceptions.DatabaseError[source]
Bases:
KstlibErrorBase exception for database operations.
- exception kstlib.db.exceptions.DatabaseConnectionError[source]
Bases:
DatabaseErrorFailed to establish database connection.
- exception kstlib.db.exceptions.EncryptionError[source]
Bases:
DatabaseErrorFailed to decrypt or access encrypted database.
- exception kstlib.db.exceptions.PoolExhaustedError[source]
Bases:
DatabaseErrorConnection pool exhausted, no connections available.
- exception kstlib.db.exceptions.TransactionError[source]
Bases:
DatabaseErrorTransaction operation failed.