Sync wiki after docs cleanup

2026-07-09 12:03:42 +02:00
parent aa14cbfb56
commit 214ae3ed3a
13 changed files with 774 additions and 896 deletions

@@ -7,13 +7,16 @@ This page is generated from repository and product-directory project files.
- [Product govoplan-split-concept-action-plan](Product govoplan-split-concept-action-plan) - `/mnt/DATA/Nextcloud/ADD ideas UG/Products/govoplan/split-concept-action-plan.md` - [Product govoplan-split-concept-action-plan](Product govoplan-split-concept-action-plan) - `/mnt/DATA/Nextcloud/ADD ideas UG/Products/govoplan/split-concept-action-plan.md`
- [Repo-README](Repo-README) - `/mnt/DATA/git/govoplan-core/README.md` - [Repo-README](Repo-README) - `/mnt/DATA/git/govoplan-core/README.md`
- [Repo-docs-ACCESS-EXTRACTION-PLAN](Repo-docs-ACCESS-EXTRACTION-PLAN) - `/mnt/DATA/git/govoplan-core/docs/ACCESS_EXTRACTION_PLAN.md` - [Repo-docs-ACCESS-EXTRACTION-PLAN](Repo-docs-ACCESS-EXTRACTION-PLAN) - `/mnt/DATA/git/govoplan-core/docs/ACCESS_EXTRACTION_PLAN.md`
- [Repo-docs-ACCESS-RBAC-MODEL](Repo-docs-ACCESS-RBAC-MODEL) - `/mnt/DATA/git/govoplan-core/docs/ACCESS_RBAC_MODEL.md`
- [Repo-docs-API-CONDITIONAL-DELTA](Repo-docs-API-CONDITIONAL-DELTA) - `/mnt/DATA/git/govoplan-core/docs/API_CONDITIONAL_DELTA.md` - [Repo-docs-API-CONDITIONAL-DELTA](Repo-docs-API-CONDITIONAL-DELTA) - `/mnt/DATA/git/govoplan-core/docs/API_CONDITIONAL_DELTA.md`
- [Repo-docs-CATALOG-TRUST-AND-LICENSING](Repo-docs-CATALOG-TRUST-AND-LICENSING) - `/mnt/DATA/git/govoplan-core/docs/CATALOG_TRUST_AND_LICENSING.md` - [Repo-docs-CATALOG-TRUST-AND-LICENSING](Repo-docs-CATALOG-TRUST-AND-LICENSING) - `/mnt/DATA/git/govoplan-core/docs/CATALOG_TRUST_AND_LICENSING.md`
- [Repo-docs-CODEX-WORKFLOW](Repo-docs-CODEX-WORKFLOW) - `/mnt/DATA/git/govoplan-core/docs/CODEX_WORKFLOW.md` - [Repo-docs-CODEX-WORKFLOW](Repo-docs-CODEX-WORKFLOW) - `/mnt/DATA/git/govoplan-core/docs/CODEX_WORKFLOW.md`
- [Repo-docs-CONFIGURATION-PACKAGES](Repo-docs-CONFIGURATION-PACKAGES) - `/mnt/DATA/git/govoplan-core/docs/CONFIGURATION_PACKAGES.md` - [Repo-docs-CONFIGURATION-PACKAGES](Repo-docs-CONFIGURATION-PACKAGES) - `/mnt/DATA/git/govoplan-core/docs/CONFIGURATION_PACKAGES.md`
- [Repo-docs-DEPLOYMENT-OPERATOR-GUIDE](Repo-docs-DEPLOYMENT-OPERATOR-GUIDE) - `/mnt/DATA/git/govoplan-core/docs/DEPLOYMENT_OPERATOR_GUIDE.md` - [Repo-docs-DEPLOYMENT-OPERATOR-GUIDE](Repo-docs-DEPLOYMENT-OPERATOR-GUIDE) - `/mnt/DATA/git/govoplan-core/docs/DEPLOYMENT_OPERATOR_GUIDE.md`
- [Repo-docs-DOCUMENTATION-MAP](Repo-docs-DOCUMENTATION-MAP) - `/mnt/DATA/git/govoplan-core/docs/DOCUMENTATION_MAP.md`
- [Repo-docs-EVENTS-AND-AUDIT](Repo-docs-EVENTS-AND-AUDIT) - `/mnt/DATA/git/govoplan-core/docs/EVENTS_AND_AUDIT.md` - [Repo-docs-EVENTS-AND-AUDIT](Repo-docs-EVENTS-AND-AUDIT) - `/mnt/DATA/git/govoplan-core/docs/EVENTS_AND_AUDIT.md`
- [Repo-docs-GITEA-ISSUES](Repo-docs-GITEA-ISSUES) - `/mnt/DATA/git/govoplan-core/docs/GITEA_ISSUES.md` - [Repo-docs-GITEA-ISSUES](Repo-docs-GITEA-ISSUES) - `/mnt/DATA/git/govoplan-core/docs/GITEA_ISSUES.md`
- [Repo-docs-GOVERNANCE-MODEL](Repo-docs-GOVERNANCE-MODEL) - `/mnt/DATA/git/govoplan-core/docs/GOVERNANCE_MODEL.md`
- [Repo-docs-GOVERNMENT-OPERATIONS-VISION](Repo-docs-GOVERNMENT-OPERATIONS-VISION) - `/mnt/DATA/git/govoplan-core/docs/GOVERNMENT_OPERATIONS_VISION.md` - [Repo-docs-GOVERNMENT-OPERATIONS-VISION](Repo-docs-GOVERNMENT-OPERATIONS-VISION) - `/mnt/DATA/git/govoplan-core/docs/GOVERNMENT_OPERATIONS_VISION.md`
- [Repo-docs-GOVOPLAN-MASTER-ROADMAP](Repo-docs-GOVOPLAN-MASTER-ROADMAP) - `/mnt/DATA/git/govoplan-core/docs/GOVOPLAN_MASTER_ROADMAP.md` - [Repo-docs-GOVOPLAN-MASTER-ROADMAP](Repo-docs-GOVOPLAN-MASTER-ROADMAP) - `/mnt/DATA/git/govoplan-core/docs/GOVOPLAN_MASTER_ROADMAP.md`
- [Repo-docs-GOVOPLAN-MODULE-ROADMAP](Repo-docs-GOVOPLAN-MODULE-ROADMAP) - `/mnt/DATA/git/govoplan-core/docs/GOVOPLAN_MODULE_ROADMAP.md` - [Repo-docs-GOVOPLAN-MODULE-ROADMAP](Repo-docs-GOVOPLAN-MODULE-ROADMAP) - `/mnt/DATA/git/govoplan-core/docs/GOVOPLAN_MODULE_ROADMAP.md`
@@ -21,9 +24,7 @@ This page is generated from repository and product-directory project files.
- [Repo-docs-MODULE-BOUNDARY-DECISIONS](Repo-docs-MODULE-BOUNDARY-DECISIONS) - `/mnt/DATA/git/govoplan-core/docs/MODULE_BOUNDARY_DECISIONS.md` - [Repo-docs-MODULE-BOUNDARY-DECISIONS](Repo-docs-MODULE-BOUNDARY-DECISIONS) - `/mnt/DATA/git/govoplan-core/docs/MODULE_BOUNDARY_DECISIONS.md`
- [Repo-docs-POLICY-CONTRACTS](Repo-docs-POLICY-CONTRACTS) - `/mnt/DATA/git/govoplan-core/docs/POLICY_CONTRACTS.md` - [Repo-docs-POLICY-CONTRACTS](Repo-docs-POLICY-CONTRACTS) - `/mnt/DATA/git/govoplan-core/docs/POLICY_CONTRACTS.md`
- [Repo-docs-PUBLIC-SECTOR-INTEGRATION-STRATEGY](Repo-docs-PUBLIC-SECTOR-INTEGRATION-STRATEGY) - `/mnt/DATA/git/govoplan-core/docs/PUBLIC_SECTOR_INTEGRATION_STRATEGY.md` - [Repo-docs-PUBLIC-SECTOR-INTEGRATION-STRATEGY](Repo-docs-PUBLIC-SECTOR-INTEGRATION-STRATEGY) - `/mnt/DATA/git/govoplan-core/docs/PUBLIC_SECTOR_INTEGRATION_STRATEGY.md`
- [Repo-docs-RBAC-MANIFEST](Repo-docs-RBAC-MANIFEST) - `/mnt/DATA/git/govoplan-core/docs/RBAC_MANIFEST.md`
- [Repo-docs-RELEASE-CATALOG-WORKFLOW](Repo-docs-RELEASE-CATALOG-WORKFLOW) - `/mnt/DATA/git/govoplan-core/docs/RELEASE_CATALOG_WORKFLOW.md` - [Repo-docs-RELEASE-CATALOG-WORKFLOW](Repo-docs-RELEASE-CATALOG-WORKFLOW) - `/mnt/DATA/git/govoplan-core/docs/RELEASE_CATALOG_WORKFLOW.md`
- [Repo-docs-RELEASE-DEPENDENCIES](Repo-docs-RELEASE-DEPENDENCIES) - `/mnt/DATA/git/govoplan-core/docs/RELEASE_DEPENDENCIES.md` - [Repo-docs-RELEASE-DEPENDENCIES](Repo-docs-RELEASE-DEPENDENCIES) - `/mnt/DATA/git/govoplan-core/docs/RELEASE_DEPENDENCIES.md`
- [Repo-docs-REMOTE-WEBUI-BUNDLES](Repo-docs-REMOTE-WEBUI-BUNDLES) - `/mnt/DATA/git/govoplan-core/docs/REMOTE_WEBUI_BUNDLES.md` - [Repo-docs-REMOTE-WEBUI-BUNDLES](Repo-docs-REMOTE-WEBUI-BUNDLES) - `/mnt/DATA/git/govoplan-core/docs/REMOTE_WEBUI_BUNDLES.md`
- [Repo-docs-SCALABILITY-AND-SIZING](Repo-docs-SCALABILITY-AND-SIZING) - `/mnt/DATA/git/govoplan-core/docs/SCALABILITY_AND_SIZING.md` - [Repo-docs-SCALABILITY-AND-SIZING](Repo-docs-SCALABILITY-AND-SIZING) - `/mnt/DATA/git/govoplan-core/docs/SCALABILITY_AND_SIZING.md`
- [Repo-docs-SYSTEM-GOVERNANCE-MANIFEST](Repo-docs-SYSTEM-GOVERNANCE-MANIFEST) - `/mnt/DATA/git/govoplan-core/docs/SYSTEM_GOVERNANCE_MANIFEST.md`

