Skip to content

Transport Bindings

Protocol mapping for HTTP and message queues


Mesh is transport-agnostic. This document specifies conventions for common transports to ensure interoperability.


AspectConvention
MethodPOST
Content-Typeapplication/json
EndpointService-defined (e.g., /mesh, /rpc, /api)
BodyJSON-encoded Mesh request
AspectConvention
Content-Typeapplication/json
Status Code200 OK for all Mesh responses
BodyJSON-encoded Mesh response

HTTP status codes MUST NOT indicate Mesh-level errors. All valid Mesh responses return 200 OK, including errors:

HTTP/1.1 200 OK
Content-Type: application/json
{
"protocol": { "name": "mesh", "version": "0.1.0" },
"id": "req_123",
"result": null,
"errors": [{
"code": "NOT_FOUND",
"message": "Order not found",
"retryable": false
}]
}

Exception: Transport-level failures use appropriate HTTP status codes:

StatusMeaning
400 Bad RequestRequest body is not valid JSON (before Mesh parsing)
413 Payload Too LargeRequest exceeds size limit
502 Bad GatewayUpstream service unreachable
503 Service UnavailableService temporarily unavailable
504 Gateway TimeoutUpstream timeout (transport level, not Mesh deadline)

These indicate the request never reached the Mesh handler.


HTTP headers provide metadata for infrastructure components (load balancers, proxies, observability tools) without parsing the JSON body.

HeaderMaps ToDescription
X-Mesh-Request-IdidRequest identifier
X-Mesh-Trace-Idtracing extensionDistributed trace ID
X-Mesh-Span-Idtracing extensionCurrent span ID
X-Mesh-Parent-Span-Idtracing extensionParent span ID
X-Mesh-Callercontext.callerCalling service name

Trace headers map to the Tracing Extension, not context.

HeaderMaps ToDescription
X-Mesh-Request-IdidEchoed request identifier
X-Mesh-Duration-Msmeta.durationProcessing time in milliseconds
X-Mesh-Nodemeta.nodeHandling node identifier
RateLimit-Limitmeta.rate_limit.limitRate limit ceiling
RateLimit-Remainingmeta.rate_limit.remainingRemaining requests
RateLimit-Resetmeta.rate_limit.resets_inSeconds until window reset
RateLimit-PolicyPolicy identifier (optional)

Rate limit headers follow IETF draft-ietf-httpapi-ratelimit-headers.

  • Headers are OPTIONAL — the JSON body is authoritative
  • Servers SHOULD set headers for observability
  • Clients SHOULD set trace headers for propagation
  • When headers and body conflict, body takes precedence

Format differences: Headers use string representations (milliseconds for duration). The JSON body uses structured objects. Servers MUST convert between formats when mapping headers to body.

POST /mesh HTTP/1.1
Host: orders-api.internal
Content-Type: application/json
X-Mesh-Request-Id: req_xyz789
X-Mesh-Trace-Id: tr_8f3a2b1c
X-Mesh-Span-Id: sp_4d5e6f
X-Mesh-Caller: checkout-service
{
"protocol": { "name": "mesh", "version": "0.1.0" },
"id": "req_xyz789",
"call": {
"function": "orders.create",
"version": "1",
"arguments": { "customer_id": 42 }
},
"context": {
"caller": "checkout-service"
},
"extensions": [
{
"urn": "urn:mesh:ext:deadline",
"options": { "value": 5, "unit": "second" }
},
{
"urn": "urn:mesh:ext:tracing",
"options": {
"trace_id": "tr_8f3a2b1c",
"span_id": "sp_4d5e6f"
}
}
]
}
HTTP/1.1 200 OK
Content-Type: application/json
X-Mesh-Request-Id: req_xyz789
X-Mesh-Duration-Ms: 127
X-Mesh-Node: orders-api-2
RateLimit-Limit: 1000
RateLimit-Remaining: 847
RateLimit-Reset: 45
{
"protocol": { "name": "mesh", "version": "0.1.0" },
"id": "req_xyz789",
"result": { "order_id": 12345 },
"meta": {
"duration": { "value": 127, "unit": "millisecond" },
"node": "orders-api-2",
"rate_limit": {
"limit": 1000,
"remaining": 847,
"resets_in": { "value": 45, "unit": "second" }
}
}
}

HTTP clients SHOULD configure connection and read timeouts:

TimeoutRecommendation
Connection5 seconds
ReadMesh deadline + buffer (e.g., deadline + 1s)

The read timeout SHOULD exceed the Mesh deadline to allow the server to return a DEADLINE_EXCEEDED error rather than the connection being cut.


HTTP/1.1 connections SHOULD use keep-alive for efficiency. HTTP/2 is RECOMMENDED for high-throughput scenarios as it provides:

  • Connection multiplexing
  • Header compression
  • Reduced latency

Message queues enable asynchronous request processing. Common implementations: RabbitMQ, Redis Streams, Amazon SQS.

AspectConvention
BodyJSON-encoded Mesh request
Content-Typeapplication/json
Correlation IDMesh id field
Reply-ToQueue for response (if expecting reply)
PropertyMaps ToDescription
correlation_ididRequest identifier
reply_toResponse queue name
expirationdeadline extensionMessage TTL in milliseconds
headers.trace_idtracing extensionDistributed trace ID
headers.span_idtracing extensionCurrent span ID
headers.callercontext.callerCalling service

Trace headers map to the Tracing Extension, not context.

PropertyMaps ToDescription
correlation_ididEchoed request identifier
headers.duration_msmeta.durationProcessing time
headers.nodemeta.nodeHandling node

