Dependencies
Dependencies express conditional relationships between configuration elements. They allow you to define rules such as “if this value exists, that one must also exist” without duplicating logic in application code.
Dependencies are declared using the vr_dependency section list.
They always operate on the presence or absence of values or sections, never on
their actual content.
Important
Default values do not count as “configured” for dependency checks. Only nodes that explicitly exist in the configuration document are considered present.
[client.username]
type: "text"
is_optional: yes
[client.password]
type: "text"
is_optional: yes
*[client.vr_dependency]*
mode: "xnor"
source: "username"
target: "password"
error: "Configure username *and* password, or none of these values"
Rules for Dependencies
Section List Required: Dependencies must be defined using a section list named
vr_dependency.[server.username] type: "text" is_optional: yes [server.password] type: "text" is_optional: yes is_secret: yes *[vr_dependency]* mode: "if" source: "server.hostname" target: "server.ip_address"
Placement and Scope: A
vr_dependencymay appear either:at the document root, or
inside a node-rules definition for a section.
The dependency applies only within the subtree of the section in which it is defined.
[server] type: "section" # ... *[server.vr_dependency]* mode: "xor" source: "hostname" target: "ip_address"
Mode Required: Each dependency must define a
modeentry with a text value. Themodedetermines how the configuration state ofsourceandtargetis interpreted.Validators must support the following modes:
if— If the source side is configured, the target side must also be configured. If the source is not configured, the dependency is considered satisfied.if_not— If the source side is configured, the target side must not be configured.or— At least one side (source or target) must be configured. Both sides may be configured.xor— Exactly one side must be configured. Neither both nor none are allowed.xnor— Either both sides must be configured, or neither.and— Both source and target must be configured.
If you are unsure how a specific mode behaves in edge cases, refer to Mode Logic Explained below. That section defines the exact evaluation matrix and should be treated as normative.
Source and Target Required: Each dependency must define both
sourceandtarget.Each entry may be a single text value or a list of text values.
Values are relative name-paths resolved within the same section.
[client] type: "section" [client.username] type: "text" is_optional: yes [client.password] type: "text" is_optional: yes *[client.vr_dependency]* mode: "if" source: "username" target: "password"
Multiple Values (OR Semantics): If multiple name-paths are listed in
sourceortarget, that side of the dependency is considered configured if any of the listed nodes exists.# ... *[vr_dependency]* mode: "if" source: "api_key", "token" target: "endpoint"
Custom Error Messages: A dependency may define an
errorentry to provide a custom validation message.# ... *[vr_dependency]* mode: "xor" source: "hostname" target: "ip_address" error: "Configure either 'hostname' or 'ip_address', not both."
What “Configured” Means: For dependency evaluation, a node on the
sourceortargetside is considered configured only if it is explicitly present in the configuration document.A node is not considered configured if it exists solely because a
defaultvalue was applied during parsing.In other words, dependencies operate on what the user actually wrote — not on values that were implicitly added by the validator.
[app.a] type: "integer" default: 1
[app] # "app.a" is NOT configured. # The value exists only because of the default.
[app] a: 1 # "app.a" IS configured. # The value is explicitly present in the document, # even if it equals the default.
Valid Source and Target Paths: Each name-path listed in
sourceandtargetmust resolve to a node that has a node-rules definition and whose presence is not already unconditionally required.In other words, the referenced node must be conditionally present. A node is considered conditionally present if at least one element in its effective path:
is marked with
is_optional: yes, ordefines a
defaultvalue.
A path must not:
reference nodes inside individual alternatives, or
reference elements within a section list (such as
vr_entryvalues).
[app.username] type: "text" [app.password] type: "text" default: "" *[vr_dependency]* mode: "if" source: "app.username" # ERROR: app.username is not optional target: "app.password"
[app.ports] type: "SectionList" [app.ports.vr_entry.port] type: "integer" default: 0 [app.interface] type: "text" default: "" *[app.vr_dependency]* mode: "if" source: "app.ports.vr_entry.port" # ERROR: Cannot reference elements of a section list target: "app.interface"
*[app.ports]* type: "Section" [app.ports.start] type: "integer" default: 0 [app.ports.end] type: "integer" default: 65534 *[app.ports]* type: "Integer" default: 80 [app.interface] type: "text" default: "" *[vr_dependency]* mode: "if" source: "app.ports.start" # ERROR: app.ports is defined in alternatives target: "app.interface"
Mode Case Insensitivity and Normalization: Mode identifiers are case-insensitive and are normalized according to the same rules as names in ELCL.
Comparisons are performed on the normalized form of the identifier. For example,
if_not,IF_NOT, andIf Notall refer to the same mode.Validators must apply normalization consistently when resolving the
modevalue.# ... *[vr_dependency]* mode: "If Not" source: "hostname" target: "ip_address" *[vr_dependency]* mode: "if_not" source: "port" target: "protocol"
Mode Logic Explained
When validating a dependency, the validator performs two steps:
Determine whether the source side is configured.
Determine whether the target side is configured.
The selected mode is then applied to these two boolean results.
Importantly, dependencies operate purely on presence. They never inspect or compare actual values.
Testing if Sources and/or Targets are Present
A single name-path in source or target is considered configured if:
the referenced node exists in the original configuration document, and
its presence is not the result of a
defaultvalue.
This distinction is essential.
A node that only exists in the parsed configuration tree because a
default value was applied is not considered configured for
dependency evaluation.
If multiple name-paths are listed in source or target,
that side is considered configured if at least one of the listed
nodes is configured (logical OR semantics).
Consider this simplified example:
*[app.vr_dependency]*
mode: "if"
source: "a", "b"
target: "x", "y"
The following configurations illustrate the four possible situations:
[app] # Neither source nor target are configured.
[app] # Source is configured, but target is not.
a: 1
[app] # Target is configured, but source is not.
x: 1
[app] # Both source and target are configured.
b: 1
y: 1
Four Situations
When evaluating a dependency, the configuration state of the source and target side results in exactly one of four possible situations.
These situations form the logical basis for all dependency modes.
Situation |
Description |
|---|---|
|
Neither source nor target values are configured. |
|
One or more source values are configured, and no target values are configured. |
|
No source values are configured, but one or more target values are configured. |
|
One or more source values are configured, and one or more target values are configured. |
The Dependency Mode Logic
Each dependency mode defines which of the four situations above are considered valid.
A green checkmark indicates that the dependency is satisfied. A red xmark indicates that the dependency is violated and validation fails.
The following table is normative and defines the exact behavior:
Mode ↓ |
|
|
|
|
|---|---|---|---|---|
if |
||||
if_not |
||||
or |
||||
xor |
||||
xnor |
||||
and |
Unsupported Logic Modes and Why They Are Not Supported
You may notice that the logical matrix above is not exhaustive. Several theoretically possible boolean modes are intentionally not supported.
The following modes are omitted by design:
Mode ↓ |
|
|
|
|
|---|---|---|---|---|
none |
||||
nand |
||||
nor |
||||
reverse_if |
These modes are excluded for the following reasons:
none |
This mode would accept all four situations and therefore impose no constraint.
It is functionally equivalent to omitting the |
nand |
While logically valid, it is semantically redundant: the same behavior is already
provided by |
nor |
This mode would only allow the situation where neither side is configured. In practice, this would prohibit configuring any of the referenced nodes, which does not express a dependency but rather a prohibition rule. |
reverse_if |
This would represent the logical inverse direction of |
Examples
Exclusion (XOR)
Exactly one of the two values must be configured:
[server.hostname]
type: "text"
is_optional: yes
[server.ip_address]
type: "text"
is_optional: yes
*[server.vr_dependency]*
mode: "xor"
source: "hostname"
target: "ip_address"
error: "Configure either 'hostname' or 'ip_address'"
Bidirectional Dependency (XNOR)
Either both values must be present, or neither:
[window.x]
type: "integer"
is_optional: yes
[window.y]
type: "integer"
is_optional: yes
*[window.vr_dependency]*
mode: "xnor"
source: "x"
target: "y"
error: "You must either specify both 'x' and 'y' or neither"
Directional Dependency (IF)
A value is required only when a related feature is configured:
[api.media_link]
type: "SectionList"
is_optional: yes
[api.media_link.vr_entry.id]
type: "text"
*[vr_key]*
name: "media_link"
key: "api.media_link.vr_entry.id"
[app.primary_medialink]
type: "text"
key: "media_link"
is_optional: yes
*[vr_dependency]*
mode: "if"
source: "api.media_link"
target: "app.primary_medialink"
error: "You must configure 'primary_medialink' when using this feature"
Version History
Changed in version 1.3.1:
Added the missing
orandandmodes.Clarified that
modeidentifiers are case-insensitive and must be compared using ELCL normalization rules.Refined the wording of all dependency modes to remove ambiguity.
Added a normative section explaining dependency mode logic in detail.
Defined stricter requirements for
sourceandtargetname-paths, including explicit restrictions on section lists and alternative branches.