@@ -1,4 +1,4 @@
<!-- codex-wiki-sync:f744da05674d7f1c94f7b85f --> <!-- codex-wiki-sync:e4c303086f9134064a521fb1 -->
> Mirrored from `/mnt/DATA/git/govoplan-core/README.md`. > Mirrored from `/mnt/DATA/git/govoplan-core/README.md`.
> Origin: `repository`. > Origin: `repository`.
@@ -26,8 +26,9 @@ Feature modules own their backend routers, models, migrations, permissions, fron
Canonical policy documents live in `docs/`: Canonical policy documents live in `docs/`:
- [RBAC_MANIFEST.md](docs/RBAC_MANIFEST.md) - [DOCUMENTATION_MAP.md](docs/DOCUMENTATION_MAP.md)
- [SYSTEM_GOVERNANCE_MANIFEST.md](docs/SYSTEM_GOVERNANCE_MANIFEST.md) - [ACCESS_RBAC_MODEL.md](docs/ACCESS_RBAC_MODEL.md)
- [GOVERNANCE_MODEL.md](docs/GOVERNANCE_MODEL.md)
- [MODULE_ARCHITECTURE.md](docs/MODULE_ARCHITECTURE.md) - [MODULE_ARCHITECTURE.md](docs/MODULE_ARCHITECTURE.md)
- [DEPLOYMENT_OPERATOR_GUIDE.md](docs/DEPLOYMENT_OPERATOR_GUIDE.md) - [DEPLOYMENT_OPERATOR_GUIDE.md](docs/DEPLOYMENT_OPERATOR_GUIDE.md)
- [CODEX_WORKFLOW.md](docs/CODEX_WORKFLOW.md) - [CODEX_WORKFLOW.md](docs/CODEX_WORKFLOW.md)
@@ -63,7 +64,7 @@ ENABLED_MODULES=access,campaigns ./.venv/bin/python -m govoplan_core.devserver \
The runner loads the same `GovoplanServerConfig` as `govoplan_core.server.app:app`, builds the platform registry, and passes core plus enabled module source roots to uvicorn as reload directories. After reinstalling the editable package, the same command is also available as `govoplan-devserver`. The runner loads the same `GovoplanServerConfig` as `govoplan_core.server.app:app`, builds the platform registry, and passes core plus enabled module source roots to uvicorn as reload directories. After reinstalling the editable package, the same command is also available as `govoplan-devserver`.
The default development database is PostgreSQL at `postgresql+psycopg://govoplan_dev@127.0.0.1:5432/govoplan_dev`. Store the password in `~/.pgpass`. To force the old disposable SQLite fallback, run with `GOVOPLAN_DEV_DATABASE_BACKEND=sqlite`; that database lives at `runtime/multimailer-dev.db`. The default development database is PostgreSQL at `postgresql+psycopg://govoplan_dev@127.0.0.1:5432/govoplan_dev`. Store the password in `~/.pgpass`. To force the disposable SQLite fallback, run with `GOVOPLAN_DEV_DATABASE_BACKEND=sqlite`; that database lives below `runtime/`.
Local devserver runs do not require Redis. `CELERY_ENABLED` defaults to `false`, so campaign queue actions update database state without publishing Celery tasks. Use the synchronous send flow for local send tests, or set `CELERY_ENABLED=true` only when a Redis broker and worker are running. Local devserver runs do not require Redis. `CELERY_ENABLED` defaults to `false`, so campaign queue actions update database state without publishing Celery tasks. Use the synchronous send flow for local send tests, or set `CELERY_ENABLED=true` only when a Redis broker and worker are running.

@@ -0,0 +1,295 @@
<!-- codex-wiki-sync:5cc4f586f61b0996b6e973b1 -->
> Mirrored from `/mnt/DATA/git/govoplan-core/docs/ACCESS_RBAC_MODEL.md`.
> Origin: `repository`.
> Active tasks and changing state belong in Gitea issues; this wiki page is durable project context.
---
# GovOPlaN RBAC And Resource-Access Model
**Updated:** 2026-07-09
## Authorization Equation
An operation is permitted only when every applicable layer allows it:
```text
effective role/API-key capability
AND resource ownership/share access
AND workflow state
AND active governance/policy constraints
```
RBAC answers what an actor may do. ACLs answer which resource the actor may do
it to. Workflow state and policy decide whether the operation is currently
valid.
## Identity And Scope
```text
Account global login identity
+- User membership tenant-local identity
+- direct tenant roles
+- active group memberships
| +- inherited tenant roles
+- tenant-local API keys
Account
+- direct system-role assignments
```
A browser session has one active tenant membership. System privileges do not
silently grant tenant data access. API keys remain tenant-local and receive the
intersection of their configured scopes and their owner's live tenant scopes on
every request.
## Wildcards
```text
tenant:* every canonical tenant permission
system:* every canonical system permission
* legacy alias interpreted as tenant:* only
```
Tenant wildcards never grant system permissions.
## Canonical Tenant Permissions
Campaigns:
```text
campaign:read
campaign:create
campaign:update
campaign:copy
campaign:archive
campaign:delete
campaign:share
campaign:validate
campaign:build
campaign:review
campaign:send_test
campaign:queue
campaign:control
campaign:send
campaign:retry
campaign:reconcile
```
Recipients:
```text
recipients:read
recipients:write
recipients:import
recipients:export
```
Files:
```text
files:read
files:download
files:upload
files:organize
files:share
files:delete
files:admin
```
Reports and audit:
```text
reports:read
reports:export
reports:send
audit:read
```
Mail servers:
```text
mail_servers:read
mail_servers:use
mail_servers:test
mail_servers:write
mail_servers:manage_credentials
```
Tenant administration:
```text
admin:users:read
admin:users:create
admin:users:update
admin:users:suspend
admin:groups:read
admin:groups:write
admin:groups:manage_members
admin:roles:read
admin:roles:write
admin:roles:assign
admin:api_keys:read
admin:api_keys:create
admin:api_keys:revoke
admin:settings:read
admin:settings:write
admin:policies:read
admin:policies:write
```
## Canonical System Permissions
```text
system:tenants:read
system:tenants:create
system:tenants:update
system:tenants:suspend
system:accounts:read
system:accounts:create
system:accounts:update
system:accounts:suspend
system:roles:read
system:roles:write
system:roles:assign
system:access:read
system:access:assign
system:audit:read
system:settings:read
system:settings:write
system:governance:read
system:governance:write
```
`system:access:*` remains a read/assignment boundary for cross-tenant and
system access handling. It is not a separate primary UI area.
## Default Tenant Roles
- **Owner:** `tenant:*`. At least one active operational owner must remain.
- **Tenant administrator:** settings, policies, users, groups, roles, API keys,
and read access to campaigns/files/reports/audit. Real delivery remains
separately delegable.
- **Administrator:** all tenant permissions for upgraded installations.
- **Access administrator:** membership and assignment management within
delegation limits.
- **Campaign manager:** prepare, validate, and build campaigns; no review
approval or real delivery by default.
- **Reviewer:** inspect and approve prepared campaign messages.
- **Sender:** mock-test, queue, control, send, retry, and reconcile prepared
campaigns; can use/test approved mail profiles.
- **File manager:** managed file operations without campaign delivery rights.
- **Viewer:** read campaigns, recipients, files, and reports.
- **Auditor:** read campaigns, recipient evidence, reports, and audit records;
export detailed evidence.
## Default System Roles
- **System owner:** `system:*`, protected. At least one active account must
retain it.
- **System administrator:** all specific system permissions, editable and not
protected.
- **System auditor:** read-only system registry/settings/governance/audit role,
editable.
## Delegation Ceiling
For role definition, assignment, and API-key creation:
```text
requested scopes subset of actor delegateable scopes
```
Rules:
1. Tenant roles may contain tenant scopes only.
2. System roles may contain system scopes only.
3. Definition rights and assignment rights are separate.
4. Group definition and group membership management are separate.
5. API-key scopes are intersected with the owner's current effective scopes on
every request.
6. Suspended accounts, users, tenants, or groups stop contributing access
immediately.
7. Administrative updates are field-sensitive; a user with only status
authority cannot change role assignments.
## Campaign Ownership And ACLs
A campaign has exactly one owner:
```text
owner user OR owner group
```
Additional active shares may target users or groups with `read` or `write`.
Resolution:
- owner user: read and write;
- member of owner group: read and write;
- explicit read share: read;
- explicit write share: read and write;
- `tenant:*`: tenant-wide ACL bypass;
- ordinary campaign permission without ownership/share: no object access.
ACLs do not add capabilities. A write share still needs the specific permission
for update, validation, review, send, report, retry, or reconciliation.
## Files
The current file access model distinguishes tenant-level file capabilities from
space/folder/file ownership:
| Scope | Meaning |
| --- | --- |
| `files:read` | browse/read visible file spaces and metadata |
| `files:download` | download file content when ACL permits |
| `files:upload` | create files in writable spaces |
| `files:organize` | create folders, move files, and update metadata where ACL permits |
| `files:share` | share files/spaces according to owner and policy rules |
| `files:delete` | delete or retire files where ACL permits |
| `files:admin` | tenant-wide administration of user/group file spaces |
External file connections and spaces are additionally constrained by connector
policy and owner/group assignment in the files module.
## Mail Servers
| Scope | Meaning |
| --- | --- |
| `mail_servers:read` | profile metadata and effective policy visibility |
| `mail_servers:use` | use an allowed profile for a campaign or message flow |
| `mail_servers:test` | run connectivity tests without revealing secrets |
| `mail_servers:write` | create/update profile metadata where policy allows |
| `mail_servers:manage_credentials` | create/replace SMTP/IMAP secrets or lower-level credentials where policy allows |
Reusable encrypted profiles exist. Effective usability is also constrained by
hierarchical mail-profile policy, ownership, allowed/forced profile sets,
credential inheritance mode, lower-level override switches, and allow/deny
patterns.
## Compatibility Aliases
Compatibility aliases may exist in backend code for upgraded installations, but
new UI and docs should use canonical scopes.
Current alias direction:
```text
* -> tenant:*
system:tenants:write -> create/update/suspend tenant scopes
system:access:write -> system access assignment/write scopes
```
A separate `retention:*` family is not currently canonical because retention is
managed through system settings and tenant policy scopes. Add it only if
retention operation duties need separation from general policy/settings
administration.

