interface CoordinationGatewayOptions { /** * Provider-neutral coordination gateway endpoint. * Credentials are delivered in the first WebSocket frame, never this URL. */ endpoint: string; /** * Provider-neutral HTTPS callable origin for credential admission. * * Managed production uses https://api.openrtc.app. Internal staging and * local lanes must provide their owned API/emulator origin explicitly so * the SDK never falls back to a public cloudfunctions.net hostname. */ controlPlaneEndpoint?: string; /** * Fail-closed lane marker. The credential issuer and Worker independently * verify the matching identity tenant and token audience. */ environment: 'staging' | 'production'; /** * Opaque native trust token loader. * * A trusted developer backend obtains this short-lived token after applying * its platform attestation or signed-distribution policy. The distributed * client must never contain the developer server secret. OpenRTC treats the * token as provider-neutral evidence and sends it only in a request header. */ nativeTrustTokenProvider?: (forceRefresh?: boolean) => Promise<{ token: string; expiresAtMs: number; } | null | undefined>; } interface CoordinationGatewayGrantRequest { avenue: { kind: 'user' | 'space' | 'room' | 'session'; id: string; }; deviceId: string; runtimeInstanceId: string; ticketFingerprint: string; purpose: 'presence' | 'device-control'; refreshGrant?: string; } interface CoordinationGatewayGrant { protocolVersion: 1 | 2; gatewayUrl: string; routeKey: string; token: string; expiresAtMs: number; avenue: CoordinationGatewayGrantRequest['avenue']; } interface AppLimits { /** Max devices per authenticated user. -1 = unlimited. */ devicesPerUser: number; /** Max total rooms the developer app can have open at once. -1 = unlimited. */ maxRooms: number; /** Max members per room. -1 = unlimited. */ maxMembersPerRoom: number; /** Max active devices across all provisioned spaces for this app. -1 = unlimited. */ maxPersonalDevices: number; } interface PlutoRTCConfiguration extends RTCConfiguration { /** Keep the legacy route, carry Iroh packets, or try the carrier before fallback. */ implementation?: TransportImpl; iceServers?: RTCIceServer[]; /** * Populate public STUN defaults when no ICE servers are configured. * Set to false with `iceServers: []` for an intentional host-only lane. */ useDefaultIceServers?: boolean; privacyMode?: boolean; useTurn?: boolean; /** Prefer host/srflx ICE candidates and upgrade aggressively on LAN. */ lanMode?: boolean; } interface LocalPeerSnapshot { nodeId: string; discoveredAtMs: number; localReachable: boolean; } interface PlutoMoQServerCertificateHash { algorithm: 'sha-256'; value: ArrayBuffer; } interface PlutoMoQConfiguration { implementation?: TransportImpl; relayUrl?: string; /** Relay JWT appended only at WebTransport connection time. Never include it in relayUrl. */ accessToken?: string; /** Explicit certificate pins for self-signed development relays. */ serverCertificateHashes?: PlutoMoQServerCertificateHash[]; } interface PlutoIrohConfiguration { relayOnly?: boolean; relayTransportPolicy?: 'auto' | 'quicRequired' | 'websocketRequired'; /** Local test harness only. Must be an HTTPS loopback Iroh relay. */ testRelayUrl?: string; persistenceMode?: 'persistent' | 'ephemeral'; /** Enable native iroh mDNS LAN discovery (native runtimes only). */ localDiscovery?: boolean; /** When false, listen for LAN peers without advertising this endpoint. */ localDiscoveryMode?: 'active' | 'passive'; } interface PlutoBleConfiguration { /** Enable native BLE discovery + transport (native runtimes only). */ enabled?: boolean; /** Timeout for a single BLE connection attempt. */ connectTimeoutMs?: number; } interface TurnCredentials { iceServers?: RTCIceServer[]; } type TurnCredentialsProvider = () => Promise; type DiscoveryMode = 'space' | 'user-scoped'; type AuthMode = 'external' | 'anonymous' | 'required'; type SignalingMode = 'hosted' | 'ticket-only'; type RoomCreationMode = 'client-open' | 'client-auth' | 'server-only'; type EndpointKind = 'device' | 'ephemeral'; type PeerLifecycleStatus = 'disconnected' | 'connecting' | 'connected' | 'failed' | 'closed'; type PeerHealth = 'unknown' | 'healthy' | 'suspect' | 'stale'; type DevicePresenceStatus = 'online' | 'idle' | 'offline'; type PeerLifecycleStage = 'discovered' | 'dialing' | 'base-connected' | 'admitted' | 'protocol-ready' | 'upgrading' | 'webrtc-ready' | 'degraded' | 'reconnecting' | 'closed'; type PeerBaseTransportState = 'unknown' | 'connecting' | 'connected' | 'degraded' | 'closed'; type PeerAdmissionState = 'unknown' | 'pending' | 'admitted' | 'rejected' | 'expired'; type PeerWebRtcState = 'unknown' | 'disabled' | 'connecting' | 'transport-open' | 'route-probing' | 'ready' | 'failed' | 'closed'; /** * Lifecycle label attached to a projected peer while the runtime tracks local ownership. * * This is not an admission grant. Use `GrantScope` for ticket/token authorization labels * such as `"share"`, `"friend"`, or `"user-device"`. */ type PeerScope = 'persistent' | 'session' | string; /** * Admission-grant label embedded in restricted compound tickets and session tokens. * * `GrantScope` controls authorization and bulk revoke behavior in the Rust admission layer. * It is distinct from `PeerScope`, which only labels local peer lifecycle ownership. */ type GrantScope = string; type ChannelOwnership = 'shared' | 'exclusive'; type ChannelPeerModel = 'device-first' | 'node-first' | 'anonymous-ticket'; type ChannelRoutingMode = 'stream-envelope' | 'session-default'; type ChannelReadinessMode = 'settled-peer' | 'transport-only'; type ChannelPromotionPolicy = 'eligible' | 'never'; type ChannelSignalingPolicy = 'hosted' | 'ticket-only'; interface ChannelDescriptor { id: string; kind: 'system' | 'auth' | 'share' | 'transfer' | 'sync' | string; ownership: ChannelOwnership; peerModel: ChannelPeerModel; routing: ChannelRoutingMode; readiness: ChannelReadinessMode; promotionPolicy?: ChannelPromotionPolicy; signalingPolicy?: ChannelSignalingPolicy; /** Drop queued superseded payloads while retaining reliable delivery for the newest state. */ delivery?: 'reliable' | 'latest-state'; description?: string; } type ProtocolName = 'iroh' | 'iroh-quic' | 'iroh-relay' | 'iroh-lan' | 'iroh-webrtc' | 'iroh-moq' | 'ble' | 'webrtc' | 'webrtc-lan' | 'webrtc-turn' | 'moq'; type ProtocolMaturity = 'stable' | 'experimental' | 'unsupported'; type TransportImpl = 'external' | 'iroh-carrier' | 'auto'; type TransportPrivacy = 'direct' | 'relay-only'; type RouteOptimization = 'balanced' | 'lowest-latency'; type ProtocolBaseName = 'iroh' | 'webrtc' | 'moq'; type RouteFamily = 'iroh-path' | 'iroh-physical' | 'optional-route'; type RouteKind = 'core' | 'host-installed' | 'carrier-adapter' | 'route-adapter' | 'path-label'; /** * Public compatibility type. `native` and `plugin` are legacy capability * labels; generated route descriptors use `RouteKind`. */ type ProtocolImplementationKind = RouteKind | 'native' | 'plugin'; type ProtocolLocality = 'nearby' | 'direct-internet' | 'relay' | 'mixed'; interface RouteDescriptor { id: ProtocolName; baseProtocol: ProtocolBaseName; family: RouteFamily; implementation: RouteKind; locality: ProtocolLocality; maturity: ProtocolMaturity; defaultRank: number; browser: boolean; native: boolean; independent: boolean; } interface ProtocolCapability { /** Canonical descriptor id; additive to the legacy capability shape. */ id?: ProtocolName; /** Compatibility alias for `id`. */ protocol: ProtocolName; baseProtocol: ProtocolBaseName; /** Canonical route family; additive to the legacy capability shape. */ family?: RouteFamily; implementation: ProtocolImplementationKind; locality: ProtocolLocality; maturity: ProtocolMaturity; /** Canonical descriptor rank; additive to the legacy capability shape. */ defaultRank?: number; /** Canonical implementation taxonomy; `implementation` retains legacy values. */ routeImplementation?: RouteKind; /** Compatibility alias for `defaultRank`. */ preferredRank: number; browser: boolean; native: boolean; /** Canonical descriptor flag; additive to the legacy capability shape. */ independent?: boolean; configured?: boolean; available: boolean; reason?: string; } type ProtocolCapabilityMap = Record; type SpaceTokenProvider = () => Promise<{ customToken: string; uid?: string; } | null | undefined>; interface ClientOptions { /** * API key issued from the Pluto developer dashboard (https://api.openrtc.app/developer). * The runtime derives its app namespace from this key. */ apiKey?: string; /** * Discovery avenue for this runtime. * * Set this explicitly in new apps: * - `space`: a small live shared namespace backed by scoped space tokens. * - `user-scoped`: a signed-in user's durable owned-device roster. * * Rooms and ticket/share grants are separate layers. Do not use `space` * as a generic account device list, and do not expect `user-scoped` * discovery to show other users unless a room or explicit grant connects them. */ discoveryMode?: DiscoveryMode; /** * Shared space name for `space` discovery mode. Use this for lightweight * lobbies, demos, cursors, and other live-only collaboration surfaces. * This is the beginner-facing name for `spaceKey`; if both are set, * `spaceKey` wins. */ space?: string; /** * Compatibility name for `space`. The SDK derives a namespace from * apiKey + spaceKey, then authenticates with spaceTokenProvider. * * Prefer `space` in public app code. Only used when discoveryMode is * `space`; ignored for user-scoped discovery. */ spaceKey?: string; /** * Pre-minted session token for the internal lower-level runtime. * Public OpenRTC 2.0 clients exchange an identity assertion instead. */ sessionToken?: string; authMode?: AuthMode; /** * Allows first-party demos to use hosted defaults when an API key is omitted. * Product apps should pass an API key. */ allowAnonymousHostedDefaults?: boolean; signalingMode?: SignalingMode; projectId?: string; /** * Provider-neutral coordination gateway. * * When set, hot presence, roster, and signaling operations fail closed * through the gateway; the SDK never falls back to direct Firestore/RTDB. * Hosted production clients receive the managed production default. */ coordinationGateway?: CoordinationGatewayOptions; /** * Supplies the provider-neutral platform-attestation token used by the * configured coordination control plane. * * Hosted Firebase deployments use App Check by default. Custom hosts and * deterministic harnesses can inject their own provider without coupling * gateway coordination to Firebase-specific SDK state. */ attestationTokenProvider?: (forceRefresh?: boolean) => Promise; storagePrefix?: string; deviceTTL?: number; deviceName?: string; /** * Persistence of the app-facing logical device identity. Defaults to * `persistent` in browsers so a reload updates one durable device record. */ deviceIdPersistence?: 'persistent' | 'ephemeral'; /** * Persistence of the Iroh endpoint secret/EndpointId. Browser runtimes * default to `ephemeral` so each tab/process incarnation has an unambiguous * transport identity. Native hosts may persist their endpoint explicitly. */ endpointIdPersistence?: 'persistent' | 'ephemeral'; /** * @deprecated Use `deviceIdPersistence` and `endpointIdPersistence`. * When supplied, this legacy option remains an explicit override for both. */ nodeIdPersistence?: 'persistent' | 'ephemeral'; secretKey?: string; strictMode?: boolean; /** * Selects the sole consumer of admitted native application streams. * * `runtime` (default) routes named OpenRTC channels through the TypeScript * runtime. `host` is for native products that install one process-level * Rust protocol dispatcher. A native host must never enable both consumers: * the underlying stream queue is ordered and each raw stream has one owner. */ nativeStreamConsumer?: 'runtime' | 'host'; /** * Optional application-provided TURN credential loader. * * OpenRTC never needs long-lived TURN secrets in the client. When strict/privacy * WebRTC needs relay candidates, provide a backend-backed loader that returns * short-lived RTCIceServer entries. Leave unset for the production default of * no paid TURN usage. */ turnCredentialsProvider?: TurnCredentialsProvider; localDeviceId?: string; onDevices?: (devices: Device[]) => void; onAutoConnect?: (devices: Device[]) => void; onIncomingSession?: (session: SignalingSession) => Promise; transports?: { iroh?: boolean | PlutoIrohConfiguration; webrtc?: boolean | PlutoRTCConfiguration; ble?: boolean | PlutoBleConfiguration; moq?: boolean | PlutoMoQConfiguration; /** Hard route-eligibility policy. Relay-only fails closed. */ privacy?: TransportPrivacy; /** Route objective applied after privacy and capability filtering. */ optimizeFor?: RouteOptimization; /** Exact-route fallback and tie-break order. Unsupported routes are skipped. */ priority?: ProtocolName[]; }; transportPriority?: ProtocolName[]; /** Subscribe to native LAN peer discovery events. */ onLocalPeer?: (peer: LocalPeerSnapshot) => void; disableIrohFallback?: boolean; /** * Optional application-layer payload envelope. When configured, OpenRTC * protects public message/media payload bytes before they are handed to * iroh, WebRTC, or MoQ and opens them before app callbacks fire. */ applicationCrypto?: PayloadCrypto; /** * Mints a namespace-scoped Firebase custom token for space discovery. * Required when `discoveryMode` is `space` and `space`/`spaceKey` is set. * Use `spaceToken(...)` for browser demos and simple static sites. */ spaceTokenProvider?: SpaceTokenProvider; } interface Device { deviceId: string; deviceName: string; online: boolean; ticket: string; friendTicket?: string; presenceStatus?: DevicePresenceStatus; presenceUpdatedAt?: number; presenceExpiresAt?: number; connectable?: boolean; kind?: EndpointKind | string; nodeId?: string; platformType?: string; capabilities?: { canHost?: boolean; canSync?: boolean; readOnly?: boolean; can_host?: boolean; can_sync?: boolean; read_only?: boolean; }; sessionId?: string; userId?: string; lastSeenAt?: any; expiresAt?: any; createdAt?: any; updatedAt?: any; metadata?: string; excludedPeers?: string[]; availableTransports?: ProtocolName[]; transports?: ProtocolName[]; } interface PeerState { peerId: string; deviceId?: string; deviceIdHint?: string; nodeId?: string; connectionId?: string; connectionIds: string[]; ticket?: string; online?: boolean; deviceName?: string; platformType?: string; status: PeerLifecycleStatus; health: PeerHealth; lifecycleStage?: PeerLifecycleStage; generation?: number; baseTransportState?: PeerBaseTransportState; admissionState?: PeerAdmissionState; transportState?: string; protocolState?: string; /** Compatibility projection of the canonical `routable` readiness facet. */ settledReady?: boolean; readinessState?: string; readinessReason?: string; webrtcState?: PeerWebRtcState; routable?: boolean; transportStableId?: number | null; transportGeneration?: number; routeGeneration?: number; activeTransport?: ProtocolName; fallbackTransport?: ProtocolName | null; parallelTransport?: ProtocolName | null; manualDisconnect?: boolean; lastAuthoritativeEventAt?: number; lastTransientEventAt?: number; promotionEligible?: boolean; scopes: PeerScope[]; lastSeenAt?: number; error?: string; /** Stable OpenRTC error code when present (projection only). */ errorCode?: string; /** Whether an app may retry the same logical operation. */ retryable?: boolean; /** Error plane for app-level handling. */ errorPlane?: string; } interface BackendConnectionState { connectionId: string; deviceId?: string | null; deviceIdHint?: string | null; remoteNodeId?: string | null; state: string; transportState?: string; protocolState?: string; routable?: boolean; activeTransport?: ProtocolName; parallelTransport?: ProtocolName | null; readinessState?: string; readinessReason?: string; transportGeneration?: number; routeGeneration?: number; transportStableId?: number | null; replacementInProgress?: boolean; lastTransitionAtMs?: number; transitionCount?: number; connectTransitions?: number; replacementCount?: number; retireCount?: number; lastDisconnectReason?: string | null; lastReconnectReason?: string | null; error?: string; errorCode?: string; retryable?: boolean; errorPlane?: string; createdAt?: number; updatedAt?: number; } interface NativeConnectResult { connectionId: string; deviceId?: string | null; deviceIdHint?: string | null; remoteNodeId?: string | null; state: string; approvedScope?: string | null; } interface NativeConnectParams { deviceId?: string | null; endpointTicket: string; /** Total native dial/admission budget. The host command must enforce it. */ timeoutMs?: number; } interface BackendPeerBiStream { readable: ReadableStream; writable: WritableStream; } interface BackendPeerUniStream { writable: WritableStream; } interface ChannelMetadata { channelId: string; metadata?: Record | null; } interface LocalDeviceInfo { deviceId: string; deviceName: string; platformType?: string; capabilities?: Device['capabilities']; lastSeenAt?: string; } interface NativeManagedSessionStartResult { localNodeId: string | null; ticket?: string | null; ticketScope?: string | null; presenceStarted: boolean; autoConnectStarted: boolean; localDevice?: LocalDeviceInfo | null; } interface RoomMember { nodeId: string; userId: string; ticket: string; joinedAt: number; lastSeenAt: number; expiresAt?: number; } interface JoinRoomOptions { bootstrapPeers?: boolean; } interface SignalingEnvelope { name?: string; appTag?: string; senderId: string; targetId: string; senderUserId?: string; targetUserId?: string; state?: string; payload: string; replyPayload?: string; timestamp?: number; expiresAt?: number; } interface SignalingSession { connectionId: string; initiator?: string; target?: string; initiatorDeviceId: string; targetDeviceId: string; connectionType?: string; offer?: any; offerE2ee?: any; answer?: any; answerE2ee?: any; iceCandidates: any[]; initiatorNodeId?: string; targetNodeId?: string; initiatorEndpointAddr?: string; targetEndpointAddr?: string; intent?: string; /** * @deprecated Internal namespace metadata. Public integrations should not depend on `appTag`. * Removal target: v0.2.0. */ appTag?: string; createdAt?: number; expiresAt?: number; state: string; } interface ScanningLoopOptions { localDeviceId: string; autoConnect?: boolean; autoConnectDebounceMs?: number; retryThrottleMs?: number; tieBreaker?: (localDeviceId: string, remoteDeviceId: string) => boolean; shouldSkipDevice?: (device: Device) => boolean; isConnected?: (deviceId: string) => boolean; isConnecting?: (deviceId: string) => boolean; onDevices?: (devices: Device[]) => void; onAutoConnect?: (device: Device) => Promise | void; onIncomingSession?: (session: SignalingSession) => Promise | void; hasProtectedConnectionIntent?: (deviceId: string) => boolean; cleanupStaleConnection?: (deviceId: string) => Promise; allowIncomingNodeId?: (nodeId: string) => Promise; acceptIncomingSession?: (sessionId: string, initiatorDeviceId: string) => Promise<{ success: boolean; error?: string; }>; onIncomingSessionAccepted?: (session: SignalingSession) => Promise | void; } interface ScanningLoopHandle { stop: () => void; } interface ConnectDeviceHooks { getConnectionState?: (deviceId: string) => Promise | string | undefined; forceDisconnect?: (deviceId: string) => Promise; refreshState?: () => Promise; directConnect: (device: Device) => Promise; signalConnect: (params: { localDeviceId: string; targetDeviceId: string; targetUserId?: string; device: Device; }) => Promise<{ success: boolean; error?: string; }>; onConnected?: (device: Device) => Promise | void; onRetry?: (device: Device, attempt: number, delayMs: number) => void; } interface ConnectDeviceOptions { localDeviceId: string; targetUserId?: string; maxRetries?: number; initialBackoffMs?: number; } interface ConnectDeviceResult { success: boolean; skipped?: 'already-connecting' | 'missing-local-device-id'; error?: string; /** Stable OpenRTC error code when `success` is false. */ errorCode?: string; /** Whether the same logical connect may be retried by the app. */ retryable?: boolean; /** Error plane for app-level handling (`transport`, `admission`, …). */ errorPlane?: string; attempts: number; } interface ManagedDeviceConnectOptions { localDeviceId: string; targetUserId?: string; maxRetries?: number; initialBackoffMs?: number; onRetry?: (device: Device, attempt: number, delayMs: number) => void; onConnected?: (device: Device) => void | Promise; } interface DeviceStatusSnapshot extends Device { /** * Presence/liveness for the roster entry. This is separate from * `connectionStatus`, which describes the runtime route to the peer. */ presenceStatus: DevicePresenceStatus; presenceUpdatedAt?: number; presenceExpiresAt?: number; connectable: boolean; connectionStatus: PeerLifecycleStatus | 'online'; settledReady?: boolean; readinessState?: string; readinessReason?: string; errorCode?: string; retryable?: boolean; errorPlane?: string; peerHealth: PeerHealth; peerId?: string; promotionEligible?: boolean; scopes: PeerScope[]; connectionId?: string; deviceIdHint?: string; transportStableId?: number | null; transportGeneration?: number; routeGeneration?: number; activeTransport?: ProtocolName; parallelTransport?: ProtocolName | null; availableTransports?: ProtocolName[]; /** Latest transport-owned RTT sample for the active route. */ latencyMs?: number | null; /** Passive RTT samples keyed by the exact usable route label. */ latencyByTransport?: LatencySnapshot; } interface LatencySnapshot { webrtc?: number | null; webrtcLan?: number | null; webrtcTurn?: number | null; iroh?: number | null; irohLan?: number | null; irohRelay?: number | null; irohWebrtc?: number | null; irohMoq?: number | null; ble?: number | null; moq?: number | null; } interface ManagedConnectionRecord { connectionId: string; nodeId?: string | null; deviceId?: string | null; deviceIdHint?: string | null; endpointId?: string | null; transportGeneration: number; routeGeneration?: number; transportStableId?: number | null; transportSource?: string | null; lastTransportChangeAtMs: number; lastRouteChangeAtMs?: number; state: string; statusReason?: string | null; transitionCount?: number; connectTransitions?: number; replacementCount?: number; retireCount?: number; lastDisconnectReason?: string | null; lastReconnectReason?: string | null; createdAtMs: number; updatedAtMs: number; } interface ResolvedPeerIdentity { peerId?: string | null; deviceId?: string | null; deviceIdHint?: string | null; nodeId?: string | null; } interface PeerSessionSnapshot { peerId: string; deviceId?: string | null; deviceIdHint?: string | null; nodeId?: string | null; activeConnectionId?: string | null; candidates: string[]; status: PeerLifecycleStatus; health: PeerHealth; settledReady?: boolean; readinessState?: string; transportStableId?: number | null; transportGeneration?: number; routeGeneration?: number; activeTransport?: ProtocolName; parallelTransport?: ProtocolName | null; replacementPending?: boolean; lastTransitionAtMs?: number; readinessReason?: string; transitionCount?: number; connectTransitions?: number; replacementCount?: number; retireCount?: number; lastDisconnectReason?: string | null; lastReconnectReason?: string | null; scopes: PeerScope[]; lastSeenAtMs: number; error?: string | null; errorCode?: string; retryable?: boolean; errorPlane?: string; } interface RuntimeBootstrapOptions { platform: 'native' | 'web'; initializeWeb?: () => Promise; isNativeReady?: () => Promise; retryNativeInit?: () => Promise; waitForNativeInitEvent?: (timeoutMs: number) => Promise; quickPollAttempts?: number; quickPollIntervalMs?: number; retryPollAttempts?: number; retryPollIntervalMs?: number; nativeInitEventTimeoutMs?: number; initializeClient?: boolean; } interface RuntimeBootstrapResult { ready: boolean; mode: 'native' | 'web'; } interface RuntimeIdentity { id: string; email?: string | null; displayName?: string | null; isAnonymous?: boolean; getToken?: (forceRefresh?: boolean) => Promise; } type DeviceEvent = { type: 'added'; device: Device; } | { type: 'modified'; device: Device; } | { type: 'removed'; deviceId: string; }; type SessionEvent = { type: 'added'; session: SignalingSession; } | { type: 'modified'; session: SignalingSession; } | { type: 'removed'; sessionId: string; }; interface ExplicitFileDataSendParams { connectionId?: string; connection?: { send: (message: unknown) => Promise; deviceId?: string; remoteNodeId?: string; requireApplicationCrypto?: boolean; getUpgradeState?: () => string; isWebRtcApplicationRouteReady?: () => boolean; sendOnWebRTC?: (data: Uint8Array) => Promise; getWebRTCTransport?: () => { getBufferedAmount?: () => number; waitForDrain?: (lowWaterMark: number) => Promise; } | null; }; remoteNodeId?: string; file: File; transferId: string; channelId?: string; receiverPlatformType?: string | null; applicationCrypto?: PayloadCrypto; requireApplicationCrypto?: boolean; } /** * A coordination-provider projection of the peers the runtime should reconcile. * * The bridge owns admission-token retention and runtime forwarding; the * coordination provider owns how a roster becomes this projection. Product code * should use managed auto-connect rather than synthesize projections. */ interface DesiredPeerProjection { deviceId: string; nodeId?: string | null; ticket?: string | null; online?: boolean; sessionId?: string | null; excludedPeers?: string[]; } interface ISignalingBackend { currentUser: RuntimeIdentity | null; signInAnonymously(): Promise; signInWithPluto?(): Promise; signOut(): Promise; stopAuthActivity?(): Promise; setCoordinationHostAvailability?(input: { available: boolean; reason: string; }): void; usesCoordinationGateway?(): boolean; joinCoordinationRoom?(roomId: string, nodeId: string, ticket: string): Promise; leaveCoordinationRoom?(roomId: string): Promise; getCoordinationRoomMembers?(roomId: string): Promise; watchCoordinationRoomMembers?(roomId: string, callback: (members: RoomMember[]) => void): () => void; getTurnCredentials(): Promise; updatePresence(localNodeId: string, ticketStr: string, isOnline: boolean, ttlMs: number, metadata?: string): Promise; refreshLivePresence?(localNodeId: string, ticketStr: string, metadata?: string): Promise; setOffline(localNodeId: string): Promise; cleanupStaleDevices(): Promise; searchDevices(excludeNodeId?: string): Promise; listDeviceStatus?(): Promise; getPeerSession?(id: string): Promise; listPeerSessions?(): Promise; waitForSettledPeer?(id: string, timeoutMs?: number): Promise; resolvePeerIdentity?(id: string): Promise; resolvePeerRecords?(id: string): Promise; updateDevice(deviceId: string, updates: { deviceName?: string; capabilities?: Device['capabilities']; metadata?: string; }): Promise; deleteDevice(deviceId: string): Promise; onDevicesChange(callback: (devices: Device[]) => void, excludeNodeId?: string): () => void; onAuthChange(callback: (user: RuntimeIdentity | null) => void): () => void; checkForSSOToken(): Promise; waitForAuth(): Promise; sendMessage(targetId: string, payload: string, state?: string, replyPayload?: string): Promise; pollMessages(targetId: string): Promise; subscribeDevices(userId: string): Promise>; startAutoConnectOnce?(userId: string, localDeviceId: string): Promise; startAutoConnect(userId: string, localDeviceId: string): void; submitDesiredPeerProjection?(revision: number, peers: DesiredPeerProjection[]): Promise; notifyDisconnectRequested?(remoteNodeId: string): void; startPresence?(userId: string, localNodeId: string, deviceName: string, ticket: string, metadata?: string): Promise; startPresenceLoop(userId: string, localNodeId: string, deviceName: string, ticket: string, metadata?: string): void; createSession(session: SignalingSession): Promise; updateSession(sessionId: string, updateData: any): Promise; subscribeSessions(localDeviceId: string): Promise>; forceReconnect(): void; getLocalDeviceId?(): Promise; getLocalDeviceInfo?(): Promise; updateDeviceName?(deviceName: string): Promise; startNativeSession?(options: { userId: string; deviceName?: string; localDeviceId?: string | null; metadata?: string; autoConnect?: boolean; presence?: boolean; }): Promise; syncNativeSession?(options?: { reason?: string; }): Promise; notifyNetworkChange?(options?: { reason?: string; }): Promise; getManagedNodeId?(options?: { initializeIfMissing?: boolean; }): Promise; getEndpointTicket?(): Promise; openPeerBi?(id: string, timeoutMs?: number): Promise; openPeerNativeBi?(id: string, label: string, timeoutMs?: number): Promise; openPeerUni?(id: string, timeoutMs?: number): Promise; connectToDevice?(params: { deviceId?: string | null; endpointTicket: string; }): Promise; /** * Optional backchannel so a backend (e.g. WasmBridge) can access the narrow * facade surface needed to escalate runtime transport dials into registered * TS connections. */ setCoreClient?(client: CoreClientBridgeHost | null): void; disconnectDevice?(deviceId: string): Promise; excludeAutoConnect?(deviceId: string, excluded: boolean): Promise; isConnected(nodeId: string): Promise; getConnectionStates?(): Promise; onStateChange?(callback: (state: BackendConnectionState) => void): Promise<() => void> | (() => void); /** @deprecated Implement file protocols over named channels, or use `openrtc-file-transfer`. */ sendExplicitFilePath?(connectionId: string, filePath: string, transferId?: string): Promise; /** @deprecated Implement file protocols over named channels, or use `openrtc-file-transfer`. */ sendExplicitFileData?(params: ExplicitFileDataSendParams): Promise; /** @deprecated Transfer history is consumer-owned application state. */ getTransferHistory?(limit?: number): Promise>; /** @deprecated Transfer history is consumer-owned application state. */ deleteTransferJob?(jobId: string): Promise; } interface CoreClientBridgeHost { connect(ticket: string, timeoutMs?: number, expectedDeviceId?: string | null, channelId?: string, options?: { admissionAlreadyPresented?: boolean; approvedScope?: string | null; }): Promise<{ id?: string | null; remoteNodeId?: string | null; deviceId?: string | null; } | unknown>; ensureManagedApplicationRoute?(options: ManagedApplicationRouteOptions): Promise<{ id?: string | null; remoteNodeId?: string | null; deviceId?: string | null; } | unknown>; waitForApplicationCryptoForPeer?(peerId?: string, timeoutMs?: number): Promise; hasApplicationRouteForPeer?(connectionId?: string, remoteNodeId?: string): boolean; rememberRouteRepairTokenFromTicket?(ticket: string): Promise; } interface ManagedApplicationRouteOptions { ticket: string; connectionId?: string | null; remoteNodeId?: string | null; expectedDeviceId?: string | null; /** Host-authoritative scope returned by the completed managed admission. */ approvedScope?: string | null; timeoutMs?: number; } interface SignalingOptions { apiKey?: string; authMode?: AuthMode; projectId?: string; storagePrefix?: string; nodeIdPersistence?: 'persistent' | 'ephemeral'; } interface TransportContext { sendMoQ: (data: Uint8Array, options?: { alreadyProtected?: boolean; }) => Promise; /** * Send one plaintext typed frame over a fresh runtime-owned Iroh application * stream. The runtime applies the negotiated application-crypto stream * envelope; callers must not pre-encrypt the frame a second time. */ sendIrohApplicationFrame?: (frame: Uint8Array) => Promise; priorities: ProtocolName[]; /** * Orders already-eligible route candidates through the Rust/WASM policy * owner. This is a pure decision call: it must not dial, retry, promote, * demote, or mutate connection state. */ rankRoutes?: (candidates: ProtocolName[]) => ProtocolName[]; disableIrohFallback: boolean; isMoQReady: () => boolean; isMoQDataReady?: () => boolean; isMoQApplicationRouteProven?: () => boolean; requestMoQApplicationRouteProof?: () => void; clearMoQApplicationRouteProof?: () => void; runtimeFlow?: 'default' | 'ticket-only'; readerCloseStrategy?: 'cancel' | 'release-lock'; } interface PayloadCrypto { /** * When true, incoming application payloads that are not encrypted by this * envelope are dropped before app callbacks fire. */ requireEncrypted?: boolean; protectPayload: (typeId: number, payload: Uint8Array) => Uint8Array; openPayload: (expectedTypeId: number, payload: Uint8Array) => Uint8Array | null; } interface CryptoBiStream { send: WritableStream; recv: ReadableStream; endpoint_id?: string; } interface CryptoStreams { readonly typeId: number; protectFrame: (payload: Uint8Array) => Uint8Array; openFrame: (payload: Uint8Array) => Uint8Array | null; outboundTransform: () => TransformStream; inboundTransform: () => TransformStream; wrapReadable: (readable: ReadableStream) => ReadableStream; wrapWritable: (writable: WritableStream) => WritableStream; wrapBiStream: (stream: T) => T; } interface ApplicationPayloadReadinessOptions { /** * When provided, a connection is considered ready only once its active * application route is one of these transports. */ preferredTransports?: ProtocolName[]; /** * If a preferred upgraded route cannot be established, allow the connection's * proven application route to carry payloads instead of remaining pending. */ allowFallback?: boolean; } interface ApplicationPayloadRouteOptions extends ApplicationPayloadReadinessOptions { /** * Include the parallel/fallback transport in diagnostic route listings. * Normal app payload probes should leave this disabled so they measure the * route OpenRTC would actually prefer for application data. */ includeFallbackTransports?: boolean; } interface ApplicationPayloadSendOptions extends ApplicationPayloadRouteOptions { } interface ApplicationRouteReadyObservation { connectionId: string; remoteNodeId: string; activeTransport: ProtocolName; parallelTransport?: ProtocolName | null; reason: 'webrtc-route-ready' | 'webrtc-heartbeat-healthy' | 'webrtc-application-send' | 'base-transport-loss-preserved'; transportStableId?: number; transportGeneration?: number; routeGeneration?: number; } interface ConnectionOptions { reliable?: boolean; rtcConfig?: PlutoRTCConfiguration; transportContext?: TransportContext; applicationCrypto?: PayloadCrypto; /** * Whether the constructor's reader/writer represent a routable base * application stream. Transport-only ticket channels set this to false: * their placeholder streams exist only to satisfy the compatibility object, * while application bytes use named runtime channels. */ hasBaseApplicationStream?: boolean; /** Framing used by the underlying iroh control stream. */ controlFrameMode?: 'typed' | 'native-main'; /** * When true, public application payload sends wait for applicationCrypto to * be installed and fail closed if key negotiation never completes. * Internal handshake/control frames are sent through a separate control path. */ requireApplicationCrypto?: boolean; applicationCryptoWaitMs?: number; onTransportStatusChange?: (status: { activeTransport: ProtocolName; parallelTransport?: ProtocolName | null; transportStableId?: number; transportGeneration?: number; routeGeneration?: number; }) => boolean | void | Promise; /** * Fired when a connection-owned application route has proven it can carry * app traffic. Consumers should use this as a lifecycle signal for the same * connection record, not as an independent projection source. */ onApplicationRouteReady?: (event: ApplicationRouteReadyObservation) => void; /** * Fired when the WebRTC data plane has proven a ping/pong round trip but the * mandatory application crypto route is not installed yet. Connection owns * transport proof; Client owns the application-route handshake recovery. */ onApplicationRouteMissing?: (event: { connectionId: string; remoteNodeId: string; reason: 'webrtc-heartbeat-before-crypto'; negotiationId?: string | null; }) => void; } interface JoinRequest { id: string; userId: string; ticket: string; state: 'pending' | 'accepted' | 'rejected'; } interface ITransport { readonly name: string; readonly isReady: boolean; init(localNodeId: string, remoteNodeId: string, options: any): Promise; send(data: Uint8Array): Promise; close(): void; onMessage(callback: (data: Uint8Array) => void): void; onStateChange(callback: (state: 'connecting' | 'connected' | 'failed' | 'disconnected') => void): void; handleSignalingMessage?(msg: any): void; getSignalingHandler?(): (msg: any) => Promise; getNativeTransport?(): any; getNegotiationId?(): string | null; getDataChannelReadyState?(): string | null; } export type { RouteDescriptor as $, AuthMode as A, BackendPeerBiStream as B, ConnectDeviceResult as C, Device as D, ExplicitFileDataSendParams as E, ApplicationPayloadReadinessOptions as F, GrantScope as G, ApplicationPayloadRouteOptions as H, ISignalingBackend as I, ApplicationPayloadSendOptions as J, ITransport as K, LocalDeviceInfo as L, ManagedConnectionRecord as M, NativeConnectParams as N, LatencySnapshot as O, PlutoIrohConfiguration as P, PeerState as Q, RouteOptimization as R, SignalingMode as S, PeerScope as T, PeerHealth as U, ChannelDescriptor as V, ChannelMetadata as W, ManagedDeviceConnectOptions as X, JoinRoomOptions as Y, DevicePresenceStatus as Z, PeerLifecycleStatus as _, PlutoRTCConfiguration as a, ProtocolCapability as a0, ProtocolCapabilityMap as a1, CryptoStreams as a2, CryptoBiStream as a3, RouteFamily as a4, RouteKind as a5, AppLimits as a6, SignalingOptions as a7, RoomCreationMode as a8, ScanningLoopOptions as a9, ScanningLoopHandle as aa, RuntimeBootstrapOptions as ab, RuntimeBootstrapResult as ac, ManagedApplicationRouteOptions as ad, JoinRequest as ae, PlutoMoQConfiguration as b, ProtocolName as c, PeerSessionSnapshot as d, ResolvedPeerIdentity as e, ConnectDeviceHooks as f, ConnectDeviceOptions as g, ClientOptions as h, CoordinationGatewayGrantRequest as i, CoordinationGatewayGrant as j, DiscoveryMode as k, RoomMember as l, RuntimeIdentity as m, SignalingEnvelope as n, DeviceStatusSnapshot as o, BackendPeerUniStream as p, DeviceEvent as q, DesiredPeerProjection as r, CoreClientBridgeHost as s, NativeConnectResult as t, SignalingSession as u, SessionEvent as v, BackendConnectionState as w, NativeManagedSessionStartResult as x, ConnectionOptions as y, PayloadCrypto as z };