cadrumo.application.user_profile.recovery_custody module¶
Optional per-profile recovery: enrol, revoke, inspect, and reset the passphrase.
Recovery is a second wrapper over the same data-encryption key, opened by a
minted recovery code instead of the operator’s passphrase. It is off by
default. An operator who wants it enrols after the profile exists, proves the
current passphrase to do so, and copies the code down once; the code is never
persisted, never logged, and never enters a result envelope. The only thing
the code is good for is reset_profile_passphrase_with_recovery(): it
proves the enrolled wrapper against the capsule’s own sentinel and re-wraps
the key under a new passphrase, so a forgotten passphrase is replaced rather
than the records lost.
Every door here works on a COMMITTED capsule under its custody transaction lock. Nothing mints, rotates, or re-derives a key schedule; the DEK epoch is preserved throughout, which is what keeps an enrolled code valid across ordinary passphrase changes.
See also
rotate_profile_passphrase()The passphrase-proved sibling of the reset door; both share one re-wrap primitive.
- exception ProfileRecoveryError(message=None, *, context=None, translated_message=None, password_refusal=None)[source]¶
Bases:
CadrumoErrorRaised when a recovery door cannot be honoured as supplied.
- Parameters:
- Return type:
None
- property password_refusal: ProspectiveProfilePasswordRefusal | None¶
Retain the typed refusal for trusted in-process consumers only.
- code¶
- class ProfileRecoveryEnrollment(envelope, recovery_key)[source]¶
Bases:
objectOne minted recovery wrapper and the secret that opens it.
The secret rides in its wipeable container rather than as a
str: the operator holds it across an interactive confirmation lasting as long as it takes a human to copy a code down, and a string copy is unreachable by any wipe primitive for its whole lifetime. The enrolment door owns the wipe and takes it as soon as the handover returns.- Parameters:
envelope (ProfileCustodyRecoveryEnvelopePort)
recovery_key (ProfileRecoveryKeyPort)
-
envelope:
ProfileCustodyRecoveryEnvelopePort¶
-
recovery_key:
ProfileRecoveryKeyPort¶
- class ProfileRecoveryStatus(**data)[source]¶
Bases:
BaseModelWhether one committed profile currently has recovery enrolled.
- Parameters:
data (Any)
- profile_id: ProfileId¶
- enrolled: bool¶
- class ProfileRecoveryEnrollmentOutcome(**data)[source]¶
Bases:
BaseModelNon-secret result of one completed enrolment or revocation.
- Parameters:
data (Any)
- profile_id: ProfileId¶
- enrolled: bool¶
- changed: bool¶
- class ProfilePassphraseResetOutcome(**data)[source]¶
Bases:
BaseModelNon-secret result of one passphrase reset proved by the recovery code.
- Parameters:
data (Any)
- profile_id: ProfileId¶
- password_generation: int¶
- dek_epoch_preserved: bool¶
- recovery_enrollment_retained: bool¶
- profile_recovery_status(*, profile_id, root=None)[source]¶
Report whether
profile_idhas a recovery wrapper enrolled.Reads only the committed capsule’s layout; no secret is required and no key material is touched.
- Return type:
- Parameters:
- enroll_profile_recovery(*, profile_id, current_passphrase, recovery_handover, root=None)[source]¶
Mint a recovery code for a committed profile and install its wrapper.
The current passphrase is the authorisation: a second door onto the records may only be opened by someone who already holds the first. The code is delivered through
recovery_handoverand nowhere else, and the callback must return the exact code it received as possession proof. The wrapper is installed only after that proof matches, so an operator who could not copy the code down is left exactly where they started rather than enrolled under a secret nobody holds.- Parameters:
profile_id (
UUID) – The committed profile to enrol.current_passphrase (
str) – Proof of the existing credential. Never logged.recovery_handover (
Callable[[ProfileRecoveryEnrollment],str]) – The one channel the code reaches the operator through. It is invoked once, before anything is written, and the key is wiped by the time this returns. Raising from the callback aborts the enrolment.root (
Path|None) – Storage root override; the effective root when omitted.
- Raises:
ProfileRecoveryError – When the profile is already enrolled, the passphrase does not open the committed envelope, or the returned proof differs from the minted code.
- Return type:
- revoke_profile_recovery(*, profile_id, current_passphrase, root=None)[source]¶
Remove the enrolled recovery wrapper after proving the current passphrase.
Idempotent: a profile that is not enrolled is reported unchanged rather than refused, because the operator’s intent (“no recovery on this profile”) already holds. The passphrase is still proved first, so the unchanged answer never discloses enrolment state to someone without it.
- Return type:
- Parameters:
- reset_profile_passphrase_with_recovery(*, profile_id, recovery_code, new_passphrase, new_passphrase_confirmation, root=None, profile_decode_context)[source]¶
Replace a forgotten passphrase by proving the enrolled recovery code.
The recovery code unwraps the DEK from the enrolled wrapper, the result is proved against the capsule’s own sentinel, and only then is the password envelope re-minted under
new_passphrasethrough the same primitive an ordinary rotation uses. The DEK epoch is preserved, so the recovery wrapper stays enrolled and the same code keeps working.Fails closed at every step before the swap: a profile without recovery, a wrong code, a new passphrase outside the profile-password contract, or a mismatched confirmation all refuse with the committed envelope untouched.
- Raises:
ProfileRecoveryError – When recovery is not enrolled, the confirmation does not match, the new passphrase is invalid, or the code does not open the enrolled wrapper.
- Return type:
- Parameters: