@crosstown/client
v0.4.2
Published
Crosstown client for ILP-gated Nostr publishing with multi-hop routing and payment channels
Maintainers
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-toolsPrerequisites
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
- Bootstrap: Client discovers peers via NIP-02 and kind:10032 events
- Channel Creation: Opens on-chain payment channel using your derived EVM address
- Off-chain Payments: Signed balance proofs settle payments off-chain
- 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
- API Reference — Constructor, config interface, and all methods
- Error Handling — Error class hierarchy, codes, and usage patterns
- HTTP Adapters — Low-level
HttpRuntimeClient,HttpConnectorAdmin, andwithRetry - Troubleshooting — Common issues and solutions
Testing
Unit & Integration Tests
cd packages/client
pnpm test # Run all unit/integration tests
pnpm test:coverage # Run with coverage reportE2E 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:e2eSee 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
- @crosstown/core — Core protocol (peer discovery, bootstrap)
- @crosstown/relay — Nostr relay with ILP payment gating
- @crosstown/bls — Business Logic Server (pricing, validation, storage)
License
MIT