@@ -1,4 +1,4 @@
<!-- codex-wiki-sync:ff49eae099d66fa2cc85476a --> <!-- codex-wiki-sync:c50d21c9bc23d7e4d8722b2d -->
> Mirrored from `/mnt/DATA/git/govoplan-core/docs/API_CONDITIONAL_DELTA.md`. > Mirrored from `/mnt/DATA/git/govoplan-core/docs/API_CONDITIONAL_DELTA.md`.
> Origin: `repository`. > Origin: `repository`.
@@ -36,6 +36,11 @@ changed inside a collection.
Collection endpoints that can expose row-level changes should use the shared Collection endpoints that can expose row-level changes should use the shared
delta contract instead of inventing module-specific formats. delta contract instead of inventing module-specific formats.
Core provides `core_change_sequence` as the shared monotonic change sequence.
Modules record append-only entries in the same database transaction as the
resource write. Watermarks are encoded as `seq:<number>` and should be treated
as opaque by clients.
Backend shape: Backend shape:
```json ```json
@@ -51,8 +56,8 @@ Backend shape:
Fields: Fields:
- `items`: changed or current items since the requested watermark. - `items`: changed or current items since the requested watermark.
- `deleted`: deleted item markers with at least `id`, and optionally `revision` - `deleted`: deleted item markers with at least `id`, and optionally
and `deleted_at`. `resource_type`, `revision`, and `deleted_at`.
- `watermark`: opaque value the client sends as `since` on the next request. - `watermark`: opaque value the client sends as `since` on the next request.
- `has_more`: true when the client should request the next page with the - `has_more`: true when the client should request the next page with the
returned watermark. returned watermark.
@@ -66,6 +71,18 @@ Recommended query parameters:
- `limit`: maximum number of changed items plus deleted markers. - `limit`: maximum number of changed items plus deleted markers.
- `include_deleted`: whether deleted markers should be returned. - `include_deleted`: whether deleted markers should be returned.
Modules should base watermarks on a monotonic revision, audit/event sequence, or Modules should record changes with:
updated/deleted timestamp that is scoped to the same tenant and authorization
rules as the endpoint response. - `module_id`: the owning module, for example `files`.
- `collection`: the delta collection, for example `files.assets`.
- `resource_type`: stable row kind, for example `file` or `folder`.
- `resource_id`: stable resource identifier.
- `operation`: `created`, `updated`, or `deleted`.
- `tenant_id`: tenant scope when the change is tenant-owned.
- `payload`: small, non-secret routing metadata that helps determine whether a
tombstone belongs to the requested view.
The first concrete consumer is `GET /api/v1/files/delta`. Without `since`, it
returns the current files/folders snapshot for the requested owner/campaign
scope. With `since=seq:<number>`, it returns changed files, changed folders,
and tombstones for resources that left the current view.

@@ -1,4 +1,4 @@
<!-- codex-wiki-sync:acb892c7bff57bb32a283613 --> <!-- codex-wiki-sync:697692fe9fef40e247e27cb5 -->
> Mirrored from `/mnt/DATA/git/govoplan-core/docs/DEPLOYMENT_OPERATOR_GUIDE.md`. > Mirrored from `/mnt/DATA/git/govoplan-core/docs/DEPLOYMENT_OPERATOR_GUIDE.md`.
> Origin: `repository`. > Origin: `repository`.
@@ -165,8 +165,8 @@ prefer `FILE_STORAGE_*`.
| Setting | Default | Notes | | Setting | Default | Notes |
| --- | --- | --- | | --- | --- | --- |
| `CORS_ORIGINS` | local dev origins | Set to the exact WebUI origins in staging/production. | | `CORS_ORIGINS` | local dev origins | Set to the exact WebUI origins in staging/production. |
| `AUTH_SESSION_COOKIE_NAME` | `msm_session` | Change only through a controlled rollout because it logs users out. | | `AUTH_SESSION_COOKIE_NAME` | configured default | Change only through a controlled rollout because it logs users out. |
| `AUTH_CSRF_COOKIE_NAME` | `msm_csrf` | Must match WebUI/API deployment. | | `AUTH_CSRF_COOKIE_NAME` | configured default | Must match WebUI/API deployment. |
| `AUTH_COOKIE_SECURE` | `false` | Set `true` behind HTTPS. | | `AUTH_COOKIE_SECURE` | `false` | Set `true` behind HTTPS. |
| `AUTH_COOKIE_SAMESITE` | `lax` | Use a stricter value only after testing login and CSRF flows. | | `AUTH_COOKIE_SAMESITE` | `lax` | Use a stricter value only after testing login and CSRF flows. |
| `AUTH_COOKIE_DOMAIN` | empty | Set only when the API and WebUI intentionally share a parent domain. | | `AUTH_COOKIE_DOMAIN` | empty | Set only when the API and WebUI intentionally share a parent domain. |
@@ -264,15 +264,25 @@ GOVOPLAN_STOP_PROFILE_DEPENDENCIES_ON_EXIT=1 scripts/launch-production-like-dev.
## Module Install/Uninstall Operations ## Module Install/Uninstall Operations
Use Admin > System > Modules for planning. Use the operator shell for package Use Admin > System > Modules for planning. The running API server validates and
changes: queues install plans; it does not run pip/npm or restart itself from an HTTP
request. Package mutation belongs to the trusted installer CLI/daemon in an
operator shell while maintenance mode is active.
Preflight from the server shell:
```bash ```bash
govoplan-module-installer --format shell govoplan-module-installer --format shell
```
Apply a prepared plan directly from a controlled shell:
```bash
govoplan-module-installer --apply --build-webui govoplan-module-installer --apply --build-webui
``` ```
For production-like runs, prefer supervised mode with restart and health checks: For production-like runs, prefer supervised mode with migrations, database
backup/restore hooks, restart commands, and health checks:
```bash ```bash
govoplan-module-installer \ govoplan-module-installer \
@@ -286,6 +296,71 @@ govoplan-module-installer \
--database-restore-command 'pg_restore --clean --if-exists --dbname "$GOVOPLAN_DATABASE_URL_PGTOOLS" "$GOVOPLAN_DATABASE_BACKUP_PATH"' --database-restore-command 'pg_restore --clean --if-exists --dbname "$GOVOPLAN_DATABASE_URL_PGTOOLS" "$GOVOPLAN_DATABASE_BACKUP_PATH"'
``` ```
To let the admin UI submit work without executing package managers inside the
API process, run the daemon in a separate operator shell:
```bash
govoplan-module-installer \
--daemon \
--migrate \
--build-webui \
--database-backup-command 'pg_dump --format=custom "$GOVOPLAN_DATABASE_URL_PGTOOLS" > "$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_PGTOOLS" "$GOVOPLAN_DATABASE_BACKUP_PATH"' \
--health-url http://127.0.0.1:8000/health \
--restart-command '<restart govoplan server>'
```
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`. Check daemon status with:
```bash
govoplan-module-installer --daemon-status --format json
```
The installer uses a runtime lock, snapshots `pip freeze` plus WebUI package
files, writes a run record, and marks planned rows as applied only after all
commands succeed. With `--migrate`, SQLite databases are backed up through
SQLite's backup API; non-SQLite databases require
`--database-backup-command`, `--database-restore-check-command`, and
`--database-restore-command`.
Database hook commands receive:
- `GOVOPLAN_INSTALLER_RUN_DIR`
- `GOVOPLAN_DATABASE_URL`
- `GOVOPLAN_DATABASE_URL_PGTOOLS` for PostgreSQL tools
- `GOVOPLAN_DATABASE_BACKUP_PATH`
- `GOVOPLAN_DATABASE_BACKUP_METADATA`
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 <run-id> --format json
govoplan-module-installer --lock-status --format json
govoplan-module-installer --list-requests --format json
govoplan-module-installer --show-request <request-id> --format json
govoplan-module-installer --cancel-request <request-id> --format json
govoplan-module-installer --retry-request <request-id> --format json
```
Rollback uses the saved run snapshot:
```bash
govoplan-module-installer --rollback <run-id>
govoplan-module-installer --rollback <run-id> --database-restore-command '<override restore command>'
```
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.
Run the rollback drill before relying on installer automation in a new Run the rollback drill before relying on installer automation in a new
environment: environment:
@@ -293,8 +368,22 @@ environment:
./.venv/bin/python scripts/module-installer-rollback-drill.py --format json ./.venv/bin/python scripts/module-installer-rollback-drill.py --format json
``` ```
See `RELEASE_DEPENDENCIES.md` for release package refs, catalog trust, The drill uses temporary SQLite databases and simulated package commands. It
installer daemon operation, rollback records, and destructive module retirement. does not install or uninstall real packages. It exercises:
- package command failure followed by supervised rollback;
- migration failure with a SQLite database snapshot;
- restart-command failure;
- health timeout after restart;
- destructive retirement executor failure with database rollback;
- PostgreSQL-style backup, restore-check, and restore hooks;
- daemon heartbeat, request queue claim/update, retry/cancel, and stale lock
detection/removal.
See `RELEASE_DEPENDENCIES.md` for release package refs and migration baseline
checks. See `CATALOG_TRUST_AND_LICENSING.md` and
`RELEASE_CATALOG_WORKFLOW.md` for catalog trust, signing, keyring, replay, and
license operation.
## Operator Checklist ## Operator Checklist

