# GovOPlaN Release Dependencies Release installs must not depend on sibling checkout paths. Local development can keep editable installs and `file:` WebUI links, but release packaging should resolve modules from tagged git refs or from a package registry. ## Backend Local development: ```bash cd /mnt/DATA/git/govoplan-core ./.venv/bin/python -m pip install -r requirements-dev.txt ``` Release install from a core checkout plus tagged module repositories: ```bash cd /mnt/DATA/git/govoplan-core ./.venv/bin/python -m pip install -r requirements-release.txt ``` `.[server]` is resolved relative to the current working directory. If you create the virtualenv elsewhere, still run the install command from the core checkout: ```bash cd /mnt/DATA/git/govoplan-core /tmp/govoplan-release-test/bin/python -m pip install -r requirements-release.txt ``` `requirements-release.txt` pins the module repositories to the release tag. Update those refs when cutting a release: ```text govoplan-access git@git.add-ideas.de:add-ideas/govoplan-access.git v0.1.4 govoplan-admin git@git.add-ideas.de:add-ideas/govoplan-admin.git v0.1.4 govoplan-tenancy git@git.add-ideas.de:add-ideas/govoplan-tenancy.git v0.1.4 govoplan-policy git@git.add-ideas.de:add-ideas/govoplan-policy.git v0.1.4 govoplan-audit git@git.add-ideas.de:add-ideas/govoplan-audit.git v0.1.4 govoplan-files git@git.add-ideas.de:add-ideas/govoplan-files.git v0.1.4 govoplan-mail git@git.add-ideas.de:add-ideas/govoplan-mail.git v0.1.4 govoplan-campaign git@git.add-ideas.de:add-ideas/govoplan-campaign.git v0.1.4 govoplan-calendar git@git.add-ideas.de:add-ideas/govoplan-calendar.git v0.1.4 ``` ## Runtime Module Package Changes The admin module manager can hot-enable and hot-disable packages that are already installed. It does not install or uninstall Python/npm packages from inside the running server. For runtime package changes, create an operator install plan in Admin > System > Modules. The module manager shows the trusted installer preflight status and blocks unsafe uninstalls before the operator touches packages. Preflight from the server shell: ```bash govoplan-module-installer --format shell ``` Apply from a controlled operator shell while maintenance mode is active: ```bash govoplan-module-installer --apply --build-webui ``` For real install/uninstall work, prefer supervised mode with the deployment's restart command and health endpoint: ```bash govoplan-module-installer \ --supervise \ --migrate \ --build-webui \ --health-url http://127.0.0.1:8000/health \ --restart-command '' ``` To let the admin UI trigger package work without executing pip/npm inside a FastAPI request, run the installer daemon in a separate operator shell. This is the preferred development/early-production mode for now because the operator can watch output, stop before queueing risky changes, and keep restart commands deployment-specific: ```bash govoplan-module-installer \ --daemon \ --migrate \ --build-webui \ --database-backup-command 'pg_dump --format=custom "$GOVOPLAN_DATABASE_URL" > "$GOVOPLAN_DATABASE_BACKUP_PATH"' \ --database-restore-check-command 'pg_restore --list "$GOVOPLAN_DATABASE_BACKUP_PATH" >/dev/null' \ --database-restore-command 'pg_restore --clean --if-exists --dbname "$GOVOPLAN_DATABASE_URL" "$GOVOPLAN_DATABASE_BACKUP_PATH"' \ --health-url http://127.0.0.1:8000/health \ --restart-command '' ``` Admin > System > Modules can then queue the saved install plan as a supervised request. Install rows can be planned directly from the approved package catalog; uninstall rows are generated from installed, disabled modules so the Python distribution and WebUI package names do not need to be typed by hand. The daemon claims one queued request at a time and writes request/run records below `runtime/module-installer`. For process-manager one-shot usage or tests, use `--daemon-once`. The daemon also writes `runtime/module-installer/daemon.status.json`; check it with: ```bash govoplan-module-installer --daemon-status --format json ``` The installer uses a runtime lock, snapshots `pip freeze` plus WebUI `package.json`/`package-lock.json`, writes a run record below `runtime/module-installer/runs`, and marks planned rows as applied only after all commands succeed. When `--migrate` is used with a `sqlite:///` database URL, the installer also snapshots the SQLite database with SQLite's backup API before running migrations. For other database engines, pass external backup/restore hooks: ```bash govoplan-module-installer \ --supervise \ --migrate \ --database-backup-command 'pg_dump --format=custom "$GOVOPLAN_DATABASE_URL" > "$GOVOPLAN_DATABASE_BACKUP_PATH"' \ --database-restore-check-command 'pg_restore --list "$GOVOPLAN_DATABASE_BACKUP_PATH" >/dev/null' \ --database-restore-command 'pg_restore --clean --if-exists --dbname "$GOVOPLAN_DATABASE_URL" "$GOVOPLAN_DATABASE_BACKUP_PATH"' \ --health-url http://127.0.0.1:8000/health \ --restart-command '' ``` The backup command runs before migrations. The restore-check command validates the produced backup artifact before migrations proceed, without restoring over the live database. The restore command is stored in the run record and runs during rollback unless an override is passed to `--rollback`. Supervised mode treats package command failure, migration failure, restart failure, and health timeout as rollback triggers. It restores the Python/WebUI package snapshots, re-runs the restart command when supplied, and restores the saved install plan state so the operator can correct it. The supervisor must run outside the FastAPI server process; the admin UI saves and validates plans but does not mutate packages from an HTTP request. After a successful install plan, the installer adds installed modules to saved startup state by default so the restarted server can discover and enable them. After a successful uninstall plan, the installer removes uninstalled modules from saved startup state by default. Use `--no-activate-installed-modules` or `--keep-uninstalled-modules-in-desired` only for staged rollout workflows that will update module state separately. Uninstall is non-destructive by default. A planned uninstall row can set `destroy_data: true` to request destructive module retirement. The module must provide an automated retirement provider, and the installer snapshots the database before dropping module-owned tables. For SQLite this uses the built-in snapshot path; for PostgreSQL or another non-SQLite database, provide `--database-backup-command`, `--database-restore-check-command`, and `--database-restore-command`. If a destructive run fails during package removal, the installer restores the database snapshot before returning the failed run result; supervised restart/health failures also roll back through the normal supervisor path. Package rollback is automatic. SQLite database rollback is automatic for installer runs that used `--migrate` and captured a database snapshot. Non-SQLite rollback is automatic when the run used `--database-backup-command` and `--database-restore-command`; otherwise migrated non-SQLite runs are blocked before package changes are applied. Rollback uses the saved run snapshot: ```bash govoplan-module-installer --rollback govoplan-module-installer --rollback --database-restore-command '' ``` Database hook commands run with these environment variables: - `GOVOPLAN_INSTALLER_RUN_DIR`: the run snapshot directory - `GOVOPLAN_DATABASE_URL`: the configured database URL - `GOVOPLAN_DATABASE_BACKUP_PATH`: a suggested backup artifact path inside the run directory - `GOVOPLAN_DATABASE_BACKUP_METADATA`: optional JSON metadata path that backup commands may write for operator diagnostics Avoid embedding secrets directly in commands; prefer environment variables, service credentials, or deployment-local secret injection. Inspect installer history and lock state from the operator shell: ```bash govoplan-module-installer --list-runs --format json govoplan-module-installer --show-run --format json govoplan-module-installer --lock-status --format json govoplan-module-installer --list-requests --format json govoplan-module-installer --show-request --format json govoplan-module-installer --cancel-request --format json govoplan-module-installer --retry-request --format json ``` Package catalogs can be local files or remote static resources, for example served by `govoplan-web`. Set `GOVOPLAN_MODULE_PACKAGE_CATALOG` for a local file or `GOVOPLAN_MODULE_PACKAGE_CATALOG_URL` for a remote catalog matching `docs/module-package-catalog.example.json`; the admin UI will show those entries and can save them into the install plan. This keeps the release approval decision outside the running server while avoiding hand-typed package refs. Remote catalogs can be cached for offline inspection: ```bash GOVOPLAN_MODULE_PACKAGE_CATALOG_URL=https://govoplan.example/catalogs/v1/channels/stable.json GOVOPLAN_MODULE_PACKAGE_CATALOG_CACHE=/srv/govoplan/runtime/catalog-cache/stable.json ``` Validate the catalog before handing it to operators: ```bash govoplan-module-installer --validate-package-catalog docs/module-package-catalog.example.json --format json ``` Release catalogs should be signed, channel-gated, expiring, and sequence tracked. The supported signing format is an Ed25519 signature over the canonical catalog JSON object with the `signature` and `signatures` fields removed. Core accepts the legacy single `signature` field and the newer `signatures` array used during key rotation. Sign a catalog with: ```bash govoplan-module-installer \ --sign-package-catalog docs/module-package-catalog.example.json \ --catalog-signing-key-id release-key-1 \ --catalog-signing-private-key /path/to/ed25519-private.pem ``` Validate an approved release catalog with: ```bash govoplan-module-installer \ --validate-package-catalog docs/module-package-catalog.example.json \ --require-signed-catalog \ --approved-catalog-channel stable \ --catalog-trusted-key release-key-1= \ --format json ``` For the admin UI/daemon path, configure the same policy through environment variables: ```bash GOVOPLAN_MODULE_PACKAGE_CATALOG=/path/to/catalog.json GOVOPLAN_MODULE_PACKAGE_CATALOG_REQUIRE_SIGNATURE=true GOVOPLAN_MODULE_PACKAGE_CATALOG_APPROVED_CHANNELS=stable GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS='{"release-key-1":""}' GOVOPLAN_MODULE_PACKAGE_CATALOG_SEQUENCE_STATE=/srv/govoplan/runtime/catalog-sequences.json GOVOPLAN_MODULE_PACKAGE_CATALOG_ENFORCE_SEQUENCE=true ``` Trusted keys can also be loaded from `GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_FILE`. A URL-backed keyring is supported for development and tightly controlled deployments through `GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_URL` plus `GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_CACHE`, but production systems should pin trusted keys locally. Catalog entries can declare `license_features`. If `GOVOPLAN_LICENSE_ENFORCEMENT=true`, core blocks planning catalog installs whose required features are not present in the configured offline license: ```bash GOVOPLAN_LICENSE_FILE=/srv/govoplan/license.json GOVOPLAN_LICENSE_TRUSTED_KEYS_FILE=/srv/govoplan/trust/license-keyring.json GOVOPLAN_LICENSE_ENFORCEMENT=true ``` See `docs/CATALOG_TRUST_AND_LICENSING.md` for key rotation, replay protection, and licensing details. See `docs/RELEASE_CATALOG_WORKFLOW.md` for the concrete release-machine workflow that generates signing keys and publishes signed catalogs through `govoplan-web`. Unsigned catalogs remain usable for local development when signature enforcement is off, but the admin UI labels them as unsigned. Install rows must use tagged package/git refs or registry packages, not local `file:` or workspace links. The installer daemon can run `npm install` and `npm run build` for WebUI package changes; that is the supported path. Browser remote bundles are still experimental and should be treated as a controlled deployment option, not the normal install/uninstall mechanism. Module manifests can declare core compatibility bounds and uninstall guard providers. Preflight blocks incompatible manifest contracts/core versions, active modules, desired startup state, protected modules, and active dependents. Default uninstall is non-destructive: module data and schema remain dormant if the package is removed. Persistent-data guards therefore warn by default instead of requiring export/delete. A module that supports explicit data/schema retirement should register a retirement provider. When `destroy_data` is set on an uninstall plan row, that provider is allowed to destroy module-owned data after the installer has captured a database snapshot; otherwise it is used only for preflight reporting. ## WebUI Local development uses `webui/package.json`, which may point at sibling module checkouts while active development is happening. Release WebUI installs should use `webui/package.release.json`. It points module dependencies at the same tagged git repositories. After the module tags referenced there exist, generate the committed release lockfile without touching the development package files: ```bash cd /mnt/DATA/git/govoplan-core scripts/generate-release-lock.sh cd webui PATH=/home/zemion/.nvm/versions/node/v22.22.3/bin:$PATH /home/zemion/.nvm/versions/node/v22.22.3/bin/npm run build ``` The module repositories include root-level npm package manifests so git installs can resolve `@govoplan/access-webui`, `@govoplan/admin-webui`, `@govoplan/files-webui`, `@govoplan/mail-webui`, `@govoplan/campaign-webui`, and `@govoplan/calendar-webui` from repository roots even though their source lives below `webui/src`. The normal release path is automated by `scripts/push-release-tag.sh`: it bumps or accepts the target version, updates Python/WebUI/module manifest versions, commits/tags/pushes the module repositories first, regenerates `webui/package-lock.release.json`, and then commits/tags/pushes core. If the working tree has already been bumped, pass the current version explicitly: ```bash cd /mnt/DATA/git/govoplan-core scripts/push-release-tag.sh --version 0.1.2 ``` The script also includes GovOPlaN roadmap/scaffold module repositories that do not yet have package metadata. Those repositories are committed, tagged, and pushed with the same release tag, but they are tag-only until they contain `pyproject.toml`, module manifests, or WebUI packages. Tag-only repositories are not listed in `requirements-release.txt` or `webui/package.release.json`. Current tag-only module repositories: - `govoplan-addresses` - `govoplan-appointments` - `govoplan-cases` - `govoplan-connectors` - `govoplan-dms` - `govoplan-erp` - `govoplan-fit-connect` - `govoplan-forms` - `govoplan-identity-trust` - `govoplan-idm` - `govoplan-ledger` - `govoplan-notifications` - `govoplan-ops` - `govoplan-payments` - `govoplan-portal` - `govoplan-reporting` - `govoplan-scheduling` - `govoplan-search` - `govoplan-tasks` - `govoplan-templates` - `govoplan-workflow` - `govoplan-xoev` - `govoplan-xrechnung` - `govoplan-xta-osci` ### Release lockfile strategy The supported release composition currently is the full Multi Seal Mail product: core plus access, admin, tenancy, policy, audit, files, mail, campaign, and calendar. Keep one committed full-product release lockfile at `webui/package-lock.release.json`, generated from `webui/package.release.json` in a clean release workspace. Development `package-lock.json` may continue to point at local `file:` dependencies. Frontend module permutations are regression-tested through `GOVOPLAN_WEBUI_MODULE_PACKAGES` and temporary build output, not through committed lockfiles for every possible combination. If a smaller composition becomes a separately shipped product, add an explicit release manifest and lockfile pair for that product, for example `package.release.files-mail.json` and `package-lock.release.files-mail.json`, generated in a clean release workspace from tagged git dependencies. ## Release Checklist - Keep Python package versions, WebUI package versions, and git tags aligned. - Tag core, access, admin, tenancy, policy, audit, files, mail, campaign, calendar, and scaffold module repositories together. - Update `requirements-release.txt` and `webui/package.release.json` when the release tag changes. - Generate the committed full-product release lockfile from `package.release.json` with `scripts/generate-release-lock.sh`. - Add separate release manifest/lockfile pairs only for module compositions that are shipped as their own products. - Do not commit local sibling paths into release manifests.