Skip to content

Mount read-only inspector screens

Some bundled features ship read-only inspector screens over their internal read-models: a list (and detail) view an operator can open to see what is going on, GDPR export jobs, invalid download attempts, and so on. The screens live in the feature; your app only opts in by navigating them. No per-app screen code, no custom queries.

  • The bundled feature whose screens you want is mounted in your app (see the table below for which features ship them).
  • You’ve read Features and composition so r.requires and qualified cross-feature names are familiar.

The screens are registered but inert, they appear nowhere until your app navs them. Add one r.nav per screen in any of your app features. They are SystemAdmin-gated, so they surface only for platform operators:

defineFeature("my-app", (r) => {
r.requires("user-data-rights");
r.nav({
id: "gdpr-export-jobs",
label: "GDPR · Export Jobs",
// qualified screen name: <feature>:screen:<id>
screen: "user-data-rights:screen:export-job-list",
access: { roles: ["SystemAdmin"] },
order: 90,
});
});

That is the entire wiring. The list, the detail and their query handlers all live in the feature, your app references the screen by its qualified name. The nav resolves cross-feature with no import or coupling, and validateBoot fails loudly if the screen name is wrong.

FeatureScreensShowsGating
user-data-rightsexport-job-list, export-job-detail, download-attempt-listGDPR Art. 20 export requests (user, status, requested / completed, expires) and invalid download attempts (result, IP, attempt time), DPO brute-force triageSystemAdmin

This table grows as more features expose inspector screens.

Inspector screens use the entityList / entityEdit machinery, which binds to an event-sourced r.entity read-model (rebuild-safe). Features whose read-model is a direct-write store (jobs, sessions, billing-foundation) cannot use this pattern, registering those as entities would wipe the live table on the next projection rebuild. A separate query-bound read-only primitive for those is tracked as a follow-up.

  • No r.nav, no screen. The feature registers its inspector screens either way; without a nav entry they are reachable by nobody and it looks like the feature never shipped them.
  • The screen name is qualified. <feature>:screen:<id>, not the bare id, validateBoot fails loudly on a wrong name, so a typo shows up at boot rather than as an empty menu.
  • r.requires("<feature>") is mandatory. Without it the boot validator fails with a clear message, instead of the nav silently pointing at nothing once the feature leaves your feature list.
  • SystemAdmin gating is not optional trim. These screens expose raw operator data across tenants; widening access.roles to hand a support role “just the list” hands it the whole read-model.