Spec revision 1.1 · IANA port 2743/tcp, 2743/udp

MURX

Multi-User Resource eXchange — an authenticated routing gateway protocol. Authenticate once, get redirected to the right backend node, and the gateway is never in the data path again.

One gatekeeper, two jobs

A MURX server sits in front of a pool of backend application servers and does what would otherwise be two separate systems: it authenticates the client, and it tells the client which backend node to actually use. It never proxies the application data itself.

01

Authentication encapsulation

A secure envelope for client credentials. The MURX server is a centralized gatekeeper on port 2743, so unauthenticated traffic never reaches a backend application server.

02

Dynamic connection routing

On success, MURX doesn't serve the resource itself — it calculates the best backend node (by load, privileges, or proximity) and hands back its network coordinates.

03

Handoff via token

The routing reply includes a one-time, short-lived token. The client presents it to the assigned backend to prove it already cleared MURX authentication.

Architecture at a glance

Both transports share port 2743: TCP carries the client-facing handshake, UDP keeps the server's live backend registry current.

MURX Server gatekeeper · port 2743 Client user@domain Backend Node ERP, DB, game server, ... AUTH_CONNECT ROUTE_REDIRECT / AUTH_REJECT (tcp) NODE_ANNOUNCE NODE_HEARTBEAT / HEARTBEAT_ACK (udp) direct session — token + application data (tcp) MURX is no longer involved here

Example: a user@domain login

A common Client ID convention is user@domain — the same shape as an email address — with the password as the opaque credential. The domain part doubles as the discovery key: by default a client just connects to murx.<domain> on port 2743, no DNS record required, with an SRV lookup as an optional override. The backend here could just as easily be a database shard, a game server instance, or a chat homeserver — MURX doesn't care what it's gatekeeping.

import asyncio
from murx import MurxClient, resolve_murx_server, split_client_id

async def main():
    client_id = "[email protected]"
    _, domain = split_client_id(client_id)
    host, port = await resolve_murx_server(domain)

    route = await MurxClient.authenticate(
        host=host, port=port,
        client_id=client_id, auth_data=b"hunter2",
    )
    reader, writer = await MurxClient.connect_to_backend(route)
    # reader/writer is now an authenticated session with whichever
    # backend node MURX decided to route this client to.

asyncio.run(main())

See the full walkthrough →