Skip to main content

Default composition: which apps a profile deploys

apps.yaml is the source of truth. Each app carries profiles (where it MAY run) and default_in (where it IS run when nobody says otherwise). default_in absent means the same set as profiles; an empty list means the app is deployed on demand and never seeded.

Appslimsinglescalemeshdocker-slimdocker-single
dfe-receiverXXXXXX
dfe-loaderXXXXXX
dfe-archiverXXXXX
dfe-engineXXXXXX
dfe-uiXXXXXX
hyperdxXXXXXX
dfe-fetchern/aX
dfe-transform-vrlXXX
dfe-transform-elasticXXX
dfe-transform-vector
culvertn/an/an/an/a

python3 scripts/composition.py prints that table from the manifest. A fetcher instance IS a source's processing step, so it arrives when the engine writes the source rather than with the tier. Each transform instead declares idle_when, so a tier seeds ONE of each, stood up empty and Ready, and the first source fills it rather than creating it. per_config multiplies them from there -- seeding places the first, it does not cap the count. culvert is offered on the two HA tiers and enabled by an operator adding its values file.

Compose is the exception, and the reason is Compose's own: it holds ONE service per app, declared in a committed file, and nothing creates a second at run time. So docker-single starts one archiver, one fetcher and one transform-vrl idle (see "Deployed before it has work"), and the source each is later given fills the one that is already there. docker-slim runs the core data path alone.

The six profile names are scripts/profiles.py's -- four Kubernetes tiers and two Compose ones, because an app can be default on a cluster and opt-in on a laptop.

How it reaches a deployment​

Helm cannot read apps.yaml, so the derived set is rendered into each tier's values file:

python3 scripts/composition.py --write-seed

That writes deployRepo.seedApps into argocd/values/profile-<tier>.yaml, which the bundled deploy repo seeds on a fresh deployment -- one values/<app>-<instance>-values.yaml per app, and one Argo Application per file. The forgejo chart's own default is empty on purpose: a list there would be a second copy of the composition that nothing keeps in step.

The engine reflects the whole manifest, not only the derived set, so the chart that mounts it carries its own copy:

python3 scripts/composition.py --write-catalogue

That writes helm/charts/dfe-extras/files/apps.yaml -- the manifest byte for byte, under a generated banner -- which dfe-extras mounts as a ConfigMap and points DFE_APP_CATALOGUE_FILE at. Reloader restarts the engine when that ConfigMap changes, so a manifest edit rolls it and GET /api/v1/apps answers with the new shape at the stack revision carrying it. An engine with no mount falls back to the snapshot baked into its image, and adding an app would then need an engine release, so dfe-ops stack-deploy fails its pre-flight when the copy is stale or the render carries no catalogue.

dfe-docker projects the same manifest. scripts/dfe-stack render --docker-profile <slim|single> reads default_in for the matching docker-<mode> profile and writes dfe-docker's service_profiles.yaml block: dfe-engine and dfe-ui become the core footprint key, hyperdx becomes hyperdx, and every other app becomes a services: entry.

Deployed before it has work​

An app in the default composition is deployed whether or not anything has configured it, so an unconfigured one starts rather than crash-loops. Those apps declare idle_when -- the config paths whose emptiness means no work -- and carry the matching predicate in their own code; the Rust apps never read this manifest.

An idle app is Ready, serves health and metrics, opens no broker connection and binds no listener, reports the work_config health component Degraded with the reason, holds the pipeline_idle gauge at 1, and starts the moment a config change gives it work. dfe-ops acceptance --mode <tier> asserts that against a live deploy, because the readiness gate passes an idle app by design.

An app whose work can arrive WITHOUT a config change declares no idle_when. The loader is the case: an empty topic list is auto-discovery, so a topic appearing on the broker gives it work, and the gate only wakes on a config change.