Skip to content

domain/creek_vault — the vault seam

backend/src/domain/creek_vault.py (283 lines). The vocabulary and value types adepthood uses to talk to an optional Creek Vault confidential-compute enclave — "and nothing else: no FastAPI, no SQLModel/DB, no httpx" (creek_vault.py:1-9). The transport lives behind an injected CreekVaultClient protocol so the concrete adapter (services.creek_vault_client) can be swapped, faked, or absent.

The governing principle is graceful degradation: "no feature adepthood ships today depends on a vault being present" (creek_vault.py:11-14). Two invariants are load-bearing (backend/src/domain/creek_vault.py:16-24):

* **Fail closed on tier.** :func:`tier_ceiling_for` raises rather than defaulting
  to :attr:`VaultTierCeiling.OPEN` for an unknown classification. Silently
  widening a tier would let sensitive content leave under a looser ceiling than
  the writer chose -- the opposite of "you choose your depth."
* **Privacy over debuggability.** The error hierarchy exists so the service layer
  can normalize any transport failure to :class:`CreekVaultUnavailableError`
  *without* echoing the entry body or an API key into the message.

The authoritative wire contract lives in docs/creek-vault-mcp-contract.md in the adepthood repo (creek_vault.py:26-28); CONTRACT_VERSION = "0.1.0-draft", CONSUMER_ID = "CREEK_MCP_CONSUMER" (creek_vault.py:39-46).

Capabilities and tiers

CreekCapability — the wire method names a vault may advertise: creek.handshake, creek.journal, creek.save, creek.classify, creek.reflect, creek.wheel; "adepthood must never assume a capability exists without first seeing it" in the handshake (creek_vault.py:49-62).

VaultTierCeilingopen / personal / intimate; OPEN is Creek's word for what adepthood calls PUBLIC (creek_vault.py:65-76). The mapping is keyed by raw JournalClassification strings so the module stays DB-free, with a drift-guard test asserting the key sets match (creek_vault.py:79-88). tier_ceiling_for(classification) fails closed: ValueError on unknown input — "the safe answer to 'I don't know this tier' is to refuse the call, not to widen it" (creek_vault.py:91-103).

Errors

  • CreekVaultError(RuntimeError) — one vault-agnostic catch type; "an unrelated internal bug propagates unchanged so the real defect is not masked" (creek_vault.py:106-112).
  • CreekVaultUnavailableError — transport failure; message deliberately static, never interpolating the entry body or an API key (creek_vault.py:115-122).
  • CreekCapabilityUnsupportedError — the handshake did not advertise the capability (or no vault is configured — the local-fallback client raises it for every read/compute capability). "Degradation is per-capability, not all-or-nothing" (creek_vault.py:125-134).

Value types

Type Shape Notes
HandshakeResult available, contract_version, ontology_version, capabilities: frozenset, attestation Frozen so a cached handshake can't be mutated under later reads; HandshakeResult.unavailable() is the single canonical "no usable vault" value every degradation path collapses to (creek_vault.py:137-169)
VaultIngestRequest entry_id, body, tier, tier_ceiling, created_at entry_id keys the stored fragment, so re-sending is idempotent and edits in place; for a journal entry tier == tier_ceiling, so Creek "stores at exactly that tier and refuses any widening (it never downgrades)" (creek_vault.py:172-190)
VaultIngestResult stored, vault_ref stored=False with vault_ref=None on the local-fallback path — a no-op, not an error (creek_vault.py:193-204)
VaultClassification tags: tuple[str, ...] Frequency/Wavelength-phase tags (creek_vault.py:207-211)
VaultWheelAspect / VaultWheelBalance per-Aspect fullness rows Domain-native mirror of the transport payload; the adapter owns the Pydantic parse (creek_vault.py:214-238)

CreekVaultClient protocol (creek_vault.py:241-283)

handshake() (never raises — incompatible/absent vaults yield unavailable()), is_available(), supports(capability), ingest(request), classify(body, tier_ceiling), reflect(body, tier_ceiling) (a Higher Self reflection grounded in the corpus), and wheel(). wheel is the one capability whose field-level parse errors are not normalized to unavailable — the consumer owns field validation, and a caller that cannot obtain the wheel "falls back to computing the balance locally" (creek_vault.py:275-283; the local computation is domain/wheel).

Persistence linkage: a successful ingest writes JournalEntry.vault_ref / vault_tags (backend/src/models/journal_entry.py:224-232); corpus-theme invitations consume wheel() readings (domain/invitations). See also ADR 0012 — local-first privacy tiers.


Grounded in adepthood@fbc529d, 2026-07-31.