An ultra-super-fast, lightweight OpenAPI, AsyncAPI and JSON Schema linter and quality checking tool inspired by Spectral.
It's fully compatible with existing Spectral rulesets.
Install using the official homebrew tap
brew install --cask daveshanley/vacuum/vacuum
Install using npm
npm i -g @quobix/vacuum
Install using yarn
yarn global add @quobix/vacuum
curl -fsSL https://quobix.com/scripts/install_vacuum.sh | shTo avoid GitHub API rate limiting in automated environments, set a GitHub token:
# Using repository token (GitHub Actions)
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} curl -fsSL https://quobix.com/scripts/install_vacuum.sh | sh
# Using personal access token
GITHUB_TOKEN=your_github_token curl -fsSL https://quobix.com/scripts/install_vacuum.sh | sh- name: Install vacuum
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Increases rate limit from 60 to 5000 requests/hour
run: |
curl -fsSL https://quobix.com/scripts/install_vacuum.sh | shNote: The GitHub token prevents intermittent installation failures in CI/CD environments caused by API rate limiting. No additional permissions are required, the token only accesses public repository information.
Install using Docker
The image is available at: https://hub.docker.com/r/dshanley/vacuum
docker pull dshanley/vacuum
Multi-platform support: Docker images are available for both
linux/amd64andlinux/arm64architectures, including native ARM64 support for Apple Silicon Macs.
To run, mount the current working dir to the container and use a relative path to your spec, like so
docker run --rm -v $PWD:/work:ro dshanley/vacuum lint <your-openapi-spec.yaml>
Alternatively, you can pull it from
GitHub packages.
To do that, replace dshanley/vacuum with ghcr.io/daveshanley/vacuum in the above commands.
If you have Go 1.25 or newer installed, you can use go run to build and run it:
go run github.com/daveshanley/vacuum@latest lint <your-openapi-spec.yaml>
If vacuum was installed with Homebrew, npm, or the shell installer, run:
vacuum upgradevacuum will detect the install path and use the matching upgrade mechanism when it can. Normal commands may also show an
update notice when a newer stable release is available. The passive check keeps only a small temporary cache in the OS temp
directory, refreshed at most every 12 hours. Use --no-update-check or VACUUM_NO_UPDATE_CHECK=true to skip that check.
If your company is using vacuum, please consider supporting this project,
like our very kind sponsors, past and present:
Need help? Have a question? Want to share your work? Join our discord and come say hi!
🔥 New in v0.29 🔥: Lint AsyncAPI 3 documents in vacuum
AsyncAPI is now a first-class document type in vacuum, alongside OpenAPI and JSON Schema.
Use the normal lint command and vacuum will detect AsyncAPI 3 documents automatically.
- Read more about AsyncAPI linting in vacuum
- See the default AsyncAPI ruleset
- See every built-in AsyncAPI rule
- Generate an AsyncAPI ruleset
- See the AsyncAPI functions
🔥 New in v0.28 🔥: Lint JSON Schema documents in vacuum
A new schema command has been added that opens up vacuum to JSON Schema specific linting rules and checks!
With a whole new set of functions and rules, specifically for JSON Schema documents.
Try it out on your own JSON Schema docs! with vacuum schema
- Read more about JSON Schema linting in vacuum
- Read the available
schemacommand options - See the new recommended JSON Schema ruleset
v0.27: Generate OpenAPI Documentation in vacuum
A new docs command will generate the best OpenAPI Documentation you ever saw! Now vacuum has
integrated with the printing press, it means
generating documentation with built-in linting results is available right inside vacuum.
Try it out! instead of the lint command try the docs command instead.
- Read more about API Docs in vacuum
- Read the
docscommand reference - Read more about the printing press
- Learn about rendering modes
- Learn about diagnostics mode
- Learn about API catalogs
- Learn about agentic AI output
- Learn about generated outputs
v0.25: Generate Open Collections from OpenAPI files
A new open-collection command will generate an Open Collection Workflow folder
(or bundled file)
Open Collection is a new standard for defining and sharing API collections. vacuum supports it natively.
v0.24: TURBO MODE
A huge tune up of hundreds of systems across the stack has resulted in significant speed boosts and improved memory performance.
| Spec | Size | v0.23 | Turbo | Speedup |
|---|---|---|---|---|
| petstore.yaml | 21KB | 0.03s | 0.02s | 33% |
| mistral.yaml | 292KB | 0.17s | 0.10s | 41% |
| neon.yaml | 370KB | 0.21s | 0.08s | 62% |
| ld.yaml | 1.9MB | 0.93s | 0.31s | 67% |
| plaid.yml | 2.9MB | 1.39s | 0.53s | 62% |
| stripe.yaml | 6.1MB | 3.11s | 0.85s | 73% |
The bigger the spec, the faster vacuum runs!
New -T flag will enable turbo mode. Go ultra-fast!
v0.23: OpenAPI Overlay Support
Ever needed to tweak an OpenAPI spec for different environments without maintaining multiple copies?
Maybe swap out server URLs between dev, staging, and production? Or perhaps strip out internal endpoints before publishing the API docs?
OpenAPI Overlays are the answer. They let us make non-destructive modifications to specs using JSONPath expressions to
target exactly what we want to change. vacuum now supports a new apply-overlay command.
v0.22: Async Functions / Promises, Fetch & Batch mode in Custom JS Functions
Do you want to call remote APIs in your vacuum javascript functions? What about async processing or the ability to use Promises?
vacuum now has its own event loop and will happily support async and await. Combined with a full implementation of Fetch.
Also added Batch Mode. This allows custom functions to receive the entire list of nodes, instead of firing the function for each result, so you can send all your data off to an API or an LLM, and have the ability parse and process everything at once vs individually.
- Read all about async JS functions in vacuum
- Learn about using fetch in vacuum
- find out how batch mode works
See all the documentation at https://quobix.com/vacuum
- Installing vacuum
- About vacuum
- Why should you care?
- Concepts
- FAQ
- Linting AsyncAPI
- Ignoring lint results
- Generate API Docs
- Linting JSON Schema
- CLI Commands
- Developer API
- Rules
- Functions
- Understanding RuleSets
vacuum can suck all the lint of a 5mb API description in milliseconds.
Designed to reliably lint API descriptions, very, very quickly. Including very large ones. Spectral can be quite slow when used as an API and does not scale for enterprise applications.
vacuum will tell you what is wrong with your spec, why, where, and how to fix it.
vacuum will work at scale and is designed as a CLI (with a web or console UI) and a library to be consumed in other applications.
vacuum comes with an interactive dashboard (vacuum dashboard <your-openapi-spec.yaml>) allowing you to explore
rules and violations in a console, without having to scroll through thousands of results.
To read about the dashboard, see the dashboard command docs.
vacuum can generate an easy to navigate and understand HTML report. Like the dashboard you can explore broken rules and violations, but in your browser.
No external dependencies, the HTML report will run completely offline.
Supports OpenAPI Version 2, OpenAPI Version 3+, AsyncAPI 3, and JSON Schema documents
You can use either YAML or JSON, vacuum supports both formats.
Vacuum can be used with pre-commit.
To do that, add to your .pre-commit-config.yaml:
repos:
- repo: https://github.com/daveshanley/vacuum
rev: # a tag or a commit hash from this repo, see https://github.com/daveshanley/vacuum/releases
hooks:
- id: vacuumSee the hook definition here for details on what options the hook uses and what files it checks by default.
If no filenames or more than one filename in your repository matches the default files pattern in the hook definition,
the pattern needs to be overridden in your config so that it matches exactly one filename to lint at a time.
To lint multiple files, specify the hook multiple times with the appropriate overrides.
vacuum html-report is included in official release binaries. If you install via go install github.com/daveshanley/vacuum@<version>, the command is compiled without the HTML report UI bundles. To enable it from source, run ./scripts/build-ui-assets.sh and build with -tags html_report_ui.
./vacuum html-report <your-openapi-spec.yaml | vacuum-report.json.gz> <report-name.html>
You can replace report-name.html with your own choice of filename. Open the report
in your favorite browser and explore the results.
./vacuum lint -d <your-openapi-spec.yaml>
./vacuum lint -d <spec1.yaml> <spec2.yaml> <spec3.yaml>
./vacuum lint -d some/path/**/*.yaml
./vacuum lint -d -s <your-openapi-spec.yaml>
./vacuum lint -d -e <your-openapi-spec.yaml>
./vacuum lint -d -c schemas <your-openapi-spec.yaml>
The options here are:
examplesoperationsinformationdescriptionsschemassecuritytagsvalidationowasp
If you're already using Spectral JSON reports, and you want to use vacuum instead, use the spectral-report command
./vacuum spectral-report <your-openapi-spec.yaml> <report-output-name.json>
The report file name is optional. The default report output name is vacuum-spectral-report.json
Vacuum reports are complete snapshots in time of a linting report for a specification. These reports can be 'replayed'
back through vacuum. Use the dashboard or the html-report commands to 'replay' the report and explore the results
as they were when the report was generated.
./vacuum report -c <your-openapi-spec.yaml> <report-prefix>
The default name of the report will be vacuum-report-MM-DD-YY-HH_MM_SS.json. You can change the prefix by supplying
it as the second argument to the report command.
Ideally, you should compress the report using -c. This shrinks down the size significantly. vacuum automatically
recognizes a compressed report file and will deal with it automatically when reading.
When using compression, the file name will be
vacuum-report-MM-DD-YY-HH_MM_SS.json.gz. vacuum uses gzip internally.
You can ignore specific linting errors by providing an --ignore-file argument to commands that run or replay lint results, including lint, report, spectral-report, html-report, dashboard, and docs.
./vacuum lint --ignore-file <path-to-ignore-file.yaml> -d <your-openapi-spec.yaml>
./vacuum report --ignore-file <path-to-ignore-file.yaml> -c <your-openapi-spec.yaml> <report-prefix>
The ignore-file should point to a .yaml file that contains exact result paths or JSONPath expressions to be ignored by vacuum. The structure of the YAML file is as follows:
<rule-id-1>:
- <json_path_to_error_or_warning_1>
- <json_path_to_error_or_warning_2>
<rule-id-2>:
- <json_path_to_error_or_warning_1>
- <json_path_to_error_or_warning_2>
...
Ignoring errors is useful for when you want to implement new rules to existing production APIs. In some cases, correcting the lint errors would result in a breaking change. Having a way to ignore these errors allows you to implement the new rules for new APIs while maintaining backwards compatibility for existing ones.
This is an early, but working console UI for vacuum. The code isn't great, it needs a lot of clean up, but if you're interested in seeing how things are progressing, it's available.
./vacuum dashboard <your-openapi-spec.yaml | vacuum-report.json.gz>
If you're already using Spectral and you have your own custom ruleset, then you can use it with vacuum!
The lint, dashboard, docs, html-report, report, and spectral-report commands all accept a -r or --ruleset flag, defining the path to your ruleset file.
All rules turned off
./vacuum lint -r rulesets/examples/norules-ruleset.yaml <your-openapi-spec.yaml>
Only recommended rules
./vacuum lint -r rulesets/examples/recommended-ruleset.yaml <your-openapi-spec.yaml>
Enable specific rules only
./vacuum lint -r rulesets/examples/specific-ruleset.yaml <your-openapi-spec.yaml>
Custom rules
./vacuum lint -r rulesets/examples/custom-ruleset.yaml <your-openapi-spec.yaml>
All rules, all of them!
./vacuum lint -r rulesets/examples/all-ruleset.yaml <your-openapi-spec.yaml>
vacuum has two resolved-execution controls:
- Per rule:
resolved - Per run:
--resolve-all-refsormotor.ExecutionOptions{ResolveAllRefs: true}
In YAML/JSON rulesets, resolved defaults to true. In that mode, given, matched nodes, Index, and SpecInfo come from the dereferenced document, but context.Document stays unresolved for compatibility.
Set resolved: false when a rule needs the raw document, for example to inspect $ref directly. If you build model.Rule values in Go, set Resolved explicitly.
rules:
response-has-content:
given: "$.paths[*][*].responses['404']"
resolved: false
then:
field: content
function: defined--resolve-all-refs forces every rule into resolved execution, overrides resolved: false, and also supplies resolved context.Document to rule functions.
Use --nested-refs-doc-context or motor.ExecutionOptions{NestedRefsDocContext: true} for split specs where an external file contains another relative $ref. That makes nested relative refs resolve from the referenced document instead of the root document. It only affects resolved execution, including runs forced by --resolve-all-refs.
You can configure vacuum using a configuration file named vacuum.conf.yaml
By default, vacuum searches for this file in the following locations:
- Working directory
$XDG_CONFIG_HOME, when set${HOME}/.config, when$XDG_CONFIG_HOMEis not set
You can also specify a path to a file using the --config flag
Global flags are configured as top level nodes
time: true
base: 'http://example.com'
...Command specific flags are configured under a node with the commands name
...
lint:
silent: true
...You can configure global vacuum flags using environment variables in the form of: VACUUM_<flag>
If a flag, has a - in it, replace with _
If you have a rule that doesn't need a human to look at it, and the change can be reliably automated you can configure an AutoFixFunction on the rule. When you then run the lint command you can pass the --fix flag and the violation will be automatically fixed.
- Define a rule that has an
autoFixFunction, e.g.:
rules:
use-compatible-extensions:
autoFixFunction: useExtensibleEnum
description: Prefer compatible extensions
id: use-compatible-extensions
given: "$.components.schemas[?@.enum]"
severity: warn
message: Use x-extensible-enum instead of enum for better compatibility
then:
field: enum
function: falsyThis rule flags any usage of enum and recommends they are updated to x-extensible-enum.
A simple change which can be easily auto fixed!
- Create a function which performs the auto-fix.
func useExtensibleEnum(
node *yaml.Node,
document *yaml.Node,
context *model.RuleFunctionContext,
) (*yaml.Node, error) {
if node.Kind != yaml.MappingNode {
return node, nil
}
for i := 0; i < len(node.Content); i += 2 {
if i+1 >= len(node.Content) {
break
}
keyNode := node.Content[i]
if keyNode.Value == "enum" {
keyNode.Value = "x-extensible-enum"
return node, nil
}
}
return node, nil
}Note
The auto fix function must satisfy the AutoFixFunction type.
It should take in the *yaml.Node of the violation, the root *yaml.Node of the document and the RuleFunctionContext.
It should return the fixed *yaml.Node and an error.
- Configure your
RuleSetExecutionto use the auto fix function.
func Lint(rulesFile string, specFile string) error {
rules, err := rulesets.LoadLocalRuleSet(ctx, rulesFile)
if err != nil {
return fmt.Errorf("error loading ruleset: %w", err)
}
rs := rulesets.BuildDefaultRuleSetsWithLogger(slog.Logger).
GenerateRuleSetFromSuppliedRuleSet(rules)
// NOTE: only showing the fields on the RuleSetExecution relevant to auto-fixing.
results := motor.ApplyRulesToRuleSet(&motor.RuleSetExecution{
AutoFixFunctions: map[string]model.AutoFixFunction{
"useExtensibleEnum": useExtensibleEnum,
},
ApplyAutoFixes: true,
RuleSet: rs,
})
// Write back to file if fixes were applied
if len(lintResults.FixedResults) > 0 && autoFix {
fileInfo, _ := os.Stat(specFile)
err = os.WriteFile(specFile, result.ModifiedSpec, fileInfo.Mode())
if err != nil {
return fmt.Errorf("failed to write file %s: %w", c.file, err)
}
}
return nil
}When the auto fix function runs, if it returns an error the fix will not be applied, the error will be logged, and the violation will be reported in the standard results.
If the auto fix function succeeds the yaml node flagged by the violation will be replaced with the transformed version returned by the auto fix function.
Tip
When using vacuum as a library You can access the fixed yaml content in the RuleSetExecutionResult.ModifiedSpec, and choose where to write the file.
When using vacuum as a cli, the --fix flag will overwrite the spec file in place, and --fix-file flag lets you specify an alternative file to write the content to, if you want to compare the outputs.
Logo gopher is modified, originally from egonelbre
