Setup App
The platform's built-in administration UI — what it is, what it shows, and how it is wired
Setup App
The Setup App (/apps/setup) is the built-in administration console
for every ObjectStack project. It surfaces the platform sys_* objects,
the System Overview dashboard, and a set of settings pages behind a
left-hand navigation tree.
Under ADR-0029 D7 the app is not a fixed navigation tree. SETUP_APP
ships as a thin shell of stable, empty navigation group anchors
("slots"); the actual menu entries are contributed at runtime by the
packages that own the underlying objects, via navigationContributions.
The runtime merges every contribution into the app's navigation tree by
group id + priority on read, so the rendered menu reflects exactly which
capability plugins are loaded — a disabled capability contributes nothing
and its slot stays empty.
The app itself ships from the dedicated @objectstack/setup package
(package id com.objectstack.setup), which registers it at runtime. Per
ADR-0048 (one app per package), /apps/setup — resolvable as
/apps/<packageId> — maps to exactly this app.
Where it lives
| File | Purpose |
|---|---|
packages/platform-objects/src/apps/setup.app.ts | The App definition — the navigation shell (group anchors), branding, required permissions |
packages/platform-objects/src/apps/setup-nav.contributions.ts | SETUP_NAV_CONTRIBUTIONS — the nav entries owned by @objectstack/platform-objects, merged into the shell at runtime |
packages/platform-objects/src/apps/dashboards/system_overview.dashboard.ts | "Overview → System Overview" dashboard |
packages/apps/setup/src/index.ts | @objectstack/setup package — SetupAppPlugin.start() performs the runtime registration via manifest.register({ apps: [SETUP_APP], navigationContributions: SETUP_NAV_CONTRIBUTIONS }) |
The
requiredPermissions: ['setup.access']permission declared on the Setup App is contributed byplugin-security(in its default permission sets), not by the package that registers the app. Other capability plugins (e.g.plugin-audit,plugin-webhooks,plugin-approvals) contribute their own Setup nav entries into the shared group anchors.
Navigation
setup.app.ts defines the stable group anchors (the shell); the entries
under each group are merged in from the contributing packages. The group
anchors are:
| Group (anchor id) | Filled by |
|---|---|
Overview (group_overview) | System Overview dashboard — platform-objects |
Apps (group_apps) | Capability plugins only (e.g. marketplace via cloud-connection); empty otherwise |
People & Organization (group_people_org) | Users · Departments · Teams · Organizations · Invitations — platform-objects |
Access Control (group_access_control) | Positions / Permission Sets — plugin-security; Sharing Rules / Record Shares — plugin-sharing; API Keys — platform-objects |
Approvals (group_approvals) | plugin-approvals |
Configuration (group_configuration) | All Settings · Branding · Authentication · Email · File Storage · AI & Embedder · Knowledge · Feature Flags — platform-objects |
Diagnostics (group_diagnostics) | Sessions · Notification Events — platform-objects; Audit Logs — plugin-audit |
Integrations (group_integrations) | plugin-webhooks |
Advanced (group_advanced) | OAuth Applications · Signing Keys (JWKS) · Identity Links · User Preferences — platform-objects |
The exact rendered menu depends on which capability plugins are loaded. A few notable entries:
- Sessions (
sys_session) — better-auth session records, contributed intogroup_diagnostics. - OAuth Applications (
sys_oauth_application) — third-party OAuth client registrations, contributed intogroup_advanced. - Signing Keys (JWKS) (
sys_jwks) — JWKS keys used for OIDC / JWT signing, contributed intogroup_advanced. The nav item is gated on themanage_platform_settingscapability, and the object itself isaccess: { default: 'private' }(ADR-0066 ④) — signing keys are never covered by the wildcard grant, so non-admins are denied server-side. (sys_verificationandsys_device_codeare deliberately not in the nav: sensitive, ephemeral secrets — not browsable, and alsoprivate.) - Audit Logs (
sys_audit_log) — contributed byplugin-auditintogroup_diagnostics. (Thesys_activityandsys_commentobjects also live inplugin-audit, but they are not contributed as Setup nav entries.)
Why a shell + contributions
The Setup App is a shell of empty group anchors rather than a fixed
navigation tree because the objects it surfaces are owned by different
capability plugins (auth / security / sharing / audit / webhooks /
approvals). Letting each package contribute its own menu entries keeps the
menu for an object shipping with the package that owns the object — the
UI-layer analog of object extend. Benefits:
- The rendered menu always matches the loaded capabilities: a disabled plugin contributes nothing and its slot stays empty.
- No code in
setup.app.tsneeds to know about objects that live in other packages; entries are merged by group id + priority on read. - The shell can still be type-checked against
Appfrom@objectstack/spec/ui, and contributions againstNavigationContribution.
Customising it
You do not edit setup.app.ts to add entries — it only holds the empty
group anchors. To add a new entry:
- Register the object/dashboard in the package that owns it (or in a project-local plugin if it is project-specific).
- Add a
NavigationContributiontargeting the relevant group id (app: 'setup',group: 'group_*') — either inSETUP_NAV_CONTRIBUTIONSfor@objectstack/platform-objects-owned objects, or in thenavigationContributionsof the plugin that owns the object. - If the entry needs its own permission, declare it where the relevant
permissions live (e.g.
setup.accessis granted inplugin-security's default permission sets) so therequiredPermissionscheck picks it up.
For project-local Setup-style entries, define a separate App in your project — Setup is reserved for platform-level administration.
Related
- App Metadata —
Appschema reference - Authentication — where
setup.accessis granted - Cloud Deployment — how Setup behaves in cloud mode