9.0 KiB
Data Custody Contract — Pointer and Consent Schema (D14)
Status: DRAFT — awaiting ratification (webui-audit S2, contract 7 of 9).
Authority: PRD D14/D6 (Part I §7) — sensitive profile categories live in
the user's own brain ONLY; PostgreSQL holds structural data, consent
records, and pointers — never the content; "user data does not leak" is
enforced by architecture, not policy. The Standalone split is a MAY with
a recommended-default (kept for conversion forward-compatibility, D3).
PRD D4 (Part I §6) — profile answers feed USER.md and/or the user's
data store subject to the custody rule; connectors carry granular
agentic-access consent. PRD D6 — estate brains hold operational records;
only product-relevant material migrates into repository docs.
This contract binds the sensitive-category registry (§2), the custody
placement rule (§3), the pointer schema (§4), the consent schema and its
evaluation (§5), mode application (§6), and witnesses (§7). It defines
schemas and placement; wizard step flow stays with contract 3, mode and
conversion with contract 6 (mode-conversion.md), identity with
identity-lifecycle.md, tool mapping with tool-gateway-mapping.md.
1. Definitions
- User brain: the git-tracked per-user data store (in Standalone, the user-files region of the single mosaic-brain; in Enterprise, the user's own brain repository).
- Sensitive content: any profile answer or derived text in a §2 sensitive category.
- Pointer: a database record referencing sensitive content that lives in a user brain, carrying no content (§4).
- Consent record: a database record granting a named grantee scope access to a category of a user's data (§5).
2. Sensitive-category registry
-
The registry is a closed, versioned list in the platform database; entries are added or reclassified by amendment to this contract, never ad hoc.
-
Initial registry, drawn from the D4 profile step and D14's "disabilities, family, communication style, and similar":
Category key Covers disabilitiesdisabilities including ADHD/autism/PDA/vision family-socialfamily, pets, friends communication-styledesired agent communication style, voice-matching interview product personal-interestshobbies, likes/dislikes connector-contentemail and drive content reached through user connectors -
Fail-closed classification. A profile category not in the registry is treated as sensitive until an amendment classifies it. Non-sensitive by classification (not by default) are structural fields the platform needs relationally: e.g. professional background/education summaries used for agent configuration MAY be classified non-sensitive by the ruling below; account identity fields (email, name, credentials) are identity-contract data, not profile custody data.
3. Custody placement rule
- Sensitive content is written to the owning user's brain ONLY. PostgreSQL tables MUST NOT store sensitive content in any column — not as text, not as excerpts or previews, not as embeddings or other derived representations that reconstruct content.
- The database MAY hold, about sensitive content: the pointer records of §4, the consent records of §5, and the category registry of §2. Nothing else.
- Every write path for profile answers routes by category: sensitive → brain write + pointer upsert; non-sensitive → its declared platform table. The routing decision is made server-side from the registry, never by the client.
- D6 boundary: operational records stay in estate brains and are linked, not migrated. This contract governs user-profile custody only and creates no new obligation on estate brains.
4. Pointer schema
A pointer row carries exactly:
id,user_id(the owning user),category_key(§2 registry FK),brain_ref(an opaque locator — repository-relative path or key in the owning user's brain),content_hash(integrity check of the referenced content),created_at/updated_at, and audit linkage.- Opacity.
brain_refand every other pointer column MUST NOT embed content or content-derived text (no titles, snippets, or free-text descriptions). A locator is structural (category + path discipline), not descriptive. - One pointer per (user, category, brain_ref); pointers are deleted when their content is deleted (dangling pointers are repaired toward deletion, never toward re-creating content in the DB).
5. Consent schema and evaluation
- A consent row carries exactly:
id,user_id(the data subject),grantee_scope(a typed reference: an enrolled agent, a connector, or a platform feature — closed enum of grantee types),category_key(§2 FK),state(granted|revoked),granted_at/revoked_at,actor(who recorded the choice), and audit linkage. - Default deny. Absence of a
grantedconsent row for (user, grantee scope, category) means no access. There are no implicit grants, no platform-admin bypass, and no mode in which default-deny is suspended. - Granularity. Consent is per grantee scope × category (the D4 "granular agentic-access consent"). A grant to one agent or connector confers nothing on another.
- Revocation. Revocation is effective for every access evaluated after the revoking write commits; revoked rows are retained as history (state flip, not row deletion).
- Evaluation placement. Access to sensitive content is mediated by
the platform (Gateway/tooling) evaluating consent before any brain
read on behalf of a grantee; the evaluation fails closed
(
rbac-grant-model.md§3.5 pattern). The user reading their own data is not a grantee and needs no consent row. - Consent records govern agentic/feature access to user data. They are distinct from hierarchy grants (contract 2) and confer no platform authorization.
6. Mode application
- The §3–§5 schemas are mode-independent: Standalone and Enterprise use the same tables and the same routing rule.
- In Standalone, D14 makes the physical split a MAY. This contract
keeps it a MAY and binds the recommended default: a fresh v1
Standalone install routes sensitive content per §3 from the start,
so the Enterprise conversion precondition (
mode-conversion.md§4.2, brains) is already satisfied. An operator electing not to keep the split accepts the resulting conversion-time partitioning work; the election is recorded. - In Enterprise, the split is mandatory (D3 table); no-leakage between users is enforced by §3 placement plus §5 default-deny — there is no cross-user read path to sensitive content through the database, because the database has no content to serve.
7. Verification requirements
Binding on the implementing PRs:
- No-content witness: a column-allowlist assertion (contract 1 §6.3 style) that the pointer and consent tables' column sets are exactly §4.1/§5.1, and that no platform table outside the declared non-sensitive profile tables carries profile answer content.
- Routing witness: a sensitive-category answer submitted through the profile surface results in a brain write plus a pointer row and zero content bytes in the database (asserted by content-hash presence in the brain and absence of the plaintext in any DB column); a non-sensitive answer lands in its declared table.
- Fail-closed classification witness: an answer in an unregistered category routes as sensitive.
- Opacity witness: pointer rows for seeded sensitive fixtures contain no fixture text in any column.
- Default-deny witness: an agent grantee with no consent row is
refused; with a
grantedrow for category A only, access to category B is refused. - Revocation witness: after revocation commits, the next access evaluation refuses; the revoked row persists as history.
- Self-access witness: the data subject reads their own content without consent rows.
- Deletion witness: deleting sensitive content removes its pointer; no path re-materializes content into the database.
Ruling request
Ratify sections 1–7 as written, with one decision embedded:
- Decision (§2): the initial sensitive-category registry is the five rows of §2.2, with fail-closed classification for anything unregistered, and with professional background/education classified non-sensitive (they exist to configure agents and reporting and are stored relationally). Alternative if rejected: classify background/education sensitive too — safe, but it moves data the platform legitimately queries into pointer-indirected storage and that cost should be chosen deliberately, not defaulted into.