Metadata-Version: 2.4
Name: liberty-next
Version: 7.0.40
Summary: Liberty Next — connector-driven low-code framework (SQL + API connectors, React admin UI, AI tool-use assistant, dependency-aware deployment packaging)
Author-email: Franck Blettner <franck.blettner@nomana-it.fr>
Maintainer-email: Franck Blettner <franck.blettner@nomana-it.fr>
License: Open framework (free); licensed connectors sold separately — see repository
Project-URL: Homepage, https://github.com/fblettner/liberty-next
Project-URL: Documentation, https://docs.nomana-it.fr/liberty/getting-started/
Project-URL: Source, https://github.com/fblettner/liberty-next
Project-URL: Issues, https://github.com/fblettner/liberty-next/issues
Project-URL: Releases, https://github.com/fblettner/liberty-next/releases
Project-URL: Container Registry, https://github.com/fblettner/liberty-next/pkgs/container/liberty-next
Project-URL: Deployment Configs, https://github.com/fblettner/liberty-next/tree/main/release
Keywords: low-code,framework,fastapi,react,sql,connector,postgres,oracle,dashboard,etl,scheduler,openapi,anthropic,ai,tool-use,admin,toml
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: FastAPI
Classifier: Framework :: Pydantic :: 2
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Information Technology
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Database :: Front-Ends
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Office/Business
Classifier: Typing :: Typed
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: fastapi>=0.115
Requires-Dist: fastapi-cli>=0.0.7
Requires-Dist: uvicorn[standard]>=0.32
Requires-Dist: pydantic>=2.9
Requires-Dist: sqlalchemy[asyncio]>=2.0.36
Requires-Dist: asyncpg>=0.30
Requires-Dist: oracledb>=2.5
Requires-Dist: httpx>=0.27
Requires-Dist: anthropic>=0.40
Requires-Dist: authlib>=1.3.2
Requires-Dist: itsdangerous>=2.1
Requires-Dist: argon2-cffi>=23.1
Requires-Dist: python-jose[cryptography]>=3.3
Requires-Dist: cryptography>=42
Requires-Dist: tomli-w>=1.0
Requires-Dist: tomlkit>=0.13
Requires-Dist: openpyxl>=3.1
Requires-Dist: python-socketio>=5.11
Requires-Dist: fastapi-socketio>=0.0.10
Requires-Dist: apscheduler<4,>=3.11
Requires-Dist: watchfiles>=0.21
Requires-Dist: alembic>=1.13
Requires-Dist: psycopg2-binary>=2.9
Requires-Dist: ldap3>=2.9
Requires-Dist: weasyprint>=63.0
Requires-Dist: markdown>=3.7
Requires-Dist: jinja2>=3.1
Provides-Extra: dev
Requires-Dist: pytest>=8.3; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
Requires-Dist: pytest-html>=4.1; extra == "dev"
Requires-Dist: aiosqlite>=0.20; extra == "dev"
Requires-Dist: aiohttp>=3.10; extra == "dev"

# Liberty Next

