Site Agent
Run the TestMesh site agent inside your private network to execute flows against internal services — no inbound firewall rules, no VPN, no exposed ports.
The TestMesh site agent runs inside your network and executes flows against your internal services. It connects outbound to the TestMesh control plane via WebSocket — no inbound ports, no firewall changes, no VPN required.
TestMesh Control Plane Your Private Network
┌──────────────────────┐ ┌──────────────────────────────┐
│ Dashboard │ │ testmesh-agent │
│ Scheduling │◄── WSS ────│ (executes flows locally) │
│ AI Analysis │── jobs ───►│ │
│ PR Write-Back │◄─ results ─│ │
└──────────────────────┘ └──────────┬───────────────────┘
│ direct access
┌──────────┴───────────────────┐
│ internal-api:8080 │
│ postgres:5432 │
│ kafka:9092 │
│ redis:6379 │
└──────────────────────────────┘Quick Start
Get an agent running in minutes
What it can run
HTTP, databases, Kafka, Redis, gRPC, and more
Security Model
Outbound-only, token-scoped, secrets stay local
Self-Hosted Mode
Point the agent at your own control plane
Quick Start
1. Get an Agent Token
In the TestMesh dashboard, go to Settings → Agents → New Agent. Copy the generated token — it is shown only once.
You can also create a token via the API:
curl -X POST https://app.testmesh.io/api/v1/agent-tokens \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{"workspace_id": "<workspace-id>", "name": "prod-agent"}'2. Start the Agent
docker run -d \
-e AGENT_TOKEN=<your-token> \
-e CLOUD_URL=https://app.testmesh.io \
--name testmesh-agent \
--restart unless-stopped \
testmesh/agent:latestcurl -fsSL https://testmesh.io/install-agent.sh | sh
testmesh-agent start --token <your-token>Add to your existing docker-compose.yml:
services:
testmesh-agent:
image: testmesh/agent:latest
environment:
AGENT_TOKEN: <your-token>
CLOUD_URL: https://app.testmesh.io
restart: unless-stopped
# No ports needed — outbound only3. Verify
The agent appears in Settings → Agents with a green status indicator once connected. Flow runs targeting this agent's workspace will now execute inside your network.
How It Works
- Agent dials
wss://app.testmesh.io/api/v1/agent/connectwith its token - Control plane authenticates the token and registers the agent
- When a flow run is triggered (schedule, webhook, manual), the control plane sends the job to the agent over the WebSocket
- Agent executes the flow using the embedded runner — the same engine as the open-source API
- Step results stream back in real-time; each step is persisted and visible in the dashboard as it completes
- On disconnect, the agent retries with exponential backoff; unacknowledged jobs are requeued
Message Protocol
agent → cloud: { "type": "register", "payload": { "workers": 4, "version": "0.1.0" } }
cloud → agent: { "type": "job", "payload": { "id": "...", "definition": {...} } }
agent → cloud: { "type": "result", "payload": { "job_id": "...", "type": "step_completed", ... } }
cloud → agent: { "type": "ping" }
agent → cloud: { "type": "pong" }Result types per job: step_started → step_completed | step_failed (per step) → flow_completed | flow_failed.
Supported Actions
The agent embeds the full OSS runner. Any action that works in local or cloud execution also works via the agent.
Protocol Actions
| Action | Description |
|---|---|
http_request | HTTP/HTTPS calls — any method, headers, body, auth |
grpc / grpc_call / grpc_stream | gRPC unary and streaming calls |
websocket | Connect, send, and assert on WebSocket messages |
kafka_producer | Publish messages to Kafka topics |
kafka_consumer | Consume messages and assert on their content |
Data Store Actions
| Action | Description |
|---|---|
database_query | Raw SQL against any PostgreSQL-compatible database |
db_poll | Poll a query until a condition is true (async verification) |
redis.* | GET, SET, DEL, EXISTS operations on Redis keys |
postgresql.* | Extended PostgreSQL operations via the native plugin |
kafka.* | Extended Kafka operations via the native plugin |
Flow Control Actions
| Action | Description |
|---|---|
condition | Branch steps based on runtime values |
for_each | Loop over a list and run sub-steps per item |
parallel | Run multiple sub-steps concurrently |
run_flow | Embed another flow as a sub-flow |
wait_for | Wait for an HTTP endpoint or condition to become true |
wait_until | Wait with a timeout and expression assertion |
delay | Fixed sleep between steps |
Utility Actions
| Action | Description |
|---|---|
assert | Standalone assertion step using expr-lang expressions |
transform | Reshape or extract data from previous step output |
log | Emit structured log messages visible in execution output |
contract_generate | Generate API contracts from observed traffic |
contract_verify | Verify a service against a contract |
docker_run / docker_stop | Start/stop Docker containers as test fixtures |
browser | Playwright-based browser automation |
All step results — outputs, assertions, durations, errors — are streamed back to the control plane in real-time and stored in the execution history. You see the same level of detail in the dashboard as you would for a direct cloud execution.
Configuration
| Flag | Environment Variable | Default | Description |
|---|---|---|---|
--token | AGENT_TOKEN | (required) | Agent token from dashboard |
--cloud-url | CLOUD_URL | https://app.testmesh.io | Control plane URL |
--workers | — | 4 | Max concurrent flow executions |
The --workers flag currently has no corresponding environment variable. Set it explicitly when starting the agent if you need a value other than the default of 4.
Security Model
The site agent is designed for zero-trust network environments.
| Property | Detail |
|---|---|
| Outbound-only | Agent initiates all connections. Your firewall never needs an inbound rule. |
| Token-scoped | Each token is tied to one workspace. Compromising a token does not affect other workspaces. |
| Secrets stay local | Environment variables and connection strings are resolved inside the agent and never sent to the control plane. |
| No raw data leaves | Only execution results and assertion outcomes are sent — not your database records or full API response bodies (unless explicitly captured in output: mappings). |
| TLS by default | The WebSocket connection uses wss:// (TLS) when connecting to the cloud control plane. |
For maximum isolation, run the agent in a dedicated network segment with access only to the services it needs to test. The agent does not need internet access — only outbound TCP to the control plane URL and access to your internal services.
Self-Hosted Mode
If you run the full TestMesh stack on your own infrastructure, point the agent at your self-hosted cloud API (port 5017 by default):
testmesh-agent start \
--token <token> \
--cloud-url http://testmesh-cloud:5017The agent relay WebSocket endpoint (/api/v1/agent/connect) lives in the cloud API, which handles token validation, job dispatch, and result persistence. The cloud API proxies everything else to the OSS API.
See Cloud vs Self-Hosted for a full comparison of deployment models.
Programmatic Dispatch
You can dispatch a flow directly to a connected agent via the control plane API — useful for CI pipelines or custom orchestration:
# 1. Get a JWT
TOKEN=$(curl -s -X POST https://app.testmesh.io/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"..."}' | jq -r .access_token)
# 2. Fetch the flow definition
FLOW=$(curl -s "https://app.testmesh.io/api/v1/workspaces/<ws-id>/flows/<flow-id>")
DEFINITION=$(echo $FLOW | jq .definition)
# 3. Dispatch to the agent
curl -X POST https://app.testmesh.io/api/v1/agent/dispatch \
-H "Content-Type: application/json" \
-d "{
\"workspace_id\": \"<ws-id>\",
\"flow_id\": \"<flow-id>\",
\"definition\": $DEFINITION,
\"variables\": { \"env\": \"staging\" }
}"The response includes an execution_id you can poll for results:
{
"job_id": "job-abc123",
"execution_id": "e1d2c3b4-...",
"status": "dispatched"
}Providing flow_id is optional but recommended — it links the execution to the flow in history, pass/fail tracking, and the dashboard.
Scaling
Multiple Agents per Workspace
Deploy agents across environments or regions. Each agent connects independently:
# Staging agent
docker run -d \
-e AGENT_TOKEN=$STAGING_TOKEN \
--name testmesh-agent-staging \
testmesh/agent:latest
# Production agent (read-only tests)
docker run -d \
-e AGENT_TOKEN=$PROD_TOKEN \
--name testmesh-agent-prod \
testmesh/agent:latestWhen multiple agents are connected for the same workspace, the control plane dispatches to the first available one.
Worker Concurrency
Each agent runs a fixed number of workers (default 4). Increase for higher throughput:
testmesh-agent start --token <token> --workers 8Workers execute flows concurrently. A 4-worker agent can run 4 flows simultaneously.
Kubernetes Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: testmesh-agent
spec:
replicas: 2
selector:
matchLabels:
app: testmesh-agent
template:
metadata:
labels:
app: testmesh-agent
spec:
containers:
- name: agent
image: testmesh/agent:latest
env:
- name: AGENT_TOKEN
valueFrom:
secretKeyRef:
name: testmesh-agent
key: token
- name: CLOUD_URL
value: "https://app.testmesh.io"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: "1"
memory: 512MiBuilding from Source
cd agent
go build -o testmesh-agent .
# Or with Docker (from repo root)
docker build -f agent/Dockerfile -t testmesh/agent:local .Troubleshooting
Agent not connecting
- Verify the token is correct and not revoked (Settings → Agents)
- Check outbound connectivity:
curl -I https://app.testmesh.io/health - If behind a corporate proxy, set
HTTPS_PROXYbefore starting the agent - Check logs:
docker logs testmesh-agent
Flows failing on the agent
- If a flow works locally via
testmesh run, it should work on the agent — both use the same runner - Verify the agent can reach your internal services:
docker exec testmesh-agent curl http://internal-api:8080/health - Environment variables and connection strings must be set on the agent container, not on the control plane
- Check that service URLs in your flow use the hostnames the agent can resolve, not
localhost
Agent disconnects frequently
- The agent auto-reconnects with exponential backoff (starts at 5s)
- Frequent disconnects may indicate network instability or a proxy terminating long-lived WebSocket connections
- Configure your proxy or load balancer to allow long-lived WebSocket connections (no idle timeout) on the control plane URL