Lint Configuration
- Applies to
- Gatewayruntime for API, AI, and MCP gateways
- Hosting
- Managed EdgeManaged DedicatedSelf-Hosted
zuplo lint reads its settings from a zuplo.lint.jsonc file in the root of
your project. The file is optional: without it, the linter runs the
zuplo:recommended preset and fails only on errors.
Start the file with $schema and version:
zuplo.lint.jsonc
$schema gives your editor completion and inline errors for every setting on
this page. "version" is the version of the configuration format; set it to
2.
The linter looks for zuplo.lint.jsonc first, then zuplo.lint.json. To use a
file somewhere else, pass its path with --config. The path is relative to the
directory you run the command from, not to --dir:
Code
The file is JSON with comments (JSONC), so you can annotate why a rule is off or a path is skipped. Trailing commas are allowed.
Full example
zuplo.lint.jsonc
How settings combine
Code
extendsbuilds the starting set of rules. When two rulesets define the same rule, the later one wins.ruleschanges the severity of those rules, limits them to some routes, or adds new ones.overrideschanges severities for the routes or files they match. When several overrides match a result, the last one wins.--fail-onon the command line replacesfailOnfor one run.
Severities
Every rule reports at one severity:
| Severity | Meaning |
|---|---|
"error" | A defect. Fails the run under the default failOn. |
"warn" | A likely problem. Fails the run only when failOn is "warn". |
"info" | Worth knowing. Reported, counted under info in the summary, never fails. |
"hint" | A suggestion. Reported like info, never fails. |
"off" | The rule doesn't run, or doesn't report on the routes it's off for. |
Options
extends
The rulesets to start from. The default is ["zuplo:recommended"]. An entry is
one of these:
A Zuplo preset. Each preset combines Zuplo's own checks with a reviewed set of OpenAPI rules that catch real defects without flooding a typical project.
| Preset | Adds |
|---|---|
zuplo:recommended | The core checks: operationId, descriptions, tags, path parameters, undefined policies, route labels, policy schemas, duplicate operations, unsafe Markdown, and more. |
zuplo:strict | Everything in recommended, plus required authentication and rate limiting, CORS on every route, API info, error responses, and documentation completeness. |
zuplo:security | Everything in recommended, plus required authentication and rate limiting, both as errors. |
A vacuum or Spectral built-in ruleset. These bring in the full catalog of general OpenAPI rules:
| Ruleset | Contains |
|---|---|
vacuum:oas or spectral:oas | General OpenAPI rules. |
vacuum:owasp or spectral:owasp | OWASP API security rules. |
On their own, these sets turn on that set's recommended rules. Write the entry as a pair to pick a mode instead, as in Spectral:
Code
| Mode | Effect |
|---|---|
"recommended" | The set's recommended rules. The default. |
"all" | Every rule in the set. |
"off" | Removes the set's rules that an earlier entry added. |
Some general OpenAPI rules don't fit Zuplo projects. For example,
oas3-api-servers reports every spec because Zuplo supplies the servers, and
paths-kebab-case reports URLPattern paths such as /users/:id. Turn those off
in rules when you extend vacuum:oas, or start from a Zuplo preset, which
leaves them out.
A local ruleset file. A .json or .jsonc file in the Spectral ruleset
format, with a path relative to zuplo.lint.jsonc. Use it to share rules
between projects. See
Custom lint rules.
Run npx zuplo lint --list-rules to see every rule and which rulesets include
it. The rules reference describes the rules in the Zuplo
presets.
rules
Rule settings, keyed by rule ID. A value is either a severity or an object.
A severity changes the rule everywhere:
Code
An object changes the severity and limits which routes the rule reports on:
Code
| Field | Description |
|---|---|
severity | The rule's severity. Leave it out to keep the severity the rule already has. |
paths | Report the rule only on these API paths. |
excludePaths | Don't report the rule on these API paths. |
Path patterns work the same everywhere in this file:
- A plain path matches that route exactly, as written in the OpenAPI file:
"/users/{userId}". - A path ending in
/**matches that path and every path beneath it."/internal/**"matches/internaland/internal/jobs, but not/internal2.
A rule with paths doesn't report results that aren't tied to a route, such as
a missing info.description or a policies.json schema error.
An object with given and then defines a new rule. See
Custom lint rules.
A rule ID that no ruleset in extends defines stops the run with exit code 2,
and the error suggests the closest ID. To configure a rule, extend a ruleset
that includes it: "zuplo/require-cors-policy" is only available when you
extend zuplo:strict.
zuplo/require-policy is the rule that enforces
requiredPolicies. Setting it applies the severity to
every required policy, and "off" turns all of them off. Use the base ID here;
zuplo/require-policy-warn, which appears in results, isn't a rule you
configure.
overrides
Change rule severities for some routes or OpenAPI files. Use an override when a group of routes is deliberately different from the rest of the API: public endpoints, a health check, a legacy spec you can't change yet.
Code
| Field | Description |
|---|---|
paths | API paths the override applies to. |
files | OpenAPI files the override applies to, relative to the project directory. * matches within one folder: config/*.oas.json. |
rules | Severities to apply, keyed by rule ID. |
An override needs paths, files, or both. With both, a result must match
both.
An override can also turn a rule on. With "operation-tags": "off" in rules
and { "paths": ["/v1/**"], "rules": { "operation-tags": "warn" } } in
overrides, the rule reports only on /v1 routes. An override works within a
rule's own paths and excludePaths, and can't turn a rule on for a path in
ignorePaths.
failOn
The lowest severity that fails the run with exit code 1.
| Value | The run fails when |
|---|---|
"error" | There is at least one error, or a breaking change reported as an error. |
"warn" | There is at least one error, warning, or breaking change. |
"none" | Never. The linter reports results and exits with 0. |
The default is "error". info and hint results never fail a run. Any other
value stops the run with exit code 2. The --fail-on flag overrides this
value for a single run.
ignorePaths
API paths whose route results the linter drops for every rule. An override can't turn a rule back on for an ignored path.
Code
ignorePaths also applies to
automatic fixes. Results that
aren't tied to a route still appear: policies.json schema results, info
rules, and breaking changes. To turn off only
some rules for a route, use overrides.
include
The OpenAPI files to lint, relative to the project directory. By default, the
linter lints every file directly in config/ that ends in .oas.json; it
doesn't look in subdirectories.
Code
Every entry must end in .oas.json, must be a relative path, and must stay
inside the project directory. Glob patterns aren't supported, so list each file.
A file that doesn't exist fails the run with exit code 2.
The CLI still requires at least one .oas.json file directly in config/, so
use include to narrow the files that are linted, not to point at files
elsewhere.
include controls which OpenAPI files are linted. The linter always validates
config/policies.json when it exists.
requiredPolicies
Policies that every route must include. The zuplo:strict and zuplo:security
presets each define a default set; requiredPolicies replaces those defaults
with your own list.
Code
Unlike other rules, requiredPolicies works with any preset, including
zuplo:recommended. Each requirement applies to every route; to require a
policy on only some routes, use overrides or a
custom rule with paths.
Each entry has these fields:
| Field | Description |
|---|---|
id | A name for the requirement. Use it to keep track of entries; results report under zuplo/require-policy. |
policyTypes | Policy types that satisfy the requirement. A route needs at least one policy whose type matches. |
direction | "inbound" or "outbound". Defaults to "inbound". |
severity | "error" or "warn". Defaults to "error". |
message | The message to report. The linter appends the method and path of the route, for example : get /todos. |
The linter matches each policy name in the route's x-zuplo-route.policies list
to its policyType in config/policies.json. An entry in policyTypes matches
that exact type and any type that starts with it followed by a dash, so
"api-key" matches api-key-inbound, and "rate-limit" matches
rate-limit-inbound. Matching starts at the beginning of the type, so
"rate-limit" doesn't match complex-rate-limit-inbound; list
"complex-rate-limit" separately if you use it.
Requirements with "severity": "error" report under the rule ID
zuplo/require-policy. Requirements with "severity": "warn" report under
zuplo/require-policy-warn.
To turn off the preset defaults without adding your own requirements, set an empty list:
Code
The defaults in zuplo:strict and zuplo:security accept these policy types:
- Authentication:
api-key,auth0-jwt-auth,clerk-jwt-auth,cognito-jwt-auth,curity-phantom-token,firebase-jwt-auth,jwt-auth,oauth2-pkce-auth,okta-jwt-auth,open-id-jwt-auth,propel-auth,stytch-jwt-auth,supabase-jwt-auth - Rate limiting:
rate-limit,complex-rate-limit
breakingChanges
Compares each OpenAPI file with the same file on a Git branch, tag, or commit, and reports changes that can break existing API consumers, such as a removed endpoint or a changed type.
Code
| Field | Description |
|---|---|
enabled | Turns detection on. Defaults to false. |
baseline | The branch, tag, or commit SHA to compare against. Defaults to "main". |
severity | "error" or "warn". Defaults to "error". |
See Breaking change detection for how to choose a baseline, what counts as a breaking change, and how to run it in CI.
Command-line flags and the configuration file
Flags take precedence over the configuration file for a single run:
| Flag | Overrides |
|---|---|
--config | Which configuration file to read. |
--fail-on | failOn. |
All other settings come only from the configuration file. See the
zuplo lint CLI reference for every flag.
Next steps
- Custom lint rules — write your own rules and share them with a ruleset file.
- Lint rules reference — what each rule checks.
- Lint in CI/CD — run the linter on every pull request.