Skills
Skills — reached at /.well-known/agent-skills/; not in the public REST contract.
Skills ships in every Hanzo cloud. It carries no operations in the public API document, so it has no generated REST reference — it is reached another way.
| Reached at | /.well-known/agent-skills/ — the Agent Skills discovery convention, GET-only |
| In the public contract | no — see below |
Why there is no reference here
This reference is generated from the public API document, one page per operation. This capability contributes no operation to that document — which is a fact about its door, not about whether it is finished. It is GA and it runs in every deployment. It is reached at /.well-known/agent-skills/ — the Agent Skills discovery convention, GET-only.
Specification
HIP-1322 · Skills — The Discovery Catalogue — Draft · read the specification →
/.well-known/agent-skills/ is the catalogue an AI client reads to learn what
this deployment can do. It is implemented in hanzoai/cloud at apps/skills
(HIP-0106) and serves two public GET routes from a catalogue embedded in the one
cloud binary.
This HIP states why the catalogue is generated rather than authored (§2), which
brand a request is answered for (§3), and why these two routes sit at the root
rather than under /v1 (§4).
Motivation
An agent that must be told what a platform offers is an agent someone has to keep telling. The discovery convention answers that once, at a fixed address, in a document the agent can fetch before it holds any credential.
The catalogue's danger is not that it is missing but that it is confidently wrong. A skill document is read as live instruction: it tells an agent an address answers and what to send there. An authored catalogue drifts from the router the moment either moves, and drift here does not degrade gracefully — it hands an agent an address that 404s and a reason to keep trying.
Specification
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.
§1 The surface
Two routes, public, GET only:
| Route | Answer |
|---|---|
GET /.well-known/agent-skills/index.json | the brand's master catalogue |
GET /.well-known/agent-skills/{skill}/SKILL.md | one skill document |
The discovery surface carries no secrets and MUST NOT require a bearer. Neither route can be a typed op, so both declare their prose through the registry the router projection consults — which is why they render only while they are served, and why the generated SDKs and the spec-derived CLI carry the same sentence the document does.
§2 Generated from the router, never authored
The catalogue is generated by plugin/gen-skills from each app's own
plugin/<app>/openapi.json — the app binary describing its own live router,
which is the same input the fleet catalogue and openapi.yaml are built from —
and embedded as catalog/.
This package MUST NOT re-derive skills at request time. It serves the embedded
bytes, so the sha256 digests in index.json match the served SKILL.md
exactly, and a catalogue cannot describe a route the binary does not serve.
The rule earns its place from a measured failure: the lane that built this
catalogue in hanzoai/openapi projected authored intent rather than served
routes and had no liveness filter, so it shipped skills for /v1/balancers and
/v1/builds — which production 404s — as live instructions to an agent. The
generator moved to cloud, beside the routes, for that reason.
§3 White-label: the brand is the Host
The brand is decided per request from the Host header, mirroring
getWhiteLabelBrand: api.hanzo.ai serves the Hanzo catalogue,
api.lux.network the Lux one, api.zoo.ngo the Zoo one. Hanzo branding MUST
NOT appear on a Lux or Zoo surface.
A Host resolving to a brand with no embedded catalogue falls back to the
deployment brand (CLOUD_BRAND), then to hanzo. Falling back is not
guessing — every fallback is a catalogue that exists.
§4 The address is the convention's, not ours
These two routes live at the root because /.well-known/ is registered by
RFC 8615 and the discovery convention names the path. HIP-0139 §3 permits an app
to answer at an address not named for it exactly where an outside convention
fixes the address, and this is such a case: an agent looks under
/.well-known/agent-skills/ or it does not find us.
Registration order matters and is part of the contract: these routes are mounted
at order 8, BEFORE IAM's /.well-known/* catch-all at order 50 and before the
console's /*, so a first-match scan reaches them.
§5 The boundary against tools
- tools (HIP-1213) is the per-tenant tool plane an agent calls through, authenticated and scoped to an org.
- skills is the unauthenticated description of what exists, read before any call.
One is the shelf, the other is the sign on the door. A skill document MUST NOT be the only place a capability's contract is written; it points at the served document, which HIP-1030 specifies.
Rationale
skills was briefly treated as having folded into tools, and the corpus
recorded it as no longer a capability. It had not folded: apps/skills is its
own package with its own row in manifest/apps.go and its own two served
routes, and it answers a question tools does not — what exists, to a caller
who has not authenticated. The record is corrected here.
Security Considerations
The catalogue is public by design, so it MUST carry nothing an unauthenticated reader may not have: no org names, no tenant addresses, no credentials, no internal hostnames. Because it is generated from each app's own router description rather than from a tenant's state, it has no tenant state to leak.
The digest in index.json is the integrity claim a client can check, and it
holds only because the same bytes are embedded and served. A future lane that
re-derived a skill at request time would break that claim silently, which is why
§2 forbids it.
How is this guide?