Feature Specification: Configuration Model and JSON Schema
Created: 2026-09-22
Status: Implemented
Input: Reconstructed from the code at 0bf4ecd36 (core/applications-type.nix, core/lib/schema-for-source.nix, core/lib/json-schema.nix, core/lib/renames-for-source.nix, core/lib/option-title.nix, core/lib/option-selection.nix, core/components/operator/*/default.nix, core/components/operator/operator-facing-options.csv, core/effects/common/submitted-config.nix, core/default-configuration.json, core/render/outputs.nix), pull requests #1219, #1289, #1839 and #1843, commit c58cd36b7, and api/specification.md.
User Scenarios & Testing (mandatory)
User Story 1 - Get the schema of the operator configuration (Priority: P1)
A front-end shows a form for the configuration of a deployment. It gets the
JSON Schema of that configuration from core, and renders the form from the
schema alone. It does not need to know the names of the applications or
their options.
Why this priority: the schema is the contract between core and every
front-end. Without it, a front-end must copy knowledge of the model.
Independent Test: build the frontend-configuration-schema package. The
output is a JSON Schema of draft 2020-12 with one property per operator-facing
application.
Acceptance Scenarios:
- Given the default groups, When the schema is built, Then it is valid draft 2020-12 JSON Schema and has a property for each operator-facing application.
- Given a hosting configuration that fixes a value, When the provider schema (
provider-configuration-schema) is built, Then the fixed field is not in the schema. - Given a new deployment, When a front-end reads the root
defaultof the schema, Then it gets the configuration that the form starts from. - Given an option with a technical abbreviation in its name (for example CIDR), When the schema is built, Then the title of the field uses the correct form of the abbreviation.
- Given the provider schema, When
provider-application-schemasis built, Then it has one schema for each application, and each schema validates a value for its application without the full schema.
User Story 2 - Read the configuration type without the setups (Priority: P1)
A front-end validates a submitted configuration with the same type that the
deployment uses. It evaluates lib.applications-type, which is built from
the components alone, and does not evaluate the setups.
Independent Test: compare the hash of applications-type with the hash of
frontendConfiguration of each deployment method. They are equal.
Acceptance Scenarios:
- Given the components, When
applications-typeis evaluated, Then it is equal to thefrontendConfigurationtype of every deployment method.
User Story 3 - Keep stored configurations valid after a rename (Priority: P2)
An option is renamed. A front-end has configurations stored with the old
name. core publishes a rename table (configuration-renames) of from and to
paths, so the front-end can migrate the stored values.
Independent Test: rename an option with mkRenamedOptionModule in a test
module. The rename table has the entry, and the schema does not show the old
name.
User Story 4 - Validate a value against a schema that an application publishes (Priority: P3)
An application (PeerTube, Mastodon and Pixelfed) publishes a JSON Schema for
its own settings. The component of that application narrows the schema to the
settings that it gives to the operator, and offers the result as its
settings option. core validates values against that schema in Nix, so a
wrong setting fails at evaluation.
Independent Test: validate a valid and an invalid value against a small schema. The first passes, the second fails with a named error.
Acceptance Scenarios:
- Given the schema of an application and a selection of its settings, When the schema is narrowed, Then it has only the selected settings, and a value with a setting that is not selected fails.
- Given a selected name that ends in
*(for exampleDEEPL_*), When the selection is resolved, Then it selects each setting whose name starts with the part before the*. - Given a selected name or pattern that matches no setting, When the selection is resolved, Then the evaluation fails with a named error.
- Given an application schema whose properties carry
x-env(Pixelfed), When the schema is keyed by environment variable, Then keys that read the same variable become one property, and keys with types that do not agree fail.
Edge Cases
- A schema of another dialect, a non-local
$ref, an anchor$ref, or a$refwith siblings: the validator fails with a named error. patternis ECMA-262, and the match uses POSIX extended regular expressions. The validator first restates in POSIX ERE the ECMA-262 escapes that POSIX ERE spells differently (\d,\s,\wand their negations,\n,\r,\t, an escaped punctuation character) and a lazy quantifier. A construct that has no POSIX ERE equivalent, such as a lookahead or\b, makes the evaluation fail with a named error. Lengths count bytes, andformatis not asserted. The test suite records each case of the official suite that fails for these reasons.- Without
--impure,provider-configuration-schemacannot readHOSTING_CONFIGandPIN_EFFECT, and gives the schema without the prune and the collapse. - A submission names only some applications. The named applications replace the baked entries, with
enableas an explicit boolean. The other applications keep the baked entries.
Requirements (mandatory)
Functional Requirements
- FR-001:
coreMUST publish the JSON Schema (draft 2020-12) of the operator configuration, generated from the module options bymodule-schema. - FR-002: The provider schema MUST NOT show fields that the hosting configuration fixes, and MUST NOT show renamed (alias) options.
- FR-003: The schema MUST have a root
default: the configuration a new deployment starts from, with each option at the default that its module declares. - FR-004:
lib.applications-typeMUST be built from the components alone and MUST be equal to thefrontendConfigurationof every deployment method. - FR-005:
coreMUST publish a rename table of the options, so front-ends can migrate stored configurations. - FR-006: The in-Nix validator MUST validate values against a JSON Schema. It MUST NOT convert a schema into Nix options, because keywords such as
patternanddependentRequireddo not have a correct equivalent. - FR-007: The submitted configuration MUST merge with the baked configuration per application, as the edge cases describe.
- FR-008: Front-ends MUST be able to treat the options as opaque: the schema, its titles and its default are enough to render and submit a form.
- FR-009: The
settingsoption of an application component MUST take its schema from the schema that the application publishes, narrowed to the settings that the component selects for the operator.core/components/operator/operator-facing-options.csvMUST record for each setting whether it is for the operator, with the reason. - FR-010:
coreMUST publish the schema of each application, cut from the provider schema (lib.schemaForSource.applicationSchemas, outputprovider-application-schemas), so a consumer does not walk the schema itself.
Key Entities
- Operator configuration: the domain, the contact email and the
applicationsset of one deployment. - Schema: the JSON Schema of the operator configuration, with a root
default. - Operator options audit:
operator-facing-options.csv, one row for each option or setting of an application, with its audience, whether it is only partly for the operator, whether it is a secret, and notes. - Rename table: a list of
{ from, to }option paths. - Default configuration:
core/default-configuration.json, the seed that the setups and the tests start from.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001: A front-end renders the form of all applications with no application names in its code.
- SC-002: The
nix-unit-lib-json-schema,nix-unit-lib-json-schema-suite,nix-unit-lib-renames-for-source,nix-unit-lib-option-title,nix-unit-lib-option-selection,nix-unit-effects-submitted-configandnix-unit-setups-operator-configchecks pass. - SC-003: The validator passes all cases of the official JSON Schema suite, except the cases that the suite test records.
Assumptions
-
api/specification.mddescribes howapiserves this schema. This specification describes only whatcoreproduces. -
The applications that a new deployment starts with are a decision of the front-end (constitution principle II).
core/default-configuration.json, the seed of the setups and the tests, turns off every application.
Known Deviations
- A secret that the operator brings (a row with
sensitiveset toyes, for exampleDEEPL_API_KEY) is a plain form field (#2133). Its value goes into the Nix store and into the stored configuration of the deployment. - A setting with
partialset toyesinoperator-facing-options.csvis given to the operator in full, not only the part that is for the operator (#2132).