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.
Menus, priority, and role resolution
Section titled “Menus, priority, and role resolution”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.
Menu structure
Section titled “Menu structure”A menu is a tree of nodes:
| Node | Where it can be placed | What users see |
|---|---|---|
| Section | At the top level or inside a submenu | A titled, collapsible group of items |
| Submenu | Only at the top level | An 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 item | Anywhere | A 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 type | Target field | Opens |
|---|---|---|
| Data Explorer | EntityDefinitionId, optional EntityListPageSurfaceKey | A selected Entity list page, or the definition’s default page |
| Module page | PageCode, plus PageTargetId for pages that open one record | A registered built-in module page of the web app |
| External link | Href | An absolute HTTP(S) URL or an app-relative SPA path beginning with / |
| File system | — | The workspace file browser |
| Workspace UI page | WorkspaceUiPageKey | A workspace-authored UI page |
| Catalog | CatalogDefinitionId | One 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.
Managing menus
Section titled “Managing menus”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:
ModulePagewithPageCode: MoltaroBoards.Boardsopens the root Board selector.ModulePagewithPageCode: MoltaroBoards.Boardand the Board id inPageTargetIdopens 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 asPageTargetKeyinstead of the id.ModulePagewithPageCode: MoltaroReporting.Reportand the report id inPageTargetIdopens that report on the reports page;MoltaroReporting.Dashboardwith 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 asPageTargetKey.ExternalLinkwithHref: /boards/supportalso opens a Board, but it is shown to every user of the menu and breaks when the key changes.WorkspaceUiPageis preferred for a registered workspace-authored page.ExternalLinkwithHref: https://example.comleaves 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.