Custom Lint Rules
- Applies to
- Gatewayruntime for API, AI, and MCP gateways
- Hosting
- Managed EdgeManaged DedicatedSelf-Hosted
When the built-in rules don't cover a convention your team follows, add your own
in zuplo.lint.jsonc. Custom rules use the
Spectral
ruleset format, so a rule written for Spectral or vacuum works here unchanged.
Write a rule
A rule selects parts of your OpenAPI document with given and checks each one
with a function in then:
zuplo.lint.jsonc
Code
The key, operation-summary-length, is the rule ID. It appears in results, and
you use it to change the rule's severity in
overrides. IDs starting with
zuplo/ are reserved for Zuplo's rules.
| Field | Description |
|---|---|
given | A JSONPath expression, or a list of them, selecting the values to check. |
then | The function to run on each selected value, or a list of functions. Each has function, and optionally field and functionOptions. |
severity | "error", "warn", "info", "hint", or "off". Defaults to "warn". |
message | The text each result starts with. Defaults to description, then to the rule ID. |
description | What the rule checks. Used as the message when message isn't set. |
paths | Report the rule only on these API paths, such as ["/v1/**"]. |
excludePaths | Don't report the rule on these API paths. |
formats | Document formats the rule applies to, such as ["oas3"]. |
then.field checks a property of each selected value instead of the value
itself, so this rule reports every operation without a summary:
Code
Functions
These are the general-purpose functions, the same as Spectral's core functions:
| Function | Reports when the value... | Options |
|---|---|---|
truthy | is missing, empty, false, or 0 | |
falsy | is set to a truthy value | |
defined | is missing | |
undefined | is set | |
pattern | doesn't match match, or matches notMatch | match, notMatch (regular expressions) |
casing | isn't in the given case | type: camel, pascal, kebab, snake, cobol, flat, macro |
length | is shorter than min or longer than max (characters, items, or keys) | min, max |
enumeration | isn't one of values | values |
alphabetical | isn't sorted | keyedBy for lists of objects |
schema | doesn't match a JSON schema | schema |
xor | has more or fewer than one of properties | properties |
A Zuplo route has settings, such as its policies, that the OpenAPI document
alone can't explain, because policies are defined in config/policies.json.
These Zuplo functions read both files:
| Function | Reports a route that... | Options |
|---|---|---|
zuplo:requirePolicy | doesn't have a policy of one of the given types | policyTypes (required), direction (inbound or outbound) |
zuplo:noUndefinedPolicies | references a policy that isn't in config/policies.json | |
zuplo:noEmptyInboundPolicies | has an empty policies.inbound list | |
zuplo:requireRouteLabel | has no label in x-zuplo-route | |
zuplo:requireCorsPolicy | has no corsPolicy in x-zuplo-route |
A Zuplo function checks every route itself, so set "given": "$" and choose the
routes with paths and excludePaths. Only zuplo:requirePolicy uses your
message; the other Zuplo functions write their own.
Run npx zuplo lint --list-rules to see every function and rule available.
Examples
Require a policy on some routes
requiredPolicies applies to
every route. A custom rule with zuplo:requirePolicy applies to the routes you
choose:
Code
Code
policyTypes matches the same way as in requiredPolicies: "api-key" matches
the api-key-inbound policy type. Add one rule per requirement, each with its
own ID and paths.
Enforce a naming convention
Code
Keep internal headers out of the public API
Code
Share rules with a ruleset file
To use the same rules in several projects, put them in a ruleset file and extend
it. A ruleset file is a .json or .jsonc file in the Spectral ruleset format:
lint/team-ruleset.jsonc
zuplo.lint.jsonc
The path is relative to zuplo.lint.jsonc. A ruleset file can extend the
built-in rulesets (vacuum:oas, spectral:oas, vacuum:owasp,
spectral:owasp) but not other files. Rulesets must be local files: the linter
doesn't download rulesets from a URL, so copy a shared ruleset into your
repository, or add it as a package, and reference that path. YAML rulesets
aren't supported; convert them to JSON.
zuplo.lint.jsonc can still change the severity or scope of a rule from a
ruleset file, the same as any other rule.
Next steps
- Lint configuration — rulesets, severities, and overrides.
- Lint rules reference — the rules in the Zuplo presets.