aeat.adapters.persistence.storage._schema_lineage module

Schema-lineage policy for persisted secure-object payloads.

Persisted rows carry the schema_version they were written under, while the consumer contract names the current version. This module owns the policy that keeps every version from the durability floor to the current version readable: the version gate is a ceiling — a version above the consumer’s current version is refused as written-by-a-newer-application, and a version below it is readable exactly when the per-hop upgrade chain up to the current version is complete. Registered upgraders transform decrypted plaintext payload bytes one version step at a time; ciphertext, AEAD associated data, and revision-lineage metadata are never rewritten by a read.

The upgrader registry is EMPTY while every registered namespace sits at schema version 1. A future schema bump MUST land the one-hop upgrader for its namespace in the same change, or the lineage gate (tests/test_schema_lineage.py) fails — that gate is what makes “a version bump strands years-old taxpayer data” structurally impossible. SECURE_OBJECT_DURABILITY_FLOOR moves forward only through a superseding accepted ADR.

See also

secure_object_record_from_row()

Row decode path that applies this policy before returning a record.

Governing vault record

2026-07-08-released-data-durability-adr (version gates become ceilings with an upgrade dispatch).

SecureObjectSchemaUpgrader

Upgrades one decrypted plaintext payload from from_version to from_version + 1 for its namespace. Pure bytes-to-bytes; never touches ciphertext or row metadata.

alias of Callable[[bytes], bytes]

SECURE_OBJECT_DURABILITY_FLOOR: Final[int]

Oldest secure-object schema version every read path keeps readable. Starts at the from-birth version; moves forward only through a superseding accepted ADR.

register_secure_object_schema_upgrader(namespace, from_version, upgrader)[source]

Register the one-hop payload upgrader for (namespace, from_version).

A schema bump for a namespace lands its upgrader through this function in the same change that raises the namespace’s declared schema_version; the lineage gate fails until it does.

Raises:

StorageValidationError – When an upgrader for the hop is already registered — two competing transformations for one hop is a wiring error, never a merge.

Return type:

None

Parameters:
deregister_secure_object_schema_upgrader(namespace, from_version)[source]

Remove a registered upgrader hop.

Production wiring never removes a hop — dropping one would re-open the stranding gap the lineage gate closes. This exists so tests that register a real upgrader against a scratch namespace can restore the registry in their teardown.

Return type:

None

Parameters:
  • namespace (str)

  • from_version (int)

missing_upgrade_hops(*, namespace, from_version, to_version, upgraders=None)[source]

Return the from_version of every unregistered hop in the chain.

An empty tuple means every step from from_version to to_version has a registered upgrader (vacuously so when the versions are equal).

Return type:

tuple[int, ...]

Parameters:
ensure_schema_version_readable(*, namespace, schema_version, current_version, upgraders=None)[source]

Refuse a stored version the current application cannot read.

The gate is a ceiling, not an equality: a version above current_version was written by a newer application and is refused outright; a version below it is accepted exactly when the registered upgrade chain up to current_version is complete, and refused loudly — naming the first missing hop — when it is not.

Raises:

EnvelopeVersionError – When schema_version exceeds current_version (future shape) or the upgrade chain has a missing hop (readable only after the missing upgrader ships).

Return type:

None

Parameters:
upgrade_secure_object_payload(payload, *, namespace, from_version, to_version, upgraders=None)[source]

Chain-upgrade a decrypted payload from from_version to to_version.

Validates the chain first (so a missing hop refuses before any transformation runs), then applies each registered one-hop upgrader in order. Equal versions return the payload unchanged.

Raises:

EnvelopeVersionError – When from_version exceeds to_version or the chain has a missing hop.

Return type:

bytes

Parameters: