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

@crosstown/client

v0.4.2

Published

Crosstown client for ILP-gated Nostr publishing with multi-hop routing and payment channels

Readme

@crosstown/client

High-level TypeScript client for publishing Nostr events to the Crosstown protocol — an ILP-gated Nostr relay that enables sustainable relay operation through micropayments.

What It Does

This client handles:

  • ILP Micropayments: Pay to publish Nostr events (read is free)
  • Payment Channels: Automatic on-chain channel creation with off-chain settlement via signed balance proofs
  • Unified Identity: One Nostr key = one EVM address (both use secp256k1, derived automatically)
  • Multi-Hop Routing: Publish to any destination address, not just your direct peer
  • Network Bootstrap: Automatically discover and register with ILP peers via NIP-02 follow lists
  • TOON Encoding: Native binary format for agent-friendly event encoding

Installation

pnpm add @crosstown/client @crosstown/core @crosstown/relay nostr-tools

Prerequisites

The client requires external services. Use docker-compose for local development:

# Start genesis node
docker compose -p crosstown-genesis -f docker-compose-genesis.yml up -d

# Verify services are healthy
curl http://localhost:8080/health  # ILP Connector (runtime)
curl http://localhost:8081/health  # ILP Connector (admin)
curl http://localhost:3100/health  # Crosstown BLS
# Nostr relay on ws://localhost:7100 (WebSocket, no HTTP endpoint)

# Stop infrastructure
docker compose -p crosstown-genesis -f docker-compose-genesis.yml down

| Service | Port | Purpose | | --------------------------- | ---- | --------------------------------------------------- | | ILP Connector (Runtime) | 8080 | Routes ILP packets to relay | | ILP Connector (Admin) | 8081 | Manages peer configuration | | Crosstown BLS | 3100 | Validates events, calculates pricing, stores events | | Nostr Relay | 7100 | WebSocket relay for peer discovery (kind:10032) |


Quick Start

import { CrosstownClient } from '@crosstown/client';
import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure';
import { encodeEventToToon, decodeEventFromToon } from '@crosstown/relay';

// 1. Generate identity — one key gives you both Nostr and EVM identities
const secretKey = generateSecretKey();
const pubkey = getPublicKey(secretKey);

// 2. Create client
const client = new CrosstownClient({
  connectorUrl: 'http://localhost:8080',
  secretKey,
  ilpInfo: {
    pubkey,
    ilpAddress: `g.crosstown.${pubkey.slice(0, 8)}`,
    btpEndpoint: 'ws://localhost:3000',
  },
  toonEncoder: encodeEventToToon,
  toonDecoder: decodeEventFromToon,
});

// 3. Start (bootstrap network, discover peers)
await client.start();

// Your EVM address is derived from the same key — no separate config needed
console.log(`EVM address: ${client.getEvmAddress()}`);

// 4. Publish event to relay via ILP payment
const event = finalizeEvent(
  { kind: 1, content: 'Hello from Crosstown!', tags: [], created_at: Math.floor(Date.now() / 1000) },
  secretKey,
);

const result = await client.publishEvent(event);
if (result.success) {
  console.log(`Published: ${result.eventId}`);
}

// 5. Clean up
await client.stop();

Payment Channels

The client supports EVM-based payment channels for off-chain settlement. Your EVM identity is derived from your Nostr secretKey automatically — no separate EVM key needed.

Enabling Payment Channels

To use payment channels, add chain configuration. The client already has your EVM identity from secretKey:

const client = new CrosstownClient({
  connectorUrl: 'http://localhost:8080',
  secretKey,
  ilpInfo: { pubkey, ilpAddress: `g.crosstown.${pubkey.slice(0, 8)}`, btpEndpoint: 'ws://localhost:3000' },
  toonEncoder: encodeEventToToon,
  toonDecoder: decodeEventFromToon,

  // Add chain config to enable payment channels
  supportedChains: ['evm:anvil:31337'],
  chainRpcUrls: { 'evm:anvil:31337': 'http://localhost:8545' },
  settlementAddresses: { 'evm:anvil:31337': client.getEvmAddress()! },
  tokenNetworks: { 'evm:anvil:31337': '0xCafac3dD18aC6c6e92c921884f9E4176737C052c' },
  initialDeposit: '1000000000000000000', // 1 ETH in wei
});

await client.start();

// Channels are created automatically during bootstrap
const channels = client.getTrackedChannels();
console.log(`Tracking ${channels.length} payment channels`);

// Publish with signed balance proof
const channelId = channels[0];
const claim = await client.signBalanceProof(channelId, 1000n);
await client.publishEvent(event, { claim });

How It Works

  1. Bootstrap: Client discovers peers via NIP-02 and kind:10032 events
  2. Channel Creation: Opens on-chain payment channel using your derived EVM address
  3. Off-chain Payments: Signed balance proofs settle payments off-chain
  4. Auto-tracking: ChannelManager automatically tracks channels and increments nonces

Using a Separate EVM Key (Advanced)

If you need a different EVM identity than your Nostr key (e.g., hardware wallet or custodial key), pass evmPrivateKey explicitly:

const client = new CrosstownClient({
  // ... required config ...
  evmPrivateKey: '0x...', // Overrides the default derivation from secretKey
});

Documentation


Testing

Unit & Integration Tests

cd packages/client
pnpm test                 # Run all unit/integration tests
pnpm test:coverage        # Run with coverage report

E2E Tests

E2E tests require the genesis node infrastructure:

# Start infrastructure
docker compose -p crosstown-genesis -f docker-compose-genesis.yml up -d
sleep 10

# Run E2E tests
cd packages/client
pnpm test:e2e

See tests/e2e/README.md for detailed E2E setup.


Examples

See examples/client-example/ for standalone client examples:

  • 01 - Publish Event: Full client lifecycle with self-describing claims
  • 02 - Payment Channel Lifecycle: Multiple events with incrementing balance proofs

Related Packages


License

MIT