Skip to content

Commit a053fd6

Browse files
os-zhuangclaude
andauthored
docs(skill): document env-global / admin-only object visibility posture (objectstack-data) (#2403)
The objectstack-data skill documented tenancy modes + RLS but not the non-obvious interaction surfaced by cloud#551: a platform admin's viewAllRecords superuser bypass is posture-gated (only fires for access.default:'private' or tenancy.enabled:false), so env-global adapter-written objects (e.g. sys_sso_provider) render an empty list under member_default's wildcard tenant_isolation. Documents the correct recipe — tenancy.enabled:false + requiredPermissions — and the two single-flag pitfalls (leak / admin-blind). Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 8c84c97 commit a053fd6

1 file changed

Lines changed: 30 additions & 0 deletions

File tree

skills/objectstack-data/SKILL.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,36 @@ enforces per-tenant data isolation:
586586
| `isolated` | Separate database per tenant | Regulatory isolation / large tenants |
587587
| `hybrid` | Shared schema, tenant-specific sharding | High-volume multi-tenant |
588588

589+
### Platform-global / admin-only objects (visibility posture)
590+
591+
Some system/config objects are **env-global** (not partitioned per org) and
592+
should be visible to a **platform admin env-wide** but hidden from members —
593+
e.g. identity tables a plugin writes via its own adapter (`sys_sso_provider`,
594+
OAuth clients). These hit a non-obvious interaction:
595+
596+
- The default `member_default` ships a **wildcard `tenant_isolation` RLS**
597+
(`organization_id == current_user.organization_id`). Any row whose
598+
`organization_id` is **null or absent** (common for adapter-written rows that
599+
never get the tenant stamp) is **denied** — the list renders empty.
600+
- A platform admin's `viewAllRecords` superuser bypass is **posture-gated**: it
601+
fires **only** for objects marked `access.default: 'private'` **or**
602+
`tenancy: { enabled: false }`. On ordinary tenant objects it deliberately does
603+
**not** grant cross-tenant visibility — so the admin sees 0 rows too.
604+
605+
**Recipe — env-global, admin-only object that admins can fully see:**
606+
607+
```typescript
608+
tenancy: { enabled: false, strategy: 'shared' }, // env IS the tenant; admin viewAllRecords bypass applies
609+
requiredPermissions: ['manage_platform_settings'], // object-level gate → members get 403
610+
```
611+
612+
> ⚠️ **Don't use either flag alone.** `tenancy.enabled:false` *by itself* drops
613+
> the wildcard RLS, and `member_default`'s `'*': allowRead` then **leaks every
614+
> row to all authenticated users**. `access.default:'private'` *by itself* opts
615+
> the admin's `'*'` grant out too, so the **admin sees nothing**. The
616+
> `tenancy.enabled:false` + `requiredPermissions` pair is the correct combo
617+
> (admin sees all, non-admins 403). Posture model: [ADR-0066](../../docs/adr/0066-unified-authorization-model.md).
618+
589619
### Cross-skill notes
590620

591621
- **API auth providers** (OIDC, JWT, API key) live in **objectstack-api**.

0 commit comments

Comments
 (0)