Read this tutorial on Signadot docs.

Devin writes code and runs unit tests in its own cloud VM. What it can't easily check there is whether a change to one microservice still works with the services it talks to. That usually waits for a staging deploy, after the pull request is open.

With the Signadot CLI and the Signadot MCP server in its environment, Devin closes that loop itself. It runs the changed service in its VM, creates a sandbox that routes tagged cluster traffic to that process, runs your end-to-end tests in the cluster as a Signadot Job, and reports back only after they pass.

In this tutorial, Devin changes the route service of HotROD, a demo ride-sharing app, and validates it with HotROD's Playwright suite.

Time: about 30 minutes, including about 8 minutes for Devin's session.

Example files: signadot/examples/devin-tutorial

How it works


Two things make this work from a cloud VM:

  • No cluster credentials. The CLI's ControlPlaneProxy connection tunnels through the Signadot control plane. Devin needs only an API key, so the cluster can even run on your laptop.
  • A full VM. Devin has passwordless sudo, so signadot local connect runs in its default privileged mode. In-cluster names such as redis.hotrod.svc resolve from the VM, and the local service can call its real dependencies.

Prerequisites

  • A Kubernetes cluster with the Signadot Operator installed. A local cluster such as minikube, kind, k3d or Docker Desktop works.
  • The Signadot CLI logged in with signadot auth login, and kubectl pointed at the cluster.
  • A Devin organization where you can edit the environment blueprint and add MCP servers (the Manage MCP Servers permission).

Step 1: Install HotROD and a Playwright runner

Skip this block if HotROD already runs in the hotrod namespace:

kubectl create namespace hotrod kubectl -n hotrod apply -k 'https://github.com/signadot/hotrod/k8s/overlays/prod/devmesh' kubectl -n hotrod wait --for=condition=available deployment --all --timeout=5m 

Create the job runner group that runs HotROD's Playwright suite inside the cluster. Replace <your-cluster> with your Signadot cluster name:

git clone https://github.com/signadot/hotrod.git cd hotrod kubectl create namespace signadot-tests signadot jobrunnergroup apply -f .signadot/testing/playwright-runner.yaml \ --set cluster=<your-cluster> --set env=dev 

Step 2: Create an API key for Devin

In the Signadot dashboard, go to Admin → Service Accounts, create a service account named devin-tutorial, and create an API key for it. Copy the key; you'll paste it into Devin twice.

The service account keeps Devin's sandboxes and devboxes separate from yours. Revoking its key cuts Devin off without touching your own login.

Step 3: Store the key as a Devin secret

In Devin, go to Settings → Secrets → Add secret. Create a Raw secret named SIGNADOT_API_KEY with the key as its value. Choose Organization scope to share it with your team, or Personal to keep it to your own sessions.

Step 4: Add the Signadot CLI to Devin's blueprint

A Devin blueprint describes what goes into the snapshot every session's VM starts from. Go to Settings → Environment → Blueprints → Organization blueprint → Edit and paste devin/blueprint.yaml. Before you save, set your Signadot org and cluster names at the top of the Configure Signadot CLI step:

 SIGNADOT_ORG_NAME=my-org SIGNADOT_CLUSTER=my-cluster 

The blueprint has three parts:

  • initialize installs Go and a pinned version of the Signadot CLI, then writes ~/.signadot/config.yaml with a ControlPlaneProxy connection to your cluster.
  • maintenance clones HotROD into ~/repos/hotrod and downloads its Go modules.
  • knowledge is never executed. It tells Devin how to connect, create the sandbox and run the tests. Copy this section into your own blueprints when you adapt the tutorial.

Full blueprint.yaml

