# Cells

> A cell is a tiny connector you deploy into your own cloud account that gives your agents tools over your real infrastructure — list what's running, search logs, scale a service. The Console's Cells page (Connect → Cells) is where you browse the catalog and register your deployments; the cell itself runs in your cloud under its own role, so your cloud credentials never leave your cloud account. As of September 25, 2026, OpenEng AI isn't live yet.

A **cell** is a tiny connector you deploy **into your own cloud account**
that gives your agents tools over your real infrastructure — list what's
running, search logs, scale a service. The Console's **Cells** page
(Connect → Cells) is where you browse the catalog and register your
deployments; the cell itself runs in *your* cloud under its own role, so your
cloud credentials never leave your cloud account.

> [!WARNING] Status on September 25, 2026: OpenEng AI isn't live yet
> The Console and Web Console interfaces load, but the OpenEng AI API routes and the Broker aren't
> deployed. Nothing can be saved or published, and no AI engine can register or connect. Pro,
> Enterprise and pay-as-you-go launch in Q4 2026 and can't be purchased yet. This page describes
> OpenEng AI as it works once those services are live ([status](ai.md)).

*Figure: Connect → Cells — the catalog and your registered deployments; registration walks cell, URL, ${ENV} credential rows, and the read/write confirmation, then saves explicitly.*

## How a cell works

- A cell is a small, **pure-source** function (you can read every line) that
  speaks a simple HTTP protocol: `GET /manifest` (describes its tools),
  `POST /call` (invokes one), plus `GET /healthz` and `GET /tools`.
- You deploy it in **your** cloud — AWS Lambda, Google Cloud Run, Azure
  Container Apps — where it uses the function's own IAM role / service
  account for cloud access. **No cloud credentials are ever uploaded to
  OpenEng.**
- You put your cloud's **API-key gateway** in front of it (AWS API Gateway /
  GCP API Gateway / Azure API Management); the cell does no authentication of
  its own. The cell deploy guides set that gateway up to validate an
  `x-api-key` header.
- OpenEng connects to the cell **from your machine**: the engine calls it with
  the credential you registered, sent as an `Authorization: Bearer` token, so
  the gateway has to accept that header for your agents to use the cell's tools.

