aeat.core._bucket_pointer_io module

Atomic IO and selector resolution for the active-profile pointer file.

The pointer file lives at <aeat-root>/active-profile and is the on-disk default after the per-invocation / per-shell override path. The CLI --profile flag is normalised into Settings.aeat_active_profile, so this module’s runtime branches are settings override first and pointer file second. The write path uses the write-then-rename pattern so a crashed switch never produces a truncated pointer; the read path returns None only when the pointer is absent.

The IO helpers serialise BucketPointer records and feed resolve_active_bucket_id(), the central core resolver consumed by storage and CLI startup flows. The resolver returns the selected bucket id string; it does not prove a buckets/<id>/manifest.toml exists, scan profile display labels, or open encrypted state. Those registry/existence checks belong to application-layer manifest scanners that return ProfileBucketPointer.

Repository factories that need a hard bucket id use resolve_repository_bucket_id() so each domain can raise its own error type while sharing the same pointer precedence.

pointer_path(root)[source]

Return the canonical active-profile pointer path under the AEAT root.

Parameters:

root (Path) – AEAT local storage root.

Return type:

Path

Returns:

root / "active-profile" without touching the filesystem.

read_pointer(root)[source]

Read and strict-validate the pointer file.

Present files are parsed by from_toml(); invalid TOML, unknown keys, and invalid scalar values propagate instead of being reclassified as an absent pointer.

Parameters:

root (Path) – AEAT local storage root directory that contains the active-profile pointer file.

Return type:

BucketPointer | None

Returns:

The parsed BucketPointer, or None when the pointer file is absent. The higher-level resolver treats None as “fall through to the next precedence rung”.

Raises:
  • OSError – If the present pointer file cannot be read.

  • tomllib.TOMLDecodeError – If the present file is not valid TOML.

  • pydantic.ValidationError – If the present TOML violates the strict BucketPointer schema.

resolve_active_bucket_id()[source]

Resolve the active bucket id via the operator-facing precedence chain.

Precedence, highest wins:

  1. Settings.aeat_active_profile — surfaced from the AEAT_ACTIVE_PROFILE environment variable (or an active override_settings() block in tests). Per-shell override useful for CI, headless invocations, and the CLI --profile flag.

  2. <aeat-root>/active-profile plaintext pointer file written by profile create / config switch. This is the canonical default for interactive sessions and resolves the chicken-and-egg defect where an encrypted state row could not be read without first knowing which bucket to unlock.

The CLI --profile flag, when supplied per-invocation, runs the process under an override_settings() block that sets aeat_active_profile so rung one handles it without a fourth precedence rung.

This resolver lives in the core layer: it reads only the settings Settings object and the plaintext pointer file, both core-layer concerns. The at-rest crypto substrate (master-key provider) resolves the active bucket through this function, so it must sit at or below the adapter layer to keep the dependency direction acyclic.

Return type:

str | None

Returns:

The selected active bucket id, or None when neither settings nor the pointer file selects one.

require_active_bucket_id()[source]

Resolve the active bucket id via the precedence chain, or raise.

Companion to resolve_active_bucket_id() for call sites that require a selected profile rather than tolerating its absence. Operator-initiated auth session paths, the Cl@ve Móvil persistence path, the SEDE declarations-register profile name, and bucket-scoped repositories all sit on flows that require a profile to be selected; a missing profile is a genuine refusal, not a degraded read. Reads env var > pointer file; raises NoActiveProfileError if neither rung resolves.

Diagnostic surfaces (browser-connectivity probe, status flows) MUST NOT call this helper — they call resolve_active_bucket_id() and supply their own fallback label so a missing profile stays diagnosable.

Return type:

str

Returns:

The selected active bucket id.

Raises:

aeat.core.errors.NoActiveProfileError – If neither settings nor the pointer file selects a bucket id.

write_pointer(root, pointer)[source]

Atomically write the pointer file via write-then-rename.

The payload is staged at a .tmp sibling and renamed via os.replace(); a crashed process therefore leaves either the previous good pointer or the new good pointer on disk, never a torn intermediate. The payload comes from to_toml(), and the AEAT root is created lazily if absent.

Parameters:
  • root (Path) – AEAT local storage root that will contain the pointer file.

  • pointer (BucketPointer) – Validated pointer record to serialise.

Raises:

OSError – If the parent directory cannot be created, the temporary file cannot be written, or the atomic replacement fails.

Return type:

None

resolve_repository_bucket_id(bucket_id, *, error_type)[source]

Resolve an explicit-or-active profile bucket id for a runtime repository.

Single canonical home for the per-domain repository bucket-id resolution that the domain.modelos, domain.filing, and application.filing runtime-repository modules each previously copied verbatim, differing only in the domain error they raise. An explicit, non-blank bucket_id is returned trimmed; a blank explicit id or an absent active profile both raise error_type (the caller’s domain error) carrying the shared no_active_profile_bucket message and a structured reason. This is the repository-facing companion to require_active_bucket_id().

Parameters:
  • bucket_id (str | None) – An explicit bucket id, or None to fall back to the active profile bucket.

  • error_type (type[AeatError]) – The caller’s domain error class raised when no usable bucket id can be resolved.

Return type:

str

Returns:

The resolved bucket id.

Raises:

AeatError – The supplied error_type when bucket_id is blank or no active profile bucket can be resolved.