Skip to main content

Host SDK (@crosslink/sdk-node)

createCrosslinkServer(options)

Creates a new Crosslink host server.

Options

CapabilityDef

pairing

PairingApproval is boolean | string[] | { approved: boolean; caps?: string[] }true grants everything requested, false refuses, and an array or { caps } grants only that subset.

PairingApprovalRequest

Server methods

server.start()

Start listening for connections.

server.stop()

Gracefully shut down.

server.getPairingCode()

Generate a new pairing code.

server.expose(method, handler, options?)

Register an RPC method.
options also accepts inputSchema (a MiniSchema), validate (a custom validator function), idempotent (safe to auto-retry across reconnects), and timeoutMs. capability may be an array, in which case all of them are required. The handler receives (input, ctx):
See Capabilities and RPC for how to use them.

server.declareEvent(name, options?)

Declare an event that can be emitted. options.capability gates who may subscribe.

server.emit(name, payload)

Send an event to all connected clients.

server.listDevices()

List all paired devices (not only currently connected ones).

server.revokeDevice(deviceId)

Revoke a paired device’s access and close any of its active sessions. Returns true if the device existed.

server.revokeAllDevices()

Revoke every currently-paired device.

server.setDeviceCaps(deviceId, caps)

Replace a paired device’s granted capability set.

server.getConnectivity()

A moment-in-time snapshot of how reachable the host is, with a human-readable message.

server.connectionEndpoints(mode?)

Every route a client could use to reach the host right now, in the order a client should try them. This is the single source of truth for pairing endpoints — it never invents a route, and a wan entry appears only when one genuinely exists.
kind is one of "lan" | "wan" | "sig" | "relay" | "tunnel". Passing mode filters to what that network mode allows, without changing the host’s own mode.

await server.setNetworkMode(mode)

Changes the running host’s actual transport policy. Switching to "local-only" releases router mappings, disconnects signaling and relay services, and reconnects active devices through a permitted local route. Switching back to a remote-capable mode starts its configured transports again. Use this method for settings UI; changing server.config.networkMode directly does not apply live transport lifecycle changes.

server.enableRemoteAccess()

Opens remote access now, if it is not already open. setNetworkMode("remote") calls it for you; call it directly when a diagnostics UI needs to probe the router without changing the host’s selected mode. Requires the LAN listener to be bound to all interfaces (lan: { bind: "all" }).

server.status()

A structured snapshot for a diagnostics panel: application info, device id and fingerprint, active transports, network mode, device count, secret-store backend, and the permission policy in force.

server.getRemoteDiagnostics()

What the router said about the last port-mapping attempt (protocol tried, public address, CGNAT detection), or null if remote access was never attempted.

server.getInstallHandoff(handoffId)

Resolves a device-link handoff minted over RPC, used by the iOS “Add to Home Screen” flow so an installed PWA inherits trust from the browser tab that paired it. Returns null once it is used or expired.

Server events

Subscribe with server.typedOn(event, callback) or the inherited EventEmitter#on.

Client SDK (@crosslink/sdk-browser)

createSecureCrosslinkClient(options?)

The preferred browser entry point. Identity and paired-app records are encrypted at rest under a non-extractable WebCrypto key.
Accepts allowPlaintextFallback to permit unencrypted storage when WebCrypto/IndexedDB is unavailable.

createCrosslinkClient(options?)

Synchronous factory that defaults storage to localStorage. Use it when you supply your own storage, or need a client without awaiting.

CrosslinkClient.create(options?)

Factory method to create a client whose identity is encrypted at rest with WebCrypto/IndexedDB when available.
create() additionally accepts allowPlaintextFallback (boolean), which lets it fall back to unencrypted storage when WebCrypto/IndexedDB is unavailable.

new CrosslinkClient(options?)

Synchronous constructor for embedders that supply their own storage. Without an explicit storage, identity lives only in memory for the process lifetime — prefer CrosslinkClient.create() in browsers.

Options

Client methods

client.pairFromQr(text, requestedCaps?, codeOverride?)

Applications rarely call this. CrosslinkMobileBootstrap (and therefore mobile.entry) runs pairing, including SAS confirmation, the install handoff and the error states. Call it directly only when you are driving a client with no Crosslink UI — see Custom UI.
Pair with a host via a scanned QR/URI (or a hosted bootstrap link).

client.connect(appId?)

Connect to a previously paired host. Without appId, connects to the first paired app on record. Returns the RpcClient once connected.

client.close()

