- user FKs text not uuid (users.id is BetterAuth text; matches
hierarchy_grants shape)
- actor receipt split from canonical audit event: receipt carries
{target id, old, new}; canonical event keeps hierarchy schema §5.2's
full immutable snapshot; no audit read conferred; §8.6.3 witnesses
both sides
- kanban SOT Amendment A3 (§10) expressly extends A2's sole-disclosure
rule by exactly the capability-holder carve-out; ruling request names
it
This commit is contained in:
@@ -514,3 +514,47 @@ itself permits, so that contract does not stretch A1 by interpretation.
|
|||||||
default-open disclosure.
|
default-open disclosure.
|
||||||
5. Every other constraint of A1 — §8.1.2's remaining bullets, §8.2 in full,
|
5. Every other constraint of A1 — §8.1.2's remaining bullets, §8.2 in full,
|
||||||
and §8.3's other acceptance criteria — is untouched.
|
and §8.3's other acceptance criteria — is untouched.
|
||||||
|
|
||||||
|
## 10. Amendment A3 — capability-holder existence disclosure
|
||||||
|
|
||||||
|
**Status:** amendment to Amendment A2, added by reviewed PR together with
|
||||||
|
contract 2 Amendment 1 (`rbac-grant-model.md` §8, this PR), under that
|
||||||
|
amendment's ruling request (decision owner Jason). It binds if and only if
|
||||||
|
contract 2 Amendment 1 ratifies; until then §9.1.2's sole-disclosure rule
|
||||||
|
stands unmodified — which is consistent, because until ratification the
|
||||||
|
company-CRUD capability class is empty and the carve-out below has no
|
||||||
|
holders. Everything in §§1–9 remains binding verbatim, with exactly the one
|
||||||
|
express modification below. The detailed contract text lives in
|
||||||
|
`rbac-grant-model.md` §8.1; this amendment changes only what A2 itself
|
||||||
|
permits, so that contract does not stretch A2 by interpretation.
|
||||||
|
|
||||||
|
### 10.1 What A3 modifies in A2
|
||||||
|
|
||||||
|
1. **Capability-holder disclosure (narrows §9.1.2's sole-disclosure rule
|
||||||
|
by one carve-out).** §9.1.2 makes the directory the sole permitted
|
||||||
|
existence disclosure and keeps private companies undisclosed to
|
||||||
|
non-granted subjects everywhere. A3 admits exactly one further
|
||||||
|
disclosure channel: a subject holding the company-CRUD capability
|
||||||
|
(contract 2 §8), when exercising the hierarchy schema §5.5 visibility
|
||||||
|
command, learns the target company's existence and its old/new
|
||||||
|
visibility values through the command's redacted actor receipt —
|
||||||
|
success for an existing target (private or directory alike) versus
|
||||||
|
`not_found` for a nonexistent id — bounded exactly as contract 2 §8.1
|
||||||
|
states: no name, slug, structure, content, grant, or membership
|
||||||
|
information, and no read command of any kind. To every other
|
||||||
|
non-granted subject, private companies remain undisclosed everywhere,
|
||||||
|
including the directory; the directory remains the sole
|
||||||
|
existence-disclosure _listing_.
|
||||||
|
|
||||||
|
### 10.2 What A3 explicitly does not change
|
||||||
|
|
||||||
|
1. The directory itself is unchanged: read-only, directory-class companies
|
||||||
|
only, existence/name/slug only (§9.1.2's enumeration is narrowed for
|
||||||
|
capability holders' receipts, widened for nothing).
|
||||||
|
2. No join-request surface, no curation listing, no read command of any
|
||||||
|
family is authorized (§9.2.2 unchanged; a curation listing is a further
|
||||||
|
amendment per contract 2 §8.1).
|
||||||
|
3. The canonical audit event for visibility mutations is untouched — it
|
||||||
|
keeps hierarchy schema §5.2's full immutable target snapshot; the
|
||||||
|
capability confers no audit read (contract 2 §8.5).
|
||||||
|
4. Every other constraint of A1 and A2 is untouched.
|
||||||
|
|||||||
@@ -30,7 +30,11 @@ anticipate. §8 defines the capability as a platform-scoped, admin-assigned,
|
|||||||
audited delegation of exactly the hierarchy schema §5.5 company visibility
|
audited delegation of exactly the hierarchy schema §5.5 company visibility
|
||||||
command — no read command, no other company operation; the mutation's
|
command — no read command, no other company operation; the mutation's
|
||||||
inherent existence disclosure is ratified as a bounded carve-out to
|
inherent existence disclosure is ratified as a bounded carve-out to
|
||||||
hierarchy schema §6.7/§2.8 (§8.1). The hierarchy role
|
hierarchy schema §6.7/§2.8 and to kanban SOT Amendment A2's
|
||||||
|
sole-disclosure rule — SOT Amendment A3 (native-kanban-sot.md §10, this
|
||||||
|
PR) expressly extends A2 by exactly this carve-out (§8.1). The holder
|
||||||
|
sees only a redacted actor receipt; the canonical audit event keeps
|
||||||
|
hierarchy schema §5.2's full immutable snapshot. The hierarchy role
|
||||||
vocabulary (§2), every evaluation rule (§3), and grant management (§4) are
|
vocabulary (§2), every evaluation rule (§3), and grant management (§4) are
|
||||||
untouched: the capability is not a `hierarchy_grants.role` value and
|
untouched: the capability is not a `hierarchy_grants.role` value and
|
||||||
evaluates outside the chain; capability-row deletion joins §6.1's
|
evaluates outside the chain; capability-row deletion joins §6.1's
|
||||||
@@ -292,15 +296,26 @@ further amendment, not an implementation decision.
|
|||||||
(`not_found`), so a holder presenting candidate ids learns existence —
|
(`not_found`), so a holder presenting candidate ids learns existence —
|
||||||
exactly as a platform admin already does through the same command.
|
exactly as a platform admin already does through the same command.
|
||||||
This amendment ratifies that disclosure as part of the §5.5 curation
|
This amendment ratifies that disclosure as part of the §5.5 curation
|
||||||
authority, bounded as follows: the mutation result and its audit event
|
authority, bounded as follows. The holder-visible surface is the
|
||||||
disclose to the holder the target's existence and its old/new
|
command's **actor receipt** — the mutation result payload, carrying
|
||||||
visibility values, and **nothing else** — no name, slug, structure,
|
exactly the target id, old visibility, and new visibility, and
|
||||||
content, grant, or membership information. A successful publish
|
**nothing else**: no name, slug, structure, content, grant, or
|
||||||
additionally makes the target directory-listed to every authenticated
|
membership information. The actor receipt is a redacted projection
|
||||||
user; that is the command's ratified purpose (hierarchy schema §5.5),
|
distinct from the **canonical audit event**, which is unchanged by
|
||||||
not a leak. Hierarchy schema §6.7's existence-oracle rule and §2.8's
|
this amendment: it keeps hierarchy schema §5.2's deletion-safe
|
||||||
directory-only disclosure are amended by exactly this carve-out for
|
immutable target snapshot (id, slug, and parent chain at event time)
|
||||||
capability holders and are otherwise untouched. Witnessed in §8.6.3.
|
in full. The two never converge on the holder: the capability confers
|
||||||
|
no audit read (§8.5), so the canonical event — and with it the slug
|
||||||
|
and parent chain — is reachable only by subjects independently
|
||||||
|
authorized to read audit data, never through this capability. A
|
||||||
|
successful publish additionally makes the target directory-listed to
|
||||||
|
every authenticated user; that is the command's ratified purpose
|
||||||
|
(hierarchy schema §5.5), not a leak. Hierarchy schema §6.7's
|
||||||
|
existence-oracle rule and §2.8's directory-only disclosure are amended
|
||||||
|
by exactly this carve-out for capability holders, kanban SOT Amendment
|
||||||
|
A3 (native-kanban-sot.md §10, this PR) expressly extends A2's
|
||||||
|
sole-disclosure enumeration by the same carve-out, and all three are
|
||||||
|
otherwise untouched. Witnessed in §8.6.3.
|
||||||
|
|
||||||
2. **Holding: platform-scoped assignment, user subjects only.** The
|
2. **Holding: platform-scoped assignment, user subjects only.** The
|
||||||
capability is not a hierarchy grant: it attaches to no node, has no
|
capability is not a hierarchy grant: it attaches to no node, has no
|
||||||
@@ -308,13 +323,19 @@ further amendment, not an implementation decision.
|
|||||||
`platform_capabilities` table whose column set is exactly (nothing
|
`platform_capabilities` table whose column set is exactly (nothing
|
||||||
else, per the contract 1 §2.7 exhaustiveness discipline):
|
else, per the contract 1 §2.7 exhaustiveness discipline):
|
||||||
- `id` — uuid, primary key;
|
- `id` — uuid, primary key;
|
||||||
- `user_id` — uuid, NOT NULL, FK `users` **ON DELETE RESTRICT**;
|
- `user_id` — text, NOT NULL, FK `users` **ON DELETE RESTRICT**;
|
||||||
- `capability` — text, NOT NULL, constraint-checked against exactly
|
- `capability` — text, NOT NULL, constraint-checked against exactly
|
||||||
`company_crud`;
|
`company_crud`;
|
||||||
- `granted_by` — uuid, NOT NULL, FK `users` **ON DELETE RESTRICT**;
|
- `granted_by` — text, NOT NULL, FK `users` **ON DELETE RESTRICT**;
|
||||||
- `created_at` — timestamptz, NOT NULL;
|
- `created_at` — timestamptz, NOT NULL;
|
||||||
- UNIQUE (`user_id`, `capability`).
|
- UNIQUE (`user_id`, `capability`).
|
||||||
|
|
||||||
|
The user FKs are **text**, not uuid, because `users.id` is a BetterAuth
|
||||||
|
text key (`packages/db/src/schema.ts`; custody schema records the same)
|
||||||
|
— PostgreSQL cannot reference a text primary key with a uuid column.
|
||||||
|
This matches the shipped `hierarchy_grants` shape exactly: uuid
|
||||||
|
surrogate `id`, text FKs to `users`.
|
||||||
|
|
||||||
Both user FKs are RESTRICT for the same reason contract 1 §3.3 pins
|
Both user FKs are RESTRICT for the same reason contract 1 §3.3 pins
|
||||||
RESTRICT on principal FKs: the identity contract (§7.3) gates user
|
RESTRICT on principal FKs: the identity contract (§7.3) gates user
|
||||||
deletion, and a cascade here could silently destroy a capability
|
deletion, and a cascade here could silently destroy a capability
|
||||||
@@ -391,12 +412,19 @@ further amendment, not an implementation decision.
|
|||||||
create behaves exactly as any active user's — always `private`,
|
create behaves exactly as any active user's — always `private`,
|
||||||
and a creation request carrying a visibility argument is refused
|
and a creation request carrying a visibility argument is refused
|
||||||
for holder and baseline alike (contract 3 §5.2).
|
for holder and baseline alike (contract 3 §5.2).
|
||||||
3. Disclosure bound (§8.1 carve-out witnessed): the mutation result
|
3. Disclosure bound (§8.1 carve-out witnessed, receipt and canonical
|
||||||
for a private-valid target, a directory-valid target, and a
|
event separately): the mutation result for a private-valid target,
|
||||||
nonexistent id is exactly {success, success, `not_found`}; no
|
a directory-valid target, and a nonexistent id is exactly {success,
|
||||||
result or error payload carries name, slug, structure, content,
|
success, `not_found`}; the actor receipt for a success carries
|
||||||
grant, or membership data; the holder's audit-visible surface
|
exactly {target id, old visibility, new visibility} and no result
|
||||||
carries only existence and old/new visibility.
|
or error payload carries name, slug, structure, content, grant, or
|
||||||
|
membership data; the canonical audit event for the same mutation —
|
||||||
|
asserted directly against the hierarchy outbox, not through any
|
||||||
|
holder-facing surface — carries hierarchy schema §5.2's full
|
||||||
|
immutable snapshot (id, slug, parent chain); and the holder's
|
||||||
|
attempt to read audit data is refused (no audit read conferred,
|
||||||
|
§8.5), proving the receipt/event separation reaches the holder as
|
||||||
|
a redaction, not a weakened event.
|
||||||
4. Assignment path, both polarities: a platform admin assigns and
|
4. Assignment path, both polarities: a platform admin assigns and
|
||||||
revokes through the normal admin command (positive witnesses —
|
revokes through the normal admin command (positive witnesses —
|
||||||
assign then observe the §8.6.2 allow, revoke then observe deny); a
|
assign then observe the §8.6.2 allow, revoke then observe deny); a
|
||||||
@@ -437,7 +465,9 @@ interpretive resolution named:
|
|||||||
|
|
||||||
## Ruling request (Amendment 1)
|
## Ruling request (Amendment 1)
|
||||||
|
|
||||||
Ratify §8 and the Amendment 1 header note as written, with one decision
|
Ratify §8, the Amendment 1 header note, and kanban SOT Amendment A3
|
||||||
|
(native-kanban-sot.md §10 — the express A2 carve-out extension, which
|
||||||
|
binds only with this ratification) as written, with one decision
|
||||||
embedded:
|
embedded:
|
||||||
|
|
||||||
- Decision: the company-CRUD capability is a platform-scoped,
|
- Decision: the company-CRUD capability is a platform-scoped,
|
||||||
|
|||||||
Reference in New Issue
Block a user