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

  • AsyncDatabase provides the main entry point for database operations

  • ConnectionPool manages connection lifecycle with health checks and retry

  • PoolStats tracks pool usage statistics

  • is_sqlcipher_available() checks if SQLCipher encryption is available

  • Encryption 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, driver_debug=False, _pool=None, _resolved_key=None)[source]

Bases: object

Async 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.

  • driver_debug (bool) – Keep aiosqlite driver DEBUG logging enabled. Defaults to False: the process-wide aiosqlite logger is capped at INFO when the pool is created, because its DEBUG records include bound parameters (secrets stored through this database would leak into logs). Pass True to opt out for conscious troubleshooting; kstlib then never touches that logger.

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_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
driver_debug: bool = False
__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:

list[tuple[Any, …]]

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.

Parameters:

table_name (str) – Name of the table.

Returns:

True if table exists.

Return type:

bool

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, driver_debug: 'bool' = False, _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, driver_debug=False, _pool=<factory>, _connections=<factory>, _lock=<factory>, _closed=False, _stats=<factory>)[source]

Bases: object

Async 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.

  • driver_debug (bool) – Keep aiosqlite driver DEBUG logging enabled. Defaults to False: the pool caps the process-wide aiosqlite logger at INFO on construction because its DEBUG records include bound parameters (secret values would leak into logs). Pass True to opt out for conscious troubleshooting; kstlib then never touches that logger (it does not restore a previously capped level).

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
cipher_key: str | None = None
on_connect: Any | None = None
driver_debug: bool = False
__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, driver_debug: 'bool' = False, _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: DatabaseError

Failed to establish database connection.

exception kstlib.db.DatabaseError[source]

Bases: KstlibError

Base exception for database operations.

exception kstlib.db.EncryptionError[source]

Bases: DatabaseError

Failed to decrypt or access encrypted database.

exception kstlib.db.PoolExhaustedError[source]

Bases: DatabaseError

Connection 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: object

Statistics for connection pool monitoring.

total_connections

Total connections created.

Type:

int

active_connections

Currently in-use connections.

Type:

int

idle_connections

Available connections in pool.

Type:

int

total_acquired

Total acquire operations.

Type:

int

total_released

Total release operations.

Type:

int

total_timeouts

Acquire operations that timed out.

Type:

int

total_errors

Connection errors encountered.

Type:

int

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: DatabaseError

Transaction operation failed.

kstlib.db.apply_cipher_key(conn: 'sqlite3.Connection', key: 'str') 'None' -> None[source]

Apply SQLCipher key to a connection.

Parameters:
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:

bool

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:
  • passphrase (str | None) – Direct passphrase string.

  • env_var (str | None) – Environment variable name containing the key.

  • sops_path (str | Path | None) – Path to SOPS-encrypted file.

  • sops_key (str) – Key name within SOPS file (default: “db_key”).

Returns:

Resolved encryption key.

Raises:

EncryptionError – If no key source provided or resolution fails.

Return type:

str

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, driver_debug=False, _pool=None, _resolved_key=None)[source]

Bases: object

Async 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.

  • driver_debug (bool) – Keep aiosqlite driver DEBUG logging enabled. Defaults to False: the process-wide aiosqlite logger is capped at INFO when the pool is created, because its DEBUG records include bound parameters (secrets stored through this database would leak into logs). Pass True to opt out for conscious troubleshooting; kstlib then never touches that logger.

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_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
driver_debug: bool = False
__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:

list[tuple[Any, …]]

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.

Parameters:

table_name (str) – Name of the table.

Returns:

True if table exists.

Return type:

bool

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, driver_debug: 'bool' = False, _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, driver_debug=False, _pool=<factory>, _connections=<factory>, _lock=<factory>, _closed=False, _stats=<factory>)[source]

Bases: object

Async 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.

  • driver_debug (bool) – Keep aiosqlite driver DEBUG logging enabled. Defaults to False: the pool caps the process-wide aiosqlite logger at INFO on construction because its DEBUG records include bound parameters (secret values would leak into logs). Pass True to opt out for conscious troubleshooting; kstlib then never touches that logger (it does not restore a previously capped level).

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
cipher_key: str | None = None
on_connect: Any | None = None
driver_debug: bool = False
__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, driver_debug: 'bool' = False, _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: object

Statistics for connection pool monitoring.

total_connections

Total connections created.

Type:

int

active_connections

Currently in-use connections.

Type:

int

idle_connections

Available connections in pool.

Type:

int

total_acquired

Total acquire operations.

Type:

int

total_released

Total release operations.

Type:

int

total_timeouts

Acquire operations that timed out.

Type:

int

total_errors

Connection errors encountered.

Type:

int

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:
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:
  • passphrase (str | None) – Direct passphrase string.

  • env_var (str | None) – Environment variable name containing the key.

  • sops_path (str | Path | None) – Path to SOPS-encrypted file.

  • sops_key (str) – Key name within SOPS file (default: “db_key”).

Returns:

Resolved encryption key.

Raises:

EncryptionError – If no key source provided or resolution fails.

Return type:

str

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:
  • database (str | Path) – Path to database file or “:memory:” for in-memory.

  • cipher_key (str) – Encryption key for SQLCipher (required).

  • iter_chunk_size (int) – Rows to fetch per iteration (default: 64).

  • **kwargs (Any) – Additional arguments passed to sqlcipher3.connect().

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
kstlib.db.aiosqlcipher.is_sqlcipher_available() 'bool' -> bool[source]

Check if sqlcipher3 is installed and available.

Returns:

True if sqlcipher3 can be imported.

Return type:

bool

Examples

>>> is_sqlcipher_available()  
True

Exceptions

Database module exceptions.

exception kstlib.db.exceptions.DatabaseError[source]

Bases: KstlibError

Base exception for database operations.

exception kstlib.db.exceptions.DatabaseConnectionError[source]

Bases: DatabaseError

Failed to establish database connection.

exception kstlib.db.exceptions.EncryptionError[source]

Bases: DatabaseError

Failed to decrypt or access encrypted database.

exception kstlib.db.exceptions.PoolExhaustedError[source]

Bases: DatabaseError

Connection pool exhausted, no connections available.

exception kstlib.db.exceptions.TransactionError[source]

Bases: DatabaseError

Transaction operation failed.