Atespaces and names
An atespace groups actors, templates, and tags in the Substrate API, and it is not a Kubernetes namespace, although the two often share a name.
You ran kubectl get ns to find where your actors live. You found a namespace with the name of your demo, with worker pods in it and no actor. Then you created a second atespace for a new team, and its template still ran on the pool of the first team. Both results follow from one fact: Substrate keeps two lists of names, and it connects them with labels only.
When you finish this section, you can create an atespace and name an actor and a template in it. You can also select a worker pool from a template, and find a snapshot in the object store from its URI.
An atespace is not a namespace
Atespace: the group that an actor, an ActorTemplate, and a tag belong to in the Substrate API. An atespace is a row in Postgres. It must exist before you create anything in it, and you can delete it only when it is empty.
| Object | Where it lives | How you list it |
|---|---|---|
| Atespace | Substrate API, global | kubectl ate get atespaces |
| Actor | Substrate API, in one atespace | kubectl ate get actors -a <atespace> |
| ActorTemplate | Substrate API, in one atespace | kubectl ate get actor-templates -a <atespace> |
| Tag | Substrate API, in one atespace | kubectl ate get tags -a <atespace> |
| Access policy | Substrate API, one for each atespace | section 5.2 |
| WorkerPool, worker pods | Kubernetes, in one namespace | kubectl get workerpools -n <namespace> |
| Worker record | Substrate API, global | kubectl ate get workers |
Our test setup made both kinds of object with one name. That is our choice, and Substrate did not ask for it.
kind: Namespace
metadata:
name: lab-c
…
kind: WorkerPool
metadata:
name: ballast
namespace: lab-c
labels:
workload: ballast
…
kubectl apply -f pool.yaml
kubectl ate create atespace lab-c 2>&1 | tail -1kubectl apply wrote the namespace and the pool to Kubernetes. kubectl ate create atespace wrote one row through ate-api-server. The quickstart demo does the same with the name ate-demo-counter, so a new user sees one name and assumes one object.
The atespace is the boundary for access. A role in an atespace covers every actor and every template in it, and no grant exists for one actor. Do not use the name of a deleted atespace again. The documentation warns that a request authorized for the old atespace can reach the new one.
Pool selection crosses namespaces
A template selects its workers with workerSelector.matchLabels. The scheduler compares those labels with the labels on the WorkerPool object. It does not read the labels of the pods, and it does not read the namespace of the pool. Each worker carries a copy of the labels of its WorkerPool object, and the scheduler matches against that copy.
Three rules follow from the code and the documentation.
- Only equal labels match. No set expressions exist.
- An actor can add its own selector. The scheduler applies both selectors together, so the selector of the actor only narrows the choice. A change takes effect at the next resume.
- A template with no selector can run on every pool.
So the namespace of a pool is not a fence. A template in any atespace can name the labels of any pool, and Kubernetes RBAC on that namespace does not stop it. No document at v1.0.0 states this rule. It follows from the code, and our run below confirms it.
We ran it. We created an atespace xa and no Kubernetes namespace of that name. A template in xa selected workload: vpool, the label of a pool in the namespace lab-v.
ATESPACE NAME TEMPLATE STATE WORKER POD …
xa x1 xa/cross-0 ACTOR_STATE_RUNNING lab-v/vpool-69595c64bf-p4slt …The template built its golden snapshot on that pool, and the actor ran on a worker pod of lab-v.
A selector that matches no pool is the opposite mistake, and Substrate does not refuse it. We created a second template in xa with the label workload: no-such-pool. The create call succeeded. The template never got a golden tag, and its ERROR column stayed empty. A request to an actor of that template failed.
2026-11-12T12:51:40.304Z call-unmatched-selector 503 5.060180 actor xa/x2 unavailable: no worker has room for the actorWe measured both templates once on the x86 server guest (24 vCPU, 47 GB, nested KVM). The first request to the actor on the pool of lab-v returned 200 in 0.26 seconds. The request with the wrong selector waited 5.06 seconds for its 503. Raw runs
That is the text of a full pool, word for word. ate-api-server logged the same reason for the template 8 times in 40 seconds of its log, and the template showed no error. When you see no worker has room for the actor, compare the selector with the pool labels before you add workers. Section 8.3 has the row.
kagent shows the convention that fills this gap. It writes the name and the namespace of the pool into two labels, and its templates select both.
workerSelector:
matchLabels:
kagent.dev/worker-pool-name: kagent-default
kagent.dev/worker-pool-namespace: kagentThe second label looks like a namespace check, and it is a label like every other. A pool in another namespace with the same two labels would match.
The worker list has one filter for each list of names.
-a, --atespace string filter worker pods hosting actors in a specific atespace
…
-n, --namespace string scope output to a specific Kubernetes namespace
--sandbox-class string filter by sandbox class (e.g. gvisor, microvm)
-l, --selector string filter by worker pool labelsNames
Every name in the Substrate API is a DNS label: lowercase letters, digits, and hyphens, at most 63 characters. A name is fixed at create time.
| What | Form | Example from our runs |
|---|---|---|
| Actor address | <atespace>/<name> | ate-demo-counter/my-counter-1 |
| Template reference | <atespace>/<name> | lab-c/ballast-200 |
| Actor uid | assigned by the server | 6f80f612-7547-4f5b-b3ab-831595621806 |
| Worker name | the uid of the worker pod | c572e96d-dd9b-4500-b0e3-880a98d9f61d |
| Golden tag name | the uid of the template | b9f97b82-b187-419d-8789-6c8c7b8a6c8c |
| kagent actor | session-<session id> | session-01a1cf43-d760-747a-b907-5e0640126e4e |
The actor address is the form that you will read most. Error texts use it, kubectl ate takes its two halves as -a and the name, and the router reads it from a request header. Section 6.1 covers that header.
The uid matters when you delete an actor and create it again with the same name. The new actor gets a new uid. Snapshot paths use the uid, so the new actor cannot resume from the state of the old one.
The snapshot URI layout we saw
Each snapshot in an actor object has a snapshotUri. The atespace and the owner are part of the path.
snapshotUri: gs://ate-snapshots/atespaces/ate-demo-counter/tags/ad19ee28-1519-432a-bcc5-40cf99cce3ca
…
snapshotUri: gs://ate-snapshots/atespaces/ate-demo-counter/actors/6f80f612-7547-4f5b-b3ab-831595621806/snapshots/8faccdff-15cc-493d-8474-5ebcb1c18d56| Owner | Layout |
|---|---|
| A tag | <location>/atespaces/<atespace>/tags/<tag uid> |
| An actor | <location>/atespaces/<atespace>/actors/<actor uid>/snapshots/<snapshot uuid> |
| An actor, on the node | /var/lib/ate/snapshots/<actor uid>/local-checkpoint/<snapshot uuid> |
We saw three details that the layout does not tell you.
- On a kind install the location is
gs://ate-snapshots/, although the store is the rustfs pod in the cluster. - kagent sets a location for each template, and its URIs start with
s3://ate-snapshots/kagent/. That field is a preview feature in the upstream project. - The last part of a golden URI is the uid of the tag. It is not the name of the tag, which is the uid of the template.
The atespace is in the path for a reason. An object store can grant access by path prefix, so you can give one team read access to the snapshots of one atespace. We did not test such a grant.
Our position: use one name for the atespace and the namespace when one team owns both, as the demos do. Then treat the labels of your pools as placement, and not as a fence. Any atespace can name them, and we found no setting at v1.0.0 that limits a pool to one atespace. Choose the labels on purpose, and check which templates name them.
Agent Substrate 1.0.0 · b34fbee · 2026-11-05 · Edition 2026.11