Open the console

Get started

Platform overview

Four nouns, and everything else is derived from them. Three of them you name; the fourth is an operational choice your callers never see.

tenantyour organizationacme

projectone git repositoryagents

workloadone pod — one or several targetsback-office

targetone agent or one workflowinvoice-triage

A tenant

Your organization. It owns your users, your secrets, your balance and your Kubernetes namespace, and it is the boundary nothing crosses: every token issued here carries its tenant, and no route on any service will read another one's data. Tenants are created by platform staff rather than by you.

A project

One git repository. It produces exactly one container image per deploy, holding every agent in the repo — so the deploy unit is the project, and two agents in one repository always ship together.

It is also the unit of authorization, which is the part worth reading twice. A service-user credential is issued for one project, and that scope travels in the token rather than being checked in one place — so a credential for agents calling anything under /v1/projects/tools/ is refused on every service, including invoking an agent there or minting a ticket for one of its users.

A name means nothing outside its project

An agent slug is unique within a project and not within a tenant, so one tenant routinely runs the same agents in salmon-dev and salmon-prod. The same is true of your own users: a wallet is keyed on (project, your user id), so ada in one project is a different person with a different balance from ada in another. This is also why an autonomy policy carries the project in its key — without it, a grant made while testing would apply to the same agent in production.

A scoped credential naming a sibling project is refused with 403, and the message names the project it is for. A project your tenant does not have at all, or an id that belongs to a project your credential cannot see, answers 404 — “no such thing, for you” — so a guess cannot confirm that something exists.

A target: an agent or a workflow

A target is one deployable unit of behavior, and it is either an agent or a workflow. The documentation says “target” wherever the difference does not matter, which is most places: both are deployed the same way, started by the same route, addressed by their own slug, and emit the same event stream.

The difference is who decides what happens next. An agent is one model loop with tools — it decides. A workflow is a graph of nodes with a declared shape — it is told. Agents and workflows covers choosing between them, and what a graph is made of.

A target is addressed by its slug and that name never changes, whatever happens to the pod underneath it.

A workload: the pod

A workload is one pod. By default it holds exactly one target — its own process, its own service account, its own database, its own scaling and its own rollback. That is runtime: isolated, and it is what you get without asking.

You would change it for one reason: money. Autopilot bills every pod at a 250m vCPU / 512Mi floor whatever it actually requests, so a project with five small agents pays for 2.5 vCPU before doing any work — and if they scale to zero you pay five cold starts instead of one. A group is several targets sharing one pod, declared in elan.yaml:

groups:
  back-office:
    resources: {cpu: "1", memory: "2Gi"}
    scaling:   {minReplicas: 1, maxReplicas: 4}
    database:  {enabled: true, sizeGb: 20}

targets:
  expense-notes:  {deployment: {runtime: shared, group: back-office}}
  vendor-lookup:  {deployment: {runtime: shared, group: back-office}}
  receipt-ocr:    {deployment: {runtime: shared, group: back-office}}

Three agents, one pod, one bill, one warm process. What you give up:

IsolatedShared
Scalingper targetper group — a busy member scales its quiet siblings too
Rollbackper targetper group — members move together
A crashtakes down one targettakes down every member
Databaseone per targetone per group, shared by every member
Filesonly this target'severy member can read and write every other member's
Cold startper targetonce for the whole group

A caller cannot tell, and that is the load-bearing part

There is no group anywhere in the API, the CLI, the invocation topic or the event stream — a caller names the target. So moving an agent into a group to save money breaks nothing that calls it, and moving it back out does not either. Each member also keeps its own session identity, so conversations do not blur together even though the rows live in one database.

A group is not a security boundary

Members share a process, a service account, an environment and a database — each can already read the others' DATABASE_URL, and each can read and write the others' files. One pod holds one identity, so no credential can separate things that share it. Group agents because they are operationally alike, not because you trust them equally; if two must not reach each other's data, keep them isolated.

What gets derived from the names

None of this is yours to configure, and it is listed because it turns up in logs, in error messages and on your bill.

ThingShapeExample
Kubernetes namespacet-<tenant>--<project>t-acme--agents
Session identity<tenant>--<project>--<target>acme--agents--invoice-triage
Invocation topicagent.invoke.<tenant>.<project>.<target>agent.invoke.acme.agents.invoice-triage
Event topicagent.events.<tenant>agent.events.acme
Databasedb_<tenant>__<project>__<target>db_acme__agents__invoice_triage

Two separators, for one reason: a slug may contain a single hyphen but never a double one, so -- is always a boundary. With a single hyphen, acme-one--two would be ambiguous between tenant acme-one and tenant acme. The database form swaps in underscores because Postgres identifiers cannot carry a hyphen.

Where to go next

The quickstart deploys one of these end to end. elan.yaml and MANIFEST is where a project declares its targets and its groups — every field is in the elan.yaml reference — and authentication covers the credential that is scoped to one project.

Reading this as a model? llms.txt indexes every page, and each page has a “Copy for AI” control.