[![PyPI](https://img.shields.io/pypi/v/liberty-next.svg)](https://pypi.org/project/liberty-next/)
[![Python](https://img.shields.io/pypi/pyversions/liberty-next.svg)](https://pypi.org/project/liberty-next/)
[![Docker](https://img.shields.io/badge/ghcr.io-liberty--next-blue?logo=docker)](https://github.com/fblettner/liberty-next/pkgs/container/liberty-next)
[![Release](https://github.com/fblettner/liberty-next/actions/workflows/release.yml/badge.svg)](https://github.com/fblettner/liberty-next/actions/workflows/release.yml)
[![Docs](https://img.shields.io/badge/docs-nomana--it.fr-blue)](https://docs.nomana-it.fr/liberty/getting-started/)

**Connector-driven low-code framework.** Configure SQL queries + HTTP endpoints in
TOML; Liberty derives schemas at query time, serves a React admin UI on the same
port, surfaces an Anthropic tool-use assistant for natural-language access, and
wraps everything in a structured-config builder + dependency-aware deployment
packager.

Declarative `connectors.toml` / `screens.toml` / `dictionary.toml` / `menus.toml` /
`charts.toml` / `dashboards.toml` files drive the runtime — schemas derived at query
time, no code-gen step, every field round-trippable through the structured editors
at **Settings → \<tab\>**.

## Quick links

- 📚 **Documentation** — <https://docs.nomana-it.fr/liberty/getting-started/>
- 💻 **Source** — <https://github.com/fblettner/liberty-next>
- 🐳 **Docker image** — <https://github.com/fblettner/liberty-next/pkgs/container/liberty-next>
- 🚀 **Deployment configs** (Compose + Swarm + helper scripts) — [`release/`](https://github.com/fblettner/liberty-next/tree/main/release)
- 🐛 **Issues** — <https://github.com/fblettner/liberty-next/issues>
- 📦 **Releases** — <https://github.com/fblettner/liberty-next/releases>

---

## Install

**Full guide:** <https://docs.nomana-it.fr/liberty/getting-started/>

Three routes — pick what fits.

### Docker Compose (recommended)

Customer hosts only need the `release/` directory — sparse-checkout pulls just that:

```bash
# 1. Sparse-clone — downloads ONLY release/ (no liberty/, no frontend/, no .git history of source)
git clone --depth 1 --filter=blob:none --sparse https://github.com/fblettner/liberty-next.git
cd liberty-next
git sparse-checkout set release
cd release

# 2. One-shot install (interactive picks light vs full; or use a flag)
./install.sh full

# 3. (Licensed customer) overlay the apps wheel — see release/README.md
./install-apps.sh /path/to/liberty_apps-X.Y.Z.whl
# Set the license key after install via Settings → App → License (encrypted at rest in app.toml).
```

Upgrade later with [`release/upgrade.sh`](release/upgrade.sh) — it pulls the newest
image (or `--tag X.Y.Z`), recreates the stack, waits for health, and reports the
framework + apps version change (also journaled in **Settings → History → Upgrades**).

The full deployment guide (TLS wiring, backups, upgrades, swarm, common ops) lives
in [`release/README.md`](release/README.md).

### PyPI

**Recommended — pipx** (isolates Liberty Next in its own venv; CLI commands stay on
your PATH; no risk of polluting system Python):

```bash
# Install pipx once if you don't have it:
#   macOS:    brew install pipx && pipx ensurepath
#   Linux:    sudo apt install pipx && pipx ensurepath
#                 # or:  python3 -m pip install --user pipx && python3 -m pipx ensurepath
#   Windows:  py -m pip install --user pipx && py -m pipx ensurepath

# Report rendering (markdown → PDF) uses WeasyPrint — install the system libs
# it links against. Skip if you don't plan to use the /api/reports endpoints.
#   macOS:    brew install pango
#   Debian/Ubuntu:  sudo apt install libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz0b libfontconfig1

pipx install liberty-next
liberty-next                      # → API + SPA on http://localhost:8000
```

This gives you every CLI tool the package ships (`liberty-next`, `liberty-admin`,
`liberty-connectors`, `liberty-migrate`, `liberty-license`, `liberty-crypto`) on the PATH,
each one routed through the same isolated venv. Upgrade with `pipx upgrade liberty-next`;
uninstall cleanly with `pipx uninstall liberty-next` (removes the venv + every shim,
leaves nothing behind).

**Plain pip** (only when pipx isn't an option — make a venv yourself to avoid breaking
system packages):

```bash
python3 -m venv ~/.local/liberty-venv
~/.local/liberty-venv/bin/pip install liberty-next
~/.local/liberty-venv/bin/liberty-next
```

First boot generates an `admin` password and prints it once — capture it from the
logs, then sign in at <http://localhost:8000>.

### Adding the licensed apps to a pipx install

For Docker, [`release/install-apps.sh`](release/install-apps.sh) handles everything.
For a pipx install, do it manually:

```bash
# 1. Inject the apps wheel into the SAME pipx venv as liberty-next
#    (delivered to licensed customers as liberty_apps-X.Y.Z-py3-none-any.whl):
pipx inject liberty-next /path/to/liberty_apps-X.Y.Z-py3-none-any.whl

# 2. Persistent secrets — generate ONCE and put in ~/.bashrc / ~/.zshrc.
#    Both must stay stable across restarts (rotating either breaks every encrypted
#    secret in app.toml + connectors.toml).
export LIBERTY_JWT_SECRET="$(openssl rand -base64 48 | tr -d '\n=+/')"
export LIBERTY_MASTER_KEY="$(openssl rand -base64 32 | tr -d '\n=+/')"

# 3. Postgres credentials — used by liberty-admin init-db to seed [pools.default]
#    AND by deploy-databases to inherit-and-set the nomasx1 / nomajde role passwords.
#    Skip if you only want SQLite for trial (licensed connectors need real pg).
export POSTGRES_PASSWORD="your-postgres-superuser-password"
export POSTGRES_USER=liberty
export POSTGRES_HOST=localhost
export POSTGRES_PORT=5432
export POSTGRES_DB=liberty
# SQLite fallback (no licensed connectors): export LIBERTY_DB_URL=sqlite+aiosqlite:///./liberty.db

# 4. Materialize the wheel's payload into a writable LIBERTY_APPS_DIR
mkdir -p ~/.local/share/liberty-next/apps
liberty-apps install --target ~/.local/share/liberty-next/apps/config
export LIBERTY_APPS_DIR=~/.local/share/liberty-next/apps/config

# 5. Bootstrap the framework DB + admin user
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;"
liberty-admin init-db    # seeds [pools.default] + creates 'admin' user, prints password

# 6. Run the install-time jobs (deploy-databases / init-schema / import-reference)
liberty-admin run-install-jobs

# 7. Start the server
liberty-next             # API + SPA on http://localhost:8000

# 8. Sign in as admin, then Settings → App → License → Set
#    (encrypted at rest in app.toml with LIBERTY_MASTER_KEY)
```

Upgrade the apps later by injecting a new wheel + re-running `liberty-apps install`
(operator-edited TOMLs are preserved; pass `--force-config` to overwrite). License /
AI api_key / OIDC settings stay in app.toml and survive the upgrade.

### From source (development)

```bash
git clone https://github.com/fblettner/liberty-next.git
cd liberty-next
python3.12 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest -v               # 1100+ tests
./start.sh init-config            # seed config/*.toml from the .example files
./start.sh init-db                # FIRST RUN: create the auth store + `admin` user (prints password)
./start.sh                        # build frontend + serve on :8000
./start.sh dev                    # same, with backend auto-reload
./start.sh frontend               # Vite HMR dev server on :5173 (pair with `./start.sh api dev`)
```

---

## What you get

| URL | Purpose |
|---|---|
| `/` | React SPA — admin UI (sign-in, workspace tabs, Settings, AI assistant) |
| `/docs` | **Swagger UI** — interactive API explorer |
| `/redoc` | **ReDoc** — print-friendly API reference (grouped by tag) |
| `/openapi.json` | OpenAPI 3 spec — generated from FastAPI routes + Pydantic models |
| `/api/*` | Public API surface (auth gates per route) |
| `/admin/*` | Operator-only endpoints — config CRUD, find-usages, packaging, AI scaffold-apply, … |
| `/info` | Public liveness + counts (connectors / screens / pools) — Docker `HEALTHCHECK` hits this |

---

## Configuration in 60 seconds

Eight TOML files under `config/` (or wherever `LIBERTY_APPS_DIR` points). Every file
is round-trippable through the structured editors at **Settings → \<tab\>**:

| File | What it carries | Editor |
|---|---|---|
| `app.toml` | App-level settings (host / port / log level / AI model / hot-reload) + encrypted secrets (license key, AI api_key, OIDC client_secret) | Settings → App |
| `connectors.toml` | DB pools + SQL connectors with named queries + API connectors with endpoints | Settings → Pools, Settings → Connectors |
| `dictionary.toml` | Shared + per-connector field metadata (labels / types / rules / lookups / sequences) | Settings → Dictionary |
| `screens.toml` | Screen definitions — per-app grids + dialog forms + actions + row menus | Settings → Screens |
| `charts.toml` | Saved chart specs referenceable from screens + dashboards | Settings → Charts |
| `dashboards.toml` | Widget grids with shared filters | Settings → Dashboards |
| `menus.toml` | Per-app navigation trees | Settings → Menus |
| `jobs.toml` | nomaflow ETL pipelines + scheduled jobs (per-step `op_kwargs`, retry, retention) | Nomaflow → Jobs |

Two secrets stay env-only: `LIBERTY_JWT_SECRET` (signs access / refresh tokens) and
`LIBERTY_MASTER_KEY` (the AES-256-GCM key that decrypts every `ENC:` value in app.toml +
connectors.toml). Everything else — license key, Anthropic API key, OIDC client secret,
pool / API-connector passwords — lives encrypted at rest in TOML and is edited through
the UI's masked-secret pattern. `${VAR}` / `${VAR:-default}` env references in TOML are
still expanded at load time for the few values an operator wants to keep externally
managed.

---

## Customer / vendor split

Liberty Next ships as an **open framework**. The customer-facing connectors + screens
+ dictionaries live in a separate apps repo (`liberty-apps`); the licensed ones
(nomasx1 / nomajde / nomaflow) are unlocked by an RS256 JWT set via **Settings → App →
License** (encrypted at rest in app.toml with `LIBERTY_MASTER_KEY`). Without a key
the framework runs in **restricted** mode — those connectors aren't loaded. Headless
installs can pre-seed the encrypted value with `liberty-crypto encrypt`.

The **Settings → Package** tab packages selected screens / menu items / dashboards
plus their full dependency closure (connectors / queries / DD entries / lookups / …)
into a ZIP for atomic deployment to another install. Each entity carries an
`override = true` flag operators can flip to mark customer customisations — the
import-package endpoint's `overwrite` strategy preserves flagged entities so vendor
upgrades don't clobber customer forks.

---

## Upgrade history & release notes

Every config save is versioned (content-addressed snapshots on the persistent config
volume), and **version changes of the software itself** are journaled too. On startup
the running container compares its installed version against the last one it recorded
and writes an `install` / `upgrade` entry — independently for the **framework**
(`liberty-next`) and the **licensed apps** (`liberty-apps`). So after a `docker compose
pull` (or [`release/upgrade.sh`](release/upgrade.sh)) you get a dated "7.0.38 → 7.0.39"
trail, with the apps bundle tracked separately from the framework.

- **Settings → History** — three toggles: **Files** (per-TOML version history with
  diff + restore), **Screens** (screen+dependency bundles), and **Upgrades** (the
  version timeline; selecting an entry renders that version's release notes inline,
  with a Framework / Apps badge).
- **Settings → Release notes** — the full changelog, EN + FR (FR falls back to EN),
  with a per-version table of contents and a Framework / Apps switch.

Release notes ship inside each wheel as `RELEASE.md` / `RELEASE.fr.md` and are served
by `GET /admin/release-notes`; the version timeline is `GET /admin/upgrades` (both
superuser-gated). The release workflow guarantees each released version has a
`## <version> — <date>` section (it inserts a stub + warns when one is missing), so the
Upgrades detail is never empty.

---

## Releasing

One GitHub Actions workflow, [`release.yml`](.github/workflows/release.yml), publishes
every release. **It runs automatically on every push to `main`.** No buttons, no tags,
no manual triggers.

### The flow

```
develop branch         →  work happens here, push freely, NOTHING triggers
                         ↓
                       PR develop → main, merge
                         ↓
main branch push       →  release.yml runs:
                          1. Reads pyproject.toml's version
                          2. If that version is already tagged → auto-bump patch (7.0.1 → 7.0.2)
                             Else use as-is (when you manually bumped for a major/minor)
                          3. Ensures liberty/RELEASE.md has a "## <version>" section
                             (inserts a dated stub + warns if missing), then commits the
                             bumped pyproject.toml + RELEASE.md back to main ([skip ci])
                          4. Builds + pushes multi-arch Docker to ghcr.io as <version> + :latest
                          5. Publishes sdist + wheel to PyPI
                          6. Tags v<version> + creates GitHub release with auto-notes
```

### Version control

- **Bugfix / patch release** (default): just merge to main. Workflow auto-bumps `7.0.1 → 7.0.2`.
- **Minor release** (`7.0.x → 7.1.0`): bump `version = "7.1.0"` in `pyproject.toml` in any commit before merging. Workflow honours it.
- **Major release** (`7.x → 8.0.0`): same — bump in pyproject.toml.

### Setting up the repo (one-time)

1. **Branch protection on `main`** (recommended):
   <https://github.com/fblettner/liberty-next/settings/branches> → add a rule for
   `main` → require pull request before merging. This forces the develop → main flow.

2. **PyPI token**: <https://pypi.org/manage/account/token/> → create a token
   (account-scoped first time; scope to `liberty-next` after the first release).
   Add it as a repo secret:
   <https://github.com/fblettner/liberty-next/settings/secrets/actions> →
   Name: `PYPI_API_TOKEN`, Value: `pypi-…`.

3. **Docker image visibility**: after the first push to ghcr.io, the image lands
   private. Make it public at <https://github.com/fblettner?tab=packages> →
   liberty-next → Package settings → Change visibility → Public. (One-time.)

### Manual override

The workflow also has a `workflow_dispatch` trigger if you need to re-publish a
specific version (rebuild after a base-image CVE, etc.). Go to
<https://github.com/fblettner/liberty-next/actions/workflows/release.yml> →
Run workflow → optional version input.

### Recovering from a failed publish

- **Pre-publish failure** (build, Docker push) — fix and re-trigger; nothing is consumed.
- **PyPI publish failure** (the only irreversible step) — the version is burned. Bump
  `pyproject.toml` to the next version and push again.
- **Tag push fails with "refusing to allow a GitHub App to create or update workflow …
  without `workflows` permission"** — this happens *only* on the release right after a
  `.github/workflows/*` file changed: the `GITHUB_TOKEN` the bot uses can't push a ref
  that carries a workflow-file change, so the final tag step is rejected (the Docker +
  PyPI steps already ran — the version is published, just untagged). Fix: push the tag
  yourself (you have the scope the bot lacks), e.g.
  `git tag -a v<version> origin/main -m "Release v<version>" && git push origin v<version>`,
  then draft the GitHub release from that tag. Pushing the tag does **not** re-trigger
  the workflow (it triggers on `push: branches: [main]`, not tags), and it breaks the
  cycle so the next release tags normally. To automate even this case, use a PAT with
  `workflow` scope for the bump/tag push steps instead of `GITHUB_TOKEN`.

---

## Stack

Python 3.12 · FastAPI · SQLAlchemy 2.0 async · asyncpg (PostgreSQL) · oracledb (Oracle,
thin) · APScheduler (nomaflow ETL + cron) · Anthropic SDK · authlib (OIDC) · argon2 ·
cryptography (AES-256-GCM) · React 19 + Vite + TypeScript + emotion ·
TanStack Table · Monaco (SQL editor) · Recharts (visualisation).

---

## Repository layout

```
config/      app.toml (committed) · {connectors,dictionary,menus,screens,charts,dashboards,auth,jobs}.toml (NOT committed — per-deployment)
liberty/     main.py · config.py · crypto.py · framework_enums.py · theme.py
             · RELEASE.md · RELEASE.fr.md  (changelog shipped in the wheel)
             · {cli,admin_cli,connectors_cli,migrate_cli,crypto_cli,license_cli}.py
             · connectors/{config,base,db,sql,api,registry,dictionary,introspect}.py
             · versioning/  config + upgrade-history store (.versions/index.db)
             · licensing/{__init__.py, public.pem}
             · menus/config.py · screens/config.py · charts/config.py · dashboards/config.py
             · auth/{authstore,password,tokens,principal,oidc,dependencies,routes,models,db,service}.py
             · ai/{tools,connector_tools,scaffold_tools,proposal,assistant,routes}.py
             · jobs/{schema,registry,db,runner,scheduler,wiring,coercion,triggers,models,steps/}
             · etl/{operations,…}.py — shared SQL helpers used by nomaflow callables
             · web/{admin,connectors,menus,screens,charts,dashboards,license,theme,jobs,
                    access,hot_reload,errors,dependencies,deps,package,package_import,
                    clone,clone_with_deps,delete_with_deps,rename,export,dictgen,usages}.py
frontend/    Vite + React 19 + TS — built dist/ served by the backend
             src/{api,auth,workspace,types,services,common,pages,components,locales}/*
.github/workflows/  release.yml — auto-publishes Docker (ghcr.io) + PyPI on every push to main
docker/      entrypoint.sh — runtime config-init (init-db when POSTGRES_PASSWORD set)
start.sh     run/dev helper (serve | dev | api | build | frontend | init-db | init-config | help)
release/     deployment configs (Docker Compose light/full/swarm, install.sh, install-apps.sh, upgrade.sh)
tests/       1100+ tests
docs/        PLAN.md · DEPLOYMENT.md · NOMAFLOW-UI.md · PHASE13.md
```

---

## Links

- **Docs (getting started, config reference, walkthroughs):** <https://docs.nomana-it.fr/liberty/getting-started/>
- **GitHub:** <https://github.com/fblettner/liberty-next>
- **PyPI:** <https://pypi.org/project/liberty-next/>
- **Docker image:** <https://github.com/fblettner/liberty-next/pkgs/container/liberty-next>
- **Deployment configs:** [`release/`](release/) (light + full Docker Compose layouts)
- **API reference:** `https://<your-install>/redoc`
- **CLI reference:** `liberty-next --help` (also `liberty-admin`, `liberty-license`, `liberty-crypto`)
- **Working with Claude Code?** See [`CLAUDE.md`](CLAUDE.md)
- **Full plan + design decisions:** [`docs/PLAN.md`](docs/PLAN.md)

---

## License

Open framework: free. Connectors flagged `licensed = true` in `connectors.toml`
(sold separately, distributed in their own repos) are unlocked by an RS256 JWT
license key, set via **Settings → App → License** (encrypted at rest in app.toml with
the install's `LIBERTY_MASTER_KEY`). `nomasx1` and `nomajde` are always-licensed —
the loader refuses to load them without a covering key regardless of the on-disk
`licensed` flag. Without a key the framework runs in "restricted" mode. Inspect
a key with `liberty-license verify`; status at `GET /api/license`.
