> ## Documentation Index
> Fetch the complete documentation index at: https://autonomy.computer/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Build step-by-step

> Create and deploy an Autonomy app, step-by-step.

Build and ship your first Autonomy application step-by-step. This walkthrough
helps you understand how the parts of a simple Autonomy app fit together.

1. Follow the initial instructions to [Get Started with Autonomy](/get-started).
2. Then continue on to the following steps:

***

## Step 1: Create a new directory

Create a new directory for your app.

```bash theme={null}
mkdir hello && cd hello
```

<Note>
  All subsequent commands should be run from inside this `hello` directory.
</Note>

***

## Step 2: Create `autonomy.yaml`

Create a file named `autonomy.yaml` in the `hello` directory with the following content:

```yaml autonomy.yaml theme={null}
name: hello
pods:
  - name: main-pod
    public: true
    containers:
      - name: main
        image: main
```

This configuration file defines a **zone** in the Autonomy Computer.
Think of a zone as your app's dedicated infrastructure.
The Autonomy Computer provisions everything needed to run it.

* **`name: hello`** - The zone's name (must be `≤ 10` characters, using only `a to z` and `0 to 9`).
* **`pods`** - List of pods to create in this zone (a pod is a group of containers that run together).
* **`public: true`** - Serve the HTTP server on port 8000 of this pod on a public address over HTTPS.
* **`containers`** - List of containers in the `main-pod`.
* **`image: main`** - Create the `main` container using the image defined in `images/main`.

***

## Step 3: Create the image directory

Create the directory structure for your container images:

```bash theme={null}
mkdir -p images/main
```

Your project structure now looks like:

```
hello/
|-- autonomy.yaml
|-- images/
    |-- main/
```

***

## Step 4: Create a Dockerfile

Create `images/main/Dockerfile` with the following content:

```dockerfile images/main/Dockerfile theme={null}
FROM ghcr.io/build-trust/autonomy-python
COPY . .
ENTRYPOINT ["python", "main.py"]
```

* The base image is pre-installed with Python and the Autonomy Framework.
* Copy all files from `images/main/` into the container.
* Run `main.py` when the container starts.

***

## Step 5: Create the application code

Create `images/main/main.py` with the following content:

```python images/main/main.py theme={null}
from autonomy import Agent, Model, Node

async def main(node):
  await Agent.start(
    node=node,
    name="henry",
    instructions="You are Henry, an expert legal assistant",
    model=Model("claude-sonnet-4-v1")
  )

Node.start(main)
```

This Python module:

1. **Imports modules from the Autonomy Framework** - `Agent`, `Model`, and `Node`
2. **Defines an async main function** that:
   * Starts an agent named "henry"
   * Gives it instructions to act as a legal assistant
   * Configures it to use Claude Sonnet 4 model
3. **Starts an Autonomy Node** - This creates the actor runtime that hosts your agent. It also starts an HTTP server on port 8000 with a set of built-in APIs to interact with your agent.

**Built-in tools**: Your agent automatically has access to a set of built-in tools:

* `get_current_time_utc` - Get current time in UTC.
* `get_current_time` - Get current time in any timezone.

***

## Step 6: Create a web interface

Create `images/main/index.html` with the following content:

```html images/main/index.html theme={null}
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <style>
    body { margin: 10% 15%; line-height: 1.8; overflow-y: scroll; overflow-x: hidden; font: 21px "Courier New", Courier, monospace; padding: 0; }
    main { max-width: 1080px; margin: 0 auto; padding: 3rem 1rem; }
    pre { padding-top: 30px; box-sizing: border-box; white-space: pre-wrap; overflow-wrap: break-word; }
    textarea { width: 100%; max-width: 1080px; box-sizing: border-box; border: 1px solid #e9e9e9; border-radius: 5px; box-shadow: 2px 2px 10px #f4f4f4; font-size: inherit; padding: 1rem; display: block; height: calc(3 * 1.8em + 14.8px); }
  </style>
  <title>Henry - Legal Assistant</title>
</head>
<body>
  <main>
    <textarea id="in" autofocus placeholder="How can I help you..."></textarea>
    <pre id="out"></pre>
  </main>

  <script>
  async function appendCharByChar(text, delay = 5) {
    for (const char of text) {
      out.textContent += char;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  async function processLine(line) {
    if (line.trim()) {
      const parsed = JSON.parse(line);
      for (const message of parsed.messages) {
        if (message && message.content && message.content.text) {
          await appendCharByChar(message.content.text);
        }
      }
    }
  }

  async function send(message) {
    const input = document.getElementById("in");
    input.disabled = true;

    const out = document.getElementById("out");
    out.textContent = "";

    const response = await fetch("/agents/henry?stream=true", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ message: message }),
    });

    const decoder = new TextDecoder();
    let buffer = "";

    for await (const chunk of response.body) {
      buffer += decoder.decode(chunk, { stream: true });
      const lines = buffer.split('\n');
      buffer = lines.pop();

      if (!out.textContent) input.value = "";
      for (const line of lines) await processLine(line);
    }
    await processLine(buffer);

    input.disabled = false;
    input.focus();
  }

  document.getElementById("in").addEventListener("keydown", async e => {
    if (e.key === "Enter" && !e.shiftKey && !e.target.disabled && e.target.value.trim()) {
      e.preventDefault();
      await send(e.target.value);
    }
  });
  </script>
</body>
</html>
```

