Skip to content

User menus

User menus define the left-hand navigation each person sees in the Moltaro web app. An administrator configures one or more menus; at sign-in the runtime picks the menu that matches the user’s roles and renders its tree.

Each menu has a name, an optional description, a priority, and an optional role scope. Resolution is per user: from the menus whose role scope matches the user (an empty scope matches everyone), candidates are considered from highest to lowest priority. A role-scoped menu is considered before a global menu when priorities are equal. The runtime removes nodes the user cannot access and selects the first candidate that still contains at least one visible node; if a candidate becomes empty, resolution continues with the next one. This lets one workspace serve different navigation to dispatchers, technicians, and managers without per-user configuration.

A menu is a tree of nodes:

NodeWhere it can be placedWhat users see
SectionAt the top level or inside a submenuA titled, collapsible group of items
SubmenuOnly at the top levelAn entry with a chevron; opening it replaces the navigation with its own items and sections and a Back to main menu action, like Administration and Constructor
Leaf itemAnywhereA link to one concrete target

Sections contain only leaf items, and submenus do not nest. Keep everyday work at the top level or in sections, and move large, less frequent areas such as reference data into submenus.

The leaf type decides which extra field applies:

Leaf typeTarget fieldOpens
Data ExplorerEntityDefinitionId, optional EntityListPageSurfaceKeyA selected Entity list page, or the definition’s default page
Module pagePageCode, plus PageTargetId for pages that open one recordA registered built-in module page of the web app
External linkHrefAn absolute HTTP(S) URL or an app-relative SPA path beginning with /
File system—The workspace file browser
Workspace UI pageWorkspaceUiPageKeyA workspace-authored UI page
CatalogCatalogDefinitionIdOne concrete Classifier or Resource Tree Catalog

To expose two subsets of one record type, configure two Entity list pages with different Statement filters. Add a Data Explorer leaf for each, using the same EntityDefinitionId and different EntityListPageSurfaceKey values. Each page can select its own table; a menu label alone does not define a filter. The active highlight distinguishes these pages by surfaceKey. A link with no explicit key is highlighted for the default-page URL, while search or filter parameters do not change which page’s menu item is active.

Every node carries a stable Id, a default label, optional LabelTranslations (a label per language code: en, uk, de, pl, es), an icon, a sort order among its siblings, and an optional RoleIds scope. The navigation shows the label in the user’s interface language and falls back to the default label. An empty node scope adds no restriction. A non-empty scope requires the signed-in user to hold at least one listed role. Restrictions compose through the tree: a node is visible only when the menu, every ancestor, and the node itself match the user. A hidden section hides its entire subtree, and a section or submenu with no remaining visible children is omitted.

Node visibility only shapes navigation. It does not grant access to the leaf target or its data. Data Explorer, Catalog, module page, and other target authorization is evaluated independently, and a leaf is still removed when the user cannot access its target. Hidden nodes are filtered before target lookup so their labels and target identifiers are not returned by runtime resolution.

In the web app, menus are managed under Constructor > User menus. The menu designer shows the structure as a tree (drag items between sections and submenus, or use Move to), an inspector for the selected item or the menu settings, and a live preview of the navigation for a chosen role. Changes stay in an unsaved draft with undo until you select Save. The same operations are available to integrations and AI agents through the Configuration API: list, read, create, replace, and delete menus (admin-runtime-menus-* operations). The menu a signed-in user actually receives is served by the runtime menu resolve endpoint of the Runtime API. Workspace UI page keys come from the pages published by the workspace’s Workspace UI Project.

The Configuration API exposes the authoritative built-in page registry at GET /api/workspace/admin/runtime-menus/module-pages. Use it for pickers and automation instead of maintaining a separate PageCode list. Each entry includes its localized display name, icon, module, backend route name, required permissions, availability, and deprecation metadata. A known page of a disabled module may still be saved or imported, but it is unavailable and omitted from resolved navigation until the module is enabled. An unknown code is rejected. Stable codes are not reused: a replacement is introduced only after the old entry is marked deprecated.

Some module pages open one module-owned record and require PageTargetId. List the allowed records with GET /api/workspace/admin/runtime-menus/module-pages/{pageCode}/targets; the module decides at runtime which of them a user may open.

Use these target shapes deliberately:

  • ModulePage with PageCode: MoltaroBoards.Boards opens the root Board selector.
  • ModulePage with PageCode: MoltaroBoards.Board and the Board id in PageTargetId opens that Board. The item is shown to users with Boards access while the Board is active, disappears when the Board is deactivated or deleted, and keeps working when the Board key changes. Installation packages carry the Board key as PageTargetKey instead of the id.
  • ModulePage with PageCode: MoltaroReporting.Report and the report id in PageTargetId opens that report on the reports page; MoltaroReporting.Dashboard with the dashboard id opens the home page with that dashboard selected. The item is shown only to users who may open the published report or dashboard under its visibility settings; an archived one hides the item and is marked unavailable in the designer. Renaming keeps the item. Installation packages carry the report or dashboard key as PageTargetKey.
  • ExternalLink with Href: /boards/support also opens a Board, but it is shown to every user of the menu and breaks when the key changes.
  • WorkspaceUiPage is preferred for a registered workspace-authored page.
  • ExternalLink with Href: https://example.com leaves the application.

App-relative links must begin with one /. Moltaro rejects protocol-relative //... values, backslashes, control or whitespace characters, and leading or trailing whitespace; it does not trim an unsafe value before validation.

Updates replace the whole menu and take the current RowVersion. Send each existing node with its Id to keep it (it may move to another parent); nodes without an Id are created and omitted nodes are deleted. An Id that is not part of the menu is rejected at that node; creating a menu or appending items does not accept ids. Updates fail with a localized moltaro.runtimeMenus.* error code when a value is missing or the menu changed concurrently — see the error code reference.

The menu designer provides a role picker for every section, submenu and leaf. Installation package YAML uses the same optional RoleIds property on every node; omitting it or exporting an empty list preserves unrestricted node visibility.

A Catalog leaf targets one concrete Classifier or Resource Tree Catalog. Entity Hierarchy is unavailable until its runtime is implemented. Menu resolution hides the leaf unless the signed-in user is a Catalog reader, Catalog administrator, workspace Owner, or Admin. Adding the leaf does not grant access to the Catalog or to any record shown inside it. Installation packages identify this target by the portable CatalogKey; the workspace resolves it to the local Catalog Definition. Moltaro does not add a generic Catalogs item to the fixed sidebar: each user-facing Catalog must be placed in a Runtime Menu explicitly.