MTE Relay Web Client Implementation
Current SDK release: 5.0.0.
MTE Relay Client for Web
The web client exposes mteFetch, which mirrors the browser's native
fetch. Swap
fetch for mteFetch, point the request at your MTE Relay Server, and the
client encrypts the request and decrypts the response for you.
This page covers the v5 client, which speaks frame version 2 and works only with v5 relays. A v4 client cannot connect to a v5 relay; see Upgrading from v4.
Installation
Install the client from npm:
npm install mte-relay-browser-public-client@5.0.0
mte-relay-browser-public-client 5.0.0 bundles the
mte-relay-browser engine 5.0.0, the MTE WASM library, and a
license, and it initializes itself when imported, so no setup call is required.
Quick Start
import { mteFetch } from "mte-relay-browser-public-client";
const response = await mteFetch("https://relay.example.com/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "John", password: "P@ssw0rd!" }),
});
const data = await response.json();
The origin of the request URL is the relay. The path, query, headers, and body
travel encrypted, and the relay forwards them to the upstream its DOMAIN_MAP
names for that host. The method is encrypted too, but for the nine standard
methods the frame also carries a one byte method code in the clear, which the
relay checks against the encrypted copy. Open the browser's network tab and you will
see POST / requests with Content-Type: application/octet-stream in place of
your original requests.
On the first request to a relay origin the client authenticates, reads the relay's discovery document, and pairs. It keeps one session per relay origin and encode type.
Configuration
Call configureMteRelay(options) once at startup, before the first mteFetch,
to change the defaults. Calling it again replaces the previous configuration.
import { configureMteRelay, mteFetch } from "mte-relay-browser-public-client";
await configureMteRelay({
unencryptedHeaders: ["traceparent"],
credentials: "include",
});
| Option | Default | Description |
|---|---|---|
defaultEncodeType | "MKE" | "MKE" or "MTE". |
minPairs | 4 | Warm baseline of the pair pool; the pool refills in the background toward it. |
initialPairs | 1 | Pairs created when a session starts, from 1 to maxPairs. |
maxPairs | 8 | Hard ceiling on the pair pool. The relay's advertised maxPairsPerClient also caps it. |
unencryptedHeaders | [] | Header names sent as ordinary HTTP headers instead of inside the encrypted frame. ["*"] sends every header plain except content-type and x-mte-relay-route, which are always encrypted. |
credentials | "include" | The fetch credentials mode for every request to the relay: "include", "same-origin", or "omit". |
warnings | true | The client's once per session console warnings. |
keepAliveIntervalMs | derived | Keepalive interval, from 60000 to 600000. Without it the client uses one third of the relay's session timeout, within the same range, which is 600000 for a relay that advertises the default or no session timeout. |
httpTimeoutMs | 30000 | Timeout for each request to the relay. |
sequenceWindow, timeWindow | relay's values | The client uses the values the relay advertises, and warns once when a configured sequenceWindow differs. |
streamDiagnostics | off | true logs a bounded preview of each frame to the console; an object with onEvent delivers them to your callback. |
onPoolEvent | none | Callback for pair pool events such as pair-appended, pair-replaced, and capacity-exhausted. |
Per request options
mteFetch takes the same two arguments as fetch, plus an optional third:
mteFetch(resource, init, { encodeType: "MTE", unencryptedHeaders: ["x-request-id"] });
| Option | Description |
|---|---|
encodeType | "MKE" or "MTE" for this request. |
unencryptedHeaders | Replaces the configured list for this request only. |
The v4 options encodeUrl, encodeHeaders, pathPrefix, and useStreaming
do not exist in v5. The path and every header are always encrypted, apart from
names listed in unencryptedHeaders, and responses stream.
Plain headers, CORS, and cookies
- A header sent plain reaches your upstream only if the relay domain's
forward_plain_headerslists it. The client warns once for each name the relay will not forward. - When the relay domain customizes
cors_allowed_headers, it must listContent-Type,X-MTE-Relay-Route, and every plain header name. - The client sends credentials by default, so the relay domain must allow them,
which it does unless
cors_allow_credentialsisfalse. - Cookies reach the upstream when the relay domain's
forward_browser_cookiesis on, which is the default.
See CORS and WAF for the relay side.
Pass through routes
Requests whose path matches one of the relay domain's pass_through_routes
are sent as plain fetch calls, with no encryption. The list arrives in the
discovery document, so the first request to a relay origin still authenticates
before any pass through call.
Errors
| Error | Thrown when | Useful fields |
|---|---|---|
MteRelayError | The relay answered with a relay error code, 470 to 490, 500, 502, or 503. | code, reason, action, relayMessage, retryAfterMs, requestId, repaired, pairReplaced |
MteRelayTransportError | The answer was not a valid frame, such as a CDN error page, a WAF block, or an ingress 404. It is never treated as application data. | status, detail, requestId |
MteRelaySetupError | The relay and the client cannot work together, for example a frame version, library profile, or Kyber strength mismatch. Retrying does not help. | message |
MteRelayRequestTooLargeError | The body is larger than the relay's maxRequestBodyBytes. Nothing was sent. | size, limit |
MteRelayCapacityError | The pair pool is at maxPairs with no idle pair. | maxPairs |
MteRelayHttpError and MteRelayProtocolError are kept as aliases of
MteRelayError and MteRelayTransportError. The client retries a request once
by itself only for 486 pair_busy and 503 state_store_unavailable, where
nothing ran. For every other error it replaces or repairs pairs as the error's
action says, then throws, and leaves the retry decision to you.
import {
mteFetch,
MteRelayError,
MteRelayRequestTooLargeError,
} from "mte-relay-browser-public-client";
try {
await mteFetch("https://relay.example.com/api/upload", { method: "POST", body });
} catch (error) {
if (error instanceof MteRelayRequestTooLargeError) {
console.error(`Body is ${error.size} bytes; the relay accepts ${error.limit}.`);
} else if (error instanceof MteRelayError) {
console.error(`Relay error ${error.code} ${error.reason}, request ${error.requestId}`);
}
}
error.requestId holds the X-MTE-Relay-Request-Id the relay returned, which
matches the correlationId in relay logs. See
Errors and recovery for every code.
The package also exports RelayCode, Reason, relayActionFor,
requiresFullRepair, classifyRelayStatus, and isRelayErrorStatus for code
that inspects relay errors.
Axios compatible Implementation
Axios is a popular network request library created before the standardization
of fetch(), and many web applications still use it. To use MTE Relay with an
existing code base that uses Axios, we recommend switching to
Redaxios, which uses fetch() under
the hood but provides the same API as Axios.
First, replace Axios with Redaxios and test that the application still works.
Then provide mteFetch as its fetch function:
npm i redaxios
import redaxios from "redaxios";
import { mteFetch } from "mte-relay-browser-public-client";
const apiClient = redaxios.create({
baseURL: "https://relay.example.com",
fetch: mteFetch, // use mteFetch instead of native fetch
});
Using the engine directly
The public client wraps the mte-relay-browser engine and exports its whole
API, including initMteRelayClient, closeMteRelayClient, and the
MteRelayClient class for code that needs its own client instance.
initMteRelayClient and MteRelayClient take the license through their own
options; the bundled license is supplied only through configureMteRelay for
the shared mteFetch client. One page runs one MTE runtime. Importing the
public client starts it with the bundled license, so with a different license
initMteRelayClient, or init() or the first fetch() on an
MteRelayClient, rejects with MTE bootstrap options conflict.