Cheela Labs
CONCEPTS

Runtimes

A runtime is the identity a capability set belongs to. It holds two API keys, a signing secret, an endpoint, and a history of deployments.

What a runtime is

Not a process, and not a server. A runtime is a record in Cheela’s control plane — an id like rt_8f2a… with credentials attached. Your capability code runs wherever you deploy it; the runtime is how Cheela knows which code that is and how to reach it.

Create one in the dashboard, or over the API:

Terminal
curl https://api.cheelalabs.com/v1/runtimes \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "storefront", "version": "1.0.0" }'

Registration is idempotent by design — cheela deploy calls it on every deploy, so re-registering an existing id updates it rather than failing.

Your plan caps how many you can create

Free includes one runtime, Pro ten, Enterprise unlimited. Only a new runtime counts against the limit; re-registration never does, so sitting exactly at your ceiling does not break deployment.

Keys and secrets

Creating a runtime mints three things. Two are API keys with different powers, and one is a signing secret.

CREDENTIALPREFIXWHERE IT GOESWHAT IT CAN DO
Deploy keych_sk_CHEELA_API_KEY, in CI or your shellPush deployments, read status, send heartbeats
Public keych_pk_Page source, client bundles, HTML attributesCall /v1/runtime/execute. Nothing else.
Runtime secretCHEELA_RUNTIME_SECRET, on your serverVerify that an incoming request came from Cheela

The two keys are separate precisely so the public one can be public. They were once a single key, which meant anyone who read a page’s source could push a deployment manifest and overwrite the runtime’s capability set.

Reading a key back

Keys are stored encrypted as well as hashed, so a mislaid one can be revealed rather than rotated:

Terminal
curl -X POST https://api.cheelalabs.com/v1/runtimes/$RUNTIME_ID/reveal-key \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": "public" }'

It is a POST on purpose — a GET would land in browser history, proxy logs, and referrer headers. Runtimes created before this existed are genuinely hash-only and will say so; rotate those instead.

Rotating

rotate-key replaces an API key, rotate-secret replaces the signing secret, and revoke-key invalidates one outright. Rotation runs with a grace period so in-flight requests signed with the old value are not cut off mid-execution.

Rotating the public key churns your HTML

Anything embedding ch_pk_… in markup needs redeploying after a rotation. That is the reason reveal exists.

The endpoint

The public HTTPS address where your runtime serves capability calls. Cheela calls in to it. Set it in cheela.config.ts:

cheela.config.ts
export default defineConfig({
  apiKey: process.env.CHEELA_API_KEY!,
  endpoint: "https://app.example.com/cheela/execute",
});

Or at registration, if you are creating the runtime over the API rather than in the dashboard:

Terminal
curl -X POST https://api.cheelalabs.com/v1/runtimes \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "storefront", "endpoint": "https://app.example.com/cheela/execute" }'

It must be https://. The request signature protects integrity, not confidentiality, and capability inputs, outputs and the end user’s credential all travel in the body. http:// is accepted only for localhost, 127.0.0.1 and [::1] — for anything else in development, use a tunnel.

A runtime with no endpoint cannot serve anything. Capability calls against it fail as though the capability did not exist.

Deployments

Each cheela deploy creates a new, numbered deployment holding the whole capability set, your website block, and your discovery namespace. The most recent one is what serves.

Everything downstream reads from the stored deployment rather than from anything local. That is what makes over-advertising impossible: a capability that was never deployed cannot appear in a manifest, and the failure mode where a third party calls something the control plane does not have simply has no path.

Health and connection

cheela status reports whether the control plane has heard from your runtime recently. Polling status is itself the check-in, so there is no extra call to wire up.

Terminal
npx cheela status
Output
Cheela Runtime

Runtime        rt_8f2a
Deployment     3
Status         active
Connection     online
Transport      http
Provider       openrouter
Model          ...

Capabilities   4, in sync

If your local registrations and the deployment disagree, status prints the diff and tells you to deploy. To check in without polling, send an empty POST /v1/runtime/heartbeat — one indexed write, no body.

Origin allowlists

A public key sits in public HTML, so anyone can copy it into their own page. An origin allowlist limits which sites browsers may use it from:

Terminal
curl -X PUT https://api.cheelalabs.com/v1/runtimes/$RUNTIME_ID/allowed-origins \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "origins": ["https://app.example.com", "https://www.example.com"] }'

Entries must be bare origins — scheme, host, and port. A trailing slash or a path is rejected with a message telling you what to use instead, because an entry with a path can never equal a browser’s Origin header and would silently match nothing. Up to 50 entries; an empty list means unrestricted.

Projects

Runtimes belong to projects, which group them for listing and analytics. If you never mention one, a default project is created on first use and everything lands there — the CLI, the dashboard and cheela deploy all work without knowing projects exist.

Pass projectId when registering to file a runtime elsewhere. A project id is always checked against the authenticated owner, so another tenant’s project reads as absent rather than being silently accepted.

See the HTTP API reference for the full project and runtime routes.