Skip to main content

Python server

Liberty Next ships as a self-contained wheel on PyPI — the React frontend bundle is baked in. One pipx install lands the server + four companion CLIs on the PATH. pipx inject adds the licensed apps (Nomasx-1 / Nomajde / Nomaflow) into the same venv when applicable. No clone, no npm, no virtualenv to manage by hand.

Pick the right shape first

This page is the Docker-averse single-host install — a laptop trial, a developer box, a tiny VM where Docker is overkill. For production / multi-user environments the Full Docker layout bundles Postgres, Traefik, pgAdmin and Portainer behind one Compose file — that's what the licensed bundles deploy against in production. The pipx path supports the same licensed apps, but you bring the Postgres yourself.


At a glance

Requirements
Python 3.12 · pipx · Postgres 13+ (for licensed apps)
Default database
SQLite (./liberty.db) — point at Postgres via the POSTGRES_* env vars; licensed apps need real Postgres
Default port
http://localhost:8000 (SPA + REST API)
First-run time
~5 minutes from pipx install to admin login (with licensed apps); ~1 minute for the framework alone

Step 0 — Prerequisites

ToolVersionNotes
Python3.12The wheel pins python_requires>=3.12.
pipxlatestbrew install pipx && pipx ensurepath on macOS; apt install pipx on recent Debian/Ubuntu; python3 -m pip install --user pipx && python3 -m pipx ensurepath everywhere else.
Postgres13+Any reachable instance — local install (brew install postgresql@16, apt install postgresql), a managed Postgres (RDS, Cloud SQL, Aiven) or your own server. Superuser access is needed for the bootstrap (the bundled deploy-databases job creates per-app roles + databases). Skip Postgres only if you want SQLite-only — but then Nomasx-1 / Nomajde / Nomaflow won't work; they need real Postgres.
liberty-apps wheel (licensed only)matching framework versionliberty_apps-X.Y.Z-py3-none-any.whl from your NOMANA-IT delivery. Skip if you only want the open framework.

The whole flow below assumes a Linux or macOS shell. Windows works through WSL2 — the same commands, the same wheel.


Step 1 — Install the framework (+ inject the licensed apps)

# The framework — required.
pipx install liberty-next

# The licensed bundle — only if you have a delivery wheel.
# `inject` adds it to the SAME pipx venv as liberty-next so the framework can import it.
pipx inject liberty-next /path/to/liberty_apps-7.0.X-py3-none-any.whl

pipx creates an isolated virtualenv under ~/.local/pipx/venvs/liberty-next and exposes entry points on the PATH:

CommandSourcePurpose
liberty-nextframeworkThe server — FastAPI backend + the bundled React SPA on the same port.
liberty-adminframeworkUser / role / DB management. init-db, run-install-jobs, set-password, reset-admin-password, create-user.
liberty-licenseframeworkLicense key inspection (verify, expiry, included products).
liberty-cryptoframeworkEncryption helpers — produce ENC: blobs for inline use in TOML.
liberty-appslicensed apps wheel (only after pipx inject)Materialiser that copies the wheel's config/ + plugins/ into the target directory.

Verify:

liberty-next --version
liberty-apps --version # only after `pipx inject`

Step 2 — Persistent env vars

Two secrets are mandatory; the rest are conventions. Add the block below to your shell profile (~/.bashrc, ~/.zshrc) so every shell sees the same values — the master key MUST stay constant across boots or every encrypted value on disk becomes unreadable.

~/.bashrc (or ~/.zshrc)
# ── Required — generate ONCE, never rotate without a plan ──────────────────
# AES-256-GCM key that encrypts pool passwords, license JWT, AI key, OIDC secret
# at rest in app.toml + connectors.toml.
export LIBERTY_MASTER_KEY="$(openssl rand -base64 32 | tr -d '\n=+/')"

# JWT signing key — rotating invalidates every active session.
export LIBERTY_JWT_SECRET="$(openssl rand -base64 48 | tr -d '\n=+/')"

# ── Postgres credentials ─────────────────────────────────────────────────────
# Read by liberty-admin init-db's _seed_default_pool to write [pools.default]
# in connectors.toml with the encrypted password. Skip if you only want SQLite.
export POSTGRES_PASSWORD="your-postgres-superuser-password"
export POSTGRES_USER="liberty"
export POSTGRES_HOST="localhost"
export POSTGRES_PORT="5432"
export POSTGRES_DB="liberty"

