cadrumo.application.user_profile.profile_record_repository module¶
Session-bound read authority for the capsule-resident current record.
This is intentionally not a generic persistence repository. It cannot create rows, save arbitrary aggregates, delete, tombstone, or reactivate anything. The physical capsule lifecycle stages and publishes bytes; this narrow owner only authenticates the one exact current fact record for an already-bound session.
Core types:
UserProfileRecord.
- invocation_profile_record_handoff()[source]¶
Scope one authenticated profile record to one CLI invocation.
CLI composition reads the selected profile once to snapshot its output language. A calculation command can consume that exact authenticated record as its initial profile load. The
ContextVaris entered by the CLI root and reset when Click closes the invocation, so a live profile session cannot carry plaintext record data into a later command.
- publish_invocation_profile_record(record)[source]¶
Offer a
UserProfileRecordto its current CLI invocation.- Return type:
- Parameters:
record (UserProfileRecord)
- take_invocation_profile_record(profile_id)[source]¶
Consume the invocation record when it belongs to
profile_id.- Return type:
- Parameters:
- bound_profile_record_session(session)[source]¶
Bind one authenticated record session for the duration of a command.
- Return type:
- Parameters:
session (ProfileRecordSession)
- activate_profile_record_session(session)[source]¶
Install the authenticated record authority for the active process session.
The caller has already authenticated and bound the matching custody DEK. A later login must retire the old record authority before publishing the new one, so a profile switch cannot leave facts decryptable through a prior session.
- Return type:
- Parameters:
session (ProfileRecordSession)
- bind_active_profile_record_session(session)[source]¶
Bind a candidate record authority without retiring the prior session.
Handover owns the two-phase lifetime: it first makes B observable, then retires A only after every required B publication succeeds. Returning A lets that owner restore the exact prior authority if a later publication fails, without a transient plaintext re-authentication or a duplicate record-session constructor.
- Return type:
- Parameters:
session (ProfileRecordSession)
- clear_active_profile_record_session_binding(expected)[source]¶
Clear only an expected active binding without closing its owner.
Candidate cleanup first removes the context reference, then zeroises the candidate. The identity check prevents a late cleanup from unbinding a replacement session installed by a nested operation.
- Return type:
- Parameters:
expected (ProfileRecordSession)
- active_profile_record_session()[source]¶
Return the record session this process currently holds, whatever installed it.
- Return type:
- close_active_profile_record_session()[source]¶
Zeroise and clear the process-local record authority.
- Return type:
- profile_record_session_if_authenticated(profile_id, *, profile_decode_context)[source]¶
Return the record authority serving this UUID, or
Nonewhen none is live.This is the structural answer to “is this profile unlocked?”, and callers that need to tell a LOCKED profile from a broken one ask it here rather than inferring the answer from a refusal message. An absent session is an ordinary, expected state – the operator has simply not logged in – so it is reported as an absence rather than raised;
require_profile_record_session()converts it for the callers whose contract is a session or nothing.The installed authority is process-local, so a second profile becoming the live custody session in the same process would otherwise be read through the first profile’s latched authority – or, because the identity guard refuses that, not be readable at all until the process restarts. An authority that does not serve the requested UUID is therefore re-derived rather than refused outright.
Re-derivation stays as narrow as the first derivation: it succeeds only from the requested profile’s already-open custody session, so an unauthenticated profile remains unreadable, and installing the derived authority retires the one it replaces so a switch cannot leave facts decryptable through a prior session.
A latched authority must be LIVE as well as correctly addressed. Retirement zeroises the key in place and leaves the object latched, so the identity check alone cannot tell a retired authority from a working one: a span that binds a session, has it retired under it, and then restores its own prior binding on exit hands the next caller a UUID-matching authority with no key behind it. That reached the caller as an integrity error from deep inside the decrypt, blaming the read for a decision made when the session was reused.
A retired authority is therefore treated exactly as an absent one – it carries no authority, so it is re-derived where a live custody session exists and reported absent where none does. Nothing is unbound on the declining path: a read that declines should not mutate process state, and a successful later derivation replaces the dead reference anyway.
A SESSION-DERIVED authority must also still be BACKED. Login mints it from a live custody session and latches it, and nothing re-checked that derivation afterwards, so closing the bucket session by any path other than the strong logout left the authority behind and facts readable through it: within one process, “logged out” and “record authority gone” were different states, and a health check after a logout still read facts through the survivor. The custody session is therefore consulted on every resolution of a derived authority – an in-memory check, no capsule read – and one whose session is gone declines exactly as an absent one does.
An EXPLICITLY BOUND authority is untouched by this. A caller that opened a record session itself and installed it owns its lifetime; requiring a live custody session behind it would revoke every such binding the moment no profile happened to be logged in, which is a different behaviour, not a stricter one.
Declining does not zeroise it. The record authority and the bucket session are bound together and rebound together during the login handover’s rollback window, so a reader is not the owner that may destroy either;
logout_active_profile(), which is the close owner, wipes it there.A
profile_idthat is not a canonical UUID still raises: that is a caller defect rather than a lock state, and returningNonefor it would let a malformed identity read as a merely locked profile.- Return type:
- Parameters:
profile_decode_context (ProfileDecodeContext)
- require_profile_record_session(profile_id, *, profile_decode_context)[source]¶
Return the record authority that serves this exact UUID, or refuse.
The authority resolution itself lives in
profile_record_session_if_authenticated(); this door is for callers whose contract admits no absence.- Raises:
ProfileNotFoundError – When no authenticated session serves this UUID, or when
profile_idis not a canonical UUID.- Return type:
- Parameters:
profile_decode_context (ProfileDecodeContext)
- class ProfileRecordRepository(*, session, root=None)[source]¶
Bases:
objectRead exactly one encrypted current record through an authenticated session.
- Parameters:
session (ProfileRecordSession)
root (Path | None)
- classmethod for_current_session(profile_id, *, root=None, profile_decode_context)[source]¶
Create a repository using the authenticated session for this profile.
- Return type:
- Parameters:
root (Path | None)
profile_decode_context (ProfileDecodeContext)
- load(profile_id)[source]¶
Load and authenticate the current record for the requested UUID.
- Return type:
- Parameters:
- complete_setup(profile_id, *, expected_revision, expected_content_digest, now=None)[source]¶
CAS-complete setup; the lifecycle owns the physical replacement.
Judged through the same authority every fact-writing door uses, but at the strictest setting the authority offers. A profile born incomplete defers exactly the missing-required-field issues, and this promotion is the moment they come due: COMPLETE is not a label for a record that has stopped being edited, it is the CLAIM that nothing required is missing. Promoting without re-applying them publishes that claim about a record that does not support it, and every downstream surface reading setup state then trusts it – which is why this door judges harder than the doors that merely write a fact, rather than the same amount.
The already-COMPLETE early return above stays ahead of the judgement on purpose: it is an idempotent no-op that publishes nothing, so holding it to a contract a stored record may predate would refuse a caller that changes no state.
- Raises:
ProfileNotFoundError – When the bound session does not serve
profile_id.ProfileRecordConflictError – When the compare-and-swap fails.
ProfileSchemaValidationError – When the record does not satisfy the complete-profile contract it is being promoted into.
- Return type:
- Parameters:
- apply_fact_changes(profile_id, *, facts, expected_revision, expected_content_digest, event_type, event_payload, now=None)[source]¶
CAS-publish an explicit fact replacement and its command event.
factsis the complete next exact fact sequence, rather than an implicit upsert patch. Callers must load and compose that sequence first, which keeps every mutation revision-bound and makes a cleared fact as explicit as an added one. The lifecycle publishes the encrypted replacement and its authenticated event in the same capsule data-file replacement.event_typeis the closed catalogue member, never a free string, and the command witness it is carried on declares the same closed type, so a caller-invented string can no longer travel as far as the capsule writer. Taking the member here rather than a string puts the accepted set in front of the caller at the call site, where the alternative was a refusal deep inside the writer that cost the whole command – every fact it carried included.- Return type:
- Parameters: