Packages
The Package page (Liberty Next → Deploy → Package) is Liberty's built-in deployment mechanism: an operator on the source install picks a few screens / menu items / dashboards as seeds, the framework walks every reference they reach (queries, connectors, dictionary entries, charts), and the result ships as a ZIP. On the target install, the same screen accepts a ZIP, previews what it would change, and applies it to the live config — followed by an automatic reload. With a target instance declared, the package can also be promoted straight to it, without handling the ZIP.
The page offers three views, picked from cards at the top: Build package, Import package and Promotion. It is reserved to superusers.
This is the standard staging → production path. It's also the right tool for cross-customer reuse: build a "starter" package of curated screens, hand it to a fresh install, apply with replace_all, done.
When to use which
| Situation | Use |
|---|---|
| Promote screens / dashboards from staging to production. | Build on staging, apply on prod with overwrite. |
| Ship a curated set of screens to a brand-new install. | Build, apply with replace_all. |
| Add a few new screens to an existing customer install without touching their edits. | Build, apply with merge. |
| Ship a fresh release of a vendor-maintained app where the customer has local edits on a few screens. | Build, apply with overwrite — entities flagged override = true on the target are preserved automatically. |
| Promote screens to production in one step, without the ZIP. | Promote to… on Build — see Promotion. |
| Promote one Nomaflow job between environments. | Pick it as a seed under nomaflow jobs, or use the Export icon on the job's line of the Jobs page. |
| Deliver a full licensed bundle (Nomasx-1 + Nomajde plugins + config). | Not this. Use the liberty-apps wheel installer — see Installation → Deploy prebuilt apps. |
The Package screen ships declarative config — TOML files. It does not ship Python code, Java JARs, plugin packages or database content. For those, the wheel installer is the canonical channel.
The Build view
The left panel lists every seed candidate on the install — screens, menu items, dashboards. The right panel shows the dependency closure of whatever is ticked on the left.
Picking seeds
Three sections in the left panel:
| Section | Drawn from |
|---|---|
| Screens | Every screen the install knows — grouped by app. |
| Menu items | Every menu item, by app. |
| Dashboards | Every dashboard, by scope. |
| Explorer models | The Data Explorer models, with their shipped analyses and dashboards. |
| nomaflow jobs | The jobs of jobs.toml. |
| Notification rules | The rules of Liberty Next → Build → Notifications. |
| Shared actions | The actions reused by several screens. |
| Assistant (starters, playbooks) | What an application ships for the AI assistant — one entry per application. |
A search box at the top filters the visible list by name / label / scope. The per-section select all tick respects the filter — narrow first (e.g. type audit), then tick select all to grab only the matching screens.
The seed picker is bulk-friendly: hold the filter at a customer name, tick select all and you've staged a per-customer slice. No need to click each screen one by one.
What the dependency walker does
The moment you tick any seed, the right panel kicks off the walker (POST /admin/find-dependencies on the backend). For every seed, the walker resolves:
| Kind | What's pulled |
|---|---|
screen | The screen's main query, all referenced sub-queries (from screen actions, nested tabs, conditional fields), every dictionary entry that backs a column, every lookup the dialog uses, every sequence and enum, the connector each query belongs to (and its pool). |
menu_item | The screen, query, endpoint, dashboard or job the menu item targets, then the closure of that. |
dashboard | Every chart on the dashboard, every query each chart reads from, the connectors. A dashboard named <app>.<id> is looked up in that app. |
notification_rule | The screen or the jobs its trigger names. |
The closure is recursive but bounded (default depth = 50). Cycles are detected and short-circuited.
The default-excluded kinds
The Dependencies panel pre-ticks every dep except three kinds:
| Kind | Why default-excluded |
|---|---|
| Connectors | The target install almost always already has its own connector with the right pool / credentials. Shipping the source's connector would clobber the target's secrets. |
| Pools | Same — pools carry DB credentials. The target's pool is the one you want. |
| API endpoints | Same — endpoints carry auth headers / Bearer tokens specific to the source install's wiring. |
This makes the typical "promote screens between envs" workflow safe out of the box: nothing that carries secrets ships unless you opt in. Tick the Connectors group manually only when the target genuinely has none — typically a brand-new install or a per-customer staging spin-up.
The frontend remembers your per-dep ticks; the select all / deselect all toggles at the group level make it cheap to flip every connector on or off at once.
Building the ZIP
Click Download ZIP and the framework runs POST /admin/build-package, returning a ZIP with up to six TOMLs:
| File | When present |
|---|---|
connectors.toml | When at least one connector / pool / query / API endpoint is included. Connector definitions are trimmed — only the queries / endpoints in the closure are kept, the rest are dropped. |
dictionary.toml | When the closure pulls in any dictionary entry, lookup, sequence or enum. Per-connector dictionary blocks are nested under [connectors.<name>]. |
screens.toml | When at least one screen is in the closure. Grouped by app under [screens.<app>]. |
menus.toml | When at least one menu item is in the closure. Grouped by app under [menus.<app>.items]. |
charts.toml | When at least one chart is in the closure. |
dashboards.toml | When at least one dashboard is in the closure. |
Plus one always-present file:
| File | What it carries |
|---|---|
MANIFEST.md | Human-readable summary — what's in the ZIP, the seeds, per-kind counts, the env vars ${VAR} the package references (and that the operator must set on the target), the missing references (closure couldn't resolve — usually a typo or a stale rename on the source), and the operator-facing instructions. |
The MANIFEST is the first thing to open on the target before applying — it tells you what to expect and what's still on you to wire (env vars, secrets, master key).
What's NOT in the package
Worth knowing so you don't try to ship through this channel:
| Not included | Where it lives instead |
|---|---|
| Liberty Next → Administer → App | Per install — application settings, licence, sign-in. Never bundled. |
auth.toml / DB-stored users | Per-install — carries Argon2 password hashes. Never bundled. |
| Python plugins | Shipped with the liberty-apps wheel or the plugin's own release channel. |
| Database content | Use a DB dump (or a bundled preload job). |
The Import view
Pick the Import package card. The view offers three strategies — Merge, Overwrite (recommended for upgrades), Replace all — and a file picker.
The three strategies
The three columns map one-to-one to the radio cards in the UI:
| Strategy | Per-entity behaviour | Use when |
|---|---|---|
merge | NEW entities land. EXISTING entities on the target are kept as-is. | Adding new things without disturbing customer edits. The package adds; it never overwrites. |
overwrite | NEW entities land. EXISTING entities are replaced by the package version — EXCEPT when the target entity is flagged override = true (then preserved, reported separately). | The standard upgrade path. The package becomes the new truth for the entities it carries. Entities elsewhere on the target (those not in the package) are untouched. |
replace_all | For every TOML in the ZIP, the whole target file is replaced by the package's version. EVERY entity not in the package is dropped from that file. Files the package doesn't touch are untouched. | Deploy-from-scratch / vendor reset / a curated baseline. |
The override = true escape hatch
A customer install sometimes needs to fork a vendor entity — patch one screen's columns, swap a query — and keep that fork across vendor upgrades. Add override = true to the entity's TOML block:
[screens.crm.customers]
label = "Customers (customer fork)"
override = true # vendor `overwrite` imports will skip this entity
query = "customers_v2_custom"
# ... rest of the customer's edit
On the next overwrite import, the framework sees the flag and leaves the entity alone. The import report shows it under preserved overrides so the operator sees exactly which entities survived the vendor push.
Two limits worth knowing:
mergealready skips existing entities — the flag is redundant there.replace_alldrops the entire file — overrides are NOT preserved. Use it knowingly.
Preview, then apply
As soon as a ZIP is picked — and again when the strategy changes — the page previews the import; nothing is written until Apply.
| Part | Content |
|---|---|
| State chips | The count per state: New, Changed, Identical, Kept (override), Skipped, Removed, Errors — with From <install> · <version>, the package's origin. |
| Warnings | A package from a newer Liberty Next — settings this instance does not know may be rejected: upgrade it first; encrypted values this instance cannot decrypt — another master key: set them again after the import. |
| Entities | One card per file. What changes comes first; Show identical adds the rest. A changed entity opens on its fields: Here now and After the import. |
| Apply N change(s) | Writes the changes, reloads, then shows the same plan as the result with Applied + reloaded. When the instance already holds every entity as the package has it, there is nothing to apply. |
What travels is merged with care:
- a connector's queries, tables, sequences, lookups and endpoints are imported one by one: importing a package never removes the target's other queries;
- a job that already exists on the target keeps its schedule, enabled flag, timezone, presets, alerts, retry, log level and timeout; a new job arrives disabled;
- the package is built from the files as written: unset values and
${VAR}references travel as they are.
If the reload fails after a successful apply, the file changes are kept on disk; run Reload config (at the top of any Liberty Next editor page) once the underlying issue is fixed.
Promotion — straight to another instance
A target instance is another Liberty Next install — production, a customer's staging — reached over its API.
Package → Promotion lists the targets: Name, Label, URL (the target's root address, with its sub-path), Account and Password of a local superuser there — the password stored encrypted —, Check the TLS certificate, Timeout (s). Test signs in on the target: Signed in on prod · 7.0.64. Save targets keeps them.
Promote to…, beside Download ZIP on Build, sends the selection to a target:
- pick the Target, the Strategy and an optional Comment;
- the target previews the import at once — the same plan as above, computed on the target: Preview on prod — nothing is written until you apply;
- Apply on prod applies it there.
Connectors, pools and API endpoints stay the target's unless ticked: a connector's queries still travel, merged into the target's connector. The same Promote to… on Liberty Next → Deploy → Changes sends an approved change package.
The History of the Promotion view lists the promotions applied from this instance — when, by whom, to which target, what, and the outcome; a row opens on the target's report.
Secrets and environment variables
The package is plaintext TOML. Two patterns let the source's secrets travel without leaking the secret material:
| Pattern | What happens at build / apply |
|---|---|
Encrypted secrets — password = "ENC:base64==" | The ciphertext lands verbatim in the package. The target must have the same master key (LIBERTY_MASTER_KEY) as the source — otherwise decryption fails on first use. |
Env var references — password = "${DB_PASSWORD}" | The literal string ${DB_PASSWORD} is in the package. The operator must set the env var on the target before reloading. MANIFEST.md lists every ${VAR} the package references. |
Pick one pattern per install — mixing both inside a single config gets confusing. Encrypted secrets travel best across containerised deployments; env vars travel best across orchestrators (Kubernetes Secrets, Docker --env-file, systemd EnvironmentFile).
What you should NOT ship
| Don't | Why |
|---|---|
Move auth.toml between installs via the Package screen. | The screen ignores auth.toml. Move users via the dedicated Users admin (or invite them per install). |
| Expect a package to carry Liberty Next → Administer → App. | It does not — application settings, licence and sign-in stay on each install. |
Ship a package with replace_all to a customer who has local edits without a heads-up. | The customer's screens / queries / dictionary entries are dropped. There is no automatic undo — keep a backup of the target's config tree first. |
Inspect-without-export
The Build tab's right panel is also a standalone dependency inspector. Tick a single screen, look at the closure — that's exactly what the screen would carry into a package, and exactly what would break if you renamed / deleted the screen. No need to click Download ZIP — the inspection is the value.
Liberty also exposes a dedicated Find dependencies modal from any screen / menu item / dashboard list (the icon next to the rename button). Same backend (POST /admin/find-dependencies), simpler shape — just the closure tree for the one entity you opened it on.
Common pitfalls
| Mistake | Symptom | Fix |
|---|---|---|
Forgot to set a ${VAR} listed in MANIFEST.md before reloading the target. | A connector / query fails at first use with a missing-substitution error. | Read MANIFEST.md before applying. Set the env vars in the target's environment file / systemd unit / Docker env. |
Source and target carry different master_keys. | Encrypted ENC:... values fail to decrypt; the affected connector errors at first use. | Sync the master key (or rotate the encrypted values on the target — see Build → Secure → Encrypted secrets). |
Used replace_all to apply a small slice — wiped customer-specific screens. | Customer screens missing from the target after import. | Restore the previous TOML from your backup. Re-apply with overwrite next time. |
Bundled connectors.toml (default-excluded) without checking pool names match the target. | Target's pools change unexpectedly; pre-existing queries error at next reload. | Leave connectors / pools default-excluded unless the target is brand-new. Or rename pools on the source before bundling. |
| Built from a source with unresolved references (broken rename / deleted query). | MANIFEST.md lists items under Missing references. | Fix on the source (rename / delete / restore) and rebuild. The target won't get the broken references — but it also won't get the screens / queries that referenced them. |
| A warning says the package holds values this instance cannot decrypt. | The source and the target use different master keys. | Set those values again on the target after the import, or share the master key. |
Import landed but Applied + reloaded is missing — a reload failed warning showed. | Disk has the new config; in-memory state is stale. | Reload manually via Reload config (at the top of any Liberty Next editor page) once the underlying issue is fixed (often a syntax error in an unrelated TOML the reload caught). |
Programmatic access
The two endpoints behind the UI are usable headless — useful for CI / scripted promotion:
curl -X POST \
-H "Authorization: Bearer <superuser-token>" \
-H "Content-Type: application/json" \
-d '{"seeds":[{"kind":"screen","name":"customers","scope":"crm"}]}' \
https://<source>/admin/build-package \
--output liberty-package.zip
curl -X POST \
-H "Authorization: Bearer <superuser-token>" \
-F "package=@liberty-package.zip" \
"https://<target>/admin/import-package?strategy=overwrite"
build-package accepts an optional include array to filter the closure (same shape as the UI's per-dep checkbox state). import-package accepts strategy as a query param (merge | overwrite | replace_all) and dry_run=true for the preview. Both endpoints require superuser auth. POST /admin/promotion/push sends a package or an approved change package to a declared target.
What's next
- Deploy prebuilt apps — when the package channel isn't enough (full bundle = config + Python plugins), use the
liberty-appswheel installer. - Nomaflow → Bundled jobs — the ready-made jobs that ship with
liberty-apps, including thenomasx1-import-referencejob that loads a curated reference bundle. - Encrypted secrets — what
ENC:values are, how to rotate them when promoting across installs with different master keys.