Skip to main content
Version: 5.x

MTE Relay Client for Python using Requests

Current SDK release: 5.0.0.

Introduction​

The MTE Relay Python client wraps requests. Your application calls client.get(), client.post(), or another familiar method and receives a requests.Response. The client encodes the request, sends it through MTE Relay, and decodes the response.

Your app --> MteRelayClient --> MTE Relay --> Upstream API
(plain) (MTE frames) (decodes) (plain HTTP)

This client speaks frame version 2, used by MTE Relay 5.

Prerequisites​

  • Python 3.8 or later
  • A running MTE Relay server that supports frame version 2
  • An MTE license (company name and license key)
  • The Python MTE SDK files and matching native client library for your platform

Installation​

Use the client source checkout for release 5.0.0. The wrapper's runtime Python dependency is requests; install its dependencies with:

python -m pip install -r requirements.txt

Keep the supplied mte/ SDK directory alongside mte_relay_requests/. The native library is mte.dll on Windows, libmte.so on Linux, or libmte.dylib on macOS. The client looks in the SDK directory and its platform library subdirectory. Use a client MTE library build with the relay's server build; incompatible builds fail to decode from the first exchange.

Quick Start​

from mte_relay_requests import MteRelayClient, MteRelayConfig

config = MteRelayConfig(
company_name="Your Company",
license_key="your-license-key",
)

with MteRelayClient(config) as client:
response = client.get("https://relay.example.com/api/users")
print(response.status_code, response.json())

response = client.post(
"https://relay.example.com/api/users",
json={"name": "Alice"},
)
print(response.status_code)

The first request to a relay origin authenticates and creates encoder/decoder pairs. To do that work before the first application request, call client.init("https://relay.example.com"). Call client.close() when finished if you do not use a context manager.

Usage​

Requests Interface​

The client supports get, post, put, patch, delete, head, options, trace, and connect. Each delegates to client.request(method, url, **kwargs). It also supports prepare_request(request) and send(prepared_request, **kwargs) for two-step flows.

response = client.request(
"POST",
"https://relay.example.com/api/login",
json={"email": "john@example.com", "password": "example-password"},
params={"redirect": "false"},
headers={"Authorization": "Bearer example-token"},
timeout=10,
)

request() accepts familiar requests options including headers, data, json, params, cookies, files, auth, timeout, allow_redirects, proxies, verify, cert, stream, and hooks. Requests prepares the URL, headers, and body before the client encodes them. Unsupported keyword arguments are ignored with a warning. Application headers, including Authorization, travel inside encrypted request metadata; the outer relay request has its own transport headers.

Route Through a Separate Relay Origin​

By default, a request is sent to a relay at the request URL's origin. Use origin_map when the upstream API and relay have different origins:

config = MteRelayConfig(
company_name="Your Company",
license_key="your-license-key",
origin_map={
"https://api.example.com": "https://relay.example.com",
},
)

Each relay origin has its own client token and pair pool. An unmapped origin uses its own origin as the relay.

Stream a Response​

With stream=True, the client returns after verifying the RESPONSE frame. iter_content() or iter_lines() then yields plaintext as each DATA frame is decoded. Large response bodies and server-sent events can be consumed incrementally.

with client.get("https://relay.example.com/api/events", stream=True) as response:
for line in response.iter_lines():
if line:
print(line)

A streamed response holds its pair until the body reaches its END frame or the response is closed. If you close it early, the client retires that pair and replenishes the pool. Close responses you do not fully consume.

Configuration​

MteRelayConfig accepts these parameters:

ParameterDefaultDescription
company_name, license_key""MTE license credentials
min_pairs7Minimum pairs maintained per relay origin
base_pairsderivedInitial pairs; defaults to min(min_pairs * 2, max_pairs)
max_pairs25Maximum pairs per relay origin
refill_pairs0Pairs added in a refill cycle; 0 selects an automatic amount
use_mkeTrueUse MKE instead of standard MTE
kyber_strength1024Local MTE profile strength; must match relay discovery; accepts 512, 768, or 1024
sequence_window-63Fallback decoder sequence window before discovery
time_window1000Fallback decoder time window in milliseconds before discovery
http_timeout30.0Default HTTP timeout in seconds
pair_wait_secondsNoneWait for a free pair; None uses http_timeout, 0 fails immediately
keepalive_enabledTrueRun background session keepalive
keepalive_interval_seconds600.0Keepalive interval in seconds; 0 derives the cadence from discovery
origin_mapNoneRequest-origin to relay-origin mapping

