Files
stack/docs/requirements/custody-schema.md
T
2026-08-26 19:10:12 -05:00

9.0 KiB
Raw Blame History

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

  1. 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).
  2. Sensitive content: any profile answer or derived text in a §2 sensitive category.
  3. Pointer: a database record referencing sensitive content that lives in a user brain, carrying no content (§4).
  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

  1. The registry is a closed, versioned list in the platform database; entries are added or reclassified by amendment to this contract, never ad hoc.

  2. Initial registry, drawn from the D4 profile step and D14's "disabilities, family, communication style, and similar":

    Category key Covers
    disabilities disabilities including ADHD/autism/PDA/vision
    family-social family, pets, friends
    communication-style desired agent communication style, voice-matching interview product
    personal-interests hobbies, likes/dislikes
    connector-content email and drive content reached through user connectors
  3. 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

  1. 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.
  2. 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.
  3. 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.
  4. 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:

  1. 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.
  2. Opacity. brain_ref and 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.
  3. 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).
  1. 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.
  2. Default deny. Absence of a granted consent 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.
  3. Granularity. Consent is per grantee scope × category (the D4 "granular agentic-access consent"). A grant to one agent or connector confers nothing on another.
  4. Revocation. Revocation is effective for every access evaluated after the revoking write commits; revoked rows are retained as history (state flip, not row deletion).
  5. 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.
  6. 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

  1. The §3–§5 schemas are mode-independent: Standalone and Enterprise use the same tables and the same routing rule.
  2. 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.
  3. 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:

  1. 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.
  2. 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.
  3. Fail-closed classification witness: an answer in an unregistered category routes as sensitive.
  4. Opacity witness: pointer rows for seeded sensitive fixtures contain no fixture text in any column.
  5. Default-deny witness: an agent grantee with no consent row is refused; with a granted row for category A only, access to category B is refused.
  6. Revocation witness: after revocation commits, the next access evaluation refuses; the revoked row persists as history.
  7. Self-access witness: the data subject reads their own content without consent rows.
  8. Deletion witness: deleting sensitive content removes its pointer; no path re-materializes content into the database.

Ruling request

Ratify sections 17 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.