Skip to content

status

status

One vault-state picture for every surface — CLI status, TUI pill, sickbay.

VaultStatus.load computes everything the frontends used to derive independently (and with independently-drifting wording): the lock classification, the per-tier chain table with shadowing, the session-shadow comparison, and a catalog of VaultWarnings whose text is authored exactly once. A renderer's whole job is picking which fields to show — never re-deciding what they mean.

"Locked" alone hides three different operator problems with three different remedies, so the classification keeps them apart:

  • VaultState.UNPROVISIONED — fresh install: no DB and no tier holds anything. The remedy is the provisioning flow, not an unlock prompt.
  • VaultState.LOCKED — a passphrase problem; lock_reason says which one (empty chain, wrong key, or a broken fail-closed tier).
  • VaultState.ERROR — the DB failed for a non-passphrase reason (schema drift, permissions); db_error carries it verbatim.
  • VaultState.UNLOCKED — a tier resolves and (when the DB exists) opens it.

__all__ = ['ChainRow', 'VaultState', 'VaultStatus', 'VaultWarning', 'VaultWarningKind', 'active_durable_source'] module-attribute

VaultState

Bases: StrEnum

The four operator-distinct answers to "can I use the vault?".

UNPROVISIONED = 'unprovisioned' class-attribute instance-attribute

Fresh install — no credentials DB and no tier holds a passphrase. The remedy is the provisioning flow (setup / the TUI tier chooser), not an unlock prompt keying a brand-new vault to a typo.

LOCKED = 'locked' class-attribute instance-attribute

A passphrase problem — lock_reason names which of the three: empty chain, wrong key, or a broken fail-closed tier.

UNLOCKED = 'unlocked' class-attribute instance-attribute

A tier resolves the passphrase and, when the DB exists, opens it.

ERROR = 'error' class-attribute instance-attribute

The DB failed for a non-passphrase reason — db_error has it verbatim.

ChainRow(tier, present, active, detail) dataclass

One tier of the resolution chain, annotated for display.

active marks the tier the resolver would use right now — the first present tier in resolution order.

tier instance-attribute

present instance-attribute

active instance-attribute

detail instance-attribute

VaultWarningKind

Bases: StrEnum

Stable identifiers for the warning catalog — frontends dispatch on these.

BROKEN_TIER = 'broken-tier' class-attribute instance-attribute

RECOVERY_UNCONFIRMED = 'recovery-unconfirmed' class-attribute instance-attribute

RECOVERY_VOLATILE = 'recovery-volatile' class-attribute instance-attribute

VaultWarning(kind, severity, brief, message) dataclass

One vault warning, authored here and only here.

brief is the compact form for pills / one-line summaries; message the full sentence for notifications and status pages. kind is the semantic identifier — a renderer that wants to attach a remedy (a CLI verb, a TUI button) maps it per frontend rather than reading command strings out of the library layer.

kind instance-attribute

severity instance-attribute

brief instance-attribute

message instance-attribute

VaultStatus(state, lock_reason, db_error, source, chain, recovery, db_path, db_exists, providers, credential_types, ssh_keys, warnings) dataclass

Everything a frontend needs to render the vault — loaded in one call.

state instance-attribute

lock_reason instance-attribute

Why the vault counts as locked, in operator language. None unless state is LOCKED.

db_error instance-attribute

Verbatim non-passphrase DB failure. None unless state is ERROR.

source instance-attribute

The tier the resolver would use right now, or None.

chain instance-attribute

recovery instance-attribute

db_path instance-attribute

db_exists instance-attribute

False on a fresh install — the DB is created encrypted on first use, so "unlocked" then means "the key is ready", not "the DB opened".

providers instance-attribute

Sorted provider slugs stored in the vault; None when the DB couldn't be read (locked / error).

credential_types instance-attribute

provider → type (api_key / oauth_token / …), read in the same DB pass as providers so no renderer ever pays a second SQLCipher key derivation just to label a row. None exactly when providers is.

ssh_keys instance-attribute

Count of stored SSH keypairs, from the same DB pass. None exactly when providers is.

warnings instance-attribute

load(cfg=None) classmethod

Compute the full vault picture for cfg (defaults if None).

Read-only by construction: unlike a bare cfg.open_credential_db() this never creates the DB — a fresh install stays fresh no matter how often status is rendered. Never prompts (a status read must not block on stdin).

Source code in src/terok_sandbox/vault/store/status.py
@classmethod
def load(cls, cfg: SandboxConfig | None = None) -> VaultStatus:
    """Compute the full vault picture for *cfg* (defaults if ``None``).

    Read-only by construction: unlike a bare
    ``cfg.open_credential_db()`` this never *creates* the DB — a
    fresh install stays fresh no matter how often status is
    rendered.  Never prompts (a status read must not block on
    stdin).
    """
    cfg = _resolve_cfg(cfg)
    recovery = RecoveryStatus.load(cfg)
    chain = _encryption.probe_passphrase_chain(
        credentials_db=cfg.db_path,
        systemd_creds_file=cfg.vault_systemd_creds_file,
        use_keyring=cfg.credentials_use_keyring,
        passphrase_command=cfg.credentials_passphrase_command,
    )
    active_index = next((i for i, tier in enumerate(chain) if tier.present), None)
    rows = tuple(
        ChainRow(
            tier=presence.source,
            present=presence.present,
            active=(i == active_index),
            detail=presence.detail,
        )
        for i, presence in enumerate(chain)
    )

    db_exists = cfg.db_path.exists()
    access = _classify_db_access(cfg, recovery, db_exists=db_exists)
    if access.db_error is not None:
        state = VaultState.ERROR
    elif access.lock_reason is None:
        state = VaultState.UNLOCKED
    elif not db_exists and recovery.source is None and recovery.resolve_error is None:
        state = VaultState.UNPROVISIONED
    else:
        state = VaultState.LOCKED

    return cls(
        state=state,
        lock_reason=access.lock_reason,
        db_error=access.db_error,
        source=recovery.source,
        chain=rows,
        recovery=recovery,
        db_path=cfg.db_path,
        db_exists=db_exists,
        providers=access.providers,
        credential_types=access.credential_types,
        ssh_keys=access.ssh_keys,
        warnings=_build_warnings(recovery),
    )

active_durable_source(cfg)

Name the durable tier that already resolves the vault, or None.

Probes the chain for a reboot-surviving tier (presence only — no unseal, no command exec). The volatile kernel-keyring cache is durable=False and so never counts here. The single source of truth for the no-cache guard, shared by the passphrase-cache writer and the CLI's skip-the-prompt early-out: if a durable tier is already present, there is nothing worth caching on top of it.

Source code in src/terok_sandbox/vault/store/status.py
def active_durable_source(cfg: SandboxConfig) -> PassphraseTier | None:
    """Name the durable tier that already resolves the vault, or ``None``.

    Probes the chain for a reboot-surviving tier (presence only — no
    unseal, no command exec).  The volatile kernel-keyring cache is
    ``durable=False`` and so never counts here.  The single source of
    truth for the no-cache guard, shared by the passphrase-cache writer
    and the CLI's skip-the-prompt early-out: if a durable tier is
    already present, there is nothing worth caching on top of it.
    """
    for tier in _encryption.probe_passphrase_chain(
        credentials_db=cfg.db_path,
        systemd_creds_file=cfg.vault_systemd_creds_file,
        use_keyring=cfg.credentials_use_keyring,
        passphrase_command=cfg.credentials_passphrase_command,
    ):
        if tier.present and tier.source in DURABLE_TIERS:
            return tier.source
    return None