aeat.adapters.persistence.storage.envelope._secure_repository module

Generic SQL-backed envelope repository for secure objects.

Concrete domain and application repositories that wrap adapters.persistence.storage.SecureObjectRepository all share the same boilerplate: adapters.persistence.storage.Envelope wrapping, namespace, sensitivity, schema-version, Pydantic payload type, and a function that extracts the natural id from the payload.

This module provides adapters.persistence.storage.SecureBoundRepository, a generic base class that captures that shared shape exactly once. Concrete subclasses override the four class-level descriptors (namespace, payload_type, sensitivity, schema_version) and implement extract_identifier; they inherit envelope_path_for, lock_target_for, load, save, delete, iter_ids, and iter_records for free.

The base class does NOT replace adapters.persistence.storage.SecureObjectRepository; it composes one.

class SecureBoundRepository(*, bucket_id=None, objects=None, settings=None)[source]

Bases: Generic

Generic repository over encrypted SQL-backed envelopes for one payload type.

Subclasses MUST set class attributes:

Subclasses MUST implement extract_identifier() so that save() and iter_ids() can recover the natural id from the decrypted payload.

Parameters:
namespace: ClassVar[str]
sensitivity: ClassVar[SensitivityClass]
schema_version: ClassVar[int]
payload_type: ClassVar[type[BaseModel]]
extract_identifier(payload)[source]

Return the natural id for payload (used as the SQL object key).

Subclasses MUST override. The base implementation raises NotImplementedError.

Return type:

str

Parameters:

payload (T)

classmethod payload_model()[source]

Return the concrete Pydantic payload model for this repository.

Return type:

type[T]

property store_dir: Path

Return a logical backend marker for diagnostic CLI output.

envelope_path_for(identifier)[source]

Return a logical path marker for identifier.

Return type:

Path

Parameters:

identifier (str)

lock_target_for(identifier)[source]

Return a logical lock-target marker for identifier.

SQL transactions govern actual write atomicity; this is only surfaced for diagnostic parity with file-backed repositories.

Return type:

Path

Parameters:

identifier (str)

property secure_object_repository: SecureObjectRepository

Return the concrete secure-object backend.

Returns:

The adapters.persistence.storage.SecureObjectRepository backing this logical repository.

load(identifier)[source]

Return the persisted payload or None if absent.

Return type:

T | None

Parameters:

identifier (str)

save(payload)[source]

Persist payload as an encrypted envelope row.

The natural id is recovered from the payload via extract_identifier().

Return type:

None

Parameters:

payload (T)

delete(identifier)[source]

Remove the row for identifier; return whether a row was deleted.

Return type:

bool

Parameters:

identifier (str)

iter_ids()[source]

Yield every persisted identifier in storage order.

Order is the secure-object storage order (the object_key digest order), not the natural-id order: a caller that needs a specific order sorts the result itself. Streams one identifier at a time rather than buffering and sorting the whole namespace in memory.

Fail-closed: adapters.persistence.storage.SecureObjectRepository.list_records() scans the whole namespace and raises SecureObjectUnreadableError if any row is unreadable, so a full consumption (tuple(...)) never yields a readable subset past a corrupt row.

Return type:

Iterator[str]

iter_records()[source]

Yield every persisted payload in storage order.

Streams each payload straight from adapters.persistence.storage.SecureObjectRepository.list_records() without buffering the whole namespace or sorting it in memory. Order is storage-defined (the object_key digest order), not the natural-id order; a caller that needs a specific order sorts the result itself.

Parses each row’s decrypted payload bytes directly from list_records rather than routing through load(). The SQL WHERE object_key = ? lookup inside load() cannot match the stored ciphertext when object_key is an EncryptedString column (AES-256-GCM uses a random nonce, so the bind-parameter ciphertext differs from the stored ciphertext every time). Iterating directly over the decrypted rows is the correct pattern for full-scan enumeration.

Fail-closed: list_records scans the whole namespace and raises SecureObjectUnreadableError if any row is unreadable before this generator yields a readable subset on full consumption.

Return type:

Iterator[T]