{"id":50398498,"url":"https://github.com/fblettner/liberty-next","last_synced_at":"2026-06-11T14:00:28.497Z","repository":{"id":357832791,"uuid":"1238601294","full_name":"fblettner/liberty-next","owner":"fblettner","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-07T20:15:38.000Z","size":5485,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-07T20:19:17.831Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fblettner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-14T09:16:05.000Z","updated_at":"2026-06-02T15:19:21.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/fblettner/liberty-next","commit_stats":null,"previous_names":["fblettner/liberty-next"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/fblettner/liberty-next","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fblettner%2Fliberty-next","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fblettner%2Fliberty-next/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fblettner%2Fliberty-next/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fblettner%2Fliberty-next/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fblettner","download_url":"https://codeload.github.com/fblettner/liberty-next/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fblettner%2Fliberty-next/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34201842,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-30T22:01:29.433Z","updated_at":"2026-06-11T14:00:28.468Z","avatar_url":"https://github.com/fblettner.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Liberty Next\n\n[![PyPI](https://img.shields.io/pypi/v/liberty-next.svg)](https://pypi.org/project/liberty-next/)\n[![Python](https://img.shields.io/pypi/pyversions/liberty-next.svg)](https://pypi.org/project/liberty-next/)\n[![Docker](https://img.shields.io/badge/ghcr.io-liberty--next-blue?logo=docker)](https://github.com/fblettner/liberty-next/pkgs/container/liberty-next)\n[![Release](https://github.com/fblettner/liberty-next/actions/workflows/release.yml/badge.svg)](https://github.com/fblettner/liberty-next/actions/workflows/release.yml)\n[![Docs](https://img.shields.io/badge/docs-nomana--it.fr-blue)](https://docs.nomana-it.fr/liberty/getting-started/)\n\n**Connector-driven low-code framework.** Configure SQL queries + HTTP endpoints in\nTOML; Liberty derives schemas at query time, serves a React admin UI on the same\nport, surfaces an Anthropic tool-use assistant for natural-language access, and\nwraps everything in a structured-config builder + dependency-aware deployment\npackager.\n\nDeclarative `connectors.toml` / `screens.toml` / `dictionary.toml` / `menus.toml` /\n`charts.toml` / `dashboards.toml` files drive the runtime — schemas derived at query\ntime, no code-gen step, every field round-trippable through the structured editors\nat **Settings → \\\u003ctab\\\u003e**.\n\n## Quick links\n\n- 📚 **Documentation** — \u003chttps://docs.nomana-it.fr/liberty/getting-started/\u003e\n- 💻 **Source** — \u003chttps://github.com/fblettner/liberty-next\u003e\n- 🐳 **Docker image** — \u003chttps://github.com/fblettner/liberty-next/pkgs/container/liberty-next\u003e\n- 🚀 **Deployment configs** (Compose + Swarm + helper scripts) — [`release/`](https://github.com/fblettner/liberty-next/tree/main/release)\n- 🐛 **Issues** — \u003chttps://github.com/fblettner/liberty-next/issues\u003e\n- 📦 **Releases** — \u003chttps://github.com/fblettner/liberty-next/releases\u003e\n\n---\n\n## Install\n\n**Full guide:** \u003chttps://docs.nomana-it.fr/liberty/getting-started/\u003e\n\nThree routes — pick what fits.\n\n### Docker Compose (recommended)\n\nCustomer hosts only need the `release/` directory — sparse-checkout pulls just that:\n\n```bash\n# 1. Sparse-clone — downloads ONLY release/ (no liberty/, no frontend/, no .git history of source)\ngit clone --depth 1 --filter=blob:none --sparse https://github.com/fblettner/liberty-next.git\ncd liberty-next\ngit sparse-checkout set release\ncd release\n\n# 2. One-shot install (interactive picks light vs full; or use a flag)\n./install.sh full\n\n# 3. (Licensed customer) overlay the apps wheel — see release/README.md\n./install-apps.sh /path/to/liberty_apps-X.Y.Z.whl\n# Set the license key after install via Settings → App → License (encrypted at rest in app.toml).\n```\n\nThe full deployment guide (TLS wiring, backups, upgrades, swarm, common ops) lives\nin [`release/README.md`](release/README.md).\n\n### PyPI\n\n**Recommended — pipx** (isolates Liberty Next in its own venv; CLI commands stay on\nyour PATH; no risk of polluting system Python):\n\n```bash\n# Install pipx once if you don't have it:\n#   macOS:    brew install pipx \u0026\u0026 pipx ensurepath\n#   Linux:    sudo apt install pipx \u0026\u0026 pipx ensurepath\n#                 # or:  python3 -m pip install --user pipx \u0026\u0026 python3 -m pipx ensurepath\n#   Windows:  py -m pip install --user pipx \u0026\u0026 py -m pipx ensurepath\n\n# Report rendering (markdown → PDF) uses WeasyPrint — install the system libs\n# it links against. Skip if you don't plan to use the /api/reports endpoints.\n#   macOS:    brew install pango\n#   Debian/Ubuntu:  sudo apt install libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz0b libfontconfig1\n\npipx install liberty-next\nliberty-next                      # → API + SPA on http://localhost:8000\n```\n\nThis gives you every CLI tool the package ships (`liberty-next`, `liberty-admin`,\n`liberty-connectors`, `liberty-migrate`, `liberty-license`, `liberty-crypto`) on the PATH,\neach one routed through the same isolated venv. Upgrade with `pipx upgrade liberty-next`;\nuninstall cleanly with `pipx uninstall liberty-next` (removes the venv + every shim,\nleaves nothing behind).\n\n**Plain pip** (only when pipx isn't an option — make a venv yourself to avoid breaking\nsystem packages):\n\n```bash\npython3 -m venv ~/.local/liberty-venv\n~/.local/liberty-venv/bin/pip install liberty-next\n~/.local/liberty-venv/bin/liberty-next\n```\n\nFirst boot generates an `admin` password and prints it once — capture it from the\nlogs, then sign in at \u003chttp://localhost:8000\u003e.\n\n### Adding the licensed apps to a pipx install\n\nFor Docker, [`release/install-apps.sh`](release/install-apps.sh) handles everything.\nFor a pipx install, do it manually:\n\n```bash\n# 1. Inject the apps wheel into the SAME pipx venv as liberty-next\n#    (delivered to licensed customers as liberty_apps-X.Y.Z-py3-none-any.whl):\npipx inject liberty-next /path/to/liberty_apps-X.Y.Z-py3-none-any.whl\n\n# 2. Persistent secrets — generate ONCE and put in ~/.bashrc / ~/.zshrc.\n#    Both must stay stable across restarts (rotating either breaks every encrypted\n#    secret in app.toml + connectors.toml).\nexport LIBERTY_JWT_SECRET=\"$(openssl rand -base64 48 | tr -d '\\n=+/')\"\nexport LIBERTY_MASTER_KEY=\"$(openssl rand -base64 32 | tr -d '\\n=+/')\"\n\n# 3. Postgres credentials — used by liberty-admin init-db to seed [pools.default]\n#    AND by deploy-databases to inherit-and-set the nomasx1 / nomajde role passwords.\n#    Skip if you only want SQLite for trial (licensed connectors need real pg).\nexport POSTGRES_PASSWORD=\"your-postgres-superuser-password\"\nexport POSTGRES_USER=liberty\nexport POSTGRES_HOST=localhost\nexport POSTGRES_PORT=5432\nexport POSTGRES_DB=liberty\n# SQLite fallback (no licensed connectors): export LIBERTY_DB_URL=sqlite+aiosqlite:///./liberty.db\n\n# 4. Materialize the wheel's payload into a writable LIBERTY_APPS_DIR\nmkdir -p ~/.local/share/liberty-next/apps\nliberty-apps install --target ~/.local/share/liberty-next/apps/config\nexport LIBERTY_APPS_DIR=~/.local/share/liberty-next/apps/config\n\n# 5. Bootstrap the framework DB + admin user\npsql -h \"$POSTGRES_HOST\" -U postgres -c \"CREATE ROLE $POSTGRES_USER LOGIN SUPERUSER PASSWORD '$POSTGRES_PASSWORD';\"\npsql -h \"$POSTGRES_HOST\" -U postgres -c \"CREATE DATABASE $POSTGRES_DB OWNER $POSTGRES_USER;\"\nliberty-admin init-db    # seeds [pools.default] + creates 'admin' user, prints password\n\n# 6. Run the install-time jobs (deploy-databases / init-schema / import-reference)\nliberty-admin run-install-jobs\n\n# 7. Start the server\nliberty-next             # API + SPA on http://localhost:8000\n\n# 8. Sign in as admin, then Settings → App → License → Set\n#    (encrypted at rest in app.toml with LIBERTY_MASTER_KEY)\n```\n\nUpgrade the apps later by injecting a new wheel + re-running `liberty-apps install`\n(operator-edited TOMLs are preserved; pass `--force-config` to overwrite). License /\nAI api_key / OIDC settings stay in app.toml and survive the upgrade.\n\n### From source (development)\n\n```bash\ngit clone https://github.com/fblettner/liberty-next.git\ncd liberty-next\npython3.12 -m venv .venv\n.venv/bin/pip install -e \".[dev]\"\n.venv/bin/pytest -v               # 920+ tests\n./start.sh init-config            # seed config/*.toml from the .example files\n./start.sh init-db                # FIRST RUN: create the auth store + `admin` user (prints password)\n./start.sh                        # build frontend + serve on :8000\n./start.sh dev                    # same, with backend auto-reload\n./start.sh frontend               # Vite HMR dev server on :5173 (pair with `./start.sh api dev`)\n```\n\n---\n\n## What you get\n\n| URL | Purpose |\n|---|---|\n| `/` | React SPA — admin UI (sign-in, workspace tabs, Settings, AI assistant) |\n| `/docs` | **Swagger UI** — interactive API explorer |\n| `/redoc` | **ReDoc** — print-friendly API reference (grouped by tag) |\n| `/openapi.json` | OpenAPI 3 spec — generated from FastAPI routes + Pydantic models |\n| `/api/*` | Public API surface (auth gates per route) |\n| `/admin/*` | Operator-only endpoints — config CRUD, find-usages, packaging, AI scaffold-apply, … |\n| `/info` | Public liveness + counts (connectors / screens / pools) — Docker `HEALTHCHECK` hits this |\n\n---\n\n## Configuration in 60 seconds\n\nEight TOML files under `config/` (or wherever `LIBERTY_APPS_DIR` points). Every file\nis round-trippable through the structured editors at **Settings → \\\u003ctab\\\u003e**:\n\n| File | What it carries | Editor |\n|---|---|---|\n| `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 |\n| `connectors.toml` | DB pools + SQL connectors with named queries + API connectors with endpoints | Settings → Pools, Settings → Connectors |\n| `dictionary.toml` | Shared + per-connector field metadata (labels / types / rules / lookups / sequences) | Settings → Dictionary |\n| `screens.toml` | Screen definitions — per-app grids + dialog forms + actions + row menus | Settings → Screens |\n| `charts.toml` | Saved chart specs referenceable from screens + dashboards | Settings → Charts |\n| `dashboards.toml` | Widget grids with shared filters | Settings → Dashboards |\n| `menus.toml` | Per-app navigation trees | Settings → Menus |\n| `jobs.toml` | nomaflow ETL pipelines + scheduled jobs (per-step `op_kwargs`, retry, retention) | Nomaflow → Jobs |\n\nTwo secrets stay env-only: `LIBERTY_JWT_SECRET` (signs access / refresh tokens) and\n`LIBERTY_MASTER_KEY` (the AES-256-GCM key that decrypts every `ENC:` value in app.toml +\nconnectors.toml). Everything else — license key, Anthropic API key, OIDC client secret,\npool / API-connector passwords — lives encrypted at rest in TOML and is edited through\nthe UI's masked-secret pattern. `${VAR}` / `${VAR:-default}` env references in TOML are\nstill expanded at load time for the few values an operator wants to keep externally\nmanaged.\n\n---\n\n## Customer / vendor split\n\nLiberty Next ships as an **open framework**. The customer-facing connectors + screens\n+ dictionaries live in a separate apps repo (`liberty-apps`); the licensed ones\n(nomasx1 / nomajde / nomaflow) are unlocked by an RS256 JWT set via **Settings → App →\nLicense** (encrypted at rest in app.toml with `LIBERTY_MASTER_KEY`). Without a key\nthe framework runs in **restricted** mode — those connectors aren't loaded. Headless\ninstalls can pre-seed the encrypted value with `liberty-crypto encrypt`.\n\nThe **Settings → Package** tab packages selected screens / menu items / dashboards\nplus their full dependency closure (connectors / queries / DD entries / lookups / …)\ninto a ZIP for atomic deployment to another install. Each entity carries an\n`override = true` flag operators can flip to mark customer customisations — the\nimport-package endpoint's `overwrite` strategy preserves flagged entities so vendor\nupgrades don't clobber customer forks.\n\n---\n\n## Releasing\n\nOne GitHub Actions workflow, [`release.yml`](.github/workflows/release.yml), publishes\nevery release. **It runs automatically on every push to `main`.** No buttons, no tags,\nno manual triggers.\n\n### The flow\n\n```\ndevelop branch         →  work happens here, push freely, NOTHING triggers\n                         ↓\n                       PR develop → main, merge\n                         ↓\nmain branch push       →  release.yml runs:\n                          1. Reads pyproject.toml's version\n                          2. If that version is already tagged → auto-bump patch (7.0.1 → 7.0.2)\n                             Else use as-is (when you manually bumped for a major/minor)\n                          3. Commits the bumped pyproject.toml back to main ([skip ci])\n                          4. Builds + pushes multi-arch Docker to ghcr.io as \u003cversion\u003e + :latest\n                          5. Publishes sdist + wheel to PyPI\n                          6. Tags v\u003cversion\u003e + creates GitHub release with auto-notes\n```\n\n### Version control\n\n- **Bugfix / patch release** (default): just merge to main. Workflow auto-bumps `7.0.1 → 7.0.2`.\n- **Minor release** (`7.0.x → 7.1.0`): bump `version = \"7.1.0\"` in `pyproject.toml` in any commit before merging. Workflow honours it.\n- **Major release** (`7.x → 8.0.0`): same — bump in pyproject.toml.\n\n### Setting up the repo (one-time)\n\n1. **Branch protection on `main`** (recommended):\n   \u003chttps://github.com/fblettner/liberty-next/settings/branches\u003e → add a rule for\n   `main` → require pull request before merging. This forces the develop → main flow.\n\n2. **PyPI token**: \u003chttps://pypi.org/manage/account/token/\u003e → create a token\n   (account-scoped first time; scope to `liberty-next` after the first release).\n   Add it as a repo secret:\n   \u003chttps://github.com/fblettner/liberty-next/settings/secrets/actions\u003e →\n   Name: `PYPI_API_TOKEN`, Value: `pypi-…`.\n\n3. **Docker image visibility**: after the first push to ghcr.io, the image lands\n   private. Make it public at \u003chttps://github.com/fblettner?tab=packages\u003e →\n   liberty-next → Package settings → Change visibility → Public. (One-time.)\n\n### Manual override\n\nThe workflow also has a `workflow_dispatch` trigger if you need to re-publish a\nspecific version (rebuild after a base-image CVE, etc.). Go to\n\u003chttps://github.com/fblettner/liberty-next/actions/workflows/release.yml\u003e →\nRun workflow → optional version input.\n\n### Recovering from a failed publish\n\n- **Pre-publish failure** (build, Docker push) — fix and re-trigger; nothing is consumed.\n- **PyPI publish failure** (the only irreversible step) — the version is burned. Bump\n  `pyproject.toml` to the next version and push again.\n\n---\n\n## Stack\n\nPython 3.12 · FastAPI · SQLAlchemy 2.0 async · asyncpg (PostgreSQL) · oracledb (Oracle,\nthin) · APScheduler (nomaflow ETL + cron) · Anthropic SDK · authlib (OIDC) · argon2 ·\ncryptography (AES-256-GCM) · React 19 + Vite + TypeScript + emotion ·\nTanStack Table · Monaco (SQL editor) · Recharts (visualisation).\n\n---\n\n## Repository layout\n\n```\nconfig/      app.toml (committed) · {connectors,dictionary,menus,screens,charts,dashboards,auth,jobs}.toml (NOT committed — per-deployment)\nliberty/     main.py · config.py · crypto.py · framework_enums.py · theme.py\n             · {cli,admin_cli,connectors_cli,migrate_cli,crypto_cli,license_cli}.py\n             · connectors/{config,base,db,sql,api,registry,dictionary,introspect}.py\n             · licensing/{__init__.py, public.pem}\n             · menus/config.py · screens/config.py · charts/config.py · dashboards/config.py\n             · auth/{authstore,password,tokens,principal,oidc,dependencies,routes,models,db,service}.py\n             · ai/{tools,connector_tools,scaffold_tools,proposal,assistant,routes}.py\n             · jobs/{schema,registry,db,runner,scheduler,wiring,coercion,triggers,models,steps/}\n             · etl/{operations,…}.py — shared SQL helpers used by nomaflow callables\n             · web/{admin,connectors,menus,screens,charts,dashboards,license,theme,jobs,\n                    access,hot_reload,errors,dependencies,deps,package,package_import,\n                    clone,clone_with_deps,delete_with_deps,rename,export,dictgen,usages}.py\nfrontend/    Vite + React 19 + TS — built dist/ served by the backend\n             src/{api,auth,workspace,types,services,common,pages,components,locales}/*\n.github/workflows/  release.yml — auto-publishes Docker (ghcr.io) + PyPI on every push to main\ndocker/      entrypoint.sh — runtime config-init (init-db when POSTGRES_PASSWORD set)\nstart.sh     run/dev helper (serve | dev | api | build | frontend | init-db | init-config | help)\nrelease/     deployment configs (Docker Compose light/full/swarm, install.sh, install-apps.sh)\ntests/       920+ tests\ndocs/        PLAN.md · DEPLOYMENT.md · NOMAFLOW-UI.md · PHASE13.md\n```\n\n---\n\n## Links\n\n- **Docs (getting started, config reference, walkthroughs):** \u003chttps://docs.nomana-it.fr/liberty/getting-started/\u003e\n- **GitHub:** \u003chttps://github.com/fblettner/liberty-next\u003e\n- **PyPI:** \u003chttps://pypi.org/project/liberty-next/\u003e\n- **Docker image:** \u003chttps://github.com/fblettner/liberty-next/pkgs/container/liberty-next\u003e\n- **Deployment configs:** [`release/`](release/) (light + full Docker Compose layouts)\n- **API reference:** `https://\u003cyour-install\u003e/redoc`\n- **CLI reference:** `liberty-next --help` (also `liberty-admin`, `liberty-license`, `liberty-crypto`)\n- **Working with Claude Code?** See [`CLAUDE.md`](CLAUDE.md)\n- **Full plan + design decisions:** [`docs/PLAN.md`](docs/PLAN.md)\n\n---\n\n## License\n\nOpen framework: free. Connectors flagged `licensed = true` in `connectors.toml`\n(sold separately, distributed in their own repos) are unlocked by an RS256 JWT\nlicense key, set via **Settings → App → License** (encrypted at rest in app.toml with\nthe install's `LIBERTY_MASTER_KEY`). `nomasx1` and `nomajde` are always-licensed —\nthe loader refuses to load them without a covering key regardless of the on-disk\n`licensed` flag. Without a key the framework runs in \"restricted\" mode. Inspect\na key with `liberty-license verify`; status at `GET /api/license`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffblettner%2Fliberty-next","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffblettner%2Fliberty-next","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffblettner%2Fliberty-next/lists"}