658493a added -DmaxYamlCodePoints to lift snakeyaml's 3145728 code-point
ceiling. I verified it on generator 7.24.0, which is what the C++ lane uses, and
did not verify it on 7.14.0, which is what THIS FILE pins for every other
language. It does not work there: 7.14.0 ships an older swagger-parser that
ignores the property. Java regeneration failed after 9m27s with the same
misleading "Issues with the OpenAPI input" the flag was supposed to prevent.
So the fleet was blocked on regenerating and the fix was cosmetic. The real fix
is not to raise the ceiling but to stop going near it: swagger-parser only hands
YAML to snakeyaml, and the JSON path is Jackson, which has no such cap. -i takes
either format.
Measured rather than assumed, because "it happens to be under the limit" is not
a fix — the JSON of this document is 3,144,674 code points, 1054 UNDER the
ceiling, which would have made a coincidence look like a solution. Padded to
3,684,674 (539 KB OVER) it still validates clean on 7.14.0. The JSON path does
not have the ceiling at all.
as_json() converts once, in main(), for every language. The temp file is
deliberately not written back to the repo: there is one document, and it is
hanzo.yaml.
Side effect worth recording: java generation went from failing at 9m27s to
succeeding in 12s. The YAML parse was not merely failing, it was thrashing.
hanzo-cpp/sdk's own scripts/generate.sh now does the same one thing rather than
carrying the flag, so there is one mechanism and not two. Verified by
--check: the generated C++ tree is byte-identical across the swap.
Co-authored-by: Hanzo Dev <[email protected]>
Hanzo OpenAPI
hanzo.yaml is THE published document. One OpenAPI 3.1 surface for every
Hanzo service — the one every SDK, every doc site and every tool generates from.
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" \
https://raw.githubusercontent.com/hanzoai/openapi/main/hanzo.yaml
This repo is private, so that URL needs a token — unauthenticated it is a
404, not a 403, which reads like a missing file rather than a missing header.
Any fetch of it without an Authorization header is broken; curl -f under
set -e turns that into a build failure with nothing that names the cause.
Nothing generates from anything else, and nothing is pushed to an SDK. The SDK repos PULL this file and regenerate themselves; a client is never hand-written and never receives a patch from here.
Forward-only. No backwards compatibility, no deprecated paths, no /api/
prefix, no cross-brand references. A single bearer JWT from Hanzo IAM
(https://hanzo.id) authenticates every route, and every route is
/v1/<service>/<resource> — the same path through the gateway
(api.hanzo.ai) and through the service's own hostname.
One exception — AI inference is top-level. hanzoai/ai serves its OpenAI-
and Claude-compatible surface at the bare /v1/* paths
(/v1/chat/completions, /v1/completions, /v1/embeddings, /v1/models,
/v1/rerank, /v1/messages) because every OpenAI/Anthropic SDK hard-codes
them: there, the path IS the compatibility contract.
curl -H "Authorization: Bearer ${HANZO_TOKEN}" \
https://api.hanzo.ai/v1/chat/completions -d '{"model":"zen5","messages":[...]}'
curl -H "Authorization: Bearer ${HANZO_TOKEN}" \
https://api.hanzo.ai/v1/billing/balance
OIDC discovery: https://hanzo.id/.well-known/openid-configuration.
hanzo.yaml is a file in this repo, not a served endpoint — /v1/discovery
was probed, it 404s, and no handler for it exists.
The files
| File | What it is |
|---|---|
hanzo.yaml |
the published document. Generated by merge.py; never edited. |
<service>/openapi.yaml |
one self-contained authored spec per service. |
cloud/openapi.yaml |
not authored here — hanzoai/cloud's own woven document, copied verbatim by sync.py. |
capabilities.yaml |
the ONE registry: every capability, in exactly one domain. |
flows.yaml |
the canonical example set — the same six journeys in every SDK. |
CAPABILITIES.md |
generated index. Never hand-edit. |
merge.py sync.py generate.py skills.py audit.py |
build · resync · clients · skills · measurement. |
The capability index is CAPABILITIES.md, generated from
capabilities.yaml, so it cannot drift from what is here. There is no second
index in this file to fall out of date.
Two commands
python3 sync.py # pull hanzoai/cloud's woven document, then merge
python3 merge.py # rebuild hanzo.yaml + CAPABILITIES.md from the specs
sync.py is the whole resync: it fetches origin/main in a hanzoai/cloud
checkout (../cloud, or --cloud DIR, or CLOUD_DIR), writes
origin/main:openapi.yaml here byte-for-byte, and re-merges. --check reports
staleness without writing. Nothing is ever written back to hanzoai/cloud.
That document is the one input here that cannot lie about what is served —
cloud weaves it from its own router and gates the weave against source — so
merge.py lets it WIN wherever it and an authored spec describe the same
route. The authored specs cover what other binaries serve, cloud's covers what
cloud serves, and hanzo.yaml is the union with truth on top.
Conventions
info.version: 8.0.0(the V8 generation). The/v1path is the immutable contract — there is nov2, ever. Breaking changes ship as new resources.- operationIds are BARE in each spec (
logs_query);merge.pynamespaces them<svc>_in the published document. Never self-prefix. - No cross-file
$ref. Each spec is self-contained. - No
deprecated: true. No/api/prefix. No dates in filenames. - Security: every operation uses
BearerAuth. No service has its own login. - Errors: 4xx client, 5xx server, JSON body with
statusandmsg. - A product whose own authored routes answer 404 — probed at
api.hanzo.aiAND at its own host — is DELETED, not annotated.skills.pyhas no liveness filter, so an unserved route ships as a live instruction to call a dead endpoint. See LLM.md, "Who reads this repo".
Validate
python3 -c "import yaml, glob; [yaml.safe_load(open(s)) for s in glob.glob('*/openapi.yaml') + ['hanzo.yaml']]; print('OK')"
java -jar ~/.cache/openapi-generator/openapi-generator-cli-7.14.0.jar validate -i hanzo.yaml
npx -y @redocly/cli preview-docs hanzo.yaml
Clients
generate.py runs openapi-generator (pinned 7.14.0) over hanzo.yaml for
every language; sdks.yaml is the per-language data, and each SDK repo carries
only a call site. There is no second generator and no unified SDK monorepo.
python3 generate.py --all # regenerate every client in place
python3 generate.py go # just one
python3 generate.py --all --check # fail if a committed client drifted
| Lang | Canonical repo | Generator | Ships as |
|---|---|---|---|
| Python | hanzoai/python-sdk |
python |
hanzoai on PyPI |
| TypeScript | hanzoai/js-sdk |
typescript-axios |
hanzoai on npm |
| Java · Kotlin | hanzoai/java-sdk · hanzoai/kotlin-sdk |
java · kotlin (okhttp) |
ai.hanzo:hanzo-{java,kotlin}-cloud |
| Ruby | hanzoai/ruby-sdk |
ruby |
gem |
| Rust | hanzo-rs/sdk |
rust (reqwest) |
crates/hanzo-client — own scripts/generate.sh (needs a template override) |
| Go | hanzo-go/sdk |
go |
package hanzoai at the module root — own scripts/generate.sh (no directory to own) |
A language stays in sdks.yaml while its whole invocation is expressible as
data, and leaves when it needs something that is not — a template file, or a
layout take cannot describe. It then owns the whole invocation in its own
repo. A row here AND flags there is two declarations of one contract.
Examples are not a per-SDK decision either: flows.yaml names the six journeys
(hello, chat, money, store, agent, tools) and every SDK renders the
same six from the same operationIds. python3 test_flows.py is the gate — an id
that no longer resolves fails here, once, instead of in six clients separately.
Agent skills
skills.py derives /.well-known/agent-skills/ from the same per-service
specs, clustering GET endpoints by resource — read-only by construction, no
destructive verb ever becomes a skill — and emits a SKILL.md per cluster plus
an index.json carrying a sha256 of every file.
python3 skills.py # dist/agent-skills/<brand>/… (hanzo, lux, zoo)
python3 skills.py --no-services # unified tree only (what the cloud binary embeds)
python3 skills.py --check # drift gate
python3 test_skills.py # golden test
Output is white-labeled per brand: a Lux surface reads Lux / api.lux.network
/ lux.id, a Zoo surface Zoo / api.zoo.ngo / zoo.id — never Hanzo.
dist/ is generated at build time and is not committed.
License
BSD 3-Clause. Copyright Hanzo AI, Inc.