Xeonr Developer Docs

Local delivery

When a viewer is on the same network as the hub holding their media, the bytes do not need to travel to a gateway and back. Local delivery is how a client finds that shorter path, and how it proves the thing answering really is the hub.

It is always optional and always degradable. Every failure — no permission, no answer, a router that filters the address, an expired certificate — resolves to the gateway path, which is the one that always works. A client that ignores this page entirely still plays everything correctly.

What a playback response carries

StartPlayback and RenewPlayback return a MediaDelivery. Two of its fields describe the short path:

FieldMeaning
gateway_urlThe path that always works, over the internet. Present in every response.
local_urlsThe same resource, once per private address the hub reports. Empty is the normal case.

local_urls is repeated because a hub is routinely multi-homed — wifi and ethernet, a docker bridge, a VPN interface — and the platform cannot know which of its addresses you can reach. It offers every candidate in preferred order; deciding is yours, because only your machine can.

Each entry is a full URL for the primary resource, with the media token already in its query string, exactly as gateway_url is:

https://10-1-2-50.k3f9x2ab.xmbsrv.com:8443/media/{uuid}/file/media.mkv?t=…

The hostname encodes the address it resolves to. That is what lets a private address have a real, publicly-trusted certificate: the name is public, the address it names is not routable from anywhere else.

local_url (singular, field 3) is deprecated and always absent. It was designed for this and never assigned, because one URL is the wrong shape for a multi-homed hub. Read local_urls.

Which hub is on the other end

MediaDelivery also names the hub serving the bytes:

FieldMeaning
hub_uuidThe hub's handle. Always present.
hub_nameWhat its owner called it. May be empty — a hub nobody has named.

The gateway hostname does begin with the uuid today, but that is an implementation detail of one URL scheme rather than a contract; read these fields instead of parsing it. Neither field authorises anything and neither affects which URL you should use — they are there so a client can say which machine is serving a file, which is the first thing anyone needs when a stream misbehaves and the household has more than one hub.

Subtitles, chapters and trickplay

These are not given local forms on the wire. They share gateway_url's origin, so once you have chosen a local origin you swap it into those URLs yourself:

const local = new URL(chosenLocalUrl).origin;
const subtitleUrl = Object.assign(new URL(track.url), {
  protocol: new URL(local).protocol,
  host: new URL(local).host,
}).toString();

This is safe because the media token is origin-agnostic: it is bound to the hub's identity, not to a route, and the hub verifies it identically on either listener. It is also what makes failover a URL swap rather than a re-mint.

HLS needs nothing at all — the hub's playlists carry relative URIs, so a playlist fetched from a local origin resolves every segment there.

Choosing a path

Probe on a schedule, not at play time. Each origin answers GET /healthz with the hub's own uuid:

GET https://10-1-2-50.k3f9x2ab.xmbsrv.com:8443/healthz
{"status":"ok","uuid":"6f1c9d2e-…"}

The certificate already proves which hub answered; the echo is what makes a misconfiguration legible rather than mysterious. Use a short timeout — a second or two — and treat every failure identically. A refused connection, a filtered DNS name and a rejected certificate all mean the same thing: use the gateway.

Never trust a probe at play time. A probe is a statement about the past, and the network changing between the probe and the play is the normal case, not the edge case. Start on a local origin if it is warm, but if the first byte has not arrived in a second or two, or the stream errors, fail over to gateway_url and stop trusting that origin. A stale positive must cost a moment of startup, never a failed play.

Re-probe on events rather than a timer alone: coming online, waking, or returning to the app after an idle period are when a device has plausibly moved between networks.

Telling the platform you could use it

ClientCapabilities.local_trust says how you would trust a hub's certificate, and it is also the demand signal that gets one issued:

ValueMeaning
LOCAL_TRUST_ANCHOR_PUBLIC_CAYou trust the public PKI. This is what causes a certificate to be ordered.
LOCAL_TRUST_ANCHOR_PINNED_SPKIYou pin pin_spki_sha256. Needs no certificate, so it never causes an order.
LOCAL_TRUST_ANCHOR_NONEYou cannot use a local path.

Certificates are issued lazily — for hubs somebody actually watches from over their own network, rather than for the whole fleet — so this field is what starts that. Send PUBLIC_CA only when you could really use it today, not merely because you are a browser. In particular, see the permission below.

Browsers: the local-network permission

Chrome 142 and later gate any request from a public page to a private address behind a permission prompt. The permission is per-origin and sticky, so a denial is close to permanent.

The state is readable without prompting:

const status = await navigator.permissions.query({
  name: 'local-network-access' as PermissionName,
});
// 'granted' | 'denied' | 'prompt'

Read it first, and design around what it says:

  • prompt — do not probe from the player. A prompt landing on somebody who just pressed play is how a permanent denial is earned. Ask from a settings screen, with an explanation already on the page.
  • denied — do not probe at all, and report local_trust as something other than PUBLIC_CA. Ordering a certificate for a viewer who can never reach the hub wastes a shared issuance quota.
  • granted — probe freely.

A browser that does not implement the permission does not enforce it, so treat a query that throws as granted.

Listing hubs and how you reach them

ConnectivityService.ListServerHubs answers, for a viewer rather than an administrator, which hubs a server plays from. It requires only xmb:user_read — every member of the server sees the same answer, because it is a fact about the household's infrastructure rather than about the person asking.

{
  "hubs": [
    {
      "uuid": "6f1c9d2e-…",
      "name": "Loft NAS",
      "status": "HUB_STATUS_ACTIVE",
      "lastSeenAt": "2026-08-24T21:04:11Z",
      "lastHandshakeAt": "2026-08-24T21:04:09Z",
      "localDeliveryAvailable": true,
      "localOrigins": ["https://10-1-2-50.k3f9x2ab.xmbsrv.com:8443"],
      "sharesPublicAddress": true
    }
  ]
}

local_delivery_available says the platform's half is ready: a certificate that has not expired, a listener the hub reports as bound, and at least one private address. It says the short path is offered, never that it works — only your own probe can know that, because only you know which network you are on.

local_origins is present whenever the hub has usable private addresses, even when the flag is false, because a client that pins needs the origins and not the certificate.

shares_public_address is the platform's answer to a question only it can ask: does this caller reach the internet from the same public address the hub's WireGuard handshakes arrive from? Same egress usually means same household. Use it to decide when to speak, never what to allow — show an unprompted "enable local playback" banner only when it is true, but keep the permission reachable from settings regardless. Carrier-grade NAT makes strangers share an address, and a dual-stack home whose browser egresses IPv6 while the hub handshakes IPv4 makes real neighbours look distant; the first costs one failed probe, and the second must not lock anyone out.

Reading this list never causes a certificate to be ordered. Opening a settings screen is curiosity; pressing play is demand.

What the local path carries

Media bytes and generated preview imagery. Nothing else.

Library, metadata, watch state, search, authentication and every RPC continue to go to the API over the internet, whatever the local path is doing. This is deliberate: an attacker who defeated every control here gets to serve fake video frames and holds one short-lived token scoped to one file. They do not get the library, the session, or the ability to enumerate anything.

On this page