Environment variables
MTE Relay Server (MRS) and MTE API Relay (MAR) run the same relay program and read the same settings. The two images differ in the MTE library build they link (see MRS and MAR images), not in configuration.
Every setting is an environment variable. When a relay is managed by the control plane, the control plane can override any setting in the group tables below, and each worker resolves a value in this order:
control plane override > environment variable > built in default
The Applies column says when a change takes effect:
- live: a control plane change reaches every worker within one poll
interval, with no restart. Six settings are live:
DOMAIN_MAP,LOG_LEVEL,MAX_REQUEST_BODY_MIB,MAX_CONTROL_BODY_MIB,FORWARD_HEADERS, andHEALTH_CHECK_PATH. - restart: the worker reads the value when it builds its runtime. A control plane change is stored at once and applied the next time each worker starts.
A changed environment variable always needs a restart, because a process reads its environment when it starts.
How values are parsed
Every setting the relay shares with SocketX has the same name, default, and parsing rule in both products.
- Booleans accept
true,1,yes,on,false,0,no, andoff, in any letter case. - An empty value in the environment is the same as an unset one: the default applies and nothing is logged.
- A value that does not parse as its type (a boolean, an integer, a level
name) logs a warning that names the variable and the value in use, and the
default applies:
RATE_LIMITS "maybe" is not a boolean; using true, orLOG_LEVEL "verbose" is not a level name; logging at info. Such a value never fails startup. Under a control plane the warning is logged once per applied configuration version. - A few rules do fail startup: a
LISTEN_PORTorPORTthat is not a port number from 1 to 65535, aKYBER_STRENGTHoutside512,768, and1024, aDOMAIN_MAPthat is not valid JSON, aTRUSTED_PROXIESentry that is not a usable IP address or CIDR, an invalidPROBE_PORTorPROBE_ADDR, and the bounds under Validation rules. A relay without a control plane exits with the reason. PORTandMAX_PAIR_MIBare aliases kept so 4.x files keep working. The canonical name wins when both are set, and a disagreement is logged once:LISTEN_PORT 8080 and PORT 9000 disagree; using LISTEN_PORT.HOSTNAMEis retired. It never bound anything in 4.x or 5.x;LISTEN_HOSTdoes. A value in the environment is ignored with the warningHOSTNAME is retired and ignored: it never bound anything; set LISTEN_HOST, except the pod or container name that every container runtime puts there, which never warns. A stored control plane override namedHOSTNAMEis dropped from a write with a warning, likeSTATE_MODE.- The control plane is stricter. It rejects an override that does not parse as the setting's type.
General
| Variable | Type | Default | Applies | Description |
|---|---|---|---|---|
LOG_LEVEL | trace, debug, info, warn, warning, error, fatal, panic, disabled | info | live | Minimum log level, in any letter case. warning is the same as warn. A value that is not a level name logs LOG_LEVEL "x" is not a level name; logging at info and runs at info. |
USE_CONSOLE_LOGS | bool | false | restart | Writes human readable console lines instead of JSON. Keep it false wherever a log platform ingests the output. |
Routing
| Variable | Type | Default | Applies | Description |
|---|---|---|---|---|
DOMAIN_MAP | JSON | none | live | Maps each request Host to an upstream and its per domain settings. See DOMAIN_MAP. A standalone relay needs at least one entry. A managed relay can receive the map from the control plane instead. |
MAX_REQUEST_BODY_MIB | int | 0 | live | Inclusive cap, in MiB, on the plaintext request body. Exactly the cap passes; one byte more is answered 481 body_too_large. 0 means unlimited. Pass through routes are never capped. See Limits and scaling. |
MAX_CONTROL_BODY_MIB | int | 4 | live | Cap, in MiB, on the JSON bodies of /api/mte-pair and /api/mte-keepalive. Over the cap is answered 481 body_too_large. 0 means unlimited. |
FORWARD_HEADERS | bool | true | live | Stamps X-Forwarded-Host, X-Forwarded-Proto, and X-Forwarded-For on upstream requests. Turn it off only for an upstream that rejects them. It does not change the per domain preserve_host setting. |
Server
| Variable | Type | Default | Applies | Description |
|---|---|---|---|---|
LISTEN_HOST | string | 0.0.0.0 | restart | Address the relay listener binds. 0.0.0.0 is every interface, 127.0.0.1 loopback only; an IPv6 address may be written with or without brackets. Leave it unset in a container. With MODE=control the control plane ignores it and binds every interface. |
LISTEN_PORT | int | 8080 | restart | Relay listen port, from 1 to 65535. Any other value refuses to start. With MODE=control it is the control plane's listen port. |
PORT | int | 8080 | restart | Alias of LISTEN_PORT, kept for 4.x configurations. LISTEN_PORT wins when both are set, and a disagreement is logged once. Set LISTEN_PORT in new files. |
HEALTH_CHECK_PATH | string | empty | live | Health check path on the relay port, for a load balancer or a security scanner that cannot reach the probe port. Empty means off, so an upgrade changes nothing. GET and HEAD at this path answer 200 {"status":"ready"} while the relay serves and 503 {"status":"not_ready"} once shutdown begins, for any Host, before Host resolution, CORS, rate limits, and the access log. HEAD sends no body. Any other method falls through to normal routing. Set it in the environment or in the control plane, where it is a Server group live field. See Health checks and scanners and the validation rules. |
DEFAULT_POOL_SIZE | int | 250 | restart | Idle encoder and decoder instances kept by each of the four codec pools (MTE encoders, MTE decoders, MKE encoders, MKE decoders). The pools fill to this size at startup. A request that finds its pool empty creates a new instance, and an instance returned to a full pool is discarded. |
MAX_POOL_SIZE_LIMIT | int | 100000 | restart | Upper bound on DEFAULT_POOL_SIZE. A larger pool size is reduced to this value. |
SESSION_TIMEOUT_MINUTES | int | 120 | restart | Idle timeout for pairs and for outbound relay clients. Traffic on a pair and keepalive calls refresh it. Advertised in discovery as sessionTimeoutSeconds. |
HTTP_CLIENT_TIMEOUT_SECONDS | int | 30 | restart | How long the relay waits for an inbound domain's upstream response headers. It does not cut off a response body that is already streaming. On an outbound domain it caps each auth, pair, and keepalive call to the next relay instead. |
HTTP_MAX_IDLE_CONNS_PER_HOST | int | 1024 | restart | Idle keep alive connections kept per upstream host. |
MTE
| Variable | Type | Default | Applies | Description |
|---|---|---|---|---|
COMPANY_NAME | string | Eclypses Inc | restart | MTE license company name. |
LICENSE_KEY | string, sensitive | development key | restart | MTE license key. The default is a development license. Set your production license. |
KYBER_STRENGTH | 512, 768, 1024 | 1024 | restart | Kyber key exchange strength. Both ends of a pair must use the same strength, and it is advertised in discovery. The browser engine always uses 1024, so keep the default on a relay that serves browsers. |
USE_MKE | bool | true | restart | Encode type the relay uses as a client on its outbound domains: MKE when true, standard MTE when false. Inbound, the relay decodes whichever type each request frame declares. |
SEQUENCE_WINDOW | int | -63 | restart | MTE decoder sequence window. Advertised in discovery, and both ends of a pair use it. |
TIME_WINDOW | int | 1000 | restart | MTE decoder time window. Advertised in discovery, and both ends of a pair use it. |
Protocol
Frame version 2 bounds and timers, and the rate limit settings. Discovery
advertises all of them except RESPONSE_COALESCE_MS, RATE_LIMITS, and
TRUSTED_PROXIES, so clients read them from the relay instead of hardcoding
them. See Discovery.
| Variable | Type | Default | Applies | Description |
|---|---|---|---|---|
MAX_FRAME_BYTES | int | 65536 | restart | Largest frame payload accepted, in bytes. Advertised as maxFrameBytes and maxMessageBytes. Minimum 4096. The relay verifies a whole frame before it releases any plaintext, so a larger frame is not faster. |
MAX_METADATA_BYTES_MKE | int | 61439 | restart | Bound on the metadata JSON (method, path, headers) under MKE. Enforced and advertised. At least 1024, and at most MAX_FRAME_BYTES minus 1024. |
MAX_METADATA_BYTES_MTE | int | 7168 | restart | Bound on the metadata JSON under standard MTE. Enforced and advertised. At least 256, and 8 times the value plus 16 must fit in MAX_FRAME_BYTES. |
MAX_PAIRS_PER_BATCH | int | 100 | restart | Pairs accepted in one pairing request. At least 1. |
MAX_PAIRS_PER_CLIENT | int | 2048 Since 5.0.1 | restart | Pairs one client may hold, and the most pair ids one keepalive call may drop. At least 1. An outbound relay clamps its CLIENT_MAX_PAIRS to this value, so keep it at or above the largest CLIENT_MAX_PAIRS that pairs with this relay. Relay 5.0.0 shipped with 200. |
MAX_PAIR_BYTES | int | 1073741824 | restart | Plaintext bytes per direction before a connection rekeys; the default is 1 GiB. Advertised as maxPairBytes for connection transports. The HTTP relay advertises it and does not enforce it. 0 takes the default and never means unlimited. |
MAX_PAIR_MIB | int | 1024 | restart | Alias of MAX_PAIR_BYTES in MiB, kept for 4.x configurations. MAX_PAIR_BYTES wins when both are set, and a disagreement is logged once. |
MAX_PAIR_SECONDS | int | 86400 | restart | Seconds before a connection rekeys. Advertised as maxPairSeconds for connection transports. The HTTP relay advertises it and does not enforce it. 0 takes the default. |
TOKEN_MAX_AGE_SECONDS | int | 86400 | restart | Maximum age of a client token, checked at pair and keepalive. The data path checks only the token signature. At least 60. |
REQUEST_SECONDS | int | 30 | restart | Silence deadline between request frames and for reading the whole request. At least 1. Per domain override: request_seconds. |
RESPONSE_SECONDS | int | 0 | restart | Bound on a whole response. 0 means unbounded, and the idle deadline applies. Per domain override: response_seconds. |
IDLE_TIMEOUT_SECONDS | int | 90 | restart | Silence deadline between response frames. Advertised as idleTimeoutSeconds, which connection transports also use as their idle timeout; the HTTP relay's own keep alive idle timeout is a fixed 60 seconds. At least 1. Per domain override: idle_seconds. |
PING_INTERVAL_SECONDS | int | 25 | restart | PING cadence advertised to connection clients. Must be below IDLE_TIMEOUT_SECONDS. The default 25 seconds stays under the 30 second idle timers common on proxies. Per domain override: ping_seconds. |
DRAIN_SECONDS | int | 30 | restart | Shutdown budget. After SIGTERM, readiness turns 503 stopping, the relay listener stops accepting, requests in flight drain for up to this many seconds, then the relay closes its state store and the probe listener closes last. Advertised as drainSeconds for connection transports, where it is also the time between GOAWAY and closing what remains on a connection. 0 takes the default. See Keepalive and timeouts. |
RESPONSE_COALESCE_MS | int | 2 | restart | Milliseconds a streamed response DATA frame stays open after its first byte, so small writes from a paced upstream share a frame. 0 turns it off. At most 1000. Per domain override: response_coalesce_ms. |
RATE_LIMITS | bool | true | restart | Per source and per client rate limits on auth, pair, and keepalive, answered 488 rate_limited with Retry-After. false turns every token bucket limiter off. See Limits and scaling. |
TRUSTED_PROXIES | string | none | restart | Comma separated IP addresses and CIDRs of the load balancers and reverse proxies in front of the relay. For a request from one of them, the per source auth and pair limits key on the client address that X-Forwarded-For names instead of on the proxy. Empty keys every request on its peer address. See Limits and scaling. |
Outbound client
These apply to the client the relay runs toward the next relay on an outbound domain, which is how MTE API Relay chains work. The relay keeps one outbound client per outbound host.
| Variable | Type | Default | Applies | Description |
|---|---|---|---|---|
CLIENT_INITIAL_PAIRS | int | 2 | restart | Pairs created before the first request. The pool then fills to CLIENT_MIN_PAIRS in the background. At least 1. |
CLIENT_MIN_PAIRS | int | 25 | restart | Minimum pair pool size. At least 1. |
CLIENT_MAX_PAIRS | int | 1024 Since 5.0.1 | restart | Maximum pair pool size. A request holds one pair from encoding until its response ends, so this is the number of requests one replica can have in flight through one outbound host. At least CLIENT_MIN_PAIRS, and clamped to the receiving relay's MAX_PAIRS_PER_CLIENT with a warning. Set both to the same value to pin a fixed pool; setup fills to it one batch at a time. Relay 5.0.0 shipped with 100. |
CLIENT_REFILL_THRESHOLD | int | 10 | restart | Refill the pool when available pairs fall to this value or fewer, or when requests are waiting for a pair. Each refill is sized to the requests waiting plus this margin, and to at least half the pairs in use, up to MAX_PAIRS_PER_BATCH; the next one starts as soon as it lands. From 0 to one below CLIENT_MAX_PAIRS. When it is unset and CLIENT_MAX_PAIRS is 10 or lower, it becomes one below the maximum. |
CLIENT_PAIR_ACQUIRE_WAIT_MS | int | 1000 Since 5.0.1 | restart | How long a request waits for a free pair before erroring. Requests wait in arrival order. Past the wait the outbound domain answers 485 upstream_capacity with Retry-After. At least 0. Relay 5.0.0 shipped with 250. |
Redis
The tuning values apply only when REDIS_URL is set.
| Variable | Type | Default | Applies | Description |
|---|---|---|---|---|
REDIS_URL | string, sensitive | none | restart | Redis URL for shared pair state. Needed when two or more replicas serve the same clients. Without it each relay keeps pair state in its own memory. |
REDIS_POOL_SIZE | int | 20 | restart | Redis connection pool size per relay instance. |
REDIS_MIN_IDLE_CONNS | int | 5 | restart | Minimum idle Redis connections. |
REDIS_POOL_TIMEOUT_SECONDS | int | 4 | restart | Wait for a pooled Redis connection before failing. |
REDIS_DIAL_TIMEOUT_SECONDS | int | 5 | restart | Redis dial timeout. |
REDIS_READ_TIMEOUT_SECONDS | int | 3 | restart | Redis read timeout. |
REDIS_WRITE_TIMEOUT_SECONDS | int | 3 | restart | Redis write timeout. |
REDIS_MAX_RETRIES | int | 1 | restart | Redis command retry count. |
REDIS_OP_TIMEOUT_SECONDS | int | 3 | restart | Timeout for each state store operation. |
Diagnostics
Everything here is off by default. Turn it on for a bounded measurement, not as a steady state.
| Variable | Type | Default | Applies | Description |
|---|---|---|---|---|
ENABLE_PPROF | bool | false | restart | Serves Go pprof profiles on PPROF_PORT. The endpoint has no authentication, so keep that port off public networks. |
PPROF_PORT | int | 6060 | restart | pprof listen port. From 1 to 65535 when pprof is on. |
MUTEX_PROFILE_FRACTION | int | 1 | restart | Mutex profile sampling fraction, used with pprof. |
BLOCK_PROFILE_RATE | int | 1 | restart | Block profile sampling rate, used with pprof. |
ENABLE_PERF_STATS | bool | false | restart | Emits periodic perf stats log events. |
PERF_STATS_INTERVAL | int | 10 | restart | Perf stats interval in seconds. Must be above 0 when perf stats are on. |
Deployment settings
These are read only from the environment. The control plane cannot override them, because they decide how a process starts and finds its control plane.
| Variable | Default | Read by | Description |
|---|---|---|---|
MODE | unset | both | control runs the control plane instead of the relay. Anything else runs the relay. |
CONTROL_PLANE_URL | none | worker | Absolute http:// or https:// URL of the control plane. Enables management. Set it together with CONTROL_PLANE_TOKEN; one without the other stops the worker at startup. |
CONTROL_PLANE_TOKEN | none | both | Shared bearer token. The control plane refuses to start without it, and each managed worker sends it on every poll. |
CONTROL_PLANE_POLL_SECONDS | 3 | worker | Poll interval, with 20 percent jitter. A value below 1 or not a number keeps the default. |
CONTROL_PLANE_DATA_DIR | data | control plane | Directory for the stored configuration, its history, service tokens, and the session signing secret. A relative path resolves against the working directory. Mount a volume here. |
CONTROL_PLANE_INITIAL_CONFIG | none | control plane | JSON object of setting name to value that becomes the first stored configuration, so no setup wizard is needed. Applied once, when the data directory has never held a configuration, and ignored after that. Object values such as DOMAIN_MAP may be written as JSON objects. Invalid JSON, an unknown setting, or a value that fails validation stops the control plane at startup. See Preset configuration instead of the wizard. |
CONTROL_PLANE_SETUP_HINTS | none | control plane | JSON object of setting name to value that the setup wizard prefills, for example {"REDIS_URL":"redis://redis:6379"}. Unknown settings and values that fail validation are dropped. |
PROBE_PORT | 9091 | worker | Liveness (GET /healthz) and readiness (GET /readyz) listener, separate from the relay port, which serves neither path. Both take GET and HEAD. 0 disables it. Keep the probe port off public networks: it is for the orchestrator or load balancer only. A load balancer that can only reach the relay port uses HEALTH_CHECK_PATH or GET /api/mte-echo instead. |
PROBE_ADDR | all interfaces | worker | Address the probe listener binds, without a port, for example 127.0.0.1 on a shared host. |
ADMIN_USERNAME | admin | control plane | Admin panel user name. |
ADMIN_PW | built in default | control plane | Admin panel password. Set your own before anyone can reach the panel; the panel warns while the default is in use. |
ADMIN_JWT_SECRET | none | control plane | Seeds the admin session signing secret on first boot. After that the copy stored in CONTROL_PLANE_DATA_DIR wins. A value under 32 bytes, or one copied from a published example, is replaced by a generated secret at startup Since 5.0.1. Usually left unset. |
RELAY_RUNTIME | detected | worker | Pins the runtime name the fleet view shows and builds restart commands for. Detection reports kubernetes, ecs, docker, or host. Display only. |
LOCAL_STATE_CACHE | false | worker | Serves pair state reads from memory and writes through to Redis. Applies only with REDIS_URL, and is correct only when a load balancer routes each client to one replica. See Limits and scaling. |
Validation rules
A relay refuses a configuration that breaks any of these rules. A standalone relay exits at startup with the reason. A managed worker keeps its current configuration and reports the reason in the control plane fleet view.
A 0 for MAX_FRAME_BYTES, MAX_METADATA_BYTES_MKE,
MAX_METADATA_BYTES_MTE, MAX_PAIRS_PER_BATCH, MAX_PAIRS_PER_CLIENT,
MAX_PAIR_BYTES, MAX_PAIR_SECONDS, TOKEN_MAX_AGE_SECONDS,
REQUEST_SECONDS, IDLE_TIMEOUT_SECONDS, PING_INTERVAL_SECONDS,
DRAIN_SECONDS, or CLIENT_INITIAL_PAIRS is not refused. It takes the
default and never means unlimited.
LISTEN_PORT, and its aliasPORT, is a port number from 1 to 65535.KYBER_STRENGTHis512,768, or1024.MAX_FRAME_BYTESis at least4096.MAX_METADATA_BYTES_MKEis at least1024andMAX_METADATA_BYTES_MTEat least256.- 8 times
MAX_METADATA_BYTES_MTEplus 16, andMAX_METADATA_BYTES_MKEplus 1024, each fit inMAX_FRAME_BYTES. MAX_PAIRS_PER_BATCHandMAX_PAIRS_PER_CLIENTare at least1.TOKEN_MAX_AGE_SECONDSis at least60.REQUEST_SECONDS,IDLE_TIMEOUT_SECONDS, andPING_INTERVAL_SECONDSare at least1;RESPONSE_SECONDSandDRAIN_SECONDSat least0.PING_INTERVAL_SECONDSis belowIDLE_TIMEOUT_SECONDS.RESPONSE_COALESCE_MSis from0to1000.CLIENT_MIN_PAIRSandCLIENT_INITIAL_PAIRSare at least1,CLIENT_MAX_PAIRSis at leastCLIENT_MIN_PAIRS,CLIENT_REFILL_THRESHOLDis from0to one belowCLIENT_MAX_PAIRS, andCLIENT_PAIR_ACQUIRE_WAIT_MSis at least0.- With
ENABLE_PERF_STATSon,PERF_STATS_INTERVALis above0. WithENABLE_PPROFon,PPROF_PORTis from 1 to 65535. TRUSTED_PROXIESholds only IP addresses and CIDRs. A host name, a CIDR with host bits set such as10.0.0.1/8, an address with an IPv6 zone, or an entry that covers every address, such as0.0.0.0/0, is refused.HEALTH_CHECK_PATH, when set, is an absolute, canonical path: no trailing slash, no//or dot segments, no wildcard, query, fragment, whitespace, or percent escapes, at most 256 bytes, not/, and not/api/mte-relay,/api/mte-echo,/api/mte-pair, or/api/mte-keepalive. A rejected value fails startup, and on the control plane the dry run and the write.DOMAIN_MAPpasses the structural rules on the DOMAIN_MAP page.
MRS and MAR images
Both images build the same relay source. They differ in the MTE library build each binary links, and a client build pairs only with a server build, while an ANY build pairs only with another ANY build:
| Image | MTE library build | Pairs with |
|---|---|---|
| MTE Relay Server (MRS) | server | Client builds: the browser engine and the mobile SDKs |
| MTE API Relay (MAR) | ANY | Another MAR, which is why both ends of a relay to relay chain run the MAR image |
A mismatch fails from the first request. An outbound relay compares the next
relay's advertised mteProfile and refuses to pair with a different build, so
the calling service gets 502 upstream_connect_failed and the outbound relay
logs mteProfile mismatch. A client SDK pointed at the wrong image fails at
setup or on its first request.