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

smokesigns

v0.2.2

Published

WebRTC and dweb connectivity library for establishing peer-to-peer connections via Autonomi network

Readme

smokesigns

WebRTC and dweb connectivity library for establishing peer-to-peer connections via Autonomi network or handshake servers.

Features

  • WebRTC peer-to-peer connections with automatic offer/answer exchange
  • Multiple signaling backends:
    • DwebConnector: Uses Autonomi network's public scratchpad
    • HandshakeserverConnector: Uses a simple handshake server (e.g., handshake.autonomi.space)
  • Ordered, reliable data channels
  • TypeScript support
  • Automatic ICE candidate gathering with timeout
  • STUN-only configuration (no TURN servers required)
  • Presence heartbeat with last-seen detection (minimal polling, automatic reconnection)

Why "smokesigns"?

Just like smoke signals in the past allowed people to communicate over long distances without relying on external infrastructure or service providers, smokesigns enables direct peer-to-peer communication without depending on centralized servers. It's just you, your communication partner, and your "fires" (devices)! 🔥

The library provides the digital equivalent of smoke signals - simple, direct, and decentralized communication.

Installation

npm install smokesigns

Usage

Using HandshakeserverConnector

The HandshakeserverConnector uses a simple REST API server for signaling. Both peers need to agree on two 96-character hex addresses - one for each direction of communication.

import { Link, HandshakeserverConnector } from 'smokesigns';

// Peer A configuration
const peerA = new Link({
  readWriteInterface: new HandshakeserverConnector({
    serverUrl: 'https://handshake.autonomi.space',
    readAddress: '2222...2222',  // 96-char hex address to read from
    writeAddress: '1111...1111'  // 96-char hex address to write to
  }),
  priority: true  // Peer A creates the offer
});

// Peer B configuration (inverse addresses)
const peerB = new Link({
  readWriteInterface: new HandshakeserverConnector({
    serverUrl: 'https://handshake.autonomi.space',
    readAddress: '1111...1111',  // Read what peer A writes
    writeAddress: '2222...2222'  // Write where peer A reads
  }),
  priority: false  // Peer B waits for offer and creates answer
});

// Connect both peers
await peerA.connect();
await peerB.connect();

Using DwebConnector

The DwebConnector uses the Autonomi network's public scratchpad for signaling.

import { Link, DwebConnector } from 'smokesigns';

const link = new Link({
  readWriteInterface: new DwebConnector({
    backendUrl: 'https://api.example.com',
    writeTuple: ['myapp', 'myobject'],
    readScratchpadAddress: 'scratchpad-address-here'
  }),
  priority: true
});

await link.connect();

Custom Read/Write Interface

You can implement your own signaling mechanism by implementing the ReadWriteInterface:

import type { ReadWriteInterface } from 'smokesigns';

class CustomConnector implements ReadWriteInterface {
  async write(data: any): Promise<void> {
    // Implement your write logic
  }
  
  async read(): Promise<any> {
    // Implement your read logic
    // Return null if no data available
  }
}

WebRTC Configuration

The library uses a STUN-only configuration optimized for direct peer-to-peer connections:

  • Multiple STUN servers for reliability
  • Ordered data channels for guaranteed message delivery
  • ICE gathering timeout of 3 seconds for cloud environments
  • No TURN servers (direct connections only)

Overview

smokesigns provides three main components:

  1. Link - A WebRTC connection object that handles offer/answer exchange
  2. DwebConnector - A connector using the dweb framework via Autonomi for handshake establishment
  3. HandshakeserverConnector - A connector using a simple handshake server for signaling

Core Objects

Link

A connection object with WebRTC functionality that manages offer/answer exchange through a read/write interface.

Properties:

  • connected: boolean - Whether the link is currently connected
  • disconnected: boolean - Inverse of connected
  • connect(): Promise<void> - Start the connection process
  • disconnect(): void - Disconnect the link
  • lastSeen: number | null - Unix timestamp of when the partner was last active (updated every minute)
  • onPresenceUpdate?: (ts: number) => void - Callback fired whenever lastSeen changes

Constructor Options:

  • readWriteInterface: ReadWriteInterface - Object with read() and write(data) methods
  • priority: boolean - If true, creates WebRTC offer; if false, waits for offer and creates answer

Behavior:

  • Timing behaviour:

    • A connection attempt is only started immediately if you call connect() within the first 10 seconds after the start of a full minute (hh:mm:00–hh:mm:09).
    • Danach werden automatische Versuche immer exakt zur nächsten vollen Minute ausgeführt.
  • Role specific behaviour:

    • Priority = true: Creates WebRTC offer, writes it via the interface, then polls for answer
    • Priority = false: Polls for incoming offer (max 30 s old), then creates and writes answer

DwebConnector

A connector that uses the dweb framework via Autonomi to establish handshakes through public scratchpads.

Constructor Options:

  • backendUrl: string - Backend URL for dweb API calls (empty string for relative URLs)
  • writeTuple: [string, string] - [appname, objectname] for writing to public scratchpad
  • readScratchpadAddress: string - Address of the public scratchpad to read from