# ── Apps dir — where liberty-apps install materialises the licensed bundle ──
export LIBERTY_APPS_DIR="$HOME/.local/share/liberty-next/apps/config"
VariableEffect
LIBERTY_MASTER_KEYAES-256-GCM key. Decrypts ENC:… blobs in app.toml / connectors.toml — pool passwords, license JWT, AI key, OIDC client secret. Generate ONCE. Losing it = every encrypted value on disk becomes unreadable; rotating it = the framework refuses to start until every ENC: value is re-encrypted with the new key.
LIBERTY_JWT_SECRETSigns the JWT cookies issued at login. Rotating it invalidates every active session — every user is forced to sign in again.
POSTGRES_*Read by liberty-admin init-db's _seed_default_pool helper. The framework writes [pools.default] in connectors.toml with the password encrypted (using LIBERTY_MASTER_KEY). Set these BEFORE the first init-db — re-running later does not re-encrypt the password.
LIBERTY_APPS_DIRWhere the per-section TOMLs and the licensed-app plugins land. Created on demand.
LIBERTY_LICENSE_KEY is no longer an env var

Earlier docs (and some README versions) listed LIBERTY_LICENSE_KEY here. That env var is gone — the license key is now set via Settings → App → License once the UI is up, encrypted at rest in app.toml with the install master key (AES-256-GCM, ENC: prefix). The Anthropic API key and the OIDC client secret follow the same pattern. See App settings. The env var still works as a ${VAR} reference in app.toml for installs that prefer secret-manager storage — but the canonical path is the UI.

For a systemd-managed install, put the same block in EnvironmentFile=/etc/liberty/secrets.env (see Step 8 — Run under systemd below) — mode 0640, owned root:liberty.


Step 3 — Bootstrap Postgres + the framework DB

The install.sh script handles this automatically in the Docker layouts. For pipx, run the equivalent two SQL statements once + liberty-admin init-db.

Create the Postgres role + database (one-time)

# Replace 'postgres' with whichever superuser your install uses.
psql -h $POSTGRES_HOST -U postgres -c \
"CREATE ROLE $POSTGRES_USER LOGIN SUPERUSER PASSWORD '$POSTGRES_PASSWORD';"

psql -h $POSTGRES_HOST -U postgres -c \
"CREATE DATABASE $POSTGRES_DB OWNER $POSTGRES_USER;"

Skip both if your role + database already exist. The liberty role is created SUPERUSER so the later deploy-databases job can CREATE ROLE and CREATE DATABASE for the licensed apps. If your policy forbids superuser, grant CREATEROLE + CREATEDB instead:

ALTER ROLE liberty CREATEROLE CREATEDB;

Bootstrap the framework schema

liberty-admin init-db

What init-db does:

ActionWhat
Resolve [pools.default] from POSTGRES_* env.The _seed_default_pool helper reads POSTGRES_PASSWORD / _USER / _HOST / _PORT / _DB and writes a [pools.default] block to connectors.toml with the password encrypted using LIBERTY_MASTER_KEY.
Connect via [pools.default].The framework opens the connection it just configured.
Run schema migrations.Additive — creates auth tables, Nomaflow run-history tables, lifecycle metadata on the configured pool. Idempotent: existing rows are left alone.
Generate the admin user.Creates the admin superuser with a freshly-generated random password. Prints the password ONCE on stdout — capture it; it isn't stored anywhere recoverable.
liberty-admin init-db output (excerpt)
✔ Pool 'default' seeded → [pools.default] in connectors.toml (encrypted with LIBERTY_MASTER_KEY)
✔ Schema migrated (auth, nomaflow, lifecycle)
✔ Admin user created
username: admin
password: 8xK2pQrM9vTzB4nF ← only shown once; capture it

If you ever lose the admin password, run liberty-admin reset-admin-password — it generates a fresh random value and prints it once.


Step 4 — Materialise the licensed apps (licensed only)

Skip this step if you only want the open framework.

mkdir -p "$LIBERTY_APPS_DIR"
liberty-apps install --target "$LIBERTY_APPS_DIR"

What lands on disk:

$LIBERTY_APPS_DIR/
├── connectors.toml ← nomasx1 / nomajde / jdedwards connector blocks
├── dictionary.toml ← every dictionary entry
├── menus.toml ← per-app menu trees
├── screens.toml ← per-app screen definitions
├── dashboards.toml
├── charts.toml
├── theme.toml
├── nomasx1-reference.tar.gz ← curated reference bundle (loaded by a job below)
└── ../plugins/
├── nomasx1/ ← Python package with the security / license / SoD / audit callables
└── nomaflow/
└── jobs.toml ← the bundled job catalogue (deploy-databases, daily syncs, …)

Operator-edited TOMLs are preserved by default on re-install. Pass --force-config to overwrite with vendor defaults (typically after restoring from a vendor wheel upgrade).


Step 5 — Run the install-time jobs (licensed only)

install.sh ends by running install-apps.sh inside the Docker container; the pipx equivalent is a single command.

