vacuum: an OpenAPI linter that speaks Spectral rulesets
vacuum is the worlds fastest and most versatile OpenAPI, Arazzo, AsyncAPI & JSON Schema linter, docs generator and toolkit. It tears through API specs at light speed. 100% compatible with Spectral rulesets, and OpenAPI 3.0, 3.1 and 3.2
At a glance
- What is it?
- vacuum is a Go linter and quality checking tool for OpenAPI, AsyncAPI, Arazzo, and JSON Schema documents, and it accepts Spectral-style YAML and JSON rulesets, which is the point: a team with a Spectral configuration can keep it. The tool is also a documentation generator, with a terminal interface, an HTML report, and a language server.
- Who is it for?
- Adopt vacuum if you already lint OpenAPI with Spectral and want the same rulesets in a faster binary that also generates documentation. Do not adopt it if your ruleset relies on Spectral functions that vacuum has not reimplemented, since the README documents the compatibility limits rather than claiming parity.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The pitch, and what is actually new
vacuum is described as the world's fastest OpenAPI, AsyncAPI, Arazzo, and JSON Schema linter, and the project description is blunter about it: an ultra-super-fast, lightweight linter and quality checking tool inspired by Spectral.
The key qualifier in the repository description is 100% compatible with Spectral rulesets, and the README is more careful about it, saying it supports Spectral-style YAML and JSON rulesets and pointing to a Spectral migration section for compatibility limits and built-in alternatives. That distinction is worth holding on to, because a Spectral ruleset is a configuration file that may reference custom functions, and a linter that reimplements the functions has to decide what to do with the ones it does not have.
The document types have grown over the project's life, and the release history in the README tracks it. Version 0.28 added a schema command for JSON Schema specific linting rules and checks, with a whole new set of functions and rules. Version 0.29 made AsyncAPI a first-class document type alongside OpenAPI and JSON Schema, with the normal lint command detecting AsyncAPI 3 documents automatically. And version 0.33, the newest, added Arazzo workflow linting and a new or function.
Arazzo is the interesting one, because Arazzo describes multi-step API workflows rather than a single API, and it is the document type that most tools ignore. The README says Arazzo 1.0 and 1.1 workflows are now a first-class document type, that you use vacuum lint workflows.yaml and vacuum detects Arazzo automatically, and that results work in the normal reports, dashboard, and editor diagnostics.
The project is MIT licensed, written in Go, and the home page is quobix.com/vacuum. The last push to the repository was on 2026-10-18, and the recent releases are v0.33.1 from 2026-10-15, v0.33.0 from 2026-10-10, and v0.32.0 from 2026-10-07, so the project is shipping often.
Five ways to install it, and the CI rate limit problem
The installation section is long, and each route exists for a reason.
Homebrew, through an official tap:
brew install --cask daveshanley/vacuum/vacuumThen npm, which is unusual for a Go tool and exists because the HTML report user interface is a web asset:
npm i -g @quobix/vacuumThen yarn with yarn global add @quobix/vacuum, and a curl installer:
curl -fsSL https://quobix.com/scripts/install_vacuum.sh | shThe curl installer has a specific problem in CI, and the README addresses it directly. To avoid GitHub API rate limiting in automated environments you set a GitHub token:
# Using personal access token
GITHUB_TOKEN=your_github_token curl -fsSL https://quobix.com/scripts/install_vacuum.sh | shThe GitHub Actions example sets the token in the environment, and the comment notes that it increases the rate limit from 60 to 5000 requests per hour. The note is also explicit that no additional permissions are required, because the token only accesses public repository information. That is a small but good practice, since the default GITHUB_TOKEN in Actions would be over-scoped for this.
Docker is the fifth route, with multi-platform support for linux/amd64 and linux/arm64, including native ARM64 for Apple Silicon Macs, and a read-only mount of the working directory:
docker run --rm -v $PWD:/work:ro dshanley/vacuum lint <your-openapi-spec.yaml>And for Go users, with Go 1.26 or newer, go run github.com/daveshanley/vacuum@latest lint <your-openapi-spec.yaml> builds and runs it in one step.
The `or` function, and the Arazzo ruleset
The newest feature worth explaining is the or core function, and the reason it exists is a specific gap in the JSON Schema and Spectral function set.
The or function checks that at least one of two or more properties exists. You use properties: [title, summary, description] to require one of those fields. And the detail that makes it useful in practice is that a property counts as present even when its value is null, false, 0, or an empty string.
That last rule is the whole point. A naive existence check on an optional field would fail for a description that is explicitly empty, and requiring a non-empty description is a different and much less common rule. So the function expresses the constraint that actually appears in API descriptions: the field must be there, and its content is a separate matter.
The Arazzo ruleset that ships with it is documented separately, with recommended rules for workflow structure, references, parameters, expressions, and linked source documents. The same release also fixes AsyncAPI header, server, and version checks, and improves result locations for YAML aliases and shared references.
That last improvement is worth a moment, because YAML anchors are a common way to share a schema fragment across a large document, and a linter that reports the wrong location for a problem inside an anchored block sends you to the wrong file. The improvement is to report the location of the reference rather than the definition.
The repository layout, and what each part does
The top level of the repository is a map of the tool, and the directory names correspond to the document types and the outputs.
There is openapi/, asyncapi/, arazzo/, and jsonschema/ for the four document types, which matches the first-class treatment each gets. There is functions/ for the function library that rules reference, and rulesets/ for the built-in rulesets, one per document type. There is model/ for the shared data structures, and parser/ for the parsing layer.
Then there are the output surfaces. The tui/ directory is the terminal interface, the html-report/ directory is the browser report, and vacuum-report/ is another reporting piece, with color/ handling the terminal colours and logging/ the logging. The motor/ directory is the rule execution engine, and config/ holds the configuration, with viper and cobra from the dependency list for configuration and command line handling.
Two directories are for tooling rather than for the linter itself. The language-server/ directory provides the editor integration that the README refers to as editor diagnostics, which is what makes the results usable in an IDE rather than only in a terminal. And the plugin/ directory is the extension point.
There is also a benchmarks/ directory, a statistics/ directory, an upgrade/ directory for the version check, and an npm-install/ directory that holds the postinstall script the npm package uses to fetch the right binary for the platform.
The top level also has ARAZZO.md and BUILD_PACKAGING.md, and a goreleaser.yaml for the release build, plus a pre-commit-hooks.yaml, so the project can be used as a pre-commit hook.
The build, the HTML report, and the Go dependencies
The Makefile is short and the build has an unusual shape, because there are two languages in the artefact.
The build-ui target runs a shell script that builds the HTML report UI assets, and the build target depends on it before building the binary to bin/vacuum. The test target depends on the UI build too, and runs go test with the html_report_ui tag and a coverage profile.
That tag is the mechanism. The Go binary is built with -tags html_report_ui to embed the web assets into the executable, which is what lets a single binary produce a self-contained HTML report with no side files. The Dockerfile does the same thing with a separate node stage that runs the UI asset build script, then a Go builder stage that compiles with the tag and the version and date stamped into the binary through ldflags.
The final image is debian:bookworm-slim with the binary copied to /usr/local/bin and a docker-entrypoint.sh, and the working directory is /work, which is the mount point for your spec.
The Go dependency list is where the linter's actual capabilities come from, and the pb33f libraries are the centre of it. libopenapi is the OpenAPI parsing and model library, libopenapi-validator is the validation engine, libasyncapi is the AsyncAPI support, and jsonschema is the JSON Schema engine. There is also doctor, which is the medical record style validator for OpenAPI, and jsonpath for path expressions.
The terminal interface is built on the charm libraries, with bubbletea for the event loop, bubbles for the components, and lipgloss for styling, plus alecthomas/chroma for syntax highlighting and charmbracelet/glamour for rendering markdown in the terminal. There is a dop251/goja JavaScript runtime, which is how custom functions written in JavaScript are executed, and goja_nodejs to give those functions a Node-style API. The rest is a fairly standard set: cobra and pflag for the command line, viper for configuration, mapstructure for decoding it, xxhash for hashing, and go-humanize for formatting numbers in the output.
Editorial conclusion
Adopt vacuum if you already lint OpenAPI with Spectral and want the same rulesets in a faster binary that also generates documentation. Do not adopt it if your ruleset relies on Spectral functions that vacuum has not reimplemented, since the README documents the compatibility limits rather than claiming parity. Verify first that the rules you depend on exist in vacuum's built-in set, and read the migration notes before pointing it at an existing Spectral configuration.
Frequently asked questions
What is vacuum and which document types does it lint?
It is a Go linter and quality checking tool for OpenAPI, AsyncAPI, Arazzo, and JSON Schema documents, and the type is detected automatically so you use the normal lint command. JSON Schema got its own schema command in v0.28, AsyncAPI became first-class in v0.29, and Arazzo workflow linting arrived in v0.33.
Is vacuum fully compatible with Spectral rulesets?
It supports Spectral-style YAML and JSON rulesets, and the README points to a Spectral migration section for the compatibility limits and the built-in alternatives, so a ruleset that relies on custom Spectral functions may not behave identically.
How do I install vacuum?
With brew install --cask daveshanley/vacuum/vacuum, with npm i -g @quobix/vacuum, with yarn global add @quobix/vacuum, with the curl installer, with the dshanley/vacuum Docker image, or with go run github.com/daveshanley/vacuum@latest if you have Go 1.26 or newer.
What does the new or function in vacuum do?
It checks that at least one of two or more properties exists, so properties: [title, summary, description] requires one of those fields. A property counts as present even when its value is null, false, 0, or an empty string, which distinguishes existence from non-emptiness.
Why does the vacuum install script want a GitHub token in CI?
To avoid GitHub API rate limiting, which causes intermittent installation failures in CI/CD environments. The token increases the rate limit from 60 to 5000 requests per hour, and no additional permissions are required because it only accesses public repository information.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/daveshanley-vacuum)Was this useful?