Skip to main content

Manage Long-Running Agents on Teleport Beams

Report an Issue

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:

CommandDescription
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 listList 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.

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