@@ -0,0 +1,60 @@
<!-- codex-wiki-sync:ce7ebc0e58d89829724fe8ae -->
> Mirrored from `/mnt/DATA/git/govoplan-core/docs/DOCUMENTATION_MAP.md`.
> Origin: `repository`.
> Active tasks and changing state belong in Gitea issues; this wiki page is durable project context.
---
# GovOPlaN Documentation Map
This map defines the source-of-truth documents for the current repository docs.
Use it to avoid duplicating long procedures across architecture, release,
operator, and roadmap pages.
## Core Platform
| Topic | Canonical document | Notes |
| --- | --- | --- |
| Module architecture and kernel contracts | `MODULE_ARCHITECTURE.md` | Stable module contracts, boundaries, lifecycle, and WebUI contribution rules. |
| Access extraction history | `ACCESS_EXTRACTION_PLAN.md` | Historical extraction plan and remaining ownership context only. |
| RBAC and resource access | `ACCESS_RBAC_MODEL.md` | Current permission, role, API-key, and resource-access model. |
| Governance hierarchy | `GOVERNANCE_MODEL.md` | System, tenant, user/group, campaign policy inheritance and admin UI structure. |
| Policy decision DTOs and provenance | `POLICY_CONTRACTS.md` | Shared explain/provenance shape; module-specific policy docs should link here. |
| Events and audit trace context | `EVENTS_AND_AUDIT.md` | Event dispatch semantics and audit payload conventions. |
## Release And Operations
| Topic | Canonical document | Notes |
| --- | --- | --- |
| Runtime configuration and operator flow | `DEPLOYMENT_OPERATOR_GUIDE.md` | Production/staging configuration, migrations, backups, installer operation, and rollback drill. |
| Release package dependencies | `RELEASE_DEPENDENCIES.md` | Release package refs, migration baselines, release lockfiles, and release checklist. |
| Catalog publishing workflow | `RELEASE_CATALOG_WORKFLOW.md` | Release-machine steps for signed catalog/keyring publication through `govoplan-web`. |
| Catalog trust and licensing rules | `CATALOG_TRUST_AND_LICENSING.md` | Catalog shape, signatures, keyrings, replay protection, and offline licenses. |
| Remote WebUI bundle design | `REMOTE_WEBUI_BUNDLES.md` | Experimental controlled-deployment design; normal releases still use package builds. |
| Scalability and sizing | `SCALABILITY_AND_SIZING.md` | Core sizing profiles until `govoplan-ops` owns executable operations tooling. |
## Product And Module Planning
| Topic | Canonical document | Notes |
| --- | --- | --- |
| Product roadmap waves | `GOVOPLAN_MASTER_ROADMAP.md` | Product-level sequencing and implementation gates. |
| Module and issue routing | `GOVOPLAN_MODULE_ROADMAP.md` | Quick map from ideas to owning repositories and issues. |
| Durable boundary decisions | `MODULE_BOUNDARY_DECISIONS.md` | Native-vs-connector decisions and missing-module criteria. |
| Public-sector integration posture | `PUBLIC_SECTOR_INTEGRATION_STRATEGY.md` | Strategy index; executable target inventory lives in `govoplan-connectors`. |
| Configuration packages | `CONFIGURATION_PACKAGES.md` | Package model, provider contract, import/export flow, and tracking slices. |
| Government operations vision | `GOVERNMENT_OPERATIONS_VISION.md` | Product vision and reference journeys; active work belongs in issues. |
## Workflow Docs
| Topic | Canonical document | Notes |
| --- | --- | --- |
| Gitea issues and wiki sync | `GITEA_ISSUES.md` | Issue labels, imports, wiki mirroring, and Codex state updates. |
| Codex local workflow | `CODEX_WORKFLOW.md` | Local agent setup and focused verification commands. |
## Cross-Repo Rule
Core docs may keep strategy, kernel contracts, and routing decisions. Module
repositories should own executable module behavior, concrete API/UI contracts,
and operator notes for their own module. When content spans both, keep the
durable decision in core and link to the module document for implementation
details.

@@ -1,4 +1,4 @@
<!-- codex-wiki-sync:594603fc964c35213662c309 --> <!-- codex-wiki-sync:140f86b93e1b868e4b7970c1 -->
> Mirrored from `/mnt/DATA/git/govoplan-core/docs/GITEA_ISSUES.md`. > Mirrored from `/mnt/DATA/git/govoplan-core/docs/GITEA_ISSUES.md`.
> Origin: `repository`. > Origin: `repository`.
@@ -215,6 +215,13 @@ Apply the wiki mirror:
./scripts/gitea-sync-wiki.py --env-file /home/zemion/.config/gitea/gitea.env --apply ./scripts/gitea-sync-wiki.py --env-file /home/zemion/.config/gitea/gitea.env --apply
``` ```
After renaming or deleting repository docs, prune previously managed wiki pages
that no longer have a source file:
```bash
./scripts/gitea-sync-wiki.py --env-file /home/zemion/.config/gitea/gitea.env --repo govoplan-core --prune-managed --apply
```
The default apply path uses the Gitea wiki git repository, not one REST API The default apply path uses the Gitea wiki git repository, not one REST API
request per page. It keeps a local checkout cache below request per page. It keeps a local checkout cache below
`/tmp/codex-gitea-wiki-sync`, commits changed pages once per repository, and `/tmp/codex-gitea-wiki-sync`, commits changed pages once per repository, and

