docs: organize cross-product documentation
Dependency Audit / dependency-audit (push) Successful in 1m46s
Deployment Installer / deployment-installer (push) Successful in 9s
Security Audit / security-audit (push) Successful in 11m48s

This commit is contained in:
2026-08-17 16:52:51 +02:00
parent 209a43592f
commit c66e1b768d
46 changed files with 384 additions and 271 deletions
@@ -0,0 +1,584 @@
# 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 |
| `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.
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`.
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.
- Replacing or removing a capability adds a review action that names the prior
and desired state/source plus declared module consumers. This does not claim
that the deployer can inspect module-owned database configuration; the
operator must review that inventory 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.
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.