Message TTL SHOULD reflect the Mesh deadline:

message_ttl = deadline_value (converted to ms)

When a message expires before processing:

  • Queue MAY move it to dead letter queue
  • Consumer SHOULD NOT process expired messages
  • If processed, server SHOULD return DEADLINE_EXCEEDED

For standard requests expecting responses:

  1. Client publishes request to service queue
  2. Client waits on reply queue (from reply_to)
  3. Server processes request
  4. Server publishes response to reply queue
  5. Client receives response, matched by correlation_id
┌──────────┐ request ┌───────────────┐
│ Client │ ───────────────► │ Service Queue │
└──────────┘ └───────────────┘
▲ │
│ ▼
│ ┌───────────┐
│ response │ Server │
└─────────────────────── │ │
(reply queue) └───────────┘

Failed messages SHOULD be routed to a dead letter queue when:

  • Message expires (TTL exceeded)
  • Processing fails after max retries
  • Message cannot be parsed

Dead letter queues enable:

  • Debugging failed requests
  • Manual retry/reprocessing
  • Alerting on failure patterns

Publishing Request:

$channel->basic_publish(
new AMQPMessage(
json_encode($meshRequest),
[
'content_type' => 'application/json',
'correlation_id' => $meshRequest['id'],
'reply_to' => 'responses.checkout-service',
'expiration' => '5000', // 5 seconds
'headers' => new AMQPTable([
'trace_id' => $meshRequest['context']['trace_id'],
'span_id' => $meshRequest['context']['span_id'],
'caller' => 'checkout-service',
]),
]
),
'',
'orders-api'
);

Publishing Response:

$channel->basic_publish(
new AMQPMessage(
json_encode($meshResponse),
[
'content_type' => 'application/json',
'correlation_id' => $meshResponse['id'],
'headers' => new AMQPTable([
'duration_ms' => 127,
'node' => 'orders-api-2',
]),
]
),
'',
$replyTo
);

For local inter-process communication:

AspectConvention
ProtocolStream socket
FramingLength-prefixed JSON
PathService-defined (e.g., /var/run/mesh/orders.sock)

Each message is prefixed with a 4-byte big-endian length:

┌──────────────┬─────────────────────┐
│ Length (4B) │ JSON Payload │
└──────────────┴─────────────────────┘

This allows efficient message boundary detection without delimiter scanning.


Mesh uses JSON exclusively. Servers SHOULD reject requests with unsupported content types:

HTTP/1.1 415 Unsupported Media Type
Content-Type: application/json
{
"protocol": { "name": "mesh", "version": "0.1.0" },
"id": null,
"result": null,
"errors": [{
"code": "INVALID_REQUEST",
"message": "Content-Type must be application/json",
"retryable": false
}]
}

Servers SHOULD enforce request size limits:

LimitRecommendation
Maximum request body1 MB
Maximum header size8 KB

Exceeded limits return 413 Payload Too Large (HTTP) or reject the message (queues).

Compression is handled at the transport layer:

  • HTTP: Use standard Accept-Encoding/Content-Encoding headers (gzip, br)
  • Queues: Configure at broker level or use compressed message body

Mesh does not specify compression — it is an infrastructure concern.


Authentication is an implementation concern handled at the transport layer. Mesh does not specify authentication mechanisms but provides guidance for common patterns.

Use standard Authorization header for token-based authentication:

POST /mesh HTTP/1.1
Host: orders-api.internal
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Servers validate the token before processing the Mesh request. Invalid tokens return transport-level errors:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="mesh-api"
{
"error": "invalid_token",
"error_description": "Token has expired"
}

Note: This is a transport-level error, not a Mesh error. The request never reached the Mesh handler.

For service-to-service authentication:

POST /mesh HTTP/1.1
Host: orders-api.internal
Content-Type: application/json
X-API-Key: sk_live_abc123xyz

For internal service mesh authentication, use mutual TLS at the transport layer. The client presents a certificate that identifies the calling service:

┌─────────────┐ mTLS ┌─────────────┐
│ Service A │ ◄────────────► │ Service B │
│ (client) │ cert: A │ (server) │
└─────────────┘ └─────────────┘

With mTLS, the server can populate context.caller from the client certificate’s Common Name (CN) or Subject Alternative Name (SAN).

For message queues, authentication typically occurs at connection time:

  • RabbitMQ: Username/password or x509 certificates
  • Amazon SQS: IAM roles and policies
  • Redis Streams: ACL authentication

The queue broker validates credentials before allowing message publication or consumption.

After transport-level authentication, authorization data should be propagated via context:

{
"context": {
"caller": "checkout-service",
"tenant_id": "tenant_acme",
"user_id": "user_42",
"scopes": ["orders:read", "orders:write"]
}
}

This enables:

  • Multi-tenant isolation (filter by tenant_id)
  • User-level authorization (validate user_id has access)
  • Scope-based access control (check scopes for permission)
AspectInternal (service-to-service)External (client-facing)
AuthenticationmTLS, API keysOAuth 2.0, JWT
Trust levelHigh (verified service)Low (validate everything)
Rate limitingPer-service quotasPer-user/tenant quotas
Context sourcePropagated from upstreamExtracted from token
  1. Always use TLS — Never send Mesh requests over unencrypted connections
  2. Validate at the edge — Authenticate external requests at API gateway
  3. Propagate identity — Pass authenticated identity via context
  4. Principle of least privilege — Scope service permissions narrowly
  5. Rotate credentials — Use short-lived tokens where possible