An Arazzo workflow is a promise. It says “call this operation, take that value, pass it to the next step”. The API it describes lives in a different file, owned by a different team, changing on a different schedule.
So workflows rot. Somebody renames an operationId, nobody tells the workflow, and you find out in production.
Since v0.33, vacuum treats Arazzo as a first-class document type alongside OpenAPI, AsyncAPI and JSON Schema. It reads
the workflow, opens every source it points at, and checks that the promise still holds.
There is no new command to learn. When vacuum lint sees an Arazzo document, it selects the built-in
arazzo-recommended ruleset and gets to work.
Supported versions
vacuum supports Arazzo 1.0 and 1.1, as YAML or JSON.
| Arazzo version | Format |
|---|---|
| Arazzo 1.0 and 1.1 | arazzo |
| Arazzo 1.0 | arazzo1_0 |
| Arazzo 1.1 | arazzo1_1 |
Any other version is a tool error, and vacuum will say so.
What gets checked?
The heavy lifting is done by libopenapi-validator v0.16.0. It runs
once per lint, and every finding comes back as a normal vacuum result, with a rule ID, a line number and a JSONPath.
| Check | What it proves |
|---|---|
| Structure | The document is structurally valid Arazzo. arazzo-structure |
| Unique IDs | Workflow, step and source IDs are unique. arazzo-duplicate-id |
| References | Operations, workflows and steps being referenced actually exist. arazzo-reference |
| Parameters | Parameters match the operation they are sent to. arazzo-parameter |
| Expressions | Runtime expressions are valid where they are used. arazzo-expression |
| Selectors | Selectors and replacement targets parse. arazzo-selector |
| Dependencies | Workflow prerequisites exist and don’t loop. arazzo-dependency |
| Input schemas | Workflow input schemas are valid JSON Schema. arazzo-input-schema |
| Source types | Sources are the type the workflow says they are. arazzo-source-type |
On top of that sit the authoring rules, the ones that nag you about descriptions, summaries and script tags. See all Arazzo rules for the complete list.
vacuum validates workflows. It does not run them. No requests are sent, no actions fire and no scripts execute.
Linked sources
This is the good bit.
A workflow lists its APIs under sourceDescriptions. Those sources can be OpenAPI, AsyncAPI, or another Arazzo document.
arazzo: 1.1.0
info:
title: Read and publish
version: '1.0'
description: Use HTTP, event, and shared workflow sources.
summary: Read and publish.
sourceDescriptions:
- name: api
url: api.yaml
type: openapi
- name: events
url: events.yaml
type: asyncapi
- name: flows
url: flows.yaml
type: arazzo
workflows:
- workflowId: run
description: Read a pet and publish its event.
summary: Process a pet.
steps:
- stepId: read
description: Read a pet.
operationId: $sourceDescriptions.api.read
- stepId: publish
description: Publish an event.
operationId: $sourceDescriptions.events.publish
action: send
- stepId: remote
description: Run the shared workflow.
workflowId: $sourceDescriptions.flows.remote
vacuum loads all three and checks every step against the real thing. Rename read in api.yaml and you get this:
workflows.yaml:18:22 ✗ operation "read" does not exist in source "api"
Problems inside a linked Arazzo source are reported too, at the file and line where they were written. Add
--snippets and vacuum prints the offending lines from that source, so you are never squinting at the wrong file.
Where does vacuum look?
| Source | Behavior |
|---|---|
Relative url |
Resolved from the workflow file’s directory |
--base ./specs |
Resolved from that directory (or URL) instead |
http(s):// |
Fetched by default |
--remote=false |
Remote sources are skipped, local files still load |
--stdin |
No file to resolve from, so pass --base |
Only local files and HTTP(S) sources are supported. The existing certificate, CA and TLS flags apply to source requests, exactly as they do for OpenAPI references.
When a check can’t finish
Some things can’t be proven by reading files. An XPath selector. A condition that only makes sense once a real response shows up. A remote source you told vacuum not to fetch.
I didn’t want vacuum to guess, and I didn’t want it to stay quiet either. So it tells you with arazzo-validation-incomplete, a warning pinned to the exact spot it could not check.
workflows.yaml:18:22 â–² linked-target: source document is unavailable
A warning here does not mean the workflow is broken. It means “I couldn’t check this bit”.
If an unchecked workflow should fail your build, say so.
Exit codes
| Code | Meaning |
|---|---|
0 |
No findings at or above the fail severity (error by default) |
1 |
Findings at or above the fail severity |
2 |
Input or tool error |
A 2 means vacuum could not do the job at all. The workflow would not parse, the version is unsupported, the run timed
out, or a source that lookup was allowed to fetch could not be found.
Rulesets
The default ruleset is arazzo-recommended, which is 16 rules.
extends: [[vacuum:arazzo, recommended]]
To run all 18 built-in Arazzo rules, use all, or pass --hard-mode on the command line.
extends: [[vacuum:arazzo, all]]
To start from nothing and switch on only what you want, use off.
extends: [[vacuum:arazzo, off]]
rules:
arazzo-reference: true
vacuum:arazzo, arazzo-recommended and spectral:arazzo are interchangeable names for the same native ruleset.
You can generate ready-to-edit rulesets from the CLI.
Learn more about the Arazzo recommended ruleset and the all Arazzo rules ruleset.
Custom Arazzo rules
Custom rules work the way they always have. Scope them with an Arazzo format so they stay out of your OpenAPI and AsyncAPI documents.
extends: [[vacuum:arazzo, recommended]]
rules:
arazzo-validation-incomplete: error
arazzo-info-summary: off
workflow-title:
description: Workflow documents need a title
severity: warn
formats: [arazzo]
given: $.info
then:
field: title
function: truthy
That ruleset promotes incomplete checks to errors, switches off a hint I don’t care about, and adds a rule of my own. Core functions, custom JavaScript functions and custom Go functions are all available.
Ignoring results
Ignore files and x-lint-ignore both work with Arazzo.
steps:
- stepId: read
description: Call the read operation.
x-lint-ignore: [arazzo-reference]
operationId: $sourceDescriptions.api.comingSoon
One thing to know. A finding that comes from a linked source is ignored in that source. An x-lint-ignore in your
workflow can’t hide a problem that lives in somebody else’s file.
Reports and editors
Arazzo results flow through the same surfaces as every other vacuum result: console output, the dashboard, report, html-report, spectral-report compatible JSON and the language server.
Findings from linked sources keep their own file and line in every one of them. In an editor, they show up on the workflow’s source declaration and link through to the real line.
--original works as well. vacuum re-checks both source graphs, so an API that changed underneath an untouched
workflow still produces a new finding.
Coming from Spectral?
spectral:arazzo is accepted as an alias, so existing rulesets load without edits.
It selects vacuum’s native rules. It does not run Spectral’s JavaScript functions, and it makes no promise of identical rule IDs or messages for the semantic checks. If you override those, move the overrides across.
| Spectral check | In vacuum |
|---|---|
| Unique IDs | Workflow and step ID uniqueness is arazzo-duplicate-id |
| Prerequisites | Workflow dependsOn checks are arazzo-dependency |
| Descriptions | Description and summary rule IDs are unchanged |
Run your existing custom rules and ignore entries against a generated report before you trust the migration.
What’s not here (yet)
| Feature | Arazzo support |
|---|---|
| Running workflows | No. vacuum is a linter. |
bundle |
No. It remains an OpenAPI-only command. |
apply-overlay |
No. It remains an OpenAPI-only command. |
vacuum docs |
No. Generated docs cover OpenAPI and AsyncAPI. |
| Breaking changes | No. Findings are compared with --original, change summaries are OpenAPI-only. |
Using it as a library
result := motor.ApplyRulesToRuleSet(&motor.RuleSetExecution{
Spec: workflowBytes,
SpecFileName: "specs/workflows.yaml",
AllowLookup: true,
RuleSet: rulesets.BuildDefaultRuleSets().GenerateArazzoRecommendedRuleSet(),
})
defer result.ReleaseOwnedResources()
// Handle result.Errors before reading result.Results.
AllowLookup and Base control where sources are loaded from. Use ApplyRulesToRuleSetWithOptions to supply your own
context, a total timeout, or a rule concurrency limit.
A ruleset with only custom rules never invokes the validator and never loads a source.
