Skip to main content

Menus — overview

A menu in Liberty is what the user sees in the left navigation panel when they open one of your apps. It groups screens, endpoints, dashboards and routes into a tree of folders and leaves.

Three things are worth knowing up front:

FactImplication
There is no separate "app" object in Liberty.An "app" is just a connector that has a menu attached and whose show_in_switcher flag is on. Both conditions are required for the application picker of the sidebar to list it.
Menus are stored as a flat list of items linked by parent.The tree is assembled by the backend — easier to hand-edit, round-trips cleanly through TOML, drag operations only ever mutate one list.
Every menu key is a connector name ([menus.<connector>]).The connector named crm carries the menu under [menus.crm]; the screen customers on crm is reached from a leaf with target = "customers_get".

The page that manages menus is Liberty Next → Build → Menus.


At the bottom of the sidebar, Open on hover folds the menu to its icons: it opens over the page while the mouse is on it and folds back once a page is picked, leaving the whole width to the screens. Keep open brings back the fixed menu. The choice is kept in the browser.

The Menus page at a glance​

Settings · Menuscrmnomajdenomasx1+ Add scopeDiscardSave[menus.crm] · 7 items+ Folder+ Item⎘ Clone🔗 Find usagesTREE▾ 🛡 Security📄 Users📄 Roles▾ 💼 Pipeline📄 Customers📊 Deals dashboard📄 Reports↑ ↓ ← → reorder · indent · outdentINSPECTOR · Customersid=customerslabel=Customersparent=pipelinetype=queryconnector=(uses the app's — crm)target=customers_geticon=usersl.fr=Clientsroles=[user, manager]

Three regions:

RegionWhat it carries
Top scope barOne chip per app (a connector that has a menu). Click a chip → its tree loads below. The + Add scope chip gives an existing connector a menu. Discard / Save on the right commit or revert page-wide edits.
Tree (left column)The selected app's menu tree. Click a row to select it for editing. On hover, action icons appear over the row's right edge — Move up / down, Indent / Outdent, Add child, Delete. A filter input narrows the list.
Inspector (right column)The full editor for the selected item — a generic form over the MenuItem schema. Fields adapt to the selected type (a folder shows fewer fields than a leaf).

What a menu carries​

The schema's top-level shape:

FieldNotes
labelThe app's display name. Falls back to the connector name. Shown in the application picker.
itemsA flat list of menu items, linked by their parent field.

Each item:

FieldRequiredWhat it does
idyesUnique inside this menu. Referenced by children's parent.
parentnoPick a folder, or leave blank for a top-level item.
labelyesThe sidebar text.
lnoPer-language label overrides — l.fr, l.de, etc.
iconnoOne of the icon names the sidebar draws (shield, users, barchart, …) — see Translations and icons.
typenoBlank = folder. Otherwise one of screen, query, endpoint, dashboard, page (see Item types).
connectornoConnector hosting the target. Blank = the app's own connector (the menu key).
targetconditionalRequired on every leaf; ignored on folders.
paramsnoFixed parameters passed to the target when the item opens.
rolesnoRestrict to these roles. Empty = visible whenever the user can run the target.

Folder vs leaf​

The type field decides:

SettingBehaviour
type blankThe item is a folder — groups children. No target, no connector, no params. A folder is hidden when none of its descendants are visible (the runtime collapses empty folders).
type setThe item is a leaf — points at a thing the user can open. Must have a target.

Folders can nest indefinitely. Leaves can't have children — they're terminal nodes.


The five leaf kinds​

typeOpenstarget isconnector
screenA designed screen.The screen's read query (customers_get), picked from the designed screens.The connector that owns it. Blank = the app's.
queryThe same grid view, picked from the raw queries — a query with no designed screen opens as a plain table.A SELECT query name.The connector that owns the query. Blank = the app's.
endpointThe HttpRunner — fires an API connector endpoint.An endpoint name.The API connector. Blank = the app's.
dashboardA dashboard (charts + KPIs).A dashboard id (from [dashboards.*]).Must NOT be set (dashboards live in their own flat namespace).
pageA registered frontend route (a custom feature area, e.g. /nomaflow).The route path.Must NOT be set (the target is a route, not a connector resource).

The schema validator enforces:

  • A leaf needs a target — saving fails without one.
  • A dashboard or page leaf with a connector is rejected (misconfiguration).
  • A folder with target / connector / params is rejected (those fields only make sense on leaves).

Save and reload​

The Save button validates the whole MenusFile (unique ids, parents exist, no cycles), writes menus.toml and triggers a hot reload. New menus apply immediately — no process restart.

The validation is strict on three things:

CheckWhy
Every parent reference must point at an existing item.A dangling parent would orphan the subtree.
No cycles (a parent chain that loops).An infinite-loop guard.
No duplicate id within the same menu.Children reference parents by id; duplicates break the link.

Cross-menu duplicates are fine — [menus.crm.security] and [menus.nomasx1.security] coexist without conflict.


How a connector becomes an app​

A connector is listed in the application picker of the sidebar only when both conditions are true:

  1. A menu exists — [menus.<connector>] is set up in Liberty Next → Build → Menus.
  2. Show In Switcher on the connector — set in Liberty Next → Build → Connectors → <connector> → Settings (on by default).

Miss either one and the connector exists but is not listed. The picker itself appears only when there is more than one application. Setting up the menu first and then ticking show_in_switcher is the usual order.

Note: the Connectors page's own Apps / Data sources grouping is based on menu existence alone — connectors with a menu but show_in_switcher = false still show under Apps there. That grouping is an internal Settings-UI affordance; the user-facing picker is what Show In Switcher gates.

The next page covers the wiring from both sides.


Permission pruning​

When the user opens the app, GET /api/menus returns the tree filtered to what the user can run:

  • A leaf is kept when the user may run what it opens (sql:<connector>:<target> for screen / query, api:<connector>:<target> for endpoint), or when a role explicitly allows its menu:<app>:<id> — see Permissions and roles.
  • A menu: or dashboard: deny removes it.
  • A folder with no surviving children is collapsed away.
  • Items with a roles filter are kept only when the user's roles intersect the list.

A user sees only the parts of the menu they can actually use — no greyed-out items, no clicks on dead-end links.


What you actually do — quick map​

GoalRead
Make a connector show up as an app in the application picker.Make a connector an app.
Build the tree — folders, leaves, drag/reorder, indent.Build the tree.
Pick the right leaf type (screen / endpoint / dashboard / route).Item types.
Restrict items to specific roles.Permissions and roles.
Add icons + per-language labels.Translations and icons.

What's next​