Skip to main content

Apps

A Liberty app is a namespace — a logical grouping of connectors, screens, menus, dashboards, charts and jobs that belong to one business domain (CRM, billing, JD Edwards admin, internal HR…). The framework collects every entity's App field at load time and exposes the resulting list as the workspace selector in the header — switching workspaces changes the sidebar to that app's menu tree.

Apps aren't a separate concept to wire — every Settings builder exposes an App dropdown on each entity. This page covers how the workspace selector works, how to package an app for transfer between environments, and the per-app permission pattern.


At a glance

WORKSPACE SELECTOR
Top of the header. Lists every app the caller can see, sorted by display order.
APP FIELD
Every connector / screen / menu / chart / dashboard / job carries one. Defaults to _default.
SETTINGS → APPS
Friendly labels, icons, display order — plus zip export / import for transfer between environments.
PERMISSIONS
sql:<app>-*:*, screen:<app>:*, menu:<app>:* — one role per app keeps things tidy.

How an app is created

Apps don't have a "create" action of their own. An app exists as soon as one entity references it. Open Settings → Connectors → New connector, type billing in the App field — billing is now an app with one connector. Add a menu under Settings → Menus → New menu with the same App value, and the workspace selector starts showing Billing.

To give the app a friendly label, icon and display order, the Settings → Apps tab provides per-app metadata:

Settings → Apps
↑ Import app+ New app
ID
Display name
Icon
Order
📜
billing
Billing
receipt
10
⬇ ✏️
👥
crm
CRM
users
20
⬇ ✏️
📊
nomajde
NomaJDE
server
30
⬇ ✏️
FieldEffect
IDThe app identifier — short, kebab-case (billing, crm). Used as namespace prefix throughout. Renaming triggers a rewrite of every reference.
Display nameShown in the workspace selector and as the sidebar header. Localised through the dictionary.
IconA Lucide icon name. Appears next to the workspace name.
OrderDrives the position in the workspace selector. Lower numbers first.
DescriptionFree text — appears as the tooltip in the workspace selector.

Per-row actions:

  • ⬇ Export — produces a zip of every entity carrying this app's identifier (see Export / import).
  • ✏️ Edit — opens the metadata editor.

The framework still works with apps that have no metadata entry — the workspace selector just shows the raw identifier instead of the friendly name.


The workspace selector

The header shows a workspace chip with a dropdown. The dropdown lists every app the calling user can see at least one menu of (filtered by per-app permissions), sorted by Order then alphabetically.

BehaviourWhen
Selector visibleAt least two apps have menus the caller can see.
Selector hiddenOnly one app (the default case for single-tenant installs).
Remembered per userThe last picked workspace is stored in localStorage.

A user with permission to only one app sees no selector — the framework defaults straight to that workspace.


Conventions per app

ConventionReason
One menu per app under Settings → Menus. The menu's App drives the workspace key.Declaring a menu is what makes the workspace selectable.
Prefix connectors with the app (billing-customers, crm-contacts).Keeps the connector catalogue readable and prevents collisions when two apps query the same database.
Namespace screen ids by app (billing/invoices, crm/customers).The framework allows the same screen id under two apps; the namespace prevents the wrong one being picked.
Dictionary entries can be shared.The dictionary is global by default — labels and lookups defined once are referenced from every app.
Jobs live in the app's namespace (the job builder's App dropdown).Keeps the Settings → Jobs catalogue organised; framework-wide jobs sit under _default.

Export / import

Apps move between environments (dev → staging → prod) via the Settings → Apps export / import flow.

Exporting

Click ⬇ Export on an app row, or open the app's metadata editor and use the Export action. The framework produces a zip containing:

Path inside the zipContent
manifest.tomlMetadata: app id, version, exported timestamp, dependencies (other apps required), framework version compatible.
connectors.tomlThe connector entries whose App matches. Plus the pools they reference (a warning appears when a pool is shared with another app).
dictionary.tomlThe dictionary entries referenced by the app's screens (lookups, formats, labels).
screens.tomlThe app's screens.
menus.tomlThe app's menu tree.
dashboards.tomlThe app's dashboards.
charts.tomlThe charts referenced by the dashboards.
jobs.tomlThe app's Nomaflow jobs.
plugins/ (if any)The whole plugin directory (custom Python callables).

The zip is downloaded by the browser. For multi-app exports, tick the checkboxes on each row + Export selected.

Importing

Click ↑ Import app at the top of the Apps tab. A dialog asks for the zip file, then surfaces a diff preview before applying:

SectionWhat the preview shows
Will addEntities present in the zip and absent on the install.
Will replaceEntities present in both; the diff is shown inline ("label change", "extra column").
Will refuseIdentifier collisions across apps (e.g. zip's billing collides with an existing screen). Operator clears them before Confirm.

Confirm applies the import in one transaction. A failure rolls back; the install stays on its previous state.

For installations under version control, the easier path is a git patch on liberty-apps — the change is reviewable in the PR, partial imports are possible and rollback is git revert. The zip flow is for installs that don't share a git repo (vendor-shipped customer apps, demo data).


Packaged vendor apps

Some apps ship pre-built from Nomana-IT — NomaUBL (e-invoicing), NomaSX-1 (security maintenance), NomaJDE (JD Edwards admin). They follow the same convention: an app identifier, a menu tree, screens, dashboards. Installing one is a license-gated Import app — the license key's features.apps list controls which can be imported.

A packaged app is opaque to the customer in the sense that the content is delivered as-is and meant to be used as-is, but every entity is still visible and editable from the Settings UI. Customer-side modifications survive vendor upgrades when they sit in a separate customer namespace (billing-customer next to vendor's billing) — see Deployment → Upgrading for the recommended layout.


Permissions per app

The role engine integrates with apps via the <surface>:<app>:* wildcard pattern. A typical pair of roles for a two-app install:

RolePermissions
billing-usersql:billing-*:*, screen:billing:*, menu:billing:*, dashboard:billing-*
crm-usersql:crm-*:*, screen:crm:*, menu:crm:*, dashboard:crm-*

A user carrying both roles sees both workspaces. A user carrying only billing-user sees the Billing workspace and isn't aware of the CRM one. See Roles & permissions.


Tips & best practices

  • Pick app identifiers early. Renaming an app triggers a propagation rewrite, but the simpler path is to start with the right name. Renames are supported through the Apps editor's Rename action.
  • Use the default app for single-tenant installs. Don't manufacture an app identifier just to fill the field.
  • Group connectors per app even when sharing a pool. A billing-invoices + billing-credits querying the same default pool is clearer than invoices + credits.
  • Keep dictionary entries shared. A currency lookup defined once and referenced from billing and crm is preferable to two parallel definitions.
  • Export before a risky vendor upgrade. A billing.zip snapshot of the current state is a one-click rollback path if the new vendor version regresses.

Under the hood

Each entity's App field is stored on the entity itself in the per-section TOML. App metadata (display name, icon, order) lives under dictionary.tomlapps. Operators do not edit these files by hand; the Settings UI is the canonical interface. The export / import flow round-trips through zip files containing the same TOML excerpts; the Apps tab is the canonical surface for that too.


What's next

  • Plugins — custom Python callables packaged with apps.
  • i18n — adding languages and per-app label packs.
  • Deployment → Upgrading — moving customer customisations across framework and vendor-app upgrades.