Close the current connection.

client.forget(appId)

Remove a paired host.

client.listApps()

List all paired hosts.

client.onStateChange(listener)

Subscribe to connection-state changes; returns an unsubscribe function. This is the only way the client reports connection lifecycle — it is not an event emitter.
state is one of: "offline", "discovering", "pairing", "connecting", "direct", "turn-relayed", "crosslink-relayed", "reconnecting", "unauthorized", "revoked", "protocol-incompatible".

RPC interface

rpc.call(method, args?, options?)

Call an RPC method. options.timeoutMs overrides the default request timeout.

rpc.subscribe(event, callback)

Subscribe to an event. Returns an unsubscribe function.

rpc.cancel(requestId)

Cancel an in-flight request by id. RpcClient has no built-in “connected” / “disconnected” / “error” events, and no hasCapability method — capability enforcement happens on the host, and a denied call rejects with a capability_denied error (see Error Codes).
These are the APIs behind the pairing, bootstrap, install, offline and revoked experiences. You mount them; Crosslink implements them.

createPairingCard(options)

@crosslink/sdk-browser — the canonical desktop pairing UI.
The card reads the application’s identity and palette from the host’s application block, delivered with every pairing session. The options below are overrides, applied field by field over what the host reported. Methods: refresh(), setNetworkMode(mode), getNetworkMode(), update(state), setBrand(theme), getBrand(), attachSource(source), destroy(). @crosslink/react exports CrosslinkPairingCard, a thin mount around the same implementation.

CrosslinkMobileBootstrap

@crosslink/sdk-browser — the mobile lifecycle: first pair, code entry, SAS confirmation, Add to Home Screen, Continue in Browser, install handoff, offline, reconnecting, revoked, and the handoff into your app. Constructed for you when the host has mobile.entry. Construct it directly only when serving the page from outside a Crosslink host. Methods: start(), getState(), getClient(), getEnvironment(), destroy().

describeBootstrapEnvironment()

@crosslink/sdk-browser — what the current origin permits: secureContext, serviceWorkerAvailable, webCryptoAvailable, installable, insecureTransportBlocked, and a limitations[] of plain sentences.

filterEndpointsForOrigin(endpoints, pageOrigin)

@crosslink/core — splits advertised endpoints into usable and blocked for a given page origin. An https page cannot open ws://; this is what reports that as a blocked route with a reason rather than a connection timeout.

Host: serving the mobile experience

host.createBootstrapHandler()

An HTTP handler serving your mobile.entry plus the manifest, service worker, icons, browser SDK and install handoff. Mounted automatically on the transport port when mobile.entry is set; call it to mount on a server you already run. mobile.attribution configures the Crosslink attribution footer the bootstrap mounts on the authorized mobile app shell — color, background, size, offset, className. It participates in normal layout flow and there is no field that removes it. See Mobile bootstrap.

host.createControlHandler(options?)

The loopback-only system endpoints the pairing card consumes: /pairing, /network-mode, /devices, /revoke, /events, /widget.js. Refuses non-loopback peers itself. /pairing answers with the session and the host’s application block, which is how the card renders your name, icon and colours without the page repeating them. options.fallback handles everything outside the Crosslink base path, so your own routes sit behind it.

host.describeMobileDelivery()

A deployment-level diagnostic with mode, directLanTransport, secureWssTransport, dynamicEndpointDiscovery, bootstrapAssetsConfigured, durableOrigin, and the origin capabilities serviceWorkerOriginEligible, offlineShell, installable, and encryptedDeviceIdentity. Its multiline message labels LAN HTTP versus a secure published bootstrap. These are configuration/origin capabilities, not a claim about a particular phone; use describeBootstrapEnvironment() in the page for actual browser runtime support. See Durable Origins.

host.writeStaticBootstrap(outDir, overrides?)

Writes Crosslink’s bootstrap as a static site — index.html, crosslink-sdk.js, crosslink-boot.js, sw.js, manifest.webmanifest, crosslink-mark.svg, two generated PNG icons, .nojekyll, and any explicitly requested assets. All framework links are relative, so ordinary static hosting and GitHub Pages project sites work. It embeds public application branding/capability metadata, not host endpoints, relay credentials, pairing codes, device keys, or trusted device records. See Durable Origins for its traffic, trust, and optional-service boundaries.

host.bootstrapOrigin()

The origin a phone should load the bootstrap from: the configured pairing.bootstrapUrl when set, otherwise the best route this host advertises.