liberty-admin run-install-jobs

This walks every job tagged install_step in ${LIBERTY_APPS_DIR}/../plugins/nomaflow/jobs.toml and runs them in declared order:

JobWhat it does
deploy-databasesCreates the nomasx1 + nomajde Postgres roles + databases (using the liberty superuser via [pools.default]). Idempotent — skips what already exists.
init-schemaRuns the alembic migrations on the nomasx1 schema. Idempotent.
import-referenceLoads the curated reference bundle (nomasx1-reference.tar.gz) into the SoD / settings tables. Idempotent — re-running with replace = false only inserts missing rows.

The command is idempotent end-to-end — re-run if anything fails partway through; it skips jobs whose previous run finished SUCCEEDED.

Full job reference (parameters, when to re-run, edge cases): Nomaflow → Bundled jobs.


Step 6 — Start the server

liberty-next

The server binds on http://localhost:8000 (override with LIBERTY_PORT) and serves the SPA at / and the REST API under /api/* and /admin/*. Sign in as admin with the password printed in Step 3 — the Connectors catalogue lands as the home page.


Step 7 — Finish via the UI

The runtime secrets are no longer env vars. Once the SPA is up, open Settings → App in the sidebar and fill in:

SectionWhat to set
LicensePaste the vendor-signed RS256 JWT. Encrypted at rest in app.toml with LIBERTY_MASTER_KEY. Connector registry rebuilds on save — licensed connectors that were filtered out at boot reappear immediately.
AI Assistant → Anthropic API key (optional)sk-ant-…. Encrypted at rest. Enables the in-app chat.
OpenID Connect (optional)Discovery URL + client ID + client secret + claim mappings. Encrypted at rest. SSO via Keycloak / Okta / Auth0 / Azure AD / Google.

The full editor walkthrough: App settings.

For the jdedwards source Nomasx-1 reads from, open Settings → Pools → jdedwards and fill in the Oracle JDBC URL + read-only credentials. The framework's LIBERTY_MASTER_KEY encrypts the password before writing back to connectors.toml.


What's installed — recap

After the seven steps above:

ComponentLives inNotes
Framework + plugins venv~/.local/pipx/venvs/liberty-next/Includes the liberty_apps package if you injected it.
CLIs on the PATH~/.local/bin/{liberty-next,liberty-admin,liberty-license,liberty-crypto,liberty-apps}Exact path depends on your PIPX_BIN_DIR.
Framework DBPostgres liberty database on $POSTGRES_HOSTAuth, Nomaflow run history, lifecycle.
Licensed-app DBsPostgres nomasx1 + nomajde databasesCreated by the deploy-databases job.
Licensed-app config$LIBERTY_APPS_DIR/TOMLs + reference bundle.
Licensed-app plugin code$LIBERTY_APPS_DIR/../plugins/The nomasx1 Python package + nomaflow/jobs.toml.
Secrets at restapp.toml / connectors.toml as ENC: valuesLicense JWT, Anthropic key, OIDC secret, pool passwords. Decrypted at startup with LIBERTY_MASTER_KEY.
Master key + JWT secret + Postgres passwordenv vars (or systemd EnvironmentFile)Never written to TOML on disk.

Step 8 — Run under systemd

For an unattended server use a system user, an EnvironmentFile for the secrets, and Restart=on-failure so the unit recovers from transient errors.

Create a dedicated user and the secrets file:

sudo useradd --system --create-home --shell /usr/sbin/nologin liberty
sudo install -d -m 0750 -o liberty -g liberty /etc/liberty
sudo install -d -m 0750 -o liberty -g liberty /var/lib/liberty-next
sudo install -d -m 0750 -o liberty -g liberty /etc/liberty-next
/etc/liberty/secrets.env (mode 0640, root:liberty)
LIBERTY_MASTER_KEY=<paste the openssl rand -base64 32 output>
LIBERTY_JWT_SECRET=<paste the openssl rand -base64 48 output>

POSTGRES_PASSWORD=<your-postgres-superuser-password>
POSTGRES_USER=liberty
POSTGRES_HOST=db.example.com
POSTGRES_PORT=5432
POSTGRES_DB=liberty

LIBERTY_APPS_DIR=/etc/liberty-next/
LIBERTY_PORT=8000

Install the wheel system-wide (one virtualenv, all users get the CLIs):

sudo PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install liberty-next
# licensed only:
sudo PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx inject liberty-next /path/to/liberty_apps-7.0.X.whl

Run the bootstrap once as the liberty user with the env file sourced:

