cadrumo.adapters.persistence.profile._secure_model_document module

Canonical encrypted persistence kernel for BARE-document profile singletons.

Profile adapters that persist one whole typed document as bare JSON bytes (document.model_dump_json(), with classification/schema-version/written-at carried only as SQL-row columns, nothing duplicated inside the payload) share the same durable contract. This module owns that mechanical boundary so individual repositories retain only their domain mutations and translated load failures. It deliberately exposes SecureObjectWrite construction for callers such as the prorrata filing path that co-commit the document with sibling writes.

This is NOT the only singleton-document persistence kernel: a namespace whose on-disk rows are wrapped in Envelope (classification/schema-version/written-at duplicated inside the JSON payload itself) is a different wire shape and belongs on ProfileEnvelopedModelSecurePersistence (_secure_enveloped_document.py) instead — enrolling an Envelope-wrapped namespace here would change what bytes get written and fail to read what is already on disk. See that module’s docstring for the two shapes compared side by side and the rule for which one a given namespace belongs to.

resolve_profile_secure_object_repository(*, objects=None, bucket_id=None)[source]

Resolve the one secure-object repository for a profile document.

Explicit repositories are the real encrypted-SQL test seam. An explicit bucket is for application callers whose authoritative context already names the target bucket; every other production caller resolves through the active-bucket runtime wrapper. No plaintext or filesystem fallback exists.

Parameters:
  • objects (SecureObjectRepository | None) – An explicit SecureObjectRepository to reuse (the test seam), bypassing bucket resolution entirely when supplied.

  • bucket_id (str | None) – An explicit bucket id to resolve against, for a caller whose authoritative context already names the target bucket.

Return type:

SecureObjectRepository

class ProfileBareModelSecurePersistence(*, objects, definition, model_type, empty_document, write_provenance='secure-object-repository')[source]

Bases: Generic

Persist one strict Pydantic document, stored bare, through a governed secure object.

“Stored bare” means document.model_dump_json() is written directly as the row payload — no Envelope wrapper. Use this kernel only for a namespace whose on-disk rows are already in that shape, or a brand-new namespace with no format to preserve; an Envelope-wrapped namespace belongs on ProfileEnvelopedModelSecurePersistence instead (see the module docstring).

The namespace definition remains the authority for object key, sensitivity, and schema version. save always delegates to save_many so an ordinary singleton save and a caller-composed to_secure_object_write follow the same encrypted SQL write path. write_provenance is optional and defaults to the substrate’s own default; a caller that already stamps a specific provenance string on its writes (an audit-trail identifier naming the owning module) passes it through the constructor rather than losing it to enrollment.

Parameters:
property object_key: str

Return the registry-owned singleton object key.

property namespace: str

Return the registry-owned namespace value.

logical_path(marker)[source]

Return the registry-defined logical marker for one repository surface.

Return type:

Path

Parameters:

marker (str)

load()[source]

Load and strictly decode the document, or return its declared empty form.

Return type:

DocumentT

load_revisioned()[source]

Return the document and revision from one secure-object record.

Return type:

tuple[DocumentT, str]

to_secure_object_write(document, *, expected_revision_id=None)[source]

Prepare the encrypted-SQL upsert without committing it.

Callers that need to co-commit this document with sibling secure objects pass the returned value into their existing save_many transaction.

expected_revision_id is the compare-and-swap half. These documents are singletons, so a co-commit composed from an unguarded read writes the whole document back and discards any entry another caller added in between. Pass the revision load_revisioned() reported.

Return type:

SecureObjectWrite

Parameters:
  • document (DocumentT)

  • expected_revision_id (str | None)

save(document)[source]

Encrypt and save one document in the transactional secure-object path.

Return type:

None

Parameters:

document (DocumentT)

mutate(mutation, *, attempts=4)[source]

Apply mutation to the stored document as one guarded unit of work.

These documents are SINGLETONS: every entry lives in one encrypted row, so an “add one record” is really read-whole-document, rebuild, write whole document. Performed unguarded, two callers adding DIFFERENT entries both read the same document and the later write silently discards the earlier caller’s entry – a lost update, not a conflict any uniqueness check would notice, because the two entries never met.

The write carries the revision the document was READ at, so the substrate refuses it if the row moved in between; the mutation is then re-applied to the newly-current document. mutation is therefore called once per attempt and MUST be a pure function of the document it is handed – it must not close over a value derived from an earlier read, or the retry re-applies a stale decision.

Parameters:
  • mutation (Callable[[DocumentT], DocumentT]) – Builds the next document from the current one. May raise to refuse the mutation outright (a duplicate-identifier check, for instance); the refusal propagates unretried, since a conflict is what the retry exists for and a refusal is not one.

  • attempts (int) – Maximum reads of the current document. Exceeding them raises the substrate’s conflict error rather than looping.

Return type:

DocumentT

Returns:

The document as written.

Raises:

SecureObjectRevisionConflictError – Contention persisted across every attempt.