Cheela Labs
GUIDES

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.

Addresses point at you, not at Cheela

Every capability address in the manifest is your own publicEndpoint. A stranger’s agent calls you directly; Cheela is not in that path at all.

Which means you have to serve it. Mount createPublicCheelaHandler from @cheela/runtime at that address — it is a different route from your signed endpoint, which accepts only requests carrying Cheela’s HMAC and would refuse every agent that found you through this document.

Without publicEndpoint the manifest is refused rather than published with an address that will not resolve. Agents cache manifests and cannot be contacted to correct one.

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.

cheela.config.ts
export default defineConfig({
  apiKey: process.env.CHEELA_API_KEY!,

  // Signed calls from Cheela — the chat widget's path.
  endpoint: "https://app.example.com/cheela/execute",

  // Unsigned calls from anyone. This is the address published below, and
  // the manifest cannot be served without it.
  publicEndpoint: "https://app.example.com/cheela/public",

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

Terminal
npx cheela manifest pull --runtime rt_8f2a
Output
Cheela Manifest

✓ Fetched manifest for rt_8f2a
✓ 4 capabilities
✓ Wrote public/.well-known/agent-discovery.json

The default output path suits most frontends. Override it with --out:

Terminal
npx cheela manifest pull --runtime rt_8f2a --out static/.well-known/agent-discovery.json

This 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.

package.json
{
  "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

agent-discovery.json
{
  "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://acme.example/cheela/public",
        "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.

  1. 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.
  2. No capability should take an identity as input. A caller writing the input directly picks whose data to read.
  3. Check the schemas. They are a public contract now. Anything you left loose will be called with things you did not expect.
  4. Check the descriptions. They are read by agents whose prompts you will never see, so they must stand on their own.
  5. 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.
A wrong manifest is expensive to retract

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

CHANGEHOW TO DO IT SAFELY
Add a capabilityDeploy, then pull and republish. Nothing breaks; agents discover it on their next read.
Widen a schemaSafe. Make new fields optional so existing callers stay valid.
Narrow a schemaBreaking. Bump the capability version and expect a period where both shapes arrive.
RenameBreaking — the address changes. Publish the new name alongside the old one, then deprecate.
RemoveMark 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.