@@ -1,18 +1,18 @@
<!-- codex-wiki-sync:52df55abecef39b15ef93822 --> <!-- codex-wiki-sync:41153300a1e89969d44135dd -->
> Mirrored from `/mnt/DATA/git/govoplan-core/docs/SYSTEM_GOVERNANCE_MANIFEST.md`. > Mirrored from `/mnt/DATA/git/govoplan-core/docs/GOVERNANCE_MODEL.md`.
> Origin: `repository`. > Origin: `repository`.
> Active tasks and changing state belong in Gitea issues; this wiki page is durable project context. > Active tasks and changing state belong in Gitea issues; this wiki page is durable project context.
--- ---
# Multi Seal Mail - Current System and Tenant Governance Model # GovOPlaN Governance Model
**Updated:** 2026-06-16 **Updated:** 2026-07-09
**Current migration head:** `f5a6b7c8d9e0`
## Governance Rule ## Governance Rule
System policy is authoritative for tenants and all lower levels. Each lower level may only narrow what it inherits: System policy is authoritative for tenants and all lower levels. Each lower
level may only narrow what it inherits:
```text ```text
system system
@@ -21,52 +21,65 @@ system
-> campaign -> campaign
``` ```
Lower levels do not widen privileges, allowed profiles, retention durations or credential rights granted by a higher level. Lower levels do not widen privileges, allowed profiles, retention durations, or
credential rights granted by a higher level.
## Administration Structure ## Administration Structure
GovOPlaN separates system administration from scoped configuration:
```text ```text
SYSTEM ADMINISTRATION
- Settings - Modules
- Retention - Packages
- Mail servers - Maintenance
- Changes
GLOBAL
- Tenants - Tenants
- Users - Roles
- Groups - Groups and users
- System roles - File connectors
- Tenant roles - Mail servers
- Audit - API keys
- Retention
TENANT TENANT
- Settings boundary
- Users
- Groups
- Roles - Roles
- API keys - Groups and users
- File connectors
- Mail servers - Mail servers
- API keys
- Retention - Retention
- Audit
USER
- User mail
- User retention
GROUP GROUP
- Group mail - File connectors
- Group retention - Mail servers
- API keys
- Retention
USER
- File connectors
- Mail servers
- API keys
- Retention
``` ```
There is no separate System access page. Compatibility access scopes remain in the backend for assignment/read boundaries. System access scopes remain in the backend for assignment/read boundaries, but
the UI should present the configuration hierarchy rather than a separate
"system access" concept.
## Tenant Governance ## Tenant Governance
System settings define tenant defaults and whether tenants may narrow selected options. Tenant overrides can only restrict: System settings define tenant defaults and whether tenants may narrow selected
options. Tenant overrides can only restrict:
- custom groups; - custom groups;
- custom roles; - custom roles;
- tenant API keys. - tenant API keys.
The backend enforces that tenant governance cannot widen system-denied privileges. The backend enforces that tenant governance cannot widen system-denied
privileges.
## Mail-Profile Governance ## Mail-Profile Governance
@@ -80,7 +93,10 @@ group
campaign campaign
``` ```
Effective campaign profile availability follows campaign ownership. A campaign owned by a user resolves through system, tenant, that user and campaign policy. A group-owned campaign resolves through system, tenant, that group and campaign policy. Effective campaign profile availability follows campaign ownership. A campaign
owned by a user resolves through system, tenant, that user, and campaign
policy. A group-owned campaign resolves through system, tenant, that group, and
campaign policy.
Policy semantics: Policy semantics:
@@ -88,12 +104,18 @@ Policy semantics:
- lower levels can further restrict the set; - lower levels can further restrict the set;
- forced profiles mean the lower level must choose from the forced set; - forced profiles mean the lower level must choose from the forced set;
- a forced set with one profile effectively enforces that profile; - a forced set with one profile effectively enforces that profile;
- campaign-level profile creation is allowed only if the effective policy permits it; - campaign-level profile creation is allowed only if the effective policy
- SMTP/IMAP credentials use one inheritance decision per protocol: lower levels must inherit profile credentials, may inherit profile credentials, or must provide local credentials; permits it;
- the lower-level override switch for `smtp_credentials.inherit` and `imap_credentials.inherit` controls whether descendants may change that inheritance decision; - SMTP/IMAP credentials use one inheritance decision per protocol: lower levels
must inherit profile credentials, may inherit profile credentials, or must
provide local credentials;
- the lower-level override switch for `smtp_credentials.inherit` and
`imap_credentials.inherit` controls whether descendants may change that
inheritance decision;
- deny patterns always win over allow patterns; - deny patterns always win over allow patterns;
- empty or `*` allowlist means allow all except denied; - empty or `*` allowlist means allow all except denied;
- non-empty allowlist means at least one allow rule must match and no deny rule may match. - non-empty allowlist means at least one allow rule must match and no deny rule
may match.
Pattern targets: Pattern targets:
@@ -105,7 +127,23 @@ From header
recipient domains recipient domains
``` ```
Ownership transfer is intentionally deferred as a two-step workflow: original owner initiates, new owner accepts and reselects/repairs the mail profile if their effective policy requires it. Ownership transfer is intentionally deferred as a two-step workflow: original
owner initiates, new owner accepts and reselects/repairs the mail profile if
their effective policy requires it.
## File-Connector Governance
File connector profiles and credentials are separated. Profiles describe
external endpoints; credentials bind authentication material and policy to a
scope. Concrete linked folders appear as file spaces in the files module.
Governance follows the same inheritance shape as mail:
- system and tenant policy can permit, require, or forbid lower-level
connections/credentials;
- user or group ownership controls which spaces appear to principals;
- spaces inherit endpoint and credential policy from their connector profile;
- connector health and credential tests must not reveal plaintext secrets.
## Retention Governance ## Retention Governance
@@ -127,20 +165,27 @@ Rules:
- system may set concrete defaults or unlimited retention; - system may set concrete defaults or unlimited retention;
- system exposes allow-limiting toggles per field; - system exposes allow-limiting toggles per field;
- tenants, users/groups and campaigns may only shorten inherited retention where the parent allows limiting; - tenants, users/groups, and campaigns may only shorten inherited retention
where the parent allows limiting;
- blank lower-level values inherit; - blank lower-level values inherit;
- mock mailbox retention is currently system-level because mock mailbox records do not yet carry tenant/campaign ownership metadata; - mock mailbox retention is currently system-level because mock mailbox records
- dry-run/apply retention actions report affected classes before destructive cleanup. do not yet carry tenant/campaign ownership metadata;
- dry-run/apply retention actions report affected classes before destructive
cleanup.
## Role Definitions and Assignments ## Role Definitions And Assignments
### System roles ### System Roles
System roles define instance-wide permissions. `system:*` is stored as one wildcard and displayed as granting the full system catalogue. System owner is protected. System roles define instance-wide permissions. `system:*` is stored as one
wildcard and displayed as granting the full system catalogue. System owner is
protected.
### Tenant roles ### Tenant Roles
Tenant roles can be system-governed templates or tenant-local definitions, subject to system tenant-governance settings and actor delegation ceilings. Wildcard counts are expanded against the canonical tenant catalogue. Tenant roles can be system-governed templates or tenant-local definitions,
subject to system tenant-governance settings and actor delegation ceilings.
Wildcard counts are expanded against the canonical tenant catalogue.
## Audit Access ## Audit Access
@@ -151,15 +196,17 @@ system audit -> system:audit:read
tenant audit -> active tenant + audit:read tenant audit -> active tenant + audit:read
``` ```
Audit pages use server pagination, filtering and bounded grids. Audit pages use server pagination, filtering, and bounded grids.
## Tenant Switching ## Tenant Switching
Tenant switching preserves the current URL when possible and falls back when a route/resource is not accessible in the new tenant context. Tenant switching preserves the current URL when possible and falls back when a
route/resource is not accessible in the new tenant context.
The tenant selector is hidden for ordinary single-tenant accounts and visible for multi-tenant or system tenant-management contexts. The tenant selector is hidden for ordinary single-tenant accounts and visible
for multi-tenant or system tenant-management contexts.
## DataGrid Contract in Administration ## Administration DataGrid Contract
Admin lists use bounded container grids: Admin lists use bounded container grids:
@@ -171,14 +218,12 @@ Admin lists use bounded container grids:
- sticky headers where needed; - sticky headers where needed;
- server pagination for audit. - server pagination for audit.
## Still Deferred ## Deferred Work
- real SMTP/IMAP test-bed verification and operator runbook; - real SMTP/IMAP test-bed verification and operator runbook;
- recipient import with column mapping; - recipient import with column mapping;
- Seafile/external connector governance;
- system/tenant/group/user file-space hierarchy and external storage hierarchy;
- session/device revocation UI; - session/device revocation UI;
- backup/restore, monitoring and update procedures; - backup/restore, monitoring, and update procedures;
- DSAR workflows and evidence bundle verifier; - DSAR workflows and evidence bundle verifier;
- campaign ownership transfer workflow; - campaign ownership transfer workflow;
- policy impact analysis before delete/disable/unshare/change; - policy impact analysis before delete/disable/unshare/change;

@@ -1,4 +1,4 @@
<!-- codex-wiki-sync:2ad17e6a9dae70657f3db355 --> <!-- codex-wiki-sync:2309280bdcc08466992f87bf -->
> Mirrored from `/mnt/DATA/git/govoplan-core/docs/GOVOPLAN_MODULE_ROADMAP.md`. > Mirrored from `/mnt/DATA/git/govoplan-core/docs/GOVOPLAN_MODULE_ROADMAP.md`.
> Origin: `repository`. > Origin: `repository`.
@@ -53,12 +53,13 @@ Gitea wiki. Boundary decisions are recorded in
| Installer/deployment operator flow | `govoplan-core`, later `govoplan-ops` | `add-ideas/govoplan-core#26` | | Installer/deployment operator flow | `govoplan-core`, later `govoplan-ops` | `add-ideas/govoplan-core#26` |
| Production-like deployment documentation | `govoplan-core`, later `govoplan-ops` | `add-ideas/govoplan-core#28` | | Production-like deployment documentation | `govoplan-core`, later `govoplan-ops` | `add-ideas/govoplan-core#28` |
## Proposed Missing Modules
## Boundary Decision Register ## Boundary Decision Register
`docs/MODULE_BOUNDARY_DECISIONS.md` is the durable decision register for older `docs/MODULE_BOUNDARY_DECISIONS.md` is the durable decision register for older
roadmap and boundary issues. Current decisions: roadmap and boundary issues. Keep decision rationale there; keep this page as a
quick routing map from ideas to repositories and tracking issues.
Current decisions:
- templates and reporting are separate modules - templates and reporting are separate modules
- RSS/source consume-publish starts in connectors; datasources/dataflow are not - RSS/source consume-publish starts in connectors; datasources/dataflow are not
@@ -72,41 +73,15 @@ roadmap and boundary issues. Current decisions:
## Proposed Missing Modules ## Proposed Missing Modules
`govoplan-datasources` is not created yet. It should exist only if source The following modules are intentionally not created yet. Their candidate
catalog ownership becomes broad enough to justify a module separate from responsibilities and creation criteria live in `MODULE_BOUNDARY_DECISIONS.md`:
connectors, files, and reporting. Candidate responsibilities:
- source catalogue for SQL databases, CSV/Excel files, uploaded files, APIs, RSS feeds, and governed file locations - `govoplan-datasources`
- credentials and connection profiles - `govoplan-dataflow`
- schema discovery and refresh cadence - `govoplan-projects`
- provenance, freshness, permission boundaries, and audit events
`govoplan-dataflow` is not created yet. It should exist only if pipelines and Create a repository only after a concrete implementation package proves that
publication become first-class product behavior beyond workflow, connectors, existing connector, files, reporting, workflow, or task ownership is too narrow.
and reporting. Candidate responsibilities:
- ingestion, transformation, validation, scheduling, and lineage
- connecting datasources to reports, APIs, RSS, exports, and downstream systems
- the "consume sources, become source" lifecycle
- publication-state and audit integration
`govoplan-projects` is intentionally not created yet. The decision is
connector-first through OpenProject. A native module is justified only if
GovOPlaN must own project semantics beyond cases, tasks, workflow,
appointments, documents, and reporting.
## Boundary Notes
- `govoplan-connectors` owns protocol and external-system integration strategy. It should not own business semantics once a domain module exists.
- `govoplan-files` owns file storage semantics and file-provider contracts. Remote provider implementations must stay optional.
- `govoplan-dms` owns document lifecycle, collaboration, versions, approvals, locks, retention, and legal hold.
- `govoplan-addresses` owns persons, organizations, postal/email addresses, distribution lists, and recipient import/export.
- `govoplan-calendar` owns events, availability, resources, recurrence, and groupware calendar adapters.
- `govoplan-scheduling` owns meeting scheduling polls, participant availability collection, candidate-slot ranking, and decision handoff.
- `govoplan-appointments` owns public/internal appointment-booking workflows.
- `govoplan-idm` owns directory, provisioning, and identity-provider integration; `govoplan-access` consumes resolved principals and permissions.
- `govoplan-reporting` owns report definitions, BI views, scheduled outputs, and export targets.
- `govoplan-templates` owns reusable renderable templates, not data selection or persistence.
## Integration Catalogue Routing ## Integration Catalogue Routing
@@ -120,23 +95,5 @@ When a target needs executable behavior, create the implementation issue in the
owning module repository and keep only the cross-module routing or architecture owning module repository and keep only the cross-module routing or architecture
decision in core. decision in core.
## Release Tooling Note Release composition and tag-only repository handling are documented in
`RELEASE_DEPENDENCIES.md`.
`scripts/push-release-tag.sh` covers the released full-product package set:
- `govoplan-access`
- `govoplan-admin`
- `govoplan-tenancy`
- `govoplan-policy`
- `govoplan-audit`
- `govoplan-files`
- `govoplan-mail`
- `govoplan-campaign`
- `govoplan-calendar`
- `govoplan-core`
It also includes existing roadmap/scaffold module repositories such as addresses, appointments, connectors, DMS, forms, IDM, reporting, scheduling, templates, workflow, XOE/V, and XRechnung as tag-only repositories. Tag-only repositories are committed, tagged, and pushed with the same release tag, but they are not added to `requirements-release.txt` or `webui/package.release.json` until they contain installable package metadata.
Proposed modules without repositories, such as `govoplan-datasources`,
`govoplan-dataflow`, and `govoplan-projects`, cannot be included until a later
decision creates their repositories.

