Registry stack documentation: machine-readable Markdown.
Index of all pages: https://docs.registrystack.org/llms.txt
Full corpus: https://docs.registrystack.org/llms-full.txt

# Configuration files

> How Registry Stack configuration files are written and read, how to read a configuration diagnostic, and which command checks each file before you deploy it.

import ConfigurationFormatsTable from '../../../components/ConfigurationFormatsTable.astro';
import formats from '../../../data/generated/configuration-formats.json';

Every Registry Stack product reads YAML configuration files. A project author writes authored
files, such as a registry project or a Casework policy, and an operator writes a runtime file for
each deployment. This reference covers how a file starts, how a runtime reads its configuration
file, how to read a diagnostic, and which command checks each file before you deploy it.
[Formats by product](#formats-by-product) lists every file a product reads and links to the
reference for its keys.

## Where the shared rules apply

One shared reader reads the runtime configuration file of each of these programs:

- Base Registry Engine (BReg), `BRegRuntimeConfig`
- Evidence Gateway, `EvidenceRuntimeConfig`
- Registry Casework, `CaseworkRuntimeConfig`
- Registry Scheduling, `SchedulingRuntimeConfig`
- Registry Render, `RenderRuntimeConfig`
- Registry Discovery, `DiscoveryRuntimeConfig`



The BReg citizen gateway, `breg-mcp`, reads `BRegMcpRuntimeConfig` through the same reader, and
the citizen review page, `breg-review`, reads `BRegReviewRuntimeConfig`.





Registry Messaging reads `MessagingRuntimeConfig` through the same reader.



The shared reader is the `registry-platform-yaml` library. Every product's check command reads
its authored files through it, as the runtimes above read their runtime files, so one YAML subset,
one value table, one header check, and one diagnostic format apply to every file on this page that
is not marked External format or Exchange model; those files follow the grammar of the project that
owns them, as [formats by product](#formats-by-product) explains. Two
rules belong to runtime files alone: the absolute path in
[how a runtime reads its file](#how-a-runtime-reads-its-file), and `${...}` substitution in
[values from the environment](#values-from-the-environment). The reference linked for each format
describes its keys.

{/* Evidence: crates/registry-platform-config/src/loader.rs, RuntimeConfigLoader;
    crates/registry-breg/src/runtime_config.rs, RuntimeConfigLoader;
    crates/registry-evidence/src/config.rs, RuntimeConfigLoader;
    crates/registry-casework/src/config.rs, RuntimeConfigLoader;
    crates/registry-scheduling/src/config.rs, RuntimeConfigLoader;
    crates/registry-render/src/runtime.rs, RuntimeConfigLoader;
    crates/registry-discovery/src/startup.rs, RuntimeConfigLoader;
    crates/registry-caseworkctl/src/project.rs, serde_norway;
    crates/registry-render/src/manifest.rs, serde_norway. */}

## How a file starts

A file whose format carries a header starts with `apiVersion` and `kind`, two plain strings at the
top level:

```yaml
apiVersion: id.registrystack.org/formats/discovery/runtime/v1alpha1
kind: DiscoveryRuntimeConfig
```

`apiVersion` names the format and its version; `kind` names the document. A runtime checks both
before it decodes the rest of the file, and refuses a pair it does not read. Write both in the file
as plain text: neither can come from an environment variable. The Header column under
[formats by product](#formats-by-product) gives each format's pair. A format marked No apiVersion
or No kind does not carry that member.

## How a runtime reads its file

A runtime reads its configuration file only from an absolute path with no `.` or `..` segment and
no symbolic link anywhere along it, and only when that path names a regular file. Every file the reader reads, authored or runtime, is at most 1 MiB
and is one YAML document in a strict subset of YAML 1.2, in UTF-8, nested at most 128 levels
deep. Write block mappings and lists, indent with spaces, and add comments wherever they help. The
reader refuses these constructs, each with its own code:

| Written | Refused with |
| --- | --- |
| A tab in indentation | `yaml.tab-indentation` |
| The same key twice in one mapping | `yaml.duplicate-key` |
| An anchor (`&name`) or an alias (`*name`) | `yaml.anchor`, `yaml.alias` |
| A merge key (`<<`) | `yaml.merge-key` |
| An explicit tag, such as `!!str` or `!custom` | `yaml.tag` |
| A mapping key that is not a string | `yaml.non-string-key` |
| A second document after `---` | `yaml.multiple-documents` |

A runtime file cannot reuse a block it already wrote: repeat the values instead. A key that starts
with `x-` is not an extension point. The reader refuses it as an unknown key and asks you to write
the note as a comment.

{/* Evidence: crates/registry-platform-config/src/loader.rs, RuntimeConfigLoader and
    DEFAULT_MAX_RUNTIME_CONFIG_BYTES;
    crates/registry-breg/src/runtime_config.rs, runtime_config_loader();
    crates/registry-platform-yaml/src/document.rs, MAXIMUM_DOCUMENT_BYTES;
    crates/registry-platform-yaml/src/structure.rs, MAXIMUM_DEPTH;
    crates/registry-platform-yaml/src/messages.rs, CODES and extension_key(). */}

### How an unquoted value is read

The reader resolves an unquoted value by one table, the same in every runtime:

| Unquoted value | Read as |
| --- | --- |
| `null`, `Null`, `NULL`, `~`, or nothing after the colon | Null |
| `true`, `True`, `TRUE`, `false`, `False`, `FALSE` | Boolean |
| Digits with an optional sign and no leading zero: `0`, `42`, `-7` | Integer |
| The same with a fraction or an exponent: `1.5`, `-0.25`, `1e3` | Number |
| `0123`, `01.5`, `.5`, `5.`, `0x1F`, `0o17`, `0b101`, `.inf`, `.nan` | Refused with `yaml.ambiguous-number` |
| Anything else, including `yes`, `no`, `on`, `off`, `1_000`, `1:30`, and `09:00` | Text |

A quoted value and a block value (`|` or `>`) are always text. So `yes` and `on` never mean true,
and a value such as `09:00` stays the text you wrote.

A member keeps the type its format gives it. An integer member refuses `1_000` and refuses a quoted
`"100"`, and a text member refuses an unquoted number or boolean until you quote it. With
`maximumResultRecords: "100"` in a Discovery runtime file, the reader reports:

```text
error[config.expected-integer] runtime.yaml:11:25 /limits/maximumResultRecords
  expected a whole number from 1 to 10000; quoted values are text
  next: Remove the quotes.
```

A message about a whole number states both bounds: the ones the format declares, as here, or else the
range of the member's type.

{/* Evidence: crates/registry-platform-yaml/src/scalar.rs, resolve_plain();
    crates/registry-platform-yaml/src/messages.rs, CODES, whole_number(), and expected_integer(). */}

### Absent and null

Leaving a key out and writing it with no value are different. An optional key you leave out takes
its default. A key with nothing after the colon, or with `null` or `~`, is null, and no member
accepts null. In a Scheduling runtime file, `authentication.oidc.scopeClaim` is optional and
defaults to `registry_scopes`. With `scopeClaim:` on a line of its own, the reader reports it
under `schedulingctl check <project> --runtime-config runtime.yaml`:

```text
error[config.null-value] runtime.yaml:79:5 /authentication/oidc/scopeClaim
  null is never a value; an empty value after a key is null too
  next: Give a value, or remove the key if the member is optional.
```

## Values from the environment

A runtime file can take part of a text value from the runtime's environment while it is read:

| Expression | Result |
| --- | --- |
| `${NAME}` | The value of `NAME`; refused when `NAME` is unset or empty |
| `${NAME:-fallback}` | The value of `NAME`, or `fallback` when `NAME` is unset or empty |
| `${NAME:?message}` | The value of `NAME`; refused when `NAME` is unset or empty, naming `NAME` and withholding the message |

`NAME` starts with a letter or `_` and continues with letters, digits, or `_`. An expression can
sit inside a longer value, such as `${PACKAGE_ROOT}/current`.

- A substituted value is text. It fills a text member and never an integer or boolean member; write
  numbers and booleans in the file.
- Substitution is refused in keys, in `apiVersion` and `kind`, in every member whose key ends in
  `Ref` or `Refs` and everything nested in it, and everywhere under `secretProviders`.
- There is no escape syntax. `${` followed by a letter, a digit, `_`, or `}` starts an expression,
  and any other `${` is text. Substitution is a single pass, so a value that needs a literal
  expression can come from an environment variable that holds it.
- A diagnostic about a substituted value points at the expression as you wrote it, and names the
  variable but never its value.

A check command fills expressions only when you pass `--environment`. With `DISCOVERY_PACKAGE_ROOT`
unset, `discoveryctl check --runtime-config runtime.yaml --environment` refuses
`root: ${DISCOVERY_PACKAGE_ROOT}`:

```text
error[config.substitution] runtime.yaml:6:9 /package/root
  the environment variable DISCOVERY_PACKAGE_ROOT is unset or empty
  next: Set DISCOVERY_PACKAGE_ROOT in the runtime's environment, or write a fallback as ${DISCOVERY_PACKAGE_ROOT:-fallback}.
```

With `root: ${DISCOVERY_PACKAGE_ROOT:?set the package root}` and the variable unset, the refusal
names the variable and says the message you wrote is withheld, since a message is configured text
that can end up in a log:

```text
error[config.substitution] runtime.yaml:6:9 /package/root
  the environment variable DISCOVERY_PACKAGE_ROOT is unset or empty; the message written for it is withheld
  next: Set DISCOVERY_PACKAGE_ROOT in the runtime's environment.
```

Substitution is for ordinary settings. A secret goes in a reference member, a key ending in `Ref`,
as `secret:env/<NAME>` or `secret:file/<name>`, and the runtime resolves it through the providers
its file declares under `secretProviders`. The
[secret reference grammar](../environment-variables/#secret-references) gives the accepted names.

An authored file is never substituted. A check command refuses a `${...}` expression in one with
`config.substitution-not-allowed`, so write the value itself.

{/* Evidence: crates/registry-platform-config/src/loader.rs, RuntimeConfigLoader and
    reject_environment_expressions_in_authored_yaml();
    crates/registry-platform-config/src/lib.rs, resolve_config_env_expression() and valid_env_key();
    crates/registry-platform-config/src/secrets.rs, SecretReference;
    crates/registry-breg/src/contract.rs, reject_environment_expressions_in_authored_yaml;
    crates/registry-caseworkctl/src/project.rs, reject_environment_expressions_in_authored_yaml;
    crates/registry-render/src/manifest.rs, reject_environment_expressions_in_authored_yaml. */}

## Read a diagnostic

A check command, and a runtime that refuses its file at startup, report diagnostics, one per
problem. Take a Discovery runtime file whose last line misspells `logLevel`:

```yaml
apiVersion: id.registrystack.org/formats/discovery/runtime/v1alpha1
kind: DiscoveryRuntimeConfig
listener:
  bind: 127.0.0.1:38080
  requestTimeoutMilliseconds: 10000
package:
  root: /srv/discovery/current
limits:
  maximumRequestBytes: 65536
  maximumResponseBytes: 1048576
  maximumResultRecords: 100
  maximumResultAlternatives: 100
  shutdownGraceMilliseconds: 10000
logLevl: info
```

`discoveryctl check --runtime-config runtime.yaml` reports two problems and a summary line:

```text
discoveryctl check refused the input.
error[config.missing-key] runtime.yaml:1:1
  the required member `logLevel` is missing
  next: Add `logLevel`.
error[config.unknown-key] runtime.yaml:14:1 /logLevl
  `logLevl` is not a member of this mapping
  next: Rename `logLevl` to `logLevel`, or remove it.
2 errors, 0 warnings in 1 file
```

Each diagnostic has the same parts:

| Part | Example | Meaning |
| --- | --- | --- |
| Severity | `error` | `error` refuses the file; `warning` reports a problem the runtime still accepts |
| Code | `config.unknown-key` | The problem, by a stable name |
| Position | `runtime.yaml:14:1` | File, line, and column, counted from 1 |
| Pointer | `/logLevl` | Where the key sits in the document, as an RFC 6901 JSON Pointer; absent when the problem is at the root |
| Message | `` `logLevl` is not a member of this mapping `` | What is wrong |
| `next:` | ``Rename `logLevl` to `logLevel`, or remove it.`` | The fix to make |
| `note:` | `runtime.yaml:3:1 /listener the first definition` | A second place that explains the problem, when there is one |

The examples on this page name the file `runtime.yaml`. A runtime names the file by the path you
gave it. A message names keys, accepted values, and bounds, and never repeats a value read from
the file or the environment.

A duplicate key shows a `note:` line that points at the first definition:

```text
error[yaml.duplicate-key] runtime.yaml:15:1 /listener
  the key is already defined in this mapping
  next: Keep one definition of the key.
  note: runtime.yaml:3:1 /listener the first definition
```

A check finds problems in passes. The first pass reads the file's structure (YAML syntax and the
header) and reports every structural problem together; a file with one is not decoded. The second
pass decodes the file, reports every unknown and removed key it finds, and stops at the first other
problem in a document. The third pass applies the semantic checks, and runs only on a file that
decoded without error. A check therefore reports every problem it finds in a pass, not every
problem the file has. Fix what it reports and run the check again: a later pass can report more.
It shows at most 100 diagnostics for one file, then one `config.too-many-problems` diagnostic that
counts the rest.

{/* Evidence: crates/registry-platform-yaml/src/diagnostic.rs, Diagnostic, Severity, and
    render_human();
    crates/registry-platform-config/src/loader.rs, RuntimeConfigLoader;
    crates/registry-discovery/src/startup.rs, RUNTIME_KIND. */}

### Codes

A reader code has two parts. `yaml.` codes concern the YAML subset, and `config.` codes concern
the header, keys, and values. A code with three parts, such as `platform.runtime-config.path`,
comes from the runtime loader or the product rather than the reader. These are the reader codes a
runtime file most often meets:

| Code | Meaning |
| --- | --- |
| `config.missing-envelope` | The document is empty, or its top-level mapping lacks `apiVersion` or `kind` |
| `config.wrong-kind` | `kind` is not one the reader accepts |
| `config.unsupported-api-version` | `apiVersion` is not one the reader accepts for this kind |
| `config.retired-api-version` | `apiVersion` is one an earlier release wrote; `next:` names the current one |
| `config.unknown-key` | A key is not a member of its mapping |
| `config.removed-key` | A key was removed or renamed; `next:` names what to write instead |
| `config.missing-key` | A required member is absent |
| `config.unknown-variant` | A value is not one of the accepted names; the message lists them |
| `config.null-value` | A member is null |
| `config.invalid-value` | A value has the right type but a product rule refuses it; the message states what is accepted |
| `config.expected-integer` | An integer member holds something other than an integer |
| `config.expected-string` | A text member holds something other than text |
| `config.substitution` | A `${...}` expression cannot be filled |
| `config.substitution-not-allowed` | A `${...}` expression is written where substitution is refused |
| `config.too-many-problems` | The file has more problems than the reader shows; this last diagnostic counts the rest |
| `yaml.duplicate-key` | A mapping holds the same key twice |
| `yaml.ambiguous-number` | An unquoted value looks like a number but is not a plain decimal |
| `yaml.invalid-escape` | A backslash in a double-quoted value starts an escape sequence YAML does not define |
| `yaml.text-after-quote` | Text follows the closing quote of a quoted value |
| `yaml.control-character` | A text value holds a control character other than tab, line feed, or carriage return |

`CODES` in `crates/registry-platform-yaml/src/messages.rs` lists every reader code with its meaning.
The reader's one warning, `config.deprecated-api-version`, reports an `apiVersion` the runtime still reads
and names the version to change to.

For an unknown key, `next:` proposes the closest accepted key when one differs only in letter
case, or by at most two edits (one edit for an accepted key of four characters or fewer, counting
a swap of two neighbouring characters as one edit). Otherwise it lists the accepted keys, once per
mapping: a later unknown key in the same mapping points at the line that lists them.

The same rule applies to accepted values. With `logLevel: inf` in the Discovery runtime file, the
reader reports:

```text
error[config.unknown-variant] runtime.yaml:14:11 /logLevel
  expected one of `error`, `warn`, `info`
  next: Use `info`, the closest accepted value.
```

With `logLevel: Info`, `next:` reads ``Use `info`; letter case matters.`` instead. Otherwise it
lists the accepted values. A value filled from the environment gets the list and no proposal, since
a proposal would reveal part of the value.

A few blocks are exceptions, such as `audit` in the Casework runtime file. These blocks are decoded
outside the reader's key checks, so a check stops at the first unknown key in them, and `next:` asks
you to remove it without proposing or listing accepted keys.

The code names the problem. The message wording is human-readable output, which
[API stability](../api-stability/#outside-the-promise) places outside its promise.

{/* Evidence: crates/registry-platform-yaml/src/messages.rs, CODES, unknown_key(), closest(),
    suggest(), unknown_variant(), too_many_problems(), and deprecated_api_version();
    crates/registry-platform-yaml/src/de.rs, serde_unknown_field();
    crates/registry-casework/src/config.rs, AuditConfig;
    crates/registry-platform-config/src/loader.rs, RuntimeConfigError. */}

### Fix a removed key, a retired version, or an empty list

A key that a format used to accept is refused with a sentence that says what to write instead. A
Discovery runtime file that still says `address` under `listener` is refused:

```text
discoveryctl check refused the input.
error[config.missing-key] runtime.yaml:3:1 /listener
  the required member `bind` is missing
  next: Add `bind`.
error[config.removed-key] runtime.yaml:4:3 /listener/address
  `address` is no longer accepted
  next: Declare listener.bind instead.
2 errors, 0 warnings in 1 file
```

Rename the key, and both problems go away. A header that carries a retired `apiVersion` is refused
the same way, with the current one named. A Scheduling runtime file that starts with the earlier
`registry.registrystack.org/scheduling-runtime/v1alpha1` is refused by
`schedulingctl check <project> --runtime-config runtime.yaml`:

```text
error[config.retired-api-version] runtime.yaml:17:13 /apiVersion
  apiVersion `registry.registrystack.org/scheduling-runtime/v1alpha1` is retired; the current apiVersion is `id.registrystack.org/formats/scheduling/runtime/v1alpha1`
  next: Write apiVersion: id.registrystack.org/formats/scheduling/runtime/v1alpha1.
1 error, 0 warnings in 5 files
```

An empty list never states an open choice. Where a member can be open, you write the word
`unrestricted` in place of the list, and an empty list is refused with that fix. With
`requiredScopes: []` in a Base Registry Engine access profile,
`bregctl check my-registry` reports:

```text
bregctl check refused the project.
error[config.invalid-value] my-registry/registry.yaml:58:21 /accessProfiles/0/requiredScopes
  expected unrestricted, or a list of at least one scope
  next: List the scopes a token must carry, or write unrestricted to require none.
1 error, 0 warnings in 2 files
```

{/* Evidence: crates/registry-discovery/src/runtime_config.rs, REMOVED_RUNTIME_KEYS;
    crates/registry-scheduling/src/config.rs, SCHEDULING_RETIRED_RUNTIME_API_VERSIONS;
    crates/registry-breg/src/contract/sentinel.rs, Scopes. */}

## Check a file before you deploy it

The Check command column under [formats by product](#formats-by-product) names the command that
checks each format without starting the service. Run it in your pipeline before you deploy, against
the project directory or runtime file it names. A format marked None has no check command of its
own; the program that reads it refuses it when it starts.

A check command that takes a project takes the project directory, not a file inside it (`discoveryctl`
names it with `--project`), and a
`--runtime-config` argument names the runtime file. Both accept a relative or an absolute path;
`bregctl check` also refuses a project path with a `..` component, so name the directory from its
parent or give its absolute path. `bregctl check reg`, `bregctl check "$PWD/reg"`,
`schedulingctl check scheduling`, and `schedulingctl check scheduling --runtime-config
scheduling/runtime.yaml` all run. A command that goes beyond an offline check, such as `bregctl
doctor`, may require an absolute runtime file; its reference says so.

Every check command reads its files through the shared reader, applies the rules the runtime would
apply that need no network or database, and reads no secret. It reports in two forms and ends with
an exit code.

**Human output** is the default. It prints a line naming the command's verdict, each diagnostic as
described in [read a diagnostic](#read-a-diagnostic), and a summary line that counts errors,
warnings, and files checked. A file with no problem prints only the summary:

```text
0 errors, 0 warnings in 1 file
```

**JSON output**, selected by `--format json`, prints one report on standard output. A tool reads
`diagnostics`, whose members mirror the human line: `severity`, `code`, `artifact`, `path`,
`message`, `suggestedAction`, `source` (`file`, `line`, `column`), and `related` when a second
place explains the problem. `discoveryctl check --runtime-config runtime.yaml --format json` on the
misspelled file above prints:

```json
{
  "ok": false,
  "command": "check",
  "status": "domain-refusal",
  "apiVersion": "id.registrystack.org/formats/discovery/ctl-report/v1alpha1",
  "diagnostics": [
    {
      "artifact": "DiscoveryRuntimeConfig",
      "code": "config.missing-key",
      "message": "the required member `logLevel` is missing",
      "path": "",
      "severity": "error",
      "source": {
        "column": 1,
        "file": "runtime.yaml",
        "line": 1
      },
      "suggestedAction": "Add `logLevel`."
    },
    {
      "artifact": "DiscoveryRuntimeConfig",
      "code": "config.unknown-key",
      "message": "`logLevl` is not a member of this mapping",
      "path": "/logLevl",
      "severity": "error",
      "source": {
        "column": 1,
        "file": "runtime.yaml",
        "line": 14
      },
      "suggestedAction": "Rename `logLevl` to `logLevel`, or remove it."
    }
  ],
  "errors": 2,
  "filesChecked": 1,
  "kind": "DiscoveryCtlReport",
  "warnings": 0
}
```

The report envelope (`ok`, `command`, `status`, `kind`, `apiVersion`, the counts) differs by
product; the diagnostics inside it do not. `path` is empty when the problem concerns the whole
document.

**Exit codes** are the same for every check command:

| Exit code | Meaning |
| --- | --- |
| 0 | No error was reported; warnings are allowed |
| 1 | The input was refused |
| 2 | The command line was wrong |
| 3 | Something the command depends on was unavailable, such as a file it could not read |

**Warnings** do not fail a check by default. A check reports a warning for a file it accepts but
that probably does not say what its author meant.



A Registry Messaging project whose access profile lists `requesterClients: ["*"]` passes with a
warning, since the item names one entry and matches nothing else:

```text
warning[messaging.project.wildcard-spelled-item] ./messaging.yaml:43:24 /accessProfiles/0/requesterClients/0
  The item is spelled like a wildcard but names one entry: `*` and `unrestricted` match nothing else in this list.
  next: Name the entry the profile is for.
0 errors, 1 warning in 18 files
```

{/* Evidence: crates/registry-messaging-core/src/finding.rs, WildcardSpelledItem. */}



Add `--deny-warnings` to make a reported warning exit 1, so a pipeline fails on it.

For a runtime file, `--environment` fills `${NAME}` expressions from the process environment and
checks the values they produce. Without it, each expression is checked by syntax and position only,
and no secret is read. `bregctl doctor --runtime-config <absolute-file>` goes further than a check:
it also connects to the database and the other dependencies the file names, so it is a deployment
preflight rather than an offline check.

Without `--environment`, the check reads the rest of the file by putting a placeholder in place of
each value that holds an expression. When a member refuses its placeholder, the check stops there
and reports the warning `platform.runtime-config.check-incomplete` at that member. The members
after it were not checked, so run the check again with `--environment`.

{/* Evidence: products/platform/config-formats.yaml;
    crates/registry-platform-config/src/offline.rs, check_offline() and INCOMPLETE_CODE;
    crates/registry-discoveryctl/src/lib.rs, deny_warnings;
    crates/registry-platform-yaml/src/diagnostic.rs, render_human() and to_json_value();
    crates/registry-bregctl/src/lib.rs, DoctorArgs;
    crates/registry-bregctl/src/doctor.rs, run(). */}

### Check as you type

A format with a Schema `$id` has a JSON Schema your editor can apply. The `$id` identifies the
schema; it is not an address the project serves. Copy the schema file from its Source copy path,
at the release you deploy, into your project, then map it to your files. The
[Base Registry Engine projects](../evidencectl/#base-registry-engine-projects) section of the
`evidencectl` reference shows the mapping in `.vscode/settings.json`. An editor that uses
`yaml-language-server` also reads a schema named in a comment at the top of a file:

```yaml
# yaml-language-server: $schema=./schemas/runtime.schema.json
apiVersion: id.registrystack.org/formats/breg/runtime/v1alpha1
kind: BRegRuntimeConfig
```

The reader treats that line as a comment. For an Evidence Gateway authoring project,
`evidencectl tooling editor <project>` writes the mappings for you, as
[editor support](../evidencectl/#editor-support) describes. A schema catches shape errors as you
type. It does not replace the check command, which also applies rules a schema cannot state, such
as references between files.

{/* Evidence: products/breg/generated/runtime/runtime.schema.json;
    crates/registry-evidencectl/src/tooling_editor.rs, EDITOR_SCHEMA_CATALOG. */}

## Stability

The Stability column gives each format's label in the format registry:

- **Promised**: the format is meant to be covered by
  [API stability and versioning](../api-stability/). The
  [promised products](../api-stability/#promised-products) table there names the configuration
  formats the promise covers; where the two differ, that table holds.
- **Experimental**: shipped, and may change in a minor release, announced with a `BREAKING:` note.
- **Unpromised**: no compatibility promise, for files such as fixtures, test journeys,
  development clients, and the Evidence Gateway authoring files.

Before `v1.0.0`, the [pre-1.0 rule](../api-stability/#versioning) applies to every format:
a minor release may change one, with a `BREAKING:` entry and migration steps in the release note.

{/* Evidence: products/platform/CONFIG-CONVENTIONS.md, Stability;
    products/platform/config-formats.yaml. */}

## Formats by product

Each product lists the files it reads, with the pages that explain their keys:

- **Audience**: Authored for a file a project author writes, Operator for a file an operator writes
  for one deployment.
- **Header**: the `apiVersion` and `kind` the file carries. External format marks a file whose
  grammar another project owns, such as a JSON Schema or an OpenAPI description. Exchange model
  marks a published data model whose member names its specification fixes. Neither carries a
  Registry Stack header.
- **Schema `$id`**: the identifier of the format's JSON Schema, with the repository path of its
  source copy.
- **Check command** and **Stability**: as described in the two previous sections.

Generated files, which no one writes by hand, are left out.

{/* Generated from src/data/generated/configuration-formats.json by
    docs/site/scripts/generate-configuration-formats.mjs, from products/platform/config-formats.yaml.
    Run npm run generate from docs/site. */}

<ConfigurationFormatsTable products={formats.products} />

## Next

- [Base Registry Engine configuration reference](../breg-configuration/) for every BReg key.
- [Evidence Gateway configuration reference](../evidence-configuration/) for every Evidence Gateway
  key.
- [Registry Scheduling configuration reference](../scheduling-configuration/) for every Scheduling key.
- [Environment variable reference](../environment-variables/) for fixed variables and secret
  references.
- [API stability and versioning](../api-stability/) for what a release may change.