Skip to main content
Crosslink’s mobile promise is:
Whether you actually get that depends on the origin the phone loaded the page from. This is browser policy, not a Crosslink setting, so it is worth understanding before you ship rather than discovering on a phone.

Stable origin and current desktop are different addresses

Installed application origin != current desktop network address.
The installed HTTPS origin owns the application identity, Service Worker, offline shell cache, installation state, cached Crosslink bootstrap, and stable mobile entry point. Crosslink separately owns desktop endpoint discovery, current LAN-address resolution, secure transport selection, optional relay or signaling, reconnect after a desktop restart, pairing/trust state, and device authorization. Installing the app does not pin it to the desktop IP that happened to exist during installation. The paired record keeps the desktop’s cryptographic identity and route hints; when signaling is configured, live presence replaces stale LAN/relay hints with the desktop’s current endpoint.

Three browser rules

A service worker needs a secure context. No worker means no cached shell, which means an offline launch shows the browser’s own cannot connect to server page instead of Crosslink’s reconnect screen. Secure means https://, or localhost / 127.0.0.1. crypto.subtle needs a secure context too. On an insecure origin Crosslink cannot wrap this device’s identity in a non-extractable key and falls back to storing it in the clear. An https:// page cannot open a ws:// socket. Browsers block it as mixed content. So the origin that can cache an offline shell is exactly the origin that cannot take the LAN shortcut. That last one is the awkward part, and Crosslink does not paper over it: a blocked route is reported as blocked rather than retried until it times out.

The two delivery modes

LAN HTTP

LAN HTTP is for local development and direct browser access while the phone can reach the desktop’s LAN address. It provides direct LAN communication, pairing, trust, and development without publishing a site. It is not a secure-context installation origin: there is no Service Worker, durable offline PWA, stable HTTPS identity, or reliable Add to Home Screen lifecycle.

Secure published bootstrap

A published bootstrap is the stable HTTPS application origin. It can register the Service Worker, cache the offline shell, support installation where the browser allows it, and reopen independently of today’s desktop address. It still needs a browser-permitted route to the desktop: wss:// via a secure tunnel or relay. An HTTPS page cannot generally connect directly to ws://192.168.x.x because that is mixed content. The deployment-level host diagnostic and browser runtime diagnostic answer different questions:
The host never claims that a worker registered merely because it generated sw.js; only the browser runtime can confirm registration and install support.

Capability comparison

The default LAN setup is fine for development and for a companion app used while the desktop is running. It is not enough for an installed app that must open when the desktop is off. Your host tells you which row you are in:

Getting a durable origin for free

Publish Crosslink’s bootstrap as a static site. It can use ordinary static hosting and needs no application server. The host must be reachable for the first load and updates; after the Service Worker has successfully cached the shell, an installed launch can render that shell while the host is offline.
That writes the following files, all with relative links so a GitHub Pages project site (https://you.github.io/your-app/) works unchanged: The output is a self-contained static application shell. It embeds no desktop endpoint, signaling/relay token, pairing code, device private key, trusted-device record, or application RPC data. Publishing a new build is how bootstrap asset updates are delivered. The writer creates/updates the listed files but does not delete unrelated files already in the output directory. It rejects a requested asset whose basename would overwrite a generated file (for example sw.js) or collide with another requested asset. Publish it once, then point your host at it:
Every QR your host mints now leads to that address. The published origin owns the install, the service worker and the cache; the desktop’s address is something the shell resolves at connect time, from the pairing payload and the trusted device identity stored on the phone. Your laptop can change networks, change IP, or be off for a week — the installed app still opens, still shows Crosslink’s reconnect screen, and still reconnects without a new pairing code.

The catch, stated plainly

A published https origin cannot use ws://, so it cannot take the direct LAN route. For those installs the host needs a wss:// route:
  • a relay (relayUrl) — Crosslink ships one you can self-host, and it only ever sees ciphertext; or
  • a tunnel (tunnelUrl) you already run.
Without one, describeMobileDelivery() says so:

Choosing

  • Building or demoing? Use the defaults. Open the mobile page on 127.0.0.1 in a same-machine browser for a quick secure-context check; use a real HTTPS origin for phone installation testing.
  • Companion app used while the desktop is running, same Wi-Fi? The LAN origin is fine. Expect a bookmark rather than an install, and say so to users.
  • Installed app that must open when the desktop is off? Publish the static bootstrap and configure a relay. This is the configuration the promise at the top of this page describes.

Static-host traffic and trust boundary

The first pairing payload is carried in the URL fragment (#pair=…), which browsers do not send in the HTTP request. The static host receives normal asset requests and their usual metadata (for example IP address, user agent, referrer policy output, and requested paths). During the iOS install handoff it may also receive an opaque, short-lived crosslink_install id and credential-free public target metadata in same-origin cookies/requests. It does not receive the nine-digit pairing code, trusted-device private keys, or plaintext RPC frames. RPC travels from the installed app to the selected desktop transport. A relay, when used, forwards Crosslink ciphertext; the static asset host is not on that path unless the operator deliberately deploys another service at the same domain. The static host is not the authority that approves devices and it cannot forge the desktop’s pinned cryptographic identity. It is part of the mobile code supply chain: whoever can replace crosslink-sdk.js, crosslink-boot.js, or the Service Worker can run code under the installed application’s origin and act as that mobile client while the code runs. Host the files somewhere whose content integrity and account access you trust. Non-extractable browser keys protect stored ciphertext from copy-and-leave theft; they do not protect against malicious same-origin JavaScript using the key in place. No Crosslink-operated service is mandatory. The generated directory works on ordinary static hosting. Runtime dependencies are selected by deployment: A secure published bootstrap without a WSS route is installable in origin terms but unable to reach the desktop. A deployment without signaling may reconnect through stored stable routes, but it must not be described as dynamically discovering a changed desktop endpoint.