aeat.application.user_profile._orchestration module¶
WorkflowState-aware orchestration for profile lifecycle services.
The lifecycle service handles secure-DB persistence via a
ProfileLifecycleService, which wraps a
SecureObjectRepository
and emits bucket events to
BucketEventHistoryRepository per profile.
This module threads active-profile selection through
WorkflowState, the plaintext
BucketPointer, and the workflow-level
WorkflowEvent audit stream around
those calls so CLI surfaces do not duplicate that wiring.
Profile identity is an immutable UUIDv4 minted at creation. The bucket
directory, keystore directory, secure-object key, and active-profile
pointer all key on that UUID; the operator-chosen display name is a
fully decoupled mutable label carried in the
BucketManifest.
See also
ProfileRepositorySole writer for the cross-store profile aggregate.
profile_create_storage_span()Create-time bucket session used before the encrypted record exists.
profile_storage_session()Existing-profile bucket session used for application-owned writes.
- build_lifecycle_service(*, bucket_id, secure_objects=None, schema=None)[source]¶
Construct a lifecycle service for one bucket.
Returns a
ProfileLifecycleService.secure_objectsis an optionalSecureObjectRepositoryoverride; a per-bucket store is resolved whenNone.The profile aggregate AND the bucket-event-history catalogue both belong to the named bucket’s own database. When no repository is injected, a single per-bucket secure-object store is resolved and handed to both the
UserProfileLifecycleRepositoryand theBucketEventHistoryRepository, so the audit trail can never split from the records it describes. The prior wiring left the event-history repository on the process-global engine, which had no URL until an active profile existed - everyregisterthen crashed before its first event landed.- Return type:
- Parameters:
bucket_id (str)
secure_objects (SecureObjectRepository | None)
schema (ProfileSchemaDefinition | None)
- profile_create_storage_span(profile_id)[source]¶
Open the first-profile storage span for a bucket being created.
The span writes the provisional
BucketPointerneeded to resolve the create-time bucket route, then entersactivate_master_key_provider()with DEK enrollment enabled. If bucket setup fails before or outsidecreate()owns rollback, the prior pointer bytes are restored and provisional bucket/DEK artifacts minted by this span are removed.- Parameters:
profile_id (str)
- profile_storage_session(profile_id)[source]¶
Open a storage session scoped to
profile_idfor application-owned writes.Existing-profile operations use this span to bind the active
BucketPointerroute and master-key session before repositories open encrypted bucket-local storage.- Parameters:
profile_id (str)
- exception ProfileAlreadyRegisteredError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
ProfileNotFoundErrorRaised when
profile createtargets a name that already has a manifest.Inherits from
ProfileNotFoundErrorso existing exception handlers that catch the broader family also catch this case; the CLI decorator translates it to a typed refusal that namesconfig switchas the next action.- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- register_active_profile(state, *, profile_id, display_name, facts=(), secure_objects=None, schema=None, enforce_unique_tax_id=True, routing_profile_id=None)[source]¶
Atomically register a new profile and make it the active one.
- Parameters:
state (
WorkflowState) – The currentWorkflowState; the returned state carries the registration event appended to the audit stream.profile_id (
str) – Immutable UUIDv4 profile identity, minted by the caller (seenew_profile_id()).display_name (
str) – Operator-chosen label carried in the bucket manifest and the encrypted record; plays no role in any key or path.facts (
tuple[UserProfileFact,...]) – Initial profile facts to persist alongside the registration.secure_objects (
SecureObjectRepository|None) – OptionalSecureObjectRepositoryoverride for the encrypted profile store.schema (
ProfileSchemaDefinition|None) – Optional profile schema definition override.enforce_unique_tax_id (
bool) – WhenTrue, refuses if another live profile already carries the same tax id.routing_profile_id (
str|None) – When set, wires a cross-bucket routing entry so the new profile can inherit data from an existing bucket.
- Return type:
This function is a thin
WorkflowStatecoordinator: the entire cross-store write — bucket directory, manifest, encrypted record, AND the active-profile pointer — plus the duplicate-label refusal and the all-or-nothing rollback are a single unit of work owned bycreate(). This function delegates that create and threads the workflow-level event audit stream onto the suppliedWorkflowState.The repository owns every store write, the pointer included. A caller that performs the cold-start pointer write early (so the workflow-state engine can resolve before this function runs inside
workflow_state_repository().update) is responsible for restoring that early pointer if the surrounding span fails before or aftercreate-capture_active_profile_pointer()andrestore_active_profile_pointer()cover the steps the repository’s own rollback cannot see (engine open, master-key activation).
- select_profile_with_lifecycle_span(profile_id)[source]¶
Select
profile_idinside an application-owned bucket session.Opens
profile_storage_session(), delegates the pointer write toselect_profile(), then appends the bucket-level activation event.
- delete_profile_with_lifecycle_span(profile_id)[source]¶
Tombstone
profile_idinside an application-owned bucket session.Returns the deleted
UserProfileRecordfromdelete().- Return type:
- Parameters:
profile_id (str)
- reactivate_profile_with_lifecycle_span(profile_id)[source]¶
Restore a tombstoned
profile_idinside an application-owned bucket session.Symmetric inverse of
delete_profile_with_lifecycle_span(). Returns the reactivatedUserProfileRecordfromreactivate().- Return type:
- Parameters:
profile_id (str)
- logout_active_profile()[source]¶
Clear the active profile pointer and return the profile that was logged out.
- capture_active_profile_pointer()[source]¶
Return the raw active-profile pointer text, or
Noneif absent.A cold-start caller — one that must write the active-profile pointer early so
workflow_state_repository()can resolve its per-bucket engine beforeregister_active_profile()runs — captures the genuine pre-write pointer with this helper, then restores it in atry/exceptif the create span fails. This closes the window the repository’s own rollback cannot reach: a failure between the early pointer write andcreate()(engine open, master-key activation) would otherwise strand the pointer at a profile whose record was never persisted.
- restore_active_profile_pointer(prior_text)[source]¶
Restore the active-profile pointer to a previously captured state.
Counterpart to
capture_active_profile_pointer(). A cold-start caller calls this from theexceptarm of the span it wraps: if there was no prior pointer the early write is removed, otherwise the captured bytes are written back, so a failed create leaves the pointer exactly as it was found.
- refuse_duplicate_label(display_name)[source]¶
Refuse a
profile createwhen a live profile carries the label.Display names (labels) are unique among live profiles, compared case-insensitively. A bucket manifest already carrying
display_nameis an existence claim; recreating in place would silently mix new profile data into the existing bucket, so the create is refused and the operator is routed toswitchordelete. The live-label lookup isread_profile_bucket().
- require_registered_label(display_name)[source]¶
Refuse a
profile editwhen no live profile carries the label.Symmetric to
refuse_duplicate_label():profile editre-runs the wizard against an existing profile, so an unknown label is an operator error, not an implicit create. The registered label authority isread_profile_bucket().
- remove_profile_bucket_directory(profile_id)[source]¶
Trash-rename and remove a profile’s on-disk bucket directory.
Used both by atomic-create rollback and by
reset_config(). The directory is first renamed to a trash-prefix sibling so a crashed removal leaves a recoverable on-disk trace, then recursively deleted. When the rename is refused - Windows denies renaming a directory whose SQLite file was only just closed - the directory is removed in place so the bucket does not survive the reset.Raises
OSErrorif the in-place removal also fails and the bucket directory genuinely survives on disk, so a caller (config reset in particular) never reports a removed profile while the bucket is still present. The atomic-create rollback caller wraps this call best-effort, since a residual directory there must not mask the original registration failure.
- select_profile(state, *, profile_id, secure_objects=None, schema=None)[source]¶
Select an existing profile as active.
secure_objectsis an optionalSecureObjectRepositoryoverride.Raises
ProfileNotFoundErrorif the profile does not already exist; registration is the explicit path (register_active_profile()).This function is a thin
WorkflowStatecoordinator: the profile load + integrity check + active-profile pointer write live solely inselect().- Return type:
- Parameters:
state (WorkflowState)
profile_id (str)
secure_objects (SecureObjectRepository | None)
schema (ProfileSchemaDefinition | None)
- set_active_field(state, fact, *, secure_objects=None, schema=None)[source]¶
Upsert one fact on the active profile and append a workflow event.
- Parameters:
state (
WorkflowState) – The current workflow state.fact (
UserProfileFact) – The profile fact to upsert.secure_objects (
SecureObjectRepository|None) – OptionalSecureObjectRepositoryoverride for the encrypted profile store.schema (
ProfileSchemaDefinition|None) – Optional profile schema definition override.
- Return type:
- Returns:
The updated
WorkflowState.
- set_active_fields(state, facts, *, secure_objects=None, schema=None)[source]¶
Upsert several facts on the active profile in sequence.
secure_objectsis an optionalSecureObjectRepositoryoverride.Returns a
WorkflowState.- Return type:
- Parameters:
state (WorkflowState)
facts (Iterable[UserProfileFact])
secure_objects (SecureObjectRepository | None)
schema (ProfileSchemaDefinition | None)
- remove_active_profile(state, *, secure_objects=None, schema=None)[source]¶
Tombstone the active profile and clear the active pointer.
secure_objectsis an optionalSecureObjectRepositoryoverride.The bucket directory and
BucketManifeststay on disk so audit and history reads can still resolve the tombstoned bucket; selecting the tombstoned profile viaselect_profile()will raise because the record is no longer live.This function is a thin
WorkflowStatecoordinator: the cross-store tombstone (encrypted-record tombstone + active-profile pointer clear) lives solely indelete().- Return type:
- Parameters:
state (WorkflowState)
secure_objects (SecureObjectRepository | None)
schema (ProfileSchemaDefinition | None)
- fact_value(record, path)[source]¶
Return the live string-rendered value of one fact path on
record.- Parameters:
record (
UserProfileRecord|None) – TheUserProfileRecordto inspect, orNone.path (
str) – Schema fact path (e.g."identity.tax_id").
- Return type:
Returns
NonewhenrecordisNone, when the path has no fact, or when the recorded value isNone. Repeated facts at the same path (effective-dated windows) resolve to the chronologically lastUserProfileFact.valid_from.
- rename_profile(*, profile_id, new_label, secure_objects=None, schema=None)[source]¶
Rename a profile by updating its display label only.
- Parameters:
profile_id (
str) – The immutable UUIDv4 identity of the profile to rename.new_label (
str) – The new display label.secure_objects (
SecureObjectRepository|None) – OptionalSecureObjectRepositoryoverride for the encrypted profile store.schema (
ProfileSchemaDefinition|None) – Optional profile schema definition override.
- Return type:
The profile identity (
profile_id), bucket directory, keystore directory, and secure-object key are immutable and never move. A rename is a pure label edit across the two stores that hold a copy of the label - the encryptedUserProfileRecorddisplay_nameand the plaintextBucketManifestlabel.This function is a thin coordinator: the cross-store label write - record AND manifest - lives solely in
rename(). Refuses ifnew_labelis already carried by another live profile.Returns the updated
UserProfileRecordafter the label change is persisted.