num_pairs remains accepted as a compatibility alias for min_pairs when min_pairs is left at its default. After authentication, the client uses the relay's advertised MTE windows, Kyber strength, frame and metadata limits, and per-batch pair limit. The local window settings are fallbacks. The local MTE profile, including Kyber strength, must match the relay's advertised profile.

When all pairs are busy, the client waits up to pair_wait_seconds while the pool grows toward max_pairs. If no pair becomes available, it raises CapacityExhaustedError. This is local capacity pressure; retry when capacity is available rather than repairing a pair.

How It Works​

Frame Protocol​

  1. Discover and authenticate: GET /api/mte-relay?frameVersions=2 returns a signed client token and the relay's discovery settings. The client checks the advertised frame version and MTE profile.
  2. Pair: POST /api/mte-pair establishes encoder/decoder pairs with Kyber public keys, personalization strings, the MTE profile, and frame version.
  3. Exchange data: Each application request becomes POST / with Content-Type: application/octet-stream. Its body contains one REQUEST frame followed by zero or more DATA frames. The reply contains one RESPONSE frame followed by zero or more DATA frames.

Frame version 2 uses a 12-byte envelope containing a signature, version, frame kind, flags, reserved bytes, and payload length. REQUEST and RESPONSE carry encrypted metadata. A DATA frame carries one encoded body chunk; its END flag is bound into the encoded plaintext. The last frame in each direction marks END, including a bodiless REQUEST or RESPONSE. Large bodies are split into DATA frames to respect the relay's advertised frame limit.

An unframed relay response is a transport error, regardless of its HTTP status.

Sessions and Keepalive​

The client checks out one pair for the full request and response exchange. Other requests use other available pairs. This prevents concurrent requests from advancing the same encoder or decoder state.

When keepalive is enabled, the background loop sends one POST /api/mte-keepalive per ready relay session. The server refreshes all of that client's pairs in that call; no MTE codec state is advanced. The call can also carry IDs of pairs the client has retired. Failed keepalives are logged and the drop list is retried on the next cycle. The client refreshes its token before the relay's advertised token age expires, retaining the pool when the client ID stays the same.

If a pair expires, the next request may receive 470 pair_not_found. The client repairs that pair and returns the error response so the caller can decide whether to repeat the application request.

Error Handling​

Relay-generated failures use an ERROR frame and repeat the code and reason in X-MTE-Relay-Error. The client uses the code and reason to determine its action. It never transparently repeats an application request.

CodeTypical reasonClient action
470–474Pair missing or encoder/decoder state failureReplace the affected pair
475invalid_client_id, invalid_tokenRe-authenticate and rebuild the pool
475unknown_keyStop; the key was revoked
476, 485, 488Pool, upstream capacity, connection, or rate limitBack off
477invalid_frame / eof_before_endSurface an invalid frame; replace the pair after an incomplete request body
478frame_version_unsupportedSurface the incompatibility
479, 483Replay or header mismatchSurface a security error
480, 482Routing or metadata errorSurface the error
481Frame or body too largeSurface; the HTTP pair remains usable
484Target policy rejectionSurface or stop, depending on the reason
486pair_busy / not_ownerReport retry action / back off
487, 489Rekey overdue / target resetReport reconnect action / surface
490Invalid request, pair, or MTE profileSurface the setup error
500, 502Relay or upstream failureSurface the error
503State store unavailable / write failedReport retry action / replace the affected pair

For a relay error response, inspect response.mte_relay_error (a RelayError when available), response.mte_relay_action (the registry action), and response.mte_relay_repair (what this call actually did). The action and repair outcome are also exposed in x-mte-relay-action and x-mte-relay-repair response headers. Repair outcomes include pair-replaced, pair-already-repaired, pair-replace-failed, full-repair-success, full-repair-failed, backoff, and none. A successful repair makes a later retry possible; it does not mean the original application request was retried.

If a failure occurs after a streamed response's headers have been returned, reading its body raises ResponseBodyError, a requests.exceptions.ChunkedEncodingError subclass. Its relay_error and action attributes provide relay details when available. For a buffered response, an ERROR frame that interrupts the body is returned as a relay error response.

Logging​

The library uses the mte_relay_requests Python logger:

import logging
logging.getLogger("mte_relay_requests").setLevel(logging.INFO)

Authentication, pairing, repair, and routine early stream closure are logged at INFO; relay and keepalive failures are logged at WARNING or ERROR. Enable DEBUG when diagnosing frame or pair selection behavior.

Support​

Contact Eclypses for the MTE SDK, license, and client distribution. See the requests documentation for the underlying HTTP interface.