Publish a manifest
A manifest is a public document describing what your product can do and where to call it. Publish one and any agent that reads it can use your capabilities without touching your UI.
What publishing does
Everything so far assumed you drive the model. A manifest inverts that: someone else’s agent reads a document at your domain, learns your capabilities and their schemas, and calls them directly.
Cheela implements the Agent Discovery Specification. The document is served from the control plane, built from your latest deployment, and republished on your own domain — conventionally at /.well-known/agent-discovery.json.
Every capability address in the manifest resolves to Cheela’s public broker. Your own endpoint only accepts signed requests from Cheela, so advertising it directly would publish a door nobody can open.
Describe who you are
The manifest has to say who operates the system. Two config blocks supply it, and a deployment carrying neither cannot produce a manifest at all.
export default defineConfig({
apiKey: process.env.CHEELA_API_KEY!,
endpoint: "https://app.example.com/cheela/execute",
// Describes your product, not Cheela.
website: {
name: "Acme Storefront",
description: "Order lookup and catalog search for Acme.",
url: "https://www.acme.com",
contact: "support@acme.com",
},
adp: {
// Reverse-DNS style. Published names become "com.acme.catalog-search".
namespace: "com.acme",
},
});The namespace supplies the dots the specification requires — which is why capability names themselves may not contain any. Deploy after changing either block; the control plane cannot read your config file, so these travel with the deployment.
Pull the manifest
npx cheela manifest pull --runtime rt_8f2aCheela Manifest
✓ Fetched manifest for rt_8f2a
✓ 4 capabilities
✓ Wrote public/.well-known/agent-discovery.jsonThe default output path suits most frontends. Override it with --out:
npx cheela manifest pull --runtime rt_8f2a --out static/.well-known/agent-discovery.jsonThis command needs no config file, no runtime module, and no credential. That is deliberate: it runs in your frontend’s build, which is often a different repository from the one holding your capabilities.
Serve it
The file needs to be reachable at a stable, conventional path on your own domain. In most frameworks, writing it into the static directory is enough.
{
"scripts": {
"prebuild": "cheela manifest pull --runtime rt_8f2a"
}
}Wiring it into prebuild means every deploy of your frontend republishes the current capability set, so the document cannot drift away from what actually serves.
The control plane serves it with a short cache window — a redeploy is visible to anyone pulling again without waiting out a long TTL.
What the document contains
{
"specVersion": "...",
"id": "com.acme",
"name": "Acme Storefront",
"description": "Order lookup and catalog search for Acme.",
"provider": {
"name": "Acme Storefront",
"url": "https://www.acme.com",
"contact": "support@acme.com"
},
"capabilities": [
{
"name": "com.acme.catalog-search",
"version": "1.2.0",
"description": "Searches the product catalog by free text",
"inputSchema": { "...": "..." },
"outputSchema": { "...": "..." },
"endpoint": {
"transport": "http",
"address": "https://api.cheelalabs.com/v1/capabilities/rt_8f2a/catalog-search",
"auth": "none"
}
}
],
"lastUpdated": "..."
}Note provider here means “who operates this system” — you — not a model provider.
Before you publish
Publishing makes every deployed capability callable by strangers. Walk the list once, deliberately.
- Anything acting for a person needs
requiresEndUser. Without it, a capability reading someone’s records is callable by anyone. With it, anonymous calls are refused before they are metered. - No capability should take an identity as input. A caller writing the input directly picks whose data to read.
- Check the schemas. They are a public contract now. Anything you left loose will be called with things you did not expect.
- Check the descriptions. They are read by agents whose prompts you will never see, so they must stand on their own.
- Know your quota. Anonymous traffic spends the owner’s allowance. It draws on a sub-allowance so it cannot starve your own widget, but it is still your quota.
It gets cached and republished by agents you cannot contact. The control plane refuses to serve one whose addresses would not resolve, for the same reason — a missing manifest costs a wait; a wrong one costs indefinitely.
Changing a published capability
| CHANGE | HOW TO DO IT SAFELY |
|---|---|
| Add a capability | Deploy, then pull and republish. Nothing breaks; agents discover it on their next read. |
| Widen a schema | Safe. Make new fields optional so existing callers stay valid. |
| Narrow a schema | Breaking. Bump the capability version and expect a period where both shapes arrive. |
| Rename | Breaking — the address changes. Publish the new name alongside the old one, then deprecate. |
| Remove | Mark deprecated first. The spec carries deprecatedSince and removalNotBefore for this. |
A stranger’s agent has no way to know you changed anything until it pulls again. Treat the manifest like a public API, because that is what it is.
Types and validation for the document itself live in @cheela/adp, if you want to check one in your own build.