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

ss-meet

v0.0.3

Published

WebRTC video conferencing library using Statement Store for decentralized signaling

Downloads

280

Readme

ss-meet

WebRTC video conferencing library using Statement Store for decentralized signaling.

Features

  • Decentralized Signaling: Uses Substrate Statement Store instead of a centralized signaling server
  • P2P Video Calls: WebRTC-based peer-to-peer video and audio
  • End-to-End Encryption: Optional AES-GCM encryption for signaling data
  • Auto-Discovery: Automatic peer discovery via presence channels
  • Data Channels: Send arbitrary data between peers
  • TURN Support: Cloudflare TURN integration with fallback to public STUN servers
  • TypeScript: Full type safety with comprehensive types

Installation

npm install ss-meet

Quick Start

import { SSMeet, generateRandomMnemonic } from 'ss-meet'

// Create a meeting instance
const meeting = new SSMeet({
  roomId: 'my-meeting-room',
  endpoint: 'wss://your-substrate-node.com',
  identity: {
    peerId: 'user-123',
    mnemonic: generateRandomMnemonic(),
    displayName: 'Alice'
  }
})

// Listen for events
meeting.on('stream', (peerId, stream) => {
  const video = document.getElementById(`video-${peerId}`)
  if (video) video.srcObject = stream
})

meeting.on('peerJoined', (peer) => {
  console.log(`${peer.displayName} joined`)
})

meeting.on('data', (peerId, data) => {
  console.log(`Message from ${peerId}:`, data)
})

// Get local media and connect
const localStream = await navigator.mediaDevices.getUserMedia({
  video: true,
  audio: true
})
meeting.setLocalStream(localStream)

await meeting.connect()

// Send messages
meeting.broadcast({ type: 'chat', message: 'Hello!' })

// Disconnect when done
await meeting.disconnect()

End-to-End Encryption

SS-Meet supports optional end-to-end encryption for signaling data. This encrypts SDPs (which contain IP addresses), presence information, and any data stored in the Statement Store.

import {
  SSMeet,
  generateRoomKey,
  importRoomKey,
  createShareableUrl,
  extractRoomKeyFromUrl
} from 'ss-meet'

// Room creator generates a key
const roomKeyString = await generateRoomKey()
const shareUrl = createShareableUrl('https://app.com/room/abc', roomKeyString)
// Share this URL - the key is in the fragment (#) and never sent to servers

// Participants extract the key from URL
const keyFromUrl = extractRoomKeyFromUrl() // Uses window.location.hash
if (keyFromUrl) {
  const roomKey = await importRoomKey(keyFromUrl)

  const meeting = new SSMeet({
    roomId: 'abc',
    endpoint: 'wss://substrate-node.com',
    identity: { peerId: 'user-1', mnemonic: '...' },
    options: {
      roomKey // Enable encryption
    }
  })
}

Encryption Functions

import {
  generateRoomKey,      // Generate a new AES-256 key (returns base64url string)
  importRoomKey,        // Import key from base64url string to CryptoKey
  encryptChannelData,   // Encrypt a string
  decryptChannelData,   // Decrypt a string
  isEncryptedData,      // Check if data looks encrypted
  isValidRoomKey,       // Validate key format
  extractRoomKeyFromUrl, // Extract key from URL fragment
  createShareableUrl    // Create URL with key in fragment
} from 'ss-meet'

Configuration

SSMeetConfig

| Property | Type | Required | Description | |----------|------|----------|-------------| | roomId | string | Yes | Unique room identifier | | endpoint | string | Yes | WebSocket URL to Substrate node | | identity | SSMeetIdentity | Yes | User identity configuration | | turn | SSMeetTurnConfig | No | TURN server configuration | | options | SSMeetOptions | No | Advanced options |

Identity Configuration

{
  peerId: 'unique-user-id',     // Unique identifier for this user
  mnemonic: '...',              // BIP39 mnemonic for signing
  displayName: 'Alice'          // Optional display name
}

TURN Configuration

{
  keyId: 'cloudflare-turn-key',
  apiToken: 'cloudflare-api-token'
}

Options

{
  forceRelay: false,     // Force TURN relay mode
  presenceExpiry: 600,   // Presence expiry in seconds
  debug: false,          // Enable debug logging
  roomKey: CryptoKey,    // Optional encryption key
  appTopic: 'my-app'     // Custom app topic (default: 'ss-meet')
}

Application Topic Isolation

By default, ss-meet uses the topic 'ss-meet' to filter messages in the Statement Store. If you're building a custom application, you should set a unique appTopic to isolate your app's messages from other applications:

const meeting = new SSMeet({
  roomId: 'my-room',
  endpoint: 'wss://substrate-node.com',
  identity: { peerId: 'user-1', mnemonic: '...' },
  options: {
    appTopic: 'my-custom-app'  // Your app's unique identifier
  }
})

This ensures that only participants using the same appTopic will discover each other.

Events

| Event | Parameters | Description | |-------|------------|-------------| | status | (status, message) | Connection status changed | | peerJoined | (peer) | A peer joined the meeting | | peerLeft | (peerId) | A peer left the meeting | | peerConnected | (peerId) | WebRTC connection established | | peerDisconnected | (peerId) | WebRTC connection closed | | stream | (peerId, stream) | Received media stream | | data | (peerId, data) | Received data message | | log | (message, type) | Debug log message | | error | (error) | An error occurred |

