Release v0.1.6
This commit is contained in:
456
docs/ACCESS_EXTRACTION_PLAN.md
Normal file
456
docs/ACCESS_EXTRACTION_PLAN.md
Normal file
@@ -0,0 +1,456 @@
|
||||
# GovOPlaN Access Extraction Plan
|
||||
|
||||
> Backlog state migrated to Gitea issues on 2026-07-06. Keep this document as
|
||||
> durable extraction context and architecture notes. Track active tasks,
|
||||
> decisions, blockers, and implementation state in `add-ideas/govoplan-core`
|
||||
> issues with `module/access` and `source/backlog-import`.
|
||||
|
||||
This plan describes how to extract access, authentication, RBAC, and related
|
||||
administration behavior from the current compatibility core into a dedicated
|
||||
`govoplan-access` platform module.
|
||||
|
||||
The goal is not to make access optional in every real deployment. The goal is to
|
||||
make ownership explicit: the kernel composes modules and exposes contracts;
|
||||
`govoplan-access` owns identity, sessions, API keys, roles, groups, and access
|
||||
administration.
|
||||
|
||||
## Current Inventory
|
||||
|
||||
### Existing Access Module Seed
|
||||
|
||||
`govoplan-access` contains the extracted module-shaped seed package under
|
||||
`src/govoplan_access/backend`. `govoplan-core` keeps compatibility import shims
|
||||
under `src/govoplan_core/access`:
|
||||
|
||||
- `manifest.py` declares `ModuleManifest(id="access")`.
|
||||
- `db/models.py` defines access-owned model candidates.
|
||||
- `auth/principals.py`, `auth/roles.py`, and `auth/tokens.py` contain auth
|
||||
helper concepts.
|
||||
- `permissions/definitions.py`, `permissions/evaluator.py`, and
|
||||
`permissions/registry.py` contain permission catalogue/evaluation concepts.
|
||||
- `tenancy/datastore.py` contains early tenancy access helpers.
|
||||
|
||||
This seed package is now the starting point for the access module, but it is
|
||||
not yet the full live source of truth for the running product.
|
||||
|
||||
### Live Compatibility Ownership In Core
|
||||
|
||||
The active implementation still uses core-owned models and services:
|
||||
|
||||
- `src/govoplan_core/db/models.py`
|
||||
- `Account`
|
||||
- `Tenant`
|
||||
- `User`
|
||||
- `Group`
|
||||
- `Role`
|
||||
- `SystemSettings`
|
||||
- `GovernanceTemplate`
|
||||
- `GovernanceTemplateAssignment`
|
||||
- `SystemRoleAssignment`
|
||||
- `UserGroupMembership`
|
||||
- `UserRoleAssignment`
|
||||
- `GroupRoleAssignment`
|
||||
- `ApiKey`
|
||||
- `AuthSession`
|
||||
- `AuditLog`
|
||||
- `src/govoplan_core/auth/dependencies.py`
|
||||
- `src/govoplan_core/security/api_keys.py`
|
||||
- `src/govoplan_core/security/permissions.py`
|
||||
- `src/govoplan_core/security/sessions.py`
|
||||
- `src/govoplan_core/security/passwords.py`
|
||||
- `src/govoplan_core/security/secrets.py`
|
||||
- `src/govoplan_core/security/module_permissions.py`
|
||||
- `src/govoplan_core/admin/service.py`
|
||||
- `src/govoplan_core/admin/governance.py`
|
||||
- `src/govoplan_core/api/v1/auth.py`
|
||||
- `src/govoplan_core/api/v1/admin.py`
|
||||
- `src/govoplan_core/api/v1/admin_schemas.py`
|
||||
- `src/govoplan_core/api/v1/audit.py`
|
||||
- `src/govoplan_core/db/bootstrap.py`
|
||||
|
||||
Core still hosts the generic login/settings shell and shared WebUI primitives.
|
||||
The legacy administration page has moved to the `govoplan-access` WebUI package
|
||||
and is contributed as the `/admin` route by the access module. Individual admin
|
||||
panels can now be split further into access, admin, tenancy, policy, and audit
|
||||
WebUI contributions without changing the core shell route wiring again.
|
||||
|
||||
### Current Module Consumers
|
||||
|
||||
Feature modules no longer import core auth dependency wrappers or access-owned
|
||||
ORM models. Backend routers import the access-published FastAPI dependency API
|
||||
from `govoplan_access.backend.auth.dependencies`; runtime cooperation uses
|
||||
kernel capabilities such as `access.directory`, `campaigns.access`,
|
||||
`campaigns.mailPolicyContext`, and `campaigns.deliveryTasks`.
|
||||
|
||||
## Target Ownership
|
||||
|
||||
### Kernel
|
||||
|
||||
The kernel keeps only platform composition and stable contracts:
|
||||
|
||||
- module discovery and registry validation
|
||||
- route aggregation
|
||||
- migration orchestration
|
||||
- database engine/session lifecycle
|
||||
- permission catalogue aggregation
|
||||
- capability registry
|
||||
- event/command envelopes
|
||||
- dependency injection hooks
|
||||
- health and diagnostics
|
||||
- compatibility facades while modules migrate
|
||||
|
||||
The kernel should not own account, tenant, role, group, session, or API-key
|
||||
semantics.
|
||||
|
||||
### `govoplan-access`
|
||||
|
||||
`govoplan-access` owns:
|
||||
|
||||
- accounts
|
||||
- authentication routes
|
||||
- session lifecycle
|
||||
- API keys
|
||||
- users
|
||||
- groups and memberships
|
||||
- roles and assignments
|
||||
- principal resolution
|
||||
- permission evaluation
|
||||
- access administration routes
|
||||
- access administration WebUI contributions
|
||||
- access-related migrations
|
||||
- access permissions and role templates
|
||||
|
||||
### Later Platform Modules
|
||||
|
||||
Some current access-adjacent behavior can remain in access initially, but should
|
||||
have clean seams for later extraction:
|
||||
|
||||
- `govoplan-tenancy`: tenants, tenant lifecycle, tenant switching, tenant
|
||||
metadata, tenant settings boundaries.
|
||||
- `govoplan-policy`: hierarchical policy/effective policy resolution and
|
||||
provenance display contracts.
|
||||
- `govoplan-audit`: audit log storage, audit routes, retention hooks, evidence
|
||||
exports.
|
||||
- `govoplan-admin`: generic administration shell contributions if they are not
|
||||
owned by access/tenancy/policy/audit directly.
|
||||
|
||||
## Kernel Contracts To Stabilize First
|
||||
|
||||
Before moving live code, define contracts that feature modules can depend on
|
||||
without importing access ORM models:
|
||||
|
||||
- `PrincipalResolver`
|
||||
- resolves the current actor from a request/session/API key.
|
||||
- returns a stable DTO, not an ORM model.
|
||||
- `AccessDirectory`
|
||||
- resolves users, groups, memberships, and display labels by stable IDs.
|
||||
- supports batch lookups for grids and policy screens.
|
||||
- `TenantResolver`
|
||||
- resolves current tenant context and tenant metadata by stable ID.
|
||||
- `PermissionEvaluator`
|
||||
- evaluates required scopes for a principal and resource.
|
||||
- `ResourceAccessProvider`
|
||||
- lets modules register resource ACL behavior without importing each other.
|
||||
- `SecretProvider`
|
||||
- encrypts/decrypts named secrets without tying consumers to access models.
|
||||
- `AuditSink`
|
||||
- records audit events without importing audit storage models.
|
||||
|
||||
DTOs should be small and serializable:
|
||||
|
||||
- `PrincipalRef`
|
||||
- `AccountRef`
|
||||
- `TenantRef`
|
||||
- `UserRef`
|
||||
- `GroupRef`
|
||||
- `RoleRef`
|
||||
|
||||
## Extraction Stages
|
||||
|
||||
### Stage 0: Baseline Safeguards
|
||||
|
||||
Status: started.
|
||||
|
||||
- Document the kernel/platform module model.
|
||||
- Add dependency-boundary checks.
|
||||
- Add backend module permutation startup tests.
|
||||
- Inventory access/auth/RBAC imports.
|
||||
- Draft this extraction plan.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- `scripts/check_dependency_boundaries.py` passes.
|
||||
- backend module permutation tests start every supported module combination.
|
||||
- current direct imports are tracked as explicit transitional debt.
|
||||
|
||||
### Stage 1: Contract Definitions
|
||||
|
||||
Add kernel-owned protocols and DTOs for access-related interaction.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Add protocol definitions under a kernel contract package.
|
||||
- Add registry/capability names for access services.
|
||||
- Keep existing core dependency functions as compatibility wrappers.
|
||||
- Make wrappers resolve through capabilities when `govoplan-access` is present.
|
||||
- Add tests for missing-capability behavior and clear error messages.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Feature modules can receive principal/tenant/group/user references without
|
||||
importing access ORM models.
|
||||
- Existing routes continue to work through compatibility wrappers.
|
||||
|
||||
### Stage 2: Create `govoplan-access`
|
||||
|
||||
Create the new repository/package and move the access seed package into it.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Create package metadata and entry points for `govoplan-access`.
|
||||
- Move `govoplan_core/access` implementation into the new package.
|
||||
- Publish `ModuleManifest(id="access")`.
|
||||
- Register access permissions, role templates, routers, and migrations.
|
||||
- Add compatibility imports in core where needed.
|
||||
- Add release/dev dependency entries in core.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- `govoplan-core + govoplan-access` starts through normal module discovery.
|
||||
- `govoplan-core` compatibility imports still work during transition.
|
||||
- access manifest, migrations, and route contributions are discovered by core.
|
||||
|
||||
### Stage 3: Move Live Models And Migrations
|
||||
|
||||
Move active identity/access models out of `govoplan_core.db.models`.
|
||||
|
||||
Current state: the stable table naming strategy is to keep legacy table names
|
||||
and move SQLAlchemy class definitions under their platform owners. The old
|
||||
`govoplan_core.db.models` compatibility re-export has been removed; callers
|
||||
must import module-owned models or use kernel capabilities. The earlier
|
||||
`access_*` candidate tables are not active metadata.
|
||||
|
||||
Current table ownership:
|
||||
|
||||
- `govoplan-tenancy`: `tenants`
|
||||
- `govoplan-access`: `accounts`, tenant memberships in `users`, `groups`,
|
||||
`roles`, role/group assignment tables, `api_keys`, and `auth_sessions`
|
||||
- `govoplan-admin`: governance templates and assignments
|
||||
- `govoplan-audit`: audit log
|
||||
- `govoplan-core`: system settings
|
||||
|
||||
Tasks:
|
||||
|
||||
- [x] Decide the stable table naming strategy.
|
||||
- [x] Map legacy model names to access-owned model classes.
|
||||
- [x] Keep database table names where possible to avoid data migration churn.
|
||||
- [x] Add Alembic migration metadata owned by the platform module owners.
|
||||
- [x] Remove core model compatibility aliases after callers moved to
|
||||
module-owned imports.
|
||||
- [x] Update bootstrap/create-all compatibility paths.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Existing development databases migrate without data loss.
|
||||
- New databases initialize with module-owned metadata.
|
||||
- Core no longer owns or re-exports live account/user/group/role/session/API-key
|
||||
model definitions.
|
||||
|
||||
### Stage 4: Move Auth Routes And Dependencies
|
||||
|
||||
Move authentication, sessions, API keys, and principal dependencies.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Move `/api/v1/auth/*` implementation to access.
|
||||
- Move session/API-key services to access.
|
||||
- Keep core route compatibility only if required by clients.
|
||||
- Replace feature-module imports of core auth dependencies with the
|
||||
access-published FastAPI dependency API.
|
||||
- Add tests for login, session refresh, tenant selection, API-key auth, and
|
||||
missing access capability failures.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Auth behavior works through the access module.
|
||||
- Feature modules do not import access internals except the published
|
||||
`govoplan_access.backend.auth.dependencies` dependency API used by FastAPI
|
||||
routers.
|
||||
- Core can explain startup failure clearly if auth-required routes are enabled
|
||||
without the access capability.
|
||||
|
||||
### Stage 5: Move Admin And WebUI Contributions
|
||||
|
||||
Move access administration UI/API ownership into modules.
|
||||
|
||||
Tasks:
|
||||
|
||||
- [x] Move users, groups, roles, API keys, and access settings pages into
|
||||
`govoplan-access`.
|
||||
- [ ] Move tenant-specific pages to `govoplan-tenancy` when that module exists, or
|
||||
keep them temporarily in access with clear boundaries.
|
||||
- [x] Move overview, system settings, and governance-template panels into
|
||||
`govoplan-admin`.
|
||||
- [x] Register admin navigation and admin sections through module
|
||||
contributions.
|
||||
- [x] Keep core shell, layout, route rendering, and generic components only.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Core WebUI shell renders admin/access pages from module contributions.
|
||||
- Core does not import access page components directly.
|
||||
- Module nav and route metadata remain serializable.
|
||||
- The access admin shell consumes module-owned `admin.sections` capability
|
||||
contributions without importing sibling module panels.
|
||||
|
||||
### Stage 6: Decouple Feature Modules
|
||||
|
||||
Remove direct ORM/model imports from files, mail, and campaign.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Replace `Group`, `Tenant`, `User`, and `UserGroupMembership` imports with
|
||||
directory/capability lookups.
|
||||
- Replace direct cross-module cleanup/count queries with registered providers,
|
||||
events, or module-owned API contracts.
|
||||
- Replace mail-profile ownership resolution with stable owner references.
|
||||
- Replace campaign/file access checks with resource ACL provider contracts.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- `govoplan-files`, `govoplan-mail`, and `govoplan-campaign` do not import
|
||||
access implementation modules or sibling feature modules.
|
||||
- Dependency-boundary allowlist stays empty after each replacement.
|
||||
|
||||
### Stage 7: Remove Compatibility Debt
|
||||
|
||||
Finalize the split.
|
||||
|
||||
Tasks:
|
||||
|
||||
- Remove obsolete core compatibility aliases.
|
||||
- Keep the dependency-boundary checker allowlist empty.
|
||||
- Update release dependency docs.
|
||||
- Update operator migration notes.
|
||||
- Add full module permutation tests including access-present and access-absent
|
||||
behavior.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- Core is a composition kernel.
|
||||
- Access behavior is owned by `govoplan-access`.
|
||||
- Feature modules communicate through kernel contracts, capabilities, events,
|
||||
and their own APIs.
|
||||
|
||||
## Immediate Backlog
|
||||
|
||||
- [x] Document kernel/platform module model.
|
||||
- [x] Add dependency-boundary check.
|
||||
- [x] Add backend permutation startup smoke checks.
|
||||
- [x] Inventory access/auth/RBAC import debt.
|
||||
- [x] Draft access extraction plan.
|
||||
- [x] Define kernel access DTOs and protocols.
|
||||
- [x] Add access capability registry names.
|
||||
- [x] Register live access directory and tenant resolver capability
|
||||
implementations backed by the current compatibility tables.
|
||||
- [x] Register an access tenant-provisioner capability so tenancy can seed
|
||||
access-owned default roles without importing access service internals.
|
||||
- [x] Replace `govoplan-files` direct user/group/tenant model imports with the
|
||||
`access.directory` capability for group membership and share-target checks.
|
||||
- [x] Replace `govoplan-files` direct campaign model imports with the
|
||||
`campaigns.access` capability for campaign share/existence checks.
|
||||
- [x] Replace `govoplan-mail` direct user/group/tenant model imports with
|
||||
mail-owned policy storage plus access-directory validation and legacy-read
|
||||
fallback.
|
||||
- [x] Replace `govoplan-mail` direct campaign model imports with the
|
||||
`campaigns.mailPolicyContext` capability for campaign-scoped mail policy and
|
||||
owner context.
|
||||
- [x] Replace `govoplan-campaign` runtime user/group/tenant model imports with
|
||||
access-directory lookups and string-based ORM relationships.
|
||||
- [x] Create `govoplan-access` repository workspace and issue workflow scaffold.
|
||||
- [x] Create `govoplan-access` repository/package skeleton.
|
||||
- [x] Move `govoplan_core/access` seed package into `govoplan-access`.
|
||||
- [x] Add compatibility wrappers in core for old import paths.
|
||||
- [x] Route existing auth dependency wrappers through access principal/evaluator capabilities.
|
||||
- [x] Move FastAPI auth dependency wrappers out of core and into
|
||||
`govoplan-access`.
|
||||
- [x] Move session, API-key, and password helper services into `govoplan-access`.
|
||||
- [x] Move interactive auth/session routes behind access module manifest.
|
||||
- [x] Move legacy admin/API-key routes behind access module manifest.
|
||||
- [x] Move access-owned legacy admin service helpers into `govoplan-access`.
|
||||
- [x] Move governance-template CRUD helpers and routes into `govoplan-admin`.
|
||||
- [x] Move governance-template materialization of access-owned groups and roles
|
||||
behind the `access.governanceMaterializer` capability.
|
||||
- [x] Replace legacy access-to-files/campaign admin lookups with module
|
||||
tenant-summary and group-delete veto providers.
|
||||
- [x] Split legacy admin route contribution across `govoplan-admin`,
|
||||
`govoplan-tenancy`, `govoplan-policy`, `govoplan-audit`, and
|
||||
`govoplan-access` route slices.
|
||||
- [x] Move legacy admin route-handler ownership out of the access compatibility
|
||||
router into access, admin, tenancy, policy, and audit module routers.
|
||||
- [x] Move shared system-settings, tenant-governance, slug/error, and
|
||||
tenant-count helpers out of access internals into core settings/tenancy
|
||||
helpers used by the split platform route modules.
|
||||
- [x] Move campaign schema routes into the campaign route contribution.
|
||||
- [x] Move development mailbox routes into the mail route contribution.
|
||||
- [x] Replace core Celery direct campaign/mail imports with the
|
||||
`campaigns.deliveryTasks` capability.
|
||||
- [x] Replace core retention direct campaign queries with
|
||||
`campaigns.policyContext` and `campaigns.retention` capabilities.
|
||||
- [x] Replace core `create_all` feature-model imports with module registry
|
||||
metadata discovery.
|
||||
- [x] Remove transitional boundary checker allowlist entries.
|
||||
- [x] Move live legacy model definitions out of core and into
|
||||
`govoplan-access` while preserving existing table names.
|
||||
- [x] Split the transitional access-owned legacy model graph further into
|
||||
tenancy, audit, admin, access, and core settings ownership.
|
||||
- [x] Reverse the tenancy/access dependency direction so `govoplan-access`
|
||||
depends on `govoplan-tenancy`, and the registry inserts tenancy before access.
|
||||
- [x] Replace tenancy-to-access default role seeding with an access
|
||||
tenant-provisioner capability.
|
||||
- [x] Replace tenancy-to-access owner candidate and owner membership handling
|
||||
with the access tenant-provisioner capability.
|
||||
- [x] Replace admin overview counts and audit actor lookup/filtering with the
|
||||
`access.administration` capability.
|
||||
- [x] Replace feature-module direct user/group/tenant model imports.
|
||||
- [x] `govoplan-files`
|
||||
- [x] `govoplan-mail`
|
||||
- [x] `govoplan-campaign`
|
||||
- [x] Replace files/mail/campaign direct cross-module SQL lookups with
|
||||
provider/capability contracts.
|
||||
- [x] Move access admin WebUI pages to module route contributions.
|
||||
- [x] Move generic admin-owned WebUI panels into `govoplan-admin` and register
|
||||
them through the `admin.sections` UI capability.
|
||||
- [x] Remove transitional boundary checker allowlist entries as each contract
|
||||
lands.
|
||||
- [x] Remove old core import shims for access models, access routes, admin
|
||||
service helpers, and access-owned security services.
|
||||
- [ ] Move campaign-scoped mail policy ownership fully behind an API/event
|
||||
workflow if direct synchronous capability calls become too tight for later
|
||||
deployment boundaries.
|
||||
|
||||
## Open Decisions
|
||||
|
||||
- Is `govoplan-access` required for any authenticated deployment, while
|
||||
core-only remains a diagnostics/settings shell?
|
||||
- Tenant models live in `govoplan-tenancy`; `govoplan-access` depends on
|
||||
tenancy for authenticated platform composition.
|
||||
- Historical table names remain stable for painless migrations.
|
||||
- Should secret encryption remain a kernel primitive or become an access-owned
|
||||
capability?
|
||||
- Audit log storage lives in `govoplan-audit`; policy provenance can build on
|
||||
that module boundary.
|
||||
- How long should core keep compatibility route paths for existing clients?
|
||||
|
||||
## Verification
|
||||
|
||||
Use the following checks while extracting access:
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan-core
|
||||
./.venv/bin/python scripts/check_dependency_boundaries.py
|
||||
./.venv/bin/python -m unittest tests.test_module_system
|
||||
```
|
||||
|
||||
For each removed dependency-boundary exception, add or update a focused test
|
||||
that proves the replacement contract starts without the old direct import.
|
||||
185
docs/CATALOG_TRUST_AND_LICENSING.md
Normal file
185
docs/CATALOG_TRUST_AND_LICENSING.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Module Catalog Trust And Licensing
|
||||
|
||||
GovOPlaN module install and uninstall must remain operator-controlled. The
|
||||
running server may plan and validate package changes, but package mutation is
|
||||
performed by the separate installer daemon or an operator shell during
|
||||
maintenance mode.
|
||||
|
||||
## Roles
|
||||
|
||||
`govoplan-web` is the public static distribution surface for official catalog
|
||||
resources:
|
||||
|
||||
- signed module package catalogs, grouped by release channel
|
||||
- public catalog keyrings
|
||||
- public license verification keyrings
|
||||
- examples and operator-facing download paths
|
||||
|
||||
`govoplan-core` is the verifier and orchestrator:
|
||||
|
||||
- fetches a local or remote module catalog
|
||||
- verifies catalog signatures against configured trusted keys
|
||||
- enforces approved release channels
|
||||
- rejects expired or not-yet-valid catalogs
|
||||
- records accepted catalog sequence numbers for replay protection
|
||||
- checks catalog entry license feature requirements before planning installs
|
||||
- writes installer plans and request records
|
||||
|
||||
Feature and platform modules own their package artifacts, manifests, migration
|
||||
metadata, retirement providers, and optional lifecycle behavior.
|
||||
|
||||
## Catalog Source
|
||||
|
||||
Core accepts either a local catalog file or a remote URL:
|
||||
|
||||
```bash
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG=/srv/govoplan/catalogs/stable.json
|
||||
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
|
||||
```
|
||||
|
||||
If both file and URL are set, the URL wins. The cache is used when a remote
|
||||
fetch fails, so an operator can still inspect the last known catalog. A cached
|
||||
catalog must still pass signature, freshness, channel, and replay validation.
|
||||
|
||||
## Catalog Shape
|
||||
|
||||
An official catalog is a JSON object with:
|
||||
|
||||
- `catalog_version`
|
||||
- `channel`
|
||||
- `sequence`
|
||||
- `generated_at`
|
||||
- `not_before` when delayed activation is needed
|
||||
- `expires_at`
|
||||
- `modules`
|
||||
- `signatures`
|
||||
|
||||
Each module entry can declare:
|
||||
|
||||
- backend package name and pinned install reference
|
||||
- WebUI package name and pinned install reference
|
||||
- display metadata and tags
|
||||
- `license_features`, the feature entitlements required to plan that install
|
||||
|
||||
The signature is Ed25519 over canonical JSON with both `signature` and
|
||||
`signatures` removed. Core accepts the legacy single `signature` field and the
|
||||
new `signatures` array.
|
||||
|
||||
## Keyrings And Rotation
|
||||
|
||||
Trusted catalog keys are configured locally:
|
||||
|
||||
```bash
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_FILE=/srv/govoplan/trust/catalog-keyring.json
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS='{"release-key-1":"<base64 public key>"}'
|
||||
```
|
||||
|
||||
For development or tightly controlled deployments, a keyring can be read from a
|
||||
URL and cached:
|
||||
|
||||
```bash
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_URL=https://govoplan.example/catalogs/v1/keyring.json
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_CACHE=/srv/govoplan/runtime/catalog-cache/keyring.json
|
||||
```
|
||||
|
||||
Production installations should pin the trusted keyring locally or ship it
|
||||
through deployment configuration. Fetching trusted keys from the same public
|
||||
origin as the catalog is convenient, but that origin must not become the only
|
||||
trust root.
|
||||
|
||||
Keyring entries support:
|
||||
|
||||
- `key_id`
|
||||
- `public_key` or `public_key_base64`
|
||||
- `status`: `active`, `next`, `retired`, `revoked`, or `disabled`
|
||||
- `not_before`
|
||||
- `not_after`
|
||||
|
||||
Rotation process:
|
||||
|
||||
1. Add the next public key to the local trusted keyring with status `next`.
|
||||
2. Publish catalogs signed by both current and next keys.
|
||||
3. Upgrade installations so the next key is locally trusted.
|
||||
4. Promote the next key to `active`.
|
||||
5. Retire the old key only after every supported installation trusts the new
|
||||
key.
|
||||
6. Mark a compromised key `revoked` and publish a higher sequence catalog
|
||||
signed by an uncompromised key.
|
||||
|
||||
## Replay And Freshness
|
||||
|
||||
Use replay state in production:
|
||||
|
||||
```bash
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_SEQUENCE_STATE=/srv/govoplan/runtime/catalog-sequences.json
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_ENFORCE_SEQUENCE=true
|
||||
```
|
||||
|
||||
Core records the accepted sequence per channel after a catalog entry is planned
|
||||
from the admin interface. With strict sequence enforcement, a previously
|
||||
accepted sequence is rejected; without strict enforcement, only older sequences
|
||||
are rejected.
|
||||
|
||||
Catalogs should always expire. Long-lived catalogs make rollback and key
|
||||
compromise harder to reason about.
|
||||
|
||||
## Release Channels
|
||||
|
||||
Approved channels are deployment policy:
|
||||
|
||||
```bash
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_APPROVED_CHANNELS=stable,lts
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_REQUIRE_SIGNATURE=true
|
||||
```
|
||||
|
||||
The admin UI can display other catalog metadata, but core rejects catalogs from
|
||||
unapproved channels when validation is configured.
|
||||
|
||||
## Licensing
|
||||
|
||||
Catalog entries can require license features:
|
||||
|
||||
```json
|
||||
"license_features": ["module.mail", "support.standard"]
|
||||
```
|
||||
|
||||
Core checks those requirements against an offline license file before allowing
|
||||
the entry into the install plan.
|
||||
|
||||
```bash
|
||||
GOVOPLAN_LICENSE_FILE=/srv/govoplan/license.json
|
||||
GOVOPLAN_LICENSE_ENFORCEMENT=true
|
||||
GOVOPLAN_LICENSE_TRUSTED_KEYS_FILE=/srv/govoplan/trust/license-keyring.json
|
||||
```
|
||||
|
||||
License files are JSON objects with:
|
||||
|
||||
- `license_id`
|
||||
- `subject`
|
||||
- `features`
|
||||
- `valid_from`
|
||||
- `valid_until`
|
||||
- `signature`
|
||||
|
||||
License enforcement can run in observe-only mode by leaving
|
||||
`GOVOPLAN_LICENSE_ENFORCEMENT` unset. In that mode, missing or invalid license
|
||||
data is surfaced as a warning but does not block planning.
|
||||
|
||||
Licensing is intentionally separate from open-source code licensing. The
|
||||
catalog/license mechanism can govern support channels, official release
|
||||
eligibility, hosted update access, professional support, or commercial
|
||||
entitlements without changing the source license of the repositories.
|
||||
|
||||
## Production Gaps
|
||||
|
||||
The current implementation validates signed catalog metadata and offline
|
||||
license entitlements. Production-grade distribution still needs:
|
||||
|
||||
- artifact digest verification for package archives or registry artifacts
|
||||
- SBOM/provenance fields and verification workflow
|
||||
- hardened catalog publishing pipeline in `govoplan-web`
|
||||
- automated key rotation runbook and emergency revocation procedure
|
||||
- license issuance and renewal tooling
|
||||
- tests for remote catalog cache fallback and replay-state upgrades
|
||||
|
||||
@@ -24,6 +24,9 @@ network_access = false
|
||||
[projects."/mnt/DATA/git/govoplan-core"]
|
||||
trust_level = "trusted"
|
||||
|
||||
[projects."/mnt/DATA/git/govoplan-access"]
|
||||
trust_level = "trusted"
|
||||
|
||||
[projects."/mnt/DATA/git/govoplan-mail"]
|
||||
trust_level = "trusted"
|
||||
|
||||
@@ -42,6 +45,8 @@ Each active repository has an `AGENTS.md` file. These files define ownership, mo
|
||||
|
||||
Use `~/.codex/config.toml` for personal defaults, auth/runtime settings, writable roots, and trust decisions. Avoid checking in absolute-path writable roots or model preferences unless they are intentionally team-wide.
|
||||
|
||||
Use Gitea issues as the canonical backlog and state log. See `docs/GITEA_ISSUES.md` for label setup, TODO import, and Codex issue update commands. Durable docs should describe stable behavior; changing work state belongs on the issue.
|
||||
|
||||
## Focused Verification
|
||||
|
||||
Use the consolidated script after changes that touch module discovery, optional integrations, shared mail components, mailbox listing, or cross-module WebUI behavior:
|
||||
@@ -70,4 +75,5 @@ PATH=/mnt/DATA/git/govoplan-core/webui/node_modules/.bin:/home/zemion/.nvm/versi
|
||||
- Avoid broad recursive scans and full builds unless the change warrants them.
|
||||
- Keep generated build/test folders ignored.
|
||||
- Keep optional module behavior behind core registry/capability/module metadata boundaries.
|
||||
- Create or update Gitea issues for TODOs, follow-ups, blockers, and feature requests instead of keeping local backlog files.
|
||||
- Do not start persistent dev servers unless the user asks.
|
||||
|
||||
287
docs/CONFIGURATION_PACKAGES.md
Normal file
287
docs/CONFIGURATION_PACKAGES.md
Normal file
@@ -0,0 +1,287 @@
|
||||
# GovOPlaN Configuration Packages
|
||||
|
||||
Configuration packages are reusable, versioned preconfigurations for a working
|
||||
GovOPlaN capability. They sit above modules: a module installs code,
|
||||
migrations, routes, permissions, and capabilities; a configuration package wires
|
||||
installed modules into a concrete operational setup.
|
||||
|
||||
Example: an application-handling package could configure a public portal form,
|
||||
a case workflow, task creation, a mail template, payment processing, access
|
||||
roles, audit evidence, and the interface bindings between those modules.
|
||||
|
||||
This document is durable architecture context and should be mirrored to the
|
||||
Gitea wiki. Active implementation should be tracked in Gitea issues.
|
||||
|
||||
## Goals
|
||||
|
||||
- Import a preconfiguration from a trusted catalog or uploaded package.
|
||||
- Detect which modules, module versions, and capabilities are required.
|
||||
- Detect which configuration fragments must be created, updated, or bound.
|
||||
- Explain import problems and offer actionable resolution paths.
|
||||
- Ask the operator only for data needed to make the package work.
|
||||
- Export an existing working configuration as a reusable package.
|
||||
- Support signed catalogs so approved configurations can be shared, adapted,
|
||||
re-exported, and provided to other installations.
|
||||
|
||||
## Vocabulary
|
||||
|
||||
| Term | Meaning |
|
||||
| --- | --- |
|
||||
| Module | Installable product code with manifest, migrations, routes, permissions, WebUI, events, and capabilities. |
|
||||
| Configuration | Module-owned settings and resources that make behavior concrete: workflows, forms, templates, roles, policies, connector profiles, routing rules, and defaults. |
|
||||
| Interface | A typed contract between modules: capabilities, commands, events, DTOs, data bindings, and UI extension points. |
|
||||
| Data | Operator- or tenant-provided values needed for a working setup: legal text, sender addresses, account mappings, payment provider credentials, templates, defaults, and optionally reference data. |
|
||||
|
||||
The system should communicate these four layers explicitly:
|
||||
|
||||
```text
|
||||
module = what can exist
|
||||
configuration = what should exist here
|
||||
interface = how configured parts connect
|
||||
data = what the operator must provide for this deployment
|
||||
```
|
||||
|
||||
## Package Model
|
||||
|
||||
A configuration package should be a signed, portable manifest plus module-owned
|
||||
configuration fragments. The package is not a database dump. It should be
|
||||
declarative, idempotent, tenant-aware, and explicit about secrets and external
|
||||
systems.
|
||||
|
||||
Required package metadata:
|
||||
|
||||
- stable package id, name, version, description, publisher, and license
|
||||
- supported GovOPlaN core/module compatibility bounds
|
||||
- required modules with version constraints
|
||||
- optional modules with conditional behavior
|
||||
- required capabilities, commands, event subscriptions, and UI extension points
|
||||
- configuration fragments grouped by owning module
|
||||
- interface bindings between fragments
|
||||
- data requirements to collect from the operator
|
||||
- preflight checks and post-import health checks
|
||||
- migration or transformation rules for older package versions
|
||||
- provenance, export source metadata, and signature metadata
|
||||
|
||||
Configuration fragments are interpreted only by the module that owns them. For
|
||||
example, workflow imports workflow definitions; forms imports form schemas;
|
||||
mail imports mail templates and delivery defaults; payments imports payment
|
||||
profiles; access imports groups, roles, and permission assignments.
|
||||
|
||||
## Module Provider Contract
|
||||
|
||||
Each module that wants to participate should expose a configuration-package
|
||||
provider capability. The core orchestrator should not understand module internals
|
||||
or write module-owned tables directly.
|
||||
|
||||
Baseline provider responsibilities:
|
||||
|
||||
- publish JSON Schema or equivalent typed schemas for importable/exportable
|
||||
fragments
|
||||
- publish data requirements that can be rendered by a generic wizard
|
||||
- validate fragments without applying them
|
||||
- report missing dependencies, missing permissions, conflicts, and unsafe
|
||||
changes
|
||||
- produce a dry-run plan with create, update, bind, skip, and blocked actions
|
||||
- apply fragments idempotently inside module-owned boundaries
|
||||
- export selected module-owned configuration into portable fragments
|
||||
- redact secrets and mark secret placeholders during export
|
||||
- provide post-apply health checks and operator-facing diagnostics
|
||||
|
||||
Provider operations should be versioned. The first stable contract can be small:
|
||||
|
||||
```text
|
||||
describe() -> schemas, supported fragment types, exported scopes
|
||||
preflight(package_fragment, context) -> diagnostics, required_data, plan
|
||||
apply(package_fragment, supplied_data, context) -> result, created_refs
|
||||
export(selection, context) -> fragment, data_placeholders, warnings
|
||||
health(import_result, context) -> diagnostics
|
||||
```
|
||||
|
||||
## Import Flow
|
||||
|
||||
1. Select a package from a trusted catalog or upload a package file.
|
||||
2. Verify signature, channel, publisher trust, package compatibility, and schema.
|
||||
3. Resolve required modules against installed modules and the module package
|
||||
catalog.
|
||||
4. Produce a dependency plan for modules that must be installed or upgraded.
|
||||
5. Ask the operator for required data using a focused wizard.
|
||||
6. Run dry-run preflight across all participating module providers.
|
||||
7. Show problems grouped by severity, owner module, and resolution.
|
||||
8. Apply module/package changes in a dependency-safe order.
|
||||
9. Run post-import health checks and show the final working status.
|
||||
10. Store import provenance, package version, supplied non-secret metadata, and
|
||||
audit events.
|
||||
|
||||
The wizard should display everything necessary and nothing unnecessary. Generic
|
||||
sections should cover package trust, dependency plan, required data, conflicts,
|
||||
review, and result. Module-specific fields should appear only when the selected
|
||||
package and installed modules require them.
|
||||
|
||||
## Problem Model
|
||||
|
||||
Import diagnostics should be structured and actionable, not free-text logs.
|
||||
Every problem should include severity, owner, affected object, explanation, and
|
||||
at least one suggested resolution when the system can infer it.
|
||||
|
||||
Common diagnostic types:
|
||||
|
||||
- missing module or incompatible module version
|
||||
- missing capability or disabled optional integration
|
||||
- missing operator-supplied data
|
||||
- missing permission or insufficient administrator scope
|
||||
- unresolved interface binding between modules
|
||||
- conflicting existing configuration
|
||||
- external service not reachable or credential test failed
|
||||
- policy or tenant-governance violation
|
||||
- unsafe overwrite, downgrade, or destructive change
|
||||
- schema validation failure
|
||||
- post-import health-check failure
|
||||
|
||||
Resolution actions can include install module, upgrade module, enable
|
||||
integration, provide value, choose existing object, rename imported object,
|
||||
skip optional fragment, replace existing configuration, or cancel import.
|
||||
|
||||
## Export Flow
|
||||
|
||||
Export should be possible from a working tenant or system configuration, but it
|
||||
must be intentional about data boundaries.
|
||||
|
||||
1. Select export scope: system, tenant, module set, workflow, form, case type,
|
||||
portal flow, or another domain object.
|
||||
2. Ask participating modules to export owned fragments and data placeholders.
|
||||
3. Classify exported material as configuration, reference data, sample data,
|
||||
secrets, or deployment-local data.
|
||||
4. Redact secrets by default and replace them with data requirements.
|
||||
5. Let the operator choose whether to include reference/sample data.
|
||||
6. Validate the assembled package by running the same preflight path against a
|
||||
clean target context where possible.
|
||||
7. Sign the package or produce an unsigned development package.
|
||||
8. Optionally publish the package metadata to a configuration catalog.
|
||||
|
||||
Exported packages should record provenance: source GovOPlaN version, module
|
||||
versions, exporter identity, timestamp, selected scope, redactions, and
|
||||
validation status.
|
||||
|
||||
## Catalogs And Trust
|
||||
|
||||
Configuration catalogs should follow the existing module package catalog model:
|
||||
a file-backed or remotely fetched JSON catalog with Ed25519 signatures, channel
|
||||
gating, trusted key ids, and operator-controlled trust policy.
|
||||
|
||||
Catalog entries should include:
|
||||
|
||||
- package id, version, name, description, publisher, tags, and channel
|
||||
- package artifact URL or repository ref
|
||||
- signature metadata for the package artifact
|
||||
- required modules and version ranges for quick compatibility display
|
||||
- package category such as workflow, portal, case type, governance, connector,
|
||||
report, or tenant baseline
|
||||
- maturity flags such as official, verified, example, local, deprecated
|
||||
|
||||
The catalog verifies that a package is approved to view and import. The package
|
||||
itself must still be signed and validated before use.
|
||||
|
||||
## Example Package
|
||||
|
||||
Application handling through a public portal:
|
||||
|
||||
Required modules:
|
||||
|
||||
- `portal` for the public user-facing entry point
|
||||
- `forms` for the application form and validation
|
||||
- `cases` for the case record and case type
|
||||
- `workflow` for status transitions and automation
|
||||
- `tasks` for internal task creation
|
||||
- `templates` for mail/template rendering
|
||||
- `mail` for delivery profile and outbound message sending
|
||||
- `payments` for provider configuration and payment capture
|
||||
- `access` for roles, groups, and permissions
|
||||
- `audit` for import, case, mail, and payment evidence
|
||||
|
||||
Required configuration:
|
||||
|
||||
- portal route and access policy
|
||||
- form schema, validation rules, confirmation texts, and attachments
|
||||
- case type, fields, lifecycle states, and retention classification
|
||||
- workflow steps, transitions, assignees, and completion triggers
|
||||
- task template and queue/group assignment
|
||||
- mail template and delivery rule
|
||||
- payment product, amount rules, provider profile, and webhook binding
|
||||
- access roles/groups for clerks, reviewers, supervisors, and operators
|
||||
- audit event categories and evidence retention defaults
|
||||
|
||||
Required operator data:
|
||||
|
||||
- tenant or organizational unit
|
||||
- service name and public contact details
|
||||
- portal URL/path and legal imprint/privacy text
|
||||
- mail sender profile and reply-to mailbox
|
||||
- payment provider credentials or test-mode selection
|
||||
- responsible groups or users for task assignment
|
||||
- escalation contacts and deadlines
|
||||
- template wording and localized text overrides
|
||||
|
||||
Potential problems:
|
||||
|
||||
- `payments` is missing or the provider capability is unavailable
|
||||
- mail transport test fails for the selected sender profile
|
||||
- imported role names conflict with existing tenant roles
|
||||
- workflow references a task queue that does not exist
|
||||
- public portal base URL is not configured
|
||||
- required privacy/legal text is empty
|
||||
- webhook endpoint cannot be validated from the payment provider
|
||||
|
||||
## Ownership
|
||||
|
||||
Core should own:
|
||||
|
||||
- package and catalog signature validation
|
||||
- dependency resolution against installed modules and module catalogs
|
||||
- orchestration of provider preflight/apply/export operations
|
||||
- generic import/export APIs and audit envelope
|
||||
- generic wizard shell and problem display components
|
||||
|
||||
Modules should own:
|
||||
|
||||
- schemas and semantics for their own fragments
|
||||
- module-specific validation, apply, export, and health checks
|
||||
- module-specific UI capabilities only when generic generated controls are not
|
||||
sufficient
|
||||
- redaction and classification of module-owned secrets or sensitive data
|
||||
|
||||
Access should own configuration fragments for users, groups, roles,
|
||||
permissions, API keys, and principal mappings. It should not own workflow, mail,
|
||||
payment, form, or portal semantics.
|
||||
|
||||
Admin should expose the operator-facing catalog, import, export, and history
|
||||
screens through admin route contributions. The UI should keep the conceptual
|
||||
layers visible: modules, configuration, interfaces, and data.
|
||||
|
||||
## Implementation Slices
|
||||
|
||||
1. Define package manifest and diagnostic schemas.
|
||||
2. Add core configuration-package provider capability contracts.
|
||||
3. Implement catalog validation and package signature verification.
|
||||
4. Add dry-run orchestration against mock providers.
|
||||
5. Add admin catalog/import wizard screens using provider data requirements.
|
||||
6. Implement export/import providers for access-owned roles/groups first.
|
||||
7. Add providers in workflow/forms/templates/mail/payments/portal/cases/tasks as
|
||||
those modules mature.
|
||||
8. Add round-trip tests: export a known setup, import into a clean tenant,
|
||||
verify health checks.
|
||||
9. Add documentation and field-level help for package authors and operators.
|
||||
|
||||
## Acceptance Criteria For Tracking Issue
|
||||
|
||||
- A Gitea tracking issue exists in `govoplan-core` for the cross-cutting
|
||||
feature.
|
||||
- The issue links module-specific follow-ups when implementation begins.
|
||||
- The first implementation exposes typed contracts rather than direct
|
||||
cross-module imports.
|
||||
- A signed example catalog and unsigned development fixture exist.
|
||||
- A dry-run import can identify required modules, missing data, and conflicts
|
||||
before applying changes.
|
||||
- An export can produce a package with secrets redacted into data requirements.
|
||||
- Admin UI shows a focused wizard and actionable diagnostic list.
|
||||
- Tests cover package validation, signature failure, dependency resolution,
|
||||
provider preflight, export redaction, and import idempotency.
|
||||
288
docs/GITEA_ISSUES.md
Normal file
288
docs/GITEA_ISSUES.md
Normal file
@@ -0,0 +1,288 @@
|
||||
# Gitea Issues And Wiki Workflow
|
||||
|
||||
Gitea issues are the canonical backlog for GovOPlaN work: bugs, feature requests, tasks, tech debt, TODO migrations, open decisions, and blocked work should live there. Gitea wiki pages are the canonical project reference for durable project context mirrored from repository docs and product-directory notes.
|
||||
|
||||
The same pattern is reusable outside GovOPlaN for any project where Codex works in a local checkout, VSCodium or another editor is used for human inspection, and Gitea is the issue tracker. In that setup, Gitea is the durable coordination layer; Codex and the editor are clients of that state.
|
||||
|
||||
## Initial Setup
|
||||
|
||||
The repository contains Gitea issue templates in `.gitea/ISSUE_TEMPLATE`, a pull request template in `.gitea/PULL_REQUEST_TEMPLATE.md`, and the label taxonomy in `docs/gitea-labels.json`.
|
||||
|
||||
The scripts infer this repository from `origin` (`git@git.add-ideas.de:add-ideas/govoplan-core.git`). Override inference when needed:
|
||||
|
||||
```bash
|
||||
export GITEA_URL=https://git.add-ideas.de
|
||||
export GITEA_OWNER=add-ideas
|
||||
export GITEA_REPO=govoplan-core
|
||||
export GITEA_TOKEN=...
|
||||
```
|
||||
|
||||
The API scripts also read `GITEA_*` values from the target repository's `.env` file. That file is gitignored in this repo, so it is suitable for local tokens:
|
||||
|
||||
```bash
|
||||
GITEA_TOKEN=...
|
||||
# Optional if origin inference is not enough:
|
||||
GITEA_URL=https://git.add-ideas.de
|
||||
GITEA_OWNER=add-ideas
|
||||
GITEA_REPO=govoplan-core
|
||||
```
|
||||
|
||||
For a shared credentials file outside the target repository, pass `--env-file`:
|
||||
|
||||
```bash
|
||||
./scripts/gitea-sync-labels.py --env-file /path/to/private/gitea.env --apply
|
||||
```
|
||||
|
||||
Create a Gitea token with issue read/write access and label-management permission for the repository. On scoped-token instances, this usually means issue read/write and, if label writes are rejected, repository write permission too.
|
||||
|
||||
Preview and apply labels:
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan-core
|
||||
./scripts/gitea-sync-labels.py
|
||||
./scripts/gitea-sync-labels.py --apply
|
||||
```
|
||||
|
||||
After the `.gitea` files are pushed to the default branch, Gitea will show the issue template chooser. Blank issues are disabled by `.gitea/ISSUE_TEMPLATE/config.yaml`.
|
||||
|
||||
## Multiple Repositories And Workspaces
|
||||
|
||||
The helper scripts are path-based and can run from this core checkout against any repository with a Gitea remote:
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan-core
|
||||
./scripts/gitea-sync-labels.py --root /mnt/DATA/git/govoplan-mail --apply
|
||||
./scripts/gitea-todo-import.py --root /mnt/DATA/git/govoplan-mail
|
||||
./scripts/gitea-codex-note.py --root /mnt/DATA/git/govoplan-mail --issue 123 --status progress
|
||||
```
|
||||
|
||||
Each target repository is inferred from its own `origin` remote. Use `GITEA_URL`, `GITEA_OWNER`, or `GITEA_REPO` only when a workspace has unusual remotes or the Gitea web URL cannot be inferred from SSH.
|
||||
|
||||
When one core checkout drives another workspace, prefer `--env-file` for shared credentials instead of putting `GITEA_REPO` in the core `.env`; a repo-specific `GITEA_REPO` can accidentally override target inference.
|
||||
|
||||
For non-GovOPlaN projects, provide a project-specific label file and module/project label:
|
||||
|
||||
```bash
|
||||
./scripts/gitea-sync-labels.py \
|
||||
--root /path/to/project \
|
||||
--labels-file /path/to/project/docs/gitea-labels.json \
|
||||
--apply
|
||||
|
||||
./scripts/gitea-todo-import.py \
|
||||
--root /path/to/project \
|
||||
--module-label project/example \
|
||||
--extra-label area/backend
|
||||
```
|
||||
|
||||
If another project does not use `area/*` labels, disable area inference:
|
||||
|
||||
```bash
|
||||
./scripts/gitea-todo-import.py \
|
||||
--root /path/to/project \
|
||||
--module-label project/example \
|
||||
--no-area-labels
|
||||
```
|
||||
|
||||
Install or refresh the shared issue templates in sibling or external repositories:
|
||||
|
||||
```bash
|
||||
./scripts/gitea-install-workflow.py /mnt/DATA/git/govoplan-mail
|
||||
./scripts/gitea-install-workflow.py /mnt/DATA/git/govoplan-mail --apply
|
||||
```
|
||||
|
||||
The installer rewrites the default template label from `module/core` to the module label inferred from the target repository name. Known mappings cover the packaged GovOPlaN repositories, and any other `govoplan-<name>` checkout maps to `module/<name>`. For another workspace or repository name, pass an explicit label:
|
||||
|
||||
```bash
|
||||
./scripts/gitea-install-workflow.py /path/to/repo --module-label module/example --apply
|
||||
```
|
||||
|
||||
Use `--include-labels-file` if a repository should carry its own copy of `docs/gitea-labels.json`; otherwise keep the shared taxonomy in core and run the sync script from core.
|
||||
|
||||
For a fully portable workflow kit, copy these files into the other project:
|
||||
|
||||
- `scripts/gitea_common.py`
|
||||
- `scripts/gitea-sync-labels.py`
|
||||
- `scripts/gitea-todo-import.py`
|
||||
- `scripts/gitea-codex-note.py`
|
||||
- `scripts/gitea-install-workflow.py`
|
||||
- `.gitea/ISSUE_TEMPLATE/*`
|
||||
- `.gitea/PULL_REQUEST_TEMPLATE.md`
|
||||
- a project-specific `docs/gitea-labels.json`
|
||||
|
||||
Keep credentials out of the repository. Put `GITEA_TOKEN` in the shell environment, a gitignored `.env`, a local direnv file, or the user-level Codex/VSCodium environment setup.
|
||||
|
||||
## Label Taxonomy
|
||||
|
||||
Use one `type/*` label:
|
||||
|
||||
- `type/bug`
|
||||
- `type/feature`
|
||||
- `type/task`
|
||||
- `type/debt`
|
||||
- `type/docs`
|
||||
|
||||
Use one `status/*` label while the issue is open:
|
||||
|
||||
- `status/triage`: needs ownership, priority, or acceptance criteria.
|
||||
- `status/ready`: ready to implement.
|
||||
- `status/in-progress`: actively being worked.
|
||||
- `status/blocked`: blocked on an external dependency, credential, or decision.
|
||||
- `status/needs-info`: blocked on clarification.
|
||||
|
||||
Use one `priority/*` label when prioritization matters: `priority/p0`, `priority/p1`, `priority/p2`, or `priority/p3`.
|
||||
|
||||
Use `module/*` and `area/*` labels to route work. Module labels are not exclusive because cross-module work can exist. Core issues should still preserve ownership boundaries: module-specific implementation belongs in the owning module repository.
|
||||
|
||||
Use `codex/ready` when the issue has enough context for Codex to work from, and `codex/needs-human` when a human decision is required first.
|
||||
|
||||
## Moving TODOs Into Gitea
|
||||
|
||||
Preview inline markers:
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan-core
|
||||
./scripts/gitea-todo-import.py
|
||||
```
|
||||
|
||||
Create missing issues after labels are synced:
|
||||
|
||||
```bash
|
||||
./scripts/gitea-todo-import.py --apply
|
||||
```
|
||||
|
||||
The importer scans `TODO`, `FIXME`, `XXX`, and `HACK` markers, skips markers that already reference an issue, applies `source/todo-scan`, and writes a hidden fingerprint into each generated issue body so reruns do not duplicate already imported items.
|
||||
|
||||
When touching code with an imported marker, either remove the marker as part of the fix or replace it with a short reference:
|
||||
|
||||
```python
|
||||
# TODO(gitea#123): keep only if the local pointer is still useful
|
||||
```
|
||||
|
||||
Do not add new untracked TODO comments. Create the Gitea issue first, then reference it inline only when the local pointer materially helps future readers.
|
||||
|
||||
For a broader project import across all local repositories hosted on `git.add-ideas.de`, use the generic backlog importer:
|
||||
|
||||
```bash
|
||||
./scripts/gitea-import-all-backlogs.py --env-file /home/zemion/.config/gitea/gitea.env
|
||||
./scripts/gitea-import-all-backlogs.py --env-file /home/zemion/.config/gitea/gitea.env --apply
|
||||
```
|
||||
|
||||
It scans repository and product-directory files with backlog-like names, creates the shared generic labels where needed, imports missing open work, and deduplicates reruns by hidden fingerprint and normalized title.
|
||||
|
||||
## Mirroring Project Docs Into Gitea Wikis
|
||||
|
||||
Preview wiki pages for all local repositories hosted on `git.add-ideas.de`, cross-referenced with product directories under `/mnt/DATA/Nextcloud/ADD ideas UG/Products`:
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan-core
|
||||
./scripts/gitea-sync-wiki.py --env-file /home/zemion/.config/gitea/gitea.env
|
||||
```
|
||||
|
||||
Apply the wiki mirror:
|
||||
|
||||
```bash
|
||||
./scripts/gitea-sync-wiki.py --env-file /home/zemion/.config/gitea/gitea.env --apply
|
||||
```
|
||||
|
||||
The default apply path uses the Gitea wiki git repository, not one REST API
|
||||
request per page. It keeps a local checkout cache below
|
||||
`/tmp/codex-gitea-wiki-sync`, commits changed pages once per repository, and
|
||||
pushes that commit. This is much faster and avoids REST wiki-page timeouts.
|
||||
Use `--transport api` only when the wiki git remote is unavailable.
|
||||
|
||||
Limit a sync to one repository or one generated page while working:
|
||||
|
||||
```bash
|
||||
./scripts/gitea-sync-wiki.py --repo govoplan-core --apply
|
||||
./scripts/gitea-sync-wiki.py --repo govoplan-core --page Repo-docs-MODULE-ARCHITECTURE --apply
|
||||
```
|
||||
|
||||
Page-limited syncs do not rewrite `Codex-Project-Index`; run a full repository
|
||||
sync when the set of mirrored pages changes.
|
||||
|
||||
The wiki sync mirrors durable text documents only: root README-style files, docs/codex project docs, and selected product notes such as roadmap, plan, concept, pitch, and whitepaper files. It skips generated folders, dependency/build output, `.gitea` templates, and filenames that look credential-related.
|
||||
|
||||
Each managed wiki page contains a `codex-wiki-sync` marker and a source path. Reruns update only managed pages unless `--overwrite-unmanaged` is passed. Each repository also gets a managed `Codex-Project-Index` page linking the mirrored pages.
|
||||
|
||||
Use the wiki for durable context:
|
||||
|
||||
- project overviews and architecture
|
||||
- workflows, operating notes, and setup references
|
||||
- product concepts, plans, pitches, and whitepapers
|
||||
- historical context that helps interpret issues
|
||||
|
||||
Keep active state in issues:
|
||||
|
||||
- open tasks, TODOs, feature requests, and bugs
|
||||
- priority, blocking status, and acceptance criteria
|
||||
- Codex progress updates and implementation notes
|
||||
|
||||
## Codex State Updates
|
||||
|
||||
Codex should read the relevant issue before making changes when issue access is available. During or after work, Codex should add issue comments with the state that would otherwise drift into local notes:
|
||||
|
||||
- scope understood
|
||||
- files changed
|
||||
- tests or manual checks run
|
||||
- blockers or decisions needed
|
||||
- follow-up issues created
|
||||
|
||||
Preview and post a standardized note:
|
||||
|
||||
```bash
|
||||
./scripts/gitea-codex-note.py \
|
||||
--issue 123 \
|
||||
--status progress \
|
||||
--summary "Implemented capability metadata fallback." \
|
||||
--changed src/govoplan_core/modules/registry.py \
|
||||
--test "./.venv/bin/python -m unittest tests.test_module_system"
|
||||
|
||||
./scripts/gitea-codex-note.py \
|
||||
--issue 123 \
|
||||
--status progress \
|
||||
--summary "Implemented capability metadata fallback." \
|
||||
--changed src/govoplan_core/modules/registry.py \
|
||||
--test "./.venv/bin/python -m unittest tests.test_module_system" \
|
||||
--apply
|
||||
```
|
||||
|
||||
Use `--close --apply` only when the acceptance criteria are satisfied and verification is recorded.
|
||||
|
||||
## Ownership Rules
|
||||
|
||||
Create the issue in the repository that owns the change:
|
||||
|
||||
- `govoplan-core`: platform runner, DB/session primitives, auth, tenancy, RBAC, governance, module discovery, migrations, shared WebUI shell, and generic WebUI components.
|
||||
- `govoplan-access`: access, identity, authentication, sessions, API keys, RBAC, groups, users, and access administration.
|
||||
- `govoplan-mail`: mail-specific backend, frontend, message workflows, and mail integrations.
|
||||
- `govoplan-files`: files-specific backend, frontend, storage, and file workflows.
|
||||
- `govoplan-campaign`: campaign-specific backend, frontend, policy, and template behavior.
|
||||
|
||||
For cross-cutting work, create a tracking issue in `govoplan-core` and link module issues from it. Do not use the core issue as a dumping ground for module-specific implementation details.
|
||||
|
||||
## Cleaning Up Mirrored Sources
|
||||
|
||||
After backlog files have been imported into issues and durable context has been mirrored to wiki, old duplicate sources can be removed from git only when they are tracked files and the Gitea issue/wiki state has been verified. Prefer deleting backlog, TODO, roadmap, and one-off planning files that have become duplicate state.
|
||||
|
||||
Do not delete standard repository entry points such as `README`, `LICENSE`, `SECURITY`, or package metadata just because they are mirrored to the wiki. They remain useful for repository browsing, package registries, and developer onboarding.
|
||||
|
||||
Do not delete untracked files or files outside git history as part of automated cleanup unless there is a separate backup or explicit human confirmation for that specific path.
|
||||
|
||||
## Docs Versus Issues
|
||||
|
||||
Keep durable facts in docs:
|
||||
|
||||
- architecture and extension points
|
||||
- command references
|
||||
- module boundaries
|
||||
- operational conventions
|
||||
|
||||
Keep changing state in Gitea:
|
||||
|
||||
- TODOs and follow-ups
|
||||
- bugs and feature requests
|
||||
- blocked status
|
||||
- acceptance criteria
|
||||
- implementation notes from active work
|
||||
|
||||
If a decision becomes durable architecture, write the durable result into docs and link back to the issue for history.
|
||||
79
docs/GOVOPLAN_MODULE_ROADMAP.md
Normal file
79
docs/GOVOPLAN_MODULE_ROADMAP.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# GovOPlaN Module And Integration Roadmap
|
||||
|
||||
This page maps current module and integration ideas to existing GovOPlaN repositories or to missing-module decisions. Issues are the active backlog. This document is durable routing context and should be mirrored to the Gitea wiki.
|
||||
|
||||
## Current Routing
|
||||
|
||||
| Idea | Owner | Tracking |
|
||||
| --- | --- | --- |
|
||||
| Access as a module | `govoplan-access` | `add-ideas/govoplan-access#7` |
|
||||
| OpenProject API / project management connector | `govoplan-connectors` | `add-ideas/govoplan-connectors#1` |
|
||||
| Native project-management module decision | `govoplan-core` | `add-ideas/govoplan-core#196` |
|
||||
| Datasources for databases, CSV, files, APIs | proposed `govoplan-datasources` | `add-ideas/govoplan-core#197` |
|
||||
| Dataflow for pipelines, BI, publication | proposed `govoplan-dataflow` | `add-ideas/govoplan-core#198` |
|
||||
| Templates for letters, emails, forms, reports | `govoplan-templates` | `add-ideas/govoplan-templates#1` |
|
||||
| Reporting and BI | `govoplan-reporting` | `add-ideas/govoplan-reporting#1` |
|
||||
| File connectors: Nextcloud, Seafile, SMB, NFS | `govoplan-files` | `add-ideas/govoplan-files#15` |
|
||||
| Public-sector software integration catalogue | `govoplan-connectors` | `add-ideas/govoplan-connectors#2` |
|
||||
| Adrema-style address and distribution-list management | `govoplan-addresses` | `add-ideas/govoplan-addresses#1` |
|
||||
| Consume sources and become a governed source | `govoplan-connectors` plus proposed `govoplan-dataflow` | `add-ideas/govoplan-connectors#3`, `add-ideas/govoplan-core#198` |
|
||||
| Terminfindung and meeting scheduling polls | `govoplan-scheduling` | local scaffold; remote creation tracked by `add-ideas/govoplan-core#199` |
|
||||
| Terminplaner and calendar primitives | `govoplan-calendar` | `add-ideas/govoplan-calendar#1` |
|
||||
| Terminbuchung appointment booking | `govoplan-appointments` | `add-ideas/govoplan-appointments#1` |
|
||||
| Collaborative documents | `govoplan-dms` | `add-ideas/govoplan-dms#1` |
|
||||
| Forms | `govoplan-forms` | `add-ideas/govoplan-forms#1` |
|
||||
| RSS consume and emit | `govoplan-connectors` | `add-ideas/govoplan-connectors#4` |
|
||||
| LDAP, Active Directory, OpenDesk identity | `govoplan-idm` | `add-ideas/govoplan-idm#1` |
|
||||
| OpenDesk stack integration map | `govoplan-connectors` | `add-ideas/govoplan-connectors#5` |
|
||||
| Open-Xchange mail/groupware | `govoplan-mail` | `add-ideas/govoplan-mail#5` |
|
||||
| Open-Xchange calendar | `govoplan-calendar` | `add-ideas/govoplan-calendar#2` |
|
||||
|
||||
## Proposed Missing Modules
|
||||
|
||||
`govoplan-datasources` should exist only if source catalog ownership becomes broad enough to justify a module separate from connectors and reporting. Candidate responsibilities:
|
||||
|
||||
- source catalogue for SQL databases, CSV/Excel files, uploaded files, APIs, RSS feeds, and governed file locations
|
||||
- credentials and connection profiles
|
||||
- schema discovery and refresh cadence
|
||||
- provenance, freshness, permission boundaries, and audit events
|
||||
|
||||
`govoplan-dataflow` should exist only if pipelines and publication become first-class product behavior. 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 undecided. The default path should be an OpenProject connector first. 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.
|
||||
|
||||
## Release Tooling Note
|
||||
|
||||
`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 possibly `govoplan-projects`, cannot be included until their repositories are created.
|
||||
@@ -1,19 +1,147 @@
|
||||
# GovOPlaN Module Architecture
|
||||
|
||||
GovOPlaN is structured as a core platform runner plus installable feature modules. Core owns the runtime shell and cross-cutting primitives. Modules own feature behavior and contribute backend routes, database metadata, permissions, WebUI routes, and navigation metadata.
|
||||
GovOPlaN is structured as a platform kernel plus installable modules. The kernel starts and composes the platform. Modules own product behavior and contribute backend routes, database metadata, permissions, WebUI routes, navigation metadata, capabilities, and events.
|
||||
|
||||
## Core Responsibilities
|
||||
The current package name is still `govoplan-core`, but the architecture target is a smaller kernel. Access, tenancy, policy, audit, and admin semantics are platform-module responsibilities and should be extracted in stages.
|
||||
|
||||
Core owns:
|
||||
The concrete access/auth/RBAC extraction path is tracked in
|
||||
[`ACCESS_EXTRACTION_PLAN.md`](ACCESS_EXTRACTION_PLAN.md).
|
||||
|
||||
## Layer Model
|
||||
|
||||
| Layer | Purpose | Examples |
|
||||
| --- | --- | --- |
|
||||
| Kernel | Bootstraps and composes the platform | app factory, module registry, route aggregation, migration orchestration, capability/event contracts, health metadata |
|
||||
| Platform modules | Cross-cutting governance capabilities | access, tenancy, policy, audit, admin, ops |
|
||||
| Service modules | Reusable operational capabilities | files, mail, templates, recipients, notifications |
|
||||
| Business modules | Public-sector workflows | campaigns, cases, forms, approvals, appointments |
|
||||
| Connector modules | External system integration | FIT-Connect, XÖV/XTA, DMS/eAkte, ERP, IDM |
|
||||
|
||||
## Kernel Responsibilities
|
||||
|
||||
The kernel target owns:
|
||||
|
||||
- the server entry point and platform configuration
|
||||
- module discovery, registry validation, route aggregation, and platform metadata APIs
|
||||
- database engine/session primitives and migration orchestration
|
||||
- auth, tenants, RBAC, governance, audit, CSRF/API helpers, and secret helpers
|
||||
- shared WebUI shell components such as `AppShell`, `IconRail`, `DataGrid`, `ExplorerTree`, `MessageDisplayPanel`, dialogs, loading frames, access boundaries, and form primitives
|
||||
- module discovery, manifest validation, registry validation, route aggregation, and platform metadata APIs
|
||||
- database engine/session lifecycle and module migration orchestration
|
||||
- module install-plan validation, signed catalog verification, license entitlement checks, and installer request orchestration
|
||||
- capability registry, command/event contracts, and lifecycle hooks
|
||||
- shared WebUI shell contracts, generic WebUI components, and module route/nav rendering
|
||||
- centralized mapping from serializable icon names to renderable frontend icons
|
||||
- health, OpenAPI aggregation, and runtime diagnostics
|
||||
|
||||
Core must not import module feature pages or module business logic directly. It should interact with modules through manifests, entry points, metadata, and route contributions.
|
||||
The kernel must not own product semantics such as users, tenants, RBAC decisions, governance policies, audit storage, mail behavior, file behavior, or campaign behavior. Those belong to platform, service, or business modules.
|
||||
|
||||
## Current Compatibility Responsibilities
|
||||
|
||||
During the staged split, `govoplan-core` still contains compatibility surfaces
|
||||
for access, auth, tenancy, RBAC, governance, audit, CSRF/API helpers, and
|
||||
secret helpers. The extracted access implementation now lives in
|
||||
`govoplan-access`; live legacy ORM table definitions have been split across
|
||||
their platform owners while retaining historical table names. The old core
|
||||
model, route, admin-service, and access-security import shims have been
|
||||
removed; callers must use module-owned imports or kernel capabilities. The
|
||||
remaining compatibility surfaces are temporary until the matching platform
|
||||
modules are fully self-contained:
|
||||
|
||||
- `govoplan-access`
|
||||
- `govoplan-tenancy`
|
||||
- `govoplan-policy`
|
||||
- `govoplan-audit`
|
||||
- `govoplan-admin`
|
||||
|
||||
New code should avoid deepening these compatibility dependencies. Prefer explicit kernel contracts and module capabilities over direct imports.
|
||||
|
||||
Core must not import module feature pages or module business logic directly. It should interact with modules through manifests, entry points, metadata, capabilities, events, and route contributions.
|
||||
|
||||
## Stable Kernel Contracts
|
||||
|
||||
The following contracts are the baseline API that modules can rely on:
|
||||
|
||||
- `ModuleManifest`
|
||||
- `ModuleCompatibility`
|
||||
- module uninstall guard provider contract
|
||||
- `MigrationSpec`
|
||||
- route factory contract
|
||||
- capability factory contract
|
||||
- access DTO/protocol contracts in `govoplan_core.core.access`
|
||||
- resource ACL provider contract
|
||||
- tenant summary provider contract
|
||||
- tenant delete-veto provider contract
|
||||
- WebUI module contribution contract
|
||||
- navigation metadata contract
|
||||
- command/event envelope contract
|
||||
|
||||
Changes to these contracts must be versioned or accompanied by compatibility shims.
|
||||
|
||||
Known access-related capability names are defined in
|
||||
`govoplan_core.core.access`, including:
|
||||
|
||||
- `access.principalResolver`
|
||||
- `access.directory`
|
||||
- `access.permissionEvaluator`
|
||||
- `access.resourceAccess`
|
||||
- `access.tenantProvisioner`
|
||||
- `access.administration`
|
||||
- `access.governanceMaterializer`
|
||||
- `tenancy.tenantResolver`
|
||||
- `security.secretProvider`
|
||||
- `audit.sink`
|
||||
|
||||
`govoplan-access` currently registers `access.principalResolver`,
|
||||
`access.permissionEvaluator`, `access.directory`, `access.tenantProvisioner`,
|
||||
`access.administration`, and `access.governanceMaterializer`.
|
||||
`govoplan-tenancy` registers `tenancy.tenantResolver`. The minimal
|
||||
authenticated platform set is now `tenancy` plus `access`; the registry
|
||||
inserts `tenancy` before `access` when only feature modules are requested.
|
||||
Feature modules should prefer these capabilities over direct reads of
|
||||
access/tenant ORM models when they need labels, group membership, default
|
||||
access provisioning, counts, audit actor labels, or tenant metadata.
|
||||
|
||||
FastAPI route dependencies for authenticated endpoints are access-owned and
|
||||
published from `govoplan_access.backend.auth.dependencies`. Routers may import
|
||||
that dependency module directly until a more generic request-principal adapter
|
||||
exists; they must not import access ORM models or other access implementation
|
||||
internals.
|
||||
|
||||
Current live table ownership:
|
||||
|
||||
- `govoplan-tenancy`: `tenants`
|
||||
- `govoplan-access`: `accounts`, `users`, `groups`, `roles`,
|
||||
`system_role_assignments`, `user_group_memberships`,
|
||||
`user_role_assignments`, `group_role_assignments`, `api_keys`,
|
||||
`auth_sessions`
|
||||
- `govoplan-admin`: `governance_templates`,
|
||||
`governance_template_assignments`
|
||||
- `govoplan-audit`: `audit_log`
|
||||
- `govoplan-core`: `system_settings`
|
||||
|
||||
Current admin route ownership follows the same boundary: access contributes
|
||||
users, groups, roles, system accounts/roles, auth, sessions, and API-key
|
||||
administration; tenancy contributes tenant registry/settings routes; admin
|
||||
contributes system settings, overview, and governance-template routes; audit
|
||||
contributes audit-log routes. Governance template metadata and assignment
|
||||
routes live in `govoplan-admin`; materializing those templates into
|
||||
access-owned groups and roles is performed by the
|
||||
`access.governanceMaterializer` capability.
|
||||
|
||||
Current admin WebUI ownership mirrors that route split. `govoplan-access`
|
||||
contributes the `/admin` route shell and admin nav item. Other platform modules
|
||||
contribute individual admin sections through the `admin.sections` UI capability.
|
||||
`govoplan-admin` contributes the overview, system settings, and governance
|
||||
template sections through that capability. Access-owned tenant/user/group/role
|
||||
sections remain in the access package until their owning platform modules take
|
||||
them over.
|
||||
|
||||
Cross-module feature contracts live under focused kernel contract modules. For
|
||||
example, `govoplan_core.core.campaigns` defines
|
||||
`campaigns.access`, `campaigns.mailPolicyContext`,
|
||||
`campaigns.policyContext`, `campaigns.deliveryTasks`, and
|
||||
`campaigns.retention`. The campaign module registers these capabilities so mail
|
||||
can resolve campaign owner/policy context and delivery tasks, files can validate
|
||||
campaign file-share access, and core retention can call campaign-owned cleanup
|
||||
logic without importing campaign ORM models. Keep these contracts small
|
||||
DTO/protocol surfaces and register concrete behavior from the owning module.
|
||||
|
||||
## Module Responsibilities
|
||||
|
||||
@@ -26,6 +154,7 @@ A module owns:
|
||||
- module migrations and migration metadata
|
||||
- module permissions and role templates
|
||||
- module-specific schemas, policies, and domain rules
|
||||
- module package metadata and retirement providers used by install/uninstall preflight
|
||||
- WebUI pages, feature-specific components, API clients, route contributions, and navigation metadata
|
||||
|
||||
A module should not own generic platform UI. If a component is useful outside one module, move it to `@govoplan/core-webui` and parameterize it there before reusing it.
|
||||
@@ -44,12 +173,15 @@ files = "govoplan_files.backend.manifest:get_manifest"
|
||||
The manifest should declare:
|
||||
|
||||
- `id`, `name`, `version`
|
||||
- `compatibility` when the module needs a minimum/maximum core version or a
|
||||
newer manifest contract
|
||||
- required `dependencies` and `optional_dependencies`
|
||||
- permissions and role templates
|
||||
- router factory
|
||||
- migration metadata and script location
|
||||
- frontend package metadata
|
||||
- navigation metadata using serializable icon names
|
||||
- uninstall guard providers for data, migration, worker, or scheduler vetoes
|
||||
|
||||
Backend nav metadata must use icon-name strings, not frontend components:
|
||||
|
||||
@@ -74,6 +206,37 @@ Rules:
|
||||
- Keep module-owned tables and migrations in the module repository.
|
||||
- Keep cross-module foreign-key assumptions explicit and conservative.
|
||||
- Register module metadata in `MigrationSpec` so core can discover it.
|
||||
- Optional module migrations may create multiple Alembic heads. Verification
|
||||
should compare the database heads to the configured script heads instead of
|
||||
assuming one linear revision when multiple modules are enabled.
|
||||
|
||||
## Install, Uninstall, And Catalogs
|
||||
|
||||
Core owns the install plan, signed catalog validation, license entitlement
|
||||
check, maintenance-mode guard, replay state, and installer request queue.
|
||||
Package mutation is performed by `govoplan-module-installer` outside the
|
||||
FastAPI request process.
|
||||
|
||||
Official catalogs can be served as static JSON from `govoplan-web`, but core
|
||||
does not trust the website by location alone. A catalog must pass the configured
|
||||
signature, channel, freshness, and replay rules before a catalog entry can be
|
||||
planned. Catalog entries may declare `license_features`; core checks those
|
||||
against the configured offline license before adding the entry to the install
|
||||
plan.
|
||||
|
||||
Modules should provide:
|
||||
|
||||
- pinned backend and WebUI package refs for official catalog entries
|
||||
- compatibility metadata in the module manifest
|
||||
- lifecycle hooks when a runtime enable/disable action needs module-specific
|
||||
work
|
||||
- uninstall guards for persistent data, active workers, schedulers, or external
|
||||
bindings
|
||||
- retirement providers when destructive uninstall can safely drop or retire
|
||||
module-owned data
|
||||
|
||||
Uninstall remains non-destructive unless the operator explicitly requests
|
||||
`destroy_data` and the module provides a retirement provider that supports it.
|
||||
|
||||
## WebUI Contract
|
||||
|
||||
@@ -103,6 +266,30 @@ WebUI modules receive only the core route context:
|
||||
|
||||
A module should call its own API client and module-owned backend routes. Shared API helpers should live in core only when they are truly platform-level concerns.
|
||||
|
||||
Modules can also contribute named UI capabilities for explicit extension
|
||||
points. Capability values must be narrow, typed contracts, not imports from a
|
||||
sibling feature package. For admin pages, modules contribute:
|
||||
|
||||
```ts
|
||||
const adminSections: AdminSectionsUiCapability = {
|
||||
sections: [
|
||||
{
|
||||
id: "system-settings",
|
||||
label: "General",
|
||||
group: "SYSTEM",
|
||||
order: 10,
|
||||
allOf: ["system:settings:read"],
|
||||
render: ({ settings, auth }) => createElement(SystemSettingsPanel, { settings, auth })
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
The access admin route shell collects all installed `admin.sections`
|
||||
capabilities with `usePlatformUiCapabilities("admin.sections")`, filters them
|
||||
by `anyOf`/`allOf`, and renders them without importing the contributing module's
|
||||
components directly.
|
||||
|
||||
## Icon Rules
|
||||
|
||||
Icons are resolved centrally by core.
|
||||
@@ -112,6 +299,7 @@ Modules must provide icon names with `iconName` in frontend nav contributions an
|
||||
Current core icon names include:
|
||||
|
||||
- `activity`
|
||||
- `admin`
|
||||
- `campaign`
|
||||
- `dashboard`
|
||||
- `file`
|
||||
@@ -124,6 +312,9 @@ Current core icon names include:
|
||||
|
||||
If a module needs a new navigation icon, add the name-to-component mapping in core first, then use the name in backend and frontend metadata.
|
||||
|
||||
The access module uses the `admin` icon for its `/admin` route. Core only
|
||||
resolves that icon name; it does not hard-code the admin route in the rail.
|
||||
|
||||
## Shared Component Rules
|
||||
|
||||
Use this rule of thumb:
|
||||
@@ -137,6 +328,9 @@ Examples:
|
||||
|
||||
- `ExplorerTree` is core because files, mailboxes, and future modules can all render hierarchical navigation.
|
||||
- `MessageDisplayPanel` is core because mail, campaign sending, and later audit/review surfaces can display message-like content.
|
||||
- `AdminPageLayout`, `AdminIconButton`, and `AdminSelectionList` are core
|
||||
because access, admin, tenancy, policy, and audit panels share the same admin
|
||||
shell language.
|
||||
- `MailProfileManagement` remains in the mail module because it is specific to mail transport policies and profiles.
|
||||
|
||||
## Cross-Module Integration
|
||||
@@ -147,16 +341,298 @@ Rules:
|
||||
|
||||
- Use core module metadata to check whether another module is installed.
|
||||
- Use backend APIs/events/service contracts for runtime cooperation.
|
||||
- If a sibling module needs owner-specific data, expose a narrow DTO/protocol
|
||||
capability from the owning module instead of importing its ORM models.
|
||||
- Keep UI integration declarative where possible: nav items, route contributions, context actions, and explicit extension points.
|
||||
- Avoid direct imports from one feature module into another feature module unless the imported package is a published API contract designed for that purpose. UI components should be promoted to core instead.
|
||||
|
||||
### Dependency Boundary Enforcement
|
||||
|
||||
The repository includes `scripts/check_dependency_boundaries.py`. It enforces the current baseline:
|
||||
|
||||
- kernel/core source may not add new direct imports of files/mail/campaign internals
|
||||
- access source may not import files/mail/campaign internals
|
||||
- feature modules may not import access implementation internals
|
||||
- feature modules may not add new direct imports of sibling feature modules
|
||||
- FastAPI routers may import the published
|
||||
`govoplan_access.backend.auth.dependencies` dependency API
|
||||
- the transitional allowlist is expected to stay empty
|
||||
|
||||
Any future exception is extraction debt and must be temporary, documented in the
|
||||
script with a reason, and removed when a capability/API/event contract replaces
|
||||
it.
|
||||
|
||||
## Module Lifecycle
|
||||
|
||||
Core exposes the installed module catalog through the admin API and WebUI. The
|
||||
current lifecycle model separates four states:
|
||||
|
||||
- installed: the Python/WebUI package is available to the process
|
||||
- active: the module is present in the running platform registry
|
||||
- desired: the module should be active on the next server startup
|
||||
- planned package change: an operator-reviewed package install/uninstall item
|
||||
saved in system settings but not executed by the running server
|
||||
|
||||
The admin module manager can change the desired enabled set and apply it to the
|
||||
running server. It always keeps `tenancy`, `access`, and `admin` enabled when
|
||||
saving through the admin UI, and it adds required module dependencies before
|
||||
saving the desired state. On startup, core always keeps the minimum
|
||||
authenticated platform set `tenancy`/`access` enabled and keeps `admin` enabled
|
||||
when the operator configuration includes it. Unknown saved module ids are
|
||||
ignored when the matching package is no longer installed. The core app factory,
|
||||
devserver, development bootstrap, background worker registry, and migration
|
||||
metadata plan all read the saved desired state from `system_settings` before
|
||||
building their module registry.
|
||||
|
||||
Hot enable/disable is a core design principle for every module:
|
||||
|
||||
- Core keeps one mutable active `PlatformRegistry` object and swaps its manifest
|
||||
set through the module lifecycle manager. Modules must read module presence,
|
||||
optional integrations, permissions, role templates, capabilities, navigation,
|
||||
and frontend contributions from that registry instead of caching sibling
|
||||
module availability.
|
||||
- Core validates install state and dependency closure before activation.
|
||||
- Core applies configured module migrations before activation. Deactivation
|
||||
never drops tables or data.
|
||||
- Core mounts module routers once and guards them by active module state. A
|
||||
deactivated module's routes remain mounted internally but return a disabled
|
||||
module response until the module is active again.
|
||||
- Module route factories must be side-effect-light and idempotent. They may
|
||||
configure module runtime references, but they must not start workers,
|
||||
schedulers, or irreversible external subscriptions. Use lifecycle hooks for
|
||||
those resources.
|
||||
- Modules that own persistent data, background jobs, schedulers, external
|
||||
subscriptions, or irreversible migration state must expose uninstall guard
|
||||
providers through their manifest. Guards return `blocker`, `warning`, or
|
||||
`info` results and may inspect live state through the core-owned DB session.
|
||||
Default package uninstall is non-destructive, so ordinary persistent data
|
||||
should warn that data will remain dormant. Guards should block only when
|
||||
removing the package would corrupt other active modules, workers, external
|
||||
subscriptions, or deployment state. A guard failure is treated as a blocker.
|
||||
- Modules that can destroy their own data must also expose a migration
|
||||
retirement provider. Destructive retirement is opt-in per uninstall plan row
|
||||
through `destroy_data: true`; the installer then snapshots the database,
|
||||
invokes the module-owned retirement executor while the package is still
|
||||
installed, and only then removes Python/WebUI packages. Without that flag,
|
||||
the same provider is used for preflight reporting only and module tables/data
|
||||
remain dormant.
|
||||
- Core refreshes the active registry before frontend metadata is returned from
|
||||
`/api/v1/platform/modules`; the WebUI shell refetches this metadata after
|
||||
module changes so navigation, routes, and UI capabilities update without a
|
||||
page reload.
|
||||
- Modules can provide `on_activate` and `on_deactivate` hooks for worker,
|
||||
scheduler, cache, or external subscription lifecycle. These hooks must be
|
||||
idempotent and must not mutate another module directly.
|
||||
- Package install/uninstall is performed by the trusted operator installer, not
|
||||
directly inside FastAPI request handlers. The admin UI can save install plans,
|
||||
show preflight blockers, and activate/deactivate installed packages.
|
||||
|
||||
The package install-plan API records operator intent only:
|
||||
|
||||
- `GET /api/v1/admin/system/modules/install-plan` reads the saved plan,
|
||||
renders shell commands, and returns installer preflight status.
|
||||
- `GET /api/v1/admin/system/modules/install-runs` returns recent installer run
|
||||
summaries and the current installer lock status.
|
||||
- `GET /api/v1/admin/system/modules/install-runs/{run_id}` returns the raw run
|
||||
record for diagnosis.
|
||||
- `GET /api/v1/admin/system/modules/install-requests` returns daemon handoff
|
||||
requests queued from the admin UI or CLI plus the current daemon heartbeat.
|
||||
- `POST /api/v1/admin/system/modules/install-requests` queues a supervised
|
||||
installer request. It requires maintenance mode and maintenance access. The
|
||||
FastAPI request writes only the request record; it does not run package
|
||||
commands.
|
||||
- `POST /api/v1/admin/system/modules/install-requests/{request_id}/cancel`
|
||||
cancels a queued request. Running requests are not interrupted by the API;
|
||||
they remain owned by the installer daemon.
|
||||
- `POST /api/v1/admin/system/modules/install-requests/{request_id}/retry`
|
||||
queues a new request using the options from a failed or cancelled request.
|
||||
- `GET /api/v1/admin/system/modules/package-catalog` reads approved package
|
||||
references from `GOVOPLAN_MODULE_PACKAGE_CATALOG` so operators can add known
|
||||
module refs to the install plan without typing them manually. The endpoint
|
||||
also reports catalog validity, channel, signature, trust state, and the
|
||||
configured path.
|
||||
- `POST /api/v1/admin/system/modules/install-plan/catalog/{module_id}` saves
|
||||
a planned install row from a validated catalog entry. Catalog signature and
|
||||
approved-channel policy are enforced before the row is saved.
|
||||
- `POST /api/v1/admin/system/modules/{module_id}/uninstall-plan` saves a
|
||||
planned non-destructive uninstall row for an installed module after it has
|
||||
been disabled. The Python distribution name is resolved from the installed
|
||||
`govoplan.modules` entry point; the WebUI package name comes from the module
|
||||
manifest. Operators can then edit the saved plan row and set `destroy_data`
|
||||
when they explicitly want module-owned tables/data retired before package
|
||||
removal.
|
||||
- `PUT /api/v1/admin/system/modules/install-plan` saves planned install or
|
||||
uninstall rows. Install rows must use tagged package or git references, not
|
||||
local `file:`/workspace paths. Python install rows must also include the
|
||||
distribution package name so rollback can uninstall newly added packages.
|
||||
- `DELETE /api/v1/admin/system/modules/install-plan` clears the plan.
|
||||
- `govoplan-module-install-plan --format shell` or
|
||||
`python -m govoplan_core.commands.module_install_plan --format shell` renders
|
||||
the same commands from a server shell.
|
||||
- `govoplan-module-installer --format shell` runs the same preflight checks from
|
||||
the server shell.
|
||||
- `govoplan-module-installer --apply --build-webui` executes the saved plan
|
||||
after preflight passes, snapshots `pip freeze` and WebUI package files, writes
|
||||
a run record under the runtime installer directory, and marks planned rows as
|
||||
applied after success. Successful installs are added to saved startup state by
|
||||
default; successful uninstalls are removed from saved startup state by default.
|
||||
Use `--no-activate-installed-modules` or
|
||||
`--keep-uninstalled-modules-in-desired` only for staged rollout workflows.
|
||||
- `govoplan-module-installer --supervise --migrate --health-url http://127.0.0.1:8000/health --restart-command '<restart govoplan server>'`
|
||||
is the preferred disruptive-change path. It applies the plan, optionally runs
|
||||
migrations in a fresh Python process after a fresh-process manifest
|
||||
verification, runs the restart command if provided, polls health, and
|
||||
automatically rolls packages back from the run snapshot if commands,
|
||||
migrations, restart, or health recovery fail.
|
||||
- `govoplan-module-installer --daemon` runs the request executor. It polls the
|
||||
runtime request queue, claims one request at a time, and executes the same
|
||||
supervised installer flow. `--daemon-once` processes at most one queued
|
||||
request and exits, which is useful for tests or process-manager one-shot
|
||||
units. The daemon writes `daemon.status.json` under the installer runtime
|
||||
directory so the admin UI and CLI can report heartbeat/status.
|
||||
- `govoplan-module-installer --enqueue-supervised` creates the same request
|
||||
record from a shell instead of from the admin UI.
|
||||
- `govoplan-module-installer --daemon-status --format json` reports the daemon
|
||||
heartbeat. `--cancel-request <request-id>` and `--retry-request <request-id>`
|
||||
provide shell equivalents for the admin UI request controls.
|
||||
- `govoplan-module-installer --validate-package-catalog [path] --format json`
|
||||
validates a catalog file or the catalog configured through
|
||||
`GOVOPLAN_MODULE_PACKAGE_CATALOG`.
|
||||
- `--sign-package-catalog <path> --catalog-signing-key-id <key-id> --catalog-signing-private-key <pem>`
|
||||
signs a catalog with Ed25519. `--require-signed-catalog`,
|
||||
`--approved-catalog-channel <channel>`, and
|
||||
`--catalog-trusted-key <key-id>=<base64-public-key>` enforce the approved
|
||||
release-channel path from an operator shell.
|
||||
- `govoplan-module-installer --rollback <run-id>` restores the saved package
|
||||
snapshots, restores the captured SQLite or external database snapshot when
|
||||
present, restores the previous desired module state when the database was not
|
||||
restored wholesale, and reruns package installation from the previous freeze
|
||||
file.
|
||||
- `--database-backup-command '<command>'` and
|
||||
`--database-restore-command '<command>'` provide non-SQLite backup/restore
|
||||
hooks for migrated installer runs. The backup hook runs before migrations;
|
||||
the restore hook runs during rollback and can be overridden on the rollback
|
||||
command line.
|
||||
- `govoplan-module-installer --list-runs --format json`,
|
||||
`--show-run <run-id> --format json`, and `--lock-status --format json`
|
||||
expose the same run-history and lock information from the operator shell.
|
||||
- `--list-requests --format json` and `--show-request <request-id> --format json`
|
||||
expose daemon handoff records from the operator shell.
|
||||
|
||||
The supervisor accepts multiple restart commands and health URLs. This is the
|
||||
process boundary for web, worker, scheduler, and auxiliary service restarts:
|
||||
each restart command is executed, each health URL is polled, and rollback uses
|
||||
the same restart/health set after restoring package and database snapshots.
|
||||
|
||||
The installer preflight is intentionally conservative:
|
||||
|
||||
- maintenance mode must be active;
|
||||
- installed module manifests must be compatible with the supported manifest
|
||||
contract and current core version;
|
||||
- uninstalling `tenancy`, `access`, or `admin` is blocked;
|
||||
- uninstalling an active module is blocked;
|
||||
- uninstalling a module still present in desired startup state is blocked;
|
||||
- uninstalling a module with active/desired dependents is blocked;
|
||||
- uninstalling a module that owns migrations is non-destructive by default:
|
||||
schema/data remain dormant and preflight emits a warning;
|
||||
- modules that declare explicit migration retirement support should also
|
||||
register a retirement provider. Without a provider, preflight emits a
|
||||
manual-review warning. Providers may return blockers, warnings, and an
|
||||
explanatory summary, but core does not drop schema or data on behalf of
|
||||
modules.
|
||||
- module-owned uninstall guard providers can veto data/migration/worker unsafe
|
||||
removals;
|
||||
- install refs must be exact versions or tagged git refs;
|
||||
- Python install rows must include the package distribution name for rollback;
|
||||
- WebUI package changes require a WebUI root and trigger rebuild/reload status.
|
||||
|
||||
The installer supervisor must run outside the FastAPI server process. A server
|
||||
request handler cannot reliably restart or roll back the process that is
|
||||
currently executing the request. The admin UI therefore remains an operator
|
||||
planning and request-submission surface; the trusted daemon/CLI is the executor.
|
||||
|
||||
Automatic rollback covers Python and WebUI package state. The manual/shell
|
||||
daemon can run `npm install` and `npm run build`; this remains the supported
|
||||
WebUI package path. Browser-loaded remote module bundles are experimental and
|
||||
reserved for controlled deployments with integrity/signature policy.
|
||||
|
||||
For `sqlite:///` database URLs, `--migrate` also captures a SQLite backup and
|
||||
rollback restores it before the supervisor restarts the server. For non-SQLite
|
||||
database URLs, `--migrate` requires deployment-specific backup and restore
|
||||
commands. A `--database-restore-check-command` can validate the created backup
|
||||
artifact before migrations proceed. Hook commands run in the installer run
|
||||
directory with:
|
||||
|
||||
- `GOVOPLAN_INSTALLER_RUN_DIR`
|
||||
- `GOVOPLAN_DATABASE_URL`
|
||||
- `GOVOPLAN_DATABASE_BACKUP_PATH`
|
||||
- `GOVOPLAN_DATABASE_BACKUP_METADATA`
|
||||
|
||||
Module uninstall does not retire data by default. Package removal leaves
|
||||
module-owned schema/data dormant. Explicit retirement is a later module-owned
|
||||
operation guarded by retirement providers.
|
||||
|
||||
The running FastAPI server still reports `package_mutation_supported=false`
|
||||
because dependency-manager operations are not executed inside request handlers.
|
||||
The trusted mutation boundary is the operator CLI/daemon. This keeps the
|
||||
interpreter, npm dependency graph, frontend bundle, migrations, and worker
|
||||
process set under process-supervisor control.
|
||||
|
||||
Frontend module loading primarily uses the build-time package graph generated by
|
||||
the core WebUI host. Installing or uninstalling a WebUI package therefore still
|
||||
uses `npm install` plus a WebUI rebuild/reload by default. Core also exposes an
|
||||
experimental hot remote-bundle path for modules that are enabled by the backend
|
||||
but absent from the local WebUI package graph:
|
||||
|
||||
- `FrontendModule.asset_manifest` points at a JSON remote asset manifest.
|
||||
- `asset_manifest_integrity` is an SRI-style hash for that manifest.
|
||||
- `asset_manifest_signature` and `asset_manifest_public_key_id` allow the shell
|
||||
to verify the manifest when a trusted browser key is registered on
|
||||
`window.__GOVOPLAN_REMOTE_MODULE_KEYS__`.
|
||||
- The remote asset manifest contract version is `1` and contains `moduleId`,
|
||||
`entry`, `entryIntegrity`, and optional `moduleExport`.
|
||||
- The browser fetches and verifies the manifest, fetches and verifies the entry
|
||||
bundle, imports it dynamically, and then applies backend metadata before
|
||||
adding the module's routes/nav/capabilities.
|
||||
|
||||
Unsigned/unhashed remote bundles are skipped. This keeps remote loading a
|
||||
controlled deployment option rather than a replacement for release package
|
||||
builds.
|
||||
|
||||
## Maintenance Mode
|
||||
|
||||
Maintenance mode is the required operating state for package install/uninstall
|
||||
and other disruptive system maintenance.
|
||||
|
||||
Core stores maintenance mode in `system_settings.settings.maintenance_mode`.
|
||||
The public platform status endpoint exposes only the flag and message so the
|
||||
WebUI can show a clear login-screen notice. Login remains reachable during
|
||||
maintenance so an operator can sign in.
|
||||
|
||||
Authenticated API access is enforced at the access-principal boundary. When
|
||||
maintenance mode is enabled, authenticated requests require the system scope
|
||||
`system:maintenance:access`; otherwise the API returns `503 Service
|
||||
Unavailable` with a maintenance-mode detail payload. The protected
|
||||
`system_owner` role grants this through `system:*`. A dedicated
|
||||
`maintenance_operator` role exists for accounts that should be able to access
|
||||
the system during maintenance without receiving broad write permissions.
|
||||
|
||||
Changing the maintenance-mode flag requires both system settings write access
|
||||
and `system:maintenance:access`, so an administrator cannot accidentally enable
|
||||
a mode they cannot use.
|
||||
|
||||
The first implementation is a platform access gate. It does not replace
|
||||
database backups, process supervision, migration checks, or external load
|
||||
balancer maintenance pages.
|
||||
|
||||
## Build And Verification
|
||||
|
||||
Backend verification from core:
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan-core
|
||||
./.venv/bin/python -m compileall src/govoplan_core ../govoplan-files/src/govoplan_files ../govoplan-mail/src/govoplan_mail ../govoplan-campaign/src/govoplan_campaign
|
||||
./.venv/bin/python -m compileall src/govoplan_core ../govoplan-access/src/govoplan_access ../govoplan-admin/src/govoplan_admin ../govoplan-tenancy/src/govoplan_tenancy ../govoplan-policy/src/govoplan_policy ../govoplan-audit/src/govoplan_audit ../govoplan-files/src/govoplan_files ../govoplan-mail/src/govoplan_mail ../govoplan-campaign/src/govoplan_campaign
|
||||
./.venv/bin/python scripts/check_dependency_boundaries.py
|
||||
```
|
||||
|
||||
Core WebUI host verification:
|
||||
|
||||
129
docs/RELEASE_CATALOG_WORKFLOW.md
Normal file
129
docs/RELEASE_CATALOG_WORKFLOW.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Release Catalog Workflow
|
||||
|
||||
GovOPlaN release catalogs are published by `govoplan-web` as static JSON and
|
||||
verified by `govoplan-core` before installer plans are accepted.
|
||||
|
||||
Private signing keys must stay outside all git repositories. Public keyrings
|
||||
are published with the website.
|
||||
|
||||
## One-Time Key Setup
|
||||
|
||||
Create the first catalog signing key on the release machine:
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan-core
|
||||
KEY_DIR="$HOME/.config/govoplan/release-keys"
|
||||
mkdir -p "$KEY_DIR"
|
||||
./.venv/bin/python scripts/generate-catalog-keypair.py \
|
||||
--key-id release-key-1 \
|
||||
--private-key "$KEY_DIR/release-key-1.pem" \
|
||||
--public-key "$KEY_DIR/release-key-1.pub" \
|
||||
--keyring "$KEY_DIR/catalog-keyring.json"
|
||||
```
|
||||
|
||||
Keep `release-key-1.pem` private. The generated keyring contains only public
|
||||
material.
|
||||
|
||||
## Publish Current Release Catalog
|
||||
|
||||
Generate the signed catalog into `govoplan-web`:
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan-core
|
||||
KEY_DIR="$HOME/.config/govoplan/release-keys"
|
||||
scripts/publish-release-catalog.sh \
|
||||
--version 0.1.4 \
|
||||
--sequence 202607071340 \
|
||||
--catalog-signing-key "release-key-1=$KEY_DIR/release-key-1.pem" \
|
||||
--build-web
|
||||
```
|
||||
|
||||
This writes:
|
||||
|
||||
- `/mnt/DATA/git/govoplan-web/public/catalogs/v1/channels/stable.json`
|
||||
- `/mnt/DATA/git/govoplan-web/public/catalogs/v1/keyring.json`
|
||||
|
||||
The wrapper validates the catalog with core using the generated public keyring.
|
||||
|
||||
## Publish After A New Release
|
||||
|
||||
For normal module/core releases, first tag and push the module/core repos. Then
|
||||
publish the website catalog:
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan-core
|
||||
KEY_DIR="$HOME/.config/govoplan/release-keys"
|
||||
scripts/publish-release-catalog.sh \
|
||||
--version <x.y.z> \
|
||||
--catalog-signing-key "release-key-1=$KEY_DIR/release-key-1.pem" \
|
||||
--build-web \
|
||||
--commit \
|
||||
--tag \
|
||||
--push
|
||||
```
|
||||
|
||||
The website tag is `catalog-v<x.y.z>`. The public URL is:
|
||||
|
||||
```text
|
||||
https://govoplan.add-ideas.de/catalogs/v1/channels/stable.json
|
||||
```
|
||||
|
||||
The public keyring URL is:
|
||||
|
||||
```text
|
||||
https://govoplan.add-ideas.de/catalogs/v1/keyring.json
|
||||
```
|
||||
|
||||
## Integrated Release Script
|
||||
|
||||
`scripts/push-release-tag.sh` can publish the web catalog after module and core
|
||||
tags have been pushed:
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan-core
|
||||
KEY_DIR="$HOME/.config/govoplan/release-keys"
|
||||
scripts/push-release-tag.sh \
|
||||
--bump subversion \
|
||||
--publish-web-catalog \
|
||||
--catalog-signing-key "release-key-1=$KEY_DIR/release-key-1.pem" \
|
||||
--build-web-catalog
|
||||
```
|
||||
|
||||
Use `--catalog-signing-key` more than once during a key rotation window. The
|
||||
catalog will contain multiple signatures and the public keyring will include the
|
||||
corresponding public keys.
|
||||
|
||||
## Consumer Configuration
|
||||
|
||||
On a GovOPlaN installation that should consume the official stable catalog:
|
||||
|
||||
```bash
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_URL=https://govoplan.add-ideas.de/catalogs/v1/channels/stable.json
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_CACHE=/srv/govoplan/runtime/catalog-cache/stable.json
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_REQUIRE_SIGNATURE=true
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_APPROVED_CHANNELS=stable
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_FILE=/srv/govoplan/trust/catalog-keyring.json
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_SEQUENCE_STATE=/srv/govoplan/runtime/catalog-sequences.json
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_ENFORCE_SEQUENCE=true
|
||||
```
|
||||
|
||||
For production, copy the public keyring into deployment configuration and pin it
|
||||
locally. Do not rely on a URL-fetched keyring as the only trust root.
|
||||
|
||||
## Core Updates
|
||||
|
||||
`stable.json` includes a top-level `core_release` section for operator/update
|
||||
tooling. Core is intentionally not listed as a normal module entry, because it
|
||||
must not be added to saved enabled-module state. Core upgrades should remain an
|
||||
operator-supervised package update with restart and health checks.
|
||||
|
||||
## Key Rotation
|
||||
|
||||
1. Generate the next private key outside git.
|
||||
2. Run `publish-release-catalog.sh` with both signing keys.
|
||||
3. Publish the web catalog/keyring.
|
||||
4. Roll the new public keyring into installations.
|
||||
5. Stop signing with the old key after the supported fleet trusts the new key.
|
||||
6. Mark compromised keys as revoked in the public keyring and publish a higher
|
||||
sequence catalog signed by a trusted uncompromised key.
|
||||
|
||||
@@ -28,11 +28,265 @@ cd /mnt/DATA/git/govoplan-core
|
||||
`requirements-release.txt` pins the module repositories to the release tag. Update those refs when cutting a release:
|
||||
|
||||
```text
|
||||
govoplan-files git@git.add-ideas.de:add-ideas/govoplan-files.git v0.1.1
|
||||
govoplan-mail git@git.add-ideas.de:add-ideas/govoplan-mail.git v0.1.1
|
||||
govoplan-campaign git@git.add-ideas.de:add-ideas/govoplan-campaign.git v0.1.1
|
||||
govoplan-access git@git.add-ideas.de:add-ideas/govoplan-access.git v0.1.4
|
||||
govoplan-admin git@git.add-ideas.de:add-ideas/govoplan-admin.git v0.1.4
|
||||
govoplan-tenancy git@git.add-ideas.de:add-ideas/govoplan-tenancy.git v0.1.4
|
||||
govoplan-policy git@git.add-ideas.de:add-ideas/govoplan-policy.git v0.1.4
|
||||
govoplan-audit git@git.add-ideas.de:add-ideas/govoplan-audit.git v0.1.4
|
||||
govoplan-files git@git.add-ideas.de:add-ideas/govoplan-files.git v0.1.4
|
||||
govoplan-mail git@git.add-ideas.de:add-ideas/govoplan-mail.git v0.1.4
|
||||
govoplan-campaign git@git.add-ideas.de:add-ideas/govoplan-campaign.git v0.1.4
|
||||
govoplan-calendar git@git.add-ideas.de:add-ideas/govoplan-calendar.git v0.1.4
|
||||
```
|
||||
|
||||
## Runtime Module Package Changes
|
||||
|
||||
The admin module manager can hot-enable and hot-disable packages that are
|
||||
already installed. It does not install or uninstall Python/npm packages from
|
||||
inside the running server.
|
||||
|
||||
For runtime package changes, create an operator install plan in Admin > System >
|
||||
Modules. The module manager shows the trusted installer preflight status and
|
||||
blocks unsafe uninstalls before the operator touches packages.
|
||||
|
||||
Preflight from the server shell:
|
||||
|
||||
```bash
|
||||
govoplan-module-installer --format shell
|
||||
```
|
||||
|
||||
Apply from a controlled operator shell while maintenance mode is active:
|
||||
|
||||
```bash
|
||||
govoplan-module-installer --apply --build-webui
|
||||
```
|
||||
|
||||
For real install/uninstall work, prefer supervised mode with the deployment's
|
||||
restart command and health endpoint:
|
||||
|
||||
```bash
|
||||
govoplan-module-installer \
|
||||
--supervise \
|
||||
--migrate \
|
||||
--build-webui \
|
||||
--health-url http://127.0.0.1:8000/health \
|
||||
--restart-command '<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" > "$GOVOPLAN_DATABASE_BACKUP_PATH"' \
|
||||
--database-restore-check-command 'pg_restore --list "$GOVOPLAN_DATABASE_BACKUP_PATH" >/dev/null' \
|
||||
--database-restore-command 'pg_restore --clean --if-exists --dbname "$GOVOPLAN_DATABASE_URL" "$GOVOPLAN_DATABASE_BACKUP_PATH"' \
|
||||
--health-url http://127.0.0.1:8000/health \
|
||||
--restart-command '<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" > "$GOVOPLAN_DATABASE_BACKUP_PATH"' \
|
||||
--database-restore-check-command 'pg_restore --list "$GOVOPLAN_DATABASE_BACKUP_PATH" >/dev/null' \
|
||||
--database-restore-command 'pg_restore --clean --if-exists --dbname "$GOVOPLAN_DATABASE_URL" "$GOVOPLAN_DATABASE_BACKUP_PATH"' \
|
||||
--health-url http://127.0.0.1:8000/health \
|
||||
--restart-command '<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_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
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
Trusted keys can also be loaded from
|
||||
`GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_FILE`. A URL-backed keyring is
|
||||
supported for development and tightly controlled deployments through
|
||||
`GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_URL` plus
|
||||
`GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_CACHE`, but production systems
|
||||
should pin trusted keys locally.
|
||||
|
||||
Catalog entries can declare `license_features`. If
|
||||
`GOVOPLAN_LICENSE_ENFORCEMENT=true`, core blocks planning catalog installs
|
||||
whose required features are not present in the configured offline license:
|
||||
|
||||
```bash
|
||||
GOVOPLAN_LICENSE_FILE=/srv/govoplan/license.json
|
||||
GOVOPLAN_LICENSE_TRUSTED_KEYS_FILE=/srv/govoplan/trust/license-keyring.json
|
||||
GOVOPLAN_LICENSE_ENFORCEMENT=true
|
||||
```
|
||||
|
||||
See `docs/CATALOG_TRUST_AND_LICENSING.md` for key rotation, replay protection,
|
||||
and licensing details. See `docs/RELEASE_CATALOG_WORKFLOW.md` for the concrete
|
||||
release-machine workflow that generates signing keys and publishes signed
|
||||
catalogs through `govoplan-web`. Unsigned catalogs remain usable for local
|
||||
development when signature enforcement is off, but the admin UI labels them as
|
||||
unsigned.
|
||||
|
||||
Install rows must use tagged package/git refs or registry packages, not local
|
||||
`file:` or workspace links. The installer daemon can run `npm install` and
|
||||
`npm run build` for WebUI package changes; that is the supported path. Browser
|
||||
remote bundles are still experimental and should be treated as a controlled
|
||||
deployment option, not the normal install/uninstall mechanism.
|
||||
|
||||
Module manifests can declare core compatibility bounds and uninstall guard
|
||||
providers. Preflight blocks incompatible manifest contracts/core versions,
|
||||
active modules, desired startup state, protected modules, and active dependents.
|
||||
Default uninstall is non-destructive: module data and schema remain dormant if
|
||||
the package is removed. Persistent-data guards therefore warn by default instead
|
||||
of requiring export/delete. A module that supports explicit data/schema
|
||||
retirement should register a retirement provider. When `destroy_data` is set on
|
||||
an uninstall plan row, that provider is allowed to destroy module-owned data
|
||||
after the installer has captured a database snapshot; otherwise it is used only
|
||||
for preflight reporting.
|
||||
|
||||
## WebUI
|
||||
|
||||
Local development uses `webui/package.json`, which may point at sibling module checkouts while active development is happening.
|
||||
@@ -46,7 +300,7 @@ cd webui
|
||||
PATH=/home/zemion/.nvm/versions/node/v22.22.3/bin:$PATH /home/zemion/.nvm/versions/node/v22.22.3/bin/npm run build
|
||||
```
|
||||
|
||||
The module repositories include root-level npm package manifests so git installs can resolve `@govoplan/files-webui`, `@govoplan/mail-webui`, and `@govoplan/campaign-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:
|
||||
|
||||
@@ -55,16 +309,45 @@ cd /mnt/DATA/git/govoplan-core
|
||||
scripts/push-release-tag.sh --version 0.1.2
|
||||
```
|
||||
|
||||
The script also includes GovOPlaN roadmap/scaffold module repositories that do not yet have package metadata. Those repositories are committed, tagged, and pushed with the same release tag, but they are tag-only until they contain `pyproject.toml`, module manifests, or WebUI packages. Tag-only repositories are not listed in `requirements-release.txt` or `webui/package.release.json`.
|
||||
|
||||
Current tag-only module repositories:
|
||||
|
||||
- `govoplan-addresses`
|
||||
- `govoplan-appointments`
|
||||
- `govoplan-cases`
|
||||
- `govoplan-connectors`
|
||||
- `govoplan-dms`
|
||||
- `govoplan-erp`
|
||||
- `govoplan-fit-connect`
|
||||
- `govoplan-forms`
|
||||
- `govoplan-identity-trust`
|
||||
- `govoplan-idm`
|
||||
- `govoplan-ledger`
|
||||
- `govoplan-notifications`
|
||||
- `govoplan-ops`
|
||||
- `govoplan-payments`
|
||||
- `govoplan-portal`
|
||||
- `govoplan-reporting`
|
||||
- `govoplan-scheduling`
|
||||
- `govoplan-search`
|
||||
- `govoplan-tasks`
|
||||
- `govoplan-templates`
|
||||
- `govoplan-workflow`
|
||||
- `govoplan-xoev`
|
||||
- `govoplan-xrechnung`
|
||||
- `govoplan-xta-osci`
|
||||
|
||||
### Release lockfile strategy
|
||||
|
||||
The supported release composition currently is the full Multi Seal Mail product: core plus files, mail, and campaign. 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.
|
||||
The supported release composition currently is the full Multi Seal Mail product: core plus access, admin, tenancy, policy, audit, files, mail, campaign, and calendar. Keep one committed full-product release lockfile at `webui/package-lock.release.json`, generated from `webui/package.release.json` in a clean release workspace. Development `package-lock.json` may continue to point at local `file:` dependencies.
|
||||
|
||||
Frontend module permutations are regression-tested through `GOVOPLAN_WEBUI_MODULE_PACKAGES` and temporary build output, not through committed lockfiles for every possible combination. If a smaller composition becomes a separately shipped product, add an explicit release manifest and lockfile pair for that product, for example `package.release.files-mail.json` and `package-lock.release.files-mail.json`, generated in a clean release workspace from tagged git dependencies.
|
||||
|
||||
## Release Checklist
|
||||
|
||||
- Keep Python package versions, WebUI package versions, and git tags aligned.
|
||||
- Tag core, files, mail, and campaign repositories together.
|
||||
- Tag core, access, admin, tenancy, policy, audit, files, mail, campaign, calendar, and scaffold module repositories together.
|
||||
- Update `requirements-release.txt` and `webui/package.release.json` when the release tag changes.
|
||||
- Generate the committed full-product release lockfile from `package.release.json` with `scripts/generate-release-lock.sh`.
|
||||
- Add separate release manifest/lockfile pairs only for module compositions that are shipped as their own products.
|
||||
|
||||
46
docs/configuration-package-catalog.example.json
Normal file
46
docs/configuration-package-catalog.example.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"channel": "stable",
|
||||
"packages": [
|
||||
{
|
||||
"package_id": "govoplan.application-handling.basic",
|
||||
"name": "Basic application handling",
|
||||
"version": "0.1.0",
|
||||
"description": "Portal form, case workflow, task creation, mail notification, payment setup, and access roles for a simple application process.",
|
||||
"publisher": "ADD ideas",
|
||||
"category": "workflow",
|
||||
"artifact_ref": "git+ssh://git@git.add-ideas.de/add-ideas/govoplan-configuration-packages.git@application-handling-basic-v0.1.0",
|
||||
"artifact_sha256": "<sha256>",
|
||||
"required_modules": [
|
||||
{ "module_id": "portal", "version": ">=0.1.0" },
|
||||
{ "module_id": "forms", "version": ">=0.1.0" },
|
||||
{ "module_id": "cases", "version": ">=0.1.0" },
|
||||
{ "module_id": "workflow", "version": ">=0.1.0" },
|
||||
{ "module_id": "tasks", "version": ">=0.1.0" },
|
||||
{ "module_id": "templates", "version": ">=0.1.0" },
|
||||
{ "module_id": "mail", "version": ">=0.1.0" },
|
||||
{ "module_id": "payments", "version": ">=0.1.0" },
|
||||
{ "module_id": "access", "version": ">=0.1.0" },
|
||||
{ "module_id": "audit", "version": ">=0.1.0" }
|
||||
],
|
||||
"required_capabilities": [
|
||||
"configuration.provider",
|
||||
"access.configuration",
|
||||
"forms.configuration",
|
||||
"workflow.configuration",
|
||||
"mail.configuration",
|
||||
"payments.configuration"
|
||||
],
|
||||
"tags": ["official", "example", "public-service"],
|
||||
"signature": {
|
||||
"algorithm": "ed25519",
|
||||
"key_id": "configuration-release-key-1",
|
||||
"value": "<base64 signature over the package artifact metadata>"
|
||||
}
|
||||
}
|
||||
],
|
||||
"signature": {
|
||||
"algorithm": "ed25519",
|
||||
"key_id": "configuration-catalog-key-1",
|
||||
"value": "<base64 signature over this JSON object without the signature field>"
|
||||
}
|
||||
}
|
||||
398
docs/gitea-labels.json
Normal file
398
docs/gitea-labels.json
Normal file
@@ -0,0 +1,398 @@
|
||||
[
|
||||
{
|
||||
"name": "type/bug",
|
||||
"color": "d73a4a",
|
||||
"description": "A reproducible defect, regression, or incorrect behavior.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "type/feature",
|
||||
"color": "0e8a16",
|
||||
"description": "New user-visible behavior or platform capability.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "type/task",
|
||||
"color": "1d76db",
|
||||
"description": "Implementation, maintenance, migration, or operational work.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "type/debt",
|
||||
"color": "fbca04",
|
||||
"description": "Cleanup, refactoring, risk reduction, or deferred engineering work.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "type/docs",
|
||||
"color": "5319e7",
|
||||
"description": "Documentation, process, or developer workflow work.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "priority/p0",
|
||||
"color": "b60205",
|
||||
"description": "Immediate stop-the-line priority.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "priority/p1",
|
||||
"color": "d93f0b",
|
||||
"description": "High priority for the next focused work window.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "priority/p2",
|
||||
"color": "fbca04",
|
||||
"description": "Normal planned priority.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "priority/p3",
|
||||
"color": "c2e0c6",
|
||||
"description": "Low priority or opportunistic cleanup.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "status/triage",
|
||||
"color": "d4c5f9",
|
||||
"description": "Needs review, ownership, priority, or acceptance criteria.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "status/ready",
|
||||
"color": "0e8a16",
|
||||
"description": "Ready for implementation.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "status/in-progress",
|
||||
"color": "0052cc",
|
||||
"description": "Currently being worked.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "status/blocked",
|
||||
"color": "b60205",
|
||||
"description": "Cannot progress without a decision, dependency, credential, or external change.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "status/needs-info",
|
||||
"color": "f9d0c4",
|
||||
"description": "Needs clarifying input before implementation can proceed safely.",
|
||||
"exclusive": true
|
||||
},
|
||||
{
|
||||
"name": "module/access",
|
||||
"color": "0e8a16",
|
||||
"description": "GovOPlaN access, identity, authentication, RBAC, and administration behavior.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/addresses",
|
||||
"color": "5319e7",
|
||||
"description": "GovOPlaN Addresses module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/admin",
|
||||
"color": "006b75",
|
||||
"description": "GovOPlaN Admin module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/appointments",
|
||||
"color": "d876e3",
|
||||
"description": "GovOPlaN Appointments module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/audit",
|
||||
"color": "0e8a16",
|
||||
"description": "GovOPlaN Audit module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/calendar",
|
||||
"color": "bfd4f2",
|
||||
"description": "GovOPlaN Calendar module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/campaign",
|
||||
"color": "d876e3",
|
||||
"description": "GovOPlaN campaign module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/cases",
|
||||
"color": "1d76db",
|
||||
"description": "GovOPlaN Cases module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/connectors",
|
||||
"color": "c2e0c6",
|
||||
"description": "GovOPlaN Connectors module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/core",
|
||||
"color": "0052cc",
|
||||
"description": "GovOPlaN core runner, shared primitives, shell, or extension points.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/dms",
|
||||
"color": "c5def5",
|
||||
"description": "GovOPlaN Dms module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/erp",
|
||||
"color": "fef2c0",
|
||||
"description": "GovOPlaN Erp module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/files",
|
||||
"color": "006b75",
|
||||
"description": "GovOPlaN files module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/fit-connect",
|
||||
"color": "fbca04",
|
||||
"description": "GovOPlaN Fit Connect module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/forms",
|
||||
"color": "f9d0c4",
|
||||
"description": "GovOPlaN Forms module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/identity-trust",
|
||||
"color": "d4c5f9",
|
||||
"description": "GovOPlaN Identity Trust module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/idm",
|
||||
"color": "0052cc",
|
||||
"description": "GovOPlaN Idm module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/ledger",
|
||||
"color": "5319e7",
|
||||
"description": "GovOPlaN Ledger module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/mail",
|
||||
"color": "5319e7",
|
||||
"description": "GovOPlaN mail module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/notifications",
|
||||
"color": "d876e3",
|
||||
"description": "GovOPlaN Notifications module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/ops",
|
||||
"color": "0e8a16",
|
||||
"description": "GovOPlaN Ops module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/payments",
|
||||
"color": "bfd4f2",
|
||||
"description": "GovOPlaN Payments module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/policy",
|
||||
"color": "d93f0b",
|
||||
"description": "GovOPlaN Policy module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/portal",
|
||||
"color": "1d76db",
|
||||
"description": "GovOPlaN Portal module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/reporting",
|
||||
"color": "c2e0c6",
|
||||
"description": "GovOPlaN Reporting module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/search",
|
||||
"color": "bfdadc",
|
||||
"description": "GovOPlaN Search module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/scheduling",
|
||||
"color": "d93f0b",
|
||||
"description": "GovOPlaN Scheduling module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/tasks",
|
||||
"color": "c5def5",
|
||||
"description": "GovOPlaN Tasks module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/templates",
|
||||
"color": "fef2c0",
|
||||
"description": "GovOPlaN Templates module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/tenancy",
|
||||
"color": "e4e669",
|
||||
"description": "GovOPlaN Tenancy module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/web",
|
||||
"color": "bfd4f2",
|
||||
"description": "GovOPlaN public website, product page, or publication content.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/workflow",
|
||||
"color": "f9d0c4",
|
||||
"description": "GovOPlaN Workflow module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/xoev",
|
||||
"color": "d4c5f9",
|
||||
"description": "GovOPlaN Xoev module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/xrechnung",
|
||||
"color": "0052cc",
|
||||
"description": "GovOPlaN Xrechnung module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/xta-osci",
|
||||
"color": "5319e7",
|
||||
"description": "GovOPlaN Xta Osci module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/auth",
|
||||
"color": "bfd4f2",
|
||||
"description": "Authentication, sessions, access bootstrap, or login behavior.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/tenancy",
|
||||
"color": "bfdadc",
|
||||
"description": "Tenant boundaries, provisioning, or tenant-scoped data behavior.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/rbac",
|
||||
"color": "c5def5",
|
||||
"description": "Permissions, roles, delegation, or authorization policy.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/governance",
|
||||
"color": "fef2c0",
|
||||
"description": "Governance policy, audit, privacy, retention, or compliance behavior.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/module-system",
|
||||
"color": "bfe5bf",
|
||||
"description": "Module discovery, manifests, capabilities, routing, or optional integrations.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/migrations",
|
||||
"color": "e4e669",
|
||||
"description": "Alembic migrations, schema bootstrap, or persistence evolution.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/webui",
|
||||
"color": "1d76db",
|
||||
"description": "Shared WebUI shell, frontend components, routing, or frontend tests.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/api",
|
||||
"color": "5319e7",
|
||||
"description": "HTTP API contracts, routers, schemas, or API smoke behavior.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/db",
|
||||
"color": "0e8a16",
|
||||
"description": "Database sessions, models, transactions, or persistence primitives.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/devex",
|
||||
"color": "bfd4f2",
|
||||
"description": "Local developer workflow, scripts, tests, tooling, or release helpers.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/release",
|
||||
"color": "c2e0c6",
|
||||
"description": "Versioning, release locks, tags, packaging, or dependency pins.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/docs",
|
||||
"color": "5319e7",
|
||||
"description": "Durable documentation and project guidance.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "area/marketing",
|
||||
"color": "f9d0c4",
|
||||
"description": "Public website, product messaging, publication copy, or legal page content.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "source/todo-scan",
|
||||
"color": "cccccc",
|
||||
"description": "Imported from inline TODO/FIXME/HACK markers by the Gitea TODO importer.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "source/backlog-import",
|
||||
"color": "bfdadc",
|
||||
"description": "Imported from markdown backlog, roadmap, plan, or TODO files.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "codex/ready",
|
||||
"color": "0e8a16",
|
||||
"description": "Suitable for Codex to pick up with the existing issue context.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "codex/needs-human",
|
||||
"color": "f9d0c4",
|
||||
"description": "Needs an explicit human decision before Codex should implement.",
|
||||
"exclusive": false
|
||||
}
|
||||
]
|
||||
47
docs/module-package-catalog.example.json
Normal file
47
docs/module-package-catalog.example.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"catalog_version": "1",
|
||||
"channel": "stable",
|
||||
"sequence": 14,
|
||||
"generated_at": "2026-07-07T00:00:00Z",
|
||||
"expires_at": "2030-12-31T23:59:59Z",
|
||||
"modules": [
|
||||
{
|
||||
"module_id": "files",
|
||||
"name": "Files",
|
||||
"description": "Managed file spaces and campaign attachment integration.",
|
||||
"version": "0.1.4",
|
||||
"action": "install",
|
||||
"python_package": "govoplan-files",
|
||||
"python_ref": "govoplan-files @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-files.git@v0.1.4",
|
||||
"webui_package": "@govoplan/files-webui",
|
||||
"webui_ref": "git+ssh://git@git.add-ideas.de/add-ideas/govoplan-files.git#v0.1.4",
|
||||
"license_features": ["module.files"],
|
||||
"tags": ["official", "service-module"]
|
||||
},
|
||||
{
|
||||
"module_id": "mail",
|
||||
"name": "Mail",
|
||||
"description": "SMTP/IMAP profile management and read-only mailbox access.",
|
||||
"version": "0.1.4",
|
||||
"action": "install",
|
||||
"python_package": "govoplan-mail",
|
||||
"python_ref": "govoplan-mail @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-mail.git@v0.1.4",
|
||||
"webui_package": "@govoplan/mail-webui",
|
||||
"webui_ref": "git+ssh://git@git.add-ideas.de/add-ideas/govoplan-mail.git#v0.1.4",
|
||||
"license_features": ["module.mail"],
|
||||
"tags": ["official", "service-module"]
|
||||
}
|
||||
],
|
||||
"signatures": [
|
||||
{
|
||||
"algorithm": "ed25519",
|
||||
"key_id": "release-key-1",
|
||||
"value": "<base64 signature over this JSON object without signature or signatures fields>"
|
||||
},
|
||||
{
|
||||
"algorithm": "ed25519",
|
||||
"key_id": "release-key-2",
|
||||
"value": "<optional second signature for key rotation>"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user