docs(custody): revision 2 — classifier registry, grantee model, exact schemas, witness closure (sol r1 F1-F6)
ci/woodpecker/pr/ci Pipeline is running
ci/woodpecker/pr/ci Pipeline is running
This commit is contained in:
+348
-110
@@ -11,161 +11,399 @@ data store subject to the custody rule; connectors carry granular
|
|||||||
agentic-access consent. PRD D6 — estate brains hold operational records;
|
agentic-access consent. PRD D6 — estate brains hold operational records;
|
||||||
only product-relevant material migrates into repository docs.
|
only product-relevant material migrates into repository docs.
|
||||||
|
|
||||||
This contract binds the sensitive-category registry (§2), the custody
|
Revision 2 (sol r1 findings F1–F6): the registry is now a full profile
|
||||||
|
classifier that can represent non-sensitive rows, with an exact row
|
||||||
|
shape, versioning, refusal semantics for unknown keys, and transition
|
||||||
|
rules (F1); the consent model gains a concrete grantee reference model,
|
||||||
|
an active-row uniqueness constraint, append-only re-grant semantics,
|
||||||
|
and explicit mutation authority (F2); the pointer schema names its
|
||||||
|
exact column set, binds the one-pointer rule to a database constraint,
|
||||||
|
defines the `brain_ref` grammar and owner-bound resolution, replaces
|
||||||
|
the unkeyed hash with a keyed construction, and bounds orphan repair
|
||||||
|
(F3); the witnesses import the hierarchy contract's §6.2 column
|
||||||
|
allowlist and §6.3-style closed route inventories, add negative
|
||||||
|
controls and a column-type allowlist, and cover every binding rule
|
||||||
|
(F4); the Standalone physical split, both valid layouts, the election
|
||||||
|
record, and v1 phase timing are defined consistently with the wizard
|
||||||
|
and mode-conversion contracts (F5); drafting additions are disclosed in
|
||||||
|
§8 and the ruling request is one sentence with one decision (F6).
|
||||||
|
|
||||||
|
This contract binds the profile-category registry (§2), the custody
|
||||||
placement rule (§3), the pointer schema (§4), the consent schema and its
|
placement rule (§3), the pointer schema (§4), the consent schema and its
|
||||||
evaluation (§5), mode application (§6), and witnesses (§7). It defines
|
evaluation (§5), mode application (§6), witnesses (§7), and disclosed
|
||||||
schemas and placement; wizard step flow stays with contract 3, mode and
|
drafting additions (§8). It defines schemas and placement; wizard step
|
||||||
conversion with contract 6 (`mode-conversion.md`), identity with
|
flow stays with contract 3 (`onboarding-wizard.md`), mode and conversion
|
||||||
|
with contract 6 (`mode-conversion.md`), identity with
|
||||||
`identity-lifecycle.md`, tool mapping with `tool-gateway-mapping.md`.
|
`identity-lifecycle.md`, tool mapping with `tool-gateway-mapping.md`.
|
||||||
|
|
||||||
## 1. Definitions
|
## 1. Definitions
|
||||||
|
|
||||||
1. **User brain**: the git-tracked per-user data store (in Standalone,
|
1. **User brain**: the git-tracked per-user data store. Its two valid
|
||||||
the user-files region of the single mosaic-brain; in Enterprise, the
|
Standalone layouts are defined in §6.2; in Enterprise it is the
|
||||||
user's own brain repository).
|
user's own brain repository.
|
||||||
2. **Sensitive content**: any profile answer or derived text in a §2
|
2. **Sensitive content**: any profile answer or derived text in a §2
|
||||||
sensitive category.
|
category classified `sensitive`.
|
||||||
3. **Pointer**: a database record referencing sensitive content that
|
3. **Pointer**: a database record referencing sensitive content that
|
||||||
lives in a user brain, carrying no content (§4).
|
lives in a user brain, carrying no content (§4).
|
||||||
4. **Consent record**: a database record granting a named grantee scope
|
4. **Grantee**: a non-subject principal that may be granted access to a
|
||||||
access to a category of a user's data (§5).
|
user's sensitive content: an enrolled agent, a connector, or a
|
||||||
|
platform feature (§5.2).
|
||||||
|
5. **Consent record**: a database record granting one grantee access to
|
||||||
|
one category of one user's data (§5).
|
||||||
|
6. **Registry version**: the monotonically increasing integer
|
||||||
|
identifying the active state of the §2 registry.
|
||||||
|
7. **Physical split** (Standalone): sensitive user content living in a
|
||||||
|
per-user brain repository separate from the estate mosaic-brain, as
|
||||||
|
opposed to the unsplit layout where it lives in a dedicated
|
||||||
|
user-files subtree of the single mosaic-brain (§6.2).
|
||||||
|
|
||||||
## 2. Sensitive-category registry
|
## 2. Profile-category registry
|
||||||
|
|
||||||
1. The registry is a closed, versioned list in the platform database;
|
1. **Model.** The registry is the single classifier for every profile
|
||||||
entries are added or reclassified by amendment to this contract,
|
category, closed and versioned, in the platform database. Table
|
||||||
never ad hoc.
|
`profile_category_registry`, columns exactly:
|
||||||
2. Initial registry, drawn from the D4 profile step and D14's
|
|
||||||
"disabilities, family, communication style, and similar":
|
|
||||||
|
|
||||||
| Category key | Covers |
|
| Column | Type | Constraints |
|
||||||
| --------------------- | ------------------------------------------------------------------- |
|
| ---------------- | ----------- | ---------------------------------------------------- |
|
||||||
| `disabilities` | disabilities including ADHD/autism/PDA/vision |
|
| `category_key` | text | primary key |
|
||||||
| `family-social` | family, pets, friends |
|
| `classification` | text | NOT NULL, CHECK in (`sensitive`, `non-sensitive`) |
|
||||||
| `communication-style` | desired agent communication style, voice-matching interview product |
|
| `since_version` | integer | NOT NULL (registry version that introduced this row) |
|
||||||
| `personal-interests` | hobbies, likes/dislikes |
|
| `created_at` | timestamptz | NOT NULL |
|
||||||
| `connector-content` | email and drive content reached through user connectors |
|
|
||||||
|
|
||||||
3. **Fail-closed classification.** A profile category not in the
|
The current registry version is a single integer held in
|
||||||
registry is treated as sensitive until an amendment classifies it.
|
`custody_config` (§6.3). Rows are added or reclassified ONLY by
|
||||||
Non-sensitive by classification (not by default) are structural
|
amendment to this contract shipped as a migration that bumps the
|
||||||
fields the platform needs relationally: e.g. professional
|
registry version; no runtime write path may insert, update, or
|
||||||
background/education summaries used for agent configuration MAY be
|
delete registry rows.
|
||||||
classified non-sensitive by the ruling below; account identity
|
|
||||||
fields (email, name, credentials) are identity-contract data, not
|
2. **Initial registry (version 1).** Drawn from the D4 profile step and
|
||||||
profile custody data.
|
D14's "disabilities, family, communication style, and similar":
|
||||||
|
|
||||||
|
| Category key | Classification | Covers |
|
||||||
|
| ------------------------- | -------------------------- | ------------------------------------------------------------------- |
|
||||||
|
| `disabilities` | sensitive | disabilities including ADHD/autism/PDA/vision |
|
||||||
|
| `family-social` | sensitive | family, pets, friends |
|
||||||
|
| `communication-style` | sensitive | desired agent communication style, voice-matching interview product |
|
||||||
|
| `personal-interests` | sensitive | hobbies, likes/dislikes |
|
||||||
|
| `connector-content` | sensitive | email and drive content reached through user connectors |
|
||||||
|
| `professional-background` | non-sensitive (per ruling) | professional background summary used for agent configuration |
|
||||||
|
| `education` | non-sensitive (per ruling) | education summary used for agent configuration |
|
||||||
|
|
||||||
|
The `Covers` column is contract documentation, not a database
|
||||||
|
column. Account identity fields (email, name, credentials) are
|
||||||
|
identity-contract data, not profile custody data, and have no
|
||||||
|
registry row.
|
||||||
|
|
||||||
|
3. **Unknown category — refusal.** A profile write naming a
|
||||||
|
`category_key` with no registry row is REFUSED with an explicit
|
||||||
|
error. Nothing is stored anywhere, no registry row is auto-added
|
||||||
|
(§2.1), and no pointer is created (so the §4.1 foreign key is never
|
||||||
|
asked to reference a missing row). Fail-closed means refusal, not
|
||||||
|
silent routing.
|
||||||
|
4. **Reclassification transitions.** A reclassification ships as a
|
||||||
|
contract amendment plus migration that bumps the registry version.
|
||||||
|
Non-sensitive → sensitive: the same migration moves every existing
|
||||||
|
relational value for that category into its owning user's brain,
|
||||||
|
creates the pointers, and deletes the relational values, all before
|
||||||
|
the new version activates. Sensitive → non-sensitive: existing
|
||||||
|
brain content and pointers remain valid and are never automatically
|
||||||
|
materialized into the database; only writes evaluated after the new
|
||||||
|
version activates route relationally.
|
||||||
|
5. **Unreadable registry — refusal.** If the registry or its version
|
||||||
|
cannot be read at decision time, every routing and consent decision
|
||||||
|
that depends on it is refused. There is no cached-default or
|
||||||
|
assume-sensitive fallback that performs a write.
|
||||||
|
|
||||||
## 3. Custody placement rule
|
## 3. Custody placement rule
|
||||||
|
|
||||||
1. Sensitive content is written to the owning user's brain ONLY.
|
1. Sensitive content is written to the owning user's brain ONLY.
|
||||||
PostgreSQL tables MUST NOT store sensitive content in any column —
|
PostgreSQL tables MUST NOT store sensitive content in any column —
|
||||||
not as text, not as excerpts or previews, not as embeddings or
|
not as text, not as excerpts or previews, not as encodings, and not
|
||||||
other derived representations that reconstruct content.
|
as embeddings or other derived representations that reconstruct
|
||||||
|
content.
|
||||||
2. The database MAY hold, about sensitive content: the pointer records
|
2. The database MAY hold, about sensitive content: the pointer records
|
||||||
of §4, the consent records of §5, and the category registry of §2.
|
of §4, the consent records of §5, the registry of §2, and the
|
||||||
Nothing else.
|
custody configuration of §6.3. Nothing else.
|
||||||
3. Every write path for profile answers routes by category: sensitive
|
3. Every write path for profile answers routes by the registry:
|
||||||
→ brain write + pointer upsert; non-sensitive → its declared
|
`sensitive` → brain write + pointer upsert; `non-sensitive` → its
|
||||||
platform table. The routing decision is made server-side from the
|
declared platform table; unknown → refusal (§2.3). The routing
|
||||||
registry, never by the client.
|
decision is made server-side from the registry at its current
|
||||||
|
version; a client-supplied classification or routing override is
|
||||||
|
ignored.
|
||||||
4. D6 boundary: operational records stay in estate brains and are
|
4. D6 boundary: operational records stay in estate brains and are
|
||||||
linked, not migrated. This contract governs user-profile custody
|
linked, not migrated. This contract governs user-profile custody
|
||||||
only and creates no new obligation on estate brains.
|
only and creates no new obligation on estate brains.
|
||||||
|
|
||||||
## 4. Pointer schema
|
## 4. Pointer schema
|
||||||
|
|
||||||
A pointer row carries exactly:
|
1. **Exact columns.** Table `profile_pointers`, columns exactly:
|
||||||
|
|
||||||
|
| Column | Type | Constraints |
|
||||||
|
| ---------------- | ----------- | ----------------------------------------------------------- |
|
||||||
|
| `id` | uuid | primary key |
|
||||||
|
| `user_id` | uuid | NOT NULL, FK → users(id) ON DELETE CASCADE |
|
||||||
|
| `category_key` | text | NOT NULL, FK → profile_category_registry(category_key) |
|
||||||
|
| `brain_ref` | text | NOT NULL, CHECK against the §4.3 grammar |
|
||||||
|
| `content_hash` | text | NOT NULL (§4.4 construction) |
|
||||||
|
| `created_at` | timestamptz | NOT NULL |
|
||||||
|
| `updated_at` | timestamptz | NOT NULL |
|
||||||
|
| `audit_event_id` | uuid | NOT NULL, FK → the platform audit log table (audit linkage) |
|
||||||
|
|
||||||
|
plus the database constraint UNIQUE (`user_id`, `category_key`,
|
||||||
|
`brain_ref`) — the one-pointer rule is a constraint, not a
|
||||||
|
convention.
|
||||||
|
|
||||||
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
|
2. **Opacity.** `brain_ref` and every other pointer column MUST NOT
|
||||||
embed content or content-derived text (no titles, snippets, or
|
embed content or content-derived text (no titles, snippets, or
|
||||||
free-text descriptions). A locator is structural (category + path
|
free-text descriptions). A locator is structural, not descriptive.
|
||||||
discipline), not descriptive.
|
3. **`brain_ref` grammar and owner binding.** `brain_ref` is a
|
||||||
3. One pointer per (user, category, brain_ref); pointers are deleted
|
normalized repository-relative POSIX path: one or more segments
|
||||||
when their content is deleted (dangling pointers are repaired
|
matching `[A-Za-z0-9][A-Za-z0-9._-]*`, joined by `/`, with no
|
||||||
toward deletion, never toward re-creating content in the DB).
|
leading `/`, no empty segment, and no `.` or `..` segment; the
|
||||||
|
stored form matches
|
||||||
|
`^[A-Za-z0-9][A-Za-z0-9._-]*(/[A-Za-z0-9][A-Za-z0-9._-]*)*$`.
|
||||||
|
Resolution ALWAYS roots at the brain owned by the row's `user_id`
|
||||||
|
(the resolver takes the owner from the row, never from the
|
||||||
|
locator); the locator carries no repository, host, or user
|
||||||
|
component, so a cross-user or traversal reference is
|
||||||
|
unrepresentable, not merely forbidden.
|
||||||
|
4. **`content_hash` construction.** `content_hash` is
|
||||||
|
`hmac-sha256:<hex>` — HMAC-SHA-256 over the canonical content
|
||||||
|
bytes, keyed with a platform integrity key held in the secrets
|
||||||
|
backend and never stored in the database or any repository. Because
|
||||||
|
the key is external, the stored value is not an offline dictionary
|
||||||
|
oracle for low-entropy answers. On read, a hash mismatch refuses
|
||||||
|
the read and flags the pointer for §4.5 reconciliation.
|
||||||
|
5. **Bounded orphan repair.** Pointers are deleted when their content
|
||||||
|
is deleted; dangling pointers are repaired toward deletion, never
|
||||||
|
toward re-creating content in the database. Reconciliation for a
|
||||||
|
user's pointers runs on two triggers: every profile write for that
|
||||||
|
user, and a periodic sweep whose interval the implementing PR
|
||||||
|
declares (at most daily). A pointer whose content is absent is
|
||||||
|
deleted by the next triggered reconciliation — an orphan survives
|
||||||
|
at most one cycle, and repair performs no database content write.
|
||||||
|
|
||||||
## 5. Consent schema and evaluation
|
## 5. Consent schema and evaluation
|
||||||
|
|
||||||
1. A consent row carries exactly: `id`, `user_id` (the data subject),
|
1. **Exact columns.** Table `profile_consents`, columns exactly:
|
||||||
`grantee_scope` (a typed reference: an enrolled agent, a connector,
|
|
||||||
or a platform feature — closed enum of grantee types), `category_key`
|
| Column | Type | Constraints |
|
||||||
(§2 FK), `state` (`granted` | `revoked`), `granted_at`/`revoked_at`,
|
| ---------------- | ----------- | --------------------------------------------------------------- |
|
||||||
`actor` (who recorded the choice), and audit linkage.
|
| `id` | uuid | primary key |
|
||||||
2. **Default deny.** Absence of a `granted` consent row for (user,
|
| `user_id` | uuid | NOT NULL, FK → users(id) ON DELETE CASCADE (the data subject) |
|
||||||
grantee scope, category) means no access. There are no implicit
|
| `grantee_type` | text | NOT NULL, CHECK in (`agent`, `connector`, `feature`) |
|
||||||
grants, no platform-admin bypass, and no mode in which default-deny
|
| `grantee_id` | text | NOT NULL (§5.2 per-type referential integrity) |
|
||||||
is suspended.
|
| `category_key` | text | NOT NULL, FK → profile_category_registry(category_key) |
|
||||||
3. **Granularity.** Consent is per grantee scope × category (the D4
|
| `state` | text | NOT NULL, CHECK in (`granted`, `revoked`) |
|
||||||
"granular agentic-access consent"). A grant to one agent or
|
| `granted_at` | timestamptz | NOT NULL |
|
||||||
connector confers nothing on another.
|
| `revoked_at` | timestamptz | CHECK ((state = 'granted') = (revoked_at IS NULL)) |
|
||||||
4. **Revocation.** Revocation is effective for every access evaluated
|
| `actor` | text | NOT NULL (the authenticated principal that recorded the change) |
|
||||||
after the revoking write commits; revoked rows are retained as
|
| `audit_event_id` | uuid | NOT NULL, FK → the platform audit log table |
|
||||||
history (state flip, not row deletion).
|
|
||||||
5. **Evaluation placement.** Access to sensitive content is mediated by
|
plus the partial unique index UNIQUE (`user_id`, `grantee_type`,
|
||||||
|
`grantee_id`, `category_key`) WHERE `state = 'granted'` — at most
|
||||||
|
one active grant per (user, concrete grantee, category), as a
|
||||||
|
database constraint.
|
||||||
|
|
||||||
|
2. **Grantee reference model.** A grantee is identified by the pair
|
||||||
|
(`grantee_type`, `grantee_id`); its canonical display form is
|
||||||
|
`<grantee_type>:<grantee_id>` (e.g. `agent:<agent id>`). The
|
||||||
|
discriminant set is closed at the three CHECK values. Referential
|
||||||
|
integrity is per type: `agent` ids reference the enrolled-agent
|
||||||
|
registry of `identity-lifecycle.md`; `connector` ids reference the
|
||||||
|
platform connector registry; `feature` ids reference a closed
|
||||||
|
feature-key list owned by amendments to this contract, and that
|
||||||
|
list is EMPTY at version 1 (no feature grantee exists until an
|
||||||
|
amendment names one). A grant to one agent confers nothing on
|
||||||
|
another agent of the same type; the constraint key includes
|
||||||
|
`grantee_id`, so two same-type grantees are distinct rows.
|
||||||
|
3. **Default deny.** Absence of an active `granted` row for (user,
|
||||||
|
grantee, category) means no access. There are no implicit grants,
|
||||||
|
no platform-admin bypass, and no mode in which default-deny is
|
||||||
|
suspended.
|
||||||
|
4. **Mutation authority.** Only the authenticated data subject may
|
||||||
|
create, grant, or revoke consent rows for their own data: the
|
||||||
|
server enforces that `actor` is the subject's principal and equals
|
||||||
|
the row's `user_id` before any consent mutation commits. A platform
|
||||||
|
admin has no consent-mutation capability over another user's rows —
|
||||||
|
an admin self-grant is refused at write time, closing the
|
||||||
|
write-side route around §5.3. One system exception (disclosed,
|
||||||
|
§8): when a grantee ceases to exist (e.g. agent retirement under
|
||||||
|
`identity-lifecycle.md`), the platform auto-revokes its active
|
||||||
|
rows, recording a system principal as `actor`.
|
||||||
|
5. **Revocation and re-grant.** Revocation flips exactly one active
|
||||||
|
row to `revoked` and stamps `revoked_at`; it is effective for every
|
||||||
|
access evaluated after the revoking write commits. Revoked rows are
|
||||||
|
retained as history and never mutated again. A re-grant after
|
||||||
|
revocation inserts a NEW row (append-only history) — repeated
|
||||||
|
grant/revoke cycles are represented as successive rows, and the
|
||||||
|
§5.1 partial unique index guarantees the old revoked rows cannot
|
||||||
|
keep access live.
|
||||||
|
6. **Evaluation placement.** Access to sensitive content is mediated by
|
||||||
the platform (Gateway/tooling) evaluating consent before any brain
|
the platform (Gateway/tooling) evaluating consent before any brain
|
||||||
read on behalf of a grantee; the evaluation fails closed
|
read on behalf of a grantee; the evaluation fails closed
|
||||||
(`rbac-grant-model.md` §3.5 pattern). The user reading their own
|
(`rbac-grant-model.md` §3.5 pattern), including when the consent
|
||||||
|
state cannot be read (§2.5 pattern). The user reading their own
|
||||||
data is not a grantee and needs no consent row.
|
data is not a grantee and needs no consent row.
|
||||||
6. Consent records govern agentic/feature access to user data. They are
|
7. Consent records govern agentic/feature access to user data. They are
|
||||||
distinct from hierarchy grants (contract 2) and confer no
|
distinct from hierarchy grants (contract 2) and confer no platform
|
||||||
platform authorization.
|
authorization.
|
||||||
|
|
||||||
## 6. Mode application
|
## 6. Mode application
|
||||||
|
|
||||||
1. The §3–§5 schemas are mode-independent: Standalone and Enterprise
|
1. The §2–§5 schemas are mode-independent: Standalone and Enterprise
|
||||||
use the same tables and the same routing rule.
|
use the same tables and the same routing rule.
|
||||||
2. In Standalone, D14 makes the physical split a MAY. This contract
|
2. **Standalone layouts.** The D14 physical split remains a MAY. Its
|
||||||
keeps it a MAY and binds the recommended default: a fresh v1
|
two valid layouts are: **split** — sensitive user content in a
|
||||||
Standalone install routes sensitive content per §3 from the start,
|
per-user brain repository separate from the estate mosaic-brain
|
||||||
so the Enterprise conversion precondition (`mode-conversion.md`
|
(the recommended default); **unsplit** — sensitive user content in
|
||||||
§4.2, brains) is already satisfied. An operator electing not to
|
the dedicated user-files subtree `users/<user id>/` of the single
|
||||||
keep the split accepts the resulting conversion-time partitioning
|
mosaic-brain. §3 binds the logical user-brain region identically in
|
||||||
work; the election is recorded.
|
both layouts; the layout election changes where the region lives,
|
||||||
3. In Enterprise, the split is mandatory (D3 table); no-leakage between
|
never whether routing applies.
|
||||||
|
3. **Election record.** The election lives in this contract's own
|
||||||
|
one-row table `custody_config`, columns exactly: `id` (uuid,
|
||||||
|
primary key), `standalone_layout` (text, NOT NULL, CHECK in
|
||||||
|
(`split`, `unsplit`), default `split`), `registry_version`
|
||||||
|
(integer, NOT NULL, §2.1), `elected_at` (timestamptz, NOT NULL),
|
||||||
|
`actor` (text, NOT NULL), `audit_event_id` (uuid, NOT NULL, FK →
|
||||||
|
the platform audit log table). It is written at bootstrap and
|
||||||
|
amended only by an explicit operator action; contract 6's exact,
|
||||||
|
immutable mode record is not touched or extended by this contract.
|
||||||
|
4. **Phase timing.** v1 ships the §2–§6 schemas and the D14 database
|
||||||
|
boundary, and the wizard collects no sensitive category in v1
|
||||||
|
(contract 3 §3), so v1 contains no sensitive write surface. §3
|
||||||
|
binds every sensitive write path from the moment one exists — the
|
||||||
|
first profile surface that accepts a sensitive category (P2/P3)
|
||||||
|
activates routing, consistent with contract 6 §5's v1 slice (D14
|
||||||
|
database boundary only, custody mechanics outside v1).
|
||||||
|
5. **Conversion precondition.** An operator electing `unsplit` accepts
|
||||||
|
conversion-time partitioning: `mode-conversion.md` §4.2 requires
|
||||||
|
the per-user partition to exist before the Enterprise flip, so
|
||||||
|
conversion from an unsplit install performs the partitioning first.
|
||||||
|
6. In Enterprise, the split is mandatory (D3 table); no-leakage between
|
||||||
users is enforced by §3 placement plus §5 default-deny — there is
|
users is enforced by §3 placement plus §5 default-deny — there is
|
||||||
no cross-user read path to sensitive content through the database,
|
no cross-user read path to sensitive content through the database,
|
||||||
because the database has no content to serve.
|
because the database has no content to serve.
|
||||||
|
|
||||||
## 7. Verification requirements
|
## 7. Verification requirements
|
||||||
|
|
||||||
Binding on the implementing PRs:
|
Binding on the implementing PRs. Every witness below MUST name, in its
|
||||||
|
implementation, the exact tables, columns, commands, and source roots
|
||||||
|
it scans; "the custody tables" means `profile_category_registry`,
|
||||||
|
`profile_pointers`, `profile_consents`, and `custody_config`.
|
||||||
|
|
||||||
1. **No-content witness:** a column-allowlist assertion (contract 1
|
1. **Column-allowlist witness** (hierarchy contract §6.2 style): the
|
||||||
§6.3 style) that the pointer and consent tables' column sets are
|
custody tables' live column sets are exactly §2.1/§4.1/§5.1/§6.3,
|
||||||
exactly §4.1/§5.1, and that no platform table outside the declared
|
and no platform table outside the declared non-sensitive profile
|
||||||
non-sensitive profile tables carries profile answer content.
|
tables carries profile answer content.
|
||||||
2. **Routing witness:** a sensitive-category answer submitted through
|
2. **Column-type allowlist witness:** the custody tables use only the
|
||||||
|
column types named in §2.1/§4.1/§5.1/§6.3 (uuid, text, integer,
|
||||||
|
timestamptz) — no bytea, json/jsonb, array, vector, or tsvector
|
||||||
|
column exists in them, closing the encoded/derived-representation
|
||||||
|
routes by type rather than by probe alone.
|
||||||
|
3. **Closed write-route witness** (hierarchy contract §6.3 style): a
|
||||||
|
static, re-export-aware inventory over `apps/` and `packages/`
|
||||||
|
(production code, tests excluded) enumerates every module that
|
||||||
|
writes the custody tables or writes profile answers, and every
|
||||||
|
enumerated route implements §3.3 registry routing; a route outside
|
||||||
|
the enumeration fails the assertion.
|
||||||
|
4. **Closed brain-read witness** (same style): the inventory enumerates
|
||||||
|
every production route that reads user-brain content on behalf of a
|
||||||
|
grantee, and every enumerated route calls the §5.6 consent
|
||||||
|
evaluation; a brain-read route outside the enumeration fails.
|
||||||
|
5. **Routing witness:** a sensitive-category answer submitted through
|
||||||
the profile surface results in a brain write plus a pointer row and
|
the profile surface results in a brain write plus a pointer row and
|
||||||
zero content bytes in the database (asserted by content-hash
|
zero content bytes in the database; a non-sensitive answer lands in
|
||||||
presence in the brain and absence of the plaintext in any DB
|
its declared table. The no-content probe is a negative control: the
|
||||||
column); a non-sensitive answer lands in its declared table.
|
witness first plants the fixture text in a scratch column of a
|
||||||
3. **Fail-closed classification witness:** an answer in an unregistered
|
throwaway table to prove the probe detects it, then asserts its
|
||||||
category routes as sensitive.
|
absence — as plaintext, base64, hex, and JSON-string encodings —
|
||||||
4. **Opacity witness:** pointer rows for seeded sensitive fixtures
|
across every column of the custody tables and declared profile
|
||||||
contain no fixture text in any column.
|
tables.
|
||||||
5. **Default-deny witness:** an agent grantee with no consent row is
|
6. **Registry witnesses:** (a) the version-1 registry state is exactly
|
||||||
refused; with a `granted` row for category A only, access to
|
the seven §2.2 rows with their classifications; (b) a
|
||||||
category B is refused.
|
`professional-background` answer routes relationally (or per the
|
||||||
6. **Revocation witness:** after revocation commits, the next access
|
ruling's alternative); (c) an unknown `category_key` is refused
|
||||||
evaluation refuses; the revoked row persists as history.
|
with nothing stored (§2.3); (d) with the registry unreadable, the
|
||||||
7. **Self-access witness:** the data subject reads their own content
|
write is refused (§2.5); (e) a runtime insert/update/delete against
|
||||||
without consent rows.
|
`profile_category_registry` outside a migration is refused (§2.1);
|
||||||
8. **Deletion witness:** deleting sensitive content removes its
|
(f) a reclassification migration (non-sensitive → sensitive) on
|
||||||
pointer; no path re-materializes content into the database.
|
seeded data moves the values to brains, creates pointers, and
|
||||||
|
leaves zero relational values (§2.4).
|
||||||
|
7. **Server-side classification witness:** a client-supplied
|
||||||
|
classification or routing override on a profile write is ignored;
|
||||||
|
the registry decision is applied (§3.3).
|
||||||
|
8. **Pointer-constraint witnesses:** inserting a second pointer for the
|
||||||
|
same (user, category, brain_ref) violates the §4.1 unique
|
||||||
|
constraint; a `brain_ref` failing the §4.3 grammar (leading `/`,
|
||||||
|
`..` segment, empty segment) is rejected by the CHECK; resolution
|
||||||
|
of a valid `brain_ref` under user A's row never reads user B's
|
||||||
|
brain (owner binding, §4.3).
|
||||||
|
9. **Hash witnesses:** `content_hash` verifies via the keyed §4.4
|
||||||
|
construction; a mismatch refuses the read and flags the pointer;
|
||||||
|
the database value alone, without the external key, does not equal
|
||||||
|
any unkeyed digest of the fixture content (oracle control).
|
||||||
|
10. **Orphan witnesses:** starting from a PRE-EXISTING orphan (content
|
||||||
|
already absent, pointer present), the next triggered
|
||||||
|
reconciliation deletes the pointer and writes no content anywhere
|
||||||
|
in the database (§4.5); a managed deletion removes its pointer in
|
||||||
|
the same transaction.
|
||||||
|
11. **Default-deny and granularity witnesses:** an agent grantee with
|
||||||
|
no active row is refused; with a `granted` row for category A
|
||||||
|
only, category B is refused; with agent X granted, agent Y of the
|
||||||
|
same type is refused for the same (user, category); each of the
|
||||||
|
three grantee types is exercised — `feature` via its refusal path,
|
||||||
|
since the version-1 feature list is empty (§5.2).
|
||||||
|
12. **Mutation-authority witnesses:** a platform admin attempting to
|
||||||
|
create a grant on another user's data is refused at write time; a
|
||||||
|
hierarchy owner or manager likewise; the data subject succeeds;
|
||||||
|
`actor` equals the subject's principal on every committed row
|
||||||
|
(§5.4).
|
||||||
|
13. **Revocation/re-grant witnesses:** after revocation commits, the
|
||||||
|
next evaluation refuses and the revoked row persists unmutated; a
|
||||||
|
full grant → revoke → re-grant cycle yields two rows (one
|
||||||
|
revoked, one active) and access follows only the active row; a
|
||||||
|
second concurrent grant attempt for the same key violates the
|
||||||
|
§5.1 partial unique index (§5.5).
|
||||||
|
14. **Self-access witness:** the data subject reads their own content
|
||||||
|
without consent rows (§5.6).
|
||||||
|
15. **Mode witnesses:** the custody-table schemas are byte-identical
|
||||||
|
under Standalone and Enterprise migrations (§6.1); a fresh
|
||||||
|
Standalone bootstrap records `standalone_layout = 'split'` by
|
||||||
|
default, and an explicit opt-out records `unsplit` with actor and
|
||||||
|
audit linkage (§6.3); conversion from an `unsplit` install refuses
|
||||||
|
the mode flip until partitioning has produced the per-user region
|
||||||
|
(§6.5, with contract 6 §4.2); in an Enterprise fixture with two
|
||||||
|
users, user A's grantee with a grant on user A cannot reach any of
|
||||||
|
user B's content (§6.6).
|
||||||
|
|
||||||
|
## 8. Drafting additions (PRD §12.1 disclosure)
|
||||||
|
|
||||||
|
The following are proposed drafting additions, visible here for
|
||||||
|
ratification; none is claimed as a PRD mandate, and each is severable:
|
||||||
|
|
||||||
|
1. The `feature` grantee type, with a closed feature-key list that is
|
||||||
|
empty at version 1 (§5.2).
|
||||||
|
2. Append-only consent history: re-grants insert new rows; revoked
|
||||||
|
rows are retained unmutated (§5.5).
|
||||||
|
3. System-actor auto-revocation when a grantee ceases to exist (§5.4).
|
||||||
|
4. The `custody_config` election record for the Standalone layout
|
||||||
|
(§6.3).
|
||||||
|
5. The keyed `content_hash` construction and its mismatch handling
|
||||||
|
(§4.4).
|
||||||
|
6. The bounded dangling-pointer reconciliation policy (§4.5).
|
||||||
|
7. The column-type allowlist verification requirement (§7.2).
|
||||||
|
|
||||||
|
The revision-1 "reporting" rationale for relational
|
||||||
|
background/education storage is withdrawn; the traced rationale is
|
||||||
|
agent configuration (PRD D4).
|
||||||
|
|
||||||
## Ruling request
|
## Ruling request
|
||||||
|
|
||||||
Ratify sections 1–7 as written, with one decision embedded:
|
Ruling requested (one decision): classify `professional-background`
|
||||||
|
and `education` as **non-sensitive** in the version-1 registry (stored
|
||||||
- Decision (§2): the initial sensitive-category registry is the five
|
relationally, used for agent configuration) — or, as the alternative,
|
||||||
rows of §2.2, with fail-closed classification for anything
|
classify both **sensitive** (user-brain custody with pointers),
|
||||||
unregistered, and with professional background/education classified
|
accepting that agent-configuration reads then go through pointer
|
||||||
**non-sensitive** (they exist to configure agents and reporting and
|
indirection and consent evaluation?
|
||||||
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.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user