This HTML file creates a simple chat interface that:

1. **Displays an input text area** where users type messages.
2. **Calls the agent's streaming API** at `/agents/henry?stream=true`.
3. **Shows responses using a typewriter effect** character by character.
4. **Handles streaming JSON** responses from the agent.

The Autonomy Node's default HTTP server automatically serves `index.html`
at the root of the pod's URL.

The complete code structure now looks like:

```
hello/
|-- autonomy.yaml
|-- images/
    |-- main/
        |-- Dockerfile
        |-- main.py
        |-- index.html
```

***

## Step 7: Enroll with your cluster

Connect your workstation to your cluster in Autonomy Computer:

```bash theme={null}
autonomy cluster enroll --no-input
```

This command:

1. Displays a code.
2. Opens your browser for authentication.
3. Completes enrollment once you sign in.

You only need to do this once per workstation.

***

## Step 8: Deploy your app

Deploy your zone to the Autonomy Computer:

```bash theme={null}
autonomy zone deploy
```

This command:

1. Builds the container image.
2. Pushes the image to your cluster.
3. Deploys the zone with its pods and containers.

The deployment takes a couple of minutes to fully start.

***

## Step 9: Your zone's URL

Your zone's URL follows this pattern:

```
https://${CLUSTER}-${ZONE}.cluster.autonomy.computer
```

To find your cluster and zone names, run:

```bash theme={null}
autonomy cluster show
autonomy zone list
```

For example, if your cluster is `a25bff50` and zone is `hello`, your URL is:

```
https://a25bff50-hello.cluster.autonomy.computer
```

***

## Step 10: Test the Agent API

Test that your agent is responding to API calls:

**Non-streaming request:**

```bash theme={null}
curl --request POST \
  --header "Content-Type: application/json" \
  --data '{"message":"What is a contract?"}' \
  "https://${CLUSTER}-${ZONE}.cluster.autonomy.computer/agents/henry"
```

**Streaming request:**

```bash theme={null}
curl --request POST \
  --header "Content-Type: application/json" \
  --data '{"message":"What is a contract?"}' \
  "https://${CLUSTER}-${ZONE}.cluster.autonomy.computer/agents/henry?stream=true"
```

<Note>
  Replace `${CLUSTER}` and `${ZONE}` with your actual cluster and zone names.
</Note>

***

## Step 11: Open your app in a browser

Open your web interface:

```
https://${CLUSTER}-${ZONE}.cluster.autonomy.computer
```

You should see the chat interface. Try asking Henry questions like:

* "What are the key elements of a contract?".
* "What is consideration in contract law?".
* "Explain breach of contract.".

***

## Step 12: View logs (optional)

To view logs from your deployed zone, create a private encrypted portal to the
logs server in your zone:

```bash theme={null}
autonomy zone inlet --to logs
```

This creates a private link to the logs server on localhost and displays
the address `http://127.0.0.1:32101`. Open it in your browser to see streaming
logs for all containers in the zone.

***

## Common Issues and Solutions

<AccordionGroup>
  <Accordion title="Zone name too long or has invalid chars">
    Zone names must be 10 characters or less, using only `a to z` and `0 to 9`. Edit `autonomy.yaml` and change the `name` field to something shorter.
  </Accordion>

  <Accordion title="Docker not running">
    Start Docker Desktop or your Docker daemon. Verify with `docker ps`.
  </Accordion>

  <Accordion title="Autonomy Command not found">
    Run `source "$HOME/.autonomy/env"` to add the command to your PATH.
  </Accordion>

  <Accordion title="Connection refused when testing">
    Wait a couple of minutes after deployment.
  </Accordion>

  <Accordion title="Agent not responding">
    Check the logs using the portal method shown in [Step 12](#step-12-view-logs-optional). Look for errors in the pod startup or agent initialization.
  </Accordion>

  <Accordion title="Web interface shows blank page">
    Verify that `index.html` is in `images/main/` and was included in the container build. Redeploy with `autonomy zone deploy`.
  </Accordion>
</AccordionGroup>

***

## Update Your App

To make changes to your app:

1. **Edit your files** (e.g., change the agent instructions in `main.py`).
2. **Redeploy** with `autonomy zone deploy`.
3. **Wait a couple of minutes** for the new version to be ready.
4. **Test** your changes.

<Tip>
  Each deployment creates a new version of your zone and replaces the old version.
</Tip>

***

## Delete the zone

When you're done experimenting, delete your zone to free up resources:

```bash theme={null}
autonomy zone delete
```

***

## Learn More

<CardGroup cols={2}>
  <Card href="/agents/agents" title="Agents" icon="star-shooting" iconType="solid">
    Create deep work agents.
  </Card>

  <Card href="/agents/tools" title="Tools" icon="screwdriver-wrench" iconType="solid">
    Give agents the ability to take actions.
  </Card>
</CardGroup>
