|
1 | 1 | # ADR-0095: Authorization Kernel Chain — Tenant Isolation as Layer 0, a Monotonic Posture Ladder, Capability-Derived Posture |
2 | 2 |
|
3 | 3 | **Status**: Accepted (2026-07-14) — implemented: D1 tenant Layer 0 (`plugin-security/src/tenant-layer.ts`), D2/D3 posture ladder (`core/src/security/posture-ladder.ts`), locked by `authz-matrix-gate.test.ts` + `posture-ladder.test.ts` (#2920 B1–B4) |
| 4 | +· **Amended** (2026-10-18, ruling of record 6250748677 on #24164, letter E′) — D1: Layer 0 is inert on 22 of the 23 platform objects that are to declare a default row scope, and walls the 23rd, `sys_user_preference`, to its organisation; on all 23, Layer 1 silence is to mean the object's declared default row scope, a protocol key (#24338), not no filter. See the note under D1. |
4 | 5 | **Deciders**: ObjectStack Protocol Architects |
5 | 6 | **Builds on**: [ADR-0002](./0002-environment-database-isolation.md) (environment-per-database), [ADR-0057](./0057-erp-authorization-core-business-units-and-scope-depth.md) (scope depth), [ADR-0066](./0066-unified-authorization-model.md) (unified authz model, superuser bypass), [ADR-0090](./0090-permission-model-v2-concept-convergence.md) (permission model v2), [ADR-0093](./0093-tenancy-mode-and-membership-lifecycle.md) (tenancy service) |
6 | 7 | **Scopes**: the "cross-tenant RLS does not exist here" claim in [ADR-0073](./0073-automation-execution-identity.md) (see Context) |
@@ -174,6 +175,29 @@ the Layer 0 pre-image path. Adding a mandatory post-image tenant check would |
174 | 175 | risk denying legitimate inserts before the auto-stamp runs — a regression, not a |
175 | 176 | fix — so it is deliberately excluded from D1. |
176 | 177 |
|
| 178 | +> **Amended 2026-10-18 (#24339; ruling of record 6250748677 on #24164, letter E′ |
| 179 | +> — the maintainer's 「同意」) — Layer 1 silence on the platform identity objects.** |
| 180 | +> Layer 0 is inert on 22 of the 23 objects the ruling names, which carry no |
| 181 | +> `organization_id` column: the seven SCIM projections (`sys_scim_user`, |
| 182 | +> `sys_scim_subject`, `sys_scim_projection_grant`, `sys_scim_identity_tombstone`, |
| 183 | +> `sys_scim_group`, `sys_scim_group_member`, `sys_scim_connection_binding`), |
| 184 | +> `sys_verification`, `sys_jwks`, `sys_organization`, `sys_user`, `sys_session`, |
| 185 | +> `sys_account`, `sys_team_member`, `sys_two_factor`, `sys_api_key`, |
| 186 | +> `sys_device_code`, `sys_oauth_access_token`, `sys_oauth_refresh_token`, |
| 187 | +> `sys_oauth_consent`, `sys_oauth_application` and `sys_audit_log`. The 23rd, |
| 188 | +> `sys_user_preference`, carries the organisation column, so Layer 0 bounds it to |
| 189 | +> its organisation and its declared default is to apply inside that wall |
| 190 | +> (measured: `scripts/platform-object-tenancy-census.json`, held to the tree by |
| 191 | +> `scripts/check-platform-object-tenancy-census.mjs`). On all 23, Layer 1 |
| 192 | +> silence (no held set carries a row policy for the object) is to mean the |
| 193 | +> object's declared default row scope, not no filter. The default is a protocol |
| 194 | +> key on the object (`ObjectSchema.defaultRowScope`, #24338); the platform |
| 195 | +> objects are to declare it themselves (#24341), the one collection point is to |
| 196 | +> apply it (#24340), and the registry is to refuse an `extend` contributor that |
| 197 | +> sets or loosens it (#24341). Mainstream precedent: Salesforce OWD on the |
| 198 | +> object; Dataverse ownership type on the table; PostgreSQL |
| 199 | +> `CREATE POLICY ON table`. |
| 200 | +
|
177 | 201 | ### D2 — A monotonic posture ladder, resolved once |
178 | 202 |
|
179 | 203 | `resolveAuthzContext` resolves and carries an explicit posture: |
|
0 commit comments