Coverage for src/local_deep_research/database/rekey_epoch.py: 100%
12 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-22 21:20 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-22 21:20 +0000
1"""Durable authorization identity projection for encrypted user databases.
3Nothing in the application imports this module yet: it is the read side
4for session and socket identity fencing, which the FastAPI layer does not
5perform yet (a planned follow-up). Its readers do not take the rekey
6sidecar lock, which is why ``read_rekey_epoch_state`` reads pending-first.
7"""
9from pathlib import Path
11from .rekey_lock import RekeyRecoveryRequiredError, read_rekey_epoch_state
14def read_authorization_epoch(db_path: Path) -> str:
15 """Project the epoch that may authorize identities without repairing state."""
16 state = read_rekey_epoch_state(db_path)
17 committed_epoch = state.committed_epoch
18 if committed_epoch is None or (
19 state.pending_epoch is not None
20 and state.pending_epoch != committed_epoch
21 ):
22 raise RekeyRecoveryRequiredError(db_path)
23 return committed_epoch
26def read_user_authorization_epoch(username: str) -> str:
27 """Resolve a user's database path and project its authorization epoch.
29 The path MUST be resolved through ``db_manager`` (lazy-imported to
30 keep this module's import graph shallow): the manager may carry a
31 pinned ``data_dir`` override while ``get_data_directory()`` resolves
32 dynamically from the environment, and epochs are written beside the
33 databases under the MANAGER's resolution. Reading via the env-only
34 path projected a different directory whenever the two diverged
35 (any process that pinned the override), which would fence perfectly
36 healthy sessions with ``RekeyRecoveryRequiredError``.
37 """
38 from .encrypted_db import db_manager
40 db_path = db_manager._get_user_db_path(username)
41 return read_authorization_epoch(db_path)