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:
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.
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.
| CREDENTIAL | PREFIX | WHERE IT GOES | WHAT IT CAN DO |
|---|---|---|---|
| Deploy key | ch_sk_ | CHEELA_API_KEY, in CI or your shell | Push deployments, read status, send heartbeats |
| Public key | ch_pk_ | Page source, client bundles, HTML attributes | Call /v1/runtime/execute. Nothing else. |
| Runtime secret | — | CHEELA_RUNTIME_SECRET, on your server | Verify 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:
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.
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:
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:
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.
npx cheela statusCheela Runtime
Runtime rt_8f2a
Deployment 3
Status active
Connection online
Transport http
Provider openrouter
Model ...
Capabilities 4, in syncIf 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:
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.