Repository navigation
Releases: mpenet/ezu
Releases · mpenet/ezu
Release list
v1.0.0-beta10
Immutable
release. Only release title and notes can be modified.
Changed
- Field options take their value after a space too, as other options do:
--q.limit 10as well as--q.limit=10(--h.,--b.,--server.,--ssl.alike);--q.limitalone saidunknown option. - The Exoscale example's
compute instance-pool evicttakes several instances, repeated:instances=ID instances=ID.
v1.0.0-beta9
Immutable
release. Only release title and notes can be modified.
Added
- Exoscale with ezu: setting up the Exoscale example as an
exocommand, with the signing plugin, where to keep the key and secret, completion, questions, and MCP. - Secrets from where they are kept: an auth parameter, or a profile's
headersorqueryvalue, can be{"env": NAME},{"file": PATH}(a file of yours no one else can read) or{"cmd": [COMMAND, ARG…]}(a keychain, a password manager), read once per command when needed, never for a dry run; errors never show a value, and headers and query values read this way count as credentials. x-ezu-completeon a workflow input names a workflow listing its values (and a selector picking them): completion runs it with the document's profile, keeps the values a minute, and offers them,exo compute instance stop <Tab>offering your instances. The Exoscale example completes instances, security groups, clusters, nodepools, DNS domains, database services and more.ezu mcp --arazzooffers the command words of a document and of the documents it mounts as tools (workflow.compute_instance_create), each run in its own document; workflows that ask first (x-ezu-confirm) only with--allow-writes.--trust-sourcesapplies tomcptoo. A document'smcp.workflowsin the configuration lists which to offer ("compute instance *"), writes included.x-ezu-confirm: "Delete instance {$inputs.instance}?"on a workflow asks before it runs; only yes goes ahead,--yesanswers for scripts, and without a terminal ezu stops. The Exoscale example asks before deleting, resetting, reverting, evicting, stopping and rebooting.--selectworks on a workflow's outputs (ezu az exo compute instance list --select=.instances[].name), and as a table (csv,tsv), outputs that are one list print a row per item.arazzo-outputon a named workflow document in the configuration sets how its runs print (progress, say);--arazzo-outputstill wins.- More options can be set in the configuration:
timing(top level or profile),no-paginateandcheck-response(profile), andmax-gotos(named workflow document). The command line still wins; the configuration documents which options stay on it, and why.
Fixed
azfetched the OpenAPI specs of a document's sources on every run, never using the spec cacheopuses: with the Exoscale example, that was about 0.6 s of each command's 0.7 s. They are now cached for an hour, asop's are, and--reloadand--cache-ttlwork onaz.
v1.0.0-beta8
Immutable
release. Only release title and notes can be modified.
Changed
- A JSONPath Selector Object's result has a type that depends on the query, not on the data: a singular query (names and indexes only, RFC 9535) gives a value or nothing, and any other query, a filter say, always gives an array, empty when nothing matches. A filter matching one node used to give that node's value: take it with
#/0($steps.find.outputs.id#/0). - Workflow payloads leave out array items that resolve to nothing (an input that isn't given), and an array whose items all do, as they already did for object members:
security-groups: [{id: $steps.sg.outputs.id}]is left out rather than sent as[{}]. Anullwritten as an item is still sent. - A field of an
arrayproperty (--b.tags=a,tags=afor a workflow input) adds an item, typed by the items schema, so repeating it builds a list; a JSON array ([…]) is still the whole list. - Workflow inputs are
name=valuearguments (ezu az vm create name=web1 size=medium), typed from the inputs schema;-i,--i./--input., and-d/--b.for workflows are gone (they say so). Arguments starting with-are always ezu's options andname=valueones always inputs, so neither can shadow the other; an unknown input name stops with a suggestion. Positional inputs are those a workflow declares inx-ezu-args, no longer guessed from the path parameters of its steps. Completion offersNAME=and allowed values;az --checkchecksx-ezu-args. - atlas is now ezu (絵図, a pictorial map): the command, the Lua modules (
require "ezu"), the rock and the Homebrew formula. Configuration and cache are in~/.config/ezuand~/.cache/ezu; copy~/.config/atlasthere to keep your profiles.
Added
- After
--, a workflow's arguments are values whatever their shape, so a positional value like a base64 string ending with=can be given as is (vm decrypt -- aGVsbG8=). - Command words:
x-ezu-command: instance createon a workflow runs it asvm instance create …, and on an Arazzo source mounts that document's commands under its words (exo compute instance create web1), loading it only when a command line reaches it. Groups list their commands, unknown words get a suggestion, completion and--helpfollow the words, andaz --checkchecks them. Workflows without words still run by id. - Workflows of other Arazzo documents: a source of
type: arazzoand$sourceDescriptions.NAME.IDwherever aworkflowIdgoes (steps,gotoandretry,dependsOn). The workflow runs in its own document with the run's profile and options; documents and OpenAPI sources are loaded once per run, and cycles across documents are stopped. Only sources next to the document (relative, on the same origin) are loaded, unless--trust-sourcesortrust-sourcesin the document's configuration.az --checkchecks those documents too. doc/examples/exoscale.arazzo.yaml: Exoscale'sexocommand line as workflows, to use as a command of its own: about 400 workflows over 360 of the API's 374 operations, in documents mounted asexo compute(withexo compute sks),exo dbaas,exo dns,exo iam,exo kms,exo aiandexo networking. They take resources by name or id and wait for each job (exo compute instance create web1), with shared steps in a document of their own.- Plugins: Lua or Fennel files listed in the configuration's
pluginsprovide authentication methods, which profiles use by name. A method is a function signing each request in-process, with helpers for SHA-256, HMAC-SHA256, base64, hex, JSON and URLs; a plugin's path is absolute (or starts with~/), and its file and directories must belong to the user (or root, for directories) and be writable only by them. The binary carries the Fennel compiler (compressed, loaded only for a Fennel plugin), anddoc/examples/plugins/exoscale.fnlsigns Exoscale's API requests. - Releases include
.deband.rpmpackages of the Linux binaries (x86_64 and arm64), with bash, zsh and fish completion and the licenses. - A document named in the configuration can be a command of its own: a link to ezu named after it (
ezu az vm --linkprints how to make one) runs its workflows (vm create name=web1), with its own completion (vm completion fish). --check-responseonopreports how the response differs from the spec (an undeclared status or content type, a body the schema doesn't allow, checked as a response) and exits 1 when it does.ezu --licensesprints the licenses of the software the binaries contain (Lua, libcurl, Mbed TLS, LuaSocket, lyaml, LibYAML, lunajson, Fennel, and zlib and musl on Linux, and the Unicode data); they are also inTHIRD-PARTY.md, attached to each release.ezu az FILE [WORKFLOW] --checkchecks a document against its sources without running it: operations and workflows that don't exist, parameters and bodies, references to steps, outputs, inputs, workflows and components, conditions, regexes and JSONPath queries, and actions. It exits 1 on an error.--until CONDITIONonopandhttpsends the request again, every--everyseconds, until the condition holds (written as in workflows:$response.body#/state == 'ready',$statusCode == 404), then prints the last response and exits 0. After--max-timeseconds (default 600) it gives up saying what the condition found. The condition is checked before anything is sent; a POST or PATCH is only sent again with--retry-unsafe.--timinglogs a line per request: method, URL, status and how long each phase took (dns, tcp, tls, server, download, total, and the redirects that led there).-vshows the same timings for each exchange, and the library's responses carry them as:timings.
Fixed
- A replacement's JSON Pointer one past the end of an array (
/items/2with two items) appended silently; only-appends now, and any other index past the end is an error. - A
{$expression}embedded in a JSONPath query was pasted as text: a value with a quote broke the query, and a crafted one could change what it selects. Inside a string literal, the value is now escaped for its quotes; elsewhere a string goes in as text, as before, and other values as JSON literals. - Keywords next to a
$refin a schema were ignored: adescriptionbeside it now replaces the target's (in help too), and other keywords apply along with the target's (nullable: truebeside a$ref, a common OpenAPI 3.0 pattern, included). - On macOS, a plugin file with extended attributes (
@inls -l) and an access list was loaded:lsshows@in place of the+that marks an access list. ezu now reads the access list itself (ls -le) and refuses such a plugin. - A workflow failing on a regex or JSONPath condition shows the condition with its
{$expressions}replaced, and says itdoesn't matchorselects nothing inits context; a failure inside a workflow run by a step names that workflow (step 'find' (instance-id) › step 'list' failed: …) rather than repeatingfailed:. - On a terminal that doesn't report its width, the lines of workflow progress were cut down to
…; they now fit 80 columns.
Removed
$find(…), an ezu-only runtime expression: Arazzo has no such expression, so documents using it ran nowhere else. A JSONPath Selector Object does the same and more:{context: $response.body, selector: "$.users[?@.name == '{$inputs.name}'].id", type: jsonpath}.
v1.0.0-beta7
Immutable
release. Only release title and notes can be modified.
Changed
--arazzo-output progressprints a line per step once its success criteria are checked:✓or✗, its position (2/5), what it calls, its time and the response's status. A step polled withgotoorretrystays on one↻line with the wait and the criterion that failed, then ends with the number of attempts and the total time; the steps of a workflow run by another one are indented. On a terminal, lines are updated in place and fit its width. Step outputs are no longer printed there;stepsandfullprint them.- A failed workflow says which criterion failed and the values it compared:
step 'find' failed: $statusCode == 200 ($statusCode is 404); HTTP 404 {…}, the body shortened to 500 characters. Warnings no longer land on a step's line while it is updated in place. - The library's
on-stepevents::workflowwhen a workflow starts,:running,:doneafter the step's criteria are checked (with:passed,:failureand:error) and:action; each carries the workflow, its depth, and the step's index, count and attempt. - In an Arazzo payload, an object member whose own members all resolve to nothing (inputs that aren't given) is left out, like a single member, rather than sent empty. The payload itself, array elements and objects written as
{}stay objects.
Fixed
- Progress output marked a step
✓when its response arrived, before its success criteria were checked, so a failing step showed✓.
v1.0.0-beta6
Immutable
release. Only release title and notes can be modified.
Added
- Arazzo 1.1 documents: Selector Objects (JSONPath or JSON pointer, with runtime expressions embedded in
{}) wherever values are evaluated;jsonpathcriteria and Expression Type Objects;{$expression}in regex and JSONPath conditions; replacements withtargetSelectorType: jsonpath;in: querystringparameters; actionparametersas the inputs of the workflow an action runs; steptimeoutanddependsOn;$self,$outputs.NAME,$workflows.ID.inputs.NAMEand source description fields. AsyncAPI steps and XPath are refused. - JSONPath (RFC 9535), passing its compliance test suite, with I-Regexp (RFC 9485) patterns for
match()andsearch(), matched in linear time. - Operations take
:querystring, the whole query string (OpenAPI 3.2). - An Arazzo example that creates an Exoscale instance from names.
Changed
- An Arazzo condition that can't be evaluated fails, with a warning on stderr saying why, instead of failing the workflow.
- Arazzo
regexconditions are ECMAScript regular expressions, as in the specification's examples (^2\d{2}$), rather than Lua patterns; they run in linear time, so back-references and lookaround are refused. - The binaries are about a quarter smaller (1.5 MB on macOS, 1.9 MB on Linux): the program is embedded as stripped Lua bytecode, the C libraries are optimized for size, and the linker drops the code nothing calls.
- An Arazzo step's
timeoutis in milliseconds, as Arazzo 1.1 defines it, rather than seconds.
Fixed
- Ctrl-C ends the binary at once, with the usual signal status; it could be taken for an error, caught, and atlas would go on, then exit 0.
v1.0.0-beta5
Immutable
release. Only release title and notes can be modified.
Added
atlas http [METHOD] URL-or-path: a request without a spec, with a profile's headers, authentication and TLS settings, retries, pagination,-v,--dry-run/--curland the usual output and exit codes. A path is appended to the profile's API URL; a profile's credentials are only sent to its API's origin.atlas.base-url: the URL a client of a schema sends requests to.- Spec discovery: a profile with
base-urland noschemafinds its spec through the base URL'sLinkheader (service-desc,service-doc,describedby),openapi.jsonandopenapi.yamlunder it, or the base URL itself; the location is cached. Restish profiles withoutspec_filesimport without a warning. - Streamed responses: server-sent events and JSON Lines are printed record by record as they arrive (with
--selectapplied to each), and with--output=rawand no--select, a body of a type other than JSON or YAML is written as it comes. Requests acceptingtext/event-streammay stay idle between events. The transport takes a:streamfunction for this. --output=tablealigns wide characters (CJK, most emoji) and combining marks by their display width; csv and tsv escape control characters on a terminal.--selectfilters:[?state==running],[?size>10],[?tags.env=="prod"],[?tags]; negative indexes ([-1]);..keyfor a field at any depth.--output=table,csvandtsvfor arrays of objects, with the columns a{a,b}selection picks.atlas mcp PROFILE: an MCP server over stdio (protocol 2026-07-28, and the earlier ones that open withinitialize). The operations become tools, authenticated with the profile: the read-only ones by default, all with--allow-writes, or those the profile'smcp.operationslists (names, or patterns likelist-*).--arazzo=NAMEadds the workflows of an Arazzo context as tools.- YAML responses are decoded, and JSON Lines responses (
application/x-ndjson,application/jsonl) become an array of their lines' values.
Changed
--output=rawprints a selected array of strings, numbers or booleans one value per line.- Output escapes C1 control characters and the bidirectional overrides (U+202A–202E, U+2066–2069) as well as ASCII control characters, so text from an API can neither drive nor reorder what the terminal shows.
- The documentation is split into a page per topic, with an index in
doc/README.md;make testchecks its links.
Fixed
- Content types that only start like JSON (
application/json-seq,application/jsonl) are no longer decoded as JSON. - YAML with an alias inside its own anchor, or whose aliases expand past a million nodes, is refused instead of crashing atlas or making it spin; this covers YAML specs and Arazzo documents too.
- OpenAPI 3.0
nullableallows null whatever else the schema says (enum,allOf…), and read-only properties declared inallOfmembers are not required in requests. - External auth tools run with stdin from
/dev/null(unless given the request to sign), and the browser of OAuth flows with stdin and stdout on/dev/null.
v1.0.0-beta4
Immutable
release. Only release title and notes can be modified.
Changed
- The binaries use mbedTLS (3.6 LTS) for TLS instead of OpenSSL: about 2 MB instead of 5.5 MB. Certificate verification, client certificates (
certificate/key, RSA or EC) and the CA file and directory work as before; TLS 1.0 and 1.1 are refused. The Linux binaries no longer contain OpenSSL 3.3, which is out of support; they are built on Alpine 3.24, as static position-independent executables.
v1.0.0-beta3
Immutable
release. Only release title and notes can be modified.
Added
- Homebrew:
brew install mpenet/tap/atlas(macOS and Linux, with shell completion); releases update the tap.
Fixed
- A spec behind an auth proxy is fetched again with credentials when the first request is redirected to a sign-in page on another origin, as after a 401;
-vshows where a redirected request ended.
v1.0.0-beta2
Immutable
release. Only release title and notes can be modified.
The first beta with release binaries: 1.0.0-beta1 was published without them, and its release can no longer take files. The code is that of 1.0.0-beta1: see its section of CHANGELOG.md for the changes since 1.0.0-alpha14.
Fixed
- The release workflow publishes a release only once all its files are uploaded, and builds on Intel macOS runners.
v1.0.0-beta1
Immutable
release. Only release title and notes can be modified.
Security
- TLS certificates are now verified. Up to and including 1.0.0-alpha14, HTTPS requests did not verify the server's certificate or host name (luasec's default), so credentials sent by atlas — bearer tokens, API keys, Basic credentials, OAuth client secrets and refresh tokens — could be intercepted by anyone able to sit between atlas and the API. Upgrade, and consider rotating credentials used over untrusted networks. Verification uses the system CA bundle (
SSL_CERT_FILE, the usual bundle locations,SSL_CERT_DIR) and fails closed when none is found;--ssl.verify=noneor a profile's"insecure": trueopt out. - Redirects are followed by atlas itself: GET and HEAD only, at most 5, never from
httpstohttp, and credential headers are dropped when the origin changes. - Header names and values containing CR, LF or NUL are rejected.
-vand--dry-runredact secret query parameters;azsends credentials to source schemas only after a 401/403.- external-tool signing and query api-key auth no longer modify the caller's request table.
- Same-origin checks for redirects and pagination parse URLs as libcurl does: a crafted
LocationorLinksuch ashttps://evil.example?@api.example.com/was taken for the API's own origin, so credentials followed it to another host. - Documents named by a spec's external
$refs are fetched without the profile's headers or credentials unless they are on the spec's own origin. - The bash completion script treats the candidates it gets (operation names, enum values from the spec…) as data:
compgen -Wexpanded$(...)in them, so a spec could run commands when completing. - On a redirect to another origin, the headers authentication added are dropped whatever their name, and logs and dry runs redact them and a query API key whatever their name; user info in URLs is redacted too.
- The schema cache directory is private to the user (0700).
- The config file written by
profile import-restish, its.bakcopy and OAuth tokens are created readable by the user only (0600), with no moment when they are readable by others. - A query API key no longer appears in the error printed when a request fails in transport.
- The profile's
headersand credentials are not sent to a spec hosted on another origin than the profile'sbase-url. --dry-rundoesn't run an external signing tool, and doesn't send placeholder credentials when fetching a spec.- Bash completion inserts values quoted: an enum value from a spec containing spaces or shell syntax (
$(...)) stays one argument and never runs. - Text from a spec (summaries, descriptions, server descriptions, completion help) is printed with its control characters escaped, so a spec cannot send terminal escape sequences; so is an OAuth provider's error description.
- The OAuth callback checks the
statebefore anything else: a forged callback without it can no longer pose as the provider's error. -vredacts credentials in response headers (set-cookie,...token) as it does in request headers.- Error messages, warnings, Arazzo step ids and the OAuth device prompt escape control characters too: they quote specs and servers.
opfetches the documents a spec's$refs name like the spec itself: through the same transport (so-vshows them), with the profile's headers and credentials only when the spec is on the API's origin; they used to get the profile's headers whatever the origin.-v,--dry-run,--curl, logged response headers and text error bodies escape control characters too.- A same-origin redirect keeps a query API key; another origin still never gets it.
- A query API key is read when a request is sent, so an unset
env:variable no longer breaks--list,--help,--serversor--dry-run; a spec answering 401 is fetched again with the query key or signature, as with header credentials;-vlogs spec fetches too. - Headers added by external-tool signing are marked as credentials: logs and dry runs redact them, and they are dropped on a redirect to another origin.
- The libcurl binding rejects URLs, header names and values and options that contain a NUL byte, rather than cutting them short.
- A URL whose host part holds a backslash or a control character (such as a NUL in a
$ref) matches no origin, so credentials never follow it (libcurl readshttp://a\@b/as hostb); retried schema fetches and next-page requests keep the marking of credential headers, so a later cross-origin redirect drops them;$referrors redact the document's URL.
Added
- HTTP goes through libcurl: proxies from
https_proxy/http_proxy/all_proxy/no_proxy, connection reuse, transparent gzip, per-request timeouts. atlas --version, also reporting the libcurl/OpenSSL build;atlas.versionin the library.atlas profile import-restish [FILE] [--dry-run]imports Restish configs (v1apis.json, v2restish.json) as profiles.- Profiles take
query(parameters on every request) andtls.ca_cert. az WORKFLOW --helpdocuments a workflow's inputs and steps.--servers,--server-indexand--server.NAME=VALto list and choose servers;atlas.serversandatlas.server-urlin the library.- Short field prefixes
--q.,--h.,--b.,--i.. - Request checks before sending: missing required parameters and invalid JSON bodies are errors, undeclared query names warn;
--no-validatesends anyway. - Shell completion of operations, parameters, body fields, enum values, server variables, Arazzo contexts, workflows and inputs.
- Operations without
operationIdare available under a name made of their method and path (GET /pets/{petId}→get-pets-pet-id); every operation exposes:name. atlas.json(decode,encode,array,array?): JSON that keeps empty arrays apart from empty objects; usejson.arrayto send an empty array from a body built in Lua.atlas.parse-schemaparses fetched schema text, rejecting documents that are not a mapping.- Release binaries for macOS x86_64 and Linux arm64, a
SHA256SUMSfile and build provenance attestations.
Changed
- Breaking (library): operations take a request table,
op.invoke {:path :query :headers :body :timeout}; positional calls remain a shorthand. - Breaking (library):
atlas.load-schemaand the:load-schemaclient option take{:ssl :headers}instead of positional arguments. - Breaking (library): the operation field
:has-body?is now:has-body;:n-pathis gone (use(length op.path-params)). - Breaking (library): every
$refto the same place resolves to the same table, so recursive schemas are cyclic tables (node.properties.nextisnode) instead of being cut to{}. Code that walksop.specorop.body-schemamust not assume a tree, and should not serialise them as is. This makes resolution linear for any spec (it was exponential on heavily recursive ones such as Stripe's) and lets validation check recursive data at every depth. - Breaking (library): JSON
nullisjson.null(fromatlas.json) in decoded response bodies, both in arrays and as object members, so a body shows and re-encodes as received; it used to drop members and corrupt arrays ([1,null,3]came out as{"1":1,"3":3}). Code testing(= nil body.x)for a nullxmust test(= json.null body.x). - Breaking: an option that doesn't apply to the command is an error:
op --profile pused to send the request without the profile,az --selectwas ignored. - Breaking: Arazzo runs fail when a source description cannot be loaded, and a plain
operationIddefined by several sources is an error; qualify it as$sourceDescriptions.NAME.ID. - Operation names are lowercase words joined by dashes whatever the operationId's style:
projects/list-cards,get_folders_id#get_shared_linkandbookmarks-(-deprecated)becomeprojects-list-cards,get-folders-id-get-shared-linkandbookmarks-deprecated. When two operationIds yield the same name, the first by path keeps it and the others are named after their method and path, the same way on every run. - Query parameters are serialized in their declared
styleandexplode(form,spaceDelimited,pipeDelimited,deepObject); array path parameters use thesimplestyle. - Arazzo: string comparisons in conditions are case-insensitive and
''escapes a quote, as the spec says; a backslash is now an ordinary character. A misspelled expression ($statuscode) is an error rather thannull, and payload text that merely starts with$is kept as is. - Arazzo: a request that fails in transport is a step failure, so
onFailureandretryhandle it;retryruns the step named bystepIdfirst; a response'sRetry-AfteroverrulesretryAfter;requestBody.contentTypeandreplacementsare supported;$request.query.NAMEand$request.path.NAMEwork. --outputand--arazzo-outputreject unknown values.- Building a client is linear in the size of the spec: Stripe's went from not finishing to 0.07 s, Kubernetes' from 1 s to 0.01 s.
- The
ssloptions arecafile,capath,certificate,keyandverify; other luasec options are no longer used. - The macOS binaries run on macOS 12 or later: Lua, OpenSSL (3.5 LTS) and libyaml are built from pinned sources for that target, where the previous ones only started on the macOS version of the machine that built them.
- The binaries link OpenSSL, libyaml, zlib and libcurl statically. The Linux binaries are fully static, built against musl: they run on any distribution (the previous ones needed glibc 2.34 or later, libssl 3 and libyaml).
- YAML is read with YAML 1.2 core schema semantics; mapping keys keep their source text.
$refresolution handles JSON Pointer escapes, references into other files and URLs, and path items; an unresolved reference is an error.- Authentication happens only when a request is sent, never for
--help,--list,--serversor--dry-run.
Fixed
- Base...