Indexes, Keys and References
Indexes allow validators to enforce uniqueness and to create references between different parts of a configuration document.
Indexes are defined using the reserved section list vr_key and can appear
either at the document root or inside a section definition.
Once defined, the values collected by an index can be referenced using the
key constraint (see Key).
This mechanism enables referential integrity within a configuration, for example by ensuring that a reference in one section points to an identifier defined elsewhere.
*[vr_key]*
name: "filter"
key: "filter.vr_entry.identifier"
[filter]
type: "SectionList"
[filter.vr_entry.identifier]
type: "text"
[app.start_filter]
type: "text"
key: "filter"
*[filter]*
identifier: "first"
*[filter]*
identifier: "second"
[app]
start_filter: "first"
Rules for Indexes
Index Creation: An index is created by defining a section list named
vr_key.*[vr_key]* name: "filter" key: "filter.vr_entry.identifier"
Uniqueness: All key values collected in an index must be unique. Validators must report an error if a duplicate key is encountered.
*[filter]* identifier: "one" *[filter]* identifier: "one" # ERROR: Identifier must be unique
Placement: A
vr_keysection list may appear either:at the document root, or
inside a node-rules definition for a section.
If defined inside a section node-rules definition, the index is scoped to each validated instance of that section.
In the following example, the index is defined inside the
app.server.vr_entrydefinition. As a result,idmust be unique within each individualserverentry.Different
serverentries may reuse the sameidvalues, because eachserverinstance has its own independent index scope.[app.server] type: "SectionList" [app.server.vr_entry.connection] type: "SectionList" [app.server.vr_entry.connection.vr_entry.id] type: "text" *[app.server.vr_entry.vr_key]* name: "connection_id" key: "connection.vr_entry.id"
Scope and Visibility: An index is visible only within the subtree in which it is defined. References outside this scope are invalid.
[app.server] type: "SectionList" [app.server.vr_entry.connection] type: "SectionList" [app.server.vr_entry.connection.vr_entry.id] type: "text" *[app.server.vr_entry.vr_key]* name: "connection_id" key: "connection.vr_entry.id" [app.main_connection] type: "text" key: "connection_id" # ERROR: No such index in this scope
Key Field: Each
vr_keyentry must contain akeyfield that specifies one or more values to be indexed.# ... *[vr_key]* key: "filter.vr_entry.identifier"
Optional Name: A
vr_keyentry may define anamefield, which assigns an identifier to the index for use inkeyconstraints.# ... *[vr_key]* name: "filter" key: "filter.vr_entry.identifier"
Case-Sensitivity: An optional
case_sensitivefield may be defined on avr_keyentry to control how indexed values are compared.If
case_sensitiveis set totrue, both uniqueness checks andkeyconstraint comparisons are performed case-sensitively. If omitted or set tofalse, comparisons are case-insensitive.The comparison mode defined by the index applies consistently to:
detection of duplicate key values, and
resolution of references using the
keyconstraint.
*[vr_key]* name: "filter" key: "filter.vr_entry.identifier" case_sensitive: true
Rules for Keys
Text Name-Path Required: Each
keyvalue must be a text string containing a valid name path.# ... *[vr_key]* name: "filter" key: "filter.vr_entry.identifier"
Allowed Value Types: A referenced key must point to either a text or an integer value.
*[vr_key]* key: "blog.vr_entry.created" # ERROR: Must reference text or integer [blog] type: "SectionList" [blog.vr_entry.created] type: "DateTime"
Section List + Value Requirement: Each name-path must resolve to:
a section list,
its
vr_entry, anda value inside each
vr_entryof that section list.
*[vr_key]* name: "filter" key: "app.filter.vr_entry.meta.id" [app.filter] type: "SectionList" [app.filter.vr_entry.meta.id] type: "text"
In this example:
app.filteridentifies the section listvr_entryidentifies the section list’s entriesmeta.ididentifies the value within each entry
No Nested Section Lists: A
keyname-path must not reference a section list that is nested inside another section list.Keys must resolve to values within a single, directly addressed section list.
[app.filters] type: "SectionList" [app.filters.vr_entry.rules] type: "SectionList" [app.filters.vr_entry.rules.vr_entry.id] type: "text" *[vr_key]* key: "app.filters.vr_entry.rules.vr_entry.id" # ERROR: Cannot reference nested section list
Alternatives, Optionality and Version Constraints in Key Paths: If a node-rules definition that is part of a
keyname-path contains version constraints, optionality, or alternatives, index creation becomes conditional on the effective structure present in the validated document.The following rules apply:
If the referenced section list does not exist in the configuration document, the index is created as an empty index.
If a referenced value inside a section list entry does not exist, is not active due to version constraints, or resolves to a value other than
textorinteger, that entry is omitted from the index.Omitted entries are not validated for uniqueness.
[app.filters] type: "SectionList" minimum_version: 2 [app.filters.vr_entry.id] type: "text" *[vr_key]* key: "app.filters.vr_entry.id" # Index is created only if app.filters is active for the current version
[app.filters] type: "SectionList" [app.filters.vr_entry.id] type: "text" minimum_version: 2 *[vr_key]* key: "app.filters.vr_entry.id" # For versions < 2, entries without an active 'id' are ignored by the index
Rules for Index Names
Optional Name: An index may define a
nameentry, which is used bykeyconstraints to reference the index.Naming Rules: Index names must follow the ELCL name rules.
# ... *[vr_key]* name: "%my-name%" # ERROR: Invalid ELCL name # ...
Normalization and Comparison: Index names are normalized and compared according to the ELCL name rules.
Underscores and spaces are equivalent, and comparisons are case-insensitive.
*[vr_key]* name: "filter_index" key: "filter.vr_entry.identifier" [app.start_filter] type: "text" key: "Filter Index" # VALID after normalization
Rules for Multi-Key Indexes
Combination Must Be Unique: If multiple
keyvalues are specified, the combination of their resolved values must be unique across all entries of the referenced section list.Uniqueness is evaluated on the tuple of key values, not on the individual components.
*[vr_key]* key: "server.vr_entry.service", "server.vr_entry.protocol" [server] type: "SectionList" [server.vr_entry.service] type: "text" in: "api", "management" [server.vr_entry.protocol] type: "text" in: "https", "json"
All Keys Must Reference the Same Section List: All name-paths defined in a composite key must resolve to values within the same section list.
Mixing keys from different section lists is not permitted.
*[vr_key]* key: * "server.vr_entry.service" * "client.vr_entry.protocol" # ERROR: Different section lists [server] type: "SectionList" # ... [client] type: "SectionList" # ...
Composite Key Representation: When a multi-key index is referenced as a whole, its values are combined into a single text value separated by commas (
,).The order of the combined values matches the order in which the
keyname-paths are defined.# ... *[vr_key]* key: * "server.vr_entry.service" * "server.vr_entry.protocol"
[server] # Creates the composite key "api,https" service: "api" protocol: "https"
Design Rationale
Although commas could theoretically appear in key values, such cases can be avoided by disallowing commas in fields that are used as keys. This keeps composite key representation simple, deterministic, and efficient.
Referencing Parts of a Composite Key: A multi-key index may be referenced either as a whole or by addressing individual components using the
key[index]syntax.The index is zero-based and refers to the position of the corresponding name-path in the
keydefinition.*[vr_key]* name: "server" key: * "server.vr_entry.service" * "server.vr_entry.protocol" [server.ports] type: "SectionList" [server.ports.vr_entry.protocol] type: "text" key: "server[1]" # References only the protocol component key_error: "No server with this protocol was configured"
Alternatives, Optionality and Version Constraints in Composite Keys: In addition to the rules for single-key indexes, the following applies to multi-key indexes:
If at least one key component of an entry resolves to a valid
textorintegervalue, an index entry is created.Missing or inactive components (due to optionality or version constraints) are substituted with an empty text value (
"") for the purpose of composite key construction.If all key components of an entry are missing, inactive, or invalid, the entry is omitted from the index entirely.
Omitted entries are not validated for uniqueness.
Design Rationale
Multi-key indexes with partially missing components are inherently ambiguous. However, silently omitting such entries would weaken uniqueness guarantees and may hide configuration mistakes.
By inserting empty values for missing components, implementations remain deterministic and consistent across validators.
It is the responsibility of the validation-rules author to enforce stricter guarantees (for example, by requiring all key components to be present) if partial keys are not acceptable.
Version History
Changed in version 1.3.1: Clarified how multi-key indexes behave when optionality or version constraints affect individual key components.
Changed in version 1.3.0: Introduced the optional case_sensitive field for vr_key entries,
defining explicit case-sensitivity semantics for both uniqueness checks
and key constraint comparisons.
Changed in version 1.2.10: Documented the canonical form of key paths including vr_entry when
referencing values inside section lists.
For compatibility, validators may treat list.value as shorthand
for list.vr_entry.value when list resolves to a section list.