initialize: - name: Install Go uses: github.com/actions/setup-go@v5 with: go-version: "1.27.1" - name: Install Signadot CLI run: | # Pin the version: without it the installer asks the GitHub API for the  # latest release, which is rate limited from Devin's shared build IPs.  curl -sSLf https://raw.githubusercontent.com/signadot/cli/main/scripts/install.sh \ | SIGNADOT_CLI_VERSION=v1.9.0 sh signadot --version - name: Configure Signadot CLI run: | # Edit these two values to match your Signadot org and cluster name.  SIGNADOT_ORG_NAME=my-org SIGNADOT_CLUSTER=my-cluster mkdir -p ~/.signadot cat > ~/.signadot/config.yaml <<EOF org: ${SIGNADOT_ORG_NAME} local: connections: - cluster: ${SIGNADOT_CLUSTER} type: ControlPlaneProxy EOF maintenance: - name: Clone or update HotROD run: | if [ ! -d ~/repos/hotrod ]; then mkdir -p ~/repos git clone https://github.com/signadot/hotrod ~/repos/hotrod fi cd ~/repos/hotrod git pull --ff-only go mod download knowledge: - name: signadot contents: | This VM can test code changes end to end against a shared Kubernetes cluster with Signadot. The HotROD baseline runs in namespace `hotrod` of the Signadot cluster named in ~/.signadot/config.yaml. HotROD source: ~/repos/hotrod (Go; one binary, `go run ./cmd/hotrod <service>`; services: frontend, location, driver, route). Credentials: the `signadot` CLI reads SIGNADOT_API_KEY from the environment (Devin secret SIGNADOT_API_KEY). Bind it to every `signadot` command. 1. Connect this VM to the cluster (once per session, before creating sandboxes). This also makes in-cluster service names such as redis.hotrod.svc resolvable from this VM. Every Devin VM boots with the same /etc/machine-id, which Signadot uses to pick the devbox, so give this session its own ID first; otherwise a second concurrent session fails with "409 Conflict: devbox ... is already claimed": [ -s ~/.signadot/machine-id ] || tr -d - < /proc/sys/kernel/random/uuid > ~/.signadot/machine-id export MACHINE_ID_FILE=~/.signadot/machine-id signadot local connect --wait connect --wait-timeout 120s signadot local status 2. Run the changed service locally. For route (gRPC, port 8083): cd ~/repos/hotrod go run ./cmd/hotrod route > ~/route.log 2>&1 & 3. Create a sandbox with the Signadot MCP `create_sandbox` tool: a `local` workload mapping Deployment `route` (namespace hotrod) port 8083 to localhost:8083, `connection.devboxId` from `list_devboxes` (this VM's devbox, the one `signadot local status` reports), plus a defaultRouteGroup endpoint for http://frontend.hotrod.svc:8080. 4. Run HotROD's Playwright e2e suite against the sandbox as a Signadot Job (runs in the cluster on runner group `playwright`): cd ~/repos/hotrod signadot job submit -f .signadot/testing/demo-playwright-job.yaml \ --set branch=main --set namespace=hotrod \ --set sandbox=<sandbox-name> --wait --timeout 15m Inspect a run with `signadot job get <job-name>`, `signadot logs --job <job-name>`, and `signadot artifact download --job <job-name> hotrod/playwright-report.tar.gz`. Never print the value of SIGNADOT_API_KEY. Do not kill processes with broad patterns like `pkill -f hotrod` (that matches signadot processes too); kill by port: `kill $(lsof -t -i :8083)`. 

Click Save blueprint. Devin builds a new snapshot in about two minutes; the page shows Last built when it's ready.

Step 5: Add the Signadot MCP server

The MCP server is a subcommand of the CLI you just installed, so it runs inside Devin's VM over STDIO. In Devin, go to Customize → MCPs, select Organization, and choose Add MCP → Add custom MCP:

Field Value
Server name Signadot
Transport type STDIO
Command signadot
Arguments mcp
Environment variables SIGNADOT_API_KEY = $SIGNADOT_API_KEY

Click Add and confirm the security notice. The server shows Needs connection because Devin doesn't share Settings → Secrets with MCP servers. Open the server, paste the API key into its Secrets field, and save. The status changes to Connected.

Step 6: Give Devin the task

Start a new Devin session and paste this prompt (prompt.md), with my-cluster changed to your cluster name:

In ~/repos/hotrod, change the route service so that every computed ETA is exactly 2 minutes (services/route/server.go). Keep the FAST_ROUTE override and the rounding logic intact. Smallest possible diff. Before you report back, validate the change end to end against the shared cluster with Signadot (see the `signadot` knowledge for commands): 1. Connect this VM to the cluster with `signadot local connect`. 2. Run your modified route service locally on port 8083. 3. Use the Signadot MCP to create a sandbox named `devin-route-eta` in cluster `my-cluster` that maps Deployment `route` in namespace `hotrod` to localhost:8083 on this VM's devbox. I approve that spec; no need to ask. 4. Run HotROD's Playwright e2e suite against the sandbox as a Signadot Job and wait for it to finish. If it fails, find out why, fix it, and rerun. 5. Prove the behavior change: dispatch one ride with the sandbox routing key and one without (baseline), then compare the driver notifications. Report the diff, the sandbox routing key, the job name and result, and the two notifications. Leave the sandbox and the route service running. 

Baseline ETAs are random, between 3 and 45 minutes, so an ETA of exactly 2m0s can only come from Devin's change. Keep the approval in step 3: the create_sandbox tool tells agents to confirm the spec with the user first, and without it Devin stops to ask.

What Devin does

1.) Makes the change. Devin edits services/route/server.go, then builds and vets the package:

 - // Generate a random number between 3 and 45 with decimals - eta := time.Duration((rand.Float64()*(45-3) + 3) * float64(time.Minute)) + eta := 2 * time.Minute 

