Configuration and Validation
Plugin configuration is an operator contract. Keep it portable JSON, make each setting’s effect clear, and reject ambiguous input before Relay changes the runtime. Provider clients, callbacks, file handles, and resolved secret values belong in implementation state rather than in the configuration document.
The runtime group is independent of the request-policy group. When either runtime
setting is enabled, the examples install a small tool-execution wrapper that emits marks
or manages an isolated stack. Disabling request rewriting, including with
requests.break_chain, does not suppress those runtime operations.
The Two Configuration Files Have Different Jobs
The binding APIs use the same canonical document shape as plugins.toml: a document
version, components, and policy. Each component has a kind, an enabled flag, and a
component-local JSON object. Keys stay snake_case in Rust, Python, Node.js, JSON, and
TOML even though Node.js API method names use camelCase.
The following configuration activates every shared feature group and the native-only executor control:
The top-level policy controls document validation that Relay owns. A custom plugin’s
validate hook is responsible for unknown fields, types, ranges, enums, and cross-field
rules inside its own config. Built-in plugins can additionally receive host policy
overrides. Do not assume the top-level unknown_field choice automatically inspects an
arbitrary third-party JSON object; implement and test that behavior in the plugin.
Publish the Component Schema with the Package
The following strict schema is used by the Python worker and native examples. The native
example adds an executor object because only the
typed native SDK owns a Tokio executor.
additionalProperties: false is repeated inside each object so a misspelled control fails
at the exact nesting level where it appears.
Defaults in a schema describe the intended value to tools and readers; JSON Schema does not insert them into the component object. The implementation must merge the same defaults before it validates and registers behavior. Keeping one checked default value beside each typed configuration structure prevents the schema, validator, and runtime from silently interpreting omitted fields differently.
The Rust worker intentionally uses a permissive schema with
additionalProperties: true, then reports unknown keys from its validation hook as
warnings. This demonstrates a warning-based migration policy. Do not copy the strict
schema into that worker unless unknown keys should become activation errors; the schema
and validator must express the same operator contract.
Write Diagnostics for the Operator
Validation should be deterministic and free of registration or lasting I/O. A diagnostic contains a level, stable code, component identity when known, field path when known, and a sentence explaining how to correct the value. Use a warning when activation remains safe and the operator should review the choice. Use an error when the plugin cannot install the promised behavior.
The following diagnostic identifies an unsupported request-policy value and tells the operator which field to correct:
A checked-in JSON Schema gives editors and package validation the same first line of
defense, but it does not replace the validation hook. The hook still owns semantic rules
such as requiring at least one blocked target in enforce mode, checking relationships
between feature groups, or applying a deliberate unknown-field policy.
Store secret references rather than secret values. A plugin can define environment variable names, credential-provider references, or another deployment-specific lookup in its schema, then resolve the secret during registration. Validation can confirm that the reference is well-formed without printing or persisting the resolved value in a diagnostic or runtime report.
Validate Before Activation
Use the following sequence to prevent invalid configuration from changing runtime state:
- Construct the effective plugin document, including any discovered
plugins.tomllayers that production startup uses. - Call the binding or CLI validation path before initialization. Treat the returned report as data and fail deployment when it contains error diagnostics.
- Test missing required fields, wrong types, unsupported enum values, unknown fields,
and invalid cross-field combinations. Repeat one invalid case with
enabled = false; disabled components are still validated. - Initialize only after the effective report is acceptable, then inspect the activation report separately. An unknown enabled kind can still prevent initialization when a permissive policy reported it as a warning.
- Exercise one call for each enabled feature group so configuration controls are tied to observable behavior.
Success means invalid or disabled-invalid input produces stable diagnostics without any registration, while a valid document activates only the requested feature groups. The complete runtime discovery and layering rules remain in Plugin Configuration Files.