npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

openrtc

v2.9.7

Published

A Rust-first realtime runtime for browser and native apps, exposed through TypeScript and WASM.

Readme

OpenRTC

OpenRTC is a provider-neutral peer coordination and transport SDK for browsers, desktop applications, and mobile applications. The 2.0 API is capability based: constructing a client is local and side-effect free, and network work starts only when the application activates a device, space, room, or ticket handle.

The core is message-agnostic. Named channels are the application protocol extension boundary; they do not replace the incoming router or authorize a parallel lifecycle owner.

Install

pnpm add openrtc

Fast prototype

Only a public API key is required for a bounded ephemeral space:

import { OpenRTC } from 'openrtc';

const rtc = OpenRTC({ apiKey: import.meta.env.VITE_OPENRTC_API_KEY });
const cursors = await rtc.spaces.join('portfolio-cursors', {
  payload: 'latest-state',
});

const stop = cursors.peers.watch((peers) => console.log(peers));

// Later
stop();
await cursors.leave();
await rtc.close();

The constructor performs no network request, storage access, authentication, attestation, timer creation, WASM load, or transport startup.

Authenticated devices

Your application owns login and supplies a short-lived assertion from its registered OIDC/JWKS provider. OpenRTC validates it, binds the installation's device key, and issues scoped gateway grants.

const auth = {
  getAssertion: ({ forceRefresh }: { forceRefresh: boolean }) =>
    fetch('/api/openrtc/assertion', {
      method: 'POST',
      headers: { 'X-Force-Refresh': String(forceRefresh) },
    }).then((response) => response.text()),
};

const rtc = OpenRTC({
  apiKey: import.meta.env.VITE_OPENRTC_API_KEY,
  auth,
});

const devices = await rtc.devices.start({
  auth,
  autoConnect: 'online',
  // Optional. Browser clients infer labels such as "Chrome on Android";
  // native clients should pass the host-reported OS and user-visible name.
  profile: { name: "Bryant's iPhone", platform: 'ios' },
});

profile is presentation metadata, not device identity. OpenRTC continues to bind enrollment and limits to its stable installation ID and device key. A changed profile updates the existing enrollment once; it does not enroll or bill a second device. User-assigned hardware names are not exposed by every browser or mobile OS, so privacy-safe defaults identify the available environment (for example iPhone, Chrome on iPhone, or Chrome on Android).

Consumer apps own login UX, product authorization, IdP registration, and platform-attestation setup. OpenRTC owns assertion validation, device binding, grant issuance and refresh, revocation, budgets, metering, and abuse controls.

Rooms

Rooms default to ephemeral membership:

const room = await rtc.rooms.join('match-123');

Durable membership is an advanced capability and must be enabled in the developer portal and requested by the application:

const team = await rtc.rooms.join('team', {
  access: 'authenticated',
  membership: 'durable',
  auth,
});

Every long-lived capability has close(); spaces and rooms also have leave(). One handle activates exactly one avenue.

Await close() before reusing an avenue. If it rejects, retry close() on the same handle or root client; OpenRTC retains that exact owner until cleanup succeeds. Concurrent calls share the pending cleanup. A handle's closed flag denies product access but does not prove resource retirement; a failed release cannot be replaced by another activation of the same avenue. Consumers should surface the cleanup failure while preserving their local documents, without adding connection recovery or cleanup timers.

Developer service nodes

openrtc/service-node exposes a preview WebSocket-server-like application surface for browser/WASM. Browser code supplies a callback that obtains a short-lived authority grant from the developer backend; it must never receive a developer sk_* key. Trusted Node/server code can mint that grant with openrtc/server.

import { ServiceNode } from 'openrtc/service-node';

const service = new ServiceNode({
  client: rtc,
  apiKey,
  serviceId: 'world-simulation',
  generation: 1,
  shardIds: ['zone-a'],
  deviceId: await rtc.devices.localId(),
  deviceSigner,
  assignmentSigner,
  grantIssuer: (request) => fetch('/api/openrtc/authority-grant', {
    method: 'POST',
    body: JSON.stringify(request),
  }).then((response) => response.json()),
});

service.onConnection(({ connection }) => console.log(connection.id));
await service.listen('world-42');

Trusted Node.js authorities use the dedicated Rust host through openrtc/node; no application IPC bridge or native addon is required:

import { ServiceNode } from 'openrtc/node';

const service = new ServiceNode({
  apiKey: process.env.OPENRTC_API_KEY!,
  secretKey: process.env.OPENRTC_SECRET_KEY!,
  serviceId: 'world-simulation',
  generation: 1,
  shardIds: ['zone-a'],
  deviceId: 'world-simulation-1',
  stateDirectory: '/var/lib/world-simulation/openrtc', // private and persistent
});

service.onConnection(({ connection }) => {
  connection.onMessage((message) => console.log(connection.peerId, message));
});
await service.listen('world-42');

maxPeers counts everyone in the room, including the service. Node defaults to eight; set a value from 1 through 50 explicitly when needed. Your app's approved limit and available credits still apply, so a larger request can be denied. Browser and standalone Rust grant requests may omit the value to use app policy. These limits are not a promise of 50 simultaneous server connections or a replacement for the remaining authority acceptance tests.

