> ## 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.

# Coding Agents: How to create and deploy a new Autonomy app

> Create an new Autonomy app with an agent and a simple streaming chat web interface for this agent. This guide covers the file structure, basic configuration, Docker setup, and other steps to deploy this new app.

# A guide for coding agents to create and deploy a new Autonomy app

Use the **Autonomy Framework** to build autonomous AI applications and deploy them on **Autonomy Computer** using **Autonomy Command**.

> **Key Terms**: See the [definitions section of the main guide](https://autonomy.computer/docs/_for-coding-agents.md#definitions).

*Keywords: setup, hello world, installation, deployment, autonomy.yaml*

This guide shows how to create a new Autonomy app with an agent and streaming chat interface, then deploy it to Autonomy Computer.

## Create and deploy a new Autonomy app

### Step 1: Create a new directory for the app

Pick a name for the app and its zone, for example `hello`.

Create that new directory and change into it:

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

All commands related to this app or zone should be run from inside this directory.

### Step 2: Create `autonomy.yaml`

Create `autonomy.yaml` with the following content:

```yaml theme={null}
name: hello # The zone's name should be less than 10 characters
pods:
  - name: main-pod
    public: true
    containers:
      - name: main
        image: main
```

Unless the user has asked for a different specific name for the app, use `hello`.

**Important**: Zone names must be 10 characters or less.

This defines a Zone in Autonomy Computer where the application will run.

### Step 3: Create `images/main/Dockerfile`

The zone (defined above) has a `main-pod` and this pod has a `main` container which is built using the `main` docker image. This `main` docker image is defined using the docker file at: `images/main/Dockerfile`.

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

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

```

### Step 4: Create `images/main/main.py`

The main image's docker file (defined above) starts the main python program defined in the file `images/main/main.py` .

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

```python 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)

```

**Note on Built-in Tools**: This agent automatically has access to these built-in tools:

* `get_current_time_utc` - Get the current time in UTC timezone
* `get_current_time` - Get the current time in a specific timezone

To enable human-in-the-loop interactions, add `enable_ask_for_user_input=True` to the `Agent.start()` call. See the [tools guide](https://autonomy.computer/docs/_for-coding-agents/tools.md) for more details.

### Step 5: Create `images/main/index.html`

The python program in `images/main/main.py` starts an Autonomy Node. The Node by default starts an HTTP server. If `images/main/index.html` is present, the Node's HTTP server will serve it at its root `/` .

Create `images/main/index.html` with the following content. It creates a simple web interface that allows users to chat with the agent directly in their browser. The interface sends messages to the agent's streaming API endpoint and displays the streaming responses with a typewriter effect.

```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></title>
</head>
<body>
    <main>
        <textarea id="in" autofocus placeholder="How can I help you..."></textarea>
        <pre id="out"></pre>
    </main>

    <script>
    // Simulate the typing or typewriter effect by appending each character
    // of the text to the output element one by one, with a delay.
    async function appendCharByChar(text, delay = 5) {
      for (const char of text) {
        out.textContent += char;
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }

    // Process a single line of streamed JSON response from the streaming api of the agent.
    // Parse the JSON and extracs text content from messages.
    // Call `appendCharByChar()` defined above to display the line character by character.
    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);
                }
            }
        }
    }

    // Send a message to the streaming api of the agent and handle the streaming response.
    // While the response is being streamed, disable input, clear output.
    // Handle streamed chunks line by line, and re-enable input when complete.
    // Call `processLine()` defined above to parse the line an display it character by character.
    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 = "";

        // Process the streaming response chunk by chunk
        for await (const chunk of response.body) {
          // Accumulate chunks in a buffer and split by newlines to get complete JSON lines
          buffer += decoder.decode(chunk, { stream: true });
          const lines = buffer.split('\n');
          buffer = lines.pop(); // Keep the incomplete line in the buffer for the next iteration

          if (!out.textContent) input.value = ""; // Clear input once we start displaying output
          for (const line of lines) await processLine(line); // Process each complete line
        }
        await processLine(buffer); // Handle any remaining content in the buffer after streaming ends

        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>
```

Later, when you deploy this zone to the Autonomy Computer, this web page will become available at: `https://${CLUSTER}-${ZONE}.cluster.autonomy.computer/`. Where `${CLUSTER}` and `${ZONE}` represent the name of this app's Cluster and Zone in the Autonomy Computer.

Unless the user has specifically asked for something different, use exactly the above content for `images/main/index.html`

If you do need to generate a new UI, prefer generating streaming output. Refer the following two examples for the structure of the output of interacting with the agent via the built-in API

Here is an example of streaming output from the built-in API:

```
timeout 15s curl --silent --request POST \
--header "Content-Type: application/json" \
--data '{"message":"What are the key elements of a contract?"}' \
"https://a9eb812238f753132652ae09963a05e9-hello.cluster.autonomy.computer/agents/henry?stream=true"
{"scope": "None", "conversation": "None", "messages": [{"content": {"text": "I", "type": "text"}, "phase": "executing", "thinking": false, "tool_calls": [], "role": "assistant"}], "type": "conversation_snippet"}
{"scope": "None", "conversation": "None", "messages": [{"content": {"text": "'ve already", "type": "text"}, "phase": "executing", "thinking": false, "tool_calls": [], "role": "assistant"}], "type": "conversation_snippet"}
...

```

Here's example output of calling the built-in API without streaming:

