Cheela Labs
CONCEPTS

Architecture

Cheela orchestrates. Your infrastructure executes. Almost everything surprising about the API follows from that one division.

The split

Cheela holds the model credential, the agent loop, the quota, and the trace history. You hold the capability code, the database, and your users’ identities. Neither side has the other’s secrets.

Topology
   your app / widget                       a stranger's agent
           │                                        │
           │  POST /v1/runtime/execute              │  POST /v1/capabilities/:rt/:cap
           │  (public key, ch_pk_)                  │  (no auth — from your manifest)
           ▼                                        ▼
  ┌──────────────────────────────────────────────────────────┐
  │                    Cheela control plane                  │
  │  agent loop · quota · traces · analytics · manifest       │
  └──────────────────────────────────────────────────────────┘
           │                                        │
           │  model call                            │  signed HTTPS
           │  (Cheela's OpenRouter credential)      │  x-cheela-signature
           ▼                                        ▼
      model provider                        your endpoint
                                            └─ your handler, your database

The request path

A chat message arriving at POST /v1/runtime/execute goes through this, in order:

  1. Authenticate. The runtime’s public key is matched to a runtime record. The runtime id comes from that record, never from the request body — so a caller can only ever execute the one runtime whose key they hold.
  2. Check the origin and the rate limit. If the runtime has an allowlist, the browser’s Origin must be on it. The limiter buckets against the runtime, not the caller’s IP.
  3. Check quota. Once, on entry. The owner’s tier sets the ceiling.
  4. Call the model with your deployed capabilities attached as tools.
  5. Dispatch tool calls. For each one, Cheela signs an HTTPS request and sends it to your endpoint. Results are appended to the transcript.
  6. Repeat from step 4 until the model stops calling tools, or the step budget runs out.
  7. Record. Messages, token counts, capability calls and duration land in the trace.

Steps 4 to 6 are the agent loop, and it is bounded — see Executions for the step budget and what one execution costs.

Streaming is opt-in per request

Send Accept: text/event-stream and the same execution arrives as server-sent events instead of one JSON body. Without that header the response is byte-for-byte what it has always been.

The public broker

The second entry point exists because of the manifest. When you publish an Agent Discovery Specification document, every capability address in it points at POST /v1/capabilities/:runtimeId/:capability — Cheela’s broker, never at your own endpoint, which only accepts signed requests from Cheela.

That path has no model and no loop. The caller has already decided what it wants and supplies the input directly. It is also unauthenticated, deliberately: the manifest is public, so requiring a credential to call what you have published the schema for would advertise a door nobody can open.

Public does not mean unprotected

Anonymous broker calls spend the owner’s quota, so they draw on a smaller sub-allowance as well as the main one — traffic against your public manifest cannot starve your own widget. Capabilities marked requiresEndUser are refused on this path before anything is metered.

Four credentials

Confusing two of these is the most common setup failure, so they have distinguishable prefixes.

CREDENTIALSHAPEHELD BYAUTHORISES
Deploy keych_sk_…Your CI, your shellPOST /v1/deployments, runtime status, heartbeat
Public keych_pk_…Your page source, publiclyPOST /v1/runtime/execute and nothing else
Runtime secretopaqueYour endpointVerifying that a request really came from Cheela
End-user tokenwhatever you issueYour user’s browserNothing, to Cheela. Your handler decides what it means.

The first two used to be one key, which meant anyone who viewed a page’s source could push a deployment and overwrite the runtime’s capability set. Keep them apart.

Why requests are signed

Your capability endpoint is on the public internet. Without proof of origin, anyone who learns its URL can run your capabilities. So Cheela signs every call:

Signature
HMAC-SHA256(runtimeSecret, "runtimeId.timestamp.nonce.sha256(body)")

Sent as four headers:

HEADERCONTENTS
x-cheela-runtime-idThe runtime this call is for
x-cheela-timestampMilliseconds since the epoch
x-cheela-nonceSingle-use value, per request
x-cheela-signatureThe HMAC, hex-encoded

createCheelaHandler checks them in a deliberate order: headers present, runtime matches, timestamp inside a five-minute tolerance, nonce unused, signature matches in constant time. The cheap structural checks run first so malformed traffic is rejected before any HMAC work happens, and the nonce is claimed after the signature verifies so an attacker cannot burn nonces with forged requests.

What the signature does not do

It proves integrity and origin. It does not provide confidentiality — capability inputs, outputs, and the end user’s credential travel in the request body. That is why endpoint must be https://, with an exception only for localhost.

What follows from this

  • You cannot choose a model. Executions run on Cheela’s central OpenRouter credential so tokens can be metered and billed. cheela.config.ts has no provider or model field, and the registration API rejects one rather than accepting it and silently dropping it.
  • Cheela never sees your users. An end-user credential is forwarded to your handler untouched and is deliberately kept out of metadata, because metadata is recorded in traces and a credential must not be.
  • A capability that was never deployed cannot be called. The manifest is built from the stored deployment, not from anything local, so over-advertising has no path.
  • Your endpoint must be reachable from the internet. During development that means a tunnel.