You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Decision] sys_* objects silently default to PUBLIC row visibility — should the platform's own objects keep an unstated default at all, and is isSystem even the right predicate? #8641
Filed unassigned by the domain:spec PM seat (session session_012MNV7ZSCjNfA38eDCjsXQL) at the maintainer's direct instruction, 2026-08-14, during the #8310 shift. This is the carve-out #8310's ruling left behind, surfaced deliberately rather than allowed to evaporate with the conversation that found it.
Where this comes from
#8310 (PR #8600) ruled that absence is not a decision at the runtime object door: an active-state object publish with no authored sharingModel is now refused 422 security-owd-unset. The ordinary-object default (ADR-0094 D1, "absence resolves to private") was overturned, and the pin asserting a write with NO OWD keys SAVES was rewritten into the refusal.
That rule exempts system objects. Per the ADR-0094 amendment landed by the same PR:
a system object (isSystem / sys_*) with no authored sharingModel is effectively PUBLIC at runtime (effectiveSharingModel, plugin-sharing) … and security-owd-unset deliberately exempts system objects for the same reason.
So after #8310 the platform's silent defaults look like this:
The widest silent default in the platform is the one that survived.
Why the exemption's rationale does not settle the default
The exemption is correct as damage control, and its stated reason is sound: R2's hardcoded private baseline was refusing system-object configurations that are not external-wider at runtime — those were false positives, which is exactly why R2 was retired. ⚠️ But that reason establishes only that private was the wrong baseline; it does not establish that public is the right default. The two got merged into one carve-out without the second half being argued.
The two populations under one rule
sys_* is not one kind of thing, and a single default serves them very differently:
A — reference / configuration data. Positions, capabilities, permission-set definitions, settings, status vocabularies. The whole org must read these for the product to function (a picklist of positions, an approval screen, the permission admin UI). For these, row visibility is not the control surface at all — CRUD/FLS and the ADR-0086 two-doors gate are. public here is an honest statement of "this object does not defend itself by row visibility," and defaulting them private would break the platform.
B — record-bearing system objects. Candidates, to be measured, not asserted: sys_approval_request / sys_approval_action (who asked for what, who approved), sys_job (payloads), sys_http_delivery (delivery records — see #8566 on its plaintext-secret shape), sys_notification_subscription (who subscribes to what), sys_user_permission_set (who holds which access — a ready-made reconnaissance map). For these, public is not "the control surface is elsewhere"; it may be "the control surface is absent."
⛔ The sharper form of the question
The exemption keys on isSystem — a provenance flag meaning "the platform shipped this." The property that should decide a visibility default is "does this object hold records whose visibility matters". Those two are not the same predicate, and population B is exactly where they come apart.
The decision needed
Should sys_* objects keep an unstated PUBLIC default — and if not, what replaces it?
A — classify per object by what actually enforces visibility (recommended shape, ruling still the maintainer's). Reuse the pattern ADR-0094's own addendum already established for the declared-metadata ↔ queryable-record family: that addendum refused a blanket rule and classified each type by one question (which store does enforcement read at request time?). The analogue here: for this object, is row visibility the control surface, or do tenancy/FLS/CRUD already gate it? Population A keeps a wide default (stated, not silent); population B states a real posture.
B — platform objects must declare, same as everyone else. Drop the exemption; every sys_* declaration authors its sharingModel explicitly. Most expensive in edits, but the platform's own objects have the largest blast radius and are the least deserving of silence. Can be staged: declare first, refuse later.
C — keep the blanket public default, and record it as a deliberate, documented decision rather than an artifact of R2's retirement. Cheapest; acceptable only if the measurement below shows population B is genuinely gated elsewhere.
⛔ Not an option: flip the blanket default to private. That is the R2 baseline that was just measured wrong and retired; it would break population A.
⚠️ What must be MEASURED before this is ruled — it is not a philosophical question
This card deliberately does not claim any object is currently exposed. The load-bearing unknown:
For each population-B object, is the PUBLIC OWD default actually reachable, or is it already masked by the tenancy filter (computeTenantLayer0Filter), FLS, or CRUD?
Worklist for whoever takes it:
Enumerate isSystem objects with no authored sharingModel (the real set — do not assume the list above is complete or correct).
Split them A/B by the criterion in option A.
For each B object, determine empirically what refuses an unauthorized read today, if anything — a live probe, not a code-reading argument.
Report the divergence set: objects where PUBLIC row visibility is the only thing standing between a caller and the rows.
⚠️Do not assume tenancy already covers it. Tonight's backlog is an active, measured line of work finding exactly these assumptions wrong: #8577 (two more security-graded inheritors of the #8323 class), #8578 (the tenancy filter alone yields 20 findings, not 6), #8555 (sys_setting's installation-wide unique key on a tenant-scoped object), #8566 (sys_webhook.headers_secret).
⛔ Not that any sys_* object is currently leaking. Step 3 above is precisely what has not been done.
⛔ Not that population B's list above is accurate. It is a starting hypothesis produced by reading, not measuring.
Not urgent by default. Grading belongs to triage — ⚠️if step 3 finds a B object where OWD is the only gate, this becomes a security card and should be regraded immediately.
Recommendation
Classify (option A) rather than pick a blanket default, and treat "stated" as separable from "wide": population A can legitimately stay world-readable, but it should say so in its declaration instead of inheriting silence. That keeps #8310's principle — a posture is something someone decided — while not pretending row visibility is the control surface where it demonstrably is not. The ruling is the maintainer's; this card does not pre-empt it, and the measurement above should inform it.
Related
#8310 / PR #8600 (the ruling and the exemption) · ADR-0094 (D1, the 2026-08-14 amendment, and the addendum whose classification pattern option A reuses) · ADR-0090 D11 · #8547 (the other #8310 downstream) · the sys_* scoping family: #8323, #8555, #8566, #8577, #8578
Filed unassigned by the
domain:specPM seat (sessionsession_012MNV7ZSCjNfA38eDCjsXQL) at the maintainer's direct instruction, 2026-08-14, during the #8310 shift. This is the carve-out #8310's ruling left behind, surfaced deliberately rather than allowed to evaporate with the conversation that found it.Where this comes from
#8310 (PR #8600) ruled that absence is not a decision at the runtime object door: an active-state object publish with no authored
sharingModelis now refused422 security-owd-unset. The ordinary-object default (ADR-0094 D1, "absence resolves to private") was overturned, and the pin asserting a write with NO OWD keys SAVES was rewritten into the refusal.That rule exempts system objects. Per the ADR-0094 amendment landed by the same PR:
So after #8310 the platform's silent defaults look like this:
sharingModelresolves toisSystem/sys_*The widest silent default in the platform is the one that survived.
Why the exemption's rationale does not settle the default
The exemption is correct as damage control, and its stated reason is sound: R2's hardcoded private baseline was refusing system-object configurations that are not external-wider at runtime — those were false positives, which is exactly why R2 was retired.⚠️ But that reason establishes only that
privatewas the wrong baseline; it does not establish thatpublicis the right default. The two got merged into one carve-out without the second half being argued.The two populations under one rule
sys_*is not one kind of thing, and a single default serves them very differently:A — reference / configuration data. Positions, capabilities, permission-set definitions, settings, status vocabularies. The whole org must read these for the product to function (a picklist of positions, an approval screen, the permission admin UI). For these, row visibility is not the control surface at all — CRUD/FLS and the ADR-0086 two-doors gate are.
publichere is an honest statement of "this object does not defend itself by row visibility," and defaulting them private would break the platform.B — record-bearing system objects. Candidates, to be measured, not asserted:
sys_approval_request/sys_approval_action(who asked for what, who approved),sys_job(payloads),sys_http_delivery(delivery records — see #8566 on its plaintext-secret shape),sys_notification_subscription(who subscribes to what),sys_user_permission_set(who holds which access — a ready-made reconnaissance map). For these,publicis not "the control surface is elsewhere"; it may be "the control surface is absent."⛔ The sharper form of the question
The exemption keys on
isSystem— a provenance flag meaning "the platform shipped this." The property that should decide a visibility default is "does this object hold records whose visibility matters". Those two are not the same predicate, and population B is exactly where they come apart.The decision needed
Should
sys_*objects keep an unstated PUBLIC default — and if not, what replaces it?sys_*declaration authors itssharingModelexplicitly. Most expensive in edits, but the platform's own objects have the largest blast radius and are the least deserving of silence. Can be staged: declare first, refuse later.private. That is the R2 baseline that was just measured wrong and retired; it would break population A.This card deliberately does not claim any object is currently exposed. The load-bearing unknown:
Worklist for whoever takes it:
isSystemobjects with no authoredsharingModel(the real set — do not assume the list above is complete or correct).security-graded inheritors of the #8323 class), #8578 (the tenancy filter alone yields 20 findings, not 6), #8555 (sys_setting's installation-wide unique key on a tenant-scoped object), #8566 (sys_webhook.headers_secret).What this card does NOT claim
runtimeTypesgainsobject+permission/book— whole rule families cross the publish door #8310's exemption was wrong. It was right as shipped — refusing system objects would have broken population A and the decision is not the customer's to make.sys_*object is currently leaking. Step 3 above is precisely what has not been done.securitycard and should be regraded immediately.Recommendation
Classify (option A) rather than pick a blanket default, and treat "stated" as separable from "wide": population A can legitimately stay world-readable, but it should say so in its declaration instead of inheriting silence. That keeps #8310's principle — a posture is something someone decided — while not pretending row visibility is the control surface where it demonstrably is not. The ruling is the maintainer's; this card does not pre-empt it, and the measurement above should inform it.
Related
#8310 / PR #8600 (the ruling and the exemption) · ADR-0094 (D1, the 2026-08-14 amendment, and the addendum whose classification pattern option A reuses) · ADR-0090 D11 · #8547 (the other #8310 downstream) · the
sys_*scoping family: #8323, #8555, #8566, #8577, #8578