> ## 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.

# Node.js Integration

> Using Crosslink in Node.js applications

## Setup

```bash theme={null}
npm install @crosslink/sdk-node
```

## Basic usage

```js theme={null}
import { createCrosslinkServer } from "@crosslink/sdk-node";

const server = createCrosslinkServer({
  application: { id: "com.example.myapp", name: "My App", version: "1.0.0" },
  capabilities: [
    { id: "app.control", title: "Control the app", risk: "medium" }
  ],
  signalingUrl: "http://127.0.0.1:8081",
  relayUrl: "http://127.0.0.1:8082",
  lan: { bind: "loopback" }
});

server.expose("app.status", () => ({ ok: true }));
await server.start();
```

## Exposing methods

### Basic method

```js theme={null}
server.expose("math.add", (args) => {
  return { result: args.a + args.b };
});
```

### With capability

```js theme={null}
server.expose("admin.shutdown", () => {
  process.exit(0);
}, { capability: "admin" });
```

### Async method

```js theme={null}
server.expose("db.query", async (args) => {
  const result = await db.query(args.sql);
  return { rows: result.rows };
});
```

### Streaming progress

A handler can emit progress chunks before returning its final result, via `emitProgress` on the RPC context:

```js theme={null}
server.expose("file.read", async (args, { emitProgress }) => {
  const chunks = [];
  for await (const chunk of fs.createReadStream(args.path)) {
    emitProgress({ bytes: chunk.length });
    chunks.push(chunk);
  }
  return { content: Buffer.concat(chunks).toString("base64") };
}, { capability: "file.read" });
```

## Emitting events

```js theme={null}
// Declare events
server.declareEvent("app.tick");
server.declareEvent("app.update");

// Emit events
setInterval(() => {
  server.emit("app.tick", { t: Date.now() });
}, 1000);
```

## Managing clients

```js theme={null}
// List paired devices
const devices = server.listDevices();
console.log(`${devices.length} devices paired`);

// Revoke a specific device (kills its active session, blocks reconnection)
server.revokeDevice(deviceId);

// Revoke every paired device
server.revokeAllDevices();
```

## Identity persistence

The host identity is persisted automatically under `storageDir` (default `./.crosslink-data/<appId>`), in the strongest available secret store (OS keychain, Electron `safeStorage`, or an encrypted file), plus a paired-devices record. Pass `storageDir` in `createCrosslinkServer()` to override the location.

## Environment variables

| Variable                  | Default  | Description                                            |
| ------------------------- | -------- | ------------------------------------------------------ |
| `CROSSLINK_SIGNALING_URL` | *(none)* | Signaling server URL, if not passed via `signalingUrl` |
| `CROSSLINK_RELAY_URL`     | *(none)* | Relay server URL, if not passed via `relayUrl`         |
| `CROSSLINK_SECRET_KEY`    | *(none)* | Passphrase for encrypted-file secret storage           |

## Graceful shutdown

```js theme={null}
process.on("SIGTERM", async () => {
  await server.stop();
  process.exit(0);
});
```

## Handling server events

`CrosslinkServer` extends Node's `EventEmitter`. Use `typedOn()` for the typed events it emits:

```js theme={null}
server.typedOn("devicePaired", (record) => {
  console.log("Paired:", record.deviceId);
});

server.typedOn("deviceConnected", ({ deviceId, transport }) => {
  console.log(`${deviceId} connected via ${transport}`);
});

server.typedOn("deviceDisconnected", ({ deviceId, transport }) => {
  console.log(`${deviceId} disconnected`);
});

server.typedOn("deviceRevoked", (deviceId) => {
  console.log(`${deviceId} revoked`);
});
```

## TypeScript

Both SDKs ship with TypeScript declarations:

```ts theme={null}
import type { CreateCrosslinkServerOptions } from "@crosslink/sdk-node";

const options: CreateCrosslinkServerOptions = {
  application: { id: "com.example.myapp", name: "My App", version: "1.0.0" },
  capabilities: [
    { id: "app.control", title: "Control the app", risk: "medium" }
  ]
};
```
