aeat.adapters.persistence.storage.master_key._active_session module¶
Active-bucket session resolution for the column-level encrypt path.
The column-level TypeDecorator set in
adapters/persistence/storage/crypto/_encrypted_columns.py cannot
thread an explicit session reference through SQLAlchemy’s
process_bind_param() signature (the method is invoked by
SQLAlchemy’s column machinery with a fixed (self, value, dialect)
shape). The substrate also forbids module-global mutable state that
could survive a bucket switch — the BucketSession instance is
the only legitimate owner of unlocked KEK and DEK bytes.
This module composes both constraints with a ContextVar (PEP 567)
holding the active BucketSession. The CLI entry point opens
a session and enters activate_session() as a contextmanager;
every column-level decrypt or encrypt call inside the block resolves
the active DEK through get_active_master_key(). On exit the
ContextVar token is reset to the previous value (None at the
top of the stack), so no key material outlives the with-block.
The pattern is per-thread and per-async-task by PEP 567 semantics.
asyncio.Task instances inherit a copy of the parent context at
creation time, so the active session crosses into spawned tasks
correctly. concurrent.futures.ThreadPoolExecutor workers do
NOT inherit ContextVar state by default; future code introducing
a thread-pool worker on the encrypt path must propagate the active
session explicitly via contextvars.copy_context().
- exception NoActiveBucketSessionError(detail=None)[source]¶
Bases:
SecretStoreErrorRaised when the encrypt path runs outside an active session block.
Carries no payload — the diagnostic message names the canonical remediation verb so operators see how to recover without re-parsing the message.
- Parameters:
detail (str | None)
- Return type:
None
- code: ClassVar[ErrorCode]¶
- activate_session(session)[source]¶
Bind
sessionas the activeBucketSessionfor the block.The previous value of the
ContextVaris restored on exit via thecontextvars.Tokenreturned byset(), so nested activations stack and unwind cleanly. The session itself is not closed on exit — ownership of theBucketSessionlifecycle stays with the caller that opened it.- Parameters:
session (
BucketSession) – The unlockedBucketSessionwhose DEK becomes the column-level encryption key for the duration of the block.- Return type:
- get_active_master_key()[source]¶
Return the DEK bytes of the currently-active
BucketSession.Used by every column-level encrypt and decrypt operation in
_encrypted_columns.py. The DEK (not the KEK) is the AES-256-GCM key for the row-ciphertext layer — the KEK only ever unwraps the DEK duringBucketSession.open().- Return type:
- Returns:
The 32-byte DEK used for AES-256-GCM column-level encryption.
- Raises:
NoActiveBucketSessionError – When no
activate_session()block is currently active on the calling thread or task.BucketLockedError – When the active session has expired.
- has_active_bucket_session()[source]¶
Return whether an active
BucketSessionis bound.- Return type:
- current_active_bucket_session()[source]¶
Return the currently-bound
BucketSession, orNone.Read-only observation of the active-session
ContextVarfor callers (storage runtime readiness, per-request secure-object session gating) that need the live session’s attributes (bucket_id,sealed, idle deadline) rather than only its DEK (get_active_master_key()) or its presence (has_active_bucket_session()). Never mutates the context; onlyactivate_session()andsuspend_active_session()may bind or clear it.- Return type:
- suspend_active_session()[source]¶
Temporarily clear the active
BucketSessionfor the current context.