@@ -1,4 +1,4 @@
<!-- codex-wiki-sync:17655e955d797a78fafc4a99 --> <!-- codex-wiki-sync:91c58fb1c95354b9b6f10f5a -->
> Mirrored from `/mnt/DATA/git/govoplan-core/docs/MODULE_ARCHITECTURE.md`. > Mirrored from `/mnt/DATA/git/govoplan-core/docs/MODULE_ARCHITECTURE.md`.
> Origin: `repository`. > Origin: `repository`.
@@ -50,9 +50,9 @@ The kernel must not own product semantics such as users, tenants, RBAC decisions
During the staged split, `govoplan-core` still contains compatibility surfaces During the staged split, `govoplan-core` still contains compatibility surfaces
for tenancy settings, governance/policy contracts, audit helpers, CSRF/API for tenancy settings, governance/policy contracts, audit helpers, CSRF/API
helpers, and secret helpers. The extracted access implementation lives in helpers, and secret helpers. The extracted access implementation lives in
`govoplan-access`; live legacy ORM table definitions have been split across `govoplan-access`; live ORM table definitions have been split across their
their platform owners while retaining historical table names. The old core platform owners using module-prefixed table names. The old core route,
route, admin-service, and access-security re-export modules have been removed. admin-service, and access-security re-export modules have been removed.
Callers must use module-owned imports, the public `govoplan_access.auth` request Callers must use module-owned imports, the public `govoplan_access.auth` request
dependency API, or kernel capabilities. dependency API, or kernel capabilities.
The remaining platform compatibility surfaces are temporary until the matching The remaining platform compatibility surfaces are temporary until the matching

@@ -1,298 +0,0 @@
<!-- codex-wiki-sync:40f840a9dc93adaef56c7dc1 -->
> Mirrored from `/mnt/DATA/git/govoplan-core/docs/RBAC_MANIFEST.md`.
> Origin: `repository`.
> Active tasks and changing state belong in Gitea issues; this wiki page is durable project context.
---
# Multi Seal Mail - Current RBAC and Resource-Access Model
**Updated:** 2026-06-16
**Current migration head:** `f5a6b7c8d9e0`
## Authorization Equation
An operation is permitted only when every applicable layer allows it:
```text
effective role/API-key capability
AND resource ownership/share access
AND workflow state
AND active governance/policy constraints
```
RBAC answers what an actor may do. ACLs answer which resource the actor may do it to. Workflow state and policy decide whether the operation is currently valid.
## Identity and Scope
```text
Account global login identity
+- User membership tenant-local identity
+- direct tenant roles
+- active group memberships
| +- inherited tenant roles
+- tenant-local API keys
Account
+- direct system-role assignments
```
A browser session has one active tenant membership. System privileges do not silently grant tenant data access. API keys remain tenant-local and receive the intersection of their configured scopes and their owner's live tenant scopes on every request.
## Wildcards
```text
tenant:* every canonical tenant permission
system:* every canonical system permission
* legacy alias interpreted as tenant:* only
```
Tenant wildcards never grant system permissions.
## Canonical Tenant Permissions - 53
### Campaigns
```text
campaign:read
campaign:create
campaign:update
campaign:copy
campaign:archive
campaign:delete
campaign:share
campaign:validate
campaign:build
campaign:review
campaign:send_test
campaign:queue
campaign:control
campaign:send
campaign:retry
campaign:reconcile
```
### Recipients
```text
recipients:read
recipients:write
recipients:import
recipients:export
```
### Files
```text
files:read
files:download
files:upload
files:organize
files:share
files:delete
files:admin
```
### Reports and Audit
```text
reports:read
reports:export
reports:send
audit:read
```
### Mail Servers
```text
mail_servers:read
mail_servers:use
mail_servers:test
mail_servers:write
mail_servers:manage_credentials
```
### Tenant Administration
```text
admin:users:read
admin:users:create
admin:users:update
admin:users:suspend
admin:groups:read
admin:groups:write
admin:groups:manage_members
admin:roles:read
admin:roles:write
admin:roles:assign
admin:api_keys:read
admin:api_keys:create
admin:api_keys:revoke
admin:settings:read
admin:settings:write
admin:policies:read
admin:policies:write
```
## Canonical System Permissions - 18
```text
system:tenants:read
system:tenants:create
system:tenants:update
system:tenants:suspend
system:accounts:read
system:accounts:create
system:accounts:update
system:accounts:suspend
system:roles:read
system:roles:write
system:roles:assign
system:access:read
system:access:assign
system:audit:read
system:settings:read
system:settings:write
system:governance:read
system:governance:write
```
`system:access:*` remains as a compatibility/read and assignment boundary for cross-tenant/system access handling. It is not a separate primary UI area.
## Default Tenant Roles
- **Owner:** `tenant:*`. At least one active operational owner must remain.
- **Tenant administrator:** settings, policies, users, groups, roles and API keys plus read access to campaigns/files/reports/audit. Real delivery remains separately delegable.
- **Administrator (legacy):** all tenant permissions for upgraded installations.
- **Access administrator:** membership and assignment management within delegation limits.
- **Campaign manager:** prepare, validate and build campaigns; no review approval or real delivery by default.
- **Reviewer:** inspect and approve prepared campaign messages.
- **Sender:** mock-test, queue, control, send, retry and reconcile prepared campaigns; can use/test approved mail profiles.
- **File manager:** managed file operations without campaign delivery rights.
- **Viewer:** read campaigns, recipients, files and reports.
- **Auditor:** read campaigns, recipient evidence, reports and audit records; export detailed evidence.
## Default System Roles
- **System owner:** `system:*`, protected. At least one active account must retain it.
- **System administrator:** all specific system permissions, editable and not protected.
- **System auditor:** read-only system registry/settings/governance/audit role, editable.
## Delegation Ceiling
For role definition, assignment and API-key creation:
```text
requested scopes subset of actor delegateable scopes
```
Rules:
1. Tenant roles may contain tenant scopes only.
2. System roles may contain system scopes only.
3. Definition rights and assignment rights are separate.
4. Group definition and group membership management are separate.
5. API-key scopes are intersected with the owner's current effective scopes on every request.
6. Suspended accounts, users, tenants or groups stop contributing access immediately.
7. Administrative updates are field-sensitive; a user with only status authority cannot change role assignments.
## Campaign Ownership and ACLs
A campaign has exactly one owner:
```text
owner user OR owner group
```
Additional active shares may target users or groups with `read` or `write`.
Resolution:
- owner user: read and write;
- member of owner group: read and write;
- explicit read share: read;
- explicit write share: read and write;
- `tenant:*`: tenant-wide ACL bypass;
- ordinary campaign permission without ownership/share: no object access.
ACLs do not add capabilities. A write share still needs the specific permission for update, validation, review, send, report, retry or reconciliation.
## Sensitive Recipient Boundary
Recipient-complete campaign JSON, message data and job detail require `recipients:read`. Recipient edits require `recipients:write`; exports require `recipients:export`; import is reserved for the dedicated recipient import/list workflow.
## Files
| Permission | Operations |
|---|---|
| `files:read` | list, search, inspect, resolve metadata |
| `files:download` | download file bytes and generated ZIP archives |
| `files:upload` | upload files and ZIP contents |
| `files:organize` | create folders, rename, move, copy and bulk rename |
| `files:share` | create/revoke file shares |
| `files:delete` | delete/hide files and folders subject to retention |
| `files:admin` | tenant-wide administration of user/group file spaces |
## Mail Servers
| Permission | Boundary |
|---|---|
| `mail_servers:read` | profile metadata and effective policy visibility |
| `mail_servers:use` | select an approved profile without reading secrets |
| `mail_servers:test` | run server-side connection tests |
| `mail_servers:write` | define/edit profiles in allowed scopes |
| `mail_servers:manage_credentials` | create/replace SMTP/IMAP secrets or campaign-level credentials where policy allows |
Reusable encrypted profiles now exist. Effective usability is also constrained by hierarchical mail-profile policy, ownership, allowed/forced profile sets, credential inheritance mode, the lower-level override switch for that mode, and allow/deny patterns.
## Sessions, API Keys and CSRF
- Browser login creates an HttpOnly session cookie and a separate readable CSRF cookie.
- Unsafe cookie-authenticated requests require matching CSRF cookie/header and stored CSRF hash.
- API keys remain supported for CLI/automation and do not use browser CSRF.
- Login responses still expose a compatibility session token in the response body; the WebUI does not persist it.
## Legacy Compatibility
Runtime aliases remain only for names that are no longer canonical, including:
```text
campaign:write
attachments:read
attachments:write
admin:users
admin:users:write
admin:api_keys:write
admin:settings
system:tenants:write
system:access:write
```
Canonical scopes are not widened by runtime alias expansion after migration.
## Deferred Permission Families
Add these only with their corresponding implemented features:
```text
templates:*
address_books:*
recipient_lists:*
connectors:*
dsar:*
system:monitoring:read
system:backups:run
system:backups:restore
system:updates:apply
system:updates:rollback
```
A separate `retention:*` family is not currently canonical because retention is managed through system settings and tenant policy scopes. Add it only if retention operation duties need separation from general policy/settings administration.

