# 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

```bash
pnpm add openrtc
```

## Fast prototype

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

```ts
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.

```ts
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:

```ts
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:

```ts
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.

## Optional trust adapters

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

```ts
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

```ts
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

```ts
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. Reviewed rooms may request up
to 99 peers with `advancedFanout: true`; OpenRTC then maintains a symmetric
four-neighbor overlay and forwards named-channel messages with bounded hops and
duplicate suppression. `state()` is intentionally unavailable in sparse rooms;
use a named channel with application-owned latest-state revisions.
