@@ -49,8 +49,12 @@ into `SalukiConfiguration`.
4949| Legacy test registry, legacy smoke test support, doc gen | ` lib/datadog-agent/config-testing/ ` |
5050| Hand-written Datadog witness implementation | ` lib/agent-data-plane-config-system/src/translators/datadog_translator.rs ` |
5151| Saluki-only source model and ` seed ` | ` lib/agent-data-plane-config-system/src/saluki_only.rs ` |
52+ | Saluki-only defaults | ` lib/agent-data-plane-config/src/defaults.rs ` |
5253| Runtime loading and authority selection | ` lib/agent-data-plane-config-system/src/loaded.rs ` |
5354| Translation gate and update loop | ` lib/agent-data-plane-config-system/src/system.rs ` |
55+ | Schema-driven environment reader (Datadog keys) | ` lib/datadog-agent/config/src/env_reader.rs ` |
56+ | Figment provider wrapping that reader | ` lib/datadog-agent/config/src/env_provider.rs ` |
57+ | Same provider, plus Saluki-only keys | ` lib/agent-data-plane-config-system/src/env_provider.rs ` |
5458
5559Paths and type names can move. Notify the user when this skill needs an update.
5660
@@ -93,6 +97,27 @@ Use the following command to regenerate it:
9397make build-schema-overlay
9498```
9599
100+ ## Environment variables and key shape
101+
102+ The Datadog Agent does not derive a variable's name from its key path: it looks up each known key's
103+ declared variable names. ` DD_PROXY_HTTP ` reaches ` proxy.http ` while ` DD_DOGSTATSD_PORT ` reaches the
104+ flat ` dogstatsd_port ` , and nothing in either name marks the nesting boundary. No separator
105+ convention can reproduce this, so both configuration paths read the environment through the
106+ generated tables instead:
107+
108+ - the typed path via ` apply_datadog_env ` plus the Saluki-only reader, and
109+ - the by-key path via ` EnvironmentProvider ` , a Figment provider wrapping those same readers.
110+
111+ ** Every source therefore delivers the Agent's canonical shape.** A struct deserialized from
112+ ` GenericConfiguration ` ** MUST** read that shape. Do not add a ` #[serde(rename)] ` or ` #[serde(alias)] `
113+ that maps a nested key onto a flattened spelling, and do not reintroduce a key-alias or
114+ environment-remapping table. Those existed once, only ran on file load, and so never applied to the
115+ Datadog Agent's configuration stream — which is the authority in the Core Agent deployment.
116+
117+ Reserve ` #[serde(flatten)] ` for a struct that genuinely groups several * top-level* Agent keys (for
118+ example, the forwarder's ` forwarder_* ` retry settings). Name a Rust field after its canonical
119+ section rather than renaming it onto one.
120+
96121## Saluki-only values
97122
98123Values absent from the Datadog schema reach ` SalukiConfiguration ` through the ` SalukiOnly ` source
@@ -107,16 +132,16 @@ Keep the authoritative Datadog path and delete the duplicate Saluki-only path.
107132
108133Exactly one layer owns each default:
109134
110- | Source class | Model type | Default owner | Translation behavior |
111- | --------------| -------------| ---------------------------------------| ----------------------------------|
112- | Saluki-only | ` Option<T> ` | No default | ` seed ` preserves ` None ` |
113- | Saluki-only | ` T ` | One declaration beside the model type | ` seed ` assigns configured values |
114- | Witnessed | ` T ` | Generated Datadog schema default | ` drive ` always writes it |
115- | Witnessed | ` Option<T> ` | No default | ` drive ` preserves ` None ` |
135+ | Source class | Model type | Default owner | Translation behavior |
136+ | --------------| -------------| ------------------------------------------------ | - ----------------------------------|
137+ | Saluki-only | ` Option<T> ` | No default | ` seed ` preserves ` None ` |
138+ | Saluki-only | ` T ` | ` agent-data-plane-config/src/defaults.rs ` | ` seed ` assigns the resolved value |
139+ | Witnessed | ` T ` | Generated Datadog schema default | ` drive ` always writes it |
140+ | Witnessed | ` Option<T> ` | No default | ` drive ` preserves ` None ` |
116141
117- For a Saluki-only default, use ` #[serde(default = "...")] ` with a nearby constant or function. If
118- the component requires a value, model ` T ` ; use ` Option<T> ` only when absence is meaningful to the
119- component, not to defer its default.
142+ Define each Saluki-only default once in ` lib/agent-data-plane-config/src/defaults.rs ` ; source and
143+ model defaults must reference that definition rather than restating its value. If the component
144+ requires a value, model ` T ` ; use ` Option<T> ` only when absence is meaningful, not to defer a default.
120145
121146Push source parsing, defaults, and input validation to the configuration boundary. Components keep
122147only validation that is truly business logic.
@@ -158,9 +183,14 @@ the witnessed model.
158183
1591841 . Verify that the key is absent from ` schema_overlay.yaml ` .
1601852 . Add its destination to the correct ` SalukiConfiguration ` slice.
161- 3 . Add the exact source hierarchy and a reliable parsing type to ` SalukiOnly ` .
162- 4 . Add one ` seed ` assignment to the destination.
163- 5 . (legacy): Keep ` SALUKI_KEYS ` consistent with the source key, type, and default.
186+ 3 . If it has a default, define it once in ` agent-data-plane-config/src/defaults.rs ` and reference it
187+ from the model and source defaults.
188+ 4 . Add the exact source hierarchy and a reliable parsing type to ` SalukiOnly ` . A ** nested** key
189+ * requires* this even when its only consumer reads the by-key view: the Saluki-only environment
190+ reader discovers its paths from ` SalukiOnly ` , and it is the sole source that places ` DD_FOO_BAR `
191+ at ` foo.bar ` . Without a field, the key is silently unreachable from the environment.
192+ 5 . Add one ` seed ` assignment to the destination.
193+ 6 . (legacy): Keep ` SALUKI_KEYS ` consistent with the source key, type, and default.
164194
165195### Migrate a raw consumer
166196
@@ -174,12 +204,17 @@ the witnessed model.
1742045 . Add any missing model, witness, or seed path with the workflows above.
1752056 . Change static construction to accept borrowed typed slices. For dynamic behavior, pass a narrow
176206 ` Live<T> ` and rebuild the reactive state after ` changed() ` .
177- 7 . Remove source serde, Datadog key names, raw-map access, key watches, parsing, and configuration
178- defaults from the component.
207+ 7 . Remove source serde, Datadog key names, raw-map access, key watches, parsing, configuration
208+ defaults, and code made unused by the cutover. ` #[allow(dead_code)] ` is not an acceptable way to
209+ retain migration residue.
1792108 . Update topology call sites and tests. Preserve behavior tests using typed inputs; remove tests
180211 only when they tested legacy deserialization and nothing else.
212+ - Do * not* rename ` from_configuration ` . Just change its signature to take typed configuration.
1812139 . Remove the component's ` run_config_smoke_tests ` invocation once it no longer deserializes from
182- ` GenericConfiguration ` . Keep ` used_by ` ; it drives legacy smoke-test codegen.
214+ ` GenericConfiguration ` . Replace migrated structs in the ` used_by ` field with
215+ ` TYPED_CONFIG_SYSTEM ` .
216+ 10 . Higher risk cutovers should be tested with correctness or integration tests that exercise the
217+ affected configurations.
183218
184219A cutover should be behaviorally transparent. If the old behavior conflicts with the source schema
185220or typed-system invariants, surface the conflict rather than silently choosing one.
0 commit comments