{
  "$defs": {
    "policy.cache": {
      "additionalProperties": false,
      "description": "Public response cache policy, declared under `policies.definitions` with `type: cache` and attached by a chain; looked up in the `request` phase. Applies to `GET` and `HEAD` only; its `when` condition is kept (for instance `!has(auth.subject)`). An authenticated cache is declared by the operation (`x-flexway-cache`), never by a policy.",
      "properties": {
        "scope": {
          "default": "public",
          "description": "Cache scope: `public` only (default). `subject` is refused here: it is declared in the operation's `x-flexway-cache`.",
          "enum": [
            "public"
          ],
          "type": "string",
          "x-flexway-description": {
            "en": "Cache scope: `public` only (default). `subject` is refused here: it is declared in the operation's `x-flexway-cache`.",
            "fr": "Portée du cache : `public` seulement (défaut). `subject` est refusé ici : il se déclare dans `x-flexway-cache` de l'opération."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        },
        "ttl": {
          "description": "How long a response is kept, a positive duration (`60s`); required.",
          "not": {
            "pattern": "^(0+(\\.0+)?(ns|us|µs|ms|s|m|h))+$"
          },
          "pattern": "^([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$",
          "type": "string",
          "x-flexway-description": {
            "en": "How long a response is kept, a positive duration (`60s`); required.",
            "fr": "Durée de conservation d'une réponse, durée positive (`60s`) ; obligatoire."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        }
      },
      "required": [
        "ttl"
      ],
      "type": "object",
      "x-flexway-description": {
        "en": "Public response cache policy, declared under `policies.definitions` with `type: cache` and attached by a chain; looked up in the `request` phase. Applies to `GET` and `HEAD` only; its `when` condition is kept (for instance `!has(auth.subject)`). An authenticated cache is declared by the operation (`x-flexway-cache`), never by a policy.",
        "fr": "Politique de cache de réponses publiques, déclarée sous `policies.definitions` avec `type: cache` et attachée par une chaîne ; consultée à la phase `request`. Ne vaut que pour `GET` et `HEAD` ; sa condition `when` est gardée (par exemple `!has(auth.subject)`). Un cache authentifié se déclare par l'opération (`x-flexway-cache`), jamais par une politique."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "1.2.0"
    },
    "policy.cors": {
      "additionalProperties": false,
      "description": "CORS policy, declared under `policies.definitions` with `type: cors`, attached globally, per host, per route or per operation; `request` phase, no `when` condition. Closed by default: an origin not listed is refused.",
      "properties": {
        "allow_credentials": {
          "description": "Allows browser requests with credentials (`Access-Control-Allow-Credentials`). Default `false`; refused with the `*` origin.",
          "type": "boolean",
          "x-flexway-description": {
            "en": "Allows browser requests with credentials (`Access-Control-Allow-Credentials`). Default `false`; refused with the `*` origin.",
            "fr": "Autorise les requêtes du navigateur avec identifiants (`Access-Control-Allow-Credentials`). Défaut `false` ; refusé avec l'origine `*`."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.3.0"
        },
        "allow_headers": {
          "description": "Request headers allowed beside the ones the operation declares. Empty: the operation's own whitelist only.",
          "items": {
            "type": "string"
          },
          "type": "array",
          "x-flexway-description": {
            "en": "Request headers allowed beside the ones the operation declares. Empty: the operation's own whitelist only.",
            "fr": "En-têtes de requête autorisés en plus de ceux que l'opération déclare. Vide : la liste blanche de l'opération seule."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.3.0"
        },
        "allow_methods": {
          "description": "Methods announced in the preflight answer. Empty: the methods the contract declares for the path.",
          "items": {
            "type": "string"
          },
          "type": "array",
          "x-flexway-description": {
            "en": "Methods announced in the preflight answer. Empty: the methods the contract declares for the path.",
            "fr": "Méthodes annoncées en réponse au préliminaire. Vide : les méthodes que le contrat déclare pour le chemin."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.3.0"
        },
        "allow_origins": {
          "description": "Allowed origins (`https://app.example.com`), at least one; `*` admits any origin but never with `allow_credentials`.",
          "items": {
            "type": "string"
          },
          "minItems": 1,
          "type": "array",
          "x-flexway-description": {
            "en": "Allowed origins (`https://app.example.com`), at least one; `*` admits any origin but never with `allow_credentials`.",
            "fr": "Origines autorisées (`https://app.example.com`), au moins une ; `*` admet toute origine mais jamais avec `allow_credentials`."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.3.0"
        },
        "expose_headers": {
          "description": "Response headers the browser lets the script read (`Access-Control-Expose-Headers`).",
          "items": {
            "type": "string"
          },
          "type": "array",
          "x-flexway-description": {
            "en": "Response headers the browser lets the script read (`Access-Control-Expose-Headers`).",
            "fr": "En-têtes de réponse que le navigateur laisse lire au script (`Access-Control-Expose-Headers`)."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.3.0"
        },
        "max_age": {
          "description": "How long the browser keeps the preflight answer (`Access-Control-Max-Age`), a positive duration (`10m`); absent: the header is not sent.",
          "not": {
            "pattern": "^(0+(\\.0+)?(ns|us|µs|ms|s|m|h))+$"
          },
          "pattern": "^([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$",
          "type": "string",
          "x-flexway-description": {
            "en": "How long the browser keeps the preflight answer (`Access-Control-Max-Age`), a positive duration (`10m`); absent: the header is not sent.",
            "fr": "Durée pendant laquelle le navigateur garde la réponse au préliminaire (`Access-Control-Max-Age`), durée positive (`10m`) ; absente : en-tête non envoyé."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.3.0"
        }
      },
      "required": [
        "allow_origins"
      ],
      "type": "object",
      "x-flexway-description": {
        "en": "CORS policy, declared under `policies.definitions` with `type: cors`, attached globally, per host, per route or per operation; `request` phase, no `when` condition. Closed by default: an origin not listed is refused.",
        "fr": "Politique CORS, déclarée sous `policies.definitions` avec `type: cors`, attachée globalement, par hôte, par route ou par opération ; phase `request`, sans condition `when`. Fermée par défaut : une origine non listée est refusée."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "0.3.0"
    },
    "policy.external-call": {
      "additionalProperties": false,
      "description": "Call to a declared service whose answer may refuse the request (`deny`) or set headers (`set_headers`), nothing else; `type: external-call` under `policies.definitions`, `request` phase (default, before the upstream) or `response`. Timeout and `on_failure` are required, the operation's budget is consumed; a simulated request calls nothing.",
      "properties": {
        "deny": {
          "additionalProperties": false,
          "description": "Refuses the request when a condition holds on the external service's answer; `when`, `status` and `code` are all required. At least one of `deny` or `set_headers` is needed.",
          "properties": {
            "code": {
              "description": "Error code of the problem served, `^[a-z][a-z0-9_]{0,63}$`; never one of the gateway's own codes (`forbidden`, `rate_limited`...).",
              "pattern": "^[a-z][a-z0-9_]{0,63}$",
              "type": "string",
              "x-flexway-description": {
                "en": "Error code of the problem served, `^[a-z][a-z0-9_]{0,63}$`; never one of the gateway's own codes (`forbidden`, `rate_limited`...).",
                "fr": "Code d'erreur du problème servi, `^[a-z][a-z0-9_]{0,63}$` ; jamais un code propre à la passerelle (`forbidden`, `rate_limited`...)."
              },
              "x-flexway-reloadable": true,
              "x-flexway-since": "1.2.0"
            },
            "status": {
              "description": "Status of the refusal: a registered 4xx, other than 401, 405, 407 and 416.",
              "maximum": 499,
              "minimum": 400,
              "type": "integer",
              "x-flexway-description": {
                "en": "Status of the refusal: a registered 4xx, other than 401, 405, 407 and 416.",
                "fr": "Statut du refus : un 4xx enregistré, hors 401, 405, 407 et 416."
              },
              "x-flexway-reloadable": true,
              "x-flexway-since": "1.2.0"
            },
            "when": {
              "description": "CEL condition on `response.status` and `response.body` - the external service's answer, never the upstream's.",
              "type": "string",
              "x-flexway-description": {
                "en": "CEL condition on `response.status` and `response.body` - the external service's answer, never the upstream's.",
                "fr": "Condition CEL sur `response.status` et `response.body` - la réponse du service externe, jamais celle de l'amont."
              },
              "x-flexway-reloadable": true,
              "x-flexway-since": "1.2.0"
            }
          },
          "required": [
            "code",
            "status",
            "when"
          ],
          "type": "object",
          "x-flexway-description": {
            "en": "Refuses the request when a condition holds on the external service's answer; `when`, `status` and `code` are all required. At least one of `deny` or `set_headers` is needed.",
            "fr": "Refus de la requête quand une condition tient sur la réponse du service externe ; `when`, `status` et `code` tous obligatoires. Il faut au moins `deny` ou `set_headers`."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        },
        "on_failure": {
          "description": "What happens without a usable answer (timeout, error, open breaker): `fail-open` goes on as if the policy did not apply, `fail-closed` refuses. Required, no default.",
          "enum": [
            "fail-open",
            "fail-closed"
          ],
          "type": "string",
          "x-flexway-description": {
            "en": "What happens without a usable answer (timeout, error, open breaker): `fail-open` goes on as if the policy did not apply, `fail-closed` refuses. Required, no default.",
            "fr": "Ce qui se passe sans réponse exploitable (délai, erreur, coupe-circuit) : `fail-open` continue comme si la politique ne s'appliquait pas, `fail-closed` refuse. Obligatoire, sans défaut."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        },
        "path": {
          "description": "Path called on the upstream, a literal starting with `/`, without `?`, `#`, `{`, `}` or space.",
          "pattern": "^/[^?#{} ]*$",
          "type": "string",
          "x-flexway-description": {
            "en": "Path called on the upstream, a literal starting with `/`, without `?`, `#`, `{`, `}` or space.",
            "fr": "Chemin appelé sur l'amont, littéral commençant par `/`, sans `?`, `#`, `{`, `}` ni espace."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        },
        "send": {
          "additionalProperties": false,
          "description": "What the call sends to the external service; nothing by default.",
          "properties": {
            "body": {
              "description": "Sends the request's body. Refused in the `response` phase, where it is already relayed.",
              "type": "boolean",
              "x-flexway-description": {
                "en": "Sends the request's body. Refused in the `response` phase, where it is already relayed.",
                "fr": "Envoie le corps de la requête. Refusé à la phase `response`, où il est déjà relayé."
              },
              "x-flexway-reloadable": true,
              "x-flexway-since": "1.2.0"
            },
            "headers": {
              "description": "Headers of the request prepared for the upstream to send along (declared headers, the tenant written by the gateway); 32 at most. Never an undeclared header of the caller.",
              "items": {
                "type": "string"
              },
              "maxItems": 32,
              "type": "array",
              "x-flexway-description": {
                "en": "Headers of the request prepared for the upstream to send along (declared headers, the tenant written by the gateway); 32 at most. Never an undeclared header of the caller.",
                "fr": "En-têtes de la requête préparée pour l'amont à transmettre (en-têtes déclarés, tenant écrit par la passerelle) ; 32 au plus. Jamais un en-tête non déclaré de l'appelant."
              },
              "x-flexway-reloadable": true,
              "x-flexway-since": "1.2.0"
            }
          },
          "type": "object",
          "x-flexway-description": {
            "en": "What the call sends to the external service; nothing by default.",
            "fr": "Ce que l'appel envoie au service externe ; rien par défaut."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        },
        "set_headers": {
          "additionalProperties": {
            "type": "string"
          },
          "description": "Headers set from the external answer: name -> CEL expression on `response`; 32 at most, values of 4,096 bytes at most (beyond, the call failed). Protected headers are refused.",
          "type": "object",
          "x-flexway-description": {
            "en": "Headers set from the external answer: name -> CEL expression on `response`; 32 at most, values of 4,096 bytes at most (beyond, the call failed). Protected headers are refused.",
            "fr": "En-têtes posés à partir de la réponse externe : nom -> expression CEL sur `response` ; 32 au plus, valeur de 4 096 octets au plus (au-delà, l'appel a échoué). En-têtes protégés refusés."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        },
        "timeout": {
          "description": "Timeout of the call, a positive duration (`200ms`); required. The call also consumes the operation's budget.",
          "pattern": "^([0-9]+(\\.[0-9]+)?(ns|us|µs|ms|s|m|h))+$",
          "type": "string",
          "x-flexway-description": {
            "en": "Timeout of the call, a positive duration (`200ms`); required. The call also consumes the operation's budget.",
            "fr": "Délai de l'appel, durée positive (`200ms`) ; obligatoire. L'appel consomme aussi le budget de l'opération."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        },
        "upstream": {
          "description": "Upstream called, declared in `upstreams`, over HTTP and without `token_exchange`; required.",
          "type": "string",
          "x-flexway-description": {
            "en": "Upstream called, declared in `upstreams`, over HTTP and without `token_exchange`; required.",
            "fr": "Amont appelé, déclaré dans `upstreams`, en HTTP et sans `token_exchange` ; obligatoire."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        }
      },
      "required": [
        "on_failure",
        "path",
        "timeout",
        "upstream"
      ],
      "type": "object",
      "x-flexway-description": {
        "en": "Call to a declared service whose answer may refuse the request (`deny`) or set headers (`set_headers`), nothing else; `type: external-call` under `policies.definitions`, `request` phase (default, before the upstream) or `response`. Timeout and `on_failure` are required, the operation's budget is consumed; a simulated request calls nothing.",
        "fr": "Appel à un service déclaré dont la réponse peut refuser la requête (`deny`) ou poser des en-têtes (`set_headers`), rien d'autre ; `type: external-call` sous `policies.definitions`, phase `request` (défaut, avant l'amont) ou `response`. Délai et `on_failure` obligatoires, le budget de l'opération est consommé ; une requête simulée n'appelle rien."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "1.2.0"
    },
    "policy.header-inject": {
      "additionalProperties": false,
      "description": "Sets or renames headers; `type: header-inject` under `policies.definitions`, `request` phase (default), `response` or `error`, optional `when` condition. Protected headers (identity, forwarding, tracing, hop-by-hop, `flexway-*`, the tenant header) are refused.",
      "properties": {
        "on_missing": {
          "description": "What a template does when its value is missing: `skip` (header not set), `empty` (set empty) or `fail` (request refused). Required as soon as a value is an expression; `skip` otherwise.",
          "enum": [
            "skip",
            "empty",
            "fail"
          ],
          "type": "string",
          "x-flexway-description": {
            "en": "What a template does when its value is missing: `skip` (header not set), `empty` (set empty) or `fail` (request refused). Required as soon as a value is an expression; `skip` otherwise.",
            "fr": "Ce que fait un gabarit dont la valeur manque : `skip` (en-tête non posé), `empty` (posé vide) ou `fail` (requête refusée). Obligatoire dès qu'une valeur est une expression ; `skip` sinon."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.5.0"
        },
        "rename": {
          "additionalProperties": {
            "type": "string"
          },
          "description": "Headers renamed: old name -> new name, values kept.",
          "type": "object",
          "x-flexway-description": {
            "en": "Headers renamed: old name -> new name, values kept.",
            "fr": "En-têtes renommés : ancien nom -> nouveau nom, valeurs gardées."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.5.0"
        },
        "set": {
          "additionalProperties": {
            "type": "string"
          },
          "description": "Headers set: name -> value, literal or a `{{ CEL expression }}` template (`{{ auth.claims.plan }}`); an existing value is replaced. At least one of `set` or `rename`.",
          "type": "object",
          "x-flexway-description": {
            "en": "Headers set: name -> value, literal or a `{{ CEL expression }}` template (`{{ auth.claims.plan }}`); an existing value is replaced. At least one of `set` or `rename`.",
            "fr": "En-têtes posés : nom -> valeur, littérale ou gabarit `{{ expression CEL }}` (`{{ auth.claims.plan }}`) ; une valeur existante est remplacée. Au moins `set` ou `rename`."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.5.0"
        }
      },
      "type": "object",
      "x-flexway-description": {
        "en": "Sets or renames headers; `type: header-inject` under `policies.definitions`, `request` phase (default), `response` or `error`, optional `when` condition. Protected headers (identity, forwarding, tracing, hop-by-hop, `flexway-*`, the tenant header) are refused.",
        "fr": "Pose ou renomme des en-têtes ; `type: header-inject` sous `policies.definitions`, phase `request` (défaut), `response` ou `error`, condition `when` possible. Les en-têtes protégés (identité, transfert, traçage, saut par saut, `flexway-*`, en-tête du tenant) sont refusés."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "0.5.0"
    },
    "policy.header-remove": {
      "additionalProperties": false,
      "description": "Removes headers; `type: header-remove` under `policies.definitions`, `request` phase (default), `response` or `error`. Protected headers are refused.",
      "properties": {
        "names": {
          "description": "Names of the headers removed, at least one.",
          "items": {
            "type": "string"
          },
          "minItems": 1,
          "type": "array",
          "x-flexway-description": {
            "en": "Names of the headers removed, at least one.",
            "fr": "Noms des en-têtes retirés, au moins un."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.5.0"
        }
      },
      "required": [
        "names"
      ],
      "type": "object",
      "x-flexway-description": {
        "en": "Removes headers; `type: header-remove` under `policies.definitions`, `request` phase (default), `response` or `error`. Protected headers are refused.",
        "fr": "Retire des en-têtes ; `type: header-remove` sous `policies.definitions`, phase `request` (défaut), `response` ou `error`. Les en-têtes protégés sont refusés."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "0.5.0"
    },
    "policy.map-status": {
      "description": "Turns an upstream response's status into a semantic one; `type: map-status` under `policies.definitions`, `response` phase. Its configuration is a list of 1 to 64 rules `{ from, to, when, code }`, the first that applies wins; a rule after an unconditional rule with the same `from` is refused.",
      "items": {
        "additionalProperties": false,
        "properties": {
          "code": {
            "description": "Error code of the problem served, only when `to` is 400 or more; `^[a-z][a-z0-9_]{0,63}$`, never one of the gateway's own codes.",
            "pattern": "^[a-z][a-z0-9_]{0,63}$",
            "type": "string",
            "x-flexway-description": {
              "en": "Error code of the problem served, only when `to` is 400 or more; `^[a-z][a-z0-9_]{0,63}$`, never one of the gateway's own codes.",
              "fr": "Code d'erreur du problème servi, seulement quand `to` est 400 ou plus ; `^[a-z][a-z0-9_]{0,63}$`, jamais un code propre à la passerelle."
            },
            "x-flexway-reloadable": true,
            "x-flexway-since": "1.2.0"
          },
          "from": {
            "description": "Upstream status matched, from 200 to 599; required.",
            "maximum": 599,
            "minimum": 200,
            "type": "integer",
            "x-flexway-description": {
              "en": "Upstream status matched, from 200 to 599; required.",
              "fr": "Statut de l'amont visé, de 200 à 599 ; obligatoire."
            },
            "x-flexway-reloadable": true,
            "x-flexway-since": "1.2.0"
          },
          "to": {
            "description": "Status served, required: 200 or more, with an RFC 9110 reason, different from `from`, never a 3xx nor 204, 205, 206, 304, 401, 405, 407, 416 or 426.",
            "maximum": 599,
            "minimum": 200,
            "type": "integer",
            "x-flexway-description": {
              "en": "Status served, required: 200 or more, with an RFC 9110 reason, different from `from`, never a 3xx nor 204, 205, 206, 304, 401, 405, 407, 416 or 426.",
              "fr": "Statut servi, obligatoire : 200 ou plus, avec une raison RFC 9110, différent de `from`, jamais un 3xx ni 204, 205, 206, 304, 401, 405, 407, 416 ou 426."
            },
            "x-flexway-reloadable": true,
            "x-flexway-since": "1.2.0"
          },
          "when": {
            "description": "Optional CEL condition, which may read `body`, the response's JSON body.",
            "type": "string",
            "x-flexway-description": {
              "en": "Optional CEL condition, which may read `body`, the response's JSON body.",
              "fr": "Condition CEL facultative, qui peut lire `body`, le corps JSON de la réponse."
            },
            "x-flexway-reloadable": true,
            "x-flexway-since": "1.2.0"
          }
        },
        "required": [
          "from",
          "to"
        ],
        "type": "object"
      },
      "maxItems": 64,
      "minItems": 1,
      "type": "array",
      "x-flexway-description": {
        "en": "Turns an upstream response's status into a semantic one; `type: map-status` under `policies.definitions`, `response` phase. Its configuration is a list of 1 to 64 rules `{ from, to, when, code }`, the first that applies wins; a rule after an unconditional rule with the same `from` is refused.",
        "fr": "Change le statut d'une réponse de l'amont en statut sémantique ; `type: map-status` sous `policies.definitions`, phase `response`. Sa configuration est une liste de 1 à 64 règles `{ from, to, when, code }`, la première qui s'applique gagne ; une règle après une règle sans condition de même `from` est refusée."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "1.2.0"
    },
    "policy.query-inject": {
      "additionalProperties": false,
      "description": "Sets query parameters; `type: query-inject` under `policies.definitions`, `request` phase.",
      "properties": {
        "on_missing": {
          "description": "What a template does when its value is missing: `skip`, `empty` or `fail`. Required as soon as a value is an expression; `skip` otherwise.",
          "enum": [
            "skip",
            "empty",
            "fail"
          ],
          "type": "string",
          "x-flexway-description": {
            "en": "What a template does when its value is missing: `skip`, `empty` or `fail`. Required as soon as a value is an expression; `skip` otherwise.",
            "fr": "Ce que fait un gabarit dont la valeur manque : `skip`, `empty` ou `fail`. Obligatoire dès qu'une valeur est une expression ; `skip` sinon."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.5.0"
        },
        "set": {
          "additionalProperties": {
            "type": "string"
          },
          "description": "Parameters set: name -> value, literal or a `{{ CEL expression }}` template; at least one.",
          "type": "object",
          "x-flexway-description": {
            "en": "Parameters set: name -> value, literal or a `{{ CEL expression }}` template; at least one.",
            "fr": "Paramètres posés : nom -> valeur, littérale ou gabarit `{{ expression CEL }}` ; au moins un."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.5.0"
        }
      },
      "required": [
        "set"
      ],
      "type": "object",
      "x-flexway-description": {
        "en": "Sets query parameters; `type: query-inject` under `policies.definitions`, `request` phase.",
        "fr": "Pose des paramètres de requête ; `type: query-inject` sous `policies.definitions`, phase `request`."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "0.5.0"
    },
    "policy.query-remove": {
      "additionalProperties": false,
      "description": "Removes query parameters; `type: query-remove` under `policies.definitions`, `request` phase.",
      "properties": {
        "names": {
          "description": "Names of the parameters removed, at least one.",
          "items": {
            "type": "string"
          },
          "minItems": 1,
          "type": "array",
          "x-flexway-description": {
            "en": "Names of the parameters removed, at least one.",
            "fr": "Noms des paramètres retirés, au moins un."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.5.0"
        }
      },
      "required": [
        "names"
      ],
      "type": "object",
      "x-flexway-description": {
        "en": "Removes query parameters; `type: query-remove` under `policies.definitions`, `request` phase.",
        "fr": "Retire des paramètres de requête ; `type: query-remove` sous `policies.definitions`, phase `request`."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "0.5.0"
    },
    "policy.quota": {
      "additionalProperties": false,
      "description": "A tenant's request quota over calendar windows, UTC (day, month); `type: quota` under `policies.definitions`, `request` phase, no condition. Requires `server.counters`: counted in memory, a monthly quota would start again at every deployment. Beyond it: `429 quota_exceeded`.",
      "properties": {
        "key": {
          "default": "tenant",
          "description": "What is counted: `tenant` only (default).",
          "enum": [
            "tenant"
          ],
          "type": "string",
          "x-flexway-description": {
            "en": "What is counted: `tenant` only (default).",
            "fr": "Ce qui est compté : `tenant` seulement (défaut)."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.0.0"
        },
        "limits": {
          "additionalProperties": {
            "minimum": 1,
            "type": "integer"
          },
          "description": "Limits by window: `day` and/or `month`, each an integer of at least 1; at least one window.",
          "propertyNames": {
            "enum": [
              "day",
              "month"
            ]
          },
          "type": "object",
          "x-flexway-description": {
            "en": "Limits by window: `day` and/or `month`, each an integer of at least 1; at least one window.",
            "fr": "Plafonds par fenêtre : `day` et/ou `month`, chacun un entier d'au moins 1 ; au moins une fenêtre."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.0.0"
        }
      },
      "required": [
        "limits"
      ],
      "type": "object",
      "x-flexway-description": {
        "en": "A tenant's request quota over calendar windows, UTC (day, month); `type: quota` under `policies.definitions`, `request` phase, no condition. Requires `server.counters`: counted in memory, a monthly quota would start again at every deployment. Beyond it: `429 quota_exceeded`.",
        "fr": "Quota de requêtes d'un tenant sur des fenêtres calendaires UTC (jour, mois) ; `type: quota` sous `policies.definitions`, phase `request`, sans condition. Exige `server.counters` : compté en mémoire, un quota mensuel repartirait à chaque déploiement. Au-delà : `429 quota_exceeded`."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "1.0.0"
    },
    "policy.rate-limit": {
      "additionalProperties": false,
      "description": "Token buckets per caller; `type: rate-limit` under `policies.definitions`, `request` phase, no condition. An operation takes the class `read` (GET, HEAD, OPTIONS) or `write`, or the one of `x-flexway-rate-class`; beyond it: `429 rate_limited`.",
      "properties": {
        "classes": {
          "additionalProperties": {
            "additionalProperties": false,
            "properties": {
              "burst": {
                "description": "Bucket size: requests admitted at once; an integer of at least 1, required.",
                "minimum": 1,
                "type": "integer",
                "x-flexway-description": {
                  "en": "Bucket size: requests admitted at once; an integer of at least 1, required.",
                  "fr": "Taille du seau : requêtes admises d'un coup ; entier d'au moins 1, obligatoire."
                },
                "x-flexway-reloadable": true,
                "x-flexway-since": "0.4.0"
              },
              "rps": {
                "description": "Bucket refill, in requests per second; a number greater than 0, required.",
                "exclusiveMinimum": 0,
                "type": "number",
                "x-flexway-description": {
                  "en": "Bucket refill, in requests per second; a number greater than 0, required.",
                  "fr": "Remplissage du seau, en requêtes par seconde ; nombre strictement positif, obligatoire."
                },
                "x-flexway-reloadable": true,
                "x-flexway-since": "0.4.0"
              },
              "scope": {
                "description": "Overrides the policy's `scope` for this class: `class` or `operation`.",
                "enum": [
                  "class",
                  "operation"
                ],
                "type": "string",
                "x-flexway-description": {
                  "en": "Overrides the policy's `scope` for this class: `class` or `operation`.",
                  "fr": "Remplace `scope` de la politique pour cette classe : `class` ou `operation`."
                },
                "x-flexway-reloadable": true,
                "x-flexway-since": "1.0.0"
              }
            },
            "required": [
              "burst",
              "rps"
            ],
            "type": "object"
          },
          "description": "Rate classes by name (`read`, `write`, or your own), each a bucket; at least one.",
          "type": "object",
          "x-flexway-description": {
            "en": "Rate classes by name (`read`, `write`, or your own), each a bucket; at least one.",
            "fr": "Classes de débit par nom (`read`, `write`, ou les vôtres), chacune un seau ; au moins une."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.4.0"
        },
        "header": {
          "description": "With `key: header`, the header whose value keys an anonymous caller.",
          "type": "string",
          "x-flexway-description": {
            "en": "With `key: header`, the header whose value keys an anonymous caller.",
            "fr": "Avec `key: header`, l'en-tête dont la valeur sert de clé pour un appelant anonyme."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.0.0"
        },
        "key": {
          "default": "caller",
          "description": "What a bucket counts: `caller` (default) - the authenticated subject, else the client address - or `header` - for an anonymous caller, the value of `header`, believed only from a peer whose identity is in `trusted`. An authenticated subject stays the key either way.",
          "enum": [
            "caller",
            "header"
          ],
          "type": "string",
          "x-flexway-description": {
            "en": "What a bucket counts: `caller` (default) - the authenticated subject, else the client address - or `header` - for an anonymous caller, the value of `header`, believed only from a peer whose identity is in `trusted`. An authenticated subject stays the key either way.",
            "fr": "Ce qu'un seau compte : `caller` (défaut) - le sujet authentifié, sinon l'adresse du client - ou `header` - pour un anonyme, la valeur de `header`, crue seulement d'un pair dont l'identité figure dans `trusted`. Un sujet authentifié reste la clé dans les deux cas."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.4.0"
        },
        "scope": {
          "default": "class",
          "description": "What one bucket spans: `class` (default) - one bucket per caller and class, shared by the class's operations - or `operation` - one bucket per caller and operation, the class giving its size.",
          "enum": [
            "class",
            "operation"
          ],
          "type": "string",
          "x-flexway-description": {
            "en": "What one bucket spans: `class` (default) - one bucket per caller and class, shared by the class's operations - or `operation` - one bucket per caller and operation, the class giving its size.",
            "fr": "Ce qu'un seau couvre : `class` (défaut) - un seau par appelant et par classe, partagé par les opérations de la classe - ou `operation` - un seau par appelant et par opération, la classe en donnant la taille."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.0.0"
        },
        "trusted": {
          "description": "With `key: header`, the verified peer identities (through `server.peer_identity_header`) whose key header is believed; required with `key: header`.",
          "items": {
            "type": "string"
          },
          "type": "array",
          "x-flexway-description": {
            "en": "With `key: header`, the verified peer identities (through `server.peer_identity_header`) whose key header is believed; required with `key: header`.",
            "fr": "Avec `key: header`, les identités de pair vérifiées (par `server.peer_identity_header`) dont l'en-tête clé est cru ; obligatoire avec `key: header`."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.0.0"
        }
      },
      "required": [
        "classes"
      ],
      "type": "object",
      "x-flexway-description": {
        "en": "Token buckets per caller; `type: rate-limit` under `policies.definitions`, `request` phase, no condition. An operation takes the class `read` (GET, HEAD, OPTIONS) or `write`, or the one of `x-flexway-rate-class`; beyond it: `429 rate_limited`.",
        "fr": "Seaux à jetons par appelant ; `type: rate-limit` sous `policies.definitions`, phase `request`, sans condition. Une opération prend la classe `read` (GET, HEAD, OPTIONS) ou `write`, ou celle de `x-flexway-rate-class` ; au-delà : `429 rate_limited`."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "0.4.0"
    },
    "policy.redact": {
      "additionalProperties": false,
      "description": "Masks or removes fields of the response's JSON body; `type: redact` under `policies.definitions`, `response` phase. Exactly one of `with` or `remove: true`.",
      "properties": {
        "fields": {
          "description": "Field paths, starting with `body.` (`body.card.number`), segments `[A-Za-z_][A-Za-z0-9_]*`; 1 to 256 paths, 32 segments at most, no duplicate.",
          "items": {
            "pattern": "^body\\.",
            "type": "string"
          },
          "maxItems": 256,
          "minItems": 1,
          "type": "array",
          "x-flexway-description": {
            "en": "Field paths, starting with `body.` (`body.card.number`), segments `[A-Za-z_][A-Za-z0-9_]*`; 1 to 256 paths, 32 segments at most, no duplicate.",
            "fr": "Chemins des champs, commençant par `body.` (`body.card.number`), segments `[A-Za-z_][A-Za-z0-9_]*` ; 1 à 256 chemins, 32 segments au plus, sans doublon."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        },
        "remove": {
          "description": "`true` removes the fields instead of masking them; never with `with`.",
          "type": "boolean",
          "x-flexway-description": {
            "en": "`true` removes the fields instead of masking them; never with `with`.",
            "fr": "`true` retire les champs au lieu de les masquer ; jamais avec `with`."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        },
        "with": {
          "description": "Value a masked field is replaced by: a string, a number, a boolean or `null`; never with `remove: true`.",
          "type": [
            "string",
            "number",
            "boolean",
            "null"
          ],
          "x-flexway-description": {
            "en": "Value a masked field is replaced by: a string, a number, a boolean or `null`; never with `remove: true`.",
            "fr": "Valeur qui remplace un champ masqué : une chaîne, un nombre, un booléen ou `null` ; jamais avec `remove: true`."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        }
      },
      "required": [
        "fields"
      ],
      "type": "object",
      "x-flexway-description": {
        "en": "Masks or removes fields of the response's JSON body; `type: redact` under `policies.definitions`, `response` phase. Exactly one of `with` or `remove: true`.",
        "fr": "Masque ou retire des champs du corps JSON de la réponse ; `type: redact` sous `policies.definitions`, phase `response`. Exactement un de `with` ou `remove: true`."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "1.2.0"
    },
    "policy.rewrite-path": {
      "additionalProperties": false,
      "description": "Points the request at another path on the upstream; `type: rewrite-path` under `policies.definitions`, `request` phase.",
      "properties": {
        "to": {
          "description": "Upstream path, starting with `/`; `{param}` takes the value of a route parameter, escaped, never the raw text of the path received.",
          "pattern": "^/",
          "type": "string",
          "x-flexway-description": {
            "en": "Upstream path, starting with `/`; `{param}` takes the value of a route parameter, escaped, never the raw text of the path received.",
            "fr": "Chemin de l'amont, commençant par `/` ; `{param}` y prend la valeur d'un paramètre de la route, échappée, jamais le texte brut du chemin reçu."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "0.5.0"
        }
      },
      "required": [
        "to"
      ],
      "type": "object",
      "x-flexway-description": {
        "en": "Points the request at another path on the upstream; `type: rewrite-path` under `policies.definitions`, `request` phase.",
        "fr": "Dirige la requête vers un autre chemin de l'amont ; `type: rewrite-path` sous `policies.definitions`, phase `request`."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "0.5.0"
    },
    "policy.transform-body": {
      "additionalProperties": false,
      "description": "Closed projection of the response's JSON body: only the fields listed are served; `type: transform-body` under `policies.definitions`, `response` phase. Body bounded by `server.body.max_bytes`.",
      "properties": {
        "on_missing": {
          "description": "Projected field absent from the response: `fail` (response refused) or `omit` (field left out). Required.",
          "enum": [
            "fail",
            "omit"
          ],
          "type": "string",
          "x-flexway-description": {
            "en": "Projected field absent from the response: `fail` (response refused) or `omit` (field left out). Required.",
            "fr": "Champ projeté absent de la réponse : `fail` (réponse refusée) ou `omit` (champ omis). Obligatoire."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        },
        "project": {
          "description": "Dotted paths of the fields kept (`id`, `customer.name`), segments `[A-Za-z_][A-Za-z0-9_]*`; 1 to 256 paths, 32 segments at most.",
          "items": {
            "type": "string"
          },
          "maxItems": 256,
          "minItems": 1,
          "type": "array",
          "x-flexway-description": {
            "en": "Dotted paths of the fields kept (`id`, `customer.name`), segments `[A-Za-z_][A-Za-z0-9_]*`; 1 to 256 paths, 32 segments at most.",
            "fr": "Chemins pointés des champs gardés (`id`, `customer.name`), segments `[A-Za-z_][A-Za-z0-9_]*` ; 1 à 256 chemins, 32 segments au plus."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.2.0"
        }
      },
      "required": [
        "on_missing",
        "project"
      ],
      "type": "object",
      "x-flexway-description": {
        "en": "Closed projection of the response's JSON body: only the fields listed are served; `type: transform-body` under `policies.definitions`, `response` phase. Body bounded by `server.body.max_bytes`.",
        "fr": "Projection fermée du corps JSON de la réponse : seuls les champs listés sont servis ; `type: transform-body` sous `policies.definitions`, phase `response`. Corps borné par `server.body.max_bytes`."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "1.2.0"
    }
  },
  "$id": "https://flexway.io/schema/v1/policy-fragment.schema.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "additionalProperties": false,
  "description": "A policy fragment (`kind: PolicyFragment`, 1.7.0): policy definitions written once and imported by several gateways, each pinning it by its digest in `policies.fragments`. Nothing else: no host, route, chain, upstream, issuer, nor import of another fragment. Strict decoding; a secret is written as a reference, resolved by the importer.",
  "properties": {
    "definitions": {
      "additionalProperties": {
        "additionalProperties": false,
        "allOf": [
          {
            "if": {
              "properties": {
                "type": {
                  "const": "cache"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.cache"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "cors"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.cors"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "external-call"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.external-call"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "header-inject"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.header-inject"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "header-remove"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.header-remove"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "map-status"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.map-status"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "query-inject"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.query-inject"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "query-remove"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.query-remove"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "quota"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.quota"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "rate-limit"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.rate-limit"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "redact"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.redact"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "rewrite-path"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.rewrite-path"
                }
              }
            }
          },
          {
            "if": {
              "properties": {
                "type": {
                  "const": "transform-body"
                }
              },
              "required": [
                "type"
              ]
            },
            "then": {
              "properties": {
                "config": {
                  "$ref": "#/$defs/policy.transform-body"
                }
              }
            }
          }
        ],
        "properties": {
          "config": {
            "description": "The type's own configuration, decoded strictly against that type's schema: an unknown key is refused.",
            "x-flexway-description": {
              "en": "The type's own configuration, decoded strictly against that type's schema: an unknown key is refused.",
              "fr": "Configuration propre au type, décodée strictement contre le schéma de ce type : une clé inconnue est refusée."
            },
            "x-flexway-reloadable": true,
            "x-flexway-since": "1.7.0"
          },
          "phase": {
            "description": "When the policy acts: `request`, `response` or `error`, among the phases its type accepts. Absent: the type's first phase.",
            "enum": [
              "request",
              "response",
              "error"
            ],
            "type": "string",
            "x-flexway-description": {
              "en": "When the policy acts: `request`, `response` or `error`, among the phases its type accepts. Absent: the type's first phase.",
              "fr": "Moment où la politique agit : `request`, `response` ou `error`, parmi les phases que son type accepte. Absente : la première phase du type."
            },
            "x-flexway-reloadable": true,
            "x-flexway-since": "1.7.0"
          },
          "type": {
            "description": "The policy's type, from the catalogue of the importing gateway. Required.",
            "enum": [
              "cache",
              "cors",
              "external-call",
              "header-inject",
              "header-remove",
              "map-status",
              "query-inject",
              "query-remove",
              "quota",
              "rate-limit",
              "redact",
              "rewrite-path",
              "transform-body"
            ],
            "type": "string",
            "x-flexway-description": {
              "en": "The policy's type, from the catalogue of the importing gateway. Required.",
              "fr": "Type de la politique, dans le catalogue de la passerelle qui importe. Obligatoire."
            },
            "x-flexway-reloadable": true,
            "x-flexway-since": "1.7.0"
          },
          "when": {
            "description": "CEL condition compiled at load by the importing gateway: the policy applies only when it holds.",
            "type": "string",
            "x-flexway-description": {
              "en": "CEL condition compiled at load by the importing gateway: the policy applies only when it holds.",
              "fr": "Condition CEL compilée au chargement par la passerelle qui importe : la politique ne s'applique que si elle est vraie."
            },
            "x-flexway-reloadable": true,
            "x-flexway-since": "1.7.0"
          }
        },
        "required": [
          "type"
        ],
        "type": "object"
      },
      "description": "The fragment's policies: name -> definition (`type`, `phase`, `when`, `config`), written as in `policies.definitions`. At least one, each of a type the gateway executes (an unknown type is refused, never skipped). An ASCII name: letters, digits, `.`, `_`, `-`, 63 at most; the importer sees them as `<import>/<name>`. A definition no chain names is loaded and reported on `/config` (`unused`), never an error.",
      "minimum": 1,
      "propertyNames": {
        "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,62}$"
      },
      "type": "object",
      "x-flexway-description": {
        "en": "The fragment's policies: name -> definition (`type`, `phase`, `when`, `config`), written as in `policies.definitions`. At least one, each of a type the gateway executes (an unknown type is refused, never skipped). An ASCII name: letters, digits, `.`, `_`, `-`, 63 at most; the importer sees them as `<import>/<name>`. A definition no chain names is loaded and reported on `/config` (`unused`), never an error.",
        "fr": "Les politiques du fragment : nom -> définition (`type`, `phase`, `when`, `config`), écrites comme dans `policies.definitions`. Au moins une, chacune d'un type que la passerelle exécute (un type inconnu est refusé, jamais ignoré). Un nom en ASCII : lettres, chiffres, `.`, `_`, `-`, 63 au plus ; l'importateur les voit sous `<import>/<nom>`. Une définition jamais référencée est chargée et signalée dans `/config` (`unused`), jamais une erreur."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "1.7.0"
    },
    "enforce": {
      "additionalProperties": false,
      "description": "What the fragment enforces on every importer.",
      "properties": {
        "global": {
          "description": "Definitions of the fragment placed at the head of every chain of the importer (HTTP listener; the native Kafka listener runs no chain), before `global`, in this order (then the fragments in the order of `policies.fragments`). Refused at load: a `!<import>/<name>` removing one; a local instance of a single-instance type (`cors`, `rate-limit`, `rewrite-path`...) that would replace it; two enforced policies of one single-instance type; a policy that is not enforced setting, renaming or removing in the same phase a header (any case) or a parameter an enforced one sets or removes. Default: nothing enforced.",
          "items": {
            "type": "string"
          },
          "type": "array",
          "x-flexway-description": {
            "en": "Definitions of the fragment placed at the head of every chain of the importer (HTTP listener; the native Kafka listener runs no chain), before `global`, in this order (then the fragments in the order of `policies.fragments`). Refused at load: a `!<import>/<name>` removing one; a local instance of a single-instance type (`cors`, `rate-limit`, `rewrite-path`...) that would replace it; two enforced policies of one single-instance type; a policy that is not enforced setting, renaming or removing in the same phase a header (any case) or a parameter an enforced one sets or removes. Default: nothing enforced.",
            "fr": "Définitions du fragment placées en tête de chaque chaîne de l'importateur (écouteur HTTP ; l'écouteur Kafka natif n'exécute aucune chaîne), avant `global`, dans cet ordre (puis les fragments dans l'ordre de `policies.fragments`). Refusent le chargement : un `!<import>/<nom>` qui en retire une ; une instance locale d'un type à instance unique (`cors`, `rate-limit`, `rewrite-path`...) qui la remplacerait ; deux imposées d'un même type à instance unique ; une politique non imposée qui pose, renomme ou retire dans la même phase un en-tête (casse ignorée) ou un paramètre qu'une imposée pose ou retire. Défaut : rien d'imposé."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.7.0"
        }
      },
      "type": "object",
      "x-flexway-description": {
        "en": "What the fragment enforces on every importer.",
        "fr": "Ce que le fragment impose à chaque importateur."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "1.7.0"
    },
    "fragment": {
      "description": "The fragment's name: lower-case letters, digits and `-`, 63 at most. Required. A gateway imports one version of a fragment: two imports of the same name are refused.",
      "pattern": "^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$",
      "type": "string",
      "x-flexway-description": {
        "en": "The fragment's name: lower-case letters, digits and `-`, 63 at most. Required. A gateway imports one version of a fragment: two imports of the same name are refused.",
        "fr": "Nom du fragment : lettres minuscules, chiffres et `-`, 63 au plus. Obligatoire. Une passerelle n'importe qu'une version d'un fragment : deux imports du même nom sont refusés."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "1.7.0"
    },
    "kind": {
      "description": "Always `PolicyFragment`. Required.",
      "enum": [
        "PolicyFragment"
      ],
      "type": "string",
      "x-flexway-description": {
        "en": "Always `PolicyFragment`. Required.",
        "fr": "Toujours `PolicyFragment`. Obligatoire."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "1.7.0"
    },
    "requires": {
      "additionalProperties": false,
      "description": "What the fragment requires of the gateway that imports it; anything missing refuses the load.",
      "properties": {
        "flexway": {
          "description": "The oldest gateway version, written `>=MAJOR.MINOR.PATCH` (`>=1.7.0`). Absent: any version. A development build (`dev`) always satisfies it, and says so (a warning in the log, `requires_unchecked` on `/config`); a version served that is not semantic is refused.",
          "pattern": "^>=\\s*(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?$",
          "type": "string",
          "x-flexway-description": {
            "en": "The oldest gateway version, written `>=MAJOR.MINOR.PATCH` (`>=1.7.0`). Absent: any version. A development build (`dev`) always satisfies it, and says so (a warning in the log, `requires_unchecked` on `/config`); a version served that is not semantic is refused.",
            "fr": "Version minimale de la passerelle, sous la forme `>=MAJEURE.MINEURE.CORRECTIF` (`>=1.7.0`). Absente : toute version. Une construction de développement (`dev`) la satisfait toujours, et le dit (avertissement au journal, `requires_unchecked` dans `/config`) ; une version servie non sémantique est refusée."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.7.0"
        },
        "upstreams": {
          "description": "Upstreams the importer must declare in `upstreams` - those an `external-call` of the fragment names. A fragment never declares an upstream. Default: none.",
          "items": {
            "type": "string"
          },
          "type": "array",
          "x-flexway-description": {
            "en": "Upstreams the importer must declare in `upstreams` - those an `external-call` of the fragment names. A fragment never declares an upstream. Default: none.",
            "fr": "Amonts que l'importateur doit déclarer dans `upstreams` - ceux qu'un `external-call` du fragment nomme. Le fragment ne déclare jamais un amont. Défaut : aucun."
          },
          "x-flexway-reloadable": true,
          "x-flexway-since": "1.7.0"
        }
      },
      "type": "object",
      "x-flexway-description": {
        "en": "What the fragment requires of the gateway that imports it; anything missing refuses the load.",
        "fr": "Ce que le fragment exige de la passerelle qui l'importe ; un manque refuse le chargement."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "1.7.0"
    },
    "version": {
      "description": "The fragment's semantic version (`1.4.0`). Required. Informative - read by people, the console and `/config`; the digest says what runs.",
      "pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?$",
      "type": "string",
      "x-flexway-description": {
        "en": "The fragment's semantic version (`1.4.0`). Required. Informative - read by people, the console and `/config`; the digest says what runs.",
        "fr": "Version sémantique du fragment (`1.4.0`). Obligatoire. Informative - lue par les personnes, la console et `/config` ; l'empreinte dit ce qui tourne."
      },
      "x-flexway-reloadable": true,
      "x-flexway-since": "1.7.0"
    }
  },
  "required": [
    "definitions",
    "fragment",
    "kind",
    "version"
  ],
  "title": "Flexway policy fragment",
  "type": "object",
  "x-flexway-description": {
    "en": "A policy fragment (`kind: PolicyFragment`, 1.7.0): policy definitions written once and imported by several gateways, each pinning it by its digest in `policies.fragments`. Nothing else: no host, route, chain, upstream, issuer, nor import of another fragment. Strict decoding; a secret is written as a reference, resolved by the importer.",
    "fr": "Un fragment de politique (`kind: PolicyFragment`, 1.7.0) : des définitions de politiques écrites une fois et importées par plusieurs passerelles, chacune l'épinglant par son empreinte dans `policies.fragments`. Rien d'autre : ni hôte, ni route, ni chaîne, ni amont, ni émetteur, ni import d'un autre fragment. Décodage strict ; un secret s'y écrit en référence, résolue chez l'importateur."
  }
}
