Build a Stateful AI with Agent Substrate
Run an AI assistant that keeps its conversation across suspend and restore.
Introduction
30 Second Summary
Most AI Agents sit idle far more than they work. Keeping a computer reserved for each one gets expensive as your team grows. Agent Substrate fixes this: idle Agents sleep, release their computer, and wake up remembering every message.
In this project, you will build an AI assistant that pauses when nobody is using it, frees the computer running it, and returns with the conversation intact. You will run it on Agent Substrate v0.5.0, a local platform that saves the assistant while it rests and wakes it for the next message.
What You'll Build
You'll watch your Agent answer a question, give up the computer it was using, then return later with the earlier conversation still intact.
By the end of this project, you'll have:
- A working assistant that answers your questions and remembers how many exchanges you have had.
- A safer way to connect to an online AI model. The platform adds your private account key only when it sends a question, so the assistant never sees or stores the key.
- Clear proof that pausing works. You will see the assistant release its assigned computer, then receive a new one when it wakes.
- Secret Mission: Create a second assistant from the same saved conversation, then prove the two assistants can take different paths.
Are there any prerequisites?
You can follow this project on macOS or Linux. On Windows, use Windows Subsystem for Linux 2 (WSL2), which gives you a Linux terminal inside Windows. You also need an account with the online AI service used in the project, permission to install local tools, internet access for the first build, and a computer with about four processor cores and 8 GB of memory.
Before We Start
Your confirmed Linux environment provides the consistent starting point for an honest lifecycle test of Atlas, a simulated platform-operations assistant that does not use a language model. Its counters make each state layer visible: process memory represents remembered interactions across suspend and resume, while the file and root-filesystem counters represent other state layers.
Set Up and Build the Local Platform
Your AI assistant needs a working local platform before it can run, pause, or restore its state. This step prepares that platform in a sequence you can verify after every major command.
You will check the host tools, pin Agent Substrate v0.5.0, install its kubectl plugin, and build the local Kubernetes environment. Each checkpoint must pass before you move to the next installer.
In this step, get ready to:
- Check the required tools on your operating system.
- Clone the pinned source and install the kubectl plugin.
- Create the cluster, control plane, and worker pool in order.
Check the host toolchain
Choose the right terminal
Agent Substrate's official setup scripts require a Unix shell. Use zsh on macOS, Bash on Linux, and Bash inside Ubuntu on WSL2 for Windows.
The build uses Git, Go 1.27 or newer, Docker, kubectl 1.37, and curl. Run the check in your operating-system tab, then use its install tab if a tool is missing or outdated.
macOS
- Check the complete toolchain from Terminal by running:
echo "git: $(git --version | awk '{print $3}')"
echo "go: $(go version | awk '{print $3}')"
echo "docker: $(docker info --format '{{.ServerVersion}}' 2>/dev/null || echo 'NOT RUNNING')"
echo "kubectl: $(kubectl version --client | awk '/Client/{print $3}')"
echo "curl: $(curl --version | awk 'NR==1{print $2}')"
✔️ Every tool is ready
Your toolchain is ready when every line shows a version, Docker shows its server version, Go is 1.27 or newer, and kubectl is 1.37.
- Keep Docker Desktop running for the rest of the project.
- Confirm Docker Desktop has about 4 CPUs and 8 GB of memory available in its settings.
ⓧ Go or kubectl is outdated
Update only the tool that reports an older version, then repeat the check.
- Install Go 1.27 or newer from the official Go installation guide.
- Install kubectl 1.37 from the official macOS kubectl guide.
- Restart Terminal after updating either tool.
- Repeat the toolchain check.
ⓧ A tool is missing or stopped
A missing command needs its official installer. A Docker result of NOT RUNNING means Docker Desktop is installed but its engine is stopped.
- Install Git from the official macOS Git page.
- Install Go from the official Go installation guide.
- Install Docker Desktop from the official macOS guide.
- Install kubectl from the official macOS kubectl guide.
- Install curl from the official curl download page.
- Start Docker Desktop before repeating the check.
Windows
Windows learners run every project command inside Ubuntu on WSL2. Use PowerShell only for the two WSL checks below, then return to Ubuntu.
- Confirm WSL meets Docker's minimum version and Ubuntu uses WSL2 from PowerShell by running:
wsl --version
wsl -l -v
- Open Ubuntu from the Windows Start menu when its VERSION column shows 2.
- Check the complete toolchain inside the Ubuntu terminal by running:
echo "git: $(git --version | awk '{print $3}')"
echo "go: $(go version | awk '{print $3}')"
echo "docker: $(docker info --format '{{.ServerVersion}}' 2>/dev/null || echo 'NOT RUNNING')"
echo "kubectl: $(kubectl version --client | awk '/Client/{print $3}')"
echo "curl: $(curl --version | awk 'NR==1{print $2}')"
✔️ WSL2 and every tool are ready
Your Windows environment is ready when WSL is 2.1.5 or newer and Ubuntu's VERSION column shows 2. Every tool check should show a version, Docker should show its server version, Go should be 1.27 or newer, and kubectl should be 1.37.
- Keep Docker Desktop running for the rest of the project.
- Confirm Docker Desktop uses its WSL2 engine.
- Enable Ubuntu in Docker Desktop's WSL integration settings.
ⓧ Ubuntu or WSL2 is missing
WSL setup is a little fiddly because Windows and Ubuntu are separate environments. Complete the Windows layer before installing tools inside Ubuntu.
- Install Ubuntu on WSL2 through the official Microsoft WSL guide.
- Restart Windows when the WSL installer asks.
- Install Docker Desktop with its WSL2 backend through the official Windows guide.
- Repeat the WSL check from PowerShell.
ⓧ An Ubuntu tool is missing or outdated
Install or update the affected tool inside Ubuntu. The rest of the project runs from that Ubuntu terminal.
- Install Git through the official Linux Git guidance.
- Install Go 1.27 or newer through the official Go installation guide.
- Install kubectl 1.37 through the official Linux kubectl guide.
- Install curl from the official curl download page.
- Start Docker Desktop before repeating the Ubuntu check.
Linux
- Check the complete toolchain from your terminal by running:
echo "git: $(git --version | awk '{print $3}')"
echo "go: $(go version | awk '{print $3}')"
echo "docker: $(docker info --format '{{.ServerVersion}}' 2>/dev/null || echo 'NOT RUNNING')"
echo "kubectl: $(kubectl version --client | awk '/Client/{print $3}')"
echo "curl: $(curl --version | awk 'NR==1{print $2}')"
✔️ Every tool is ready
Your toolchain is ready when every line shows a version, Docker shows its server version, Go is 1.27 or newer, and kubectl is 1.37.
- Keep Docker Engine running for the rest of the project.
- Confirm your container runtime can use about 4 CPUs and 8 GB of memory.
ⓧ Go or kubectl is outdated
Update only the tool that reports an older version, then repeat the check.
- Install Go 1.27 or newer through the official Go installation guide.
- Install kubectl 1.37 through the official Linux kubectl guide.
- Restart your shell after updating either tool.
- Repeat the toolchain check.
ⓧ A tool is missing or stopped
Use the official installer for each missing tool. A Docker result of NOT RUNNING means the engine is installed but stopped.
- Install Git through the official Linux Git guidance.
- Install Go through the official Go installation guide.
- Install Docker Engine through the official Docker guide.
- Install kubectl through the official Linux kubectl guide.
- Install curl from the official curl download page.
- Start Docker Engine before repeating the check.
A prerequisite check still fails?
- Restart your terminal after installing a tool or changing PATH.
- Confirm Docker responds before continuing.
- Help me diagnose my Agent Substrate prerequisite checks.
Clone the source and install the plugin
Pinning the repository prevents later source changes from altering the behavior you test. The plugin is installed from that same checkout, so the release and CLI stay aligned.
- Create the Desktop folder under your shell's home directory if needed and move into it by running these commands:
mkdir -p "$HOME/Desktop"
cd "$HOME/Desktop"
- Clone the repository and check out the pinned commit by running:
git clone https://github.com/agent-substrate/substrate.git
cd substrate
git checkout e8d40c33f96f4b7a4984bfea7334a36ab5b45b7b
git rev-parse HEAD
You should see e8d40c33f96f4b7a4984bfea7334a36ab5b45b7b. That exact match anchors the project to Agent Substrate v0.5.0.
Commit hash does not match?
- Confirm your terminal is inside the substrate folder.
- Repeat the checkout command before running the hash check again.
- Help me fix my pinned Agent Substrate checkout.
- Install the pinned kubectl plugin from the substrate folder by running:
go install ./cmd/kubectl-ate
The first install can pause while Go downloads and compiles dependencies. Leave the terminal open until the command returns to the prompt.
- Add Go's binary folder to your shell PATH from your operating-system tab.
macOS
- Add Go's binary folder to your zsh PATH by running:
echo 'export PATH="$PATH:$(go env GOPATH)/bin"' >> ~/.zshrc && source ~/.zshrc
Windows
- Add Go's binary folder to your Ubuntu bash PATH by running:
echo 'export PATH="$PATH:$(go env GOPATH)/bin"' >> ~/.bashrc && source ~/.bashrc
Linux
- Add Go's binary folder to your bash PATH by running:
echo 'export PATH="$PATH:$(go env GOPATH)/bin"' >> ~/.bashrc && source ~/.bashrc
- Confirm kubectl can discover the installed plugin by running:
kubectl plugin list
You should see a line ending in kubectl-ate. The plugin is now available for the platform checks later in this step.
kubectl cannot find ate?
The plugin binary is outside your active PATH when kubectl cannot discover it. Repeat the PATH command for your operating system, then open a new terminal.
Help me fix kubectl plugin discovery.
Build and verify the local platform
The platform now comes together in three checkpoints: cluster, control plane, then worker pool. Do not start the next installer until the current checkpoint passes.
- Create the kind cluster and local image registry by running:
./hack/create-kind-cluster.sh
- Select the new cluster and confirm kubectl can reach it by running:
kubectl config use-context kind-kind
kubectl cluster-info
You should see Switched to context "kind-kind" followed by the local Kubernetes control-plane address. The cluster checkpoint now passes.
Keep this terminal open
The first control-plane install can take 10–15 minutes while it builds and publishes images. Keep the terminal open until the installer returns to the prompt.
- Install the control plane with the Kubernetes credential provider by running:
./hack/install-ate-kind.sh --deploy-ate-system --credential-provider='{"name":"k8s.io"}'
Before you check the installation, do you expect the ate-system namespace and api service to exist?
- Verify the control plane after the installer returns to the prompt by running:
kubectl --context kind-kind get namespace ate-system
kubectl --context kind-kind get service api -n ate-system
kubectl --context kind-kind get pods -n ate-system
You should see the ate-system namespace, an api service, and control-plane pods that are running or completed. That proves the control plane is available before the demo installer starts.
Seeing services "api" not found?
The error failed to connect to ate-api-server: services "api" not found means kubectl points at a cluster without Agent Substrate.
Help me restore the Agent Substrate cluster context.
The counter demo provides the WorkerPool that your AI assistant runs on in Step 2.
- Deploy that verified WorkerPool and its template by running:
./hack/install-ate-kind.sh --deploy-demo-counter
Before you run the final check, do you expect both the demo template and its workers to be available?
- Verify the template and workers by running:
kubectl ate get actor-templates -a ate-demo-counter
kubectl ate get workers
What do the final checks confirm?
- The actor-template query confirms the reusable demo pattern is available.
- The worker query confirms the platform has capacity ready to host your assistant.
You should see the counter template and available workers. That's the setup complete: the local platform can now host your AI workload.
A platform installer stopped early?
- Confirm Docker is still running before retrying the failed checkpoint.
- Confirm kubectl still reports the kind-kind context.
- Retry only the installer that stopped.
- Help me diagnose my Agent Substrate platform installation.
Your local platform now has a verified control plane and WorkerPool. Next, you'll secure the OpenRouter key and replace the demo template with your AI assistant.
Wake Atlas and Send Its First Interaction
Build and Run Your AI Assistant
Your pinned local platform now has a verified gVisor WorkerPool and actor-template pattern. The next goal is to replace the counter workload with a real AI service.
The service keeps its conversation in process memory. Agent Substrate protects the OpenRouter key by replacing a placeholder header at the egress gateway before the request leaves the cluster.
This separation keeps the key outside the actor's code, environment, filesystem, process memory, and snapshots. Your first routed prompt provides the visible win for the platform.
In this step, get ready to:
- Store the OpenRouter key behind the egress credential provider.
- Build the one-file AI workload and gVisor actor template.
- Create actor assistant and send its first prompt.
Secure OpenRouter access
OpenRouter supplies the model, while the Kubernetes Secrets provider supplies its key only to the egress gateway. The actor sends Authorization: placeholder and never receives the replacement value.
✔️ I already have OpenRouter
An existing OpenRouter account can create a project key directly from its settings. OpenRouter displays the plaintext key once, so keep the page open until you have copied it.
- Open the OpenRouter API Keys page in your browser.
- Click Get API Key
- Create a key named agent-substrate-local.
- Copy the key once.
- Keep the key out of screenshots, messages, and project files.
ⓧ I don’t have OpenRouter yet
You need an OpenRouter account before you can create the project key. The key appears in plaintext once, so keep the page open until you have copied it.
- Open the OpenRouter website in your browser.
- Create your OpenRouter account.
- Sign in to your new account.
- Open the OpenRouter API Keys page.
- Create a key named agent-substrate-local.
- Copy the key once.
- Return to your substrate terminal.
- Keep the key out of screenshots, messages, and project files.
- Return to your substrate terminal.
- Create or update the ai-assistant namespace and openrouter Secret without displaying the copied key by running:
kubectl create namespace ai-assistant --dry-run=client -o yaml | kubectl apply -f -
printf 'Paste your OpenRouter key: '
IFS= read -rs OPENROUTER_KEY
printf '\n'
if [ -z "$OPENROUTER_KEY" ]; then
printf 'No key entered. The Secret was not changed.\n'
else
printf '%s' "$OPENROUTER_KEY" | \
kubectl -n ai-assistant create secret generic openrouter --from-file=api-key=/dev/stdin --dry-run=client -o yaml | \
kubectl apply -f -
kubectl -n ai-assistant get secret openrouter
fi
unset OPENROUTER_KEY
The prompt hides the pasted key in Bash and zsh. You should see the openrouter Secret listed without exposing its value, so keep this command sequence out of screenshots.
- Grant atespace ai-assistant access to Secrets in the matching Kubernetes namespace by running:
cat > agent-substrate-namespace-policy.yaml <<'EOF'
policies:
- atespace: ai-assistant
allowedNamespaces:
- ai-assistant
EOF
kubectl -n ate-system create configmap k8s-credential-provider-namespace-policy \
--from-file=namespace-policy.yaml=agent-substrate-namespace-policy.yaml \
--dry-run=client -o yaml | kubectl apply -f -
rm agent-substrate-namespace-policy.yaml
kubectl -n ate-system rollout restart deployment/k8s-credential-provider
kubectl -n ate-system rollout status deployment/k8s-credential-provider
You should see the credential-provider deployment complete its rollout. The provider reads this policy once at startup, which is why the restart matters.
Credential provider returns 403?
A 403 from the credential provider means the atespace lacks permission or the provider still holds its earlier policy. Confirm the policy names ai-assistant and namespace ai-assistant, then restart the provider again.
Help me diagnose the credential-provider 403 without exposing my key.
💡 Free-tier limits
OpenRouter currently limits free-model requests to 20 per minute and 50 per day for accounts below the purchased-credit threshold. The project uses only a few calls, so normal completion stays inside those limits.
Build the one-file AI workload
The service uses only Python's standard library. It keeps the chat in one process-level list, asks OpenRouter, then returns the actual model, reply, and completed turn count.
- Create the assistant-workload folder and start writing app.py by running:
mkdir -p assistant-workload
cd assistant-workload
cat > app.py
Your terminal now waits for the file contents. Paste this complete service as one block:
import json
from http.client import HTTPSConnection
from http.server import BaseHTTPRequestHandler, HTTPServer
# Process memory holds every completed conversation turn.
messages = []
models = ["openai/gpt-oss-20b:free", "openrouter/free"]
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200); self.end_headers(); self.wfile.write(b"ready")
def do_POST(self):
size = int(self.headers.get("Content-Length", 0))
prompt = json.loads(self.rfile.read(size)).get("prompt", "")
messages.append({"role": "user", "content": prompt})
# The gateway replaces this placeholder before egress.
body = json.dumps({"models": models, "messages": messages})
client = HTTPSConnection("openrouter.ai", timeout=60)
client.request("POST", "/api/v1/chat/completions", body, {"Content-Type": "application/json", "Authorization": "placeholder"})
response = client.getresponse(); result = json.loads(response.read())
if response.status != 200: self.send_response(response.status); self.end_headers(); self.wfile.write(json.dumps(result).encode()); return
reply = result["choices"][0]["message"]["content"]
messages.append({"role": "assistant", "content": reply})
# Return the state learners verify after each lifecycle change.
output = json.dumps({"model": result["model"], "reply": reply, "turn_count": len(messages) // 2})
self.send_response(200); self.send_header("Content-Type", "application/json"); self.end_headers(); self.wfile.write(output.encode())
HTTPServer(("0.0.0.0", 8080), Handler).serve_forever()
What does this service do?
- messages stores every user and assistant message in process memory.
- models tries openai/gpt-oss-20b:free first, then openrouter/free if the primary model fails.
- The outbound request sends a placeholder Authorization header for the gateway to replace.
- The JSON response exposes the model that answered, its reply, and the completed turn count.
- Press Ctrl+D to save app.py.
- Create the workload image definition by running:
cat > Dockerfile <<'EOF'
FROM python:3.13-slim
WORKDIR /app
COPY app.py /app/app.py
CMD ["python", "/app/app.py"]
EOF
- Build and push the only changed workload image by running:
docker build -t localhost:5001/assistant:0.1 .
docker push localhost:5001/assistant:0.1
You should see the image layers finish uploading to the local registry on port 5001.
- Create atespace ai-assistant by running:
kubectl ate create atespace ai-assistant
- Start the actor template file by running:
cat > assistant-template.yaml <<'EOF'
metadata:
atespace: ai-assistant
name: assistant
workerSelector:
matchLabels:
workload: counter
containers:
- name: assistant
image: localhost:5001/assistant:0.1
command: ["python", "/app/app.py"]
env:
- name: SSL_CERT_FILE
value: /run/ate/egress-trust-bundle/trust-bundle.pem
wakeupProbe:
httpGet:
path: /
port: 8080
volumeMounts:
- name: system-info
mountPath: /run/ate/egress-trust-bundle/
EOF
- Finish the template with the counter demo's memory snapshot and gVisor pattern by running:
cat >> assistant-template.yaml <<'EOF'
resources:
limits:
- name: cpu
quantity: "1"
- name: memory
quantity: 512Mi
snapshotConfig:
preferredFidelity: SNAPSHOT_FIDELITY_MEMORY
storageLocation: gs://ate-snapshots/ai-assistant/
sandboxConfig:
sandboxClass: gvisor
configName: gvisor-default
volumes:
- name: system-info
systemInfo:
dataSources:
- trustBundle:
names:
- egress-mitm.ate.dev
- system-roots.ate.dev
path: trust-bundle.pem
EOF
kubectl ate create actor-template -f assistant-template.yaml
SSL_CERT_FILE tells Python to trust the mounted egress bundle when the gateway intercepts OpenRouter's HTTPS connection.
Certificate verification failed?
- Confirm the template mounts trust-bundle.pem and sets SSL_CERT_FILE to the same path.
- Confirm the trust bundle includes both egress-mitm.ate.dev and system-roots.ate.dev.
- Help me diagnose the certificate verification error.
Create your assistant and send its first prompt
- Create actor assistant from template assistant by typing:
kubectl ate create actor assistant -a ai-assistant --template assistant
- Create the actor's egress policy file by running:
cat > assistant-egress.yaml <<'EOF'
rules:
- https:
hostnames: ["openrouter.ai"]
effects:
replaceHeaders:
- header: Authorization
prefix: "Bearer "
credentialUri: ate-secret://k8s.io/default/ai-assistant/openrouter/api-key
EOF
kubectl ate create egress-policy assistant -a ai-assistant -f assistant-egress.yaml
- Open a second terminal and start the router port-forward by typing:
kubectl port-forward -n ate-system svc/atenet-router 8000:80
Keep that terminal running. The command owns the local route until you press Ctrl+C.
Before you send the first prompt, do you expect the response to reveal the placeholder or the real key?
- Return to the assistant-workload terminal and send the first prompt by typing:
curl -sS -X POST \
-H "Content-Type: application/json" \
-H "ate-target-actor: ai-assistant/assistant" \
--data '{"prompt":"Remember that my favorite observability signal is worker reuse."}' \
http://localhost:8000
kubectl ate get actor assistant -a ai-assistant
You'll see a JSON response with a model name, an AI reply about your prompt, and turn count 1. The reply's wording can vary.
The actor row shows ACTOR_STATE_RUNNING with a populated WORKER IPS field. That proves a real model request activated your assistant on a worker.
OpenRouter returns 429?
A 429 means the free-model limit is temporarily exhausted. Wait for the limit window to reset, then retry the same prompt once.
Help me handle the OpenRouter 429 safely.
Your AI assistant is live, remembers one turn, and still holds its worker. Next, you'll release that worker and test whether the conversation returns.
Suspend Atlas and Restore Its Memory
Suspend and Restore Your AI Assistant
Your AI assistant has one completed turn in process memory. It remains attached to a worker even though the request has finished.
Agent Substrate can capture that running process in a durable snapshot, then release the worker. A later request restores the same process before Python handles the prompt.
The final response tests conversation continuity instead of exact model wording. The reply should use the detail from your first prompt.
In this step, get ready to:
- Confirm the assistant template's memory and sandbox contract.
- Suspend actor assistant and verify its worker is released.
- Send a follow-up prompt and verify the earlier conversation returns.
Confirm the assistant's state contract
The template must capture process memory for the Python conversation list to survive. Its sandbox must also match the official gVisor WorkerPool.
- Inspect template assistant by typing:
kubectl ate get actor-template assistant -a ai-assistant -o yaml
- Locate preferredFidelity: SNAPSHOT_FIDELITY_MEMORY.
- Locate sandboxClass: gvisor.
- Locate SSL_CERT_FILE with the mounted trust-bundle path.
Suspend the assistant and release its worker
A running actor occupies WorkerPool capacity. Suspension preserves its process while making that capacity available for another actor.
Before you suspend the assistant, do you think WORKER IPS will remain populated?
- Suspend actor assistant by typing:
kubectl ate suspend actor assistant -a ai-assistant
- Inspect the actor and WorkerPool after suspension by typing:
kubectl ate get actor assistant -a ai-assistant
kubectl ate get workers
You should see ACTOR_STATE_SUSPENDED with a blank WORKER IPS field. The worker view shows the released capacity.
That is the reclamation win: your AI assistant keeps its snapshot without occupying a worker.
Worker assignment still populated?
- Wait for the actor to finish ACTOR_STATE_SUSPENDING before checking again.
- Confirm you suspended assistant in atespace ai-assistant.
- Help me diagnose the unfinished suspension.
Restore the conversation on demand
A routed request wakes a suspended actor. The snapshot restores Python's process before the new prompt is appended to its conversation.
Before you run the final check, do you think the assistant can refer to your earlier observability preference?
- Send a follow-up prompt and inspect the restored actor by typing:
curl -sS -X POST \
-H "Content-Type: application/json" \
-H "ate-target-actor: ai-assistant/assistant" \
--data '{"prompt":"Which observability signal did I say was my favorite?"}' \
http://localhost:8000
kubectl ate get actor assistant -a ai-assistant
The reply should mention what you said earlier about worker reuse. Its exact wording can vary, while the turn count should continue to 2.
The actor row should return to ACTOR_STATE_RUNNING with a populated WORKER IPS field. The restored conversation is the evidence that process memory survived.
Conversation did not return?
- Confirm the router port-forward is still running in the second terminal.
- Confirm the target header uses ai-assistant/assistant.
- Confirm the earlier response completed before you suspended the actor.
- Help me diagnose the missing conversation memory.
You have closed the lifecycle loop. Your AI assistant releases idle compute, then returns with its conversation intact.
Secret mission
Branch Your Assistant's Memory
Create assistant-branch from your AI assistant's shared checkpoint. Send one different prompt to each actor, then prove their conversations develop in separate directions.
Clean Up Your Resources
Clean Up Your Resources
Your Agent Substrate platform runs locally on a kind cluster. Keeping it active creates no cloud charges. It continues to use local memory plus disk space.
Resources you used:
- The local kind cluster and kind-registry container.
- The Agent Substrate control plane, snapshot store, and counter WorkerPool.
- The assistant workload image in the local registry.
- Atespace ai-assistant, template assistant, and actor assistant.
- The optional assistant-branch actor and assistant-checkpoint tag.
- The Kubernetes Secret and credential-provider namespace policy.
- The router port-forward on localhost:8000.
- The OpenRouter API key created for this project.
Keep everything running
No action needed. Choose this if you are still testing your AI assistant or want to explore more lifecycle behavior.
- Leave the local kind cluster and registry running.
- Keep the router port-forward active while you send more prompts.
- Retain assistant-checkpoint while assistant-branch borrows its snapshot.
- Keep the OpenRouter key only if you plan to reuse it.
The platform continues to use local memory and disk space. Low-volume OpenRouter free-model requests remain subject to its free limits.
Pause - I'll come back to this later
Shut down active processes to free local memory while keeping the cluster state for your return.
- Switch to the terminal running the router port-forward.
- Press Ctrl+C to stop the port-forward.
- Return to the substrate terminal.
- Suspend the original actor by running:
kubectl ate suspend actor assistant -a ai-assistant
- Suspend the optional branch if you completed the Secret Mission by running:
kubectl ate suspend actor assistant-branch -a ai-assistant
- Check the suspended actors by running:
kubectl ate get actor assistant -a ai-assistant
kubectl ate get actor assistant-branch -a ai-assistant
Each existing actor should show ACTOR_STATE_SUSPENDED with a blank WORKER IPS field.
Delete - I don't want to use this again
Remove the local platform and revoke the project key to start fresh. Your local substrate source folder remains on disk.
Deletion is permanent
Deleting the cluster removes every local actor, template, checkpoint, Kubernetes Secret, policy, WorkerPool, image registry, and snapshot stored inside it.
Revoking the OpenRouter key prevents any later request from using that credential.
- Stop the router port-forward with Ctrl+C if it is still running.
- Open the OpenRouter API Keys page.
- Delete the key you created for this project.
- Return to the substrate terminal.
- Delete the cluster and local registry through the official script by running:
./hack/delete-kind-cluster.sh
- Wait for the command to return to the terminal prompt.
- Confirm the final output reports that the kind cluster and registry were removed.
Your local Agent Substrate resources no longer consume memory. The revoked OpenRouter key can no longer authorize requests.
Cleanup script not running?
- Confirm your terminal is inside the pinned substrate folder.
- Confirm Docker is running so the script can remove the registry container.
- Help me diagnose the local cleanup failure.
Nice Work!
Nice Work!
You made it! You built a local Agent Substrate v0.5.0 platform and ran a real AI assistant through OpenRouter.
Your assistant answered real prompts, released its idle worker, then restored the earlier conversation from process memory.
You've learned how to:
- Build a reproducible local platform from the pinned Agent Substrate release across macOS, Windows with Ubuntu on WSL2, or Linux.
- Route credential-safe AI requests through an egress policy that injects the OpenRouter key outside the actor.
- Prove stateful suspend and restore by observing ACTOR_STATE_SUSPENDED, a blank WORKER IPS field, and the restored turn count.
- Secret Mission: Create assistant-branch from assistant-checkpoint, then prove the two actors develop different conversations from shared memory.
Ready to quiz yourself?