{"token_count": 2185}

# Manage Long-Running Agents on Teleport Beams

You can use Teleport Beams to securely manage long-running agentic workloads, such as complex coding projects. Each beam deploys with a tool, `beamctl`, that allows you to manage services that continue running after you exit your SSH session.

In this guide, you will learn how to manage long-running agentic workloads on a Teleport beam by walking through a small demo project.

## How it works

Teleport Beams are micro VM sandboxes for running agentic workloads, hosted on the Teleport Cloud infrastructure. `beam-init` is the init system for a Teleport beam, similar to `systemd` in that it runs as PID 1 and spawns other processes that run on the host. `beamctl` is a CLI for managing processes spawned by `beam-init`, which it communicates with over a local Unix socket.

These processes are called **services**. Unlike `systemd`, which loads service definition files from disk, `beam-init` requires the user to declare new services while executing them over the command line. This is consistent with the ephemeral nature of a Teleport beam, which expires after 24 hours.

This guide illustrates the following `beamctl` commands in action:

| Command                          | Description                                                                |
| -------------------------------- | -------------------------------------------------------------------------- |
| `beamctl start <command>`        | Create a service.                                                          |
| `beamctl stop <service name>`    | Terminate a service process.                                               |
| `beamctl restart <service name>` | Terminate a service process and start it again using its original command. |
| `beamctl list`                   | List all services and their statuses.                                      |
| `beamctl logs <service name>`    | Show the output of a service.                                              |

Deleting a service

If you terminate a `beam-init` service with `beamctl stop`, the default behavior is for `beam-init` to retain the service in its list of managed services. To remove a service from the list of services `beam-init` tracks, run the following command:

```
$ beamctl stop --prune <service name>
```

Otherwise, `beam-init` does not allow you to start a service with the same name as an existing one, even if that service is stopped.

For a full list of commands, run the following on your beam:

```
$ beamctl help
```

## Prerequisites

