---
hip: 1322
title: Skills — The Discovery Catalogue
author: Hanzo AI
type: Standards Track
category: Interface
capability: skills
status: Final
implementation-go: shipped
created: 2026-08-21
requires: HIP-0106, HIP-0139
---

# HIP-1322: Skills — The Discovery Catalogue

## Abstract

`/.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.

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.

## References

- HIP-0106 — Hanzo Plugin Contract
- HIP-0139 — Capability
- HIP-1030 — OpenAPI — The Served Contract
- HIP-1213 — Tools — The Tool Plane
- RFC 8615 — Well-Known Uniform Resource Identifiers

## Copyright

Released under CC0 1.0 Universal Public Domain Dedication.
