9.1 The discovery surface (/.well-known/rs2/*) and OpenAPI
Every tenant exposes a generated, read-only discovery surface describing what it offers. It's generated from the live config and filtered by the caller's read permission, so a client (or an AI agent) sees exactly the mounts it may use — no separate documentation to drift out of date.
The three endpoints
| Endpoint | Returns |
|---|---|
GET /.well-known/rs2/services |
The mount catalogue plus a control block pointing generic tools at config, catalogue, mounts, and code — only mounts the caller may read |
GET /.well-known/rs2/agent-surface |
{entities, actions, queries} — actions with their effect class and idempotency guidance, stored queries with their params schemas, and declared I/O schemas |
GET /.well-known/rs2/openapi |
OpenAPI 3.1 for the tenant |
Any non-GET on the surface is 405.
services — the catalogue
This is the entry point for a polymorphic client (Part 3): it reports each mount's
pattern (the conversation shape — store, store-view, transform,
api) and facets (optional capabilities within it). A client reads these
to decide how to talk to a mount, rather than hard-coding service names (3.4).
Spec stores also advertise specSubtree (.pipelines, .queries, or
.templates) and, when needed, an authoring description — for example JSX +
Preact compilation for templates. The control block points to whichever path
the tenant mounted its services API; clients need not assume /services.
agent-surface — for tools and agents
The agent surface describes the tenant as entities, actions, and queries:
- actions carry their effect class and idempotency guidance
(
idempotency: {header: "Idempotency-Key", honored: true}), so a caller knows what's safe to retry and how to dedupe (9.3). - queries carry their
paramsJSON Schema, read live from the query store (6.5) — so an agent knows exactly how to invoke each. - actions and entities carry
inputSchema/outputSchemawhen a wrapper, pipeline envelope, or custom-code manifest declares them.
Filter it with ?surface=<name> against each mount's x-expose metadata (a
string or array; absent means exposed everywhere).
openapi — the standard contract
GET /.well-known/rs2/openapi is OpenAPI 3.1. Crucially, the schemas it
references are the ones enforced at runtime — stored-query param schemas are the
request-body schemas, store paths reference shared path-item shapes, and
components.schemas.Problem describes the error body. Because it's generated from
the same definitions the runtime enforces, there's no drift between the docs
and behavior. Operations also carry x-effect and x-idempotency-key
extensions.
Permission filtering
The surface only ever shows what the caller may read. An anonymous client sees the open mounts; an authenticated admin sees more. This means the discovery document doubles as an authorization-aware capability list — useful for building UIs that adapt to the logged-in user.
OPTIONS — probing a single path
The three endpoints above describe the whole tenant. To ask about one
specific path, send OPTIONS to it. Any mounted path answers with a read-only
description of the resolved mount — its pattern, facets, and schema hint — as
a JSON body, plus an Allow header listing the methods it accepts:
OPTIONS /data/orders/42
→ 200 Allow: GET, HEAD, PUT, POST, DELETE, OPTIONS
{ "pattern": "store", "facets": ["schema", …], … }
This lets a generic client render or validate any path from a single round trip,
without fetching the whole catalogue. (A genuine CORS preflight — OPTIONS with
an Origin and the preflight headers — is answered as a preflight instead; 5.5.)
The next section covers the error format every one of these (and every other) endpoint uses on failure.
← Part 8 — Custom Services · Manual home · Next: 9.2 Structured errors →