Found during review of #92 (pre-existing, not introduced by it).
configKey builds a config var as COMPOSER_<address segments>_<owner>_<name>, joining on _. validateName only checks length, so a node name may itself contain _. That makes the mapping from address to key non-injective: a node at a.b_c and a node at a.b.c generate the identical key, and whichever env var is written last wins.
Node/param names elsewhere already reject _ for exactly this reason — ADR-0029 records it for secret slots ("secret slot names may not contain _"; a slot may not collide with a same-named param because they would derive the same key). The same constraint is not enforced on node names feeding the address segments.
Consequence: a service silently reads another service’s value — no error, no warning, at deploy or at boot.
Suggested shape: reject _ in node names at authoring (the same rule secret slots already carry), or escape the segments when building the key. Authoring-time rejection matches the existing precedent and fails loudly.
🤖 Generated with Claude Code
Found during review of #92 (pre-existing, not introduced by it).
configKeybuilds a config var asCOMPOSER_<address segments>_<owner>_<name>, joining on_.validateNameonly checks length, so a node name may itself contain_. That makes the mapping from address to key non-injective: a node ata.b_cand a node ata.b.cgenerate the identical key, and whichever env var is written last wins.Node/param names elsewhere already reject
_for exactly this reason — ADR-0029 records it for secret slots ("secret slot names may not contain_"; a slot may not collide with a same-named param because they would derive the same key). The same constraint is not enforced on node names feeding the address segments.Consequence: a service silently reads another service’s value — no error, no warning, at deploy or at boot.
Suggested shape: reject
_in node names at authoring (the same rule secret slots already carry), or escape the segments when building the key. Authoring-time rejection matches the existing precedent and fails loudly.🤖 Generated with Claude Code