```
timeout 15s curl --silent --request POST \
--header "Content-Type: application/json" \
--data '{"message":"What is a contract?"}' \
"https://a9eb812238f753132652ae09963a05e9-hello.cluster.autonomy.computer/agents/henry"

[
  {
    "content": {
      "text": "Great question! A contract legally ...",
      "type": "text"
    },
    "phase": "executing",
    "thinking": false,
    "tool_calls": [],
    "role": "assistant"
  }
]
```

At this point the new zone's directory looks should look like this:

```

├── autonomy.yaml
└── images
    └── main
        ├── Dockerfile
        ├── index.html
        └── main.py
```

### Step 6: Confirm that the user has signed up

Before proceeding forward ask the user if they have already created an account with Autonomy.
If not, instruct them to [sign up](https://my.autonomy.computer) so they get a dedicated Cluster in the Autonomy Computer.

Once they have signed up, proceed to the next step.

### Step 7: Install the `autonomy` command

Check if the `autonomy` command is already installed by running `autonomy --version`. If the command is not installed, install it by running the following in a terminal.

```bash theme={null}
curl -sSfL autonomy.computer/install | bash && . "$HOME/.autonomy/env"
```

You can check that it is successfully installed by running `autonomy --version`. If it is not found, you may need to run `source "$HOME/.autonomy/env"` to make it available.

### Step 8: Ensure docker is installed and running

Before running the `autonomy` command, ensure [Docker](https://www.docker.com/get-started/) is installed and running in your environment. You can check that docker is working correctly by running:

```
docker ps
```

The `autonomy` command uses docker to build docker images and push them to a Zone in a Cluseter in the Autonomy Computer.

### Step 9: Enroll with your Cluster

Enroll your workstation with your Cluster in Autonomy Computer

```
autonomy cluster enroll --no-input
```

This command will block and print a code that your user has to paste into their browser to sign in, remind them to do that. The command will continue once they have signed in.

### Step 10: Deploy the zone

Deploy the zone dedicated to this app as follows:

```
autonomy zone deploy
```

This will display the name of this app's zone and cluster. Take note of those values, you will need them later.

The `autonomy.yaml` file sets `public: true` on the main pod of the app. That instructs the Autonomy Computer to give this app a public api endpoint and cause the interactive web UI defined in `images/main/index.html` to be served from `https://${CLUSTER}-${ZONE}.cluster.autonomy.computer/`

The zone can take up to a minute to be ready to use.

### Step 11: Wait for deployment to complete

After running `autonomy zone deploy`, **wait 2-3 minutes** before testing:

```bash theme={null}
echo "Waiting 120 seconds for pods to be ready..."
sleep 120
```

**Expected Timeline:**

| Stage | Time | What's Happening |
| - | - | - |
| Build & Push | 30-60s | Docker images building and uploading |
| Pod Scheduling | 60-90s | Kubernetes scheduling pods |
| Container Start | 90-120s | Containers pulling images, starting |
| **First Request** | **120-180s** | **Cold start - nodes establishing connections** |
| Warm Requests | \~1-5s | Normal operation |

**Cold Start Behavior:**

* The **first request** after deployment may take 10-30 seconds to complete
* Subsequent requests are much faster (1-5s)
* This is normal for distributed systems
* Happens because nodes need to establish connections

**Best Practice:** Add a health check endpoint and call it first to "warm up" the system.

### Step 12: List all agents

Get a list of all running agents by calling the `/agents` endpoint:

```
timeout 15s curl --silent --request GET \
"https://${CLUSTER}-${ZONE}.cluster.autonomy.computer/agents"
```

This will return a JSON array containing information about all agents that are currently running.

### Step 13: Check that the agent is responding

Check that the agent defined in `images/main/main.py` is responding to api calls:

```
timeout 15s curl --silent --request POST \
--header "Content-Type: application/json" \
--data '{"message":"what is a contract?"}' \
"https://${CLUSTER}-${ZONE}.cluster.autonomy.computer/agents/henry"
```

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

### Step 14: Guide your user to interact with the app in their browser

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

### Step 15: See logs

To view logs from your deployed zone, follow the [logging instructions](https://autonomy.computer/docs/_for-coding-agents#how-to-view-logs).

> **IMPORTANT**: Never run `autonomy zone inlet --to logs` by itself as it will block your terminal. Always use the background pattern shown in the [logging instructions](https://autonomy.computer/docs/_for-coding-agents#how-to-view-logs).

## Troubleshooting

* **Zone name too long**: Keep it to 10 characters or less
* **Docker not running**: Verify by running `docker ps`
* **Command not found**: Fix by running `source "$HOME/.autonomy/env"`
* **Connection refused**: Wait 2-3 minutes after deployment for pods to be ready
* **First request slow**: Cold start takes 10-30s, subsequent requests are 1-5s
* **Deployment issues**: [Check the logs using a portal to the logs server in your zone](https://autonomy.computer/docs/_for-coding-agents#how-to-view-logs) to diagnose deployment problems

## Next Steps

* [How to configure apps with environment variables and secrets](https://autonomy.computer/docs/_for-coding-agents/configure-apps.md)
* [How to create custom APIs](https://autonomy.computer/docs/_for-coding-agents/create-custom-apis.md)
* [How to use memory, conversation, and scope in agents](https://autonomy.computer/docs/_for-coding-agents/memory.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.