The trust covenant
Five guarantees about how the daemon treats your data — local-first, quote-grounded truth, reversible writes, scoped access with an elevated-write approval gate, and no silent provider fallback — each one a specific code path, not a policy statement.
Verified 2026-07-09 @ dfd671a3
What this is
The trust covenant is five guarantees the daemon makes about your data, and each one is a specific piece of code rather than a promise in a policy document: a bind check that refuses non-loopback hosts, a substring check that drops ungrounded claims, a mutation journal that makes writes reversible, an approval gate that blocks unapproved elevated writes, and a provider registry that throws instead of guessing which model to call.
Why it exists
Most “AI memory” products ask you to trust their cloud, their prompt, and a
model that says it didn’t hallucinate. Ouroboros makes a narrower, checkable
claim: here is the file and line that enforces this, and here is what
happens if you try to make it do otherwise. That’s the same design axiom the
rest of this system follows — enforcement lives in protocol and schema,
never in an instruction an agent could choose to ignore. A skill file can
suggest good behavior; only a for loop, a NOT NULL constraint, or a
thrown error actually stops a bad one.
How it works
Local-first. The daemon refuses to start unless it’s bound to a loopback
address — a non-loopback HOST logs a warning and calls process.exit(1)
before the server ever listens (proxy/src/daemon.ts:374-379). Both
databases — the ops DB (bearers, scopes, the mutation journal) and the
subscriber DB (your graph, documents, code index, wiki) — are encrypted at rest — libsql’s built-in encryption (proxy/src/utils/encryptedDatabase.ts), keyed from the OS keychain (GNOME Keyring via secret-tool on Linux) with a chmod-600 key-file fallback (proxy/src/utils/encryptionKey.ts) — and opened with
PRAGMA locking_mode = EXCLUSIVE so no second sqlite3 process can attach
underneath the daemon while it’s running (proxy/src/backend/local.ts:597-599).
No cloud sync path exists in this codebase for your data to leave through.
Quote-grounded truth. Every claim the extraction model emits carries an
evidence string capped at 300 characters (MAX_EVIDENCE_LEN in
proxy/src/extraction/claimGraph.ts), and the module’s own header names the
mechanism directly — “a for-loop, not a model, not negotiable” — dropping
any claim whose evidence isn’t a literal substring of the source text. A
correction is enforced just as mechanically on the read side: ranked queries
order corrected_by_user='1' rows first (BELIEF_COVENANT_PREFER_USER,
proxy/src/utils/beliefCovenant.ts:87), and the write path checks for an
existing correction before it dedupes — writeFactDeduped()
(proxy/src/knowledge/factDedup.ts) returns
{ outcome: 'skipped', reason: 'corrected' } rather than writing a
conflicting row. Truth covers both mechanisms in full.
Reversible writes. LocalBunSqliteWriter.journal()
(proxy/src/backend/local.ts) fires after every insert, update, or delete
on a subscriber table — “for v1 we journal everything user-facing” per its
own comment (local.ts:173) — recording actor, table, row, and full
before/after JSON. sophia.revert_mutation computes the inverse and applies
it in one transaction, refusing with row_state_conflict if the row has
changed since the mutation it’s reverting. Time Machine
is the full mechanism.
Scoped access, elevated writes need a human gesture. A connection’s
entity scope is enforced at the daemon’s query layer, not by agent good
behavior — a connection scoped to specific entities cannot return rows
outside that scope regardless of what it asks for. A fixed set of elevated
writes — remember_fact, create_entity, begin_import_session,
revert_mutation — always requires an explicit owner approval gesture,
regardless of connection profile (ELEVATED_WRITE_TOOLS and
needsApproval(), proxy/src/mcp/approval.ts:402-436). Even a
pre_approved full-trust connection can’t skip this; only one thing can, by
design: a portal connection (conn.is_portal, approval.ts:421).
No silent provider fallback. getProviderForTask() — “the one
chokepoint,” per its own header — either returns a real provider or throws:
NoProviderConfiguredError when no route exists for the task,
ProviderArchivedError when the configured provider was archived
(proxy/src/backend/providers/registry.ts:15-19, 88-116). There is no
built-in vendor key and no fallback provider chosen for you. The Anthropic
adapter reinforces the same rule one layer down: its SDK client is
constructed with maxRetries: 0 specifically so a transient 429 surfaces as
an honest error instead of being hidden behind silent backoff
(proxy/src/backend/providers/anthropic.ts:17-20).
flowchart LR
T["MCP write call"] --> W{"isWriteTool?"}
W -- no --> OK["applied — not a covenant check"]
W -- yes --> P{"conn.is_portal?"}
P -- yes --> OK
P -- no --> E{"elevated write?<br/>remember_fact / create_entity /<br/>begin_import_session / revert_mutation"}
E -- yes --> A["pending_approval —<br/>ALWAYS, every profile"]
E -- no --> M{"write_approval_mode<br/>== pre_approved?"}
M -- yes --> OK
M -- no --> A
style A fill:#3a1a1a,stroke:#ef4444,color:#fff What your agent does with it
An agent doesn’t take the covenant on faith — it can call the same checks the daemon runs.
// Real responses from this daemon, captured 2026-07-09:
const check = await sophia.verify_evidence({
source_text: 'Elevated writes ALWAYS require approval regardless of profile.',
quotes: [
{ id: 'real', text: 'Elevated writes ALWAYS require approval regardless of profile' },
{ id: 'fabricated', text: 'Elevated writes are approved automatically for trusted agents' },
],
});
// → { ok: false, found_count: 1, results: [
// { id: 'real', found: true, match_span: [0, 61] },
// { id: 'fabricated', found: false } ] }
const providers = await sophia.list_providers({});
// → { providers: [
// { providerKind: 'claude_code', displayName: 'Opus47_Dev',
// defaultTextModel: 'sonnet 4.6', isActive: true /* no key material */ } ],
// routes: [ { taskKey: 'mine.text', providerKeyId: '...' }, /* 5 more tasks */ ] }
// remember_fact's shape on a non-portal connection, per source — not called
// live, to keep this session read-only:
// first call, no approval_id → { status: 'pending_approval', approval_id }
// second call, approved id → the write actually lands The first call is the grounding primitive itself, run against a sentence
from this page. The second, list_providers, is a live read-only call —
the response really does come back scrubbed, no keys or decrypted
endpoints. sophia.get_permissions is the matching identity check:
permission_profile, entity_scope, and write_approval_mode in one
call.
Boundaries
The covenant governs what the daemon does with data already inside it. It
says nothing about model quality — a poorly-prompted model can still
produce a claim that passes the substring check because the quote is real
but misleading in context. It doesn’t cover key hygiene either:
list_providers scrubs key material from every read, but a BYOK key
compromised outside this system is outside any daemon-side check.
The daemon and its storage layers are The State Layer; the grounding and correction mechanics behind quote-grounded truth are Truth; the mutation journal and revert mechanics are Time Machine; the full connection, scope, and approval model is Security Model; the tool catalog every call above is drawn from is MCP Surface.