Methods:

  • write(data: any): Promise<void> - Write data to public scratchpad
  • read(): Promise<any> - Read data from public scratchpad (returns null if no data)

Usage Examples

Basic WebRTC Connection with Dweb

import { Link, DwebConnector } from 'smokesigns';

// Create dweb connector
const dwebConnector = new DwebConnector({
  backendUrl: 'https://your-dweb-backend.com', // or '' for relative URLs
  writeTuple: ['my-app', 'handshake-channel'],
  readScratchpadAddress: 'peer-scratchpad-address'
});

// Create WebRTC link (as offer creator)
const link = new Link({
  readWriteInterface: dwebConnector,
  priority: true
});

// Connect
try {
  await link.connect();
  console.log('Connected!', link.connected);
} catch (error) {
  console.error('Connection failed:', error);
}

// Later disconnect
link.disconnect();

Custom ReadWriteInterface

You can implement your own ReadWriteInterface for different transport mechanisms:

import { Link } from 'smokesigns';
import type { ReadWriteInterface } from 'smokesigns';

class LocalStorageInterface implements ReadWriteInterface {
  constructor(private key: string) {}

  async write(data: any): Promise<void> {
    localStorage.setItem(this.key, JSON.stringify(data));
  }

  async read(): Promise<any> {
    const stored = localStorage.getItem(this.key);
    return stored ? JSON.parse(stored) : null;
  }
}

const link = new Link({
  readWriteInterface: new LocalStorageInterface('webrtc-handshake'),
  priority: false // Wait for offer, create answer
});

Two-Way Setup

// Peer A (creates offer)
const peerA = new Link({
  readWriteInterface: new DwebConnector({
    backendUrl: '',
    writeTuple: ['chat-app', 'peer-a-channel'],
    readScratchpadAddress: 'peer-b-scratchpad-address'
  }),
  priority: true
});

// Peer B (creates answer)
const peerB = new Link({
  readWriteInterface: new DwebConnector({
    backendUrl: '',
    writeTuple: ['chat-app', 'peer-b-channel'],  
    readScratchpadAddress: 'peer-a-scratchpad-address'
  }),
  priority: false
});

// Connect both simultaneously
await Promise.all([peerA.connect(), peerB.connect()]);

Standalone Dweb Usage

import { DwebConnector } from 'smokesigns';

const connector = new DwebConnector({
  backendUrl: 'https://autonomi-backend.example.com',
  writeTuple: ['my-app', 'data-exchange'],
  readScratchpadAddress: 'target-scratchpad-address'
});

// Write data
await connector.write({
  message: 'Hello from smokesigns!',
  timestamp: Date.now()
});

// Read data
const data = await connector.read();
if (data) {
  console.log('Received:', data);
}

ReadWriteInterface

The ReadWriteInterface is the core abstraction that allows different transport mechanisms:

interface ReadWriteInterface {
  write(data: any): Promise<void> | void;
  read(): Promise<any> | any;
}

This allows Link to work with various backends:

  • Dweb/Autonomi scratchpads (via DwebConnector)
  • Local storage
  • WebSocket servers
  • HTTP APIs
  • Any custom transport mechanism

Development

# Install dependencies
npm install

# Run tests
npm test

# Build library
npm run build

# Development mode
npm run dev

License

MIT

Connection Flow

graph TD
    start["Link.connect() called"] --> attempt["Immediate connection attempt"]
    attempt --> priority{priority flag}
    priority -->|true| offerFlow["createOfferFlow()"]
    priority -->|false| waitFlow["waitForOfferFlow()"]

    %% Offer side
    offerFlow --> writeOffer["Write Offer via ReadWriteInterface"]
    writeOffer --> pollAns["Poll for Answer (max 30 s)"]
    pollAns --> ansReceived{Answer received?}
    ansReceived -->|yes| establish1["Establish WebRTC"]
    ansReceived -->|no| retry1["Wait until next full minute"]

    %% Answer side
    waitFlow --> pollOffer["Poll for Offer (max 30 s)"]
    pollOffer --> offerReceived{Recent offer?}
    offerReceived -->|yes| genAnswer["Generate Answer & write"]
    genAnswer --> establish2["Establish WebRTC"]
    offerReceived -->|no| retry2["Wait until next full minute"]

    %% Retry logic
    retry1 --> attempt
    retry2 --> attempt

    %% Reconnection after disconnect
    establish1 --> monitor["Monitor connection state"]
    establish2 --> monitor
    monitor --> disconnected{Disconnected?}
    disconnected -->|yes| retry3["Wait until next full minute"]
    retry3 --> attempt

Running the test suite

Unit tests

Run all unit tests:

npm test

End-to-end browser test

The E2E test spins up two real Chrome instances via Puppeteer and establishes a full WebRTC connection through the public handshake server.

Headless (default):

npm run test:e2e

Visible browser with DevTools (handy for debugging):

npm run test:e2e:ui

Or manually:

HEADLESS=false npm run test:e2e

When run in UI mode the script automatically opens DevTools and slows actions down a bit (slowMo=100ms) so you can watch the handshake in real time. It also keeps the windows open for 5 seconds after the test finishes.