The released package must contain the signed native artifact for the current process.platform/process.arch; a missing artifact fails closed. A reviewed source checkout can instead opt into buildFromSource: true with an absolute sourceRoot. The developer secret is sent only over the child's stdin, and the Node layer only demultiplexes bounded application messages and authority assignments. The Rust peer-session actor remains the sole lifecycle owner. connection.peerId is the admitted remote endpoint ID, not an app account or durable device ID. Room access alone does not grant those identities. Keep stateDirectory across process restarts to retain the host's private keys. Await service.close() before reusing that directory; it waits for the host process to finish, not just acknowledge shutdown. JSON and string messages use the same room-scoped envelope as browser Connection.send; onMessage receives the application value, not the envelope. Messages explicitly addressed to another room are discarded. Node byte payloads (Uint8Array/ArrayBuffer) retain the lower-level native byte contract; use JSON values for portable browser/Node application messages. This preview Node facade intentionally owns one room and exposes only message-oriented send/onMessage/onClose plus assignment publication; it does not yet claim the browser facade's streams, media, or full Room namespaces. Browser resolution of openrtc/node fails closed; browser code uses WASM plus delegated grants through openrtc/service-node. Native Rust uses ControlPlane::join_authority_room directly and does not require JavaScript. Its RoomAuthority::compose_client(builder) method binds the dedicated service-room client to the existing Rust peer-session owner before endpoint initialization. Native hosts publish a room-scoped compound ticket; attaching the signaling backend alone is not service startup.

The authority surface remains preview-only until signed prebuilt artifacts and source-bound browser, Node, and native Rust authority E2E lanes pass.

Optional trust adapters

Attestation is evaluated at device enrollment, key rotation, recovery, or risk escalation—not as a connection heartbeat.

import { AppCheckProvider } from 'openrtc-trust-firebase-app-check';

const rtc = OpenRTC({
  apiKey,
  auth,
  trust: {
    attestation: new AppCheckProvider({ appCheck }),
  },
});

Managed Firebase App Check, Apple App Attest, and Play Integrity are optional packages. A consumer backend may instead validate platform evidence and issue the registered OIDC assertion; that is the canonical production integration.

Usage controls

const rtc = OpenRTC({
  apiKey,
  usage: {
    warnAtSessionCreditsUsd: 0.25,
    maxSessionCreditsUsd: 0.50,
    onUsage: console.log,
    onWarning: console.warn,
  },
});

rtc.usage.estimate({ operation: 'coordination.connection.open', units: 3 });

The client ceiling is an additional local guard. Server budgets and developer manifest limits remain authoritative.

Native and WASM

import { OpenRTC } from 'openrtc';
import { createBridge } from 'openrtc-tauri/ipc';

const rtc = OpenRTC.native({ apiKey, auth }, createBridge());

Native hosts keep one Ed25519 installation key in host secure storage. Browsers use a non-extractable WebCrypto key in IndexedDB when supported. The Rust and WASM clients use the same provider-neutral avenue-grant contract as TypeScript.

Low-level runtime access is intentionally isolated in openrtc/runtime for transport authors, migration harnesses, and diagnostics. Test endpoint overrides live only in openrtc/testing.

Extensions

  • y-openrtc adapts one activated room to Yjs.
  • openrtc-netcode owns matches, replicated state, latest-state transforms, chat/control, and optional voice helpers.
  • openrtc-file-transfer owns bounded, acknowledged, resumable file transfer.

Relay, durable membership, managed attestation, MoQ, and BLE require both portal enablement and explicit runtime opt-in. Avenues default to 8 peers. Reviewed non-room avenues may request 9-12 peers. Rooms default to architecture: 'auto' and payload: 'reliable'. Small rooms use a direct mesh. A room may declare payload: 'latest-state' so auto can use the reviewed four-neighbor sparse graph through 50 members. Reliable rooms above the safe mesh limit require the explicitly enabled, content-blind managed path. advancedFanout: true is a one-release alias for fixed sparse latest-state delivery. state() remains unavailable in sparse rooms; use a named channel with application revisions.

Passive connection metrics

const stop = ticket.diagnostics.watchConnectionMetrics((connections) => {
  for (const connection of connections) {
    console.log(connection.latencyMs, connection.activeTransport, connection.carrier);
  }
});
// Stop observing when the view closes. Ticket peers remain SDK-owned.
stop();

OpenRTC automatically samples local Rust/WASM connection diagnostics every two seconds while observed. All observers of a runtime share one sampler. It sends no pings and makes no coordination requests. Ticket observers see only that ticket's scope. Iroh supplies passive selected-path RTT and the observed carrier or path (webrtc, moq, iroh-quic, iroh-lan, iroh-relay, or ble). A missing, unadmitted, replaced, or retired path has null measurements. Samples carry the existing transport stable ID, transport/route generations, and metricsSampledAtMs. These read-only diagnostics do not determine readiness.