Harden source release preparation and record verified security follow-up
Dependency Audit / dependency-audit (push) Successful in 1m51s
Deployment Installer / deployment-installer (push) Successful in 8s
Security Audit / security-audit (push) Successful in 12m32s

This commit is contained in:
2026-09-08 08:04:12 +02:00
parent 9554657bb5
commit 58d320d9b3
26 changed files with 3833 additions and 163 deletions
@@ -361,6 +361,15 @@ 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
@@ -263,11 +263,198 @@ 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