aeat.adapters.persistence.storage.bucket._manifest module

Strict pydantic v2 record for the per-bucket plaintext manifest.

The bucket manifest sits at <aeat-root>/buckets/<bucket-id>/manifest.toml and carries only non-sensitive metadata: the bucket identifier and label, creation and unlock timestamps, the Argon2id KDF parameters and salt (salt is public per Argon2 design), the recovery-enrollment flag, and a schema version. The manifest never contains the derived key, the wrapped key, the passphrase, or any byte derivable from them; those artefacts travel through the separate master-key surface.

The ManifestKdfParams nested model carried here is the manifest-side shape (algorithm tag, parameter version, the four Argon2id cost parameters, salt). The canonical OWASP-pinned constructor and the parameter-window validators live alongside it under master_key/_kdf_params.py; manifest I/O wires the two together.

class ManifestKdfParams(**data)[source]

Bases: BaseModel

Argon2id parameters and salt as carried in the bucket manifest.

Strict pydantic v2 record. The canonical OWASP-baseline constructor is declared in adapters.persistence.storage.master_key._kdf_params; this manifest-side record holds whatever parameters the bucket was enrolled under so a future cost-bump can be non-breaking.

Parameters:
  • algorithm (str)

  • version (int)

  • memory_cost (int)

  • time_cost (int)

  • parallelism (int)

  • salt (bytes)

  • output_length (int)

algorithm: str
version: int
memory_cost: int
time_cost: int
parallelism: int
salt: bytes
output_length: int
class BucketLifecycleStatus(*values)[source]

Bases: StrEnum

Plaintext lifecycle marker carried on the bucket manifest.

The encrypted UserProfileStatus record is the lifecycle authority, but reading it costs a bucket decryption. The manifest mirrors that status as a plaintext marker so the manifest scan can exclude a tombstoned profile from every live operator surface (list / switch / name-uniqueness) without unlocking the bucket. The ProfileRepository is the sole writer of both stores and keeps the two in lockstep.

Values match UserProfileStatus so the application-layer repository maps the two enums one-to-one.

ACTIVE
TOMBSTONED
class BucketKeySchedule(*values)[source]

Bases: StrEnum

Data-key schedule used by encrypted records in this bucket.

BUCKET_DEK_V1
class BucketManifest(**data)[source]

Bases: BaseModel

Plaintext manifest for one per-bucket directory.

The manifest never carries sensitive bytes; see ManifestKdfParams for the salt contract.

Parameters:
bucket_id: Annotated[str, Field(min_length=1)]
label: str
created_at: datetime
last_unlocked_at: datetime | None
kdf_params: ManifestKdfParams
recovery_enrolled: bool
idle_lock_minutes: int | None
key_schedule: BucketKeySchedule
schema_version: int
status: BucketLifecycleStatus

Plaintext mirror of the encrypted record’s lifecycle status.

Required, with no default. A manifest that omits status is rejected at the read boundary (fail-closed) rather than silently hydrating as a live profile — a silent default would risk leaking a tombstoned bucket back onto the operator surface. The ProfileRepository sets it explicitly on every write: ACTIVE at creation, TOMBSTONED in the same write that tombstones the encrypted record.