API Reference

SSMeet

Methods

// Connection
setLocalStream(stream: MediaStream | null): void
getLocalStream(): MediaStream | null
connect(): Promise<void>
disconnect(): Promise<void>
isConnected(): boolean
getStatus(): ConnectionStatus

// Peers
getConnectedPeers(): string[]
getPeerInfo(peerId: string): PeerInfo | undefined
getAllPeers(): PeerInfo[]

// Data
send(peerId: string, data: unknown): boolean
broadcast(data: unknown): void

// Info
getRoomId(): string
getPeerId(): string
getDisplayName(): string | undefined

// Events
on(event, callback): () => void  // Returns unsubscribe function
off(event, callback): void
once(event, callback): () => void
removeAllListeners(): void

Wallet Utilities

import {
  generateRandomMnemonic,
  generateSecureMnemonic,
  deriveWallet,
  validateMnemonic,
  createWallet
} from 'ss-meet'

// Generate a 12-word mnemonic
const mnemonic = generateRandomMnemonic()

// Generate a 24-word mnemonic (more secure)
const secureMnemonic = generateSecureMnemonic()

// Derive wallet from mnemonic
const wallet = deriveWallet(mnemonic)
console.log(wallet.address) // SS58 address

// Validate a mnemonic
if (validateMnemonic(userInput)) {
  // Valid mnemonic
}

// Create a new wallet with fresh mnemonic
const newWallet = createWallet()

Error Handling

SS-Meet uses typed errors for better error handling:

import { SSMeet, SSMeetError } from 'ss-meet'

const meeting = new SSMeet(config)

meeting.on('error', (error) => {
  if (error instanceof SSMeetError) {
    switch (error.code) {
      case 'CONNECTION_FAILED':
        console.error('Failed to connect:', error.message)
        break
      case 'PEER_CONNECTION_FAILED':
        console.error('Peer connection failed:', error.message)
        break
      case 'BLOCKCHAIN_ERROR':
        console.error('Blockchain error:', error.message)
        break
      // ... handle other error codes
    }
  }
})

try {
  await meeting.connect()
} catch (error) {
  if (error instanceof SSMeetError) {
    console.error(`[${error.code}] ${error.message}`)
    if (error.cause) {
      console.error('Caused by:', error.cause)
    }
  }
}

Error Codes

| Code | Description | |------|-------------| | CONNECTION_FAILED | Failed to connect to meeting | | SIGNALING_FAILED | Signaling operation failed | | PEER_CONNECTION_FAILED | WebRTC peer connection failed | | ICE_GATHERING_FAILED | ICE gathering timed out | | DATA_CHANNEL_FAILED | Data channel error | | MEDIA_FAILED | Media access error | | INVALID_CONFIG | Invalid configuration | | NOT_CONNECTED | Operation requires connection | | ALREADY_CONNECTED | Already connected | | BLOCKCHAIN_ERROR | Statement Store error | | TURN_CREDENTIALS_FAILED | TURN credential fetch failed |

Testing

Run the test suite:

npm test              # Run all tests
npm run test:watch    # Watch mode
npm run test:coverage # With coverage report

Architecture

SS-Meet uses a layered architecture:

┌─────────────────────────────────────┐
│              SSMeet                 │  ← High-level API
├─────────────────────────────────────┤
│          SSWebRTCProvider           │  ← Orchestration
├──────────────┬──────────────────────┤
│ SignalingMgr │     PeerManager      │  ← Signaling & WebRTC
├──────────────┴──────────────────────┤
│          StatementStore             │  ← Blockchain I/O
├─────────────────────────────────────┤
│     @novasamatech/sdk-statement     │  ← Statement SDK
└─────────────────────────────────────┘

Signaling Protocol

  1. Presence: Each peer writes a presence message with their ID and join timestamp
  2. Older Peer Offers: The peer who joined first initiates WebRTC connections
  3. Handshake Channels: Shared channels for offer/answer exchange
  4. Non-Trickle ICE: ICE candidates are embedded in SDP

Statement Store Topics

  • {appTopic} - Application topic for filtering (default: 'ss-meet')
  • {roomId} - Room-specific topic for isolation

Advanced Usage

Using Lower-Level Components

For advanced use cases, you can use the underlying components directly:

import {
  SSWebRTCProvider,
  StatementStore,
  SignalingManager,
  PeerManager,
  TurnCredentials
} from 'ss-meet'

// Direct StatementStore usage
const store = new StatementStore({
  endpoint: 'wss://substrate-node.com',
  documentId: 'my-room',
  mnemonic: '...'
})

await store.connect()
store.onStatement((value) => {
  console.log('Received:', value)
})
await store.write('my-channel', { type: 'custom', data: '...' })

Meeting Optimizations

SS-Meet includes optional optimization modules:

import {
  ActiveSpeakerDetector,
  VideoVisibilityManager,
  AdaptiveBitrateController,
  SimulcastManager,
  LastNVideoManager,
  QualityTierManager
} from 'ss-meet'

Browser Support

  • Chrome 80+
  • Firefox 75+
  • Safari 14+
  • Edge 80+

Requires WebRTC and Web Crypto API support.

License

MIT