> ## Documentation Index
> Fetch the complete documentation index at: https://crosslink.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Reconnection

> Automatic reconnection with backoff

Crosslink automatically reconnects when a connection is lost. On a mobile
client this is presented by Crosslink's own reconnect screen — you do not build
one, and `crosslink.onConnected(rpc)` fires again on the other side of it.

Reconnection reuses the device's existing trust. Closing the desktop app,
sleeping or restarting the machine, changing Wi-Fi or moving the phone between
networks all reconnect silently. A new pairing code is required only when the
trust relationship itself is invalid — revoked, removed, or unreadable.

<Note>
  A route the browser refuses is not retried as if the host were down. A page
  served over `https` cannot open a `ws://` socket, so those endpoints are
  reported as blocked with the reason, rather than burning a dial timeout on each
  attempt. See [Durable Origins](/client/durable-origin).
</Note>

## How reconnection works

```text theme={null}
1. Transport lost detected (WebSocket close / timeout)
2. Client enters "reconnecting" state
3. Backoff timer: 500ms -> 1s -> 2s -> 4s -> 8s -> 16s -> 30s (capped), +/-30% jitter
4. Fresh CLX1 handshake with new ephemeral keys
5. Subscriptions restored
6. Queued idempotent calls replayed
7. Client returns to a connected state (direct / turn-relayed / crosslink-relayed)
```

## Backoff strategy

Delay is `min(30s, 500ms * 2^attempt)`, with random jitter of +/-30% applied on top:

```text theme={null}
Attempt 1: ~500ms
Attempt 2: ~1 second
Attempt 3: ~2 seconds
Attempt 4: ~4 seconds
Attempt 5: ~8 seconds
Attempt 6: ~16 seconds
Attempt 7+: ~30 seconds (capped)
```

The backoff resets after a successful connection.

## Reconnection is a new session

Each reconnection performs a fresh CLX1 handshake with new ephemeral keys:

* Forward secrecy is maintained per-session
* No bearer tokens are reused
* Revocation is re-checked on every handshake
* Device identity is verified against stored records

## Offline call queueing

Idempotent methods are queued during disconnection:

```ts theme={null}
// This will be queued and replayed after reconnection
const result = await rpc.call("data.get", null, { idempotent: true });
```

Non-idempotent methods fail immediately:

```ts theme={null}
// This fails with "not_connected" during disconnection
await rpc.call("data.create", { title: "New" });
```

## Subscription restoration

Event subscriptions are automatically restored after reconnection:

```ts theme={null}
// Subscribe once -- survives reconnections
rpc.subscribe("data.changed", (payload) => {
  console.log("Update:", payload);
});
```

## Transport resilience

The client adapts to network changes:

```text theme={null}
Phone moves from WiFi to cellular:
  LAN connection lost
  -> Client falls back to relay
  -> Session re-established
  -> RPC continues
```

```text theme={null}
Desktop goes to sleep:
  All connections lost
  -> Clients enter reconnect state
  -> Desktop wakes up
  -> Clients reconnect automatically
```

## Monitoring reconnection

```ts theme={null}
const client = createCrosslinkClient({
  onStateChange: (state, detail) => {
    if (state === "reconnecting") {
      console.log("Reconnecting...", detail);
      // detail may include attempt count, backoff delay
    }
    if (state === "direct" || state === "crosslink-relayed" || state === "turn-relayed") {
      console.log("Reconnected!");
    }
  }
});
```

## Relay channel recovery

If a relay channel expires (24h lifetime), the host:

1. Allocates a new channel
2. Publishes the new channel ID through signaling
3. Clients discover the new channel via presence lookup
4. Reconnection proceeds normally

This is transparent to the application.
