Record first verified runtime distribution

This commit is contained in:
2026-08-03 20:17:29 +02:00
parent 1f039dd39c
commit ce5528e3b8
3 changed files with 43 additions and 18 deletions
@@ -221,26 +221,39 @@ references and archive hashes; mutable tags or incomplete bundles are rejected.
## Current Production Gates ## Current Production Gates
The tool deliberately reports blockers instead of pretending the source tree is The first immutable production-distribution baseline is published as
a production distribution: [`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:
1. **First publication.** The protected workflow and fail-closed artifact - API: `git.add-ideas.de/govoplan/runtime-api@sha256:197ed01790986f2bc927eaa5d8348fa118702e5d2dc05feb851fc2643c23764a`
contracts are implemented, but a release operator must configure the Gitea - WebUI: `git.add-ideas.de/govoplan/runtime-web@sha256:e936cca124f1fad29a067834cf17627d4c236410fdc3fa129e0ccb26b8193812`
registry/release tokens and runtime Ed25519 key, publish the first pinned
release, and retain its amd64/arm64 readiness evidence. The signed bootstrap has SHA-256
3. **First administrator.** Production needs a one-time, restricted enrollment `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. identity. The development bootstrap must not be enabled in production.
4. **Image/module composition.** The deployer now enforces the signed 2. **Image/module composition.** The deployer enforces the signed
composition. A selected module not shipped by that release cannot be composition. A selected module not shipped by that release cannot be
enabled. enabled.
5. **Deployment agent.** Web updates need a separate privileged reconciler with 3. **Deployment agent.** Web updates need a separate privileged reconciler with
a typed command allowlist. The API and browser must never receive the Docker a typed command allowlist. The API and browser must never receive the Docker
socket or arbitrary shell access. socket or arbitrary shell access.
6. **Ingress reachability evidence.** Managed Caddy ingress and the 4. **Target reachability evidence.** Managed Caddy ingress and the
existing-proxy contract are implemented. A production claim still requires existing-proxy contract are implemented. A production claim still requires
running `doctor` from the target host after public DNS/firewall changes and running `doctor` from the target host after public DNS/firewall changes and
retaining the first successful container drill and public TLS/readiness retaining public TLS/readiness evidence for that deployment.
evidence.
`apply --allow-unverified-images` is therefore restricted to the evaluation `apply --allow-unverified-images` is therefore restricted to the evaluation
profile. It explicitly acknowledges both mutable image identities and profile. It explicitly acknowledges both mutable image identities and
@@ -141,12 +141,16 @@ The canonical backlog item is
Implementation status as of the current source tree: Implementation status as of the current source tree:
- Slice 1 now has the source-controlled production artifact boundary: offline - Slice 1 has a published production-artifact baseline. Immutable
per-architecture wheel resolution, non-root API/Web image definitions, [`v0.1.14`](https://git.add-ideas.de/GovOPlaN/govoplan/releases/tag/v0.1.14)
multi-architecture OCI publication, signed composition/SBOM/provenance, binds source commit `1f039dd39c1ce2672f4978c8abc6dff862ef1445`, a signed
immutable Gitea assets, a signed one-file deployer, and fail-closed manifest one-file deployer, exact API/Web and managed-dependency image digests,
adoption. The first real published release and cross-architecture runtime composition, SBOMs, and provenance. Runtime Distribution
evidence remain release-operator work rather than source-code claims. [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 - Slice 6 has a working application-tier foundation: state profiles, shared
object storage, runtime node registration/heartbeats/drain, fenced scheduler, object storage, runtime node registration/heartbeats/drain, fenced scheduler,
migration serialization, exact-head startup waiting, Ops visibility, and a migration serialization, exact-head startup waiting, Ops visibility, and a
+8
View File
@@ -93,6 +93,14 @@ class RuntimeDistributionTests(unittest.TestCase):
with self.assertRaisesRegex(DistributionError, "active trusted key"): with self.assertRaisesRegex(DistributionError, "active trusted key"):
verify_manifest(unknown, self.keyring, now=self.now) verify_manifest(unknown, self.keyring, now=self.now)
with self.assertRaisesRegex(DistributionError, "expected 'candidate'"):
verify_manifest(
self._manifest(),
self.keyring,
expected_channel="candidate",
now=self.now,
)
def test_offline_image_index_is_complete_and_digest_bound(self) -> None: def test_offline_image_index_is_complete_and_digest_bound(self) -> None:
with tempfile.TemporaryDirectory(prefix="govoplan-offline-images-") as value: with tempfile.TemporaryDirectory(prefix="govoplan-offline-images-") as value:
root = Path(value) root = Path(value)