sudo -u liberty bash -c 'set -a; . /etc/liberty/secrets.env; set +a; liberty-admin init-db'
# licensed only:
sudo -u liberty bash -c 'set -a; . /etc/liberty/secrets.env; set +a; liberty-apps install --target $LIBERTY_APPS_DIR && liberty-admin run-install-jobs'

Then the unit:

/etc/systemd/system/liberty-next.service
[Unit]
Description=Liberty Next
After=network-online.target postgresql.service
Wants=network-online.target

[Service]
Type=simple
User=liberty
Group=liberty
WorkingDirectory=/var/lib/liberty-next
EnvironmentFile=/etc/liberty/secrets.env
ExecStart=/usr/local/bin/liberty-next
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/var/lib/liberty-next /etc/liberty-next

[Install]
WantedBy=multi-user.target

Enable + start:

sudo systemctl daemon-reload
sudo systemctl enable --now liberty-next
sudo journalctl -u liberty-next -f

Verify the install

CheckHow
Server is upcurl -fsS http://localhost:8000/info returns a JSON payload with the framework version + the loaded-apps list.
OpenAPI loadshttp://localhost:8000/docs — the full REST surface is browsable.
SPA rendershttp://localhost:8000/ — the sign-in screen appears.
Admin can sign inSign in as admin with the password printed by init-db.
Licensed apps loaded/info shows screens.apps containing nomasx1 / nomajde. The app switcher in the SPA's top bar lists them.
License activeSettings → App → License badge reads configured; curl /info | jq '.license.mode' returns "full".
AI assistant (optional)After setting the Anthropic key in Settings → App → AI, open /chat — the input field is enabled.

Upgrade

The Liberty framework and the licensed bundle upgrade on separate cadences.

Framework

pipx upgrade liberty-next
sudo systemctl restart liberty-next # if running under systemd

pipx upgrade swaps the wheel in the isolated venv. The new framework runs init-db again on the first start — idempotent, additive only.

To pin a specific release:

pipx install --force liberty-next==<version>

Licensed apps

pipx inject --force liberty-next /path/to/liberty_apps-NEW-VERSION.whl
liberty-apps install --target "$LIBERTY_APPS_DIR"
liberty-admin run-install-jobs # picks up new install-step jobs (e.g. schema deltas)
sudo systemctl restart liberty-next

The --force flag on pipx inject replaces the previously-injected wheel. liberty-apps install preserves operator edits in $LIBERTY_APPS_DIR/*.toml unless --force-config is passed.

For the full upgrade picture (Docker + pipx + Swarm + rollback): Upgrading.


Troubleshooting

SymptomCauseFix
LIBERTY_JWT_SECRET is required on startThe env var didn't propagate.Re-export it in the same shell, or add it to the systemd EnvironmentFile.
Address already in use on port 8000Another process holds the port.Set LIBERTY_PORT=8001 (or whatever's free).
init-db fails with permission denied for databaseThe liberty Postgres role isn't a superuser / lacks CREATEDB.ALTER ROLE liberty SUPERUSER (or CREATEROLE CREATEDB).
Sign-in says "invalid credentials"Lost the bootstrap admin password.liberty-admin reset-admin-password — prints a fresh random value once.
liberty-apps: command not found after pipx injectpipx inject doesn't auto-expose the wheel's CLI entry points unless the wheel declares them.The shipped wheel does declare liberty-apps — re-run pipx ensurepath and reopen the shell. If still missing, call ~/.local/pipx/venvs/liberty-next/bin/liberty-apps directly.
run-install-jobs fails with target_connector nomasx1 not configuredliberty-apps install step skipped or wrong LIBERTY_APPS_DIR.Run liberty-apps install --target "$LIBERTY_APPS_DIR" and re-run run-install-jobs.
Config TOML edits don't show upWrong LIBERTY_APPS_DIR in the running shell vs. the one used at boot.liberty-next logs the resolved path on boot — grep for apps_dir=.
liberty-next not found after installpipx's bin dir isn't on the PATH.Run pipx ensurepath and reopen the shell, or call ~/.local/bin/liberty-next directly.
Cannot decrypt ENC:… on first startLIBERTY_MASTER_KEY changed (or wasn't set when init-db ran).Either restore the original key, or liberty-crypto rewrap to re-encrypt every ENC: value with the current key.

What's next

  • App settings — the Settings → App editor; masked secrets, what's live vs needs restart.
  • Deploy prebuilt apps — the same Nomasx-1 / Nomajde / Nomaflow story for the Docker path.
  • Nomaflow → Bundled jobs — reference for the install-step jobs run-install-jobs invokes.
  • Docker — the Compose-based alternative. Pick the Full layout for production / multi-user setups.
  • Production — hardening checklist, OIDC, scheduler pin, backup cadence.
  • Upgrading — the wider upgrade picture (Compose + pipx + Swarm side by side).