Audience-based interfaces
The same data serves different audiences. Give end users a curated app/page and keep builder surfaces — Studio, raw object tables, automation config — out of their view. Separate consumer and builder paths by default.
Audience-based interfaces
Scenario
The same object is touched by two very different audiences: builders who design the schema and end users who just enter and read data. How do I give each the right surface — without end users seeing (and being confused by) the builder chrome?
Recommended solution
Separate the consumer surface from the builder surface by default — don't rely on each admin to hide things by hand. ObjectStack has two first-class run modes (ADR-0047); navigation type decides which a user gets.
| Audience | Surface | Gated by |
|---|---|---|
| End user (consumer) | a curated App → page / view (interface mode) | App.requiredPermissions, permission-set tabPermissions, nav-item gating |
| Builder / admin | Setup / Studio, raw object tables (data mode) | capabilities: setup.access, studio.access, manage_metadata |
The built-in permission sets already encode this split: member_default and viewer_readonly do not carry studio.access / manage_metadata, so Studio and schema-design surfaces are invisible to them; admin_full_access and organization_admin do (see default-permission-sets.ts).
1. Gate the app and its navigation
defineApp({
name: 'crm',
label: 'CRM',
requiredPermissions: ['crm.access'], // who may open the app at all
navigation: [
{ id: 'nav_contacts', type: 'object', label: 'Contacts', objectName: 'showcase_contact' },
// Builder-only entries are gated so consumers never render them:
{ id: 'nav_designer', type: 'component', label: 'Object Designer',
componentRef: 'metadata:resource', params: { type: 'object' },
requiredPermissions: ['manage_metadata'] },
],
});Each nav item supports three independent gates (app.zod):
requiredPermissions— RBAC capability (e.g.manage_metadata).visible— a CEL predicate (e.g.P\'org_admin' in current_user.positions``).requiresObject/requiresService— hide unless a runtime object/service is installed.
Hide, don't disable. A disabled-but-visible builder entry is still noise and still confuses end users. Gated nav items are not rendered for users who lack the capability.
2. Hand end users a page, not the raw table
In data mode (navigation → object) users get the permissive grid: switchable views, personal views, a full toolbar. In interface mode (navigation → page) the author curates exactly what's exposed — selected columns, fixed visualisation, only the filters and actions you enabled. For end users, prefer interface-mode pages. ADR-0047's default is deliberately asymmetric: data mode is open, interface mode is closed until the author opens it.
3. Curate the data surface itself
Use a curated view (selected fields, progressive disclosure) rather than granting read on the raw object and dropping users onto a 40-column grid.
Why
This is the lesson from Airtable's surfaces: the Automations tab is visible to every base collaborator, even read-only — so end users see builder chrome they can't use and don't understand. ObjectStack avoids that by making "consumer vs builder" a capability boundary that the built-in permission sets already draw, plus per-nav gating that omits (not merely disables) what a user can't use. Action gates are dual-surface (ADR-0066 D4): the ActionRunner hides/disables an action in the UI and the server rejects the call — no "UI-gated but server-open" footgun.
Runnable example
- Permission sets:
default-permission-sets.ts—member_default/viewer_readonlyvsorganization_admin/admin_full_access. - Apps:
examples/app-showcase/src/apps.
Anti-patterns
- Making every end user a builder-grade collaborator and then hiding tabs one by one. Make the split the default.
- Disabling builder entries instead of hiding them. Visible-but-dead chrome is still confusing.
- Granting raw object read to end users when a curated page would expose exactly what they need.
See also
- Decision records: ADR-0047 (run modes), ADR-0066 (unified authorization, dual-surface gates).
- Who can see data / automation / interface.
- Guide: Security; reference: App schema, Permission schema.