OpenPrinterOpenPrinter

Server SDK

Framework-neutral discovery, pairing, authentication, and sessions.

@openprinter/server fits inside an existing application. It validates discovery and pairing, verifies the initial Ed25519 challenge, and runs each protocol session. The application owns HTTP/WebSocket routing, durable stores, rate limiting, live-session routing, cluster coordination, and durable jobs.

const server = createOpenPrinterServer({
  brand: { name: 'Acme Print Service' },
  serverId: 'acme-print-v1',
  serverVersion: '1.0.0',
  pairingCodeStore,
  credentialStore,
  checkPairingRateLimit: ({ remoteAddress }) =>
    limiter.check(remoteAddress),
  onAgentConnected: ({ agent, session }) =>
    sessions.register(agent.agentId, session),
  onJobReceived: ({ agent, message }) =>
    jobs.markReceived(agent.agentId, message.payload.jobId),
});

Discovery and pairing routes

app.get(server.paths.discovery, async (_request, response) => {
  response.json(await server.discover());
});

app.post(server.paths.pairing, async (request, response) => {
  response.json(
    await server.pair(request.body, { remoteAddress: request.ip }),
  );
});

createPairingCode creates a secure, short-lived, single-use grant. Protect code creation with application authorization. Use the in-memory stores only for tests and local examples.

Gateway

wss.handleUpgrade(request, socket, head, (webSocket) => {
  server.handleGatewayConnection(webSocket);
});

The convenience adapter is structural rather than tied to ws. Custom transports call server.accept({ transport }), forward frames to session.receive, and report closure through session.transportClosed.

accept() starts unauthenticated. The SDK sends and verifies the initial challenge before accepting agent.hello. The authenticated identity comes from the stored public credential, not from host-supplied socket metadata.

Delivery ownership

Store application jobs durably before selecting a live session and calling sendJob. If no worker owns the session, keep the job queued. Two sessions for one agent are independent; host code or a backplane decides affinity, replacement, and distributed delivery.

agent.job_received confirms local durable receipt. agent.job_submitted confirms backend acceptance, not universal physical completion.

On this page