@@ -1,4 +1,4 @@
<!-- codex-wiki-sync:6527293c59e25e64f6553a58 --> <!-- codex-wiki-sync:7b2a17802d161c1f7bb900e6 -->
> Mirrored from `/mnt/DATA/git/govoplan-core/docs/RELEASE_CATALOG_WORKFLOW.md`. > Mirrored from `/mnt/DATA/git/govoplan-core/docs/RELEASE_CATALOG_WORKFLOW.md`.
> Origin: `repository`. > Origin: `repository`.
@@ -39,7 +39,7 @@ Generate the signed catalog into `govoplan-web`:
cd /mnt/DATA/git/govoplan-core cd /mnt/DATA/git/govoplan-core
KEY_DIR="$HOME/.config/govoplan/release-keys" KEY_DIR="$HOME/.config/govoplan/release-keys"
scripts/publish-release-catalog.sh \ scripts/publish-release-catalog.sh \
--version 0.1.4 \ --version <x.y.z> \
--sequence 202607071340 \ --sequence 202607071340 \
--catalog-signing-key "release-key-1=$KEY_DIR/release-key-1.pem" \ --catalog-signing-key "release-key-1=$KEY_DIR/release-key-1.pem" \
--build-web --build-web
@@ -89,9 +89,10 @@ https://govoplan.add-ideas.de/catalogs/v1/keyring.json
## Integrated Release Script ## Integrated Release Script
`scripts/push-release-tag.sh` can publish the web catalog after module and core `scripts/push-release-tag.sh` can publish the web catalog after module and core
tags have been pushed. It runs the migration release audit in non-strict mode tags have been pushed. It runs the migration release audit in automatic mode:
by default; add `--strict-migration-audit` for stable releases after warning-only before the first recorded migration baseline, strict after a
`docs/migration-release-baselines.json` has been updated: baseline exists. Add `--strict-migration-audit` when you want to force strict
mode explicitly:
```bash ```bash
cd /mnt/DATA/git/govoplan-core cd /mnt/DATA/git/govoplan-core

