Part 4

Run a first ax Task

Install ax, declare a Workspace, a Model, and a Task, then run, watch, and inspect it. Each step shows what success looks like.

After this page you can:

  • Install the ax CLI and control plane at release v0.3.1 on the upstream Substrate cluster from Setup.
  • Declare a Workspace, a Model, and a Task, and apply them to an atespace.
  • Watch a Task reach Ready and inspect it from inside the sandbox.

Before you start

Complete the shared setup first, and use the cluster with upstream Substrate v0.3.0 from that page. ax also needs Go, kubectl, ko, and a container registry your cluster can pull from, because the control plane is built from source.1 Commands below follow the ax README and docs at main ac23328, which match release v0.3.1 for everything used here, and both install commands are pinned to the v0.3.1 tag.14

Install ax

  1. Install the CLI. The binary lands in $(go env GOPATH)/bin, which must be on your PATH. The README uses @latest; this lab pins @v0.3.1 so you get the version the page was checked against.1

    go install github.com/google/ax/cmd/ax@v0.3.1
  2. Deploy the control plane from a checkout of google/ax. Replace REGISTRY with a registry your cluster can pull from. This deploys Redis, then builds and deploys the control plane images with ko, all into the ax-system namespace.1

    git clone --branch v0.3.1 https://github.com/google/ax.git
    cd ax
    make deploy AX_IMAGE_REPO=REGISTRY

    Expected result: kubectl get pods -n ax-system shows the Redis and control plane pods Running.

    If it does not work: check that kubectl get svc api -n ate-system still lists the api Service; ax cannot start without a reachable Substrate Control API.1

Declare a Workspace, a Model, and a Task

All three kinds use apiVersion: ax.io/v1alpha1 and live in an atespace, a Substrate tenancy unit that is separate from Kubernetes namespaces. The default atespace is named default.320 Names and atespaces must be lowercase RFC 1123 labels.4 Save the three blocks below into one file, task.yaml, separated by --- lines.

Workspace: what the sandbox starts with

apiVersion: ax.io/v1alpha1
kind: Workspace
metadata:
  name: default-workspace
  atespace: default
spec:
  git:
    - name: origin
      repo: "https://github.com/chalk/chalk.git"
      branch: "main"
  mcp:
    servers:
      - name: git-tools
        endpoint: "http://git-mcp.default.svc.cluster.local:8080"

spec.git clones repositories into the workspace path, and spec.mcp.servers lists MCP servers the agent may call. Replace the MCP endpoint with a server that exists in your cluster, or delete the mcp block.34

Model: which LLM, with which credential

Create the Secret first, then reference it. The ax docs pass the key with --from-literal; this lab reads it with read -rs, which does not echo it, and pipes it with the printf builtin, so the key never appears in the process list or your shell history and the Secret holds no trailing newline. Paste the key at the prompt and press Enter.4

read -rs KEY && printf %s "$KEY" | kubectl create secret generic anthropic-api-secret -n default --from-file=ANTHROPIC_API_KEY=/dev/stdin; unset KEY
apiVersion: ax.io/v1alpha1
kind: Model
metadata:
  name: claude-model
  atespace: default
spec:
  provider: anthropic
  model: claude-opus-5
  secretKey:
    name: anthropic-api-secret
    key: ANTHROPIC_API_KEY

A Model keeps provider configuration in one place, and ax's own components read it too, for example when planning a workspace from a goal.3

Task: the unit of execution

apiVersion: ax.io/v1alpha1
kind: Task
metadata:
  name: task123
  atespace: default
spec:
  # Placeholder: replace with your agent image and its entrypoint.
  image: "ghcr.io/my-org/my-agent-image"
  command:
    - python
    - agent.py
  resources:
    limits:
      cpu: "2"
      memory: "4Gi"
  workspaces:
    - name: default-workspace
  debug: true
  • image and command are your agent. ax does not supply a loop.3
  • workspaces binds the Workspace above; the first entry is the command's working directory and mounts at /workspace/<name> unless a path is set.4
  • debug: true serves Substrate guest services inside the sandbox, which ax ssh needs. It is off by default because it allows arbitrary process execution and file access.5

A binding may also carry a goal. On first boot the runner hands that goal to an Antigravity agent (Google's coding agent, bundled in the runner) to finish setup. That agent needs GEMINI_API_KEY in the container and gets 10 minutes by default, set by AX_BOOTSTRAP_TIMEOUT.5

Run, watch, and inspect

  1. Apply the file and list Tasks.1

    ax apply -f task.yaml
    ax get tasks

    Expected result (column layout from the ax README; IP and age vary):1

    NAME      ATESPACE   PHASE     ACTOR           WORKER-IP    AGE
    task123   default    Running   task123         10.20.3.67   1m
  2. Stream phase and condition changes. Wait for WorkspaceReady and then Ready to become True; Ready is the condition to wait on.3

    ax watch task task123

    If it does not work: a Task that stays not-ready is usually still preparing its workspace, since the runner's /readyz returns 503 until clones, MCP configuration, and skills are in place.5 Use ax describe task task123 to read the conditions.1

  3. Look inside the sandbox. This works only because the Task set debug: true.15

    ax ssh task123 -- ls -la /workspace
  4. Suspend and resume. Suspending sets Ready to False with reason TaskSuspended; resuming sets it back.3

    ax suspend task task123
    ax resume task task123
  5. Clean up. ax delete blocks until the sandbox has been torn down on Substrate.3

    ax delete task task123

ax keeps these resources in Redis, so kubectl get tasks does not exist; the ax CLI is the interface.2 The State page covers why.

What happens inside the sandbox

Figure: ax-task-runner boots in four steps and stays as PID 1 after your command exits.

Your process reads its own configuration from $AX_METADATA_URL/metadata/v1alpha1/ax/task without any SDK.5 The runner stays up as PID 1 whether or not the command is still running, so ax ssh keeps working after the command exits. On stop or suspend, it sends the command's process group SIGTERM, waits ten seconds, and kills what remains.5

Figure: an ax apply goes from the CLI to ax-server, which records state in Redis and reconciles the Task into a Substrate Actor.

ax-server serves gRPC on port 8080, uses Redis for storage, locks, and PubSub, and reconciles directly with Agent Substrate.2