diff --git a/Repo-docs-MODULE-ARCHITECTURE.md b/Repo-docs-MODULE-ARCHITECTURE.md index 4ba9181..4f746e0 100644 --- a/Repo-docs-MODULE-ARCHITECTURE.md +++ b/Repo-docs-MODULE-ARCHITECTURE.md @@ -1,4 +1,4 @@ - + > Mirrored from `/mnt/DATA/git/govoplan-core/docs/MODULE_ARCHITECTURE.md`. > Origin: `repository`. @@ -382,7 +382,10 @@ Hot enable/disable is a core design principle for every module: subscriptions, or irreversible migration state must expose uninstall guard providers through their manifest. Guards return `blocker`, `warning`, or `info` results and may inspect live state through the core-owned DB session. - A guard failure is treated as a blocker. + Default package uninstall is non-destructive, so ordinary persistent data + should warn that data will remain dormant. Guards should block only when + removing the package would corrupt other active modules, workers, external + subscriptions, or deployment state. A guard failure is treated as a blocker. - Core refreshes the active registry before frontend metadata is returned from `/api/v1/platform/modules`; the WebUI shell refetches this metadata after module changes so navigation, routes, and UI capabilities update without a @@ -416,7 +419,8 @@ The package install-plan API records operator intent only: - `GET /api/v1/admin/system/modules/package-catalog` reads approved package references from `GOVOPLAN_MODULE_PACKAGE_CATALOG` so operators can add known module refs to the install plan without typing them manually. The endpoint - also reports catalog validity and the configured path. + also reports catalog validity, channel, signature, trust state, and the + configured path. - `PUT /api/v1/admin/system/modules/install-plan` saves planned install or uninstall rows. Install rows must use tagged package or git references, not local `file:`/workspace paths. Python install rows must also include the @@ -451,6 +455,11 @@ The package install-plan API records operator intent only: - `govoplan-module-installer --validate-package-catalog [path] --format json` validates a catalog file or the catalog configured through `GOVOPLAN_MODULE_PACKAGE_CATALOG`. +- `--sign-package-catalog --catalog-signing-key-id --catalog-signing-private-key ` + signs a catalog with Ed25519. `--require-signed-catalog`, + `--approved-catalog-channel `, and + `--catalog-trusted-key =` enforce the approved + release-channel path from an operator shell. - `govoplan-module-installer --rollback ` restores the saved package snapshots, restores the captured SQLite or external database snapshot when present, and reruns package installation from the previous freeze file. @@ -479,12 +488,13 @@ The installer preflight is intentionally conservative: - uninstalling an active module is blocked; - uninstalling a module still present in desired startup state is blocked; - uninstalling a module with active/desired dependents is blocked; -- uninstalling a module that owns migrations is blocked until that module - explicitly supports migration retirement; -- modules that declare migration retirement support should also register a - retirement provider. Without a provider, preflight emits a manual-review - warning. Providers may return blockers, warnings, and an explanatory summary, - but core does not drop schema or data on behalf of modules. +- uninstalling a module that owns migrations is non-destructive by default: + schema/data remain dormant and preflight emits a warning; +- modules that declare explicit migration retirement support should also + register a retirement provider. Without a provider, preflight emits a + manual-review warning. Providers may return blockers, warnings, and an + explanatory summary, but core does not drop schema or data on behalf of + modules. - module-owned uninstall guard providers can veto data/migration/worker unsafe removals; - install refs must be exact versions or tagged git refs; @@ -496,19 +506,26 @@ request handler cannot reliably restart or roll back the process that is currently executing the request. The admin UI therefore remains an operator planning and request-submission surface; the trusted daemon/CLI is the executor. -Automatic rollback covers Python and WebUI package state. For `sqlite:///` -database URLs, `--migrate` also captures a SQLite backup and rollback restores -it before the supervisor restarts the server. For non-SQLite database URLs, -`--migrate` requires deployment-specific backup and restore commands. Hook -commands run in the installer run directory with: +Automatic rollback covers Python and WebUI package state. The manual/shell +daemon can run `npm install` and `npm run build`; this remains the supported +WebUI package path. Browser-loaded remote module bundles are experimental and +reserved for controlled deployments with integrity/signature policy. + +For `sqlite:///` database URLs, `--migrate` also captures a SQLite backup and +rollback restores it before the supervisor restarts the server. For non-SQLite +database URLs, `--migrate` requires deployment-specific backup and restore +commands. A `--database-restore-check-command` can validate the created backup +artifact before migrations proceed. Hook commands run in the installer run +directory with: - `GOVOPLAN_INSTALLER_RUN_DIR` - `GOVOPLAN_DATABASE_URL` - `GOVOPLAN_DATABASE_BACKUP_PATH` - `GOVOPLAN_DATABASE_BACKUP_METADATA` -Module uninstall remains blocked until data and migration-state retirement rules -are satisfied. +Module uninstall does not retire data by default. Package removal leaves +module-owned schema/data dormant. Explicit retirement is a later module-owned +operation guarded by retirement providers. The running FastAPI server still reports `package_mutation_supported=false` because dependency-manager operations are not executed inside request handlers. diff --git a/Repo-docs-RELEASE-DEPENDENCIES.md b/Repo-docs-RELEASE-DEPENDENCIES.md index ae2af1a..4aa0534 100644 --- a/Repo-docs-RELEASE-DEPENDENCIES.md +++ b/Repo-docs-RELEASE-DEPENDENCIES.md @@ -1,4 +1,4 @@ - + > Mirrored from `/mnt/DATA/git/govoplan-core/docs/RELEASE_DEPENDENCIES.md`. > Origin: `repository`. @@ -81,13 +81,19 @@ govoplan-module-installer \ ``` To let the admin UI trigger package work without executing pip/npm inside a -FastAPI request, run the installer daemon under the process supervisor: +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 '' ``` @@ -115,14 +121,16 @@ 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 command is stored in the -run record and runs during rollback unless an override is passed to -`--rollback`. +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 @@ -178,22 +186,55 @@ refs. Validate the catalog before handing it to operators: govoplan-module-installer --validate-package-catalog docs/module-package-catalog.example.json --format json ``` +Release catalogs should be signed and channel-gated. The supported signing +format is an Ed25519 signature over the canonical catalog JSON object with the +`signature` field removed. 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":""}' +``` + +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. WebUI package changes still require `npm install` -and a WebUI rebuild/reload because the current shell resolves WebUI modules from -the build-time package graph. After backend package changes, run migrations and -restart or reload the affected server/worker processes according to the -deployment model. +`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 and -lets module-owned guards veto package removal when data, migrations, workers, or -schedulers would make uninstall unsafe. Modules that own migrations are treated -as non-retirable by default, so package uninstall is blocked unless the manifest -explicitly declares migration retirement support. A migration-owning module that -does declare retirement support should register a retirement provider; otherwise -preflight emits a manual-review warning instead of pretending schema/data -retirement is automated. +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; that provider may block when +its retirement preconditions are not met. ## WebUI