cadrumo.adapters.persistence.storage.custody.acceleration_receipt module¶
Profile acceleration receipt: cross-process custody of a wrapped bucket DEK.
This is NOT a session. It has no counterparty and no protocol: it is a
locally held wrap of an already-unlocked DEK, carrying a deadline, that lets a
later process skip the passphrase. The AEAT authority session – an encrypted
row inside the bucket, revocable only with the key – is the artefact that
owns the word “session” in this codebase, and conflating the two has produced
a wrong architectural premise more than once.
The “logged in” state minted by aeat config login survives across CLI
processes as two split-knowledge artefacts, either of which is useless
alone:
an ephemeral 32-byte session key held ONLY in the OS keychain under the service
cadrumo:profile-session:v2with the immutable profile UUID plus minted session UUID as account, andan on-disk
session.v2.jsonrecord in the separated profile keystore directory carrying the AES-256-GCM wrap of the bucket DEK under that session key, with EVERY metadata field (schema version, immutable profile UUID, random session UUID, custody generation, DEK epoch,issued_at, the sliding idle deadline, and immutable absolute deadline) bound as AEAD associated data.
A disk-only attacker sees ciphertext no more revealing than the
already-persisted wrapped bucket.dek.json; a keychain-only attacker
holds a random key with nothing to decrypt; altering any persisted
deadline breaks the GCM tag. Resume evaluation is FAIL-CLOSED: an
expired, tampered, version-mismatched, or keychain-orphaned record is
deleted and refused with a typed
ProfileSessionRefusalReason, never silently
tolerated. No plaintext KEK, DEK, or session-key byte ever lands on disk.
Zeroisation honesty: the session key and DEK are held in bytearray
buffers wiped through zeroise()
on every exit path, but the AEAD primitives and the pydantic boundary
require transient immutable bytes views whose lifetime the garbage
collector owns — the same best-effort contract
BucketSession documents for the live in-process buffers.
Keychain failures are normalised to KeyringUnavailableError on every
path, including failures raised OUTSIDE the keyring library’s own
exception hierarchy. A backend can pass the class-level usability probe and
still fail at call time: the Windows credential store raises
win32ctypes.pywin32.pywintypes.error (for example WinError 1312, “a
specified logon session does not exist”) from a process whose logon session
the credential manager cannot reach, and that type derives directly from
Exception – it is neither a KeyringError nor an OSError, so a
guard naming either lets it escape as a raw traceback. Normalising here is
what makes the documented degradation reachable: no persisted artefact, a
process-scoped login, and a warning to the operator. The error type is caught
structurally rather than imported by name so this module carries no
platform-specific import.
See also
BucketSessionThe in-process materialisation this persisted record re-opens.
- PROFILE_SESSION_KEYCHAIN_SERVICE: Final[str]¶
OS-keychain service name for current profile acceleration-receipt keys.
The token deliberately lags the concept name. It addresses entries in the OS credential store, OUTSIDE the storage root, so a rename orphans every existing entry: deleting the storage root cannot reap them, and code that deleted under the old token first would be exactly the migration path this project forbids. Permanent credential residue on real machines is the worse outcome, so the wire identifier stays fixed while the code name says what the artefact is.
- PROFILE_SESSION_RECORD_MAX_BYTES: Final[int]¶
Strict ceiling for one canonical
session.v2.jsonreceipt.
- class ProfileSessionResumeOutcome(**data)[source]¶
Bases:
BaseModelTyped outcome of a fail-closed persisted-session resume evaluation.
Never carries key material: the resumed DEK travels beside this record as a separate return value so no pydantic dump can surface it.
- Parameters:
data (Any)
- resumed: bool¶
- refusal: ProfileSessionRefusalReason | None¶
- record: _crypto.PersistedProfileSession | None¶
- profile_session_path(*, storage_root, profile_id)[source]¶
Return the locator consumed exclusively by custody local-record operations.
- exception AccelerationReceiptRevocationError(message=None, *, context=None, translated_message=None)[source]¶
Bases:
StorageErrorRaised when a receipt survived the revocation that was asked to remove it.
Deliberately NOT a subclass of the custody record error this module already catches and logs, because the point is that it must not be swallowed by the same handler.
A boolean return would have repeated the defect one level up: the caller that ignored the value is precisely the caller that reports the profile closed. The revocation entry point is the single authority for “this profile is no longer logged in”, so when it cannot prove the receipt is gone it must not return as though it had.
- Parameters:
- Return type:
None
- code¶
- delete_profile_session(*, storage_root, profile_id)[source]¶
Revoke a profile receipt under the custody-wide session lifecycle lock.
- mint_profile_session(*, storage_root, profile_id, custody_generation, dek_epoch, dek, now, idle_minutes, absolute_minutes)[source]¶
Mint a profile receipt under the custody-wide session lifecycle lock.
Validate all deterministic receipt inputs before opening the custody root. A rejected bootstrap call must not provision a durable lock leaf merely to report malformed identity, metadata, key material, or time windows.
- resume_profile_session(*, storage_root, profile_id, custody_generation, dek_epoch, now)[source]¶
Evaluate a provisioned profile receipt under the custody-wide lifecycle lock.
The active-profile transaction provisions the root lock before a profile can reach this resume path. A raw first call against an unprovisioned storage root is therefore bootstrap work, not an observational refusal; established logged-out profiles take the lock without changing their session or retirement leaves.
- advance_persisted_profile_session_idle_deadline(*, storage_root, profile_id, record, new_idle_deadline)[source]¶
Advance a profile receipt under the custody-wide session lifecycle lock.
- Return type:
- Parameters:
storage_root (Path)
profile_id (UUID)
record (PersistedProfileSession)
new_idle_deadline (datetime)