Compare commits
254
Commits
bd715a8473
...
v0.1.46
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14b19fbead | ||
|
|
845dcbafdb | ||
|
|
58d320d9b3 | ||
|
|
9554657bb5 | ||
|
|
88b685ff5e | ||
|
|
be57a1823a | ||
|
|
6bcb75f577 | ||
|
|
32fe4b7238 | ||
|
|
6a8f53b87d | ||
|
|
1cec4ee1d8 | ||
|
|
29d03aa2ca | ||
|
|
6fb928d6cf | ||
|
|
6edaaadf37 | ||
|
|
0b171fbdd4 | ||
|
|
ed6790c057 | ||
|
|
3766e26377 | ||
|
|
6c2b36af0f | ||
|
|
3f75ca8e48 | ||
|
|
a886a9b3de | ||
|
|
fe83290d56 | ||
|
|
c50f699399 | ||
|
|
59b45a0829 | ||
|
|
861abcc573 | ||
|
|
5e995fed88 | ||
|
|
41ca242004 | ||
|
|
85caa8d337 | ||
|
|
64640327ae | ||
|
|
cc7c2a91ee | ||
|
|
7c92565d9d | ||
|
|
23bfe5e2f8 | ||
|
|
cf2f7f6890 | ||
|
|
23b601bc0d | ||
|
|
99c52c2153 | ||
|
|
ca68d98806 | ||
|
|
79c4cb067a | ||
|
|
cd498dc1d8 | ||
|
|
e07b3487e3 | ||
|
|
72279de2c0 | ||
|
|
88543ab115 | ||
|
|
81fe0f4680 | ||
|
|
fe784cc562 | ||
|
|
9fe7ad2cb4 | ||
|
|
f1eebd849c | ||
|
|
4eb90079d5 | ||
|
|
628714804b | ||
|
|
ff9fa37a88 | ||
|
|
4c7552f0dd | ||
|
|
a20e02291d | ||
|
|
b6452c6f53 | ||
|
|
75103d49af | ||
|
|
a60b8b0752 | ||
|
|
7f2f896a0f | ||
|
|
60e04a324c | ||
|
|
6a74e53a1c | ||
|
|
6517b6ac27 | ||
|
|
26a66814b4 | ||
|
|
d08f9f0f2d | ||
|
|
47c90400af | ||
|
|
5d4535f7b5 | ||
|
|
83ccb7f198 | ||
|
|
f407419d25 | ||
|
|
e88dceb639 | ||
|
|
ef8fd45457 | ||
|
|
d0ff2f1510 | ||
|
|
f8b06887d2 | ||
|
|
69519a92b4 | ||
|
|
e2f505eeab | ||
|
|
a1b80eda27 | ||
|
|
5bb8028147 | ||
|
|
5efb0eea6f | ||
|
|
5e9234d4b6 | ||
|
|
b76581a89a | ||
|
|
bec62f38d1 | ||
|
|
c66e1b768d | ||
|
|
209a43592f | ||
|
|
50b81c9ca7 | ||
|
|
612a44bc8e | ||
|
|
dce725636d | ||
|
|
78811f7f6e | ||
|
|
09046e6e62 | ||
|
|
f3cfd1bccc | ||
|
|
241db623c7 | ||
|
|
b269791c48 | ||
|
|
69ba1037bf | ||
|
|
8bdf7b5f7e | ||
|
|
d6fdd7ddf5 | ||
|
|
7b0ab31adf | ||
|
|
389df7c3d5 | ||
|
|
3b9ae901dd | ||
|
|
bf2f02891f | ||
|
|
7ec0bbb826 | ||
|
|
3ca068f76a | ||
|
|
61463a24cb | ||
|
|
8262215fcd | ||
|
|
8aba74e01e | ||
|
|
a24c94435e | ||
|
|
774793976c | ||
|
|
492449a4e2 | ||
|
|
8e890b37ed | ||
|
|
077735bc24 | ||
|
|
d9522d3cc4 | ||
|
|
bad0ea37a7 | ||
|
|
9ffd46fe22 | ||
|
|
be51a9c347 | ||
|
|
e36a6573bf | ||
|
|
629bfec1f1 | ||
|
|
9e956eec6f | ||
|
|
9bb2c808a6 | ||
|
|
f7590a7b8b | ||
|
|
1a68565ba0 | ||
|
|
d9f67b5c26 | ||
|
|
1cfdaec250 | ||
|
|
62501d399a | ||
|
|
ce5528e3b8 | ||
|
|
1f039dd39c | ||
|
|
d107d94fec | ||
|
|
6163c5992f | ||
|
|
2f28f22fd1 | ||
|
|
909862afdb | ||
|
|
eb04804d36 | ||
|
|
017aa7a702 | ||
|
|
af27b9fbdf | ||
|
|
cb45251c59 | ||
|
|
3b3d5b3386 | ||
|
|
313249b8fc | ||
|
|
282c90c54b | ||
|
|
25424187a8 | ||
|
|
ff8ee991c3 | ||
|
|
a5a0731d20 | ||
|
|
a0f161041d | ||
|
|
cb85999a14 | ||
|
|
cbfe8b03a7 | ||
|
|
768e9a51c9 | ||
|
|
087561ee12 | ||
|
|
11c1aa1815 | ||
|
|
478ecb5d0e | ||
|
|
32689d027a | ||
|
|
b7cc2d2df4 | ||
|
|
b70869e747 | ||
|
|
0c84afb158 | ||
|
|
145aa58c11 | ||
|
|
a3566c9311 | ||
|
|
3219460064 | ||
|
|
4b2a15adb5 | ||
|
|
acc5ffc247 | ||
|
|
f4f9836a09 | ||
|
|
758fa1bba7 | ||
|
|
0acc8cfc31 | ||
|
|
344bcaf1bc | ||
|
|
794622e4ed | ||
|
|
7653e9851f | ||
|
|
6f896d9c04 | ||
|
|
8f5ac52b58 | ||
|
|
2bc9ad7f00 | ||
|
|
fa1a4bacfb | ||
|
|
0c0669768d | ||
|
|
7b6ceeb185 | ||
|
|
ff12f676a1 | ||
|
|
eb9ab9ef1c | ||
|
|
935c1fe162 | ||
|
|
c7d1cd0e8f | ||
|
|
ac80d7e4e3 | ||
|
|
5e449b0983 | ||
|
|
d4bf07b446 | ||
|
|
adc4db9fdf | ||
|
|
484f2af3ac | ||
|
|
abf9564cee | ||
|
|
5bef966119 | ||
|
|
5e80b39bbd | ||
|
|
9370f501a0 | ||
|
|
e8f7e2c194 | ||
|
|
cbbe08d912 | ||
|
|
2c515f73c2 | ||
|
|
b40f1428fd | ||
|
|
43380eb068 | ||
|
|
29acb55b7c | ||
|
|
4f08b52333 | ||
|
|
d3713bf2ee | ||
|
|
be4410ef1a | ||
|
|
29d07fe375 | ||
|
|
d9003bf63a | ||
|
|
ed31409034 | ||
|
|
50e9607e72 | ||
|
|
f7a30682b3 | ||
|
|
2c56a0fc11 | ||
|
|
be8ba10ae3 | ||
|
|
d78b13f9d3 | ||
|
|
3c658fa32d | ||
|
|
efd734fad2 | ||
|
|
186aa104ce | ||
|
|
ff9d2404ab | ||
|
|
ba82a85547 | ||
|
|
f1fd143ef5 | ||
|
|
b4248a849e | ||
|
|
ff47659899 | ||
|
|
908090dd0f | ||
|
|
3864ce28b1 | ||
|
|
857dbe55f7 | ||
|
|
c9fcdc90c1 | ||
|
|
82e836b720 | ||
|
|
fcb8296812 | ||
|
|
f2e2eb5517 | ||
|
|
1aea3e7c4f | ||
|
|
3f9567af18 | ||
|
|
de16f11ce8 | ||
|
|
9d6cdff4b8 | ||
|
|
aa4050c0ca | ||
|
|
d3cdbd8c7a | ||
|
|
7115c4711d | ||
|
|
a3beca6fc5 | ||
|
|
11d45bce25 | ||
|
|
7b6135b89b | ||
|
|
5b79e7d377 | ||
|
|
6afb8fea76 | ||
|
|
163b35c0af | ||
|
|
603e07cec5 | ||
|
|
97dfd333c6 | ||
|
|
ba88c574b9 | ||
|
|
8d292184d4 | ||
|
|
52bd3527cd | ||
|
|
ff49dabf8f | ||
|
|
cd15aa514e | ||
|
|
a042baa1d3 | ||
|
|
e80de00f29 | ||
|
|
c69acf0dee | ||
|
|
8b93bbc6b6 | ||
|
|
3fc17701df | ||
|
|
9788bdde0c | ||
|
|
a10a01c903 | ||
|
|
e005e54333 | ||
|
|
76e4baa76e | ||
|
|
b6e9a9bd81 | ||
|
|
dc46bda224 | ||
|
|
b00d4e55ee | ||
|
|
76f19dc602 | ||
|
|
5381e37a9e | ||
|
|
7ecf1f17b0 | ||
|
|
349a099e5a | ||
|
|
fdee766993 | ||
|
|
47b9c05bde | ||
|
|
4e42911477 | ||
|
|
9c0650b2ed | ||
|
|
4dc0c8d013 | ||
|
|
35c346a1fa | ||
|
|
4edbc11fe5 | ||
|
|
ddee5c00dc | ||
|
|
4c16069f88 | ||
|
|
22f5c2ff4e | ||
|
|
19c4b63ade | ||
|
|
1dc9148ec3 | ||
|
|
ead697d049 | ||
|
|
65aa8e0b7c | ||
|
|
6c0b003dee | ||
|
|
d0dc916837 |
+16
-3
@@ -2,18 +2,27 @@
|
||||
# Copy to a deployment-local .env or secret store. Do not commit populated secrets.
|
||||
|
||||
APP_ENV=production
|
||||
# Live graph changes are useful in development. Production should apply saved
|
||||
# module state through a coordinated restart of all API and worker processes.
|
||||
GOVOPLAN_MODULE_LIVE_APPLY_ENABLED=
|
||||
GOVOPLAN_INSTALL_PROFILE=self-hosted
|
||||
MASTER_KEY_B64=<generate-with-govoplan-config-env-template-generate-secrets>
|
||||
|
||||
DATABASE_URL=postgresql+psycopg://govoplan:change-me@127.0.0.1:5432/govoplan
|
||||
GOVOPLAN_DATABASE_URL_PGTOOLS=postgresql://govoplan:change-me@127.0.0.1:5432/govoplan
|
||||
GOVOPLAN_DB_POOL_SIZE=5
|
||||
GOVOPLAN_DB_MAX_OVERFLOW=10
|
||||
GOVOPLAN_DB_POOL_TIMEOUT_SECONDS=30
|
||||
GOVOPLAN_DB_POOL_RECYCLE_SECONDS=1800
|
||||
|
||||
ENABLED_MODULES=tenancy,organizations,identity,access,admin,dashboard,policy,audit,files,mail,campaigns,calendar,docs,ops
|
||||
ENABLED_MODULES=tenancy,organizations,identity,idm,access,admin,dashboard,policy,audit,files,templates,mail,campaigns,calendar,poll,scheduling,connectors,datasources,dataflow,dist_lists,workflow_engine,workflow,tasks,views,quick_access,search,risk_compliance,postbox,notifications,services,parties,mandates,decisions,portal,cases,committee,docs,ops
|
||||
|
||||
CELERY_ENABLED=true
|
||||
REDIS_URL=redis://127.0.0.1:6379/0
|
||||
CELERY_QUEUES=send_email,append_sent,notifications,calendar,default
|
||||
CELERY_QUEUES=send_email,append_sent,notifications,calendar,dataflow,events,default
|
||||
CALENDAR_OUTBOX_TERMINAL_RETENTION_DAYS=90
|
||||
SCHEDULING_PUBLIC_SELF_ENROLLMENT_ENABLED=true
|
||||
SCHEDULING_PUBLIC_SELF_ENROLLMENT_MAX_CAPACITY=10000
|
||||
|
||||
GOVOPLAN_CONNECTOR_ALLOW_PRIVATE_NETWORKS=false
|
||||
GOVOPLAN_CONNECTOR_MAX_STRUCTURED_RESPONSE_BYTES=16777216
|
||||
@@ -24,10 +33,14 @@ GOVOPLAN_HTTP_MAX_REQUEST_BODY_BYTES=536870912
|
||||
GOVOPLAN_HTTP_HSTS_SECONDS=31536000
|
||||
|
||||
AUTH_LOGIN_THROTTLE_ENABLED=true
|
||||
AUTH_ACTIVITY_TOUCH_INTERVAL_SECONDS=300
|
||||
AUTH_LOGIN_THROTTLE_IDENTITY_LIMIT=10
|
||||
AUTH_LOGIN_THROTTLE_CLIENT_LIMIT=100
|
||||
AUTH_LOGIN_THROTTLE_WINDOW_SECONDS=900
|
||||
AUTH_LOGIN_THROTTLE_REDIS_RETRY_SECONDS=30
|
||||
# Production startup fails without Redis unless this explicit single-process
|
||||
# risk acknowledgement is enabled.
|
||||
GOVOPLAN_ALLOW_PROCESS_LOCAL_LOGIN_THROTTLE=false
|
||||
|
||||
CORS_ORIGINS=https://govoplan.example.org
|
||||
GOVOPLAN_TRUSTED_HOSTS=govoplan.example.org
|
||||
@@ -46,4 +59,4 @@ DEV_MAILBOX_API_ENABLED=false
|
||||
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_URL=https://govoplan.add-ideas.de/catalogs/v1/channels/stable.json
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_FILE=/etc/govoplan/catalog-keyring.json
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_APPROVED_CHANNEL=stable
|
||||
GOVOPLAN_MODULE_PACKAGE_CATALOG_APPROVED_CHANNELS=stable
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
name: Dependency Audit
|
||||
|
||||
permissions: read-all
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
@@ -21,23 +23,13 @@ jobs:
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
|
||||
with:
|
||||
node-version: "22"
|
||||
- name: Configure SSH for release dependencies
|
||||
env:
|
||||
GOVOPLAN_RELEASE_SSH_KEY_B64: ${{ secrets.GOVOPLAN_RELEASE_SSH_KEY_B64 }}
|
||||
- name: Use HTTPS for GovOPlaN repositories
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
if [ -z "${GOVOPLAN_RELEASE_SSH_KEY_B64:-}" ]; then
|
||||
echo "GOVOPLAN_RELEASE_SSH_KEY_B64 secret is required for git+ssh release dependencies."
|
||||
exit 1
|
||||
fi
|
||||
printf '%s' "$GOVOPLAN_RELEASE_SSH_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
echo 'git.add-ideas.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDe48IOof2fJS1dTbJtLWQnWnr+JorZXKIFdOAM9ct8G' > ~/.ssh/known_hosts
|
||||
chmod 600 ~/.ssh/known_hosts
|
||||
git config --global --add url."https://git.add-ideas.de/GovOPlaN/govoplan".insteadOf "git@git.add-ideas.de:GovOPlaN/govoplan"
|
||||
git config --global --add url."https://git.add-ideas.de/GovOPlaN/govoplan".insteadOf "ssh://git@git.add-ideas.de/GovOPlaN/govoplan"
|
||||
- name: Bootstrap GovOPlaN repositories
|
||||
working-directory: govoplan
|
||||
run: python tools/repo/bootstrap-repositories.py --parent ..
|
||||
run: python tools/repo/bootstrap-repositories.py --parent .. --transport public-https --reuse-checkout-auth --exclude-repo addideas-govoplan-website
|
||||
- name: Install backend dev audit dependencies
|
||||
working-directory: govoplan
|
||||
run: |
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
name: Deployment Installer
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deployment-installer:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
||||
with:
|
||||
path: govoplan
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Compile deployment tooling
|
||||
working-directory: govoplan
|
||||
run: python -m py_compile tools/deployment/govoplan-deploy.py tools/deployment/govoplan_deploy/*.py
|
||||
- name: Test declarative deployment bundle
|
||||
working-directory: govoplan
|
||||
run: python -m unittest -v tests.test_deployment_installer
|
||||
- name: Test WebUI installer retry failures
|
||||
working-directory: govoplan
|
||||
run: python -m unittest -v tests.test_webui_release_dependency_retries
|
||||
- name: Build single-file deployer artifact
|
||||
working-directory: govoplan
|
||||
run: |
|
||||
python tools/deployment/build-deployer-zipapp.py --output /tmp/govoplan-deploy.pyz
|
||||
python /tmp/govoplan-deploy.pyz --help
|
||||
@@ -1,5 +1,7 @@
|
||||
name: Module Matrix
|
||||
|
||||
permissions: read-all
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
@@ -7,6 +9,25 @@ on:
|
||||
jobs:
|
||||
module-matrix:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
env:
|
||||
POSTGRES_DB: govoplan_test
|
||||
POSTGRES_USER: govoplan
|
||||
POSTGRES_PASSWORD: govoplan_test
|
||||
options: >-
|
||||
--health-cmd "pg_isready -U govoplan -d govoplan_test"
|
||||
--health-interval 5s
|
||||
--health-timeout 5s
|
||||
--health-retries 20
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 5s
|
||||
--health-timeout 5s
|
||||
--health-retries 20
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
||||
with:
|
||||
@@ -17,32 +38,48 @@ jobs:
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
|
||||
with:
|
||||
node-version: "22"
|
||||
- name: Configure SSH for release dependencies
|
||||
env:
|
||||
GOVOPLAN_RELEASE_SSH_KEY_B64: ${{ secrets.GOVOPLAN_RELEASE_SSH_KEY_B64 }}
|
||||
- name: Use HTTPS for GovOPlaN repositories
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
if [ -z "${GOVOPLAN_RELEASE_SSH_KEY_B64:-}" ]; then
|
||||
echo "GOVOPLAN_RELEASE_SSH_KEY_B64 secret is required for git+ssh release dependencies."
|
||||
exit 1
|
||||
fi
|
||||
printf '%s' "$GOVOPLAN_RELEASE_SSH_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
echo 'git.add-ideas.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDe48IOof2fJS1dTbJtLWQnWnr+JorZXKIFdOAM9ct8G' > ~/.ssh/known_hosts
|
||||
chmod 600 ~/.ssh/known_hosts
|
||||
git config --global --add url."https://git.add-ideas.de/GovOPlaN/govoplan".insteadOf "git@git.add-ideas.de:GovOPlaN/govoplan"
|
||||
git config --global --add url."https://git.add-ideas.de/GovOPlaN/govoplan".insteadOf "ssh://git@git.add-ideas.de/GovOPlaN/govoplan"
|
||||
- name: Bootstrap GovOPlaN repositories
|
||||
working-directory: govoplan
|
||||
run: python tools/repo/bootstrap-repositories.py --parent ..
|
||||
run: python tools/repo/bootstrap-repositories.py --parent .. --transport public-https --reuse-checkout-auth --exclude-repo addideas-govoplan-website
|
||||
- name: Install backend release dependencies
|
||||
working-directory: govoplan
|
||||
run: |
|
||||
python -m venv .venv
|
||||
.venv/bin/python tools/repo/sync-python-environment.py --requirements requirements-release.txt --python .venv/bin/python --upgrade-pip
|
||||
.venv/bin/python -m pip install '../govoplan-core[dev]'
|
||||
.venv/bin/python -m pip install --no-deps ../govoplan-search
|
||||
- name: Install WebUI release dependencies with test scripts
|
||||
working-directory: govoplan
|
||||
run: bash tools/release/install-webui-release-dependencies.sh ../govoplan-core/webui
|
||||
- name: Validate platform interface and endpoint declarations
|
||||
working-directory: govoplan
|
||||
run: .venv/bin/python tools/inventory/platform-interface-inventory.py --strict-declarations --strict-endpoints
|
||||
- name: Validate Search against PostgreSQL
|
||||
working-directory: govoplan
|
||||
env:
|
||||
GOVOPLAN_SEARCH_POSTGRES_URL: postgresql+psycopg://govoplan:govoplan_test@postgres:5432/govoplan_test
|
||||
run: |
|
||||
GOVOPLAN_CORE_ROOT="$PWD/../govoplan-core" .venv/bin/python tools/checks/postgres-integration-check.py \
|
||||
--database-url "$GOVOPLAN_SEARCH_POSTGRES_URL" \
|
||||
--module-set search=tenancy,access,search \
|
||||
--reset-schema \
|
||||
--skip-retirement-atomicity
|
||||
PYTHONPATH="$PWD/../govoplan-search/src:$PWD/../govoplan-core/src" \
|
||||
.venv/bin/python -m unittest discover \
|
||||
-s ../govoplan-search/tests \
|
||||
-p test_postgres_search.py \
|
||||
-v
|
||||
- name: Prove worker delivery and shutdown guarantees
|
||||
working-directory: govoplan
|
||||
env:
|
||||
GOVOPLAN_WORKER_DRILL_REDIS_URL: redis://redis:6379/15
|
||||
run: |
|
||||
.venv/bin/python tools/checks/worker-runtime-drill.py \
|
||||
--output audit-reports/worker-runtime.json
|
||||
- name: Run module matrix and contract tests
|
||||
working-directory: govoplan
|
||||
run: GOVOPLAN_CORE_ROOT="$PWD/../govoplan-core" PYTHON="$PWD/.venv/bin/python" bash tools/checks/check-module-matrix.sh
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
name: Developer Meta-package Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: Existing protected release version without leading v
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
publish-package:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||
TRIGGER_TAG: ${{ gitea.ref_name }}
|
||||
REQUESTED_VERSION: ${{ inputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Validate protected release tag and package version
|
||||
run: |
|
||||
python - <<'PY'
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tomllib
|
||||
|
||||
requested_version = os.environ.get("REQUESTED_VERSION", "").strip()
|
||||
tag = f"v{requested_version}" if requested_version else os.environ["TRIGGER_TAG"]
|
||||
if not tag.startswith("v") or not tag[1:]:
|
||||
raise SystemExit("release tag is missing")
|
||||
project_text = subprocess.check_output(
|
||||
["git", "show", f"{tag}:packages/govoplan-meta/pyproject.toml"],
|
||||
text=True,
|
||||
)
|
||||
project = tomllib.loads(project_text)["project"]
|
||||
if tag != f"v{project['version']}":
|
||||
raise SystemExit("meta-package version does not match the release tag")
|
||||
tag_commit = subprocess.check_output(
|
||||
["git", "rev-parse", f"refs/tags/{tag}^{{commit}}"], text=True
|
||||
).strip()
|
||||
if subprocess.run(
|
||||
["git", "merge-base", "--is-ancestor", tag_commit, "origin/main"]
|
||||
).returncode:
|
||||
raise SystemExit("release tag is not contained in main")
|
||||
if not requested_version:
|
||||
head_commit = subprocess.check_output(
|
||||
["git", "rev-parse", "HEAD"], text=True
|
||||
).strip()
|
||||
if head_commit != tag_commit:
|
||||
raise SystemExit("tag-triggered checkout does not match the release tag")
|
||||
with Path(os.environ["GITEA_ENV"]).open("a", encoding="utf-8") as env_file:
|
||||
env_file.write(f"RELEASE_TAG={tag}\n")
|
||||
subprocess.run(["git", "checkout", "--detach", tag_commit], check=True)
|
||||
PY
|
||||
- name: Build developer package
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python -m pip install --disable-pip-version-check build==1.5.0 twine==7.0.0
|
||||
python -m build --wheel --outdir dist packages/govoplan-meta
|
||||
python -m twine check dist/*.whl
|
||||
python - <<'PY'
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
wheels = tuple(Path("dist").glob("*.whl"))
|
||||
if len(wheels) != 1:
|
||||
raise SystemExit("meta release must contain exactly one wheel")
|
||||
wheel = wheels[0]
|
||||
evidence = {
|
||||
"schema_version": "1",
|
||||
"repository": os.environ["GITEA_REPOSITORY"],
|
||||
"tag": os.environ["RELEASE_TAG"],
|
||||
"commit": subprocess.check_output(
|
||||
["git", "rev-parse", "HEAD"], text=True
|
||||
).strip(),
|
||||
"artifacts": [
|
||||
{
|
||||
"filename": wheel.name,
|
||||
"sha256": hashlib.sha256(wheel.read_bytes()).hexdigest(),
|
||||
"size": wheel.stat().st_size,
|
||||
}
|
||||
],
|
||||
}
|
||||
Path("dist/package-artifacts.json").write_text(
|
||||
json.dumps(evidence, indent=2, sort_keys=True) + "\n", encoding="utf-8"
|
||||
)
|
||||
PY
|
||||
- name: Retain package hash evidence
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
||||
with:
|
||||
name: developer-meta-package
|
||||
path: dist/package-artifacts.json
|
||||
- name: Check immutable registry state
|
||||
env:
|
||||
PACKAGE_TOKEN: ${{ secrets.GOVOPLAN_PACKAGE_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -n "$PACKAGE_TOKEN"
|
||||
python - <<'PY'
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import tomllib
|
||||
from urllib.error import HTTPError
|
||||
from urllib.parse import quote
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
project = tomllib.loads(
|
||||
Path("packages/govoplan-meta/pyproject.toml").read_text(encoding="utf-8")
|
||||
)["project"]
|
||||
wheels = tuple(Path("dist").glob("*.whl"))
|
||||
if len(wheels) != 1:
|
||||
raise SystemExit("meta release must contain exactly one wheel")
|
||||
wheel = wheels[0]
|
||||
digest = hashlib.sha256(wheel.read_bytes()).hexdigest()
|
||||
package_url = "/".join(
|
||||
(
|
||||
"https://git.add-ideas.de/api/v1/packages/GovOPlaN",
|
||||
"pypi",
|
||||
quote(str(project["name"]), safe=""),
|
||||
quote(str(project["version"]), safe=""),
|
||||
"files",
|
||||
)
|
||||
)
|
||||
request = Request(
|
||||
package_url,
|
||||
headers={
|
||||
"Accept": "application/json",
|
||||
"Authorization": f"token {os.environ['PACKAGE_TOKEN']}",
|
||||
},
|
||||
)
|
||||
publish = True
|
||||
try:
|
||||
with urlopen(request, timeout=30) as response:
|
||||
files = json.load(response)
|
||||
except HTTPError as exc:
|
||||
if exc.code != 404:
|
||||
raise
|
||||
else:
|
||||
if not isinstance(files, list) or len(files) != 1:
|
||||
raise SystemExit("immutable meta-package has an unexpected file set")
|
||||
if files[0].get("sha256") != digest:
|
||||
raise SystemExit(
|
||||
"immutable meta-package already exists with a different SHA-256"
|
||||
)
|
||||
publish = False
|
||||
with Path(os.environ["GITEA_ENV"]).open("a", encoding="utf-8") as env_file:
|
||||
env_file.write(f"PUBLISH_PYPI={int(publish)}\n")
|
||||
PY
|
||||
- name: Publish developer package
|
||||
env:
|
||||
PACKAGE_USERNAME: ${{ secrets.GOVOPLAN_PACKAGE_USERNAME }}
|
||||
PACKAGE_TOKEN: ${{ secrets.GOVOPLAN_PACKAGE_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -n "$PACKAGE_USERNAME"
|
||||
test -n "$PACKAGE_TOKEN"
|
||||
if [[ "$PUBLISH_PYPI" == 1 ]]; then
|
||||
TWINE_USERNAME="$PACKAGE_USERNAME" TWINE_PASSWORD="$PACKAGE_TOKEN" \
|
||||
python -m twine upload --non-interactive \
|
||||
--repository-url https://git.add-ideas.de/api/packages/GovOPlaN/pypi \
|
||||
dist/*.whl
|
||||
else
|
||||
echo "Exact developer meta-package is already present; skipping immutable retry."
|
||||
fi
|
||||
@@ -1,5 +1,7 @@
|
||||
name: Release Integration
|
||||
|
||||
permissions: read-all
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -16,29 +18,25 @@ jobs:
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
|
||||
with:
|
||||
node-version: "22"
|
||||
- name: Configure SSH for release dependencies
|
||||
env:
|
||||
GOVOPLAN_RELEASE_SSH_KEY_B64: ${{ secrets.GOVOPLAN_RELEASE_SSH_KEY_B64 }}
|
||||
- name: Use HTTPS for GovOPlaN repositories
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
if [ -z "${GOVOPLAN_RELEASE_SSH_KEY_B64:-}" ]; then
|
||||
echo "GOVOPLAN_RELEASE_SSH_KEY_B64 secret is required for git+ssh release dependencies."
|
||||
exit 1
|
||||
fi
|
||||
printf '%s' "$GOVOPLAN_RELEASE_SSH_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
echo 'git.add-ideas.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDe48IOof2fJS1dTbJtLWQnWnr+JorZXKIFdOAM9ct8G' > ~/.ssh/known_hosts
|
||||
chmod 600 ~/.ssh/known_hosts
|
||||
git config --global --add url."https://git.add-ideas.de/GovOPlaN/govoplan".insteadOf "git@git.add-ideas.de:GovOPlaN/govoplan"
|
||||
git config --global --add url."https://git.add-ideas.de/GovOPlaN/govoplan".insteadOf "ssh://git@git.add-ideas.de/GovOPlaN/govoplan"
|
||||
- name: Bootstrap GovOPlaN repositories
|
||||
working-directory: govoplan
|
||||
run: python tools/repo/bootstrap-repositories.py --parent ..
|
||||
run: python tools/repo/bootstrap-repositories.py --parent .. --transport public-https --reuse-checkout-auth --exclude-repo addideas-govoplan-website
|
||||
- name: Validate package publication contracts
|
||||
working-directory: govoplan
|
||||
run: |
|
||||
python tools/repo/sync-module-package-workflows.py --check
|
||||
python tools/release/generate-developer-meta-package.py --check
|
||||
python -m unittest tests.test_module_package_workflows tests.test_package_registry_release
|
||||
- name: Install backend release integration dependencies
|
||||
working-directory: govoplan
|
||||
run: |
|
||||
python -m venv .venv
|
||||
.venv/bin/python tools/repo/sync-python-environment.py --requirements requirements-release.txt --python .venv/bin/python --upgrade-pip
|
||||
.venv/bin/python -m pip install '../govoplan-core[dev]'
|
||||
.venv/bin/python -m pip install -r requirements-release-tests.txt '../govoplan-core[dev]'
|
||||
- name: Install WebUI release dependencies
|
||||
working-directory: govoplan
|
||||
run: bash tools/release/install-webui-release-dependencies.sh ../govoplan-core/webui
|
||||
|
||||
@@ -0,0 +1,403 @@
|
||||
name: Runtime Distribution
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: Release version without leading v
|
||||
required: true
|
||||
type: string
|
||||
python_image:
|
||||
description: Digest-pinned multi-architecture Python 3.12 slim image
|
||||
required: true
|
||||
type: string
|
||||
nginx_image:
|
||||
description: Digest-pinned multi-architecture nginx-unprivileged image
|
||||
required: true
|
||||
type: string
|
||||
postgres_image:
|
||||
description: Digest-pinned PostgreSQL image
|
||||
required: true
|
||||
type: string
|
||||
redis_image:
|
||||
description: Digest-pinned Redis image
|
||||
required: true
|
||||
type: string
|
||||
load_balancer_image:
|
||||
description: Digest-pinned HAProxy image
|
||||
required: true
|
||||
type: string
|
||||
managed_ingress_image:
|
||||
description: Digest-pinned Caddy image
|
||||
required: true
|
||||
type: string
|
||||
garage_image:
|
||||
description: Digest-pinned Garage image
|
||||
required: true
|
||||
type: string
|
||||
test_mail_image:
|
||||
description: Digest-pinned GreenMail image
|
||||
required: true
|
||||
type: string
|
||||
binfmt_image:
|
||||
description: Digest-pinned tonistiigi/binfmt image for arm64 CI execution
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
publish-runtime:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
||||
with:
|
||||
path: govoplan
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
|
||||
with:
|
||||
node-version: "22"
|
||||
- name: Validate immutable release inputs
|
||||
env:
|
||||
VERSION: ${{ inputs.version }}
|
||||
PYTHON_IMAGE: ${{ inputs.python_image }}
|
||||
NGINX_IMAGE: ${{ inputs.nginx_image }}
|
||||
POSTGRES_IMAGE: ${{ inputs.postgres_image }}
|
||||
REDIS_IMAGE: ${{ inputs.redis_image }}
|
||||
LOAD_BALANCER_IMAGE: ${{ inputs.load_balancer_image }}
|
||||
MANAGED_INGRESS_IMAGE: ${{ inputs.managed_ingress_image }}
|
||||
GARAGE_IMAGE: ${{ inputs.garage_image }}
|
||||
TEST_MAIL_IMAGE: ${{ inputs.test_mail_image }}
|
||||
BINFMT_IMAGE: ${{ inputs.binfmt_image }}
|
||||
run: |
|
||||
python - <<'PY'
|
||||
import os
|
||||
import re
|
||||
|
||||
version = os.environ["VERSION"]
|
||||
if re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+(?:[-+][A-Za-z0-9.-]+)?", version) is None:
|
||||
raise SystemExit("version must be a SemVer value without a leading v")
|
||||
image_pattern = re.compile(r"^[^@\s]+@sha256:[0-9a-f]{64}$")
|
||||
for name in (
|
||||
"PYTHON_IMAGE",
|
||||
"NGINX_IMAGE",
|
||||
"POSTGRES_IMAGE",
|
||||
"REDIS_IMAGE",
|
||||
"LOAD_BALANCER_IMAGE",
|
||||
"MANAGED_INGRESS_IMAGE",
|
||||
"GARAGE_IMAGE",
|
||||
"TEST_MAIL_IMAGE",
|
||||
"BINFMT_IMAGE",
|
||||
):
|
||||
if image_pattern.fullmatch(os.environ[name]) is None:
|
||||
raise SystemExit(f"{name} must be an exact sha256 image reference")
|
||||
PY
|
||||
- name: Resolve immutable release source
|
||||
working-directory: govoplan
|
||||
env:
|
||||
VERSION: ${{ inputs.version }}
|
||||
run: |
|
||||
git fetch --force --no-tags origin "refs/tags/v$VERSION:refs/tags/v$VERSION"
|
||||
mkdir -p runtime-output
|
||||
git rev-parse "v$VERSION^{commit}" > runtime-output/release-source-commit
|
||||
grep -Eq '^[0-9a-f]{40}$' runtime-output/release-source-commit
|
||||
git show "v$VERSION:requirements-release.txt" > runtime-output/requirements-release.source.txt
|
||||
git show "v$VERSION:packages/govoplan-meta/pyproject.toml" > runtime-output/govoplan-meta.source.toml
|
||||
- name: Use HTTPS for GovOPlaN repositories
|
||||
run: |
|
||||
git config --global --add url."https://git.add-ideas.de/GovOPlaN/govoplan".insteadOf "git@git.add-ideas.de:GovOPlaN/govoplan"
|
||||
git config --global --add url."https://git.add-ideas.de/GovOPlaN/govoplan".insteadOf "ssh://git@git.add-ideas.de/GovOPlaN/govoplan"
|
||||
- name: Bootstrap release sources
|
||||
working-directory: govoplan
|
||||
run: python tools/repo/bootstrap-repositories.py --parent .. --transport public-https --reuse-checkout-auth --exclude-repo addideas-govoplan-website
|
||||
- name: Build release wheel roots and WebUI
|
||||
working-directory: govoplan
|
||||
env:
|
||||
VERSION: ${{ inputs.version }}
|
||||
GOVOPLAN_PACKAGE_USERNAME: ${{ secrets.GOVOPLAN_PACKAGE_USERNAME }}
|
||||
GOVOPLAN_PACKAGE_TOKEN: ${{ secrets.GOVOPLAN_PACKAGE_TOKEN }}
|
||||
run: |
|
||||
python -m venv .runtime-build
|
||||
.runtime-build/bin/python -m pip install --upgrade pip cryptography
|
||||
.runtime-build/bin/python tools/release/generate-release-package-set.py \
|
||||
--version "$VERSION" \
|
||||
--profile full \
|
||||
--requirements runtime-output/requirements-release.source.txt \
|
||||
--meta-package runtime-output/govoplan-meta.source.toml \
|
||||
--output runtime-output/release-packages.json
|
||||
.runtime-build/bin/python tools/release/resolve-package-artifacts.py \
|
||||
--package-set runtime-output/release-packages.json \
|
||||
--wheelhouse runtime-output/local-wheels \
|
||||
--webui-packages runtime-output/webui-packages \
|
||||
--lock-output runtime-output/package-artifacts.lock.json \
|
||||
--requirements-output runtime-output/requirements-release.packages.txt \
|
||||
--python .runtime-build/bin/python
|
||||
PYTHON="$PWD/.runtime-build/bin/python" \
|
||||
GOVOPLAN_WEBUI_PACKAGE_LOCK="$PWD/runtime-output/package-artifacts.lock.json" \
|
||||
GOVOPLAN_WEBUI_PACKAGE_DIR="$PWD/runtime-output/webui-packages" \
|
||||
GOVOPLAN_WEBUI_INSTALL_ALL_PACKAGES=true \
|
||||
bash tools/release/install-webui-release-dependencies.sh ../govoplan-core/webui
|
||||
npm --prefix ../govoplan-core/webui run build
|
||||
.runtime-build/bin/python tools/release/prepare-runtime-context.py \
|
||||
--wheelhouse runtime-output/local-wheels \
|
||||
--web-dist ../govoplan-core/webui/dist \
|
||||
--output runtime-output/common \
|
||||
--required-module tenancy \
|
||||
--required-module organizations \
|
||||
--required-module identity \
|
||||
--required-module idm \
|
||||
--required-module access \
|
||||
--required-module admin \
|
||||
--required-module dashboard \
|
||||
--required-module policy \
|
||||
--required-module audit \
|
||||
--required-module docs \
|
||||
--required-module ops
|
||||
- name: Resolve architecture-specific offline wheelhouses
|
||||
working-directory: govoplan
|
||||
run: |
|
||||
mkdir -p runtime-output/wheels-amd64 runtime-output/wheels-arm64
|
||||
cp runtime-output/local-wheels/*.whl runtime-output/wheels-amd64/
|
||||
cp runtime-output/local-wheels/*.whl runtime-output/wheels-arm64/
|
||||
.runtime-build/bin/python -m pip download --only-binary=:all: \
|
||||
--platform manylinux_2_17_x86_64 --platform manylinux2014_x86_64 \
|
||||
--implementation cp --python-version 3.12 --abi cp312 \
|
||||
--find-links runtime-output/local-wheels \
|
||||
--dest runtime-output/wheels-amd64 \
|
||||
--requirement runtime-output/common/requirements-runtime.txt
|
||||
.runtime-build/bin/python -m pip download --only-binary=:all: \
|
||||
--platform manylinux_2_17_aarch64 --platform manylinux2014_aarch64 \
|
||||
--implementation cp --python-version 3.12 --abi cp312 \
|
||||
--find-links runtime-output/local-wheels \
|
||||
--dest runtime-output/wheels-arm64 \
|
||||
--requirement runtime-output/common/requirements-runtime.txt
|
||||
.runtime-build/bin/python tools/release/prepare-runtime-context.py \
|
||||
--wheelhouse runtime-output/wheels-amd64 \
|
||||
--web-dist ../govoplan-core/webui/dist \
|
||||
--output runtime-output/context-amd64
|
||||
.runtime-build/bin/python tools/release/prepare-runtime-context.py \
|
||||
--wheelhouse runtime-output/wheels-arm64 \
|
||||
--web-dist ../govoplan-core/webui/dist \
|
||||
--output runtime-output/context-arm64
|
||||
cmp runtime-output/context-amd64/composition.json runtime-output/context-arm64/composition.json
|
||||
- name: Build one-file deployer
|
||||
working-directory: govoplan
|
||||
run: python tools/deployment/build-deployer-zipapp.py --output runtime-output/govoplan-deploy.pyz
|
||||
- name: Authenticate OCI publication
|
||||
working-directory: govoplan
|
||||
env:
|
||||
REGISTRY_USERNAME: ${{ secrets.GOVOPLAN_REGISTRY_USERNAME }}
|
||||
REGISTRY_TOKEN: ${{ secrets.GOVOPLAN_REGISTRY_TOKEN }}
|
||||
run: |
|
||||
test -n "$REGISTRY_USERNAME"
|
||||
test -n "$REGISTRY_TOKEN"
|
||||
printf '%s' "$REGISTRY_TOKEN" | docker login git.add-ideas.de --username "$REGISTRY_USERNAME" --password-stdin
|
||||
docker buildx create --name govoplan-runtime --use
|
||||
- name: Build and publish architecture images
|
||||
working-directory: govoplan
|
||||
env:
|
||||
VERSION: ${{ inputs.version }}
|
||||
PYTHON_IMAGE: ${{ inputs.python_image }}
|
||||
NGINX_IMAGE: ${{ inputs.nginx_image }}
|
||||
run: |
|
||||
COMPOSITION_SHA256="$(sha256sum runtime-output/context-amd64/composition.json | cut -d' ' -f1)"
|
||||
for ARCH in amd64 arm64; do
|
||||
docker buildx build --platform "linux/$ARCH" --push \
|
||||
--file tools/release/runtime/Dockerfile.api \
|
||||
--build-arg "PYTHON_IMAGE=$PYTHON_IMAGE" \
|
||||
--build-arg "GOVOPLAN_RELEASE_VERSION=$VERSION" \
|
||||
--build-arg "GOVOPLAN_COMPOSITION_SHA256=$COMPOSITION_SHA256" \
|
||||
--tag "git.add-ideas.de/govoplan/runtime-api:$VERSION-$ARCH" \
|
||||
"runtime-output/context-$ARCH"
|
||||
docker buildx build --platform "linux/$ARCH" --push \
|
||||
--file tools/release/runtime/Dockerfile.web \
|
||||
--build-arg "NGINX_IMAGE=$NGINX_IMAGE" \
|
||||
--build-arg "GOVOPLAN_RELEASE_VERSION=$VERSION" \
|
||||
--build-arg "GOVOPLAN_COMPOSITION_SHA256=$COMPOSITION_SHA256" \
|
||||
--tag "git.add-ideas.de/govoplan/runtime-web:$VERSION-$ARCH" \
|
||||
"runtime-output/context-$ARCH"
|
||||
done
|
||||
docker buildx imagetools create \
|
||||
--tag "git.add-ideas.de/govoplan/runtime-api:$VERSION" \
|
||||
"git.add-ideas.de/govoplan/runtime-api:$VERSION-amd64" \
|
||||
"git.add-ideas.de/govoplan/runtime-api:$VERSION-arm64"
|
||||
docker buildx imagetools create \
|
||||
--tag "git.add-ideas.de/govoplan/runtime-web:$VERSION" \
|
||||
"git.add-ideas.de/govoplan/runtime-web:$VERSION-amd64" \
|
||||
"git.add-ideas.de/govoplan/runtime-web:$VERSION-arm64"
|
||||
docker buildx imagetools inspect "git.add-ideas.de/govoplan/runtime-api:$VERSION" --raw > runtime-output/api-index.json
|
||||
docker buildx imagetools inspect "git.add-ideas.de/govoplan/runtime-web:$VERSION" --raw > runtime-output/web-index.json
|
||||
API_DIGEST="sha256:$(sha256sum runtime-output/api-index.json | cut -d' ' -f1)"
|
||||
WEB_DIGEST="sha256:$(sha256sum runtime-output/web-index.json | cut -d' ' -f1)"
|
||||
python tools/release/resolve-oci-platforms.py --repository git.add-ideas.de/govoplan/runtime-api --index-digest "$API_DIGEST" --index runtime-output/api-index.json --output runtime-output/api-metadata.json
|
||||
python tools/release/resolve-oci-platforms.py --repository git.add-ideas.de/govoplan/runtime-web --index-digest "$WEB_DIGEST" --index runtime-output/web-index.json --output runtime-output/web-metadata.json
|
||||
- name: Resolve managed dependency platform images
|
||||
working-directory: govoplan
|
||||
env:
|
||||
POSTGRES_IMAGE: ${{ inputs.postgres_image }}
|
||||
REDIS_IMAGE: ${{ inputs.redis_image }}
|
||||
run: |
|
||||
docker buildx imagetools inspect "$POSTGRES_IMAGE" --raw > runtime-output/postgres-index.json
|
||||
docker buildx imagetools inspect "$REDIS_IMAGE" --raw > runtime-output/redis-index.json
|
||||
python tools/release/resolve-oci-platforms.py \
|
||||
--repository "${POSTGRES_IMAGE%@*}" \
|
||||
--index-digest "${POSTGRES_IMAGE##*@}" \
|
||||
--index runtime-output/postgres-index.json \
|
||||
--output runtime-output/postgres-metadata.json
|
||||
python tools/release/resolve-oci-platforms.py \
|
||||
--repository "${REDIS_IMAGE%@*}" \
|
||||
--index-digest "${REDIS_IMAGE##*@}" \
|
||||
--index runtime-output/redis-index.json \
|
||||
--output runtime-output/redis-metadata.json
|
||||
- name: Register arm64 execution for runtime smoke
|
||||
working-directory: govoplan
|
||||
env:
|
||||
BINFMT_IMAGE: ${{ inputs.binfmt_image }}
|
||||
run: docker run --privileged --rm "$BINFMT_IMAGE" --install arm64
|
||||
- name: Exercise amd64 and arm64 runtime images
|
||||
working-directory: govoplan
|
||||
run: |
|
||||
for ARCH in amd64 arm64; do
|
||||
.runtime-build/bin/python tools/checks/runtime-image-smoke.py \
|
||||
--api-metadata runtime-output/api-metadata.json \
|
||||
--web-metadata runtime-output/web-metadata.json \
|
||||
--postgres-metadata runtime-output/postgres-metadata.json \
|
||||
--redis-metadata runtime-output/redis-metadata.json \
|
||||
--platform "linux/$ARCH" \
|
||||
--output "runtime-output/evidence/runtime-smoke-$ARCH.json"
|
||||
done
|
||||
- name: Generate and sign distribution evidence
|
||||
working-directory: govoplan
|
||||
env:
|
||||
VERSION: ${{ inputs.version }}
|
||||
SIGNING_KEY: ${{ secrets.RUNTIME_DISTRIBUTION_SIGNING_KEY }}
|
||||
SIGNING_KEY_ID: ${{ secrets.RUNTIME_DISTRIBUTION_SIGNING_KEY_ID }}
|
||||
TRUSTED_KEYRING: ${{ secrets.RUNTIME_DISTRIBUTION_KEYRING }}
|
||||
POSTGRES_IMAGE: ${{ inputs.postgres_image }}
|
||||
REDIS_IMAGE: ${{ inputs.redis_image }}
|
||||
LOAD_BALANCER_IMAGE: ${{ inputs.load_balancer_image }}
|
||||
MANAGED_INGRESS_IMAGE: ${{ inputs.managed_ingress_image }}
|
||||
GARAGE_IMAGE: ${{ inputs.garage_image }}
|
||||
TEST_MAIL_IMAGE: ${{ inputs.test_mail_image }}
|
||||
run: |
|
||||
SOURCE_COMMIT="$(cat runtime-output/release-source-commit)"
|
||||
test -n "$SIGNING_KEY"
|
||||
test -n "$SIGNING_KEY_ID"
|
||||
test -n "$TRUSTED_KEYRING"
|
||||
printf '%s\n' "$SIGNING_KEY" > runtime-output/signing-key.pem
|
||||
printf '%s\n' "$TRUSTED_KEYRING" > runtime-output/distribution-keyring.json
|
||||
chmod 600 runtime-output/signing-key.pem
|
||||
ARTIFACT_BASE="https://git.add-ideas.de/GovOPlaN/govoplan/releases/download/v$VERSION"
|
||||
python tools/release/finalize-runtime-distribution.py \
|
||||
--composition runtime-output/context-amd64/composition.json \
|
||||
--api-metadata runtime-output/api-metadata.json \
|
||||
--web-metadata runtime-output/web-metadata.json \
|
||||
--deployer runtime-output/govoplan-deploy.pyz \
|
||||
--deployer-url "$ARTIFACT_BASE/govoplan-deploy.pyz" \
|
||||
--package-lock runtime-output/package-artifacts.lock.json \
|
||||
--artifact-base-url "$ARTIFACT_BASE" \
|
||||
--source-commit "$SOURCE_COMMIT" \
|
||||
--version "$VERSION" \
|
||||
--sequence "$(date -u +%Y%m%d%H%M)" \
|
||||
--dependency "postgres=$POSTGRES_IMAGE" \
|
||||
--dependency "redis=$REDIS_IMAGE" \
|
||||
--dependency "load_balancer=$LOAD_BALANCER_IMAGE" \
|
||||
--dependency "managed_ingress=$MANAGED_INGRESS_IMAGE" \
|
||||
--dependency "garage=$GARAGE_IMAGE" \
|
||||
--dependency "test_mail=$TEST_MAIL_IMAGE" \
|
||||
--output-directory runtime-output/evidence \
|
||||
--descriptor runtime-output/distribution-descriptor.json
|
||||
.runtime-build/bin/python tools/release/generate-runtime-distribution.py \
|
||||
--descriptor runtime-output/distribution-descriptor.json \
|
||||
--signing-key "$SIGNING_KEY_ID=runtime-output/signing-key.pem" \
|
||||
--output runtime-output/distribution-manifest.json
|
||||
openssl pkeyutl -sign -inkey runtime-output/signing-key.pem -rawin \
|
||||
-in runtime-output/govoplan-deploy.pyz \
|
||||
-out runtime-output/govoplan-deploy.pyz.sig
|
||||
(cd runtime-output && sha256sum govoplan-deploy.pyz > govoplan-deploy.pyz.sha256)
|
||||
(cd runtime-output && sha256sum distribution-manifest.json > distribution-manifest.json.sha256)
|
||||
rm runtime-output/signing-key.pem
|
||||
- name: Verify the published bundle contract with the zipapp
|
||||
working-directory: govoplan
|
||||
env:
|
||||
VERSION: ${{ inputs.version }}
|
||||
SIGNING_KEY_ID: ${{ secrets.RUNTIME_DISTRIBUTION_SIGNING_KEY_ID }}
|
||||
run: |
|
||||
(cd runtime-output && sha256sum --check govoplan-deploy.pyz.sha256)
|
||||
(cd runtime-output && sha256sum --check distribution-manifest.json.sha256)
|
||||
.runtime-build/bin/python - <<'PY'
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
keyring = json.loads(
|
||||
Path("runtime-output/distribution-keyring.json").read_text(encoding="utf-8")
|
||||
)
|
||||
key_id = os.environ["SIGNING_KEY_ID"]
|
||||
matches = [item for item in keyring["keys"] if item.get("key_id") == key_id]
|
||||
if len(matches) != 1 or matches[0].get("status") != "active":
|
||||
raise SystemExit("runtime signing key is not uniquely active in the keyring")
|
||||
Path("runtime-output/runtime-release-public.pem").write_text(
|
||||
matches[0]["public_key_pem"], encoding="utf-8"
|
||||
)
|
||||
PY
|
||||
openssl pkeyutl -verify -pubin \
|
||||
-inkey runtime-output/runtime-release-public.pem -rawin \
|
||||
-in runtime-output/govoplan-deploy.pyz \
|
||||
-sigfile runtime-output/govoplan-deploy.pyz.sig
|
||||
cp runtime-output/govoplan-deploy.pyz runtime-output/govoplan-deploy.tampered.pyz
|
||||
printf '\0' >> runtime-output/govoplan-deploy.tampered.pyz
|
||||
if openssl pkeyutl -verify -pubin \
|
||||
-inkey runtime-output/runtime-release-public.pem -rawin \
|
||||
-in runtime-output/govoplan-deploy.tampered.pyz \
|
||||
-sigfile runtime-output/govoplan-deploy.pyz.sig >/dev/null 2>&1; then
|
||||
echo "Tampered deployment bootstrap unexpectedly verified" >&2
|
||||
exit 1
|
||||
fi
|
||||
MANIFEST_SHA256="$(cut -d' ' -f1 runtime-output/distribution-manifest.json.sha256)"
|
||||
python runtime-output/govoplan-deploy.pyz init \
|
||||
--directory runtime-output/acceptance-install \
|
||||
--non-interactive --module-set base
|
||||
python runtime-output/govoplan-deploy.pyz verify-release \
|
||||
--directory runtime-output/acceptance-install \
|
||||
--manifest runtime-output/distribution-manifest.json \
|
||||
--manifest-sha256 "$MANIFEST_SHA256" \
|
||||
--trusted-keyring runtime-output/distribution-keyring.json \
|
||||
--adopt
|
||||
- name: Exercise the managed ingress boundary
|
||||
working-directory: govoplan
|
||||
env:
|
||||
MANAGED_INGRESS_IMAGE: ${{ inputs.managed_ingress_image }}
|
||||
LOAD_BALANCER_IMAGE: ${{ inputs.load_balancer_image }}
|
||||
run: >-
|
||||
python tools/checks/managed-ingress-drill.py
|
||||
--caddy-image "$MANAGED_INGRESS_IMAGE"
|
||||
--load-balancer-image "$LOAD_BALANCER_IMAGE"
|
||||
--probe-image "$(jq -r '.platforms["linux/amd64"]' runtime-output/api-metadata.json)"
|
||||
- name: Publish immutable Gitea release assets
|
||||
working-directory: govoplan
|
||||
env:
|
||||
VERSION: ${{ inputs.version }}
|
||||
GITEA_RELEASE_TOKEN: ${{ secrets.GOVOPLAN_RELEASE_TOKEN }}
|
||||
run: |
|
||||
SOURCE_COMMIT="$(cat runtime-output/release-source-commit)"
|
||||
python tools/release/publish-runtime-release.py \
|
||||
--tag "v$VERSION" \
|
||||
--target-commit "$SOURCE_COMMIT" \
|
||||
--title "GovOPlaN v$VERSION runtime distribution" \
|
||||
--asset runtime-output/govoplan-deploy.pyz \
|
||||
--asset runtime-output/govoplan-deploy.pyz.sig \
|
||||
--asset runtime-output/govoplan-deploy.pyz.sha256 \
|
||||
--asset runtime-output/distribution-manifest.json \
|
||||
--asset runtime-output/distribution-manifest.json.sha256 \
|
||||
--asset runtime-output/distribution-keyring.json \
|
||||
--asset runtime-output/context-amd64/composition.json \
|
||||
--asset runtime-output/release-packages.json \
|
||||
--asset runtime-output/package-artifacts.lock.json \
|
||||
--asset runtime-output/requirements-release.packages.txt \
|
||||
--asset runtime-output/evidence/api-sbom.cdx.json \
|
||||
--asset runtime-output/evidence/web-sbom.cdx.json \
|
||||
--asset runtime-output/evidence/api-provenance.json \
|
||||
--asset runtime-output/evidence/web-provenance.json \
|
||||
--asset runtime-output/evidence/runtime-smoke-amd64.json \
|
||||
--asset runtime-output/evidence/runtime-smoke-arm64.json
|
||||
@@ -0,0 +1,44 @@
|
||||
name: Runtime Ingress Drill
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
caddy_image:
|
||||
description: Digest-pinned Caddy image
|
||||
required: true
|
||||
type: string
|
||||
load_balancer_image:
|
||||
description: Digest-pinned HAProxy image
|
||||
required: true
|
||||
type: string
|
||||
probe_image:
|
||||
description: Digest-pinned amd64 GovOPlaN API image
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
managed-ingress:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
||||
with:
|
||||
path: govoplan
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Authenticate runtime image pull
|
||||
env:
|
||||
REGISTRY_USERNAME: ${{ secrets.GOVOPLAN_REGISTRY_USERNAME }}
|
||||
REGISTRY_TOKEN: ${{ secrets.GOVOPLAN_REGISTRY_TOKEN }}
|
||||
run: echo "$REGISTRY_TOKEN" | docker login git.add-ideas.de --username "$REGISTRY_USERNAME" --password-stdin
|
||||
- name: Exercise the managed ingress boundary
|
||||
working-directory: govoplan
|
||||
env:
|
||||
CADDY_IMAGE: ${{ inputs.caddy_image }}
|
||||
LOAD_BALANCER_IMAGE: ${{ inputs.load_balancer_image }}
|
||||
PROBE_IMAGE: ${{ inputs.probe_image }}
|
||||
run: >-
|
||||
python tools/checks/managed-ingress-drill.py
|
||||
--caddy-image "$CADDY_IMAGE"
|
||||
--load-balancer-image "$LOAD_BALANCER_IMAGE"
|
||||
--probe-image "$PROBE_IMAGE"
|
||||
@@ -1,7 +1,8 @@
|
||||
name: Security Audit
|
||||
|
||||
permissions: read-all
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
@@ -13,37 +14,31 @@ jobs:
|
||||
security-audit:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SECURITY_AUDIT_MODE: ci
|
||||
SECURITY_AUDIT_MODE: full
|
||||
SECURITY_AUDIT_SCOPE: govoplan
|
||||
SECURITY_AUDIT_FAIL_ON_FINDINGS: "0"
|
||||
SECURITY_AUDIT_REQUIRE_TOOLS: "1"
|
||||
GIT_CONFIG_COUNT: "2"
|
||||
GIT_CONFIG_KEY_0: url.https://git.add-ideas.de/GovOPlaN/govoplan.insteadOf
|
||||
GIT_CONFIG_VALUE_0: git@git.add-ideas.de:GovOPlaN/govoplan
|
||||
GIT_CONFIG_KEY_1: url.https://git.add-ideas.de/GovOPlaN/govoplan.insteadOf
|
||||
GIT_CONFIG_VALUE_1: ssh://git@git.add-ideas.de/GovOPlaN/govoplan
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
|
||||
with:
|
||||
path: govoplan
|
||||
- name: Configure SSH for repository bootstrap
|
||||
env:
|
||||
GOVOPLAN_RELEASE_SSH_KEY_B64: ${{ secrets.GOVOPLAN_RELEASE_SSH_KEY_B64 }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
if [ -z "${GOVOPLAN_RELEASE_SSH_KEY_B64:-}" ]; then
|
||||
echo "GOVOPLAN_RELEASE_SSH_KEY_B64 secret is required for git+ssh repository bootstrap."
|
||||
exit 1
|
||||
fi
|
||||
printf '%s' "$GOVOPLAN_RELEASE_SSH_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
|
||||
chmod 600 ~/.ssh/id_ed25519
|
||||
echo 'git.add-ideas.de ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDe48IOof2fJS1dTbJtLWQnWnr+JorZXKIFdOAM9ct8G' > ~/.ssh/known_hosts
|
||||
chmod 600 ~/.ssh/known_hosts
|
||||
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Bootstrap GovOPlaN repositories
|
||||
working-directory: govoplan
|
||||
run: python tools/repo/bootstrap-repositories.py --parent ..
|
||||
- name: Run security audit
|
||||
run: python tools/repo/bootstrap-repositories.py --parent .. --transport public-https --reuse-checkout-auth --exclude-repo addideas-govoplan-website
|
||||
- name: Run whole-system security audit
|
||||
working-directory: govoplan
|
||||
run: tools/checks/security-audit/run.sh --mode "$SECURITY_AUDIT_MODE" --scope "$SECURITY_AUDIT_SCOPE" --reports-dir audit-reports
|
||||
- name: Upload audit reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
|
||||
with:
|
||||
name: security-audit-reports
|
||||
path: govoplan/audit-reports
|
||||
|
||||
@@ -4,7 +4,16 @@
|
||||
.ruff_cache/
|
||||
.venv/
|
||||
runtime/
|
||||
!tools/release/runtime/
|
||||
tools/release/runtime/*
|
||||
!tools/release/runtime/Dockerfile.api
|
||||
!tools/release/runtime/Dockerfile.web
|
||||
!tools/release/runtime/nginx.conf
|
||||
!tools/release/runtime/web-entrypoint.sh
|
||||
__pycache__/
|
||||
build/
|
||||
dist/
|
||||
*.egg-info/
|
||||
audit-reports/
|
||||
coverage/
|
||||
htmlcov/
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# GovOPlaN Workspace Codex Guide
|
||||
|
||||
## Scope
|
||||
|
||||
This repository coordinates the GovOPlaN workspace, release catalog, shared
|
||||
checks, development environment, and cross-repository automation. Business and
|
||||
platform behavior remains owned by the corresponding module repository.
|
||||
|
||||
## Documentation Contract
|
||||
|
||||
- Treat documentation as part of every behavior change. Update the owning module's manifest-driven `DocumentationTopic` contributions for each affected user and administrator workflow, setting, permission, limitation, and operational consequence.
|
||||
- Keep feature content in the owning module. The optional `govoplan-docs` module projects module contributions and must not import feature internals.
|
||||
- Every module manifest must provide a static user and administrator baseline, even when `documentation_providers` add configured-state details.
|
||||
- Run `tools/checks/check-manifest-shapes.py` after module behavior or manifest changes. Run `tools/checks/check-focused.sh` for cross-module changes.
|
||||
|
||||
## Working Rules
|
||||
|
||||
- Treat Gitea issues as the canonical backlog and state log.
|
||||
- Preserve optional module boundaries and use Core contracts or capabilities for integrations.
|
||||
- Prefer targeted checks before full workspace scans.
|
||||
- Do not start persistent development servers unless requested.
|
||||
@@ -4,6 +4,12 @@
|
||||
**Repository type:** system (meta).
|
||||
<!-- govoplan-repository-type:end -->
|
||||
|
||||
[](https://git.add-ideas.de/GovOPlaN/govoplan/actions?workflow=module-matrix.yml&actor=0&status=0)
|
||||
[](https://git.add-ideas.de/GovOPlaN/govoplan/actions?workflow=release-integration.yml&actor=0&status=0)
|
||||
[](https://git.add-ideas.de/GovOPlaN/govoplan/actions?workflow=deployment-installer.yml&actor=0&status=0)
|
||||
[](https://git.add-ideas.de/GovOPlaN/govoplan/actions?workflow=dependency-audit.yml&actor=0&status=0)
|
||||
[](https://git.add-ideas.de/GovOPlaN/govoplan/actions?workflow=security-audit.yml&actor=0&status=0)
|
||||
|
||||
This is the GovOPlaN meta repository. It is the operator entry point for
|
||||
whole-product development, release orchestration, repository bootstrap, and
|
||||
system-level Docker composition.
|
||||
@@ -36,6 +42,16 @@ Open the WebUI in a browser after launch only when explicitly requested:
|
||||
GOVOPLAN_OPEN_BROWSER=1 ./tools/launch/launch-dev.sh
|
||||
```
|
||||
|
||||
Limit backend reload triggers during focused module work without changing the
|
||||
enabled module graph:
|
||||
|
||||
```sh
|
||||
GOVOPLAN_BACKEND_RELOAD_MODULES=calendar,campaign ./tools/launch/launch-dev.sh
|
||||
```
|
||||
|
||||
Set `GOVOPLAN_BACKEND_RELOAD_MODULES=none` to watch only core/config sources.
|
||||
Leaving it unset keeps the broad default and watches all enabled modules.
|
||||
|
||||
Start the shared development PostgreSQL service:
|
||||
|
||||
```sh
|
||||
@@ -54,6 +70,12 @@ Clone missing repositories listed in `repositories.json`:
|
||||
./tools/repo/bootstrap-repositories.py
|
||||
```
|
||||
|
||||
Gitea Actions jobs bootstrap the registered repositories over HTTPS and reuse
|
||||
only the checkout job's short-lived authentication header. If registered
|
||||
modules are private, allow the meta repository read access under
|
||||
`GovOPlaN -> Settings -> Actions -> General -> Cross-Repository Access`; no
|
||||
long-lived personal token is stored by the workflow or bootstrap tool.
|
||||
|
||||
Update generated repository type notes in all READMEs:
|
||||
|
||||
```sh
|
||||
@@ -91,6 +113,18 @@ Generate the CycloneDX dependency inventory from a resolved release environment:
|
||||
./.venv/bin/python tools/release/generate-release-sbom.py --python ./.venv/bin/python
|
||||
```
|
||||
|
||||
Synchronize module package workflows and inspect the registry release contract:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/repo/sync-module-package-workflows.py --check
|
||||
./.venv/bin/python tools/release/generate-release-package-set.py \
|
||||
--output /tmp/govoplan-release-packages.json
|
||||
```
|
||||
|
||||
Package publication, exact artifact locking, and the optional `govoplan`
|
||||
developer meta-package are documented in
|
||||
[Package Registry Releases](docs/operations/PACKAGE_REGISTRY_RELEASES.md).
|
||||
|
||||
For reproducible release artifacts, set `SOURCE_DATE_EPOCH` to the release
|
||||
commit timestamp (or pass an explicit timezone-qualified `--timestamp`):
|
||||
|
||||
@@ -126,6 +160,30 @@ Start the local release console:
|
||||
./.venv/bin/python tools/release/release-console.py
|
||||
```
|
||||
|
||||
Create and validate a private, declarative installation bundle:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/deployment/govoplan-deploy.py init \
|
||||
--directory ~/.local/share/govoplan/installations/default
|
||||
./.venv/bin/python tools/deployment/govoplan-deploy.py doctor \
|
||||
--directory ~/.local/share/govoplan/installations/default
|
||||
```
|
||||
|
||||
The current executable slice and remaining production gates are documented in
|
||||
[Installation and Deployment Architecture](docs/operations/INSTALLATION_AND_DEPLOYMENT_ARCHITECTURE.md).
|
||||
The canonical distinction between local source development, split source
|
||||
integration, immutable single-host rehearsal, one-host production and
|
||||
multi-host Kubernetes production is in
|
||||
[Deployment Profiles](docs/operations/DEPLOYMENT_PROFILES.md).
|
||||
Same-host replica balancing and the multi-host promotion boundary are documented
|
||||
in [Scaling and Multi-Host Deployment](docs/operations/SCALING_AND_MULTI_HOST_DEPLOYMENT.md).
|
||||
Create, update, pause, resume, verify and remove a local or multi-hypervisor K3s
|
||||
VM target with the guarded lifecycle documented in
|
||||
[Kubernetes VM Test Lab](docs/operations/KUBERNETES_TEST_LAB.md).
|
||||
The recovery state machine, migration rollback boundary, and required restore
|
||||
drills are documented in
|
||||
[Recovery and Rollback Guarantees](docs/operations/RECOVERY_AND_ROLLBACK_GUARANTEES.md).
|
||||
|
||||
## Configuration
|
||||
|
||||
The repository root `.env.example` is the self-hosted operator template for a
|
||||
@@ -137,26 +195,14 @@ such as `~/.config/gitea/gitea.env` and be passed with `--env-file`.
|
||||
|
||||
## Structure
|
||||
|
||||
The repository categories are documented in
|
||||
`docs/REPOSITORY_STRUCTURE.md`. The machine-readable list lives in
|
||||
`repositories.json`; the clickable human-readable index is
|
||||
`docs/REPOSITORY_INDEX.md`.
|
||||
Start with the [documentation map](docs/README.md). It separates stable
|
||||
strategy, architecture, operations, project reference, pinned evidence, and
|
||||
historical records and identifies the canonical source for each question.
|
||||
|
||||
Meta ownership and module install/contract boundaries are documented in
|
||||
`docs/META_REPO_SCAN.md` and `docs/MODULE_CONTRACTS_AND_INSTALLS.md`.
|
||||
Frontend layout principles for module pages are documented in
|
||||
`docs/FRONTEND_LAYOUT_PRINCIPLES.md`.
|
||||
The cross-product destination, stakeholder visions, configuration archetypes,
|
||||
connected outcome stories, and capability horizons are documented in
|
||||
the [Connected Governance Platform Roadmap](docs/CONNECTED_GOVERNANCE_PLATFORM_ROADMAP.md).
|
||||
The selected Campaign-to-Postbox-to-data-to-collaboration implementation path,
|
||||
including stage gates and shared documentation expectations, is in the
|
||||
[Reference Journey Program](docs/REFERENCE_JOURNEY_PROGRAM.md).
|
||||
The administrator journey from Core-only bootstrap through online module
|
||||
installation, scale-out, and reversible environment promotion is defined in
|
||||
[System Administrator Lifecycle User Story](docs/SYSTEM_ADMINISTRATOR_LIFECYCLE_USER_STORY.md).
|
||||
The first Campaign-centric capability and infrastructure fit assessment is in
|
||||
`docs/CAPABILITY_AND_INFRASTRUCTURE_FIT.md`.
|
||||
The machine-readable repository list lives in `repositories.json`; the
|
||||
clickable directory is the
|
||||
[Repository Index](docs/project/REPOSITORY_INDEX.md), and ownership boundaries
|
||||
are in [Repository Structure](docs/project/REPOSITORY_STRUCTURE.md).
|
||||
|
||||
# GovOPlaN Docker
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
This profile runs the shared services that production depends on while keeping
|
||||
API, worker, scheduler, and WebUI code in the editable local repositories.
|
||||
It is the **split source integration** profile defined in
|
||||
[`docs/operations/DEPLOYMENT_PROFILES.md`](../../docs/operations/DEPLOYMENT_PROFILES.md). It does not
|
||||
exercise signed application images. Use an installer-generated evaluation
|
||||
Compose bundle for an immutable Dockerized whole-product rehearsal.
|
||||
|
||||
It provides:
|
||||
|
||||
|
||||
@@ -7,6 +7,11 @@ Current shared profiles:
|
||||
- `govoplan/dev/postgres`
|
||||
- `govoplan/dev/production-like`
|
||||
|
||||
The generated whole-product Compose profile is owned by
|
||||
`tools/deployment/govoplan-deploy.py`. It renders a deployment-specific
|
||||
`compose.json` from a versioned installation specification; generated files and
|
||||
secrets remain outside the repository.
|
||||
|
||||
Module-specific Docker test beds remain in their owning repositories:
|
||||
|
||||
- `govoplan-campaign/dev/mail-testbed`
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
# GovOPlaN Frontend Layout Principles
|
||||
|
||||
GovOPlaN modules should choose their page layout by the kind of work the user is
|
||||
doing, not by the repository that owns the feature.
|
||||
|
||||
These concise layout choices are one canonical input to the broader
|
||||
[`INTERFACE_PATTERN_LANGUAGE.md`](INTERFACE_PATTERN_LANGUAGE.md). The current
|
||||
route and rollout evidence lives in
|
||||
[`INTERFACE_SURFACE_INVENTORY.md`](INTERFACE_SURFACE_INVENTORY.md).
|
||||
|
||||
## Structured Data Directories
|
||||
|
||||
Use a full-available-space workspace for structured data directories: files,
|
||||
addresses, calendars, records, mailboxes, document stores, and similar domains
|
||||
where the primary task is browsing, selecting, filtering, inspecting, and acting
|
||||
on related objects.
|
||||
|
||||
Principles:
|
||||
|
||||
- The module route should use the full available content area.
|
||||
- Do not add a separate page heading row above the main workspace.
|
||||
- Prefer persistent navigation panes, such as tree panels, source panels, folder
|
||||
panels, calendar list panels, or mailbox folder panels.
|
||||
- Keep collection navigation and collection-level actions close to the relevant
|
||||
pane header.
|
||||
- In a list-detail workspace such as Scheduling, keep related lists stacked in
|
||||
the left pane and use the remaining main pane for view/create/edit. A single
|
||||
Add action stays in the relevant list-pane header and opens the common main
|
||||
editor; it does not create an additional menu or launcher.
|
||||
- Use bounded widths for navigation/list panes and let the main detail/content
|
||||
pane take the remaining space.
|
||||
- Keep filtering controls inside the pane they affect.
|
||||
- Use overlays, toasts, or floating alerts for transient messages so the
|
||||
workspace height does not change.
|
||||
|
||||
This pattern is appropriate when the user is working inside one coherent data
|
||||
domain and needs spatial continuity.
|
||||
|
||||
## Workflow And Configuration Surfaces
|
||||
|
||||
Use the standard heading/menu/card visual language for workflow structures,
|
||||
settings, administration, dashboards, and pages that collect essentially
|
||||
unrelated areas.
|
||||
|
||||
Principles:
|
||||
|
||||
- A page heading and subnavigation are appropriate when the page explains a
|
||||
task, workflow stage, or administrative area.
|
||||
- Cards are appropriate for repeated independent panels, settings groups,
|
||||
summaries, and dashboard widgets.
|
||||
- Collapsible panels and segmented controls are appropriate when a dense
|
||||
configuration area needs controlled disclosure.
|
||||
- A collapsible card whose sole content is a table gives that table the full
|
||||
available card body; avoid nested cards, duplicate padding, inner max-widths,
|
||||
and nested scrolling.
|
||||
- Avoid forcing workflow/configuration pages into a file-explorer style unless
|
||||
the primary interaction is genuinely directory browsing.
|
||||
|
||||
This pattern is appropriate when the user is comparing or configuring separate
|
||||
concerns rather than navigating one structured object space.
|
||||
|
||||
## Shared Components
|
||||
|
||||
Reusable layout components belong in `govoplan-core` WebUI. Modules may consume
|
||||
shared components from core, but must not import another module's private UI
|
||||
components directly.
|
||||
|
||||
When a module-specific component becomes generally useful, promote it to core
|
||||
with a parameterized API before reusing it elsewhere.
|
||||
|
||||
Non-self-explanatory fields use Core `FieldLabel`; documented omissions must
|
||||
name their accessible-label source. Explicit Discard and dirty navigation use
|
||||
the same Core unsaved-changes dialog. Table action sets retain unavailable row
|
||||
actions as disabled controls and reserve empty-state slots so Add remains
|
||||
aligned. Use central feedback/dialog components; `window.alert` is not an
|
||||
authorized product surface unless a product-owner-approved exception is first
|
||||
recorded in the Core decision ledger.
|
||||
@@ -1,236 +0,0 @@
|
||||
# GovOPlaN Interface Surface Inventory And Rollout
|
||||
|
||||
This is the initial evidence inventory for the product-wide interface pattern
|
||||
language. It records code contributions, not an assertion that every listed
|
||||
surface is complete, enabled in a deployment, usable, or compliant.
|
||||
|
||||
The applicable design contract is
|
||||
[`INTERFACE_PATTERN_LANGUAGE.md`](INTERFACE_PATTERN_LANGUAGE.md).
|
||||
|
||||
## Snapshot And Method
|
||||
|
||||
Snapshot refreshed: 2026-07-22.
|
||||
|
||||
Evidence was read from tracked Git `HEAD` in the local GovOPlaN checkouts:
|
||||
|
||||
- core routes and fallback behavior in `govoplan-core/webui/src/App.tsx`
|
||||
- every present `govoplan-*/webui/src/module.ts`
|
||||
- the matching backend `src/*/backend/manifest.py`
|
||||
- named UI capabilities and contribution identifiers in each `module.ts`
|
||||
- Campaign nested routes in its tracked `CampaignWorkspace.tsx` and
|
||||
`SectionSidebar.tsx`
|
||||
|
||||
Tracked commits are used as the integrated baseline. Dirty worktree changes
|
||||
are not treated as delivered behavior. The former Campaign recipient-editor
|
||||
and preview WIP is integrated in tracked commits; completed work is no longer
|
||||
described as a local exception.
|
||||
|
||||
Runtime visibility remains conditional on the module being packaged and
|
||||
enabled, server metadata, installed optional capabilities, the authenticated
|
||||
actor, tenant context, route permission guards, and inner-surface permission
|
||||
checks. A route in this inventory therefore means "the code contributes this
|
||||
route when its module is active", not "every user sees it".
|
||||
|
||||
Inventory states:
|
||||
|
||||
- **Contributed**: a route or named UI capability exists in tracked source.
|
||||
- **Metadata gap**: frontend runtime source contributes a route but the backend
|
||||
manifest does not describe the same route/navigation surface.
|
||||
- **Unreviewed**: the surface has not yet completed the pattern, state,
|
||||
accessibility, privacy, and consequence audit. This is the default unless an
|
||||
issue supplies verification evidence.
|
||||
- **Pilot**: the surface is in the Campaign-first rollout.
|
||||
|
||||
## Core Shell Surfaces
|
||||
|
||||
| Surface | Owner and code evidence | Audience/access evidence | Primary task and target archetype | Audit / rollout |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Public landing and login | `govoplan-core` `PublicLandingPage`; rendered while no authenticated principal exists | Unauthenticated; maintenance and backend-reachability context are shell inputs | Understand the service and authenticate; public entry | Unreviewed; later public-entry audit |
|
||||
| Session/bootstrap state | `govoplan-core` `App.tsx` and `AppShell` | All browser sessions during bootstrap | Understand that session/platform state is loading; state contract | Unreviewed; core shell |
|
||||
| `/` authenticated redirect | `govoplan-core` chooses the first visible navigation destination | Authenticated; result depends on visible nav contributions | Enter the actor's first accessible service area; navigation behavior, not a content page | Unreviewed; focused-view/default-route work must preserve this fallback |
|
||||
| `/dashboard` fallback | `govoplan-core` `DashboardPage` only when the Dashboard module is absent | Authenticated; no route-specific scope in core | Cross-module starting point; dashboard | Unreviewed; compare with module dashboard before shared changes |
|
||||
| `/settings` | `govoplan-core` `SettingsPage` | Authenticated; contributed sections and integrations filter internally | Profile, UI/workspace preference, local connection, and user-scoped integration settings; configuration | Unreviewed; Core #225 program |
|
||||
| Shell chrome | `AppShell`, `Titlebar`, `IconRail`, `BreadcrumbBar`, `HelpMenu`, language menu, unsaved-change provider | Public/authenticated variants; nav filtered later | Tenant/actor context, global navigation, help, language, session and maintenance state | Unreviewed; platform-owned prerequisite for focused views |
|
||||
|
||||
## Direct Module Route Contributions
|
||||
|
||||
The access guard column reports only the route-level declaration in
|
||||
`module.ts`. Inner APIs and controls may impose additional checks.
|
||||
|
||||
| Route | Owner / render evidence | Route-level access evidence | Primary task | Target archetype | Status / priority |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `/admin` | `govoplan-access` `AdminPage` | Any core `adminReadScopes` | Administer system and tenant concerns assembled from module sections | Administration/configuration | Contributed; unreviewed; P1 under [Core #225](https://git.add-ideas.de/add-ideas/govoplan-core/issues/225) |
|
||||
| `/address-book` | `govoplan-addresses` `AddressBookPage` | `addresses:contact:read` | Browse and manage contacts, address books, and lists | Directory/list-detail | Contributed; unreviewed; P2 after Campaign |
|
||||
| `/calendar` | `govoplan-calendar` `CalendarPage` | `calendar:event:read` | Browse calendars/events and act on calendar data | Directory/list-detail | Contributed; metadata gap; unreviewed; P2 after Campaign |
|
||||
| `/campaigns` | `govoplan-campaign` `CampaignListPage` | `campaigns:campaign:read` | Find, compare, create, and open campaigns | List-detail entry | Pilot; P1 [Campaign #74](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/74) |
|
||||
| `/campaigns/:campaignId/*` | `govoplan-campaign` `CampaignResourceRoute` and `CampaignWorkspace` | `campaigns:campaign:read`, plus resource probe | Configure, review, send, and inspect one campaign/version | List-detail workspace containing edit, review, monitoring, and evidence surfaces | Pilot; P1 Campaign #74 |
|
||||
| `/operator` | `govoplan-campaign` `OperatorQueuePage` | `campaigns:campaign:read` and any of queue, control, retry, or reconcile | Monitor and intervene in campaign jobs through authority-specific controls | Monitoring/work queue | Pilot; durable queue controls delivered in [Campaign #78](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/78); #74 audit remains |
|
||||
| `/reports` | `govoplan-campaign` `AggregateReportsPage` | `campaigns:report:read` | Compare privacy-protected cross-campaign outcome totals without recipient detail, diagnostics, export, or drill-down | Aggregate reporting | Pilot; aggregate-reader surface delivered in [Campaign #80](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/80); #74 audit remains |
|
||||
| `/templates` | `govoplan-campaign` `TemplatesPage` | No route guard declared in `module.ts` | Browse/manage campaign templates | Directory/list-detail | Pilot audit; permission intent must be verified; P2 |
|
||||
| `/dashboard` | `govoplan-dashboard` `DashboardPage` | No route-specific scope | Assemble module-provided actionable widgets | Dashboard | Contributed; unreviewed; P2 |
|
||||
| `/docs` | `govoplan-docs` `DocsPage` | Docs read or system/tenant settings read scopes | Read configured, available, and evidence-aware documentation | Documentation directory/reference | Contributed; unreviewed; P1 [Docs #15](https://git.add-ideas.de/add-ideas/govoplan-docs/issues/15) after initial pattern content |
|
||||
| `/files` | `govoplan-files` `FilesPage` | `files:file:read` | Browse folders/files and perform managed-file work | Directory/explorer | Contributed; metadata gap; unreviewed; P2 after Campaign |
|
||||
| `/idm` | `govoplan-idm` `IdmPage` | Any IDM assignment/write or organization function-assign scope | Inspect and govern identity/function assignments | List-detail/configuration | Contributed; unreviewed; P2 |
|
||||
| `/mail` | `govoplan-mail` `MailboxPage` | `mail:mailbox:read` | Browse mailboxes and messages | Directory/list-detail | Contributed; metadata gap; unreviewed; P2 after Campaign |
|
||||
| `/notifications` | `govoplan-notifications` `NotificationCenterPage` | `notifications:notification:read` | Inspect and acknowledge notification state | List-detail/inbox | Contributed without a nav item or backend frontend metadata; navigation intent unknown; P2 discovery |
|
||||
| `/ops` | `govoplan-ops` `OpsPage` | Ops read or system/tenant settings read scopes | Inspect runtime health and readiness | Monitoring | Contributed; unreviewed; P2 |
|
||||
| `/organizations` | `govoplan-organizations` `OrganizationsPage` | Organization model/unit/function or admin settings read scopes | Model and inspect organizational structures/functions | Directory/list-detail | Contributed; unreviewed; P2 |
|
||||
| `/scheduling` | `govoplan-scheduling` `SchedulingPage` | `scheduling:schedule:read` | Plan and decide scheduling requests and availability | List-detail/guided decision | Contributed; metadata gap; unreviewed; P2 |
|
||||
|
||||
## Manifest And Runtime Route Alignment
|
||||
|
||||
Backend route metadata lets operators, Docs, release tooling, and remote bundle
|
||||
loading reason about the configured interface without executing module UI code.
|
||||
`module.ts` remains the executable local route/render source. Missing metadata
|
||||
is recorded here as an evidence gap; this inventory does not infer whether each
|
||||
gap is intentional.
|
||||
|
||||
| Module | `module.ts` routes | Backend manifest frontend routes | Backend nav alignment | Result |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Access | `/admin` | `/admin` | Aligned | Described |
|
||||
| Addresses | `/address-book` | `/address-book` | Aligned | Described |
|
||||
| Admin | No direct route; `admin.sections` | None | Not applicable | Composed surface |
|
||||
| Audit | No direct route; `admin.sections` | None | Not applicable | Composed surface |
|
||||
| Calendar | `/calendar` | None | `/calendar` nav exists | Metadata gap |
|
||||
| Campaign | Five routes | None | Four top-level nav items exist | Metadata gap; wildcard resource route is also undescribed |
|
||||
| Dashboard | `/dashboard` | `/dashboard` | Aligned | Described |
|
||||
| Docs | `/docs` | `/docs` | Aligned | Described |
|
||||
| Files | `/files` | None | `/files` nav exists | Metadata gap |
|
||||
| IDM | `/idm` | `/idm` | Aligned | Described |
|
||||
| Mail | `/mail` | None | `/mail` nav exists | Metadata gap |
|
||||
| Notifications | `/notifications` | No frontend metadata | No nav item | Metadata and discovery gap |
|
||||
| Ops | `/ops` | `/ops` | Aligned | Described |
|
||||
| Organizations | `/organizations` | `/organizations` | Aligned | Described |
|
||||
| Policy | No direct route; `admin.sections` | None | Not applicable | Composed surface |
|
||||
| Scheduling | `/scheduling` | None | `/scheduling` nav exists | Metadata gap |
|
||||
|
||||
Before a release claims a complete configured-system route inventory, add a
|
||||
contract check or explicit exceptions so executable routes and manifest
|
||||
metadata cannot silently diverge.
|
||||
|
||||
## Composed Surfaces And Extension Points
|
||||
|
||||
These surfaces are active only when the host and contributing modules are
|
||||
enabled and the actor passes the declared filters.
|
||||
|
||||
| Host surface | Contributor and evidence | Contributed regions/actions | Pattern implication | Audit |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `/admin` | Access host (`AdminPage`) | System tenants/users/roles, tenant users/groups/roles/API keys/settings, function-role mappings, user/group mail and file connector scopes | One stable admin information architecture must contain both host-owned and contributed sections | Unreviewed; P1 Core #225 |
|
||||
| `/admin` | `govoplan-admin` `admin.sections` | Overview; system settings; configuration changes; configuration packages; role/group templates; module management | Configuration, guided operations, review/preflight, consequence | In progress under Core #225; surface-level evidence still needed |
|
||||
| `/admin` | `govoplan-audit` `admin.sections` | System audit; tenant audit | Evidence/provenance and reporting | Unreviewed |
|
||||
| `/admin` | `govoplan-files` `admin.sections` and `files.connectors` | System and tenant file connections plus scoped connector managers used by Access | Adaptive configuration, discovery/test, policy and credentials | First migration family in Core #225; verification incomplete in this inventory |
|
||||
| `/admin` | `govoplan-organizations` `admin.sections` | Tenant organization settings | Configuration/list-detail | Unreviewed |
|
||||
| `/admin` | `govoplan-policy` `admin.sections` | System, tenant, group, and user retention | Effective value, source/provenance, consequential configuration | Unreviewed; Core #225 phase 4 |
|
||||
| `/admin` and `/settings` | `govoplan-mail` `mail.profiles` | System/tenant/group/user mail profile and policy managers | Same server/credential/policy grammar as file connectors | Unreviewed; Core #225 mail migration |
|
||||
| `/settings` | Core host | Profile; interface; workspace; local connection | Personal configuration with adaptive forms and immediate feedback | Unreviewed |
|
||||
| `/settings` | Files and Mail named capabilities | User-scoped file connections and mail profiles/policy | Optional integration regions disappear cleanly when capability absent | Unreviewed |
|
||||
| `/settings` | `govoplan-notifications` `settings.sections` | Notification preferences | Personal configuration | Unreviewed |
|
||||
| `/dashboard` | Dashboard host and `dashboard.widgets` | Installed-modules widget; Ops health widget when Ops contributes it | Widget ordering, staleness, permissions, destination behavior | Unreviewed |
|
||||
| `/organizations` | IDM `organizations.functionActions` | Action leading to assignment view filtered by IDM scopes | Cross-module context action through explicit capability | Unreviewed |
|
||||
| Campaign attachments/import | Files `files.fileExplorer` | Folder tree, managed chooser, file listing/pattern resolution/sharing | Optional domain composition without sibling-private imports | Pilot audit under Campaign #74 |
|
||||
| Campaign review/send | Mail runtime `mail.devMailbox` | Mock-mail verification when backend advertises runtime capability | Optional review stage with unavailable/optional states | Pilot audit under Campaign #63/#62 |
|
||||
|
||||
Other named capability exports (`files.connectors`, `organizations.functionPicker`,
|
||||
and mail profile validation) are contracts consumed inside the composed surfaces
|
||||
above; they are not independent routes.
|
||||
|
||||
## Campaign Pilot Surface Map
|
||||
|
||||
Campaign is detailed first because it exercises almost every archetype. The
|
||||
recipient-data editor is now consolidated into the `recipients` section on
|
||||
remote `main`; [Campaign #67](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/67)
|
||||
records the accepted and verified integration boundary.
|
||||
|
||||
Campaign already consumes core primitives including `ModuleSubnav`, `Card`,
|
||||
`PageTitle`, `Button`, `LoadingFrame`, `DismissibleAlert`, `FormField`,
|
||||
`StatusBadge`, `MetricCard`, `DataGrid`, `TableActionGroup`, `Dialog`,
|
||||
`ConfirmDialog`, `FileDropZone`, `MessageDisplayPanel`, policy components,
|
||||
access/module capabilities, and unsaved-navigation guards. Reuse alone does not
|
||||
prove that the composition or states satisfy the pattern.
|
||||
|
||||
| Surface / code evidence | Primary task | Target pattern | Material consequence/state | Known issue / rollout |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Campaign list (`CampaignListPage`) | Find, compare, create, open | List-detail entry | Campaign lifecycle/status and creation | Audit in [#74](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/74); guided entry [#35](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/35) |
|
||||
| Overview (`CampaignOverviewPage`) | Understand/edit campaign identity, version, access, lifecycle | Object overview plus adaptive edit | Lock/archive/delete/access changes need real consequence and reversibility wording | #74 remaining audit |
|
||||
| Fields (`CampaignFieldsPage`) | Define recipient/template field schema | Structured editor | Schema changes can invalidate recipient/template data | #74 audit |
|
||||
| Attachments/files (`AttachmentsDataPage`, `AttachmentRulesOverlay`) | Select sources and attachment/ZIP rules | Directory chooser plus adaptive rule editor | Missing or mismatched files affect built messages | #74; attachment-detail [#59](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/59) |
|
||||
| Recipients (`RecipientDataPage`) | Select/import/map/edit recipients, address fields and per-recipient values/files | Import/mapping plus list-detail editor | Personal data, validation, bulk activation, file links | Consolidated editor delivered in #67; #74 remaining audit and guided entry #35 |
|
||||
| Template (`TemplateDataPage`, placeholder/expression dialogs) | Author subject/body and preview substitutions | Adaptive editor plus stable preview | Generated communication content and unresolved expressions | #74; stable overlay [#73](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/73) |
|
||||
| Mail settings (`MailSettingsPage` settings view) | Select/configure campaign mail transport | Adaptive configuration | Credentials, SMTP/IMAP destinations, test outcomes | #74; align with Core #225 mail pattern |
|
||||
| Campaign settings (`GlobalSettingsPage` settings view) | Configure campaign behavior | Adaptive configuration | Can alter validation/build/send behavior | #74 audit |
|
||||
| Mail policy (`MailSettingsPage` policy view) | Inspect/override effective mail policy | Effective policy/provenance editor | Inheritance and locks affect allowed delivery | #74; Core #225 policy pattern |
|
||||
| Campaign policy (`GlobalSettingsPage` policy view) | Inspect/override campaign policy | Effective policy/provenance editor | Inheritance, actor authority, and blocked edits | #74; Core #225 policy pattern |
|
||||
| Review/send (`ReviewSendPage`) | Validate, build, mock-test, confirm/send, inspect results | Guided review/decision plus durable progress | External communication, bounded synchronous execution, persisted queue mode, partial effects, retries, evidence | Bounded synchronous and explicit/persisted queued modes delivered in [#62](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/62) and [#79](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/79); [#63](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/63) wording and #74 audit remain |
|
||||
| Message and attachment detail overlays | Inspect one built/mock message and its attachment links | Stable detail/review dialog | Personal data, exact outbound content, reviewed state | Delivered and verified in [#59](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/59) and [#73](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/73) |
|
||||
| Campaign report (`CampaignReportPage`) | Filter and inspect delivery outcomes | Reporting/list-detail | Partial, failed, explicitly excluded/skipped, SMTP/IMAP outcomes and retries | Server-owned filtering and counts delivered in [#65](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/65) with the full-result DataGrid contract from [Core #263](https://git.add-ideas.de/add-ideas/govoplan-core/issues/263); excluded semantics in [#66](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/66) |
|
||||
| Audit (`CampaignAuditPage`) | Inspect campaign evidence/history | Provenance timeline/report | Actor/action/effect trace | #74 audit |
|
||||
| JSON (`CampaignJsonView`) | Inspect expert representation | Advanced diagnostics/reference | Raw data may contain personal/configuration values; not a primary editor | #74 privacy/redaction audit |
|
||||
| Create wizard (`CreateWizard`) | Seed a campaign through basics, sender, fields, recipients, template, attachments, review, send | Guided setup | Current steps mix creation and later consequential delivery; completion semantics need audit | Guided first campaign [#35](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/35) |
|
||||
| Review/send wizard routes | Alternate guided review/send shells | Guided review | Tracked routes exist; implementation relationship to `ReviewSendPage` must be established, not guessed | #74 inventory decision |
|
||||
| Operator queue (`OperatorQueuePage`) | Monitor jobs and intervene | Monitoring/work queue | Campaign/version/job identity, historical active-version discovery, fixed action positions, authority-aware disabled states, exact non-overlapping queue counts, server-paged jobs, bounded refresh, retry/queue/reconcile per version, campaign-wide pause/resume/cancel, and leave/return progress | Durable operator controls delivered in [#78](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/78); #74 wording/accessibility audit remains |
|
||||
| Aggregate reports (`AggregateReportsPage`) | Compare cross-campaign delivery outcomes | Privacy-preserving aggregate reporting | Tenant/campaign ACL, deployment/tenant small-cell policy, complementary and overlapping-cell suppression, explicit denominator, and no recipient detail/diagnostics/export/drill-down | Separate aggregate-reader surface delivered in [#80](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/80); not parity with the permission-gated per-campaign detail report |
|
||||
| Templates route (`TemplatesPage`) | Browse template records | Directory/list-detail | Template availability and later generated outputs | #74 audit; verify missing route guard intent |
|
||||
|
||||
The five review stages currently named in code are `Validate and inspect`,
|
||||
`Build and review`, `Mock send and verify`, `Confirm and send`, and `Delivery
|
||||
results`. Campaign #63 owns the intervention and status vocabulary; Workflow is
|
||||
not required to define or implement it.
|
||||
|
||||
## Repositories Without A WebUI Route Contribution
|
||||
|
||||
The following local repositories contain a backend manifest but no
|
||||
`webui/src/module.ts` at this snapshot:
|
||||
|
||||
`govoplan-approvals`, `govoplan-assets`, `govoplan-booking`,
|
||||
`govoplan-certificates`, `govoplan-committee`, `govoplan-consultation`,
|
||||
`govoplan-contracts`, `govoplan-dist-lists`, `govoplan-evaluation`,
|
||||
`govoplan-facilities`, `govoplan-forms-runtime`, `govoplan-grants`,
|
||||
`govoplan-helpdesk`, `govoplan-identity`, `govoplan-inspections`,
|
||||
`govoplan-issue-reporting`, `govoplan-learning`, `govoplan-permits`,
|
||||
`govoplan-poll`, `govoplan-procurement`, `govoplan-records`,
|
||||
`govoplan-resources`, `govoplan-rest`, `govoplan-risk-compliance`,
|
||||
`govoplan-soap`, `govoplan-tenancy`, and `govoplan-transparency`.
|
||||
|
||||
This is only negative route evidence. It does not classify the backend module's
|
||||
maturity or decide that it needs a WebUI. Connector-only, capability-only, or
|
||||
backend-only modules may remain intentionally headless.
|
||||
|
||||
## Rollout Matrix
|
||||
|
||||
| Order | Scope | Current evidence | Target | Owner / issue | Verification gate | Status |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| 0 | Product grammar and route inventory | Doctrine, ledger, layout rules, module contract, current route sources | One reconciled pattern language and evidence inventory | Meta [#11](https://git.add-ideas.de/add-ideas/govoplan/issues/11) | Docs links/diff checks; issue/wiki sync after integration | Initial slice in this document |
|
||||
| 1 | Campaign baseline integration | Recipient-editor WIP and tracker state have been reconciled with remote `main` | Integrated, testable baseline before migration claims | Campaign #67 and tracker cleanup | Backend and focused WebUI suites; issue evidence | Complete 2026-07-22 |
|
||||
| 2 | Campaign previews/details | Stable shared dialog with bounded scrolling and fixed responsive preview workspace | Stable header/body/footer, accessible long-content detail | Campaign #59 and #73 | Review-preview and overlay structure tests | Complete 2026-07-22 |
|
||||
| 3 | Campaign review/interventions | Five domain-owned stages with unresolved intervention language | Clear stages, outcomes, blockers, next actor/action, reviewed evidence | Campaign #63 | State matrix behavior/accessibility tests and agreed vocabulary | P1 needs product wording decision |
|
||||
| 4 | Campaign send/progress | A hard deployment ceiling bounds synchronous delivery; the selected synchronous, worker-queue, or database-queue mode is explicit and persisted; progress and recovery survive navigation; immediate-send response and audit evidence are allowlisted | Pre-send mode/consequence plus durable leave/return progress, retry and reconciliation without recipient/provider leakage | Campaign #62 and #79 | Boundary/concurrency/preflight, async selection, persisted mode, sanitized response/audit, partial/failure/retry and reload/return tests | Complete 2026-07-22 (`7e16603`, `60efd1c`, `62a6879`, `b0282eb`, `f095a3e`) |
|
||||
| 5 | Campaign report filtering | Core DataGrid distinguishes client/full-result from server-owned queries; Campaign applies filter/sort/count before pagination and synchronizes count shortcuts with the grid query | One shared server-owned status/list/filter/count model | Campaign #65 and Core #263 | DataGrid contract/build tests plus exact shortcut/query/filter/count and large-result behavior | Complete 2026-07-22 (`e6062fe`, `cece71d`, `aa4ec66`, `4eb651c`) |
|
||||
| 6 | Campaign operator recovery | A durable campaign/version queue page exposes historical work, exact non-overlapping state counts, persisted mode, permission-safe controls, server-paged job evidence, bounded refresh and active-state recovery | Fixed-position actions, disabled explanations, leave/return state, version-scoped retry/queue/reconcile and explicit campaign-wide pause/resume/cancel | Campaign #78 | Queue model/structure, historical-version, permission, paging, recovery-control, stale-response and delta tests | Complete 2026-07-22 (`21f3014`, `99d44ee`, `735e874`) |
|
||||
| 7 | Campaign aggregate reports | A separate aggregate-reader projection and UI expose only policy-suppressed business totals with a stable status domain | Explicit denominator and exclusions, deployment floor plus tenant-strengthened small-cell threshold, complementary and overlapping-cell suppression, no detail/export/diagnostics | Campaign #80 | Aggregate query, cross-metric suppression, route/role/ACL, stable filter and UI structure tests | Complete 2026-07-22 (`06125cc`, `fc36aee`, `8ee87b7`, `ac3329c`, `1225802`) |
|
||||
| 8 | Campaign excluded outcomes | Excluded build rows become explicit skipped transport outcomes and remain protected from queue/cancel/retry ambiguity | One durable source-to-job-to-report meaning with guarded historical normalization | Campaign #66 | Builder/persistence, migration, query/count, queue-control and report-explanation tests | Complete 2026-07-22 (`7229fb8`) |
|
||||
| 9 | Guided first campaign | Existing wizard routes and ordinary workspace overlap | Task-oriented entry that hands off clearly to normal editing/review | Campaign #35 | First-run flow, resume/back, validation, optional modules, no implicit send | P1 after core pilot patterns stabilize |
|
||||
| 10 | Prove/extract generic primitives | Core already exports many primitives; Campaign composition still unreviewed | Extract only contracts with a second consumer or clear platform ownership | Core #225 plus bounded follow-ups | Core behavior/accessibility tests and module-permutation tests | After Campaign proof |
|
||||
| 11 | Configured-system pattern help | Docs route and classification exist | Role/config-aware pattern and route/field/blocker help | Docs #15 | Topic grouping, audience filtering, stable links/anchors | P1 after initial pattern IDs stabilize |
|
||||
| 12 | Admin/configuration family | Phase inventory and connector primitives exist in the ledger | Apply the pattern to files, mail, policy, retention, packages, modules, API keys, settings | Core #225 and module children | Per-surface state/accessibility/consequence evidence | Parallel where independent of Campaign shared decisions |
|
||||
| 13 | Remaining direct routes | Routes are contributed; most are unreviewed | Per-module bounded audit and migration plan | New module issues derived from this inventory | Applicable definition-of-done gates | P2 after Campaign, not a bulk rewrite |
|
||||
| 14 | Manifest/runtime alignment | Several executable routes are absent from manifest metadata | Declared alignment or explicit validated exception | Core contract issue to create | Automated manifest/module route check and configured Docs verification | Discovery follow-up |
|
||||
|
||||
Workflow/user-story implementation is postponed. It is not on the critical path
|
||||
for this rollout matrix. Focused views can be specified, manually selected, and
|
||||
tested through core composition contracts; a later workflow step may become one
|
||||
activation source without changing the proven surface patterns.
|
||||
|
||||
## Inventory Maintenance
|
||||
|
||||
When a route, nav item, named UI capability, host section, or Campaign workspace
|
||||
surface changes:
|
||||
|
||||
1. Update the owner, evidence, task, archetype, and consequence here.
|
||||
2. Link the implementation issue and verification evidence.
|
||||
3. Keep "unreviewed" until state, permission/privacy, consequence/provenance,
|
||||
accessibility, responsive, theme, i18n, and applicable async behavior have
|
||||
been checked.
|
||||
4. Re-scan both `module.ts` and the backend manifest; do not infer one from the
|
||||
other.
|
||||
5. Recreate the inventory from a clean release lockfile before using it as
|
||||
release evidence.
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
# GovOPlaN Documentation
|
||||
|
||||
This directory contains cross-repository product, architecture, delivery, and
|
||||
project documentation. Start here instead of browsing every file.
|
||||
|
||||
## Read First
|
||||
|
||||
| Need | Source |
|
||||
| --- | --- |
|
||||
| Understand the platform in ten minutes | [Platform Core Ideas](strategy/PLATFORM_CORE_IDEAS.md) |
|
||||
| See the intended product sequence | [Roadmap](strategy/ROADMAP.md) |
|
||||
| Check the reconciled state and material gaps | [Strategy Status](strategy/STRATEGY_STATUS.md) |
|
||||
| Find active work, priority, or ownership | [Gitea issue workflow](project/GITEA_ISSUES.md) and Gitea issues |
|
||||
| Understand the selected end-to-end proofs | [Reference Journey Program](strategy/REFERENCE_JOURNEY_PROGRAM.md) |
|
||||
|
||||
The first three documents are the normal entry points. Detailed architecture,
|
||||
runbooks, evidence, and historical assessments support them; they are not
|
||||
parallel roadmaps.
|
||||
|
||||
## Strategy
|
||||
|
||||
| Document | Role |
|
||||
| --- | --- |
|
||||
| [Platform Core Ideas](strategy/PLATFORM_CORE_IDEAS.md) | Stable purpose, principles, planes, distinctions, and non-goals |
|
||||
| [Roadmap](strategy/ROADMAP.md) | Concise product outcomes, horizons, and current sequence |
|
||||
| [Strategy Status](strategy/STRATEGY_STATUS.md) | Only prose source for current cross-product status |
|
||||
| [Reference Journey Program](strategy/REFERENCE_JOURNEY_PROGRAM.md) | Acceptance journeys and their gates |
|
||||
| [Product Input Register](strategy/PRODUCT_INPUT_REGISTER.md) | Normalized ideas and user-story source material |
|
||||
| [System Administrator Lifecycle](strategy/SYSTEM_ADMINISTRATOR_LIFECYCLE_USER_STORY.md) | Installation and lifecycle outcome story |
|
||||
| [Detailed Connected-Platform Vision](strategy/reference/CONNECTED_GOVERNANCE_PLATFORM_ROADMAP.md) | Long-form stakeholder, configuration, and outcome catalogue |
|
||||
|
||||
## Architecture
|
||||
|
||||
| Topic | Canonical source |
|
||||
| --- | --- |
|
||||
| Institutional model and ownership | [Institutional Governance Target Architecture](architecture/INSTITUTIONAL_GOVERNANCE_TARGET_ARCHITECTURE.md) |
|
||||
| Product experience and technical boundaries | [Product Experience and Module Boundaries](architecture/PRODUCT_EXPERIENCE_AND_MODULE_BOUNDARIES.md) |
|
||||
| Shared interface and layout rules | [Interface Pattern Language](architecture/INTERFACE_PATTERN_LANGUAGE.md) |
|
||||
| Focused task views | [Views Architecture](architecture/VIEWS_ARCHITECTURE.md) |
|
||||
| Product areas and task-local tools | [Quick Access and Product Areas](architecture/QUICK_ACCESS_AND_PRODUCT_AREAS.md) |
|
||||
| Platform self-description and configuration | [Platform Control Plane](architecture/PLATFORM_CONTROL_PLANE.md) |
|
||||
| Data sources, definitions, and graph execution | [Datasource and Definition Graph Architecture](architecture/DATASOURCE_AND_DEFINITION_GRAPH_ARCHITECTURE.md) |
|
||||
| Federation between autonomous installations | [Federated GovOPlaN Architecture](architecture/FEDERATED_GOVOPLAN_ARCHITECTURE.md) |
|
||||
| Institutional digital twin | [Institutional Digital Twin](architecture/INSTITUTIONAL_DIGITAL_TWIN.md) |
|
||||
| Assisted and non-digital participation | [Assisted and Non-Digital Channels](architecture/ASSISTED_AND_NON_DIGITAL_CHANNELS.md) |
|
||||
|
||||
Module-specific architecture remains in the owning repository. In particular,
|
||||
information-governance adoption is in
|
||||
`govoplan-core/docs/INFORMATION_GOVERNANCE_ADOPTION.md`, and the eAkte model is
|
||||
in `govoplan-records/docs/EAKTE_ARCHITECTURE.md`.
|
||||
|
||||
## Operations
|
||||
|
||||
| Need | Source |
|
||||
| --- | --- |
|
||||
| Installation model and managed components | [Installation and Deployment Architecture](operations/INSTALLATION_AND_DEPLOYMENT_ARCHITECTURE.md) |
|
||||
| Supported operating modes | [Deployment Profiles](operations/DEPLOYMENT_PROFILES.md) |
|
||||
| Horizontal scaling and multi-host topology | [Scaling and Multi-Host Deployment](operations/SCALING_AND_MULTI_HOST_DEPLOYMENT.md) |
|
||||
| Local Kubernetes evidence target | [Kubernetes VM Test Lab](operations/KUBERNETES_TEST_LAB.md) |
|
||||
| Recovery guarantees and state machine | [Recovery and Rollback Guarantees](operations/RECOVERY_AND_ROLLBACK_GUARANTEES.md) |
|
||||
| Recovery-ledger rollout | [Recovery Ledger Adoption](operations/RECOVERY_LEDGER_ADOPTION.md) |
|
||||
| Backup evidence contract | [Backup and Restore Evidence](operations/BACKUP_AND_RESTORE_EVIDENCE.md) |
|
||||
| Target handoff and independent evidence | [Production Target Handoff](operations/PRODUCTION_TARGET_HANDOFF.md) |
|
||||
| Evidence collection and promotion | [Target Maturity Evidence Runbook](operations/TARGET_MATURITY_EVIDENCE_RUNBOOK.md) |
|
||||
| Package publication and consumption | [Package Registry Releases](operations/PACKAGE_REGISTRY_RELEASES.md) |
|
||||
| Release-console operation | [Release Console](operations/RELEASE_CONSOLE.md) |
|
||||
| Module compatibility and install behavior | [Module Contracts and Installs](operations/MODULE_CONTRACTS_AND_INSTALLS.md) |
|
||||
| Security-audit toolchain | [Security Audit](operations/SECURITY_AUDIT.md) |
|
||||
|
||||
## Project Reference
|
||||
|
||||
- [Repository Index](project/REPOSITORY_INDEX.md) is the human-readable module
|
||||
and repository directory; `../repositories.json` is authoritative for tools.
|
||||
- [Repository Structure](project/REPOSITORY_STRUCTURE.md) defines ownership of
|
||||
meta, module, deployment, and website content.
|
||||
- [Gitea Issues](project/GITEA_ISSUES.md) defines labels, templates, import, and
|
||||
state-update conventions.
|
||||
|
||||
## Evidence And Archive
|
||||
|
||||
Pinned evidence is retained under `evidence/`; completed reviews and migration
|
||||
inventories are under `archive/`. They explain or prove a dated state and must
|
||||
not be read as current product status.
|
||||
|
||||
- [Generated Campaign capability and infrastructure fit, 2026-07-22](evidence/snapshots/CAPABILITY_AND_INFRASTRUCTURE_FIT.generated.md)
|
||||
- [Supporting narrative for the 2026-07-22 assessment](evidence/snapshots/CAPABILITY_AND_INFRASTRUCTURE_FIT.md)
|
||||
- [Interface surface inventory, 2026-08-03](evidence/snapshots/INTERFACE_SURFACE_INVENTORY.md)
|
||||
- [Strategic review, 2026-08-05](archive/2026-08/STRATEGIC_REVIEW_2026-08-05.md)
|
||||
- [Meta repository scan, 2026-07-13](archive/2026-07/META_REPO_SCAN.md)
|
||||
- [Meta repository migration audit](archive/2026-07/META_REPOSITORY_MIGRATION_AUDIT.md)
|
||||
|
||||
The JSON files at the root of this directory are machine-readable schemas,
|
||||
evidence inputs, and project configuration. Their paths are intentionally
|
||||
stable because tools and published schema identifiers consume them; they are
|
||||
not additional reading-list entries.
|
||||
|
||||
Regenerate and verify the human fit report from its JSON input with:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/assessments/generate-capability-fit-report.py
|
||||
./.venv/bin/python tools/assessments/generate-capability-fit-report.py --check
|
||||
```
|
||||
|
||||
## Maintenance Rules
|
||||
|
||||
1. Gitea issues are the only live source for work state, priority, and owner.
|
||||
2. `strategy/STRATEGY_STATUS.md` is the only prose reconciliation of current
|
||||
portfolio state. Do not copy its volatile counts into durable documents.
|
||||
3. Durable documents state decisions, invariants, ownership, and acceptance
|
||||
gates. They link to Gitea for implementation detail.
|
||||
4. Dated evidence and archive documents retain their original composition and
|
||||
conclusion. Add a snapshot notice instead of silently modernizing them.
|
||||
5. Module-specific behavior and user/admin documentation stay in the owning
|
||||
repository. Meta documentation covers cross-module outcomes and contracts.
|
||||
6. Do not add another top-level Markdown file. Place new content in the
|
||||
appropriate directory and add it to this map only when it has a distinct
|
||||
canonical purpose.
|
||||
7. A new strategy document must replace, narrow, or become a reference for an
|
||||
existing source; it must not introduce a parallel roadmap.
|
||||
8. The Product Input Register preserves source ideas. Only a named journey,
|
||||
package, or Gitea issue turns an idea into implementation work.
|
||||
|
||||
After moving or adding documentation, run:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python -m unittest tests.test_documentation_structure
|
||||
```
|
||||
@@ -1,283 +0,0 @@
|
||||
# GovOPlaN Release Console
|
||||
|
||||
The release console is a local operator tool for planning and executing
|
||||
GovOPlaN releases. It belongs to the `govoplan` meta repository because it works
|
||||
across all local checkouts, release scripts, module manifests, migration audits,
|
||||
catalog files, Git state, and signing keys.
|
||||
|
||||
The current implementation has a read-only dashboard plus guarded local
|
||||
candidate/publish actions:
|
||||
|
||||
- inspect repositories from `repositories.json`
|
||||
- show dirty, ahead, behind, missing, no-HEAD, and tag state
|
||||
- show local package catalog and keyring state
|
||||
- optionally run release/dev migration audits
|
||||
- propose next actions without executing them
|
||||
- compare local catalog/keyring JSON with the published channel and public
|
||||
keyring when online checks are enabled
|
||||
- configure target versions per release unit in the web UI
|
||||
- select the repositories that should advance through checkboxes
|
||||
- generate dry-run selective release plans for independently versioned packages
|
||||
- create annotated source release tags for selected clean, version-aligned
|
||||
repositories and publish each branch/tag pair atomically
|
||||
- generate signed catalog candidates for the selected release units
|
||||
- preview/apply/push reviewed catalog candidates behind explicit confirmations
|
||||
|
||||
Start it from the meta repository:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/release/release-console.py
|
||||
```
|
||||
|
||||
The server binds to `127.0.0.1` by default and prints a URL containing a local
|
||||
API token. Open that URL in a browser on the same machine.
|
||||
|
||||
The web UI starts with a repository table. Each repository can be checked
|
||||
independently and assigned its own target version. Repositories without version
|
||||
metadata remain visible so they can be planned as initial releases. `Build Plan`
|
||||
shows the dry-run commands for the selected rows, and `Generate Candidate`
|
||||
creates a signed catalog candidate that advances only selected repositories that
|
||||
already have a catalog entry.
|
||||
|
||||
`Build Plan` also returns structured release-gate findings for each selected
|
||||
repository. The plan names the recommended next action and gives an explicit
|
||||
remediation for source-version, lockfile, Core WebUI composition, Git state, and
|
||||
worktree findings. A target version that has not yet been applied consistently
|
||||
to the selected source tree is therefore explained before the source-tag
|
||||
preflight rather than appearing only as an error after the operator tries to
|
||||
tag. `source_preflight_ready` means that the plan-visible source gates pass; the
|
||||
non-mutating `Preview Tag + Publish` remains mandatory for remote, manifest, and
|
||||
immutable-tag checks.
|
||||
|
||||
Dashboard collection is also fail closed. Unreadable Core version metadata or a
|
||||
malformed module contract is returned as a bounded `collection_errors` entry
|
||||
with a remediation, marks the dashboard blocked, and becomes a structured
|
||||
blocker in both full and selective release plans. The console does not silently
|
||||
omit a contract or turn these source errors into an HTTP 500.
|
||||
|
||||
The release-control area above the repository table is read-only and is meant
|
||||
to become the central release cockpit. It shows:
|
||||
|
||||
- local and published channel health
|
||||
- catalog/keyring drift
|
||||
- signature and trusted-key status
|
||||
- published module versions and refs
|
||||
- local checkout version drift against the catalog
|
||||
- catalog-declared interface compatibility
|
||||
|
||||
The target-version control can generate the next major, minor, or subversion
|
||||
from the current base version. Manual target input accepts explicit versions
|
||||
such as `0.2.0` or `0.2.0-alpha1`, but requires the first three version numbers
|
||||
to move forward.
|
||||
|
||||
Plain repository pushes are separate from catalog publication. `Preview Push`
|
||||
shows the selected repository push commands. `Push Selected` requires `PUSH` in
|
||||
the repository push confirmation field.
|
||||
|
||||
The source release panel closes the former gap between a release plan and a
|
||||
catalog candidate. `Preview Tag + Publish` is non-mutating. `Create Tags`
|
||||
requires `TAG`; `Publish Tags` requires `PUBLISH` and atomically pushes the
|
||||
selected branch and annotated tag. The gate requires an aligned target version,
|
||||
a clean named branch with a HEAD, and a checkout that is not behind. Existing
|
||||
local or remote tags must resolve to the selected HEAD and are never moved.
|
||||
The local and remote annotated tag objects must also be identical, not merely
|
||||
point at the same commit. Before any source tag is created, the console loads
|
||||
the cross-repository module registry. This release gate also rejects every
|
||||
user-facing workflow documentation topic that has no scope condition, or has
|
||||
an alternative condition without `required_scopes` or `any_scopes`.
|
||||
|
||||
The catalog workflow panel can also operate on the same selected rows:
|
||||
|
||||
- `Generate` creates a signed candidate below `runtime/release-candidates/`.
|
||||
- `Preview` validates a candidate and shows what would be copied into the
|
||||
website repository.
|
||||
- `Apply + Website Tag` requires `APPLY` in the confirmation field.
|
||||
- `Push Website Release` requires `PUSH` in the confirmation field.
|
||||
|
||||
Source release tags belong to Core or module repositories. Website catalog
|
||||
publication creates a separate catalog tag in the website repository; the UI
|
||||
names these independently to avoid confusing the two immutable references.
|
||||
|
||||
Candidate signing is fail-closed: every Core and module source ref in the
|
||||
complete post-update catalog must already have the requested annotated tag both
|
||||
locally and on the configured source remote. Both tags must be the same
|
||||
annotation object with version-aligned tagged package metadata. Selected tags
|
||||
must additionally resolve to the selected clean, version-aligned HEAD, and the
|
||||
signed `selected_units` record captures the peeled commit and tag-object
|
||||
identifiers. Create and publish the source tags before using `Generate`.
|
||||
|
||||
Catalog publication repeats that check for selected units and also verifies
|
||||
every Core and module source ref in the complete candidate, including entries
|
||||
preserved from the previous catalog. Historical refs need not be at the current
|
||||
worktree HEAD, but their local and remote annotated tags must match and their
|
||||
tagged installable package and module-manifest metadata must declare the catalog
|
||||
version. New selected releases additionally pass the stricter current-source
|
||||
alignment gate, including public runtime version declarations. A candidate
|
||||
cannot be applied or published while any referenced source tag is absent or
|
||||
inconsistent; the preview and API response list each repository and missing or
|
||||
invalid ref so the operator can repair the exact source releases first.
|
||||
|
||||
Read-only provenance checks derive a same-host HTTPS Git URL from conventional
|
||||
SSH remotes when possible, with a non-interactive check of the configured remote
|
||||
as fallback. Source tag creation and atomic publication always use the explicit
|
||||
configured Git remote.
|
||||
|
||||
The default signing key is
|
||||
`$HOME/.config/govoplan/release-keys/release-key-1.pem` when no signing key is
|
||||
entered in the UI.
|
||||
|
||||
Generate a selective release plan from the terminal:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/release/release-plan.py \
|
||||
--repo govoplan-files \
|
||||
--target-version 0.1.9 \
|
||||
--channel stable \
|
||||
--online
|
||||
```
|
||||
|
||||
`--online` compares the local catalog/keyring with the published channel and
|
||||
published keyring. Use `--remote-tags` only when the plan also needs to check
|
||||
Git remotes for tag existence; that can be slower across the full repository
|
||||
set.
|
||||
|
||||
Build a signed selective catalog candidate:
|
||||
|
||||
```sh
|
||||
KEY_DIR="$HOME/.config/govoplan/release-keys"
|
||||
|
||||
./.venv/bin/python tools/release/release-catalog.py selective \
|
||||
--repo-version govoplan-files=0.1.9 \
|
||||
--channel stable \
|
||||
--catalog-signing-key "release-key-1=$KEY_DIR/release-key-1.pem"
|
||||
```
|
||||
|
||||
This writes candidate catalog/keyring files below `runtime/release-candidates/`,
|
||||
generates the browsable module directory below `modules/`, validates the signed
|
||||
candidate with the module installer validator, and reports whether the candidate
|
||||
catalog/keyring match the currently published channel. It does not publish to
|
||||
the website repository.
|
||||
|
||||
Regenerate the browsable module directory from an existing catalog/keyring:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/release/release-catalog.py module-directory \
|
||||
--catalog ../addideas-govoplan-website/public/catalogs/v1/channels/stable.json \
|
||||
--keyring ../addideas-govoplan-website/public/catalogs/v1/keyring.json \
|
||||
--output-dir runtime/module-directory-preview \
|
||||
--channel stable
|
||||
```
|
||||
|
||||
Preview publication of a reviewed candidate:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/release/release-catalog.py publish-candidate \
|
||||
--candidate-dir runtime/release-candidates/stable-YYYYMMDD-HHMMSS \
|
||||
--channel stable
|
||||
```
|
||||
|
||||
Apply the reviewed candidate into the website repository without pushing:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/release/release-catalog.py publish-candidate \
|
||||
--candidate-dir runtime/release-candidates/stable-YYYYMMDD-HHMMSS \
|
||||
--channel stable \
|
||||
--apply \
|
||||
--commit \
|
||||
--tag
|
||||
```
|
||||
|
||||
Push is a separate explicit flag:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/release/release-catalog.py publish-candidate \
|
||||
--candidate-dir runtime/release-candidates/stable-YYYYMMDD-HHMMSS \
|
||||
--channel stable \
|
||||
--apply \
|
||||
--commit \
|
||||
--tag \
|
||||
--push
|
||||
```
|
||||
|
||||
Published channels are expected below the public catalog base URL:
|
||||
|
||||
- `https://govoplan.add-ideas.de/catalogs/v1/channels/stable.json`
|
||||
- `https://govoplan.add-ideas.de/catalogs/v1/keyring.json`
|
||||
|
||||
The console treats channels as release artifacts. A package can advance without
|
||||
forcing every repository to the same tag, but channel publication must preserve
|
||||
the unchanged package versions, validate interface compatibility, sign the
|
||||
updated catalog, and keep the published keyring healthy.
|
||||
|
||||
When a selected module exposes a WebUI package, its requested version must also
|
||||
match Core's `webui/package.release.json` input and the resolved
|
||||
`package-lock.release.json` entry. The source-tag preflight, selective plan, and
|
||||
catalog-candidate writer all enforce this composition boundary. Pins for modules
|
||||
that are not part of the selective release remain unchanged.
|
||||
|
||||
Release integration also enforces repository and composition version alignment
|
||||
and generates a CycloneDX SBOM from the resolved Python environment and the
|
||||
release WebUI lockfile. Catalog publication should attach that immutable SBOM
|
||||
and its digest to the corresponding Core/composition release.
|
||||
|
||||
Addresses and Notifications have current WebUI source contributions but are
|
||||
not part of the pinned v0.1.8 WebUI release composition because their v0.1.8
|
||||
tags predate those packages. They re-enter the release composition only through
|
||||
new, immutable module tags whose backend, manifest, frontend, and lock metadata
|
||||
pass the alignment gate.
|
||||
|
||||
Candidate publication verifies signatures against the already published
|
||||
keyring, not against keys supplied only by the candidate. A changed keyring
|
||||
must have its canonical SHA-256 embedded in the signed catalog. The public
|
||||
module-directory files are regenerated from that verified catalog and keyring
|
||||
at publication time; candidate-supplied directory files are never copied as
|
||||
authoritative provenance.
|
||||
|
||||
## Published Module Directory
|
||||
|
||||
The target release repository is an online, browsable module directory. The
|
||||
catalog remains the machine-readable channel entry point, but the published
|
||||
space should also expose a tree that operators and installations can inspect:
|
||||
|
||||
- `/catalogs/v1/channels/<channel>.json` describes the active channel state.
|
||||
- `/catalogs/v1/keyring.json` publishes trusted release signing keys.
|
||||
- `/catalogs/v1/modules/<module>/<version>/manifest.json` describes one
|
||||
published module version, including repository refs, package refs, contracts,
|
||||
compatibility windows, signatures, and available artifacts.
|
||||
- `/catalogs/v1/modules/<module>/index.json` lists available versions for
|
||||
one module.
|
||||
- `/catalogs/v1/modules/index.json` lists all published modules.
|
||||
|
||||
Gitea tags/releases remain the source release anchors. The public GovOPlaN
|
||||
catalog directory becomes the installation-facing release repository that points
|
||||
to those anchors and carries structured compatibility information.
|
||||
|
||||
Selective catalog candidates now include this directory tree. Publishing a
|
||||
candidate copies the channel catalog, keyring, and `modules/` tree into the
|
||||
website repository together.
|
||||
|
||||
Selective catalog generation can add a module that is not yet represented in
|
||||
the source channel. Initial entries are synthesized from the selected
|
||||
repository's `[project]` metadata, `govoplan.modules` entry point, and runtime
|
||||
`ModuleManifest`; there is no separate hand-maintained initial-entry list. The
|
||||
release gate requires the entry point to resolve inside the selected checkout,
|
||||
exact package/manifest/frontend version agreement, verified source-tag
|
||||
provenance, a non-conflicting module id, and complete required dependency and
|
||||
interface closure in the resulting candidate. Classification beyond the
|
||||
manifest-derived `official` tag remains an explicit later catalog curation
|
||||
step rather than an inferred business/service label.
|
||||
|
||||
## Direction
|
||||
|
||||
The console should grow in slices:
|
||||
|
||||
1. Read-only dashboard and next-action suggestions.
|
||||
2. Release plan builder that writes explicit JSON plans.
|
||||
3. Dry-run executor that shows exact commands and expected file changes.
|
||||
4. Apply executor for commit, tag, push, artifact build, signing, and catalog
|
||||
publication.
|
||||
5. Compatibility planner for manifest contracts and version ranges.
|
||||
6. Install/test workflow that validates a release from tags or catalog entries.
|
||||
|
||||
All mutation must stay explicit: plan first, dry run second, apply only after a
|
||||
clear confirmation.
|
||||
@@ -1,89 +0,0 @@
|
||||
# GovOPlaN Repository Index
|
||||
|
||||
Generated from `repositories.json`. Use that JSON file as the machine-readable source of truth; this page is the human-readable link index.
|
||||
|
||||
## System
|
||||
|
||||
| Repository | Subtype | Local path | Gitea |
|
||||
| --- | --- | --- | --- |
|
||||
| `govoplan` | `meta` | `../govoplan` | [govoplan](https://git.add-ideas.de/add-ideas/govoplan) |
|
||||
| `govoplan-core` | `kernel` | `../govoplan-core` | [govoplan-core](https://git.add-ideas.de/add-ideas/govoplan-core) |
|
||||
|
||||
## Module
|
||||
|
||||
| Repository | Subtype | Local path | Gitea |
|
||||
| --- | --- | --- | --- |
|
||||
| `govoplan-access` | `platform` | `../govoplan-access` | [govoplan-access](https://git.add-ideas.de/add-ideas/govoplan-access) |
|
||||
| `govoplan-addresses` | `domain` | `../govoplan-addresses` | [govoplan-addresses](https://git.add-ideas.de/add-ideas/govoplan-addresses) |
|
||||
| `govoplan-admin` | `platform` | `../govoplan-admin` | [govoplan-admin](https://git.add-ideas.de/add-ideas/govoplan-admin) |
|
||||
| `govoplan-appointments` | `domain` | `../govoplan-appointments` | [govoplan-appointments](https://git.add-ideas.de/add-ideas/govoplan-appointments) |
|
||||
| `govoplan-approvals` | `domain` | `../govoplan-approvals` | [govoplan-approvals](https://git.add-ideas.de/add-ideas/govoplan-approvals) |
|
||||
| `govoplan-assets` | `domain` | `../govoplan-assets` | [govoplan-assets](https://git.add-ideas.de/add-ideas/govoplan-assets) |
|
||||
| `govoplan-audit` | `platform` | `../govoplan-audit` | [govoplan-audit](https://git.add-ideas.de/add-ideas/govoplan-audit) |
|
||||
| `govoplan-booking` | `domain` | `../govoplan-booking` | [govoplan-booking](https://git.add-ideas.de/add-ideas/govoplan-booking) |
|
||||
| `govoplan-calendar` | `domain` | `../govoplan-calendar` | [govoplan-calendar](https://git.add-ideas.de/add-ideas/govoplan-calendar) |
|
||||
| `govoplan-campaign` | `domain` | `../govoplan-campaign` | [govoplan-campaign](https://git.add-ideas.de/add-ideas/govoplan-campaign) |
|
||||
| `govoplan-cases` | `domain` | `../govoplan-cases` | [govoplan-cases](https://git.add-ideas.de/add-ideas/govoplan-cases) |
|
||||
| `govoplan-certificates` | `domain` | `../govoplan-certificates` | [govoplan-certificates](https://git.add-ideas.de/add-ideas/govoplan-certificates) |
|
||||
| `govoplan-committee` | `domain` | `../govoplan-committee` | [govoplan-committee](https://git.add-ideas.de/add-ideas/govoplan-committee) |
|
||||
| `govoplan-consultation` | `domain` | `../govoplan-consultation` | [govoplan-consultation](https://git.add-ideas.de/add-ideas/govoplan-consultation) |
|
||||
| `govoplan-contracts` | `domain` | `../govoplan-contracts` | [govoplan-contracts](https://git.add-ideas.de/add-ideas/govoplan-contracts) |
|
||||
| `govoplan-dashboard` | `platform` | `../govoplan-dashboard` | [govoplan-dashboard](https://git.add-ideas.de/add-ideas/govoplan-dashboard) |
|
||||
| `govoplan-dms` | `domain` | `../govoplan-dms` | [govoplan-dms](https://git.add-ideas.de/add-ideas/govoplan-dms) |
|
||||
| `govoplan-dist-lists` | `domain` | `../govoplan-dist-lists` | [govoplan-dist-lists](https://git.add-ideas.de/add-ideas/govoplan-dist-lists) |
|
||||
| `govoplan-docs` | `platform` | `../govoplan-docs` | [govoplan-docs](https://git.add-ideas.de/add-ideas/govoplan-docs) |
|
||||
| `govoplan-erp` | `domain` | `../govoplan-erp` | [govoplan-erp](https://git.add-ideas.de/add-ideas/govoplan-erp) |
|
||||
| `govoplan-evaluation` | `domain` | `../govoplan-evaluation` | [govoplan-evaluation](https://git.add-ideas.de/add-ideas/govoplan-evaluation) |
|
||||
| `govoplan-facilities` | `domain` | `../govoplan-facilities` | [govoplan-facilities](https://git.add-ideas.de/add-ideas/govoplan-facilities) |
|
||||
| `govoplan-files` | `domain` | `../govoplan-files` | [govoplan-files](https://git.add-ideas.de/add-ideas/govoplan-files) |
|
||||
| `govoplan-forms` | `domain` | `../govoplan-forms` | [govoplan-forms](https://git.add-ideas.de/add-ideas/govoplan-forms) |
|
||||
| `govoplan-forms-runtime` | `platform` | `../govoplan-forms-runtime` | [govoplan-forms-runtime](https://git.add-ideas.de/add-ideas/govoplan-forms-runtime) |
|
||||
| `govoplan-grants` | `domain` | `../govoplan-grants` | [govoplan-grants](https://git.add-ideas.de/add-ideas/govoplan-grants) |
|
||||
| `govoplan-helpdesk` | `domain` | `../govoplan-helpdesk` | [govoplan-helpdesk](https://git.add-ideas.de/add-ideas/govoplan-helpdesk) |
|
||||
| `govoplan-identity` | `platform` | `../govoplan-identity` | [govoplan-identity](https://git.add-ideas.de/add-ideas/govoplan-identity) |
|
||||
| `govoplan-identity-trust` | `platform` | `../govoplan-identity-trust` | [govoplan-identity-trust](https://git.add-ideas.de/add-ideas/govoplan-identity-trust) |
|
||||
| `govoplan-idm` | `platform` | `../govoplan-idm` | [govoplan-idm](https://git.add-ideas.de/add-ideas/govoplan-idm) |
|
||||
| `govoplan-inspections` | `domain` | `../govoplan-inspections` | [govoplan-inspections](https://git.add-ideas.de/add-ideas/govoplan-inspections) |
|
||||
| `govoplan-issue-reporting` | `domain` | `../govoplan-issue-reporting` | [govoplan-issue-reporting](https://git.add-ideas.de/add-ideas/govoplan-issue-reporting) |
|
||||
| `govoplan-learning` | `domain` | `../govoplan-learning` | [govoplan-learning](https://git.add-ideas.de/add-ideas/govoplan-learning) |
|
||||
| `govoplan-ledger` | `domain` | `../govoplan-ledger` | [govoplan-ledger](https://git.add-ideas.de/add-ideas/govoplan-ledger) |
|
||||
| `govoplan-mail` | `domain` | `../govoplan-mail` | [govoplan-mail](https://git.add-ideas.de/add-ideas/govoplan-mail) |
|
||||
| `govoplan-notifications` | `platform` | `../govoplan-notifications` | [govoplan-notifications](https://git.add-ideas.de/add-ideas/govoplan-notifications) |
|
||||
| `govoplan-ops` | `platform` | `../govoplan-ops` | [govoplan-ops](https://git.add-ideas.de/add-ideas/govoplan-ops) |
|
||||
| `govoplan-organizations` | `platform` | `../govoplan-organizations` | [govoplan-organizations](https://git.add-ideas.de/add-ideas/govoplan-organizations) |
|
||||
| `govoplan-payments` | `domain` | `../govoplan-payments` | [govoplan-payments](https://git.add-ideas.de/add-ideas/govoplan-payments) |
|
||||
| `govoplan-permits` | `domain` | `../govoplan-permits` | [govoplan-permits](https://git.add-ideas.de/add-ideas/govoplan-permits) |
|
||||
| `govoplan-policy` | `platform` | `../govoplan-policy` | [govoplan-policy](https://git.add-ideas.de/add-ideas/govoplan-policy) |
|
||||
| `govoplan-poll` | `domain` | `../govoplan-poll` | [govoplan-poll](https://git.add-ideas.de/add-ideas/govoplan-poll) |
|
||||
| `govoplan-portal` | `domain` | `../govoplan-portal` | [govoplan-portal](https://git.add-ideas.de/add-ideas/govoplan-portal) |
|
||||
| `govoplan-postbox` | `domain` | `../govoplan-postbox` | [govoplan-postbox](https://git.add-ideas.de/add-ideas/govoplan-postbox) |
|
||||
| `govoplan-procurement` | `domain` | `../govoplan-procurement` | [govoplan-procurement](https://git.add-ideas.de/add-ideas/govoplan-procurement) |
|
||||
| `govoplan-records` | `domain` | `../govoplan-records` | [govoplan-records](https://git.add-ideas.de/add-ideas/govoplan-records) |
|
||||
| `govoplan-reporting` | `domain` | `../govoplan-reporting` | [govoplan-reporting](https://git.add-ideas.de/add-ideas/govoplan-reporting) |
|
||||
| `govoplan-resources` | `domain` | `../govoplan-resources` | [govoplan-resources](https://git.add-ideas.de/add-ideas/govoplan-resources) |
|
||||
| `govoplan-risk-compliance` | `domain` | `../govoplan-risk-compliance` | [govoplan-risk-compliance](https://git.add-ideas.de/add-ideas/govoplan-risk-compliance) |
|
||||
| `govoplan-scheduling` | `domain` | `../govoplan-scheduling` | [govoplan-scheduling](https://git.add-ideas.de/add-ideas/govoplan-scheduling) |
|
||||
| `govoplan-search` | `platform` | `../govoplan-search` | [govoplan-search](https://git.add-ideas.de/add-ideas/govoplan-search) |
|
||||
| `govoplan-tasks` | `domain` | `../govoplan-tasks` | [govoplan-tasks](https://git.add-ideas.de/add-ideas/govoplan-tasks) |
|
||||
| `govoplan-templates` | `domain` | `../govoplan-templates` | [govoplan-templates](https://git.add-ideas.de/add-ideas/govoplan-templates) |
|
||||
| `govoplan-tenancy` | `platform` | `../govoplan-tenancy` | [govoplan-tenancy](https://git.add-ideas.de/add-ideas/govoplan-tenancy) |
|
||||
| `govoplan-transparency` | `domain` | `../govoplan-transparency` | [govoplan-transparency](https://git.add-ideas.de/add-ideas/govoplan-transparency) |
|
||||
| `govoplan-workflow` | `platform` | `../govoplan-workflow` | [govoplan-workflow](https://git.add-ideas.de/add-ideas/govoplan-workflow) |
|
||||
|
||||
## Connector
|
||||
|
||||
| Repository | Subtype | Local path | Gitea |
|
||||
| --- | --- | --- | --- |
|
||||
| `govoplan-connectors` | `connector-hub` | `../govoplan-connectors` | [govoplan-connectors](https://git.add-ideas.de/add-ideas/govoplan-connectors) |
|
||||
| `govoplan-fit-connect` | `standard` | `../govoplan-fit-connect` | [govoplan-fit-connect](https://git.add-ideas.de/add-ideas/govoplan-fit-connect) |
|
||||
| `govoplan-rest` | `protocol` | `../govoplan-rest` | [govoplan-rest](https://git.add-ideas.de/add-ideas/govoplan-rest) |
|
||||
| `govoplan-soap` | `protocol` | `../govoplan-soap` | [govoplan-soap](https://git.add-ideas.de/add-ideas/govoplan-soap) |
|
||||
| `govoplan-xoev` | `standard` | `../govoplan-xoev` | [govoplan-xoev](https://git.add-ideas.de/add-ideas/govoplan-xoev) |
|
||||
| `govoplan-xrechnung` | `standard` | `../govoplan-xrechnung` | [govoplan-xrechnung](https://git.add-ideas.de/add-ideas/govoplan-xrechnung) |
|
||||
| `govoplan-xta-osci` | `standard` | `../govoplan-xta-osci` | [govoplan-xta-osci](https://git.add-ideas.de/add-ideas/govoplan-xta-osci) |
|
||||
|
||||
## Website
|
||||
|
||||
| Repository | Subtype | Local path | Gitea |
|
||||
| --- | --- | --- | --- |
|
||||
| `addideas-govoplan-website` | `public-site` | `../addideas-govoplan-website` | [addideas-govoplan-website](https://git.add-ideas.de/add-ideas/addideas-govoplan-website) |
|
||||
@@ -0,0 +1,177 @@
|
||||
# Assisted and Non-Digital Channels
|
||||
|
||||
## Purpose
|
||||
|
||||
GovOPlaN must support people who cannot or do not use a self-service portal.
|
||||
Telephone, paper, in-person service, authorized representation, mobile staff,
|
||||
interpreters, and temporary offline work are not exceptional side systems.
|
||||
They are governed channels into the same service, case, workflow, record, and
|
||||
decision.
|
||||
|
||||
The goal is equivalent institutional treatment, not forced channel identity.
|
||||
The system preserves which channel was used and which evidence is available
|
||||
without giving digitally confident users stronger substantive rights.
|
||||
|
||||
The first end-to-end journey is tracked in
|
||||
[GovOPlaN #42](https://git.add-ideas.de/GovOPlaN/govoplan/issues/42).
|
||||
|
||||
## Actor Model
|
||||
|
||||
Every assisted interaction distinguishes:
|
||||
|
||||
- the affected person or organization;
|
||||
- the real staff member or external helper entering information;
|
||||
- the represented party and representation basis;
|
||||
- an interpreter, witness, guardian, or support person where relevant;
|
||||
- the responsible institutional function;
|
||||
- the channel and location;
|
||||
- the person who reviewed or confirmed the captured information.
|
||||
|
||||
"Entered by" is not "declared by". "Declared by" is not "verified by".
|
||||
Authentication assurance, representation authority, and evidence quality are
|
||||
separate fields.
|
||||
|
||||
## Channel-Neutral Intake Contract
|
||||
|
||||
All channels create the same versioned service/form submission contract with
|
||||
additional provenance:
|
||||
|
||||
- service, form, schema, language, and accessibility version;
|
||||
- valid and recorded time;
|
||||
- channel (`portal`, `counter`, `telephone`, `paper`, `email`, `mobile`,
|
||||
`representative`, `offline_import`, or configured extension);
|
||||
- affected and represented parties;
|
||||
- capture actor and responsible function;
|
||||
- consent, notice, purpose, legal basis, and information source;
|
||||
- field-level source and confidence where staff transcribed or inferred data;
|
||||
- attachments, scans, originals, signatures, recordings, and attestations as
|
||||
governed evidence references;
|
||||
- read-back/confirmation result and correction path;
|
||||
- receipt and chosen return channels;
|
||||
- duplicate/matching assessment and any manual resolution.
|
||||
|
||||
Forms Runtime owns the submission lifecycle. Parties owns procedural capacity
|
||||
and representation. Identity/Addresses own subject and contact references.
|
||||
Cases owns the matter. Records owns filing and retention. Audit preserves the
|
||||
action/effect evidence.
|
||||
|
||||
## Assisted Session
|
||||
|
||||
An assisted session is a resumable work item, not a privileged bypass. It:
|
||||
|
||||
1. selects service, language, channel, affected party, and represented capacity;
|
||||
2. shows the staff member only fields and evidence relevant to the service;
|
||||
3. explains why sensitive data is requested and what evidence quality is
|
||||
required;
|
||||
4. records source per value when information comes from speech, paper, an
|
||||
existing register, or staff observation;
|
||||
5. validates and previews consequences before submission;
|
||||
6. supports read-back, correction, confirmation, and a second-person check
|
||||
where policy requires it;
|
||||
7. generates an accessible receipt through the requested channel;
|
||||
8. creates follow-up tasks when original documents, signatures, translation,
|
||||
or verification remain outstanding.
|
||||
|
||||
The first executable slice is implemented in Forms Runtime for authenticated
|
||||
assisted sessions. Administrators enable an exact published Form revision;
|
||||
operators then record channel, party and representation references, authority,
|
||||
purpose, notice, responsible function, language, accessibility needs, and
|
||||
field-level source/confidence provenance. Read-back outcomes are append-only and
|
||||
payload-bound. A draft correction changes the Form revision and invalidates the
|
||||
prior confirmation for submission. The resident-parking-permit fixture proves
|
||||
resume and submission enforcement; browser accessibility and target archive
|
||||
evidence remain acceptance work.
|
||||
|
||||
The helper's normal account and represented function remain in the audit
|
||||
chain. Assistance never grants access to unrelated records about the person.
|
||||
|
||||
## Paper And Scanning
|
||||
|
||||
- Register receipt before scanning so custody and deadlines do not depend on
|
||||
successful OCR.
|
||||
- Store the original scan or external archive reference with digest, pages,
|
||||
capture device/provider, time, operator, and quality assessment.
|
||||
- Treat OCR and extracted fields as derived data with confidence and source
|
||||
coordinates. A person confirms consequential values.
|
||||
- Support separation, ordering, missing-page, duplicate, malware, and
|
||||
readability review.
|
||||
- File the resulting document and submission into the appropriate eAkte;
|
||||
retain or return the physical original according to policy.
|
||||
- Produce cover sheets, barcodes, and return instructions through Templates,
|
||||
not a separate print domain.
|
||||
|
||||
## Telephone And In-Person Handling
|
||||
|
||||
- Show a scripted but adaptable interview from the same Form definition.
|
||||
- Record how identity and representation were checked; do not equate caller ID
|
||||
with identity proof.
|
||||
- Require explicit confirmation of consequential declarations and capture the
|
||||
method (read-back, signed summary, one-time code, witness, later letter).
|
||||
- Record call audio only when a lawful, declared profile permits it; an
|
||||
interaction note is the default.
|
||||
- Make interrupted sessions resumable without exposing prior answers to an
|
||||
unauthorized caller or visitor.
|
||||
|
||||
## Offline And Mobile Work
|
||||
|
||||
Offline packages are encrypted, device-bound, time-limited, purpose-limited,
|
||||
and contain only the required forms/reference data. Synchronization uses
|
||||
idempotent intents and exposes conflicts rather than last-write-wins. Device
|
||||
loss, expiry, revocation, duplicate submission, clock drift, and outcome
|
||||
unknown have explicit recovery paths.
|
||||
|
||||
## Outbound Non-Digital Delivery
|
||||
|
||||
Campaign and Postbox model one delivery intent with channel choices and policy:
|
||||
|
||||
- portal/postbox delivery;
|
||||
- email;
|
||||
- print and postal fulfillment through a managed provider or local handoff;
|
||||
- in-person collection;
|
||||
- telephone notification followed by durable confirmation;
|
||||
- accessible or language-specific variants.
|
||||
|
||||
Distribution preferences are purpose- and service-specific, effective-dated,
|
||||
and may be overridden only by a documented legal or urgent-delivery rule. A
|
||||
fallback occurs only before a channel has accepted the effect unless policy
|
||||
explicitly authorizes duplicate delivery. Receipts distinguish creation,
|
||||
provider acceptance, dispatch, delivery, return, and acknowledgement.
|
||||
|
||||
## Accessibility And Equality
|
||||
|
||||
- The person can request language, easy-language, large-print, screen-reader,
|
||||
sign-language, relay, interpreter, or representative support without those
|
||||
preferences becoming a general-purpose profile visible everywhere.
|
||||
- Staff interfaces support keyboard-only capture, clear focus, error summary,
|
||||
read-back, and printable/offline alternatives.
|
||||
- Channel choice and need for assistance must not be used as an adverse risk
|
||||
signal.
|
||||
- Reports compare completion, wait, correction, abandonment, and outcome by
|
||||
channel only under a declared equality/service-quality purpose and with
|
||||
privacy thresholds.
|
||||
|
||||
## Security And Abuse Controls
|
||||
|
||||
- purpose-aware field access and session timeout;
|
||||
- current authority checks for every read and effect;
|
||||
- dual control for high-risk identity, payment, address, or representation
|
||||
changes;
|
||||
- immutable source/attestation evidence and correction history;
|
||||
- rate and anomaly controls that do not silently reject a person;
|
||||
- explicit safe handling of domestic-abuse, protected-address, witness, or
|
||||
sealed-record cases;
|
||||
- no secret answers or full documents in ordinary operational logs.
|
||||
|
||||
## First Reference Journey
|
||||
|
||||
Implement the permit-to-payment/service-to-decision journey through three
|
||||
equivalent starts:
|
||||
|
||||
1. self-service portal submission;
|
||||
2. staff-assisted counter/telephone submission;
|
||||
3. paper receipt, scan, extraction, confirmation, and filing.
|
||||
|
||||
All three must create the same Case and Workflow contract, preserve different
|
||||
provenance, support correction, produce a receipt, file an eAkte, reach the same
|
||||
decision rules, and prove accessibility, privacy, recovery, and channel
|
||||
fallback in browser and operator tests.
|
||||
@@ -0,0 +1,90 @@
|
||||
# Datasource And Definition Graph Architecture
|
||||
|
||||
## Two-Layer Data Boundary
|
||||
|
||||
GovOPlaN separates governed data identity from external acquisition:
|
||||
|
||||
| Layer | Owner | Responsibilities |
|
||||
| --- | --- | --- |
|
||||
| Datasource layer | `govoplan-datasources` | Governed data/register catalogue, tenant visibility, live/cached/static mode, source authority, staging, immutable materializations, frozen states, schema, quality/freshness policy, institutional provenance, dependencies, and bounded reads |
|
||||
| Connector layer | `govoplan-connectors` and protocol/provider modules | External protocols, endpoints, connection profiles, credentials, discovery, provider maturity/authority support, health, source-side filtering, query pushdown, and effect reconciliation |
|
||||
|
||||
Connectors publish versioned datasource origins. Datasources registers those
|
||||
origins and presents one stable capability to Dataflow, Workflow, Reporting,
|
||||
Risk Compliance, and other consumers. Consumers must not import connector
|
||||
implementations or retain credentials.
|
||||
|
||||
The initial provider path is:
|
||||
|
||||
1. Connectors imports a bounded JSON/CSV snapshot and exposes it as an origin.
|
||||
2. Datasources registers it as live or cached, or accepts a direct static upload
|
||||
through staging.
|
||||
3. Cached refreshes and static promotions append immutable materializations.
|
||||
4. Any datasource may expose a frozen state for reproducible execution evidence.
|
||||
5. Dataflow stores an opaque datasource reference, state policy, and expected
|
||||
fingerprint.
|
||||
6. A pinned Dataflow run may publish a complete bounded result as a new
|
||||
immutable materialization through an idempotent Datasources capability.
|
||||
|
||||
Database, REST/HTTP, LDAP/directory, managed file, watched-directory, feed, and
|
||||
stream providers fit behind the same origin contract. Provider-specific
|
||||
configuration remains in Connectors.
|
||||
|
||||
## Shared Definition Graph
|
||||
|
||||
Core owns domain-neutral graph primitives:
|
||||
|
||||
- nodes, typed ports, edges, and configuration field descriptors;
|
||||
- node libraries and category labels;
|
||||
- graph size, connectivity, cycle, and node-count constraints;
|
||||
- shared backend validation and frontend connection checks.
|
||||
|
||||
Domain modules own their semantics:
|
||||
|
||||
- Dataflow provides load, combine, filter, transform, and output nodes. Its
|
||||
graph is acyclic and has one output.
|
||||
- Workflow Engine provides trigger, activity, review, decision, wait,
|
||||
module-action, Dataflow, and outcome semantics. It permits governed loops and
|
||||
has exactly one trigger plus one or more outcomes. The optional Workflow
|
||||
module supplies the editor over the same native graph/BPMN language.
|
||||
|
||||
This division permits a shared editor shell without making Workflow a special
|
||||
kind of Dataflow or leaking either module into Core.
|
||||
|
||||
## Current Implementation
|
||||
|
||||
- Core graph and datasource contracts are versioned at `0.1.0`.
|
||||
- Workflow Engine owns tenant-isolated definitions, immutable revisions,
|
||||
activation pinning, module-contributed versioned baselines, runtime instances,
|
||||
governed action/effect execution, retries, waits, and reconciliation. The
|
||||
optional Workflow module exposes the reusable native BPMN graph editor.
|
||||
- Datasources exposes catalogue, origins, staging, promotion, preview,
|
||||
materialization history, refresh, freeze, retirement, and producer
|
||||
publication APIs.
|
||||
- Datasources WebUI exposes all current lifecycle views.
|
||||
- Connectors adapts existing tabular snapshots to datasource origins.
|
||||
- Dataflow consumes only Datasources catalogue/lifecycle capabilities and can
|
||||
request current, live, or latest-frozen state.
|
||||
- Dataflow exposes typed graph/IR, registry-driven validation/execution/SQL
|
||||
compilation, expressions and reusable subflows, a pinned run-lifecycle
|
||||
capability, production worker boundary, and Run/Publish surface. Runs record
|
||||
lineage and intermediate artifacts and publish only complete bounded results.
|
||||
- The focused composition check proves Connector origin -> Datasource ->
|
||||
pinned Dataflow run -> frozen published materialization, including replay.
|
||||
|
||||
## Next Slices
|
||||
|
||||
1. Add the provider declaration and source-authority binding used consistently
|
||||
by Connectors, Datasources, configuration packages, Ops, and Docs.
|
||||
2. Add typed datasource owner/steward, legal/purpose, quality/freshness,
|
||||
classification, correction, service/process, and downstream dependency
|
||||
metadata under
|
||||
[Datasources #6](https://git.add-ideas.de/GovOPlaN/govoplan-datasources/issues/6).
|
||||
3. Add SQL database and governed REST origin providers with credential-envelope
|
||||
references and bounded pushdown.
|
||||
4. Add managed-file and directory origins.
|
||||
5. Complete datasource quality rules, schema compatibility policy, retention, and
|
||||
promotion approvals.
|
||||
6. Complete scheduled/event/API/chained Dataflow trigger governance, reusable
|
||||
template inheritance, Reporting publication, human reconciliation transforms,
|
||||
and target resource/recovery evidence for large runs.
|
||||
@@ -0,0 +1,155 @@
|
||||
# Federated GovOPlaN Architecture
|
||||
|
||||
## Purpose
|
||||
|
||||
Federation lets autonomous GovOPlaN installations exchange data,
|
||||
configuration, work, messages, records, and evidence without sharing a database
|
||||
or surrendering local policy. It is institution-to-institution cooperation,
|
||||
not multi-tenancy across an untrusted network.
|
||||
|
||||
The first implementation should prove a bounded exchange between two
|
||||
installations. A new federation module is not justified until the shared
|
||||
protocol has at least two independent consumers. Core owns neutral envelopes
|
||||
and trust contracts; Connectors owns transport providers; domain modules own
|
||||
the objects and effects they exchange.
|
||||
|
||||
Implementation is tracked in
|
||||
[GovOPlaN #41](https://git.add-ideas.de/GovOPlaN/govoplan/issues/41).
|
||||
|
||||
## Invariants
|
||||
|
||||
1. Every installation remains authoritative for its tenants, identities,
|
||||
policies, keys, records, and local mappings.
|
||||
2. A remote identity or permission never becomes a local authorization claim.
|
||||
3. Every exchange declares purpose, legal/organizational basis, classification,
|
||||
minimization, retention expectation, and permitted onward use.
|
||||
4. Every object reference identifies origin instance, owner tenant, object type,
|
||||
object ID, exact revision, and source-authority mode.
|
||||
5. Payloads and receipts are signed; sensitive transports use mutually
|
||||
authenticated encrypted channels.
|
||||
6. Acceptance, rejection, outcome unknown, retry, revocation, correction, and
|
||||
reconciliation are durable states.
|
||||
7. Local policy may reject or narrow a remote request. It cannot silently claim
|
||||
to have accepted an effect that did not occur.
|
||||
8. Federation works asynchronously and can exchange signed offline bundles
|
||||
where continuous connectivity is unavailable.
|
||||
|
||||
## Trust Domains
|
||||
|
||||
An instance publishes a signed, versioned federation descriptor containing:
|
||||
|
||||
- stable instance and operator identity;
|
||||
- supported protocol and schema versions;
|
||||
- signing and transport key identifiers with rotation history;
|
||||
- accepted object and exchange profiles;
|
||||
- endpoint locations and size/rate limits;
|
||||
- support, incident, revocation, and data-protection contacts;
|
||||
- evidence and conformance references.
|
||||
|
||||
Pairing is a two-sided administrative workflow. Each side verifies the other,
|
||||
maps the remote institution to a local trusted-party record, selects permitted
|
||||
profiles and purposes, sets policy ceilings, and records approvals. Trust is
|
||||
directional and profile-specific; trusting signed Postbox delivery does not
|
||||
automatically permit case transfer or configuration import.
|
||||
|
||||
## Exchange Envelope
|
||||
|
||||
Every request, response, receipt, correction, and revocation uses one neutral
|
||||
envelope with:
|
||||
|
||||
- message ID, correlation ID, causation ID, creation and expiry;
|
||||
- origin and destination instance/institution/tenant references;
|
||||
- real actor and represented institutional capacity where disclosure is
|
||||
permitted;
|
||||
- exchange profile and semantic schema version;
|
||||
- exact domain object references and content digests;
|
||||
- purpose, legal basis, classification, data categories, retention expectation,
|
||||
onward-transfer constraint, and subject notice status;
|
||||
- requested action and idempotency key;
|
||||
- encryption recipients and signature chain;
|
||||
- attachment/object manifests rather than unbounded embedded blobs;
|
||||
- previous-envelope references for correction, replacement, or revocation.
|
||||
|
||||
The envelope is evidence, not a universal domain object. Each owner validates
|
||||
and imports or links its own payload.
|
||||
|
||||
## Exchange Profiles
|
||||
|
||||
| Profile | First owners | Behavior |
|
||||
| --- | --- | --- |
|
||||
| Postbox delivery | Postbox, Campaign, Notifications | Address or derive a remote function-bound postbox, obtain acceptance receipt, and track acknowledgement where permitted |
|
||||
| Case handoff | Cases, Parties, Services, Workflow Engine | Offer exact context and evidence; destination accepts into a new local case and returns the mapping |
|
||||
| Record transfer | Records, Files, DMS, Audit | Transfer or offer a signed record package with file-plan, metadata, content digests, holds, and disposition constraints |
|
||||
| Decision/evidence reference | Decisions, Committee, Audit | Publish a protected exact outcome or verifiable reference without transferring unrelated case content |
|
||||
| Data product publication | Datasources, Dataflow, Reporting | Publish immutable governed materializations with schema, quality, freshness, lineage, and use constraints |
|
||||
| Configuration package | Core, Policy, Views, Workflow, Forms, Templates | Exchange signed definitions; destination assesses compatibility, maps values, derives locally, and never imports secrets |
|
||||
| Search discovery | Search and domain providers | Return permission-filtered metadata or a handoff link; never expose raw remote indexes as local authority |
|
||||
|
||||
## State Machine
|
||||
|
||||
```text
|
||||
draft -> authorized -> queued -> transmitted -> received
|
||||
| |
|
||||
v v
|
||||
outcome_unknown rejected
|
||||
|
|
||||
received -> validating -> accepted -> applied -> acknowledged
|
||||
| | |
|
||||
v v v
|
||||
rejected accepted_ reconciled
|
||||
pending
|
||||
```
|
||||
|
||||
Acceptance means the destination durably owns the received intent. It does not
|
||||
mean the requested domain effect completed. Receipts distinguish transport,
|
||||
validation, acceptance, application, and human acknowledgement.
|
||||
|
||||
## Conflict And Autonomy
|
||||
|
||||
- Incoming native objects become local references, mirrors, or newly owned
|
||||
objects according to the profile. They do not overwrite local authority by
|
||||
ID coincidence.
|
||||
- Local mappings are effective-dated and auditable.
|
||||
- Corrections create a linked revision. They do not erase what the destination
|
||||
previously observed.
|
||||
- Revocation is a request and evidence event; the destination applies its own
|
||||
legal and retention rules.
|
||||
- Configuration imports use assessment and derivation. A remote package cannot
|
||||
weaken local policy or install code implicitly.
|
||||
- A disconnected partner remains a visible pending/failed state; work can be
|
||||
rerouted through an approved alternative channel.
|
||||
|
||||
## Security And Privacy
|
||||
|
||||
- Use mTLS for paired online transports and signed envelopes for end-to-end
|
||||
origin evidence.
|
||||
- Encrypt payload objects for the destination, with key rotation and outcome-
|
||||
unknown recovery; transport encryption alone is insufficient for queued
|
||||
bundles.
|
||||
- Do not put bearer credentials, local permission scopes, or reusable secrets
|
||||
in an exchange.
|
||||
- Rate-limit and size-bound discovery and transfer; quarantine unknown schemas
|
||||
and active content.
|
||||
- Evaluate current local authorization at every effect even when the envelope
|
||||
describes historical authority.
|
||||
- Log metadata separately from protected content so operators can reconcile
|
||||
without broad content access.
|
||||
- Subject access, correction, restriction, legal hold, and deletion requests
|
||||
become federated workflows with local decisions and receipts, not remote
|
||||
direct database operations.
|
||||
|
||||
## First Reference Proof
|
||||
|
||||
1. Pair two disposable installations with independent tenants, keys, and
|
||||
policies.
|
||||
2. Exchange signed descriptors and approve only the Postbox delivery profile.
|
||||
3. Deliver one Campaign message to a remote function-bound Postbox.
|
||||
4. Prove replay safety, rejection, timeout/outcome unknown, retry,
|
||||
acknowledgement, correction, key rotation, and revoked trust.
|
||||
5. Export the complete evidence bundle and restore both sides from backup.
|
||||
6. Add configuration-package exchange only after the delivery proof passes.
|
||||
|
||||
The result is a provider-neutral federation contract. A future dedicated
|
||||
module becomes appropriate only when pairing, trust administration, exchange
|
||||
queues, and evidence have a lifecycle independent of Connectors and the first
|
||||
domain owner.
|
||||
@@ -0,0 +1,149 @@
|
||||
# Institutional Digital Twin
|
||||
|
||||
## Definition
|
||||
|
||||
The institutional digital twin is a governed, time-aware projection of how an
|
||||
institution is constituted and operates. It connects structure, authority,
|
||||
services, work, information, technology, obligations, controls, evidence, and
|
||||
outcomes without becoming a second source of truth.
|
||||
|
||||
The twin is not one editable graph database and not an employee-surveillance
|
||||
system. Domain modules and external systems keep ownership. The twin stores or
|
||||
materializes exact references, declared relationships, provenance, confidence,
|
||||
and projection versions. Changes flow through owner actions.
|
||||
|
||||
Implementation is tracked in
|
||||
[GovOPlaN #43](https://git.add-ideas.de/GovOPlaN/govoplan/issues/43).
|
||||
|
||||
## Questions It Should Answer
|
||||
|
||||
- Which unit and function is responsible for a service, decision, record,
|
||||
system, dataset, control, or risk at a given valid and recorded time?
|
||||
- Which mandates and policies permit or constrain an action?
|
||||
- Which processes, providers, staff capacities, data sources, and records are
|
||||
required to deliver a service?
|
||||
- What is affected if a system, provider, organizational unit, role, package,
|
||||
or legal rule changes?
|
||||
- Where are responsibilities missing, conflicting, expired, or concentrated?
|
||||
- Which controls are evidenced, stale, failed, or dependent on an unverified
|
||||
assertion?
|
||||
- How do actual process traces differ from defined workflows?
|
||||
- Which public outcomes can be explained from protected internal evidence?
|
||||
|
||||
## Projection Planes
|
||||
|
||||
| Plane | Meaning |
|
||||
| --- | --- |
|
||||
| Current | Valid now, reconstructed from owner projections and current provider state |
|
||||
| Historical | Valid at and recorded by selected instants, with present-day security enforced |
|
||||
| Planned | Approved or proposed future structures, services, policies, projects, and package changes |
|
||||
| Observed | Events, process traces, service measures, incidents, effects, and evidence actually recorded |
|
||||
| Scenario | Non-authoritative simulation of a proposed change and its estimated consequences |
|
||||
|
||||
The UI must label these planes unambiguously. Scenario output never becomes an
|
||||
institutional fact until an authorized owner action accepts it.
|
||||
|
||||
## Canonical Graph
|
||||
|
||||
Nodes are stable institutional references, including institution, tenant,
|
||||
unit, function, assignment, mandate, jurisdiction, service, case, party, task,
|
||||
workflow, approval, decision, record, file, message, appointment, dataset,
|
||||
report, provider, system, control, risk, project, asset, and configuration
|
||||
package.
|
||||
|
||||
Edges have:
|
||||
|
||||
- owner and source authority;
|
||||
- relationship type and direction;
|
||||
- valid-from/valid-to and recorded/superseded times;
|
||||
- exact source revision and evidence digest;
|
||||
- institution/tenant boundary;
|
||||
- purpose and visibility classification;
|
||||
- confidence and derivation method for inferred relationships;
|
||||
- correction and replacement references.
|
||||
|
||||
Inferred edges are never displayed as owner assertions. They remain
|
||||
explainable analytical products with source lineage.
|
||||
|
||||
## Ownership And Implementation
|
||||
|
||||
- Core owns neutral institutional references, temporal context, provider
|
||||
registration, and graph projection contracts.
|
||||
- Domain modules publish bounded nodes and edges through provider interfaces.
|
||||
- Search indexes discoverable identities and links.
|
||||
- Reporting materializes governed analytical projections.
|
||||
- Dataflow computes derived relationships, quality checks, and scenarios.
|
||||
- Policy evaluates visibility, purpose, retention, and allowed scenario/action
|
||||
transitions.
|
||||
- Audit supplies observed events and evidence references.
|
||||
- Projects supplies planned change and benefit relationships.
|
||||
- Views renders role- and task-focused twin perspectives.
|
||||
- Workflow Engine coordinates accepted changes but does not edit owner tables.
|
||||
|
||||
No new digital-twin module is required for the first slice. A dedicated owner
|
||||
is justified later if persisted scenario models, graph revisions, and
|
||||
cross-domain projection lifecycle become independent product objects.
|
||||
|
||||
## Beyond The Current Platform
|
||||
|
||||
### Continuous assurance
|
||||
|
||||
Controls become versioned assertions with evidence requirements, evaluation
|
||||
frequency, responsible function, exception workflow, and freshness. Dataflow
|
||||
and provider checks evaluate them continuously; Policy decides whether a stale
|
||||
or failed control advises, requires review, or blocks an effect.
|
||||
|
||||
### Process mining and conformance
|
||||
|
||||
Governed event histories can derive actual paths, wait times, rework, and
|
||||
exceptions. Comparison to Workflow definitions should improve procedures, not
|
||||
rank individuals. Access to personal or small-cohort detail is purpose-limited
|
||||
and separately governed.
|
||||
|
||||
### Change-impact simulation
|
||||
|
||||
A proposed organizational, provider, policy, or package change can be assessed
|
||||
against dependencies, mandates, open work, records, controls, capacity, and
|
||||
recovery plans before activation. Results identify uncertainty rather than
|
||||
inventing precision.
|
||||
|
||||
### Federated institutional models
|
||||
|
||||
Installations can exchange signed public or partner-specific subsets of their
|
||||
service, mandate, provider, and evidence graph. Every side maps the references
|
||||
locally and retains autonomy. Federation does not create one supranational
|
||||
master graph.
|
||||
|
||||
### Accountable assistance
|
||||
|
||||
Assistance may summarize context, identify missing evidence, draft a decision
|
||||
or workflow, propose mappings, and explain policy. Every output records model,
|
||||
inputs, constraints, uncertainty, human review, and accepted edits. Assistance
|
||||
does not become the acting authority.
|
||||
|
||||
### Public evidence chains
|
||||
|
||||
Transparency packages can publish a minimized chain from rule and aggregate
|
||||
facts to decision and observed outcome, with digests proving relation to
|
||||
protected evidence. Public verification does not require disclosure of the
|
||||
underlying personal data.
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Do not infer competence, misconduct, intent, or personal performance from
|
||||
graph proximity or incomplete events.
|
||||
- Do not centralize protected content merely to make graph queries easier.
|
||||
- Do not use historical authorization to expose data now prohibited.
|
||||
- Do not let a scenario engine write domain state directly.
|
||||
- Do not hide source authority, freshness, uncertainty, or missing evidence.
|
||||
- Do not retain analytical detail longer than the declared purpose requires.
|
||||
|
||||
## Delivery Slices
|
||||
|
||||
1. Publish exact institutional reference/edge providers for the service-to-
|
||||
decision and monthly-data journeys.
|
||||
2. Build a current/historical dependency explorer with source and access
|
||||
explanations.
|
||||
3. Add planned Project/package changes and bounded impact reports.
|
||||
4. Add control evidence/freshness and process conformance for one journey.
|
||||
5. Prove a minimized federated projection and a public evidence package.
|
||||
@@ -0,0 +1,616 @@
|
||||
# Institutional Governance Target Architecture
|
||||
|
||||
## Status and sources
|
||||
|
||||
This document is the accepted architectural reconciliation of two product
|
||||
concepts prepared outside the repositories:
|
||||
|
||||
- `govoplan_concept_dev.md`
|
||||
- `software_big_picture.md`
|
||||
|
||||
The source concepts describe GovOPlaN as an operational governance platform for
|
||||
public institutions. This document is the canonical repository version of that
|
||||
durable architectural direction. Its implementation table records the accepted
|
||||
2026-08-01 baseline; it is not a rolling status report. Current reconciliation
|
||||
lives in [Strategy Status](../strategy/STRATEGY_STATUS.md), and Gitea issues remain the
|
||||
source of truth for delivery state.
|
||||
|
||||
Read this together with:
|
||||
|
||||
- [Connected Governance Platform Roadmap](../strategy/reference/CONNECTED_GOVERNANCE_PLATFORM_ROADMAP.md)
|
||||
- [Platform Core Ideas](../strategy/PLATFORM_CORE_IDEAS.md)
|
||||
- [Strategy Status](../strategy/STRATEGY_STATUS.md)
|
||||
- [Reference Journey Program](../strategy/REFERENCE_JOURNEY_PROGRAM.md)
|
||||
- [Module Contracts and Install Boundaries](../operations/MODULE_CONTRACTS_AND_INSTALLS.md)
|
||||
- [Datasource and Definition Graph Architecture](DATASOURCE_AND_DEFINITION_GRAPH_ARCHITECTURE.md)
|
||||
- [Generated Capability and Infrastructure Fit](../evidence/snapshots/CAPABILITY_AND_INFRASTRUCTURE_FIT.generated.md)
|
||||
- [Core Module Architecture](../../../govoplan-core/docs/MODULE_ARCHITECTURE.md)
|
||||
- [Core External References and Integration Maturity](../../../govoplan-core/docs/EXTERNAL_REFERENCES_AND_INTEGRATION_MATURITY.md)
|
||||
- [Core Action, Effect, and Automation Layer](../../../govoplan-core/docs/ACTION_EFFECT_AUTOMATION_LAYER.md)
|
||||
|
||||
## Decision
|
||||
|
||||
GovOPlaN is a configurable **institutional governance and operations layer** for
|
||||
public institutions. It should model the institution, coordinate its work,
|
||||
connect its specialist systems, and preserve why and under whose authority an
|
||||
action occurred.
|
||||
|
||||
GovOPlaN is not intended to become one universal ERP, DMS, groupware suite,
|
||||
workflow editor, or specialist procedure. It should own the governance concepts
|
||||
that must remain understandable across those systems and support native,
|
||||
external, mirrored, synchronized, overlay, and link-only operation explicitly.
|
||||
|
||||
This changes product emphasis, not the modular architecture:
|
||||
|
||||
1. The current kernel and optional-module model remains.
|
||||
2. Existing domain owners keep their data and behavior.
|
||||
3. Cross-module semantics become explicit, versioned contracts.
|
||||
4. Successful compositions become product and sector packages, not forks or
|
||||
monolithic replacement applications.
|
||||
5. Repository creation follows a proof threshold; a noun in the information
|
||||
model does not automatically require a module.
|
||||
|
||||
## What recent work already supersedes
|
||||
|
||||
The source concepts predate several implemented foundations. These items are
|
||||
accepted as the current baseline and must not be reopened as greenfield work.
|
||||
|
||||
| Concept requirement | Reconciled current state |
|
||||
| --- | --- |
|
||||
| Slim kernel plus installable modules | Implemented through entry-point discovery, `ModuleManifest`, migrations, capabilities, interfaces, WebUI contributions, and permutation checks. |
|
||||
| Versioned cross-module contracts | Implemented through named interface ranges, capability protocols, static workspace graph checks, activation validation, and release checks. |
|
||||
| Separate headless workflow runtime and editor | Implemented as `govoplan-workflow-engine` and optional `govoplan-workflow`. Module-owned workflow baselines are versioned and reconciled without replacing local overrides. |
|
||||
| Provider-neutral external references | Implemented in Core with stable external identity and cumulative integration maturity from discovery through replacement. |
|
||||
| Governed asynchronous effects | Implemented foundations include the action/effect contract, transactional platform event outbox, module outboxes, idempotency, outcome-unknown states, reconciliation, and worker health. Coverage still varies by provider. |
|
||||
| Acting identity, function assignment, mandate, and ownership recovery | Implemented foundations span Identity, Organizations, IDM, Access, Mandates, generic ownership transfer/recovery, and audit provenance. Effective competence now resolves through a tenant-bound Mandate capability. |
|
||||
| Governed data foundations | Connectors, Datasources, Dataflow, Reporting, and Search now exist. Datasources already provides live/cached/static modes, staging, immutable materializations, and publication contracts. |
|
||||
| Task-focused projections and configured documentation | Views, view-surface declarations, configurable dashboards, and manifest-driven user/admin documentation exist. Rollout and content depth remain incremental. |
|
||||
| Encryption as an optional capability | Core defines provider-neutral contracts; `govoplan-identity-trust` persists public device keys, key epochs and assurance evidence; and `govoplan-encryption` persists opaque vault/key lifecycle, versioned protection envelopes, quorum recovery authorization, outcome-unknown reconciliation, and disable preflight. A bundled local AES-256-GCM server-envelope provider now stores wrapped key material and supports Files/Postbox protection, rotation, revocation, destruction, rewrap, tamper detection, and fail-closed restore behavior. It is explicitly neither E2EE nor a certified KMS/HSM. |
|
||||
| Search without mandatory OpenSearch | PostgreSQL-backed, permission-aware search and module provider contracts exist; OpenSearch remains an optional adapter. |
|
||||
| Scale-out and recovery architecture | Stateless API/worker, shared database/object storage, event delivery, deployment, and recovery contracts are documented and partly exercised. Production profiles and drills remain active work. |
|
||||
|
||||
The institutional semantics, provider declaration gate, and first product
|
||||
compositions described here are now implemented. Subsequent work is
|
||||
**product depth and stronger maturity evidence**, not another runtime rewrite
|
||||
or an unimplemented architecture boundary.
|
||||
|
||||
## Accepted implementation baseline (2026-08-01)
|
||||
|
||||
This section is retained as the dated baseline against which the architecture
|
||||
decision was accepted. Later implementation must be reconciled in
|
||||
`STRATEGY_STATUS.md` rather than editing individual rows here into a competing
|
||||
status report.
|
||||
|
||||
The architecture contract is implemented as a bounded, executable vertical
|
||||
slice. The portfolio declarations and provider governance gates apply to the
|
||||
whole workspace, while the four semantic domains whose repository thresholds
|
||||
were proven now have independent persistent owners:
|
||||
|
||||
| Area | Implemented state | Remaining rollout |
|
||||
| --- | --- | --- |
|
||||
| Module portfolio metadata | Core validates versioned architecture layer/kind, maturity evidence, known limits, ownership boundaries, authority modes, reference packages, target-tested providers, and migration/upgrade/recovery/security/operations documentation. | Complete for the source manifests in the 2026-08-01 snapshot. Focused and release checks enforce `--require-architecture`; current portfolio counts belong in `STRATEGY_STATUS.md`. |
|
||||
| External providers | Core validates provider objects/field groups, operations, integration maturity, source authority, bounded reads, freshness/health, idempotency, conflicts, outcome-unknown handling, evidence, correction, reconciliation, outage, classification, purpose, retention, and secret handling. Addresses/CardDAV, Files remote storage, Mail SMTP/IMAP, Calendar CalDAV/ICS/Graph/EWS, and Connectors tabular/sanctions providers declare the contract and tenant-bounded secret-free runtime state. | Registry validation rejects any declared external provider without a sanitized state provider. Future adapters must cross the same gate before activation. |
|
||||
| Institutional context | Core provides versioned temporal, actor/representation, institution/unit/function/task/mandate/jurisdiction/service/case/party/work-item/workflow/approval/decision/record, legal-basis, evidence, information-governance, external-source, presentation, and geographic references. Events, automation actions, audit records, and the transactional Audit outbox preserve the envelope. | Owning modules must progressively require the relevant subset for consequential operations. |
|
||||
| Semantic provider contracts | Provider-neutral DTOs and protocols cover Mandate resolution, versioned Service definitions, procedure Parties/representation, and formal Decisions. `govoplan-mandates`, `govoplan-services`, `govoplan-parties`, and `govoplan-decisions` now persist immutable revisions behind those contracts with tenant isolation, bounded reads, replay safety, OCC, migrations, uninstall guards, permissions, APIs, capability documentation, and recovery documentation. | The owners are deliberately headless. Procedure-specific UI remains with consuming modules. |
|
||||
| Formal-outcome proof | Committee persists bodies, meetings, agenda items, minutes, lifecycle events, and an optional protected local Decision projection. Voting separately owns immutable ballot definitions, frozen electorates, recorded casting/replacement, deterministic tally, certification, challenge, annulment, and provider-backed assurance profiles. Committee consumes `voting.ballots` and retains only deliberation linkage and verified aggregate outcome. A bundled `local_confidential` reference provider encrypts server-readable casts outside native ballot rows and exposes only receipts plus aggregate evidence to Committee. | Native recorded ballots remain reconstructable, and the reference confidential provider is neither secret nor certified: its server can decrypt casts while tallying. Secret/electronic-ballot protocol selection, custody, legal acceptance, independent review, and target evidence remain explicit product decisions. |
|
||||
| Service-to-case proof | Services owns the persistent exact definitions consumed by Portal discovery and Cases intake. Forms owns immutable multi-page/conditional/localized schemas, accessibility assessment and package fragments. Forms Runtime owns definition-aware drafts, validation, submission receipts, status/evidence history, and durable native Case/Workflow handoffs with intent-before-effect and outcome-unknown reconciliation. Portal delegates URL, Case, Form, or Workflow launch to an installed owner while retaining exact Service/Form provenance. | Anonymous intake and concrete attachment/signature providers remain product depth. Portal and Runtime fail closed and explain any absent launcher, target capability, or provider prerequisite. |
|
||||
| Generic approvals and process execution | Approvals persists exact-subject chains, delegation, separation of duties, quorum, signatures-as-evidence, escalation, OCC, and replay-safe decisions. Campaign proves an exact-version delivery gate. Workflow Engine owns immutable definitions/instances plus API, schedule, event and parent triggers, durable timer/event waits, scale-out claims, current-authority rechecks, and idempotent starts independently of the optional editor. | Policy-authored Approval template selection, concrete signature providers, cron adapters, and broader BPMN execution profiles are product/provider depth on explicit contracts. |
|
||||
| Device trust and content protection | Identity Trust separates public device keys, epochs, assurance and key-access decisions from login and Access. Encryption separates resource ownership from opaque provider key custody, versioned envelopes, migration evidence, quorum recovery authorization and uninstall proof. Its local server-envelope provider and Files/Postbox adapters prove ciphertext persistence, integrity, rotation/rewrap and fail-closed key loss without leaking plaintext keys across the capability boundary. | Reviewed KMS/HSM/client providers, more owner adapters, target backup/restore/key-loss drills, and E2EE interoperability/certification remain required before stronger deployment claims. |
|
||||
| Procedure-party proof | Parties persists effective procedure roles, frozen contact snapshots, and representation powers. Existing powers cannot disappear or be silently rewritten; explicit OCC-guarded revocation is required. Cases resolves the provider capability and excludes expired/revoked authority from downstream delivery. | Procedure modules still decide which contextual fields and actions to present. |
|
||||
| Integrated institutional journey | The executable `product.service-to-decision` fixture uses real SQL-backed Services, Cases, Parties, Mandates, Committee, and Decisions providers. It carries one exact Service version through persisted Case intake, representation and frozen delivery authority, effective Mandate resolution, a body/meeting/agendum/vote/minute sequence, a persisted formal Decision, confirmed Postbox effect, Audit/record evidence, remedy/review, and protected reconstruction. A second executable path proves Portal to exact Form revision, persisted submission, and idempotent replay. | This is architecture and composition evidence. Signed, release-bound target accessibility, privacy, security, operator, delivery-provider, and recovery-drill evidence is still required before the package may claim `reference_ready`. |
|
||||
| Governed data catalogue | Datasources stores typed governance metadata, exposes bounded tenant-scoped filters and update APIs/UI, carries governance through staging, and snapshots it into immutable materializations. Reporting now persists immutable dataset, semantic-model, report, quality-plan, saved-view, and schedule revisions; executes typed semantic queries with quality gates, access checks, replay, pivoting, export/import assessment, and provenance; and exposes the governed analytical WebUI. | Rich dependency/impact traversal, additional expression functions, and policy-specific field visibility can grow on the established contracts without moving connector, transformation, or source ownership. |
|
||||
| Portfolio and change governance | Projects now persists tenant-safe, immutable portfolio/project/milestone revisions with OCC, replay, lifecycle rules, restricted memberships, Search ACL indexing, outcomes, benefits, dependencies, capacity assumptions, change impact, and institutional references. Its WebUI exposes the planning catalogue and core planning fields. | Advanced planning structures already accepted by the API can receive deeper specialized editors without creating a second Policy, Reporting, Resources, or Goals owner. |
|
||||
| Product/package governance | Signed configuration packages distinguish reference, product, sector, deployment, and integration classes; preserve parent/evidence provenance; prevent derived packages from loosening constraints; and preflight provider authority, maturity, exact binding, health, freshness, and recovery expectations. Executable product manifests now exist for governed communication and governed data/assurance and are checked in the module matrix. | Both artifacts deliberately remain product-class until target, accessibility, privacy, security, operations, and recovery evidence justifies reference readiness. |
|
||||
| Projection and release | Platform metadata, signed module catalogs, release synthesis, Ops, and role-aware Docs retain and display architecture/provider declarations. Module-owned state providers add bounded configured/active, authority, health, freshness, conflict, recovery, and observation state; ordinary-user Docs omits binding detail. Static checks validate evidence paths, and the WebUI build verifies consuming types. | Runtime-state adoption and broader portfolio presentation follow truthful provider declaration rollout. |
|
||||
|
||||
The implementation deliberately keeps shared reference contracts in Core and
|
||||
domain tables in their owners. It does not claim unsupported release maturity:
|
||||
the four extracted owners and package remain `vertical_slice`/`product` until
|
||||
target evidence supports a stronger claim. Gitea remains authoritative for
|
||||
feature depth beyond this architecture contract.
|
||||
|
||||
## Target capability layers
|
||||
|
||||
The layers describe ownership and dependency direction. They are not navigation
|
||||
groups and do not imply that every installation exposes every module.
|
||||
|
||||
| Layer | Responsibility | Current owners and declared directions |
|
||||
| --- | --- | --- |
|
||||
| 0. Runtime and meta | Composition, release, migrations, shared contracts, operations, deployment | Core, meta repository, Admin, Ops |
|
||||
| 1. Institutional foundation | Institution, tenant, identity, organization, function, authority, access, trust | Tenancy, Identity, Organizations, IDM, Access, Identity Trust, Encryption, Mandates |
|
||||
| 2. Governance and accountability | Policy, audit, risk, control, explainability, configured projection | Policy, Audit, Risk Compliance, Docs, Views, Search, Decisions |
|
||||
| 3. Human work and procedure | Intake, cases, tasks, approvals, process execution and editing | Services, Forms, Forms Runtime, Cases, Parties, Tasks, Approvals, Workflow Engine, Workflow, Tickets |
|
||||
| 4. Communication and participation | Delivery, participation, scheduling, channels, consultation | Portal, Postbox, Notifications, Mail, Campaign, Calendar, Scheduling, Poll, Appointments, Booking, Consultation, Committee, Addresses, Distribution Lists |
|
||||
| 5. Content, records, and evidence | Managed content, templates, records, knowledge, disclosure | Files, Templates, DMS, Records, Wiki, Transparency, Certificates |
|
||||
| 6. Data, reporting, and integration | Source access, staging, transformation, search, analytics, protocols | Connectors, Datasources, Dataflow, Reporting, Dashboard, REST, SOAP, XOE/V, XTA/OSCI, FIT-Connect, XRechnung, ERP adapters |
|
||||
| 7. Domain capabilities | Reusable public-sector subject matter | Projects, Procurement, Contracts, Grants, Resources, Assets, Facilities, Learning, Payments, Ledger, Permits, Inspections, Evaluation, Helpdesk |
|
||||
| 8. Product and sector packages | Versioned compositions, terminology, forms, processes, controls, reports, integration profiles | Signed configuration packages and reference packages; not runtime modules by default |
|
||||
|
||||
## Canonical institutional semantics
|
||||
|
||||
The connected model must keep these concepts distinct even where one UI
|
||||
combines them.
|
||||
|
||||
| Concept | Canonical answer | Owner or direction |
|
||||
| --- | --- | --- |
|
||||
| Institution and tenant | In which governed installation and tenant does work occur? | Tenancy and Organizations |
|
||||
| Organization and unit | Where is responsibility situated? | Organizations |
|
||||
| Function | Which named organizational responsibility can an incumbent hold? | Organizations |
|
||||
| Identity and account | Who is the person or machine, and through which account do they act? | Identity and Access |
|
||||
| Function assignment | Who holds or represents a function, for which interval and source? | IDM |
|
||||
| Role and permission | What application behavior may the acting principal perform? | Access, constrained by Policy |
|
||||
| Mandate and jurisdiction | Why is an institution, unit, or function competent to act on this subject, territory, population, or interval? | Mandates |
|
||||
| Service | What governed promise can an institution offer, to whom, under which prerequisites, evidence, channel, deadline, and responsibility? | Services; Portal presents it |
|
||||
| Case | Which concrete administrative matter is being handled? | Cases |
|
||||
| Party | In what procedural capacity does a person or organization participate, and who may represent or receive for it? | Parties; Identity/Organizations remain the subject owners |
|
||||
| Work item | What must a responsible actor do next? | Tasks and domain modules |
|
||||
| Workflow | How is work coordinated, including waits, human hand-offs, and governed actions? | Workflow Engine; Workflow is the optional editor |
|
||||
| Approval | Has a proposed action passed a configured review or separation-of-duties gate? | Approvals |
|
||||
| Decision | What formal institutional outcome was reached, by which competent authority, on which facts, rules, evidence, reasoning, and review path? | Decisions |
|
||||
| Evidence and record | What proves the input, state, action, effect, correction, and retained institutional memory? | Domain owner, Files/DMS/Records, and Audit |
|
||||
|
||||
### Extracted semantic modules
|
||||
|
||||
Four horizontal concepts passed the repository proof threshold. Their Core
|
||||
DTOs and provider protocols remain neutral; their persistent data, lifecycle,
|
||||
security, APIs, migrations, and recovery behavior now live in independent
|
||||
repositories.
|
||||
|
||||
#### Mandates
|
||||
|
||||
Mandates should own public or internal tasks, jurisdiction, responsibility,
|
||||
decision/signature authority, legal or organizational basis, and effective
|
||||
history. Organizations continues to own structures and functions; IDM owns
|
||||
incumbency; Access owns permissions; Policy owns constraints.
|
||||
|
||||
`govoplan-mandates` answers: *Was this function competent to act for this case
|
||||
at the relevant time, and on what basis?* Its resolver evaluates effective
|
||||
time, task, authority, unit, function, jurisdiction, subject, conflicts, legal
|
||||
basis, and evidence deterministically. Missing or ambiguous authority fails
|
||||
closed.
|
||||
|
||||
#### Services
|
||||
|
||||
Services should own versioned service definitions: audience, prerequisites,
|
||||
legal basis, evidence, fees, deadlines, channels, responsible unit/function,
|
||||
jurisdiction, forms, case/workflow/result bindings, remedies, service levels,
|
||||
and publication status. Portal presents and starts services but should not own
|
||||
their institutional definition.
|
||||
|
||||
`govoplan-services` now owns those exact versioned definitions. Portal is the
|
||||
first presentation consumer and Cases freezes the selected revision into its
|
||||
intake context. Availability is an independent capability so publication does
|
||||
not imply that all runtime prerequisites are satisfied.
|
||||
|
||||
#### Parties
|
||||
|
||||
Parties should own procedure-local roles and relationships: applicant,
|
||||
respondent, beneficiary, representative, joint applicant, delivery recipient,
|
||||
power or authority to represent, and permitted/preferred channels for the
|
||||
matter. Identity answers who the subject is; Organizations answers which
|
||||
institutional unit it is; Addresses owns contact points; Parties answers how
|
||||
the subject participates here.
|
||||
|
||||
`govoplan-parties` owns the shared effective-dated lifecycle. Cases retains a
|
||||
bounded compatibility projection only when the module is absent; that fallback
|
||||
contains no representation lifecycle and cannot silently become a second
|
||||
authority source.
|
||||
|
||||
#### Decisions
|
||||
|
||||
Decisions should own formal outcomes: subject, type, competent authority,
|
||||
facts, evidence, applicable rule versions, reasoning, operative result,
|
||||
conditions, effect, delivery/publication, remedy/review, correction, revocation,
|
||||
and links to observed effects. Approvals own review gates; Poll owns response
|
||||
collection; Committee owns deliberation, meetings, and votes; Workflow owns
|
||||
coordination.
|
||||
|
||||
`govoplan-decisions` owns the persistent lifecycle and protected reconstruction
|
||||
surface. Committee supplies deliberation context and records through the
|
||||
provider capability. Consumers retain exact Decision references without
|
||||
gaining table access.
|
||||
|
||||
## Source authority and integration maturity
|
||||
|
||||
Two independent dimensions must be recorded. They must not be collapsed into a
|
||||
single `sync` flag.
|
||||
|
||||
### Source-authority mode
|
||||
|
||||
| Mode | Meaning |
|
||||
| --- | --- |
|
||||
| `native_authoritative` | GovOPlaN owns the authoritative object and lifecycle. |
|
||||
| `external_authoritative` | The external system owns the object; GovOPlaN reads or acts through it. |
|
||||
| `external_mirror` | The external system is authoritative and GovOPlaN keeps a governed local projection or immutable snapshots. |
|
||||
| `governed_sync` | Both sides may change supported fields under explicit conflict and reconciliation rules. |
|
||||
| `governance_overlay` | GovOPlaN owns policy, responsibility, evidence, or coordination around an externally executed object. |
|
||||
| `linked_reference` | GovOPlaN keeps only a stable link and minimal display/provenance metadata. |
|
||||
|
||||
Authority may be declared per tenant, organization, service, object type,
|
||||
object, field group, or process step. A broad default must not hide a narrower
|
||||
override.
|
||||
|
||||
### Integration maturity
|
||||
|
||||
The implemented maturity ladder remains `discover`, `link`, `search`, `read`,
|
||||
`publish`, `synchronize`, `migrate`, and `replace`. Maturity says what an
|
||||
adapter can do. Source-authority mode says who owns truth in a particular
|
||||
configuration. For example, a connector may support `synchronize`, while a
|
||||
tenant deliberately configures it as `external_mirror`.
|
||||
|
||||
### Provider declaration
|
||||
|
||||
Every provider that reads or causes external effects must declare:
|
||||
|
||||
- owned object and field groups;
|
||||
- supported source-authority modes and integration maturity;
|
||||
- read, write, delete, search, preview, and dry-run operations;
|
||||
- revision/concurrency tokens, freshness, health, and bounded-read limits;
|
||||
- idempotency, retry, timeout, conflict, and outcome-unknown behavior;
|
||||
- evidence, audit, correction, rollback/compensation, and reconciliation paths;
|
||||
- degraded and outage behavior;
|
||||
- classification, purpose, retention, and secret-handling requirements.
|
||||
|
||||
The common provider declaration composes the external-reference, action/effect,
|
||||
connector-lifecycle, capability, operational-check, and documentation
|
||||
contracts. Core, release tooling, Ops, Docs, and configuration-package
|
||||
preflight validate it; Registry refuses to activate a declared external
|
||||
provider without bounded, sanitized runtime state.
|
||||
|
||||
## Cross-cutting contracts
|
||||
|
||||
The following contracts are mandatory for consequential domain objects. They
|
||||
should be shared reference DTOs and provider protocols, not shared domain
|
||||
tables in Core.
|
||||
|
||||
1. **Time and history:** valid-from/to, recorded-at, superseded-at, revision,
|
||||
change reason, and stable identity.
|
||||
2. **Actor and representation:** real account/identity, system or service
|
||||
account, represented account/function/party, delegation or power, and
|
||||
mandate reference.
|
||||
3. **Institutional context:** tenant, institution, organization unit, function,
|
||||
task/mandate, jurisdiction, service, case, and decision references.
|
||||
4. **Legal and policy basis:** typed, versioned references to rules,
|
||||
obligations, policies, exceptions, and the effective decision source.
|
||||
5. **Requested and observed effect:** intent, approval, dispatch, possible
|
||||
execution, confirmation, reconciliation, correction, and terminal evidence.
|
||||
6. **Evidence and provenance:** source, version, checksum, derivation,
|
||||
responsible actor, timestamps, and inspection links.
|
||||
7. **Information governance:** classification, purpose, legal basis, retention,
|
||||
hold, minimization, and disclosure state.
|
||||
8. **External source:** system/profile/object identity, authority mode,
|
||||
maturity, version, freshness, health, and conflict state.
|
||||
9. **Presentation:** language, accessibility, channel, explanation, and
|
||||
configured availability.
|
||||
|
||||
Existing contracts already cover substantial parts of items 1, 2, 5, 6, 8,
|
||||
and 9. New work should extend those contracts instead of creating parallel DTO
|
||||
families.
|
||||
|
||||
## Existing module direction changes
|
||||
|
||||
### Datasources becomes the governed data and register catalogue
|
||||
|
||||
The implemented live/cached/static, staging, immutable materialization, and
|
||||
publication model includes typed governance metadata for owner/steward,
|
||||
authoritative source and authority mode, legal basis and purpose, semantic
|
||||
definition, quality and freshness policy, classification, transfer agreement,
|
||||
correction process, affected services/processes, and dependent flows,
|
||||
reports, controls, and decisions. Connector credentials and protocol behavior
|
||||
remain outside Datasources.
|
||||
|
||||
### Projects grows into portfolio and change governance
|
||||
|
||||
The Projects boundary already includes portfolios and goals. Extend it through
|
||||
versioned objectives/outcomes, dependencies, capacity, benefits, change impact,
|
||||
and links to mandates, services, risks, contracts, resources, and indicators.
|
||||
Do not create a separate Goals module before more than one domain proves an
|
||||
independent goal lifecycle.
|
||||
|
||||
### Reporting becomes evidence-backed institutional measurement
|
||||
|
||||
Every report, measure, and indicator should explain the institutional question
|
||||
or obligation it serves, owner, source/materialization and flow revision,
|
||||
freshness/quality, calculation version, visibility/purpose limits, publication,
|
||||
and decisions or actions that consumed it. Reporting owns presentation and
|
||||
execution; source and transformation owners retain their domains.
|
||||
|
||||
### Risk Compliance becomes the horizontal assurance model
|
||||
|
||||
Sanctions screening remains a complete vertical slice. The broader reusable
|
||||
model is:
|
||||
|
||||
```text
|
||||
Obligation -> governed object -> risk -> control -> evidence -> finding -> measure -> effectiveness review
|
||||
```
|
||||
|
||||
Risk Compliance now persists that effective-dated, immutable-revision assurance
|
||||
graph, exposes bounded tenant-safe traversal/search/editing, and projects each
|
||||
completed sanctions run into it idempotently. Policy
|
||||
owns enforceable rules and decisions; Audit owns immutable event evidence;
|
||||
domain modules own the governed objects and corrective actions.
|
||||
|
||||
### Connectors exposes authority and effect behavior
|
||||
|
||||
Connector direction (`consume`, `publish`, `bidirectional`) remains useful but
|
||||
is not enough. Profiles and bindings need the source-authority mode and
|
||||
provider declaration above. ERP remains an integration family: finance,
|
||||
workforce, procurement, asset, or other domain modules own semantics while
|
||||
connectors own transport and source interaction.
|
||||
|
||||
### Geography starts as a reference contract
|
||||
|
||||
Before adding a `govoplan-geo` module, define a common reference shape for
|
||||
coordinates, geometry, administrative area, address/location, CRS, source,
|
||||
accuracy, validity, and external GIS identity. Create a repository only when
|
||||
GovOPlaN must own spatial datasets, topology, or independent geospatial
|
||||
lifecycles rather than link to an external GIS.
|
||||
|
||||
## Product and sector packages
|
||||
|
||||
A module says what capability can exist. A product package says how capabilities
|
||||
work together for a bounded outcome. A sector package specializes vocabulary,
|
||||
forms, rules, process baselines, controls, reports, and integration profiles
|
||||
without forking the platform.
|
||||
|
||||
The signed configuration-package mechanism distinguishes:
|
||||
|
||||
- **reference package:** tested composition proving a journey and its recovery
|
||||
behavior;
|
||||
- **product package:** reusable operating capability such as governed
|
||||
communication, service-to-decision, procurement/contracts, or governed BI;
|
||||
- **sector package:** institutional specialization such as municipality,
|
||||
university/research, ministry/program, regulator, grants authority, or
|
||||
committee/council;
|
||||
- **deployment profile:** supported infrastructure and operational topology;
|
||||
- **integration profile:** supported set of external systems, authority modes,
|
||||
bindings, and health expectations.
|
||||
|
||||
Packages may require modules and capabilities, but package definitions remain
|
||||
configuration and evidence. They do not gain access to module-owned tables.
|
||||
|
||||
## Module portfolio metadata
|
||||
|
||||
Repository category is not capability maturity. The runtime manifest, release
|
||||
catalog, Docs projection, and meta repository inventory use one
|
||||
machine-readable declaration with at least:
|
||||
|
||||
- architecture layer and module kind;
|
||||
- lifecycle/maturity claim: `concept`, `scaffold`, `vertical_slice`,
|
||||
`reference_ready`, `supported`, or `lts`;
|
||||
- evidence supporting the claim and known limits;
|
||||
- supported source-authority modes;
|
||||
- owned and explicitly non-owned concepts;
|
||||
- provided/required capabilities and interfaces;
|
||||
- reference packages and target-tested providers;
|
||||
- migration, upgrade, recovery, security, and operations documentation.
|
||||
|
||||
Maturity is a release claim and must be checked against evidence. A manifest
|
||||
must not become “supported” merely because a maintainer changes one string.
|
||||
|
||||
Create a repository only when the capability has distinct data ownership,
|
||||
independent installability, technical assets, a security/lifecycle profile, a
|
||||
release reason, more than one consumer or a proven reference process, and tests
|
||||
that justify the boundary. Otherwise use a shared DTO, provider capability,
|
||||
submodule, configuration fragment, package, or profile.
|
||||
|
||||
## Implemented migration sequence
|
||||
|
||||
### 0. Align the portfolio and contracts - complete
|
||||
|
||||
- This reconciliation is canonical in the meta repository and mirrored to the
|
||||
Gitea wiki.
|
||||
- All source manifests in the accepted 2026-08-01 baseline carried validated
|
||||
evidence-based architecture metadata; current counts belong in
|
||||
`STRATEGY_STATUS.md`.
|
||||
- External-reference, action/effect, operational-health, ownership, policy,
|
||||
audit, and documentation primitives compose into one enforced provider
|
||||
declaration and sanitized runtime-state contract.
|
||||
- Institutional context, legal basis, evidence, presentation, external source,
|
||||
information governance, temporal revision, and geo references are shared
|
||||
Core DTOs rather than shared domain tables.
|
||||
|
||||
### 1. Prove responsibility and formal outcome - complete
|
||||
|
||||
- Mandate and Decision contracts, deterministic resolution, lifecycle
|
||||
transitions, persistence providers, APIs, permissions, migrations, recovery,
|
||||
and tests are implemented.
|
||||
- Committee and the SQL-backed institutional fixture prove effective-time
|
||||
authority, persisted meeting/agendum/vote/minute context, approval context,
|
||||
reasoning, evidence, observed effect, correction/revision rules, protected
|
||||
reconstruction, and review references.
|
||||
- The independent Mandates and Decisions repositories were created only after
|
||||
persistence and reuse passed the repository threshold.
|
||||
|
||||
### 2. Separate service and party semantics - complete
|
||||
|
||||
- Portal remains the presentation surface while Services owns reusable,
|
||||
versioned definitions and explainable availability.
|
||||
- Parties owns procedure roles, contact snapshots, and append-only
|
||||
representation/revocation authority; Cases consumes the common resolver.
|
||||
- `product.service-to-decision` proves both through a portable administrative
|
||||
service composition.
|
||||
- Forms owns immutable, versioned schemas while Forms Runtime owns drafts,
|
||||
server validation, submission receipts, status/evidence history, and exact
|
||||
Service/Form provenance. Portal delegates Form launch through the runtime
|
||||
capability and fails closed when it is unavailable.
|
||||
|
||||
### 3. Complete governed data, portfolio, and assurance - vertical slices complete
|
||||
|
||||
- Datasources carries typed governance through staging and immutable
|
||||
materializations, with bounded catalogue filters and dependency references.
|
||||
- Reporting owns immutable semantic definitions, safe execution, quality gates,
|
||||
provenance, schedules, saved views, pivoting, and export/import assessment
|
||||
without taking source or transformation ownership.
|
||||
- Risk Compliance persists the horizontal obligation/risk/control/evidence/
|
||||
finding/measure graph and projects sanctions runs idempotently.
|
||||
- Projects persists portfolio/outcome/change-governance revisions as a
|
||||
consuming domain without becoming a second policy or reporting engine.
|
||||
|
||||
### 4. Package repeatable public-sector outcomes - complete at product maturity
|
||||
|
||||
- Governed communication, governed data/assurance, and service-to-decision are
|
||||
portable product package manifests with repository-local evidence.
|
||||
- Package preflight enforces module, capability, provider authority, health,
|
||||
freshness, and recovery expectations without cross-module table access.
|
||||
- Sector and `reference_ready` claims remain gated on target-environment,
|
||||
recovery, accessibility, privacy, security, and operator evidence. This is a
|
||||
maturity gate, not missing architecture implementation.
|
||||
|
||||
## What remains within the accepted 2026-08-01 architecture slice
|
||||
|
||||
The remaining work is not another Core or cross-module architecture rewrite.
|
||||
It falls into two explicitly different categories, neither of which can be
|
||||
truthfully completed by adding generic platform code:
|
||||
|
||||
1. **Concrete provider packages:** the Committee ballot adapter contract is
|
||||
complete, but a real secret/electronic ballot provider requires a selected
|
||||
protocol and product decisions for voter eligibility, custody, secrecy,
|
||||
recount, challenge, retention, and operational assurance. Equivalent future
|
||||
adapters must satisfy the declared provider and recovery gates.
|
||||
Provider selection and certification are tracked in
|
||||
[Committee #1](https://git.add-ideas.de/GovOPlaN/govoplan-committee/issues/1).
|
||||
2. **Target-produced maturity evidence:** `reference_ready`, `supported`, and
|
||||
`lts` cannot be generated from source code. An exact release and deployment
|
||||
must produce signed, expiring accessibility, privacy, security, operator,
|
||||
provider, backup/restore, rollback, and recovery-drill evidence. The verifier
|
||||
and schemas are implemented; the actual claims require those real runs.
|
||||
A bounded issuer now hashes retained reports, checks role-scoped signing
|
||||
authority and exact installed-release origin, emits sanitized signed
|
||||
receipts, verifies them immediately, and exposes admission-enforcing CLI
|
||||
gates. The real pinned-release evidence run is tracked in
|
||||
[GovOPlaN #37](https://git.add-ideas.de/GovOPlaN/govoplan/issues/37).
|
||||
|
||||
Forms and Forms Runtime no longer constitute an architecture gap. Conditional
|
||||
multi-page/localized authoring, package-fragment import, and durable native
|
||||
Case/Workflow handoffs are implemented. Remaining depth is limited to
|
||||
anonymous/public identity profiles, concrete file/signature providers, and
|
||||
additional handoff target adapters. Those use the implemented immutable
|
||||
definition, runtime, policy, evidence, service-launch, and domain-owner
|
||||
boundaries rather than requiring another split. Public/provider decisions stay
|
||||
tracked in Forms Runtime
|
||||
[#2](https://git.add-ideas.de/GovOPlaN/govoplan-forms-runtime/issues/2) and
|
||||
[#3](https://git.add-ideas.de/GovOPlaN/govoplan-forms-runtime/issues/3).
|
||||
|
||||
Approvals, Voting, Workflow trigger/wait dispatch, Identity Trust, and
|
||||
Encryption now likewise have repository owners, neutral Core contracts,
|
||||
persistence, migrations, recovery/disable semantics, documentation and focused
|
||||
tests. Their remaining tickets concern concrete providers, deeper adapters and
|
||||
target evidence, not an unresolved institutional architecture boundary.
|
||||
|
||||
Everything else described in the accepted baseline of this document now has a
|
||||
repository owner, versioned contract, bounded implementation, migration and
|
||||
recovery boundary where state exists, documentation, and executable evidence.
|
||||
Further work in those modules is product breadth, UX depth, provider adoption,
|
||||
and evidence renewal.
|
||||
|
||||
## Strategic extensions accepted after the baseline
|
||||
|
||||
The completed baseline does not imply that institutional product architecture
|
||||
can no longer grow. The 2026-08-05 strategic review accepted four extensions
|
||||
that consume the existing contracts without reopening the kernel or moving
|
||||
domain ownership into Core:
|
||||
|
||||
- [Product Experience and Module Boundaries](PRODUCT_EXPERIENCE_AND_MODULE_BOUNDARIES.md)
|
||||
separates technical package topology from stable task/object/product
|
||||
surfaces; implementation is tracked in Core #283.
|
||||
- [Federated GovOPlaN Architecture](FEDERATED_GOVOPLAN_ARCHITECTURE.md)
|
||||
defines governed exchange between autonomous installations; implementation
|
||||
is tracked in GovOPlaN #41.
|
||||
- [Assisted and Non-Digital Channels](ASSISTED_AND_NON_DIGITAL_CHANNELS.md)
|
||||
makes channel inclusion part of the service-to-decision journey; the first
|
||||
reference proof is tracked in GovOPlaN #42.
|
||||
- [Institutional Digital Twin](INSTITUTIONAL_DIGITAL_TWIN.md) defines a
|
||||
time-aware, policy-filtered projection over owner data; implementation is
|
||||
tracked in GovOPlaN #43.
|
||||
|
||||
The eAkte depth required by those journeys is owned by Records and specified in
|
||||
`govoplan-records/docs/EAKTE_ARCHITECTURE.md`, tracked in Records #1. These are
|
||||
new product-depth programs with bounded contracts and acceptance journeys, not
|
||||
evidence that the original institutional semantics or module architecture
|
||||
failed.
|
||||
|
||||
## Delivery tracking
|
||||
|
||||
The completed cross-repository architecture epic is
|
||||
[GovOPlaN #29](https://git.add-ideas.de/GovOPlaN/govoplan/issues/29).
|
||||
Its implementation work packages and resulting owners are:
|
||||
|
||||
- [Core #279](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/279):
|
||||
validated module architecture and provider authority declarations;
|
||||
- [Core #280](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/280):
|
||||
shared institutional-context and governed reference primitives;
|
||||
- [GovOPlaN #30](https://git.add-ideas.de/GovOPlaN/govoplan/issues/30) and
|
||||
[govoplan-mandates](https://git.add-ideas.de/GovOPlaN/govoplan-mandates):
|
||||
Mandates semantics and persistent resolver;
|
||||
- [GovOPlaN #31](https://git.add-ideas.de/GovOPlaN/govoplan/issues/31) and
|
||||
[govoplan-services](https://git.add-ideas.de/GovOPlaN/govoplan-services):
|
||||
Services semantics, catalogue, and availability;
|
||||
- [GovOPlaN #32](https://git.add-ideas.de/GovOPlaN/govoplan/issues/32) and
|
||||
[govoplan-parties](https://git.add-ideas.de/GovOPlaN/govoplan-parties):
|
||||
Parties and representation semantics and resolver;
|
||||
- [GovOPlaN #33](https://git.add-ideas.de/GovOPlaN/govoplan/issues/33) and
|
||||
[govoplan-decisions](https://git.add-ideas.de/GovOPlaN/govoplan-decisions):
|
||||
formal Decisions semantics and registry;
|
||||
- [Datasources #6](https://git.add-ideas.de/GovOPlaN/govoplan-datasources/issues/6):
|
||||
governed data/register catalogue;
|
||||
- [Risk Compliance #7](https://git.add-ideas.de/GovOPlaN/govoplan-risk-compliance/issues/7):
|
||||
horizontal assurance graph;
|
||||
- [GovOPlaN #34](https://git.add-ideas.de/GovOPlaN/govoplan/issues/34):
|
||||
product and sector package classes; and
|
||||
- [Docs #19](https://git.add-ideas.de/GovOPlaN/govoplan-docs/issues/19):
|
||||
configured architecture, maturity, and source-authority explanations;
|
||||
- [Forms #2](https://git.add-ideas.de/GovOPlaN/govoplan-forms/issues/2):
|
||||
immutable reusable definitions and the designer surface; and
|
||||
- [Forms Runtime #1](https://git.add-ideas.de/GovOPlaN/govoplan-forms-runtime/issues/1):
|
||||
definition-aware submissions and Portal service launch;
|
||||
- [Forms #3](https://git.add-ideas.de/GovOPlaN/govoplan-forms/issues/3) and
|
||||
[Forms Runtime #4](https://git.add-ideas.de/GovOPlaN/govoplan-forms-runtime/issues/4):
|
||||
conditional/localized definition depth and governed native handoffs;
|
||||
- [Approvals #1](https://git.add-ideas.de/GovOPlaN/govoplan-approvals/issues/1)
|
||||
and [Campaign #22](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/22):
|
||||
generic exact-subject approval chains and one consequential delivery gate;
|
||||
- `govoplan-voting`: governed recorded ballots plus fail-closed provider-backed
|
||||
assurance profiles consumed by Committee; and
|
||||
- Identity Trust #1 and Encryption #1-#3: public device trust, provider-neutral
|
||||
key/protection lifecycle, recovery authorization and disable proof, plus a
|
||||
bounded local server-envelope provider and Files/Postbox fixtures; external
|
||||
KMS/HSM/client-provider conformance remains separately gated.
|
||||
|
||||
Existing Projects #1, Reporting #4, Portal #1, Cases #1, Datasources #1,
|
||||
Risk Compliance #2, GovOPlaN #14, and GovOPlaN #19 carry product-depth and
|
||||
reference-readiness work instead of duplicating the completed architecture
|
||||
contract.
|
||||
|
||||
## Completion evidence
|
||||
|
||||
The architecture direction is established by the following executable and
|
||||
machine-enforced evidence:
|
||||
|
||||
- the `product.service-to-decision` composition and SQL-backed golden fixture
|
||||
retain institutional context from service entry through a persisted case,
|
||||
party, authority/work context, committee deliberation, decision, observed
|
||||
communication effect, minute/record, and review references;
|
||||
- the Portal/Form journey retains the exact published Service and Form
|
||||
revisions through persisted draft state, validates on the server, and returns
|
||||
the same submission on an idempotent launch replay;
|
||||
- the system can answer who acted, for whom, in which function, under which
|
||||
mandate and jurisdiction, using which rule and evidence versions;
|
||||
- every implemented external binding declares authority mode, maturity,
|
||||
operations, health, freshness, conflict, and recovery behavior, and Registry
|
||||
rejects a declaration without sanitized runtime state;
|
||||
- every material report or decision can be reconstructed from governed source
|
||||
and transformation versions;
|
||||
- product/package manifests are portable without cross-module table access or
|
||||
code forks, while future sector packages inherit the same signed-package
|
||||
constraints; and
|
||||
- documentation and Ops explain the configured composition and its limits to
|
||||
users, administrators, operators, and auditors.
|
||||
|
||||
These criteria complete the architecture contract at `vertical_slice` and
|
||||
`product` maturity. They do not waive the separately enforced evidence needed
|
||||
for a module or package to claim `reference_ready`, `supported`, or `lts`.
|
||||
The capability-fit verifier now computes that cumulative readiness gate from
|
||||
independently signed, expiring claims bound to the exact assessed release,
|
||||
installed payload, deployment subject, controls, and artifact hashes. Actual
|
||||
target runs and recovery drills remain operator-produced evidence.
|
||||
+170
-22
@@ -2,12 +2,13 @@
|
||||
|
||||
This document is the cross-repository pattern language for GovOPlaN user
|
||||
interfaces. It turns the existing ethical doctrine, binding UI/UX decisions,
|
||||
layout principles, and module boundary into a common composition and review
|
||||
grammar. It does not replace those sources.
|
||||
layout rules, and module boundary into a common composition and review grammar.
|
||||
This document also owns the former standalone frontend-layout principles.
|
||||
|
||||
The companion [interface surface inventory](INTERFACE_SURFACE_INVENTORY.md)
|
||||
records which surfaces the current code contributes and where each surface
|
||||
enters the rollout.
|
||||
The dated [interface surface inventory](../evidence/snapshots/INTERFACE_SURFACE_INVENTORY.md)
|
||||
records the 2026-08-03 rollout snapshot. Current implementation state belongs
|
||||
in Gitea and generated inventory evidence, not in this durable pattern
|
||||
language.
|
||||
|
||||
## Source Of Truth And Precedence
|
||||
|
||||
@@ -19,14 +20,11 @@ Use the narrowest owning document when changing a rule:
|
||||
2. `govoplan-core/docs/UI_UX_DECISION_LEDGER.md` owns accepted product decisions
|
||||
such as progressive disclosure, adaptive forms, blocker language, guided
|
||||
operations, and the platform theme contract.
|
||||
3. `docs/FRONTEND_LAYOUT_PRINCIPLES.md` owns the high-level choice between a
|
||||
full-space structured-data workspace and a heading/menu/card workflow or
|
||||
configuration surface.
|
||||
4. `govoplan-core/docs/MODULE_ARCHITECTURE.md` owns the shell, route, navigation,
|
||||
3. `govoplan-core/docs/MODULE_ARCHITECTURE.md` owns the shell, route, navigation,
|
||||
UI-capability, and shared-component boundaries.
|
||||
5. This document owns the common pattern names, placement grammar, wording and
|
||||
state conventions, focused-view composition, and definition of done across
|
||||
those sources.
|
||||
4. This document owns the high-level layout choice, common pattern names,
|
||||
placement grammar, wording and state conventions, focused-view composition,
|
||||
and definition of done across those sources.
|
||||
|
||||
If two rules appear to conflict, do not create a third local convention. Record
|
||||
the conflict in the owning decision ledger, resolve it there, and update the
|
||||
@@ -53,6 +51,148 @@ rules:
|
||||
- Preserve a stable way back to the containing object and the broader system.
|
||||
- Do not let navigation, selection, or a view switch imply consent.
|
||||
|
||||
## Shared Component And Layout Architecture
|
||||
|
||||
Core owns the reusable WebUI vocabulary; modules own domain composition and
|
||||
behavior. Centralization follows four layers:
|
||||
|
||||
| Layer | Owner | Examples | Rule |
|
||||
| --- | --- | --- | --- |
|
||||
| Foundation | Core | theme tokens, spacing, typography, focus and responsive breakpoints | Modules consume the contract and do not redefine it. |
|
||||
| Primitives | Core | buttons, fields, dialogs, alerts, cards, tables, loading, empty and blocked states | A matching primitive is reused rather than copied locally. |
|
||||
| Structural layouts | Core | page frame and header, action region, workspace panes, toolbars, grids, form sections and dialog anatomy | Layout owns geometry, scroll, responsive collapse and accessibility, but no domain decisions. |
|
||||
| Domain compositions | Owning module | a campaign review, mailbox, records explorer or operations dashboard | Modules select shared pieces, bind data and permissions, and retain domain wording and consequences. |
|
||||
|
||||
A component belongs in Core when it is used or expected in more than one
|
||||
module and central ownership materially protects accessibility, responsive
|
||||
behavior, localization, contextual help, theming, or interaction consistency.
|
||||
A component stays module-owned when its API would otherwise encode a domain
|
||||
entity, permission, workflow state, endpoint, or policy decision. Reuse does
|
||||
not justify moving domain semantics into Core.
|
||||
|
||||
`PageLayout` is the standard frame for headed workflow, dashboard,
|
||||
configuration, monitoring and explanatory pages. It owns the content inset,
|
||||
sticky responsive header, title and rich-description geometry, route-action
|
||||
placement, transient and custom notices, loading boundary and page help
|
||||
identity. Its modes make scroll ownership explicit: `standalone` owns a page
|
||||
viewport, `workspace` defers scrolling to a full-canvas content pane while
|
||||
retaining the standard inset, and `embedded` owns neither scroll nor inset.
|
||||
|
||||
`WorkspaceLayout` is the standard full-canvas shell. Its `navigation` variant
|
||||
owns module/resource subnavigation plus content; its `split` variant owns
|
||||
collection/detail panes. It centralizes pane sizing, internal scroll,
|
||||
responsive collapse/stacking, accessible pane labels and workspace help
|
||||
identity. `WorkspaceFrame` is the outer full-height module frame and owns
|
||||
container or application-viewport height, overflow, surface, landmark, help,
|
||||
and accessible-name behavior. `PageHeader` remains available when an
|
||||
exceptional canvas needs only the shared heading. Specialized layouts such as
|
||||
`AdminPageLayout` compose these lower-level Core contracts; they do not repeat
|
||||
markup or responsive CSS.
|
||||
|
||||
`PageActionBar` is the semantic action contract for headed pages;
|
||||
`WorkspaceActionBar` applies the identical ordering and lifecycle rules to a
|
||||
full canvas and its collection, detail, and editor panes. Reload is always the
|
||||
leading action on a refreshable projection. Help and ordinary task actions
|
||||
follow contextual controls; Create is the far-right collection action;
|
||||
destructive actions occupy a named separated group; an editor ends with
|
||||
Discard and Save, with Save at the far right. Editor state is explicit:
|
||||
`clean`, `dirty`, `invalid`, `saving`, `save-failed`, or `conflict`. Lower-level
|
||||
`ActionToolbar` remains appropriate for a section-local view switch or compact
|
||||
control group, but it must not recreate page or pane action placement.
|
||||
|
||||
Composite workspaces whose selected contribution supplies its own semantic
|
||||
heading may use `PageLayout` with its visible header delegated. This preserves
|
||||
the central inset, loading boundary, help identity, and content frame without
|
||||
adding a duplicate heading. It is not permission to recreate the page header
|
||||
locally on ordinary headed pages.
|
||||
|
||||
Module CSS may arrange domain content inside a shared layout. It must not
|
||||
override Core layout internals or copy the outer page, dialog, toolbar, form or
|
||||
state skeleton under a module-prefixed name. If an archetype cannot be
|
||||
expressed by the central API, extend the central contract or record a bounded
|
||||
exception before introducing local structure.
|
||||
|
||||
Migration is incremental and enforceable:
|
||||
|
||||
1. inventory copied structures and register existing debt;
|
||||
2. introduce the smallest domain-neutral Core contract with accessibility,
|
||||
help, localization, theme and narrow-layout tests;
|
||||
3. migrate representative Core and optional-module consumers;
|
||||
4. reject new copies while removing registered debt in bounded module batches;
|
||||
5. promote the next repeated structure only after its variants and extension
|
||||
points are understood.
|
||||
|
||||
The current page-frame and workspace migration has no legacy exceptions. New
|
||||
raw frames fail the focused layout contract instead of entering a new baseline.
|
||||
|
||||
The current structural vocabulary is:
|
||||
|
||||
- `ActionToolbar`, `ToolbarGroup`, and `ToolbarSpacer` own action alignment,
|
||||
distribution, density, grouping, panel/section surfaces, accessible toolbar
|
||||
naming, help identity, and responsive wrapping. Modules may add
|
||||
domain-specific presentation; they do not recreate the flex/wrap skeleton.
|
||||
- `PageActionBar` and `WorkspaceActionBar` own semantic ordering, Reload,
|
||||
editor persistence state, destructive separation, and page/pane scope. A
|
||||
module supplies action behavior, authority, blocker reasons, and wording;
|
||||
it does not assemble another panel-header action convention.
|
||||
- `WorkspaceFrame` and `WorkspaceLayout` own application-viewport framing,
|
||||
surfaces, overflow, list/detail and navigation/content pane geometry,
|
||||
accessible region identity, and responsive pane behavior. Modules own only
|
||||
the domain regions placed inside those contracts.
|
||||
- `FilterBar` owns submitted or live filter/search arrangement, wrapping,
|
||||
width and surface. `SelectionList`, `SelectionListItem`, and
|
||||
`SelectionListItemContent` own selectable resource navigation and its
|
||||
title/description/leading-icon geometry. `CountBadge` owns compact numeric
|
||||
emphasis. Modules retain filter behavior, selection state, and count meaning.
|
||||
- `StatePanel` owns whole-surface, compact, inline and fill state presentation
|
||||
for empty, unavailable, blocked, warning and recoverable-error compositions.
|
||||
Modules provide the cause, consequence, permitted action and authority.
|
||||
- `ContentGrid`, `FormGrid`, `FormLayout`, and `GridItem` own equal-column
|
||||
geometry, standard gaps, alignment, spans, native form semantics, and named
|
||||
responsive collapse points. A module-local grid remains appropriate only
|
||||
when unequal tracks or domain visualization semantics are material.
|
||||
- `ContentSection` owns repeated bordered or subtle content-section surfaces,
|
||||
density, stacked flow and surrounding rhythm without prescribing a domain
|
||||
heading or body schema.
|
||||
- `FormSection` owns form-section heading, description, actions, content flow,
|
||||
separation, and panel presentation. It does not own field values,
|
||||
validation, permissions, or domain wording.
|
||||
- `MetricGrid` owns the responsive grouping around `MetricCard`: fixed one-to-five
|
||||
columns or auto-fit, minimum card width, density, surrounding rhythm, and a
|
||||
named collapse point. `MetricCard.drilldown` provides an explicit link or
|
||||
in-page action when an authorized underlying detail helps the user act; it
|
||||
names that destination and preserves the current scope and filters. The card
|
||||
itself is never the hidden click target. Derived, privacy-suppressed,
|
||||
non-enumerable, and purely informational aggregates remain inert. Modules
|
||||
provide the metric, tone, destination, and consequence; they do not recreate
|
||||
the group grid or reach across module CSS to size it.
|
||||
- `DescriptionList` and `DescriptionItem` own semantic property presentation.
|
||||
The stacked variant supports compact multi-column facts; the inline variant
|
||||
supports one-column term/value rows with a standard term width. Both own
|
||||
density, wrapping, and responsive collapse while modules retain the terms,
|
||||
values, provenance, and actions.
|
||||
- `Dialog` owns size and administration variants, body padding, description,
|
||||
notices, and fixed footer placement. `DialogActions`, `DialogForm`, and
|
||||
`DialogSection` own the footer action flow, native form flow, and body
|
||||
grouping used inside it. Modules compose fields and consequences rather than
|
||||
recreating dialog anatomy.
|
||||
- `DefinitionPalette`, `DefinitionPaletteGroup`, `DefinitionPaletteItem`, and
|
||||
`DefinitionNodeIcon`, together with the shared definition-canvas classes,
|
||||
own reusable graph-editor palette, canvas-control, node-icon, port and empty
|
||||
overlay visuals. Workflow/Dataflow retain node types, shapes, edges,
|
||||
validation and execution semantics. `FloatingStatus` owns the common
|
||||
non-shifting activity overlay.
|
||||
|
||||
Raw toolbar tags, the former generic grid and property-list classes, retired
|
||||
module-local shells/states/metrics/badges, raw dialog-form wrappers, and
|
||||
module-local definitions of these contracts are rejected by the focused
|
||||
workspace checks. Dialog widths matching the Core size scale must use `Dialog
|
||||
size`; other local widths require a reviewed exception and may only decrease.
|
||||
Remaining local layout is acceptable only for unequal-track domain editors,
|
||||
visualizations, trees, timelines, data tables, or domain-specific multi-pane
|
||||
interaction. Generic resemblance alone is not a reason to create one oversized
|
||||
page template, while exact repeated structural anatomy must be promoted.
|
||||
|
||||
## Surface Archetypes
|
||||
|
||||
Choose an archetype from the task, then specialize it for the domain. A route
|
||||
@@ -95,15 +235,23 @@ one.
|
||||
|
||||
- Structured directories use the full available content space and persistent
|
||||
panes. They do not add a decorative heading row that reduces working height.
|
||||
Give navigation and list panes bounded widths and let the main content or
|
||||
detail pane consume the remaining space.
|
||||
- In a list-detail workspace, related lists may be stacked in the left pane
|
||||
while the main pane owns view, create, and edit. Keep one create action in
|
||||
the relevant list heading instead of adding a second launcher or permanent
|
||||
creation panel.
|
||||
- Workflow, configuration, dashboard, and explanatory pages may use a heading.
|
||||
The heading names the task or scoped object and contains only route-level
|
||||
actions.
|
||||
actions. Use the Core `PageLayout` contract for the frame and `PageHeader`
|
||||
only when a full-canvas archetype owns its own scrolling.
|
||||
- Put a collection-wide create action in the heading of the collection it
|
||||
affects. Use a short, specific label such as `Add` when the heading already
|
||||
names the object. Do not duplicate that action in a permanently visible side
|
||||
panel. A side panel used as the creation surface appears for creation and is
|
||||
otherwise absent or returns to its documented non-creation purpose.
|
||||
- Put filters beside the list or pane they affect. Put bulk actions immediately
|
||||
- Put filters beside the list or pane they affect. Put collection, detail, and
|
||||
editor-pane actions in `WorkspaceActionBar` with the matching scope. Put bulk actions immediately
|
||||
above or beside the current selection. Put object actions with the object
|
||||
detail, not in the global title bar.
|
||||
- Full-page create and edit surfaces put their persistent action cluster in the
|
||||
@@ -444,25 +592,25 @@ reason to infer that a pattern is satisfied.
|
||||
and does not duplicate a central component.
|
||||
- Behavioral/accessibility evidence is linked from the rollout matrix and issue.
|
||||
- Configured-system help can reach the applicable pattern or reference topic
|
||||
when [Docs #15](https://git.add-ideas.de/add-ideas/govoplan-docs/issues/15)
|
||||
when [Docs #15](https://git.add-ideas.de/GovOPlaN/govoplan-docs/issues/15)
|
||||
supplies that experience.
|
||||
|
||||
## First Pilot: Campaign
|
||||
|
||||
[Campaign #74](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/74)
|
||||
[Campaign #74](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/74)
|
||||
is the first full-domain audit and migration. It should prove patterns before
|
||||
generic extraction:
|
||||
|
||||
- [#59](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/59) and
|
||||
[#73](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/73): stable,
|
||||
- [#59](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/59) and
|
||||
[#73](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/73): stable,
|
||||
accessible preview and attachment-detail overlays
|
||||
- [#63](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/63): review
|
||||
- [#63](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/63): review
|
||||
stages, outcomes, blockers, and intervention vocabulary
|
||||
- [#62](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/62): explicit
|
||||
- [#62](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/62): explicit
|
||||
synchronous/asynchronous send mode and durable delivery progress
|
||||
- [#65](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/65): one
|
||||
- [#65](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/65): one
|
||||
coherent report filtering and count-affordance model
|
||||
- [#35](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/35): guided
|
||||
- [#35](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/35): guided
|
||||
first-campaign entry
|
||||
|
||||
These slices do not depend on the Workflow runtime. Campaign's current
|
||||
@@ -0,0 +1,201 @@
|
||||
# Platform Control Plane And Self-Description
|
||||
|
||||
## Objective
|
||||
|
||||
GovOPlaN should be able to describe its installed structure without becoming a
|
||||
self-modifying application. The platform model is a declarative control plane:
|
||||
module manifests, UI contributions, schemas, policy provenance, runtime
|
||||
capabilities, and generated source evidence describe what can be configured.
|
||||
Ordinary administrators edit validated data through those contracts; they do
|
||||
not edit Python, TypeScript, routes, or database code from the product UI.
|
||||
|
||||
This distinction provides the requested overview while preserving reviewable
|
||||
releases, module boundaries, migrations, and security controls.
|
||||
|
||||
## Canonical Sources
|
||||
|
||||
| Concern | Canonical source |
|
||||
| --- | --- |
|
||||
| Installed modules and dependency graph | Runtime `ModuleManifest` registry |
|
||||
| Backend routes | Registered FastAPI application; Python AST is build-time evidence |
|
||||
| Frontend routes and navigation | `PlatformWebModule` contributions |
|
||||
| View-filterable regions | Versioned `viewSurfaces` declarations |
|
||||
| Admin sections and module settings | `admin.sections`, including `moduleId`, `kind`, scope group, permission guards, and surface ID |
|
||||
| User settings | `settings.sections` and core settings schemas |
|
||||
| Labels and translations | Generated translation catalogs plus source usage |
|
||||
| Fields and help coverage | Shared form components plus generated TypeScript AST inventory |
|
||||
| API use by the WebUI | Typed API clients plus generated static reference inventory |
|
||||
| Stable platform interface IDs | Typed manifest/WebUI declarations plus line-independent source anchors for low-level controls |
|
||||
| Effective configuration | Owning module data plus Policy provenance |
|
||||
|
||||
Runtime introspection is authoritative for an installed system. Static source
|
||||
inventory is authoritative evidence for a checkout or release candidate. The
|
||||
two should be compared in CI and by Ops, not conflated.
|
||||
|
||||
## Generated Inventory
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
cd /mnt/DATA/git/govoplan
|
||||
./.venv/bin/python tools/inventory/platform-interface-inventory.py
|
||||
```
|
||||
|
||||
The command writes:
|
||||
|
||||
- `audit-reports/platform-inventory/platform-interface-inventory.json`
|
||||
- `audit-reports/platform-inventory/platform-interface-inventory.md`
|
||||
|
||||
Use `--strict` for the combined translation, endpoint, and declaration audit.
|
||||
Use `--strict-declarations` for duplicate/stale/undeclared interface checks
|
||||
without making existing translation coverage a release blocker. Use
|
||||
`--strict-endpoints` in the endpoint-surface CI gate so unrelated translation
|
||||
catalog work cannot disable route classification enforcement. Both strict modes
|
||||
require every backend endpoint without a statically visible WebUI path to have
|
||||
an exact entry in
|
||||
`tools/inventory/endpoint-surface-declarations.json`. The registry is keyed by
|
||||
repository, HTTP method, and canonical version-independent path. It accepts:
|
||||
|
||||
- `ui_reachable`: a mounted router, generic action, or provider path hides the
|
||||
reference from static extraction;
|
||||
- `intentionally_headless`: a capability/API is deliberately consumed without
|
||||
its own UI;
|
||||
- `public_integration`: a documented public or interoperability endpoint;
|
||||
- `worker_internal`: a worker, scheduler, reconciliation, or monitoring path;
|
||||
- `compatibility`: a retained transition endpoint with a current replacement;
|
||||
- `missing_ui`: a real UI gap, which must include a Gitea tracking issue;
|
||||
- `removable`: a reviewed dead endpoint pending removal.
|
||||
|
||||
Strict mode also rejects declarations that no longer match source. When an
|
||||
endpoint is added, changed, or removed, update its declaration in the same
|
||||
change. Do not classify an endpoint from a string mismatch alone: first check
|
||||
mounted prefixes, dynamic action paths, public clients, worker use, and
|
||||
capability consumers.
|
||||
|
||||
It combines:
|
||||
|
||||
1. loaded module manifests
|
||||
2. TypeScript AST extraction of fields, label attributes, visible text,
|
||||
translations, frontend routes, navigation, capabilities, and API references
|
||||
3. Python AST extraction of FastAPI route decorators and router prefixes
|
||||
4. normalized runtime declarations from every loaded `ModuleManifest`
|
||||
|
||||
The declaration set covers routes, navigation, View surfaces, fields, actions,
|
||||
help references, translations, admin/settings sections, widgets, search
|
||||
objects, permissions, provided interfaces, and backend capabilities. Typed
|
||||
module contributions keep their declared IDs. Shared controls may declare
|
||||
`interfaceId` and `helpTopicId`; otherwise the extractor assigns a deterministic
|
||||
source anchor based on repository, file, component context, control type, and
|
||||
semantic label rather than a line number. The JSON records which identity
|
||||
source was used.
|
||||
|
||||
The JSON includes exact repository, file, and line evidence. A missing-help
|
||||
entry is a review candidate because dynamic parent components may supply help.
|
||||
A backend route without a static frontend reference is also a review candidate:
|
||||
public APIs, workers, callbacks, health checks, connectors, and dynamic URL
|
||||
assembly are valid explanations.
|
||||
|
||||
The module matrix enforces endpoint and interface declarations with
|
||||
`--strict-endpoints --strict-declarations`.
|
||||
Combined `--strict` additionally fails when used translation keys are absent
|
||||
from generated locale catalogs. Help-text findings remain review candidates
|
||||
rather than a release gate because dynamic parent components can supply help.
|
||||
|
||||
## Runtime Comparison
|
||||
|
||||
Core exposes a sanitized read-only catalog at
|
||||
`GET /api/v1/platform/interface-catalog`. Access requires
|
||||
`admin:module:read` or `system:settings:read`. Tenant module entitlements are
|
||||
applied before serialization, so the response describes only the effective
|
||||
installed combination. It contains IDs, paths, authorization metadata,
|
||||
versions, counts, and canonical digests; it excludes factories, callbacks,
|
||||
credentials, and mutable runtime state.
|
||||
|
||||
Capture and compare a running installation:
|
||||
|
||||
```bash
|
||||
curl --fail --silent \
|
||||
-H "Authorization: Bearer $GOVOPLAN_ACCESS_TOKEN" \
|
||||
"$GOVOPLAN_URL/api/v1/platform/interface-catalog" \
|
||||
> /tmp/govoplan-runtime-interface.json
|
||||
|
||||
./.venv/bin/python tools/inventory/platform-interface-inventory.py \
|
||||
--runtime-snapshot /tmp/govoplan-runtime-interface.json \
|
||||
--strict-declarations \
|
||||
--strict-endpoints
|
||||
```
|
||||
|
||||
The comparison accepts any installed subset. Every module present in the
|
||||
runtime response must have the same contract version, module version, and
|
||||
declaration digest as the static release inventory. Unknown, duplicate, or
|
||||
mismatched runtime modules fail strict declaration mode.
|
||||
|
||||
## Admin Information Architecture
|
||||
|
||||
The Admin host uses a tree because system, tenant, group, user, and module
|
||||
settings form a hierarchy rather than one flat list. Every contributed section
|
||||
can identify:
|
||||
|
||||
- its owning `moduleId`
|
||||
- whether it is `management` or `settings`
|
||||
- its system/tenant/group/user scope group
|
||||
- an optional future `parentId`
|
||||
- permission and View visibility requirements
|
||||
|
||||
Existing panels remain their own render owners. The tree only changes discovery
|
||||
and grouping. A later embedded-settings contract may add named slots inside an
|
||||
owning page; it must not allow one module to import another module's private
|
||||
component.
|
||||
|
||||
## Navigation And Workflow
|
||||
|
||||
The intended maximum visible navigation stack is:
|
||||
|
||||
1. global shell context
|
||||
2. one task/object navigation surface
|
||||
3. one workflow stage surface when a workflow is active
|
||||
|
||||
Workflow instance pages should reuse the Campaign stage language: clear stage
|
||||
state, optional/skipped/blocked semantics, partial progress, and a stable current
|
||||
step. Workflow definition pages remain graph editors. Views may activate a
|
||||
focused workflow view that suppresses unrelated shell and module surfaces while
|
||||
retaining an explicit way out.
|
||||
|
||||
Nested module submenus should not be added merely because a data hierarchy
|
||||
exists. Prefer a tree inside configuration/directory surfaces, tabs for sibling
|
||||
views, and the workflow stage rail for ordered work.
|
||||
|
||||
## Safe Meta-Configuration
|
||||
|
||||
The platform can eventually render many configuration editors from versioned
|
||||
JSON Schema and UI Schema supplied by modules. Generated editors remain bounded
|
||||
by:
|
||||
|
||||
- explicit typed schemas and migrations
|
||||
- module-owned validation and preview
|
||||
- Policy locks and provenance
|
||||
- permission and View filtering
|
||||
- preflight, consequence, and rollback information
|
||||
- auditable apply operations
|
||||
|
||||
Custom code, new routes, arbitrary SQL, and executable workflow nodes remain
|
||||
release artifacts. Modeling them as ordinary configuration would create an
|
||||
unreviewed code-execution and migration channel.
|
||||
|
||||
## Enforced Contract
|
||||
|
||||
1. Public WebUI routes and View surfaces must reconcile with runtime manifest
|
||||
metadata; stale runtime routes and source-only public surfaces fail CI.
|
||||
2. Duplicate stable IDs fail CI. Shared controls support explicit field/action
|
||||
and help-topic identities; fallback anchors remain visible review evidence.
|
||||
3. Every statically unreferenced backend endpoint has an exact reviewed
|
||||
consumer classification, and stale classifications fail CI.
|
||||
4. Runtime module combinations can be compared exactly with static release
|
||||
evidence through versioned per-module digests.
|
||||
5. Runtime introspection is authorized, tenant-filtered, and read-only. It is
|
||||
safe for Ops/Docs projection but is not a generic configuration or code
|
||||
mutation channel.
|
||||
|
||||
Generated JSON and Markdown remain build/audit artifacts. Do not hand-edit or
|
||||
use them as a backlog; change the owning manifest, typed WebUI contribution,
|
||||
translation/help declaration, or exact endpoint classification instead.
|
||||
@@ -0,0 +1,211 @@
|
||||
# Product Experience and Module Boundaries
|
||||
|
||||
## Problem
|
||||
|
||||
GovOPlaN's runtime modularity is a strength, but the implementation structure
|
||||
is exposed too directly in the product. Ordinary users encounter module names,
|
||||
one top-level route per module, one navigation item per repository, package and
|
||||
provider identifiers, and errors framed as missing modules. This makes the
|
||||
system look like a toolbox of adjacent applications instead of one operating
|
||||
environment for institutional work.
|
||||
|
||||
The correction is not a monolithic frontend and not hidden provenance. It is a
|
||||
separate product information architecture assembled from typed module
|
||||
contributions.
|
||||
|
||||
Implementation is tracked in
|
||||
[Core #283](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/283).
|
||||
The accepted configurable product-area and task-local tool design is defined
|
||||
in [Quick Access And Product Areas](QUICK_ACCESS_AND_PRODUCT_AREAS.md).
|
||||
|
||||
## Current Exposure Inventory
|
||||
|
||||
| Surface | Direct exposure | Appropriate audience | Product-facing alternative |
|
||||
| --- | --- | --- | --- |
|
||||
| Side rail | One icon and route for many installed modules | Administrators and power users | Work areas, services, inboxes, records, communication, data and assurance |
|
||||
| Route paths | Technical owners such as `/dataflow`, `/forms`, or `/postbox` | Deep links and diagnostics | Stable product aliases and journey routes that resolve to owner surfaces |
|
||||
| Dashboard | Installed module count and module-owned widget library | Operators | Outcome, obligation, work, exception, and service widgets |
|
||||
| Administration | Package names, database state, capabilities, providers | Module and system administrators | Guided product/package configuration with technical details on demand |
|
||||
| Errors | "Module/capability not installed" | Diagnostics | Explain the unavailable outcome, responsible administrator, and enabling path |
|
||||
| Documentation | Topics grouped primarily by module | Administrators | Task, role, service, and object documentation with module provenance secondary |
|
||||
| Permissions | Module-namespaced scopes | Access administrators | Human-readable responsibility bundles; exact scopes remain inspectable |
|
||||
| Search | Provider/module as a result facet | Advanced filtering | Object type, institution, time, purpose, case/service, and source authority |
|
||||
| Workflow | Steps can expose target route/module details | Workflow designers | User-facing action and expected result; technical binding in definition details |
|
||||
| Connector state | Provider IDs and source types | Integration owners | Named source, authority, freshness, health, last effect, and recovery state |
|
||||
|
||||
## Boundary Decision
|
||||
|
||||
Three layers remain distinct:
|
||||
|
||||
1. **Technical module layer:** package ownership, dependencies, capabilities,
|
||||
permissions, migrations, routes, and provider identifiers.
|
||||
2. **Product composition layer:** work areas, object types, journeys, commands,
|
||||
inboxes, configuration packages, and role-based defaults.
|
||||
3. **Presentation projection:** active view, tenant policy, current task,
|
||||
temporal context, language, accessibility preferences, and device layout.
|
||||
|
||||
Modules own implementation and contribute typed product metadata. Core
|
||||
assembles it. Views filters it. Policy constrains it. Access authorizes the
|
||||
underlying actions. No consumer imports another optional module's UI directly.
|
||||
|
||||
## Product Surface Contract
|
||||
|
||||
Each WebUI module should be able to announce:
|
||||
|
||||
- `product_areas`: stable areas to which a route, command, widget, or object
|
||||
belongs;
|
||||
- `object_types`: user-facing nouns, icons, search context, detail route, and
|
||||
owner provenance;
|
||||
- `work_item_sources`: open work, exceptions, deadlines, and responsible
|
||||
capacity;
|
||||
- `journey_actions`: launch, resume, review, correct, decide, publish, and
|
||||
reconcile commands;
|
||||
- `workspace_surfaces`: embeddable but owner-rendered list, detail, editor, and
|
||||
status surfaces;
|
||||
- `configuration_contributions`: guided settings with consequence and
|
||||
prerequisite metadata;
|
||||
- `help_contexts`: user/admin documentation for the product identity as well as
|
||||
the technical owner;
|
||||
- `technical_provenance`: module, interface version, capability, and provider
|
||||
identifiers shown only in details and evidence.
|
||||
|
||||
The contract references surfaces. It does not permit Core or a product package
|
||||
to import their implementation.
|
||||
|
||||
The versioned `product_surfaces` slice is implemented in Core. It
|
||||
binds a stable product identity and entry path to one or more owner routes,
|
||||
View surfaces, presentations, capabilities, search sources, help contexts and
|
||||
documentation topics. It also carries standard unavailable/degraded
|
||||
explanations and migration aliases. Mail and Postbox contribute the first
|
||||
shared identity, `communication.messages`: `/messages` and the migration alias
|
||||
`/inbox` select the first currently authorized, View-visible owner while the
|
||||
underlying `/mail` and `/postbox` deep links, custody and permissions remain
|
||||
unchanged. Tasks, Calendar and Files contribute the corresponding single-owner
|
||||
identities:
|
||||
|
||||
| Product identity | Stable destination | Compatible owner route |
|
||||
| --- | --- | --- |
|
||||
| Work | `/work` | `/tasks` |
|
||||
| Calendar | `/agenda` | `/calendar` |
|
||||
| Messages | `/messages` (`/inbox` alias) | `/mail`, `/postbox` |
|
||||
| Files | `/documents` | `/files` |
|
||||
|
||||
Core replaces those owner entries in the ordinary rail with the stable product
|
||||
destinations. A collapsed **All available tools** catalogue retains every
|
||||
authorized technical owner route independently of View focus; unauthorized
|
||||
entries are never disclosed. The original deep links remain valid, and all
|
||||
contributing owner paths keep the corresponding product entry active. Alias
|
||||
resolution emits a bounded client telemetry event before the redirect.
|
||||
|
||||
Core's `ProductAvailabilityState` is the shared presentation primitive for
|
||||
authorization, Policy, configuration, disabled, missing-capability, offline and
|
||||
provider-degraded states. Product language is primary; exact module,
|
||||
capability, provider and correlation provenance is available only in an
|
||||
expandable technical section.
|
||||
|
||||
## Navigation Model
|
||||
|
||||
The default shell should prioritize:
|
||||
|
||||
1. global search and create/resume commands;
|
||||
2. personal and function-bound work;
|
||||
3. configured product areas;
|
||||
4. pinned user destinations;
|
||||
5. administration and technical module inspection when authorized.
|
||||
|
||||
The baseline product areas are Work, Services and Cases, Records and
|
||||
Documents, Communication, Meetings and Decisions, Data and Assurance, and
|
||||
People and Responsibility. They are configurable system/tenant defaults and
|
||||
Views projections, not hard-coded repository groups. Empty areas disappear;
|
||||
single-destination areas may link directly; familiar tools may remain pinned.
|
||||
|
||||
The complete permission-derived module rail is available as the collapsed
|
||||
**All available tools** escape. It is deliberately independent of the active
|
||||
View while still enforcing authorization. Its ability to scroll is useful and
|
||||
is not itself the product defect. The defect is requiring people to infer a
|
||||
task or outcome from repository topology.
|
||||
|
||||
Task-local Work, Calendar, Messages and Files tools may be contributed to the
|
||||
optional `govoplan-quick-access` rail. Messages composes Mail, Postbox and
|
||||
future governed chat presentation without merging their channel semantics or
|
||||
state.
|
||||
|
||||
A module route remains a valid deep link. A product area may combine links and
|
||||
owner-rendered surfaces from several modules. When a required contribution is
|
||||
absent, the area explains the missing outcome rather than rendering a broken
|
||||
placeholder.
|
||||
|
||||
Views remain the projection mechanism. They may select product areas, routes,
|
||||
sections, commands, widgets, and fields. A view must not grant a permission or
|
||||
change data semantics. Policy can force, allow, or prohibit a surface at system,
|
||||
tenant, group, or user scope.
|
||||
|
||||
Core browser conformance exercises the German Anwohnerparkausweis reference
|
||||
context with Work, Calendar, Messages and Files entries, verifies that package
|
||||
owner labels are absent from the primary rail, expands the technical catalogue,
|
||||
and runs WCAG 2 A/AA checks over the result. Unit permutations cover two-owner,
|
||||
one-owner, unauthorized-owner and focused-View compositions.
|
||||
|
||||
## Error And Provenance Language
|
||||
|
||||
Normal errors answer:
|
||||
|
||||
- what the person was trying to achieve;
|
||||
- why it is unavailable or failed;
|
||||
- whether data was saved or an external effect may have occurred;
|
||||
- who can resolve it and where;
|
||||
- the correlation/evidence reference.
|
||||
|
||||
An expandable technical section may then identify the module, capability,
|
||||
provider, request, and version. This keeps the product intelligible without
|
||||
hiding operational truth.
|
||||
|
||||
## Migration
|
||||
|
||||
Core's product-area and Quick Access contracts, the optional Quick Access
|
||||
module, the first five providers and immutable View presentation revisions are
|
||||
implemented. The migration below now concerns broader classification and
|
||||
product-language adoption; it is not a prerequisite for safely enabling the
|
||||
first rail slice.
|
||||
|
||||
### Slice 1: inventory and aliases
|
||||
|
||||
- continue classifying every route, navigation item, widget, setting, search object, and
|
||||
help context by product area and object type;
|
||||
- extend the implemented product-surface aliases without removing existing deep links;
|
||||
- flag raw module IDs in ordinary-user labels and errors.
|
||||
|
||||
### Slice 2: work-first shell
|
||||
|
||||
- provide a generic work/exception/deadline aggregation capability;
|
||||
- make work areas and configured packages the default navigation;
|
||||
- move the complete module catalogue to administration and an optional power-
|
||||
user surface.
|
||||
- implement the configurable Quick Access rail through Core-mediated
|
||||
contributions, system/tenant/user resolution and View/Policy ceilings.
|
||||
|
||||
### Slice 3: composite journeys
|
||||
|
||||
- let product packages define journey launch/resume actions and default views;
|
||||
- let Workflow Engine activate a view and focus an owner surface without
|
||||
controlling authorization;
|
||||
- expose provider provenance and technical bindings on demand.
|
||||
|
||||
### Slice 4: enforceability
|
||||
|
||||
- make product classification mandatory for user-visible manifest surfaces;
|
||||
- reject duplicate product identities and missing owner routes in CI;
|
||||
- add browser tests proving that reference users can complete a journey without
|
||||
knowing module names.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- An ordinary user can describe every primary navigation item as work or an
|
||||
institutional object, not as a package.
|
||||
- A product package can remove irrelevant navigation while retaining deep-link
|
||||
and help integrity.
|
||||
- Missing optional modules produce an actionable product explanation.
|
||||
- Administrators can still inspect exact module, capability, provider, schema,
|
||||
and evidence provenance.
|
||||
- Module permutation tests prove that no product surface assumes an optional
|
||||
owner is installed.
|
||||
@@ -0,0 +1,241 @@
|
||||
# Quick Access And Product Areas
|
||||
|
||||
## Purpose
|
||||
|
||||
GovOPlaN presents institutional work without requiring ordinary users to
|
||||
understand the installed package graph. Two complementary projections provide
|
||||
that experience:
|
||||
|
||||
- **product areas** group destinations, objects, work and actions by the
|
||||
outcome a person recognizes;
|
||||
- **Quick Access** keeps a small set of task-local tools available without
|
||||
leaving the current page, case, record or Workflow context.
|
||||
|
||||
Technical modules remain the implementation, release and provenance boundary.
|
||||
Product areas and Quick Access are presentation contracts over those owners;
|
||||
they do not copy domain state or bypass authorization.
|
||||
|
||||
Implementation is tracked by Core #283 and #285, GovOPlaN's product-experience
|
||||
umbrella, Views, Policy and `govoplan-quick-access`.
|
||||
|
||||
The repository and product name is `govoplan-quick-access`, with module id
|
||||
`quick_access`. `govoplan-qar` was rejected because the abbreviation hides the
|
||||
purpose in package catalogues, diagnostics, permissions and operations.
|
||||
|
||||
## Implementation Status
|
||||
|
||||
The first production-shaped slice is implemented:
|
||||
|
||||
- Core validates and publishes versioned `product_areas` and
|
||||
`quick_access_tools` manifest contracts;
|
||||
- `govoplan-quick-access` derives its live catalogue from installed modules,
|
||||
persists optimistic-concurrency-protected system, tenant and user profiles,
|
||||
and resolves blocked, forced, ordered and stale preferences;
|
||||
- the shell hosts the optional right rail and one composed drawer with keyboard
|
||||
dismissal, focus return, responsive mobile behavior and full-page fallbacks;
|
||||
- Tasks, Calendar, Mail, Postbox and Files contribute the first owner-rendered
|
||||
tools; Mail and Postbox remain separate sections inside Messages;
|
||||
- immutable View revisions now carry grouped/flat navigation, product-area
|
||||
order and optional labels. Scoped Views therefore configure product
|
||||
presentation for system, tenant, group, user and Workflow contexts;
|
||||
- the expanded left rail groups classified destinations while retaining
|
||||
Dashboard and every authorized unclassified destination under More tools;
|
||||
- Core promotes Work (`/work`), Calendar (`/agenda`), Messages (`/messages`)
|
||||
and Files (`/documents`) into stable primary destinations and collapses the
|
||||
compatible owner routes under **All available tools**;
|
||||
- **All available tools** is permission-derived but independent of the active
|
||||
View, providing a deliberate escape without granting access or discarding
|
||||
the original `/tasks`, `/calendar`, `/mail`, `/postbox` and `/files` links.
|
||||
|
||||
The baseline classification and the four initial stable destinations are now
|
||||
manifest-declared. The area classification covers every ordinary user-facing
|
||||
module and is enforced by the workspace manifest check. A separately
|
||||
versioned launch-context contract carries bounded active-object, acting,
|
||||
temporal, View and return references into full-page Quick Access fallbacks;
|
||||
Cases publishes the first active-object reference. The remaining rollout is to
|
||||
add useful bounded tools and active-object publishers only where a maintained
|
||||
journey benefits. The pinned German Anwohnerparkausweis browser composition
|
||||
verifies stable product labels, technical escape, keyboard access and WCAG
|
||||
conformance. Authorized global and technical routes remain visible through
|
||||
their dedicated shell entry or **All available tools**.
|
||||
|
||||
## Quick Access Boundary
|
||||
|
||||
Core owns a versioned contribution contract. Feature modules may register a
|
||||
tool when they have a useful bounded surface. They do not import Quick Access.
|
||||
`govoplan-quick-access` owns configuration, effective resolution, ordering,
|
||||
the right-side rail and its drawer. Views may narrow tools for the current
|
||||
task. Policy may constrain availability and customization. Access and each
|
||||
owner's backend remain authoritative.
|
||||
|
||||
The initial categories are:
|
||||
|
||||
| Category | Typical contributions |
|
||||
| --- | --- |
|
||||
| Work | Explicit Tasks, Workflow handoffs, approvals, deadlines and exceptions |
|
||||
| Calendar | Today/upcoming agenda, event creation and scheduling launch |
|
||||
| Messages | Mail, function-bound Postbox messages and future governed chat providers |
|
||||
| Files | Contextual/recent files, attachment selection and upload |
|
||||
|
||||
Messages is one shell category but not one data model. Mail, Postbox and future
|
||||
chat providers retain their channel semantics, custody, policy, audit and
|
||||
delivery behavior. The drawer identifies the channel where that distinction
|
||||
matters.
|
||||
|
||||
## Contribution Contract
|
||||
|
||||
A Quick Access contribution declares:
|
||||
|
||||
- contract version 1, a stable id, category and human label;
|
||||
- icon, order and optional badge/summary provider;
|
||||
- required permissions and optional dependencies;
|
||||
- global or active-object availability, accepted context-reference kinds and
|
||||
produced result-reference kinds;
|
||||
- an owner-rendered bounded WebUI surface and full-page fallback route;
|
||||
- View surface, help context and availability explanation;
|
||||
- whether the contribution supports preview, create, select or resume.
|
||||
|
||||
The shell passes only bounded references: tenant, acting context, temporal
|
||||
read context, active task/Workflow, current institutional object, selected
|
||||
resources and a safe return location. The owner reauthorizes every read and
|
||||
effect. Credentials, protected content and permission decisions are never
|
||||
embedded in launch context.
|
||||
|
||||
Launch-context version 2 identifies reference contract version 1 and carries
|
||||
the exact resolved View revision plus optional recommended and focused tool
|
||||
ids. Recommendations affect order and emphasis only. Focus narrows the rail
|
||||
only when at least one focused contribution survives module enablement,
|
||||
configuration, context compatibility and authorization; otherwise the normal
|
||||
effective rail remains available. Workflow gets the same behavior by resolving
|
||||
the exact View revision instead of acquiring separate presentation authority.
|
||||
|
||||
An owner-rendered tool explicitly returns result contract version 1 as either
|
||||
`completed` with an action and typed owner reference, or `cancelled` with a
|
||||
reason. The shell correlates the result with the source and tool, rejects
|
||||
cross-tenant or undeclared reference kinds, and does not interpret closing the
|
||||
drawer as completion. Owner modules validate, persist, recover and audit their
|
||||
own effects. The overlay leaves the host route mounted, so unsaved host-page
|
||||
state is preserved; the full-page route remains the bounded-work fallback.
|
||||
|
||||
## Effective Configuration
|
||||
|
||||
The effective rail is resolved from:
|
||||
|
||||
1. installed and enabled modules and their registered contributions;
|
||||
2. system availability, forced entries and ordering defaults;
|
||||
3. tenant availability, forced entries and ordering defaults;
|
||||
4. group and user View/Policy ceilings where configured;
|
||||
5. the user's enabled categories, entries and ordering;
|
||||
6. the active View and optional Workflow-step narrowing overlay;
|
||||
7. current authorization and contribution availability.
|
||||
|
||||
Lower scopes may narrow or reorder allowed entries but cannot enable a tool
|
||||
blocked above them. A forced entry cannot be removed below its source. User
|
||||
configuration stores stable contribution ids; unavailable or retired ids are
|
||||
retained as explained stale preferences without rendering broken controls.
|
||||
|
||||
Configuration screens derive their available choices from the live registry.
|
||||
Installing or enabling a contributing module adds its permitted choices;
|
||||
disabling it removes the runtime tool while preserving harmless preferences.
|
||||
If Quick Access is absent, contributors behave exactly as before.
|
||||
|
||||
## Interaction Model
|
||||
|
||||
Desktop uses a narrow right-side rail with at most four initial category
|
||||
buttons and an overflow when an administrator or user adds more categories.
|
||||
Selecting a category opens one fixed, owner-neutral drawer. Contributions are
|
||||
shown inside that drawer as tabs, sections or commands according to the
|
||||
category contract. The default drawer overlays content so DataGrid and fixed
|
||||
workspace layouts do not resize unexpectedly; a later explicit pinned mode may
|
||||
reserve layout width on sufficiently wide screens.
|
||||
|
||||
The drawer preserves host-page state, has a deterministic focus return, closes
|
||||
with Escape, supports keyboard traversal, and provides explicit completion,
|
||||
cancellation and full-page actions. Mobile and narrow layouts use the same
|
||||
category/configuration semantics in a bottom sheet or compact menu.
|
||||
|
||||
## Product Areas
|
||||
|
||||
Product areas are stable configurable identities, not repositories. The
|
||||
recommended baseline is:
|
||||
|
||||
- Work;
|
||||
- Services and Cases;
|
||||
- Records and Documents;
|
||||
- Communication;
|
||||
- Meetings and Decisions;
|
||||
- Data and Assurance;
|
||||
- People and Responsibility.
|
||||
|
||||
Modules contribute routes, objects, actions, widgets, work sources and help to
|
||||
one or more areas. Product packages and administrators may define sensible
|
||||
system and tenant defaults. Views select, order, rename or narrow allowed
|
||||
areas, and users may personalize them within Policy ceilings. An empty area is
|
||||
omitted. An area with one destination may open it directly. A multi-destination
|
||||
area provides a useful work/recent/action surface rather than another menu.
|
||||
|
||||
Familiar product nouns such as Calendar or Files remain direct product
|
||||
destinations. The objective is not to hide every implementation name from
|
||||
administrators; it is to prevent repository topology from determining a
|
||||
person's workflow.
|
||||
|
||||
The initial module classification is deliberately outcome-oriented:
|
||||
|
||||
| Product area | Contributing user-facing modules |
|
||||
| --- | --- |
|
||||
| Work | Approvals, Projects, Tasks, Workflow |
|
||||
| Services and Cases | Cases, Forms, Forms Runtime, Portal |
|
||||
| Records and Documents | Files, Records, Templates |
|
||||
| Communication | Campaigns, Distribution Lists, Mail, Notifications, Postbox |
|
||||
| Meetings and Decisions | Calendar, Committee, Scheduling, Voting |
|
||||
| Data and Assurance | Dataflow, Datasources, Reporting, Risk Compliance |
|
||||
| People and Responsibility | Address Book, IDM, Organizations |
|
||||
|
||||
Dashboard, Search, Documentation and Quick Access remain global shell
|
||||
affordances. Access, Administration, Audit, Encryption, Identity Trust,
|
||||
Operations, Policy, Tenancy and Views remain administrative or platform
|
||||
surfaces available through their dedicated entry point or **All available
|
||||
tools**. The manifest-shape check enforces both this explicit exception set and
|
||||
the shared label, icon, description and ordering of every canonical area.
|
||||
|
||||
## Full Access And Provenance
|
||||
|
||||
The existing permission-derived module rail remains available as **All
|
||||
available tools** for power users and deliberate escape from a focused View.
|
||||
It contains only currently authorized destinations. Technical module,
|
||||
capability, provider and package provenance remains visible in administration,
|
||||
diagnostics, evidence and expandable details.
|
||||
|
||||
Search, deep links and help distinguish three states:
|
||||
|
||||
- available in the active View;
|
||||
- authorized but outside the active View, with a temporary escape or View
|
||||
switch;
|
||||
- unavailable because of authorization, Policy, configuration or a missing
|
||||
capability, with an actionable explanation.
|
||||
|
||||
## Delivery Order
|
||||
|
||||
1. Define Core product-area and Quick Access contracts and validation.
|
||||
2. Implement `govoplan-quick-access` configuration, effective resolution and
|
||||
shell capability.
|
||||
3. Contribute Work, Calendar, Messages and Files bounded surfaces.
|
||||
4. Add configurable product-area defaults through Views and product packages.
|
||||
5. Migrate navigation, breadcrumbs, search, errors, documentation, dashboard
|
||||
and administration toward product terminology.
|
||||
6. Prove keyboard, focus, responsive, optional-module and reference-journey
|
||||
behavior before making it the ordinary-user default.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- A user can configure allowed Quick Access categories and ordering without
|
||||
gaining authority.
|
||||
- System and tenant administrators can make entries available, forced or
|
||||
unavailable with provenance.
|
||||
- Mail, Postbox and another future channel can share Messages presentation
|
||||
while retaining independent state and channel semantics.
|
||||
- A reference journey can use a bounded tool and return without losing host
|
||||
state or Workflow context.
|
||||
- Product areas remain useful under sparse and rich permission sets and under
|
||||
optional-module permutations.
|
||||
- All available tools and technical provenance remain deliberately reachable.
|
||||
@@ -0,0 +1,153 @@
|
||||
# GovOPlaN Views Architecture
|
||||
|
||||
## Purpose
|
||||
|
||||
GovOPlaN Views are governed presentation projections for a task,
|
||||
responsibility, or workflow step. A View can reduce the visible modules,
|
||||
navigation entries, routes, page sections, and commands to the interface
|
||||
needed for the current job.
|
||||
|
||||
Views also project configurable product areas and Quick Access contributions.
|
||||
They may select, order, rename or hide permitted presentation identities but
|
||||
do not move ownership or merge Mail, Postbox, Files, Calendar, Tasks or other
|
||||
domain state.
|
||||
|
||||
Views are optional. If `govoplan-views` is not installed or enabled, the normal
|
||||
permission-derived interface remains unchanged.
|
||||
|
||||
## Security Boundary
|
||||
|
||||
A View is not an authorization mechanism.
|
||||
|
||||
- Access, tenant isolation, resource guards, and backend permission checks
|
||||
remain authoritative.
|
||||
- A View may hide an interface surface that the actor is otherwise allowed to
|
||||
use.
|
||||
- A View can never expose a route, action, tenant, or resource that normal
|
||||
authorization denies.
|
||||
- An authorized deep link outside the current View should offer an explicit
|
||||
temporary escape or View switch. It must not be presented as a permission
|
||||
denial.
|
||||
|
||||
This boundary lets Views improve focus without creating a second, weaker RBAC
|
||||
system.
|
||||
|
||||
## Ownership
|
||||
|
||||
Core owns the versioned, module-neutral surface contract and WebUI runtime
|
||||
hooks. Modules declare stable surfaces and use shared hooks to respect the
|
||||
effective projection. Modules do not import `govoplan-views`.
|
||||
|
||||
`govoplan-views` owns:
|
||||
|
||||
- draft and immutable published View revisions
|
||||
- system, tenant, group, and user assignments
|
||||
- default, mandatory, and user-selectable Views
|
||||
- active per-user View state
|
||||
- effective projection resolution and provenance
|
||||
- the View editor, preview, validation, and stale-surface diagnostics
|
||||
|
||||
Policy optionally owns inherited ceilings and explainable decisions. Workflow
|
||||
optionally references a pinned View revision for an instance or step and may
|
||||
narrow it further.
|
||||
|
||||
## Surface Contract
|
||||
|
||||
Modules announce only useful, semantic surfaces:
|
||||
|
||||
- module
|
||||
- navigation item
|
||||
- route or workspace
|
||||
- section or panel
|
||||
- command or action
|
||||
|
||||
Each descriptor has a stable namespaced id, parent id, kind, label, default
|
||||
visibility, ordering, and dependency metadata where needed. Surface ids are
|
||||
public module contracts, not CSS selectors, component paths, or arbitrary DOM
|
||||
fragments.
|
||||
|
||||
The first release supports visible or hidden. Read-only states, layout
|
||||
replacement, visual emphasis, and arbitrary styling are separate concerns and
|
||||
are deferred.
|
||||
|
||||
## Effective Resolution
|
||||
|
||||
The effective interface is the intersection of:
|
||||
|
||||
1. installed and enabled modules
|
||||
2. actor permissions and resource access
|
||||
3. administrator and Policy ceilings
|
||||
4. an assigned or user-selected View
|
||||
5. an optional workflow instance or step overlay
|
||||
|
||||
Lower scopes and workflow overlays may narrow inherited visibility but cannot
|
||||
broaden it. Every inherited, locked, hidden, unavailable, or stale choice
|
||||
should carry provenance that the editor and runtime can explain.
|
||||
|
||||
Published View revisions are immutable. Active workflow instances pin the
|
||||
revision they use. Unknown or retired surface ids produce diagnostics rather
|
||||
than breaking startup. If no valid effective View can be resolved, the system
|
||||
uses the last valid projection or the normal authorized interface and reports
|
||||
the configuration problem to administrators.
|
||||
|
||||
## Workflow Behavior
|
||||
|
||||
A workflow definition may reference a View for the whole instance or a
|
||||
particular step. Starting, resuming, or advancing the workflow activates the
|
||||
appropriate projection. Users can intentionally leave focused mode and return
|
||||
from an open-work widget or notification without losing workflow state.
|
||||
|
||||
Module handoffs carry the workflow and View context through Core contracts.
|
||||
Workflow does not import the target module or the Views implementation.
|
||||
|
||||
## Delivery Order
|
||||
|
||||
1. Define the Core surface registry and runtime hooks.
|
||||
2. Initialize `govoplan-views` and persist versioned definitions.
|
||||
3. Add assignment, selection, resolution, provenance, and the editor.
|
||||
4. Add Policy inheritance and administrator ceilings.
|
||||
5. Add Workflow instance and step activation.
|
||||
6. Adopt semantic section/action descriptors module by module.
|
||||
|
||||
## Implementation Status
|
||||
|
||||
Implemented in the initial Views slice:
|
||||
|
||||
- Core contract version `1`, stable module/navigation/route identifiers, custom
|
||||
section/action descriptors, manifest validation, and platform API metadata
|
||||
- shell navigation, route-boundary, settings, administration, dashboard-widget,
|
||||
embedded-capability, and organization-action filtering
|
||||
- `govoplan-views` definitions, immutable revisions, system/tenant/group/user
|
||||
assignments, user selection, provenance, and stale-surface recovery
|
||||
- a system and tenant administration editor with unsaved-change protection,
|
||||
publish/archive controls, assignment management, and server-enforced lockout
|
||||
prevention
|
||||
- surface declarations for every currently installed module that contributes a
|
||||
WebUI, including finer-grained shared administration and settings surfaces
|
||||
- immutable presentation settings for grouped or flat navigation, product-area
|
||||
order and product-area labels; the shell resolves these settings through the
|
||||
same system, tenant, group, user and Workflow-aware View projection
|
||||
- live product-area identities from module manifests, with authorized
|
||||
unclassified destinations retained under More tools during incremental
|
||||
adoption
|
||||
|
||||
Still intentionally separate:
|
||||
|
||||
- Policy-owned inherited ceilings and policy decision provenance
|
||||
- workflow-instance and workflow-step activation of pinned View revisions
|
||||
- read-only and layout-replacement projections beyond the version `1`
|
||||
visible/hidden contract
|
||||
|
||||
Quick Access ordering and availability remain owned by
|
||||
`govoplan-quick-access`; Views only narrow its declared surfaces for the active
|
||||
task. Neither contract permits arbitrary layout or styling. See
|
||||
`docs/architecture/QUICK_ACCESS_AND_PRODUCT_AREAS.md` in the meta repository.
|
||||
|
||||
## Gitea Work Packages
|
||||
|
||||
- `govoplan#17`: task-focused Views user story
|
||||
- `govoplan#16`: initialize and implement `govoplan-views`
|
||||
- `govoplan-core#271`: versioned surface and runtime contracts
|
||||
- `govoplan-policy#9`: inheritance, ceilings, and provenance
|
||||
- `govoplan-workflow#7`: workflow instance and step activation
|
||||
- `govoplan-workflow#3`: focused workflow mode user story
|
||||
+5
-1
@@ -1,5 +1,9 @@
|
||||
# Meta Repository Migration Audit
|
||||
|
||||
> **Archived migration record:** The ownership migration described here is
|
||||
> complete. Current boundaries are defined by Repository Structure, module
|
||||
> manifests, and the owning repositories.
|
||||
|
||||
This audit records which existing GovOPlaN files should move toward the
|
||||
`govoplan` meta repository and which should remain with their current runtime
|
||||
owner.
|
||||
@@ -148,7 +152,7 @@ It should not own:
|
||||
Known references reviewed after the server-side rename:
|
||||
|
||||
- `govoplan/repositories.json`
|
||||
- `govoplan/docs/REPOSITORY_STRUCTURE.md`
|
||||
- `govoplan/docs/project/REPOSITORY_STRUCTURE.md`
|
||||
- `govoplan/docker/README.md`
|
||||
- `govoplan-core/docs/RELEASE_DEPENDENCIES.md`
|
||||
- `govoplan-core/docs/MODULE_ARCHITECTURE.md`
|
||||
@@ -1,5 +1,8 @@
|
||||
# Meta Repository Scan
|
||||
|
||||
> **Archived assessment:** This file records the 2026-07-13 repository state.
|
||||
> Use `repositories.json` and the current documentation map for present state.
|
||||
|
||||
Scan date: 2026-07-13.
|
||||
|
||||
This scan checked local repositories under `/mnt/DATA/git` listed in
|
||||
@@ -13,7 +16,7 @@ Checked-out repositories not listed in `repositories.json`: none.
|
||||
|
||||
Repositories listed in `repositories.json` but not checked out locally: none.
|
||||
|
||||
The human-readable link index is `docs/REPOSITORY_INDEX.md`; the JSON file
|
||||
The human-readable link index is `docs/project/REPOSITORY_INDEX.md`; the JSON file
|
||||
remains the machine-readable source of truth.
|
||||
|
||||
## Meta-Owned Content
|
||||
@@ -0,0 +1,135 @@
|
||||
# Strategic Review - 2026-08-05
|
||||
|
||||
> **Archived assessment:** This review explains the 2026-08-05 strategy reset.
|
||||
> It is not updated with later implementation or portfolio state.
|
||||
|
||||
## Assessment
|
||||
|
||||
GovOPlaN has not lost its central direction. The architecture now expresses a
|
||||
coherent institutional governance platform, but architecture and repository
|
||||
breadth have advanced faster than complete, usable outcomes. The immediate
|
||||
need is convergence: fewer simultaneous fronts, stronger cross-cutting
|
||||
adoption, and end-to-end reference journeys that non-developers can complete.
|
||||
|
||||
This is a dated review. Current status belongs in
|
||||
[Strategy Status](../../strategy/STRATEGY_STATUS.md); stable direction belongs in
|
||||
[Platform Core Ideas](../../strategy/PLATFORM_CORE_IDEAS.md).
|
||||
|
||||
## What Is Already Strong
|
||||
|
||||
- A modular runtime with manifests, capabilities, interfaces, migrations,
|
||||
optional integrations, signed releases, and permutation checks.
|
||||
- Explicit institutional semantics for identity, representation,
|
||||
organization, function, mandate, service, case, party, approval, decision,
|
||||
evidence, and record references.
|
||||
- Governed communication foundations spanning Campaign, Mail, Files, Postbox,
|
||||
Addresses, Distribution Lists, Templates, Audit, and Policy.
|
||||
- Governed data foundations spanning Connectors, Datasources, Dataflow,
|
||||
Reporting, Search, and immutable provenance.
|
||||
- Bitemporal browsing, views, contextual documentation, action/effect
|
||||
contracts, event delivery, recovery ledgers, and stateless deployment
|
||||
contracts.
|
||||
- A credible deployment and release foundation with signed artifacts and
|
||||
reproducible composition evidence.
|
||||
|
||||
## Where The Program Veered
|
||||
|
||||
### Repository breadth preceded product proof
|
||||
|
||||
Logical modularity often became a repository before a reference journey proved
|
||||
that an independent release boundary was required. Scaffolds are useful as
|
||||
ownership markers, but their number makes the product appear broader and more
|
||||
complete than its supported outcomes.
|
||||
|
||||
### Foundations outran reference gates
|
||||
|
||||
Later-stage contracts such as federation, encryption, formal governance,
|
||||
deployment evidence, and broad module metadata were developed while basic
|
||||
human-work and records journeys remained incomplete. Those foundations are not
|
||||
wasted; they now need to be consumed by a small number of demonstrable
|
||||
products.
|
||||
|
||||
### The module graph leaked into the experience
|
||||
|
||||
Navigation, routes, administration, errors, documentation, and configuration
|
||||
often present module names and package structure directly. This is appropriate
|
||||
for operators, but ordinary users should see work, services, records, and
|
||||
outcomes.
|
||||
|
||||
This is not primarily a rail-length or scrolling problem. Sparse permissions
|
||||
already reduce navigation and the complete technical rail remains useful for
|
||||
power users. The correction is configurable product areas, task-focused Views
|
||||
and a bounded Quick Access rail, while preserving deliberate access to every
|
||||
authorized tool and technical provenance. The accepted design is maintained in
|
||||
[Quick Access And Product Areas](../../architecture/QUICK_ACCESS_AND_PRODUCT_AREAS.md).
|
||||
|
||||
### Status became duplicated
|
||||
|
||||
Roadmaps, target architecture, fit assessments, issue comments, and release
|
||||
documents each contained partial implementation snapshots. Their stable
|
||||
decisions remain valuable, but volatile counts and maturity claims diverged.
|
||||
|
||||
### Too much work remained active simultaneously
|
||||
|
||||
The issue portfolio had many high-priority and in-progress items without
|
||||
milestones. This reduces the signal of both labels and roadmap order and makes
|
||||
completion harder to demonstrate.
|
||||
|
||||
## Where GovOPlaN Has Not Gone Far Enough
|
||||
|
||||
1. No composition has yet crossed the full `reference_ready` gate.
|
||||
2. The human-work spine is incomplete: work queues, tasks, handoffs, deadlines,
|
||||
reminders, escalation, and resumption need a coherent user experience.
|
||||
3. Records and document management remain too shallow for a public-sector
|
||||
operating platform.
|
||||
4. Real target integrations and GovOPlaN-to-GovOPlaN federation are not yet
|
||||
proven.
|
||||
5. Temporal browsing, purpose-aware access, retention, and institutional
|
||||
context exist as contracts but are not adopted uniformly by domain reads
|
||||
and effects.
|
||||
6. German completeness, contextual help, accessibility, responsive behavior,
|
||||
and browser-level journey testing are not yet release gates everywhere.
|
||||
7. Multi-host, backup/restore, provider interoperability, and independent
|
||||
signed target evidence still require real environments and operators.
|
||||
|
||||
## Important Omissions
|
||||
|
||||
- a named first institution, bounded users, volumes, and operating constraints;
|
||||
- measurable usability outcomes, not only functional tests;
|
||||
- installable sector packages and migration/exit demonstrations;
|
||||
- support, upgrade, deprecation, and LTS promises;
|
||||
- complete assisted, paper, telephone, and in-person channel handling;
|
||||
- a native eAkte/records model that can also overlay an external DMS or archive.
|
||||
|
||||
## Opportunities Beyond The Original Idea
|
||||
|
||||
- an institutional digital twin that exposes responsibilities, dependencies,
|
||||
obligations, services, work, data, controls, and change impact over time;
|
||||
- continuous assurance that evaluates controls and evidence as work happens;
|
||||
- process mining and conformance analysis over governed event histories;
|
||||
- federated product packages and inter-institution case/evidence exchange;
|
||||
- accountable assistance that drafts and explains without obscuring authority;
|
||||
- public evidence chains that disclose decisions and provenance without
|
||||
exposing protected source data.
|
||||
|
||||
## Recommended Reset
|
||||
|
||||
1. Freeze new repositories unless a real journey proves an independent owner,
|
||||
release lifecycle, security boundary, or optional installation need.
|
||||
2. Use one generated maturity/status dashboard and one current status document.
|
||||
3. Complete governed communication and function-bound Postbox against a real
|
||||
target.
|
||||
4. Complete the monthly-data journey, then sanctions screening on the same
|
||||
data foundations.
|
||||
5. Complete one browser-driven service-to-decision journey, including assisted
|
||||
intake and records.
|
||||
6. Make eAkte/records the next major product-depth program.
|
||||
7. Tie feature work to a reference journey, a security/recovery gate, or a
|
||||
measured usability defect.
|
||||
|
||||
## Success Criterion
|
||||
|
||||
The reset succeeds when a public institution can install a signed composition,
|
||||
configure a named procedure, complete it through digital and assisted channels,
|
||||
connect an external source, reconstruct the authority and evidence, recover it
|
||||
after failure, and transfer or retire it without custom code.
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://govoplan.add-ideas.de/schemas/backup-evidence-keyring-v1.json",
|
||||
"title": "GovOPlaN backup evidence trust keyring",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema_version", "purpose", "keys"],
|
||||
"properties": {
|
||||
"schema_version": { "const": "1" },
|
||||
"purpose": { "const": "govoplan-backup-evidence" },
|
||||
"keys": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 64,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"key_id",
|
||||
"algorithm",
|
||||
"status",
|
||||
"public_key_pem",
|
||||
"not_before",
|
||||
"expires_at"
|
||||
],
|
||||
"properties": {
|
||||
"key_id": { "type": "string", "minLength": 1, "maxLength": 128 },
|
||||
"algorithm": { "const": "ed25519" },
|
||||
"status": { "enum": ["active", "retired", "revoked"] },
|
||||
"public_key_pem": { "type": "string", "minLength": 1, "maxLength": 8192 },
|
||||
"not_before": { "type": "string", "format": "date-time" },
|
||||
"expires_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://govoplan.add-ideas.de/schemas/backup-evidence-v1.json",
|
||||
"title": "GovOPlaN coordinated backup and restore evidence",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"evidence_id",
|
||||
"installation_id",
|
||||
"deployment_subject",
|
||||
"release",
|
||||
"recovery_point",
|
||||
"components",
|
||||
"restore_drill",
|
||||
"issued_at",
|
||||
"expires_at",
|
||||
"revoked",
|
||||
"signatures"
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": { "const": "1" },
|
||||
"evidence_id": { "$ref": "#/$defs/token" },
|
||||
"installation_id": { "$ref": "#/$defs/token" },
|
||||
"deployment_subject": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["profile", "topology", "subject_ref"],
|
||||
"properties": {
|
||||
"profile": { "enum": ["evaluation", "self-hosted"] },
|
||||
"topology": { "$ref": "#/$defs/token" },
|
||||
"subject_ref": { "$ref": "#/$defs/reference" }
|
||||
}
|
||||
},
|
||||
"release": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"channel",
|
||||
"version",
|
||||
"manifest_sha256",
|
||||
"composition_sha256",
|
||||
"api_image",
|
||||
"web_image"
|
||||
],
|
||||
"properties": {
|
||||
"channel": { "$ref": "#/$defs/token" },
|
||||
"version": { "$ref": "#/$defs/token" },
|
||||
"manifest_sha256": { "$ref": "#/$defs/sha256" },
|
||||
"composition_sha256": { "$ref": "#/$defs/sha256" },
|
||||
"api_image": { "$ref": "#/$defs/digest_image" },
|
||||
"web_image": { "$ref": "#/$defs/digest_image" }
|
||||
}
|
||||
},
|
||||
"recovery_point": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["id", "captured_at", "consistency", "rpo_seconds", "write_fence"],
|
||||
"properties": {
|
||||
"id": { "$ref": "#/$defs/token" },
|
||||
"captured_at": { "type": "string", "format": "date-time" },
|
||||
"consistency": {
|
||||
"enum": ["provider-atomic", "application-quiesced", "transaction-consistent"]
|
||||
},
|
||||
"rpo_seconds": { "$ref": "#/$defs/duration" },
|
||||
"write_fence": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["mode", "token_sha256", "established_at"],
|
||||
"properties": {
|
||||
"mode": {
|
||||
"enum": ["provider-snapshot", "application-quiesce", "transaction-boundary"]
|
||||
},
|
||||
"token_sha256": { "$ref": "#/$defs/sha256" },
|
||||
"established_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["database", "objects", "configuration", "key_custody"],
|
||||
"properties": {
|
||||
"database": { "$ref": "#/$defs/database" },
|
||||
"objects": { "$ref": "#/$defs/objects" },
|
||||
"configuration": { "$ref": "#/$defs/configuration" },
|
||||
"key_custody": { "$ref": "#/$defs/key_custody" }
|
||||
}
|
||||
},
|
||||
"restore_drill": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"drill_id",
|
||||
"recovery_point_id",
|
||||
"started_at",
|
||||
"completed_at",
|
||||
"isolated_target_ref",
|
||||
"release_manifest_sha256",
|
||||
"migration_heads_sha256",
|
||||
"representative_object_manifest_sha256",
|
||||
"database_verified",
|
||||
"objects_verified",
|
||||
"configuration_verified",
|
||||
"key_custody_verified",
|
||||
"semantic_checks",
|
||||
"measured_rpo_seconds",
|
||||
"measured_rto_seconds",
|
||||
"evidence_ref"
|
||||
],
|
||||
"properties": {
|
||||
"drill_id": { "$ref": "#/$defs/token" },
|
||||
"recovery_point_id": { "$ref": "#/$defs/token" },
|
||||
"started_at": { "type": "string", "format": "date-time" },
|
||||
"completed_at": { "type": "string", "format": "date-time" },
|
||||
"isolated_target_ref": { "$ref": "#/$defs/reference" },
|
||||
"release_manifest_sha256": { "$ref": "#/$defs/sha256" },
|
||||
"migration_heads_sha256": { "$ref": "#/$defs/sha256" },
|
||||
"representative_object_manifest_sha256": { "$ref": "#/$defs/sha256" },
|
||||
"database_verified": { "const": true },
|
||||
"objects_verified": { "const": true },
|
||||
"configuration_verified": { "const": true },
|
||||
"key_custody_verified": { "const": true },
|
||||
"semantic_checks": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 128,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["id", "status", "evidence_ref"],
|
||||
"properties": {
|
||||
"id": { "$ref": "#/$defs/token" },
|
||||
"status": { "const": "passed" },
|
||||
"evidence_ref": { "$ref": "#/$defs/reference" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"measured_rpo_seconds": { "$ref": "#/$defs/duration" },
|
||||
"measured_rto_seconds": { "$ref": "#/$defs/duration" },
|
||||
"evidence_ref": { "$ref": "#/$defs/reference" }
|
||||
}
|
||||
},
|
||||
"issued_at": { "type": "string", "format": "date-time" },
|
||||
"expires_at": { "type": "string", "format": "date-time" },
|
||||
"revoked": { "const": false },
|
||||
"signatures": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 16,
|
||||
"items": { "$ref": "#/$defs/signature" }
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"token": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 128,
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
|
||||
},
|
||||
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
||||
"digest_image": {
|
||||
"type": "string",
|
||||
"maxLength": 300,
|
||||
"pattern": "^[^@\\s]+@sha256:[0-9a-f]{64}$"
|
||||
},
|
||||
"reference": {
|
||||
"type": "string",
|
||||
"minLength": 3,
|
||||
"maxLength": 2048,
|
||||
"pattern": "^[A-Za-z][A-Za-z0-9+.-]*:[^\\s]+$"
|
||||
},
|
||||
"duration": { "type": "integer", "minimum": 0, "maximum": 2592000 },
|
||||
"protected_key": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"protected": { "const": true },
|
||||
"encryption_key_ref": { "$ref": "#/$defs/reference" },
|
||||
"captured_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"database": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"provider", "artifact_ref", "artifact_sha256", "snapshot_id", "lsn",
|
||||
"protected", "encryption_key_ref", "captured_at"
|
||||
],
|
||||
"properties": {
|
||||
"provider": { "$ref": "#/$defs/token" },
|
||||
"artifact_ref": { "$ref": "#/$defs/reference" },
|
||||
"artifact_sha256": { "$ref": "#/$defs/sha256" },
|
||||
"snapshot_id": { "$ref": "#/$defs/token" },
|
||||
"lsn": { "type": "string", "minLength": 1, "maxLength": 256 },
|
||||
"protected": { "const": true },
|
||||
"encryption_key_ref": { "$ref": "#/$defs/reference" },
|
||||
"captured_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"objects": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"provider", "artifact_ref", "manifest_sha256", "version_id",
|
||||
"object_count", "total_bytes", "protected", "encryption_key_ref", "captured_at"
|
||||
],
|
||||
"properties": {
|
||||
"provider": { "$ref": "#/$defs/token" },
|
||||
"artifact_ref": { "$ref": "#/$defs/reference" },
|
||||
"manifest_sha256": { "$ref": "#/$defs/sha256" },
|
||||
"version_id": { "$ref": "#/$defs/token" },
|
||||
"object_count": { "type": "integer", "minimum": 0 },
|
||||
"total_bytes": { "type": "integer", "minimum": 0 },
|
||||
"protected": { "const": true },
|
||||
"encryption_key_ref": { "$ref": "#/$defs/reference" },
|
||||
"captured_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"configuration": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["artifact_ref", "sha256", "protected", "encryption_key_ref", "captured_at"],
|
||||
"properties": {
|
||||
"artifact_ref": { "$ref": "#/$defs/reference" },
|
||||
"sha256": { "$ref": "#/$defs/sha256" },
|
||||
"protected": { "const": true },
|
||||
"encryption_key_ref": { "$ref": "#/$defs/reference" },
|
||||
"captured_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"key_custody": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["provider", "keyset_ref", "keyset_version", "recoverable", "captured_at"],
|
||||
"properties": {
|
||||
"provider": { "$ref": "#/$defs/token" },
|
||||
"keyset_ref": { "$ref": "#/$defs/reference" },
|
||||
"keyset_version": { "$ref": "#/$defs/token" },
|
||||
"recoverable": { "const": true },
|
||||
"captured_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"signature": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["key_id", "algorithm", "value"],
|
||||
"properties": {
|
||||
"key_id": { "$ref": "#/$defs/token" },
|
||||
"algorithm": { "const": "ed25519" },
|
||||
"value": { "type": "string", "minLength": 1, "maxLength": 256 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://git.add-ideas.de/GovOPlaN/govoplan/src/branch/main/docs/capability-fit-boundary-evidence.schema.json",
|
||||
"title": "GovOPlaN externally issued capability-fit boundary evidence",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"evidence_kind",
|
||||
"proof_id",
|
||||
"assessment_id",
|
||||
"assessment_release",
|
||||
"installed_evidence_sha256",
|
||||
"issued_at",
|
||||
"expires_at",
|
||||
"claims",
|
||||
"signatures"
|
||||
],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"format": "uri-reference"
|
||||
},
|
||||
"schema_version": {
|
||||
"const": "0.1.0"
|
||||
},
|
||||
"evidence_kind": {
|
||||
"const": "govoplan.capability-fit-boundary-proof"
|
||||
},
|
||||
"proof_id": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
},
|
||||
"assessment_id": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
},
|
||||
"assessment_release": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
},
|
||||
"installed_evidence_sha256": {
|
||||
"$ref": "#/$defs/sha256"
|
||||
},
|
||||
"issued_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"expires_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"claims": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 8,
|
||||
"items": {
|
||||
"$ref": "#/$defs/claim"
|
||||
}
|
||||
},
|
||||
"signatures": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 16,
|
||||
"items": {
|
||||
"$ref": "#/$defs/signature"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"opaque_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160,
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
||||
},
|
||||
"claim": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["scope", "result", "subject_id", "control_ids", "artifacts"],
|
||||
"properties": {
|
||||
"scope": {
|
||||
"enum": [
|
||||
"target_environment",
|
||||
"external_providers",
|
||||
"accessibility",
|
||||
"privacy",
|
||||
"security",
|
||||
"operations",
|
||||
"recovery",
|
||||
"production_approval"
|
||||
]
|
||||
},
|
||||
"result": {
|
||||
"enum": ["passed", "failed", "approved", "rejected"]
|
||||
},
|
||||
"subject_id": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
},
|
||||
"control_ids": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 256,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
}
|
||||
},
|
||||
"artifacts": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 256,
|
||||
"items": {
|
||||
"$ref": "#/$defs/evidence_artifact"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"evidence_artifact": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["artifact_id", "sha256"],
|
||||
"properties": {
|
||||
"artifact_id": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
},
|
||||
"sha256": {
|
||||
"$ref": "#/$defs/sha256"
|
||||
}
|
||||
}
|
||||
},
|
||||
"signature": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["algorithm", "key_id", "value"],
|
||||
"properties": {
|
||||
"algorithm": {
|
||||
"const": "ed25519"
|
||||
},
|
||||
"key_id": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 256,
|
||||
"contentEncoding": "base64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sha256": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-f]{64}$"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://git.add-ideas.de/GovOPlaN/govoplan/src/branch/main/docs/capability-fit-boundary-run.schema.json",
|
||||
"title": "GovOPlaN private target-run claim manifest",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"evidence_kind",
|
||||
"proof_id",
|
||||
"expires_at",
|
||||
"claims"
|
||||
],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"format": "uri-reference"
|
||||
},
|
||||
"schema_version": {
|
||||
"const": "0.1.0"
|
||||
},
|
||||
"evidence_kind": {
|
||||
"const": "govoplan.capability-fit-boundary-run"
|
||||
},
|
||||
"proof_id": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
},
|
||||
"expires_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"claims": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 8,
|
||||
"items": {
|
||||
"$ref": "#/$defs/claim"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"opaque_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160,
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
||||
},
|
||||
"claim": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["scope", "result", "control_ids", "artifacts"],
|
||||
"properties": {
|
||||
"scope": {
|
||||
"enum": [
|
||||
"target_environment",
|
||||
"external_providers",
|
||||
"accessibility",
|
||||
"privacy",
|
||||
"security",
|
||||
"operations",
|
||||
"recovery",
|
||||
"production_approval"
|
||||
]
|
||||
},
|
||||
"result": {
|
||||
"enum": ["passed", "failed", "approved", "rejected"]
|
||||
},
|
||||
"control_ids": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 256,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
}
|
||||
},
|
||||
"artifacts": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 256,
|
||||
"items": {
|
||||
"$ref": "#/$defs/artifact"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"artifact": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["artifact_id", "path"],
|
||||
"properties": {
|
||||
"artifact_id": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 4096
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "./capability-fit.schema.json",
|
||||
"schema_version": "0.1.0",
|
||||
"schema_version": "0.2.0",
|
||||
"assessment_id": "campaign-reference-2026-07-22",
|
||||
"assessed_at": "2026-07-22",
|
||||
"scope": {
|
||||
@@ -13,16 +13,30 @@
|
||||
"Workflow and workflow-driven user stories"
|
||||
]
|
||||
},
|
||||
"facts": [
|
||||
"The assessment is pinned to signed stable catalog sequence 202607220843 and the exact module commits listed below.",
|
||||
"The Campaign authoring, validation, build, mock-delivery, managed-file, local-access, and local-audit paths have direct test or contract evidence.",
|
||||
"The production-like development profile runs PostgreSQL and Redis in containers while application processes use editable source trees.",
|
||||
"No installed-target, external-provider, reference-readiness, recovery, or production-approval evidence bundle is attached to this assessment."
|
||||
],
|
||||
"decisions": [
|
||||
"Use Campaign as the first reference journey and flagship pilot scenario.",
|
||||
"Keep Workflow and workflow-driven user stories planned and explicitly postponed for this assessment.",
|
||||
"Use local GovOPlaN accounts for the bounded pilot; do not claim federated identity support.",
|
||||
"Do not approve small production until installed-artifact, target mail, monitoring, backup/restore, and recovery proof checks pass."
|
||||
],
|
||||
"release": {
|
||||
"kind": "tagged_release",
|
||||
"ref": "stable-catalog-202607220843",
|
||||
"meta_commit": "5447299289a1",
|
||||
"reproducible": true,
|
||||
"configuration_packages": [],
|
||||
"configuration_packages": [
|
||||
"none: environment-profile basis only"
|
||||
],
|
||||
"notes": [
|
||||
"The live stable catalog has a valid Ed25519 signature trusted through release-key-1.",
|
||||
"Core v0.1.13 and Campaign v0.1.10 are tagged and package-integrated; this is not target-environment or production approval.",
|
||||
"No configuration revision or configuration package is pinned yet."
|
||||
"The absence of a configuration package is pinned explicitly as an environment-profile-only basis; this remains a promotion gap."
|
||||
]
|
||||
},
|
||||
"composition": [
|
||||
@@ -188,6 +202,125 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"scenarios": [
|
||||
{
|
||||
"id": "campaign-pilot",
|
||||
"label": "Controlled Campaign pilot",
|
||||
"status": "partial",
|
||||
"recommendation": "Proceed with a bounded internal pilot after its provider, privacy, workload, and recovery proof checks are assigned and passed.",
|
||||
"composition": [
|
||||
"core",
|
||||
"tenancy",
|
||||
"organizations",
|
||||
"identity",
|
||||
"access",
|
||||
"admin",
|
||||
"dashboard",
|
||||
"policy",
|
||||
"audit",
|
||||
"campaigns",
|
||||
"files",
|
||||
"mail",
|
||||
"docs",
|
||||
"ops"
|
||||
],
|
||||
"topology": [
|
||||
"One supervised GovOPlaN API process and one immutable built WebUI behind deployment-owned TLS termination",
|
||||
"One PostgreSQL database and a durable single-node or shared managed-file path",
|
||||
"One persistent private Redis broker and one supervised Celery worker when asynchronous delivery is enabled",
|
||||
"One dedicated non-production SMTP/IMAP account with a restricted safe-recipient policy",
|
||||
"External health checks, centralized logs, protected secret injection, and coordinated backup storage"
|
||||
],
|
||||
"conditions": [
|
||||
"Use one internal tenant or office and controlled operators.",
|
||||
"Keep recipient volume non-critical until measured.",
|
||||
"Enable Addresses only when reusable recipient lists or CardDAV are explicitly in scope.",
|
||||
"Do not enable or claim Workflow from this assessment."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "small-production-candidate",
|
||||
"label": "Small-production candidate",
|
||||
"status": "partial",
|
||||
"recommendation": "Do not approve production until every listed operational gate has target evidence and the residual risks have named owners.",
|
||||
"composition": [
|
||||
"core",
|
||||
"tenancy",
|
||||
"organizations",
|
||||
"identity",
|
||||
"access",
|
||||
"admin",
|
||||
"dashboard",
|
||||
"policy",
|
||||
"audit",
|
||||
"campaigns",
|
||||
"files",
|
||||
"mail",
|
||||
"docs",
|
||||
"ops"
|
||||
],
|
||||
"topology": [
|
||||
"Immutable separately supervised WebUI, API, and worker artifacts behind monitored reverse-proxy TLS",
|
||||
"Dedicated or managed PostgreSQL with measured coordinated backup and isolated restore",
|
||||
"Persistent authenticated Redis with queue-age, queue-depth, and worker-health alerts",
|
||||
"Durable shared or S3-compatible object storage with versioning, lifecycle, and restore evidence",
|
||||
"Target-native secret management, centralized monitoring/logging/audit export, and an exercised incident and disaster-recovery procedure"
|
||||
],
|
||||
"conditions": [
|
||||
"Pin and promote a configuration package instead of relying on an environment-only basis.",
|
||||
"Pass installed-release, target SMTP/IMAP, accessibility, privacy, security, operations, and recovery evidence gates.",
|
||||
"Agree availability, RPO, RTO, retention, support, and procurement requirements.",
|
||||
"Run only one scheduler unless distributed leadership or locking is proved."
|
||||
]
|
||||
}
|
||||
],
|
||||
"functional_context": {
|
||||
"required_modules": [
|
||||
"core",
|
||||
"tenancy",
|
||||
"organizations",
|
||||
"identity",
|
||||
"access",
|
||||
"admin",
|
||||
"dashboard",
|
||||
"policy",
|
||||
"audit",
|
||||
"campaigns",
|
||||
"files",
|
||||
"mail",
|
||||
"docs",
|
||||
"ops"
|
||||
],
|
||||
"optional_modules": [
|
||||
"addresses"
|
||||
],
|
||||
"external_systems": [
|
||||
"Deployment-owned reverse proxy and TLS certificate lifecycle",
|
||||
"Target SMTP/IMAP service and its DNS, certificate, throttling, bounce, and reply policies",
|
||||
"Target-native secret store, monitoring/logging platform, backup storage, and incident-response process"
|
||||
],
|
||||
"missing_contracts": [
|
||||
"End-to-end federated identity provider and lifecycle contract",
|
||||
"Target monitoring, alert delivery, and central audit/SIEM acceptance contract",
|
||||
"Production configuration-package promotion and approval evidence"
|
||||
],
|
||||
"policy_decisions": [
|
||||
"Recipient allow-list, permitted sender, attachment, retention, and external-disclosure policy",
|
||||
"Identity, MFA, break-glass, service-account, and joiner/mover/leaver policy",
|
||||
"Availability, RPO, RTO, support, procurement, and residual-risk ownership"
|
||||
],
|
||||
"manual_workarounds": [
|
||||
"Use controlled local accounts while federation remains outside the verified slice",
|
||||
"Use one supervised scheduler where periodic work is unavoidable",
|
||||
"Keep provider reconciliation and production promotion under explicit operator review"
|
||||
],
|
||||
"blockers": [
|
||||
"No promoted configuration package is pinned",
|
||||
"No installed-target or target SMTP/IMAP proof is attached",
|
||||
"No coherent target backup/restore or disaster-recovery drill with measured RPO/RTO is attached",
|
||||
"No target privacy, security, accessibility, operations, or production-approval evidence is attached"
|
||||
]
|
||||
},
|
||||
"questionnaire": {
|
||||
"scope_outcomes": [
|
||||
{
|
||||
@@ -203,6 +336,20 @@
|
||||
"state": "answered",
|
||||
"answer": "No; Workflow is planned and explicitly postponed.",
|
||||
"evidence": []
|
||||
},
|
||||
{
|
||||
"id": "scope.users_tenants_organizations",
|
||||
"question": "Which users, roles, tenants, organization units, and delegated functions participate?",
|
||||
"state": "assumed",
|
||||
"answer": "One internal tenant or office with controlled Campaign operators; detailed organization and delegation shape remains target-specific.",
|
||||
"evidence": []
|
||||
},
|
||||
{
|
||||
"id": "outcome.acceptance",
|
||||
"question": "What constitutes pilot success and production acceptance?",
|
||||
"state": "answered",
|
||||
"answer": "Pilot success requires the bounded Campaign journey and proof checks; production additionally requires installed-artifact, provider, privacy, security, operations, recovery, and approval evidence.",
|
||||
"evidence": []
|
||||
}
|
||||
],
|
||||
"data_policy": [
|
||||
@@ -219,6 +366,13 @@
|
||||
"state": "not_assessed",
|
||||
"answer": null,
|
||||
"evidence": []
|
||||
},
|
||||
{
|
||||
"id": "data.privacy_security_disclosure",
|
||||
"question": "Which privacy, security, residency, minimization, access, and external-disclosure constraints apply?",
|
||||
"state": "not_assessed",
|
||||
"answer": null,
|
||||
"evidence": []
|
||||
}
|
||||
],
|
||||
"identity_integrations": [
|
||||
@@ -235,22 +389,50 @@
|
||||
"state": "not_assessed",
|
||||
"answer": null,
|
||||
"evidence": []
|
||||
},
|
||||
{
|
||||
"id": "identity.protocols_lifecycle",
|
||||
"question": "Which identity protocols, MFA, joiner/mover/leaver, service-account, and break-glass rules are mandatory?",
|
||||
"state": "not_assessed",
|
||||
"answer": null,
|
||||
"evidence": []
|
||||
},
|
||||
{
|
||||
"id": "integration.protocols_network",
|
||||
"question": "Which connector protocols, versions, directions, authentication, certificate, rate-limit, egress, and degraded-mode requirements apply?",
|
||||
"state": "not_assessed",
|
||||
"answer": null,
|
||||
"evidence": []
|
||||
}
|
||||
],
|
||||
"workload_growth": [
|
||||
{
|
||||
"id": "workload.campaign",
|
||||
"id": "workload.campaign_volume_peaks",
|
||||
"question": "What are Campaign frequency, recipients per Campaign, send window, import size and attachment volume?",
|
||||
"state": "not_assessed",
|
||||
"answer": null,
|
||||
"evidence": []
|
||||
},
|
||||
{
|
||||
"id": "workload.platform",
|
||||
"id": "workload.tenants_users_concurrency",
|
||||
"question": "What are tenant, named-user, active-user, concurrent-user, and peak-request assumptions?",
|
||||
"state": "not_assessed",
|
||||
"answer": null,
|
||||
"evidence": []
|
||||
},
|
||||
{
|
||||
"id": "workload.files_jobs_audit_growth_retention",
|
||||
"question": "What are tenant, user, concurrency, file, database, queue and audit growth assumptions?",
|
||||
"state": "not_assessed",
|
||||
"answer": null,
|
||||
"evidence": []
|
||||
},
|
||||
{
|
||||
"id": "workload.connector_traffic_batches",
|
||||
"question": "What connector traffic, scheduled-job, batch, queue-depth, queue-age, and external-rate-limit peaks apply?",
|
||||
"state": "not_assessed",
|
||||
"answer": null,
|
||||
"evidence": []
|
||||
}
|
||||
],
|
||||
"availability_operations": [
|
||||
@@ -267,6 +449,13 @@
|
||||
"state": "not_assessed",
|
||||
"answer": null,
|
||||
"evidence": []
|
||||
},
|
||||
{
|
||||
"id": "hosting.network_constraints",
|
||||
"question": "Which hosting, network-zone, egress, proxy, DNS, NTP, certificate-authority, residency, or disconnected-operation constraints apply?",
|
||||
"state": "not_assessed",
|
||||
"answer": null,
|
||||
"evidence": []
|
||||
}
|
||||
],
|
||||
"procurement_decisions": [
|
||||
@@ -507,7 +696,7 @@
|
||||
{
|
||||
"kind": "issue",
|
||||
"scope": "documented_model",
|
||||
"locator": "https://git.add-ideas.de/add-ideas/govoplan/issues/12"
|
||||
"locator": "https://git.add-ideas.de/GovOPlaN/govoplan/issues/12"
|
||||
}
|
||||
],
|
||||
"conditions": [],
|
||||
@@ -754,6 +943,63 @@
|
||||
"recommendation": "Use target-native secret injection and document rotation/recovery.",
|
||||
"proof_check": "Rotate a non-production credential and recover from a protected backup."
|
||||
},
|
||||
{
|
||||
"id": "identity.access",
|
||||
"requirement": "Authenticate users and enforce tenant-scoped authorization through the selected identity mode.",
|
||||
"status": "verified",
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "test",
|
||||
"scope": "committed_source",
|
||||
"locator": "govoplan-access/tests/test_auth_dependencies.py"
|
||||
},
|
||||
{
|
||||
"kind": "test",
|
||||
"scope": "committed_source",
|
||||
"locator": "govoplan-core/tests/test_api_smoke.py#cookie-session-csrf"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
"The bounded pilot accepts local GovOPlaN accounts."
|
||||
],
|
||||
"gaps": [
|
||||
"Target MFA, federation, provisioning, and joiner/mover/leaver requirements are not assessed."
|
||||
],
|
||||
"risks": [
|
||||
"A local-only identity topology may not satisfy institutional production policy."
|
||||
],
|
||||
"recommendation": "Use controlled local pilot accounts and assess the mandatory production identity topology separately.",
|
||||
"proof_check": "Exercise login, role change, account suspension, protected bootstrap, and break-glass recovery in the target."
|
||||
},
|
||||
{
|
||||
"id": "connectors.mail",
|
||||
"requirement": "Reach the selected SMTP/IMAP and other external connector endpoints under explicit network and provider policy.",
|
||||
"status": "available_unconfigured",
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "test",
|
||||
"scope": "current_workspace",
|
||||
"locator": "govoplan-mail/tests",
|
||||
"note": "Protocol adapters have direct tests; no target provider was exercised"
|
||||
},
|
||||
{
|
||||
"kind": "documentation",
|
||||
"scope": "documented_model",
|
||||
"locator": "govoplan-campaign/docs/CAMPAIGN_DELIVERY_RUNBOOK.md"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
"The deployment supplies DNS, egress, proxy, CA trust, scoped service accounts, and provider limits."
|
||||
],
|
||||
"gaps": [
|
||||
"No target endpoint, TLS chain, throttling, sender policy, bounce/reply path, or disclosure agreement is assessed."
|
||||
],
|
||||
"risks": [
|
||||
"Provider rejection, delay, or ambiguous outcomes can affect delivery and evidence completeness."
|
||||
],
|
||||
"recommendation": "Use a dedicated safe provider account for the pilot and require target interoperability evidence before production.",
|
||||
"proof_check": "Exercise target-like SMTP acceptance, IMAP append, throttling, outage, retry, and reconciliation through the approved network path."
|
||||
},
|
||||
{
|
||||
"id": "operations.monitoring",
|
||||
"requirement": "Detect API, database, worker, queue, storage and delivery degradation.",
|
||||
@@ -780,6 +1026,30 @@
|
||||
"recommendation": "Integrate external monitoring before small production.",
|
||||
"proof_check": "Trigger each readiness/delivery failure and verify an actionable alert."
|
||||
},
|
||||
{
|
||||
"id": "operations.audit",
|
||||
"requirement": "Retain, monitor, review, and where required export security and business audit evidence.",
|
||||
"status": "partial",
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "test",
|
||||
"scope": "current_workspace",
|
||||
"locator": "govoplan-audit/tests",
|
||||
"note": "Local audit persistence and retry behavior are exercised"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
"Local database audit evidence is part of coordinated backup and access review."
|
||||
],
|
||||
"gaps": [
|
||||
"Target retention enforcement, tamper-evident export, SIEM integration, alerting, and privileged review are not verified."
|
||||
],
|
||||
"risks": [
|
||||
"Local evidence alone may not meet institutional security, records, or incident-response requirements."
|
||||
],
|
||||
"recommendation": "Define the target audit retention, export, monitoring, and review controls before production approval.",
|
||||
"proof_check": "Exercise privileged-event review, retention, export failure/retry, and target SIEM or archive ingestion."
|
||||
},
|
||||
{
|
||||
"id": "operations.backup_restore",
|
||||
"requirement": "Back up and restore database, files, configuration and keys as a coherent service.",
|
||||
@@ -793,7 +1063,7 @@
|
||||
{
|
||||
"kind": "issue",
|
||||
"scope": "documented_model",
|
||||
"locator": "https://git.add-ideas.de/add-ideas/govoplan-core/issues/29"
|
||||
"locator": "https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/29"
|
||||
}
|
||||
],
|
||||
"conditions": [],
|
||||
@@ -967,10 +1237,12 @@
|
||||
],
|
||||
"proof_checks": [
|
||||
"Materialize the signed catalog into an isolated installation and rerun contract, migration and module-permutation gates against the installed artifacts.",
|
||||
"Collect the isolated installation with the bounded installed-composition evidence contract; require exact enabled package/module versions, complete RECORD verification and immutable provenance anchored to this assessment.",
|
||||
"Run a safe target-like Campaign through SMTP acceptance, IMAP append, reporting and audit.",
|
||||
"Drill worker, Redis and ambiguous-delivery failures without duplicate sends.",
|
||||
"Restore PostgreSQL, managed files, configuration and encrypted credentials and measure RPO/RTO.",
|
||||
"Validate proxy/TLS, cookies/CORS, account bootstrap, secret redaction, monitoring and alert delivery.",
|
||||
"Measure representative Campaign/file/queue/database load and external throttling."
|
||||
"Measure representative Campaign/file/queue/database load and external throttling.",
|
||||
"Require separately issued, expiring and independently scope-authorized evidence before marking target environment, external provider or production approval proof as checked."
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://git.add-ideas.de/GovOPlaN/govoplan/src/branch/main/docs/capability-fit-proof-authority-keyring.schema.json",
|
||||
"title": "GovOPlaN capability-fit proof authority keyring",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema_version", "purpose", "keys"],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"format": "uri-reference"
|
||||
},
|
||||
"schema_version": {
|
||||
"const": "0.1.0"
|
||||
},
|
||||
"purpose": {
|
||||
"const": "govoplan.capability-fit-proof-authorities"
|
||||
},
|
||||
"keys": {
|
||||
"type": "array",
|
||||
"maxItems": 64,
|
||||
"items": {
|
||||
"$ref": "#/$defs/key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"opaque_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160,
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
||||
},
|
||||
"key": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["key_id", "status", "public_key", "allowed_scopes"],
|
||||
"properties": {
|
||||
"key_id": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
},
|
||||
"status": {
|
||||
"enum": ["active", "next", "revoked", "disabled", "retired"]
|
||||
},
|
||||
"public_key": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 256,
|
||||
"contentEncoding": "base64"
|
||||
},
|
||||
"allowed_scopes": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 8,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"enum": [
|
||||
"target_environment",
|
||||
"external_providers",
|
||||
"accessibility",
|
||||
"privacy",
|
||||
"security",
|
||||
"operations",
|
||||
"recovery",
|
||||
"production_approval"
|
||||
]
|
||||
}
|
||||
},
|
||||
"not_before": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"not_after": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://git.add-ideas.de/add-ideas/govoplan/src/branch/main/docs/capability-fit.schema.json",
|
||||
"$id": "https://git.add-ideas.de/GovOPlaN/govoplan/src/branch/main/docs/capability-fit.schema.json",
|
||||
"title": "GovOPlaN capability and infrastructure fit assessment",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
@@ -9,9 +9,13 @@
|
||||
"assessment_id",
|
||||
"assessed_at",
|
||||
"scope",
|
||||
"facts",
|
||||
"decisions",
|
||||
"release",
|
||||
"composition",
|
||||
"deployment_profile",
|
||||
"scenarios",
|
||||
"functional_context",
|
||||
"questionnaire",
|
||||
"capabilities",
|
||||
"infrastructure",
|
||||
@@ -28,7 +32,7 @@
|
||||
"format": "uri-reference"
|
||||
},
|
||||
"schema_version": {
|
||||
"const": "0.1.0"
|
||||
"const": "0.2.0"
|
||||
},
|
||||
"assessment_id": {
|
||||
"$ref": "#/$defs/non_empty_string"
|
||||
@@ -54,6 +58,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"facts": { "$ref": "#/$defs/string_list" },
|
||||
"decisions": { "$ref": "#/$defs/string_list" },
|
||||
"release": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
@@ -95,6 +101,33 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"scenarios": {
|
||||
"type": "array",
|
||||
"minItems": 2,
|
||||
"items": { "$ref": "#/$defs/scenario" }
|
||||
},
|
||||
"functional_context": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"required_modules",
|
||||
"optional_modules",
|
||||
"external_systems",
|
||||
"missing_contracts",
|
||||
"policy_decisions",
|
||||
"manual_workarounds",
|
||||
"blockers"
|
||||
],
|
||||
"properties": {
|
||||
"required_modules": { "$ref": "#/$defs/string_list" },
|
||||
"optional_modules": { "$ref": "#/$defs/string_list" },
|
||||
"external_systems": { "$ref": "#/$defs/string_list" },
|
||||
"missing_contracts": { "$ref": "#/$defs/string_list" },
|
||||
"policy_decisions": { "$ref": "#/$defs/string_list" },
|
||||
"manual_workarounds": { "$ref": "#/$defs/string_list" },
|
||||
"blockers": { "$ref": "#/$defs/string_list" }
|
||||
}
|
||||
},
|
||||
"questionnaire": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
@@ -239,6 +272,37 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"scenario": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"id",
|
||||
"label",
|
||||
"status",
|
||||
"recommendation",
|
||||
"composition",
|
||||
"topology",
|
||||
"conditions"
|
||||
],
|
||||
"properties": {
|
||||
"id": { "$ref": "#/$defs/non_empty_string" },
|
||||
"label": { "$ref": "#/$defs/non_empty_string" },
|
||||
"status": { "$ref": "#/$defs/status" },
|
||||
"recommendation": { "$ref": "#/$defs/non_empty_string" },
|
||||
"composition": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"uniqueItems": true,
|
||||
"items": { "$ref": "#/$defs/non_empty_string" }
|
||||
},
|
||||
"topology": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "$ref": "#/$defs/non_empty_string" }
|
||||
},
|
||||
"conditions": { "$ref": "#/$defs/string_list" }
|
||||
}
|
||||
},
|
||||
"assessed_item": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
@@ -259,6 +323,7 @@
|
||||
"status": { "$ref": "#/$defs/status" },
|
||||
"evidence": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "$ref": "#/$defs/evidence" }
|
||||
},
|
||||
"conditions": { "$ref": "#/$defs/string_list" },
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# Shared WebUI Primitive Inventory
|
||||
|
||||
This 2026-08-18 inventory records the implementation state after the
|
||||
product-wide structural consolidation and second duplicate-rule audit. It is
|
||||
evidence for enforcement, not a substitute for the normative
|
||||
[interface pattern language](../architecture/INTERFACE_PATTERN_LANGUAGE.md).
|
||||
|
||||
## Implemented And Enforced
|
||||
|
||||
| Contract | Adoption evidence | Ownership now enforced |
|
||||
| --- | ---: | --- |
|
||||
| `ActionToolbar` and groups | 50 source files | Raw module-prefixed toolbar elements and local toolbar definitions are rejected. Distribution, wrapping, density, grouping and panel/section surfaces are Core-owned. |
|
||||
| `PageLayout` / `WorkspaceLayout` / `WorkspaceFrame` | 25 / 16 / 17 source files | Headed page anatomy, full-height viewport frames and navigation/list-detail panes no longer repeat inset, heading, notices, loading, shell height, surface, overflow or pane geometry. The raw page-frame and raw workspace exception baselines are both empty. |
|
||||
| `FilterBar` | 14 source files | Catalogue and pane search/filter rows share width, surface, layout and wrapping. |
|
||||
| `SelectionList` family | 19 source files | Resource navigation shares selection, title/description, leading-icon and truncation anatomy. |
|
||||
| `StatePanel` | 25 source files | Whole-surface, compact and fill empty/blocked/error states replace module-local state shells. |
|
||||
| `CountBadge` | 8 source files | Notification, folder, search, postbox and graph counts use one compact badge contract. |
|
||||
| `ContentSection` | 5 source files | Repeated bordered/subtle editor sections and compact provenance panels share surface, density, flow and rhythm. |
|
||||
| `ContentGrid` | 22 source files | Equal-column content geometry and former dashboard/settings/assignment copies are Core-owned. |
|
||||
| `FormGrid` and `FormLayout` | 53 source files | Former generic/admin grids and equal-column dialog/editor copies use named collapse points and native form semantics. |
|
||||
| `MetricGrid` / `MetricCard` | 31 / 33 source files | Module-local metric helpers, grids and card visual definitions were removed. |
|
||||
| `DescriptionList` and `DescriptionItem` | 28 source files | Former generic property grids use semantic `dl`/`dt`/`dd` composition with central density and collapse. |
|
||||
| `DefinitionPalette`, node/canvas visuals and `FloatingStatus` | 2 Dataflow/Workflow consumers each | The copied graph palette, canvas controls, minimap, node icon/port, empty overlay and activity overlay definitions are Core-owned; graph semantics remain local. |
|
||||
| `DialogActions`, `DialogForm`, `DialogSection` | every Core footer / 6 / 6 source files | Footer action flow, native dialog form flow and dialog content grouping are Core-owned. |
|
||||
| Standard dialog sizing | 61 reviewed specialized selectors | Any width matching the Core 460/560/680/1040/1440px scale must use `Dialog size`; the remaining decrease-only exceptions are explicit. |
|
||||
|
||||
`tools/checks/check-shared-webui-primitives.py` verifies Core exports and
|
||||
ownership, representative consumers, the absence of the retired raw anatomy,
|
||||
and composition of every `Dialog` footer through `DialogActions`.
|
||||
`tools/checks/check-shared-webui-layouts.py` additionally requires the reviewed
|
||||
Core and module consumers and rejects any raw page or workspace frame; there
|
||||
are no remaining allow-listed layout exceptions.
|
||||
|
||||
## Dialog Width Classification
|
||||
|
||||
The remaining 61 width selectors do not duplicate the Core 460/560/680/1040/
|
||||
1440px scale. They cover bounded editor widths between scale steps, high-density
|
||||
definition and governance editors, preview/chooser canvases, message and file
|
||||
overlays with coupled height behavior, and responsive full-canvas workflows.
|
||||
Their exact selector set lives in
|
||||
`tools/checks/shared-webui-dialog-width-exceptions.txt`. The focused check fails
|
||||
for a new selector, a stale baseline entry, or any local width that duplicates
|
||||
the Core scale.
|
||||
|
||||
## Audit Result And Deliberate Local Ownership
|
||||
|
||||
The second scan compared exact CSS declaration bodies and JSX anatomy across
|
||||
every WebUI module after migration. All repeated generic structural candidates
|
||||
found in that pass were promoted: viewport frames, catalogue/list shells,
|
||||
filters, selectable lists, state panels, count badges, section frames,
|
||||
equal-column grids, section headers, metrics, and definition-editor chrome.
|
||||
The final legacy-baseline pass also migrated Access administration, Core
|
||||
Settings, Docs, Mail bounce processing, and Organizations to the shared page
|
||||
and workspace layouts and removed their copied responsive geometry.
|
||||
|
||||
The remaining cross-module declaration matches are not independent component
|
||||
anatomy. They are small token-based rules such as ellipsis, muted captions,
|
||||
uppercase terms, or flex-column containment applied to different semantic
|
||||
elements. Moving those rules into a component would erase meaning; their
|
||||
visual values already come from Core tokens. Remaining larger local layouts
|
||||
are deliberately domain-owned:
|
||||
|
||||
- unequal-track editors, import mappings and schema/data tables;
|
||||
- calendar time grids, charts, graph node shapes and graph edge semantics;
|
||||
- file/mail/postbox/records explorer panes whose interaction contracts differ;
|
||||
- timelines, evidence histories, recipient compositions and policy-specific
|
||||
detail sections;
|
||||
- compact list-row internals that cannot preserve their semantics through
|
||||
`SelectionListItemContent`.
|
||||
|
||||
A future candidate is promoted only when a new audit identifies repeated
|
||||
structure plus the same responsive, accessibility and interaction contract.
|
||||
The enforcement script prevents regression for the patterns centralized in
|
||||
this pass and maintains the reviewed dialog-width baseline.
|
||||
@@ -0,0 +1,323 @@
|
||||
# GovOPlaN Capability and IT-Infrastructure Fit Assessment
|
||||
|
||||
> Generated from [`capability-fit-current.json`](../../capability-fit-current.json).
|
||||
> Edit and validate the machine-readable assessment, then regenerate this file;
|
||||
> do not maintain conclusions independently in Markdown.
|
||||
|
||||
This is an evidence-based fit assessment, not a production approval or
|
||||
security certification. Repository or manifest existence alone never counts
|
||||
as an implemented capability. Unknown target requirements remain explicitly
|
||||
`not_assessed`.
|
||||
|
||||
## Assessment record
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| Assessment ID | `campaign-reference-2026-07-22` |
|
||||
| Schema version | `govoplan.fit-assessment/0.2.0` |
|
||||
| Assessed on | 2026-07-22 |
|
||||
| Scope | Campaign-centric internal pilot and small-production candidate |
|
||||
| Release | `stable-catalog-202607220843` (tagged_release) |
|
||||
| Meta commit | `5447299289a1` |
|
||||
| Deployment profile | `production-like-dev` · `partial` |
|
||||
| Configuration packages | `none: environment-profile basis only` |
|
||||
| Canonical input SHA-256 | `5a23f17c5289c5a89d2e92445f2c8b2eef54e3753f1392ebf300aff5508f0bfe` |
|
||||
|
||||
## Controlled status vocabulary
|
||||
|
||||
| Status | Meaning |
|
||||
| --- | --- |
|
||||
| `verified` | Implemented and directly exercised by evidence appropriate to the stated scope. |
|
||||
| `available_unconfigured` | Implemented with supporting evidence, but not configured and exercised in the target. |
|
||||
| `partial` | A useful subset exists, but a material part of the requirement is missing or unproved. |
|
||||
| `scaffold` | Contracts or structure exist, but the end-to-end capability is not usable. |
|
||||
| `external_system` | The deployment or another system must supply the capability. |
|
||||
| `planned` | Only a concept, backlog item, or design direction exists. |
|
||||
| `not_fit` | Evidence shows that the assessed composition cannot meet the requirement. |
|
||||
| `not_assessed` | The requirement or target environment is not sufficiently known. |
|
||||
|
||||
## Scope and reference journeys
|
||||
|
||||
Reference journeys:
|
||||
|
||||
- Internal operator authors, validates, builds, queues, sends and reconciles an email Campaign with managed attachments
|
||||
- Operator inspects delivery and audit evidence
|
||||
|
||||
Explicitly postponed:
|
||||
|
||||
- Workflow and workflow-driven user stories
|
||||
|
||||
## Facts
|
||||
|
||||
- The assessment is pinned to signed stable catalog sequence 202607220843 and the exact module commits listed below.
|
||||
- The Campaign authoring, validation, build, mock-delivery, managed-file, local-access, and local-audit paths have direct test or contract evidence.
|
||||
- The production-like development profile runs PostgreSQL and Redis in containers while application processes use editable source trees.
|
||||
- No installed-target, external-provider, reference-readiness, recovery, or production-approval evidence bundle is attached to this assessment.
|
||||
|
||||
## Decisions
|
||||
|
||||
- Use Campaign as the first reference journey and flagship pilot scenario.
|
||||
- Keep Workflow and workflow-driven user stories planned and explicitly postponed for this assessment.
|
||||
- Use local GovOPlaN accounts for the bounded pilot; do not claim federated identity support.
|
||||
- Do not approve small production until installed-artifact, target mail, monitoring, backup/restore, and recovery proof checks pass.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- The pilot can use local accounts and one internal tenant or office.
|
||||
- A dedicated non-production SMTP/IMAP account and safe recipients are available.
|
||||
- Pilot load fits one API and one worker until measured otherwise.
|
||||
- Durable local storage is acceptable for the pilot.
|
||||
|
||||
## Unresolved decisions
|
||||
|
||||
- What are the target organization's data classes, legal bases, retention and external-disclosure rules?
|
||||
- Which identity, mail, file, address and monitoring systems are mandatory?
|
||||
- What are Campaign volume, concurrency, growth, availability, RPO and RTO?
|
||||
- Who owns each external runtime component and operational control?
|
||||
- Which accessibility, security, support and procurement constraints are mandatory?
|
||||
|
||||
## Pinned release and composition
|
||||
|
||||
Release reproducible: **yes**.
|
||||
|
||||
Release notes:
|
||||
|
||||
- The live stable catalog has a valid Ed25519 signature trusted through release-key-1.
|
||||
- Core v0.1.13 and Campaign v0.1.10 are tagged and package-integrated; this is not target-environment or production approval.
|
||||
- The absence of a configuration package is pinned explicitly as an environment-profile-only basis; this remains a promotion gap.
|
||||
|
||||
| Module | Repository and commit | Manifest version | Enabled | Role |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `core` | `govoplan-core` @ `d487726f4d2c` | `0.1.13` | yes | API, registry, migrations, sessions, kernel contracts and shared WebUI |
|
||||
| `tenancy` | `govoplan-tenancy` @ `efbec827616b` | `0.1.8` | yes | Tenant context and lifecycle |
|
||||
| `organizations` | `govoplan-organizations` @ `39c081c4fb8f` | `0.1.8` | yes | Organization model |
|
||||
| `identity` | `govoplan-identity` @ `7a1710af896f` | `0.1.8` | yes | Normalized internal identity directory |
|
||||
| `access` | `govoplan-access` @ `f1d64d247e12` | `0.1.11` | yes | Local authentication, sessions, API keys and RBAC |
|
||||
| `admin` | `govoplan-admin` @ `11ecf362a36d` | `0.1.8` | yes | Administration surfaces |
|
||||
| `dashboard` | `govoplan-dashboard` @ `4b960ad37f0d` | `0.1.8` | yes | Module-aware home surface |
|
||||
| `policy` | `govoplan-policy` @ `1063622d311a` | `0.1.9` | yes | Policy explanation and configuration boundary |
|
||||
| `audit` | `govoplan-audit` @ `d3d2c60d7dc1` | `0.1.8` | yes | Database audit records and retrying audit outbox |
|
||||
| `campaigns` | `govoplan-campaign` @ `735e874bd03c` | `0.1.10` | yes | Campaign authoring, build, delivery control and reporting |
|
||||
| `files` | `govoplan-files` @ `2b34f6e30578` | `0.1.9` | yes | Managed files and Campaign attachments |
|
||||
| `mail` | `govoplan-mail` @ `3e2302909022` | `0.1.10` | yes | SMTP and IMAP profiles and transports |
|
||||
| `calendar` | `govoplan-calendar` @ `9bcf41bb1fbb` | `0.1.8` | yes | Optional calendar outside the Campaign pilot minimum |
|
||||
| `docs` | `govoplan-docs` @ `be52b716caed` | `0.1.10` | yes | Configured-system documentation |
|
||||
| `ops` | `govoplan-ops` @ `341773a4ff8a` | `0.1.8` | yes | Readiness and deployment-profile visibility |
|
||||
| `addresses` | `govoplan-addresses` @ `93dddbb8c52a` | `0.1.9` | no | Optional reusable recipient sources and CardDAV |
|
||||
|
||||
## Deployment profile
|
||||
|
||||
Status: `partial`
|
||||
|
||||
PostgreSQL and Redis run in containers while API, WebUI, worker and scheduler run from editable source trees.
|
||||
|
||||
Evidence:
|
||||
|
||||
- configuration/current_workspace: govoplan/dev/production-like/docker-compose.yml
|
||||
- documentation/documented_model: govoplan/dev/production-like/README.md
|
||||
|
||||
## Recommended scenarios
|
||||
|
||||
### Controlled Campaign pilot
|
||||
|
||||
Status: `partial`
|
||||
|
||||
Proceed with a bounded internal pilot after its provider, privacy, workload, and recovery proof checks are assigned and passed.
|
||||
|
||||
Composition: `core`, `tenancy`, `organizations`, `identity`, `access`, `admin`, `dashboard`, `policy`, `audit`, `campaigns`, `files`, `mail`, `docs`, `ops`.
|
||||
|
||||
Topology:
|
||||
|
||||
- One supervised GovOPlaN API process and one immutable built WebUI behind deployment-owned TLS termination
|
||||
- One PostgreSQL database and a durable single-node or shared managed-file path
|
||||
- One persistent private Redis broker and one supervised Celery worker when asynchronous delivery is enabled
|
||||
- One dedicated non-production SMTP/IMAP account with a restricted safe-recipient policy
|
||||
- External health checks, centralized logs, protected secret injection, and coordinated backup storage
|
||||
|
||||
Conditions:
|
||||
|
||||
- Use one internal tenant or office and controlled operators.
|
||||
- Keep recipient volume non-critical until measured.
|
||||
- Enable Addresses only when reusable recipient lists or CardDAV are explicitly in scope.
|
||||
- Do not enable or claim Workflow from this assessment.
|
||||
|
||||
### Small-production candidate
|
||||
|
||||
Status: `partial`
|
||||
|
||||
Do not approve production until every listed operational gate has target evidence and the residual risks have named owners.
|
||||
|
||||
Composition: `core`, `tenancy`, `organizations`, `identity`, `access`, `admin`, `dashboard`, `policy`, `audit`, `campaigns`, `files`, `mail`, `docs`, `ops`.
|
||||
|
||||
Topology:
|
||||
|
||||
- Immutable separately supervised WebUI, API, and worker artifacts behind monitored reverse-proxy TLS
|
||||
- Dedicated or managed PostgreSQL with measured coordinated backup and isolated restore
|
||||
- Persistent authenticated Redis with queue-age, queue-depth, and worker-health alerts
|
||||
- Durable shared or S3-compatible object storage with versioning, lifecycle, and restore evidence
|
||||
- Target-native secret management, centralized monitoring/logging/audit export, and an exercised incident and disaster-recovery procedure
|
||||
|
||||
Conditions:
|
||||
|
||||
- Pin and promote a configuration package instead of relying on an environment-only basis.
|
||||
- Pass installed-release, target SMTP/IMAP, accessibility, privacy, security, operations, and recovery evidence gates.
|
||||
- Agree availability, RPO, RTO, retention, support, and procurement requirements.
|
||||
- Run only one scheduler unless distributed leadership or locking is proved.
|
||||
|
||||
## Functional matrix context
|
||||
|
||||
### Required modules
|
||||
|
||||
- core
|
||||
- tenancy
|
||||
- organizations
|
||||
- identity
|
||||
- access
|
||||
- admin
|
||||
- dashboard
|
||||
- policy
|
||||
- audit
|
||||
- campaigns
|
||||
- files
|
||||
- mail
|
||||
- docs
|
||||
- ops
|
||||
|
||||
### Optional modules
|
||||
|
||||
- addresses
|
||||
|
||||
### External systems and connectors
|
||||
|
||||
- Deployment-owned reverse proxy and TLS certificate lifecycle
|
||||
- Target SMTP/IMAP service and its DNS, certificate, throttling, bounce, and reply policies
|
||||
- Target-native secret store, monitoring/logging platform, backup storage, and incident-response process
|
||||
|
||||
### Missing contracts
|
||||
|
||||
- End-to-end federated identity provider and lifecycle contract
|
||||
- Target monitoring, alert delivery, and central audit/SIEM acceptance contract
|
||||
- Production configuration-package promotion and approval evidence
|
||||
|
||||
### Policy decisions
|
||||
|
||||
- Recipient allow-list, permitted sender, attachment, retention, and external-disclosure policy
|
||||
- Identity, MFA, break-glass, service-account, and joiner/mover/leaver policy
|
||||
- Availability, RPO, RTO, support, procurement, and residual-risk ownership
|
||||
|
||||
### Manual workarounds
|
||||
|
||||
- Use controlled local accounts while federation remains outside the verified slice
|
||||
- Use one supervised scheduler where periodic work is unavoidable
|
||||
- Keep provider reconciliation and production promotion under explicit operator review
|
||||
|
||||
### Blockers
|
||||
|
||||
- No promoted configuration package is pinned
|
||||
- No installed-target or target SMTP/IMAP proof is attached
|
||||
- No coherent target backup/restore or disaster-recovery drill with measured RPO/RTO is attached
|
||||
- No target privacy, security, accessibility, operations, or production-approval evidence is attached
|
||||
|
||||
## Assessment questionnaire
|
||||
|
||||
Every required area remains visible even when its target answer is unknown.
|
||||
|
||||
| Area | Question | State | Answer | Evidence |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Scope Outcomes | Which journey is assessed? | `answered` | An internal operator authors, validates, builds, queues, sends and reconciles a Campaign with managed attachments. | — |
|
||||
| Scope Outcomes | Is Workflow in scope? | `answered` | No; Workflow is planned and explicitly postponed. | — |
|
||||
| Scope Outcomes | Which users, roles, tenants, organization units, and delegated functions participate? | `assumed` | One internal tenant or office with controlled Campaign operators; detailed organization and delegation shape remains target-specific. | — |
|
||||
| Scope Outcomes | What constitutes pilot success and production acceptance? | `answered` | Pilot success requires the bounded Campaign journey and proof checks; production additionally requires installed-artifact, provider, privacy, security, operations, recovery, and approval evidence. | — |
|
||||
| Data Policy | Which data classes and legal bases apply? | `not_assessed` | — | — |
|
||||
| Data Policy | What retention, deletion, archive and legal-hold rules apply? | `not_assessed` | — | — |
|
||||
| Data Policy | Which privacy, security, residency, minimization, access, and external-disclosure constraints apply? | `not_assessed` | — | — |
|
||||
| Identity Integrations | May the pilot use local GovOPlaN accounts? | `assumed` | Yes; federation is outside the verified composition. | — |
|
||||
| Identity Integrations | Which target SMTP/IMAP service and policy apply? | `not_assessed` | — | — |
|
||||
| Identity Integrations | Which identity protocols, MFA, joiner/mover/leaver, service-account, and break-glass rules are mandatory? | `not_assessed` | — | — |
|
||||
| Identity Integrations | Which connector protocols, versions, directions, authentication, certificate, rate-limit, egress, and degraded-mode requirements apply? | `not_assessed` | — | — |
|
||||
| Workload Growth | What are Campaign frequency, recipients per Campaign, send window, import size and attachment volume? | `not_assessed` | — | — |
|
||||
| Workload Growth | What are tenant, named-user, active-user, concurrent-user, and peak-request assumptions? | `not_assessed` | — | — |
|
||||
| Workload Growth | What are tenant, user, concurrency, file, database, queue and audit growth assumptions? | `not_assessed` | — | — |
|
||||
| Workload Growth | What connector traffic, scheduled-job, batch, queue-depth, queue-age, and external-rate-limit peaks apply? | `not_assessed` | — | — |
|
||||
| Availability Operations | What availability, RPO and RTO are required? | `not_assessed` | — | — |
|
||||
| Availability Operations | Who operates database, queue, storage, TLS, secrets, monitoring, backup and incident response? | `not_assessed` | — | — |
|
||||
| Availability Operations | Which hosting, network-zone, egress, proxy, DNS, NTP, certificate-authority, residency, or disconnected-operation constraints apply? | `not_assessed` | — | — |
|
||||
| Procurement Decisions | Which licensing, accessibility, security, certification, support and procurement conditions are mandatory? | `not_assessed` | — | — |
|
||||
|
||||
## Functional capability matrix
|
||||
|
||||
| Requirement | Status | Evidence | Conditions and gaps | Recommendation and proof |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **platform.composition**<br>Compose enabled backend and WebUI modules without hard optional-module dependencies. | `verified` | test/committed_source: govoplan-core/tests/test_module_system.py; contract/current_workspace: govoplan/tools/checks/check-contracts.py (43 modules, 33 providers, 19 requirements, no issues) | Condition: Package integration is verified; repeat checks on the installed target composition.; Gap: No target deployment acceptance is recorded.; Risk: A reproducible module graph can still be installed or configured incorrectly. | Use the signed stable catalog and verify the minimal Campaign composition after installation.<br>**Proof:** Run contract, migration, API and WebUI module-permutation gates on the installed release. |
|
||||
| **access.local**<br>Provide tenant-scoped local accounts, sessions, API keys and RBAC. | `verified` | test/committed_source: govoplan-access/tests/test_auth_dependencies.py; test/committed_source: govoplan-core/tests/test_api_smoke.py#cookie-session-csrf | Condition: Pilot accepts local accounts.; Gap: MFA and federated lifecycle are not part of this conclusion.; Risk: Manual account lifecycle may not satisfy production identity policy. | Use controlled local pilot accounts and define break-glass/bootstrap rules.<br>**Proof:** Exercise joiner, role change, suspension and protected-owner recovery. |
|
||||
| **campaign.journey**<br>Author, validate, build, queue, send, reconcile and report a Campaign with frozen execution evidence. | `verified` | test/committed_source: govoplan-core/tests/test_api_smoke.py#campaign-create-validate-build-mock-send; test/committed_source: govoplan-campaign/tests (Campaign v0.1.10 is exactly the catalog-selected tagged source); configuration/committed_source: https://govoplan.add-ideas.de/catalogs/v1/channels/stable.json#sequence-202607220843 (Core v0.1.13 and Campaign v0.1.10 have matching catalogued Python and WebUI refs) | Condition: This verifies implementation paths, not target-provider delivery.; Gap: Usability and target-provider acceptance remain separate.; Risk: Package integration does not prove provider behavior or production operations. | Use the catalogued Campaign release for usability and target-provider acceptance.<br>**Proof:** Run the complete journey with safe data and the target-like mail service. |
|
||||
| **files.managed_attachments**<br>Store and resolve managed Campaign attachments on durable storage. | `verified` | test/current_workspace: govoplan-files/tests (14 tests passed); test/current_workspace: govoplan-campaign/tests/test_attachment_building.py | Condition: Deployment provides a durable storage root.; Gap: Target backup and restore are not verified.; Risk: Node-local storage prevents safe independent API scaling. | Use durable local storage for the pilot and assess object/shared storage before scaling.<br>**Proof:** Back up and restore files together with database references. |
|
||||
| **mail.smtp_imap**<br>Send Campaign mail through SMTP and optionally append sent messages through IMAP. | `available_unconfigured` | test/current_workspace: govoplan-mail/tests (22 tests passed); documentation/documented_model: govoplan-campaign/docs/CAMPAIGN_DELIVERY_RUNBOOK.md | Condition: Use a dedicated non-production service account and safe recipients.; Gap: No target provider, TLS chain, throttling or bounce/reply process was exercised.; Risk: Ambiguous provider outcomes can cause duplicate-send risk if reconciled incorrectly. | Run target-like interoperability and failure drills before production use.<br>**Proof:** Prove SMTP acceptance, IMAP append, throttling and outcome reconciliation. |
|
||||
| **addresses.recipient_sources**<br>Select reusable address lists as Campaign recipient sources. | `available_unconfigured` | test/current_workspace: govoplan-addresses/tests (14 tests passed) | Condition: Enable the Addresses module explicitly.; Gap: Addresses is disabled in the pinned root profile.; Risk: Recipient governance may differ between source data and frozen Campaign evidence. | Enable only when reusable lists are a pilot requirement.<br>**Proof:** Build a Campaign from a source list and verify immutable recipient provenance. |
|
||||
| **audit.local**<br>Retain tenant/system audit evidence and retry governed audit events. | `verified` | test/current_workspace: govoplan-audit/tests (5 tests passed) | Condition: Conclusion covers local database evidence only.; Gap: No central sink, retention enforcement or tamper-evident archive is verified.; Risk: Local audit evidence may not satisfy organizational records or SIEM requirements. | Define retention and export requirements before production approval.<br>**Proof:** Exercise privileged-event review, retention and any required external export. |
|
||||
| **identity.federation**<br>Integrate external LDAP/AD, OIDC/SAML or SCIM identity infrastructure. | `scaffold` | documentation/documented_model: govoplan-idm/README.md | Gap: No end-to-end provider connector or federated login is verified.; Risk: Federation-dependent organizations cannot use the current pilot composition without extra implementation. | Use local pilot accounts or assess and implement the selected provider path.<br>**Proof:** Run provider metadata, login/provisioning, deprovisioning and failure tests. |
|
||||
| **compliance.export_control**<br>Screen persons and organizations against embargo/sanctions lists with review evidence. | `planned` | issue/documented_model: https://git.add-ideas.de/GovOPlaN/govoplan/issues/12 | Gap: No provider, list provenance, match policy, review flow or legal evidence exists.; Risk: The current composition must not be represented as performing export-control screening. | Keep outside pilot claims until the user story is implemented and legally validated.<br>**Proof:** Validate list ingestion, versioning, matching, false-positive review and audit evidence. |
|
||||
| **workflow**<br>Orchestrate the journey through Workflow. | `planned` | observation/documented_model: Assessment scope (Explicitly postponed) | Gap: Workflow is outside this assessment.; Risk: Including it would overstate the assessed composition. | Do not enable or claim Workflow for this reference pilot.<br>**Proof:** Reassess in a later Workflow-focused composition. |
|
||||
|
||||
## Infrastructure matrix
|
||||
|
||||
| Requirement | Status | Evidence | Conditions and gaps | Recommendation and proof |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **runtime.web_api**<br>Serve matching WebUI and API artifacts with health endpoints. | `verified` | test/committed_source: govoplan-core/tests/test_module_system.py; route/committed_source: govoplan-core/src/govoplan_core/server/fastapi.py#/health | Condition: Materialize the matching catalogued artifacts in the target.; Gap: No production image or service bundle is supplied by the profile.; Risk: Editable source processes are unsuitable as a production artifact. | Install matching catalogued WebUI/API refs and supervise them as immutable artifacts.<br>**Proof:** Deploy the built artifacts and run health/module-route checks. |
|
||||
| **runtime.worker**<br>Run durable asynchronous Campaign jobs. | `available_unconfigured` | configuration/current_workspace: govoplan/tools/launch/launch-production-like-dev.sh | Condition: Redis and a supervised worker are required when Celery is enabled.; Gap: Target heartbeat, restart and queue-age alerting are not proved.; Risk: Queued work can stall silently without monitoring. | Start one worker for the pilot and split queues only after measurement.<br>**Proof:** Interrupt and restart a worker while preserving job/reconciliation safety. |
|
||||
| **runtime.scheduler**<br>Run periodic recovery and cleanup safely. | `partial` | test/committed_source: govoplan-calendar/tests/test_outbox.py (Committed and pushed after the catalogued Calendar v0.1.8 tag) | Condition: Calendar outbox and recovery work is remote-integrated source but not stable-package-integrated.; Gap: No distributed leader election or target supervision is established.; Risk: Multiple schedulers can duplicate periodic dispatch without locking. | Omit from the Campaign-only pilot or run one supervised instance.<br>**Proof:** Prove missed-schedule recovery and single-leader behavior. |
|
||||
| **data.postgresql**<br>Persist application state in PostgreSQL with explicit migrations. | `verified` | configuration/committed_source: govoplan/dev/postgres; test/committed_source: govoplan/tools/checks/postgres-integration-check.py | Condition: Target database remains deployment-owned.; Gap: HA, patching, WAL policy and capacity are not assessed.; Risk: A single unprotected database is a system-wide failure point. | Use managed or dedicated PostgreSQL with explicit migration and backup controls.<br>**Proof:** Run migrations and restore a target-like database. |
|
||||
| **queue.redis**<br>Provide the Celery broker and queue persistence. | `available_unconfigured` | configuration/current_workspace: govoplan/dev/production-like/docker-compose.yml#redis | Gap: Authentication, TLS, eviction, HA and queue-loss policy are not assessed.; Risk: Broker loss or eviction can delay work even when database business state survives. | Configure private persistent Redis and monitor queue age/depth.<br>**Proof:** Exercise broker interruption and worker recovery. |
|
||||
| **storage.local**<br>Persist managed files on a durable single-node/shared path. | `verified` | contract/committed_source: govoplan-files/src/govoplan_files/backend/storage/backends.py | Condition: Path is durable, private, writable and backed up.; Gap: Node-local storage cannot support independent API replicas.; Risk: Files can be lost or become inconsistent with database state. | Use for a bounded pilot only with coordinated backup.<br>**Proof:** Restore files and verify all database references. |
|
||||
| **storage.object**<br>Use S3-compatible storage for independently scalable file persistence. | `partial` | test/current_workspace: govoplan-files/tests/test_connector_providers.py | Gap: No chosen target service or storage-backend interoperability drill.; Risk: Provider semantics, CA or lifecycle mismatch can break file access/retention. | Select and exercise the target object store before horizontal scaling.<br>**Proof:** Upload, retrieve, version, back up and restore representative objects. |
|
||||
| **edge.proxy_tls**<br>Terminate HTTPS and enforce proxy/security policy. | `external_system` | route/committed_source: govoplan-ops/src/govoplan_ops/backend/api/v1/routes.py#deployment-security | Gap: No proxy, certificates, renewal, header or request-limit configuration is shipped here.; Risk: Incorrect proxy/cookie/CORS configuration can expose sessions or block legitimate use. | Supply and monitor the edge through the target platform.<br>**Proof:** Run external TLS/header/cookie/CORS and upload-limit tests. |
|
||||
| **security.secret_store**<br>Inject and rotate master, database, mail and connector secrets. | `external_system` | configuration/committed_source: govoplan/.env.example | Gap: No target secret manager or rotation drill is selected.; Risk: Loss of the master key makes encrypted credentials unavailable; leakage compromises connectors. | Use target-native secret injection and document rotation/recovery.<br>**Proof:** Rotate a non-production credential and recover from a protected backup. |
|
||||
| **identity.access**<br>Authenticate users and enforce tenant-scoped authorization through the selected identity mode. | `verified` | test/committed_source: govoplan-access/tests/test_auth_dependencies.py; test/committed_source: govoplan-core/tests/test_api_smoke.py#cookie-session-csrf | Condition: The bounded pilot accepts local GovOPlaN accounts.; Gap: Target MFA, federation, provisioning, and joiner/mover/leaver requirements are not assessed.; Risk: A local-only identity topology may not satisfy institutional production policy. | Use controlled local pilot accounts and assess the mandatory production identity topology separately.<br>**Proof:** Exercise login, role change, account suspension, protected bootstrap, and break-glass recovery in the target. |
|
||||
| **connectors.mail**<br>Reach the selected SMTP/IMAP and other external connector endpoints under explicit network and provider policy. | `available_unconfigured` | test/current_workspace: govoplan-mail/tests (Protocol adapters have direct tests; no target provider was exercised); documentation/documented_model: govoplan-campaign/docs/CAMPAIGN_DELIVERY_RUNBOOK.md | Condition: The deployment supplies DNS, egress, proxy, CA trust, scoped service accounts, and provider limits.; Gap: No target endpoint, TLS chain, throttling, sender policy, bounce/reply path, or disclosure agreement is assessed.; Risk: Provider rejection, delay, or ambiguous outcomes can affect delivery and evidence completeness. | Use a dedicated safe provider account for the pilot and require target interoperability evidence before production.<br>**Proof:** Exercise target-like SMTP acceptance, IMAP append, throttling, outage, retry, and reconciliation through the approved network path. |
|
||||
| **operations.monitoring**<br>Detect API, database, worker, queue, storage and delivery degradation. | `partial` | route/committed_source: govoplan-ops/src/govoplan_ops/backend/api/v1/routes.py#/ops/readiness; contract/committed_source: govoplan-core/src/govoplan_core/server/fastapi.py#slow-request-logging | Gap: No metrics exporter, log collector, dashboards, alert routes or SLO is verified.; Risk: Failures and queue backlog can remain unnoticed. | Integrate external monitoring before small production.<br>**Proof:** Trigger each readiness/delivery failure and verify an actionable alert. |
|
||||
| **operations.audit**<br>Retain, monitor, review, and where required export security and business audit evidence. | `partial` | test/current_workspace: govoplan-audit/tests (Local audit persistence and retry behavior are exercised) | Condition: Local database audit evidence is part of coordinated backup and access review.; Gap: Target retention enforcement, tamper-evident export, SIEM integration, alerting, and privileged review are not verified.; Risk: Local evidence alone may not meet institutional security, records, or incident-response requirements. | Define the target audit retention, export, monitoring, and review controls before production approval.<br>**Proof:** Exercise privileged-event review, retention, export failure/retry, and target SIEM or archive ingestion. |
|
||||
| **operations.backup_restore**<br>Back up and restore database, files, configuration and keys as a coherent service. | `partial` | documentation/documented_model: govoplan-core/docs/DEPLOYMENT_OPERATOR_GUIDE.md; issue/documented_model: https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/29 | Gap: No target full-service restore drill or measured RPO/RTO exists.; Risk: Partial restore can produce missing files, unusable secrets or inconsistent evidence. | Treat Core #29 and a target restore drill as a production gate.<br>**Proof:** Restore the whole service into an isolated environment and measure it. |
|
||||
| **operations.disaster_recovery**<br>Recover the service after site or dependency loss within agreed RPO/RTO. | `not_assessed` | absence/current_workspace: No target DR plan or exercise evidence supplied | Gap: RPO/RTO, off-site copies, recovery order, failover, communications and exercise schedule are unknown.; Risk: Service and evidence may be unrecoverable after a major incident. | Define and exercise DR before any availability commitment.<br>**Proof:** Run a documented end-to-end recovery exercise. |
|
||||
## Data flows and trust boundaries
|
||||
|
||||
| Flow | From → to | Data | Trust boundary | Controls |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `browser.api` | User browser → Reverse proxy and GovOPlaN WebUI/API | Session and CSRF cookies; Campaign content; Recipient personal data; Managed files | Client/public to application | HTTPS; Exact CORS origins; Secure cookies; Tenant and RBAC enforcement; Request limits |
|
||||
| `api.database` | GovOPlaN API and workers → PostgreSQL | Tenant and identity records; Campaign drafts, snapshots and jobs; Connector metadata; Audit evidence | Application to primary state store | Dedicated database identity; Private or encrypted transport; Migrations; Backup and retention |
|
||||
| `api.queue.worker` | GovOPlaN API → Redis and Celery worker | Job identifiers; Queue routing and retry metadata | Request plane to asynchronous processing plane | Private authenticated broker; Bounded payloads; Idempotent claims; Queue monitoring |
|
||||
| `worker.mail` | GovOPlaN Campaign worker → External SMTP and IMAP services | Recipient addresses; Message bodies; Attachments; Sent-message copy | GovOPlaN to external communication provider | Scoped service account; TLS and CA policy; Sender and recipient policy; Rate limits; Outcome reconciliation |
|
||||
| `worker.connectors` | GovOPlaN connector worker → External address, file, object or calendar service | Addresses; Files and provenance; Calendar resources | GovOPlaN to organizational/external content systems | Explicit sync direction; Scoped credentials; Endpoint allow-list; Provenance; Conflict and reconciliation policy |
|
||||
|
||||
## Risks and residual risks
|
||||
|
||||
| Risk | Impact | Treatment | Owner | Residual risk |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **risk.reproducibility**<br>The signed package selection is reproducible but has not been accepted as an installed target composition. | Installation or configuration drift can still produce uncertain deployed behavior. | Materialize the signed catalog in an isolated target and run installed-artifact acceptance gates. | unassigned | Module and environment differences still require release-environment verification. |
|
||||
| **risk.delivery_provider**<br>Target SMTP/IMAP behavior and failure modes are unproved. | Failed, delayed or duplicate communication and incomplete evidence. | Run target-like interoperability, throttling and uncertainty drills. | unassigned | External provider outages and ambiguous outcomes remain operational risks. |
|
||||
| **risk.recovery**<br>Backup/restore and disaster recovery are not demonstrated across all state and keys. | Irrecoverable or inconsistent service after loss. | Complete Core #29 and an isolated full-service restore/DR exercise. | unassigned | Recovery time and data loss remain bounded by the selected external infrastructure. |
|
||||
|
||||
## Recommendations
|
||||
|
||||
- Proceed only with a controlled internal Campaign pilot after the bounded proof checks pass.
|
||||
- Use the minimal composition and enable Addresses only for an explicit reusable-recipient journey.
|
||||
- Do not claim Workflow, export-control screening, identity federation or production DR as implemented.
|
||||
- Treat installed-release acceptance, target mail proof, monitoring and a coherent restore drill as production gates.
|
||||
|
||||
## Proof-of-concept and promotion checks
|
||||
|
||||
1. Materialize the signed catalog into an isolated installation and rerun contract, migration and module-permutation gates against the installed artifacts.
|
||||
2. Collect the isolated installation with the bounded installed-composition evidence contract; require exact enabled package/module versions, complete RECORD verification and immutable provenance anchored to this assessment.
|
||||
3. Run a safe target-like Campaign through SMTP acceptance, IMAP append, reporting and audit.
|
||||
4. Drill worker, Redis and ambiguous-delivery failures without duplicate sends.
|
||||
5. Restore PostgreSQL, managed files, configuration and encrypted credentials and measure RPO/RTO.
|
||||
6. Validate proxy/TLS, cookies/CORS, account bootstrap, secret redaction, monitoring and alert delivery.
|
||||
7. Measure representative Campaign/file/queue/database load and external throttling.
|
||||
8. Require separately issued, expiring and independently scope-authorized evidence before marking target environment, external provider or production approval proof as checked.
|
||||
|
||||
## Generation contract
|
||||
|
||||
This report is deterministic output from the schema-validated JSON companion.
|
||||
The generator rejects duplicate JSON keys, schema drift, secret-bearing field
|
||||
names, stale checked-in output, and oversized inputs. A new assessment or
|
||||
release changes the canonical input hash and requires review of the affected
|
||||
evidence and conclusions through the release-aware reassessment tool.
|
||||
+275
-17
@@ -1,4 +1,16 @@
|
||||
# GovOPlaN Capability and IT-Infrastructure Fit Assessment
|
||||
# Supporting Narrative: 2026-07-22 Capability and Infrastructure Assessment
|
||||
|
||||
> **Canonical report:** The schema-validated human report is generated from the
|
||||
> machine-readable input at
|
||||
> [`CAPABILITY_AND_INFRASTRUCTURE_FIT.generated.md`](CAPABILITY_AND_INFRASTRUCTURE_FIT.generated.md).
|
||||
> This file retains the original hand-authored evidence narrative and operator
|
||||
> guidance; it is not maintained as a second set of conclusions.
|
||||
|
||||
> **Pinned historical evidence:** This document assesses the exact 2026-07-22
|
||||
> Campaign composition below. It is intentionally not updated to describe later
|
||||
> main-branch work. Use [Strategy Status](../../strategy/STRATEGY_STATUS.md) for the current
|
||||
> cross-product reconciliation and create a new dated fit assessment for a new
|
||||
> target composition.
|
||||
|
||||
## Assessment record
|
||||
|
||||
@@ -11,8 +23,16 @@
|
||||
| Configuration basis | Root `.env.example` and the production-like development profile |
|
||||
| Scope | Campaign-centric internal pilot and small-production candidate |
|
||||
| Explicitly postponed | Workflow and workflow-driven user stories |
|
||||
| Machine-readable companion | [`capability-fit-current.json`](capability-fit-current.json) |
|
||||
| Input schema | [`capability-fit.schema.json`](capability-fit.schema.json) |
|
||||
| Machine-readable companion | [`capability-fit-current.json`](../../capability-fit-current.json) |
|
||||
| Input schema | [`capability-fit.schema.json`](../../capability-fit.schema.json) |
|
||||
|
||||
**Snapshot notice:** this assessment remains valid only for the pinned
|
||||
2026-07-22 composition above. Workflow Engine, the optional Workflow editor,
|
||||
Datasources, Dataflow, Search, encryption contracts, and other later main-branch
|
||||
work must not be inferred into this evidence record. The current product
|
||||
direction and implemented-state reconciliation are documented separately in
|
||||
the [Institutional Governance Target Architecture](../../architecture/INSTITUTIONAL_GOVERNANCE_TARGET_ARCHITECTURE.md)
|
||||
and [Strategy Status](../../strategy/STRATEGY_STATUS.md).
|
||||
|
||||
This is a fit assessment, not a production approval or security certification.
|
||||
It deliberately does not infer implementation from a repository, issue, or
|
||||
@@ -57,7 +77,7 @@ profile intentionally runs only PostgreSQL and Redis in containers; API,
|
||||
WebUI, worker, and scheduler processes still run from editable source trees. A
|
||||
target deployment must supply TLS termination, process supervision, secret
|
||||
injection, monitoring, backup storage, and recovery procedures. The open
|
||||
[Core backup/restore issue #29](https://git.add-ideas.de/add-ideas/govoplan-core/issues/29)
|
||||
[Core backup/restore issue #29](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/29)
|
||||
is a production gate. Identity federation, external audit export, and a tested
|
||||
disaster-recovery plan are also not complete.
|
||||
|
||||
@@ -134,7 +154,7 @@ persistence, and durable shared or object storage. Run one scheduler only when a
|
||||
selected module needs scheduled recovery. Multiple scheduler replicas require
|
||||
leader election or an external lock, which is not established by this report.
|
||||
|
||||
This topology is a recommendation from the [Ops scalability profiles](https://git.add-ideas.de/add-ideas/govoplan-ops/src/branch/main/docs/SCALABILITY_PROFILES.md),
|
||||
This topology is a recommendation from the [Ops scalability profiles](https://git.add-ideas.de/GovOPlaN/govoplan-ops/src/branch/main/docs/SCALABILITY_PROFILES.md),
|
||||
not a currently shipped production Compose/Kubernetes/systemd package.
|
||||
|
||||
## Functional capability matrix
|
||||
@@ -154,7 +174,7 @@ not a currently shipped production Compose/Kubernetes/systemd package.
|
||||
| Configured-system documentation and Ops status pages | `verified` | Docs/Ops manifests contribute protected routes and WebUI packages; Ops code checks database, Redis, workers, storage and deployment-security settings. | This does not replace external monitoring or a target runbook. |
|
||||
| Calendar/CalDAV integration | `partial` | Calendar `v0.1.8` supplies the catalogued storage/sync foundation. The durable external-write outbox, worker recovery, reconciliation, and retention work is committed, tested, and pushed on Calendar `main` after that tag. | The post-tag outbox work is remote-integrated source, not local-only WIP, but it is not in the signed stable package baseline and has not passed a target CalDAV drill. Bulk synchronized-calendar migration semantics remain separate work. |
|
||||
| External LDAP/AD, OIDC/SAML or SCIM identity integration | `scaffold` | IDM owns normalized assignment APIs and documents connector boundaries. | Provider connectors, login callback flow and target directory reconciliation are not an implemented end-to-end capability. Use local accounts for this pilot. |
|
||||
| Export-control/embargo-list screening | `planned` | Product-level [GovOPlaN #12](https://git.add-ideas.de/add-ideas/govoplan/issues/12) defines the consumer-independent user story. | No screening provider, list provenance, matching policy, review flow or legal evidence exists in this composition. |
|
||||
| Export-control/embargo-list screening | `planned` | Product-level [GovOPlaN #12](https://git.add-ideas.de/GovOPlaN/govoplan/issues/12) defines the consumer-independent user story. | No screening provider, list provenance, matching policy, review flow or legal evidence exists in this composition. |
|
||||
| Workflow-driven journeys and views | `planned` | Workflow contracts/concepts exist outside this assessment. | Explicitly postponed. Do not include Workflow in pilot or production claims from this report. |
|
||||
|
||||
## Infrastructure matrix
|
||||
@@ -177,13 +197,13 @@ not a currently shipped production Compose/Kubernetes/systemd package.
|
||||
| Health/readiness | `verified` | `/health`, protected `/health/details`, and Ops checks for DB, Redis, workers, storage, maintenance and cookie/CORS posture. | Add external probes and distinguish liveness from dependency readiness for the chosen orchestrator. |
|
||||
| Metrics, logs and alerting | `partial` | Correlation IDs, slow-request/query metrics in logs, worker inspection and operator status are implemented. | No bundled metrics exporter, log collector, dashboards, queue-depth alerts, pager route or SLO is verified. |
|
||||
| Audit | `partial` | Local audit tables and retry outbox are verified. | Retention, tamper-evident export, privileged access review and SIEM integration are unproved. |
|
||||
| Backup and restore | `partial` | Operator guide and installer hooks describe `pg_dump`/`pg_restore`; SQLite and simulated installer rollback drills exist. | [Core #29](https://git.add-ideas.de/add-ideas/govoplan-core/issues/29) remains open. No target PostgreSQL + files + secrets restore drill or measured RTO/RPO exists. |
|
||||
| Backup and restore | `partial` | Operator guide and installer hooks describe `pg_dump`/`pg_restore`; SQLite and simulated installer rollback drills exist. | [Core #29](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/29) remains open. No target PostgreSQL + files + secrets restore drill or measured RTO/RPO exists. |
|
||||
| Disaster recovery | `not_assessed` | The Ops guide asks for RPO/RTO and restore drills. | No agreed RPO/RTO, off-site copy, failover topology, dependency recovery order, communications plan or exercise evidence was supplied. |
|
||||
|
||||
The scalability and sizing documentation delivered the documentation portions
|
||||
of [Core #217](https://git.add-ideas.de/add-ideas/govoplan-core/issues/217) and
|
||||
[Core #219](https://git.add-ideas.de/add-ideas/govoplan-core/issues/219).
|
||||
[Core #28](https://git.add-ideas.de/add-ideas/govoplan-core/issues/28) records the
|
||||
of [Core #217](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/217) and
|
||||
[Core #219](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/219).
|
||||
[Core #28](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/28) records the
|
||||
operator-documentation slice. These closed tickets are evidence of documented
|
||||
models, not evidence that an organization's production environment has passed
|
||||
them.
|
||||
@@ -360,7 +380,11 @@ windows. Only `active` and `next` entries are trusted; unknown statuses,
|
||||
duplicate key IDs and malformed structured keyrings fail closed, while revoked,
|
||||
disabled and retired entries are ignored. Do not establish this trust file by
|
||||
downloading it in the same rerun. Public JSON responses are bounded to 16 MiB.
|
||||
Use `--json` or `--output PATH` for automation.
|
||||
Use `--json` or `--output PATH` for automation. Evidence and report files are
|
||||
written through a bounded same-directory atomic replacement, with mode `0600`
|
||||
and file and directory `fsync`. All parent directories must already exist, no
|
||||
parent component may be a symlink, and an existing symlink or non-regular output
|
||||
target is rejected.
|
||||
|
||||
Exit status `0` means the assessed release metadata is current, `2` means
|
||||
explicit review is required, and `1` means the schema or trust checks are
|
||||
@@ -372,23 +396,257 @@ metadata, published-keyring integrity and optional local tag provenance; it does
|
||||
not prove installed artifacts, target providers, target infrastructure, or
|
||||
production fitness. Those remain separate proof checks above.
|
||||
|
||||
### Installed-composition evidence
|
||||
|
||||
The same rerun can inspect the Python environment in which it executes and bind
|
||||
that observation to the assessment and signed catalog:
|
||||
|
||||
```bash
|
||||
./.venv/bin/python tools/assessments/capability-fit.py \
|
||||
--public \
|
||||
--trusted-keyring /srv/govoplan/trust/catalog-keyring.json \
|
||||
--collect-installed-evidence /var/tmp/govoplan-installed-evidence.json \
|
||||
--output /var/tmp/govoplan-fit-review.json
|
||||
```
|
||||
|
||||
The collector follows the strict version `0.4.0`
|
||||
[`installed-composition-evidence.schema.json`](../../installed-composition-evidence.schema.json)
|
||||
contract. It enumerates all installed distributions whose normalized name starts
|
||||
with `govoplan-`, compares the enabled assessed package and module-manifest
|
||||
versions, and identifies missing, duplicate and extra GovOPlaN distributions.
|
||||
Those differences produce stable review targets for the affected composition
|
||||
entries and evidence-backed conclusions.
|
||||
|
||||
For each distribution, the collector also:
|
||||
|
||||
- reads the installed wheel `RECORD` file directly, with a 4 MiB metadata bound,
|
||||
rather than relying on an interpreted distribution file list; missing files,
|
||||
duplicate or malformed declarations, mismatched declared sizes and hashes all
|
||||
prevent a `verified` result;
|
||||
- verifies every supported SHA-256 wheel-payload declaration in that local
|
||||
metadata, within fixed limits of 256 GovOPlaN distributions, 10,000 files and
|
||||
512 MiB hashed per distribution, 50,000 files and 2 GiB hashed for one
|
||||
collection, and 64 MiB for any single file;
|
||||
- accepts package-owned data-file targets below the installation prefix and
|
||||
declared `console_scripts`, while rejecting other traversal, undeclared
|
||||
scripts, paths outside the prefix and leaf symlinks before opening a file;
|
||||
- reports pip-generated, unhashed `__pycache__/*.pyc` entries separately only
|
||||
when their derived source is a hashed payload entry, including generated
|
||||
files below a confined package-owned runtime-data prefix;
|
||||
- labels every PEP 610 observation with the explicit
|
||||
`local-pep610-metadata` basis and distinguishes coherent declared Git commits
|
||||
and archive hashes from editable installs, local directories,
|
||||
package-index/unknown origins and malformed metadata;
|
||||
- compares a non-editable VCS commit with the assessment commit and, when one is
|
||||
present, the signed catalog `selected_units` commit;
|
||||
- derives two content identities from bytes it actually verifies: an
|
||||
install-stable identity for immutable wheel-declared files and a full
|
||||
installed-RECORD identity, while retaining only SHA-256 digests and counts;
|
||||
- records only package/module identifiers, versions, bounded counters, hashes
|
||||
and stable error codes. It never writes direct URLs, paths, hostnames,
|
||||
usernames, exception text or file contents.
|
||||
|
||||
`RECORD` `verified` means complete matching coverage of the wheel payload
|
||||
declared by the local metadata, not every runtime byte and not a release-origin
|
||||
binding. The proof scope exposes the aggregate count of generated unhashed
|
||||
bytecode; runtime activation remains unchecked. `RECORD` agreement proves that
|
||||
payload files agree with installed metadata, but does not make self-consistent
|
||||
metadata a trusted release origin. Similarly, a version string is not artifact
|
||||
integrity. PEP 610 VCS metadata is accepted only for
|
||||
`vcs: git`, a full 40–64 hexadecimal commit, one mutually exclusive provenance
|
||||
form and a coherent bounded URL shape. Editable and directory-backed installs
|
||||
remain mutable even when their small editable-install `RECORD` is valid. Archive
|
||||
or index artifacts without a hash anchored by the assessed release remain
|
||||
unanchored. A matching declaration is reported under
|
||||
`installed_source_provenance` as local consistency only, with
|
||||
`release_origin_bound: false` until the separate origin proof succeeds. A signed
|
||||
catalog can contain `release.artifacts` identities computed directly from built,
|
||||
bounded wheel files. Wheels with installer-transformed scripts or headers are
|
||||
marked `requires_installer_receipt`; catalog metadata alone cannot attest those
|
||||
post-install bytes. A role-scoped installer receipt instead binds the exact
|
||||
installed-evidence digest, full installed payload identities, catalog archive
|
||||
digests and canonical signed-catalog digest. The tool never accepts an installed
|
||||
hash merely because the installed evidence asserted it.
|
||||
|
||||
Only evidence produced in-process by `--collect-installed-evidence` is a local
|
||||
observation, and it must be no more than five minutes old (with at most 30
|
||||
seconds of future clock skew) at the selected verification time. Use
|
||||
`--installed-evidence PATH` to compare evidence collected in a separate
|
||||
environment. Imported JSON without a valid independent installer receipt
|
||||
carries a blocker: it can identify differences but cannot establish checked or
|
||||
valid installed proof. A receipt signed by a separately provisioned installer
|
||||
authority authenticates that exact imported evidence across the process
|
||||
boundary. The proof scope records observation mode, collection/evaluation time,
|
||||
receipt authentication, freshness, and whether evaluation used current time or
|
||||
an explicit historical override. Evidence input and output are bounded to 4
|
||||
MiB. Collection loads the `govoplan.modules` entry-point factories in order to
|
||||
read module IDs and manifest versions; that executes installed GovOPlaN manifest
|
||||
code. Run it only inside the installation being assessed and with the same
|
||||
isolation expected for other installed-artifact acceptance checks. This
|
||||
collector does not observe
|
||||
which modules a running service activated, migrations, configuration, health,
|
||||
or reference-journey behavior. Runtime activation therefore remains an explicit
|
||||
unchecked boundary.
|
||||
|
||||
### Reference-readiness, provider and production proof boundary
|
||||
|
||||
Installed evidence cannot establish target acceptance, accessibility, privacy,
|
||||
security, operations, recovery, an external provider, or production use. These
|
||||
scopes use a separate, expiring
|
||||
[`capability-fit-boundary-evidence.schema.json`](../../capability-fit-boundary-evidence.schema.json)
|
||||
bundle. The bundle is bound to the assessment ID, assessment release and exact
|
||||
installed-evidence SHA-256 digest. It contains only opaque subject/control/result
|
||||
IDs and content hashes, not endpoints, credentials, people or raw result files.
|
||||
|
||||
Boundary evidence is accepted only when at least one Ed25519 signature validates
|
||||
against a separately provisioned
|
||||
[`capability-fit-proof-authority-keyring.schema.json`](../../capability-fit-proof-authority-keyring.schema.json).
|
||||
Each authority key explicitly lists the scopes it may attest. Target,
|
||||
accessibility, privacy, security, operations, recovery, and provider claims use
|
||||
`passed` or `failed`; production claims use `approved` or `rejected`.
|
||||
One claim per scope, unique control/artifact IDs, `issued_at < expires_at`, current
|
||||
validity and exact digest binding are mandatory. Any schema, binding, time,
|
||||
signature or authority blocker leaves every supplied boundary claim unchecked;
|
||||
one malformed authority member or invalid validity interval invalidates the
|
||||
supplied authority set as a whole. An authorized negative result is checked but
|
||||
requires review only after every prerequisite, including installed
|
||||
release-origin binding, is established.
|
||||
Signatures cover UTF-8 JSON with the `signatures` member omitted, object keys
|
||||
sorted, compact `,`/`:` separators and non-ASCII characters escaped, matching
|
||||
the tool's deterministic canonicalization.
|
||||
|
||||
`tools/assessments/boundary-evidence.py` is the bounded issuance path. It
|
||||
accepts a private target-run manifest conforming to
|
||||
[`capability-fit-boundary-run.schema.json`](../../capability-fit-boundary-run.schema.json),
|
||||
hashes each retained result file without following a final-component symlink,
|
||||
and excludes all paths and raw results from the signed receipt. Issuance is
|
||||
refused unless an independently trusted catalog, exact installed payload,
|
||||
signed installer receipt, and role-scoped installer authority already pass.
|
||||
Every claim must be covered by a supplied Ed25519 private key whose public key
|
||||
is authorized for the full proof interval; catalog and installer key reuse is
|
||||
rejected. The command immediately verifies its own result and atomically writes
|
||||
both the proof and a sanitized review. The complete operator procedure and
|
||||
recovery measurement definition are in
|
||||
[`TARGET_MATURITY_EVIDENCE_RUNBOOK.md`](../../operations/TARGET_MATURITY_EVIDENCE_RUNBOOK.md).
|
||||
|
||||
```bash
|
||||
./.venv/bin/python tools/assessments/capability-fit.py \
|
||||
--public \
|
||||
--trusted-keyring /srv/govoplan/trust/catalog-keyring.json \
|
||||
--installed-evidence /srv/govoplan/evidence/installed.json \
|
||||
--boundary-evidence /srv/govoplan/evidence/target-proof.json \
|
||||
--boundary-authority-keyring /srv/govoplan/trust/proof-authorities.json \
|
||||
--expected-external-provider-subject provider-production
|
||||
```
|
||||
|
||||
Promotion automation must opt into its required boundaries. Add
|
||||
`--require-reference-readiness` to require all six target scopes,
|
||||
`--require-external-provider-proof` when the product depends on a provider, and
|
||||
`--require-production-approval` for production admission. These switches turn
|
||||
missing, expired, revoked, mismatched, negative, or otherwise unchecked claims
|
||||
into a blocking exit status rather than merely reporting them as an unproven
|
||||
boundary.
|
||||
|
||||
With `--installed-evidence`, this command performs comparison and proof-binding
|
||||
diagnostics. Without an installer receipt, the imported document remains
|
||||
unsigned, so neither it nor the boundary claim becomes accepted proof. Direct
|
||||
in-process collection can match catalog-anchored artifact identities for wheels
|
||||
without installer-transformed files. The installer-receipt flow below
|
||||
authenticates a durable cross-process observation and is required for transformed
|
||||
script/header payloads.
|
||||
|
||||
Issue a receipt only in the installation process performing the live collection.
|
||||
The command refuses evidence supplied from a file and first validates the
|
||||
assessment, independently trusted signed catalog, composition and RECORD bytes:
|
||||
|
||||
```bash
|
||||
./.venv/bin/python tools/assessments/installer-receipt.py \
|
||||
--assessment /srv/govoplan/assessment.json \
|
||||
--catalog /srv/govoplan/catalogs/stable.json \
|
||||
--keyring /srv/govoplan/catalogs/keyring.json \
|
||||
--trusted-keyring /srv/govoplan/trust/catalog-keyring.json \
|
||||
--receipt-id install:production:20260722 \
|
||||
--signing-key installer-production=/run/secrets/installer-ed25519.pem \
|
||||
--python-artifact govoplan-core=/srv/govoplan/staged/govoplan_core-0.1.13-py3-none-any.whl \
|
||||
--python-artifact govoplan-campaign=/srv/govoplan/staged/govoplan_campaign-0.1.10-py3-none-any.whl \
|
||||
--evidence-output /srv/govoplan/evidence/installed.json \
|
||||
--receipt-output /srv/govoplan/evidence/installer-receipt.json
|
||||
```
|
||||
|
||||
Repeat `--python-artifact PACKAGE=/exact/consumed.whl` for every enabled
|
||||
GovOPlaN distribution. The issuer reopens each exact wheel, verifies its archive
|
||||
and payload identities against the signed catalog, and refuses a different
|
||||
build with the same name and version. Receipt issuance must follow collection
|
||||
within five minutes, and live verification must still fall inside that bounded
|
||||
window; retain the pair for a reproducible historical review at its explicit
|
||||
verification time.
|
||||
|
||||
Verify that durable pair with its separately managed trust root:
|
||||
|
||||
```bash
|
||||
./.venv/bin/python tools/assessments/capability-fit.py \
|
||||
--catalog /srv/govoplan/catalogs/stable.json \
|
||||
--keyring /srv/govoplan/catalogs/keyring.json \
|
||||
--trusted-keyring /srv/govoplan/trust/catalog-keyring.json \
|
||||
--installed-evidence /srv/govoplan/evidence/installed.json \
|
||||
--installer-receipt /srv/govoplan/evidence/installer-receipt.json \
|
||||
--installer-authority-keyring /srv/govoplan/trust/installer-authorities.json
|
||||
```
|
||||
|
||||
Target-environment and production-approval claims must use the assessment's
|
||||
`deployment_profile.id` as `subject_id`. External-provider claims require the
|
||||
operator to supply a bounded opaque expected subject with
|
||||
`--expected-external-provider-subject`; without it, such a claim remains
|
||||
unchecked and blocks. Expected and observed IDs are retained in proof scope.
|
||||
Accessibility, privacy, security, operations, and recovery claims use the same
|
||||
deployment subject. The report emits a cumulative `reference_readiness` verdict
|
||||
only when all six required scopes are checked and positive. This verdict remains
|
||||
separate from production approval and from provider-specific acceptance.
|
||||
|
||||
Both authority keyrings are governance trust roots. Installer receipt keys use
|
||||
the strict
|
||||
[`installer-receipt-authority-keyring.schema.json`](../../installer-receipt-authority-keyring.schema.json)
|
||||
contract and may attest only `installed_release_origin`; their public material
|
||||
must not be reused by catalog or boundary-proof authorities. Do not download or
|
||||
generate them from the proof bundle being checked. The checker rejects
|
||||
proof-authority public keys reused by either the published or independently
|
||||
trusted catalog keyring, and rejects the same proof public-key material assigned
|
||||
to multiple authority IDs. A malformed key, an invalid or empty validity
|
||||
interval, or ambiguous key
|
||||
identity invalidates the supplied authority set. Authority `not_after` is
|
||||
exclusive. A target operator or approver must validate the referenced
|
||||
drill/result artifacts before signing. The rerun verifies the
|
||||
attestation and its bindings; it does not fetch or reinterpret those artifacts.
|
||||
`--verification-time` exists only for reproducible historical review. Supplying
|
||||
it always marks the machine and human report as a **HISTORICAL override**; callers
|
||||
cannot relabel it as current. Live admission must omit it and use the actual
|
||||
current time.
|
||||
|
||||
No boundary bundle or production authority has been supplied for this current
|
||||
assessment. Reference-readiness, provider, and production proof therefore
|
||||
remain explicitly unchecked rather than inferred from the local GreenMail
|
||||
journey, source tests, or signed release metadata.
|
||||
|
||||
## Evidence used in this slice
|
||||
|
||||
- [Production-like profile](../dev/production-like/README.md) and
|
||||
[Compose dependencies](../dev/production-like/docker-compose.yml)
|
||||
- [Module contracts and install boundaries](MODULE_CONTRACTS_AND_INSTALLS.md)
|
||||
- [Core deployment operator guide](https://git.add-ideas.de/add-ideas/govoplan-core/src/branch/main/docs/DEPLOYMENT_OPERATOR_GUIDE.md)
|
||||
- [Ops scalability profiles](https://git.add-ideas.de/add-ideas/govoplan-ops/src/branch/main/docs/SCALABILITY_PROFILES.md)
|
||||
- [Production-like profile](../../../dev/production-like/README.md) and
|
||||
[Compose dependencies](../../../dev/production-like/docker-compose.yml)
|
||||
- [Module contracts and install boundaries](../../operations/MODULE_CONTRACTS_AND_INSTALLS.md)
|
||||
- [Core deployment operator guide](https://git.add-ideas.de/GovOPlaN/govoplan-core/src/branch/main/docs/DEPLOYMENT_OPERATOR_GUIDE.md)
|
||||
- [Ops scalability profiles](https://git.add-ideas.de/GovOPlaN/govoplan-ops/src/branch/main/docs/SCALABILITY_PROFILES.md)
|
||||
- Actual module manifests in the pinned repositories and the static contract
|
||||
checker in this meta repository
|
||||
- Core module-system/API smoke/auth/install-config tests, plus focused Campaign,
|
||||
Files, Mail, Audit and Addresses tests
|
||||
- [Campaign delivery runbook](https://git.add-ideas.de/add-ideas/govoplan-campaign/src/branch/main/docs/CAMPAIGN_DELIVERY_RUNBOOK.md)
|
||||
- [Campaign delivery runbook](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/src/branch/main/docs/CAMPAIGN_DELIVERY_RUNBOOK.md)
|
||||
- [Live signed stable catalog](https://govoplan.add-ideas.de/catalogs/v1/channels/stable.json)
|
||||
and [published keyring](https://govoplan.add-ideas.de/catalogs/v1/keyring.json),
|
||||
verified against a separately provisioned local trust keyring
|
||||
- Annotated source tags `govoplan-core/v0.1.13` and
|
||||
`govoplan-campaign/v0.1.10`, including their catalogued Python and WebUI refs
|
||||
- Installed-composition, boundary-proof and independently scoped proof-authority
|
||||
schemas plus their deterministic review tests; no current target or production
|
||||
proof bundle is asserted
|
||||
|
||||
Checks retained from the first assessment slice against its then-current
|
||||
workspace:
|
||||
@@ -0,0 +1,91 @@
|
||||
# DSAR Provider Coverage
|
||||
|
||||
This generated matrix is enforced by `tools/checks/check-dsar-coverage.py`.
|
||||
A migration-owning module must register and document its canonical DSAR provider.
|
||||
Every other active module requires a reviewed explanation of why it owns no
|
||||
persistent subject-data store. Adding a migration invalidates that explanation.
|
||||
|
||||
- Active modules: 72
|
||||
- Registered and documented DSAR providers: 49
|
||||
- Reviewed no-store rationales: 23
|
||||
- Unexplained coverage gaps: 0
|
||||
|
||||
| Module | Repository | Persistence | Coverage | Rationale |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `access` | `govoplan-access` | Migration-owned | Provider | Provider `privacy.dsar.access` is registered and documented. |
|
||||
| `addresses` | `govoplan-addresses` | Migration-owned | Provider | Provider `privacy.dsar.addresses` is registered and documented. |
|
||||
| `admin` | `govoplan-admin` | Migration-owned | Provider | Provider `privacy.dsar.admin` is registered and documented. |
|
||||
| `approvals` | `govoplan-approvals` | Migration-owned | Provider | Provider `privacy.dsar.approvals` is registered and documented. |
|
||||
| `assets` | `govoplan-assets` | No module migration | Reviewed no-store rationale | Contract-only module: asset persistence and lifecycle APIs are not implemented; reassess before adding a migration-owned store. |
|
||||
| `audit` | `govoplan-audit` | Migration-owned | Provider | Provider `privacy.dsar.audit` is registered and documented. |
|
||||
| `booking` | `govoplan-booking` | No module migration | Reviewed no-store rationale | Contract-only module: booking persistence and reservation workflows are not implemented; reassess before adding a migration-owned store. |
|
||||
| `calendar` | `govoplan-calendar` | Migration-owned | Provider | Provider `privacy.dsar.calendar` is registered and documented. |
|
||||
| `campaigns` | `govoplan-campaign` | Migration-owned | Provider | Provider `privacy.dsar.campaigns` is registered and documented. |
|
||||
| `cases` | `govoplan-cases` | Migration-owned | Provider | Provider `privacy.dsar.cases` is registered and documented. |
|
||||
| `certificates` | `govoplan-certificates` | No module migration | Reviewed no-store rationale | Contract-only module: certificate issuance and revocation persistence are not implemented; reassess before adding a migration-owned store. |
|
||||
| `committee` | `govoplan-committee` | Migration-owned | Provider | Provider `privacy.dsar.committee` is registered and documented. |
|
||||
| `connectors` | `govoplan-connectors` | Migration-owned | Provider | Provider `privacy.dsar.connectors` is registered and documented. |
|
||||
| `consultation` | `govoplan-consultation` | No module migration | Reviewed no-store rationale | Contract-only module: consultation submissions and evaluation persistence are not implemented; reassess before adding a migration-owned store. |
|
||||
| `contracts` | `govoplan-contracts` | No module migration | Reviewed no-store rationale | Contract-only module: contract, amendment, and obligation persistence are not implemented; reassess before adding a migration-owned store. |
|
||||
| `dashboard` | `govoplan-dashboard` | Migration-owned | Provider | Provider `privacy.dsar.dashboard` is registered and documented. |
|
||||
| `dataflow` | `govoplan-dataflow` | Migration-owned | Provider | Provider `privacy.dsar.dataflow` is registered and documented. |
|
||||
| `datasources` | `govoplan-datasources` | Migration-owned | Provider | Provider `privacy.dsar.datasources` is registered and documented. |
|
||||
| `decisions` | `govoplan-decisions` | Migration-owned | Provider | Provider `privacy.dsar.decisions` is registered and documented. |
|
||||
| `dist_lists` | `govoplan-dist-lists` | Migration-owned | Provider | Provider `privacy.dsar.dist_lists` is registered and documented. |
|
||||
| `dms` | `govoplan-dms` | No module migration | Reviewed no-store rationale | Stateless integration-preview module: DMS retains no document, person, credential, or provider-response store; Files and Records remain the subject-data owners. Reassess before persisting a target binding, plan, receipt, or diagnostic. |
|
||||
| `docs` | `govoplan-docs` | Migration-owned | Provider | Provider `privacy.dsar.docs` is registered and documented. |
|
||||
| `encryption` | `govoplan-encryption` | Migration-owned | Provider | Provider `privacy.dsar.encryption` is registered and documented. |
|
||||
| `erp` | `govoplan-erp` | No module migration | Reviewed no-store rationale | Stateless integration-contract module: ERP retains no invoice, payable, plan, booking observation, provider response, or credential store; Procurement, Payments, Ledger, Files, and Audit remain the subject-data owners. Reassess before persisting a target binding, plan, receipt, reconciliation decision, or diagnostic. |
|
||||
| `evaluation` | `govoplan-evaluation` | No module migration | Reviewed no-store rationale | Contract-only module: evaluation runs, responses, and scores are not persisted; reassess before adding a migration-owned store. |
|
||||
| `facilities` | `govoplan-facilities` | No module migration | Reviewed no-store rationale | Contract-only module: facility and maintenance persistence are not implemented; reassess before adding a migration-owned store. |
|
||||
| `files` | `govoplan-files` | Migration-owned | Provider | Provider `privacy.dsar.files` is registered and documented. |
|
||||
| `fit_connect` | `govoplan-fit-connect` | No module migration | Reviewed no-store rationale | Stateless transport-contract module: FIT-Connect retains no submission, attachment, receipt, acknowledgement plan, key, provider response, or diagnostic store; the owning Service, Forms, Cases, Files, and Audit workflows remain responsible for subject data. Reassess before persisting any ingress or event-log evidence. |
|
||||
| `forms` | `govoplan-forms` | Migration-owned | Provider | Provider `privacy.dsar.forms` is registered and documented. |
|
||||
| `forms_runtime` | `govoplan-forms-runtime` | Migration-owned | Provider | Provider `privacy.dsar.forms_runtime` is registered and documented. |
|
||||
| `grants` | `govoplan-grants` | No module migration | Reviewed no-store rationale | Contract-only module: grant applications, awards, and monitoring are not persisted; reassess before adding a migration-owned store. |
|
||||
| `helpdesk` | `govoplan-helpdesk` | Migration-owned | Provider | Provider `privacy.dsar.helpdesk` is registered and documented. |
|
||||
| `identity` | `govoplan-identity` | Migration-owned | Provider | Provider `privacy.dsar.identity` is registered and documented. |
|
||||
| `identity_trust` | `govoplan-identity-trust` | Migration-owned | Provider | Provider `privacy.dsar.identity_trust` is registered and documented. |
|
||||
| `idm` | `govoplan-idm` | Migration-owned | Provider | Provider `privacy.dsar.idm` is registered and documented. |
|
||||
| `inspections` | `govoplan-inspections` | No module migration | Reviewed no-store rationale | Contract-only module: inspections, findings, and measures are not persisted; reassess before adding a migration-owned store. |
|
||||
| `learning` | `govoplan-learning` | No module migration | Reviewed no-store rationale | Contract-only module: learning offers, enrollment, and completion are not persisted; reassess before adding a migration-owned store. |
|
||||
| `mail` | `govoplan-mail` | Migration-owned | Provider | Provider `privacy.dsar.mail` is registered and documented. |
|
||||
| `mandates` | `govoplan-mandates` | Migration-owned | Provider | Provider `privacy.dsar.mandates` is registered and documented. |
|
||||
| `notifications` | `govoplan-notifications` | Migration-owned | Provider | Provider `privacy.dsar.notifications` is registered and documented. |
|
||||
| `ops` | `govoplan-ops` | No module migration | Reviewed no-store rationale | Projection-only module: Ops reads bounded platform and provider status; durable recovery evidence remains owned by Core and domain modules. |
|
||||
| `organizations` | `govoplan-organizations` | Migration-owned | Provider | Provider `privacy.dsar.organizations` is registered and documented. |
|
||||
| `parties` | `govoplan-parties` | Migration-owned | Provider | Provider `privacy.dsar.parties` is registered and documented. |
|
||||
| `payments` | `govoplan-payments` | Migration-owned | Provider | Provider `privacy.dsar.payments` is registered and documented. |
|
||||
| `permits` | `govoplan-permits` | No module migration | Reviewed no-store rationale | Contract-only module: permit applications, assessments, and decisions are not persisted; reassess before adding a migration-owned store. |
|
||||
| `policy` | `govoplan-policy` | Migration-owned | Provider | Provider `privacy.dsar.policy` is registered and documented. |
|
||||
| `poll` | `govoplan-poll` | Migration-owned | Provider | Provider `privacy.dsar.poll` is registered and documented. |
|
||||
| `portal` | `govoplan-portal` | No module migration | Reviewed no-store rationale | Projection-only module: Portal stores no applicant records; Services, Forms Runtime, Cases, and Postbox own and export authoritative subject data. |
|
||||
| `postbox` | `govoplan-postbox` | Migration-owned | Provider | Provider `privacy.dsar.postbox` is registered and documented. |
|
||||
| `procurement` | `govoplan-procurement` | No module migration | Reviewed no-store rationale | Contract-only module: procurement procedures, tenders, and awards are not persisted; reassess before adding a migration-owned store. |
|
||||
| `projects` | `govoplan-projects` | Migration-owned | Provider | Provider `privacy.dsar.projects` is registered and documented. |
|
||||
| `quick_access` | `govoplan-quick-access` | Migration-owned | Provider | Provider `privacy.dsar.quick_access` is registered and documented. |
|
||||
| `records` | `govoplan-records` | Migration-owned | Provider | Provider `privacy.dsar.records` is registered and documented. |
|
||||
| `reporting` | `govoplan-reporting` | Migration-owned | Provider | Provider `privacy.dsar.reporting` is registered and documented. |
|
||||
| `resources` | `govoplan-resources` | No module migration | Reviewed no-store rationale | Contract-only module: resource catalog and allocation persistence are not implemented; reassess before adding a migration-owned store. |
|
||||
| `rest` | `govoplan-rest` | No module migration | Reviewed no-store rationale | Transport-only module: REST binds explicitly published functions and owns no domain or subject-data store. |
|
||||
| `risk_compliance` | `govoplan-risk-compliance` | Migration-owned | Provider | Provider `privacy.dsar.risk_compliance` is registered and documented. |
|
||||
| `scheduling` | `govoplan-scheduling` | Migration-owned | Provider | Provider `privacy.dsar.scheduling` is registered and documented. |
|
||||
| `search` | `govoplan-search` | Migration-owned | Provider | Provider `privacy.dsar.search` is registered and documented. |
|
||||
| `services` | `govoplan-services` | Migration-owned | Provider | Provider `privacy.dsar.services` is registered and documented. |
|
||||
| `soap` | `govoplan-soap` | No module migration | Reviewed no-store rationale | Transport-only module: SOAP binds explicitly published operations and owns no domain or subject-data store. |
|
||||
| `tasks` | `govoplan-tasks` | Migration-owned | Provider | Provider `privacy.dsar.tasks` is registered and documented. |
|
||||
| `templates` | `govoplan-templates` | Migration-owned | Provider | Provider `privacy.dsar.templates` is registered and documented. |
|
||||
| `tenancy` | `govoplan-tenancy` | Migration-owned | Provider | Provider `privacy.dsar.tenancy` is registered and documented. |
|
||||
| `tickets` | `govoplan-tickets` | Migration-owned | Provider | Provider `privacy.dsar.tickets` is registered and documented. |
|
||||
| `transparency` | `govoplan-transparency` | No module migration | Reviewed no-store rationale | Contract-only module: requests, disclosure reviews, and publications are not persisted; reassess before adding a migration-owned store. |
|
||||
| `views` | `govoplan-views` | Migration-owned | Provider | Provider `privacy.dsar.views` is registered and documented. |
|
||||
| `voting` | `govoplan-voting` | Migration-owned | Provider | Provider `privacy.dsar.voting` is registered and documented. |
|
||||
| `wiki` | `govoplan-wiki` | Migration-owned | Provider | Provider `privacy.dsar.wiki` is registered and documented. |
|
||||
| `workflow` | `govoplan-workflow` | No module migration | Reviewed no-store rationale | Presentation-only module: Workflow edits and projects Workflow Engine state; Workflow Engine owns persistence and DSAR coverage. |
|
||||
| `workflow_engine` | `govoplan-workflow-engine` | Migration-owned | Provider | Provider `privacy.dsar.workflow_engine` is registered and documented. |
|
||||
| `xrechnung` | `govoplan-xrechnung` | No module migration | Reviewed no-store rationale | Stateless validation-contract module: XRechnung persists no invoice, report, diagnostic, or handoff; the invoking Files, Procurement, or Payments workflow remains the subject-data owner. Reassess before adding a validation store. |
|
||||
|
||||
Provider search, export minimization, retention, and erasure behavior remains
|
||||
documented and tested by each owning module. This matrix verifies adoption and
|
||||
ownership coverage; Core continues to test disabled providers, partial failure,
|
||||
retry, authorization evidence, and horizontally coordinated execution.
|
||||
@@ -0,0 +1,376 @@
|
||||
# GovOPlaN Interface Surface Inventory And Rollout
|
||||
|
||||
> **Pinned snapshot:** This inventory records the source-derived state reviewed
|
||||
> on 2026-08-03. It is retained as evidence, not maintained as the current
|
||||
> rollout ledger. Generate a new inventory and use Gitea issues for current
|
||||
> implementation state.
|
||||
|
||||
This is the initial evidence inventory for the product-wide interface pattern
|
||||
language. It records code contributions, not an assertion that every listed
|
||||
surface is complete, enabled in a deployment, usable, or compliant.
|
||||
|
||||
The applicable design contract is
|
||||
[`INTERFACE_PATTERN_LANGUAGE.md`](../../architecture/INTERFACE_PATTERN_LANGUAGE.md).
|
||||
|
||||
## Snapshot And Method
|
||||
|
||||
The source-derived inventory command is documented in
|
||||
[`PLATFORM_CONTROL_PLANE.md`](../../architecture/PLATFORM_CONTROL_PLANE.md). It produces
|
||||
machine-readable field, label, translation, route, API-reference, and module
|
||||
manifest evidence. This hand-maintained document is the reviewed interpretation
|
||||
of that snapshot; generated evidence does not retroactively change it.
|
||||
|
||||
Snapshot refreshed: 2026-08-03.
|
||||
|
||||
The generated snapshot contains 65 module manifests, 35 WebUI-contributing
|
||||
repositories, 40 statically declared module routes, 1,156 UI fields, and 836
|
||||
backend endpoints. All backend endpoints are classified and no stale endpoint
|
||||
declarations were found. The 234 endpoints without a static WebUI reference are
|
||||
kept visible as review evidence; they may intentionally serve workers, public
|
||||
clients, connectors, or external integrations.
|
||||
|
||||
Evidence was read from tracked Git `HEAD` in the local GovOPlaN checkouts:
|
||||
|
||||
- core routes and fallback behavior in `govoplan-core/webui/src/App.tsx`
|
||||
- every present `govoplan-*/webui/src/module.ts`
|
||||
- the matching backend `src/*/backend/manifest.py`
|
||||
- named UI capabilities and contribution identifiers in each `module.ts`
|
||||
- Campaign nested routes in its tracked `CampaignWorkspace.tsx` and
|
||||
`SectionSidebar.tsx`
|
||||
|
||||
Tracked commits are used as the integrated baseline. Dirty worktree changes
|
||||
are not treated as delivered behavior. The former Campaign recipient-editor
|
||||
and preview WIP is integrated in tracked commits; completed work is no longer
|
||||
described as a local exception.
|
||||
|
||||
Runtime visibility remains conditional on the module being packaged and
|
||||
enabled, server metadata, installed optional capabilities, the authenticated
|
||||
actor, tenant context, route permission guards, and inner-surface permission
|
||||
checks. A route in this inventory therefore means "the code contributes this
|
||||
route when its module is active", not "every user sees it".
|
||||
|
||||
Inventory states:
|
||||
|
||||
- **Contributed**: a route or named UI capability exists in tracked source.
|
||||
- **Metadata gap**: frontend runtime source contributes a route but the backend
|
||||
manifest does not describe the same route/navigation surface.
|
||||
- **Unreviewed**: the surface has not yet completed the pattern, state,
|
||||
accessibility, privacy, and consequence audit. This is the default unless an
|
||||
issue supplies verification evidence.
|
||||
- **Pilot**: the surface is in the Campaign-first rollout.
|
||||
|
||||
## Core Shell Surfaces
|
||||
|
||||
| Surface | Owner and code evidence | Audience/access evidence | Primary task and target archetype | Audit / rollout |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Public landing and login | `govoplan-core` `PublicLandingPage`; rendered while no authenticated principal exists | Unauthenticated; maintenance and backend-reachability context are shell inputs | Understand the service and authenticate; public entry | Core shell contract complete under Core #227: semantic entry/login, uniform reachable/offline/maintenance feedback, keyboard focus, responsive layout and privacy-safe pre-authentication state |
|
||||
| Session/bootstrap state | `govoplan-core` `App.tsx` and `AppShell` | All browser sessions during bootstrap | Understand that session/platform state is loading; state contract | Core shell contract complete: loading, unreachable, maintenance, authentication-required and module-load failure states use shared status/alert boundaries without erasing the shell |
|
||||
| `/` authenticated redirect | `govoplan-core` chooses the first visible navigation destination | Authenticated; result depends on visible nav contributions | Enter the actor's first accessible service area; navigation behavior, not a content page | Core route/module-permutation contract complete; permission, module, View and fallback filtering precede navigation and do not execute a domain action |
|
||||
| `/dashboard` fallback | `govoplan-core` `DashboardPage` only when the Dashboard module is absent | Authenticated; no route-specific scope in core | Cross-module starting point; dashboard | Core fallback and Dashboard module permutations complete; fallback remains usable without the optional Dashboard module |
|
||||
| `/settings` | `govoplan-core` `SettingsPage` | Authenticated; contributed sections and integrations filter internally | Profile, UI/workspace preference, local connection, and user-scoped integration settings; configuration | Core-owned pattern migration complete in [Core #225](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/225), commit `fa32cca` |
|
||||
| Shell chrome | `AppShell`, `Titlebar`, `IconRail`, `BreadcrumbBar`, `HelpMenu`, language menu, unsaved-change provider | Public/authenticated variants; nav filtered later | Tenant/actor context, global navigation, help, language, session and maintenance state | Core shell contract complete under Core #227/#225 and Views #2: semantic global controls, scroll-safe rail, visible maintenance state, guarded navigation, configured Docs fallback, optional Search, responsive/theme/i18n checks and module permutations |
|
||||
|
||||
## Direct Module Route Contributions
|
||||
|
||||
The access column summarizes only the route-level declaration in `module.ts`.
|
||||
Inner APIs and controls may impose additional checks. Public and compatibility
|
||||
routes are called out explicitly because they do not have the same manifest
|
||||
semantics as authenticated navigation routes.
|
||||
|
||||
| Routes | Owner | Route-level access | Primary archetype | Migration issue |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `/admin` | Access | Any declared administration/read scope | Administration/configuration host | Access pattern migration complete in [Access #19](https://git.add-ideas.de/GovOPlaN/govoplan-access/issues/19), commit `1409dbf`; shared host contract complete in [Core #225](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/225) |
|
||||
| `/address-book` | Addresses | `addresses:contact:read` | Governed source directory, contact/list detail, external-provider operation, governance facts, and reversible correction | Addresses pattern migration complete in [Addresses #23](https://git.add-ideas.de/GovOPlaN/govoplan-addresses/issues/23), commit `f9a7185` |
|
||||
| `/approvals` | Approvals | `approvals:workspace:read` | Work queue/guided decision | Approvals pattern migration complete in [Approvals #3](https://git.add-ideas.de/GovOPlaN/govoplan-approvals/issues/3), commit `24e9559` |
|
||||
| `/calendar` | Calendar | `calendar:event:read` | Full-height calendar workspace with filterable collection/agenda sidebar, continuous and bounded date views, guarded VEVENT and source editors, synchronized-source status, durable outbox recovery, and destructive remote-move evidence | Calendar pattern migration complete in [Calendar #22](https://git.add-ideas.de/GovOPlaN/govoplan-calendar/issues/22), commit `d7fd944` |
|
||||
| `/campaigns`, `/campaigns/:campaignId/*`, `/campaigns/queue`, `/campaigns/reports` | Campaign | Campaign read/report/control scopes | List-detail, guided review, monitoring, reporting | Campaign pattern pilot complete in [Campaign #74](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/74); bounded product features such as watched-folder policy remain independently tracked |
|
||||
| `/operator` | Campaign | Campaign read plus queue/control scope | Compatibility redirect to `/campaigns/queue` | Campaign #74 complete; redirect remains declared for saved links and is retired under the compatibility policy rather than through the UI migration |
|
||||
| `/cases`, `/cases/:caseId` | Cases | `cases:case:read` | Governed case directory and detail workspace with guarded OCC lifecycle editor, provider-owned references, immutable timeline/history, and confirmed object-access editor | Cases pattern migration complete in [Cases #4](https://git.add-ideas.de/GovOPlaN/govoplan-cases/issues/4), commit `43b4cc8` |
|
||||
| `/committee` | Committee | `committee:workspace:read` | Governed workspace | Committee pattern migration complete in [Committee #2](https://git.add-ideas.de/GovOPlaN/govoplan-committee/issues/2), commit `e64af30` |
|
||||
| `/dashboard` | Dashboard | No route-specific scope | View-specific personal workspace with module/permission-filtered widget library, guarded four-column composition, nested widget settings, server/browser fallback, and optimistic layout persistence | Dashboard pattern migration complete in [Dashboard #3](https://git.add-ideas.de/GovOPlaN/govoplan-dashboard/issues/3), commit `da3947f` |
|
||||
| `/dataflow` | Dataflow | Pipeline read/admin | Governed library, guarded graph/constrained-SQL definition editor, typed node inspector, bounded intermediate preview, automation triggers, and durable run/deployment evidence | Dataflow pattern migration complete in [Dataflow #20](https://git.add-ideas.de/GovOPlaN/govoplan-dataflow/issues/20), commit `109ddcd` |
|
||||
| `/datasources` | Datasources | Catalogue read/source admin | Governed catalogue, staging preflight, optional-origin directory, authority editor, and immutable evidence | Datasources pattern migration complete in [Datasources #7](https://git.add-ideas.de/GovOPlaN/govoplan-datasources/issues/7), commit `6406ce7` |
|
||||
| `/distribution-lists` | Distribution Lists | List read/write/admin | Governed directory, immutable-revision editor, expansion preview, and evidence register | Distribution Lists pattern migration complete in [Distribution Lists #8](https://git.add-ideas.de/GovOPlaN/govoplan-dist-lists/issues/8), commit `6cdd804` |
|
||||
| `/docs` | Docs | Documentation or settings read | Documentation/reference | Configured-system workflow/reference/pattern help complete in [Docs #15](https://git.add-ideas.de/GovOPlaN/govoplan-docs/issues/15), commit `abe2f78` |
|
||||
| `/files` | Files | `files:file:read` | Directory/explorer | Files pattern migration complete in [Files #42](https://git.add-ideas.de/GovOPlaN/govoplan-files/issues/42), commit `d8ae506` |
|
||||
| `/forms` | Forms | `forms:definition:read` | Definition library/editor | Forms pattern migration complete in [Forms #4](https://git.add-ideas.de/GovOPlaN/govoplan-forms/issues/4), commit `e505536` |
|
||||
| `/forms-runtime`, `/forms-runtime/:instanceId` | Forms Runtime | Participate or workspace read | Guided form execution | Forms Runtime pattern migration complete in [Forms Runtime #5](https://git.add-ideas.de/GovOPlaN/govoplan-forms-runtime/issues/5), commit `07dd35b` |
|
||||
| `/idm` | IDM | Assignment, function-change, relationship, or organization scopes | Directory/governed change | IDM pattern migration complete in [IDM #12](https://git.add-ideas.de/GovOPlaN/govoplan-idm/issues/12), commit `d864317` |
|
||||
| `/mail`, `/mail/bounces` | Mail | Mailbox or bounce read/manage | Directory/explorer, operational evidence | Mail pattern migration complete in [Mail #20](https://git.add-ideas.de/GovOPlaN/govoplan-mail/issues/20), commit `7844d9c` |
|
||||
| `/notifications` | Notifications | `notifications:notification:read` | Inbox/list-detail with guarded recipient state, confirmed local cancellation/dispatch, and sanitized delivery evidence | Notifications pattern migration complete in [Notifications #4](https://git.add-ideas.de/GovOPlaN/govoplan-notifications/issues/4), commit `ad6a31f` |
|
||||
| `/ops` | Ops | Operations or settings read | Monitoring/evidence with contextual run, drain, readiness-blocker, and recovery guidance | Ops pattern migration complete in [Ops #4](https://git.add-ideas.de/GovOPlaN/govoplan-ops/issues/4), commit `2b32643` |
|
||||
| `/organizations` | Organizations | Model/unit/function or settings read | Directory/hierarchy editor | Organizations pattern migration complete in [Organizations #7](https://git.add-ideas.de/GovOPlaN/govoplan-organizations/issues/7), commit `97acfcb` |
|
||||
| `/portal` | Portal | `portal:service:read` | Explained service directory and governed handoff | Portal pattern migration complete in [Portal #2](https://git.add-ideas.de/GovOPlaN/govoplan-portal/issues/2); durable evidence in `govoplan-portal/docs/INTERFACE_PATTERN_MIGRATION.md` |
|
||||
| `/postbox` | Postbox | `postbox:postbox:read` | Inbox/list-detail | Postbox pattern migration complete in [Postbox #26](https://git.add-ideas.de/GovOPlaN/govoplan-postbox/issues/26), commit `a97eb3b` |
|
||||
| `/projects` | Projects | `projects:project:read` | Revisioned list-detail/project workspace | Projects pattern migration complete in [Projects #2](https://git.add-ideas.de/GovOPlaN/govoplan-projects/issues/2); durable evidence in `govoplan-projects/docs/INTERFACE_PATTERN_MIGRATION.md` |
|
||||
| `/reporting`, `/reports` | Reporting | `reporting:definition:read` | Governed report catalogue, analytical workspace and evidence | Reporting pattern migration complete in [Reporting #8](https://git.add-ideas.de/GovOPlaN/govoplan-reporting/issues/8); durable evidence in `govoplan-reporting/docs/INTERFACE_PATTERN_MIGRATION.md` |
|
||||
| `/risk-compliance` | Risk Compliance | Workspace or sanctions read | Immutable source evidence, version-pinned screening, list-detail review, and revisioned assurance graph with explicit blockers and consequences | Risk Compliance pattern migration complete in [Risk Compliance #8](https://git.add-ideas.de/GovOPlaN/govoplan-risk-compliance/issues/8), commit `24d80a6` |
|
||||
| `/scheduling` | Scheduling | `scheduling:schedule:read` | List-detail/guided decision | Scheduling pattern migration complete in [Scheduling #8](https://git.add-ideas.de/GovOPlaN/govoplan-scheduling/issues/8), commit `c17cbda` |
|
||||
| `/scheduling/public/:requestId/:token` | Scheduling | Public signed token | Public participation | Scheduling #8 complete in `c17cbda` |
|
||||
| `/search` | Search | `search:result:read` | Keyboard-first global/context overlay and full results fallback | Search pattern migration complete in [Search #4](https://git.add-ideas.de/GovOPlaN/govoplan-search/issues/4); durable evidence in `govoplan-search/docs/INTERFACE_PATTERN_MIGRATION.md` |
|
||||
| `/templates` | Templates | Template read/write/publish/render/admin | Governed library, immutable-revision editor, compatibility preview, and render evidence | Templates pattern migration complete in [Templates #5](https://git.add-ideas.de/GovOPlaN/govoplan-templates/issues/5), commit `72fafa2` |
|
||||
| `/tickets` | Tickets | `tickets:ticket:read` | Governed operational queue/detail workspace with distinct report, triage, assignment, resolution, comment, reference and removal boundaries | Tickets vertical slice and pattern migration complete in [Tickets #1](https://git.add-ideas.de/GovOPlaN/govoplan-tickets/issues/1), release `v0.1.20` |
|
||||
| `/voting` | Voting | `voting:ballot:read` | Governed ballot workspace | Voting pattern migration complete in [Voting #1](https://git.add-ideas.de/GovOPlaN/govoplan-voting/issues/1), commit `2625990` |
|
||||
| `/wiki` | Wiki | `wiki:page:read` | Governed space-tree/page workspace with draft editing, immutable revision comparison, publication, comments, typed references and archival | Native Wiki vertical slice and pattern migration implemented in [Wiki #1](https://git.add-ideas.de/GovOPlaN/govoplan-wiki/issues/1), release `v0.1.20` |
|
||||
| `/workflow` | Workflow | Definition read or instance admin | Native BPMN editor, governed revision actions and execution evidence | Workflow pattern migration complete in [Workflow #15](https://git.add-ideas.de/GovOPlaN/govoplan-workflow/issues/15); durable evidence in `govoplan-workflow/docs/INTERFACE_PATTERN_MIGRATION.md` |
|
||||
|
||||
## Final Module Closure Evidence
|
||||
|
||||
The final five module-owned work packages complete the 2026-08-03 rollout
|
||||
snapshot. Their module documents are the durable detailed inventories; the
|
||||
table below records the cross-product closure evidence.
|
||||
|
||||
| Owner | Dominant archetype and consequential boundary | Focused evidence |
|
||||
| --- | --- | --- |
|
||||
| Workflow | Definition list-detail plus specialized native BPMN editor; save/activate/archive/delete/reset and instance transitions remain revisioned, confirmed, and Engine-owned | Shared dialogs/status/alerts/help, dirty-navigation guard, keyboard palette insertion, edge inspector alternative, responsive/reduced-motion contract, TypeScript and focused structure test |
|
||||
| Search | Focus-contained global/context overlay plus URL-stable full results; filters only narrow permission-aware source results | F3/Ctrl/Cmd+K, listbox keyboard navigation, provider-partial diagnostics, shared controls/help, narrow layout and focused overlay/interface tests |
|
||||
| Reporting | Three-region governed analytical workspace; runs, schedules, exports and publications retain purpose, permission, source and policy provenance | Shared grid/dialog/status/help, keyboard-explainable Run blockers, responsive task order, provider/semantic backend tests and focused interface test |
|
||||
| Projects | Revisioned list-detail planning workspace; visibility and saves are ACL/OCC-governed and retain a change reason | Shared dialog/status/help/field labels, save errors attached to the editor, semantic list controls, responsive/focus contract and focused interface test |
|
||||
| Portal | Explained service directory and exact-revision provider handoff; Portal never owns the launched case/form/workflow effect | Shared status/alert/toggle/help/blocker controls, stable disabled Open action with actor/action/destination, guarded navigation, responsive layout and focused interface test |
|
||||
|
||||
Future WebUI modules and newly added routes are not grandfathered by this
|
||||
snapshot. They must meet the same surface definition of done in their owning
|
||||
feature issue and pass the source/runtime inventory gates; they do not reopen
|
||||
this finite migration program unless the pattern contract itself changes.
|
||||
|
||||
## Manifest And Runtime Route Alignment
|
||||
|
||||
Backend route metadata lets operators, Docs, release tooling, and remote bundle
|
||||
loading reason about the configured interface without executing module UI code.
|
||||
`module.ts` remains the executable local route/render source. Missing metadata
|
||||
is recorded here as an evidence gap; this inventory does not infer whether each
|
||||
gap is intentional.
|
||||
|
||||
The generated comparison is aligned for all authenticated canonical routes.
|
||||
Two deliberate exceptions remain visible:
|
||||
|
||||
- Campaign contributes `/operator` as a compatibility redirect for saved View
|
||||
projections; its canonical and manifest-declared destination is
|
||||
`/campaigns/queue`.
|
||||
- Scheduling contributes `/scheduling/public/:requestId/:token` through the
|
||||
separate `publicRoutes` contract. Authenticated manifest routes intentionally
|
||||
do not describe public signed-token entry points yet.
|
||||
|
||||
Admin, Audit, Policy, Tenancy, and Views contribute composed administration or
|
||||
settings surfaces rather than direct routes. Their migration issues are
|
||||
[Admin #8](https://git.add-ideas.de/GovOPlaN/govoplan-admin/issues/8),
|
||||
[Audit #8](https://git.add-ideas.de/GovOPlaN/govoplan-audit/issues/8),
|
||||
[Policy #11](https://git.add-ideas.de/GovOPlaN/govoplan-policy/issues/11),
|
||||
[Tenancy #6](https://git.add-ideas.de/GovOPlaN/govoplan-tenancy/issues/6), and
|
||||
[Views #2](https://git.add-ideas.de/GovOPlaN/govoplan-views/issues/2).
|
||||
|
||||
Release evidence must continue to run the generated inventory and manifest
|
||||
shape checks so new executable routes, public routes, aliases, and composed
|
||||
surfaces cannot silently diverge from their declared metadata.
|
||||
|
||||
## Composed Surfaces And Extension Points
|
||||
|
||||
These surfaces are active only when the host and contributing modules are
|
||||
enabled and the actor passes the declared filters.
|
||||
|
||||
| Host surface | Contributor and evidence | Contributed regions/actions | Pattern implication | Audit |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `/admin` | Access host (`AdminPage`) | System tenants/users/roles, tenant users/groups/roles/API keys/settings, function-role mappings, user/group mail and file connector scopes | One stable admin information architecture must contain both host-owned and contributed sections | Pattern migration, contextual help, explained permission/protection states, optional-module blockers, localization, and focused evidence complete in Access #19 (`1409dbf`); Core #225 shared host contract complete |
|
||||
| `/admin` | `govoplan-admin` `admin.sections` | Overview; system settings; configuration changes; configuration packages; role/group templates; module management | Configuration, guided operations, review/preflight, consequence | Pattern migration, contextual help, explained permission/protection/applicability states, guarded consequential actions, localization, and focused evidence complete in Admin #8 (`d428f33`) |
|
||||
| `/admin` | `govoplan-tenancy` `admin.sections` | System tenant registry and active-tenant settings | Administration directory, effective configuration, lifecycle consequence | Pattern migration, contextual help, explained permission/lifecycle/system-policy states, dirty-state guards, localization, and focused evidence complete in Tenancy #6 (`e76fe16`) |
|
||||
| `/admin` | `govoplan-audit` `admin.sections` | System audit; tenant audit | Evidence/provenance and reporting | Pattern migration, localized evidence projection, contextual help, and focused tests complete in Audit #8 (`6d3fcc1`) |
|
||||
| `/admin` | `govoplan-files` `admin.sections` and `files.connectors` | System and tenant file connections plus scoped connector managers used by Access | Adaptive configuration, discovery/test, policy and credentials | Pattern migration, contextual help, blocker explanations and focused evidence complete in Files #42 (`d8ae506`) |
|
||||
| `/admin` | `govoplan-organizations` `admin.sections` | Tenant organization settings | Configuration/list-detail | Pattern migration, tenant-owned provenance, contextual help, guarded settings/editor drafts, explained permission states, localization and focused evidence complete in Organizations #7 (`97acfcb`) |
|
||||
| `/admin` | `govoplan-policy` `admin.sections` | System, tenant, group, and user retention | Effective value, source/provenance, consequential configuration | Pattern migration complete in Policy #11 (`f964ed7`) with Core editor contract `fa32cca` |
|
||||
| `/admin` and `/settings` | `govoplan-mail` `mail.profiles` | System/tenant/group/user mail profile and policy managers | Same server/credential/policy grammar as file connectors | Pattern migration, contextual help, policy/target/permission blockers and focused evidence complete in Mail #20 (`7844d9c`; shared test-reason contract Core `2d0551a`) |
|
||||
| `/settings` | Core host | Profile; interface; workspace; local connection | Personal configuration with adaptive forms and immediate feedback | Pattern migration complete in Core #225 (`fa32cca`) |
|
||||
| `/settings` | Files and Mail named capabilities | User-scoped file connections and mail profiles/policy | Optional integration regions disappear cleanly when capability absent | Files #42, Mail #20 and Core #225 complete |
|
||||
| `/admin` and `/settings` | `govoplan-views` `admin.sections`, `settings.sections`, and `views.runtime` | System/tenant definition and assignment editors, personal/group editors, global selector | Versioned presentation projection with inheritance, lockout safeguards, optional directory targets, and no authorization effect | Pattern migration, contextual help, localized selector/editor, guarded drafts, explained inherited/permission/capability states, and focused evidence complete in Views #2 (`c125f33`) |
|
||||
| `/settings` | `govoplan-notifications` `settings.sections` | Notification preferences | Personal configuration | Pattern migration, contextual help, permission/target explanation, typed toggles and focused evidence complete in Notifications #4 (`ad6a31f`) |
|
||||
| `/dashboard` | Dashboard host and `dashboard.widgets` | Installed-modules widget; Ops health widget when Ops contributes it | Widget ordering, staleness, permissions, destination behavior | Pattern migration, view-aware composition, keyboard/drag alternatives, responsive packing, module filtering and focused evidence complete in Dashboard #3 (`da3947f`) |
|
||||
| `/organizations` | IDM `organizations.functionActions` | Action leading to assignment view filtered by IDM scopes | Cross-module context action through explicit capability | IDM pattern migration complete in IDM #12 (`d864317`) |
|
||||
| Campaign attachments/import | Files `files.fileExplorer` | Folder tree, managed chooser, file listing/pattern resolution/sharing | Optional domain composition without sibling-private imports | Campaign #74 pilot complete; watched-folder and duplicate-attachment product policy remain independent Campaign #60/#61 features |
|
||||
| Campaign review/send | Mail runtime `mail.devMailbox` | Mock-mail verification when backend advertises runtime capability | Optional review stage with unavailable/optional states | Explicit intervention and review-progress vocabulary delivered in Campaign #63; send modes/progress delivered in #62/#79 |
|
||||
|
||||
Other named capability exports (`files.connectors`, `organizations.functionPicker`,
|
||||
and mail profile validation) are contracts consumed inside the composed surfaces
|
||||
above; they are not independent routes.
|
||||
|
||||
## Core Configuration Surface Map
|
||||
|
||||
Core #225 now supplies and verifies the platform-owned configuration contract.
|
||||
The durable Core inventory is
|
||||
`govoplan-core/docs/INTERFACE_PATTERN_MIGRATION.md`.
|
||||
|
||||
| Surface / code evidence | Primary task | Target pattern | Material consequence/state | Completion evidence |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `/settings` (`SettingsPage`) | Change personal profile, interface/workspace preferences, or local development connection | Two-zone typed settings workspace | Changes are user-scoped; save and test actions distinguish clean, busy, and active states | Contextual help, unsaved guard, typed controls and keyboard-explainable disabled actions in Core `fa32cca` |
|
||||
| Reusable credentials (`CredentialEnvelopeManager`) | Compare and configure scoped reusable authentication material | Repeated administration plus adaptive create/edit | Secret values are write-only; permission and missing-owner states block mutation explicitly; deletion can break dependent connections | Actionable blocker, stable row actions, typed references, unsaved guard and shared destructive confirmation |
|
||||
| Retention (`RetentionPolicyManagement`) | Inspect effective retention and narrow permitted local values | Effective-policy editor | Parent locks, source paths and write authority control whether sensitive evidence can be retained | Typed narrowing controls, source-path help, lock/target/permission blockers and clean/loading/save reasons |
|
||||
| Shared configuration primitives | Compose module-owned settings without sibling-private imports | Platform behavior contract | Consequence, focus, help, async, confirmation and permission semantics remain consistent | Core component suites, 121 module-system tests and full-product type/build/bundle gates |
|
||||
|
||||
No primary Core configuration flow requires raw JSON. Expert JSON remains
|
||||
limited to diagnostics, interchange, conflict evidence, or read-only inspection.
|
||||
|
||||
## Policy Surface Map
|
||||
|
||||
Policy #11 verifies the four composed retention sections. The durable
|
||||
module-level inventory is
|
||||
`govoplan-policy/docs/INTERFACE_PATTERN_MIGRATION.md`.
|
||||
|
||||
| Surface / code evidence | Primary task | Target pattern | Material consequence/state | Completion evidence |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| System retention | Set the instance ceiling and run retention | Effective-policy editor plus destructive operation | An applied run can irreversibly redact/delete retained content; dry-run and applied evidence remain distinct | Core source-path/lock contract, permission and busy reasons, shared confirmation, typed/filterable outcome grid and audit-oriented wording |
|
||||
| Tenant retention | Narrow the inherited system ceiling | Effective-policy editor | Tenant policy cannot silently loosen its parent | Core typed controls, effective path and parent-lock explanation |
|
||||
| Group and user retention | Select an authorized target and narrow inherited policy | Targeted effective-policy editor | Selection exposes only bounded account/group labels; no retained content is returned | Delta-backed target loading, retry, missing-target blocker and responsive shared admin composition |
|
||||
|
||||
Automated evidence for Policy `f964ed7` comprises 50 backend/manifest tests,
|
||||
the Policy interface structural gate, 65 manifest-shape checks, and the
|
||||
full-product TypeScript/Vite build with structural localization, theme and
|
||||
bundle-budget gates. Policy uses no sibling-private imports.
|
||||
|
||||
## Files Surface Map
|
||||
|
||||
Files #42 classifies and verifies the complete Files-owned route and composition
|
||||
boundary. The durable module-level inventory is
|
||||
`govoplan-files/docs/INTERFACE_PATTERN_MIGRATION.md`.
|
||||
|
||||
| Surface / code evidence | Primary task | Target pattern | Material consequence/state | Completion evidence |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `/files` (`FilesPage`) | Browse spaces/folders and repeatedly act on current content | Full-height directory/explorer | Navigation is low consequence; upload, synchronize, move, copy and share are medium; delete is high | Stable two-pane composition, contextual help, selection/permission/state-specific disabled reasons, shared confirmation and responsive collapse |
|
||||
| Upload/archive, transfer, rename and connector-import dialogs | Supply, validate and review one bounded change | Adaptive create/edit or guided import | Writes managed content and may resolve conflicts or import untrusted bytes | Shared dialogs/drop zone, bounded archive preflight, conflict review, explicit confirmation and no browser-native confirmation |
|
||||
| Share/access explanation | Inspect or change who can use a resource | Review/decision | Grants can disclose content; delete/revoke changes access | Shared access explanation, action components and destructive confirmation; backend redaction remains authoritative |
|
||||
| File connector tree and connection/credential dialogs | Compare and configure external endpoints and reusable credentials | Administration plus adaptive create/edit | Endpoint, secret and capability changes can enable remote access | Shared connection tree/forms/advanced panel, endpoint discovery and login test, unsaved-change guard, read-only deployment provenance and actionable disabled reasons |
|
||||
| Connector policy card | Narrow effective connector use | Effective-policy editor | Inherited deny/allow rules affect lower scopes | Typed selectors, deny-precedence warning, effective sources, contextual admin help and permission blocker |
|
||||
| `files.widget.spaces` | See available spaces and enter Files | Dashboard widget | Space/provider names remain permission-filtered | Shared loading, alert and status components; bounded configuration and refresh |
|
||||
| `files.fileExplorer` capability | Select a governed managed snapshot for another module | Directory chooser | Exact file/version becomes another module's governed input | Capability-only composition, no sibling-private import, stable chooser/confirmation and exact snapshot evidence |
|
||||
|
||||
Automated evidence for commit `d8ae506` comprises 104 Files backend tests,
|
||||
three focused Files WebUI structure tests, the full-product TypeScript/Vite
|
||||
build, structural localization audit, theme contract and bundle budget. Shared
|
||||
Dialog and disabled-tooltip behavior provide focus entry/return and
|
||||
keyboard-reachable explanations; responsive source order is guarded at 1050 px
|
||||
and 760 px. Secrets are not returned to the WebUI, and JSON remains only an
|
||||
advanced provider-compatibility escape hatch rather than the primary editor.
|
||||
|
||||
## Mail Surface Map
|
||||
|
||||
Mail #20 classifies and verifies the complete Mail-owned route and composition
|
||||
boundary. The durable module-level inventory is
|
||||
`govoplan-mail/docs/INTERFACE_PATTERN_MIGRATION.md`.
|
||||
|
||||
| Surface / code evidence | Primary task | Target pattern | Material consequence/state | Completion evidence |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `/mail` (`MailboxPage`) | Browse an authorized provider mailbox without changing it | Full-height directory/explorer | Message metadata and content are private; every provider read is bounded and non-mutating | Stable three-pane composition, contextual help, explicit no-profile blocker, refresh reasons, keyboard rows, paging and responsive collapse |
|
||||
| Mail profile tree and profile/server/credential dialogs | Compare and configure reusable transport identities | Administration plus guided/adaptive create/edit | Endpoint and credential changes can enable external effects | Shared connection tree/dialog/stage rail/forms, focused hierarchy editors, unsaved guard, connection tests, permission/target blockers and disabled-save reasons |
|
||||
| Mail policy card | Narrow profile visibility, lower-scope definitions and transport/address patterns | Effective-policy editor | Inherited allow/deny rules affect delivery and lower scopes | Typed selectors and controls, effective source path, lock/read-only blocker, dirty-save state and contextual admin help |
|
||||
| `/mail/bounces` watcher table | Configure and explicitly scan bounded IMAP evidence sources | Operational administration | Provider access changes durable source cursors and evidence | Shared grid/status/loading/alerts, actionable no-profile and busy states, field help and stable row actions |
|
||||
| `/mail/bounces` observations and watcher removal | Review sanitized delivery outcomes or stop future scans | Evidence/reporting plus destructive confirmation | Recipient diagnostics are sensitive; watcher removal retains existing evidence | Bounded sanitized rows and shared confirmation with retained-evidence consequence |
|
||||
| `mail.profiles` and reference-selector capabilities | Select/validate Mail-owned transport from another module | Governed capability composition | A selected identity can perform external effects | Stable references, Mail-owned authorization/secret resolution, no sibling-private imports and clean optional absence |
|
||||
|
||||
Automated evidence for Mail commit `7844d9c` and Core commit `2d0551a`
|
||||
comprises 114 Mail backend tests, Mail's focused UI/model/structure suite, the
|
||||
Core shared mail-component suite, 65 manifest-shape checks and the full-product
|
||||
TypeScript/Vite build with structural localization, theme and bundle-budget
|
||||
gates. Shared Dialog and disabled-tooltip behavior provides focus containment,
|
||||
return and keyboard-reachable explanations. Responsive source order is guarded
|
||||
at 1250 px, 900 px and 760 px. Passwords remain write-only, mailbox responses
|
||||
are bounded, and bounce evidence excludes raw provider messages.
|
||||
|
||||
## Campaign Pilot Surface Map
|
||||
|
||||
Campaign is detailed first because it exercises almost every archetype. The
|
||||
recipient-data editor is now consolidated into the `recipients` section on
|
||||
remote `main`; [Campaign #67](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/67)
|
||||
records the accepted and verified integration boundary.
|
||||
|
||||
Campaign already consumes core primitives including `ModuleSubnav`, `Card`,
|
||||
`PageTitle`, `Button`, `LoadingFrame`, `DismissibleAlert`, `FormField`,
|
||||
`StatusBadge`, `MetricCard`, `DataGrid`, `TableActionGroup`, `Dialog`,
|
||||
`ConfirmDialog`, `FileDropZone`, `MessageDisplayPanel`, policy components,
|
||||
access/module capabilities, and unsaved-navigation guards. Reuse alone does not
|
||||
prove that the composition or states satisfy the pattern.
|
||||
|
||||
| Surface / code evidence | Primary task | Target pattern | Material consequence/state | Known issue / rollout |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Campaign list (`CampaignListPage`) | Find, compare, create, open | List-detail entry | Campaign lifecycle/status and creation | #74 and guided entry [#35](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/35) complete |
|
||||
| Overview (`CampaignOverviewPage`) | Understand/edit campaign identity, version, access, lifecycle | Object overview plus adaptive edit | Lock/archive/delete/access changes expose consequence, reversibility, owner/access and lifecycle evidence | #74 complete; lifecycle policy is independently extended in Campaign #26 |
|
||||
| Fields (`CampaignFieldsPage`) | Define recipient/template field schema | Structured editor | Schema changes can invalidate recipient/template data | #74 complete |
|
||||
| Attachments/files (`AttachmentsDataPage`, `AttachmentRulesOverlay`) | Select sources and attachment/ZIP rules | Directory chooser plus adaptive rule editor | Missing or mismatched files affect built messages | #74 and attachment-detail [#59](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/59) complete |
|
||||
| Recipients (`RecipientDataPage`) | Select/import/map/edit recipients, address fields and per-recipient values/files | Import/mapping plus list-detail editor | Personal data, validation, bulk activation, file links | Consolidated editor #67, guided entry #35 and #74 audit complete; independent bulk action #68 remains product scope |
|
||||
| Template (`TemplateDataPage`, placeholder/expression dialogs) | Author subject/body and preview substitutions | Adaptive editor plus stable preview | Generated communication content and unresolved expressions | #74 and stable overlay [#73](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/73) complete |
|
||||
| Mail settings (`MailSettingsPage` settings view) | Select/configure campaign mail transport | Adaptive configuration | Credentials, SMTP/IMAP destinations, test outcomes | #74 and Core #225 shared mail pattern complete; final credential hierarchy remains Mail #10 |
|
||||
| Campaign settings (`GlobalSettingsPage` settings view) | Configure campaign behavior | Adaptive configuration | Can alter validation/build/send behavior | #74 complete |
|
||||
| Mail policy (`MailSettingsPage` policy view) | Inspect/override effective mail policy | Effective policy/provenance editor | Inheritance and locks affect allowed delivery | #74 and Core #225 effective-policy pattern complete |
|
||||
| Campaign policy (`GlobalSettingsPage` policy view) | Inspect/override campaign policy | Effective policy/provenance editor | Inheritance, actor authority, and blocked edits | #74 and Core #225 effective-policy pattern complete |
|
||||
| Review/send (`ReviewSendPage`) | Validate, build, mock-test, confirm/send, inspect results | Guided review/decision plus durable progress | External communication, bounded synchronous execution, persisted queue mode, partial effects, retries, evidence | Interventions #63, send/progress #62/#79 and #74 wording/accessibility audit complete |
|
||||
| Message and attachment detail overlays | Inspect one built/mock message and its attachment links | Stable detail/review dialog | Personal data, exact outbound content, reviewed state | Delivered and verified in [#59](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/59) and [#73](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/73) |
|
||||
| Campaign report (`CampaignReportPage`) | Filter and inspect delivery outcomes | Reporting/list-detail | Partial, failed, explicitly excluded/skipped, SMTP/IMAP outcomes and retries | Server-owned filtering and counts delivered in [#65](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/65) with the full-result DataGrid contract from [Core #263](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/263); excluded semantics in [#66](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/66) |
|
||||
| Audit (`CampaignAuditPage`) | Reach campaign evidence/history | Explained provenance handoff | Campaign emits platform evidence; Audit owns reading, retention and bundles | #74 complete as an explicit Audit handoff; object-scoped projection may follow Audit #3 without a sibling-private import |
|
||||
| JSON (`CampaignJsonView`) | Inspect/download expert representation | Advanced diagnostics/reference | Full authorized configuration may contain personal data but no inline transport secrets | #74 privacy audit complete with explicit sensitivity warning and campaign-read boundary |
|
||||
| Create wizard (`CreateWizard`) | Seed a campaign through basics, sender, fields, recipients, template, attachments, review, send | Guided setup | Current steps mix creation and later consequential delivery; completion semantics need audit | Guided first campaign [#35](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/35) |
|
||||
| Review/send wizard routes | Focus the canonical review or send stage | Guided review | Thin wrappers render the same `ReviewSendPage` with a stable initial stage; no parallel workflow state exists | #74 inventory decision complete |
|
||||
| Operator queue (`OperatorQueuePage`) | Monitor jobs and intervene | Monitoring/work queue | Campaign/version/job identity, historical active-version discovery, fixed action positions, authority-aware disabled states, exact non-overlapping queue counts, server-paged jobs, bounded refresh, retry/queue/reconcile per version, campaign-wide pause/resume/cancel, and leave/return progress | Durable controls #78 and #74 wording/accessibility audit complete |
|
||||
| Aggregate reports (`AggregateReportsPage`) | Compare cross-campaign delivery outcomes | Privacy-preserving aggregate reporting | Tenant/campaign ACL, deployment/tenant small-cell policy, complementary and overlapping-cell suppression, explicit denominator, and no recipient detail/diagnostics/export/drill-down | Separate aggregate-reader surface delivered in [#80](https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/80); not parity with the permission-gated per-campaign detail report |
|
||||
|
||||
The five review stages currently named in code are `Validate and inspect`,
|
||||
`Build and review`, `Mock send and verify`, `Confirm and send`, and `Delivery
|
||||
results`. Campaign #63 owns the intervention and status vocabulary; Workflow is
|
||||
not required to define or implement it.
|
||||
|
||||
## Repositories Without A WebUI Package
|
||||
|
||||
The generated manifest snapshot reports no WebUI package for:
|
||||
|
||||
`govoplan-assets`, `govoplan-booking`, `govoplan-certificates`,
|
||||
`govoplan-connectors`, `govoplan-consultation`, `govoplan-contracts`,
|
||||
`govoplan-decisions`, `govoplan-encryption`, `govoplan-evaluation`,
|
||||
`govoplan-facilities`, `govoplan-grants`, `govoplan-helpdesk`,
|
||||
`govoplan-identity`, `govoplan-identity-trust`, `govoplan-inspections`,
|
||||
`govoplan-learning`, `govoplan-mandates`, `govoplan-parties`,
|
||||
`govoplan-permits`, `govoplan-poll`, `govoplan-procurement`,
|
||||
`govoplan-records`, `govoplan-resources`, `govoplan-rest`,
|
||||
`govoplan-services`, `govoplan-soap`, `govoplan-transparency`, and
|
||||
`govoplan-workflow-engine`.
|
||||
|
||||
Tenancy does provide composed administration surfaces despite having no direct
|
||||
route. This section is only negative package evidence; connector-only,
|
||||
capability-only, runtime-only, and backend-only modules may intentionally remain
|
||||
headless. A new WebUI should be created only for a concrete user task, not to
|
||||
make every module symmetrical.
|
||||
|
||||
## Rollout Matrix
|
||||
|
||||
| Order | Scope | Current evidence | Target | Owner / issue | Verification gate | Status |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| 0 | Product grammar and route inventory | Doctrine, ledger, layout rules, module contract, current route sources | One reconciled pattern language and evidence inventory | Meta [#11](https://git.add-ideas.de/GovOPlaN/govoplan/issues/11) | Reviewed route/component inventory, module documents, manifest shapes and focused contracts | Complete 2026-08-03 |
|
||||
| 1 | Campaign baseline integration | Recipient-editor WIP and tracker state have been reconciled with remote `main` | Integrated, testable baseline before migration claims | Campaign #67 and tracker cleanup | Backend and focused WebUI suites; issue evidence | Complete 2026-07-22 |
|
||||
| 2 | Campaign previews/details | Stable shared dialog with bounded scrolling and fixed responsive preview workspace | Stable header/body/footer, accessible long-content detail | Campaign #59 and #73 | Review-preview and overlay structure tests | Complete 2026-07-22 |
|
||||
| 3 | Campaign review/interventions | Five domain-owned stages use central blocker and guided-review primitives; validation/build warnings name action, actor, and destination; hard blockers, individual review, and group review remain distinct; reviewed/remaining counts survive reload through build-bound review evidence | Clear stages, outcomes, blockers, next actor/action, reviewed evidence | Campaign #63 | `reviewProgress` state tests, shared-component structure contract, TypeScript build, configured-system help topic, and Campaign documentation tests | Complete 2026-08-03 (`d635f3a`; Core primitives and contextual help `b823a22`) |
|
||||
| 4 | Campaign send/progress | A hard deployment ceiling bounds synchronous delivery; the selected synchronous, worker-queue, or database-queue mode is explicit and persisted; progress and recovery survive navigation; immediate-send response and audit evidence are allowlisted | Pre-send mode/consequence plus durable leave/return progress, retry and reconciliation without recipient/provider leakage | Campaign #62 and #79 | Boundary/concurrency/preflight, async selection, persisted mode, sanitized response/audit, partial/failure/retry and reload/return tests | Complete 2026-07-22 (`7e16603`, `60efd1c`, `62a6879`, `b0282eb`, `f095a3e`) |
|
||||
| 5 | Campaign report filtering | Core DataGrid distinguishes client/full-result from server-owned queries; Campaign applies filter/sort/count before pagination and synchronizes count shortcuts with the grid query | One shared server-owned status/list/filter/count model | Campaign #65 and Core #263 | DataGrid contract/build tests plus exact shortcut/query/filter/count and large-result behavior | Complete 2026-07-22 (`e6062fe`, `cece71d`, `aa4ec66`, `4eb651c`) |
|
||||
| 6 | Campaign operator recovery | A durable campaign/version queue page exposes historical work, exact non-overlapping state counts, persisted mode, permission-safe controls, server-paged job evidence, bounded refresh and active-state recovery | Fixed-position actions, disabled explanations, leave/return state, version-scoped retry/queue/reconcile and explicit campaign-wide pause/resume/cancel | Campaign #78 | Queue model/structure, historical-version, permission, paging, recovery-control, stale-response and delta tests | Complete 2026-07-22 (`21f3014`, `99d44ee`, `735e874`) |
|
||||
| 7 | Campaign aggregate reports | A separate aggregate-reader projection and UI expose only policy-suppressed business totals with a stable status domain | Explicit denominator and exclusions, deployment floor plus tenant-strengthened small-cell threshold, complementary and overlapping-cell suppression, no detail/export/diagnostics | Campaign #80 | Aggregate query, cross-metric suppression, route/role/ACL, stable filter and UI structure tests | Complete 2026-07-22 (`06125cc`, `fc36aee`, `8ee87b7`, `ac3329c`, `1225802`) |
|
||||
| 8 | Campaign excluded outcomes | Excluded build rows become explicit skipped transport outcomes and remain protected from queue/cancel/retry ambiguity | One durable source-to-job-to-report meaning with guarded historical normalization | Campaign #66 | Builder/persistence, migration, query/count, queue-control and report-explanation tests | Complete 2026-07-22 (`7229fb8`) |
|
||||
| 9 | Guided first campaign | Eight-stage creation flow persists current step/draft and hands off to ordinary review/delivery preparation | Task-oriented entry that hands off clearly to normal editing/review | Campaign #35 | First-run flow, resume/back, partial validation, immutable-history and optional-module behavior, no implicit send | Complete 2026-07-30 |
|
||||
| 10 | Prove/extract generic primitives | Shared consequence, focus, help, blocker, unsaved-change, confirmation, connection-tree and effective-policy contracts now have Core and multiple module consumers | Keep Core behavior-only and leave domain composition in owning modules | Core #225 plus bounded follow-ups | Core behavior/accessibility tests and module-permutation tests | Complete 2026-08-03 (`fa32cca`; Files `d8ae506`; Mail `7844d9c`) |
|
||||
| 11 | Configured-system pattern help | Role/config-aware workflow, reference, pattern, and system topics are projected by Docs; shared route, field, blocker, and action links resolve to configured Docs or the hosted fallback | Stable configured-system guidance without feature-to-Docs imports | Docs #15 | Docs suite, shared component tests, Campaign review tests, 46 module permutations, full-product bundle budget | Complete 2026-08-03 (Docs `abe2f78`; Core `b823a22`; Campaign `d635f3a`) |
|
||||
| 12 | Admin/configuration family | Core host/settings/credential/retention contracts, shared primitives, module lifecycle, Files, Mail, Policy, Access, Admin, Tenancy, Views, and Organizations are integrated and verified | Continue the same consequence/provenance grammar only through bounded module-owned migrations | Core #225 and module children | Per-surface state/accessibility/consequence evidence | Core #225 complete `fa32cca`; Access `1409dbf`; Files `d8ae506`; Mail `7844d9c`; Policy `f964ed7`; Admin `d428f33`; Tenancy `e76fe16`; Views `c125f33`; Organizations `97acfcb` |
|
||||
| 13 | Remaining module surfaces | 33 bounded module-owned issues cover every WebUI contributor not already tracked by Campaign #74 or completed Docs #15 | Per-module audit and migration, ordered by user task and consequence rather than a bulk rewrite | Issues linked in the direct-route and composed-surface sections | Module-focused tests, manifest shapes, contextual Docs, and applicable definition-of-done gates | Complete: prior 28 recorded commits plus Workflow #15, Search #4, Reporting #8, Projects #2 and Portal #2 verified 2026-08-03 |
|
||||
| 14 | Manifest/runtime alignment | Authenticated canonical routes align; public signed-token and compatibility routes are explicit exceptions | Stable declarations reconcile with source and any effective runtime module combination | [Meta #25](https://git.add-ideas.de/GovOPlaN/govoplan/issues/25) | Strict duplicate/stale/undeclared declaration CI, per-module digests, and authorized read-only runtime inventory | Complete 2026-08-04 |
|
||||
|
||||
Workflow remains outside this rollout matrix because it has its own runtime and
|
||||
editor workstream, not because it is postponed. Focused views can be specified,
|
||||
manually selected, and tested through core composition contracts today.
|
||||
Workflow steps may activate those views through the same contract without
|
||||
changing the proven surface patterns.
|
||||
|
||||
## Inventory Maintenance
|
||||
|
||||
When a route, nav item, named UI capability, host section, or Campaign workspace
|
||||
surface changes:
|
||||
|
||||
1. Update the owner, evidence, task, archetype, and consequence here.
|
||||
2. Link the implementation issue and verification evidence.
|
||||
3. Keep "unreviewed" until state, permission/privacy, consequence/provenance,
|
||||
accessibility, responsive, theme, i18n, and applicable async behavior have
|
||||
been checked.
|
||||
4. Re-scan both `module.ts` and the backend manifest; do not infer one from the
|
||||
other.
|
||||
5. Recreate the inventory from a clean release lockfile before using it as
|
||||
release evidence.
|
||||
@@ -113,6 +113,12 @@
|
||||
"description": "GovOPlaN Appointments module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/approvals",
|
||||
"color": "d93f0b",
|
||||
"description": "GovOPlaN Approvals module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/audit",
|
||||
"color": "0e8a16",
|
||||
@@ -143,30 +149,72 @@
|
||||
"description": "GovOPlaN Connectors module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/committee",
|
||||
"color": "5319e7",
|
||||
"description": "GovOPlaN Committee 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/dashboard",
|
||||
"color": "1d76db",
|
||||
"description": "GovOPlaN Dashboard module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/dataflow",
|
||||
"color": "1d76db",
|
||||
"description": "GovOPlaN Dataflow module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/datasources",
|
||||
"color": "006b75",
|
||||
"description": "GovOPlaN governed datasource contracts, catalogs, and integrations.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/dms",
|
||||
"color": "c5def5",
|
||||
"description": "GovOPlaN Dms module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/docs",
|
||||
"color": "c5def5",
|
||||
"description": "GovOPlaN Docs module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/dist-lists",
|
||||
"color": "0e8a16",
|
||||
"description": "GovOPlaN Distribution Lists module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/decisions",
|
||||
"color": "d93f0b",
|
||||
"description": "GovOPlaN formal Decisions module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/erp",
|
||||
"color": "fef2c0",
|
||||
"description": "GovOPlaN Erp module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/encryption",
|
||||
"color": "b60205",
|
||||
"description": "GovOPlaN Encryption key custody, cryptographic policy, and E2EE integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/evaluation",
|
||||
"color": "bfdadc",
|
||||
@@ -191,6 +239,18 @@
|
||||
"description": "GovOPlaN Forms module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/forms-runtime",
|
||||
"color": "f9d0c4",
|
||||
"description": "GovOPlaN Forms Runtime module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/helpdesk",
|
||||
"color": "c2e0c6",
|
||||
"description": "GovOPlaN Helpdesk module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/identity-trust",
|
||||
"color": "d4c5f9",
|
||||
@@ -221,6 +281,12 @@
|
||||
"description": "GovOPlaN mail module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/mandates",
|
||||
"color": "006b75",
|
||||
"description": "GovOPlaN Mandates, jurisdiction, responsibility, and authority behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/notifications",
|
||||
"color": "d876e3",
|
||||
@@ -245,6 +311,12 @@
|
||||
"description": "GovOPlaN Payments module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/parties",
|
||||
"color": "bfd4f2",
|
||||
"description": "GovOPlaN procedure Parties, representation, and delivery-authority behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/permits",
|
||||
"color": "fbca04",
|
||||
@@ -275,12 +347,36 @@
|
||||
"description": "GovOPlaN Postbox module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/projects",
|
||||
"color": "5319e7",
|
||||
"description": "GovOPlaN Projects module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/records",
|
||||
"color": "0052cc",
|
||||
"description": "GovOPlaN Records and eAkte lifecycle behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/reporting",
|
||||
"color": "c2e0c6",
|
||||
"description": "GovOPlaN Reporting module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/risk-compliance",
|
||||
"color": "b60205",
|
||||
"description": "GovOPlaN Risk Compliance module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/quick-access",
|
||||
"color": "c5def5",
|
||||
"description": "GovOPlaN configurable task-local Quick Access behavior and integrations.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/search",
|
||||
"color": "bfdadc",
|
||||
@@ -293,6 +389,12 @@
|
||||
"description": "GovOPlaN Scheduling module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/services",
|
||||
"color": "1d76db",
|
||||
"description": "GovOPlaN versioned institutional Services behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/tasks",
|
||||
"color": "c5def5",
|
||||
@@ -311,6 +413,24 @@
|
||||
"description": "GovOPlaN Tenancy module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/tickets",
|
||||
"color": "0e8a16",
|
||||
"description": "GovOPlaN Tickets module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/views",
|
||||
"color": "c5def5",
|
||||
"description": "GovOPlaN governed task views, interface projections, and workflow view integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/wiki",
|
||||
"color": "006b75",
|
||||
"description": "GovOPlaN Wiki module behavior or integration.",
|
||||
"exclusive": false
|
||||
},
|
||||
{
|
||||
"name": "module/workflow",
|
||||
"color": "f9d0c4",
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://govoplan.add-ideas.de/schemas/installation-spec-v1.json",
|
||||
"title": "GovOPlaN installation specification",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"installation_id",
|
||||
"profile",
|
||||
"public_url",
|
||||
"listen",
|
||||
"network_subnet",
|
||||
"release",
|
||||
"components",
|
||||
"enabled_modules"
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": {
|
||||
"const": 1
|
||||
},
|
||||
"installation_id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]{1,47}$"
|
||||
},
|
||||
"profile": {
|
||||
"enum": [
|
||||
"evaluation",
|
||||
"self-hosted"
|
||||
]
|
||||
},
|
||||
"public_url": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"pattern": "^https?://"
|
||||
},
|
||||
"listen": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"address",
|
||||
"port"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
}
|
||||
}
|
||||
},
|
||||
"network_subnet": {
|
||||
"type": "string"
|
||||
},
|
||||
"release": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"channel",
|
||||
"version",
|
||||
"manifest_url",
|
||||
"manifest_sha256",
|
||||
"api_image",
|
||||
"web_image"
|
||||
],
|
||||
"properties": {
|
||||
"channel": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]{1,31}$"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 80
|
||||
},
|
||||
"manifest_url": {
|
||||
"type": "string"
|
||||
},
|
||||
"manifest_sha256": {
|
||||
"type": "string",
|
||||
"pattern": "^$|^[0-9a-f]{64}$"
|
||||
},
|
||||
"manifest_keyring_sha256": {
|
||||
"type": "string",
|
||||
"pattern": "^$|^[0-9a-f]{64}$"
|
||||
},
|
||||
"manifest_signature_key_id": {
|
||||
"type": "string",
|
||||
"pattern": "^$|^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
|
||||
},
|
||||
"composition_sha256": {
|
||||
"type": "string",
|
||||
"pattern": "^$|^[0-9a-f]{64}$"
|
||||
},
|
||||
"api_image": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 300
|
||||
},
|
||||
"web_image": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 300
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"postgres",
|
||||
"redis",
|
||||
"mail",
|
||||
"storage"
|
||||
],
|
||||
"properties": {
|
||||
"postgres": {
|
||||
"$ref": "#/$defs/postgres"
|
||||
},
|
||||
"redis": {
|
||||
"$ref": "#/$defs/redis"
|
||||
},
|
||||
"mail": {
|
||||
"$ref": "#/$defs/mail"
|
||||
},
|
||||
"storage": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"mode"
|
||||
],
|
||||
"properties": {
|
||||
"mode": {
|
||||
"enum": [
|
||||
"local",
|
||||
"garage",
|
||||
"s3"
|
||||
]
|
||||
},
|
||||
"image": {
|
||||
"type": "string",
|
||||
"maxLength": 300
|
||||
}
|
||||
}
|
||||
},
|
||||
"load_balancer": {
|
||||
"$ref": "#/$defs/load_balancer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"replicas": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"api",
|
||||
"web",
|
||||
"worker"
|
||||
],
|
||||
"properties": {
|
||||
"api": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 64
|
||||
},
|
||||
"web": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 64
|
||||
},
|
||||
"worker": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 128
|
||||
}
|
||||
}
|
||||
},
|
||||
"ingress": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"mode",
|
||||
"image",
|
||||
"trusted_proxy_cidrs",
|
||||
"http_port",
|
||||
"https_port",
|
||||
"acme_email"
|
||||
],
|
||||
"properties": {
|
||||
"mode": {
|
||||
"enum": [
|
||||
"local",
|
||||
"existing-proxy",
|
||||
"managed",
|
||||
"unconfigured"
|
||||
]
|
||||
},
|
||||
"image": {
|
||||
"type": "string",
|
||||
"maxLength": 300
|
||||
},
|
||||
"trusted_proxy_cidrs": {
|
||||
"type": "array",
|
||||
"maxItems": 16,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"maxLength": 64
|
||||
}
|
||||
},
|
||||
"http_port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
},
|
||||
"https_port": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
},
|
||||
"acme_email": {
|
||||
"type": "string",
|
||||
"maxLength": 254
|
||||
}
|
||||
}
|
||||
},
|
||||
"enabled_modules": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9_]{1,63}$"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"service": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"mode",
|
||||
"image",
|
||||
"url_env"
|
||||
],
|
||||
"properties": {
|
||||
"mode": {
|
||||
"type": "string"
|
||||
},
|
||||
"image": {
|
||||
"type": "string"
|
||||
},
|
||||
"url_env": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"postgres": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/service"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"mode": {
|
||||
"enum": [
|
||||
"managed",
|
||||
"external"
|
||||
]
|
||||
},
|
||||
"url_env": {
|
||||
"const": "DATABASE_URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"redis": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/service"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"mode": {
|
||||
"enum": [
|
||||
"managed",
|
||||
"external",
|
||||
"disabled"
|
||||
]
|
||||
},
|
||||
"url_env": {
|
||||
"const": "REDIS_URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"mail": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/service"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"mode": {
|
||||
"enum": [
|
||||
"disabled",
|
||||
"external-relay",
|
||||
"test-mail"
|
||||
]
|
||||
},
|
||||
"url_env": {
|
||||
"const": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"load_balancer": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/service"
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"mode": {
|
||||
"const": "managed"
|
||||
},
|
||||
"url_env": {
|
||||
"const": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"profile": {
|
||||
"const": "self-hosted"
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"properties": {
|
||||
"public_url": {
|
||||
"pattern": "^https://"
|
||||
},
|
||||
"components": {
|
||||
"properties": {
|
||||
"redis": {
|
||||
"properties": {
|
||||
"mode": {
|
||||
"enum": [
|
||||
"managed",
|
||||
"external"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"mail": {
|
||||
"properties": {
|
||||
"mode": {
|
||||
"enum": [
|
||||
"disabled",
|
||||
"external-relay"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://git.add-ideas.de/GovOPlaN/govoplan/src/branch/main/docs/installed-composition-evidence.schema.json",
|
||||
"title": "GovOPlaN installed composition evidence",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"evidence_kind",
|
||||
"assessment_id",
|
||||
"assessment_release",
|
||||
"collected_at",
|
||||
"scope",
|
||||
"artifacts",
|
||||
"collection_issues"
|
||||
],
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"format": "uri-reference"
|
||||
},
|
||||
"schema_version": {
|
||||
"const": "0.4.0"
|
||||
},
|
||||
"evidence_kind": {
|
||||
"const": "govoplan.installed-composition"
|
||||
},
|
||||
"assessment_id": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
},
|
||||
"assessment_release": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
},
|
||||
"collected_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"scope": {
|
||||
"const": "current-python-environment.govoplan-distributions"
|
||||
},
|
||||
"artifacts": {
|
||||
"type": "array",
|
||||
"maxItems": 256,
|
||||
"items": {
|
||||
"$ref": "#/$defs/artifact"
|
||||
}
|
||||
},
|
||||
"collection_issues": {
|
||||
"type": "array",
|
||||
"maxItems": 256,
|
||||
"items": {
|
||||
"$ref": "#/$defs/collection_issue"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"opaque_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160,
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
||||
},
|
||||
"package_name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 128,
|
||||
"pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 128,
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9._+!-]*$"
|
||||
},
|
||||
"artifact": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"package_name",
|
||||
"package_version",
|
||||
"modules",
|
||||
"source_provenance",
|
||||
"record_integrity"
|
||||
],
|
||||
"properties": {
|
||||
"package_name": {
|
||||
"$ref": "#/$defs/package_name"
|
||||
},
|
||||
"package_version": {
|
||||
"$ref": "#/$defs/version"
|
||||
},
|
||||
"modules": {
|
||||
"type": "array",
|
||||
"maxItems": 16,
|
||||
"items": {
|
||||
"$ref": "#/$defs/module"
|
||||
}
|
||||
},
|
||||
"source_provenance": {
|
||||
"$ref": "#/$defs/source_provenance"
|
||||
},
|
||||
"record_integrity": {
|
||||
"$ref": "#/$defs/record_integrity"
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["module_id", "manifest_version"],
|
||||
"properties": {
|
||||
"module_id": {
|
||||
"$ref": "#/$defs/opaque_id"
|
||||
},
|
||||
"manifest_version": {
|
||||
"$ref": "#/$defs/version"
|
||||
}
|
||||
}
|
||||
},
|
||||
"source_provenance": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["basis", "kind", "commit"],
|
||||
"properties": {
|
||||
"basis": {
|
||||
"const": "local-pep610-metadata"
|
||||
},
|
||||
"kind": {
|
||||
"const": "vcs-commit"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-f]{40,64}$"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["basis", "kind", "sha256"],
|
||||
"properties": {
|
||||
"basis": {
|
||||
"const": "local-pep610-metadata"
|
||||
},
|
||||
"kind": {
|
||||
"const": "archive"
|
||||
},
|
||||
"sha256": {
|
||||
"$ref": "#/$defs/sha256"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["basis", "kind"],
|
||||
"properties": {
|
||||
"basis": {
|
||||
"const": "local-pep610-metadata"
|
||||
},
|
||||
"kind": {
|
||||
"enum": [
|
||||
"editable-local",
|
||||
"local-directory",
|
||||
"index-or-unknown",
|
||||
"malformed-direct-url"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"record_integrity": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"status",
|
||||
"hashed_file_count",
|
||||
"permitted_unhashed_file_count",
|
||||
"generated_unhashed_file_count",
|
||||
"unverifiable_file_count",
|
||||
"missing_file_count",
|
||||
"mismatched_file_count",
|
||||
"artifact_payload_identity",
|
||||
"installed_payload_identity"
|
||||
],
|
||||
"properties": {
|
||||
"status": {
|
||||
"enum": [
|
||||
"verified",
|
||||
"partial",
|
||||
"mismatch",
|
||||
"unavailable",
|
||||
"limit-exceeded"
|
||||
]
|
||||
},
|
||||
"hashed_file_count": {
|
||||
"$ref": "#/$defs/count"
|
||||
},
|
||||
"permitted_unhashed_file_count": {
|
||||
"$ref": "#/$defs/count"
|
||||
},
|
||||
"generated_unhashed_file_count": {
|
||||
"$ref": "#/$defs/count"
|
||||
},
|
||||
"unverifiable_file_count": {
|
||||
"$ref": "#/$defs/count"
|
||||
},
|
||||
"missing_file_count": {
|
||||
"$ref": "#/$defs/count"
|
||||
},
|
||||
"mismatched_file_count": {
|
||||
"$ref": "#/$defs/count"
|
||||
},
|
||||
"artifact_payload_identity": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/$defs/artifact_payload_identity" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
},
|
||||
"installed_payload_identity": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/$defs/installed_payload_identity" },
|
||||
{ "type": "null" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": { "properties": { "status": { "const": "verified" } } },
|
||||
"then": {
|
||||
"properties": {
|
||||
"hashed_file_count": { "minimum": 1 },
|
||||
"permitted_unhashed_file_count": { "minimum": 1 },
|
||||
"unverifiable_file_count": { "const": 0 },
|
||||
"missing_file_count": { "const": 0 },
|
||||
"mismatched_file_count": { "const": 0 },
|
||||
"artifact_payload_identity": { "$ref": "#/$defs/artifact_payload_identity" },
|
||||
"installed_payload_identity": { "$ref": "#/$defs/installed_payload_identity" }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": { "properties": { "status": { "const": "partial" } } },
|
||||
"then": {
|
||||
"properties": {
|
||||
"hashed_file_count": { "minimum": 1 },
|
||||
"unverifiable_file_count": { "minimum": 1 },
|
||||
"missing_file_count": { "const": 0 },
|
||||
"mismatched_file_count": { "const": 0 }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": { "properties": { "status": { "const": "mismatch" } } },
|
||||
"then": {
|
||||
"anyOf": [
|
||||
{ "properties": { "missing_file_count": { "minimum": 1 } } },
|
||||
{ "properties": { "mismatched_file_count": { "minimum": 1 } } }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": { "properties": { "status": { "const": "unavailable" } } },
|
||||
"then": {
|
||||
"properties": {
|
||||
"hashed_file_count": { "const": 0 },
|
||||
"missing_file_count": { "const": 0 },
|
||||
"mismatched_file_count": { "const": 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"collection_issue": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["code", "package_name"],
|
||||
"properties": {
|
||||
"code": {
|
||||
"enum": [
|
||||
"distribution-metadata-invalid",
|
||||
"duplicate-distribution",
|
||||
"module-entry-point-load-failed",
|
||||
"module-entry-point-invalid",
|
||||
"module-entry-point-limit-exceeded",
|
||||
"collection-limit-exceeded"
|
||||
]
|
||||
},
|
||||
"package_name": {
|
||||
"$ref": "#/$defs/package_name"
|
||||
}
|
||||
}
|
||||
},
|
||||
"artifact_payload_identity": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["algorithm", "sha256", "file_count"],
|
||||
"properties": {
|
||||
"algorithm": { "const": "govoplan-wheel-declared-payload-v1" },
|
||||
"sha256": { "$ref": "#/$defs/sha256" },
|
||||
"file_count": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 10000
|
||||
}
|
||||
}
|
||||
},
|
||||
"installed_payload_identity": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["algorithm", "sha256", "file_count"],
|
||||
"properties": {
|
||||
"algorithm": { "const": "govoplan-installed-record-payload-v1" },
|
||||
"sha256": { "$ref": "#/$defs/sha256" },
|
||||
"file_count": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 10000
|
||||
}
|
||||
}
|
||||
},
|
||||
"count": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 1000000
|
||||
},
|
||||
"sha256": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-f]{64}$"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://git.add-ideas.de/GovOPlaN/govoplan/src/branch/main/docs/installer-receipt-authority-keyring.schema.json",
|
||||
"title": "GovOPlaN installer receipt authority keyring",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema_version", "purpose", "keys"],
|
||||
"properties": {
|
||||
"$schema": { "type": "string", "format": "uri-reference" },
|
||||
"schema_version": { "const": "0.1.0" },
|
||||
"purpose": { "const": "govoplan.installer-receipt-authorities" },
|
||||
"keys": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 64,
|
||||
"items": { "$ref": "#/$defs/key" }
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"opaque_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160,
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
||||
},
|
||||
"key": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["key_id", "status", "public_key", "allowed_scopes"],
|
||||
"properties": {
|
||||
"key_id": { "$ref": "#/$defs/opaque_id" },
|
||||
"status": {
|
||||
"enum": ["active", "next", "revoked", "disabled", "retired"]
|
||||
},
|
||||
"public_key": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 256,
|
||||
"contentEncoding": "base64"
|
||||
},
|
||||
"allowed_scopes": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 1,
|
||||
"uniqueItems": true,
|
||||
"items": { "const": "installed_release_origin" }
|
||||
},
|
||||
"not_before": { "type": "string", "format": "date-time" },
|
||||
"not_after": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://git.add-ideas.de/GovOPlaN/govoplan/src/branch/main/docs/installer-receipt.schema.json",
|
||||
"title": "GovOPlaN signed installer receipt",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"evidence_kind",
|
||||
"receipt_id",
|
||||
"assessment_id",
|
||||
"assessment_release",
|
||||
"installed_evidence_sha256",
|
||||
"catalog",
|
||||
"issued_at",
|
||||
"artifacts",
|
||||
"signatures"
|
||||
],
|
||||
"properties": {
|
||||
"$schema": { "type": "string", "format": "uri-reference" },
|
||||
"schema_version": { "const": "0.1.0" },
|
||||
"evidence_kind": { "const": "govoplan.installer-receipt" },
|
||||
"receipt_id": { "$ref": "#/$defs/opaque_id" },
|
||||
"assessment_id": { "$ref": "#/$defs/opaque_id" },
|
||||
"assessment_release": { "$ref": "#/$defs/opaque_id" },
|
||||
"installed_evidence_sha256": { "$ref": "#/$defs/sha256" },
|
||||
"catalog": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["channel", "sequence", "sha256"],
|
||||
"properties": {
|
||||
"channel": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9_-]{0,63}$"
|
||||
},
|
||||
"sequence": { "type": "integer", "minimum": 1 },
|
||||
"sha256": { "$ref": "#/$defs/sha256" }
|
||||
}
|
||||
},
|
||||
"issued_at": { "type": "string", "format": "date-time" },
|
||||
"artifacts": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 256,
|
||||
"items": { "$ref": "#/$defs/artifact" }
|
||||
},
|
||||
"signatures": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 16,
|
||||
"items": { "$ref": "#/$defs/signature" }
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"opaque_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160,
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$"
|
||||
},
|
||||
"package_name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 128,
|
||||
"pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 128,
|
||||
"pattern": "^[A-Za-z0-9][A-Za-z0-9._+!-]*$"
|
||||
},
|
||||
"artifact": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"package_name",
|
||||
"package_version",
|
||||
"catalog_archive_sha256",
|
||||
"installed_payload"
|
||||
],
|
||||
"properties": {
|
||||
"package_name": { "$ref": "#/$defs/package_name" },
|
||||
"package_version": { "$ref": "#/$defs/version" },
|
||||
"catalog_archive_sha256": { "$ref": "#/$defs/sha256" },
|
||||
"installed_payload": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["algorithm", "sha256", "file_count"],
|
||||
"properties": {
|
||||
"algorithm": { "const": "govoplan-installed-record-payload-v1" },
|
||||
"sha256": { "$ref": "#/$defs/sha256" },
|
||||
"file_count": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 10000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"signature": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["algorithm", "key_id", "value"],
|
||||
"properties": {
|
||||
"algorithm": { "const": "ed25519" },
|
||||
"key_id": { "$ref": "#/$defs/opaque_id" },
|
||||
"value": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 256,
|
||||
"contentEncoding": "base64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sha256": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-f]{64}$"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
# Backup And Restore Evidence
|
||||
|
||||
## Boundary
|
||||
|
||||
`govoplan-deploy` verifies backup and restore evidence; it does not receive
|
||||
database, object-store, KMS, or orchestrator administration credentials and it
|
||||
does not create the backup. A provider-owned backup controller creates one
|
||||
coordinated recovery point, a separate drill runner restores it into an
|
||||
isolated target, and an evidence authority signs the resulting receipt.
|
||||
|
||||
The application containers receive only a sanitized projection: evidence,
|
||||
recovery-point and drill identifiers, hashes, timestamps, component count, and
|
||||
measured RPO/RTO. Artifact locations, provider credentials, encryption-key
|
||||
references, the public trust keyring, and private signing keys remain in the
|
||||
deployment/evidence boundary.
|
||||
|
||||
The machine-readable contracts are:
|
||||
|
||||
- [`backup-evidence.schema.json`](../backup-evidence.schema.json);
|
||||
- [`backup-evidence-keyring.schema.json`](../backup-evidence-keyring.schema.json).
|
||||
|
||||
One evidence document is bound to the installation id, deployment profile,
|
||||
topology subject, exact signed release manifest, image digests, and composition
|
||||
digest. It covers PostgreSQL, objects, protected configuration, and recoverable
|
||||
key custody at one recovery point. It contains references, never key material.
|
||||
|
||||
## Production Sequence
|
||||
|
||||
1. Establish the provider snapshot, application quiesce, or transaction
|
||||
boundary and retain a hash of its fencing token.
|
||||
2. Capture PostgreSQL, object storage, protected deployment configuration, and
|
||||
key-custody state within five minutes of that recovery point.
|
||||
3. Restore all four components into a target isolated from production write
|
||||
endpoints and production queues.
|
||||
4. Start the exact immutable release named in the evidence, verify migration
|
||||
heads, verify a deterministic manifest of representative object hashes, and
|
||||
execute the documented semantic journey checks.
|
||||
5. Record actual data loss and elapsed recovery as measured RPO and RTO. A
|
||||
measured RPO above the declared objective invalidates the evidence.
|
||||
6. Sign the canonical receipt using an evidence-authority Ed25519 key held
|
||||
outside the application and deployment host. During key rotation, include
|
||||
both accepted signatures.
|
||||
7. Transfer the evidence SHA-256 through an independent approved channel, then
|
||||
verify and adopt it on the deployment host.
|
||||
|
||||
Provider automation can sign and validate an unsigned receipt with:
|
||||
|
||||
```sh
|
||||
python tools/deployment/sign-backup-evidence.py \
|
||||
--input unsigned-backup-evidence.json \
|
||||
--output backup-evidence.json \
|
||||
--trusted-keyring backup-evidence-keyring.json \
|
||||
--signing-key backup-authority-2026=/run/keys/backup-authority.pem
|
||||
```
|
||||
|
||||
The private key file must be owner-only. The tool refuses an unexpected key
|
||||
type, an inactive/untrusted signer, malformed or partial evidence, stale
|
||||
recovery points, failed drill checks, mismatched releases, and non-canonical
|
||||
output.
|
||||
|
||||
Adopt the result using the independently obtained digest:
|
||||
|
||||
```sh
|
||||
python3 govoplan-deploy.pyz verify-backup \
|
||||
--directory /srv/govoplan/default \
|
||||
--evidence ./backup-evidence.json \
|
||||
--evidence-sha256 "$APPROVED_BACKUP_EVIDENCE_SHA256" \
|
||||
--trusted-keyring ./backup-evidence-keyring.json \
|
||||
--adopt
|
||||
```
|
||||
|
||||
Evidence is fresh for at most 24 hours and may declare an earlier expiry. Every
|
||||
self-hosted release identity change is conservatively treated as a migration
|
||||
boundary. `doctor`, Compose `apply`, and `render-kubernetes` fail closed when
|
||||
fresh evidence for the previously applied immutable release is unavailable.
|
||||
Compose verifies once before changing runtime state and again after API/worker
|
||||
quiescing immediately before migration. The exported Kubernetes migration Job
|
||||
is generated only after verification and is annotated with the sanitized
|
||||
evidence digest, recovery-point id, and drill id.
|
||||
|
||||
## Provider Runbooks
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
Use a managed transaction-consistent snapshot or a base backup plus retained
|
||||
WAL sufficient to reconstruct the declared point. Record the provider,
|
||||
protected artifact reference and digest, snapshot identity, and PostgreSQL LSN.
|
||||
The restore drill must connect only to the isolated database and must compare
|
||||
the resulting migration-head digest with the release expectation.
|
||||
|
||||
### Object Storage
|
||||
|
||||
Use provider snapshots/versioning or an immutable object copy. Build a sorted
|
||||
manifest containing object key, version, size, and content digest, then record
|
||||
its digest, object count, total bytes, provider version identity, and protected
|
||||
artifact reference. Verify representative objects from every owning module
|
||||
after restore. Single-node managed Garage is persistent but not highly
|
||||
available; copy its coordinated recovery material to an independent failure
|
||||
domain.
|
||||
|
||||
### Configuration And Key Custody
|
||||
|
||||
Back up the private installation bundle and external secret-manager bindings as
|
||||
an encrypted artifact. Record only its reference and digest. For KMS/HSM/vault
|
||||
state, record the provider keyset reference, version, and a successful
|
||||
recoverability assertion. Never put a key, recovery share, token, password, or
|
||||
credential-bearing URL in evidence. The isolated drill must prove that the
|
||||
restored release can decrypt representative protected content without
|
||||
exporting the key material into the report.
|
||||
|
||||
## Ownership And Retention
|
||||
|
||||
The deployment owner approves the RPO/RTO objectives. State-service owners
|
||||
operate backup capture and restoration. Module owners define representative
|
||||
objects and semantic checks. Security owns evidence-authority keys and
|
||||
revocation. Operations schedules drills and retains sanitized status.
|
||||
|
||||
Retain backup artifacts for the approved legal/operational period and at least
|
||||
through the release's rollback window. Retain signed evidence, drill reports,
|
||||
and deletion receipts for the audit period. Disposal must remove every backup
|
||||
copy and provider version according to policy, then revoke or retire references
|
||||
without deleting the audit receipt. Cryptographic erasure is valid only when
|
||||
key-destruction evidence and provider-copy coverage are independently proven.
|
||||
|
||||
## Failure Handling
|
||||
|
||||
Missing components, component-time skew, stale or expired evidence, revocation,
|
||||
signature/key mismatch, changed stored files, release mismatch, failed semantic
|
||||
checks, or an RPO breach block migration. The deployment journal records the
|
||||
rejection without private provider details. If migration has not started, the
|
||||
operator may supply fresh evidence and retry. Once migration starts, recovery
|
||||
is explicitly forward-only until the verified coordinated recovery point is
|
||||
restored with its matching release.
|
||||
@@ -0,0 +1,120 @@
|
||||
# GovOPlaN Deployment Profiles
|
||||
|
||||
## Purpose
|
||||
|
||||
GovOPlaN distinguishes how code is executed, where it is placed, and how mature
|
||||
the target is. These are separate concerns:
|
||||
|
||||
- **execution basis:** editable source trees or an immutable signed release;
|
||||
- **topology:** local processes, one-host containers, or a multi-host
|
||||
orchestrator;
|
||||
- **component ownership:** installer-managed or externally supplied state and
|
||||
infrastructure services; and
|
||||
- **assurance state:** development, rehearsal/acceptance, or approved
|
||||
production.
|
||||
|
||||
PostgreSQL, Redis, object storage, mail and ingress choices are component
|
||||
bindings inside a profile. They do not create a new application topology by
|
||||
themselves.
|
||||
|
||||
## Canonical Profiles
|
||||
|
||||
| Profile | Entry point | Application execution | State services | Intended use | Explicit boundary |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Local source development | `tools/launch/launch-dev.sh` | Editable Uvicorn/Vite processes with reload | Local development bindings, optionally the shared PostgreSQL helper | Fast module and UI work | No production packaging, isolation, availability or capacity claim |
|
||||
| Split source integration | `tools/launch/launch-production-like-dev.sh` | Editable API, WebUI, worker and scheduler processes | Containerized PostgreSQL/Redis by default; environment bindings may point at developer-owned services | Queue, migration, Redis and split-role integration while retaining source reload | “Production-like” describes behavior, not immutable artifacts or a production security boundary |
|
||||
| Immutable single-host rehearsal | `govoplan-deploy init/apply --profile evaluation` | Signed API/WebUI images and generated Compose roles | Bounded managed components or explicit external bindings | Test the downloadable artifacts, installer, migrations, load balancer and component choices | All containers and managed services may share one host and failure domain; evaluation conveniences are not production controls |
|
||||
| Single-host production | `govoplan-deploy init/apply --profile self-hosted` | Signed API/WebUI images behind generated HAProxy and selected TLS ingress | Durable local/single-node managed services where accepted, or external services | Small and medium installations whose accepted availability boundary is one host | Multiple containers add capacity and rolling-process resilience, but do not survive host loss |
|
||||
| Multi-host Kubernetes production | `govoplan-deploy render-kubernetes` or the guarded K3s lab/acceptance workflow | Immutable API, WebUI and queue-specific worker Deployments across failure domains | External PostgreSQL, Redis and S3-compatible storage; external secret and ingress control | Institution-scale availability and horizontal application-tier capacity | Production claims require independent nodes, HA state services, load/capacity evidence and signed recovery evidence |
|
||||
|
||||
Docker Compose services are containers or replicas, not Kubernetes pods. The
|
||||
immutable single-host rehearsal is the appropriate Dockerized whole-product
|
||||
test when source reload is not required.
|
||||
|
||||
The K3s VM lab has two modes over the same Kubernetes profile:
|
||||
|
||||
- `rehearsal` may place VMs on one physical hypervisor and proves bounded
|
||||
orchestration behavior;
|
||||
- `acceptance` requires independently controlled worker failure domains and can
|
||||
contribute target evidence.
|
||||
|
||||
## Module composition and availability
|
||||
|
||||
Official immutable API and WebUI images carry the verified `full` package
|
||||
profile. This is package availability, not runtime activation and not a license
|
||||
or tenant entitlement. The signed distribution manifest records the complete
|
||||
package composition; the desired module graph selects which installed modules
|
||||
are active; tenant module policy applies unavailable/available/forced ceilings;
|
||||
and Views/Policy control group and user presentation.
|
||||
|
||||
Local and single-host profiles may use the supervised installer to download a
|
||||
signed catalog artifact into a private digest cache and mutate the local package
|
||||
environment during maintenance. A multi-host/shared-state profile must never
|
||||
change one replica in place. Its Admin install plan is a composition request:
|
||||
publish and roll out a new signed image whose package lock contains the target,
|
||||
then activate the module graph after all replicas report the same composition.
|
||||
|
||||
## Component Choices
|
||||
|
||||
The installer may manage a component where its bounded profile is appropriate,
|
||||
or consume an operator-provided service:
|
||||
|
||||
| Component | Managed boundary | External/BYO boundary |
|
||||
| --- | --- | --- |
|
||||
| PostgreSQL | Single-host Compose database | Stable primary-aware endpoint supplied by a PostgreSQL provider/operator |
|
||||
| Redis | Single-host persistent Redis | Tested HA Redis endpoint compatible with queues, throttling and coordination |
|
||||
| File/object storage | Durable local storage or single-node Garage | Shared, redundant S3-compatible storage |
|
||||
| Mail | Development GreenMail only | Institution/provider SMTP and IMAP services |
|
||||
| Ingress/TLS | Generated Caddy on one host | Existing reverse proxy or Kubernetes ingress and secret management |
|
||||
|
||||
Switching to an external component changes ownership and evidence requirements;
|
||||
it does not remove GovOPlaN's health, capacity, backup and recovery checks.
|
||||
|
||||
## Scaling Responsibilities
|
||||
|
||||
GovOPlaN scales application roles, while the infrastructure control plane owns
|
||||
machines and state-service replication:
|
||||
|
||||
| Concern | Scaling model | Owner |
|
||||
| --- | --- | --- |
|
||||
| API and WebUI | Increase replicas behind health-aware Services/Ingress | GovOPlaN deployment desired state, reconciled by Compose or Kubernetes |
|
||||
| Background work | Add queue-specific worker replicas and bounded concurrency | GovOPlaN deployment desired state and worker-pool configuration |
|
||||
| Scheduler, migrations and module lifecycle | Singleton execution protected by database leases/fencing | GovOPlaN; these roles are never scaled as unfenced active-active workers |
|
||||
| Kubernetes worker/control nodes | Add, drain, replace and upgrade machines; optionally use a cluster autoscaler | Kubernetes/platform operator, not the GovOPlaN application |
|
||||
| PostgreSQL | Replication, failover, backups, connection pooling and stable writer endpoint | Database operator/provider; GovOPlaN currently consumes the stable endpoint and does not route arbitrary reads to replicas |
|
||||
| Redis | Replication/failover, persistence, eviction and TLS/authentication | Redis operator/provider |
|
||||
| S3-compatible storage | Placement, replication, repair and capacity | Storage operator/provider |
|
||||
|
||||
Administrators should eventually be able to review and change permitted
|
||||
application replica and worker-pool desired state through the Ops surface.
|
||||
Creating physical machines, database replicas or storage members remains an
|
||||
orchestrator/provider action. GovOPlaN must observe their health and block unsafe
|
||||
changes rather than becoming a second infrastructure scheduler.
|
||||
|
||||
Every scale change must recalculate the database connection budget, preserve
|
||||
queue coverage, verify software/module-composition consistency and respect
|
||||
drain and fencing state.
|
||||
|
||||
## What Has Been Proven
|
||||
|
||||
The current implementation and the signed `v0.1.18` rehearsal prove that the
|
||||
application tier can run as stateless API, WebUI and worker replicas against
|
||||
logically shared state. Two Kubernetes worker VMs hosted API and WebUI replicas,
|
||||
and an API pod was replaced without an observed public-readiness failure.
|
||||
|
||||
This is not yet proof of general “large organization fit.” That claim also
|
||||
requires:
|
||||
|
||||
- representative concurrent-user, dataset, report and background-job load
|
||||
tests with latency and saturation budgets;
|
||||
- independent physical failure domains and real ingress/network behavior;
|
||||
- HA PostgreSQL, Redis and object storage with failover drills;
|
||||
- session, accepted-job and provider-effect continuity under node and service
|
||||
loss;
|
||||
- coordinated backup/isolated restore, measured RTO/RPO and semantic recovery;
|
||||
- observability, alerting, capacity forecasting and sustained soak evidence.
|
||||
|
||||
The profile therefore proves the architecture is horizontally deployable. A
|
||||
specific institution is production-fit only after its target topology and load
|
||||
envelope have produced the governed evidence described in
|
||||
`TARGET_MATURITY_EVIDENCE_RUNBOOK.md`.
|
||||
@@ -0,0 +1,138 @@
|
||||
# Full registry candidates / Vollständige Registry-Kandidaten
|
||||
|
||||
## Operator workflow (EN)
|
||||
|
||||
The canonical `release-catalog.py full-registry` command takes `--package-set`,
|
||||
`--package-lock`, `--wheelhouse`, `--webui-packages`, `--output-dir`, and a
|
||||
configured `--catalog-signing-key`. Generate the package set with
|
||||
`generate-release-package-set.py --profile full` and download its exact artifacts
|
||||
with `resolve-package-artifacts.py`; do not substitute locally rebuilt wheels.
|
||||
The candidate compares the package set with the exact developer meta-package
|
||||
pins, checks archive bytes and package metadata against the lock, and synthesizes
|
||||
every entry from its immutable tagged manifest. Native package publication and
|
||||
its CI authority remain trusted: this verifies the published artifact identity,
|
||||
not independent reproducible-build equivalence to source.
|
||||
|
||||
Pass `--selected-repository` once for each newly released repository, including
|
||||
Core when it changes. These selected units must have clean, version-aligned
|
||||
named branches whose HEAD equals the annotated local and remote release tag.
|
||||
Other full-profile packages retain their exact older annotated tags; a later
|
||||
workflow-only commit on `main` does not relabel those package contents or force
|
||||
a version bump. Every source fetch/push endpoint must match the registered
|
||||
origin, and all entries bind their source commit and annotated tag object.
|
||||
Git replacement objects, caller Git configuration, and executable-path
|
||||
redirection cannot substitute another tagged manifest tree.
|
||||
|
||||
The fixed existing website catalog and keyring are authenticated before signing.
|
||||
An older catalog without a signed keyring hash can be migrated only through this
|
||||
complete rebuild, only when its signature verifies and its entire keyring
|
||||
exactly matches the configured known signers. No old entries or artifact hashes
|
||||
are reused. The new catalog signs the exact unchanged website keyring hash;
|
||||
key rotation remains a separate reviewed operation. Selective candidates still
|
||||
reject unpinned base keyrings. New candidate directories are private and
|
||||
exclusive: a retry must choose a new directory, not overwrite a reviewed one.
|
||||
|
||||
Run these commands on the trusted host, with an operator-private source workspace
|
||||
and artifact directory. `RELEASE_PYTHON` must select its private environment and
|
||||
`RELEASE_NPM` an absolute npm executable with a trusted sibling Node 22 binary.
|
||||
In Flatpak, execute host commands through `flatpak-spawn --host`; sandbox and
|
||||
host UID mappings are not interchangeable. Do not weaken trust gates or change
|
||||
system-wide permissions.
|
||||
|
||||
```sh
|
||||
# These paths identify previously prepared private operator resources.
|
||||
RELEASE_WORKSPACE=/path/to/private/workspace
|
||||
RELEASE_PYTHON="$RELEASE_WORKSPACE/govoplan/.host-venv/bin/python"
|
||||
RELEASE_NPM=/path/to/private/node22/bin/npm
|
||||
ARTIFACT_ROOT=/path/to/private/artifacts
|
||||
RELEASE_CANDIDATE=/path/to/private/new-candidate
|
||||
RELEASE_VERSION=0.1.45
|
||||
RELEASE_TOOLS="$RELEASE_WORKSPACE/govoplan/tools/release"
|
||||
|
||||
umask 077
|
||||
"$RELEASE_PYTHON" "$RELEASE_TOOLS/generate-release-package-set.py" \
|
||||
--version "$RELEASE_VERSION" --profile full \
|
||||
--workspace "$RELEASE_WORKSPACE" --output "$ARTIFACT_ROOT/packages.json"
|
||||
PATH="$(dirname "$RELEASE_NPM"):/usr/bin:/bin" \
|
||||
"$RELEASE_PYTHON" "$RELEASE_TOOLS/resolve-package-artifacts.py" \
|
||||
--package-set "$ARTIFACT_ROOT/packages.json" \
|
||||
--wheelhouse "$ARTIFACT_ROOT/wheels" \
|
||||
--webui-packages "$ARTIFACT_ROOT/webui" \
|
||||
--lock-output "$ARTIFACT_ROOT/artifacts.lock.json" \
|
||||
--python "$RELEASE_PYTHON" --npm "$RELEASE_NPM"
|
||||
|
||||
# Repeat --selected-repository for EVERY newly released unit, not just Core.
|
||||
"$RELEASE_PYTHON" "$RELEASE_TOOLS/release-catalog.py" full-registry \
|
||||
--workspace-root "$RELEASE_WORKSPACE" \
|
||||
--package-set "$ARTIFACT_ROOT/packages.json" \
|
||||
--package-lock "$ARTIFACT_ROOT/artifacts.lock.json" \
|
||||
--wheelhouse "$ARTIFACT_ROOT/wheels" --webui-packages "$ARTIFACT_ROOT/webui" \
|
||||
--output-dir "$RELEASE_CANDIDATE" --selected-repository govoplan-core \
|
||||
--catalog-signing-key known-key=/path/to/private/known-key.pem --json
|
||||
|
||||
"$RELEASE_PYTHON" "$RELEASE_TOOLS/release-catalog.py" publish-candidate \
|
||||
--workspace-root "$RELEASE_WORKSPACE" --candidate-dir "$RELEASE_CANDIDATE" \
|
||||
--channel stable --npm "$RELEASE_NPM" --build-web \
|
||||
--commit --tag --push --tag-name "catalog-v$RELEASE_VERSION" --json
|
||||
```
|
||||
|
||||
The last command is a strict non-mutating preview because `--apply` is absent.
|
||||
Review its output, then repeat it with `--apply` to publish. The website's locked
|
||||
build dependencies must already be installed before `--build-web`. The publisher
|
||||
sanitizes the build and Git environments and pushes the verified immutable
|
||||
website commit/tag. Source tag publication and registry package availability
|
||||
must be complete before candidate generation.
|
||||
|
||||
Source tags, registry packages, and a signed module catalog do not imply that a
|
||||
new runtime distribution exists. While runtime images are held, leave the Meta
|
||||
Gitea runtime Release held too: a normal source-only Release can replace Gitea's
|
||||
`releases/latest` discovery result despite having no deployment assets. The
|
||||
deployer still requires an explicit signed manifest, digest, and trusted
|
||||
keyring; it does not deploy a tag or module catalog directly.
|
||||
|
||||
## Betriebsablauf (DE)
|
||||
|
||||
`release-catalog.py full-registry` übernimmt den vollständigen Paketbestand,
|
||||
die Registry-Sperrdatei, das Wheel-Verzeichnis, die WebUI-Archive und den
|
||||
konfigurierten Signaturschlüssel. Zuerst mit
|
||||
`generate-release-package-set.py --profile full` die exakten Meta-Paketversionen
|
||||
ermitteln und mit `resolve-package-artifacts.py` die veröffentlichten Artefakte
|
||||
herunterladen. Lokal neu gebaute Wheels sind kein Ersatz. Der Kandidat prüft
|
||||
Paketidentitäten, Dateigrößen und Hashes und erzeugt alle Einträge aus den
|
||||
unveränderlichen getaggten Manifesten. Die Registry und ihre veröffentlichende
|
||||
CI bleiben eine Vertrauensgrundlage; dies ist kein unabhängiger Nachweis eines
|
||||
reproduzierbaren Builds aus dem Quellcode.
|
||||
|
||||
Jedes neu veröffentlichte Repository wird mit `--selected-repository`
|
||||
angegeben. Nur diese Auswahl muss mit dem sauberen, versionsgleichen HEAD eines
|
||||
benannten Branches und dem annotierten lokalen und entfernten Tag übereinstimmen.
|
||||
Unveränderte Pakete behalten ihren ursprünglichen Tag, auch wenn auf `main`
|
||||
bereits eine spätere Workflow-Korrektur liegt. Alle Quelladressen müssen dem
|
||||
registrierten Ursprung entsprechen; Commit und annotiertes Tag-Objekt werden
|
||||
für jeden Eintrag gebunden. Git-Ersetzungsobjekte oder fremde Git-Konfiguration
|
||||
können dabei keinen anderen Manifestbaum unterschieben.
|
||||
|
||||
Vor dem Signieren werden der bestehende Website-Katalog und sein Schlüsselbund
|
||||
geprüft. Ein alter Katalog ohne signierten Schlüsselbund-Hash darf ausschließlich
|
||||
durch diesen vollständigen Neuaufbau migriert werden: Seine Signatur muss gültig
|
||||
sein und der gesamte Schlüsselbund exakt den konfigurierten bekannten Signierern
|
||||
entsprechen. Alte Einträge oder Artefakt-Hashes werden nicht übernommen. Der neue
|
||||
Katalog bindet den unveränderten Schlüsselbund-Hash; ein Schlüsselwechsel bleibt
|
||||
ein eigener geprüfter Vorgang. Selektive Kandidaten verlangen weiterhin einen
|
||||
bereits gebundenen Schlüsselbund. Kandidaten werden nur in neuen privaten
|
||||
Verzeichnissen erzeugt und niemals überschrieben.
|
||||
|
||||
Das obige Befehlsbeispiel wird auf dem vertrauenswürdigen Host ausgeführt. Dafür
|
||||
die private Python-Umgebung und einen absoluten `--npm`-Pfad zu Node 22 verwenden;
|
||||
unter Flatpak die Host-Werkzeuge über `flatpak-spawn --host` aufrufen. Die
|
||||
gesperrten Website-Build-Abhängigkeiten vorher installieren. Keine
|
||||
Vertrauensprüfung umgehen und keine globalen Rechte ändern. Die Artefaktordner
|
||||
müssen privat und bei der Auflösung leer sein. Vor der Kandidatenerzeugung
|
||||
müssen Quell-Tags und Registry-Pakete vollständig veröffentlicht sein.
|
||||
|
||||
Die Veröffentlichung zunächst mit `publish-candidate --commit --tag --push
|
||||
--build-web` ohne `--apply` prüfen und erst nach Prüfung mit `--apply` ausführen.
|
||||
Solange Laufzeit-Images zurückgestellt sind, bleibt auch das Meta-Gitea-Runtime-
|
||||
Release zurückgestellt: Ein reines Quellcode-Release könnte sonst als neuestes
|
||||
Release erscheinen. Eine Installation benötigt weiterhin ein signiertes
|
||||
Laufzeitmanifest, dessen Digest und einen explizit vertrauenswürdigen Schlüsselbund.
|
||||
@@ -0,0 +1,633 @@
|
||||
# Installation And Deployment Architecture
|
||||
|
||||
## Goal
|
||||
|
||||
A supported GovOPlaN installation starts with one downloaded, verified
|
||||
bootstrap artifact. The administrator answers a bounded set of questions and
|
||||
receives a working base system. Re-running the same tool repairs or
|
||||
reconfigures that installation instead of creating unrelated state.
|
||||
|
||||
The canonical product journey remains
|
||||
[System Administrator Lifecycle User Story](../strategy/SYSTEM_ADMINISTRATOR_LIFECYCLE_USER_STORY.md).
|
||||
This document defines the deployer boundary and the first executable slice.
|
||||
The execution, topology, component-ownership and assurance modes are defined
|
||||
canonically in [Deployment Profiles](DEPLOYMENT_PROFILES.md). In particular,
|
||||
the editable production-like developer launcher is distinct from both an
|
||||
immutable Compose rehearsal and a supported one-host production deployment.
|
||||
|
||||
## First Executable Slice
|
||||
|
||||
`tools/deployment/govoplan-deploy.py` is a standard-library-only deployment
|
||||
compiler and reconciler. It can be tested without installing GovOPlaN itself.
|
||||
|
||||
It currently supports:
|
||||
|
||||
- evaluation and self-hosted profiles;
|
||||
- managed or external PostgreSQL;
|
||||
- managed, external, or evaluation-only disabled Redis;
|
||||
- disabled mail, an external relay declaration, or an evaluation-only
|
||||
GreenMail service;
|
||||
- durable local file storage, managed single-node Garage S3, or external
|
||||
S3-compatible storage;
|
||||
- an explicit HAProxy service that load-balances configured WebUI and API
|
||||
replicas without access to the Docker socket;
|
||||
- declarative API, WebUI, and worker replica counts while keeping migrations
|
||||
and the scheduler singleton;
|
||||
- Core, base, or full initial module selections;
|
||||
- deterministic Compose JSON accepted by Compose v2;
|
||||
- generated secrets stored in a private `0600` file;
|
||||
- service-specific environment allowlists so infrastructure containers do not
|
||||
receive unrelated application credentials;
|
||||
- plan, render, doctor, status, apply, Kubernetes export, operation history,
|
||||
and bounded recovery commands;
|
||||
- an installation lock, migration-before-start ordering, readiness polling,
|
||||
and an applied-state receipt;
|
||||
- a durable hash-chained deployment journal captured before runtime mutation;
|
||||
- PostgreSQL advisory serialization for Core and module migrations;
|
||||
- runtime initialization that waits for exact configured migration heads
|
||||
without mutating schema;
|
||||
- runtime node registration, heartbeats, drain state, and a fenced scheduler;
|
||||
- idempotent reconfiguration that preserves generated secrets;
|
||||
- a keyed environment fingerprint that detects private binding changes without
|
||||
writing secret values to plans or receipts;
|
||||
- host CPU, memory, disk, entropy, architecture, Docker daemon, Compose,
|
||||
listen-port, and external endpoint preflight checks;
|
||||
- service removal without implicit data-volume deletion.
|
||||
|
||||
Create a local evaluation bundle:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/deployment/govoplan-deploy.py init \
|
||||
--directory /tmp/govoplan-evaluation \
|
||||
--profile evaluation \
|
||||
--postgres managed \
|
||||
--redis managed \
|
||||
--storage garage \
|
||||
--mail test-mail \
|
||||
--api-replicas 2 \
|
||||
--web-replicas 2 \
|
||||
--worker-replicas 2 \
|
||||
--module-set base
|
||||
```
|
||||
|
||||
Inspect the generated intent and host requirements:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/deployment/govoplan-deploy.py doctor \
|
||||
--directory /tmp/govoplan-evaluation
|
||||
```
|
||||
|
||||
Change a component without rotating existing generated secrets:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/deployment/govoplan-deploy.py configure \
|
||||
--directory /tmp/govoplan-evaluation \
|
||||
--redis external \
|
||||
--redis-url 'rediss://:password@redis.example.org:6379/0'
|
||||
```
|
||||
|
||||
The private installation directory contains:
|
||||
|
||||
| File | Purpose |
|
||||
| --- | --- |
|
||||
| `installation.json` | Versioned, non-secret desired state |
|
||||
| `secrets.env` | Deployment-local secrets and external service bindings |
|
||||
| `compose.json` | Deterministic generated Compose definition |
|
||||
| `garage.toml` | Non-secret managed Garage server configuration |
|
||||
| `load-balancer.cfg` | Non-secret HAProxy WebUI/API discovery configuration |
|
||||
| `Caddyfile` | Non-secret managed-ingress route and ACME policy |
|
||||
| `existing-proxy.json` | Exact upstream, trusted-source, header, and health contract for an operator-owned proxy |
|
||||
| `plan.json` | Latest desired-state diff and readiness findings |
|
||||
| `receipt.json` | Last successfully applied immutable identities |
|
||||
| `infrastructure-capabilities.json` | Deterministic non-secret capability states, endpoint metadata, secret references, consumers, and resumable post-install tasks |
|
||||
| `infrastructure-dependency-inventory.json` | Owner-only, short-lived Ops evidence of actual module-owned configuration and data that depend on infrastructure capabilities |
|
||||
| `distribution-manifest.json` | Canonical signed runtime/image selection adopted by the installer |
|
||||
| `distribution-keyring.json` | Explicitly installed public trust anchor for runtime releases |
|
||||
| `backup-evidence.json` | Signed provider-neutral coordinated backup and isolated-restore receipt |
|
||||
| `backup-keyring.json` | Explicit public trust anchor for backup evidence authorities |
|
||||
| `backup-verification.json` | Sanitized local verification/adoption receipt |
|
||||
| `applied-state/` | Checksum-verified snapshot of the last healthy deployment bundle |
|
||||
| `operations/<id>/` | Private hash-chained deployment progress and recovery evidence |
|
||||
| `kubernetes.json` | Optional stateless multi-host Kubernetes export |
|
||||
| `.deployment.lock` | Same-host operation exclusion |
|
||||
|
||||
The specification contract is
|
||||
[`installation-spec.schema.json`](../installation-spec.schema.json).
|
||||
|
||||
The API, workers, scheduler, and Ops read the capability receipt through the
|
||||
same bounded Core validator. Configuration-package providers receive that typed
|
||||
receipt in preflight context. Mail uses `mail.smtp` to offer an idempotent SMTP
|
||||
profile plan and accepts only an existing credential-envelope reference; Files
|
||||
uses `files.storage` to prove that the deployment-owned local/S3 runtime binding
|
||||
already matches. Files deliberately blocks drift instead of rewriting process
|
||||
environment or initiating an implicit object migration. Invalid receipts fail
|
||||
closed, while a deployment without a mounted receipt continues to run but
|
||||
cannot apply receipt-bound configuration fragments.
|
||||
|
||||
Enabled modules may also register a Core infrastructure-dependency provider.
|
||||
The authorized Ops endpoint aggregates those providers without importing their
|
||||
tables. Mail reports persisted SMTP endpoints, credential-binding counts and
|
||||
legacy profiles; Files reports its runtime storage binding plus persisted blob
|
||||
counts and byte totals grouped by backend. Ops reports the active PostgreSQL,
|
||||
Redis coordination, ingress, and load-balancing runtime bindings. Provider output contains stable
|
||||
references, bounded numeric metrics and required migration actions, never
|
||||
credentials, endpoint secrets, tenant identifiers or file keys. A provider
|
||||
failure makes the entire inventory incomplete.
|
||||
|
||||
Build the same dependency-free tool as one downloadable artifact:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/deployment/build-deployer-zipapp.py \
|
||||
--output /tmp/govoplan-deploy.pyz
|
||||
python /tmp/govoplan-deploy.pyz --help
|
||||
```
|
||||
|
||||
To exercise reconciliation with locally available evaluation images:
|
||||
|
||||
```sh
|
||||
python /tmp/govoplan-deploy.pyz init \
|
||||
--non-interactive \
|
||||
--directory /tmp/govoplan-evaluation \
|
||||
--profile evaluation \
|
||||
--api-image local/govoplan-api:test \
|
||||
--web-image local/govoplan-web:test
|
||||
python /tmp/govoplan-deploy.pyz apply \
|
||||
--directory /tmp/govoplan-evaluation \
|
||||
--allow-unverified-images \
|
||||
--skip-pull
|
||||
```
|
||||
|
||||
Those images must already contain the selected module set. The override exists
|
||||
only to exercise local orchestration before release artifacts exist; it is
|
||||
rejected for `self-hosted`.
|
||||
|
||||
## Runtime Distribution Boundary
|
||||
|
||||
The protected `Runtime Distribution` workflow builds GovOPlaN wheels first,
|
||||
resolves architecture-specific third-party wheels into offline wheelhouses, and
|
||||
then assembles the API images with `pip --no-index`. The target host never
|
||||
clones Git repositories and neither runtime image performs network package
|
||||
installation. Separate amd64/arm64 API and WebUI images are joined into OCI
|
||||
indexes and run as non-root identities. The release assets include CycloneDX
|
||||
application SBOMs, SLSA-style provenance, exact composition evidence, the
|
||||
single-file deployer, its detached Ed25519 signature, and a signed, expiring
|
||||
distribution manifest. Evidence generation and signing run through the
|
||||
workflow's isolated release Python environment so their cryptographic tooling
|
||||
is explicit and independent of packages preinstalled in the Actions runner.
|
||||
The API image points Core at the migration scripts installed from the verified
|
||||
wheel under `/opt/govoplan/runtime/govoplan_core_runtime`; migrations therefore
|
||||
do not depend on a source checkout or the build host's Python installation
|
||||
scheme.
|
||||
Before publication, the exact amd64 and arm64 image manifests each run release
|
||||
migrations against the pinned PostgreSQL image, reach API and WebUI readiness
|
||||
as non-root/read-only processes, and complete a task through the pinned Redis
|
||||
image and packaged worker. Sanitized per-platform smoke receipts are retained
|
||||
as immutable release assets.
|
||||
PostgreSQL and Redis indexes are resolved to untagged platform-child digests
|
||||
before each smoke run. This keeps the evidence architecture-specific and
|
||||
avoids retargeting one local Docker tag between incompatible platforms.
|
||||
The CI host registers arm64 execution with an explicitly supplied,
|
||||
digest-pinned `tonistiigi/binfmt` image immediately before the smoke. This
|
||||
privileged helper is confined to the release runner and is never part of a
|
||||
GovOPlaN target deployment or its runtime image set.
|
||||
Because QEMU user-mode execution triggers Redis's arm64 host-kernel COW guard,
|
||||
the arm64 smoke suppresses only `ARM64-COW-BUG` while persistence, snapshots,
|
||||
and append-only files are disabled. Target Redis services never inherit this
|
||||
test-only option.
|
||||
The smoke also proves a bounded post-migration table contract and aborts as
|
||||
soon as a required container exits, rather than allowing a dead process to
|
||||
consume the full readiness timeout.
|
||||
|
||||
Ingress acceptance streams generated configuration into Docker-managed
|
||||
volumes before starting the read-only containers. It therefore also works when
|
||||
an Actions job reaches a host or remote Docker daemon through a mounted socket;
|
||||
the drill never assumes that a job-container path is visible to that daemon.
|
||||
The drill allocates explicit loopback-only host ports and verifies Docker's
|
||||
host binding configuration, avoiding daemon-specific random-port shorthand
|
||||
behavior. Because an Actions job and deployment containers may be Docker
|
||||
siblings, functional HTTP/TLS checks run from the digest-pinned API image on
|
||||
the deployment network instead of assuming the Docker host is job-local.
|
||||
The dispatch-only `Runtime Ingress Drill` workflow exposes the same bounded
|
||||
check independently so ingress changes can be diagnosed before an immutable
|
||||
runtime publication; it accepts only digest-pinned Caddy, HAProxy, and API
|
||||
images and has no push trigger.
|
||||
The official Caddy binary carries the `NET_BIND_SERVICE` file capability. The
|
||||
managed-ingress container therefore drops every capability and adds back only
|
||||
`NET_BIND_SERVICE`; otherwise Linux rejects the binary at `execve` before its
|
||||
high-port configuration can start. `no-new-privileges`, a read-only root
|
||||
filesystem, and non-privileged container ports remain enforced.
|
||||
The bounded setup helper writes only generated public configuration as root so
|
||||
it can initialize a new volume; the actual HAProxy process retains the image's
|
||||
non-root identity and runs read-only with all capabilities dropped.
|
||||
|
||||
The manifest contract is
|
||||
[`runtime-distribution-manifest.schema.json`](../runtime-distribution-manifest.schema.json),
|
||||
and its separately distributed trust-anchor contract is
|
||||
[`runtime-distribution-keyring.schema.json`](../runtime-distribution-keyring.schema.json).
|
||||
Publication is immutable: an existing Gitea release asset must have the same
|
||||
size and SHA-256 digest or publication fails.
|
||||
|
||||
Adopt a downloaded or prefetched release only after obtaining the manifest
|
||||
digest and trusted keyring through the documented independent channel:
|
||||
|
||||
```sh
|
||||
python3 govoplan-deploy.pyz verify-release \
|
||||
--directory /srv/govoplan/installation \
|
||||
--manifest ./distribution-manifest.json \
|
||||
--manifest-sha256 "$(cut -d' ' -f1 distribution-manifest.json.sha256)" \
|
||||
--trusted-keyring ./distribution-keyring.json \
|
||||
--adopt
|
||||
```
|
||||
|
||||
`doctor` and `apply` rehash both stored files, re-run OpenSSL Ed25519
|
||||
verification, enforce channel/expiry/revocation, compare every selected image,
|
||||
and prove that all enabled module ids occur in the signed image composition.
|
||||
An offline image index can bind prefetched OCI archives to the same exact image
|
||||
references and archive hashes; mutable tags or incomplete bundles are rejected.
|
||||
|
||||
## Current Production Gates
|
||||
|
||||
The first immutable production-distribution baseline is published as
|
||||
[`v0.1.14`](https://git.add-ideas.de/GovOPlaN/govoplan/releases/tag/v0.1.14)
|
||||
from source commit `1f039dd39c1ce2672f4978c8abc6dff862ef1445`. Runtime
|
||||
Distribution [run #459](https://git.add-ideas.de/GovOPlaN/govoplan/actions/runs/459)
|
||||
proved migrations, schema compatibility, non-root API/Web readiness, and worker
|
||||
delivery/shutdown on both `linux/amd64` and `linux/arm64`. Its signed manifest
|
||||
has SHA-256
|
||||
`d703267e01855dee63200cb20921c91c3f95fbff550c8ca76e9a35cba3f69109`
|
||||
and pins these runtime indexes:
|
||||
|
||||
- API: `git.add-ideas.de/govoplan/runtime-api@sha256:197ed01790986f2bc927eaa5d8348fa118702e5d2dc05feb851fc2643c23764a`
|
||||
- WebUI: `git.add-ideas.de/govoplan/runtime-web@sha256:e936cca124f1fad29a067834cf17627d4c236410fdc3fa129e0ccb26b8193812`
|
||||
|
||||
The signed bootstrap has SHA-256
|
||||
`1ff946fba82b0895d153b23352d06e30fe18388450dfd37fed6fb9912310efc5`
|
||||
and key id `runtime-distribution-2026-01`. The managed-ingress boundary passed
|
||||
the same publication run and the independently dispatchable Runtime Ingress
|
||||
Drill [run #458](https://git.add-ideas.de/GovOPlaN/govoplan/actions/runs/458).
|
||||
Every later release must renew this evidence; the following target-specific
|
||||
gates remain:
|
||||
|
||||
1. **First administrator.** Production needs a one-time, restricted enrollment
|
||||
identity. The development bootstrap must not be enabled in production.
|
||||
2. **Image/module composition.** The deployer enforces the signed
|
||||
composition. A selected module not shipped by that release cannot be
|
||||
enabled.
|
||||
3. **Deployment agent.** Web updates need a separate privileged reconciler with
|
||||
a typed command allowlist. The API and browser must never receive the Docker
|
||||
socket or arbitrary shell access.
|
||||
4. **Target reachability evidence.** Managed Caddy ingress and the
|
||||
existing-proxy contract are implemented. A production claim still requires
|
||||
running `doctor` from the target host after public DNS/firewall changes and
|
||||
retaining public TLS/readiness evidence for that deployment.
|
||||
|
||||
`apply --allow-unverified-images` is therefore restricted to the evaluation
|
||||
profile. It explicitly acknowledges both mutable image identities and
|
||||
unverified image/module composition. It is a local test escape hatch, not a
|
||||
production setting.
|
||||
|
||||
## Component Choices
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
`managed` creates a persistent PostgreSQL container and private generated
|
||||
credentials. `external` requires an explicit `DATABASE_URL`; switching from
|
||||
managed to external cannot reuse the old `postgres` Docker hostname
|
||||
accidentally.
|
||||
|
||||
Interactive entry hides external URLs because they commonly contain
|
||||
credentials. For unattended automation, provide them through a protected
|
||||
operator mechanism and avoid storing secret-bearing flags in shell history.
|
||||
|
||||
Production policy should support external managed databases and local managed
|
||||
PostgreSQL equally at the application boundary. Backup, point-in-time recovery,
|
||||
high availability, and major-version upgrades remain deployment properties.
|
||||
|
||||
### Redis
|
||||
|
||||
`managed` creates an authenticated, append-only Redis container. `external`
|
||||
requires an explicit `REDIS_URL`. `disabled` is evaluation-only and disables
|
||||
workers while recording the single-process login-throttle risk acknowledgement.
|
||||
|
||||
`doctor` performs a bounded TCP connection check for external PostgreSQL,
|
||||
Redis, and S3 endpoints. This verifies DNS, routing, and that the port accepts a
|
||||
connection; it is not an authentication or semantic health check.
|
||||
|
||||
Production base installations include Redis because durable queues, distributed
|
||||
throttling, notifications, scheduled work, and transactional event delivery
|
||||
must survive API restarts.
|
||||
|
||||
### Mail
|
||||
|
||||
The first slice distinguishes:
|
||||
|
||||
- `disabled`;
|
||||
- `external-relay`, which records the infrastructure decision but leaves Mail
|
||||
server/credential creation as a visible post-install task;
|
||||
- `test-mail`, an evaluation-only GreenMail service.
|
||||
|
||||
A bundled production mail server is intentionally not a default. Operating one
|
||||
requires DNS, reverse DNS, TLS, DKIM, SPF, DMARC, reputation, abuse handling,
|
||||
queue monitoring, and upgrade policy. A later profile may support an
|
||||
operator-selected MTA/relay, but it must expose these requirements rather than
|
||||
presenting a container as a complete mail service.
|
||||
|
||||
### File Storage
|
||||
|
||||
`local` uses a durable Compose volume and is appropriate for one-host
|
||||
installations. `garage` provisions Garage 2.3 in its supported single-node
|
||||
bootstrap mode, generates a private application key and bucket, and connects
|
||||
the Files S3 backend to the exact installer-owned internal endpoint. The
|
||||
managed trust marker cannot authorize another S3 host.
|
||||
Garage metadata and object data use separate persistent volumes. `s3` requires
|
||||
an external endpoint, region, access key, secret key, and bucket values.
|
||||
Self-hosted external S3 endpoints must be clean HTTPS origins. The generated
|
||||
runtime explicitly sets `FILE_STORAGE_S3_ENDPOINT_TRUSTED=true` for that
|
||||
operator-selected endpoint. The trust flag is not accepted for local storage
|
||||
and cannot be combined with installer-managed Garage trust.
|
||||
|
||||
Local storage must be included in backup and restore drills. Horizontal API or
|
||||
worker scale-out requires shared/object storage. The managed Garage profile is
|
||||
persistent but has no data redundancy; availability-sensitive installations
|
||||
must use a tested multi-node Garage cluster or another external S3 service.
|
||||
|
||||
### Load Balancing And Replicas
|
||||
|
||||
The generated Compose topology publishes only `load-balancer` for local or
|
||||
existing-proxy profiles. With managed ingress, only Caddy publishes host ports
|
||||
and HAProxy remains private. HAProxy uses
|
||||
Docker DNS service discovery to distribute public traffic across WebUI replicas
|
||||
and WebUI API proxy traffic across API replicas. The WebUI and API services do
|
||||
not publish host ports. HAProxy has no Docker socket and discovers only the
|
||||
bounded replica slots rendered into `load-balancer.cfg`.
|
||||
|
||||
New installation specifications default to HAProxy `3.2.23-alpine`, pinned to
|
||||
registry index `sha256:6343ce34a132a5dceaa24767d739df2bd519f8f7c1079ae39e4821334e8eb42e`.
|
||||
This patch remains on HAProxy 3.2 LTS and Alpine 3.24.1. Loading an existing
|
||||
specification preserves its explicit image; it does not perform an upgrade.
|
||||
The [runtime remediation evidence](../security/RUNTIME_IMAGE_REMEDIATION_2026-09-08.md)
|
||||
records both architecture scans and the pending binary/configuration, runtime,
|
||||
inventory and final-image checks. The source default is not a release approval:
|
||||
runtime publication remains held in Meta #52.
|
||||
|
||||
Replica counts are desired state:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/deployment/govoplan-deploy.py configure \
|
||||
--directory /tmp/govoplan-evaluation \
|
||||
--api-replicas 3 \
|
||||
--web-replicas 2 \
|
||||
--worker-replicas 4
|
||||
./.venv/bin/python tools/deployment/govoplan-deploy.py apply \
|
||||
--directory /tmp/govoplan-evaluation
|
||||
```
|
||||
|
||||
Workers are queue consumers, so they are scaled through Redis rather than put
|
||||
behind an HTTP load balancer. Migrations are serialized with a deployment-wide
|
||||
PostgreSQL advisory lock. The Celery scheduler is run under a renewable,
|
||||
fencing-token lease. Multiple API replicas are rejected when Redis is disabled
|
||||
because distributed throttling and queued work cannot then be shared correctly.
|
||||
|
||||
### Public Ingress And TLS
|
||||
|
||||
A self-hosted installation is fail-closed until one of these boundaries is
|
||||
selected:
|
||||
|
||||
- `existing-proxy` publishes HAProxy at `listen.address:listen.port` and emits
|
||||
`existing-proxy.json`. The operator-owned proxy must use the recorded host,
|
||||
upstream, and health paths. Only the exact CIDRs listed with repeated
|
||||
`--trusted-proxy-cidr` values may supply `X-Forwarded-*` headers. Public
|
||||
proxy addresses must be `/32` or `/128`; private ranges are limited to `/24`
|
||||
or narrower for IPv4 and `/64` or narrower for IPv6.
|
||||
- `managed` publishes Caddy on the selected HTTP/HTTPS ports, redirects HTTP to
|
||||
HTTPS, obtains and renews certificates through ACME, and keeps certificate
|
||||
material exclusively in the private `caddy-data` and `caddy-config` volumes.
|
||||
The application containers receive no ACME account or TLS private keys.
|
||||
|
||||
Example existing-proxy configuration:
|
||||
|
||||
```sh
|
||||
python govoplan-deploy.py configure \
|
||||
--directory /srv/govoplan \
|
||||
--ingress existing-proxy \
|
||||
--trusted-proxy-cidr 172.20.0.7/32
|
||||
```
|
||||
|
||||
Example managed configuration:
|
||||
|
||||
```sh
|
||||
python govoplan-deploy.py configure \
|
||||
--directory /srv/govoplan \
|
||||
--ingress managed \
|
||||
--acme-email operator@example.org
|
||||
```
|
||||
|
||||
Before managed ingress starts, public A/AAAA records must resolve to the target
|
||||
and inbound TCP 80/443 must reach it. Existing-proxy mode additionally requires
|
||||
the public proxy and valid certificate to be reachable before apply. After a
|
||||
successful receipt, `doctor` reports DNS resolution, certificate validity and
|
||||
remaining lifetime, public `/health/ready`, and the private HAProxy/WebUI path
|
||||
as separate checks. Reconfiguration retains the certificate volumes; bundle
|
||||
rollback never deletes or exposes their contents. Include both Caddy volumes
|
||||
in coordinated backup and restore evidence.
|
||||
|
||||
This is same-host scaling. Docker Compose uses a bridge network and does not
|
||||
place containers on another machine. See
|
||||
[Scaling And Multi-Host Deployment](SCALING_AND_MULTI_HOST_DEPLOYMENT.md) for
|
||||
the supported topology and promotion path.
|
||||
|
||||
## Reconfiguration Semantics
|
||||
|
||||
`installation.json` is desired state. `receipt.json` is the last successfully
|
||||
applied state. `plan` compares their canonical hashes, service sets, and
|
||||
infrastructure capability projections.
|
||||
|
||||
- Adding a managed component creates its service and persistent volume.
|
||||
- Removing a component removes its service container on apply.
|
||||
- Reconfiguring, replacing or removing a capability adds a review action that
|
||||
names the prior and desired state/source, declared consumers, actual
|
||||
provider-reported dependency records and each required migration action.
|
||||
- The deployer blocks that change when provider inventory is missing,
|
||||
incomplete, more than five minutes old, from another installation, timestamped
|
||||
in the future, or does not cover every impacted capability. It never treats
|
||||
installer-declared consumers as proof that persisted module state is absent.
|
||||
- The inventory reports impact; it does not migrate or delete module-owned
|
||||
configuration or data. Complete the reported preparation and collect again
|
||||
immediately before apply.
|
||||
- Volumes are retained by default; deleting data requires a separate,
|
||||
deliberately destructive workflow.
|
||||
- Existing generated credentials are retained unless an explicit future rotate
|
||||
operation is requested.
|
||||
- Private configuration changes are represented by a keyed fingerprint in the
|
||||
plan and receipt; plaintext values are never copied there.
|
||||
- Capability documents contain sanitized scheme/host/port metadata and stable
|
||||
`env:` references only. Credential values and secret-bearing URLs remain in
|
||||
`secrets.env` or module-owned credential envelopes.
|
||||
- Managed-to-external transitions require the new endpoint in the same
|
||||
operation.
|
||||
- Migrations run as a one-shot service before API/worker replacement.
|
||||
- API, worker, and scheduler start commands wait for exact configured migration
|
||||
heads; only the migration command is permitted to change schema.
|
||||
- API and worker replicas register their software/module composition and
|
||||
heartbeat in PostgreSQL. Ops can request and cancel a node drain.
|
||||
- The first upgrade from a direct WebUI host port stops that legacy WebUI
|
||||
container immediately before HAProxy claims the same endpoint.
|
||||
- Health must recover before a new receipt and applied-state snapshot are
|
||||
committed.
|
||||
|
||||
Compose mounts the capability document read-only into API and worker runtime
|
||||
containers. The Kubernetes export projects the same document through a
|
||||
dedicated ConfigMap and read-only file mount. Ops validates the bounded schema
|
||||
before displaying configured, externally supplied, available-unconfigured, or
|
||||
unavailable states and any pending post-install tasks.
|
||||
|
||||
Collect current dependency evidence with an API key whose principal has one of
|
||||
the Ops read scopes:
|
||||
|
||||
```sh
|
||||
export GOVOPLAN_OPS_API_KEY='<short-lived operator API key>'
|
||||
python3 govoplan-deploy.pyz collect-infrastructure-inventory \
|
||||
--directory /srv/govoplan/example
|
||||
python3 govoplan-deploy.pyz doctor \
|
||||
--directory /srv/govoplan/example
|
||||
python3 govoplan-deploy.pyz apply \
|
||||
--directory /srv/govoplan/example
|
||||
unset GOVOPLAN_OPS_API_KEY
|
||||
```
|
||||
|
||||
The command defaults to
|
||||
`<public-url>/api/v1/ops/infrastructure/dependencies`; `--ops-url` may select an
|
||||
explicit HTTPS endpoint (plain HTTP is accepted only on loopback). `apply`
|
||||
refreshes the inventory automatically when `GOVOPLAN_OPS_API_KEY` is present.
|
||||
Otherwise an already collected, current inventory may be used. The API key is
|
||||
sent only as `X-API-Key`, is never written to the bundle, and the inventory file
|
||||
is owner-readable only. Because it contains operational references and counts,
|
||||
handle it as private evidence even though it contains no secret material.
|
||||
|
||||
Every apply operation is journalled before image pulls or runtime mutation. A
|
||||
failure before migration may restore a verified previous bundle. Once migration
|
||||
starts, recovery is forward-only unless an independently verified database
|
||||
backup is restored. See
|
||||
[Recovery And Rollback Guarantees](RECOVERY_AND_ROLLBACK_GUARANTEES.md).
|
||||
|
||||
Production updates still need operator/provider-created coordinated backup and
|
||||
restore evidence, a database compatibility declaration, and a
|
||||
deployment-specific drain policy. The deployer now verifies and enforces the
|
||||
signed evidence before migration, but does not manufacture backups or receive
|
||||
provider administration credentials. See
|
||||
[Backup And Restore Evidence](BACKUP_AND_RESTORE_EVIDENCE.md).
|
||||
|
||||
## Stateless Kubernetes Runtime
|
||||
|
||||
`render-kubernetes` exports the application tier for a standard orchestrator.
|
||||
It requires external PostgreSQL, Redis, and S3 and emits no stateful service or
|
||||
secret value:
|
||||
|
||||
```sh
|
||||
python tools/deployment/govoplan-deploy.py render-kubernetes \
|
||||
--directory /srv/govoplan/default \
|
||||
--namespace govoplan \
|
||||
--secret-name govoplan-runtime
|
||||
```
|
||||
|
||||
The output includes a release-specific migration Job, database-head wait init
|
||||
containers, API readiness/liveness probes, rolling Deployments, Services, Pod
|
||||
disruption budgets, a tokenless ServiceAccount, and one fenced scheduler. Apply
|
||||
the named Secret through the cluster's secret manager and review ingress proxy
|
||||
CIDRs before deployment. A release-changing export requires adopted backup
|
||||
evidence and carries only its sanitized digest and identifiers as Job
|
||||
annotations. Detailed rollout and scaling rules live in
|
||||
[Scaling And Multi-Host Deployment](SCALING_AND_MULTI_HOST_DEPLOYMENT.md).
|
||||
|
||||
## Recovery Commands
|
||||
|
||||
List durable deployment operations:
|
||||
|
||||
```sh
|
||||
python tools/deployment/govoplan-deploy.py operations \
|
||||
--directory /srv/govoplan/default
|
||||
```
|
||||
|
||||
Recover a selected failed operation after reviewing its stage evidence:
|
||||
|
||||
```sh
|
||||
python tools/deployment/govoplan-deploy.py recover \
|
||||
--directory /srv/govoplan/default \
|
||||
--operation-id <operation-id>
|
||||
```
|
||||
|
||||
The command reports whether it restored the pre-migration applied bundle,
|
||||
requires forward recovery, or needs manual intervention. Add `--apply` only
|
||||
after that decision has been reviewed.
|
||||
|
||||
## Web Update Boundary
|
||||
|
||||
The intended update path is:
|
||||
|
||||
1. Ops reads the non-secret installation receipt and reports management mode,
|
||||
current release, component health, and update availability.
|
||||
2. An authorized administrator asks Core to create a typed deployment request,
|
||||
for example `reconcile_release` or `rollback_release`.
|
||||
3. Core persists the reviewed immutable plan, actor, expected current receipt,
|
||||
and idempotency key.
|
||||
4. A separately deployed, narrow deployment agent claims the request.
|
||||
5. The agent verifies signatures/digests, acquires a fenced deployment lock,
|
||||
backs up, pulls, migrates, reconciles, probes health, and writes evidence.
|
||||
6. Ops presents durable progress and the resulting receipt.
|
||||
|
||||
The agent owns container-runtime access. It accepts no command strings from the
|
||||
browser and has no domain-data permissions. Installations managed by Kubernetes,
|
||||
systemd, or another external orchestrator expose read-only status and an export
|
||||
of the reviewed update recipe instead of a non-functional update button.
|
||||
|
||||
## Distribution Workflow
|
||||
|
||||
The downloadable entry point is a reproducible release asset: sorted source
|
||||
paths, fixed ZIP metadata, fixed compression settings, and identical source
|
||||
bytes produce an identical zipapp regardless of checkout timestamps. Obtain the
|
||||
zipapp, detached signature, checksum, and trusted public keyring through
|
||||
independently authenticated paths before execution:
|
||||
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 --fail --location \
|
||||
https://git.add-ideas.de/GovOPlaN/govoplan/releases/download/vX.Y.Z/govoplan-deploy.pyz \
|
||||
--output govoplan-deploy.pyz
|
||||
sha256sum --check govoplan-deploy.pyz.sha256
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
keyring = json.loads(Path("distribution-keyring.json").read_text())
|
||||
active = [key for key in keyring["keys"] if key["status"] == "active"]
|
||||
if len(active) != 1:
|
||||
raise SystemExit("expected exactly one active runtime release key")
|
||||
Path("runtime-release-public.pem").write_text(active[0]["public_key_pem"])
|
||||
PY
|
||||
openssl pkeyutl -verify -pubin -inkey runtime-release-public.pem -rawin \
|
||||
-in govoplan-deploy.pyz -sigfile govoplan-deploy.pyz.sig
|
||||
python3 govoplan-deploy.pyz init
|
||||
```
|
||||
|
||||
The zipapp has no GovOPlaN package dependency. It accepts a bounded HTTPS
|
||||
manifest or a prefetched file, requires an independently supplied SHA-256
|
||||
digest and explicit trusted keyring, and executes OpenSSL with a fixed argument
|
||||
vector for Ed25519 verification. It never evaluates downloaded shell text or
|
||||
accepts an arbitrary command string.
|
||||
|
||||
## Verification
|
||||
|
||||
Run the focused tests:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python -m unittest -v tests.test_deployment_installer
|
||||
```
|
||||
|
||||
The tests cover signed release adoption, tamper/expiry/revocation/unknown-key
|
||||
rejection, architecture composition, offline image integrity, profile
|
||||
restrictions, secret persistence, external endpoint
|
||||
requirements, managed Garage bootstrap, S3 policy, replica validation, HAProxy
|
||||
discovery configuration, Compose service selection, secret non-disclosure,
|
||||
service-specific environment isolation, private file modes, external endpoint
|
||||
preflight, first-plan generation, apply ordering, receipt idempotency,
|
||||
hash-chained recovery journals, migration recovery boundaries, and stateless
|
||||
Kubernetes rendering.
|
||||
@@ -0,0 +1,91 @@
|
||||
# Integrity and performance source release — September 2026
|
||||
|
||||
Coordinated tracking: [Core #298](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/298).
|
||||
This source publication advances only the affected packages; it is not a signed
|
||||
catalog publication, runtime-image release, or remote production rollout.
|
||||
|
||||
## Package set
|
||||
|
||||
| Package | Version |
|
||||
| --- | --- |
|
||||
| Core / developer meta-package | 0.1.46 |
|
||||
| Addresses | 0.1.23 |
|
||||
| Calendar | 0.1.24 |
|
||||
| Campaign | 0.1.29 |
|
||||
| Cases | 0.1.25 |
|
||||
| Committee | 0.1.22 |
|
||||
| Connectors | 0.1.27 |
|
||||
| Dataflow | 0.1.25 |
|
||||
| Datasources | 0.1.26 |
|
||||
| Files | 0.1.27 |
|
||||
| Forms Runtime | 0.1.22 |
|
||||
| IDM | 0.1.26 |
|
||||
| Mail | 0.1.28 |
|
||||
| Reporting | 0.1.22 |
|
||||
| Tickets | 0.1.23 |
|
||||
|
||||
Consumers of new Core helpers require Core 0.1.46 or later. Dataflow and
|
||||
Datasources also require the matching Core WebUI contract. The release manifests,
|
||||
immutable Git lock, and developer package describe this coordinated composition.
|
||||
Unchanged packages retain their independent versions.
|
||||
|
||||
## Database and data integrity
|
||||
|
||||
The reviewed additive heads are Connectors `d2a4c6e8f0b1`, Datasources
|
||||
`e2b8d4a0f6c3`, Files `a2b3c4d5e701`, and Mail `b5d6e7f8091a`.
|
||||
Back up the target deployment and rehearse its normal upgrade before rollout.
|
||||
Never downgrade away retained CSV originals without a separate recovery plan.
|
||||
Files preserves historical duplicate copies; Mail leaves legacy maildrop identity
|
||||
unset rather than guessing an account. A schema upgrade does not authorize POP3
|
||||
retrieval/reconciliation, provider deletion, or message resending.
|
||||
|
||||
Development startup may automatically apply pending migrations after a watched
|
||||
source change. Therefore, inspect actual Alembic heads and schema before assuming
|
||||
a live development database is still at its pre-change state. A backup taken
|
||||
after such an upgrade is a current-state recovery copy, not a pre-upgrade backup.
|
||||
|
||||
On 8 September, the local PostgreSQL development database already contained all
|
||||
four heads. Schema and constraint inspection passed; recomputing the Files
|
||||
identity backfill checked 7,385 rows with zero mismatches. A private current-state
|
||||
database backup was created. Per-instance backup paths and row contents are not
|
||||
published in this repository. The implementation's initial receipt claiming no
|
||||
live migration occurred was incorrect: watched development-server restarts had
|
||||
applied the migration files automatically. Restoring the backup into an isolated
|
||||
PostgreSQL 16 cluster and running the normal upgrade preserved all 281 public
|
||||
tables, 142,287 rows, and migration heads exactly. Both Datasources PostgreSQL
|
||||
concurrency regressions passed. The test-only cluster was then stopped.
|
||||
|
||||
Release preparation additionally corrected Alembic's ConfigParser handling of
|
||||
percent-escaped connection URLs, preserving the exact database URL. The static
|
||||
migration auditor now recognizes the existing reviewed development-wrapper
|
||||
aliases and peer filenames without executing migration code; historical
|
||||
migrations are unchanged.
|
||||
|
||||
## Verification and boundaries
|
||||
|
||||
The implementation passed the required focused workspace gate, including 63
|
||||
frontend build configurations and 230 browser conformance tests. Owning-module
|
||||
regressions cover exact import/rollback evidence, authorization-before-pagination,
|
||||
bounded recurrence and response processing, collision-safe attachment naming,
|
||||
and stale asynchronous UI completion. English/German feature documentation stays
|
||||
in each owning module; Core documents shared integrity contracts.
|
||||
|
||||
Mock-provider tests and local work-count measurements do not establish production
|
||||
throughput or provider race behavior. Real S3/SMB/Seafile and POP3 acceptance,
|
||||
representative load testing, and deployment authentication checks remain separate
|
||||
operational validation. Source tags trigger package workflows; a pushed source
|
||||
tag alone does not prove a registry artifact or signed catalog is published.
|
||||
|
||||
## Deutsch
|
||||
|
||||
Dieses koordinierte Quellrelease veröffentlicht nur die betroffenen Pakete.
|
||||
Produktions-Images, signierter Modulkatalog und entfernte Produktivinstanzen
|
||||
werden dadurch nicht ausgerollt. Die vier Migrationen bewahren bestehende Daten;
|
||||
historische POP3-Zuordnungen werden nicht geraten. POP3 ist ein eigener
|
||||
Import-Arbeitsablauf innerhalb von **Mail**, kein separat installierbares Modul.
|
||||
|
||||
Der Entwicklungsserver kann Migrationen beim automatischen Neustart nach einer
|
||||
Quelländerung bereits anwenden. Tatsächliche Schema-Stände prüfen; eine danach
|
||||
erstellte Sicherung enthält den aktuellen Stand und ist keine Sicherung vor dem
|
||||
Upgrade. Externe Transportaktionen, erneutes Versenden und Löschungen werden
|
||||
durch die Migration oder Quellveröffentlichung nicht ausgelöst.
|
||||
@@ -0,0 +1,343 @@
|
||||
# Kubernetes VM Test Lab
|
||||
|
||||
`tools/lab/govoplan-lab.py` creates and operates an amd64 Ubuntu/K3s test
|
||||
environment on local or SSH-accessible libvirt hypervisors. It provides the
|
||||
commands requested for the complete VM lifecycle:
|
||||
|
||||
| Command | Effect |
|
||||
| --- | --- |
|
||||
| `doctor` | Validate the strict inventory and, with `--online`, every hypervisor. |
|
||||
| `create --apply` | Download checksum-pinned cloud images, create VM overlays and boot the declared VMs. |
|
||||
| `deploy --apply` | Verify the signed GovOPlaN release, deploy shared state, install pinned K3s and apply GovOPlaN. |
|
||||
| `update --apply` | Pull newly pinned state images, update K3s serially and roll the selected GovOPlaN release. |
|
||||
| `status` | Show libvirt VM state, Kubernetes nodes and GovOPlaN pods. |
|
||||
| `pause --apply` | Gracefully shut down workers, control planes and shared state while retaining disks. |
|
||||
| `resume --apply` | Start the retained environment in dependency order and wait for readiness. |
|
||||
| `verify` | Collect sanitized live-cluster evidence and optionally perform the API-pod-loss drill. |
|
||||
| `destroy --apply --confirm <lab>` | Delete only the lab-owned domains and overlays; local evidence is retained by default. |
|
||||
|
||||
Every mutating command is a dry run unless `--apply` is present. Destruction
|
||||
also requires the exact lab name. Generated credentials, CA keys, manifests and
|
||||
evidence are written below the configured `state_directory` with owner-only
|
||||
permissions. Keep that directory outside the repository and include it in the
|
||||
workstation backup policy. Existing domains are reused or removed only when
|
||||
their GovOPlaN ownership description and both expected lab disk paths match.
|
||||
|
||||
## What The Lab Proves
|
||||
|
||||
The supplied inventories describe two different assurance levels:
|
||||
|
||||
- `tools/lab/govoplan-lab.example.toml` creates four VMs on one libvirt host.
|
||||
It is suitable for development, deployment rehearsal, migration testing,
|
||||
application-pod replacement and recovery-tool exercises. It cannot close
|
||||
GovOPlaN #27 because one physical host remains one failure domain.
|
||||
- `tools/lab/govoplan-lab.acceptance.example.toml` places the two workers on
|
||||
different hypervisors and puts the control and state VMs on a third. It can
|
||||
produce the bounded stateless application-tier evidence required by #27 when
|
||||
the declared hypervisors are genuinely independent physical failure domains.
|
||||
|
||||
Both examples use one control-plane VM and one state VM. This keeps the bounded
|
||||
#27 target economical, but it does not prove control-plane or state-service
|
||||
high availability. For control-plane failover, declare exactly three control
|
||||
nodes on independent hosts. PostgreSQL, Redis and object-storage failover must
|
||||
be tested against independently operated HA services; the lab's single state
|
||||
VM is intentionally a replaceable integration fixture.
|
||||
|
||||
Approximate minimum capacity for the four-VM profile is 10 vCPUs, 16 GiB RAM
|
||||
and 192 GiB of thin-provisioned disk. A six-VM profile with three controls needs
|
||||
additional capacity. Do not overcommit memory on an acceptance target.
|
||||
|
||||
## 1. Prepare The Hypervisors
|
||||
|
||||
On each Ubuntu/Debian libvirt host:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
qemu-kvm libvirt-daemon-system libvirt-clients virtinst cloud-image-utils curl
|
||||
sudo systemctl enable --now libvirtd
|
||||
```
|
||||
|
||||
Use a dedicated lab-administration account. Remote hypervisors are managed over
|
||||
SSH and the lifecycle invokes `sudo -n` there, so that account needs bounded
|
||||
non-interactive permission for libvirt, image and cloud-init operations.
|
||||
`NOPASSWD: ALL` is acceptable only on isolated lab hypervisors.
|
||||
|
||||
On a local hypervisor, put the workstation account in the `libvirt` group and
|
||||
point `vm_image_directory` at a directory writable by that account and
|
||||
traversable by `libvirt-qemu`. The lifecycle connects explicitly to
|
||||
`qemu:///system` and does not require passwordless local sudo. Log out and back
|
||||
in after a new group assignment before running `doctor --online`. Create the
|
||||
configured image directory before running the doctor; it deliberately rejects
|
||||
a missing or non-writable storage root instead of silently falling back to a
|
||||
different filesystem.
|
||||
|
||||
Create a dedicated SSH key on the management workstation:
|
||||
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -f "$HOME/.ssh/govoplan-lab" \
|
||||
-C "GovOPlaN Kubernetes lab"
|
||||
```
|
||||
|
||||
Install its public key for every remote hypervisor account. The same public key
|
||||
is injected into the VMs. The lifecycle keeps its own `ssh_known_hosts` file,
|
||||
uses `accept-new` for first contact, and rejects changed host keys until a
|
||||
lab-owned VM is deliberately recreated.
|
||||
|
||||
### Network contract
|
||||
|
||||
The configured `bridge` must exist on every selected hypervisor. All VM
|
||||
addresses are static. Reserve them outside DHCP allocation and ensure that the
|
||||
management workstation can route directly to every VM address; the lifecycle
|
||||
does not tunnel VM traffic through the hypervisor SSH connection.
|
||||
|
||||
Permit only these flows inside the lab network:
|
||||
|
||||
| Port | Source and destination | Purpose |
|
||||
| --- | --- | --- |
|
||||
| TCP 22 | management workstation to every VM/hypervisor | Provisioning and evidence collection |
|
||||
| TCP 6443 | all K3s nodes and management path to controls | Kubernetes API |
|
||||
| UDP 8472 | K3s node to K3s node | Default Flannel VXLAN; never expose publicly |
|
||||
| TCP 10250 | K3s node to K3s node | Kubelet metrics and API |
|
||||
| TCP 2379-2380 | control to control, only with three controls | Embedded etcd |
|
||||
| TCP 80/443 | test clients to K3s nodes | Traefik/ServiceLB ingress |
|
||||
| TCP 5432/6379/9443 | K3s nodes to the state VM | PostgreSQL, Redis and TLS-protected Garage S3 |
|
||||
| TCP 3025/3143 | approved test clients/workers to the state VM | GreenMail SMTP/IMAP test endpoints |
|
||||
|
||||
The official
|
||||
[K3s networking requirements](https://docs.k3s.io/installation/requirements#networking)
|
||||
remain authoritative. Restrict state ports to the lab network even though the
|
||||
generated integration stack binds them on the state VM.
|
||||
|
||||
## 2. Create The Inventory
|
||||
|
||||
Start with the one-host rehearsal:
|
||||
|
||||
```bash
|
||||
install -d -m 0700 "$HOME/.config/govoplan/labs"
|
||||
cp tools/lab/govoplan-lab.example.toml \
|
||||
"$HOME/.config/govoplan/labs/development.toml"
|
||||
chmod 0600 "$HOME/.config/govoplan/labs/development.toml"
|
||||
```
|
||||
|
||||
Edit at least the bridge, network, static addresses and SSH key paths. For a
|
||||
multi-host run, copy the acceptance example and replace every example hostname,
|
||||
failure-domain declaration and network value. Strict parsing rejects unknown
|
||||
keys, mutable HTTP inputs, malformed checksums, duplicate addresses/MACs and an
|
||||
acceptance inventory that collapses workers onto one declared hypervisor or
|
||||
failure domain.
|
||||
|
||||
Cloud image, K3s binary, K3s installer and GovOPlaN release inputs are URL plus
|
||||
SHA-256 pairs. Updating means changing those reviewed pins and then running the
|
||||
`update` command; the tool deliberately does not follow `latest` aliases.
|
||||
|
||||
The one-host example uses the dedicated `govoplan-lab` NAT network. Its DHCP
|
||||
pool ends at `192.168.123.99`; the static lab addresses start at
|
||||
`192.168.123.201`. Define and start it once on the local hypervisor:
|
||||
|
||||
```bash
|
||||
virsh --connect qemu:///system net-define \
|
||||
tools/lab/libvirt/govoplan-lab-network.xml
|
||||
virsh --connect qemu:///system net-autostart govoplan-lab
|
||||
virsh --connect qemu:///system net-start govoplan-lab
|
||||
```
|
||||
|
||||
Re-running those commands is unnecessary when `virsh net-info govoplan-lab`
|
||||
already reports an active, persistent network. The lab destroy command leaves
|
||||
this reusable network in place.
|
||||
|
||||
## 3. Validate And Create The VMs
|
||||
|
||||
```bash
|
||||
LAB="$HOME/.config/govoplan/labs/development.toml"
|
||||
PYTHON="/mnt/DATA/git/govoplan/.venv/bin/python"
|
||||
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" doctor --online
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" create
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" create --apply
|
||||
```
|
||||
|
||||
The preview is safe to run repeatedly. Creation reuses a domain whose exact
|
||||
lab-owned name already exists and otherwise creates a thin qcow2 overlay under
|
||||
`vm_image_directory/<lab>/<node>`.
|
||||
|
||||
## 4. Deploy GovOPlaN
|
||||
|
||||
If `git.add-ideas.de` requires authentication for release images, export a
|
||||
read-only package/container-registry identity for this shell. A Gitea package
|
||||
token can be used as the password:
|
||||
|
||||
```bash
|
||||
export GOVOPLAN_LAB_REGISTRY_USERNAME='package-reader'
|
||||
read -r -s GOVOPLAN_LAB_REGISTRY_PASSWORD
|
||||
export GOVOPLAN_LAB_REGISTRY_PASSWORD
|
||||
```
|
||||
|
||||
Then preview and apply:
|
||||
|
||||
```bash
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" deploy
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" deploy --apply
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" status
|
||||
unset GOVOPLAN_LAB_REGISTRY_PASSWORD
|
||||
```
|
||||
|
||||
Deployment verifies the downloaded release manifest and keyring by pinned
|
||||
digest and by the existing GovOPlaN signature policy. It deploys PostgreSQL,
|
||||
Redis, single-node Garage and GreenMail on the state VM. The API, WebUI, workers
|
||||
and scheduler run in K3s from digest-pinned release images. A private lab CA
|
||||
protects both ingress and S3; backend pods receive only the CA Secret and keep
|
||||
TLS verification enabled. The CA profile carries critical `CA:TRUE` and
|
||||
`keyCertSign,cRLSign` constraints. `deploy` and `update` rotate older lab CAs
|
||||
that do not satisfy that profile and reissue the ingress/S3 certificate.
|
||||
|
||||
The final output identifies two local files below `state_directory`:
|
||||
|
||||
- `hosts` maps the public GovOPlaN and S3 test names to their VM addresses;
|
||||
- `pki/ca.crt` is the private lab CA certificate.
|
||||
|
||||
Add the host mappings to the test client's resolver and trust the CA only on
|
||||
devices used for this lab. On Debian/Ubuntu:
|
||||
|
||||
```bash
|
||||
STATE="$HOME/.local/share/govoplan/labs/govoplan-k8s-lab"
|
||||
cat "$STATE/hosts"
|
||||
sudo install -m 0644 "$STATE/pki/ca.crt" \
|
||||
/usr/local/share/ca-certificates/govoplan-k8s-lab.crt
|
||||
sudo update-ca-certificates
|
||||
```
|
||||
|
||||
Review mappings before adding them to `/etc/hosts`; the lifecycle does not edit
|
||||
the workstation's trust or resolver configuration. Reinstall `pki/ca.crt` in
|
||||
the client trust store after an automatic CA rotation.
|
||||
|
||||
### Enroll the first administrator
|
||||
|
||||
The production runtime does not create a default password. Issue one expiring,
|
||||
single-use first-administrator credential inside an API pod and copy its
|
||||
owner-only artifact out immediately:
|
||||
|
||||
```bash
|
||||
KUBECTL="$STATE/bin/kubectl"
|
||||
POD="$($KUBECTL -n govoplan get pods \
|
||||
-l app.kubernetes.io/component=api \
|
||||
-o jsonpath='{.items[0].metadata.name}')"
|
||||
ARTIFACT="$STATE/first-admin-enrollment.json"
|
||||
umask 077
|
||||
|
||||
$KUBECTL -n govoplan exec "$POD" -- \
|
||||
python -m govoplan_core.commands.first_admin issue \
|
||||
--reason 'initial Kubernetes lab enrollment' \
|
||||
--output /tmp/first-admin-enrollment.json
|
||||
$KUBECTL -n govoplan exec "$POD" -- \
|
||||
cat /tmp/first-admin-enrollment.json > "$ARTIFACT"
|
||||
$KUBECTL -n govoplan exec "$POD" -- \
|
||||
rm -f /tmp/first-admin-enrollment.json
|
||||
chmod 0600 "$ARTIFACT"
|
||||
```
|
||||
|
||||
Submit the token from that artifact once to
|
||||
`/api/v1/bootstrap/first-admin` with the administrator email, display name,
|
||||
password, tenant slug and tenant name. The password must contain at least 12
|
||||
characters. The lab command performs that exchange without placing either the
|
||||
token or password in process arguments, rejects redirects, and removes the
|
||||
artifact only after HTTP 201:
|
||||
|
||||
```bash
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" enroll-admin \
|
||||
--email 'owner@example.org' \
|
||||
--display-name 'System Owner' \
|
||||
--tenant-slug default \
|
||||
--tenant-name 'Default Tenant'
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" enroll-admin \
|
||||
--email 'owner@example.org' \
|
||||
--display-name 'System Owner' \
|
||||
--tenant-slug default \
|
||||
--tenant-name 'Default Tenant' \
|
||||
--apply
|
||||
```
|
||||
|
||||
The public lab hostname must already resolve on the management workstation;
|
||||
the command verifies TLS through the generated private CA directly.
|
||||
|
||||
## 5. Collect #27 Evidence
|
||||
|
||||
Create a short-lived API key authorized to read the Ops status endpoint. In the
|
||||
current Access administration UI, open **Tenant API keys** and select only
|
||||
**View tenant settings** (`admin:settings:read`); the Ops endpoint explicitly
|
||||
accepts that compatibility scope. A dedicated operator credential may instead
|
||||
use `ops:operations:read`. Then run:
|
||||
|
||||
```bash
|
||||
export GOVOPLAN_OPS_API_KEY='short-lived-value'
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" verify \
|
||||
--exercise-api-pod-loss
|
||||
unset GOVOPLAN_OPS_API_KEY
|
||||
```
|
||||
|
||||
The verifier requires ready API and WebUI pods across at least two Kubernetes
|
||||
nodes, all Deployments available, consistent runtime composition, queue
|
||||
coverage and a valid database-connection budget. During the optional drill it
|
||||
deletes one ready API pod, probes public readiness and waits for replacement.
|
||||
It writes sanitized output to
|
||||
`state_directory/evidence/kubernetes-multi-host.json` and never stores the API
|
||||
key. A rehearsal inventory prints an explicit warning that its result is not
|
||||
independent-failure-domain evidence.
|
||||
|
||||
Retain these private artifacts together for review:
|
||||
|
||||
1. `inventory.json` and the reviewed inventory TOML;
|
||||
2. the adopted release manifest/keyring and installation receipt;
|
||||
3. `kubernetes.json`;
|
||||
4. the Kubernetes verifier output;
|
||||
5. private cluster logs for the approved drill window;
|
||||
6. the operator's out-of-band evidence that the worker hypervisors are
|
||||
independent physical hosts or availability zones.
|
||||
|
||||
GovOPlaN #37 additionally requires independent assessment and production
|
||||
approval keys. Running its evidence jobs in containers is supported, but a
|
||||
container does not create an independent authority. Follow
|
||||
`TARGET_MATURITY_EVIDENCE_RUNBOOK.md` after the #27 drill passes.
|
||||
|
||||
## 6. Update, Pause, Resume And Remove
|
||||
|
||||
After reviewing and changing pinned image/K3s/release values in the inventory:
|
||||
|
||||
```bash
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" update
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" update --apply
|
||||
```
|
||||
|
||||
Workers are cordoned, drained, updated and uncordoned one at a time. K3s
|
||||
controls are reconciled serially. The release-specific migration Job remains
|
||||
subject to GovOPlaN's signed backup-evidence gate. The lab update command is not
|
||||
a substitute for creating recovery evidence before a destructive state-schema
|
||||
change.
|
||||
|
||||
To stop compute use without deleting disks:
|
||||
|
||||
```bash
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" pause --apply
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" resume --apply
|
||||
```
|
||||
|
||||
To remove VM resources while preserving local evidence:
|
||||
|
||||
```bash
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" destroy
|
||||
"$PYTHON" tools/lab/govoplan-lab.py --config "$LAB" destroy \
|
||||
--apply --confirm govoplan-k8s-lab
|
||||
```
|
||||
|
||||
Add `--purge-local-state` only after evidence and recovery material have been
|
||||
retained elsewhere. That option deletes the generated local CA, secrets,
|
||||
manifests and evidence as well as the VMs.
|
||||
|
||||
## Acceptance Boundary
|
||||
|
||||
This tool supplies reproducible infrastructure and executes the bounded
|
||||
stateless-node drill. It does not certify the truth of operator-entered failure
|
||||
domains, provide HA PostgreSQL/Redis/Garage, create production backup evidence,
|
||||
or approve its own results. Those boundaries are deliberate: #27 can close
|
||||
after a passing run on independently controlled hosts; broader production
|
||||
maturity remains governed by #35, #37 and the target evidence runbook.
|
||||
+4
-1
@@ -114,7 +114,10 @@ For release validation:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/repo/sync-python-environment.py --requirements requirements-release.txt --python ./.venv/bin/python
|
||||
./.venv/bin/python -m pip install -r requirements-release-tests.txt
|
||||
```
|
||||
|
||||
That install is necessary because the release environment intentionally resolves
|
||||
tagged package refs, not local editable source trees.
|
||||
tagged package refs, not local editable source trees. The second requirements
|
||||
file contains only the harness needed to execute tests from those immutable
|
||||
source tags; it is not part of the deployable release dependency set.
|
||||
@@ -0,0 +1,465 @@
|
||||
# Package Registry Releases
|
||||
|
||||
GovOPlaN publishes reusable module artifacts through Gitea's native PyPI and
|
||||
npm registries. These packages improve developer installation, release
|
||||
resolution, cacheability, and artifact inspection. They do not replace the
|
||||
signed runtime distribution: the signed manifest and digest-pinned OCI images
|
||||
remain the production deployment authority.
|
||||
|
||||
## Publication boundary
|
||||
|
||||
Every repository with a `pyproject.toml` contains
|
||||
`.gitea/workflows/module-package-release.yml`. The meta repository owns the
|
||||
canonical template and installs it with:
|
||||
|
||||
```bash
|
||||
python tools/repo/sync-module-package-workflows.py --write
|
||||
python tools/repo/sync-module-package-workflows.py --check
|
||||
```
|
||||
|
||||
The workflow runs for `v*` tags and may be dispatched manually for an existing
|
||||
tag. The organization preflight verifies that every package repository protects
|
||||
the `v*` namespace. Before building, the workflow itself verifies that:
|
||||
|
||||
- the tagged commit is contained in `main`;
|
||||
- the tag, Python project version, and optional WebUI package version agree;
|
||||
- package names remain in the `govoplan-*` and `@govoplan/*-webui` namespaces.
|
||||
|
||||
The workflow binds the repository explicitly from the Gitea Actions context.
|
||||
Do not rely on GitHub-compatible environment variables being injected by the
|
||||
runner image; Gitea runners may expose only the context values. Gitea 1.24 job
|
||||
tokens cannot read repository tag-protection settings, so package jobs must not
|
||||
receive a broad administrator token merely to repeat the organization preflight.
|
||||
Run the following before the first publication and after repository or tag-rule
|
||||
changes:
|
||||
|
||||
```bash
|
||||
python tools/gitea/gitea-configure-package-releases.py
|
||||
```
|
||||
|
||||
Preview and dispatch the exact wheel/WebUI versions selected by the developer
|
||||
meta-package with:
|
||||
|
||||
```bash
|
||||
python tools/gitea/gitea-dispatch-package-set.py \
|
||||
--env-file ~/.config/gitea/gitea.env
|
||||
python tools/gitea/gitea-dispatch-package-set.py \
|
||||
--env-file ~/.config/gitea/gitea.env \
|
||||
--apply
|
||||
```
|
||||
|
||||
The dispatcher reads exact versions from `packages/govoplan-meta/pyproject.toml`,
|
||||
inspects the selected tag to determine whether a WebUI package is expected,
|
||||
skips complete registry pairs and does not duplicate an active workflow. Use
|
||||
`--repository govoplan-core` for a bounded dispatch or `--verify-existing` to
|
||||
rebuild and hash-verify versions already present in both registries.
|
||||
|
||||
For coordinated lockstep tags, `push-release-tag.sh` pushes module tags first,
|
||||
Core next, and the meta tag last. This is a dependency guarantee for a
|
||||
single-capacity Actions runner: the developer package cannot run before its
|
||||
exact Core and module versions have entered the queue.
|
||||
|
||||
The same release entry point first validates the migration graph, then records
|
||||
the reviewed current Alembic heads under the target release version and reruns
|
||||
the strict migration audit before it changes package versions, commits, or
|
||||
tags. The default preflight intentionally does not require those heads to exist
|
||||
in the previous release baseline. A failed candidate-baseline check therefore
|
||||
cannot produce a protected package release.
|
||||
|
||||
The source gate validates `pyproject.toml`, the module version declaration
|
||||
(`MODULE_VERSION` or the top-level `ModuleManifest.version`), public package
|
||||
`__version__`, and WebUI metadata before creating tags. Release-tag artifact
|
||||
checks run only after the candidate tags and immutable WebUI lock have been
|
||||
created locally.
|
||||
|
||||
Release-lock regeneration resolves a fresh immutable lock from the reviewed
|
||||
candidate manifests; it does not seed resolution from the previous release
|
||||
lock. This prevents removed transitive packages and stale peer metadata from
|
||||
blocking or contaminating the new release. Candidate resolution also uses an
|
||||
isolated temporary npm cache, so a locally replaced tag cannot reuse metadata
|
||||
from a failed, unpushed release attempt.
|
||||
|
||||
Modules that retain the same WebUI package identity in both a root publish
|
||||
manifest and `webui/package.json` use the WebUI manifest as the canonical peer
|
||||
contract. The coordinated release synchronizes `peerDependencies` and
|
||||
`peerDependenciesMeta` into the publish manifest before creating the module
|
||||
tag, then synchronizes each lockfile root from the final package metadata. A
|
||||
distinct root package remains independent.
|
||||
|
||||
Every module referenced by Core's Git-based `package.release.json` must expose
|
||||
its WebUI identity at the repository root, including matching peer requirements
|
||||
and `webui/`-prefixed entry exports (also CSS subpaths). npm resolves Git
|
||||
dependencies from the repository root, while the native-package workflow packs
|
||||
`webui/`; success in one path does not verify the other. Run
|
||||
`python tools/checks/check-webui-package-facades.py` after changing either
|
||||
manifest or the release composition. The focused gate also runs this check.
|
||||
Adding or correcting a facade in an already published repository requires a
|
||||
new patch tag; never repair an existing immutable tag in place.
|
||||
|
||||
It builds one wheel and, where applicable, one npm tarball. The workflow records
|
||||
the source tag, source commit, filename, size, and SHA-256 in
|
||||
`package-artifacts.json` before publishing. Gitea rejects a second upload of the
|
||||
same package version, so correction requires a new version rather than artifact
|
||||
replacement.
|
||||
|
||||
A retry after partial publication is safe. Before upload, the workflow reads the
|
||||
native package registry file record and compares its SHA-256 with the artifact
|
||||
rebuilt from the protected tag. An exact existing artifact is skipped; a
|
||||
same-version artifact with another digest or an unexpected file set fails
|
||||
closed. This permits a failed npm publication to resume without weakening
|
||||
package immutability or accepting `--skip-existing` blindly.
|
||||
|
||||
The npm tarball is always published through an explicit local `./dist/...`
|
||||
path. Without that prefix, npm may interpret a relative tarball name as a Git
|
||||
package shorthand before it ever contacts the configured registry.
|
||||
|
||||
Published WebUI packages contain registry-compatible dependencies only. The
|
||||
workflow converts an internal dependency pinned to a protected `vX.Y.Z` Git tag
|
||||
into the exact `X.Y.Z` registry version and rejects unresolved `file:` or Git
|
||||
dependencies. Repository development metadata may therefore keep local or Git
|
||||
references without leaking them into the published package contract.
|
||||
Historical `add-ideas` and current `GovOPlaN` organization URLs are accepted
|
||||
for immutable tagged releases; both normalize to the same exact registry
|
||||
dependency and no branch or unversioned Git reference is accepted.
|
||||
|
||||
## One-time Gitea setup
|
||||
|
||||
Protect `v*` tags in every package repository and the meta repository. Allow
|
||||
only the `Owners` team to create or delete those tags.
|
||||
|
||||
```bash
|
||||
set -a
|
||||
. ~/.config/gitea/gitea.env
|
||||
set +a
|
||||
python tools/gitea/gitea-configure-package-releases.py --apply
|
||||
```
|
||||
|
||||
Create a dedicated personal access token with only `write:package` scope and
|
||||
store these organization-level Actions secrets on `GovOPlaN`:
|
||||
|
||||
- `GOVOPLAN_PACKAGE_USERNAME`: account owning the package token;
|
||||
- `GOVOPLAN_PACKAGE_TOKEN`: dedicated package-write token.
|
||||
|
||||
Do not use an administrator or general release token. Gitea 1.24 does not grant
|
||||
package publication to the automatic Actions job token. Organization secrets
|
||||
allow the same least-privilege credential to serve every module workflow.
|
||||
|
||||
## Exact release consumption
|
||||
|
||||
`tools/release/generate-release-package-set.py` supports two explicit package
|
||||
profiles. `base` translates the reviewed roots in `requirements-release.txt`;
|
||||
`full` reads the exact `govoplan[full]` dependency set from the developer
|
||||
meta-package. Both profiles resolve every version tag to its commit and verify
|
||||
the package metadata from that exact Git tree. The official module directory
|
||||
and immutable runtime distribution use `full`, so every publicly released
|
||||
module can be discovered without rebuilding the application image.
|
||||
|
||||
`tools/release/resolve-package-artifacts.py` then downloads exactly those wheel
|
||||
and WebUI versions from Gitea. It reads the identity embedded in every wheel and
|
||||
npm tarball, rejects missing, duplicate, unexpected, or oversized artifacts,
|
||||
and writes `package-artifacts.lock.json` with credential-free HTTPS download
|
||||
URLs, SHA-256 values, and npm registry integrity values. The resolver verifies
|
||||
that the bytes downloaded by `npm pack` match the registry's own integrity
|
||||
record. Credentials are accepted only through environment variables and are
|
||||
never written to the lock. Python resolution ignores ambient pip configuration
|
||||
and extra indexes for GovOPlaN roots, preventing an internal package name from
|
||||
being selected from an undeclared registry.
|
||||
|
||||
The runtime distribution workflow uses the verified full-profile wheelhouse
|
||||
directly and installs every selected module WebUI tarball only after matching
|
||||
it to the lock. It publishes the package set, package lock, and hash-locked
|
||||
requirements as release assets.
|
||||
The WebUI installer receives the absolute runtime-build interpreter path so its
|
||||
directory changes cannot escape the isolated release environment.
|
||||
Gitea 1.24 dispatches this workflow from a branch, but that branch is only the
|
||||
workflow implementation. The job fetches and peels the protected `v<version>`
|
||||
tag explicitly and materializes both `requirements-release.txt` and the
|
||||
developer meta-package from that Git tree. It then binds the signed distribution
|
||||
source and Gitea release assets to the same exact commit. A post-tag workflow
|
||||
repair can therefore retry publication without changing the released package
|
||||
composition or relabelling the later branch commit as released source.
|
||||
The package-lock SHA-256 is part of the signed distribution manifest. Runtime
|
||||
finalization also requires the lock's package versions and hashes to match the
|
||||
wheel composition embedded in the images. OCI assembly remains network-free
|
||||
after package and third-party dependency resolution.
|
||||
|
||||
The source refs remain in the module catalog for source provenance and release
|
||||
planning. Production installation consumes the signed runtime images rather
|
||||
than invoking `pip`, `npm`, or Git on the target host.
|
||||
|
||||
## Public module directory
|
||||
|
||||
For an operator-reviewed full publication, use
|
||||
`tools/release/release-catalog.py full-registry` followed by the same tool's
|
||||
`publish-candidate` command. Resolve the package set and registry lock first;
|
||||
the older direct-write shell wrapper is not the strict candidate publication
|
||||
path. See [Full registry candidates / Vollständige Registry-Kandidaten](FULL_REGISTRY_CANDIDATES.md)
|
||||
for the private host runtime, exact artifact checks, and legacy keyring transition.
|
||||
Catalog entries are synthesized from
|
||||
the exact tagged module manifests, never from a hand-maintained module list or
|
||||
the current workspace. Each entry binds its Python wheel and optional WebUI
|
||||
tarball to the registry URL, filename, size, SHA-256, package identity, source
|
||||
tag, and source commit before the complete catalog is signed.
|
||||
|
||||
The same publication transaction regenerates and prunes the browsable static
|
||||
directory under `public/catalogs/v1/modules/`. It writes a global
|
||||
`modules/index.json`, one `<module>/index.json`, and one
|
||||
`<module>/<version>/manifest.json` for every entry in the signed channel.
|
||||
These files are derived from that exact signed payload and keyring; stale JSON
|
||||
from an older partial catalog is removed while unrelated static assets are left
|
||||
untouched. The signed channel remains the trust anchor, while the module
|
||||
directory provides stable discovery URLs for browsers and external tooling.
|
||||
|
||||
Official GovOPlaN modules are open-source directory entries and do not require
|
||||
license entitlements. The generic `license_features` contract remains available
|
||||
for third-party package directories, support/configuration packages, or future
|
||||
deployment-specific presets. A catalog entry is gated only when that entry
|
||||
explicitly declares such features.
|
||||
|
||||
Core carries the public stable catalog URL and its independently pinned trust
|
||||
anchor. In the absence of an operator-configured catalog, Admin discovers the
|
||||
official directory automatically. Selecting an entry creates a reviewed
|
||||
install/update plan; the trusted installer downloads the exact signed artifacts
|
||||
into a private digest cache, verifies size and hash, and installs only from that
|
||||
cache. A saved plan is rejected if any package ref, artifact identity, catalog
|
||||
channel, sequence, or signing-key identity differs from the currently validated
|
||||
catalog.
|
||||
|
||||
The Admin directory can be searched by module, package, repository, or tag and
|
||||
filtered by available, installed, update, and blocked/withdrawn states. It
|
||||
shows the source revision, artifact digest, release notes, and configuration
|
||||
requirements. Missing dependency/interface providers and unsupported update
|
||||
windows are surfaced before an operator adds the entry to a plan; installer
|
||||
preflight remains authoritative.
|
||||
|
||||
Catalog entries also carry the permission definitions declared by the tagged
|
||||
module manifest. Admin groups and exposes their scopes before an install or
|
||||
update is planned. This is disclosure only: installing a module does not grant
|
||||
its permissions to an account, role, group, tenant, or service account.
|
||||
|
||||
Package lifecycle and availability are intentionally separate:
|
||||
|
||||
- install, update, and uninstall change the instance-wide package composition;
|
||||
- enable and disable change the active instance runtime graph;
|
||||
- tenant module entitlements define unavailable, available, and forced modules;
|
||||
- group/user presentation is governed through Views and Policy; and
|
||||
- enabling a capability module does not opt data into that capability.
|
||||
|
||||
Single-process or single-host installations may execute a supervised package
|
||||
plan locally. Shared-state and Kubernetes profiles reject node-local package
|
||||
mutation: operators compose and roll out a new signed full-profile runtime image
|
||||
instead. This prevents replicas from drifting while retaining the same Admin
|
||||
catalog and preflight experience.
|
||||
|
||||
## Developer meta-package
|
||||
|
||||
`packages/govoplan-meta` builds the optional `govoplan` package. Its default
|
||||
dependencies mirror the reviewed runtime roots; `govoplan[full]` adds all
|
||||
currently packageable workspace modules. Regenerate it after changing release
|
||||
requirements or package versions:
|
||||
|
||||
```bash
|
||||
python tools/release/generate-developer-meta-package.py
|
||||
python tools/release/generate-developer-meta-package.py --check
|
||||
```
|
||||
|
||||
The direct generator is a development synchronization tool, not a receipt-gated
|
||||
release executor. For release preparation, use the guarded out-of-run stage below.
|
||||
|
||||
`push-release-tag.sh` performs this synchronization before release commits and
|
||||
tags. The meta-package is for editable/developer setup and composition tests. It
|
||||
does not enable modules, apply migrations, provision services, or establish
|
||||
backup and recovery evidence.
|
||||
|
||||
### Shared source-tag contract and Meta composition
|
||||
|
||||
The shared version collector names Meta's real
|
||||
`packages/govoplan-meta/pyproject.toml` separately from root `pyproject.toml`.
|
||||
Only the registered `govoplan` system/meta repository with nested project name
|
||||
`govoplan` receives this contract. Missing, unknown, or misidentified metadata
|
||||
does not become a versionless exception. Version alignment compares the complete
|
||||
nested file with the canonical operator-tool generator output: its version must
|
||||
match Core, and dependencies and `full` composition must match the reviewed
|
||||
requirements and workspace package versions. Validation never executes a
|
||||
generator from a selected checkout. The shared trusted manifest checker can
|
||||
load reviewed application manifests; these checks are not a code sandbox.
|
||||
|
||||
Meta's complete generated file is recognized by shared version-mutation discovery,
|
||||
but the generic durable version executor deliberately cannot write it. A durable
|
||||
run freezes the release console's own Meta checkout as trusted runtime code;
|
||||
changing it in place would invalidate that run. The planner therefore places Meta
|
||||
after Core and exposes only non-executable support preparation/publication steps,
|
||||
not misleading automatic Meta version, commit, tag, or push actions. A missing or
|
||||
different Core target produces an actionable preparation prerequisite.
|
||||
|
||||
Prepare Core and the intended module inputs first, commit their reviewed state,
|
||||
then stop active durable runs for the target workspace. Use trusted operator tools
|
||||
against a separate registered, private source checkout, never the running operator
|
||||
Meta directory. Preview outside any selected source checkout, for example:
|
||||
|
||||
```sh
|
||||
python tools/release/prepare-developer-meta-package.py \
|
||||
--workspace /private/release-workspace --target-version X.Y.Z \
|
||||
> /private/operator/meta-preview.json
|
||||
python tools/release/prepare-developer-meta-package.py \
|
||||
--workspace /private/release-workspace --target-version X.Y.Z \
|
||||
--receipt /private/operator/meta-preview.json --apply --confirm-out-of-run
|
||||
```
|
||||
|
||||
The explicit confirmation attests that no durable run is active for that target
|
||||
workspace; the helper does not discover or stop other processes. Preview/apply
|
||||
requires registered clean main sources, matching origins and live-main ancestry,
|
||||
Core already aligned at the target, the exact nested identity, and no existing or
|
||||
unverifiable target Meta tag. Frozen receipts cover source HEADs/filesystem
|
||||
identities, release requirements, every discovered registered full-composition
|
||||
pyproject, the trusted generator snapshot, and the resulting full-file hash.
|
||||
Inputs are limited to 128 selected files, 2 MiB per file and 16 MiB aggregate.
|
||||
The canonical generator renders copied bounded data in a temporary directory;
|
||||
no generator from the selected checkout executes. Changed receipts block before
|
||||
the file effect. Apply writes only `packages/govoplan-meta/pyproject.toml`, then
|
||||
rechecks the other sources and exact output. A write or post-check failure that
|
||||
may have changed the file reports `needs-reconciliation` and leaves that bounded
|
||||
delta for explicit review; it never retries, rolls back, commits or publishes.
|
||||
|
||||
Review the complete generated composition and manually commit the resulting file.
|
||||
Complete matching Core publication before guarded Meta source tagging/publication,
|
||||
then start a fresh durable run from reviewed, clean, published operator tooling.
|
||||
Hot self-updating durable Meta release execution remains explicitly unsupported;
|
||||
this out-of-run preparation is the existing developer-meta support contract.
|
||||
|
||||
For every `tag_repositories` batch, strict checks apply to every selected
|
||||
repository before any tag creation, fetch, or push: registered checkout and
|
||||
origin/push URL, a clean `main` tracking `origin/main`, live remote-main ancestry,
|
||||
exact frozen HEADs, and immutable annotated local/remote tag objects. Git metadata
|
||||
must remain inside the operator's trusted workspace. Missing local knowledge of
|
||||
live remote main is a blocker; fetch and review it separately. Unknown repositories
|
||||
and non-registered remote aliases fail closed. If selected, Meta runs last.
|
||||
For Meta only, the matching annotated Core release must exist before its effect; Core may be
|
||||
an earlier selected repository, or an already tagged dependency. Publication
|
||||
requires that Core's exact tag and main commit are already remote.
|
||||
|
||||
Non-Meta selections do not acquire Meta's composition or Core-tag prerequisite.
|
||||
Local module-candidate tags still work before Core's final release lock or tag.
|
||||
The existing Core WebUI bundle gate still applies to module publication and
|
||||
batches selecting Core: relevant Core release-package and release-lock inputs
|
||||
must be operator-owned regular files, at most 16 MiB each, and their identities
|
||||
and content hashes are frozen before preflight and rechecked before every effect.
|
||||
When Core is unselected, this does not require its checkout to be clean or tagged;
|
||||
reviewed pending composition inputs retain their previous meaning. Backend-only
|
||||
selections never read irrelevant Core WebUI files.
|
||||
|
||||
Before even read-only Git commands, source ancestry must be owned by root or the
|
||||
current operator and must not be group/world writable. A sticky shared ancestor
|
||||
such as `/tmp` is permitted only above an owned, protected child; the workspace
|
||||
and checkouts receive no writable-directory exception. The current operator must
|
||||
own source inputs and actual Git/worktree/common metadata, which must be regular
|
||||
files/directories, non-symlinked, and non-writable by other users. Metadata walks
|
||||
are bounded to 500,000 entries and 128 levels, and tracked inputs to 100,000 paths
|
||||
and 16 MiB of listing text. Read-only Git targets, object alternates/grafts and
|
||||
hidden/sparse/unmerged index entries are blocked. Frozen receipts include actual
|
||||
checkout/Git directory paths, devices, inodes, owners and modes, so replacing Git
|
||||
metadata with the same HEAD is still detected. All selected version/composition
|
||||
inputs must be tracked, including root and WebUI package/lock metadata, discovered
|
||||
module manifests and package initializers, and Meta's nested package and release
|
||||
requirements; ignored working files cannot supply declarations absent from a tag.
|
||||
No chmod, ownership repair or
|
||||
global Git trust change is performed. A shared writable workspace must first be
|
||||
recreated or reviewed in the operator's protected release area by an explicitly
|
||||
authorized preparation workflow.
|
||||
|
||||
Preview is read-only. Local-tag mode creates only pinned annotated tags (or
|
||||
retrieves an identical published annotation); it does not publish main or tags.
|
||||
Publish mode atomically pushes the frozen main commit and annotation object,
|
||||
without force, retagging, fallback, or automatic retry. The complete source
|
||||
receipt is rechecked before every effect and afterward; remote main and the
|
||||
exact annotated tag must both match, not merely the Git exit status. Changes
|
||||
after preflight stop the remaining batch. Atomicity is per repository, not
|
||||
across repositories: earlier successful publications and a newly created local
|
||||
tag can remain after a later failure. Inspect reported receipts and obtain a new
|
||||
review before retrying; do not move immutable tags.
|
||||
|
||||
Whole-batch revalidation deliberately repeats source and live-remote checks around
|
||||
each repository effect; the number of checks can grow quadratically with batch
|
||||
size. Plan release time accordingly rather than bypassing trust checks. The
|
||||
shared internal preflight is read-only and exposes no legacy mutation path.
|
||||
The fixture suite covers Meta and non-Meta preview/local-tag/
|
||||
publication using temporary local bare remotes, including stale compositions,
|
||||
unsafe origins, divergent branches, damaged tag identity, changed receipts and
|
||||
false publication success. This is local tooling evidence, not a real release
|
||||
publication or production permission check.
|
||||
|
||||
Deutsch: Die gemeinsamen Helfer erkennen ausschließlich das registrierte
|
||||
Meta-Repository mit dem echten Paket `packages/govoplan-meta/pyproject.toml`
|
||||
(Projektname `govoplan`). Version und vollständige Zusammensetzung müssen dem
|
||||
kanonischen Generator, Core und den geprüften Anforderungen entsprechen; der
|
||||
Generator stammt niemals aus dem ausgewählten Checkout. Der gemeinsame
|
||||
Manifestprüfer kann geprüften Anwendungscode laden und ist keine Sandbox.
|
||||
Die gemeinsame Änderungsplanung erkennt die vollständig generierte Paketdatei,
|
||||
aber der dauerhafte Versionsausführer darf Meta nicht selbst verändern: sein
|
||||
eingefrorener Lauf bindet den Meta-Checkout als vertrauenswürdigen Programmstand.
|
||||
Meta erscheint deshalb nach Core ausschließlich mit nicht automatisch ausführbaren
|
||||
Vorbereitungs-/Veröffentlichungsschritten. Zuerst Core und Modulquellen vorbereiten
|
||||
und geprüft committen; bei abweichender Core-Zielversion nennt der Plan diese
|
||||
Voraussetzung ausdrücklich. Aktive dauerhafte Läufe des Ziel-Workspaces beenden.
|
||||
Mit `prepare-developer-meta-package.py` zunächst eine Vorschau außerhalb der
|
||||
Quell-Checkouts speichern, dann deren JSON über `--receipt` zusammen mit `--apply`
|
||||
und `--confirm-out-of-run` bestätigen. Das Ziel muss ein separater registrierter
|
||||
privater Checkout sein, niemals das laufende Operator-Meta. Die Bestätigung ist
|
||||
eine Betreibererklärung; der Helfer sucht oder beendet keine fremden Prozesse.
|
||||
Quell-HEADs, Pfadidentitäten, Anforderungen, alle registrierten vollständigen
|
||||
Paket-Eingaben, der vertrauenswürdige Generator und der vollständige Ausgabehash
|
||||
werden eingefroren. Es gelten höchstens 128 Quelldateien, 2 MiB je Datei und
|
||||
16 MiB insgesamt. Core muss bereits vollständig zur Zielversion passen; vorhandene
|
||||
oder nicht verifizierbare Meta-Zieltags sperren die Vorbereitung. Geänderte
|
||||
Nachweise stoppen vor dem Schreiben. Ausschließlich die verschachtelte Paketdatei
|
||||
wird vollständig generiert und danach geprüft; ein Fehler nach dem Schreiben
|
||||
meldet `needs-reconciliation` und erfordert die manuelle Prüfung dieser begrenzten
|
||||
Änderung, ohne automatisches Zurücksetzen. Kein automatischer
|
||||
Commit, Push oder Wiederholungsversuch findet statt. Zusammensetzung prüfen,
|
||||
manuell committen, Core zuerst veröffentlichen, dann die geschützte Meta-Tag-Route
|
||||
verwenden und einen neuen dauerhaften Lauf starten. Eine Selbstaktualisierung
|
||||
des aktiven dauerhaften Meta-Laufs bleibt ausdrücklich nicht unterstützt.
|
||||
Für jeden Tag-Stapel, auch ohne Meta, gelten
|
||||
Vertrauens-, Origin-, saubere Main- und Live-Abstammungsprüfungen für die gesamte
|
||||
Auswahl vor jeder Änderung. Unbekannte Repositories und nicht registrierte
|
||||
Remote-Aliase sind gesperrt. Nur bei ausgewähltem Meta gelten zusätzlich dessen
|
||||
Zusammensetzungsprüfung und der passende annotierte Core-Tag als Voraussetzung;
|
||||
Meta folgt zuletzt. Für Metas Veröffentlichung müssen Core-Tag und Main-Commit
|
||||
bereits auf dem Remote vorliegen. Lokale Modul-Kandidatentags bleiben vor Cores
|
||||
abschließendem Release-Lock und Tag möglich. Die vorhandene Core-WebUI-Bundleprüfung
|
||||
bleibt bei Modulveröffentlichung und Core-Auswahl erhalten. Relevante Core-Paket-
|
||||
und Lockdateien müssen eigene reguläre Dateien mit höchstens je 16 MiB sein;
|
||||
Identität und Inhaltshash werden eingefroren und vor jeder Aktion erneut geprüft.
|
||||
Nicht ausgewähltes Core benötigt dafür weder einen sauberen Checkout noch einen
|
||||
Tag. Reine Backend-Auswahlen lesen keine irrelevanten Core-WebUI-Dateien.
|
||||
Vor Git-Aufrufen werden Eigentümer, Schreibrechte, sichere
|
||||
Pfadabstammung und echte Git-/Worktree-Metadaten geprüft; veränderbare gemeinsame
|
||||
Verzeichnisse, fremde Eigentümer, Alternates, Grafts und versteckte Indexeinträge
|
||||
sind gesperrt. Ein Sticky-Bit-Vorfahr wie `/tmp` ist nur oberhalb eines eigenen
|
||||
geschützten Unterverzeichnisses zulässig. Es erfolgen weder Rechtereparaturen
|
||||
noch globale Git-Vertrauensänderungen. Ausgewählte Versions- und Zusammensetzungs-
|
||||
dateien müssen versioniert sein: Paket-/Lockdateien, Modulmanifeste und
|
||||
Paketinitialisierer sowie Metas verschachteltes Paket und Release-Anforderungen.
|
||||
Ignorierte Arbeitsdateien dürfen keine vom Tag abweichenden Angaben liefern.
|
||||
Die Vorschau schreibt nichts, lokale Tags veröffentlichen nichts,
|
||||
und die Veröffentlichung überträgt Main und den exakten annotierten Tag atomar
|
||||
je Repository. Unmittelbar vor und nach den Aktionen werden die eingefrorenen
|
||||
Quellnachweise erneut geprüft, einschließlich entferntem Main und Tag-Objekt.
|
||||
Bei Änderungen oder Fehlern stoppt der Rest des Stapels ohne automatischen
|
||||
Wiederholungsversuch. Frühere Veröffentlichungen und neu erzeugte lokale Tags
|
||||
können bestehen bleiben: vor einem neuen Versuch Nachweise prüfen und erneut
|
||||
freigeben, niemals unveränderliche Tags verschieben. Die vollständigen Quell- und
|
||||
Live-Remote-Prüfungen werden um jede Aktion wiederholt; bei großen Stapeln kann
|
||||
deren Anzahl quadratisch wachsen. Diese konservativen Prüfkosten gehören zur
|
||||
Release-Planung. Der interne Vorprüfer ist ausschließlich lesend und besitzt
|
||||
keinen alten Änderungspfad. Tests für Auswahlen mit und ohne Meta verwenden
|
||||
nur temporäre lokale Remotes und ersetzen keine echte Veröffentlichungsprüfung.
|
||||
|
||||
If the tag-triggered developer meta-package job fails before publication, rerun
|
||||
`publish-developer-meta-package.yml` with the existing protected version. The
|
||||
manual path validates that tag against `main`, checks out its exact commit, and
|
||||
publishes only when the registry does not already contain the same wheel hash.
|
||||
|
||||
Generic Packages are intentionally not used. Add that transport only when a
|
||||
consumer needs an artifact format unsupported by PyPI, npm, Gitea Releases, or
|
||||
the OCI registry.
|
||||
@@ -0,0 +1,190 @@
|
||||
# Production Target And Independent Evidence Handoff
|
||||
|
||||
This runbook identifies the external inputs needed to finish
|
||||
[GovOPlaN #27](https://git.add-ideas.de/GovOPlaN/govoplan/issues/27) and
|
||||
[GovOPlaN #37](https://git.add-ideas.de/GovOPlaN/govoplan/issues/37). The
|
||||
repository can render, inspect and sign evidence for a target, but it cannot
|
||||
manufacture an independent failure domain or an independent approval authority.
|
||||
|
||||
## GovOPlaN #27: real two-node target
|
||||
|
||||
The bounded acceptance target is two independently schedulable worker nodes.
|
||||
The API and WebUI must each have ready replicas on both nodes, all Deployments
|
||||
must be available, the active module composition and software versions must be
|
||||
consistent, every configured queue must have a worker, and the database
|
||||
connection budget must pass. The validation then deletes one ready API pod and
|
||||
requires replacement without an observed readiness outage.
|
||||
|
||||
Two virtual machines on different physical hosts or availability zones meet the
|
||||
failure-domain intent. Two containers, VMs or Kubernetes nodes on one physical
|
||||
host are useful development targets but do not close #27. A two-worker cluster
|
||||
also does not prove control-plane high availability. For a self-managed
|
||||
production cluster, use three control-plane nodes plus at least two workers; a
|
||||
managed control plane plus two workers is the shorter path.
|
||||
|
||||
### What the target owner must provide
|
||||
|
||||
Provide these through a secure handoff, not an issue, chat message or Git:
|
||||
|
||||
1. A kubeconfig path with access to the target, for example
|
||||
`~/.config/govoplan/targets/<target>.kubeconfig`, mode `0600`.
|
||||
2. A stable installation ID, public HTTPS hostname, namespace, ingress class and
|
||||
TLS-secret or certificate-manager arrangement.
|
||||
3. Two independently schedulable workers and permission to place API and WebUI
|
||||
replicas on both.
|
||||
4. External, logically shared PostgreSQL, Redis and S3 endpoints with trusted
|
||||
CA material and network reachability from every worker. Do not co-locate the
|
||||
only copies of these services on the two workers used for the failure drill.
|
||||
5. The six runtime secret values required by the generated manifest:
|
||||
`MASTER_KEY_B64`, `DATABASE_URL`, `GOVOPLAN_DATABASE_URL_PGTOOLS`,
|
||||
`REDIS_URL`, `FILE_STORAGE_S3_ACCESS_KEY_ID` and
|
||||
`FILE_STORAGE_S3_SECRET_ACCESS_KEY`.
|
||||
6. A short-lived GovOPlaN API key limited to `ops:operations:read`, supplied in
|
||||
`GOVOPLAN_OPS_API_KEY` only for evidence collection.
|
||||
7. An approved drill window and permission to delete one API pod.
|
||||
|
||||
If no Kubernetes target exists, provide hostnames/IP addresses for the machines,
|
||||
an SSH user and key path, the internal/external DNS plan, and the permitted
|
||||
firewall ports. Those inputs are sufficient to provision a k3s target. They are
|
||||
not sufficient to claim control-plane HA unless three control-plane failure
|
||||
domains are present.
|
||||
|
||||
The repository now supplies the strict libvirt/K3s lifecycle and example
|
||||
inventories for this handoff in
|
||||
[`KUBERNETES_TEST_LAB.md`](KUBERNETES_TEST_LAB.md). Its `acceptance` mode
|
||||
rejects a declared topology unless the workers and shared-state fixture occupy
|
||||
different hypervisor and failure-domain identifiers. Reviewers must still
|
||||
verify that those identifiers correspond to genuinely independent hosts.
|
||||
|
||||
### Separate deployment and evidence authorities
|
||||
|
||||
The deployment identity may create and update the namespace, Secret,
|
||||
ConfigMap, Deployments, Services, Jobs, PodDisruptionBudgets and Ingress. The
|
||||
evidence collector only needs:
|
||||
|
||||
- cluster scope: `get` and `list` for `nodes`;
|
||||
- target namespace: `get` and `list` for `pods` and `deployments`;
|
||||
- target namespace during the approved drill: `delete` for `pods`.
|
||||
|
||||
Use separate kubeconfig contexts or service accounts when the same person does
|
||||
not hold both roles.
|
||||
|
||||
### Render, apply and verify
|
||||
|
||||
Use the signed, digest-pinned installation bundle selected for the target:
|
||||
|
||||
```bash
|
||||
export KUBECONFIG="$HOME/.config/govoplan/targets/<target>.kubeconfig"
|
||||
|
||||
python tools/deployment/govoplan-deploy.py render-kubernetes \
|
||||
--directory /srv/govoplan/<installation-id> \
|
||||
--namespace govoplan \
|
||||
--secret-name govoplan-runtime \
|
||||
--tls-secret-name govoplan-tls \
|
||||
--s3-ca-secret-name govoplan-s3-ca \
|
||||
--ingress-class-name nginx \
|
||||
--output /srv/govoplan/<installation-id>/kubernetes.json
|
||||
|
||||
kubectl apply -f /srv/govoplan/<installation-id>/kubernetes.json
|
||||
kubectl -n govoplan wait --for=condition=available deployment --all --timeout=10m
|
||||
|
||||
export GOVOPLAN_OPS_API_KEY="$(cat /run/secrets/govoplan-ops-evidence-key)"
|
||||
python tools/deployment/govoplan-deploy.py verify-kubernetes \
|
||||
--directory /srv/govoplan/<installation-id> \
|
||||
--namespace govoplan \
|
||||
--exercise-api-pod-loss \
|
||||
--output /srv/govoplan/<installation-id>/evidence/kubernetes-multi-host.json
|
||||
unset GOVOPLAN_OPS_API_KEY
|
||||
```
|
||||
|
||||
The verifier emits sanitized JSON and exits nonzero if the topology, runtime,
|
||||
queue, connection-budget or pod-loss checks fail. Preserve the private cluster
|
||||
logs and manifest alongside the sanitized result in the controlled evidence
|
||||
store.
|
||||
|
||||
## GovOPlaN #37: controlled signed target evidence
|
||||
|
||||
Yes, collection, review and signing can run in containers. A container provides
|
||||
repeatability and process isolation; it does not create independent authority.
|
||||
The production approver must control a different private key from the target
|
||||
operator/assessor and must review the evidence before signing the
|
||||
`production_approval` scope.
|
||||
|
||||
Use at least these three key boundaries:
|
||||
|
||||
1. **Installer authority:** signs installed-release-origin receipts only.
|
||||
2. **Target assessment authority:** signs the permitted target, accessibility,
|
||||
privacy, security, operations and recovery scopes.
|
||||
3. **Production approval authority:** independently signs only
|
||||
`production_approval` after reviewing the other evidence.
|
||||
|
||||
Do not reuse release-catalog keys for any of these roles. Keep private Ed25519
|
||||
keys outside Git, Gitea, GovOPlaN application storage and chat. Publish only the
|
||||
public keyrings. The proof issuer already rejects key reuse across release,
|
||||
installer and proof trust domains.
|
||||
|
||||
### Generate independently held keys
|
||||
|
||||
Each authority runs this command in its own `0700` directory. The generator
|
||||
refuses existing output paths and writes both files as `0600`:
|
||||
|
||||
```bash
|
||||
install -d -m 0700 "$HOME/.config/govoplan/authority-keys"
|
||||
|
||||
python tools/assessments/generate-authority-keypair.py \
|
||||
--purpose proof \
|
||||
--key-id authority:target-2026 \
|
||||
--scope target_environment \
|
||||
--scope accessibility \
|
||||
--scope privacy \
|
||||
--scope security \
|
||||
--scope operations \
|
||||
--scope recovery \
|
||||
--private-key "$HOME/.config/govoplan/authority-keys/target-2026.pem" \
|
||||
--keyring "$HOME/.config/govoplan/authority-keys/target-2026-public.json"
|
||||
```
|
||||
|
||||
The independent production approver generates another key with only
|
||||
`--scope production_approval`. An installer authority uses `--purpose installer`
|
||||
and no `--scope`. Merge public key entries into the separately controlled
|
||||
keyrings only after the responsible authorities verify fingerprints out of
|
||||
band.
|
||||
|
||||
### Container boundary
|
||||
|
||||
Use two one-shot jobs or containers:
|
||||
|
||||
- **Collector/assessor:** network access, read-only source and trust mounts,
|
||||
read/write private evidence output, and the narrowly scoped kubeconfig. It
|
||||
must not receive the production-approval private key.
|
||||
- **Production approver:** `--network none`, read-only assessment/evidence/trust
|
||||
mounts, a read-only secret mount containing only the approval key, and a
|
||||
separate output mount. It must not receive deployment credentials.
|
||||
|
||||
Build or select the assessment image by digest and record that digest in the
|
||||
evidence log. A representative runtime shape is:
|
||||
|
||||
```bash
|
||||
docker run --rm --network none --read-only --tmpfs /tmp \
|
||||
--user "$(id -u):$(id -g)" \
|
||||
--mount type=bind,src="$PWD/evidence",dst=/evidence,readonly \
|
||||
--mount type=bind,src="$PWD/trust",dst=/trust,readonly \
|
||||
--mount type=bind,src="$HOME/.config/govoplan/authority-keys",dst=/run/keys,readonly \
|
||||
--mount type=bind,src="$PWD/approved",dst=/output \
|
||||
<assessment-image>@sha256:<digest> \
|
||||
<assessment command>
|
||||
```
|
||||
|
||||
The current evidence commands and required scopes are documented in
|
||||
[`TARGET_MATURITY_EVIDENCE_RUNBOOK.md`](TARGET_MATURITY_EVIDENCE_RUNBOOK.md).
|
||||
The final proof must cover `target_environment`, `accessibility`, `privacy`,
|
||||
`security`, `operations`, `recovery` and independent `production_approval`, and
|
||||
must bind to the verified installed composition and installer receipt.
|
||||
|
||||
## Completion boundary
|
||||
|
||||
#27 can close after the real target produces a passing pod-loss result. #37 can
|
||||
close after an independently approved, schema-valid proof is generated for that
|
||||
same installed composition and the public authority keyrings, proof and private
|
||||
evidence custody references are recorded. Neither issue should close from a
|
||||
single-host simulation or a self-approved signature.
|
||||
@@ -0,0 +1,155 @@
|
||||
# Recovery And Rollback Guarantees
|
||||
|
||||
## Principle
|
||||
|
||||
GovOPlaN must prove recovery claims with durable state recorded before and
|
||||
after side effects. A failed operation is not automatically rolled back merely
|
||||
because the previous application image still exists. Database schema and
|
||||
external effects may make release rollback unsafe.
|
||||
|
||||
Core therefore distinguishes five recovery modes:
|
||||
|
||||
| Mode | Meaning |
|
||||
| --- | --- |
|
||||
| `atomic` | One database transaction either commits or rolls back. No external effect is claimed. |
|
||||
| `compensation` | Durable evidence identifies explicit inverse actions for completed effects. |
|
||||
| `snapshot_restore` | A separately verified backup reference and restore procedure exist. |
|
||||
| `forward_recovery` | Repair or resume the current version; reverting code/configuration is not claimed safe. |
|
||||
| `irreversible` | No automated recovery is claimed and an approval reference is mandatory. |
|
||||
|
||||
An operation plan must include verification steps. Compensation requires named
|
||||
compensation steps, snapshot restore requires a verified backup reference,
|
||||
forward recovery requires repair steps, and irreversible work requires explicit
|
||||
approval.
|
||||
|
||||
## Core Recovery Ledger
|
||||
|
||||
Core stores recovery operations and append-only, hash-chained checkpoints in
|
||||
PostgreSQL. The contract provides:
|
||||
|
||||
- installation/module/resource identity;
|
||||
- an idempotency key bound to a canonical request hash;
|
||||
- recovery mode, preconditions, verification steps, and references;
|
||||
- optional runtime lease holder and fencing token;
|
||||
- explicit planned, prepared, running, recovery-required, recovering,
|
||||
succeeded, recovered, failed, outcome-unknown, and manual-intervention states;
|
||||
- an evidence-chain head and sequence count;
|
||||
- rejection of plaintext secrets in metadata or evidence.
|
||||
|
||||
Preparation cannot succeed without durable precondition evidence. A non-atomic
|
||||
operation cannot hide a partial effect by transitioning directly from running
|
||||
to failed. Success and recovery require explicit verification evidence with at
|
||||
least one check. The ledger verifies its hash chain before evidence is trusted.
|
||||
|
||||
This is a platform contract, not an assertion that every existing module
|
||||
operation has adopted it. Module operations with external or multi-resource
|
||||
effects must be migrated to the ledger before claiming these guarantees.
|
||||
The owning-module inventory and adoption state are maintained in
|
||||
[Recovery Ledger Adoption](RECOVERY_LEDGER_ADOPTION.md); CI validates the
|
||||
machine-readable inventory so newly identified boundaries cannot disappear from
|
||||
the backlog silently.
|
||||
|
||||
## Deployment Journal
|
||||
|
||||
Every `govoplan-deploy apply` begins an operation journal before it pulls images
|
||||
or mutates runtime state. The private installation directory records:
|
||||
|
||||
```text
|
||||
operations/<operation-id>/operation.json
|
||||
operations/<operation-id>/before/
|
||||
applied-state/
|
||||
```
|
||||
|
||||
Each stage is hash-chained. The previous applied bundle is copied with per-file
|
||||
SHA-256 evidence. Applied state is replaced atomically after health verification;
|
||||
an interrupted replacement restores its previous directory.
|
||||
New journals also bind the complete desired deployment plan, snapshot
|
||||
availability, failure summary, recovery mode, and terminal status to the
|
||||
evidence chain. Recovery verifies every snapshot entry and checksum before it
|
||||
changes any live bundle file, then replaces each live file atomically. A crash
|
||||
between file replacements is recoverable by rerunning the same idempotent
|
||||
recovery command under the deployment lock.
|
||||
|
||||
Inspect operations:
|
||||
|
||||
```sh
|
||||
python tools/deployment/govoplan-deploy.py operations \
|
||||
--directory /srv/govoplan/default
|
||||
```
|
||||
|
||||
Recover the latest failed operation, or provide its identifier:
|
||||
|
||||
```sh
|
||||
python tools/deployment/govoplan-deploy.py recover \
|
||||
--directory /srv/govoplan/default \
|
||||
--operation-id 20260801T120000Z-1234abcd
|
||||
```
|
||||
|
||||
Add `--apply` only after reviewing the reported action.
|
||||
|
||||
## Migration Boundary
|
||||
|
||||
Before database migration starts, a failed deployment with a verified prior
|
||||
applied snapshot may restore its prior release/configuration bundle and
|
||||
reconcile that desired state.
|
||||
|
||||
As soon as migration starts, the journal permanently changes to
|
||||
`forward_recovery`. It will not restore old application configuration because
|
||||
old code may not understand the new schema. Recovery then means one of:
|
||||
|
||||
1. fix and re-run the current release;
|
||||
2. deploy a newer compatible repair release;
|
||||
3. restore a separately verified, coordinated database/object/key backup and
|
||||
then deploy the matching release.
|
||||
|
||||
The deployment tool does not create that backup. It does verify an externally
|
||||
produced, signed evidence contract covering PostgreSQL, objects, protected
|
||||
configuration, and key custody at one recovery point plus an isolated restore
|
||||
drill. A self-hosted release change cannot reach the migration command or be
|
||||
exported as a Kubernetes migration Job until fresh evidence bound to the
|
||||
previous immutable release has been adopted. Compose verifies it again after
|
||||
runtime quiescing. See
|
||||
[Backup And Restore Evidence](BACKUP_AND_RESTORE_EVIDENCE.md) for the contract,
|
||||
provider runbooks, RPO/RTO ownership, retention, and disposal rules.
|
||||
|
||||
## Scaled Nodes
|
||||
|
||||
Recovery actions must be safe across replicas:
|
||||
|
||||
- drain affected API and worker nodes before incompatible changes;
|
||||
- use the deployment-wide PostgreSQL advisory lock for schema migration;
|
||||
- use distributed leases and fencing tokens for singleton or externally
|
||||
visible effects;
|
||||
- use idempotency keys for retried commands and jobs;
|
||||
- retain shared object keys and database references until deletion succeeds;
|
||||
- classify uncertain external outcomes instead of retrying blindly;
|
||||
- verify the exact software/module composition after replacement.
|
||||
|
||||
Campaign generated-message objects now follow this model: object writes are
|
||||
compensated when a build fails before database commit, workers verify stored
|
||||
size and digest before delivery, and retention keeps the database reference
|
||||
when storage deletion fails. A hard process loss between object creation and
|
||||
database commit can still leave an orphan object; an inventory reconciler is a
|
||||
separate operational slice and must use the build-specific object prefix.
|
||||
|
||||
## Required Drills
|
||||
|
||||
Record evidence for at least these scenarios before production acceptance:
|
||||
|
||||
1. Kill an API replica and verify traffic continues without session loss.
|
||||
2. Drain and replace a worker while work is queued and while one job is active.
|
||||
3. Start two migration jobs and verify only one mutates schema.
|
||||
4. Kill the fenced scheduler and verify one replacement acquires a higher
|
||||
fencing token.
|
||||
5. Fail deployment before migration and restore the prior applied bundle.
|
||||
6. Fail deployment after migration and verify old configuration is not
|
||||
restored.
|
||||
7. Restore PostgreSQL, object storage, and encryption keys to one coordinated
|
||||
recovery point and verify representative object hashes.
|
||||
8. Interrupt object storage during Campaign build and retention and verify
|
||||
compensation/reference-preservation behavior.
|
||||
9. Tamper with a deployment or Core recovery checkpoint and verify chain
|
||||
validation rejects it.
|
||||
|
||||
No runbook, status badge, or green health endpoint substitutes for a dated,
|
||||
repeatable restore drill against the actual deployment topology.
|
||||
@@ -0,0 +1,89 @@
|
||||
# Recovery Ledger Adoption
|
||||
|
||||
The Core recovery ledger is a platform primitive, not automatic protection for
|
||||
module-owned effects. The canonical, machine-checked inventory is
|
||||
[`recovery-operation-inventory.json`](../recovery-operation-inventory.json).
|
||||
|
||||
## Classification Rules
|
||||
|
||||
- Use `atomic` only when every mutation commits in one database transaction and
|
||||
no external effect occurs.
|
||||
- Use `compensation` when every completed effect has a bounded, verifiable
|
||||
inverse action. A best-effort delete is not proof of compensation.
|
||||
- Use `snapshot_restore` only with fresh, signed backup evidence that covers all
|
||||
affected state services at one recovery point.
|
||||
- Use `forward_recovery` for provider acceptance, queue publication, cursor
|
||||
advancement, and other effects that may be resumable but cannot safely be
|
||||
undone.
|
||||
- Use `irreversible` for approved purge or destruction where no automated
|
||||
recovery is claimed.
|
||||
|
||||
One feature may cross more than one boundary. Module installation is
|
||||
compensatable before schema migration, forward-only after migration starts, and
|
||||
snapshot-restorable for an approved destructive retirement. Mail submission is
|
||||
forward recovery because losing the response after provider acceptance must not
|
||||
cause an automatic resend.
|
||||
|
||||
## Adoption Order
|
||||
|
||||
1. Campaign build is the reference implementation for a database plus object
|
||||
storage operation. Its operation reserves a build-specific object prefix,
|
||||
persists request and precondition evidence before writes, records the final
|
||||
object manifest, and verifies database/object state before success.
|
||||
2. Campaign delivery and Mail provider effects adopt outcome-unknown semantics
|
||||
without weakening their existing provider-specific idempotency records.
|
||||
3. Files applies the same contract to uploads, purge, integrity reconciliation,
|
||||
and writable connector synchronization.
|
||||
4. Connectors, Dataflow, and Workflow Engine consume the contract at their
|
||||
registry/capability boundaries so optional providers remain optional.
|
||||
5. Core module lifecycle uses the ledger in addition to, not instead of, signed
|
||||
deployment and backup evidence.
|
||||
|
||||
Every fenced operation uses a process incarnation and distributed lease. A
|
||||
stale process cannot append a checkpoint or report success. An expired operation
|
||||
is claimed for recovery through an explicit takeover that preserves the prior
|
||||
fence in the checkpoint chain; it is never resumed as a normal retry.
|
||||
|
||||
Connectors read-only sanctions and feed acquisitions are adopted: source
|
||||
revision/cursor and dry-run evidence are recorded before provider I/O, while
|
||||
the immutable snapshot and terminal checkpoint commit atomically. The generic
|
||||
external-mutation contract is conformance-tested but remains `planned` until a
|
||||
production connector actually publishes, updates, or deletes provider state.
|
||||
|
||||
Dataflow runs are adopted. Database-only execution uses one atomic terminal
|
||||
commit for the run projection and recovery checkpoint. Output publication uses
|
||||
forward recovery: source and output digests are checkpointed before dispatch,
|
||||
a conclusive provider result commits with the run projection, and an expired
|
||||
or failed attempt after dispatch becomes `outcome_unknown`. A stale attempt may
|
||||
be retried only when its durable boundary proves dispatch had not started.
|
||||
|
||||
Workflow Engine is adopted at both declared boundaries. Instance workers,
|
||||
trigger deliveries, and timer resumptions use process-bound distributed fences.
|
||||
Every module-action invocation records the pinned definition, input, preview,
|
||||
authority, provider-idempotency, and action-contract hashes before dispatch.
|
||||
Conclusive results commit with the Workflow projection. A lost acknowledgement,
|
||||
invalid result, or unannounced non-atomic effect becomes `outcome_unknown` and
|
||||
cannot be retried until evidence confirms either that the effect occurred or is
|
||||
absent. Linked Dataflow uncertainty blocks the Workflow without duplicating
|
||||
Dataflow's recovery authority.
|
||||
|
||||
Core module lifecycle is adopted at four boundaries. Installer recovery is
|
||||
prepared before snapshots so a full database restore preserves the attempted
|
||||
operation. Pre-migration package changes use compensation, migrated changes use
|
||||
forward recovery, destructive retirement requires a hashed and restore-checked
|
||||
snapshot, and live graph changes restore the prior registry when no migration
|
||||
ran. A deployment-wide database fence serializes these effects; any unresolved
|
||||
predecessor blocks a differently keyed retry until explicit reconciliation.
|
||||
Supervised installs become successful only after restart and health evidence is
|
||||
recorded.
|
||||
|
||||
## Operator Contract
|
||||
|
||||
Ops lists non-terminal and manual-intervention operations. Operators must verify
|
||||
the checkpoint chain before trusting evidence, distinguish `outcome_unknown`
|
||||
from rejection, and use the owning module's documented reconciliation action.
|
||||
No evidence payload may contain credentials or resolved secrets.
|
||||
|
||||
The parent adoption issue remains open until all inventory rows are adopted and
|
||||
the module matrix proves crash, retry, stale-fence, tamper, and optional-module
|
||||
behavior for each consequential path.
|
||||
@@ -0,0 +1,650 @@
|
||||
# GovOPlaN Release Console
|
||||
|
||||
The release console is a local operator tool for planning and executing
|
||||
GovOPlaN releases. It belongs to the `govoplan` meta repository because it works
|
||||
across all local checkouts, release scripts, module manifests, migration audits,
|
||||
catalog files, Git state, and signing keys.
|
||||
|
||||
The current implementation has a read-only dashboard, preview-only legacy
|
||||
controls, and a bounded durable executor for release steps whose inputs and
|
||||
effects can be verified safely:
|
||||
|
||||
- inspect repositories from `repositories.json`
|
||||
- show dirty, ahead, behind, missing, no-HEAD, and tag state
|
||||
- show local package catalog and keyring state
|
||||
- optionally run release/dev migration audits
|
||||
- propose next actions without executing them
|
||||
- compare local catalog/keyring JSON with the published channel and public
|
||||
keyring when online checks are enabled
|
||||
- configure target versions per release unit in the web UI
|
||||
- select the repositories that should advance through checkboxes
|
||||
- generate dry-run selective release plans for independently versioned packages
|
||||
- freeze a selective plan as a durable, resumable local release run
|
||||
- durably preflight a creation-time-bound repository, create its annotated tag,
|
||||
and publish its branch/tag pair atomically
|
||||
- deterministically update recognized package/manifest version declarations and
|
||||
commit only the receipt-bound metadata paths
|
||||
- order selected module providers before consumers, create module tags before
|
||||
Core, regenerate Core's selected WebUI release lock, and re-run alignment
|
||||
before any remote push
|
||||
- build selected Python wheels and generate a private, signed, receipt-bound
|
||||
catalog candidate
|
||||
- publish that exact candidate through a verified website commit and immutable
|
||||
tag after explicit confirmation
|
||||
- install selected candidate wheels into a private no-network/no-dependency
|
||||
target and verify their installed metadata against the frozen plan
|
||||
|
||||
Start it from the meta repository:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/release/release-console.py
|
||||
```
|
||||
|
||||
The launcher accepts only a numeric loopback address, binds to `127.0.0.1` by
|
||||
default, always creates a fresh API token, and prints that token only in the URL
|
||||
fragment. Open that URL in a browser on the same machine. A deliberately
|
||||
tokenless embedded app is read-only: non-GET `/api/` requests fail with `403`.
|
||||
Non-loopback operation needs a separately deployed authenticated TLS boundary;
|
||||
the local launcher will not expose the mutation API that way.
|
||||
|
||||
Run durable release mutation only against an operator-private workspace.
|
||||
Repository roots, every path ancestor, critical and nested Git metadata, and
|
||||
tracked worktree files must be owned by the console process UID (or root where
|
||||
appropriate) and must not be group/world writable. Symlink/special Git
|
||||
metadata, object alternates, and grafts are rejected. The console pins
|
||||
`/usr/bin/git`, `/usr/bin/ssh`, a fixed system `PATH`, disabled hooks, isolated
|
||||
Git configuration, and no replace objects. Shared or `nfsnobody`-owned
|
||||
checkouts remain usable for read-only planning, but every durable executor
|
||||
fails closed there; clone the registered origins into a private workspace
|
||||
before releasing.
|
||||
|
||||
For a host with a confirmed IPv6 connection timeout, set
|
||||
`GOVOPLAN_RELEASE_SSH_ADDRESS_FAMILY=inet` only for the release-tool invocation.
|
||||
When unset, the original SSH command is preserved, including the trusted
|
||||
operator's per-host `AddressFamily` configuration (normally `any`). Explicit
|
||||
values accepted by the shared source/tag Git helper are exactly `any`, `inet`
|
||||
(IPv4 only), and `inet6` (IPv6 only). Empty, misspelled, whitespace-padded, or
|
||||
injected values fail before Git starts. The selector only adds the corresponding
|
||||
fixed SSH `AddressFamily` option: it does not change DNS, host-key verification,
|
||||
the registered remote, authentication, `BatchMode=yes`, or `ConnectTimeout=8`.
|
||||
Arbitrary `GIT_SSH_COMMAND` overrides remain ignored. For example, start a
|
||||
single local console invocation with:
|
||||
|
||||
```sh
|
||||
GOVOPLAN_RELEASE_SSH_ADDRESS_FAMILY=inet \
|
||||
./.venv/bin/python tools/release/release-console.py
|
||||
```
|
||||
|
||||
The same process-scoped setting applies to canonical source/tag readbacks and
|
||||
registry-candidate source verification. Under Flatpak, pass it explicitly to
|
||||
the host invocation with `flatpak-spawn --host /usr/bin/env
|
||||
GOVOPLAN_RELEASE_SSH_ADDRESS_FAMILY=inet ...`. It is not a global SSH setting
|
||||
and does not affect the website publisher's separate transport sanitizer, npm,
|
||||
or HTTP downloads. An IPv4-only setting cannot reach IPv6-only hosts; omit it
|
||||
or use `any` when the diagnosed restriction no longer applies.
|
||||
|
||||
Deutsch: Bei einem bestätigten IPv6-Verbindungs-Timeout kann für genau einen
|
||||
Release-Werkzeugaufruf `GOVOPLAN_RELEASE_SSH_ADDRESS_FAMILY=inet` gesetzt werden.
|
||||
Ohne diese Variable bleibt der bisherige SSH-Befehl einschließlich der
|
||||
vertrauenswürdigen Host-Konfiguration unverändert (normalerweise `any`).
|
||||
Explizit zulässig sind ausschließlich `any`, `inet` (nur IPv4) und `inet6`
|
||||
(nur IPv6). Andere oder leere Werte werden vor dem Git-Aufruf abgewiesen.
|
||||
DNS, Hostschlüsselprüfung, registrierte Quelladresse, Authentifizierung und
|
||||
Zeitlimit bleiben unverändert; frei vorgegebene SSH-Befehle bleiben gesperrt.
|
||||
Unter Flatpak die Variable ausdrücklich an den Host-Aufruf übergeben. Die
|
||||
Auswahl gilt für den gemeinsamen Git-Helfer der Quell-/Tag-Prüfungen, nicht
|
||||
für den separaten Website-Publisher, npm oder HTTP-Downloads. Sie ändert keine
|
||||
globale Konfiguration. Nach Behebung des Netzwerkproblems die Variable
|
||||
weglassen oder auf `any` setzen; IPv4-only erreicht keine IPv6-only-Ziele.
|
||||
|
||||
The runtime itself is part of the authority boundary. Durable run creation
|
||||
verifies the meta checkout, release/check tooling, repository registry, Python
|
||||
environment, loaded `govoplan_core` and cryptography packages, and Git/SSH
|
||||
executables. It then binds a clean meta-repository HEAD and named branch that
|
||||
exactly match the registered `origin`. Every execution and reconciliation
|
||||
rechecks that receipt. Permission checks cannot prove that code was safe before
|
||||
it entered a writable tree, so release from a fresh operator-private clone or a
|
||||
separately verified installed console artifact.
|
||||
|
||||
The web UI starts with a repository table. Each repository can be checked
|
||||
independently and assigned its own target version. Repositories without version
|
||||
metadata remain visible so they can be planned as initial releases. `Build Plan`
|
||||
shows the dry-run commands for the selected rows, and `Generate Candidate`
|
||||
creates a signed catalog candidate that advances only selected repositories that
|
||||
already have a catalog entry.
|
||||
|
||||
The full-width **Release Workflow** guide projects the server state into seven
|
||||
operator phases: Inspect, Targets, Validate, Source, Package, Publish, and
|
||||
Verify. It does not maintain a second workflow state. Completed, current,
|
||||
blocked, locked, and unavailable phases are derived from the dashboard,
|
||||
selective plan, and durable run record. The next-action panel opens the exact
|
||||
section or durable step that needs attention. Changing a channel, target
|
||||
version, repository selection, or release gate detaches the browser from the
|
||||
current run and invalidates the draft plan; the persisted run remains available
|
||||
from the saved-run selector. Problems in unselected repositories remain visible
|
||||
as workspace notices but do not lock an unrelated release; the selective plan
|
||||
is the authority for blockers in the selected repository set.
|
||||
|
||||
Installation verification is an explicit durable step after catalog
|
||||
publication. It verifies the exact candidate receipt, installs every selected
|
||||
Python wheel into a temporary target with network and dependency resolution
|
||||
disabled, and compares installed names and versions with the frozen plan.
|
||||
Deployment startup, database upgrades, and module-combination smoke tests remain
|
||||
release-integration CI gates; the console does not represent its local wheel
|
||||
check as a production deployment.
|
||||
|
||||
`Build Plan` also returns structured release-gate findings for each selected
|
||||
repository. The plan names the recommended next action and gives an explicit
|
||||
remediation for source-version, lockfile, Core WebUI composition, Git state, and
|
||||
worktree findings. A target version that differs from internally consistent
|
||||
source metadata becomes a bounded `UPDATE` step. Unsupported, missing, or
|
||||
internally inconsistent declarations remain a blocker with exact remediation.
|
||||
`source_preflight_ready` means that plan-visible source gates pass or have a
|
||||
bounded deterministic mutation; the non-mutating `Preview Tag + Publish`
|
||||
remains mandatory for remote, manifest, and immutable-tag checks.
|
||||
|
||||
## Durable release runs
|
||||
|
||||
The **Durable Run State** card turns the current repository/version selection
|
||||
into a versioned local run record. The server rebuilds the selective plan and
|
||||
requires the plan to resolve exactly the requested repositories and target
|
||||
versions; the browser cannot submit or replace the plan snapshot. The input and
|
||||
plan are then immutable and covered by a canonical SHA-256 integrity digest.
|
||||
Every executable repository step also carries its creation-time full HEAD,
|
||||
branch, target tag, a SHA-256 over both the fetch and push URLs of `origin`, and
|
||||
a SHA-256 over bounded dirty-path names and bytes. Both URLs must exactly equal
|
||||
the remote registered in `repositories.json`; a changed HEAD, branch, worktree,
|
||||
remote, push URL, or metadata byte requires a new run or explicit
|
||||
interrupted-step reconciliation rather than silently retargeting the frozen
|
||||
compatibility decision.
|
||||
The complete record also has a checksum so a valid-looking manual edit to its
|
||||
mutable state fails closed. File permissions remain the authority boundary;
|
||||
these digests detect accidental or manual corruption, not an attacker who can
|
||||
replace the private record and recompute its checksums. Changing a target,
|
||||
channel, or gate input requires a new run.
|
||||
|
||||
Creation requires a caller-generated `request_id`. Its SHA-256 fingerprint is
|
||||
the private, workspace-scoped durable mapping to exactly one run; the raw
|
||||
identifier is never persisted. Repeating the same identifier with the same
|
||||
immutable inputs returns that run without rebuilding the plan while it remains
|
||||
inside the bounded local retention window, even if the live dashboard has
|
||||
since drifted. Reusing it with different inputs fails closed. The browser keeps
|
||||
an uncertain create identifier in session storage,
|
||||
replays it after reload, and selects the known run returned by the server. A
|
||||
successful create remains shown as saved if only the subsequent list refresh
|
||||
fails.
|
||||
|
||||
Run records survive console restarts, but remain local operator state rather
|
||||
than a signed release artifact or the system audit log. The default location is
|
||||
`$XDG_STATE_HOME/govoplan/release-console/workspace-<sha256>/release-runs/`, or
|
||||
`~/.local/state/...` when `XDG_STATE_HOME` is unset or relative. It is outside
|
||||
the source checkout so filesystems without enforceable POSIX modes cannot
|
||||
silently weaken the journal. Newly created state directories use mode `0700`
|
||||
and records use `0600`. Writes use a cross-process lock, a same-directory
|
||||
temporary file, `fsync`, atomic replacement, and directory/parent `fsync`.
|
||||
Symbolic-link paths, untrusted owners or writable ancestry, overly broad record
|
||||
modes, malformed schemas, unknown fields, invalid state combinations,
|
||||
oversized files, and digest mismatches fail closed. A bad record is neither
|
||||
rewritten nor automatically quarantined.
|
||||
|
||||
The store retains at most 512 workspace-scoped run records created through the
|
||||
console. On creation at that limit it removes only the oldest fully completed,
|
||||
integrity-verified record. Running, planned, attention, blocked, foreign, and
|
||||
unreadable records are never deleted implicitly; if no completed record is
|
||||
available, creation fails closed with a retention remediation. Unavailable
|
||||
records still consume the bound and remain visible in cursor-paginated lists
|
||||
so corruption cannot be hidden by normal turnover.
|
||||
|
||||
The full resolved-workspace SHA-256 is part of both the private storage
|
||||
namespace and immutable input snapshot. Every list, read, and state transition
|
||||
checks it. An alternate workspace therefore cannot list or resume another
|
||||
workspace's runs. This remains true for an embedding/test `run_state_root`
|
||||
override: the server always appends
|
||||
`workspace-<full-sha256>/release-runs/` rather than treating the override as a
|
||||
shared record directory. Durable candidates use its private sibling
|
||||
`release-candidates/` directory, never a checkout-local runtime path. A corrupt
|
||||
record from one workspace therefore cannot leak even its identifier or an
|
||||
integrity error into another workspace.
|
||||
|
||||
Each frozen plan step has an explicit `pending`, `running`, `succeeded`,
|
||||
`failed`, or `interrupted` state. Plan order remains a prerequisite: a later
|
||||
step is unavailable until earlier steps have succeeded. Exact attempt and
|
||||
resume/retry/reconciliation request identifiers are fingerprinted so delayed
|
||||
repetitions remain idempotent for the retained run's lifetime. These fingerprints are
|
||||
never evicted: the store fails closed before accepting more than 2,048 commands
|
||||
or 2,048 attempts and asks the operator to create a fresh run. The display
|
||||
record keeps at most 256 server-generated state events. Events contain only an
|
||||
enum event type, timestamp, step identifier, and bounded result code—never
|
||||
commands, process output, confirmation text, credentials, bearer tokens, or
|
||||
signing material.
|
||||
|
||||
Before a step can enter `running`, the store reserves the two command-ledger
|
||||
slots needed for worst-case recovery. It also projects the serialized record
|
||||
through start, finish/failure, resume, and the required terminal reconciliation
|
||||
or read-only retry; the start is rejected unless every required atomic write
|
||||
fits the record-size bound. An explicit resume consumes one slot and is
|
||||
accepted only while a persisted attempt is actually running. A mutating attempt
|
||||
always retains its final `effect_absent` or `effect_succeeded` slot;
|
||||
`unresolved` may be recorded at most once for that attempt and only when an
|
||||
additional ledger slot and serialized terminal-write capacity are available.
|
||||
Read-only interruption and known failure retain the slot and byte capacity
|
||||
needed to prepare a retry. Capacity exhaustion is therefore detected before
|
||||
starting an effect rather than stranding an ambiguous attempt.
|
||||
|
||||
An explicit resume after a process restart converts every persisted `running`
|
||||
step to `interrupted`; it never guesses whether an external effect happened.
|
||||
An interrupted read-only step can be prepared for retry. An interrupted
|
||||
mutating step remains unavailable until the operator independently reconciles
|
||||
local and remote state, selects `effect_absent`, `effect_succeeded`, or
|
||||
`unresolved`, and types `RECONCILE`. `effect_absent` prepares a safe new
|
||||
attempt, `effect_succeeded` advances the run without repeating the external
|
||||
effect, and `unresolved` keeps the run blocked. Each outcome emits a bounded,
|
||||
code-only state event. A known failed attempt can likewise be prepared for
|
||||
retry. The UI keeps unavailable controls visible and disabled.
|
||||
|
||||
Supported executors durably claim the step before invoking an effect. Exact
|
||||
attempt replays return the recorded outcome and never invoke the executor a
|
||||
second time. Successful repository preflight, version, commit, Core-bundle,
|
||||
tag, and push steps persist a bounded repository-state receipt. Version
|
||||
reconciliation requires aligned declarations in the same commit; commit
|
||||
reconciliation requires the expected single-parent release commit and only
|
||||
recognized metadata paths. Tag reconciliation independently requires an
|
||||
annotated local tag at the frozen HEAD; push reconciliation additionally
|
||||
requires both the remote annotated tag object and remote branch to match.
|
||||
Catalog generation persists
|
||||
only its server-issued opaque candidate ID and canonical catalog SHA-256, then
|
||||
re-resolves and re-hashes that private candidate before publication.
|
||||
|
||||
Repository capabilities are frozen into each plan unit (`python-package`,
|
||||
`webui-package`, `module-manifest`, `database-migrations`, `documentation`,
|
||||
`core-release-bundle`, and the universal `git-source`) and determine which
|
||||
steps appear. Internally aligned version changes are rendered deterministically
|
||||
from recognized TOML, JSON, lockfile, manifest, and package declarations.
|
||||
The manifest may use a literal version or a top-level literal `MODULE_VERSION`;
|
||||
the latter is updated without rewriting independently versioned interfaces.
|
||||
Computed or missing version declarations fail before any metadata is written.
|
||||
Pre-existing dirty worktrees remain visible but have no commit executor; the
|
||||
console never absorbs unrelated operator changes.
|
||||
|
||||
For mixed releases, module interface providers are ordered before consumers
|
||||
and Core is tagged last. The durable sequence creates and commits module
|
||||
metadata, creates local module tags, updates Core's selected WebUI references,
|
||||
regenerates the release lock against those local tags, commits/tags Core, and
|
||||
runs a receipt-bound alignment gate before exposing any atomic branch/tag push.
|
||||
A failed step stops later steps while preserving prior receipts for explicit
|
||||
retry or reconciliation.
|
||||
|
||||
Local module candidate creation deliberately does not require those candidates
|
||||
to be resolved already in Core's release lock: their annotated tags are inputs
|
||||
to the next lock-generation step. The internal tag helper applies this ordering
|
||||
only when no Core repository is selected and remote publication is disabled.
|
||||
Module version/lock consistency, manifest validity, clean/non-behind worktrees,
|
||||
and local/remote tag immutability checks still apply. Core candidate tagging
|
||||
continues to validate its own complete bundle, and every remote-publication
|
||||
preview and execution requires the selected modules to match Core's release
|
||||
input and resolved lock. A local candidate is therefore not publication
|
||||
approval; a stale Core lock blocks publication without changing remote refs.
|
||||
|
||||
The browser likewise retains the request identifier for an uncertain
|
||||
resume/retry/reconciliation response and replays it after reload. A successful
|
||||
replay selects the returned run state. Transport and server failures retain the
|
||||
identifier; a deterministic `4xx` rejection clears it so a stale command cannot
|
||||
poison a later attempt.
|
||||
|
||||
The run API is covered by the same local console token middleware as every
|
||||
other `/api/` route:
|
||||
|
||||
- `POST /api/release-runs` requires `request_id`, then idempotently rebuilds and
|
||||
freezes a selective plan only when that creation is not already known.
|
||||
- `GET /api/release-runs` lists bounded summaries ordered by immutable
|
||||
`created_at` and run identifier. `next_cursor` advances a stable descending
|
||||
traversal even while older runs are updated, without offset duplicates or
|
||||
skips. Unreadable entries
|
||||
remain a deterministic final section and are therefore reachable through
|
||||
pagination instead of displacing newer verified runs.
|
||||
- `GET /api/release-runs/{run_id}` reads and verifies one exact record.
|
||||
- `POST /api/release-runs/{run_id}/resume` records explicit recovery.
|
||||
- `POST /api/release-runs/{run_id}/steps/{step_id}/retry` prepares a failed or
|
||||
read-only interrupted step for another attempt.
|
||||
- `POST /api/release-runs/{run_id}/steps/{step_id}/reconcile` records a
|
||||
confirmed observed outcome for an interrupted mutating step.
|
||||
- `POST /api/release-runs/{run_id}/steps/{step_id}/execute` claims and invokes
|
||||
only the narrow executor declared by the immutable plan step. Durable release
|
||||
execution accepts only the registered `origin`, never a caller-selected
|
||||
remote.
|
||||
- `POST /api/release-runs/{run_id}/steps/{step_id}/preview` provides the
|
||||
non-mutating preview for receipt-bound catalog publication.
|
||||
|
||||
Run-storage errors are confined to the Durable Release Run section; dashboard
|
||||
and release-preview collection continue and the workflow guide points to the
|
||||
bounded storage remediation.
|
||||
|
||||
The run record is execution evidence only for a supported step whose durable
|
||||
claim and bounded result receipt were persisted. The console never infers
|
||||
success from a button click or process exit alone. An executor exception or a
|
||||
lost result write leaves the attempt interrupted and non-retriable until
|
||||
explicit recovery. Catalog publication persists the candidate and keyring
|
||||
hashes, exact website commit, annotated tag object and peeled commit, branch,
|
||||
tag name, and registered-origin digest. A successful reconciliation revalidates
|
||||
the candidate against the trust anchor in the frozen website parent, requires
|
||||
the exact deterministic catalog/keyring/module blobs and full commit delta, a
|
||||
sole frozen parent, and matching local and remote branch/tag identities. If any
|
||||
part cannot be proved, the run remains interrupted and may only be recorded as
|
||||
`unresolved`.
|
||||
|
||||
Dashboard collection is also fail closed. Unreadable Core version metadata or a
|
||||
malformed module contract is returned as a bounded `collection_errors` entry
|
||||
with a remediation, marks the dashboard blocked, and becomes a structured
|
||||
blocker in both full and selective release plans. The console does not silently
|
||||
omit a contract or turn these source errors into an HTTP 500.
|
||||
|
||||
The release-control area above the repository table is read-only and is meant
|
||||
to become the central release cockpit. It shows:
|
||||
|
||||
- local and published channel health
|
||||
- catalog/keyring drift
|
||||
- signature and trusted-key status
|
||||
- published module versions and refs
|
||||
- local checkout version drift against the catalog
|
||||
- catalog-declared interface compatibility
|
||||
|
||||
The target-version control can generate the next major, minor, or subversion
|
||||
from the current base version. Manual target input accepts explicit versions
|
||||
such as `0.2.0` or `0.2.0-alpha1`, but requires the first three version numbers
|
||||
to move forward.
|
||||
|
||||
Plain repository pushes are separate from catalog publication. `Preview Push`
|
||||
shows the selected repository push commands. `Push Selected` requires `PUSH` in
|
||||
the repository push confirmation field.
|
||||
|
||||
The source release panel retains `Preview Tag + Publish` as a non-mutating
|
||||
inspection. Its legacy `Create Tags` and `Publish Tags` controls stay visible
|
||||
but disabled; the corresponding mutation endpoint rejects apply requests.
|
||||
Creation and atomic branch/tag publication use the `TAG` and `PUBLISH`
|
||||
confirmations on the durable run steps. The gate requires an aligned target
|
||||
version, a clean named branch with a HEAD, and a checkout that is not behind. Existing
|
||||
local or remote tags must resolve to the selected HEAD and are never moved.
|
||||
The local and remote annotated tag objects must also be identical, not merely
|
||||
point at the same commit. Before any source tag is created, the console loads
|
||||
the cross-repository module registry. This release gate also rejects every
|
||||
user-facing workflow documentation topic that has no scope condition, or has
|
||||
an alternative condition without `required_scopes` or `any_scopes`.
|
||||
|
||||
The catalog workflow panel can also operate on the same selected rows for
|
||||
generation and preview. Its legacy apply/push controls stay disabled; durable
|
||||
publication consumes only the candidate receipt recorded by that run:
|
||||
|
||||
- `Generate` creates a signed candidate in the operator's private XDG state
|
||||
directory and records only an opaque candidate ID plus the canonical catalog
|
||||
SHA-256 in durable run state.
|
||||
- `Preview` validates a candidate and shows what would be copied into the
|
||||
website repository.
|
||||
- `Apply + Website Tag` remains visible but disabled outside a durable run.
|
||||
- `Push Website Release` remains visible but disabled outside a durable run.
|
||||
|
||||
Source release tags belong to Core or module repositories. Website catalog
|
||||
publication creates a separate catalog tag in the website repository; the UI
|
||||
names these independently to avoid confusing the two immutable references.
|
||||
|
||||
Candidate signing is fail-closed: every Core and module source ref in the
|
||||
complete post-update catalog must already have the requested annotated tag both
|
||||
locally and on the configured source remote. Both tags must be the same
|
||||
annotation object with version-aligned tagged package metadata. Selected tags
|
||||
must additionally resolve to the selected clean, version-aligned HEAD, and the
|
||||
signed `selected_units` record captures the peeled commit and tag-object
|
||||
identifiers. Create and publish the source tags before using `Generate`.
|
||||
|
||||
Catalog publication repeats that check for selected units and also verifies
|
||||
every Core and module source ref in the complete candidate, including entries
|
||||
preserved from the previous catalog. Historical refs need not be at the current
|
||||
worktree HEAD, but their local and remote annotated tags must match and their
|
||||
tagged installable package and module-manifest metadata must declare the catalog
|
||||
version. New selected releases additionally pass the stricter current-source
|
||||
alignment gate, including public runtime version declarations. A candidate
|
||||
cannot be applied or published while any referenced source tag is absent or
|
||||
inconsistent; the preview and API response list each repository and missing or
|
||||
invalid ref so the operator can repair the exact source releases first.
|
||||
|
||||
Every selected Python release must also supply its exact built wheel. Durable
|
||||
generation first verifies that the receipt-bound annotated tag is the same
|
||||
object locally and on the registered origin. It clones an isolated checkout at
|
||||
the receipt's exact commit and builds from that checkout, never from the mutable
|
||||
live worktree. Every authenticated base-catalog source is likewise cloned from
|
||||
its registered origin at an annotated release tag; only selected repositories
|
||||
contribute synthesized fields. The base catalog and keyring are read as one
|
||||
authenticated, exact private snapshot before synthesis.
|
||||
|
||||
Python wheels are built by a required Bubblewrap worker with no network,
|
||||
private temporary/home directories, a read-only exact source mount, no host
|
||||
home or file keys, cleared environment, fixed system tools, and CPU/address
|
||||
space/process/file-size limits. If a trusted Bubblewrap launcher is unavailable,
|
||||
generation fails closed. The worker must return one bounded regular wheel; the
|
||||
console copies it through no-follow descriptors into a fresh private file,
|
||||
`fsync`s it, then validates its package identity. Generation computes the
|
||||
archive SHA-256 and an install-stable
|
||||
payload identity from one bounded, regular-file descriptor and signs those
|
||||
values into `release.artifacts`. Updating a Python version without a matching
|
||||
wheel removes the stale identity. A source-only preview can still explain the
|
||||
gap, but apply, commit, tag, and push fail closed until every selected Python
|
||||
unit has a matching built-artifact identity. Repositories selected only for a
|
||||
meta/source tag do not need a Python artifact.
|
||||
|
||||
Candidate directories and files are operator-owned `0700`/`0600` state. Before
|
||||
any later release step consumes one, the executor re-resolves the opaque handle
|
||||
below the configured root and re-hashes the signed catalog against its persisted
|
||||
receipt. Shared roots, symlinks, channel path fragments, altered candidates, and
|
||||
unowned files are rejected.
|
||||
|
||||
The current same-host worker is a containment baseline, not the final hostile
|
||||
build-service boundary. `RLIMIT_FSIZE` is per file, and the worker does not yet
|
||||
have a size-limited filesystem/cgroup quota, a fresh kernel keyring, or a
|
||||
seccomp profile denying keyctl/request-key/ptrace/mount operations. A malicious
|
||||
backend could therefore exhaust scratch disk/inodes or target same-UID kernel
|
||||
facilities. Closing that denial-of-service/isolation gap requires a dedicated
|
||||
quota/cgroup worker with a fresh keyring and seccomp policy.
|
||||
|
||||
The server-owned wheel builds remain under the private candidate's `artifacts/`
|
||||
directory. This slice signs their identities but does **not** upload the wheel or
|
||||
add a download URL to the public catalog; the existing Git `python_ref` is source
|
||||
provenance and rebuilding it is not an equivalent artifact. Keep the private
|
||||
candidate until the exact wheels have been transferred through an approved
|
||||
deployment channel, verified against `archive_sha256`, consumed by the installer,
|
||||
and covered by its signed receipt. Public artifact transport and receipt-aware
|
||||
candidate cleanup remain separate release-lifecycle work.
|
||||
|
||||
Read-only provenance checks derive a same-host HTTPS Git URL from conventional
|
||||
SSH remotes when possible, with a non-interactive check of the configured remote
|
||||
as fallback. Source tag creation and atomic publication always use the explicit
|
||||
configured Git remote.
|
||||
|
||||
The default signing key is
|
||||
`$HOME/.config/govoplan/release-keys/release-key-1.pem` when no signing key is
|
||||
entered in the UI. Signing-key files must be regular, owned by the operator, and
|
||||
inaccessible to group/other users. The browser sends a configured key path only
|
||||
on the initial execution request; it never persists signing material in session
|
||||
storage, and request-ID recovery replays no key path.
|
||||
|
||||
Generate a selective release plan from the terminal:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/release/release-plan.py \
|
||||
--repo govoplan-files \
|
||||
--target-version 0.1.9 \
|
||||
--channel stable \
|
||||
--online
|
||||
```
|
||||
|
||||
`--online` compares the local catalog/keyring with the published channel and
|
||||
published keyring. Use `--remote-tags` only when the plan also needs to check
|
||||
Git remotes for tag existence; that can be slower across the full repository
|
||||
set.
|
||||
|
||||
Build a signed selective catalog candidate:
|
||||
|
||||
```sh
|
||||
KEY_DIR="$HOME/.config/govoplan/release-keys"
|
||||
ARTIFACT_DIR="$(mktemp -d)"
|
||||
|
||||
../govoplan-files/.venv/bin/python -m pip wheel \
|
||||
--no-deps \
|
||||
--no-build-isolation \
|
||||
--wheel-dir "$ARTIFACT_DIR" \
|
||||
../govoplan-files
|
||||
|
||||
./.venv/bin/python tools/release/release-catalog.py selective \
|
||||
--repo-version govoplan-files=0.1.9 \
|
||||
--python-artifact \
|
||||
"govoplan-files=$ARTIFACT_DIR/govoplan_files-0.1.9-py3-none-any.whl" \
|
||||
--channel stable \
|
||||
--catalog-signing-key "release-key-1=$KEY_DIR/release-key-1.pem"
|
||||
```
|
||||
|
||||
This writes candidate catalog/keyring files below
|
||||
`$XDG_STATE_HOME/govoplan/release-candidates/` (or
|
||||
`~/.local/state/govoplan/release-candidates/`), generates the browsable module
|
||||
directory below `modules/`, validates the signed candidate with the module
|
||||
installer validator, and reports whether the candidate catalog/keyring match the
|
||||
currently published channel. It does not publish to the website repository.
|
||||
|
||||
Regenerate the browsable module directory from an existing catalog/keyring:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/release/release-catalog.py module-directory \
|
||||
--catalog ../addideas-govoplan-website/public/catalogs/v1/channels/stable.json \
|
||||
--keyring ../addideas-govoplan-website/public/catalogs/v1/keyring.json \
|
||||
--output-dir runtime/module-directory-preview \
|
||||
--channel stable
|
||||
```
|
||||
|
||||
Preview publication of a reviewed candidate:
|
||||
|
||||
```sh
|
||||
CANDIDATE_ROOT="${XDG_STATE_HOME:-$HOME/.local/state}/govoplan/release-candidates"
|
||||
|
||||
./.venv/bin/python tools/release/release-catalog.py publish-candidate \
|
||||
--candidate-dir "$CANDIDATE_ROOT/stable-YYYYMMDD-HHMMSS" \
|
||||
--channel stable
|
||||
```
|
||||
|
||||
Apply the reviewed candidate into the website repository without pushing:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/release/release-catalog.py publish-candidate \
|
||||
--candidate-dir "$CANDIDATE_ROOT/stable-YYYYMMDD-HHMMSS" \
|
||||
--channel stable \
|
||||
--apply \
|
||||
--commit \
|
||||
--tag
|
||||
```
|
||||
|
||||
Push is a separate explicit flag:
|
||||
|
||||
```sh
|
||||
./.venv/bin/python tools/release/release-catalog.py publish-candidate \
|
||||
--candidate-dir "$CANDIDATE_ROOT/stable-YYYYMMDD-HHMMSS" \
|
||||
--channel stable \
|
||||
--apply \
|
||||
--commit \
|
||||
--tag \
|
||||
--push
|
||||
```
|
||||
|
||||
Publication validates the same in-memory catalog object that it writes; it does
|
||||
not copy a path that can change after validation. On commit, the console reads
|
||||
the catalog, keyring, and complete module directory back from the immutable Git
|
||||
tree and requires byte-for-byte equality with those validated objects. Tags and
|
||||
remote branch updates then reference that exact commit SHA rather than the
|
||||
mutable worktree `HEAD`.
|
||||
|
||||
For a full registry-backed release, first build a fresh private candidate using
|
||||
`release-catalog.py full-registry`. Pass `--selected-repository` for newly
|
||||
released HEAD-bound units, not every unchanged package in the full profile.
|
||||
The command independently checks all full-profile registry bytes and annotated
|
||||
tag provenance, then feeds this same strict `publish-candidate` transaction.
|
||||
It does not create Gitea runtime Releases or dispatch image builds. See
|
||||
[Full registry candidates / Vollständige Registry-Kandidaten](FULL_REGISTRY_CANDIDATES.md)
|
||||
for the complete EN/DE workflow and the narrowly scoped legacy keyring transition.
|
||||
|
||||
Published channels are expected below the public catalog base URL:
|
||||
|
||||
- `https://govoplan.add-ideas.de/catalogs/v1/channels/stable.json`
|
||||
- `https://govoplan.add-ideas.de/catalogs/v1/keyring.json`
|
||||
|
||||
The console treats channels as release artifacts. A package can advance without
|
||||
forcing every repository to the same tag, but channel publication must preserve
|
||||
the unchanged package versions, validate interface compatibility, sign the
|
||||
updated catalog, and keep the published keyring healthy.
|
||||
|
||||
When a selected module exposes a WebUI package, its requested version must also
|
||||
match Core's `webui/package.release.json` input and the resolved
|
||||
`package-lock.release.json` entry. The source-publication preflight, selective
|
||||
plan, and catalog-candidate writer all enforce this composition boundary;
|
||||
module-only local candidate tags use the staged order described above. Pins for modules
|
||||
that are not part of the selective release remain unchanged.
|
||||
|
||||
Release integration also enforces repository and composition version alignment
|
||||
and generates a CycloneDX SBOM from the resolved Python environment and the
|
||||
release WebUI lockfile. Catalog publication should attach that immutable SBOM
|
||||
and its digest to the corresponding Core/composition release.
|
||||
|
||||
Addresses and Notifications have current WebUI source contributions but are
|
||||
not part of the pinned v0.1.8 WebUI release composition because their v0.1.8
|
||||
tags predate those packages. They re-enter the release composition only through
|
||||
new, immutable module tags whose backend, manifest, frontend, and lock metadata
|
||||
pass the alignment gate.
|
||||
|
||||
Candidate publication verifies signatures against the already published
|
||||
keyring, not against keys supplied only by the candidate. A changed keyring
|
||||
must have its canonical SHA-256 embedded in the signed catalog. The public
|
||||
module-directory files are regenerated from that verified catalog and keyring
|
||||
at publication time; candidate-supplied directory files are never copied as
|
||||
authoritative provenance.
|
||||
|
||||
## Published Module Directory
|
||||
|
||||
The target release repository is an online, browsable module directory. The
|
||||
catalog remains the machine-readable channel entry point, but the published
|
||||
space should also expose a tree that operators and installations can inspect:
|
||||
|
||||
- `/catalogs/v1/channels/<channel>.json` describes the active channel state.
|
||||
- `/catalogs/v1/keyring.json` publishes trusted release signing keys.
|
||||
- `/catalogs/v1/modules/<module>/<version>/manifest.json` describes one
|
||||
published module version, including repository refs, package refs, contracts,
|
||||
compatibility windows, signatures, and available artifacts.
|
||||
- `/catalogs/v1/modules/<module>/index.json` lists available versions for
|
||||
one module.
|
||||
- `/catalogs/v1/modules/index.json` lists all published modules.
|
||||
|
||||
Gitea tags/releases remain the source release anchors. The public GovOPlaN
|
||||
catalog directory becomes the installation-facing release repository that points
|
||||
to those anchors and carries structured compatibility information.
|
||||
|
||||
Selective catalog candidates now include this directory tree. Publishing a
|
||||
candidate copies the channel catalog, keyring, and `modules/` tree into the
|
||||
website repository together.
|
||||
|
||||
Selective catalog generation can add a module that is not yet represented in
|
||||
the source channel. Initial entries are synthesized from the selected
|
||||
repository's `[project]` metadata, `govoplan.modules` entry point, and runtime
|
||||
`ModuleManifest`; there is no separate hand-maintained initial-entry list. The
|
||||
release gate requires the entry point to resolve inside the selected checkout,
|
||||
exact package/manifest/frontend version agreement, verified source-tag
|
||||
provenance, a non-conflicting module id, and complete required dependency and
|
||||
interface closure in the resulting candidate. Classification beyond the
|
||||
manifest-derived `official` tag remains an explicit later catalog curation
|
||||
step rather than an inferred business/service label.
|
||||
|
||||
## Direction
|
||||
|
||||
The console should grow in slices:
|
||||
|
||||
1. Read-only dashboard and next-action suggestions.
|
||||
2. Release plan builder that writes explicit JSON plans.
|
||||
3. Dry-run executor that shows exact commands and expected file changes.
|
||||
4. Apply executor for commit, tag, push, artifact build, signing, and catalog
|
||||
publication.
|
||||
5. Compatibility planner for manifest contracts and version ranges.
|
||||
6. Install/test workflow that validates a release from tags or catalog entries.
|
||||
|
||||
All mutation must stay explicit: plan first, dry run second, apply only after a
|
||||
clear confirmation.
|
||||
@@ -0,0 +1,280 @@
|
||||
# Scaling And Multi-Host Deployment
|
||||
|
||||
For the exact external handoff, least-privilege collector permissions and live
|
||||
two-node acceptance procedure, see
|
||||
[`PRODUCTION_TARGET_HANDOFF.md`](PRODUCTION_TARGET_HANDOFF.md).
|
||||
For a reproducible local or multi-hypervisor libvirt/K3s target, use
|
||||
[`KUBERNETES_TEST_LAB.md`](KUBERNETES_TEST_LAB.md).
|
||||
|
||||
## Implemented Contract
|
||||
|
||||
GovOPlaN now supports a stateless application tier backed by logically shared
|
||||
state services. The runtime roles are independently replaceable API, WebUI,
|
||||
worker, and scheduler processes. Every replica in one installation must use the
|
||||
same immutable release composition and the same:
|
||||
|
||||
- `GOVOPLAN_INSTALLATION_ID`;
|
||||
- PostgreSQL database;
|
||||
- Redis broker and coordination service;
|
||||
- `MASTER_KEY_B64` and deployment secret references;
|
||||
- enabled-module graph;
|
||||
- S3-compatible object-storage namespace.
|
||||
|
||||
The application tier must not use node-local durable business data in a
|
||||
multi-host deployment. Files owns managed file metadata while Core provides the
|
||||
storage-backend contract. Campaign build artifacts are stored under opaque
|
||||
object keys and workers read those objects from the shared backend. Temporary
|
||||
build and materialization directories may remain node-local because they are
|
||||
discardable.
|
||||
|
||||
Core validates three explicit state profiles:
|
||||
|
||||
| Profile | Supported shape | Storage rule |
|
||||
| --- | --- | --- |
|
||||
| `local` | One API and one worker process for development | Local filesystem permitted. |
|
||||
| `host-shared` | Multiple processes on one Docker host | A shared host volume is permitted; PostgreSQL and Redis are required. |
|
||||
| `shared` | Multiple independent hosts | PostgreSQL, Redis, and S3-compatible object storage are required. |
|
||||
|
||||
`shared` also requires a stable installation identifier. Module package
|
||||
mutation is blocked in this profile: build and verify a new immutable release,
|
||||
then roll the complete cluster to it.
|
||||
|
||||
## Same-Host Compose
|
||||
|
||||
The generated Compose bundle provides:
|
||||
|
||||
```text
|
||||
client -> TLS proxy -> HAProxy -> WebUI replicas -> HAProxy -> API replicas
|
||||
|
||||
API/worker/scheduler -> PostgreSQL
|
||||
-> Redis
|
||||
-> local volume, managed Garage, or external S3
|
||||
```
|
||||
|
||||
HAProxy discovers Compose replicas through Docker DNS and performs health-aware
|
||||
balancing without mounting the Docker socket. This improves concurrency and
|
||||
permits process replacement, but the Docker host and installer-managed stateful
|
||||
services remain single failure domains. Generated Compose therefore declares
|
||||
the `host-shared` state profile even when its shared storage happens to be an
|
||||
external S3 service. Its API backend checks `/health/ready`, so drain or
|
||||
coordination loss removes a replica from rotation. Container, load-balancer,
|
||||
and Kubernetes probes send the configured public host explicitly, keeping
|
||||
readiness compatible with strict trusted-host validation.
|
||||
|
||||
Managed Garage is a convenient single-node S3-compatible service. It is not a
|
||||
multi-host storage cluster. Use an independently operated Garage cluster or
|
||||
another S3-compatible service for the `shared` profile.
|
||||
|
||||
## Kubernetes Export
|
||||
|
||||
The deployment compiler exports a stateless Kubernetes runtime when PostgreSQL,
|
||||
Redis, and S3 are all external:
|
||||
|
||||
```sh
|
||||
python tools/deployment/govoplan-deploy.py render-kubernetes \
|
||||
--directory /srv/govoplan/default \
|
||||
--namespace govoplan \
|
||||
--secret-name govoplan-runtime \
|
||||
--tls-secret-name govoplan-tls \
|
||||
--s3-ca-secret-name govoplan-s3-ca \
|
||||
--ingress-class-name nginx \
|
||||
--output /srv/govoplan/default/kubernetes.json
|
||||
```
|
||||
|
||||
The export contains a Namespace, tokenless ServiceAccount, non-secret
|
||||
ConfigMap, API/WebUI/worker/scheduler Deployments, Services, Pod disruption
|
||||
budgets, Ingress, and a release-specific migration Job. It deliberately emits
|
||||
no Secret values, persistent volume, PostgreSQL, Redis, or object-store
|
||||
deployment. Export is rejected unless both release images use immutable
|
||||
`image@sha256:...` references.
|
||||
|
||||
Create the named Secret through the cluster's secret-management path. The
|
||||
command prints the exact required key contract. Review the generated
|
||||
`FORWARDED_ALLOW_IPS` value and replace it with the exact ingress-proxy network
|
||||
before production use.
|
||||
|
||||
When an external S3 endpoint is signed by a private CA, create the optional CA
|
||||
Secret with a `ca.crt` key and pass `--s3-ca-secret-name`. The renderer mounts
|
||||
that Secret read-only and sets `AWS_CA_BUNDLE` for API, worker, scheduler,
|
||||
migration and database-wait containers. It does not disable certificate
|
||||
verification or replace the WebUI trust store.
|
||||
|
||||
The generated containers run as non-root with a read-only root filesystem and
|
||||
an ephemeral `/tmp`. Celery Beat keeps its replaceable schedule database there;
|
||||
durable schedule definitions remain in shared state. The WebUI resolves its
|
||||
configured API Service when the container starts, so Kubernetes deployments do
|
||||
not inherit the Compose-only `load-balancer` hostname. Runtime Deployments wait
|
||||
for the exact dependency-resolved database migration heads before starting.
|
||||
The API exposes `/health/ready`, which fails while that API node is draining or
|
||||
cannot prove its runtime-coordination heartbeat.
|
||||
|
||||
Replicated API, WebUI, and worker Deployments use a hard hostname-spread
|
||||
constraint scoped to the current pod-template hash. A rollout therefore keeps
|
||||
each replica set distributed across independently schedulable nodes instead of
|
||||
allowing all replacement pods to settle on one node after the old set exits.
|
||||
|
||||
## Runtime Coordination
|
||||
|
||||
Each API and worker incarnation registers in PostgreSQL with its role, software
|
||||
version, module-composition hash, queue set, and heartbeat. Ops shows active,
|
||||
draining, stopped, and stale nodes and compares active counts with configured
|
||||
replica expectations.
|
||||
|
||||
An operator may request or cancel drain from Ops:
|
||||
|
||||
- API readiness becomes unavailable on the next heartbeat so the load balancer
|
||||
stops assigning new requests.
|
||||
- A worker stops consuming its configured queues and may finish work already
|
||||
claimed by that process.
|
||||
- A stale process incarnation cannot overwrite a replacement incarnation's
|
||||
heartbeat.
|
||||
- A coordination outage removes API readiness and cancels worker consumers;
|
||||
the existing incarnation must heartbeat successfully before either resumes.
|
||||
|
||||
Singleton work uses PostgreSQL-backed leases with monotonically increasing
|
||||
fencing tokens. The generated scheduler runs Celery beat through
|
||||
`govoplan_core.commands.fenced_run`; loss of its lease terminates the child and
|
||||
returns a distinct failure code. A fenced business operation must validate the
|
||||
same lease token immediately before committing its effect.
|
||||
|
||||
## Release Ordering
|
||||
|
||||
Use this order for every multi-replica rollout:
|
||||
|
||||
1. Verify immutable image identities, module composition, external state
|
||||
reachability, backup evidence, and the generated plan.
|
||||
2. Drain application replicas when the migration compatibility declaration
|
||||
requires it.
|
||||
3. Run the release-specific migration Job exactly once. PostgreSQL advisory
|
||||
locking serializes all Core and module migration tasks across competing
|
||||
deployment jobs.
|
||||
4. Let runtime init containers run `wait_for_database`. They wait for exact
|
||||
configured Alembic heads and never mutate schema.
|
||||
5. Roll API, workers, scheduler, and WebUI using health-aware replacement.
|
||||
6. Verify runtime composition, expected replica counts, queue consumers,
|
||||
object-storage round trips, and recovery status in Ops.
|
||||
|
||||
Applying the complete generated manifest is fail-closed: runtime pods remain in
|
||||
their init phase until the migration Job reaches the expected heads. A second
|
||||
release may be submitted concurrently, but advisory locking prevents concurrent
|
||||
schema mutation and each release has a distinct migration Job name.
|
||||
|
||||
## Storage Trust Boundary
|
||||
|
||||
Installer-managed Garage uses its exact generated endpoint. An arbitrary
|
||||
external S3 endpoint is accepted only when the deployment explicitly sets
|
||||
`FILE_STORAGE_S3_ENDPOINT_TRUSTED=true`; that endpoint must be a clean HTTPS
|
||||
origin without embedded credentials, query, fragment, or path. This is an
|
||||
operator trust declaration, not a user-controlled connector bypass. Operators
|
||||
remain responsible for DNS, certificate, network-egress, bucket-policy,
|
||||
versioning, and lifecycle controls.
|
||||
|
||||
## Capacity
|
||||
|
||||
- Set `GOVOPLAN_DB_CONNECTION_LIMIT` to the PostgreSQL role's effective
|
||||
connection limit. The Kubernetes export reserves
|
||||
`GOVOPLAN_DB_CONNECTION_RESERVE` connections and rejects a topology whose
|
||||
calculated rolling-update peak would exceed the remainder. The calculation
|
||||
includes API pools, every Celery parent and prefork child, the scheduler,
|
||||
migration, and one surge replica per deployment. Role-specific pool and
|
||||
overflow values are emitted into each workload rather than inherited from one
|
||||
unconstrained global default.
|
||||
- Scale workers by queue, with upper bounds based on external provider limits.
|
||||
`GOVOPLAN_WORKER_POOLS` may contain a JSON list of exact queue owners, for
|
||||
example:
|
||||
|
||||
```json
|
||||
[
|
||||
{"name":"delivery","queues":["send_email","append_sent"],"replicas":2,"concurrency":2},
|
||||
{"name":"platform","queues":["events","workflow","default"],"replicas":2,"concurrency":2}
|
||||
]
|
||||
```
|
||||
|
||||
Pool replica totals must equal `replicas.worker`, and the pools must cover
|
||||
`CELERY_QUEUES` exactly without duplicate ownership. Each pool receives its
|
||||
own Deployment, disruption budget, topology-spread selector, runtime identity,
|
||||
and declared concurrency.
|
||||
- Keep one fenced scheduler rather than load-balancing schedulers.
|
||||
- Increase WebUI replicas for asset/proxy capacity.
|
||||
- Measure request latency, database query time and locks, active connections,
|
||||
queue age, retry rate, storage latency, and provider throttling before adding
|
||||
replicas.
|
||||
|
||||
Workers compete for Redis-backed work and are not placed behind a load balancer.
|
||||
SMTP, IMAP, directory, connector, workflow, dataflow, and reporting queues often
|
||||
hit external-system limits before host CPU is exhausted.
|
||||
|
||||
## What This Does Not Claim
|
||||
|
||||
The implemented contract provides stateless runtime placement, shared artifact
|
||||
access, node visibility, drain controls, migration serialization, and scheduler
|
||||
fencing. It does not by itself provide:
|
||||
|
||||
- a highly available PostgreSQL, Redis, or object-store deployment;
|
||||
- automatic PostgreSQL/object backup creation or point-in-time recovery;
|
||||
- autoscaling policy;
|
||||
- central logs, metrics, traces, or alert routing;
|
||||
- certificate portability between independently managed ingress providers;
|
||||
- automatic reconciliation of every possible module side effect;
|
||||
- a service-level availability guarantee.
|
||||
|
||||
The deployer verifies and gates migrations on signed coordinated backup and
|
||||
isolated-restore evidence, but backup capture and restoration remain owned by
|
||||
the selected state-service providers. Before claiming high
|
||||
availability, drill replica loss, rolling replacement, session continuity, job
|
||||
redelivery, scheduler failover, migration exclusion, object-store outage, and a
|
||||
coordinated database/object/key restore. Recovery rules and evidence are
|
||||
defined in [Recovery And Rollback Guarantees](RECOVERY_AND_ROLLBACK_GUARANTEES.md).
|
||||
|
||||
## Worker Delivery Evidence
|
||||
|
||||
The module-matrix workflow runs `tools/checks/worker-runtime-drill.py` against a
|
||||
real isolated Redis database. The drill starts supervised Celery worker
|
||||
processes and records four guarantees without accessing tenant data:
|
||||
|
||||
1. a task published through the broker is consumed exactly once;
|
||||
2. an application retry is delivered again and completes;
|
||||
3. warm `SIGTERM` lets an in-flight late-ack task complete before shutdown; and
|
||||
4. loss of a worker after task start causes the unacknowledged task to be
|
||||
redelivered after the configured visibility timeout.
|
||||
|
||||
Run the same drill with the release Python environment and target Redis before
|
||||
promoting a worker composition. Use a dedicated Redis database, retain the JSON
|
||||
evidence, and set `CELERY_VISIBILITY_TIMEOUT_SECONDS` above the longest supported
|
||||
business-task duration. The short visibility timeout used by CI is an isolated
|
||||
test setting, not a production recommendation.
|
||||
|
||||
```bash
|
||||
GOVOPLAN_WORKER_DRILL_REDIS_URL=redis://redis.example.test:6379/15 \
|
||||
.venv/bin/python tools/checks/worker-runtime-drill.py \
|
||||
--output evidence/worker-runtime.json
|
||||
```
|
||||
|
||||
## Live Multi-Host Evidence
|
||||
|
||||
After deploying a pinned release on at least two Kubernetes nodes, create an API
|
||||
key with Ops read scope and run:
|
||||
|
||||
```bash
|
||||
export GOVOPLAN_OPS_API_KEY='...'
|
||||
python tools/deployment/govoplan-deploy.py verify-kubernetes \
|
||||
--directory /srv/govoplan/installation \
|
||||
--namespace govoplan
|
||||
```
|
||||
|
||||
The command fails unless API and WebUI pods are ready on at least two nodes,
|
||||
all rendered deployments are available, Ops reports a consistent release and
|
||||
module composition, every declared worker queue is served, and the calculated
|
||||
database peak remains below its budget. It writes a private, sanitized JSON
|
||||
record under the installation evidence directory and never retains the API key.
|
||||
|
||||
Use `--exercise-api-pod-loss` in an approved drill window to delete one API pod,
|
||||
observe the public readiness path continuously, and record its replacement.
|
||||
Generated API workloads use a ten-second pre-stop drain so Kubernetes can remove
|
||||
the terminating endpoint from ingress and service routing before Uvicorn exits.
|
||||
Do not remove or shorten this drain without repeating the public-path pod-loss
|
||||
test against the target ingress controller and network implementation.
|
||||
This proves the bounded stateless-node-loss slice only. Session continuity,
|
||||
accepted-job redelivery, state-service failover, and coordinated restore remain
|
||||
separate target exercises whose signed evidence is governed by
|
||||
`docs/operations/TARGET_MATURITY_EVIDENCE_RUNBOOK.md` and GovOPlaN #37.
|
||||
@@ -50,6 +50,10 @@ accept findings, but scanner execution errors and malformed JSON/SARIF reports
|
||||
always fail the run. The manifest lists the expected, present, and missing
|
||||
reports for that invocation. Validation and checksums use that explicit set, so
|
||||
reusing a report directory cannot make stale output look like part of a new run.
|
||||
It also contains `coverage_status` and structured `scanner_coverage` entries.
|
||||
Every required scanner is recorded as `no-findings`, `findings`,
|
||||
`scanner-failure`, or `skipped`; this makes an incomplete local run visible
|
||||
without treating it as a clean audit.
|
||||
|
||||
The wrapper tags the toolbox image by a fingerprint of the Dockerfile,
|
||||
`requirements-audit.txt`, and the Semgrep smoke-test inputs. If those inputs have
|
||||
@@ -90,6 +94,12 @@ tools/checks/security-audit/run.sh --mode quick --scope current --update --build
|
||||
- `ci`: quick plus Semgrep public registry rulesets, Trivy, pip-audit, npm audit.
|
||||
- `full`: ci plus OSV-Scanner, jscpd, Radon, and Xenon.
|
||||
|
||||
The Gitea workflow uses `full` mode so its coverage contract includes every
|
||||
scanner above. Missing scanners fail strict runs and all Actions runs, even
|
||||
while actual findings remain report-only. Local report-only runs may finish
|
||||
with missing tools for diagnostics, but their manifest is marked
|
||||
`coverage_status: incomplete`.
|
||||
|
||||
Semgrep and Trivy are invoked with finding-sensitive exit codes. Their exit 1
|
||||
is therefore a finding under the wrapper contract; higher exit codes, missing
|
||||
output, invalid JSON/SARIF, and scanner error payloads are execution failures.
|
||||
@@ -103,7 +113,7 @@ baseline.
|
||||
|
||||
## Gating
|
||||
|
||||
The initial Gitea workflow runs in report-only mode:
|
||||
The Gitea workflow currently runs findings in report-only mode:
|
||||
|
||||
```bash
|
||||
SECURITY_AUDIT_FAIL_ON_FINDINGS=0
|
||||
@@ -119,13 +129,13 @@ SECURITY_AUDIT_FAIL_ON_FINDINGS=1
|
||||
or run locally with:
|
||||
|
||||
```bash
|
||||
tools/checks/security-audit/run.sh --mode ci --scope current --strict
|
||||
tools/checks/security-audit/run.sh --mode full --scope govoplan --strict
|
||||
```
|
||||
|
||||
## Audit Burndown Workflow
|
||||
|
||||
Treat Gitea issues as the active audit state. A full GovOPlaN audit should
|
||||
produce one tracker issue in `add-ideas/govoplan` and child issues in the
|
||||
produce one tracker issue in `GovOPlaN/govoplan` and child issues in the
|
||||
repository that owns each fix.
|
||||
|
||||
Use the tracker issue for:
|
||||
@@ -153,16 +163,33 @@ important scanner counts in the tracker issue.
|
||||
|
||||
The jscpd step is intentionally scoped to application and test source. It
|
||||
excludes documentation snippets, package manifests, generated translations,
|
||||
public SVG assets, workflow YAML, and declarative backend schema JSON because
|
||||
those reports produce metadata or asset repetition rather than actionable source
|
||||
duplication. Keep exclusions narrow and create child issues for source-code
|
||||
clusters that cross module ownership or make behavior harder to change safely.
|
||||
public SVG assets and catalog output, workflow YAML, declarative backend schema
|
||||
JSON, the generated migration baseline, and mirrored development migration
|
||||
directories because those reports produce metadata or generated-source
|
||||
repetition rather than actionable source duplication. Keep exclusions narrow
|
||||
and create child issues for source-code clusters that cross module ownership or
|
||||
make behavior harder to change safely.
|
||||
|
||||
The 2026-08-02 full-workspace baseline covered 64 repositories and reported
|
||||
1.82% duplicated lines before those generated-source exclusions. The reviewed
|
||||
high-value clusters were catalog acceptance persistence, release publication
|
||||
result assembly, and local WebUI JSON mutation wrappers. Similar Dataflow and
|
||||
Workflow graph/governance code remains independently owned until its shared
|
||||
contract is stable enough for Core; a raw similarity score is not grounds for a
|
||||
module-to-module dependency.
|
||||
|
||||
## Image Freshness
|
||||
|
||||
The regular `Security Audit` workflow reuses the fingerprinted toolbox image
|
||||
when the Docker daemon is persistent, which is the normal case for the
|
||||
self-hosted Gitea runner using the host Docker socket. The separate
|
||||
self-hosted Gitea runner using the host Docker socket. Trusted push, schedule,
|
||||
and manual runs scan all registered repositories; authenticated SSH is used
|
||||
only for the private website repository. The wrapper inspects the Actions job
|
||||
mount table and forwards only the narrowest writable mount covering the audit
|
||||
scope; it never inherits the job's Docker socket or unrelated runner mounts.
|
||||
Pull-request audit runs stay disabled while the audit runner exposes its host
|
||||
Docker socket: PR-controlled audit code must run on a disposable or rootless
|
||||
runner without host-socket access. The separate
|
||||
`Security Audit Toolbox Update` workflow runs weekly with
|
||||
`SECURITY_AUDIT_UPDATE=1`; it pulls current base images and re-resolves the
|
||||
allowed tool version ranges into a refreshed local image.
|
||||
@@ -0,0 +1,157 @@
|
||||
# Target Maturity Evidence Runbook
|
||||
|
||||
For authority-key generation, container isolation and the concrete inputs that
|
||||
must be supplied by the target owner and independent production approver, see
|
||||
[`PRODUCTION_TARGET_HANDOFF.md`](PRODUCTION_TARGET_HANDOFF.md).
|
||||
|
||||
This runbook turns retained target-environment results into a sanitized,
|
||||
signed GovOPlaN capability-fit proof. It does not make a deployment suitable,
|
||||
certified, supported, or production-approved by itself. The proof records what
|
||||
independent authorities assessed against one exact installed release.
|
||||
|
||||
## Roles and custody
|
||||
|
||||
Use separate trust domains for release signing, installation receipts,
|
||||
boundary assessment, and production approval. A private proof key must be
|
||||
provisioned outside the assessed application and its matching public key must
|
||||
already exist in a separately managed
|
||||
`capability-fit-proof-authority-keyring.schema.json` document. Do not store
|
||||
private keys, raw reports, credentials, personal data, backup material, or
|
||||
target endpoints in Git.
|
||||
|
||||
Each authority key lists only the scopes that role may attest. At least one
|
||||
supplied signing key must cover every claim, and the issuer rejects a key that:
|
||||
|
||||
- is absent, inactive, expired, or revoked in the authority keyring;
|
||||
- expires before the proof;
|
||||
- does not match its independently provisioned public key;
|
||||
- reuses release-catalog or installer-authority key material.
|
||||
|
||||
## Target run
|
||||
|
||||
Install one pinned catalog release and issue its installed-composition receipt
|
||||
with `tools/assessments/installer-receipt.py`. Exercise the actual target
|
||||
topology, including:
|
||||
|
||||
- PostgreSQL and Redis as shared state services;
|
||||
- shared S3-compatible object storage;
|
||||
- at least two stateless API replicas and the intended worker topology;
|
||||
- fenced singleton work, ingress, certificates, proxy headers, and the real
|
||||
network/trust boundary;
|
||||
- provider health and freshness for every provider required by the product;
|
||||
- monitoring, alerting, failure response, accessibility, privacy, and security
|
||||
controls;
|
||||
- backup, isolated restore, failed-deployment rollback, and forward recovery.
|
||||
|
||||
For the recovery claim, retain the observed recovery point, measured RPO and
|
||||
RTO, database/object-store consistency result, and semantic reconstruction of
|
||||
the institutional and Service/Form reference journeys. Measure RPO from the
|
||||
last acknowledged durable effect that survives recovery and RTO until service
|
||||
health plus semantic reconstruction pass. Failed runs are evidence too and
|
||||
must use a negative result.
|
||||
|
||||
The private reports stay in the approved evidence store. Give each report an
|
||||
opaque artifact ID and each evaluated control a versioned opaque control ID.
|
||||
|
||||
## Private claim manifest
|
||||
|
||||
Create a private manifest conforming to
|
||||
`capability-fit-boundary-run.schema.json`. Relative artifact paths resolve from
|
||||
the manifest directory. Paths are read and hashed by the issuer and are never
|
||||
copied into the signed output.
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "./capability-fit-boundary-run.schema.json",
|
||||
"schema_version": "0.1.0",
|
||||
"evidence_kind": "govoplan.capability-fit-boundary-run",
|
||||
"proof_id": "target:production:20260802",
|
||||
"expires_at": "2026-09-01T00:00:00Z",
|
||||
"claims": [
|
||||
{
|
||||
"scope": "target_environment",
|
||||
"result": "passed",
|
||||
"control_ids": ["topology:shared-state-v1"],
|
||||
"artifacts": [
|
||||
{"artifact_id": "target:run-20260802", "path": "private/target.json"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"scope": "recovery",
|
||||
"result": "passed",
|
||||
"control_ids": ["recovery:restore-rollback-v1"],
|
||||
"artifacts": [
|
||||
{"artifact_id": "recovery:run-20260802", "path": "private/recovery.json"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Reference readiness needs positive `target_environment`, `accessibility`,
|
||||
`privacy`, `security`, `operations`, and `recovery` claims. Provider acceptance
|
||||
and production approval are separate scopes. A production-approval authority
|
||||
must not approve its own unreviewed target run.
|
||||
|
||||
## Issue and verify
|
||||
|
||||
Issue only while the signed installer observation is current. Repeat
|
||||
`--signing-key` when multiple independent roles are needed. The command first
|
||||
verifies the catalog, independent catalog trust root, exact installed payload,
|
||||
installer receipt, and installer authority. It then hashes artifacts, signs the
|
||||
sanitized proof, verifies it immediately, and writes both proof and review with
|
||||
atomic private-file permissions.
|
||||
|
||||
```bash
|
||||
./.venv/bin/python tools/assessments/boundary-evidence.py \
|
||||
--assessment /srv/govoplan/assessment.json \
|
||||
--catalog /srv/govoplan/catalogs/stable.json \
|
||||
--keyring /srv/govoplan/catalogs/keyring.json \
|
||||
--trusted-keyring /srv/govoplan/trust/catalog-keyring.json \
|
||||
--installed-evidence /srv/govoplan/evidence/installed.json \
|
||||
--installer-receipt /srv/govoplan/evidence/installer-receipt.json \
|
||||
--installer-authority-keyring /srv/govoplan/trust/installer-authorities.json \
|
||||
--claims /srv/govoplan/evidence/private/target-run.json \
|
||||
--authority-keyring /srv/govoplan/trust/proof-authorities.json \
|
||||
--signing-key authority-target=/run/secrets/target-proof-ed25519.pem \
|
||||
--output /srv/govoplan/evidence/target-proof.json \
|
||||
--review-output /srv/govoplan/evidence/target-proof-review.json
|
||||
```
|
||||
|
||||
Add `--expected-external-provider-subject provider-production` when the claim
|
||||
manifest contains `external_providers`. This value is an opaque deployment ID,
|
||||
not a URL or credential.
|
||||
|
||||
## Promotion gate
|
||||
|
||||
The general verifier can now be made admission-enforcing. These switches return
|
||||
a blocking exit status when a required claim is absent, expired, negative,
|
||||
revoked, or bound to another assessment, release, installation, or subject:
|
||||
|
||||
```bash
|
||||
./.venv/bin/python tools/assessments/capability-fit.py \
|
||||
--catalog /srv/govoplan/catalogs/stable.json \
|
||||
--keyring /srv/govoplan/catalogs/keyring.json \
|
||||
--trusted-keyring /srv/govoplan/trust/catalog-keyring.json \
|
||||
--installed-evidence /srv/govoplan/evidence/installed.json \
|
||||
--installer-receipt /srv/govoplan/evidence/installer-receipt.json \
|
||||
--installer-authority-keyring /srv/govoplan/trust/installer-authorities.json \
|
||||
--boundary-evidence /srv/govoplan/evidence/target-proof.json \
|
||||
--boundary-authority-keyring /srv/govoplan/trust/proof-authorities.json \
|
||||
--require-reference-readiness \
|
||||
--require-production-approval \
|
||||
--output /srv/govoplan/evidence/admission-review.json
|
||||
```
|
||||
|
||||
Use `--require-external-provider-proof` as well when the promoted product
|
||||
requires an external provider. Live admission must not use
|
||||
`--verification-time`; that switch is only for clearly labelled historical
|
||||
review.
|
||||
|
||||
## Renewal and failure
|
||||
|
||||
Renew evidence after release, installed composition, deployment, control, or
|
||||
provider changes and before expiry. Revoke an authority key immediately after
|
||||
custody loss and rerun the affected assessment with a new independent key.
|
||||
Never copy a previous positive claim to a new release. Preserve negative and
|
||||
superseded receipts according to the approved evidence-retention policy.
|
||||
@@ -0,0 +1,91 @@
|
||||
# WebUI release dependency installer retries
|
||||
|
||||
## English
|
||||
|
||||
This operational note covers
|
||||
[`install-webui-release-dependencies.sh`](../../tools/release/install-webui-release-dependencies.sh)
|
||||
and the exit-status repair tracked in
|
||||
[Meta #54](https://git.add-ideas.de/GovOPlaN/govoplan/issues/54).
|
||||
It applies to release administrators using the legacy runtime WebUI installer;
|
||||
there are no new application settings, permissions, or end-user workflows.
|
||||
|
||||
Each retried npm install or Git clone has at most three attempts. The installer
|
||||
waits 10 seconds after the first failure and 20 seconds after the second, and
|
||||
continues immediately after success. If all attempts fail, it exits with the
|
||||
last command's nonzero status. Its `set -e` execution stops before subsequent
|
||||
installation stages; callers using `set -e` also stop before subsequent work.
|
||||
Previously, the retry helper could report success after three failures because
|
||||
it captured the status of a completed `if` statement instead of the command.
|
||||
|
||||
On exhaustion, inspect the npm or Git error and correct the reported cause
|
||||
before rerunning the installation. The temporary dependency workspace is
|
||||
removed on exit. Earlier changes to `package.json`, removal of `package-lock.json`,
|
||||
cache cleaning, and completed dependency installations are not rolled back;
|
||||
prepare a fresh disposable release workspace when a clean retry is required.
|
||||
|
||||
The repair preserves the existing retry count, backoff, cache behavior, and
|
||||
peer-resolution flags. It does not lift the runtime publication hold tracked in
|
||||
[Meta #52](https://git.add-ideas.de/GovOPlaN/govoplan/issues/52).
|
||||
Review the historical `--legacy-peer-deps` workaround separately before lifting
|
||||
that hold. Strict disposable Git-release and signed catalog verification do not
|
||||
use this installer; strict release verification must not bypass peer checks.
|
||||
See [Package Registry Releases](PACKAGE_REGISTRY_RELEASES.md) for release context.
|
||||
|
||||
Run the isolated regression suite from the meta repository:
|
||||
|
||||
```sh
|
||||
python3 -m unittest -v tests.test_webui_release_dependency_retries
|
||||
```
|
||||
|
||||
The suite executes the actual Bash installer and a caller using `set -e`, with
|
||||
local npm, Git, Node, and sleep stubs. It covers success on attempts one, two, and
|
||||
three, final failure status, backoff, and termination at each retry call site.
|
||||
It performs no network access, real waiting, or changes to the real npm cache.
|
||||
It checks shell control flow, not package resolution or runtime publication.
|
||||
|
||||
## Deutsch
|
||||
|
||||
Dieser Betriebshinweis beschreibt
|
||||
[`install-webui-release-dependencies.sh`](../../tools/release/install-webui-release-dependencies.sh)
|
||||
und die unter [Meta #54](https://git.add-ideas.de/GovOPlaN/govoplan/issues/54)
|
||||
erfasste Korrektur des Rückgabestatus. Er richtet sich an Release-Administratoren,
|
||||
die den bisherigen WebUI-Installer für Laufzeit-Releases verwenden. Neue
|
||||
Anwendungseinstellungen, Berechtigungen oder Endanwenderabläufe entstehen nicht.
|
||||
|
||||
Jede wiederholte npm-Installation und jeder Git-Klon erhält höchstens drei
|
||||
Versuche. Nach dem ersten Fehlschlag wartet der Installer 10 Sekunden, nach dem
|
||||
zweiten 20 Sekunden; nach einem Erfolg fährt er sofort fort. Scheitern alle
|
||||
Versuche, endet er mit dem letzten von null verschiedenen Rückgabestatus.
|
||||
Durch `set -e` werden nachfolgende Installationsschritte nicht ausgeführt;
|
||||
auch aufrufende Skripte mit `set -e` brechen vor ihren nächsten Schritten ab.
|
||||
Bisher konnte die Hilfsfunktion nach drei Fehlschlägen Erfolg melden, weil sie
|
||||
den Status der abgeschlossenen `if`-Anweisung statt des Befehls übernahm.
|
||||
|
||||
Prüfen Sie nach dem Abbruch die npm- oder Git-Fehlermeldung und beheben Sie deren
|
||||
Ursache vor einem erneuten Installationslauf. Das temporäre Verzeichnis für
|
||||
Abhängigkeiten wird beim Beenden entfernt. Vorherige Änderungen an `package.json`,
|
||||
das Entfernen von `package-lock.json`, die Cache-Bereinigung und abgeschlossene
|
||||
Installationen werden nicht zurückgerollt. Bereiten Sie bei Bedarf einen neuen
|
||||
temporären Release-Arbeitsbereich für einen sauberen Wiederholungslauf vor.
|
||||
|
||||
Die Korrektur erhält Anzahl und Wartezeiten der Versuche, Cache-Verhalten und
|
||||
Optionen zur Peer-Auflösung. Die Sperre für Laufzeitveröffentlichungen aus
|
||||
[Meta #52](https://git.add-ideas.de/GovOPlaN/govoplan/issues/52) bleibt bestehen.
|
||||
Der bisherige Einsatz von `--legacy-peer-deps` muss vor ihrer Aufhebung gesondert
|
||||
geprüft werden. Die strenge Git-Release-Prüfung in einem temporären Arbeitsbereich
|
||||
und die Prüfung signierter Kataloge verwenden diesen Installer nicht; die strenge
|
||||
Release-Prüfung darf Peer-Prüfungen nicht umgehen. Weitere Zusammenhänge erläutert
|
||||
[Package Registry Releases](PACKAGE_REGISTRY_RELEASES.md).
|
||||
|
||||
Führen Sie die isolierten Regressionstests im Meta-Repository aus:
|
||||
|
||||
```sh
|
||||
python3 -m unittest -v tests.test_webui_release_dependency_retries
|
||||
```
|
||||
|
||||
Die Tests führen den tatsächlichen Bash-Installer und ein aufrufendes Skript mit
|
||||
`set -e` aus. Lokale Testprogramme ersetzen npm, Git, Node und sleep. Geprüft werden
|
||||
Erfolge im ersten, zweiten und dritten Versuch, der letzte Fehlerstatus,
|
||||
Warteintervalle und der Abbruch an jeder Aufrufstelle. Es gibt keine
|
||||
Netzwerkzugriffe, echten Wartezeiten oder Änderungen am tatsächlichen npm-Cache.
|
||||
Die Tests prüfen den Shell-Ablauf, nicht die Paketauflösung oder Veröffentlichung.
|
||||
@@ -8,11 +8,11 @@ The same pattern is reusable outside GovOPlaN for any project where Codex works
|
||||
|
||||
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.git`). Override inference when needed:
|
||||
The scripts infer this repository from `origin` (`git@git.add-ideas.de:GovOPlaN/govoplan.git`). Override inference when needed:
|
||||
|
||||
```bash
|
||||
export GITEA_URL=https://git.add-ideas.de
|
||||
export GITEA_OWNER=add-ideas
|
||||
export GITEA_OWNER=GovOPlaN
|
||||
export GITEA_REPO=govoplan
|
||||
export GITEA_TOKEN=...
|
||||
```
|
||||
@@ -23,7 +23,7 @@ The API scripts also read `GITEA_*` values from the target repository's `.env` f
|
||||
GITEA_TOKEN=...
|
||||
# Optional if origin inference is not enough:
|
||||
GITEA_URL=https://git.add-ideas.de
|
||||
GITEA_OWNER=add-ideas
|
||||
GITEA_OWNER=GovOPlaN
|
||||
GITEA_REPO=govoplan
|
||||
```
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# GovOPlaN Repository Index
|
||||
|
||||
Generated from `repositories.json`. Use that JSON file as the machine-readable source of truth; this page is the human-readable link index.
|
||||
|
||||
## System
|
||||
|
||||
| Repository | Subtype | Local path | Gitea |
|
||||
| --- | --- | --- | --- |
|
||||
| `govoplan` | `meta` | `../govoplan` | [govoplan](https://git.add-ideas.de/GovOPlaN/govoplan) |
|
||||
| `govoplan-core` | `kernel` | `../govoplan-core` | [govoplan-core](https://git.add-ideas.de/GovOPlaN/govoplan-core) |
|
||||
|
||||
## Module
|
||||
|
||||
| Repository | Subtype | Local path | Gitea |
|
||||
| --- | --- | --- | --- |
|
||||
| `govoplan-access` | `platform` | `../govoplan-access` | [govoplan-access](https://git.add-ideas.de/GovOPlaN/govoplan-access) |
|
||||
| `govoplan-addresses` | `domain` | `../govoplan-addresses` | [govoplan-addresses](https://git.add-ideas.de/GovOPlaN/govoplan-addresses) |
|
||||
| `govoplan-admin` | `platform` | `../govoplan-admin` | [govoplan-admin](https://git.add-ideas.de/GovOPlaN/govoplan-admin) |
|
||||
| `govoplan-appointments` | `domain` | `../govoplan-appointments` | [govoplan-appointments](https://git.add-ideas.de/GovOPlaN/govoplan-appointments) |
|
||||
| `govoplan-approvals` | `domain` | `../govoplan-approvals` | [govoplan-approvals](https://git.add-ideas.de/GovOPlaN/govoplan-approvals) |
|
||||
| `govoplan-assets` | `domain` | `../govoplan-assets` | [govoplan-assets](https://git.add-ideas.de/GovOPlaN/govoplan-assets) |
|
||||
| `govoplan-audit` | `platform` | `../govoplan-audit` | [govoplan-audit](https://git.add-ideas.de/GovOPlaN/govoplan-audit) |
|
||||
| `govoplan-booking` | `domain` | `../govoplan-booking` | [govoplan-booking](https://git.add-ideas.de/GovOPlaN/govoplan-booking) |
|
||||
| `govoplan-calendar` | `domain` | `../govoplan-calendar` | [govoplan-calendar](https://git.add-ideas.de/GovOPlaN/govoplan-calendar) |
|
||||
| `govoplan-campaign` | `domain` | `../govoplan-campaign` | [govoplan-campaign](https://git.add-ideas.de/GovOPlaN/govoplan-campaign) |
|
||||
| `govoplan-cases` | `domain` | `../govoplan-cases` | [govoplan-cases](https://git.add-ideas.de/GovOPlaN/govoplan-cases) |
|
||||
| `govoplan-certificates` | `domain` | `../govoplan-certificates` | [govoplan-certificates](https://git.add-ideas.de/GovOPlaN/govoplan-certificates) |
|
||||
| `govoplan-committee` | `domain` | `../govoplan-committee` | [govoplan-committee](https://git.add-ideas.de/GovOPlaN/govoplan-committee) |
|
||||
| `govoplan-consultation` | `domain` | `../govoplan-consultation` | [govoplan-consultation](https://git.add-ideas.de/GovOPlaN/govoplan-consultation) |
|
||||
| `govoplan-contracts` | `domain` | `../govoplan-contracts` | [govoplan-contracts](https://git.add-ideas.de/GovOPlaN/govoplan-contracts) |
|
||||
| `govoplan-dashboard` | `platform` | `../govoplan-dashboard` | [govoplan-dashboard](https://git.add-ideas.de/GovOPlaN/govoplan-dashboard) |
|
||||
| `govoplan-dataflow` | `platform` | `../govoplan-dataflow` | [govoplan-dataflow](https://git.add-ideas.de/GovOPlaN/govoplan-dataflow) |
|
||||
| `govoplan-datasources` | `platform` | `../govoplan-datasources` | [govoplan-datasources](https://git.add-ideas.de/GovOPlaN/govoplan-datasources) |
|
||||
| `govoplan-decisions` | `domain` | `../govoplan-decisions` | [govoplan-decisions](https://git.add-ideas.de/GovOPlaN/govoplan-decisions) |
|
||||
| `govoplan-dms` | `domain` | `../govoplan-dms` | [govoplan-dms](https://git.add-ideas.de/GovOPlaN/govoplan-dms) |
|
||||
| `govoplan-dist-lists` | `domain` | `../govoplan-dist-lists` | [govoplan-dist-lists](https://git.add-ideas.de/GovOPlaN/govoplan-dist-lists) |
|
||||
| `govoplan-docs` | `platform` | `../govoplan-docs` | [govoplan-docs](https://git.add-ideas.de/GovOPlaN/govoplan-docs) |
|
||||
| `govoplan-encryption` | `platform` | `../govoplan-encryption` | [govoplan-encryption](https://git.add-ideas.de/GovOPlaN/govoplan-encryption) |
|
||||
| `govoplan-erp` | `domain` | `../govoplan-erp` | [govoplan-erp](https://git.add-ideas.de/GovOPlaN/govoplan-erp) |
|
||||
| `govoplan-evaluation` | `domain` | `../govoplan-evaluation` | [govoplan-evaluation](https://git.add-ideas.de/GovOPlaN/govoplan-evaluation) |
|
||||
| `govoplan-facilities` | `domain` | `../govoplan-facilities` | [govoplan-facilities](https://git.add-ideas.de/GovOPlaN/govoplan-facilities) |
|
||||
| `govoplan-files` | `domain` | `../govoplan-files` | [govoplan-files](https://git.add-ideas.de/GovOPlaN/govoplan-files) |
|
||||
| `govoplan-forms` | `domain` | `../govoplan-forms` | [govoplan-forms](https://git.add-ideas.de/GovOPlaN/govoplan-forms) |
|
||||
| `govoplan-forms-runtime` | `platform` | `../govoplan-forms-runtime` | [govoplan-forms-runtime](https://git.add-ideas.de/GovOPlaN/govoplan-forms-runtime) |
|
||||
| `govoplan-grants` | `domain` | `../govoplan-grants` | [govoplan-grants](https://git.add-ideas.de/GovOPlaN/govoplan-grants) |
|
||||
| `govoplan-helpdesk` | `domain` | `../govoplan-helpdesk` | [govoplan-helpdesk](https://git.add-ideas.de/GovOPlaN/govoplan-helpdesk) |
|
||||
| `govoplan-identity` | `platform` | `../govoplan-identity` | [govoplan-identity](https://git.add-ideas.de/GovOPlaN/govoplan-identity) |
|
||||
| `govoplan-identity-trust` | `platform` | `../govoplan-identity-trust` | [govoplan-identity-trust](https://git.add-ideas.de/GovOPlaN/govoplan-identity-trust) |
|
||||
| `govoplan-idm` | `platform` | `../govoplan-idm` | [govoplan-idm](https://git.add-ideas.de/GovOPlaN/govoplan-idm) |
|
||||
| `govoplan-inspections` | `domain` | `../govoplan-inspections` | [govoplan-inspections](https://git.add-ideas.de/GovOPlaN/govoplan-inspections) |
|
||||
| `govoplan-learning` | `domain` | `../govoplan-learning` | [govoplan-learning](https://git.add-ideas.de/GovOPlaN/govoplan-learning) |
|
||||
| `govoplan-ledger` | `domain` | `../govoplan-ledger` | [govoplan-ledger](https://git.add-ideas.de/GovOPlaN/govoplan-ledger) |
|
||||
| `govoplan-mail` | `domain` | `../govoplan-mail` | [govoplan-mail](https://git.add-ideas.de/GovOPlaN/govoplan-mail) |
|
||||
| `govoplan-mandates` | `domain` | `../govoplan-mandates` | [govoplan-mandates](https://git.add-ideas.de/GovOPlaN/govoplan-mandates) |
|
||||
| `govoplan-notifications` | `platform` | `../govoplan-notifications` | [govoplan-notifications](https://git.add-ideas.de/GovOPlaN/govoplan-notifications) |
|
||||
| `govoplan-ops` | `platform` | `../govoplan-ops` | [govoplan-ops](https://git.add-ideas.de/GovOPlaN/govoplan-ops) |
|
||||
| `govoplan-organizations` | `platform` | `../govoplan-organizations` | [govoplan-organizations](https://git.add-ideas.de/GovOPlaN/govoplan-organizations) |
|
||||
| `govoplan-payments` | `domain` | `../govoplan-payments` | [govoplan-payments](https://git.add-ideas.de/GovOPlaN/govoplan-payments) |
|
||||
| `govoplan-parties` | `domain` | `../govoplan-parties` | [govoplan-parties](https://git.add-ideas.de/GovOPlaN/govoplan-parties) |
|
||||
| `govoplan-permits` | `domain` | `../govoplan-permits` | [govoplan-permits](https://git.add-ideas.de/GovOPlaN/govoplan-permits) |
|
||||
| `govoplan-policy` | `platform` | `../govoplan-policy` | [govoplan-policy](https://git.add-ideas.de/GovOPlaN/govoplan-policy) |
|
||||
| `govoplan-poll` | `domain` | `../govoplan-poll` | [govoplan-poll](https://git.add-ideas.de/GovOPlaN/govoplan-poll) |
|
||||
| `govoplan-portal` | `domain` | `../govoplan-portal` | [govoplan-portal](https://git.add-ideas.de/GovOPlaN/govoplan-portal) |
|
||||
| `govoplan-postbox` | `domain` | `../govoplan-postbox` | [govoplan-postbox](https://git.add-ideas.de/GovOPlaN/govoplan-postbox) |
|
||||
| `govoplan-procurement` | `domain` | `../govoplan-procurement` | [govoplan-procurement](https://git.add-ideas.de/GovOPlaN/govoplan-procurement) |
|
||||
| `govoplan-projects` | `domain` | `../govoplan-projects` | [govoplan-projects](https://git.add-ideas.de/GovOPlaN/govoplan-projects) |
|
||||
| `govoplan-records` | `domain` | `../govoplan-records` | [govoplan-records](https://git.add-ideas.de/GovOPlaN/govoplan-records) |
|
||||
| `govoplan-reporting` | `domain` | `../govoplan-reporting` | [govoplan-reporting](https://git.add-ideas.de/GovOPlaN/govoplan-reporting) |
|
||||
| `govoplan-resources` | `domain` | `../govoplan-resources` | [govoplan-resources](https://git.add-ideas.de/GovOPlaN/govoplan-resources) |
|
||||
| `govoplan-risk-compliance` | `domain` | `../govoplan-risk-compliance` | [govoplan-risk-compliance](https://git.add-ideas.de/GovOPlaN/govoplan-risk-compliance) |
|
||||
| `govoplan-scheduling` | `domain` | `../govoplan-scheduling` | [govoplan-scheduling](https://git.add-ideas.de/GovOPlaN/govoplan-scheduling) |
|
||||
| `govoplan-search` | `platform` | `../govoplan-search` | [govoplan-search](https://git.add-ideas.de/GovOPlaN/govoplan-search) |
|
||||
| `govoplan-services` | `domain` | `../govoplan-services` | [govoplan-services](https://git.add-ideas.de/GovOPlaN/govoplan-services) |
|
||||
| `govoplan-tasks` | `domain` | `../govoplan-tasks` | [govoplan-tasks](https://git.add-ideas.de/GovOPlaN/govoplan-tasks) |
|
||||
| `govoplan-templates` | `domain` | `../govoplan-templates` | [govoplan-templates](https://git.add-ideas.de/GovOPlaN/govoplan-templates) |
|
||||
| `govoplan-tenancy` | `platform` | `../govoplan-tenancy` | [govoplan-tenancy](https://git.add-ideas.de/GovOPlaN/govoplan-tenancy) |
|
||||
| `govoplan-tickets` | `domain` | `../govoplan-tickets` | [govoplan-tickets](https://git.add-ideas.de/GovOPlaN/govoplan-tickets) |
|
||||
| `govoplan-transparency` | `domain` | `../govoplan-transparency` | [govoplan-transparency](https://git.add-ideas.de/GovOPlaN/govoplan-transparency) |
|
||||
| `govoplan-views` | `platform` | `../govoplan-views` | [govoplan-views](https://git.add-ideas.de/GovOPlaN/govoplan-views) |
|
||||
| `govoplan-voting` | `domain` | `../govoplan-voting` | [govoplan-voting](https://git.add-ideas.de/GovOPlaN/govoplan-voting) |
|
||||
| `govoplan-wiki` | `domain` | `../govoplan-wiki` | [govoplan-wiki](https://git.add-ideas.de/GovOPlaN/govoplan-wiki) |
|
||||
| `govoplan-workflow` | `platform` | `../govoplan-workflow` | [govoplan-workflow](https://git.add-ideas.de/GovOPlaN/govoplan-workflow) |
|
||||
| `govoplan-workflow-engine` | `platform` | `../govoplan-workflow-engine` | [govoplan-workflow-engine](https://git.add-ideas.de/GovOPlaN/govoplan-workflow-engine) |
|
||||
|
||||
## Connector
|
||||
|
||||
| Repository | Subtype | Local path | Gitea |
|
||||
| --- | --- | --- | --- |
|
||||
| `govoplan-connectors` | `connector-hub` | `../govoplan-connectors` | [govoplan-connectors](https://git.add-ideas.de/GovOPlaN/govoplan-connectors) |
|
||||
| `govoplan-fit-connect` | `standard` | `../govoplan-fit-connect` | [govoplan-fit-connect](https://git.add-ideas.de/GovOPlaN/govoplan-fit-connect) |
|
||||
| `govoplan-rest` | `protocol` | `../govoplan-rest` | [govoplan-rest](https://git.add-ideas.de/GovOPlaN/govoplan-rest) |
|
||||
| `govoplan-soap` | `protocol` | `../govoplan-soap` | [govoplan-soap](https://git.add-ideas.de/GovOPlaN/govoplan-soap) |
|
||||
| `govoplan-xoev` | `standard` | `../govoplan-xoev` | [govoplan-xoev](https://git.add-ideas.de/GovOPlaN/govoplan-xoev) |
|
||||
| `govoplan-xrechnung` | `standard` | `../govoplan-xrechnung` | [govoplan-xrechnung](https://git.add-ideas.de/GovOPlaN/govoplan-xrechnung) |
|
||||
| `govoplan-xta-osci` | `standard` | `../govoplan-xta-osci` | [govoplan-xta-osci](https://git.add-ideas.de/GovOPlaN/govoplan-xta-osci) |
|
||||
|
||||
## Website
|
||||
|
||||
| Repository | Subtype | Local path | Gitea |
|
||||
| --- | --- | --- | --- |
|
||||
| `addideas-govoplan-website` | `public-site` | `../addideas-govoplan-website` | [addideas-govoplan-website](https://git.add-ideas.de/add-ideas/addideas-govoplan-website) |
|
||||
@@ -76,6 +76,13 @@ one place. If a deployment profile later needs pinned SHAs for every repository,
|
||||
generate that lock as a release artifact instead of making day-to-day
|
||||
development depend on submodule updates.
|
||||
|
||||
Module release tags also publish wheels and WebUI tarballs to the organization
|
||||
PyPI/npm registries. The meta release resolves exact versions into a hash-bound
|
||||
package lock before producing the signed OCI runtime. See
|
||||
`docs/operations/PACKAGE_REGISTRY_RELEASES.md`. Git tags remain source provenance; package
|
||||
registries are reusable artifact transport; the signed runtime manifest and
|
||||
digest-pinned images remain production authority.
|
||||
|
||||
## Docker Placement
|
||||
|
||||
Whole-product Docker and production-like deployment composition belongs in
|
||||
@@ -0,0 +1,195 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"parent_issue": "https://git.add-ideas.de/GovOPlaN/govoplan/issues/36",
|
||||
"operations": [
|
||||
{
|
||||
"id": "campaign.build.publish-artifacts",
|
||||
"repository": "govoplan-campaign",
|
||||
"resources": ["postgresql", "object-storage", "templates-capability", "files-capability"],
|
||||
"mode": "compensation",
|
||||
"fenced": true,
|
||||
"adoption": "reference-implementation",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/92"
|
||||
},
|
||||
{
|
||||
"id": "campaign.delivery.external-channels",
|
||||
"repository": "govoplan-campaign",
|
||||
"resources": ["postgresql", "queue", "smtp", "imap", "postbox", "print-provider"],
|
||||
"mode": "forward_recovery",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/92"
|
||||
},
|
||||
{
|
||||
"id": "campaign.retention.generated-artifacts",
|
||||
"repository": "govoplan-campaign",
|
||||
"resources": ["postgresql", "object-storage"],
|
||||
"mode": "forward_recovery",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-campaign/issues/92"
|
||||
},
|
||||
{
|
||||
"id": "files.upload.finalize",
|
||||
"repository": "govoplan-files",
|
||||
"resources": ["postgresql", "object-storage", "filesystem-staging"],
|
||||
"mode": "compensation",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-files/issues/41"
|
||||
},
|
||||
{
|
||||
"id": "files.retention.purge",
|
||||
"repository": "govoplan-files",
|
||||
"resources": ["postgresql", "object-storage", "encryption-key-custody"],
|
||||
"mode": "irreversible",
|
||||
"fenced": true,
|
||||
"adoption": "planned",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-files/issues/41"
|
||||
},
|
||||
{
|
||||
"id": "files.integrity.reconcile",
|
||||
"repository": "govoplan-files",
|
||||
"resources": ["postgresql", "object-storage"],
|
||||
"mode": "forward_recovery",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-files/issues/41"
|
||||
},
|
||||
{
|
||||
"id": "files.connector.write-sync",
|
||||
"repository": "govoplan-files",
|
||||
"resources": ["postgresql", "object-storage", "external-connector"],
|
||||
"mode": "forward_recovery",
|
||||
"fenced": true,
|
||||
"adoption": "planned",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-files/issues/41"
|
||||
},
|
||||
{
|
||||
"id": "mail.outbox.smtp-submit",
|
||||
"repository": "govoplan-mail",
|
||||
"resources": ["postgresql", "queue", "smtp"],
|
||||
"mode": "forward_recovery",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-mail/issues/19"
|
||||
},
|
||||
{
|
||||
"id": "mail.sent.imap-append",
|
||||
"repository": "govoplan-mail",
|
||||
"resources": ["postgresql", "imap"],
|
||||
"mode": "forward_recovery",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-mail/issues/19"
|
||||
},
|
||||
{
|
||||
"id": "mail.mailbox.imap-mutate",
|
||||
"repository": "govoplan-mail",
|
||||
"resources": ["postgresql", "imap"],
|
||||
"mode": "forward_recovery",
|
||||
"fenced": true,
|
||||
"adoption": "planned",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-mail/issues/19"
|
||||
},
|
||||
{
|
||||
"id": "mail.mailbox.sync-cursor",
|
||||
"repository": "govoplan-mail",
|
||||
"resources": ["postgresql", "imap"],
|
||||
"mode": "atomic",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-mail/issues/19"
|
||||
},
|
||||
{
|
||||
"id": "connectors.sync.read-snapshot",
|
||||
"repository": "govoplan-connectors",
|
||||
"resources": ["postgresql", "external-provider"],
|
||||
"mode": "atomic",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-connectors/issues/15"
|
||||
},
|
||||
{
|
||||
"id": "connectors.sync.external-mutation",
|
||||
"repository": "govoplan-connectors",
|
||||
"resources": ["postgresql", "queue", "external-provider"],
|
||||
"mode": "forward_recovery",
|
||||
"fenced": true,
|
||||
"adoption": "planned",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-connectors/issues/15"
|
||||
},
|
||||
{
|
||||
"id": "dataflow.run.database-only",
|
||||
"repository": "govoplan-dataflow",
|
||||
"resources": ["postgresql"],
|
||||
"mode": "atomic",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-dataflow/issues/19"
|
||||
},
|
||||
{
|
||||
"id": "dataflow.run.publish-output",
|
||||
"repository": "govoplan-dataflow",
|
||||
"resources": ["postgresql", "queue", "object-storage", "external-sink"],
|
||||
"mode": "forward_recovery",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-dataflow/issues/19"
|
||||
},
|
||||
{
|
||||
"id": "workflow-engine.instance.state-transition",
|
||||
"repository": "govoplan-workflow-engine",
|
||||
"resources": ["postgresql"],
|
||||
"mode": "atomic",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-workflow-engine/issues/1"
|
||||
},
|
||||
{
|
||||
"id": "workflow-engine.activity.external-effect",
|
||||
"repository": "govoplan-workflow-engine",
|
||||
"resources": ["postgresql", "queue", "module-capability", "external-provider"],
|
||||
"mode": "forward_recovery",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-workflow-engine/issues/1"
|
||||
},
|
||||
{
|
||||
"id": "core.module-lifecycle.pre-migration",
|
||||
"repository": "govoplan-core",
|
||||
"resources": ["postgresql", "package-environment", "webui-bundle", "filesystem"],
|
||||
"mode": "compensation",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/281"
|
||||
},
|
||||
{
|
||||
"id": "core.module-lifecycle.post-migration",
|
||||
"repository": "govoplan-core",
|
||||
"resources": ["postgresql", "package-environment", "webui-bundle", "runtime-nodes"],
|
||||
"mode": "forward_recovery",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/281"
|
||||
},
|
||||
{
|
||||
"id": "core.module-retirement.destroy-data",
|
||||
"repository": "govoplan-core",
|
||||
"resources": ["postgresql", "object-storage", "package-environment"],
|
||||
"mode": "snapshot_restore",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/281"
|
||||
},
|
||||
{
|
||||
"id": "core.module-runtime.apply-graph",
|
||||
"repository": "govoplan-core",
|
||||
"resources": ["postgresql", "runtime-nodes", "module-registry"],
|
||||
"mode": "compensation",
|
||||
"fenced": true,
|
||||
"adoption": "adopted",
|
||||
"issue": "https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/281"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
# GovOPlaN 0.1.45 — usability, reliability and security hardening
|
||||
|
||||
Release coordination: [GovOPlaN #51](https://git.add-ideas.de/GovOPlaN/govoplan/issues/51).
|
||||
The exact independently versioned composition is recorded in
|
||||
`packages/govoplan-meta/pyproject.toml`; unchanged modules retain their versions.
|
||||
This source release does not by itself establish a deployed or independently
|
||||
approved production environment. Package, signed catalog and runtime publication
|
||||
results are recorded separately in the coordination issue.
|
||||
|
||||
## Runtime publication hold
|
||||
|
||||
The [runtime image audit](../security/RUNTIME_IMAGE_AUDIT_2026-09-08.md) completed
|
||||
eleven registry-only amd64 scans, but found unresolved vulnerabilities and
|
||||
inventory gaps. Runtime publication remains held separately from this source
|
||||
release. Patch-only image updates are insufficient; maintained minor-line
|
||||
changes, narrowly evidenced finding decisions, arm64/final-layer scans and
|
||||
deployment checks remain necessary. No audited candidate was automatically
|
||||
adopted and no image was executed during those scans.
|
||||
The remaining gates are tracked in
|
||||
[GovOPlaN #52](https://git.add-ideas.de/GovOPlaN/govoplan/issues/52).
|
||||
|
||||
## Included changes
|
||||
|
||||
- Shared page/action placement, reusable navigation grouping/editing, table and
|
||||
dialog sizing, field alignment, multi-select filters and predictable tree
|
||||
selection. Files, Mail, Search, Notifications and domain pages use the same
|
||||
contracts, with browser regression coverage.
|
||||
- Campaign draft saving and independent Mail/ZIP-policy repair, persistent and
|
||||
bulk message review, clearer delivery eligibility, bounded configurable
|
||||
synchronous delivery, guarded workerless recovery, lightweight SMTP/IMAP
|
||||
progress, reused IMAP connections and recipient-complete reporting.
|
||||
- Files archive staging/reuse, unpacking previously uploaded archives, numeric
|
||||
progress and bounded traversal. Optional native archive acceleration retains
|
||||
the same validation rules; portable fallbacks remain available.
|
||||
- Mail credential references and IMAP folder-name decoding; help topics can be
|
||||
found by area and tags without expanding every occurrence of the same topic.
|
||||
- Authentication provenance/scope and browser-cache hardening, patched rich-text
|
||||
dependencies, spreadsheet/archive/template/Dataflow resource limits, batched
|
||||
Docs/Notifications queries and safe Reporting bind names. See the
|
||||
[security/performance review](../security/SECURITY_PERFORMANCE_REVIEW_2026-09-08.md)
|
||||
for measurements, test evidence and remaining limitations.
|
||||
- A deterministic governance-journey clock fixture, fresh-process Campaign
|
||||
import coverage, and a new Cases patch aligning its root npm facade with its
|
||||
Python/WebUI package. Historical published tags are not rewritten.
|
||||
- Git-root WebUI package facades are aligned with their owning packages, with
|
||||
a cross-composition parity check. Tasks is included in default module
|
||||
discovery; it remains subject to enabled modules and normal permissions.
|
||||
|
||||
## Upgrade and verification
|
||||
|
||||
Back up the database and file storage before upgrading. Apply the complete
|
||||
selected migration graph before starting the new API/workers. This release
|
||||
includes additive repair migrations `c58a2d7e9f10` (Core ownership history) and
|
||||
`d8f1b4e7a0c3` (Access external-function mappings), plus Campaign delivery-state
|
||||
migrations. Existing business evidence is retained; a schema downgrade is not
|
||||
a substitute for a reviewed backup/restore plan. Restart API and worker
|
||||
processes together after upgrading their matching packages.
|
||||
|
||||
Updated UI consumers require Core 0.1.45 where they use its new shared contracts.
|
||||
Tenant keys that previously relied on unintended system permissions/wildcards
|
||||
must be corrected; the release does not preserve that unsafe behavior. Extremely
|
||||
sparse spreadsheets, oversized generated output and excessive archive paths
|
||||
can now fail early with a diagnostic.
|
||||
|
||||
For archive staging across multiple hosts, provide shared POSIX storage with
|
||||
working locks or sticky routing. Background delivery still needs configured
|
||||
workers; increasing the synchronous limit does not create a worker or guarantee
|
||||
delivery after a process failure. An unknown SMTP outcome must be reconciled,
|
||||
not automatically resent.
|
||||
|
||||
After deployment, manually verify login/logout and least-privilege API keys,
|
||||
Campaign Settings and independent Mail/ZIP saves, archive upload/unpack,
|
||||
recipient-complete reports, and SMTP/IMAP progress with an explicitly approved
|
||||
test mailbox. No release verification sends real campaign mail automatically.
|
||||
|
||||
Hard process isolation, forced-password-change/recovery enforcement, bounded
|
||||
Xrechnung subprocess output and large-history pagination remain separate open
|
||||
issues. This release is not a claim that all security or performance debt is
|
||||
resolved. Production-image scans and multi-host evidence must refer to the
|
||||
actual signed runtime being deployed.
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://govoplan.add-ideas.de/schemas/runtime-distribution-keyring-v1.json",
|
||||
"title": "GovOPlaN runtime distribution trust keyring",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema_version", "purpose", "keys"],
|
||||
"properties": {
|
||||
"schema_version": { "const": "1" },
|
||||
"purpose": { "const": "govoplan-runtime-distribution" },
|
||||
"keys": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"key_id",
|
||||
"algorithm",
|
||||
"status",
|
||||
"public_key_pem",
|
||||
"not_before",
|
||||
"expires_at"
|
||||
],
|
||||
"properties": {
|
||||
"key_id": { "type": "string", "minLength": 1, "maxLength": 128 },
|
||||
"algorithm": { "const": "ed25519" },
|
||||
"status": { "enum": ["active", "retired", "revoked"] },
|
||||
"public_key_pem": { "type": "string", "minLength": 1, "maxLength": 8192 },
|
||||
"not_before": { "type": "string", "format": "date-time" },
|
||||
"expires_at": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://govoplan.add-ideas.de/schemas/runtime-distribution-manifest-v1.json",
|
||||
"title": "GovOPlaN runtime distribution manifest",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"channel",
|
||||
"sequence",
|
||||
"version",
|
||||
"issued_at",
|
||||
"expires_at",
|
||||
"revoked",
|
||||
"deployer",
|
||||
"package_lock",
|
||||
"images",
|
||||
"dependencies",
|
||||
"composition",
|
||||
"signatures"
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": { "const": "1" },
|
||||
"channel": { "type": "string", "pattern": "^[a-z][a-z0-9_]{1,63}$" },
|
||||
"sequence": { "type": "integer", "minimum": 1 },
|
||||
"version": { "type": "string", "minLength": 1, "maxLength": 128 },
|
||||
"issued_at": { "type": "string", "format": "date-time" },
|
||||
"expires_at": { "type": "string", "format": "date-time" },
|
||||
"revoked": { "const": false },
|
||||
"deployer": { "$ref": "#/$defs/artifact" },
|
||||
"package_lock": { "$ref": "#/$defs/artifact" },
|
||||
"images": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["api", "web"],
|
||||
"properties": {
|
||||
"api": { "$ref": "#/$defs/image" },
|
||||
"web": { "$ref": "#/$defs/image" }
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"propertyNames": { "pattern": "^[a-z][a-z0-9_]{1,63}$" },
|
||||
"additionalProperties": { "$ref": "#/$defs/imageReference" }
|
||||
},
|
||||
"composition": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["sha256", "module_ids", "packages"],
|
||||
"properties": {
|
||||
"sha256": { "$ref": "#/$defs/sha256" },
|
||||
"module_ids": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": { "type": "string", "pattern": "^[a-z][a-z0-9_]{1,63}$" }
|
||||
},
|
||||
"packages": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "version", "wheel_sha256"],
|
||||
"properties": {
|
||||
"name": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" },
|
||||
"version": { "type": "string", "minLength": 1, "maxLength": 128 },
|
||||
"wheel_sha256": { "$ref": "#/$defs/sha256" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"signatures": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["key_id", "algorithm", "value"],
|
||||
"properties": {
|
||||
"key_id": { "type": "string", "minLength": 1, "maxLength": 128 },
|
||||
"algorithm": { "const": "ed25519" },
|
||||
"value": { "type": "string", "minLength": 1, "maxLength": 256 }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
||||
"imageReference": {
|
||||
"type": "string",
|
||||
"pattern": "^[^@\\s]+@sha256:[0-9a-f]{64}$",
|
||||
"maxLength": 300
|
||||
},
|
||||
"artifact": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["url", "sha256"],
|
||||
"properties": {
|
||||
"url": { "type": "string", "format": "uri", "pattern": "^https://" },
|
||||
"sha256": { "$ref": "#/$defs/sha256" }
|
||||
}
|
||||
},
|
||||
"image": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["index", "platforms", "sbom", "provenance"],
|
||||
"properties": {
|
||||
"index": { "$ref": "#/$defs/imageReference" },
|
||||
"platforms": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["linux/amd64", "linux/arm64"],
|
||||
"properties": {
|
||||
"linux/amd64": { "$ref": "#/$defs/imageReference" },
|
||||
"linux/arm64": { "$ref": "#/$defs/imageReference" }
|
||||
}
|
||||
},
|
||||
"sbom": { "$ref": "#/$defs/artifact" },
|
||||
"provenance": { "$ref": "#/$defs/artifact" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
# Runtime image candidate audit — 8 September 2026
|
||||
|
||||
Release coordination: [GovOPlaN #51](https://git.add-ideas.de/GovOPlaN/govoplan/issues/51).
|
||||
Canonical remediation: [GovOPlaN #52](https://git.add-ideas.de/GovOPlaN/govoplan/issues/52).
|
||||
This follow-up to the [source security/performance review](SECURITY_PERFORMANCE_REVIEW_2026-09-08.md)
|
||||
records registry-only scans of nine proposed runtime dependencies and two
|
||||
same-minor patch candidates. **Runtime publication is held:** patch-only updates
|
||||
do not resolve the baseline. Source/package publication is a separate outcome.
|
||||
No images were executed, rebuilt, selected for CI, or published by this audit.
|
||||
|
||||
## Method and reproducible evidence
|
||||
|
||||
Official Trivy **0.74.0** was installed only in a private local task directory,
|
||||
without sudo or Docker access. Its Linux-64bit release archive matched both the
|
||||
official checksums file and GitHub release asset metadata:
|
||||
|
||||
- Archive SHA256: `2ae6fe3ee734b7fdf11335663e18c75ea12dccc76062f09f164a3b0f8be4371a`.
|
||||
- Checksums-file SHA256: `bc701c3c3ee8b9acbea2c23257e41381e3854888f51281616a6ba5dc96963821`.
|
||||
- Vulnerability database schema 2, updated `2026-09-07T19:06:01.154199452Z`,
|
||||
downloaded from `mirror.gcr.io/aquasec/trivy-db:2`.
|
||||
- Scan flags: `--image-src remote --platform linux/amd64 --scanners vuln
|
||||
--format json --no-progress --timeout 8m --max-image-size 2GB --exit-code 0`.
|
||||
Findings were counted from validated JSON; exit zero did not mean clean.
|
||||
- Existing Docker credentials were not read; no private keys or secrets were
|
||||
used. Checksums over official HTTPS metadata were verified, not independent
|
||||
Sigstore signatures. See the [official release](https://github.com/aquasecurity/trivy/releases/tag/v0.74.0)
|
||||
and [registry-only scan documentation](https://trivy.dev/docs/latest/target/container_image/).
|
||||
|
||||
Raw evidence is retained locally, not committed:
|
||||
`/home/zemion/.cache/govoplan-trivy-remote.yKZgjDOg/scan/`.
|
||||
It contains eleven `reports/*-amd64.json` reports/logs, scanner scripts,
|
||||
`patch-candidate-inspection.json`, exact successor registry indices, and
|
||||
`evidence-checksums.json`. Summary SHA256 values:
|
||||
|
||||
- `summary.json`: `f2785a731d637452ab9c0b1f5399772c0f8828a63ca83d5fa7496abdad1c757a`.
|
||||
- `patch-summary.json`: `b3fc6273fcdad98864040ccdf3477ecf379afd46e9f94444b1f2910f48c1d85b`.
|
||||
|
||||
All eleven executions succeeded without timeout/rate-limit failure. Initial
|
||||
summary fields distinguish `scan_execution_complete: true` from
|
||||
`coverage_complete: false`: Garage has no detectable package inventory.
|
||||
Checksums preserve evidence identity, not indefinite storage availability.
|
||||
|
||||
## Exact requested pins and results
|
||||
|
||||
All references below use `docker.io/`. Counts are package-vulnerability records,
|
||||
not distinct CVEs or confirmed exploitable application defects. A vulnerability
|
||||
can appear against several installed packages. Unfixed/unknown records remain.
|
||||
|
||||
| Image tag | Exact index SHA256 | Critical / High / Medium / Low / Unknown | Fixable C/H |
|
||||
| --- | --- | --- | ---: |
|
||||
| `library/python:3.12-slim-bookworm` | `782412e85d0f0984994c290652577d4018aff08145c85b262bb63dc0c7522254` | 5 / 55 / 102 / 103 / 5 | 0 |
|
||||
| `library/postgres:16-alpine` | `cf78e76683b9ca8c5733cbbdce6c9262b45b6767934dd0a95e671f9a0fc20685` | 1 / 30 / 28 / 14 / 1 | 31 |
|
||||
| `library/redis:7-alpine` | `ff02b58f971e7d7d156a1267e283fcbbeee91773b6aa36c49dac28ecfe28eadf` | 0 / 0 / 0 / 0 / 0 | 0 |
|
||||
| `nginxinc/nginx-unprivileged:1.29-alpine` | `0c79d56aee561a1d81c63f00eee5fb5fe29279560cdc55e91425133104c7fbe6` | 0 / 33 / 74 / 37 / 20 | 33 |
|
||||
| `library/haproxy:3.2.21-alpine` | `66e25cc9a8332635f4e897f7f4b1e5622c25f09f0ee23cddc6ce9bdb3a24772a` | 0 / 2 / 6 / 12 / 0 | 2 |
|
||||
| `library/caddy:2.10.2-alpine` | `4c6e91c6ed0e2fa03efd5b44747b625fec79bc9cd06ac5235a779726618e530d` | 7 / 75 / 67 / 37 / 4 | 82 |
|
||||
| `dxflrs/garage:v2.3.0` | `866bd13ed2038ba7e7190e840482bc27234c4afaf77be8cfa439ae088c1e4690` | **Unknown: no inventory** | — |
|
||||
| `greenmail/standalone:2.1.9` | `3ac5a83dd6727cf95e4d50e18907fb8ee7bbf5f67e8534714dee2fb1b5b2e1d4` | 0 / 0 / 116 / 35 / 0 | 0 |
|
||||
| `tonistiigi/binfmt:qemu-v10.2.3-68` | `400a4873b838d1b89194d982c45e5fb3cda4593fbfd7e08a02e76b03b21166f0` | 0 / 9 / 2 / 1 / 1 | 9 |
|
||||
|
||||
## Patch-only options and limits
|
||||
|
||||
Complete publisher tag listings were inspected for nginx 1.29, Caddy 2.10,
|
||||
HAProxy 3.2, GreenMail 2.1 and binfmt qemu10.2. Two newer candidates were
|
||||
scanned; their registry index bytes matched both registry and publisher digests,
|
||||
and contained amd64 and arm64 manifests:
|
||||
|
||||
- `library/haproxy:3.2.23-alpine@sha256:6343ce34a132a5dceaa24767d739df2bd519f8f7c1079ae39e4821334e8eb42e`:
|
||||
same Alpine 3.24.1, 24 detected OS packages, zero reported findings. This is
|
||||
a useful candidate, not a completed compatibility test or application audit.
|
||||
- `greenmail/standalone:2.1.13@sha256:3df66b7edd01c8a301343ca5e3601d8674760d4708655573560c24745e624fb2`:
|
||||
upstream changes Ubuntu 22.04 to Debian 13.6; **3 C / 80 H / 98 M / 85 L /
|
||||
5 unknown**, 30 fixable C/H records. Not selected as a no-base-change update.
|
||||
- nginx's newest matching Alpine patch is already 1.29.8 at the scanned pin;
|
||||
Caddy 2.10 remains 2.10.2; binfmt qemu10.2 remains 10.2.3-68. No newer matching
|
||||
publisher images were found. The current [official Caddy image catalogue](https://raw.githubusercontent.com/docker-library/official-images/master/library/caddy)
|
||||
uses 2.11.4; switching minor lines requires new scans and compatibility checks.
|
||||
|
||||
Priority remediation: Caddy's own seven HIGH records require fixes through
|
||||
2.11.4, with additional bundled Go/library fixes that must be re-scanned;
|
||||
nginx's packages include curl/libcurl fixes through 8.22.0-r0, OpenSSL 3.5.8-r0,
|
||||
c-ares 1.34.8-r0, expat 2.8.1-r0 and libuuid 2.41.6-r1. PostgreSQL's OS records
|
||||
require OpenSSL 3.5.8-r0 and libuuid 2.42.3-r1; its CRITICAL plus 21 HIGH Go
|
||||
records concern the **gosu helper**, not PostgreSQL server code. binfmt's nine
|
||||
HIGH records concern its Go 1.26.4 build, with fixes through 1.26.6. Package
|
||||
presence does not establish vulnerable-symbol reachability. No unscanned tag
|
||||
is claimed to meet every fix requirement.
|
||||
|
||||
## Python triage and coverage caveats
|
||||
|
||||
Python image metadata identifies CPython 3.12.14, but Trivy inventories only
|
||||
Debian packages and pip, **not CPython/stdlib**. All 60 C/H records concern
|
||||
Debian packages: 21 CVEs, 50 `affected` records, 9 `fix_deferred`, 1
|
||||
`will_not_fix`, without a recorded fixed Bookworm version. Five util-linux CVEs
|
||||
repeat across eight binary packages. These remain installed; they are not all
|
||||
removed build dependencies. Pip 25.0.1 separately has five MEDIUM/one LOW
|
||||
records, with fixes through 26.2.0; it is install tooling, and the API image uses
|
||||
an offline `--no-index` wheelhouse rather than an arbitrary package index.
|
||||
|
||||
Narrow triage examples, **not blanket exemptions**:
|
||||
|
||||
- Debian states [CVE-2023-45853](https://security-tracker.debian.org/tracker/CVE-2023-45853)
|
||||
does not affect the built Bookworm zlib binaries because vulnerable minizip
|
||||
code is not included. Other bundled minizip implementations are separate.
|
||||
- [CVE-2026-8376](https://security-tracker.debian.org/tracker/CVE-2026-8376)
|
||||
explicitly requires 32-bit Perl; this scan targets amd64.
|
||||
- [CVE-2025-7458](https://security-tracker.debian.org/tracker/CVE-2025-7458)
|
||||
requires crafted arbitrary SQLite SQL; the managed runtime uses PostgreSQL,
|
||||
but alternate SQLite use must be reviewed.
|
||||
- Perl's regex and Archive::Tar records need exact binary/module applicability
|
||||
checks; vendor-deferred status alone is not a finding dismissal.
|
||||
|
||||
Only amd64 was scanned. arm64, newly built GovOPlaN API/Web layers and optional
|
||||
dependency combinations remain unverified. Garage has no inventory; Redis,
|
||||
HAProxy and PostgreSQL source-built executables, CPython and QEMU static
|
||||
binaries need supplemental SBOM/source coverage. Zero detected OS findings is
|
||||
not zero application vulnerabilities. Trivy also lacks Alpine 3.24 EOL metadata
|
||||
and nginx CVE-2026-80256 detail; unknowns are retained. There were no runtime,
|
||||
exploitability, secret, misconfiguration, malware or signature-policy checks.
|
||||
|
||||
Before lifting the runtime hold: approve and test maintained image-line changes
|
||||
where necessary, fix or narrowly disposition findings with evidence, close
|
||||
inventory gaps, scan both architectures and final runtime layers, then run
|
||||
deployment/ingress smoke checks. Do not silently change base OS, use unpinned
|
||||
`latest`, rebuild third-party images, or accept all HIGH/CRITICAL findings.
|
||||
@@ -0,0 +1,139 @@
|
||||
# Runtime image remediation follow-up — 8 September 2026
|
||||
|
||||
Canonical tracking: [Meta #52](https://git.add-ideas.de/GovOPlaN/govoplan/issues/52)
|
||||
and [website #9](https://git.add-ideas.de/add-ideas/addideas-govoplan-website/issues/9).
|
||||
This addendum supplements the [original audit](RUNTIME_IMAGE_AUDIT_2026-09-08.md);
|
||||
it does not replace that historical baseline or lift either publication or
|
||||
deployment gate. The immutable 0.1.45 release and `catalog-v0.1.45` are unchanged.
|
||||
|
||||
## Source change and candidate decisions
|
||||
|
||||
New installer specifications now use
|
||||
`haproxy:3.2.23-alpine@sha256:6343ce34a132a5dceaa24767d739df2bd519f8f7c1079ae39e4821334e8eb42e`.
|
||||
This is a patch update from 3.2.21 within the supported
|
||||
[3.2 LTS branch](https://www.haproxy.org/), keeping Alpine 3.24.1.
|
||||
The [publisher's exact build source](https://github.com/docker-library/haproxy/blob/7a5c202cde713867a737033dca56e7a211a8b8df/3.2/alpine/Dockerfile)
|
||||
and scanned image configuration retain the non-root `haproxy` user,
|
||||
`/usr/local/etc/haproxy/haproxy.cfg`, entrypoint and graceful-stop signal.
|
||||
The [upstream changelog](https://www.haproxy.org/download/3.2/src/CHANGELOG)
|
||||
includes HTTP parsing, TLS and memory-safety fixes in 3.2.22/3.2.23.
|
||||
Existing specifications retain their explicit image, including an older pin;
|
||||
this source change does not update a running installation.
|
||||
|
||||
| Candidate | OS | C / H / M / L / Unknown, per architecture | Disposition |
|
||||
| --- | --- | --- | --- |
|
||||
| HAProxy `3.2.23-alpine` | Alpine 3.24.1 | 0 / 0 / 0 / 0 / 0 | Installer source default updated; binary/runtime checks pending |
|
||||
| nginx-unprivileged `1.30.4-alpine` | Alpine 3.24.1 | 0 / 0 / 0 / 0 / 0 | Candidate only; compatibility and website OS upgrade review pending |
|
||||
| Caddy `2.11.4-alpine` | Alpine 3.23.5 | 1 / 38 / 41 / 12 / 23 | Not selected; all 39 C/H records have recorded fixes |
|
||||
| Node `24-alpine` (24.20.0) | Alpine 3.24.1 | 0 / 6 / 11 / 12 / 0 | Not selected; major change and six fixable HIGH records |
|
||||
|
||||
Each row was scanned separately for **linux/amd64 and linux/arm64**, with the
|
||||
same counts on both. Counts are package-vulnerability records, not distinct
|
||||
CVEs or proven exploits. The [machine-readable evidence](runtime-image-candidates-2026-09-08.json)
|
||||
contains exact index, platform-manifest, config and report digests, inventory
|
||||
counts, scanner bounds and decisions. It is audit data, not an accepted release
|
||||
manifest or an installer input.
|
||||
|
||||
nginx's candidate reference is
|
||||
`docker.io/nginxinc/nginx-unprivileged:1.30.4-alpine@sha256:442753882674b49ae2c1de83ed67896131c0777f56df5005e356e62bc3f7e7ce`.
|
||||
It inventories 70 Alpine packages including nginx/NJS, uses UID 101 and exposes
|
||||
8080. The [upstream stable release](https://nginx.org/en/download.html) and
|
||||
[security advisories](https://nginx.org/en/security_advisories.html) include the
|
||||
1.30.4 fixes. The publisher retains its
|
||||
[unprivileged port and temporary-path contract](https://github.com/nginx/docker-nginx-unprivileged).
|
||||
For the website, this changes nginx 1.27.5 to 1.30.4, NJS 0.8.10 to 1.0.1 and
|
||||
Alpine 3.21.3 to 3.24.1. These changes are explicit review items; the website
|
||||
Dockerfile has not been changed. The GovOPlaN Web image still requires an
|
||||
explicit verified `NGINX_IMAGE` build argument.
|
||||
|
||||
Caddy's candidate reference is
|
||||
`docker.io/library/caddy:2.11.4-alpine@sha256:5f5c8640aae01df9654968d946d8f1a56c497f1dd5c5cda4cf95ab7c14d58648`.
|
||||
Although this is the current
|
||||
[official image line](https://raw.githubusercontent.com/docker-library/official-images/master/library/caddy),
|
||||
its inventory still includes Go 1.26.3, `x/crypto` 0.52.0, `x/net` 0.55.0,
|
||||
`x/text` 0.37.0 and gRPC 1.81.0. Recorded fixes include Go 1.26.6,
|
||||
`x/crypto` 0.55.0, `x/net` 0.56.0, `x/text` 0.39.0 and gRPC 1.83.1; Alpine
|
||||
findings also remain in c-ares, curl/libcurl and OpenSSL. The CRITICAL
|
||||
`CVE-2026-56854` concerns `x/crypto/ssh` source-address enforcement. A module
|
||||
record alone does not establish that this binary exposes that SSH path; exact
|
||||
binary symbol/reachability analysis is still required for a disposition.
|
||||
|
||||
The [official Node image catalogue](https://raw.githubusercontent.com/docker-library/official-images/master/library/node)
|
||||
still maps Node 22 Alpine to 22.23.2 and the previously scanned digest. Node 24's
|
||||
candidate is
|
||||
`docker.io/library/node:24-alpine@sha256:e67514e5d0f6c46656005e1b693b2ec9d52e80b641307de684d4a015ba7a4eaf`.
|
||||
Its HIGH records remain in two OpenSSL packages and npm dependencies
|
||||
`brace-expansion`, `ip-address` and `tar`; fixing the earlier critical tar
|
||||
record alone is insufficient. The website builder stays on Node 22 pending
|
||||
a reviewed build-tool remedy and a final builder scan.
|
||||
|
||||
## Method, verification and retained evidence
|
||||
|
||||
The existing Trivy 0.74.0 executable was rehashed against the previously verified
|
||||
archive member: `d89bcc6510a267f11b773398cbf1be5520ce39f9e8b6633178c4487f05b7d791`.
|
||||
The same schema-2 vulnerability database was used, updated
|
||||
`2026-09-07T19:06:01.154199452Z`. No tool installation or database refresh occurred.
|
||||
Index bytes matched both the registry digest header and Docker Hub publisher
|
||||
metadata; both platform-manifest byte hashes matched the index. All eight
|
||||
registry-only scans completed successfully with validated JSON, `--list-all-pkgs`,
|
||||
`--scanners vuln`, an eight-minute/2GB image bound, an empty Docker configuration
|
||||
and no inherited credentials. Exit zero means execution succeeded. Private
|
||||
temporary paths and in-memory artifact cache isolated this follow-up from the
|
||||
earlier scanner's artifact cache; its vulnerability database was read only.
|
||||
|
||||
Raw reports, logs, manifests, publisher metadata and the scanner script are in
|
||||
`/home/zemion/.cache/govoplan-runtime-remediation.qfDSWHJh/`:
|
||||
|
||||
- `summary.json` SHA-256: `0d44390408ab35270e4430516f77bf11aa7877334eff2ef19e11e9a863fe5c56`.
|
||||
- `frozen-images.json` SHA-256: `d0cb156f4a88998531ec55ab950067a3f1350ded648f07650c463af101dad467`.
|
||||
- `scan_successors.py` SHA-256: `f64c69e06efc2ad7b5a657a25aa73f9ff586e3685737d525456bcecbe3ab5f07`.
|
||||
|
||||
Local retention is not permanent artifact hosting; preserve this evidence with
|
||||
the eventual reviewed release. The JSON evidence records compressed registry
|
||||
layer sizes; these are not expanded filesystem limits or final GovOPlaN sizes.
|
||||
|
||||
Installer regression checks cover the new generated image pin, legacy
|
||||
specification fallback, preserved explicit images, generated topology and
|
||||
configuration: `python -I -m unittest discover -s tests -p
|
||||
test_deployment_installer.py` ran 45 tests successfully with one skip because
|
||||
Core was not importable in that isolated test environment. The skipped Core
|
||||
startup-configuration integration was subsequently rerun in the shared development
|
||||
environment with Core available: all 45 installer tests passed with no skips,
|
||||
including generated-environment startup validation. This is configuration
|
||||
validation, not execution of the candidate image.
|
||||
Both repositories passed `git diff --check`; the audit JSON and all eight
|
||||
report hashes were checked against the retained evidence.
|
||||
**Docker, Podman and HAProxy executables are unavailable on
|
||||
this host**, so no image or HAProxy configuration was executed and no daemon was
|
||||
installed. Publisher metadata and installer tests support the scoped source
|
||||
patch; they do not establish binary or deployed compatibility.
|
||||
|
||||
## Gates that remain open
|
||||
|
||||
- Validate `haproxy -c` on generated local, existing-proxy and managed-ingress
|
||||
configurations using the exact pinned image and target architectures. Run
|
||||
bounded isolated checks without live mounts, secrets, privilege or external
|
||||
network access. Then verify DNS discovery, readiness, forwarded headers,
|
||||
replica routing and graceful termination in the intended runtime.
|
||||
- Test the nginx candidate with both the website configuration and GovOPlaN
|
||||
WebUI entrypoint/proxy configuration, including UID 101, writable temporary
|
||||
paths, health paths, cache headers and static catalog bytes. Approve the
|
||||
website nginx/NJS/Alpine version changes before changing its Dockerfile.
|
||||
- Resolve Caddy, Node build-tool and all unchanged baseline dependencies with
|
||||
updated publisher images or narrow reviewed applicability evidence. No
|
||||
severity-wide exceptions or custom third-party rebuilds were introduced.
|
||||
- Close the original source-built/static inventory gaps. HAProxy's 24-package
|
||||
OS inventory still omits the source-built HAProxy executable. Node's npm
|
||||
inventory still omits the Node executable/stdlib. Garage, CPython, Redis,
|
||||
PostgreSQL and QEMU gaps are unchanged. Alpine 3.24 EOL metadata is still
|
||||
missing from this scanner; zero findings is not complete coverage.
|
||||
- Scan **final built** API/Web/website layers and the selected managed
|
||||
dependencies on both architectures, then perform migration, worker,
|
||||
readiness and ingress smoke checks. Record failure and unknown states.
|
||||
Secrets, misconfiguration and image signature policy need separate checks.
|
||||
- Obtain the website deployment host/operator and rebuild/restart authority,
|
||||
preserving the exact immutable catalog/keyring/module-directory bytes and
|
||||
verifying fresh public responses after an authorized rollout.
|
||||
|
||||
No images were built, executed, published or deployed; no running service,
|
||||
release tag, signed manifest, CI image input or live infrastructure was changed.
|
||||
@@ -0,0 +1,219 @@
|
||||
# Security and performance follow-up — 8 September 2026
|
||||
|
||||
This follows the [original review](SECURITY_PERFORMANCE_REVIEW_2026-09-08.md)
|
||||
and its post-release issue reconciliation. It describes new source work after
|
||||
the frozen 0.1.45 release; it does not change published tags, packages, signed
|
||||
catalogs or deployed images. Gitea remains the canonical state log.
|
||||
|
||||
## Implemented source slices
|
||||
|
||||
- [Core #297](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/297):
|
||||
a shared disposable-process runner enforces wall/CPU/address-space/input/output
|
||||
limits, bounded stderr, process-group cleanup and non-queuing per-process
|
||||
admission. A private binary codec bounds decoding before allocating a full
|
||||
object graph and preserves explicitly supported data types without pickle.
|
||||
Read the owning Core `docs/BOUNDED_PROCESS_CONTRACT.md` before adding callers.
|
||||
- Connectors XLSX parsing, Templates rendering, Files ZIP/TAR inspection and
|
||||
extraction, and Dataflow reference previews/development execution now use
|
||||
that boundary. Existing authorization, sessions, provider credentials,
|
||||
idempotency and persistence remain in the parent. No unprotected inline
|
||||
fallback is used. Each module contributes static EN/DE user/admin limits and
|
||||
operational consequences through its manifest.
|
||||
- Files snapshots authorized sources inside shared admission, validates private
|
||||
staged members, and acknowledges each persisted member before decoding the
|
||||
next. Numeric progress remains available. The acknowledgement is event-driven,
|
||||
not a fixed sleep per member. Reads allocate by validated actual file size,
|
||||
not by the configured ceiling. Failures reap children, clear private staging
|
||||
and retain the existing transaction/blob cleanup and explicit retry behavior.
|
||||
- Dataflow's normal reference preview formerly bypassed the backend wrapper;
|
||||
it now enters the worker too. Nested source configurations cannot collide
|
||||
merely because subflows reuse node IDs. Combined reference-source data is
|
||||
checked before creating further columnar copies, while individual providers
|
||||
retain their own authorized-read bounds. Staging/production still require
|
||||
DuckDB; this change does not replace that separate backend.
|
||||
- [Access #22](https://git.add-ideas.de/GovOPlaN/govoplan-access/issues/22):
|
||||
current-password change, session/CSRF rotation, cross-tenant session and human
|
||||
API-key revocation, and optional administrator-assisted recovery. Recovery
|
||||
codes are hashed, single-use, expire after 15 minutes, require a current local
|
||||
System owner and explicit identity verification, and recheck current account,
|
||||
membership, tenant and issuer authority at redemption. A password change also
|
||||
invalidates outstanding codes issued by that account for other people. Audit
|
||||
evidence and validation/error responses do not contain passwords or codes.
|
||||
External-provider and service-account rules remain separate.
|
||||
- The Access UI provides first-login/required change, self-service change,
|
||||
policy-aware sign-in help, public code redemption and eligible owner issuance.
|
||||
Core consumes an optional lazy auth-action capability rather than importing
|
||||
Access internals. The required-action gate fails closed if its UI is missing.
|
||||
- [Workflow Engine #3](https://git.add-ideas.de/GovOPlaN/govoplan-workflow-engine/issues/3):
|
||||
full-history lists batch pinned revisions, while new summary and bounded
|
||||
step/event endpoints preserve authorization and explicit pagination. Existing
|
||||
full-history responses are not silently truncated. Exact inbox total semantics
|
||||
are retained and their counting cost is documented.
|
||||
- [Meta #55](https://git.add-ideas.de/GovOPlaN/govoplan/issues/55):
|
||||
shared version/planning helpers recognize the existing nested developer
|
||||
package, not invented root metadata. All tag batches enforce trusted private
|
||||
source ownership, registered origins and clean main/upstream state. Meta
|
||||
batches additionally require exact composition and matching Core evidence.
|
||||
Whole-batch preflight,
|
||||
frozen source receipts, annotated immutable tags, object-pinned atomic
|
||||
publication and post-effect remote checks are covered with temporary local
|
||||
repositories. Hidden Git index flags, unsafe ancestry, alternates and changed
|
||||
Git-directory identities are rejected. Selected version/composition metadata
|
||||
must be tracked, so ignored files cannot describe bytes absent from a tag.
|
||||
Applicable unselected Core WebUI inputs have bounded, frozen read receipts;
|
||||
backend-only releases do not read them. The existing local module-candidate
|
||||
exception remains intact. The weaker legacy mutation path was removed.
|
||||
Canonical whole-package preview and receipt-bound apply now cover Meta's
|
||||
version preparation too. Core must already match the target. Preparation
|
||||
requires a separate trusted checkout, explicit out-of-run confirmation and
|
||||
unchanged source/tooling receipts; it cannot rewrite the running operator.
|
||||
Plans place Meta after Core and explain the manual preparation/publication
|
||||
steps instead of promising a durable self-update. Ambiguous partial writes
|
||||
require reconciliation, without automatic rollback or retry.
|
||||
- [Runtime-image follow-up](RUNTIME_IMAGE_REMEDIATION_2026-09-08.md): eight new
|
||||
registry-only scans cover four exact candidates on amd64 and arm64. New
|
||||
installer specifications select the patched same-line HAProxy digest;
|
||||
existing specifications retain their explicit image. Other candidates and
|
||||
unresolved inventory/deployment gates remain visible, not blanket-approved.
|
||||
|
||||
## Verification record
|
||||
|
||||
Targeted checks include actual child execution, catastrophic regex CPU,
|
||||
aggregate memory exhaustion, TAR extension metadata, noisy output, malformed
|
||||
transport/staging data, Unicode allocation limits, cancellation/callback
|
||||
failures, descendant cleanup, rollback and explicit retries. Local mixed-owner
|
||||
composition tests completed nine real children, rejected six overlapping
|
||||
requests as busy, observed at most one unreaped child and recovered all slots.
|
||||
This is local admission evidence, not a target deployment load certification.
|
||||
|
||||
Workflow fixtures serialize 40 different pinned revisions with five SQL reads;
|
||||
summary lists use one query for 40 ordinary rows. Exact inbox totals for
|
||||
40/400/4,000 candidates used one query, with measured local costs approximately
|
||||
0.011/0.057/0.492 seconds. These are fixture measurements, not production SLOs.
|
||||
|
||||
The broader Core API smoke suite exposed three stale campaign assertions.
|
||||
All three failures were reproduced against the unchanged private frozen 0.1.45
|
||||
sources. Updated fixtures verify recipient-summary projection, detailed payload
|
||||
separation and explicit fenced recovery of a confirmed stopped runtime; observing
|
||||
SENDING alone must not make a claim recoverable. All 76 smoke tests then passed.
|
||||
No production Campaign behavior was changed to satisfy these tests.
|
||||
|
||||
The final release-tool suite passed 279 tests and 68 subtests, including
|
||||
temporary local remotes and adversarial source/tag/receipt changes. Rechecking
|
||||
the whole batch before effects is deliberately conservative: its repeated
|
||||
filesystem/Git/remote work grows quadratically with batch size. It is not a
|
||||
new unattended publication path or permission to execute unreviewed source.
|
||||
|
||||
Strict interface inventory now reports no unclassified endpoints and exact
|
||||
contextual help for all 133 high-risk controls. Seventeen password browser cases
|
||||
include actual F1 help from the restricted screen, empty workspace scopes,
|
||||
EN/DE layouts, Unicode boundaries and no credential values in help URLs.
|
||||
The initial production bundle remains within the unchanged limits (512,036
|
||||
raw bytes and 162,415 gzip bytes; 1,713 gzip bytes below its ceiling), with
|
||||
46 optional descriptors and no eager optional-module imports.
|
||||
|
||||
The focused checker now includes the new Core process, mixed-owner admission,
|
||||
Access password, Templates and Files worker tests, the repaired campaign smoke
|
||||
cases, and browser-side auth/password transport contracts. The full focused run
|
||||
passed, including 63 production module/build permutations and all 230 browser
|
||||
cases. Its two opt-in Datasources PostgreSQL cases were skipped in that run
|
||||
and subsequently passed against the isolated real database described below.
|
||||
The final Meta preparation gate was added after that full run and verified
|
||||
with the owning release-tool suite and the focused release-gate command.
|
||||
Manifest validation passed for all 72 modules. The full focused log is
|
||||
`/mnt/DATA/tmp/govoplan-security-followup-20260908-focused.log`.
|
||||
|
||||
The first follow-up quick audit captured an unchanged 79-repository snapshot
|
||||
in `/mnt/DATA/tmp/govoplan-security-followup-quick-20260908-7s8Sgh/`.
|
||||
All four required scanners completed, with zero missing/execution reports;
|
||||
all 168 report checksums and 163 machine-readable reports were validated.
|
||||
Gitleaks found no secrets in all 79 histories and 79 worktrees. Local Semgrep
|
||||
rules reported zero findings. Production Bandit reported 65 low and four medium
|
||||
warnings, and production Ruff retained 54 warnings. The two added Bandit
|
||||
warnings identify the new Core subprocess import and invocation: trusted
|
||||
server-owned arguments, no shell, and the documented resource/process boundary
|
||||
were reviewed; warnings remain visible. This is report-only evidence, not a
|
||||
warning-free audit or a penetration test. A final snapshot follows the
|
||||
cross-module declaration/contextual-help corrections and release-tool checks.
|
||||
|
||||
That final audit completed on 8 September, 05:59:46–06:02:18 UTC, in
|
||||
`/mnt/DATA/tmp/govoplan-security-final-quick-20260908-vAwQIh/`. All 79 start/end
|
||||
source fingerprints were identical; all four scanners completed, all 168
|
||||
registered report checksums matched, and all 163 JSON/SARIF reports parsed.
|
||||
There were no missing reports or scanner execution errors. Semgrep and both
|
||||
Gitleaks scopes again reported zero findings. Production counts were unchanged
|
||||
from the first follow-up: Bandit 65 low/four medium and Ruff 54. Test-only
|
||||
counts were Bandit 140 low/34 medium and Ruff 136. A separate frozen scan of
|
||||
all ten changed Meta release/deployment Python files reported seven low Bandit
|
||||
and four Ruff S603 warnings, with no execution errors. Its four argv-only
|
||||
subprocess sites were reviewed; the preparation additions introduced no new
|
||||
warnings. No findings were hidden or severity-wide exceptions added.
|
||||
The audit manifest SHA-256 is
|
||||
`a997b786239cd11443cb665d5f9041a968cc38f9d49171e68bb868bf2bd73310`;
|
||||
its report-checksum list SHA-256 is
|
||||
`dc590ca5b0a4e445019a05536d410226088d67b40d61cd7657bdef4a4eae56d8`.
|
||||
|
||||
The audit includes the eight committed feature/website source changes and the
|
||||
final uncommitted Meta source. Only this evidence document was updated after
|
||||
the source freeze ended; the final Meta commit and remote publication are
|
||||
recorded in the linked Gitea issues, not inferred from local audit completion.
|
||||
|
||||
Fresh dependency audits are retained in
|
||||
`/mnt/DATA/tmp/govoplan-dependency-final-20260908-d24LEK/`: all four full npm
|
||||
lockfile audits (Core WebUI, Mail root/WebUI and website) report zero known
|
||||
vulnerabilities. Installed Python auditing covers 137 distributions with zero
|
||||
known vulnerabilities; 51 local GovOPlaN distributions lack PyPI advisory
|
||||
coverage. Core's 46 linked packages are likewise not claimed covered by public
|
||||
registry advisories. All 12 dependency-file hashes and the installed inventory
|
||||
were unchanged. No packages were installed or automatically fixed.
|
||||
|
||||
Managed PostgreSQL 16.15 fixtures used private Unix sockets, synthetic roles
|
||||
and databases, no TCP listener, per-case schemas and bounded SQL/lock waits.
|
||||
Both previously skipped Datasources races passed. Twenty-one existing Access
|
||||
password HTTP tests and four additional races passed on PostgreSQL: single-use
|
||||
redemption, stale-session/password replacement, competing issuance, and issuer
|
||||
password revocation during redemption. Four release/development migration checks
|
||||
also passed for Access and Workflow, including credential preservation and
|
||||
idempotent indexes. The four races are now owning opt-in Access regressions;
|
||||
see `govoplan-access/docs/PASSWORD_RECOVERY_POSTGRES_TESTS.md`. These local
|
||||
database checks do not certify a deployment, fleet load or external recovery
|
||||
handover. With both explicit PostgreSQL test URLs enabled, the full Access suite
|
||||
passed 122 tests and 18 subtests, and the full Datasources suite passed 57 tests,
|
||||
without skips. Access retained 12 existing SQLite datetime-adapter warnings in
|
||||
its separate SQLite migration cases. Both temporary PostgreSQL fixtures were
|
||||
stopped and independently verified: no server process, private socket,
|
||||
generated schema or synthetic cluster remains. Scripts, logs and shutdown
|
||||
receipts are retained under `/home/zemion/.cache/govoplan-pg-security-20260908.RAUitg/`
|
||||
and `/home/zemion/.cache/govoplan-pg-promoted-20260908.JZcIKL/`.
|
||||
|
||||
## Adoption and remaining gates
|
||||
|
||||
1. `AUTH_LOCAL_PASSWORD_RECOVERY_ENABLED` remains **false** by default. The
|
||||
existing flag is still advisory until an operator explicitly adopts and
|
||||
enables the complete recovery policy. Confirm who verifies identity and how
|
||||
the one-time code is handed over; automated email recovery is not enabled.
|
||||
Test first-login, lost-password, code expiry and administrator availability
|
||||
in the target environment before enforcement.
|
||||
2. Access migration `e9a2c5f8b1d4` adds recovery evidence; Workflow migration
|
||||
`9e6b3f8a2c7d` adds summary-pagination indexes. Use normal backed-up upgrade
|
||||
procedures and account for index-build cost. No manual live migration or
|
||||
server restart was performed during this work. The user's existing devserver
|
||||
has automatic reload, so live schema state must not be assumed unchanged.
|
||||
3. Release preparation must assign new source/package versions and require a
|
||||
Core version containing the new worker/auth contracts in the affected module
|
||||
metadata, including matching WebUI assets. The old immutable release must
|
||||
not be relabelled or treated as containing these APIs.
|
||||
4. Resource limits are not an arbitrary-code, filesystem or network sandbox.
|
||||
Admission is per API/worker process, not fleet-wide. Validate Linux/cgroup
|
||||
memory, disk quotas, process counts, cancellation and legitimate large-file
|
||||
workloads on the intended runtime before increasing concurrency. Core #297
|
||||
retains this target-evidence follow-up.
|
||||
5. Meta #52 and website #9 retain runtime-image/publication/deployment holds.
|
||||
Docker/Podman/HAProxy executables are unavailable here. Final built images,
|
||||
binary/source inventories, ingress behavior, migration/readiness/worker
|
||||
smoke checks and the website's target/operator authority remain outstanding.
|
||||
Zero findings in a detected package inventory is not full image coverage.
|
||||
|
||||
No real messages, IMAP appends, password resets, provider operations or deployment
|
||||
actions were used as test fixtures. Development tests use temporary databases,
|
||||
private temporary files, mock transports and managed test-browser servers.
|
||||
@@ -0,0 +1,207 @@
|
||||
# Security and performance review — 8 September 2026
|
||||
|
||||
Coordinated status: [Core #296](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/296).
|
||||
This records a workspace-wide automated scan, targeted manual boundary review,
|
||||
and a verified implementation pass. It is not a penetration test, an exhaustive
|
||||
line-by-line review, or a security certification. The audit was completed on
|
||||
local, unpublished changes, preserving existing worktree changes. Subsequent
|
||||
release preparation/publication is tracked in
|
||||
[GovOPlaN #51](https://git.add-ideas.de/GovOPlaN/govoplan/issues/51) and the
|
||||
[0.1.45 release notes](../releases/0.1.45.md).
|
||||
|
||||
## Implemented findings
|
||||
|
||||
| Area | Finding and change | Evidence / ownership |
|
||||
| --- | --- | --- |
|
||||
| Authentication — high | Preserve service-account provenance and current scope ceilings instead of recalculating them as ordinary membership permissions. Tenant API keys cannot retain canonical system permissions or unsafe wildcard grants. | Previously failing isolated regressions; [Access #21](https://git.add-ideas.de/GovOPlaN/govoplan-access/issues/21). |
|
||||
| Authentication — medium | Warm-cache API keys must follow the same explicit-header credential rules as cold authentication. A session cookie cannot turn an API key into a session credential. | Regression covering source-dependent authentication. |
|
||||
| Browser authority/cache — medium | Clear reusable data on auth changes and write settlement; fence late 200/304 writes and obsolete 401 side effects. Honor server no-store/no-cache and explicit fresh-read requests. Interactive login/logout remove retained automation keys that could shadow cookie-session identity. | 23 real-client regressions. Unchanged settings keep their object identity, preventing profile-fetch loops. Core `docs/API_CLIENT_CACHE_CONTRACT.md`; owning Access EN/DE session/field documentation. |
|
||||
| Spreadsheet resource exhaustion | Validate actual XLSX coordinates before openpyxl traversal; ignore misleading declared dimensions; count blank row gaps toward the existing limits. | [Connectors #18](https://git.add-ideas.de/GovOPlaN/govoplan-connectors/issues/18), 13 tests and 2 subtests. |
|
||||
| Template resource exhaustion | Enforce the existing 5 MiB output budget during substitution and item construction, including UTF-8, HTML escaping and separators. | [Templates #7](https://git.add-ideas.de/GovOPlaN/govoplan-templates/issues/7), full 20 tests; independent 3,000-case valid-output comparison. |
|
||||
| Archive resource exhaustion | Inspect regular TAR member limits before traversing payloads. Limit archive paths to 4,096 UTF-8 bytes / 128 components and count derived directories against entry limits. | [Files #46](https://git.add-ideas.de/GovOPlaN/govoplan-files/issues/46), 55 archive and 15 documentation tests. Extension-header decoding still needs stronger isolation. |
|
||||
| Dataflow resource exhaustion | Reject LPAD/RPAD target lengths above the existing 1,000,000-byte preview budget before fill evaluation/allocation. Preserve final serialized-byte checks. | [Dataflow #22](https://git.add-ideas.de/GovOPlaN/govoplan-dataflow/issues/22), full 104 tests and 39 subtests; 7 new guard tests independently rerun. |
|
||||
| Docs performance / defense in depth | Batch revision reads per request, avoid loading pending draft bodies for readers, and validate tenant/entry/publication consistency while retaining owner/audience checks. | [Docs #22](https://git.add-ideas.de/GovOPlaN/govoplan-docs/issues/22), full 39 tests. |
|
||||
| Notifications performance / defense in depth | Batch delivery-attempt loading while preserving recipient checks and rejecting inconsistent attempt references, including already-loaded relationships. | [Notifications #6](https://git.add-ideas.de/GovOPlaN/govoplan-notifications/issues/6), full 23 tests. |
|
||||
| Session-list performance | Apply active/expiry predicates and the existing 100-row cap in SQL, before loading session history. | Query-shape regression in Access. |
|
||||
| Reporting correctness | Use structural bind-name suffixes for recursive calculated measures, preserving valid dotted/hyphenated public keys and parameter uniqueness. | [Reporting #10](https://git.add-ideas.de/GovOPlaN/govoplan-reporting/issues/10), full 29 tests. |
|
||||
| Audit hygiene | Redact Gitleaks logs and machine reports on current, history and legacy scanner paths. | 13 audit-wrapper tests enforce the flag. |
|
||||
|
||||
All changed module workflows/limits have owning EN/DE DocumentationTopic updates.
|
||||
Independent review found no concrete regression in the backend changes.
|
||||
|
||||
## Measured performance changes
|
||||
|
||||
These are SQL-query counts in isolated 40-item fixtures, not production latency
|
||||
or throughput claims. Authorization is still evaluated for each request.
|
||||
|
||||
| Projection | Before | After |
|
||||
| --- | ---: | ---: |
|
||||
| Docs reader entries | 41 SELECTs | 2 SELECTs |
|
||||
| Docs editor entries | 81 SELECTs | 2 SELECTs |
|
||||
| Notification list with attempts | 41 SELECTs | 2 SELECTs |
|
||||
|
||||
The Docs 401-entry batching regression uses 3 SELECTs. Resource guards reject
|
||||
oversized work before the formerly expensive allocation/traversal. This does
|
||||
not make every legitimate upload or campaign faster. Honoring no-cache can
|
||||
increase server validation requests; ETags still avoid retransmitting unchanged
|
||||
bodies. That authorization/freshness trade-off is deliberate.
|
||||
|
||||
The original audit snapshot measured 517,380 initial JavaScript bytes and
|
||||
164,119 gzip bytes. Release preparation's pure-defaults split reduces this to
|
||||
516,730 initial bytes and 163,908 gzip bytes. Restoring the missing Tasks
|
||||
descriptor then measures 516,987 initial / 163,976 gzip bytes with all 46 module
|
||||
descriptors lazy, within the unchanged 524,288 / 164,128 caps. The gzip margin is still small; future
|
||||
startup work should reduce eager dependencies, not raise the cap automatically.
|
||||
The full 209-case browser suite passed before the split, followed by 13 focused
|
||||
browser checks after it. Radon recorded 238 rank-D-or-higher entries; complexity
|
||||
is a review-priority signal, not a performance measurement.
|
||||
|
||||
## Dependency remediation
|
||||
|
||||
Core's full npm audit went from 30 affected package entries to zero. Most initial
|
||||
entries were transitive effects of the same Tiptap advisory, not 30 independent
|
||||
application exploits. The website went from two affected entries to zero; both
|
||||
Mail lockfiles also report zero.
|
||||
|
||||
- Tiptap packages are aligned at 3.31.3, with direct minimum ranges raised to
|
||||
3.30.4 in both development and release manifests, with a parity regression.
|
||||
Added an actual installed-library prototype-attribute regression for
|
||||
the [maintainer's security advisory](https://github.com/ueberdosis/tiptap/security/advisories/GHSA-cp6q-959q-f8rh).
|
||||
- Core now resolves xmldom 0.9.12, browserslist 4.28.9 and nanoid 3.3.18.
|
||||
The website's affected browserslist/nanoid dependencies are patched too.
|
||||
- Development/audit requirements now require pip >=26.2; the local development
|
||||
environment uses 26.2.1. The installed audit originally flagged
|
||||
[CVE-2026-13346](https://github.com/advisories/GHSA-qwm4-qh6w-59xr), requiring an
|
||||
attacker-controlled package index. This is an installation-tool vulnerability,
|
||||
not evidence of an exposed application endpoint.
|
||||
|
||||
The final installed Python audit enumerated 188 distributions: 137 were
|
||||
auditable with zero known vulnerabilities, and 51 local distributions were not
|
||||
available in PyPI. Those skips are covered by source review, not by a claim of
|
||||
dependency-advisory coverage. Production images and every optional dependency
|
||||
combination were not independently resolved or scanned.
|
||||
|
||||
## Scan coverage and limitations
|
||||
|
||||
Evidence directory:
|
||||
`/mnt/DATA/tmp/govoplan-security-performance-20260908-gsk8jn/`.
|
||||
|
||||
The final `final-quick/manifest.json` captures 79 repositories, tool versions,
|
||||
start/end repository fingerprints, report checksums, 168 report artifacts and
|
||||
163 validated JSON/SARIF reports. It records an unchanged workspace, complete
|
||||
coverage for its four required scanners, no execution errors and no missing
|
||||
reports. It ran in report-only mode: exit zero does **not** mean zero warnings.
|
||||
|
||||
- Final production Bandit: 447,008 Python lines; 67 warnings (63 low, 4 medium),
|
||||
no high findings. Ruff security rules: 54 warnings. SQL-construction warnings
|
||||
were reviewed against identifier/operator validation and bound values in
|
||||
DuckDB/Reporting; no injection fix was warranted there. XML import warnings
|
||||
were checked: feed/BPMN input parsing uses defusedxml; stdlib imports support
|
||||
types/output construction. Operator-owned fenced-run argv is not a public
|
||||
arbitrary-command endpoint. Xrechnung output buffering remains a follow-up.
|
||||
Assertions and error-swallowing markers remain review/maintenance warnings,
|
||||
not proof that all such code is harmless.
|
||||
- Final local Semgrep rules: no findings. The broader OWASP-rule pass applied
|
||||
272 rules to 4,169 tracked targets. Its seven warnings recommended weakening
|
||||
owner-only 0700 permissions; they were rejected as false positives. One
|
||||
Calendar rule timeout was rerun with a 60-second budget: zero findings/errors.
|
||||
Bash and conformance TypeScript checks passed despite two scanner-specific
|
||||
parser limitations. Ignored/dependency/generated paths are not a complete
|
||||
line-by-line source audit.
|
||||
- Gitleaks: 79 Git histories plus 79 worktrees, 158 redacted reports, zero
|
||||
detected secrets. This does not establish that deployed credentials are safe
|
||||
or that formerly exposed credentials have been rotated.
|
||||
- Tool versions included Semgrep 1.176.1, Bandit 1.9.4, Ruff 0.15.21 and
|
||||
Gitleaks 8.30.1. The downloaded Gitleaks binary archive matched the official
|
||||
release SHA-256 before execution.
|
||||
- The containerized full-toolbox path could not access Docker's daemon. Its
|
||||
full-mode Trivy/misconfiguration and additional OSV scans were **not** run.
|
||||
A subsequent [registry-only runtime image audit](RUNTIME_IMAGE_AUDIT_2026-09-08.md)
|
||||
successfully scanned nine pinned candidates and two same-minor successors
|
||||
for amd64 without Docker. It found unresolved vulnerabilities and inventory
|
||||
gaps; runtime publication is held. This does not complete full-toolbox,
|
||||
arm64, final-runtime-image or deployment coverage.
|
||||
|
||||
No live application probes, database changes, file operations, mail sends,
|
||||
IMAP appends, imports, notification delivery, deployments, commits or pushes
|
||||
were performed. Browser tests used isolated mocked fixtures. Package installs,
|
||||
builds and temporary audit-tool installation were local development operations.
|
||||
|
||||
## Verification and remaining work
|
||||
|
||||
- 209/209 browser conformance tests pass; production Core/website builds,
|
||||
conformance TypeScript, 24 Core client/dependency regressions, 4 real-client
|
||||
Files reload checks, and 72/72 manifest checks pass.
|
||||
- Access's full 91-test suite passed before the final documentation-only update;
|
||||
the final documentation suite passed all 4 tests. Other module counts appear
|
||||
above. The new authentication/resource tests include demonstrated pre-fix
|
||||
failures rather than only structural assertions.
|
||||
- The original focused workspace run stopped at the institutional
|
||||
governance/Portal fixture (`tests/test_institutional_governance_journey.py:223`,
|
||||
`IndexError`). Release preparation fixes its mixed clocks using the existing
|
||||
temporal context, retaining validity-boundary exclusions; 7 journey tests and
|
||||
ambient-year checks pass. Tracked in
|
||||
[Meta #50](https://git.add-ideas.de/GovOPlaN/govoplan/issues/50).
|
||||
- Campaign's apparent host-path issue was ruled out by existing tracked
|
||||
API/build/snapshot guards and 11 passing tests under normal initialization.
|
||||
Release preparation fixes the standalone import cycle through a deferred
|
||||
resolver import without changing validation rules. Fresh-process coverage,
|
||||
all 11 path tests and Campaign's full 611-test suite pass.
|
||||
|
||||
Next coordinated work:
|
||||
|
||||
1. [Hard resource isolation — Core #297](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/297):
|
||||
regex CPU, aggregate allocation, TAR extension metadata, bounded workers and
|
||||
cancellation, followed by production-like concurrent load tests.
|
||||
2. [Forced password change/recovery — Access #22](https://git.add-ideas.de/GovOPlaN/govoplan-access/issues/22):
|
||||
the current flag is advisory only. Do not enable enforcement without a usable
|
||||
local-password/recovery flow and external-provider rules.
|
||||
3. [Bound subprocess output — Xrechnung #2](https://git.add-ideas.de/GovOPlaN/govoplan-xrechnung/issues/2):
|
||||
enforce the existing 2 MiB limit while draining stdout/stderr, not afterwards.
|
||||
4. [Workflow revision batching/history projection — Workflow Engine #3](https://git.add-ideas.de/GovOPlaN/govoplan-workflow-engine/issues/3):
|
||||
batch evidence lookups; separately define explicit history pagination and
|
||||
authorized-total semantics. Docs/notification history volumes also remain.
|
||||
5. Resolve the [runtime image audit](RUNTIME_IMAGE_AUDIT_2026-09-08.md) findings
|
||||
tracked in [Meta #52](https://git.add-ideas.de/GovOPlaN/govoplan/issues/52)
|
||||
and coverage gaps before lifting its publication hold; complete deployment
|
||||
audits, review exposed development credentials and worker quotas, and
|
||||
benchmark realistic tenant sizes/concurrency. The sanctions
|
||||
transport's fixed HTTPS/redirect allowlist is not a demonstrated arbitrary-URL
|
||||
issue, but migration to Core's pinned egress transport remains desirable.
|
||||
|
||||
Operational compatibility: tenant keys relying on accidental system/wildcard
|
||||
permissions must be corrected rather than weakening the guard. Extreme sparse
|
||||
spreadsheets, overly deep/long archive paths and oversized padding intermediates
|
||||
can now fail early with diagnostics. No stored documents or configurations were
|
||||
deleted or silently migrated.
|
||||
|
||||
## Post-release follow-up — 2026-09-08
|
||||
|
||||
The findings and scanner counts above describe the original audit snapshot.
|
||||
The following source fixes are subsequent to the frozen `0.1.45` composition;
|
||||
they do not change its immutable tags or published package bytes.
|
||||
|
||||
- [Xrechnung #2](https://git.add-ideas.de/GovOPlaN/govoplan-xrechnung/issues/2)
|
||||
now enforces the existing shared 2 MiB stdout/stderr limit during execution
|
||||
and kills/reaps the direct validator on overflow, timeout or cancellation.
|
||||
Report reads are bounded to 16 MiB plus one probe byte before interpretation.
|
||||
The 30-test module suite passes; noisy-child and report-read regressions were
|
||||
also demonstrated to fail against the previous source. Owning EN/DE static
|
||||
documentation is updated. POSIX pipe capture is required; disk quotas,
|
||||
descendant isolation and process-level CPU/memory limits remain separate work.
|
||||
- [Meta #54](https://git.add-ideas.de/GovOPlaN/govoplan/issues/54) now preserves
|
||||
the last command's failure status after exhausted installer retries. Twelve
|
||||
isolated stage/scenario combinations cover every retry call site, success,
|
||||
backoff and caller termination under `set -e`. The test is included in the
|
||||
focused checks and installer CI. See the bilingual
|
||||
[installer retry note](../operations/WEBUI_RELEASE_DEPENDENCY_RETRIES.md).
|
||||
|
||||
These are unreleased follow-up source changes, not a new runtime release or
|
||||
deployment. The runtime-image hold under Meta #52 remains in force; the
|
||||
historical peer-dependency workaround still needs its separate review.
|
||||
|
||||
Further implementation and adoption gates are tracked in the
|
||||
[security follow-up](SECURITY_FOLLOWUP_2026-09-08.md), including disposable
|
||||
parsing/execution workers, opt-in password recovery, workflow read projections
|
||||
and the newer runtime-image evidence. The original scanner counts above remain
|
||||
historical and are not silently replaced by later test results.
|
||||
@@ -0,0 +1,305 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"purpose": "Audit evidence only; not a release manifest or active installer configuration.",
|
||||
"observed_at": "2026-09-08T03:56:47.666808+00:00",
|
||||
"runtime_publication_held": true,
|
||||
"website_deployment_held": true,
|
||||
"scan_execution_complete": true,
|
||||
"coverage_complete": false,
|
||||
"scanner": {
|
||||
"name": "Trivy",
|
||||
"version": "0.74.0",
|
||||
"binary_sha256": "d89bcc6510a267f11b773398cbf1be5520ce39f9e8b6633178c4487f05b7d791",
|
||||
"database_metadata": {
|
||||
"Version": 2,
|
||||
"NextUpdate": "2026-09-08T19:06:01.154199291Z",
|
||||
"UpdatedAt": "2026-09-07T19:06:01.154199452Z",
|
||||
"DownloadedAt": "2026-09-07T23:30:53.198039224Z"
|
||||
},
|
||||
"source": "remote",
|
||||
"scanners": [
|
||||
"vuln"
|
||||
],
|
||||
"list_all_packages": true,
|
||||
"images_executed": false,
|
||||
"existing_docker_credentials_used": false,
|
||||
"timeout": "8m",
|
||||
"maximum_image_size": "2GB"
|
||||
},
|
||||
"evidence": {
|
||||
"private_directory": "/home/zemion/.cache/govoplan-runtime-remediation.qfDSWHJh",
|
||||
"summary_sha256": "0d44390408ab35270e4430516f77bf11aa7877334eff2ef19e11e9a863fe5c56",
|
||||
"frozen_images_sha256": "d0cb156f4a88998531ec55ab950067a3f1350ded648f07650c463af101dad467",
|
||||
"scanner_script_sha256": "f64c69e06efc2ad7b5a657a25aa73f9ff586e3685737d525456bcecbe3ab5f07"
|
||||
},
|
||||
"candidates": [
|
||||
{
|
||||
"name": "haproxy",
|
||||
"image": "docker.io/library/haproxy:3.2.23-alpine@sha256:6343ce34a132a5dceaa24767d739df2bd519f8f7c1079ae39e4821334e8eb42e",
|
||||
"disposition": "source_default_updated_binary_runtime_verification_pending",
|
||||
"platforms": [
|
||||
{
|
||||
"platform": "linux/amd64",
|
||||
"manifest_digest": "sha256:0666a2c2f41d341084ed2da85392b48cdcd766adfa28231f31305724ed5c6ea5",
|
||||
"config_digest": "sha256:9621d75e50a8f26d3738ae3cdbf15e98c6aa5cd48e6baca0699492de502156f5",
|
||||
"compressed_layer_bytes": 20516844,
|
||||
"scan_exit_code": 0,
|
||||
"os": {
|
||||
"Family": "alpine",
|
||||
"Name": "3.24.1"
|
||||
},
|
||||
"inventory": [
|
||||
{
|
||||
"type": "alpine",
|
||||
"packages": 24
|
||||
}
|
||||
],
|
||||
"counts": {
|
||||
"CRITICAL": 0,
|
||||
"HIGH": 0,
|
||||
"MEDIUM": 0,
|
||||
"LOW": 0,
|
||||
"UNKNOWN": 0
|
||||
},
|
||||
"fixable_high_critical": 0,
|
||||
"unique_cves": 0,
|
||||
"report_sha256": "40cfc3db74e29f09723f38698660ccdd50ac935c8d6e1e75c6e0555b3e9361fb",
|
||||
"log_sha256": "46881f695780f89c037d5b0b4dc0ded9ea4e459077c7658d80b786efc5754084"
|
||||
},
|
||||
{
|
||||
"platform": "linux/arm64",
|
||||
"manifest_digest": "sha256:cd20b9dc6b4713956a2a043997001a1948167d345e7c8d5bd5ff2e667166651f",
|
||||
"config_digest": "sha256:19796bff8905d4a46c9463c576203b20cac8984d41b7b01ea9cbff55e8422c34",
|
||||
"compressed_layer_bytes": 20970772,
|
||||
"scan_exit_code": 0,
|
||||
"os": {
|
||||
"Family": "alpine",
|
||||
"Name": "3.24.1"
|
||||
},
|
||||
"inventory": [
|
||||
{
|
||||
"type": "alpine",
|
||||
"packages": 24
|
||||
}
|
||||
],
|
||||
"counts": {
|
||||
"CRITICAL": 0,
|
||||
"HIGH": 0,
|
||||
"MEDIUM": 0,
|
||||
"LOW": 0,
|
||||
"UNKNOWN": 0
|
||||
},
|
||||
"fixable_high_critical": 0,
|
||||
"unique_cves": 0,
|
||||
"report_sha256": "0e1326c58abf358d592fa55c94333228ce1094edf4e489fcc4ece6e32d3320f6",
|
||||
"log_sha256": "397c2fbf1044cf50733d68b4b9cc4eb68211d96f1f302d5e9f8af0d11fb0de1c"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "nginx-stable",
|
||||
"image": "docker.io/nginxinc/nginx-unprivileged:1.30.4-alpine@sha256:442753882674b49ae2c1de83ed67896131c0777f56df5005e356e62bc3f7e7ce",
|
||||
"disposition": "candidate_pending_compatibility",
|
||||
"platforms": [
|
||||
{
|
||||
"platform": "linux/amd64",
|
||||
"manifest_digest": "sha256:b8c179cd3c2ae222a873dd59fbae240fadc03836cae5198afc9e9c19919c3880",
|
||||
"config_digest": "sha256:8b5953dae38d27a76bca22373bb920fd6ce8d9d7da21578d2926e678002de8a0",
|
||||
"compressed_layer_bytes": 25526590,
|
||||
"scan_exit_code": 0,
|
||||
"os": {
|
||||
"Family": "alpine",
|
||||
"Name": "3.24.1"
|
||||
},
|
||||
"inventory": [
|
||||
{
|
||||
"type": "alpine",
|
||||
"packages": 70
|
||||
}
|
||||
],
|
||||
"counts": {
|
||||
"CRITICAL": 0,
|
||||
"HIGH": 0,
|
||||
"MEDIUM": 0,
|
||||
"LOW": 0,
|
||||
"UNKNOWN": 0
|
||||
},
|
||||
"fixable_high_critical": 0,
|
||||
"unique_cves": 0,
|
||||
"report_sha256": "3ad164dae3cdf891b12e41451b8a8e65a5e1688824a7c01d848e1274363e6bf9",
|
||||
"log_sha256": "0f99ff3d9b4396de48655bf8299df30c14ba0c579f480d37baa7d2f6a4c11f1d"
|
||||
},
|
||||
{
|
||||
"platform": "linux/arm64",
|
||||
"manifest_digest": "sha256:b6742a0cbd749add25346658991c3da06a8e38796949df120fed78db8c512576",
|
||||
"config_digest": "sha256:4d8b10f5d2ff99e7aa5f161930d8a4693a86a26f288ec8c0d4cd47f2ef5af179",
|
||||
"compressed_layer_bytes": 25892370,
|
||||
"scan_exit_code": 0,
|
||||
"os": {
|
||||
"Family": "alpine",
|
||||
"Name": "3.24.1"
|
||||
},
|
||||
"inventory": [
|
||||
{
|
||||
"type": "alpine",
|
||||
"packages": 70
|
||||
}
|
||||
],
|
||||
"counts": {
|
||||
"CRITICAL": 0,
|
||||
"HIGH": 0,
|
||||
"MEDIUM": 0,
|
||||
"LOW": 0,
|
||||
"UNKNOWN": 0
|
||||
},
|
||||
"fixable_high_critical": 0,
|
||||
"unique_cves": 0,
|
||||
"report_sha256": "506fdeb97525ed8e4d9ae538c42bab7aa2217f662a7135f0f12d16d20410c7a6",
|
||||
"log_sha256": "c41ef9ea091d00f18c7a097404672591bee85e7477ae17d8f5ca771d8e42b2bb"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "caddy",
|
||||
"image": "docker.io/library/caddy:2.11.4-alpine@sha256:5f5c8640aae01df9654968d946d8f1a56c497f1dd5c5cda4cf95ab7c14d58648",
|
||||
"disposition": "not_selected_remaining_fixable_findings",
|
||||
"platforms": [
|
||||
{
|
||||
"platform": "linux/amd64",
|
||||
"manifest_digest": "sha256:98eb57d882ccd5213d1688764db10c1ca2c58a1ca3a6717a3411ad798f7a423a",
|
||||
"config_digest": "sha256:af555904a0961945f16bb323a501457b13a4f7e9bde969b145b97da80b38ecbe",
|
||||
"compressed_layer_bytes": 23907283,
|
||||
"scan_exit_code": 0,
|
||||
"os": {
|
||||
"Family": "alpine",
|
||||
"Name": "3.23.5"
|
||||
},
|
||||
"inventory": [
|
||||
{
|
||||
"type": "alpine",
|
||||
"packages": 32
|
||||
},
|
||||
{
|
||||
"type": "gobinary",
|
||||
"packages": 146
|
||||
}
|
||||
],
|
||||
"counts": {
|
||||
"CRITICAL": 1,
|
||||
"HIGH": 38,
|
||||
"MEDIUM": 41,
|
||||
"LOW": 12,
|
||||
"UNKNOWN": 23
|
||||
},
|
||||
"fixable_high_critical": 39,
|
||||
"unique_cves": 68,
|
||||
"report_sha256": "e483352a1d5b9b97950dcf92e4dfcf4600f5b09306af3d0640b10ca229a07779",
|
||||
"log_sha256": "9cd599d6dd8c101b421fdeb57036cec1f69f815d765a8bf1f850ec60061036f4"
|
||||
},
|
||||
{
|
||||
"platform": "linux/arm64",
|
||||
"manifest_digest": "sha256:1172d4213087d3fc30bafc7ff2c2896180eb0c41ff7f75f315568fb36cabdcba",
|
||||
"config_digest": "sha256:6b08c1b9858ca9a7d99c1da13c3695081e0e604c6cf214ca26a7ce0e2c4fd9b4",
|
||||
"compressed_layer_bytes": 22722712,
|
||||
"scan_exit_code": 0,
|
||||
"os": {
|
||||
"Family": "alpine",
|
||||
"Name": "3.23.5"
|
||||
},
|
||||
"inventory": [
|
||||
{
|
||||
"type": "alpine",
|
||||
"packages": 32
|
||||
},
|
||||
{
|
||||
"type": "gobinary",
|
||||
"packages": 146
|
||||
}
|
||||
],
|
||||
"counts": {
|
||||
"CRITICAL": 1,
|
||||
"HIGH": 38,
|
||||
"MEDIUM": 41,
|
||||
"LOW": 12,
|
||||
"UNKNOWN": 23
|
||||
},
|
||||
"fixable_high_critical": 39,
|
||||
"unique_cves": 68,
|
||||
"report_sha256": "20e04d820e3b27d57575ea177e523e23109fd83344cdf57e184a4d2fad27e803",
|
||||
"log_sha256": "a1d9c37aa7948c137db64040d95e5930c5859a8acd71ac5bc41ff168e2b270c5"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "node-lts",
|
||||
"image": "docker.io/library/node:24-alpine@sha256:e67514e5d0f6c46656005e1b693b2ec9d52e80b641307de684d4a015ba7a4eaf",
|
||||
"disposition": "not_selected_remaining_fixable_findings",
|
||||
"platforms": [
|
||||
{
|
||||
"platform": "linux/amd64",
|
||||
"manifest_digest": "sha256:4caaaf42195bcd6f6f3559a413b20cb8f8ad089e231ee874cf7701643966689f",
|
||||
"config_digest": "sha256:ee289c69ed1ac50a5a042112ea97f132800e2dd53e832da27784f00e45b3289c",
|
||||
"compressed_layer_bytes": 58486244,
|
||||
"scan_exit_code": 0,
|
||||
"os": {
|
||||
"Family": "alpine",
|
||||
"Name": "3.24.1"
|
||||
},
|
||||
"inventory": [
|
||||
{
|
||||
"type": "alpine",
|
||||
"packages": 18
|
||||
},
|
||||
{
|
||||
"type": "node-pkg",
|
||||
"packages": 146
|
||||
}
|
||||
],
|
||||
"counts": {
|
||||
"CRITICAL": 0,
|
||||
"HIGH": 6,
|
||||
"MEDIUM": 11,
|
||||
"LOW": 12,
|
||||
"UNKNOWN": 0
|
||||
},
|
||||
"fixable_high_critical": 6,
|
||||
"unique_cves": 19,
|
||||
"report_sha256": "c927d995dde700c92027f6328dc6c273f0f1445cd8154301480757cb962e02b2",
|
||||
"log_sha256": "c7dc1900cbe39c9f91e228b2770bcbd394ec2914d2b3879478295fc7f8f77ab3"
|
||||
},
|
||||
{
|
||||
"platform": "linux/arm64",
|
||||
"manifest_digest": "sha256:d3724e44ee368606d753e0027eb8d2a94fc1f275e5d9e4620178a12edb655f5f",
|
||||
"config_digest": "sha256:722cc1507731edf58a4c0bc3e29553c44ce5774d0849242c1b237abc79926a0a",
|
||||
"compressed_layer_bytes": 58935654,
|
||||
"scan_exit_code": 0,
|
||||
"os": {
|
||||
"Family": "alpine",
|
||||
"Name": "3.24.1"
|
||||
},
|
||||
"inventory": [
|
||||
{
|
||||
"type": "alpine",
|
||||
"packages": 18
|
||||
},
|
||||
{
|
||||
"type": "node-pkg",
|
||||
"packages": 146
|
||||
}
|
||||
],
|
||||
"counts": {
|
||||
"CRITICAL": 0,
|
||||
"HIGH": 6,
|
||||
"MEDIUM": 11,
|
||||
"LOW": 12,
|
||||
"UNKNOWN": 0
|
||||
},
|
||||
"fixable_high_critical": 6,
|
||||
"unique_cves": 19,
|
||||
"report_sha256": "e5f7799761f23cefc36929381b4186eeb3afb46a2a559c789d12a7eea5dc30d0",
|
||||
"log_sha256": "fd24671f0da4d3b20dc8bcc2718531e6f6e19155d49bed15958965e21dd10813"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
# GovOPlaN Platform Core Ideas
|
||||
|
||||
## Purpose
|
||||
|
||||
GovOPlaN is an institutional governance and operations layer. Its central
|
||||
promise is:
|
||||
|
||||
> Model the institution, orchestrate its work, connect its systems, and
|
||||
> preserve why and under whose authority it acted.
|
||||
|
||||
The platform should let people complete a real task without understanding its
|
||||
repository or module graph. It should let institutions retain control over
|
||||
their data, procedures, providers, and deployment while still sharing
|
||||
interoperable definitions and evidence.
|
||||
|
||||
This document is the stable summary of the ideas that every product package,
|
||||
module, interface, and integration must preserve. Current implementation state
|
||||
lives in [Strategy Status](STRATEGY_STATUS.md).
|
||||
|
||||
## Ten Core Ideas
|
||||
|
||||
### 1. Institutional context before application context
|
||||
|
||||
Work happens for a tenant, institution, organizational unit, function,
|
||||
mandate, jurisdiction, service, case, and represented party. The real actor
|
||||
and represented capacity remain distinct. Application permissions alone do not
|
||||
prove institutional competence.
|
||||
|
||||
### 2. Governance is executable
|
||||
|
||||
Policy is not explanatory prose around an operation. Consequential actions
|
||||
must expose applicable rules, authority, purpose, expected effects, review
|
||||
requirements, recovery behavior, and evidence. Inheritance may tighten a rule
|
||||
but must not silently loosen an upstream constraint.
|
||||
|
||||
### 3. Time has two independent meanings
|
||||
|
||||
Valid time answers when a fact applied. Recorded time answers what the system
|
||||
knew at a point in history. Historical browsing changes the business-data
|
||||
projection, never the current authorization context. Corrections and
|
||||
supersession remain visible rather than rewriting history.
|
||||
|
||||
### 4. One context, many owners
|
||||
|
||||
Cases, tasks, decisions, records, messages, files, appointments, reports, and
|
||||
external objects remain owned by their domain modules or source systems. Stable
|
||||
references create one navigable context without a universal copied master
|
||||
record or cross-module table access.
|
||||
|
||||
### 5. Native and connected operation are peers
|
||||
|
||||
For every integration, GovOPlaN states whether it is authoritative, mirrors an
|
||||
external source, synchronizes governed fields, adds a governance overlay, or
|
||||
keeps a link only. An external system can be used today and replaced later
|
||||
without losing provenance or institutional control.
|
||||
|
||||
### 6. Human work is a first-class system object
|
||||
|
||||
An intake becomes owned, reviewable work. A person can see the current context,
|
||||
next responsible action, reason, deadline, consequence, and completion
|
||||
evidence. Workflow Engine coordinates machine and human transitions; focused
|
||||
views guide people through the relevant platform surfaces.
|
||||
|
||||
Tasks owns explicit work items and the unified work inbox. Workflow Engine owns
|
||||
process execution and resumable handoffs. Notifications attract attention, and
|
||||
domain modules retain their business objects. These boundaries prevent an
|
||||
inbox, workflow, or notification from becoming a second copy of institutional
|
||||
state.
|
||||
|
||||
### 7. Views reduce complexity without changing authority
|
||||
|
||||
The interface is a task- and role-sensitive projection of installed
|
||||
capabilities. Views, dashboards, search, documentation, and workflow-guided
|
||||
surfaces may hide irrelevant functions, but they never grant access. Users can
|
||||
escape a focused mode when policy permits and can always understand why
|
||||
something is unavailable.
|
||||
|
||||
Configurable product areas organize authorized capabilities around work,
|
||||
services, records, communication, meetings, data and institutional
|
||||
responsibility. The optional Quick Access rail presents task-local Work,
|
||||
Calendar, Messages and Files contributions without merging their owners or
|
||||
turning presentation settings into permissions.
|
||||
|
||||
### 8. Evidence and recovery are part of the operation
|
||||
|
||||
Intent, exact input versions, approvals, external effects, receipts,
|
||||
outcome-unknown states, reconciliation, corrections, retention, and recovery
|
||||
belong to one evidence chain. A retry must be idempotent; rollback claims must
|
||||
distinguish reversible local state from effects already observed elsewhere.
|
||||
|
||||
### 9. Inclusion is multi-channel, not portal-only
|
||||
|
||||
Public portal, postbox, mail, telephone, paper, in-person assistance, APIs, and
|
||||
external systems are channels around the same governed work. Assisted entry
|
||||
records who entered information, for whom, from which source, with which
|
||||
attestation, and how the affected person receives a usable receipt and
|
||||
correction path.
|
||||
|
||||
Responsive, mobile, desktop, and embedded launch surfaces are additional ways
|
||||
to enter the same governed context, not separate products with weaker authority
|
||||
or evidence. Common task-local actions may open in bounded overlays while their
|
||||
owning modules retain validation, policy, and persistence.
|
||||
|
||||
### 10. Successful configurations are portable products
|
||||
|
||||
Modules are ingredients. A usable product is a signed configuration package
|
||||
with terminology, forms, policies, workflows, views, reports, provider
|
||||
profiles, documentation, migration rules, and evidence. Institutions derive
|
||||
local packages without forking code or weakening inherited constraints.
|
||||
|
||||
## Platform Planes
|
||||
|
||||
The planes below are ownership lenses, not navigation groups or mandatory
|
||||
deployment tiers.
|
||||
|
||||
| Plane | Responsibility |
|
||||
| --- | --- |
|
||||
| Experience | Shell, views, dashboard, search, help, accessibility, and task-focused composition |
|
||||
| Participation and channels | Portal, postbox, mail, campaigns, calendar, scheduling, consultation, and assisted channels |
|
||||
| Human work and procedure | Services, forms/runtime, cases, tasks, approvals, workflow execution, and domain procedures |
|
||||
| Content, records, and evidence | Files, templates, DMS, eAkte/records, audit, reporting, transparency, and publication |
|
||||
| Institutional governance | Identity, access, tenancy, organizations, functions, mandates, policy, trust, and formal decisions |
|
||||
| Data and integration | Connectors, datasources, dataflow, search, external references, provider health, and reconciliation |
|
||||
| Runtime and assurance | Module composition, operations, deployment, recovery, security evidence, and signed packages |
|
||||
|
||||
Collected product ideas and normalized actor outcomes are preserved in the
|
||||
[Product Input Register](PRODUCT_INPUT_REGISTER.md). They enter implementation
|
||||
only through a named journey, package, or explicit discovery issue.
|
||||
|
||||
## Canonical Distinctions
|
||||
|
||||
The platform must not collapse these pairs:
|
||||
|
||||
- identity vs account vs represented capacity;
|
||||
- role/permission vs function/mandate/competence;
|
||||
- valid time vs recorded time;
|
||||
- purpose for use vs general technical access;
|
||||
- document content vs managed file bytes vs institutional record;
|
||||
- task vs workflow definition vs workflow instance;
|
||||
- approval vs formal decision;
|
||||
- message intent vs transport delivery vs recipient acknowledgement;
|
||||
- source authority vs connector maturity;
|
||||
- current state vs historical evidence;
|
||||
- correction/compensation vs erasure of an observed effect;
|
||||
- a module boundary vs a user-visible product boundary.
|
||||
|
||||
## Product Experience Rule
|
||||
|
||||
The normal user interface speaks in services, work, records, messages,
|
||||
meetings, decisions, and outcomes. Module names, provider IDs, capability names,
|
||||
package coordinates, and schema details are technical provenance. They are
|
||||
visible to administrators and in expandable diagnostics, but they are not the
|
||||
primary information architecture for ordinary work.
|
||||
|
||||
The complete permission-derived tool catalogue remains deliberately available
|
||||
to power users. Product areas and Quick Access provide sensible system and
|
||||
tenant defaults plus governed user personalization; they do not make familiar
|
||||
tools harder to reach merely to conceal modular implementation.
|
||||
|
||||
## Maturity Rule
|
||||
|
||||
A repository, route, model, or unit test does not make a capability complete.
|
||||
Claims advance only with evidence appropriate to the claim:
|
||||
|
||||
1. `scaffold`: boundary and documentation exist;
|
||||
2. `vertical_slice`: useful behavior has focused tests;
|
||||
3. `reference_ready`: an end-to-end reference journey passed target,
|
||||
accessibility, privacy, security, operations, and recovery evidence;
|
||||
4. `supported`: upgrades, interoperability, support procedures, and release
|
||||
guarantees are defined;
|
||||
5. `lts`: compatibility and maintenance windows are contractual.
|
||||
|
||||
## Deliberate Non-Goals
|
||||
|
||||
GovOPlaN does not aim to:
|
||||
|
||||
- replace every specialist system, ERP, DMS, groupware, or data tool;
|
||||
- make one database authoritative for every connected fact;
|
||||
- expose every installed capability to every person;
|
||||
- infer authority from organizational membership alone;
|
||||
- make historical browsing weaken current security;
|
||||
- treat AI output as an unaccountable institutional decision;
|
||||
- create a repository for every noun in the information model;
|
||||
- claim production maturity from local development evidence.
|
||||
|
||||
## Decision Test
|
||||
|
||||
A proposed feature fits the platform when it improves at least one real
|
||||
institutional journey and can answer:
|
||||
|
||||
1. Who owns the object and source of truth?
|
||||
2. In which institutional and temporal context does it apply?
|
||||
3. For which declared purpose may it be used?
|
||||
4. Which policy and authority permit the action?
|
||||
5. What effect, evidence, retention, and recovery behavior result?
|
||||
6. How can it operate with an external owner without losing autonomy?
|
||||
7. How will a person discover and complete it without learning the module
|
||||
graph?
|
||||
@@ -0,0 +1,203 @@
|
||||
# GovOPlaN Product Input Register
|
||||
|
||||
## Purpose
|
||||
|
||||
This document preserves and normalizes product ideas and user-story notes that
|
||||
inform GovOPlaN without turning a private note file into a second backlog.
|
||||
Gitea issues remain the source of live work state; the stable platform direction
|
||||
remains in [Platform Core Ideas](PLATFORM_CORE_IDEAS.md), the
|
||||
[Connected Governance Platform Roadmap](reference/CONNECTED_GOVERNANCE_PLATFORM_ROADMAP.md),
|
||||
and the [Reference Journey Program](REFERENCE_JOURNEY_PROGRAM.md).
|
||||
|
||||
The register was reconciled on 2026-08-06 from:
|
||||
|
||||
- `/mnt/DATA/Nextcloud/ADD ideas UG/Products/govoplan/ideas.md`;
|
||||
- `/mnt/DATA/Nextcloud/ADD ideas UG/Products/govoplan/user_stories.txt`.
|
||||
|
||||
The source notes remain useful as the original capture. This maintained version
|
||||
uses consistent terminology, makes ownership explicit, and records where an
|
||||
idea enters the product program.
|
||||
|
||||
## Product Themes
|
||||
|
||||
### Operable and scalable installation
|
||||
|
||||
An operator should be able to install, update, reconfigure, scale, back up,
|
||||
restore, pause, and retire GovOPlaN through one explainable control plane.
|
||||
Existing infrastructure may be reused or managed components may be provisioned.
|
||||
The WebUI and CLI must invoke the same governed operations, show the planned and
|
||||
completed effects, preserve recovery evidence, and never claim rollback for an
|
||||
external effect that cannot actually be reversed.
|
||||
|
||||
This theme is owned by Core, Admin, Ops, Policy, Files, and the signed product
|
||||
package. It is tracked primarily by GovOPlaN #13 and the production evidence
|
||||
issues. It advances in parallel with, but does not replace, actor-facing
|
||||
reference journeys.
|
||||
|
||||
### Focused, consistent work
|
||||
|
||||
People should see the work and tools relevant to the current task, not the
|
||||
installed module graph. Views may be defined by administrators, groups, or
|
||||
users within policy. Workflow instances may pin a governed View. Contextual
|
||||
help, predictable action placement, consistent central components, visible
|
||||
intermediate results, and plain institutional terminology are product
|
||||
requirements.
|
||||
|
||||
Small task-local actions such as writing a Mail or Postbox message, completing
|
||||
a Template, or manipulating Files should be launchable without abandoning the
|
||||
current context. These actions remain owned by their modules and use bounded
|
||||
overlays or workspaces; the shell supplies discovery and return context rather
|
||||
than reimplementing them.
|
||||
|
||||
The accepted first presentation is the optional, configurable Quick Access
|
||||
rail: Work, Calendar, Messages and Files. Messages may compose Mail, Postbox
|
||||
and future chat contributions while preserving their separate authority and
|
||||
channel semantics. System and tenant administrators govern availability and
|
||||
forced entries; users select categories and ordering within those ceilings.
|
||||
Modules register typed contributions through Core and continue to work when
|
||||
Quick Access is absent.
|
||||
|
||||
This theme is owned by Core experience contracts, Views, Dashboard, Tasks,
|
||||
Workflow Engine, Quick Access, Docs, and the contributing feature modules. The
|
||||
first proof is the resumable service-to-decision/eAkte journey in GovOPlaN #42.
|
||||
|
||||
### Governed human work
|
||||
|
||||
An intake or event becomes owned work with a responsible actor or function,
|
||||
priority, deadline, current action, consequence, source context, and completion
|
||||
evidence. Tasks owns explicit work items and the unified work inbox. Workflow
|
||||
Engine owns process execution, waits, retries, and handoffs. Domain modules own
|
||||
the business objects and commands. Notifications attract attention but do not
|
||||
replace durable work state.
|
||||
|
||||
This distinction applies to service requests, technical support, approvals,
|
||||
data reconciliation, campaigns, meetings, decisions, records, and failed
|
||||
automation. It is the immediate shared implementation priority because users
|
||||
must be able to leave work and resume it safely.
|
||||
|
||||
### Institutional responsibility and workforce context
|
||||
|
||||
Organization units, functions, mandates, assignments, delegations, and acting
|
||||
context determine institutional responsibility. Presence, absence, illness,
|
||||
availability, and similar status are effective-dated operational facts used to
|
||||
route work, suppress or redirect notifications, explain planning, and trigger
|
||||
policy. They are not merely profile decorations and they do not replace the IDM
|
||||
lifecycle status of an identity or account.
|
||||
|
||||
Time recording, absence management, sickness reporting, return-to-work
|
||||
management, and applicant management form a possible workforce package. The
|
||||
first implementation must be driven by a real journey and legal/privacy
|
||||
profile; no new module boundary is implied solely by this register.
|
||||
|
||||
### Integration-first and provider-neutral operation
|
||||
|
||||
GovOPlaN should integrate tightly with software already used by an institution
|
||||
and offer native alternatives only where that produces a better governed
|
||||
outcome. Core-mediated provider contracts expose stable, vendor-neutral
|
||||
capabilities; adapters encapsulate specific products. Authority, synchronized
|
||||
fields, conflict behavior, health, credential custody, provenance, and
|
||||
retirement must be explicit.
|
||||
|
||||
The LBV Baden-Wuerttemberg idea is retained as a candidate workforce/payroll
|
||||
integration profile and as a test of provider-neutral contracts. Desktop and
|
||||
groupware integration for Microsoft Office, Outlook, LibreOffice, Thunderbird,
|
||||
file managers, Windows, Unix, and macOS should use standards, deep links,
|
||||
protocol handlers, synchronization, and governed connectors before custom
|
||||
desktop software is introduced.
|
||||
|
||||
### Inclusive channels and device surfaces
|
||||
|
||||
Portal, Postbox, Mail, telephone, paper, in-person assistance, API, desktop,
|
||||
and mobile are channels around the same governed work. A responsive or native
|
||||
mobile surface must not create a second authority or data model. Assisted work
|
||||
records representation, source, attestation, receipt, correction, and delivery
|
||||
choice. People may opt into permitted distribution channels while policy keeps
|
||||
mandatory channels and legal delivery requirements explicit.
|
||||
|
||||
Video meetings, chat, instant messaging, and forums are retained as governed
|
||||
collaboration-channel candidates. The default direction is integration with an
|
||||
established provider through typed message, meeting, participant, evidence, and
|
||||
retention contracts before building another communications stack.
|
||||
|
||||
### Meetings, deliberation, decisions, and voting
|
||||
|
||||
An institutional meeting spans scheduling, participants and mandates,
|
||||
documents, agenda, discussion, formal motions, votes, decisions, minutes,
|
||||
follow-up work, publication, and eligible expense settlement. Committee owns
|
||||
the meeting and deliberation semantics while Calendar, Scheduling, Files,
|
||||
Templates, Decisions, Tasks, Reporting, Ledger, and Voting contribute optional
|
||||
capabilities.
|
||||
|
||||
Voting requiring certified assurance remains a provider program. POLYAS is the
|
||||
first external profile; a native provider may progress only through the
|
||||
controlled assurance and certification program already tracked in Voting.
|
||||
|
||||
### Controlled data work and understandable reporting
|
||||
|
||||
People should manipulate data through immutable inputs, previewed operations,
|
||||
intermediate materializations, reversible definition changes, durable review
|
||||
decisions, quality rules, and complete lineage. Reports expose their definitions
|
||||
and source revisions so controllers can understand and change how a result is
|
||||
produced. Technical support may package controlled workflows that let
|
||||
non-technical users safely operate otherwise hidden data.
|
||||
|
||||
The monthly-data journey is the first proof. Sanctions screening follows on the
|
||||
same source, snapshot, transformation, review, reporting, workflow, and
|
||||
delivery contracts.
|
||||
|
||||
### Institutional memory and consequence
|
||||
|
||||
Decisions should be prepared, discussed, made, communicated, implemented, and
|
||||
filed with their authority and consequences visible. A record/eAkte provides
|
||||
the familiar administrative context across exact source revisions without
|
||||
copying ownership from Cases, Decisions, Files, Forms, Campaign, Postbox, or
|
||||
other modules. The institutional digital twin may later use governed
|
||||
projections to model and simulate organizational change, but simulation output
|
||||
never becomes authority without an explicit adoption decision.
|
||||
|
||||
## Normalized Story Catalogue
|
||||
|
||||
The following catalogue preserves the intent of the collected notes. It is an
|
||||
orientation index, not a completion checklist.
|
||||
|
||||
| Actor and desired outcome | Product owner or composition | First proof |
|
||||
| --- | --- | --- |
|
||||
| Operator installs, updates, scales, backs up, restores, and rolls back through one explainable workflow | Core, Admin, Ops, signed package | GovOPlaN #13 and target-evidence lane |
|
||||
| System and tenant module administrators govern module availability and lifecycle | Core, Admin, Policy, Tenancy | Module entitlement and lifecycle composition |
|
||||
| User works in a decluttered, consistent and task-sensitive interface | Views, Core, Dashboard, Workflow, Docs | Service-to-decision workspace |
|
||||
| Policy maker defines inherited, explainable and enforced rules | Policy plus every consequential owner | Information-governance adoption gate |
|
||||
| Controller and auditor reconstruct results, rules, evidence and correction paths | Audit, Reporting, Records, Dataflow | Monthly-data and eAkte journeys |
|
||||
| User sees institutional terminology, current progress, intermediate results and consequences | Domain owner, Tasks, Workflow, Views | All reference journey acceptance tests |
|
||||
| Voting body and voter obtain independently assured democratic voting | Voting, Committee, Identity Trust, Encryption | POLYAS profile and controlled native-provider program |
|
||||
| Support staff packages safe guided manipulation of hidden data | Workflow, Dataflow, Tasks, Views | Monthly reconciliation workflow |
|
||||
| Data worker performs controlled, understandable and recoverable transformations | Datasources, Connectors, Dataflow, Reporting | GovOPlaN #8 |
|
||||
| Decision maker prepares, deliberates, decides, records and follows consequences | Committee, Decisions, Tasks, Records, Reporting | Service-to-decision journey |
|
||||
| Management delegates responsibility and receives governed activity reports | Organizations, IDM, Access, Policy, Reporting | Function-bound Postbox and work inbox |
|
||||
| Institution models and simulates organizational change | Organizations, Policy, Dataflow, Reporting, Digital Twin | Later governed digital-twin package |
|
||||
| Sender distributes generated files to functions without knowing incumbents | Campaign, Distribution Lists, Postbox, Organizations, IDM | Governed communication package |
|
||||
| Function holder receives current and policy-selected historical work and information | IDM, Access, Postbox, Tasks, Records | Postbox reassignment/history tests |
|
||||
| Administrative worker accesses one familiar eAkte context across exact owned objects | Records and record-source providers | GovOPlaN #42 and Records #8 |
|
||||
| User invokes common message, template and file actions without leaving the current task | Core shell, Views, Workflow and contributing modules | Task-local action contract and service workspace |
|
||||
|
||||
## Idea Preservation Map
|
||||
|
||||
| Original idea cluster | Preserved direction |
|
||||
| --- | --- |
|
||||
| Time recording, absence, sickness, reintegration, applicant management | Governed workforce-context journey; effective-dated status and privacy profile before module expansion |
|
||||
| LBV BW interface | Candidate provider-neutral workforce/payroll connector profile |
|
||||
| Abstract interfaces | Versioned Core contracts with product adapters and explicit source authority |
|
||||
| Desktop and groupware integration | Standards, connectors, deep launch and synchronization before custom clients |
|
||||
| GovOPlaN app/mobile-first pages | Responsive shared semantics; native shell only when a proven journey needs device capabilities |
|
||||
| Video, chat, instant messaging and forum | Optional governed collaboration providers with retention/evidence contracts |
|
||||
| Somacos Session-style meeting management | Committee-led meeting composition across Calendar, Files, Decisions, Templates, Tasks, Reporting and Ledger |
|
||||
| Stronger software integration | Integration-first roadmap rule and first full external product connectors |
|
||||
|
||||
## Maintenance
|
||||
|
||||
When a source idea becomes actionable:
|
||||
|
||||
1. link it to a named reference journey or explicit discovery issue;
|
||||
2. identify the owning module and external authority;
|
||||
3. create or update the Gitea issue with acceptance criteria;
|
||||
4. keep live status out of this document;
|
||||
5. update this register only when the durable interpretation changes.
|
||||
@@ -2,15 +2,86 @@
|
||||
|
||||
## Status
|
||||
|
||||
This is the selected product-development sequence as of 2026-07-21. It turns
|
||||
the long-term connected-platform roadmap into five demonstrable journeys while
|
||||
the Workflow program remains deliberately deferred.
|
||||
This is the selected product-development sequence, originally chosen on
|
||||
2026-07-21 and reconciled with the implemented platform on 2026-07-31. It turns
|
||||
the long-term connected-platform roadmap into five demonstrable journeys.
|
||||
Workflow Engine and the optional Workflow editor now exist, but they are used
|
||||
by a stage only when its package explicitly composes and proves them; Workflow
|
||||
is not an automatic dependency of every journey.
|
||||
|
||||
The institutional semantics and source-authority model applied to these stages
|
||||
are defined in the
|
||||
[Institutional Governance Target Architecture](../architecture/INSTITUTIONAL_GOVERNANCE_TARGET_ARCHITECTURE.md).
|
||||
|
||||
The stages are ordered, but they are not monolithic releases. Each stage is
|
||||
delivered as small, reviewable, green increments and is complete only when its
|
||||
user journey, failure behavior, documentation, and operator evidence work in a
|
||||
pinned composition.
|
||||
|
||||
## 2026 outcome reset
|
||||
|
||||
Repository completion is not product completion. From 2026-08-05 onward, work
|
||||
is accepted primarily through three maintained real-life journeys:
|
||||
|
||||
1. **Governed communication:** select accountable recipients, prepare content
|
||||
and attachments, approve, deliver through Mail and/or a function-bound
|
||||
Postbox, reconcile uncertain outcomes, and file the evidence.
|
||||
2. **Inclusive service-to-decision:** accept a request through a digital or
|
||||
assisted channel, establish identity and purpose, guide the case through
|
||||
human and automatic work, decide, notify, and file the resulting eAkte.
|
||||
3. **Monthly data and sanctions:** acquire immutable source snapshots, validate
|
||||
and reconcile them interactively, preserve decisions and lineage, produce
|
||||
reports and files, and deliver the accepted result through Campaign.
|
||||
|
||||
The staged program below remains the architectural build order. These journeys
|
||||
are the acceptance lens across those stages. Every significant feature should
|
||||
identify the journey it improves, or provide security, operability, recovery,
|
||||
accessibility, or usability evidence that those journeys require. Work that
|
||||
does neither stays in the backlog until a concrete consumer exists.
|
||||
|
||||
The maintained service-to-decision scenario is the German resident parking
|
||||
permit (`Anwohnerparkausweis`), pinned by
|
||||
`tests/fixtures/resident_parking_permit_journey.json`. It replaces generic
|
||||
permit examples as acceptance evidence and fixes the service, exact Form
|
||||
revision, digital and assisted intake, Case and Workflow handoff, formal
|
||||
Decision, Postbox delivery, and Records target. Changing this flagship scenario
|
||||
is a product decision; implementations may add further scenarios without
|
||||
weakening or silently replacing its acceptance gates.
|
||||
|
||||
The reference fixes an email-link applicant-status profile. The exact
|
||||
published Form revision names the linked email field and bounded expiry/request
|
||||
limits. Submission issues a tracking grant, a matching request delegates mail
|
||||
delivery to Notifications using a hash-only short-lived secret, and Portal
|
||||
presents only the public lifecycle projection. Forms Runtime's module tests
|
||||
also cover authenticated-only and permanent-link variants; the flagship keeps
|
||||
email-link mode because it exercises identity minimization, delivery,
|
||||
revocation, resend, expiry, and non-enumerating failure behavior in one slice.
|
||||
|
||||
The Case-to-payment handoff now has an executable first contract as well. The
|
||||
flagship requests a fixed EUR obligation through `payments.requests`, retains
|
||||
the Case and Workflow context references, proves exact replay, and reconciles a
|
||||
full offline receipt against a Files-owned immutable evidence reference. This
|
||||
does not simulate online checkout or accounting: provider callbacks, partial
|
||||
payments, corrections, refunds, Ledger posting, and XRechnung remain separate
|
||||
governed slices.
|
||||
|
||||
The Records vertical now supplies the journey's native file plan, immutable
|
||||
record and item revisions, chronology, close/reopen, retention calculation,
|
||||
holds, appraisal, independent disposition approval, recovery-ledger evidence,
|
||||
and archive-neutral package simulation. Forms Runtime, Cases, and Decisions
|
||||
expose exact, permission-rechecked source revisions for explicit filing, and
|
||||
all three contribute metadata-only native Search projections that can be
|
||||
rebuilt from authoritative state. The executable fixtures prove those native
|
||||
transitions without claiming archival custody. A persisted Workflow Engine
|
||||
handoff is now reloaded through the Tasks aggregation surface and remains
|
||||
visible until the authoritative Workflow transition completes. Authenticated
|
||||
assisted intake now uses the same exact Form revision and validation as digital
|
||||
intake while retaining purpose, authority, party, channel, accessibility,
|
||||
source, correction, and payload-bound read-back evidence across a session
|
||||
restart. The journey still needs browser accessibility evidence for both
|
||||
channels, pinned-composition reconstruction evidence, and one target-tested
|
||||
archive profile.
|
||||
|
||||
## Why this sequence
|
||||
|
||||
The sequence grows one connected product rather than advancing repositories in
|
||||
@@ -79,6 +150,23 @@ journey needs and supplies contracts shared by all five stages.
|
||||
execution. Database, broker, cache, and worker channels are constrained by
|
||||
deployment network policy and authenticated transport rather than treated as
|
||||
tenant connector profiles.
|
||||
10. **Information governance.** Temporal browsing, purpose-aware access,
|
||||
retention/legal-hold behavior, and institutional acting context are applied
|
||||
to every owned object type. Historical reads use current authorization.
|
||||
Module manifests state `contract_only`, `partial`, `enforced`, or
|
||||
`not_applicable` adoption with evidence; supported maturity is blocked until
|
||||
every applicable dimension is enforced.
|
||||
11. **Durable human work.** Tasks aggregates explicit work and module-owned
|
||||
attention items; Workflow Engine persists process state and handoffs;
|
||||
Notifications attracts attention; Views focuses the relevant surfaces.
|
||||
Leaving or refreshing the browser never becomes the only record that work
|
||||
remains unfinished.
|
||||
12. **Task-local tools.** Mail, Postbox, Templates, Files, and other common
|
||||
actions may contribute bounded launch surfaces with return context. The
|
||||
shell and Workflow compose them without copying their data or validation.
|
||||
The optional Quick Access module presents configurable Work, Calendar,
|
||||
Messages and Files categories; system/tenant/user settings and View/Policy
|
||||
ceilings resolve their availability and ordering.
|
||||
|
||||
## Documentation contract for every reference stage
|
||||
|
||||
@@ -101,6 +189,9 @@ Every demonstrated journey provides:
|
||||
provenance, evidence, retention, and destructive actions.
|
||||
- **Acceptance view:** runnable examples, expected results, failure injection,
|
||||
and release gates.
|
||||
- **Channel and records view:** assisted/non-digital intake and output,
|
||||
representation, provenance, filing, retention, legal hold, and archive
|
||||
consequences where the journey creates evidence or a record.
|
||||
|
||||
The Docs module selects and links these views according to installed
|
||||
capabilities and actor context. Feature repositories remain the source of
|
||||
@@ -429,6 +520,9 @@ or the external editor the document-lifecycle owner.
|
||||
link, callback, webhook, file, identity, or data row.
|
||||
- Do not claim a stage complete from local unit tests. Use pinned composition,
|
||||
target integration, failure drills, adaptive docs, and operator evidence.
|
||||
- Do not claim a module complete while its relevant information-governance
|
||||
dimensions remain `contract_only` or while the reference journey lacks an
|
||||
assisted-channel and records outcome where those are applicable.
|
||||
- A later stage may prototype contracts while the preceding gate is being
|
||||
proven, but it may not redefine an owning module's boundary by convenience.
|
||||
|
||||
@@ -445,8 +539,9 @@ that first needs them:
|
||||
- exact HIS/CampusOnline source endpoints, student-statistics keys and accepted
|
||||
calculation, freeze/correction policy, privacy profile, and permitted
|
||||
drill-down level;
|
||||
- whether repeated data-source and dataflow contracts justify separate
|
||||
`govoplan-datasources` and `govoplan-dataflow` modules after the Stage 3 proof;
|
||||
- datasource provider selection and quality/promotion policy; the architecture
|
||||
now separates `govoplan-datasources` lifecycle from `govoplan-connectors`
|
||||
acquisition and `govoplan-dataflow` transformation;
|
||||
- first collaborative editor/provider and whether the first UX is concurrent
|
||||
editing, controlled check-out, or both; and
|
||||
- first Records/archive target and approval/signature assurance level.
|
||||
@@ -0,0 +1,100 @@
|
||||
# GovOPlaN Roadmap
|
||||
|
||||
## Purpose
|
||||
|
||||
GovOPlaN should become the connective, governance-aware operating layer of an
|
||||
institution: people complete services and work without learning the module
|
||||
graph, while the institution can explain authority, policy, source data,
|
||||
effects, evidence, and recovery.
|
||||
|
||||
This is the concise product roadmap. It states durable outcomes and sequence,
|
||||
not release dates or issue state. Use [Strategy Status](STRATEGY_STATUS.md) for
|
||||
the current reconciliation and Gitea issues for active work. The
|
||||
[detailed connected-platform vision](reference/CONNECTED_GOVERNANCE_PLATFORM_ROADMAP.md)
|
||||
retains stakeholder perspectives, configuration archetypes, and the complete
|
||||
outcome-story catalogue.
|
||||
|
||||
## Product Promise
|
||||
|
||||
GovOPlaN will:
|
||||
|
||||
1. model institutional context, responsibility, authority, and time;
|
||||
2. turn incoming information into owned, reviewable human and machine work;
|
||||
3. connect native and external systems without obscuring the source of truth;
|
||||
4. preserve decisions, effects, records, corrections, and recovery evidence;
|
||||
5. support digital, assisted, paper, message, calendar, and system channels as
|
||||
paths through the same governed work; and
|
||||
6. package successful configurations so institutions can adopt them without
|
||||
code forks or loss of local autonomy.
|
||||
|
||||
It will not replace every specialist system, copy all data into one master
|
||||
database, infer authority from membership, or claim production maturity from
|
||||
repository breadth.
|
||||
|
||||
## Outcome Horizons
|
||||
|
||||
| Horizon | Outcome | Completion evidence |
|
||||
| --- | --- | --- |
|
||||
| Trustworthy baseline | A pinned composition can be installed, upgraded, operated, explained, and recovered. | Signed artifacts, clean install/upgrade, provider failure tests, restore drill, coherent UI, and target evidence |
|
||||
| Connected work | Intake becomes accountable work with context, assignment, review, communication, and evidence. | One digital and assisted service reaches a decision and eAkte without losing responsibility or state |
|
||||
| Reusable products | Complete service, communication, and data outcomes ship as governed configuration packages. | Two materially different deployments adapt packages without code forks |
|
||||
| Institutional assurance | Records, transparency, privacy, risk, regulated review, and reporting connect to real operations. | A consequential decision can be reconstructed, corrected, retained, and disclosed under policy |
|
||||
| Federated ecosystem | Autonomous installations exchange signed data and configuration across explicit trust boundaries. | Paired-instance exchange, reconciliation, supported deployment profiles, and independent evidence |
|
||||
|
||||
## Current Sequence
|
||||
|
||||
The sequence is outcome-led. Shared foundation work enters when one of these
|
||||
proofs needs it.
|
||||
|
||||
1. **Enforce the platform quality contract.** German is the reference locale;
|
||||
help, accessibility, temporal browsing, purpose-aware access, retention,
|
||||
institutional context, optional-module combinations, and recovery behavior
|
||||
become measurable release gates.
|
||||
2. **Complete governed communication.** Prove recipient selection, Campaign,
|
||||
Files, Mail, function-bound Postbox delivery, acknowledgement, uncertain
|
||||
outcomes, correction, filing, and recovery against a named target.
|
||||
3. **Complete the monthly-data and sanctions journey.** Acquire immutable
|
||||
source snapshots, validate and reconcile data interactively, preserve
|
||||
lineage and review, publish reports and files, and deliver accepted results.
|
||||
4. **Complete inclusive service to decision.** Accept digital or assisted
|
||||
input, establish actor and purpose, persist human handoffs, decide, notify,
|
||||
and reconstruct the exact eAkte under current authorization.
|
||||
5. **Complete discovery and external coexistence.** Finish native PostgreSQL
|
||||
search coverage, prove reauthorization and reindexing, then prove one
|
||||
external product connector and one paired GovOPlaN federation exchange.
|
||||
6. **Prove production operation.** Complete multi-host, provider, restore,
|
||||
accessibility, volume, key-custody, and independently signed target
|
||||
evidence before raising maturity claims.
|
||||
|
||||
## Continuous Foundation
|
||||
|
||||
Every journey applies the same boundaries:
|
||||
|
||||
- modules cooperate through versioned Core contracts and typed references;
|
||||
- permissions, policy, institutional context, purpose, and current authority
|
||||
are evaluated before presenting or acting on data;
|
||||
- requested actions, durable intent, observed effects, unknown outcomes,
|
||||
retries, reconciliation, and correction remain distinct;
|
||||
- Workflow Engine coordinates stable module-owned actions and human handoffs;
|
||||
it does not become a second owner of domain state;
|
||||
- Files owns managed bytes, Records owns institutional filing and retention,
|
||||
and source systems retain explicitly declared authority;
|
||||
- focused views and product areas reduce interface complexity without granting
|
||||
access or hiding material consequences;
|
||||
- configuration packages include terminology, forms, policies, workflows,
|
||||
views, reports, providers, documentation, migration, and evidence; and
|
||||
- maturity advances from scaffold to vertical slice, reference-ready,
|
||||
supported, and LTS only with evidence appropriate to each claim.
|
||||
|
||||
## Decision Rule
|
||||
|
||||
A roadmap item should answer all of the following before implementation:
|
||||
|
||||
1. Which real journey and actor outcome does it improve?
|
||||
2. Which module or external system owns each object and source of truth?
|
||||
3. Which institutional, temporal, purpose, and policy context applies?
|
||||
4. Which effects, evidence, retention, failure, and recovery states result?
|
||||
5. Which package and target evidence will prove the outcome?
|
||||
|
||||
If those answers are missing, retain the idea in the Product Input Register or
|
||||
Gitea discovery work rather than opening an unbounded implementation program.
|
||||
@@ -0,0 +1,137 @@
|
||||
# GovOPlaN Strategy Status
|
||||
|
||||
## Status Record
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| Reconciled on | 2026-08-17 |
|
||||
| Source scope | Local workspace manifests, source inventory, focused journey checks, signed release evidence, and live Gitea issue state |
|
||||
| Stable direction | [Platform Core Ideas](PLATFORM_CORE_IDEAS.md) and [Roadmap](ROADMAP.md) |
|
||||
| Collected product input | [Product Input Register](PRODUCT_INPUT_REGISTER.md) |
|
||||
| Delivery source | Gitea issues |
|
||||
|
||||
This is the only prose source for current cross-product status. It is a
|
||||
reconciliation, not a release certification. Module manifests and target
|
||||
evidence remain authoritative for specific maturity claims.
|
||||
|
||||
## Portfolio Snapshot
|
||||
|
||||
- 67 source module manifests were loadable and architecture-declared.
|
||||
- 50 modules declared `vertical_slice`; 17 declared `scaffold`.
|
||||
- No module declared `reference_ready`, `supported`, or `lts`.
|
||||
- The coordinated package version was `0.1.18`, with version alignment passing
|
||||
across all 78 release repositories.
|
||||
- The live portfolio had 137 open issues: 42 priority-P1, 92 priority-P2, and
|
||||
3 priority-P3 items. Every open issue had labels.
|
||||
- 129 open issues had no milestone, so issue labels do not yet express a
|
||||
reliable completion sequence on their own.
|
||||
- Three product package manifests existed: governed communication, governed
|
||||
data and assurance, and service to decision. None had crossed the complete
|
||||
target-evidence gate.
|
||||
|
||||
These counts are dated. Refresh them rather than copying them into another
|
||||
document.
|
||||
|
||||
## Interface And Contract Evidence
|
||||
|
||||
The 2026-08-17 source inventory found:
|
||||
|
||||
- 1,344 UI fields and 1,331 UI actions;
|
||||
- 8,412 stable interface declarations with no duplicate IDs;
|
||||
- 43 frontend routes and 943 backend endpoints;
|
||||
- no public WebUI surfaces missing runtime declarations;
|
||||
- no stale runtime route declarations;
|
||||
- no unclassified endpoint without a static UI reference;
|
||||
- all 1,344 fields with a resolvable F1 context; 175 have statically specific
|
||||
help and 1,169 remain candidates for richer field-specific content beyond
|
||||
page/module fallback;
|
||||
- German (`de`) as the complete reference locale and no used key missing from
|
||||
the required German or English catalogs;
|
||||
- 3 module information-governance dimensions classified as `enforced`, 1 as
|
||||
`partial`, and 264 as `contract_only`.
|
||||
This is an honest platform-wide baseline, not a claim that temporal,
|
||||
purpose, retention, and institutional-context adoption is complete.
|
||||
|
||||
## Credible Current Outcomes
|
||||
|
||||
### Platform foundation
|
||||
|
||||
Module discovery, optional dependency validation, migrations, shared WebUI,
|
||||
tenant and access foundations, signed catalogs/packages, event delivery,
|
||||
recovery contracts, contextual help, views, temporal titlebar context, and
|
||||
stateless-runtime patterns are implemented and tested at varying depths.
|
||||
|
||||
### Governed communication
|
||||
|
||||
Campaign authoring, recipient data, attachments, templates, mail profiles,
|
||||
mock/real delivery paths, audit evidence, reporting, distribution-list
|
||||
composition, and optional Postbox delivery form the deepest product cluster.
|
||||
Target provider, accessibility, recovery, and high-volume evidence still
|
||||
prevent a reference-ready claim.
|
||||
|
||||
### Institutional service and decision
|
||||
|
||||
Services, Forms, Forms Runtime, Cases, Parties, Mandates, Approvals, Committee,
|
||||
Voting, Decisions, Portal, Postbox, and Audit have an executable service-to-
|
||||
decision fixture. Public and invitation intake can retain Files-backed
|
||||
evidence; Forms submissions, Cases, and formal Decisions can be explicitly
|
||||
filed as exact eAkte source revisions and reconstructed through permission-
|
||||
rechecked native Search projections. A durable Workflow Engine handoff now
|
||||
survives session restart and appears through the Tasks work inbox until the
|
||||
authoritative transition completes. Browser-complete assisted intake, broader
|
||||
work projections and escalation, production identity and delivery, a named
|
||||
archive profile, and target evidence remain.
|
||||
|
||||
### Governed data and assurance
|
||||
|
||||
Connectors, Datasources, Dataflow, Reporting, Search, Policy, Risk Compliance,
|
||||
and Workflow provide source governance, immutable snapshots, transformation,
|
||||
quality, semantic reporting, and provenance foundations. The monthly-data and
|
||||
sanctions compositions now prove immutable connector snapshots, pinned
|
||||
Dataflow publication, Risk Compliance review, and rescreening in process. The
|
||||
journeys still need target connector profiles, complete interactive
|
||||
reconciliation, governed export/delivery, and browser-level handoff evidence.
|
||||
|
||||
## Material Gaps
|
||||
|
||||
| Gap | Consequence | Next proof |
|
||||
| --- | --- | --- |
|
||||
| No reference-ready product package | The platform cannot yet make a bounded supported-product claim | Complete one named target composition and evidence bundle |
|
||||
| Human-work spine is only an MVP | Tasks aggregates explicit work plus Workflow, Approval, and unread Postbox projections, but broad domain coverage, deadline escalation, assignment lifecycle, and focused product UX remain | Extend source providers through the three reference journeys and prove overdue/reassignment behavior in browser tests |
|
||||
| Records/eAkte target integration incomplete | Native lifecycle, retention, holds, approval, recovery, and transfer simulation are implemented, but real custody is not proved | Target-test one archive/xdomea profile and browser-test the now server-enforced assisted reference journey |
|
||||
| Cross-cutting governance adoption uneven | Historical and purpose-sensitive behavior varies by module | Enforced adoption declarations and route/query/effect migration |
|
||||
| Explicit help/accessibility depth incomplete | German/reference and F1 association gates now pass, but generic fallback remains too common | High-risk German help content and browser/a11y matrix |
|
||||
| Real federation absent | Cross-institution exchange remains connector-specific | Paired-instance signed exchange and reconciliation proof |
|
||||
| External production evidence incomplete | Scale, restore, interoperability and custody claims remain conditional | Real target drills and independent signed evidence |
|
||||
|
||||
## Active Strategic Order
|
||||
|
||||
1. Establish German, help, temporal, purpose, retention, and institutional
|
||||
context as enforceable platform quality contracts.
|
||||
2. Complete governed communication and Postbox against a named target.
|
||||
3. Complete the monthly-data flow and use it as the data foundation for
|
||||
sanctions screening.
|
||||
4. Complete the browser proof for the digital and assisted service-to-decision
|
||||
journey; server-side assisted resume, provenance, correction, and read-back
|
||||
enforcement now complement its existing exact eAkte filing contracts.
|
||||
5. Complete native PostgreSQL search coverage for remaining journey-owned
|
||||
objects and prove reauthorization and reindex operations at target volume;
|
||||
keep OpenSearch optional. Communication, Records, service-to-decision,
|
||||
Dataflow, Reporting, Risk Compliance, and Datasource catalogue sources now
|
||||
exist.
|
||||
6. Prove one external product connector and one GovOPlaN federation exchange.
|
||||
7. Finish multi-host, restore, provider, accessibility, and independent signed
|
||||
target evidence before increasing maturity claims.
|
||||
|
||||
## Refresh Procedure
|
||||
|
||||
Refresh this page only from evidence:
|
||||
|
||||
1. run `tools/checks/check-manifest-shapes.py`;
|
||||
2. run `tools/inventory/platform-interface-inventory.py --strict
|
||||
--strict-declarations --strict-endpoints`;
|
||||
3. run the selected reference-journey checks;
|
||||
4. inspect signed release and target evidence;
|
||||
5. query live Gitea issue/milestone state;
|
||||
6. update the dated values and material gaps here;
|
||||
7. retain prior assessments as dated evidence rather than rewriting them.
|
||||
+50
-17
@@ -4,7 +4,8 @@
|
||||
|
||||
> As a system administrator, I can execute one shell command that downloads a
|
||||
> verified GovOPlaN distribution and starts a completely configured Core control
|
||||
> plane without optional modules. In the WebUI I can browse compatible signed
|
||||
> plane with the official package directory available but only the protected
|
||||
> baseline active. In the WebUI I can browse compatible signed
|
||||
> module releases, select the modules for this installation, and follow every
|
||||
> download, validation, migration, installation, activation, and health-check
|
||||
> step. When an update is available, I can review its impact and confirm it.
|
||||
@@ -19,13 +20,14 @@ This is a product-level story owned by the GovOPlaN platform rather than by an
|
||||
individual domain module. It joins installation, module lifecycle, operations,
|
||||
configuration packages, and release provenance into one administrator journey.
|
||||
The canonical backlog item is
|
||||
[GovOPlaN #13](https://git.add-ideas.de/add-ideas/govoplan/issues/13).
|
||||
[GovOPlaN #13](https://git.add-ideas.de/GovOPlaN/govoplan/issues/13).
|
||||
|
||||
## Terms
|
||||
|
||||
- **Core control plane:** the smallest bootable distribution: Core API, Core
|
||||
WebUI, PostgreSQL, Redis, installer worker, migration runner, and durable
|
||||
storage configuration. No optional GovOPlaN module package is installed.
|
||||
storage configuration. An immutable image may carry the full verified package
|
||||
profile, but optional modules are not active or tenant-entitled by implication.
|
||||
- **Bootstrap administrator:** a single-use, time-limited installation identity
|
||||
that may access only first-run and module-lifecycle functions. It is retired
|
||||
when the selected identity/access configuration becomes healthy.
|
||||
@@ -55,7 +57,9 @@ The canonical backlog item is
|
||||
5. It prints the local URL and one-time bootstrap credential. Re-running the
|
||||
command is idempotent and shows or repairs the existing installation rather
|
||||
than creating another identity or database.
|
||||
6. No optional module is installed or enabled at this point.
|
||||
6. Only the protected baseline is enabled. Installed package availability does
|
||||
not grant permissions, tenant entitlement, View visibility, or capability
|
||||
opt-in.
|
||||
|
||||
### Module selection, installation, and update
|
||||
|
||||
@@ -139,25 +143,53 @@ The canonical backlog item is
|
||||
|
||||
## Implementation slices
|
||||
|
||||
1. **Reproducible Core-only distribution.** Publish pinned multi-architecture
|
||||
images, signed distribution manifest, Core-only Compose profile, bootstrap
|
||||
Implementation status as of the current source tree:
|
||||
|
||||
- Slice 1 has a published production-artifact baseline. Immutable
|
||||
[`v0.1.14`](https://git.add-ideas.de/GovOPlaN/govoplan/releases/tag/v0.1.14)
|
||||
binds source commit `1f039dd39c1ce2672f4978c8abc6dff862ef1445`, a signed
|
||||
one-file deployer, exact API/Web and managed-dependency image digests,
|
||||
composition, SBOMs, and provenance. Runtime Distribution
|
||||
[run #459](https://git.add-ideas.de/GovOPlaN/govoplan/actions/runs/459)
|
||||
passed migrations, schema checks, non-root API/Web readiness, and worker
|
||||
delivery/shutdown on both amd64 and arm64. Each future release must renew the
|
||||
evidence, and a real installation must still produce topology-specific
|
||||
ingress, failover, backup, and recovery receipts.
|
||||
- Slice 6 has a working application-tier foundation: state profiles, shared
|
||||
object storage, runtime node registration/heartbeats/drain, fenced scheduler,
|
||||
migration serialization, exact-head startup waiting, Ops visibility, and a
|
||||
Kubernetes export. Production acceptance still requires topology-specific
|
||||
failover and restore drills.
|
||||
- The recovery foundation for slices 4 and 5 is implemented as a Core recovery
|
||||
ledger and deployment operation journal. Automatic database backup and broad
|
||||
adoption by module-owned external effects remain open work.
|
||||
|
||||
1. **Reproducible Core-baseline distribution.** Publish pinned multi-architecture
|
||||
full-package images, signed distribution manifest, Core-baseline Compose profile, bootstrap
|
||||
preflight, generated secrets, readiness, and idempotent rerun/repair.
|
||||
2. **First-run control plane.** Add the restricted bootstrap administrator,
|
||||
one-time enrollment, initial catalog/keyring configuration, and retirement
|
||||
after durable administrator access is established.
|
||||
3. **Read-only online module directory.** Move the existing catalog and module
|
||||
directory contracts into the installed Core WebUI with compatibility,
|
||||
provenance, release-note, and update-state presentation.
|
||||
4. **Durable module plan and install.** Reuse the existing installer queue,
|
||||
locks, signed-package validator, rollback drill, and run evidence behind a
|
||||
plan/confirm/progress UI. Add initial catalog-entry synthesis and artifact
|
||||
acquisition where the current release console still assumes local sources.
|
||||
3. **Read-only online module directory (implemented foundation).** Admin falls
|
||||
back to the signed public stable directory, presents installed/update state,
|
||||
searchable availability/blocker filters, immutable source/artifact
|
||||
provenance, configuration requirements, release notes, and technical
|
||||
compatibility. Withdrawn releases remain visible but cannot be planned.
|
||||
Operator-configured catalogs remain an explicit override.
|
||||
4. **Durable module plan and install (implemented local boundary).** Catalog
|
||||
selection creates a reviewed plan; the installer queue, lock, preflight,
|
||||
maintenance gate, digest-verified artifact cache, rollback drill, and run
|
||||
evidence remain separate from the API process. Shared deployments convert
|
||||
the same intent into a new immutable release composition instead of mutating
|
||||
one replica.
|
||||
5. **Safe module update.** Add drain/maintenance coordination, backup gate,
|
||||
migration compatibility window, reconnectable progress, health verification,
|
||||
retry/recovery, and update notification.
|
||||
6. **Stateless replica profile.** Remove remaining local-runtime assumptions,
|
||||
expose role-specific commands/images, implement worker registration/drain,
|
||||
and prove multiple API and worker replicas against shared dependencies.
|
||||
6. **Stateless replica profile.** Continue module adoption and operational
|
||||
proof for the implemented role commands, shared-state validation, runtime
|
||||
registration/drain, fenced scheduler, and Kubernetes application-tier
|
||||
export. Prove multiple API and worker replicas against the target shared
|
||||
dependencies.
|
||||
7. **Configuration revision model.** Define provider export/import schemas,
|
||||
canonical serialization, secret references, validation/diff, immutable
|
||||
revision storage, audit, apply, and undo-as-new-revision.
|
||||
@@ -171,7 +203,8 @@ The canonical backlog item is
|
||||
|
||||
## Explicit non-goals for the first distribution slice
|
||||
|
||||
- Shipping optional modules in the Core image.
|
||||
- Activating, tenant-entitling, or exposing optional modules merely because the
|
||||
immutable image carries their verified packages.
|
||||
- Exporting secrets or production business data with configuration.
|
||||
- Pretending every schema migration can be reversed automatically.
|
||||
- Building a proprietary orchestrator instead of supporting Compose and a
|
||||
+57
-108
@@ -1,28 +1,32 @@
|
||||
# GovOPlaN Connected Governance Platform Roadmap
|
||||
# GovOPlaN Detailed Connected-Platform Vision
|
||||
|
||||
## Purpose and status
|
||||
|
||||
This document describes the long-term product destination for GovOPlaN from an
|
||||
outcome and stakeholder perspective. It answers what a completely connected
|
||||
governance platform should enable, how the same platform can be configured for
|
||||
different institutions, and which capability horizons lead from the current
|
||||
baseline to that destination.
|
||||
This reference catalogue describes the long-term product destination from an
|
||||
outcome and stakeholder perspective. It preserves the detailed perspectives,
|
||||
configuration archetypes, stories, horizons, and maturity notes behind the
|
||||
concise [Roadmap](../ROADMAP.md).
|
||||
|
||||
It is a durable direction, not a release promise or a substitute for issue
|
||||
tracking. Live work state belongs in Gitea issues. The
|
||||
[Core master roadmap](https://git.add-ideas.de/add-ideas/govoplan-core/src/branch/main/docs/GOVOPLAN_MASTER_ROADMAP.md)
|
||||
It is not a release promise, live plan, or second status source. The concise
|
||||
roadmap owns the current durable sequence, Strategy Status owns the reconciled
|
||||
state, and Gitea issues own work state. Where dated detail here differs from
|
||||
those sources, those sources take precedence. The
|
||||
[Core master roadmap](https://git.add-ideas.de/GovOPlaN/govoplan-core/src/branch/main/docs/GOVOPLAN_MASTER_ROADMAP.md)
|
||||
remains the technical module and wave sequence; this document supplies the
|
||||
cross-product vision that sequence serves.
|
||||
|
||||
Read it together with:
|
||||
|
||||
- the [selected reference-journey program](REFERENCE_JOURNEY_PROGRAM.md)
|
||||
- the [current capability and infrastructure fit assessment](CAPABILITY_AND_INFRASTRUCTURE_FIT.md)
|
||||
- the [interface pattern language](INTERFACE_PATTERN_LANGUAGE.md)
|
||||
- the [interface surface inventory](INTERFACE_SURFACE_INVENTORY.md)
|
||||
- the [module contract and install model](MODULE_CONTRACTS_AND_INSTALLS.md)
|
||||
- the [repository and module index](REPOSITORY_INDEX.md)
|
||||
- the [Gitea issue workflow](GITEA_ISSUES.md)
|
||||
- the [concise product roadmap](../ROADMAP.md)
|
||||
- the [institutional governance target architecture](../../architecture/INSTITUTIONAL_GOVERNANCE_TARGET_ARCHITECTURE.md)
|
||||
- the [selected reference-journey program](../REFERENCE_JOURNEY_PROGRAM.md)
|
||||
- the [current strategy status](../STRATEGY_STATUS.md)
|
||||
- the [generated, pinned Campaign capability and infrastructure fit assessment](../../evidence/snapshots/CAPABILITY_AND_INFRASTRUCTURE_FIT.generated.md)
|
||||
- the [interface pattern language](../../architecture/INTERFACE_PATTERN_LANGUAGE.md)
|
||||
- the [interface surface inventory](../../evidence/snapshots/INTERFACE_SURFACE_INVENTORY.md)
|
||||
- the [module contract and install model](../../operations/MODULE_CONTRACTS_AND_INSTALLS.md)
|
||||
- the [repository and module index](../../project/REPOSITORY_INDEX.md)
|
||||
- the [Gitea issue workflow](../../project/GITEA_ISSUES.md)
|
||||
|
||||
### How to read this roadmap
|
||||
|
||||
@@ -40,16 +44,17 @@ Read it together with:
|
||||
- Use [Near-term portfolio order](#near-term-portfolio-order) for the bridge to
|
||||
implementation and [Product decisions](#product-decisions-to-make-progressively)
|
||||
for choices that can remain deferred.
|
||||
- Use the [dated snapshot appendix](#snapshot-appendix-2026-07-20) only to
|
||||
understand which live backlog and release facts informed this revision.
|
||||
- Use the [dated strategic review](../../archive/2026-08/STRATEGIC_REVIEW_2026-08-05.md) to understand
|
||||
why the current convergence and reference-journey order was chosen.
|
||||
|
||||
### Planning ownership
|
||||
|
||||
| Question | Canonical source |
|
||||
| --- | --- |
|
||||
| What product should GovOPlaN become, for whom, in which configurations, and through which outcome horizons? | This meta roadmap |
|
||||
| What product should GovOPlaN become and in which durable sequence? | The concise Roadmap |
|
||||
| Which stakeholder perspectives, configuration archetypes, and detailed outcome stories inform that direction? | This reference catalogue |
|
||||
| Which module owns a capability, which technical wave should deliver it, and what implementation gates apply? | The Core master roadmap and owning-module concepts |
|
||||
| What is actively planned, blocked, implemented, or closed now? | Gitea issues |
|
||||
| What is actively planned, blocked, implemented, or closed now? | Gitea issues and the dated reconciliation in `STRATEGY_STATUS.md` |
|
||||
| What can a named composition credibly claim in a target environment? | A dated capability/infrastructure fit assessment |
|
||||
|
||||
The horizons and near-term order below express product outcomes and portfolio
|
||||
@@ -108,7 +113,7 @@ safe modules -> connected work -> reusable services -> institutional assurance -
|
||||
```
|
||||
|
||||
The active implementation path is the
|
||||
[Reference Journey Program](REFERENCE_JOURNEY_PROGRAM.md), selected on
|
||||
[Reference Journey Program](../REFERENCE_JOURNEY_PROGRAM.md), selected on
|
||||
2026-07-21. Its five stages do not replace these product horizons: they are the
|
||||
ordered demonstrations through which the shared platform contracts and horizon
|
||||
gates are to be proved. Connector safety, identity/function semantics,
|
||||
@@ -151,7 +156,7 @@ responsibilities, not a new dependency hierarchy.
|
||||
| Participation and channels | Let internal and external actors enter, receive, discuss, schedule, and respond through suitable channels. | Portal, postbox, mail, notifications, calendar, scheduling, appointments, campaign, consultation, poll |
|
||||
| Work coordination | Turn an input into owned, reviewable work and make exceptions visible. | Forms/runtime, cases, tasks, approvals, workflow, booking, resources, domain modules |
|
||||
| Evidence and institutional memory | Preserve what was known, decided, produced, sent, received, retained, corrected, and disclosed. | Files, templates, DMS, records, audit, search, reporting, transparency |
|
||||
| Institutional governance | Establish who may act, in which capacity, for which organization and tenant, under which policy. | Identity, access, IDM, tenancy, organizations, policy, identity trust, risk/compliance |
|
||||
| Institutional governance | Establish who may act, in which capacity, for which organization and tenant, under which mandate, jurisdiction, and policy. | Identity, access, IDM, tenancy, organizations, policy, identity trust, risk/compliance; candidate mandate and decision contracts |
|
||||
| Integration and operations | Connect sources and destinations, operate them safely, and prove their health and recovery. | Connectors, REST/SOAP and public-sector protocols, mail/calendar/file adapters, ERP handoffs, ops, release and configuration packages |
|
||||
|
||||
Across these planes, GovOPlaN should maintain a connected context graph rather
|
||||
@@ -264,8 +269,8 @@ Diagnostics minimize personal data and link to governed evidence when deeper
|
||||
inspection is authorized.
|
||||
|
||||
The complete installation and lifecycle journey is specified in the
|
||||
[System Administrator Lifecycle User Story](SYSTEM_ADMINISTRATOR_LIFECYCLE_USER_STORY.md):
|
||||
one-command Core-only bootstrap, signed online module installation and updates,
|
||||
[System Administrator Lifecycle User Story](../SYSTEM_ADMINISTRATOR_LIFECYCLE_USER_STORY.md):
|
||||
one-command Core-baseline bootstrap, signed online module installation and updates,
|
||||
stateless scale-out, versioned configuration transfer, undo, and reproducible
|
||||
environment-promotion recipes.
|
||||
|
||||
@@ -448,7 +453,7 @@ without creating disconnected ticket systems.
|
||||
- As a manager, I can distinguish demand, backlog, SLA risk, recurring cause,
|
||||
and verified resolution.
|
||||
|
||||
**Composition.** Issue reporting, helpdesk, cases, tasks, facilities, assets,
|
||||
**Composition.** Tickets, helpdesk profiles, cases, tasks, facilities, assets,
|
||||
inspections, booking, resources, calendar, files, notifications, connectors,
|
||||
reporting, policy, and audit.
|
||||
|
||||
@@ -530,7 +535,7 @@ access, no hidden cross-module coupling, correction rather than fictional undo,
|
||||
and a durable action/effect trail.
|
||||
|
||||
**Roadmap role.** The canonical product story is
|
||||
[meta issue #12](https://git.add-ideas.de/add-ideas/govoplan/issues/12). It is a
|
||||
[meta issue #12](https://git.add-ideas.de/GovOPlaN/govoplan/issues/12). It is a
|
||||
future cross-product configuration and contract program, not Campaign work and
|
||||
not a claim that screening is implemented today.
|
||||
|
||||
@@ -948,7 +953,7 @@ first analytical product prove Horizons 2 and 3; governed BI adds assurance and
|
||||
ecosystem capabilities across Horizons 3–5; collaborative documents combine
|
||||
the evidence spine, service packages, and records assurance across Horizons
|
||||
2–4. The detailed mapping and gates are in the
|
||||
[Reference Journey Program](REFERENCE_JOURNEY_PROGRAM.md).
|
||||
[Reference Journey Program](../REFERENCE_JOURNEY_PROGRAM.md).
|
||||
|
||||
### Current baseline: modular pilot foundations
|
||||
|
||||
@@ -974,8 +979,9 @@ checkouts.
|
||||
Priorities:
|
||||
|
||||
1. Deliver the first slices of the
|
||||
[System Administrator Lifecycle User Story](SYSTEM_ADMINISTRATOR_LIFECYCLE_USER_STORY.md):
|
||||
a verified Core-only distribution, first-run control plane, read-only online
|
||||
[System Administrator Lifecycle User Story](../SYSTEM_ADMINISTRATOR_LIFECYCLE_USER_STORY.md):
|
||||
a verified full-package distribution with only the Core baseline active,
|
||||
first-run control plane, read-only online
|
||||
module directory, and durable plan/confirm/install progress.
|
||||
2. Pin and publish a compatible Core/WebUI/module composition and first
|
||||
reference configuration package.
|
||||
@@ -1136,11 +1142,12 @@ boundary, and retain operational, security, and evidence guarantees.
|
||||
| Addresses and directories | Implemented adapters exist but configuration/target proof varies | Directory source, privacy, conflict and lifecycle package | Reusable people/contact source capability |
|
||||
| Docs/admin/ops/dashboard | Useful cross-product surfaces with incomplete rollout | Configured-system inventory, guided config, monitoring/recovery evidence | Explainability and operation of the configured product |
|
||||
| Organizations/identity/IDM/access/postbox | Normalized ownership concepts and uneven runtime slices | Function-bound delivery, reassignment/delegation, vacancy and access-evidence proof | Institutional responsibility and durable communication spine |
|
||||
| Forms/cases/tasks/approvals/search | Concepts and uneven first slices | Extend the proven responsibility path into one manual end-to-end work/evidence journey | Reusable administrative coordination layer |
|
||||
| Templates/reporting/data sources | Boundary concepts or scaffolds | One reproducible data-backed document/report and safe HIS-style launch | Governed document production, reports, dashboards, and analytical consumption |
|
||||
| Analytical data products/dataflow | Selected direction; platform contracts not yet implemented | One bounded university source-to-indicator path with staging, quality, lineage and promotion proof | Transparent institutional BI and cross-process reporting |
|
||||
| Forms/cases/tasks/approvals | Concepts and uneven first slices | Extend the proven responsibility path into one manual end-to-end work/evidence journey, with shared service, party, mandate, and decision semantics | Reusable administrative coordination layer |
|
||||
| Search | PostgreSQL-backed permission-aware provider and global/contextual UI foundation | Complete provider rollout, indexing operations, and target authorization/performance evidence | Optional cross-module discovery with OpenSearch only as an adapter |
|
||||
| Templates/reporting/data sources | Templates and governed Datasources foundations exist; Reporting remains an early semantic-model slice | One reproducible data-backed document/report and safe HIS-style launch | Governed document production, reports, dashboards, and analytical consumption |
|
||||
| Analytical data products/dataflow | Typed graph/runtime foundations, triggers, staging integration, reusable definitions, and golden-flow fixtures exist | One bounded university source-to-indicator path with quality, lineage, promotion, and target-performance proof | Transparent institutional BI and cross-process reporting |
|
||||
| DMS/collaborative editing | Boundary concept and tag-only scaffold | One Files-backed version lifecycle, then one provider-neutral editing session | Collaborative documents, review, approval and records-ready renditions |
|
||||
| Workflow/automation | Concept only; no discoverable Workflow runtime; program postponed | Stable action/effect providers and an explicitly reprioritized bounded journey | Configurable governed process coordination |
|
||||
| Workflow/automation | Headless Workflow Engine and optional editor are implemented foundations with versioned module baselines, BPMN interchange, action/effect execution, and reconciliation | Prove one resumable human/system journey with target recovery and conformance evidence | Configurable governed process coordination without becoming a second domain layer |
|
||||
| Domain modules | Mostly boundary concepts or seeds | Only the modules required by a reference package | Reusable semantics above the shared spine |
|
||||
| Connectors and protocols | Catalogue/strategy plus several module-specific adapters | Profile/runtime, source-of-truth, health and one real landscape | Coexistence with institutional IT |
|
||||
| Records/transparency/risk-compliance/export screening | Planned or early concepts | Evidence contracts and one regulated reference story | Institutional memory, oversight, and assurance |
|
||||
@@ -1216,8 +1223,9 @@ provides all applicable evidence below.
|
||||
## Near-term portfolio order
|
||||
|
||||
This order is now selected. Detailed slices and gates are in the
|
||||
[Reference Journey Program](REFERENCE_JOURNEY_PROGRAM.md). Workflow-driven
|
||||
user-story implementation remains paused.
|
||||
[Reference Journey Program](../REFERENCE_JOURNEY_PROGRAM.md). Workflow Engine and
|
||||
the optional editor may support these stages, but Workflow work enters the
|
||||
portfolio only through an explicit bounded package or reference journey.
|
||||
|
||||
0. **Continuous safe foundation.** Keep the composition green and version
|
||||
aligned; close connector destination pinning, response limits, throttling,
|
||||
@@ -1250,8 +1258,9 @@ user-story implementation remains paused.
|
||||
target acceptance for calendar/scheduling/poll and other shipped foundations
|
||||
without displacing the selected reference path; implement new feature
|
||||
programs only when they are required by a stage or explicitly reprioritized.
|
||||
7. **Resume Workflow only by explicit priority decision.** When resumed, start
|
||||
with stable actions from a demonstrated package; do not turn it into a
|
||||
7. **Use Workflow only through bounded journeys.** The headless engine and
|
||||
optional editor now exist. Extend them through stable module-owned actions,
|
||||
versioned baselines, and demonstrated packages; do not turn Workflow into a
|
||||
second domain layer.
|
||||
|
||||
## Product decisions to make progressively
|
||||
@@ -1263,8 +1272,8 @@ them; they do not block the product vision today.
|
||||
reference release?
|
||||
- Which real identity, groupware, file/DMS, and deployment stack should define
|
||||
the first integration profile?
|
||||
- When should the postponed Workflow program resume, and which single package
|
||||
will constrain its first implementation?
|
||||
- Which demonstrated package should provide the first target-accepted Workflow
|
||||
execution and recovery profile?
|
||||
- Which objects and fields remain authoritative in GovOPlaN versus each target
|
||||
system, and which conflict/failure behavior is acceptable?
|
||||
- Which default participant privacy profiles should ship for scheduling,
|
||||
@@ -1279,8 +1288,9 @@ them; they do not block the product vision today.
|
||||
- Which exact HIS/CampusOnline interfaces, student-statistics fields and
|
||||
official keys, accepted calculation, freeze/correction policy, privacy
|
||||
profile, and drill-down level should define the first analytical data product?
|
||||
- After the first source-to-report proof, do repeated source/dataflow contracts
|
||||
justify separate `govoplan-datasources` and `govoplan-dataflow` modules?
|
||||
- Which database, REST, directory, and managed-file providers should follow the
|
||||
implemented separation of `govoplan-connectors`, `govoplan-datasources`, and
|
||||
`govoplan-dataflow`, and which quality/promotion policy should prove it first?
|
||||
- Which collaborative editor should be the first target, and should its first
|
||||
accepted experience emphasize concurrent editing, controlled check-out, or
|
||||
both?
|
||||
@@ -1346,71 +1356,10 @@ language, what service it configured, who can act, which systems participate,
|
||||
what happens when they fail, how a decision can be reviewed, and where the
|
||||
evidence remains—and the product can prove that explanation at runtime.
|
||||
|
||||
## Snapshot appendix: 2026-07-20
|
||||
## Dated Context
|
||||
|
||||
This appendix records volatile facts that informed this revision. It is not a
|
||||
second source of truth and should be refreshed or removed when a later roadmap
|
||||
review uses a new release/backlog snapshot.
|
||||
|
||||
### Composition and release snapshot
|
||||
|
||||
The cross-repository contract scan found 43 module manifest contracts, 29
|
||||
provided interface names, 16 requirements, and no contract error across 65
|
||||
scanned repositories. That is meaningful composition evidence, but the release
|
||||
metadata trailed the integrated code: Core, Policy, Poll, and Scheduling
|
||||
declared `0.1.9` while the whole-product release requirements remained on
|
||||
module tag `v0.1.8`; the root self-hosted `.env.example` and release smoke
|
||||
composition did not yet exercise all installed release modules. Other
|
||||
development compositions already included some of those modules. This was a
|
||||
release/composition gap, not evidence that the underlying slices did not exist.
|
||||
|
||||
### Backlog snapshot
|
||||
|
||||
The Gitea audit found 206 open issues across 36 of 66 catalogued repositories
|
||||
and 362 closed issues. Campaign had 51 open issues and Core 44; together they
|
||||
held 46% of current work. This reflected substantial completed kernel,
|
||||
security, and platform work and a deliberate concentration on the first usable
|
||||
vertical, but also risked crowding out production evidence and the shared
|
||||
process spine.
|
||||
|
||||
The issue workflow needed a reconciliation pass before another delivery
|
||||
program could be inferred from labels: 119 open issues remained in triage, 116
|
||||
had no milestone, and several recently pushed Calendar, Scheduling, Poll,
|
||||
Campaign, and Files slices still described themselves as local or awaiting
|
||||
integration. Conversely, 30 repositories had no open issue; for many
|
||||
later-wave modules this meant no implementation program had been opened, not
|
||||
that the capability was complete.
|
||||
|
||||
[Poll #2](https://git.add-ideas.de/add-ideas/govoplan-poll/issues/2) was a clear
|
||||
tracker-drift example: its configurable transition engine, agreed transition
|
||||
matrix/history, idempotent keyed retries, re-decision audit, archive/unarchive,
|
||||
and preservation behavior were implemented and pushed while the issue still
|
||||
reported `needs-info`.
|
||||
|
||||
Issue anchors that informed the bridge from the baseline into this roadmap:
|
||||
|
||||
- [Meta #10](https://git.add-ideas.de/add-ideas/govoplan/issues/10) for the
|
||||
capability/infrastructure assessment and its target proof;
|
||||
- [Meta #11](https://git.add-ideas.de/add-ideas/govoplan/issues/11) for the
|
||||
universal interface and focused-view direction;
|
||||
- [Core #225](https://git.add-ideas.de/add-ideas/govoplan-core/issues/225) for
|
||||
guided, safe configuration;
|
||||
- [Core #29](https://git.add-ideas.de/add-ideas/govoplan-core/issues/29) for the
|
||||
backup/restore production gate;
|
||||
- [Core #263](https://git.add-ideas.de/add-ideas/govoplan-core/issues/263) and
|
||||
[Campaign #63](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/63),
|
||||
[#62](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/62),
|
||||
[#65](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/65), and
|
||||
[#69](https://git.add-ideas.de/add-ideas/govoplan-campaign/issues/69) for the
|
||||
reference interface/delivery vocabulary and behavior;
|
||||
- [Poll #1](https://git.add-ideas.de/add-ideas/govoplan-poll/issues/1) for the
|
||||
database-enforced respondent invariant exposed by Scheduling;
|
||||
- [Connectors #6](https://git.add-ideas.de/add-ideas/govoplan-connectors/issues/6)
|
||||
for the governed connector configuration/simulation foundation;
|
||||
- [Meta #9](https://git.add-ideas.de/add-ideas/govoplan/issues/9) for the first
|
||||
permit-to-payment reference process; and
|
||||
- [Meta #12](https://git.add-ideas.de/add-ideas/govoplan/issues/12) for the
|
||||
deliberately deferred, consumer-independent export-control story.
|
||||
|
||||
Live Gitea issue state remains canonical. These dated facts explain the roadmap
|
||||
sequence only.
|
||||
The volatile release and backlog appendix that originally accompanied this
|
||||
roadmap has been removed so the durable direction cannot become a competing
|
||||
status source. The [Strategic Review 2026-08-05](../../archive/2026-08/STRATEGIC_REVIEW_2026-08-05.md)
|
||||
retains the dated assessment and reasoning. Current reconciliation belongs in
|
||||
[Strategy Status](../STRATEGY_STATUS.md), and live work state belongs in Gitea.
|
||||
@@ -0,0 +1,11 @@
|
||||
# GovOPlaN developer meta-package
|
||||
|
||||
`govoplan` is an optional convenience package for local development and
|
||||
composition tests. The default dependency set matches the reviewed runtime
|
||||
release roots; `govoplan[full]` adds every packageable module present in the
|
||||
workspace at generation time.
|
||||
|
||||
This package is not a production deployment artifact. Production installations
|
||||
consume the signed runtime distribution manifest and digest-pinned OCI images.
|
||||
The package does not enable modules, apply migrations, choose infrastructure,
|
||||
or replace installation and recovery evidence.
|
||||
@@ -0,0 +1,97 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=69", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "govoplan"
|
||||
version = "0.1.46"
|
||||
description = "Developer convenience package for a versioned GovOPlaN composition"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
license = { text = "AGPL-3.0-or-later" }
|
||||
dependencies = [
|
||||
"govoplan-core[server]==0.1.46",
|
||||
"govoplan-tenancy==0.1.22",
|
||||
"govoplan-organizations==0.1.21",
|
||||
"govoplan-identity==0.1.21",
|
||||
"govoplan-idm==0.1.26",
|
||||
"govoplan-access==0.1.25",
|
||||
"govoplan-admin==0.1.23",
|
||||
"govoplan-policy==0.1.23",
|
||||
"govoplan-audit==0.1.20",
|
||||
"govoplan-dashboard==0.1.20",
|
||||
"govoplan-files==0.1.27",
|
||||
"govoplan-mail==0.1.28",
|
||||
"govoplan-campaign==0.1.29",
|
||||
"govoplan-calendar==0.1.24",
|
||||
"govoplan-docs==0.1.23",
|
||||
"govoplan-ops==0.1.22",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
full = [
|
||||
"govoplan-addresses==0.1.23",
|
||||
"govoplan-approvals==0.1.21",
|
||||
"govoplan-assets==0.1.20",
|
||||
"govoplan-booking==0.1.20",
|
||||
"govoplan-cases==0.1.25",
|
||||
"govoplan-certificates==0.1.20",
|
||||
"govoplan-committee==0.1.22",
|
||||
"govoplan-connectors==0.1.27",
|
||||
"govoplan-consultation==0.1.20",
|
||||
"govoplan-contracts==0.1.20",
|
||||
"govoplan-dataflow==0.1.25",
|
||||
"govoplan-datasources==0.1.26",
|
||||
"govoplan-decisions==0.1.19",
|
||||
"govoplan-dist-lists==0.1.21",
|
||||
"govoplan-dms==0.1.20",
|
||||
"govoplan-encryption==0.1.20",
|
||||
"govoplan-erp==0.1.20",
|
||||
"govoplan-evaluation==0.1.20",
|
||||
"govoplan-facilities==0.1.20",
|
||||
"govoplan-fit-connect==0.1.20",
|
||||
"govoplan-forms==0.1.23",
|
||||
"govoplan-forms-runtime==0.1.22",
|
||||
"govoplan-grants==0.1.20",
|
||||
"govoplan-helpdesk==0.1.21",
|
||||
"govoplan-identity-trust==0.1.21",
|
||||
"govoplan-inspections==0.1.20",
|
||||
"govoplan-learning==0.1.20",
|
||||
"govoplan-mandates==0.1.19",
|
||||
"govoplan-notifications==0.1.20",
|
||||
"govoplan-parties==0.1.19",
|
||||
"govoplan-payments==0.1.22",
|
||||
"govoplan-permits==0.1.20",
|
||||
"govoplan-poll==0.1.20",
|
||||
"govoplan-portal==0.1.22",
|
||||
"govoplan-postbox==0.1.23",
|
||||
"govoplan-procurement==0.1.20",
|
||||
"govoplan-projects==0.1.20",
|
||||
"govoplan-quick-access==0.1.21",
|
||||
"govoplan-records==0.1.24",
|
||||
"govoplan-reporting==0.1.22",
|
||||
"govoplan-resources==0.1.20",
|
||||
"govoplan-rest==0.1.19",
|
||||
"govoplan-risk-compliance==0.1.21",
|
||||
"govoplan-scheduling==0.1.22",
|
||||
"govoplan-search==0.1.20",
|
||||
"govoplan-services==0.1.19",
|
||||
"govoplan-soap==0.1.19",
|
||||
"govoplan-tasks==0.1.23",
|
||||
"govoplan-templates==0.1.22",
|
||||
"govoplan-tickets==0.1.23",
|
||||
"govoplan-transparency==0.1.20",
|
||||
"govoplan-views==0.1.22",
|
||||
"govoplan-voting==0.1.21",
|
||||
"govoplan-wiki==0.1.22",
|
||||
"govoplan-workflow==0.1.23",
|
||||
"govoplan-workflow-engine==0.1.21",
|
||||
"govoplan-xrechnung==0.1.21",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://git.add-ideas.de/GovOPlaN/govoplan"
|
||||
Documentation = "https://govoplan.add-ideas.de"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
@@ -0,0 +1,12 @@
|
||||
"""Metadata helpers for the optional GovOPlaN developer composition."""
|
||||
|
||||
from importlib.metadata import PackageNotFoundError, version
|
||||
|
||||
|
||||
try:
|
||||
__version__ = version("govoplan")
|
||||
except PackageNotFoundError: # pragma: no cover - source checkout only
|
||||
__version__ = "0+unknown"
|
||||
|
||||
|
||||
__all__ = ["__version__"]
|
||||
@@ -0,0 +1,34 @@
|
||||
# Governed Communication Product Package
|
||||
|
||||
This package composes Campaign, Mail, Postbox, Files, Templates, Policy, Audit,
|
||||
and Access into one governed-delivery capability. Modules retain their own
|
||||
tables and lifecycle. The package declares installation and configuration
|
||||
expectations; it does not import module internals.
|
||||
|
||||
## Reference journey
|
||||
|
||||
1. Select governed recipient and attachment data.
|
||||
2. Build and review an immutable Campaign version.
|
||||
3. Apply attachment, access, and delivery policy.
|
||||
4. Deliver through Mail and/or function-bound Postbox targets.
|
||||
5. Reconcile outcomes, acknowledgements, correction, and reports against Audit
|
||||
evidence.
|
||||
|
||||
## Reference-readiness gates
|
||||
|
||||
The artifact remains a `product` package. Promotion to `reference` requires:
|
||||
|
||||
- target-environment delivery and reconciliation tests, including an
|
||||
outcome-unknown transport result;
|
||||
- backup/restore and interrupted-dispatch recovery evidence;
|
||||
- security evidence for recipient, attachment, and postbox isolation;
|
||||
- operator runbooks for queue health, retry, reconciliation, and retirement;
|
||||
- keyboard, focus, screen-reader, and responsive workflow evidence;
|
||||
- privacy evidence for minimization, purpose, retention, redaction, and report
|
||||
access; and
|
||||
- version-pinned user and administrator documentation.
|
||||
|
||||
Optional Notifications, Portal, Reporting, Tasks, and Workflow Engine
|
||||
integrations do not change the package boundary when absent. When Tasks is
|
||||
present, acknowledgement, reconciliation, and operator intervention remain
|
||||
owned by their source modules and are projected into the common work inbox.
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"package_id": "product.governed-communication",
|
||||
"name": "Governed Communication",
|
||||
"version": "0.1.0",
|
||||
"package_class": "product",
|
||||
"description": "Compose evidence-backed campaigns, mail and function-bound postboxes without merging their domain ownership.",
|
||||
"publisher": "GovOPlaN",
|
||||
"category": "communication",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"required_modules": [
|
||||
{"module_id": "access"},
|
||||
{"module_id": "audit"},
|
||||
{"module_id": "campaign"},
|
||||
{"module_id": "files"},
|
||||
{"module_id": "mail"},
|
||||
{"module_id": "policy"},
|
||||
{"module_id": "postbox"},
|
||||
{"module_id": "templates"}
|
||||
],
|
||||
"optional_modules": [
|
||||
{"module_id": "notifications"},
|
||||
{"module_id": "portal"},
|
||||
{"module_id": "reporting"},
|
||||
{"module_id": "tasks"},
|
||||
{"module_id": "workflow_engine"}
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "documentation",
|
||||
"reference": "packages/product/governed-communication/README.md",
|
||||
"summary": "Defines the package boundary, journey, and reference-readiness gates."
|
||||
}
|
||||
],
|
||||
"tags": ["campaign", "mail", "postbox", "public-sector"]
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
# Governed Data and Assurance Product Package
|
||||
|
||||
This package composes Datasources, Dataflow, Reporting, Search, Risk
|
||||
Compliance, Policy, Audit, and Access. Connectors may acquire external data,
|
||||
but Datasources owns the governed catalogue and immutable snapshots; Dataflow
|
||||
owns transformation definitions and runs; Reporting owns measures and
|
||||
presentation; Risk Compliance owns controls, findings, and effectiveness
|
||||
review.
|
||||
|
||||
## Reference journey
|
||||
|
||||
1. Register a typed datasource with source authority, purpose, classification,
|
||||
owner, freshness, and correction policy.
|
||||
2. Acquire or upload an immutable source state.
|
||||
3. Execute a versioned flow and retain intermediate materializations and
|
||||
provenance;
|
||||
4. publish a report or decision input against exact source and flow revisions;
|
||||
5. link obligation, governed object, risk, control, evidence, finding,
|
||||
corrective measure, and effectiveness review; and
|
||||
6. search current authorized objects while preserving ownership and access
|
||||
rechecks.
|
||||
|
||||
## Reference-readiness gates
|
||||
|
||||
The artifact remains a `product` package. Promotion to `reference` requires:
|
||||
|
||||
- a target-tested monthly-data and sanctions-screening fixture with expected
|
||||
outputs and complete provenance;
|
||||
- database/object-storage backup and restore plus interrupted-run recovery;
|
||||
- security evidence for datasource credentials, staged data, intermediate
|
||||
states, search documents, and assurance references;
|
||||
- operator runbooks for stale sources, failed runs, index rebuilds, and graph
|
||||
reconciliation;
|
||||
- accessibility evidence for the catalogue, graph editors, result inspection,
|
||||
reports, and assurance graph;
|
||||
- privacy evidence for minimization, purpose, retention, field visibility, and
|
||||
aggregate disclosure; and
|
||||
- version-pinned user and administrator documentation.
|
||||
|
||||
Optional Connectors, Files, Notifications, Tasks, and Workflow Engine
|
||||
integrations must remain capability-based and absence-safe. Tasks may present
|
||||
review and recovery handoffs, but Dataflow and Risk Compliance remain the
|
||||
authoritative owners of run and screening state.
|
||||
|
||||
## Executable evidence
|
||||
|
||||
- `tools/checks/check-datasource-composition.py` composes Connector snapshots,
|
||||
governed Datasources, queued Dataflow execution, frozen publication,
|
||||
idempotent replay, and recovery evidence.
|
||||
- `govoplan-dataflow/fixtures/golden/monthly-reconciliation` pins synthetic
|
||||
monthly inputs, stable reconciliation hashes, reviewed decisions, expected
|
||||
output, source fingerprints, and output hashes.
|
||||
- `tools/checks/check-sanctions-screening-composition.py` composes an immutable
|
||||
Connector acquisition, idempotent Risk Compliance import and screening,
|
||||
independent disposition, a cleared gate, changed-source invalidation, and
|
||||
the rescreening queue through the registered versioned capabilities.
|
||||
- `govoplan-dataflow/fixtures/golden/sanctions-screening` independently proves
|
||||
the deterministic normalization and matching graph with exact expected
|
||||
output.
|
||||
|
||||
These checks use synthetic data and run without network access. They prove the
|
||||
module contracts and durable state transitions; they do not replace the
|
||||
deployment, security, privacy, accessibility, and operator evidence still
|
||||
listed above.
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"package_id": "product.governed-data-assurance",
|
||||
"name": "Governed Data and Assurance",
|
||||
"version": "0.1.0",
|
||||
"package_class": "product",
|
||||
"description": "Compose governed sources, transformations, reports, search, controls and assurance evidence through stable contracts.",
|
||||
"publisher": "GovOPlaN",
|
||||
"category": "data-governance",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"required_modules": [
|
||||
{"module_id": "access"},
|
||||
{"module_id": "audit"},
|
||||
{"module_id": "dataflow"},
|
||||
{"module_id": "datasources"},
|
||||
{"module_id": "policy"},
|
||||
{"module_id": "reporting"},
|
||||
{"module_id": "risk_compliance"},
|
||||
{"module_id": "search"}
|
||||
],
|
||||
"optional_modules": [
|
||||
{"module_id": "connectors"},
|
||||
{"module_id": "files"},
|
||||
{"module_id": "notifications"},
|
||||
{"module_id": "tasks"},
|
||||
{"module_id": "workflow_engine"}
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "documentation",
|
||||
"reference": "packages/product/governed-data-assurance/README.md",
|
||||
"summary": "Defines the package boundary, provenance chain, and reference-readiness gates."
|
||||
},
|
||||
{
|
||||
"kind": "target_test",
|
||||
"reference": "tools/checks/check-datasource-composition.py",
|
||||
"summary": "Proves governed Connector acquisition, Datasource registration, queued Dataflow execution, frozen publication, idempotency, and recovery evidence."
|
||||
},
|
||||
{
|
||||
"kind": "target_test",
|
||||
"reference": "tools/checks/check-sanctions-screening-composition.py",
|
||||
"summary": "Proves immutable sanctions acquisition, import, screening replay, independent review, freshness gates, and rescreening across module capabilities."
|
||||
}
|
||||
],
|
||||
"tags": ["datasources", "dataflow", "reporting", "sanctions", "assurance"]
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
# Governed Service To Decision
|
||||
|
||||
This product package composes independently owned institutional semantics into
|
||||
one reconstructable administrative journey:
|
||||
|
||||
```text
|
||||
Service discovery -> Case intake -> Party and representation -> Mandate
|
||||
resolution -> approval/deliberation -> formal Decision -> observed delivery
|
||||
effect -> record and review references
|
||||
```
|
||||
|
||||
The maintained concrete scenario is a German resident parking permit
|
||||
(`Anwohnerparkausweis`). Its versioned fixture is
|
||||
`tests/fixtures/resident_parking_permit_journey.json`. It pins the service,
|
||||
exact Form revision, resident inputs, digital and assisted channels, Case type,
|
||||
human review handoff, formal outcome, Postbox delivery channel, and Records
|
||||
filing/retention target. Generic permit wording is no longer acceptance
|
||||
evidence for this package.
|
||||
|
||||
The package is now executable rather than metadata-only. Its Access fragments
|
||||
create the bounded resident-permit clerk role, collect only the tenant-local
|
||||
responsibility group key and name, create that group, and bind the role. The
|
||||
Forms-owned fragment carries a digest-bound German-reference application schema
|
||||
and imports it as a tenant-local draft with source provenance. Reapplying the
|
||||
same source digest is a no-op; replacing an unrelated local definition remains
|
||||
blocked unless the reviewed package explicitly selects a new revision. Normal
|
||||
Forms review and publication are still required before the definition can serve
|
||||
new applications. The Workflow Engine-owned fragment materializes and activates
|
||||
the tenant review baseline, resolves the chosen responsibility group into each
|
||||
human handoff, and preserves the evidence, decision, and EUR 30 payment-review
|
||||
steps as a replay-safe contributed definition.
|
||||
|
||||
Services, Cases, Payments, Tasks, and the optional delivery and Records modules
|
||||
already execute the pinned journey through their runtime
|
||||
contracts, but their reusable configuration fragments are not yet claimed by
|
||||
this package. Until those module-owned configuration providers are added, the
|
||||
package preflight deliberately distinguishes the installed runtime composition
|
||||
from the Access, Forms, and Workflow configurations it can currently materialize.
|
||||
|
||||
An installed Forms and Forms Runtime pair adds an alternative governed entry
|
||||
path before case/workflow handoff:
|
||||
|
||||
```text
|
||||
Service discovery -> exact Form revision -> validated draft/submission
|
||||
-> receipt and handoff evidence -> Case or Workflow owner
|
||||
```
|
||||
|
||||
The assisted path now creates an authenticated, resumable session against that
|
||||
same exact Form revision. It records channel, affected and represented parties,
|
||||
authority, purpose, notice, responsible function, language, accessibility
|
||||
support, and field provenance. Submission fails closed until an immutable
|
||||
read-back outcome matches the current revision, values, attachments, and
|
||||
signatures. Saving a correction therefore requires a fresh confirmation rather
|
||||
than silently reusing old evidence.
|
||||
|
||||
Services, Cases, Parties, Mandates, Committee, and Decisions retain immutable
|
||||
provider-owned revisions for the parts they own. Portal, Cases, and Committee
|
||||
consume capabilities for cross-module semantics only. The package does not
|
||||
grant cross-module table access and can omit optional presentation, work,
|
||||
deliberation, delivery, or records modules while retaining explicit references
|
||||
to externally performed steps.
|
||||
|
||||
When Records is present, Forms Runtime, Cases, and Decisions expose exact,
|
||||
digest-bound source snapshots for explicit filing. The source module rechecks
|
||||
current access, Records chooses the destination and preserves chronology, and
|
||||
the filed reference never becomes an editable copy. When Search is present,
|
||||
the same three owners contribute rebuildable metadata-only projections. Form
|
||||
values, evidence payloads, Decision reasoning, operative results, and
|
||||
conditions are excluded; every candidate is authorized again before it is
|
||||
shown.
|
||||
|
||||
When Tasks is present, explicit work and source-owned Workflow handoffs appear
|
||||
in one resumable inbox with typed account, group, role, function, or assignment
|
||||
responsibility. Workflow Engine retains process state and completion commands;
|
||||
Tasks retains only explicit tasks and the aggregation surface.
|
||||
|
||||
## Security And Recovery
|
||||
|
||||
Every provider is tenant-bound. Missing or conflicting authority fails closed.
|
||||
Protected Decision content has a separate permission. Writes are replay-safe
|
||||
and OCC-guarded. Database restore is the semantic-state recovery unit; file and
|
||||
communication effects remain governed by their owning providers and are linked
|
||||
through requested/observed effect, evidence, and audit references. Search is a
|
||||
derived recovery unit and can be rebuilt from authoritative module state.
|
||||
|
||||
The executable fixture in
|
||||
`tests/test_institutional_governance_journey.py` proves SQL-backed Service,
|
||||
Case, Party, Mandate, Committee meeting/agendum/vote/minute, and Decision state.
|
||||
`tests/test_institutional_service_journey.py` separately proves exact Portal
|
||||
Form launch, persisted submission provenance, idempotent replay, resumable
|
||||
assisted intake with enforced read-back evidence, and a durable Workflow handoff
|
||||
that remains visible through Tasks after the database session is reopened and
|
||||
disappears only after the Workflow Engine records completion.
|
||||
Core's production-component browser conformance suite additionally executes the
|
||||
German self-service and assisted Anwohnerparkausweis paths at desktop and mobile
|
||||
widths. It proves native keyboard order, accessible names and landmarks, WCAG
|
||||
2.1 A/AA automation, responsive geometry, first-draft persistence, and mixed
|
||||
per-field person/document/system provenance. Physical screen-reader spot checks
|
||||
remain target-environment release evidence.
|
||||
Module-level Records source tests prove exact Form submission, Case revision,
|
||||
and Decision revision filing. Target-environment browser accessibility,
|
||||
production identity and delivery, a named archive profile, and recovery evidence
|
||||
are still required before this product package may claim `reference_ready`
|
||||
maturity.
|
||||
|
||||
The generic package orchestrator stops at the first provider apply or health
|
||||
blocker. Access and Forms may commit in separate provider transactions, so the
|
||||
operator must retain the reviewed pre-apply database snapshot until verification
|
||||
is complete. The Admin result reports no-op, snapshot-required, or partial-apply
|
||||
recovery state and never describes this as atomic cross-module undo. Exported
|
||||
fragments carry source/module/operator/scope provenance; supplied values and
|
||||
credentials are not serialized into that provenance.
|
||||
@@ -0,0 +1,395 @@
|
||||
{
|
||||
"package_id": "product.service-to-decision",
|
||||
"name": "Governed Service To Decision",
|
||||
"version": "0.1.0",
|
||||
"package_class": "product",
|
||||
"description": "Carry one exact institutional context from service discovery and case intake through parties, authority, formal outcome, communication evidence, and review.",
|
||||
"publisher": "GovOPlaN",
|
||||
"category": "institutional-governance",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"required_modules": [
|
||||
{"module_id": "access"},
|
||||
{"module_id": "audit"},
|
||||
{"module_id": "cases"},
|
||||
{"module_id": "decisions"},
|
||||
{"module_id": "forms"},
|
||||
{"module_id": "forms_runtime"},
|
||||
{"module_id": "mandates"},
|
||||
{"module_id": "parties"},
|
||||
{"module_id": "payments"},
|
||||
{"module_id": "policy"},
|
||||
{"module_id": "portal"},
|
||||
{"module_id": "services"},
|
||||
{"module_id": "tasks"},
|
||||
{"module_id": "workflow_engine"}
|
||||
],
|
||||
"required_capabilities": [
|
||||
"access.configuration",
|
||||
"cases.party_context",
|
||||
"cases.service_intake",
|
||||
"decisions.registry",
|
||||
"forms.configuration",
|
||||
"forms.definitions",
|
||||
"mandates.resolver",
|
||||
"parties.resolver",
|
||||
"payments.requests",
|
||||
"portal.service_directory",
|
||||
"services.availability",
|
||||
"services.definitions",
|
||||
"workflow.configuration"
|
||||
],
|
||||
"optional_modules": [
|
||||
{"module_id": "approvals"},
|
||||
{"module_id": "committee"},
|
||||
{"module_id": "files"},
|
||||
{"module_id": "postbox"},
|
||||
{"module_id": "records"},
|
||||
{"module_id": "search"}
|
||||
],
|
||||
"data_requirements": [
|
||||
{
|
||||
"key": "responsible_group_slug",
|
||||
"label": "Responsible permit group key",
|
||||
"data_type": "string",
|
||||
"required": true,
|
||||
"secret": false,
|
||||
"description": "Tenant-local stable key for the group that reviews resident parking permit applications."
|
||||
},
|
||||
{
|
||||
"key": "responsible_group_name",
|
||||
"label": "Responsible permit group name",
|
||||
"data_type": "string",
|
||||
"required": true,
|
||||
"secret": false,
|
||||
"description": "Human-readable tenant-local name shown for the responsible permit group."
|
||||
}
|
||||
],
|
||||
"fragments": [
|
||||
{
|
||||
"module_id": "access",
|
||||
"fragment_type": "roles",
|
||||
"fragment_id": "resident-parking-permit-clerk",
|
||||
"payload": {
|
||||
"items": [
|
||||
{
|
||||
"slug": "resident-parking-permit-clerk",
|
||||
"name": "Resident parking permit clerk",
|
||||
"description": "Reviews resident parking permit submissions, workflow handoffs, cases, decisions, and payment evidence.",
|
||||
"permissions": [
|
||||
"cases:case:read",
|
||||
"cases:case:create",
|
||||
"cases:case:update",
|
||||
"decisions:decision:read",
|
||||
"decisions:decision:write",
|
||||
"forms:definition:read",
|
||||
"forms_runtime:workspace:read",
|
||||
"forms_runtime:workspace:write",
|
||||
"payments:payment:read",
|
||||
"payments:payment:write",
|
||||
"tasks:item:read",
|
||||
"tasks:item:write",
|
||||
"workflow:definition:read",
|
||||
"workflow:instance:read",
|
||||
"workflow:instance:start",
|
||||
"workflow:instance:transition"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"module_id": "access",
|
||||
"fragment_type": "groups",
|
||||
"fragment_id": "resident-parking-permit-responsibility",
|
||||
"payload": {
|
||||
"items": [
|
||||
{
|
||||
"slug": {"$data": "responsible_group_slug"},
|
||||
"name": {"$data": "responsible_group_name"},
|
||||
"description": "Tenant-local responsibility group for the resident parking permit reference journey."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"module_id": "access",
|
||||
"fragment_type": "group_role_assignments",
|
||||
"fragment_id": "resident-parking-permit-clerk-assignment",
|
||||
"payload": {
|
||||
"items": [
|
||||
{
|
||||
"group": {"$data": "responsible_group_slug"},
|
||||
"role": "resident-parking-permit-clerk"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"module_id": "forms",
|
||||
"fragment_type": "definition",
|
||||
"fragment_id": "resident-parking-permit-application",
|
||||
"payload": {
|
||||
"on_conflict": "new_revision",
|
||||
"change_reason": "Install the reviewed resident parking permit reference form.",
|
||||
"fragment": {
|
||||
"kind": "govoplan.forms.definition",
|
||||
"contract_version": "0.1.0",
|
||||
"definition": {
|
||||
"reference": {
|
||||
"kind": "form",
|
||||
"owner_module": "forms",
|
||||
"object_id": "resident-parking-permit-application",
|
||||
"tenant_id": "reference-package",
|
||||
"version": "3",
|
||||
"valid_at": null,
|
||||
"label": null
|
||||
},
|
||||
"key": "resident-parking-permit-application",
|
||||
"temporal": {
|
||||
"revision": "3",
|
||||
"valid_from": null,
|
||||
"valid_to": null,
|
||||
"recorded_at": "2026-08-22T00:00:00+00:00",
|
||||
"superseded_at": null,
|
||||
"change_reason": "Reference package revision."
|
||||
},
|
||||
"title": "Resident parking permit",
|
||||
"description": "Apply for a resident parking permit through a digital or assisted channel.",
|
||||
"fields": [
|
||||
{
|
||||
"key": "applicant_name",
|
||||
"label": "Name",
|
||||
"value_type": "text",
|
||||
"required": true,
|
||||
"help_text": null,
|
||||
"options": [],
|
||||
"constraints": {"min_length": 2, "max_length": 200},
|
||||
"default_value": null
|
||||
},
|
||||
{
|
||||
"key": "applicant_email",
|
||||
"label": "Email",
|
||||
"value_type": "text",
|
||||
"required": true,
|
||||
"help_text": null,
|
||||
"options": [],
|
||||
"constraints": {"format": "email"},
|
||||
"default_value": null
|
||||
},
|
||||
{
|
||||
"key": "residence_address",
|
||||
"label": "Primary residence",
|
||||
"value_type": "text",
|
||||
"required": true,
|
||||
"help_text": null,
|
||||
"options": [],
|
||||
"constraints": {"max_length": 500},
|
||||
"default_value": null
|
||||
},
|
||||
{
|
||||
"key": "licence_plate",
|
||||
"label": "Licence plate",
|
||||
"value_type": "text",
|
||||
"required": true,
|
||||
"help_text": null,
|
||||
"options": [],
|
||||
"constraints": {"max_length": 20},
|
||||
"default_value": null
|
||||
}
|
||||
],
|
||||
"publication_state": "published",
|
||||
"allow_drafts": true,
|
||||
"max_attachments": 4,
|
||||
"signature_requirement": "none",
|
||||
"policy_refs": [
|
||||
"law:resident-parking-permit",
|
||||
"records:resident-parking-permit"
|
||||
],
|
||||
"handoff_kinds": ["case", "workflow"],
|
||||
"metadata": {},
|
||||
"pages": [
|
||||
{
|
||||
"key": "application",
|
||||
"title": "Application",
|
||||
"description": null,
|
||||
"sections": [
|
||||
{
|
||||
"key": "applicant-and-vehicle",
|
||||
"title": "Applicant and vehicle",
|
||||
"description": null,
|
||||
"field_keys": [
|
||||
"applicant_name",
|
||||
"applicant_email",
|
||||
"residence_address",
|
||||
"licence_plate"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"localizations": [
|
||||
{
|
||||
"locale": "de",
|
||||
"title": "Anwohnerparkausweis beantragen",
|
||||
"description": "Einen Anwohnerparkausweis digital oder mit Unterstützung beantragen.",
|
||||
"field_labels": {
|
||||
"applicant_name": "Name",
|
||||
"applicant_email": "E-Mail-Adresse",
|
||||
"residence_address": "Hauptwohnsitz",
|
||||
"licence_plate": "Kennzeichen"
|
||||
},
|
||||
"field_help_texts": {},
|
||||
"option_labels": {},
|
||||
"page_titles": {"application": "Antrag"},
|
||||
"section_titles": {
|
||||
"applicant-and-vehicle": "Antragstellende Person und Fahrzeug"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fallback_locale": "de"
|
||||
},
|
||||
"definition_sha256": "7dc108002d532c07e5e7f3b14029a9d4deb3836ebb65d97fb6b51166a70e0ed4",
|
||||
"provenance": {
|
||||
"owner_module": "forms",
|
||||
"tenant_id": "reference-package",
|
||||
"form_id": "resident-parking-permit-application",
|
||||
"revision": "3",
|
||||
"exported_at": "2026-08-22T12:00:00+00:00",
|
||||
"exported_by": "GovOPlaN reference package"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"module_id": "workflow_engine",
|
||||
"fragment_type": "workflow_definitions",
|
||||
"fragment_id": "resident-parking-permit-workflow",
|
||||
"payload": {
|
||||
"schema_version": 1,
|
||||
"origin_module_id": "configuration_package.service_to_decision",
|
||||
"origin_module_version": "0.1.0",
|
||||
"items": [
|
||||
{
|
||||
"definition_key": "resident-parking-permit-review",
|
||||
"name": "Resident parking permit review",
|
||||
"description": "Review evidence, record the formal decision, and verify payment evidence for the resident parking permit reference journey.",
|
||||
"scope_type": "tenant",
|
||||
"allow_start": true,
|
||||
"allow_reuse": true,
|
||||
"allow_automation": false,
|
||||
"execution_mode": "guided",
|
||||
"activate_on_install": true,
|
||||
"graph": {
|
||||
"schema_version": 1,
|
||||
"nodes": [
|
||||
{
|
||||
"id": "start",
|
||||
"type": "workflow.start.manual",
|
||||
"label": "Application received",
|
||||
"config": {"input_schema_ref": "form:resident-parking-permit-application"}
|
||||
},
|
||||
{
|
||||
"id": "review-evidence",
|
||||
"type": "workflow.review",
|
||||
"label": "Review application evidence",
|
||||
"config": {
|
||||
"title": "Review resident parking permit evidence",
|
||||
"reviewer": {
|
||||
"kind": "group",
|
||||
"id": {"$data": "responsible_group_slug"},
|
||||
"label": {"$data": "responsible_group_name"}
|
||||
},
|
||||
"due_after": "P14D",
|
||||
"required_evidence": [
|
||||
"identity",
|
||||
"primary_residence",
|
||||
"vehicle_registration"
|
||||
],
|
||||
"view_surface_ids": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "record-decision",
|
||||
"type": "workflow.activity",
|
||||
"label": "Record formal decision",
|
||||
"config": {
|
||||
"title": "Record the resident parking permit decision",
|
||||
"instructions": "Record the operative result, reasoning, legal basis, remedy, and exact evidence references through the Decisions capability.",
|
||||
"assignee": {
|
||||
"kind": "group",
|
||||
"id": {"$data": "responsible_group_slug"},
|
||||
"label": {"$data": "responsible_group_name"}
|
||||
},
|
||||
"due_after": "P7D",
|
||||
"view_surface_ids": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "verify-payment",
|
||||
"type": "workflow.activity",
|
||||
"label": "Verify payment evidence",
|
||||
"config": {
|
||||
"title": "Verify the resident parking permit fee",
|
||||
"instructions": "Verify the EUR 30.00 obligation, immutable receipt evidence, currency, amount, and transaction reference before completion.",
|
||||
"assignee": {
|
||||
"kind": "group",
|
||||
"id": {"$data": "responsible_group_slug"},
|
||||
"label": {"$data": "responsible_group_name"}
|
||||
},
|
||||
"due_after": "P14D",
|
||||
"view_surface_ids": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "completed",
|
||||
"type": "workflow.end.completed",
|
||||
"label": "Permit journey complete",
|
||||
"config": {"output_mapping": {}}
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{"id": "start-review", "source": "start", "target": "review-evidence"},
|
||||
{"id": "review-decision", "source": "review-evidence", "source_port": "approved", "target": "record-decision"},
|
||||
{"id": "decision-payment", "source": "record-decision", "target": "verify-payment"},
|
||||
{"id": "payment-completed", "source": "verify-payment", "target": "completed"}
|
||||
],
|
||||
"metadata": {
|
||||
"reference_journey": "resident-parking-permit",
|
||||
"locale": "de-DE",
|
||||
"payment_amount_minor": 3000,
|
||||
"payment_currency": "EUR"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"reference_package": "product.service-to-decision",
|
||||
"form_id": "resident-parking-permit-application"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "documentation",
|
||||
"reference": "packages/product/service-to-decision/README.md",
|
||||
"summary": "Defines the package boundary, authority path, recovery contract, and known operational limits."
|
||||
},
|
||||
{
|
||||
"kind": "target_test",
|
||||
"reference": "tests/fixtures/resident_parking_permit_journey.json",
|
||||
"summary": "Pins the resident parking permit actors, channels, exact inputs, work item, formal outcome, filing target, and remaining manual acceptance gates."
|
||||
},
|
||||
{
|
||||
"kind": "target_test",
|
||||
"reference": "tests/test_institutional_governance_journey.py",
|
||||
"summary": "Executes the SQL-backed provider composition from service discovery through persisted formal Decision reconstruction."
|
||||
},
|
||||
{
|
||||
"kind": "target_test",
|
||||
"reference": "tests/test_institutional_service_journey.py",
|
||||
"summary": "Executes Portal delegation from an exact Service revision through an exact immutable Form revision to a replay-safe persisted submission."
|
||||
}
|
||||
],
|
||||
"tags": ["service", "case", "mandate", "party", "decision", "public-sector"]
|
||||
}
|
||||
+80
-67
@@ -1,73 +1,86 @@
|
||||
{
|
||||
"version": 1,
|
||||
"organization": "add-ideas",
|
||||
"organization": "GovOPlaN",
|
||||
"default_parent": "/mnt/DATA/git",
|
||||
"repositories": [
|
||||
{"name": "govoplan", "category": "system", "subtype": "meta", "remote": "git@git.add-ideas.de:add-ideas/govoplan.git", "path": "govoplan"},
|
||||
{"name": "govoplan-core", "category": "system", "subtype": "kernel", "remote": "git@git.add-ideas.de:add-ideas/govoplan-core.git", "path": "govoplan-core"},
|
||||
{"name": "govoplan-access", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-access.git", "path": "govoplan-access"},
|
||||
{"name": "govoplan-addresses", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-addresses.git", "path": "govoplan-addresses"},
|
||||
{"name": "govoplan-admin", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-admin.git", "path": "govoplan-admin"},
|
||||
{"name": "govoplan-appointments", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-appointments.git", "path": "govoplan-appointments"},
|
||||
{"name": "govoplan-approvals", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-approvals.git", "path": "govoplan-approvals"},
|
||||
{"name": "govoplan-assets", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-assets.git", "path": "govoplan-assets"},
|
||||
{"name": "govoplan-audit", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-audit.git", "path": "govoplan-audit"},
|
||||
{"name": "govoplan-booking", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-booking.git", "path": "govoplan-booking"},
|
||||
{"name": "govoplan-calendar", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-calendar.git", "path": "govoplan-calendar"},
|
||||
{"name": "govoplan-campaign", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-campaign.git", "path": "govoplan-campaign"},
|
||||
{"name": "govoplan-cases", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-cases.git", "path": "govoplan-cases"},
|
||||
{"name": "govoplan-certificates", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-certificates.git", "path": "govoplan-certificates"},
|
||||
{"name": "govoplan-committee", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-committee.git", "path": "govoplan-committee"},
|
||||
{"name": "govoplan-connectors", "category": "connector", "subtype": "connector-hub", "remote": "git@git.add-ideas.de:add-ideas/govoplan-connectors.git", "path": "govoplan-connectors"},
|
||||
{"name": "govoplan-consultation", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-consultation.git", "path": "govoplan-consultation"},
|
||||
{"name": "govoplan-contracts", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-contracts.git", "path": "govoplan-contracts"},
|
||||
{"name": "govoplan-dashboard", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-dashboard.git", "path": "govoplan-dashboard"},
|
||||
{"name": "govoplan-dms", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-dms.git", "path": "govoplan-dms"},
|
||||
{"name": "govoplan-dist-lists", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-dist-lists.git", "path": "govoplan-dist-lists"},
|
||||
{"name": "govoplan-docs", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-docs.git", "path": "govoplan-docs"},
|
||||
{"name": "govoplan-erp", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-erp.git", "path": "govoplan-erp"},
|
||||
{"name": "govoplan-evaluation", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-evaluation.git", "path": "govoplan-evaluation"},
|
||||
{"name": "govoplan-facilities", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-facilities.git", "path": "govoplan-facilities"},
|
||||
{"name": "govoplan-files", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-files.git", "path": "govoplan-files"},
|
||||
{"name": "govoplan-fit-connect", "category": "connector", "subtype": "standard", "remote": "git@git.add-ideas.de:add-ideas/govoplan-fit-connect.git", "path": "govoplan-fit-connect"},
|
||||
{"name": "govoplan-forms", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-forms.git", "path": "govoplan-forms"},
|
||||
{"name": "govoplan-forms-runtime", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-forms-runtime.git", "path": "govoplan-forms-runtime"},
|
||||
{"name": "govoplan-grants", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-grants.git", "path": "govoplan-grants"},
|
||||
{"name": "govoplan-helpdesk", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-helpdesk.git", "path": "govoplan-helpdesk"},
|
||||
{"name": "govoplan-identity", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-identity.git", "path": "govoplan-identity"},
|
||||
{"name": "govoplan-identity-trust", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-identity-trust.git", "path": "govoplan-identity-trust"},
|
||||
{"name": "govoplan-idm", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-idm.git", "path": "govoplan-idm"},
|
||||
{"name": "govoplan-inspections", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-inspections.git", "path": "govoplan-inspections"},
|
||||
{"name": "govoplan-issue-reporting", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-issue-reporting.git", "path": "govoplan-issue-reporting"},
|
||||
{"name": "govoplan-learning", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-learning.git", "path": "govoplan-learning"},
|
||||
{"name": "govoplan-ledger", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-ledger.git", "path": "govoplan-ledger"},
|
||||
{"name": "govoplan-mail", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-mail.git", "path": "govoplan-mail"},
|
||||
{"name": "govoplan-notifications", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-notifications.git", "path": "govoplan-notifications"},
|
||||
{"name": "govoplan-ops", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-ops.git", "path": "govoplan-ops"},
|
||||
{"name": "govoplan-organizations", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-organizations.git", "path": "govoplan-organizations"},
|
||||
{"name": "govoplan-payments", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-payments.git", "path": "govoplan-payments"},
|
||||
{"name": "govoplan-permits", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-permits.git", "path": "govoplan-permits"},
|
||||
{"name": "govoplan-policy", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-policy.git", "path": "govoplan-policy"},
|
||||
{"name": "govoplan-poll", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-poll.git", "path": "govoplan-poll"},
|
||||
{"name": "govoplan-portal", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-portal.git", "path": "govoplan-portal"},
|
||||
{"name": "govoplan-postbox", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-postbox.git", "path": "govoplan-postbox"},
|
||||
{"name": "govoplan-procurement", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-procurement.git", "path": "govoplan-procurement"},
|
||||
{"name": "govoplan-records", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-records.git", "path": "govoplan-records"},
|
||||
{"name": "govoplan-reporting", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-reporting.git", "path": "govoplan-reporting"},
|
||||
{"name": "govoplan-resources", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-resources.git", "path": "govoplan-resources"},
|
||||
{"name": "govoplan-rest", "category": "connector", "subtype": "protocol", "remote": "git@git.add-ideas.de:add-ideas/govoplan-rest.git", "path": "govoplan-rest"},
|
||||
{"name": "govoplan-risk-compliance", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-risk-compliance.git", "path": "govoplan-risk-compliance"},
|
||||
{"name": "govoplan-scheduling", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-scheduling.git", "path": "govoplan-scheduling"},
|
||||
{"name": "govoplan-search", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-search.git", "path": "govoplan-search"},
|
||||
{"name": "govoplan-soap", "category": "connector", "subtype": "protocol", "remote": "git@git.add-ideas.de:add-ideas/govoplan-soap.git", "path": "govoplan-soap"},
|
||||
{"name": "govoplan-tasks", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-tasks.git", "path": "govoplan-tasks"},
|
||||
{"name": "govoplan-templates", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-templates.git", "path": "govoplan-templates"},
|
||||
{"name": "govoplan-tenancy", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-tenancy.git", "path": "govoplan-tenancy"},
|
||||
{"name": "govoplan-transparency", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:add-ideas/govoplan-transparency.git", "path": "govoplan-transparency"},
|
||||
{"name": "addideas-govoplan-website", "category": "website", "subtype": "public-site", "remote": "git@git.add-ideas.de:add-ideas/addideas-govoplan-website.git", "path": "addideas-govoplan-website"},
|
||||
{"name": "govoplan-workflow", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:add-ideas/govoplan-workflow.git", "path": "govoplan-workflow"},
|
||||
{"name": "govoplan-xoev", "category": "connector", "subtype": "standard", "remote": "git@git.add-ideas.de:add-ideas/govoplan-xoev.git", "path": "govoplan-xoev"},
|
||||
{"name": "govoplan-xrechnung", "category": "connector", "subtype": "standard", "remote": "git@git.add-ideas.de:add-ideas/govoplan-xrechnung.git", "path": "govoplan-xrechnung"},
|
||||
{"name": "govoplan-xta-osci", "category": "connector", "subtype": "standard", "remote": "git@git.add-ideas.de:add-ideas/govoplan-xta-osci.git", "path": "govoplan-xta-osci"}
|
||||
{"name": "govoplan", "category": "system", "subtype": "meta", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan.git", "path": "govoplan"},
|
||||
{"name": "govoplan-core", "category": "system", "subtype": "kernel", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-core.git", "path": "govoplan-core"},
|
||||
{"name": "govoplan-access", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-access.git", "path": "govoplan-access"},
|
||||
{"name": "govoplan-addresses", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-addresses.git", "path": "govoplan-addresses"},
|
||||
{"name": "govoplan-admin", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-admin.git", "path": "govoplan-admin"},
|
||||
{"name": "govoplan-appointments", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-appointments.git", "path": "govoplan-appointments"},
|
||||
{"name": "govoplan-approvals", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-approvals.git", "path": "govoplan-approvals"},
|
||||
{"name": "govoplan-assets", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-assets.git", "path": "govoplan-assets"},
|
||||
{"name": "govoplan-audit", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-audit.git", "path": "govoplan-audit"},
|
||||
{"name": "govoplan-booking", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-booking.git", "path": "govoplan-booking"},
|
||||
{"name": "govoplan-calendar", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-calendar.git", "path": "govoplan-calendar"},
|
||||
{"name": "govoplan-campaign", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-campaign.git", "path": "govoplan-campaign"},
|
||||
{"name": "govoplan-cases", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-cases.git", "path": "govoplan-cases"},
|
||||
{"name": "govoplan-certificates", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-certificates.git", "path": "govoplan-certificates"},
|
||||
{"name": "govoplan-committee", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-committee.git", "path": "govoplan-committee"},
|
||||
{"name": "govoplan-connectors", "category": "connector", "subtype": "connector-hub", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-connectors.git", "path": "govoplan-connectors"},
|
||||
{"name": "govoplan-consultation", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-consultation.git", "path": "govoplan-consultation"},
|
||||
{"name": "govoplan-contracts", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-contracts.git", "path": "govoplan-contracts"},
|
||||
{"name": "govoplan-dashboard", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-dashboard.git", "path": "govoplan-dashboard"},
|
||||
{"name": "govoplan-dataflow", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-dataflow.git", "path": "govoplan-dataflow"},
|
||||
{"name": "govoplan-datasources", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-datasources.git", "path": "govoplan-datasources"},
|
||||
{"name": "govoplan-decisions", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-decisions.git", "path": "govoplan-decisions"},
|
||||
{"name": "govoplan-dms", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-dms.git", "path": "govoplan-dms"},
|
||||
{"name": "govoplan-dist-lists", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-dist-lists.git", "path": "govoplan-dist-lists"},
|
||||
{"name": "govoplan-docs", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-docs.git", "path": "govoplan-docs"},
|
||||
{"name": "govoplan-encryption", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-encryption.git", "path": "govoplan-encryption"},
|
||||
{"name": "govoplan-erp", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-erp.git", "path": "govoplan-erp"},
|
||||
{"name": "govoplan-evaluation", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-evaluation.git", "path": "govoplan-evaluation"},
|
||||
{"name": "govoplan-facilities", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-facilities.git", "path": "govoplan-facilities"},
|
||||
{"name": "govoplan-files", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-files.git", "path": "govoplan-files"},
|
||||
{"name": "govoplan-fit-connect", "category": "connector", "subtype": "standard", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-fit-connect.git", "path": "govoplan-fit-connect"},
|
||||
{"name": "govoplan-forms", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-forms.git", "path": "govoplan-forms"},
|
||||
{"name": "govoplan-forms-runtime", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-forms-runtime.git", "path": "govoplan-forms-runtime"},
|
||||
{"name": "govoplan-grants", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-grants.git", "path": "govoplan-grants"},
|
||||
{"name": "govoplan-helpdesk", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-helpdesk.git", "path": "govoplan-helpdesk"},
|
||||
{"name": "govoplan-identity", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-identity.git", "path": "govoplan-identity"},
|
||||
{"name": "govoplan-identity-trust", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-identity-trust.git", "path": "govoplan-identity-trust"},
|
||||
{"name": "govoplan-idm", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-idm.git", "path": "govoplan-idm"},
|
||||
{"name": "govoplan-inspections", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-inspections.git", "path": "govoplan-inspections"},
|
||||
{"name": "govoplan-learning", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-learning.git", "path": "govoplan-learning"},
|
||||
{"name": "govoplan-ledger", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-ledger.git", "path": "govoplan-ledger"},
|
||||
{"name": "govoplan-mail", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-mail.git", "path": "govoplan-mail"},
|
||||
{"name": "govoplan-mandates", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-mandates.git", "path": "govoplan-mandates"},
|
||||
{"name": "govoplan-notifications", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-notifications.git", "path": "govoplan-notifications"},
|
||||
{"name": "govoplan-ops", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-ops.git", "path": "govoplan-ops"},
|
||||
{"name": "govoplan-organizations", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-organizations.git", "path": "govoplan-organizations"},
|
||||
{"name": "govoplan-payments", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-payments.git", "path": "govoplan-payments"},
|
||||
{"name": "govoplan-parties", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-parties.git", "path": "govoplan-parties"},
|
||||
{"name": "govoplan-permits", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-permits.git", "path": "govoplan-permits"},
|
||||
{"name": "govoplan-policy", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-policy.git", "path": "govoplan-policy"},
|
||||
{"name": "govoplan-poll", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-poll.git", "path": "govoplan-poll"},
|
||||
{"name": "govoplan-portal", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-portal.git", "path": "govoplan-portal"},
|
||||
{"name": "govoplan-postbox", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-postbox.git", "path": "govoplan-postbox"},
|
||||
{"name": "govoplan-procurement", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-procurement.git", "path": "govoplan-procurement"},
|
||||
{"name": "govoplan-projects", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-projects.git", "path": "govoplan-projects"},
|
||||
{"name": "govoplan-quick-access", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-quick-access.git", "path": "govoplan-quick-access"},
|
||||
{"name": "govoplan-records", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-records.git", "path": "govoplan-records"},
|
||||
{"name": "govoplan-reporting", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-reporting.git", "path": "govoplan-reporting"},
|
||||
{"name": "govoplan-resources", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-resources.git", "path": "govoplan-resources"},
|
||||
{"name": "govoplan-rest", "category": "connector", "subtype": "protocol", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-rest.git", "path": "govoplan-rest"},
|
||||
{"name": "govoplan-risk-compliance", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-risk-compliance.git", "path": "govoplan-risk-compliance"},
|
||||
{"name": "govoplan-scheduling", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-scheduling.git", "path": "govoplan-scheduling"},
|
||||
{"name": "govoplan-search", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-search.git", "path": "govoplan-search"},
|
||||
{"name": "govoplan-services", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-services.git", "path": "govoplan-services"},
|
||||
{"name": "govoplan-soap", "category": "connector", "subtype": "protocol", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-soap.git", "path": "govoplan-soap"},
|
||||
{"name": "govoplan-tasks", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-tasks.git", "path": "govoplan-tasks"},
|
||||
{"name": "govoplan-templates", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-templates.git", "path": "govoplan-templates"},
|
||||
{"name": "govoplan-tenancy", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-tenancy.git", "path": "govoplan-tenancy"},
|
||||
{"name": "govoplan-tickets", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-tickets.git", "path": "govoplan-tickets"},
|
||||
{"name": "govoplan-transparency", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-transparency.git", "path": "govoplan-transparency"},
|
||||
{"name": "govoplan-views", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-views.git", "path": "govoplan-views"},
|
||||
{"name": "govoplan-voting", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-voting.git", "path": "govoplan-voting"},
|
||||
{"name": "govoplan-wiki", "category": "module", "subtype": "domain", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-wiki.git", "path": "govoplan-wiki"},
|
||||
{"name": "addideas-govoplan-website", "category": "website", "subtype": "public-site", "remote": "git@git.add-ideas.de:add-ideas/addideas-govoplan-website.git", "path": "addideas-govoplan-website", "bootstrap_transport": "registered"},
|
||||
{"name": "govoplan-workflow", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-workflow.git", "path": "govoplan-workflow"},
|
||||
{"name": "govoplan-workflow-engine", "category": "module", "subtype": "platform", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-workflow-engine.git", "path": "govoplan-workflow-engine"},
|
||||
{"name": "govoplan-xoev", "category": "connector", "subtype": "standard", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-xoev.git", "path": "govoplan-xoev"},
|
||||
{"name": "govoplan-xrechnung", "category": "connector", "subtype": "standard", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-xrechnung.git", "path": "govoplan-xrechnung"},
|
||||
{"name": "govoplan-xta-osci", "category": "connector", "subtype": "standard", "remote": "git@git.add-ideas.de:GovOPlaN/govoplan-xta-osci.git", "path": "govoplan-xta-osci"}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ bandit>=1.8,<2
|
||||
click>=8.3.3
|
||||
filelock>=3.20.3
|
||||
idna>=3.15
|
||||
pip>=26.1.2
|
||||
pip>=26.2
|
||||
pip-audit>=2.9,<3
|
||||
python-multipart>=0.0.31
|
||||
radon>=6,<7
|
||||
|
||||
+29
-1
@@ -12,25 +12,53 @@
|
||||
-e ../govoplan-admin
|
||||
-e ../govoplan-policy
|
||||
-e ../govoplan-audit
|
||||
-e ../govoplan-approvals
|
||||
-e ../govoplan-dashboard
|
||||
-e ../govoplan-addresses
|
||||
-e ../govoplan-dist-lists
|
||||
-e ../govoplan-templates
|
||||
-e ../govoplan-files
|
||||
-e ../govoplan-forms
|
||||
-e ../govoplan-forms-runtime
|
||||
-e ../govoplan-mail
|
||||
-e ../govoplan-campaign
|
||||
-e ../govoplan-calendar
|
||||
-e ../govoplan-committee
|
||||
-e ../govoplan-cases
|
||||
-e ../govoplan-portal
|
||||
-e ../govoplan-services
|
||||
-e ../govoplan-parties
|
||||
-e ../govoplan-mandates
|
||||
-e ../govoplan-decisions
|
||||
-e ../govoplan-payments
|
||||
-e ../govoplan-connectors
|
||||
-e ../govoplan-datasources
|
||||
-e ../govoplan-dataflow
|
||||
-e ../govoplan-workflow-engine
|
||||
-e ../govoplan-workflow
|
||||
-e ../govoplan-tasks
|
||||
-e ../govoplan-quick-access
|
||||
-e ../govoplan-views
|
||||
-e ../govoplan-voting
|
||||
-e ../govoplan-search
|
||||
-e ../govoplan-risk-compliance
|
||||
-e ../govoplan-postbox
|
||||
-e ../govoplan-poll
|
||||
-e ../govoplan-scheduling
|
||||
-e ../govoplan-notifications
|
||||
-e ../govoplan-evaluation
|
||||
-e ../govoplan-docs
|
||||
-e ../govoplan-encryption
|
||||
-e ../govoplan-identity-trust
|
||||
-e ../govoplan-ops
|
||||
httpx==0.28.1
|
||||
httpx2>=2.5,<3
|
||||
filelock>=3.20.3
|
||||
idna>=3.15
|
||||
jsonschema>=4,<5
|
||||
pip>=26.1.2
|
||||
pip>=26.2
|
||||
pip-audit>=2.9,<3
|
||||
pytest>=9.0.3,<10
|
||||
pygments>=2.20,<3
|
||||
python-multipart>=0.0.31
|
||||
ruff>=0.14,<1
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Test-harness dependencies used against immutable release source tags.
|
||||
# Keep these separate from requirements-release.txt so they are not part of the
|
||||
# deployable product dependency set.
|
||||
pytest>=9.0.3,<10
|
||||
pygments>=2.20,<3
|
||||
+15
-15
@@ -1,18 +1,18 @@
|
||||
# Whole-product release install from immutable, independently versioned module tags.
|
||||
# Only add a module after its referenced tag has been published.
|
||||
../govoplan-core[server]
|
||||
govoplan-tenancy @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-tenancy.git@v0.1.8
|
||||
govoplan-organizations @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-organizations.git@v0.1.8
|
||||
govoplan-identity @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-identity.git@v0.1.8
|
||||
govoplan-idm @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-idm.git@v0.1.8
|
||||
govoplan-access @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-access.git@v0.1.8
|
||||
govoplan-admin @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-admin.git@v0.1.8
|
||||
govoplan-policy @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-policy.git@v0.1.8
|
||||
govoplan-audit @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-audit.git@v0.1.8
|
||||
govoplan-dashboard @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-dashboard.git@v0.1.8
|
||||
govoplan-files @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-files.git@v0.1.8
|
||||
govoplan-mail @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-mail.git@v0.1.8
|
||||
govoplan-campaign @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-campaign.git@v0.1.10
|
||||
govoplan-calendar @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-calendar.git@v0.1.8
|
||||
govoplan-docs @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-docs.git@v0.1.8
|
||||
govoplan-ops @ git+ssh://git@git.add-ideas.de/add-ideas/govoplan-ops.git@v0.1.8
|
||||
govoplan-tenancy @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-tenancy.git@v0.1.22
|
||||
govoplan-organizations @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-organizations.git@v0.1.21
|
||||
govoplan-identity @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-identity.git@v0.1.21
|
||||
govoplan-idm @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-idm.git@v0.1.26
|
||||
govoplan-access @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-access.git@v0.1.25
|
||||
govoplan-admin @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-admin.git@v0.1.23
|
||||
govoplan-policy @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-policy.git@v0.1.23
|
||||
govoplan-audit @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-audit.git@v0.1.20
|
||||
govoplan-dashboard @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-dashboard.git@v0.1.20
|
||||
govoplan-files @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-files.git@v0.1.27
|
||||
govoplan-mail @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-mail.git@v0.1.28
|
||||
govoplan-campaign @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-campaign.git@v0.1.29
|
||||
govoplan-calendar @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-calendar.git@v0.1.24
|
||||
govoplan-docs @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-docs.git@v0.1.23
|
||||
govoplan-ops @ git+ssh://git@git.add-ideas.de/GovOPlaN/govoplan-ops.git@v0.1.22
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"id": "resident-parking-permit-berlin-style-reference",
|
||||
"title": "Resident parking permit",
|
||||
"title_de": "Anwohnerparkausweis",
|
||||
"locale": "de-DE",
|
||||
"service": {
|
||||
"object_id": "resident-parking-permit",
|
||||
"key": "resident_parking_permit.apply",
|
||||
"version": "6",
|
||||
"audience": "resident",
|
||||
"required_evidence_types": [
|
||||
"application",
|
||||
"identity",
|
||||
"primary_residence",
|
||||
"vehicle_registration"
|
||||
],
|
||||
"channels": ["portal", "assisted"]
|
||||
},
|
||||
"form": {
|
||||
"object_id": "resident-parking-permit-application",
|
||||
"version": "3",
|
||||
"fields": {
|
||||
"applicant_name": "Ada Lovelace",
|
||||
"applicant_email": "ada.lovelace@example.test",
|
||||
"residence_address": "Musterstrasse 17, 10115 Berlin",
|
||||
"licence_plate": "B-AL 1843"
|
||||
}
|
||||
},
|
||||
"status_access": {
|
||||
"mode": "email_link",
|
||||
"email_field_key": "applicant_email",
|
||||
"token_ttl_seconds": 1800,
|
||||
"request_limit_per_hour": 3
|
||||
},
|
||||
"assisted_intake": {
|
||||
"channel": "counter",
|
||||
"affected_party_ref": "party:resident-ada-lovelace",
|
||||
"represented_party_ref": null,
|
||||
"authority_basis": "self",
|
||||
"purpose": "Apply for a resident parking permit.",
|
||||
"legal_basis_ref": "law:resident-parking-permit",
|
||||
"consent_basis": "in-person-confirmation",
|
||||
"notice_given": true,
|
||||
"responsible_function_ref": "function:parking-permits",
|
||||
"language": "de",
|
||||
"accessibility_needs": ["plain-language"],
|
||||
"confirmation_method": "written_preview",
|
||||
"confirmation_outcome": "confirmed"
|
||||
},
|
||||
"case": {
|
||||
"type_key": "resident-parking-permit-application",
|
||||
"number": "RPP-2026-0001",
|
||||
"initial_status": "intake",
|
||||
"decided_status": "decided",
|
||||
"deadline_days": 30
|
||||
},
|
||||
"workflow": {
|
||||
"definition_name": "Resident parking permit decision",
|
||||
"work_item_title": "Decide the resident parking permit application",
|
||||
"instructions": "Review identity, primary residence, vehicle evidence, and the effective local rule before recording the decision."
|
||||
},
|
||||
"decision": {
|
||||
"type": "resident-parking-permit",
|
||||
"operative_result": "Resident parking permit granted.",
|
||||
"reasoning": "Identity, primary residence, vehicle registration, and the effective local rule were verified.",
|
||||
"delivery_channel": "postbox",
|
||||
"remedy": "review:administrative-court"
|
||||
},
|
||||
"payment": {
|
||||
"mode": "manual",
|
||||
"amount_minor": 3000,
|
||||
"currency": "EUR",
|
||||
"subject": "Resident parking permit fee",
|
||||
"due_days": 14,
|
||||
"evidence_owner": "files"
|
||||
},
|
||||
"records": {
|
||||
"file_plan_key": "traffic.resident-parking-permits",
|
||||
"retention_policy_ref": "records:resident-parking-permit"
|
||||
},
|
||||
"acceptance": {
|
||||
"automated": [
|
||||
"The published service and exact form revision drive digital intake.",
|
||||
"An authenticated assisted session uses the same exact form and validation rules while retaining purpose, authority, channel, party, accessibility, source, correction, and read-back provenance.",
|
||||
"The configured applicant email issues a short-lived, hash-only status link through Notifications and exposes only the bounded status timeline.",
|
||||
"An idempotent replay returns the same persisted submission.",
|
||||
"The human review handoff survives a database-session restart and remains visible in Tasks until completion.",
|
||||
"The production self-service and assisted WebUI paths preserve keyboard order, accessible names, WCAG 2.1 A/AA automation, and responsive geometry at desktop and mobile widths.",
|
||||
"The assisted operator can assign independent source, confidence, and governed declaring-party, document, or system references to every populated field before immutable read-back.",
|
||||
"The formal decision retains party, mandate, legal-basis, evidence, delivery, review, and exact revision references.",
|
||||
"The Case-bound payment handoff creates a replay-safe obligation and accepts a full manual receipt only with exact amount, currency, transaction reference, and immutable evidence.",
|
||||
"Forms Runtime, Cases, and Decisions can expose exact snapshots for explicit eAkte filing."
|
||||
],
|
||||
"manual_or_target": [
|
||||
"Perform physical screen-reader spot checks for the digital journey at desktop and mobile widths.",
|
||||
"Perform physical screen-reader spot checks for the assisted operator journey at desktop and mobile widths.",
|
||||
"Open, resend, expire, and revoke the applicant status link with keyboard and screen reader at desktop and mobile widths.",
|
||||
"Verify the configured Postbox or external delivery provider, including unknown outcome and reconciliation.",
|
||||
"Restore the pinned composition and reconstruct the exact form, case, decision, delivery evidence, and eAkte chronology.",
|
||||
"Transfer through a named archive profile and retain independently signed target evidence."
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { findTypeOnlyJsxImports } from "../tools/checks/check-jsx-value-imports.mjs";
|
||||
|
||||
const findings = (source) => findTypeOnlyJsxImports([{ path: "/fixture.tsx", source }]).map((item) => item.component);
|
||||
assert.deepEqual(findings('import type { FormGrid, AuthInfo } from "@govoplan/core-webui"; const page = <Dialog><FormGrid /></Dialog>;'), ["FormGrid"]);
|
||||
assert.deepEqual(findings('import { type FormGrid as Layout } from "ui"; const page = <Layout>Content</Layout>;'), ["Layout"]);
|
||||
assert.deepEqual(findings('import type Layout from "ui"; const page = <Layout />;'), ["Layout"]);
|
||||
assert.deepEqual(findings('import type * as ui from "ui"; const page = <ui.Layout />;'), ["ui.Layout"]);
|
||||
assert.deepEqual(findings('import type { FormGrid } from "ui"; const page = <div title={<FormGrid />} />;'), ["FormGrid"]);
|
||||
assert.deepEqual(findings('import { FormGrid, type AuthInfo } from "ui"; const page = <FormGrid />;'), []);
|
||||
assert.deepEqual(findings('import type { FormGrid } from "ui"; function Page({ FormGrid }: Props) { return <FormGrid />; }'), []);
|
||||
assert.deepEqual(findings('import type * as ui from "ui"; function Page(ui: RuntimeControls) { return <ui.Layout />; }'), []);
|
||||
assert.deepEqual(findings('import type { Layout } from "ui"; const page: Layout = {};'), []);
|
||||
assert.deepEqual(findings('import type { input } from "ui"; const page = <input />;'), []);
|
||||
console.log("JSX runtime-import AST regression tests passed (10 cases).");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user