This guide requires a Teleport Beams account. [Start your free trial](https://www.beams.run/).

In the demo project we'll use for this guide, you will run two continuous agent workloads to build a simple web application:

- **Agent 1:** Runs tests and gets them to pass by writing and optimizing code.
- **Agent 2:** Writes tests and designs testable function signatures, but leaves implementations up to Agent 1.

Agent 2 writes tests that handle edge cases Agent 1 hasn't addressed yet. This forces the agents to adopt a test-driven development (TDD) workflow and prevents the agent writing the application code from adding trivial tests.

---

WARNING

The demo we run in this guide skips permissions in agent prompts. Since an agent on a Teleport beam inherits the permissions of the user who started the beam, we recommend that you authenticate to Teleport as a user with no access to Teleport-protected resources before starting this guide.

---

## Step 1/2. Set up the demo project

The demo project we'll create in this guide lets users enter Morse code using a browser button and converts it to text.

---

TIP

Feel free to use another project idea. If you do, read through the rest of the guide first to understand the relevant workflows.

---

Create a Teleport beam and set up a project so that your agents have a place to carry out their work:

1. Create a beam:

   ```
   $ tsh beams add
   ```

2. On the beam, set up a project directory:

   ```
   $ mkdir morsecode
   $ cd morsecode
   ```

3. Set up your project:

   ```
   $ npm init
   ```

4. Name the package `morsecode` and accept all the `npm` defaults.

5. Install Vite, which we'll use to set up the NodeJS project, and `vitest` for testing:

   ```
   $ npm install -D vitest vite@~8.2.0
   ```

6. Add empty directories:

   ```
   $ mkdir tests src
   ```

7. Start a dev server, which reloads automatically when source files change:

   ```
   $ beamctl start --name=server -- \
     bash -c 'export HOME=/home/beams; \
       cd /home/beams/morsecode; \
       npx vite --host --port=8080;'
   Started service server
   ```

   `beamctl start` starts a serviced managed by the `beam-init` init system, which manages all processes that run on a Teleport beam. Here we set the home directory as the one in which we created our Vite project to ensure `npm` can work as expected.

   The server must run on port `8080` so you can visit it via Teleport later in the guide.

8. Copy the following text to a file called SPEC.md on the beam:

   ```
   # Morse code in your browser

   When the user taps a button, the app reads it as Morse code and converts text
   to a readout below the button. There is a collapsable Morse code key as a
   reference for the user.

   ```

9. Start the testing agent with `claude`, which comes preinstalled on the beam (as does `codex`). Every 10 seconds, the testing agent adds new tests to implement the application and address potential edge cases. Like the coding agent we'll start next, the testing agent has a limit of 30 executions, amounting to around five minutes of work:

   ```
   $ beamctl start --name=tester -- bash -c ' \
     export HOME=/home/beams; \
     export count=0; \
     cd /home/beams/morsecode; \
     while [ $count -lt 30 ]; do \
       claude -p --dangerously-skip-permissions < /dev/null "\
         Read SPEC.md and the existing test files under tests. Identify one \
         behavior from the spec that is not covered by an existing test yet, \
         e.g., a new function, or an additional edge case for a function  \
         you have already tested. If a test calls a function that does not exist \
         yet, import it in the new test and assume it has a testable function \
         signature, but do not create or modify anything in src. Another agent \
         will define functions and get tests to pass. Do not modify or remove \
         existing tests."; \
      sleep 10; \
      count=$((count+1)); \
    done;'
    Started service tester
   ```

10. Start the coding agent:

    ```
    $ beamctl start --name=coder -- bash -c ' \
      export HOME=/home/beams; \
      export count=0; \
      cd /home/beams/morsecode; \
      while [ $count -lt 30 ]; do \
        if ! npx vitest run --silent; then \
          claude -p --dangerously-skip-permissions < /dev/null "\
            Read SPEC.md and existing test files under tests. Get the tests to \
            pass by editing files in src. Maintain a minimal Vite entrypoint at \
            index.html to wire everything up."; \
        fi; \
       sleep 10; \
       count=$((count+1)); \
     done;'
     Started service coder
    ```

11. Make sure all services are running as expected. All should report `running`:

    ```
    $ beamctl list
      bootstrap (running PID=2)
      coder (running PID=168)
      server (running PID=5817)
      tester (running PID=136)
    ```

    Problem starting a service?

    If a service has not started correctly, check its logs:

    ```
    $ beamctl logs tester
    $ beamctl logs coder
    $ beamctl logs server
    ```

    If the problem has to do with the command you ran to start a service (e.g., a typo), prune the service and start it again with the correct command:

    ```
    $ beamctl stop --prune <service>
    $ beamctl start <correct command>
    ```

12. Exit your session.

    ```
    $ exit
    logout
    the connection was closed on the remote side at <timestamp>
    ```

Do something else for five minutes.

## Step 2/2. Check your app

At this point, your agents should have made substantial progress on your demo project.

1. Find the beam you created so you can access it:

   ```
   $ tsh beams ls
   ```

2. Publish the application. The command below enrolls your demo project as a Teleport-protected web app. Assign beam-name to the name of your beam:

   ```
   $ tsh beams publish beam-name
   ```

3. Follow the prompts to authenticate to Teleport and visit your app.

4. In your terminal, SSH into your beam:

   ```
   $ tsh beams ssh beam-name
   ```

5. Show the status of all the `beamctl` services you started:

   ```
   $ beamctl list
     bootstrap (running PID=2)
     coder (running PID=168)
     server (running PID=5817)
     tester (running PID=136)
   ```

6. Check the logs of your `beamctl` services. For example, you can see the tester discussing the tests it has added:

   ```
   $ beamctl logs tester
   ```

   You can add the `--follow` flag to attach your terminal's standard input to the service's logs.

7. If the services are still running, you can terminate their processes with the following command:

   ```
   $ beamctl stop tester
   $ beamctl stop coder
   ```

8. If any services have stopped, and you would like them to resume their work, you can run the following command to restart them. For example:

   ```
   $ beamctl restart coder
   $ beamctl restart tester
   $ beamctl restart server
   ```