> [!WARNING] Known gap: the credential header
> A gateway set up exactly as the deploy guides describe checks `x-api-key`, while the engine sends
> `Authorization: Bearer`, so it refuses the engine's calls with 401 or 403. Until the engine and the
> guides are aligned, configure your gateway to accept the bearer header ([Troubleshooting](#troubleshooting)).

The catalog holds **550 cells** as of 2026-09-25, one cloud service each: 260
for AWS, 150 for Azure and 140 for Google Cloud; 435 are read cells and 115 are
write cells.

## Read vs write

Every cell has a type, and it's enforced:

| Type | What it may do | Available in |
|---|---|---|
| **read** | Inspect only — list, describe, search | every run |
| **write** | Performs actions — scale, restart, delete, send | execution (goal) runs **only** |

> **Note:** A read-only analysis run can never trigger a write cell — by
> design, not convention. The engine refuses the call with
> `(blocked: '<cell>' is a WRITE cell — it runs only in a goal/execution context, never in read-only analysis)`.
> The register flow makes you confirm the type, with the write-consequence
> spelled out.

## The Cells page: two tabs

**Catalog** — the shipped default cells (`cell_def_…`), browsable read-only
as cards: read/write badge, version, a short description, the tools the cell
exposes, and a **Register deployment** button that pre-fills the registration.
The catalog comes from the content CDN; if the CDN can't be reached, the page
shows the last loaded copy with a "may be stale" notice. Cell *packages*
(source, `deploy.sh`, per-cloud quick paths) are downloaded from
**[cells.openeng.app](https://cells.openeng.app)**, linked at the top of the
page.

**My deployments** — every deployment you've registered, as a table: name, id,
type, number of tools, the config it's attached to (or **unassigned** when no
config uses it yet) and when it was last updated.

## Registering a deployment, step by step

The register flow (*Register deployment*, or ⌘K → *Register cell
deployment*) walks four sections:

1. **Cell** — pick a catalog cell (seeding the name, type, and tool list
   verbatim) or start a **Custom cell** for your own code. If a catalog entry
   carries `envVars` or `staticParams`, the editor says so and drops them:
   the engine doesn't read them from a config.
2. **Deployment URL** — the gateway URL in front of your function.
   Required — the engine drops a cell entry without one.
3. **Credential** — **one or more** `${ENV_VAR}` rows (*Add variable*); a
   raw value is refused outright. The **first row** is the credential the
   engine sends with each call, as a bearer token
   (`Authorization: Bearer …`); extra rows publish as numbered siblings for
   cells whose handlers need additional variables. Each row is resolved on
   each machine from its environment or from the value stored for the config
   on the Web Console's **Placeholder values** screen; the environment wins.
   A missing variable disables the cell there, with a message naming it.
4. **Read/write confirmation** — confirm the type; write cells show the
   consequence copy before the editor lets you Save.

Like every console editor, registration writes **only when you press Save**
(disabled with the reason while a required field is missing); your
in-progress entries mirror to a local browser draft until then. Deployment
rows in *My deployments* carry trailing **Edit** / **Delete** actions, with
delete behind a confirmation; a deleted deployment disappears from machines
on their next config pull.

Under the Credential section, the editor previews the line a machine reports
while the variable is missing:

```text
cell "prod-aws" disabled — set PROD_CELL_KEY to enable it
```

> **Warning:** The **https law** — a credentialed cell on plain `http://` to
> a non-loopback host is **skipped by the engine at runtime**. The editor
> pre-flags it; use `https://` (loopback hosts are exempt for local dev).

There is deliberately **no test/ping button**: the Console never contacts
your infrastructure. Discovery and verification happen from your machine
when the engine first connects.

## Using cells

A config uses the deployments you pick in the config builder's **Cells**
step ([Configs](configs.md#components)); only registered deployments with a
URL can be picked, so register a catalog cell before you select it. When the
engine serves the config, it loads the config's cells (its log says
`cells registry — N from config`), and your agents can call their tools — just
ask. Read cells answer in any run; write cells only in execution runs. A
[Human-in-the-Loop](human-in-the-loop.md) rule can require approval before
any call to a given cell, or to every cell.

## Writing your own cell

Take a BYOC starter (AWS / GCP / Azure) from cells.openeng.app, edit one file:
set the id, the type (`read` or `write`), declare the tools with their
parameters, and implement each handler with your cloud's SDK. Deploy like any
other cell. Keep read cells genuinely read-only — put actions in a separate
write cell so the read/write gating protects you.

## Plan & tier notes

The Cells page — catalog and registration — is open on **every plan** with
no count limit. Running cells needs the `openeng` engine, which registers only
with an active **Pro** plan; Pro launches in Q4 2026 and can't be purchased
yet ([Plans & billing](account.md#ai-pricing)).

## Troubleshooting

**`cell "<name>" disabled — set <VAR> to enable it` on a machine.** The cell's `${ENV_VAR}` has no
value where the engine runs. Export it in the shell that starts the engine
(`export PROD_CELL_KEY=…`) and restart the engine, or give it a value on the Web Console's
**Placeholder values** screen — OpenEng never sends an empty secret.

**The cell answers HTTP 401/403.** The gateway rejected the credential. Check
that the variable holds the key your gateway issued and that the gateway has a
usage plan or key attached. Also check which header the gateway reads: the
engine sends the credential as `Authorization: Bearer …`, while the cell
packages' deploy guides set the gateway up to read `x-api-key`.

**The engine skips the cell entirely.** Three causes, all by design: no
URL/name saved (the editor prevents this), the https law (credential over
plain http to a remote host), or `disabled: true` on the entry.

**A write cell doesn't appear in the toolbox.** Write cells are only
available in execution runs. That's the safety model, not a bug.

**Unreachable when the engine first connects.** The Console never tested the
URL (by design). `curl` the gateway URL from the same machine — wrong URL,
undeployed function, or network policy are the usual suspects.