2.) Connects its VM to the cluster with signadot local connect, then starts the changed route service on port 8083.

3.) Creates the sandbox through the MCP server. Devin calls list_clusters, resolve_workload, resolve_workload_port and list_devboxes, then create_sandbox with a spec like this:

 name: devin-route-eta spec: cluster: my-cluster connection: devboxId: <this VM's devbox ID> local: - name: local-route from: kind: Deployment namespace: hotrod name: route mappings: - port: 8083 toLocal: localhost:8083 defaultRouteGroup: endpoints: - name: frontend target: http://frontend.hotrod.svc:8080 

Requests in the cluster that carry the sandbox's routing key now reach route in Devin's VM. Everything else still reaches the baseline.

4.) Runs the Playwright suite as a Job, and fixes what fails. In our test run, the first Job failed on "Resolving routes": route defaults to REDIS_ADDR=redis:6379, a short name that resolves only inside the cluster. Devin read the Job's logs with signadot logs, downloaded the Playwright report, and restarted route with REDIS_ADDR=redis.hotrod.svc:6379. No code change was needed, and the second Job passed.

5.) Proves the behavior change by dispatching one ride with the routing key and one without. Its report includes:

 Job: hotrod-playwright-e2e-zihjipq succeeded, 2 of 2 tests passed. With the sandbox key: Driver T792260C arriving in 2m0s Baseline: Driver T775730C arriving in 8m36s 

The whole session took Devin about eight minutes. You can open each Job, with its logs and the uploaded Playwright report, in the Signadot dashboard under Validations → Jobs.

Clean up

Ask Devin to delete the sandbox and stop route before you end the session, or delete the sandbox from your machine:

signadot sandbox delete devin-route-eta 

Once the Devin VM shuts down, its devbox claim lapses within about a minute.

Use it with your own services

  • Your repository. Connect Devin to GitHub, add your repository under Settings → Environment → Repositories, and commit the blueprint as .devin/blueprint.yaml. In its knowledge section, write down how each service runs outside the cluster: environment variables, ports and dependencies.
  • Pull requests with evidence. Add "open a pull request and link the passing Signadot Job" to the prompt, so reviewers see the end-to-end result next to the diff.
  • Automation. The Devin API starts a session from a prompt, so a ticket or alert can kick off the same write, sandbox and test loop.
  • Agent skills. The signadot-validate skill packages this validation loop. Devin loads skills from .agents/skills/ in your repository.

Troubleshooting

  • Blueprint build fails with signadot: command not found. The CLI installer couldn't resolve the latest release. Pin it with SIGNADOT_CLI_VERSION, as the blueprint does.
  • 409 Conflict: devbox ... is already claimed. Another Devin session with the same API key holds the devbox. Make sure Devin sets a per-session MACHINE_ID_FILE (step 1 of the blueprint's knowledge), or end the other session.
  • The MCP server shows "Needs connection". Paste the API key into the MCP server's own Secrets field.
  • list_clusters asks which cluster to use. Your org has more than one cluster. Name it in the prompt.
  • The driver logs DeadlineExceeded. The driver gives each route call one second, and traffic to Devin's VM goes through the control plane. Run route with ROUTE_CALC_DELAY=1ms to remove its simulated 50 ms of work.

Frequently asked questions

How does Devin reach my Kubernetes cluster?

Through the Signadot control plane. The blueprint configures the Signadot CLI with a ControlPlaneProxy connection, so Devin's VM needs only a Signadot API key: no kubeconfig and no network path to your cluster's API server. This also works for a local cluster such as minikube, kind, k3d or Docker Desktop.

Does Devin's sandbox affect other people using the cluster?

No. A Signadot sandbox only receives requests that carry its routing key. Every other request, including your teammates' traffic and other sandboxes, keeps reaching the baseline services.

Can several Devin sessions use Signadot at the same time?

Yes. Every Devin VM boots with the same /etc/machine-id, which Signadot uses to pick a devbox. The blueprint tells Devin to write a fresh machine ID for each session and point MACHINE_ID_FILE at it, so each session gets its own devbox and sandbox under one service account.

Which Signadot MCP tools does Devin use?

list_clusters, resolve_workload, resolve_workload_port, list_devboxes and create_sandbox, plus get_workload_object while debugging. The MCP server has no Job tools yet, so Devin runs the end-to-end suite with signadot job submit.

What does my application need for this to work?

Your services must propagate the routing key from incoming to outgoing requests, for example as OpenTelemetry baggage, so that requests tagged for the sandbox reach Devin's copy of the service. HotROD already does this with OpenTelemetry. The Signadot docs on context propagation show how to set it up in your own services.

See also