Lint & render Arazzo workflows. Single Go binary. Eco-designed. Accessible by default.
arazzo-maestro is a CLI that turns Arazzo workflow specifications into something useful for the rest of your team:
lint: validate Arazzo files against the official JSON Schema, internal semantic rules (unique IDs,$stepsreferences), and cross-file checks against the referenced OpenAPI contracts.view: generate a standalone HTML page per workflow (or a Mermaid flowchart with--format mermaid), no server, no build, no JavaScript framework. Open in any browser, commit to a docs folder, ship to GitHub Pages.test: generate runnable tests from a workflow. End-to-end withtest gen e2e(Hurl), or generate and run them against an endpoint with an optional HTML report (test run e2e); load/performance withtest gen perf(k6).
*.arazzo.yaml (Arazzo) βββββ βββΊ lint β exit 0/1 + structured findings
ββββΊ arazzo-maestro βββΌββΊ view β dist/*.html (standalone)
*-openapi.yaml / *-api.yaml β€ βββΊ test e2e β dist/e2e/hurl/*.hurl (+ run / HTML report)
themes.yml (opt.) βββ βββΊ test perf β dist/perf/k6/*.k6.js
| Need | What we do | What we don't try to be |
|---|---|---|
| Validate Arazzo files in CI | β
Single binary, deterministic, offline. lint β exit code + parseable findings |
An IDE plugin |
| Share workflows with non-devs | β Standalone HTML, no IDE, no auth | A live editor |
| Cross-file integrity (operationId exists?) | β
Reads sourceDescriptions.url, indexes operations, validates references |
A full OpenAPI validator |
| Turn a workflow into runnable tests | β
test gen/run e2e emits Hurl files ({{baseUrl}}, captures, asserts), runs them, writes an HTML report; test gen perf emits k6 scripts |
A workflow runtime/orchestrator |
| Eco-designed output | β 1 network request, system fonts, ~29 kB HTML | A pixel-perfect design system |
| Accessibility-first | β
WCAG 2.2 AA contrasts, semantic HTML, aria-hidden on decoratives |
An a11y testing tool |
See "What makes us different" below for the longer take.
# Install (Go 1.25+)
go install github.com/emmanuelperu/arazzo-maestro/cmd/arazzo-maestro@latest
# Or build the Docker image locally (~20 MB FROM scratch)
docker build --build-arg VERSION=0.4.0 -t arazzo-maestro:0.4.0 .
# Mount cwd into /work AND set it as workdir, so relative paths
# (e.g. examples/shop.arazzo.yaml, dist/) resolve against your host cwd.
docker run --rm -v "$PWD":/work -w /work arazzo-maestro:0.4.0 \
view examples/shop.arazzo.yaml
# Lint
arazzo-maestro lint examples/shop.arazzo.yaml
# Render every examples/*.arazzo.yaml in light + dark themes
make dist
open dist/shop/light/index.htmlThe repo ships with three demo Arazzo files in examples/:
shop.arazzo.yaml, happy-path checkout + a retry-on-failure path (showcasesonFailure: retry). Referencesshop-openapi.yaml(scored 100/100 by Vacuum).checkout-branching.arazzo.yaml, single payment step that branches viaonSuccess: goto/onFailure: gototo a confirm or cancel step. Referencescheckout-branching-api.yaml.auth.arazzo.yaml, authenticated scenario showing where test data comes from: credentials as run-time inputs, token captured from the login step, order id self-provisioned from the listing. Referencesauth-api.yaml(scored 100/100 by Vacuum).
The happy-path-checkout workflow rendered by view, in the light and dark themes (click to enlarge):
| Light | Dark |
|---|---|
![]() |
![]() |
Portrait is the default. --layout landscape lays the same workflow out as a horizontal, sideways-scrolling row (START left, steps in between, END right), handy for wide screens and single-slide screenshots:
--format mermaid writes one .mmd flowchart per workflow instead of HTML: theme-agnostic text that renders inline on GitHub and in IDEs. Success paths are solid, failure paths dotted, retries loop back. payment-refused-path from shop.arazzo.yaml:
flowchart TD
wfStart([Start])
s0["01 add-to-cart<br/>addToCart"]
s1["02 pay-refused<br/>processPayment"]
wfEnd([End])
wfStart --> s0
s0 --> s1
s1 --> wfEnd
s1 -. "retry x2 after 2s" .-> s1
s1 -. "on failure" .-> wfEnd
# Render every examples/*.arazzo.yaml into dist/<workflow>/{light,dark}/
make distThe Makefile iterates over examples/*.arazzo.yaml: adding a new file requires no change to the build command.
π Try the HTML output locally:
make distrenders every example intodist/<workflow>/{light,dark}/(the directory is gitignored).
- JSON Schema: embedded official OAI Arazzo schema (1.0, patched at load to accept 1.0.x and 1.1.x). Catches types, required fields, enums, formats, regex.
- Semantic rules: uniqueness of
workflowId/stepId, resolution of$steps.X.outputs.Yreferences, no forward references between steps. - Cross-file: loads each
sourceDescriptions[].urlfrom local disk, indexes operations, validates that every step reference (operationId, short form or qualified$sourceDescriptions.<name>.<op>, andoperationPathJSON pointers) points at an operation that exists in anopenapi-typed source. HTTP/HTTPS URLs are intentionally refused (offline-first).
$ arazzo-maestro lint examples/shop.arazzo.yaml
OK: examples/shop.arazzo.yaml, no issues found
$ arazzo-maestro lint broken.yaml
[error] arazzo: value does not match expected pattern '^1\.[01]\.\d+(-.+)?$'
[error] workflows[checkout].steps[create-order].operationId:
operation "createOrder" not found in source "shop-api"
Error: 2 issue(s) found
Three themes ship built-in (light default, dark, and pb33f). All pass WCAG 2.2 AA on all critical colour pairs, verified in tests.
Customise without rebuilding by dropping a themes.yml at the root of your project:
# themes.yml, change the default with one line
default: darkOr override / extend:
default: corporate
themes:
- name: corporate
font: serif # sans | serif | mono (system stacks only)
shape: square # rounded | square
colors:
bg: "#fafaf7"
cardBg: "#ffffff"
runtime: "#7e22ce"
# β¦Custom themes that drop below WCAG AA contrast emit warnings at load time. See themes.yml.example for the full template, and internal/theme/themes/builtin.yml for the reference palette.
What works, what degrades, and what is missing, audited field by field
against the official Arazzo 1.1.0 spec: the full, honest matrix lives
in docs/SPEC_COMPLIANCE.md (tracking
issue #58).
The short version:
- Solid: the core path:
info/sourceDescriptions/workflows, steps withoperationId(short + qualified) oroperationPath, parameters (path/query/header), request bodies with whole-string and embedded{$expr}substitution and payload replacements, success criteria conditions, outputs and capture chaining,onSuccess/onFailureincl. self-retry rendering. Nestedstep.workflowIdsteps render with a workflow tag and link; the generators skip them with an explicit not-supported comment. - Validated but dropped downstream: reusable
components.inputsschemas and, insideinputs, arrays/enums/$ref: the official JSON Schema pass accepts them, the renderer and generators do not act on them yet.componentsand their$components.*references are inlined at parse time, workflow-level parameters/actions are merged into every step (overridable per step),dependsOntargets are validated and rendered,Criterion.type/contextand inputrequired/nested properties are carried through. - Known non-compliances (each tracked): dotted names and
$inputspointer sub-access cannot be rendered by Hurl (templating limitations, flagged in the generated file);#/json-pointersub-access on$stepsoutputs IS translated in both generators (derived captures in Hurl, value navigation in k6), and$inputssub-access is translated in k6. The embedded schema is the official 1.0 one with the 1.1 structural additions ($self,channelPath,in: querystring, AsyncAPI sources, expression versions) grafted on at load time, so 1.1 documents lint; their semantics are not implemented yet.
Turn an Arazzo workflow into a runnable test artifact, then run it against any environment. The subcommand grammar names the kind of test first, the target technology second:
arazzo-maestro test gen e2e <file> [flags] End-to-end functional tests (Hurl)
arazzo-maestro test run e2e <file> [flags] Generate + run them against an endpoint
arazzo-maestro test gen perf <file> [flags] Load / performance tests (k6)
e2e --format=hurl(default): one.hurlper workflow, every request prefixed with the{{baseUrl}}variable so the same file runs against staging, pre-prod or a local mock.test run e2egenerates and executes them with Hurl, with an optional HTML report.perf --format=k6: one k6 script per workflow; load profile and thresholds from--vus/--duration/--threshold, target from theBASE_URLenv var.
Parameters, outputs, success criteria and runtime expressions (including the embedded {$expr} form) are translated per format; unresolvable parts degrade to comments rather than guesses, and secrets stay out of the YAML (run-time inputs, captured outputs, self-provisioned data).
π Full guide, the ArazzoβHurl translation table and the test-data model: TEST_GENERATION.
These are engineering constraints, not afterthoughts. The rules are formalised in .agents/rules/ and enforced by reviews and tests:
- Eco-design: 1 network request at page load (Tailwind CDN), ~29 kB HTML, ~4.5 kB gzipped, no JavaScript, no fonts loaded from third parties, single Go binary (~20 MB) packaged in a
FROM scratchDocker image. - Accessibility: WCAG 2.2 AA contrasts (4.5:1 on body text), semantic HTML (
<main>,<section>,<h1>β<h2>β<h3>),aria-hiddenon decorative icons, visible focus,prefers-reduced-motionhonoured, no info conveyed by colour alone, fluidremsizing.
arazzo-maestro --version Print version and exit
arazzo-maestro lint <file> Validate against schema + rules + cross-file
arazzo-maestro view <file> [flags] Render to HTML or Mermaid
arazzo-maestro test gen e2e <file> [flags] Generate e2e tests (hurl)
arazzo-maestro test run e2e <file> [flags] Generate + run e2e tests, optional HTML report
arazzo-maestro test gen perf <file> [flags] Generate perf tests (k6)
view flags:
-o, --output <dir> Output directory (default: dist)
--workflow <id> Only render this workflow
--no-index Skip generating index.html
--theme <name> Theme (default: light, or themes.yml's default:)
--themes <path> Path to a themes YAML (bypasses ./themes.yml)
--list-themes List available themes and exit
--layout <orientation> Diagram orientation: portrait (default) or landscape
--format <name> Output format: html (default) or mermaid (one .mmd flowchart per workflow)
test gen e2e flags:
-o, --output <dir> Output directory (default: dist)
--workflow <id> Only generate this workflow
--format <name> Output format (default: hurl)
test run e2e flags:
--base-url <url> Target endpoint, e.g. https://staging.example.com/api/v1 (required)
--report-html <dir> Also write a Hurl HTML report to this directory
--variable <name=value> Hurl variable for a workflow input (repeatable)
--workflow <id> Only run this workflow
--format <name> Output format (default: hurl)
test gen perf flags:
-o, --output <dir> Output directory (default: dist)
--workflow <id> Only generate this workflow
--format <name> Output format (default: k6)
--vus <n> Concurrent virtual users (default: 1)
--duration <d> Test duration (e.g. 30s, 5m) (default: 30s)
--threshold <m=expr> k6 threshold as metric=expression (repeatable)
internal/
βββ model/ Pure data types + small shared helpers on them
βββ parser/ YAML β model.ArazzoDocument
βββ expr/ Runtime-expression parsing shared by both generators
βββ oasresolver/ Loads a local OpenAPI 3.x doc (via pb33f/libopenapi)
β and resolves operationIds β (Method, Path, BaseURL, Spec)
βββ linter/ Validates a document, three passes:
β schema.go (official JSON Schema, via santhosh-tekuri/jsonschema)
β linter.go (uniqueness, $steps.X.outputs.Y references)
β crossfile.go (sourceDescriptions[].url β oasresolver, opId checks)
βββ payload/ Applies requestBody replacements (RFC 6901 targets)
βββ hurlgen/ model.Workflow + oasresolver β Hurl (.hurl) e2e test text
βββ k6gen/ model.Workflow + oasresolver β k6 (.k6.js) perf test script
βββ theme/ Loads built-in + user themes, validates, audits WCAG contrast
βββ renderer/ model + theme β standalone HTML (html/template + embedded assets)
βββ mermaidgen/ model.Workflow β Mermaid flowchart (.mmd) text (no theme, no deps)
cmd/arazzo-maestro/ Cobra CLI entry point
Dependency graph: model β β
, expr β β
, parser β model, oasresolver β model + expr (external: pb33f/libopenapi), payload β model + expr, linter β parser + model + oasresolver, hurlgen/k6gen β model + expr + payload + oasresolver, theme β β
, renderer β model + theme, mermaidgen β model, cmd β all. No cycles.
There are already Arazzo plugins for VS Code and a Node-based validator from Jentic. They solve authoring: autocomplete, in-IDE preview, live validation while typing. We solve everything that happens after authoring:
| Editor plugins | arazzo-maestro |
|
|---|---|---|
| Validate in CI / GitHub Actions / pre-commit | β | β |
| Share rendering with non-devs | β Needs the IDE | β Standalone HTML, any browser |
| Versionable artifact (commit, deploy to Pages) | β Nothing to commit | β
HTML pages or Mermaid .mmd (renders on GitHub) |
| Zero runtime dependency | β Needs IDE | β
Single Go binary, FROM scratch Docker |
| Cross-editor (vim, emacs, Zed, Cursorβ¦) | β Lock-in | β Any editor or none |
| Explicit eco-design + accessibility contract | β | β Enforced by rules + tests |
These are complementary. The same user can have a VS Code plugin and arazzo-maestro in their CI.
Ready-to-copy recipes for CI (GitHub Actions + Pages), a pre-commit hook, and Docker (FROM scratch, ~20 MB) live in RECIPES.
Core (parse, lint, render, themes, e2e + perf test generation, landscape and Mermaid view outputs) is shipped, and so is the spec-compliance wave (components, workflow-level defaults and dependsOn, operationPath, criterion context/type, JSON-pointer sub-access). What's next: the OpenSSF passing badge, binary-size shrink, nested workflow execution, SVG/PNG export. Full checklist of done and planned items in docs/ROADMAP.md.
TEST_GENERATION.md, the full e2e (Hurl) + perf (k6) generation guide, translation tables and test-data modelRECIPES.md, CI, pre-commit and Docker integrationsROADMAP.md, shipped milestones and what's planned nextSPEC_COMPLIANCE.md, field-by-field status against the official Arazzo spec, auditedAGENTS.md, entry point for any coding agent (humans too) working on this repoCONTRIBUTING.md, dev environment + PR checklist + conventionsSECURITY.md, vulnerability reporting policy (private GitHub Security Advisories).agents/rules/, eco-design, accessibility, and code-style rulesthemes.yml.example, annotated theme templateexamples/,*.arazzo.yaml(Arazzo files, picked up bymake dist/make lint) + their referenced OpenAPI contractsMakefile, the canonicalmake help|build|test|vet|lint|dist|cleantargetsDockerfile, multi-stageFROM scratchbuild, accepts--build-arg VERSION=β¦
| Metric | Value |
|---|---|
Generated HTML (happy-path-checkout.html) |
~29 kB raw, ~4.5 kB gzipped |
| Network requests at page load | 1 (Tailwind CDN, to be internalised) |
Binary size (-s -w -trimpath) |
~20 MB |
Docker image (FROM scratch) |
~20 MB |
| Direct dependencies | 4 (cobra, yaml.v3, jsonschema, libopenapi) |
| Lines of Go (excl. tests) | ~6,200 |
| Test coverage | parser 86 %, linter 87 %, oasresolver 77 %, hurlgen 97 %, k6gen 98 %, expr 86 %, payload 86 %, mermaidgen 93 %, theme 86 %, renderer 82 %, cmd 78 % |
| Built-in themes WCAG AA conformance | 100 % on critical pairs (11/11, incl. jsonRuntime on jsonBg) |
Found a bug, or have an idea to improve the tool? Your feedback is welcome:
- Bug reports and feature requests: open an issue. Please describe what you expected, what happened, and the
arazzo-maestroversion where relevant. - Security vulnerabilities: do not open a public issue. Follow the private disclosure process in
SECURITY.md.
PRs welcome. See CONTRIBUTING.md for the full
guide (dev environment, PR checklist, conventions). The short version:
- Read
AGENTS.mdand the rules in.agents/rules/. make test vet: both must be clean.make lint: everyexamples/*.arazzo.yamlmust lint with no issues.make dist: every example must render without errors.- If you touch the HTML output, attach the gzipped byte count of an
examples/*.arazzo.yamlrendering to the PR. Regressions > 10 % require discussion.
Please do not open public issues for security reports. See
SECURITY.md for the supported channels and our
coordinated disclosure timeline.
Apache 2.0. Compatible with enterprise legal review; patent grant included.
- The OpenAPI Initiative for the Arazzo specification.
santhosh-tekuri/jsonschema, the JSON Schema validator powering the linter's first pass.pb33f/libopenapi, the OpenAPI 3.x parser behindoasresolver.- Hurl and k6, the runners targeted by the test generators.
- Cobra, CLI framework.
yaml.v3, YAML parsing with node-level access.- The WebAIM contrast checker, the reference we test against.



