cadrumo.application.user_profile.registration module

Credential-first profile registration.

This is the door behind the terminal’s first screen. It exists because profile creation used to be split across two surfaces that never met: the operator’s chosen label arrived as a shell argument to config profile create, while the passphrase that actually protects the bucket was only ever collected later, by config login, through a separate command. Nothing in the setup flow collected a credential at all.

The contract here is deliberately minimal: a profile exists the moment a label and a passphrase are supplied. No tax id, no régimen, no activity — those are profile completeness, filled in afterwards against a live record, not preconditions for the record existing. The profile is therefore born SETUP_INCOMPLETE: real, addressable, and writable, while modelo work stays refused until the facts that filing depends on are present.

Registration is the sole creation path. It stages the first encrypted record and then commits the capsule, label projection, and pointer transaction. The bucket’s key-encryption key is derived from the credential the operator chose rather than from an ambient environment value.

See also

login_profile()

The returning-operator counterpart; this module is the first-time path that has no key material to unwrap yet.

exception ProfileRegistrationError(message=None, *, context=None, translated_message=None, password_refusal=None)[source]

Bases: CadrumoError

Raised when a registration request 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
exception ProfileRegistrationConflictError(message=None, *, context=None, translated_message=None, password_refusal=None)[source]

Bases: ProfileRegistrationError

Raised when registration lost a race it can win by simply repeating.

A custody transaction refuses when the witness it captured no longer matches live state, which a re-read resolves – so the identical call may succeed. That is a different answer to the operator than “this label is taken”, which no retry can change, and the two arrive here as the same exception family because the permanent case is a SUBCLASS of the transient one.

Split as a subclass for the same reason that one was: every existing handler catching ProfileRegistrationError keeps catching this, and only the published code and its retryability differ.

Parameters:
Return type:

None

code
class ProfileRegistrationOutcome(**data)[source]

Bases: BaseModel

Typed result of one successful registration.

Carries no key material. The created profile is left UNLOCKED: the create span publishes its live bucket session and record authority process-wide through publish_created_profile_session(), exactly as login_profile() leaves them, so the operator is not asked for the passphrase they just chose. No acceleration receipt is minted, so the next process authenticates normally.

Parameters:

data (Any)

profile_id: ProfileId
bucket_id: BucketId
label: str
setup_state: ProfileSetupState
register_profile_with_credentials(*, label, passphrase, facts=(), profile_create_context, profile_decode_context)[source]

Create a profile from a label and a passphrase, and unlock it.

The profile is born ProfileSetupState.INCOMPLETE: it is a real, writable record from this moment, and the operator completes it afterwards against a live profile rather than through a gated wizard.

Recovery is not part of creation. A profile is born with its passphrase as its only door; the operator may enrol a recovery code afterwards through enroll_profile_recovery(), and every surface that creates a profile offers that step as an explicit, skippable follow-up rather than a precondition.

Parameters:
  • label (str) – Operator-chosen display name. Must be non-blank and must not collide with an existing profile’s label.

  • passphrase (str) – The credential protecting the bucket. Must satisfy the canonical profile-password contract; never logged, never echoed, and held only for the duration of the create span.

  • facts (tuple[UserProfileFact, ...]) – Optional initial facts. Empty by default — the whole point of this door is that a profile needs no tax data to exist.

  • profile_create_context (ProfileCreateContext) – Schema context pinned for the new record.

  • profile_decode_context (ProfileDecodeContext) – Schema context pinned for the authenticated record session. It must share the authority generation with profile_create_context.

Return type:

ProfileRegistrationOutcome

The created profile is left unlocked for this process: its live bucket session and record authority are published exactly as a login publishes them. The operator has just chosen and proven this passphrase, so asking for it again before the profile can be used answers a question they have already answered.

Return type:

ProfileRegistrationOutcome

Returns:

A ProfileRegistrationOutcome for the newly-live profile.

Raises:
  • ProfileRegistrationError – When the label is blank or the passphrase does not satisfy the canonical profile-password contract.

  • ProfileRegistrationError – When the label is already bound.

  • ProfileSchemaValidationError – When an initial fact names an unknown or engine-derived path, or carries a value its field will not take. Missing filing fields are not refused: the profile is born incomplete on purpose.

Parameters: