Skip to main content

Probe API

HTTP endpoints for testing connectivity to Muti Metroo listeners. The probe dials a listener, performs the PEER_HELLO handshake, and reports diagnostics (success, RTT, remote agent ID, classified error) without establishing a real peer connection. This is the same operation exposed by the muti-metroo probe CLI command, but available over HTTP so dashboards and other tools can run tests programmatically.

Endpoints​

EndpointMethodDescription
/api/probePOSTProbe from the local agent (the one serving the API)
/agents/{agent-id}/probePOSTProbe from a remote agent in the mesh

/api/probe requires http.dashboard: true in configuration. /agents/{agent-id}/probe requires http.remote_api: true in configuration.

A probe that ran to completion always returns HTTP 200, even when the handshake failed. The boolean success field in the response body indicates whether the listener was reachable. HTTP 4xx is reserved for invalid requests (bad JSON, unknown transport, malformed PEM, etc.).

:::warning Treat as outbound network access An authenticated caller of /api/probe can dial any host:port reachable from the agent, including internal addresses the caller could not reach directly. The probe only sends a PEER_HELLO handshake, but the TCP/TLS dial itself is a stronger primitive than anything else exposed by the dashboard API. Protect it with http.token_hash and gate the endpoint groups with http.dashboard / http.remote_api. The remote variant is additionally blocked when management-key decryption is unavailable. :::


POST /api/probe​

Probe a Muti Metroo listener from the local agent.

Request​

curl -X POST http://localhost:8080/api/probe \
-H "Content-Type: application/json" \
-d '{"transport": "quic", "address": "agent2.example.com:4433"}'

Probe over HTTP/2 with strict TLS verification using a custom CA bundle:

curl -X POST http://localhost:8080/api/probe \
-H "Content-Type: application/json" \
-d @- <<'JSON'
{
"transport": "h2",
"address": "agent2.example.com:443",
"path": "/mesh",
"strict_verify": true,
"ca_cert_pem": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n"
}
JSON

Probe a plaintext WebSocket listener (e.g. behind a reverse proxy):

curl -X POST http://localhost:8080/api/probe \
-H "Content-Type: application/json" \
-d '{"transport": "ws", "address": "127.0.0.1:8080", "path": "/mesh", "plaintext": true}'

Request Body​

FieldTypeRequiredDescription
addressstringYesTarget host:port to probe
transportstringNoquic (default), h2, or ws
pathstringNoHTTP path for h2/ws transports (default /mesh)
timeout_msintegerNoProbe timeout in milliseconds (default 10000, max 60000)
strict_verifybooleanNoIf true, verify the server's TLS certificate (default false)
plaintextbooleanNoUse plaintext (no TLS); valid only with transport: ws
ca_cert_pemstringNoInline CA certificate PEM for verification
client_cert_pemstringNoInline client certificate PEM for mTLS (must be paired with client_key_pem)
client_key_pemstringNoInline client key PEM for mTLS (must be paired with client_cert_pem)
alpn_protocolstringNoOverride the ALPN identifier
http_headerstringNoOverride the protocol HTTP header
ws_subprotocolstringNoOverride the WebSocket subprotocol

:::note Inline PEM, not file paths Unlike the muti-metroo probe CLI, the HTTP API never accepts filesystem paths for TLS material. Provide PEM blocks inline in the request body. This avoids turning the API into a remote file-read primitive. :::

Response​

Probe Succeeded (200):

{
"success": true,
"transport": "quic",
"address": "agent2.example.com:4433",
"remote_id": "abc123def456789012345678",
"remote_display_name": "agent-eu-west",
"rtt_ms": 23
}

Probe Ran but Listener Unreachable (200):

{
"success": false,
"transport": "quic",
"address": "agent2.example.com:9999",
"error": "Connection refused - listener not running or port blocked"
}
{
"success": false,
"transport": "h2",
"address": "agent2.example.com:443",
"error": "TLS error - certificate signed by unknown authority (disable verification or provide a trusted CA)"
}

Bad Request (400):

{
"error": "address is required"
}
{
"error": "invalid transport \"foo\" (expected quic, h2, or ws)"
}
{
"error": "plaintext mode is only supported for the ws transport"
}
{
"error": "ca_cert_pem must be a CERTIFICATE PEM block"
}
{
"error": "client_cert_pem and client_key_pem must be provided together"
}
{
"error": "timeout_ms must be between 0 and 60000"
}

Method Not Allowed (405):

method not allowed

Not Found (404):

Returned when http.dashboard: false in the agent's configuration.


POST /agents/{agent-id}/probe​

Probe a Muti Metroo listener from the perspective of a remote mesh agent. The request is forwarded to the target agent over the mesh control channel, which executes the probe locally and returns the result. Useful for verifying connectivity from any vantage point in the mesh from a single dashboard.

Request​

curl -X POST http://localhost:8080/agents/abc123def456/probe \
-H "Content-Type: application/json" \
-d '{"transport": "quic", "address": "10.0.0.4:4433"}'

Request Body​

Same as /api/probe.

Response​

Same response format as /api/probe. The body reflects the probe result as seen from the target agent.

Additional Error Responses​

Forbidden (403):

probe restricted: management key decryption unavailable

Bad Gateway (502):

failed to send request: <error details>

Returned when the control request to the remote agent fails (agent unknown, unreachable, or timed out).


Error Responses​

StatusDescription
200Probe ran (inspect success field for the result)
400Invalid request body, validation error, or malformed PEM
401Bearer token authentication required (when http.token_hash is set)
403Management key decryption unavailable (remote variant only)
404Endpoint disabled (http.dashboard: false for /api/probe, http.remote_api: false for the remote variant)
405Method not allowed (must be POST)
502Remote agent not found, unreachable, or timed out (remote variant only)

:::tip success vs HTTP status The HTTP status reflects whether the API call itself succeeded. The success field in the JSON body reflects whether the probe reached the listener and completed the handshake. A handshake failure is an HTTP 200 with "success": false, mirroring the /api/mesh-test convention. :::


Examples​

Verify a Set of Listeners​

#!/bin/bash
AGENT="http://localhost:8080"

LISTENERS=(
"quic agent1.example.com:4433"
"quic agent2.example.com:4433"
"h2 agent3.example.com:443"
)

for ENTRY in "${LISTENERS[@]}"; do
read -r TRANSPORT ADDR <<<"$ENTRY"
RESULT=$(curl -s -X POST "$AGENT/api/probe" \
-H "Content-Type: application/json" \
-d "{\"transport\":\"$TRANSPORT\",\"address\":\"$ADDR\",\"timeout_ms\":3000}")
OK=$(echo "$RESULT" | jq -r '.success')
if [ "$OK" = "true" ]; then
echo "OK $TRANSPORT $ADDR rtt=$(echo "$RESULT" | jq -r '.rtt_ms')ms"
else
echo "FAIL $TRANSPORT $ADDR error=$(echo "$RESULT" | jq -r '.error')"
fi
done

Probe a Listener From Every Agent​

#!/bin/bash
ENTRY_AGENT="http://localhost:8080"
TARGET="agent2.example.com:4433"

# Get all known agents
AGENTS=$(curl -s "$ENTRY_AGENT/agents" | jq -r '.[].id')

for AGENT_ID in $AGENTS; do
RESULT=$(curl -s -X POST "$ENTRY_AGENT/agents/$AGENT_ID/probe" \
-H "Content-Type: application/json" \
-d "{\"transport\":\"quic\",\"address\":\"$TARGET\",\"timeout_ms\":3000}")
OK=$(echo "$RESULT" | jq -r '.success')
echo "$AGENT_ID -> $TARGET: $OK ($(echo "$RESULT" | jq -r '.rtt_ms // .error'))"
done