@@ -1,4 +1,4 @@
<!-- codex-wiki-sync:61ed52f82af2f6866512f2cf --> <!-- codex-wiki-sync:46958666bd160e10691e4273 -->
> Mirrored from `/mnt/DATA/git/govoplan-core/docs/RELEASE_DEPENDENCIES.md`. > Mirrored from `/mnt/DATA/git/govoplan-core/docs/RELEASE_DEPENDENCIES.md`.
> Origin: `repository`. > Origin: `repository`.
@@ -7,9 +7,20 @@
--- ---
# GovOPlaN Release Dependencies # 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. This document owns release package composition: Python package refs, WebUI
release package refs, release lockfiles, migration baselines, and the final
release checklist.
## Backend Operator runtime configuration and module install/uninstall execution live in
`DEPLOYMENT_OPERATOR_GUIDE.md`. Catalog signatures, keyrings, replay
protection, and licensing live in `CATALOG_TRUST_AND_LICENSING.md`. Publishing
the public catalog through `govoplan-web` lives in `RELEASE_CATALOG_WORKFLOW.md`.
## Backend Packages
Release installs must not depend on sibling checkout paths. Local development
can keep editable installs and `file:` WebUI links, but release packaging must
resolve modules from tagged git refs or from a package registry.
Local development: Local development:
@@ -25,14 +36,17 @@ cd /mnt/DATA/git/govoplan-core
./.venv/bin/python -m pip install -r requirements-release.txt ./.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: `.[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 ```bash
cd /mnt/DATA/git/govoplan-core cd /mnt/DATA/git/govoplan-core
/tmp/govoplan-release-test/bin/python -m pip install -r requirements-release.txt /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: `requirements-release.txt` pins the module repositories to the release tag.
Update those refs when cutting a release:
```text ```text
govoplan-access git@git.add-ideas.de:add-ideas/govoplan-access.git v0.1.6 govoplan-access git@git.add-ideas.de:add-ideas/govoplan-access.git v0.1.6
@@ -46,452 +60,15 @@ govoplan-campaign git@git.add-ideas.de:add-ideas/govoplan-campaign.git v0.1.6
govoplan-calendar git@git.add-ideas.de:add-ideas/govoplan-calendar.git v0.1.6 govoplan-calendar git@git.add-ideas.de:add-ideas/govoplan-calendar.git v0.1.6
``` ```
### PostgreSQL Release Check ## WebUI Packages
Release candidates should pass a disposable PostgreSQL migration and startup Local development uses `webui/package.json`, which may point at sibling module
smoke check before tagging or publishing catalogs. Start the local testbed, then checkouts while active development is happening.
run the permutation check from the core checkout:
```bash Release WebUI installs should use `webui/package.release.json`. It points
cd /mnt/DATA/git/govoplan-core/dev/postgres module dependencies at the same tagged git repositories. After the module tags
cp .env.example .env referenced there exist, generate the committed release lockfile without
docker compose --env-file .env up -d touching the development package files:
cd /mnt/DATA/git/govoplan-core
set -a
. dev/postgres/.env
set +a
./.venv/bin/python scripts/postgres-integration-check.py \
--database-url "$GOVOPLAN_POSTGRES_DATABASE_URL" \
--reset-schema
```
The script checks migrations and `/health` startup for core-only, files-only,
mail-only, campaign-only, campaign+files, campaign+mail, and full-product
module sets. `--reset-schema` is destructive and must only be used against a
throwaway database.
### Migration Baselines
Development migrations may be small and numerous while a feature is moving.
Before a stable release, unreleased migrations may be rewritten or squashed into
a release-level baseline or release-to-release upgrade migration. After a
release tag has shipped, released migration revision IDs are immutable.
The release policy is:
- unreleased migrations may be folded before release;
- released migrations are never rewritten or deleted;
- each stable release records the public migration head revisions in
`docs/migration-release-baselines.json`;
- fresh installations should apply release-level baselines/upgrades, not
unreleased create-then-rename churn;
- release-to-release schema changes should be folded into one reviewed
migration per migration owner where practical.
Audit the current graph during release preparation:
```bash
cd /mnt/DATA/git/govoplan-core
./.venv/bin/python scripts/release-migration-audit.py
```
Use strict mode after the baseline file has been updated for the release:
```bash
./.venv/bin/python scripts/release-migration-audit.py --strict
```
`scripts/push-release-tag.sh` runs the audit in non-strict mode by default so
release operators see the current migration heads before tagging. Pass
`--strict-migration-audit` when cutting a release whose migration baselines have
already been recorded, or `--skip-migration-audit` only for emergency/manual
release work.
Before the first stable release, fold the current development chain into the
first public baseline and record that baseline in
`docs/migration-release-baselines.json`. The tracking issue is
`add-ideas/govoplan-core#223`.
## 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 '<restart govoplan server>'
```
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_PGTOOLS" > "$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_PGTOOLS" "$GOVOPLAN_DATABASE_BACKUP_PATH"' \
--health-url http://127.0.0.1:8000/health \
--restart-command '<restart govoplan server>'
```
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_PGTOOLS" > "$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_PGTOOLS" "$GOVOPLAN_DATABASE_BACKUP_PATH"' \
--health-url http://127.0.0.1:8000/health \
--restart-command '<restart govoplan server>'
```
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 <run-id>
govoplan-module-installer --rollback <run-id> --database-restore-command '<override 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_URL_PGTOOLS`: the same PostgreSQL URL converted from
`postgresql+driver://` to `postgresql://` for `pg_dump`, `pg_restore`, and
`psql`; set only for PostgreSQL URLs
- `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 <run-id> --format json
govoplan-module-installer --lock-status --format json
govoplan-module-installer --list-requests --format json
govoplan-module-installer --show-request <request-id> --format json
govoplan-module-installer --cancel-request <request-id> --format json
govoplan-module-installer --retry-request <request-id> --format json
```
### Rollback Drill
Before using the installer daemon in production, run the rollback drill in a
controlled shell from the core checkout:
```bash
./.venv/bin/python scripts/module-installer-rollback-drill.py --format json
```
The drill uses temporary SQLite databases and simulated package commands. It
does not install or uninstall real packages. It exercises:
- package command failure followed by supervised rollback
- migration failure with a SQLite database snapshot
- restart-command failure
- health timeout after restart
- destructive retirement executor failure with database rollback
- PostgreSQL-style backup, restore-check, and restore hooks
- daemon heartbeat, request queue claim/update, retry/cancel, and stale lock
detection/removal
Keep the drill runtime for inspection with:
```bash
./.venv/bin/python scripts/module-installer-rollback-drill.py \
--keep-runtime \
--runtime-root /srv/govoplan/drills/installer-rollback-$(date +%Y%m%d)
```
Each scenario writes an installer run record below
`<runtime-root>/<scenario>/installer/runs/<run-id>/record.json`. For a successful
rollback drill, check:
- `status` is `applied` only for the original package phase when rollback later
happens in the supervisor
- `rollback_status` is `rolled-back` when package/database snapshots were
restored
- `supervisor.status` is `rolled-back` for supervised package, migration,
restart, and health failures
- `supervisor.failure_reason` names the failing restart or health target
- `snapshot.database_backup` exists for migrated SQLite runs and destructive
retirement runs
- PostgreSQL hook drills write `database.external.backup`,
`restore-check.marker`, and `restore.marker`
The PostgreSQL drill validates the hook interface without connecting to a live
database. In production, replace the documented example hooks with deployment
commands that target a disposable staging database first:
```bash
--database-backup-command 'pg_dump --format=custom "$GOVOPLAN_DATABASE_URL_PGTOOLS" > "$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_PGTOOLS" "$GOVOPLAN_DATABASE_BACKUP_PATH"'
```
If a daemon request fails, inspect it and retry only after the underlying cause
is corrected:
```bash
govoplan-module-installer --show-request <request-id> --format json
govoplan-module-installer --show-run <run-id> --format json
govoplan-module-installer --retry-request <request-id> --format json
```
Cancel queued duplicate work before retrying:
```bash
govoplan-module-installer --list-requests --format json
govoplan-module-installer --cancel-request <request-id> --format json
```
For stale locks, first check the lock payload and confirm the recorded process
is no longer running on the host that owns the runtime directory:
```bash
govoplan-module-installer --lock-status --format json
```
Only remove `runtime/module-installer/install.lock` after confirming there is
no active installer process and no package manager command still running. Then
rerun preflight before applying or retrying the request.
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=<base64-ed25519-public-key> \
--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":"<base64-ed25519-public-key>"}'
GOVOPLAN_MODULE_PACKAGE_CATALOG_SEQUENCE_STATE=/srv/govoplan/runtime/catalog-sequences.json
GOVOPLAN_MODULE_PACKAGE_CATALOG_ENFORCE_SEQUENCE=true
```
Back up the sequence state with other runtime metadata. If it is lost or
corrupted, reconstruct the highest accepted sequence per channel from installer
run records or release records, never by lowering the sequence to admit an
older catalog. `docs/CATALOG_TRUST_AND_LICENSING.md` includes the recovery JSON
shape and reset procedure.
Catalog module entries may include `artifact_integrity.python` and
`artifact_integrity.webui` metadata. Each artifact entry can declare:
- `ref`: the exact Python or WebUI install ref expected in the plan
- `sha256`: the expected SHA-256 digest of the resolved artifact
- `path` or `artifact_path`: a local artifact path that the installer can hash
before applying the plan
- `sbom_url`: the published SBOM for the artifact
- `provenance_url`: the published provenance or attestation for the artifact
- `registry_identity` or `git_ref`: the expected registry or source identity
When a saved install plan includes this metadata, installer preflight records
verification results in the run record under `preflight.artifact_integrity`.
Set `GOVOPLAN_MODULE_INSTALLER_REQUIRE_ARTIFACT_INTEGRITY=true` in production
to block package changes whose Python/WebUI artifacts are missing integrity
metadata or cannot be locally verified.
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
```
Operators can validate the imported license and required catalog entitlements:
```bash
govoplan-module-installer \
--validate-license /srv/govoplan/license.json \
--license-trusted-key license-issuer-1="<base64 public key>" \
--require-trusted-license \
--license-required-feature module.mail \
--format json
```
Release or support operators can issue/renew signed offline licenses with
`govoplan-module-installer --issue-license`. Keep the signing private key off
the application server; the admin UI only displays public diagnostics such as
license id, subject, validity window, trusted key id, features, and missing
entitlements.
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. The target
design and follow-up slices are in `docs/REMOTE_WEBUI_BUNDLES.md`.
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 ```bash
cd /mnt/DATA/git/govoplan-core cd /mnt/DATA/git/govoplan-core
@@ -500,16 +77,47 @@ 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 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 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: ### Release Lockfile Strategy
The supported release composition currently is the full GovOPlaN 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 Tag Script
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 ```bash
cd /mnt/DATA/git/govoplan-core cd /mnt/DATA/git/govoplan-core
scripts/push-release-tag.sh --version 0.1.6 scripts/push-release-tag.sh --version 0.1.6
``` ```
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`. 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: Current tag-only module repositories:
@@ -538,17 +146,112 @@ Current tag-only module repositories:
- `govoplan-xrechnung` - `govoplan-xrechnung`
- `govoplan-xta-osci` - `govoplan-xta-osci`
### Release lockfile strategy ## PostgreSQL Release Check
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. Release candidates should pass a disposable PostgreSQL migration and startup
smoke check before tagging or publishing catalogs. Start the local testbed,
then run the permutation check from the core checkout:
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. ```bash
cd /mnt/DATA/git/govoplan-core/dev/postgres
cp .env.example .env
docker compose --env-file .env up -d
cd /mnt/DATA/git/govoplan-core
set -a
. dev/postgres/.env
set +a
./.venv/bin/python scripts/postgres-integration-check.py \
--database-url "$GOVOPLAN_POSTGRES_DATABASE_URL" \
--reset-schema
```
The script checks migrations and `/health` startup for core-only, files-only,
mail-only, campaign-only, campaign+files, campaign+mail, and full-product
module sets. `--reset-schema` is destructive and must only be used against a
throwaway database.
## Migration Baselines
Development migrations may be small and numerous while a feature is moving.
Before a stable release, unreleased migrations may be rewritten or squashed into
a release-level baseline or release-to-release upgrade migration. After a
release tag has shipped, released migration revision IDs are immutable.
The release policy is:
- unreleased migrations may be folded before release;
- released migrations are never rewritten or deleted;
- each stable release records the public migration head revisions in
`docs/migration-release-baselines.json`;
- fresh installations should apply release-level baselines/upgrades, not
unreleased create-then-rename churn;
- release-to-release schema changes should be folded into one reviewed
migration per migration owner where practical.
Audit the current graph during release preparation:
```bash
cd /mnt/DATA/git/govoplan-core
./.venv/bin/python scripts/release-migration-audit.py
```
Generate the reviewed/manual squash checklist:
```bash
./.venv/bin/python scripts/release-migration-audit.py --squash-plan
```
After the release migrations have been reviewed and the graph is final, record
the release baseline:
```bash
./.venv/bin/python scripts/release-migration-audit.py --record-release <x.y.z>
```
Use strict mode to verify that the current heads are recorded:
```bash
./.venv/bin/python scripts/release-migration-audit.py --strict
```
`scripts/push-release-tag.sh` runs the audit by default in automatic mode:
non-strict while no release baseline exists, strict after the first baseline is
recorded. Pass `--warn-migration-audit` for an explicit non-strict audit,
`--strict-migration-audit` to force strict mode, or `--skip-migration-audit`
only for emergency/manual release work.
Before the first stable release, fold the current development chain into the
first public baseline and record that baseline in
`docs/migration-release-baselines.json`. The tracking issue is
`add-ideas/govoplan-core#223`.
## Related Operator Documents
- `DEPLOYMENT_OPERATOR_GUIDE.md`: runtime environment, explicit migrations,
backup/restore commands, module installer daemon/supervisor operation, and
rollback drills.
- `CATALOG_TRUST_AND_LICENSING.md`: catalog JSON shape, signature validation,
key rotation, sequence-state recovery, artifact integrity, and license
enforcement.
- `RELEASE_CATALOG_WORKFLOW.md`: release-machine steps for signing and
publishing `govoplan-web` catalog/keyring artifacts.
- `REMOTE_WEBUI_BUNDLES.md`: experimental browser-loaded module bundles for
controlled deployments; normal releases use package builds.
## Release Checklist ## Release Checklist
- Keep Python package versions, WebUI package versions, and git tags aligned. - 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. - Tag core, access, admin, tenancy, policy, audit, files, mail, campaign,
- Update `requirements-release.txt` and `webui/package.release.json` when the release tag changes. calendar, and scaffold module repositories together.
- Generate the committed full-product release lockfile from `package.release.json` with `scripts/generate-release-lock.sh`. - Update `requirements-release.txt` and `webui/package.release.json` when the
- Add separate release manifest/lockfile pairs only for module compositions that are shipped as their own products. release tag changes.
- Generate the committed full-product release lockfile from
`package.release.json` with `scripts/generate-release-lock.sh`.
- Run `scripts/release-migration-audit.py --strict` after recording a release
baseline.
- Run the PostgreSQL release check against a disposable database.
- Publish the signed catalog through `RELEASE_CATALOG_WORKFLOW.md`.
- 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. - Do not commit local sibling paths into release manifests.