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

@canister-software/consensus-cli

v0.1.0-beta.7

Published

Consensus SDK for interacting with the Consensus protocol

Readme



Installation

npm install @canister-software/consensus-cli

Environment Setup

Create a .env file in the root of your project with your CDP credentials:

CDP_API_KEY_ID=your_cdp_key_id
CDP_API_KEY_SECRET=your_cdp_key_secret
CDP_WALLET_SECRET=your_cdp_wallet_secret

Get your CDP credentials at portal.cdp.coinbase.com.

You can also override the default server with:

CONSENSUS_SERVER_URL=https://your-custom-node.example.com

ProxyClient

ProxyClient(fetchWithPayment, options) returns a framework-agnostic proxy controller with Express-compatible middleware behavior. It routes outbound HTTP requests through Consensus proxy nodes and supports automatic spend-limit stand-down.

ProxyClient Options

| Option | Type | Default | Description | | ------------------ | ---------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------- | | mode | "inclusive" \| "exclusive" | "inclusive" | inclusive proxies all routes except those in routes. exclusive proxies only the listed routes. | | routes | string[] | [] | Route paths to include or exclude, depending on mode. | | matchSubroutes | boolean | false | When true, a route match also applies to all sub-paths beneath it. | | strategy | "auto" \| "manual" | "auto" | auto transparently intercepts fetch() calls within middleware. manual exposes req.consensus.fetch() for explicit control. | | cache_ttl | number | — | TTL in seconds for node-level response caching. | | verbose | boolean | false | Enables verbose response metadata from the proxy node. | | node_region | string | — | Prefer proxy nodes in a specific geographic region. | | node_domain | string | — | Route through a specific node domain. | | node_exclude | string | — | Exclude a specific node domain from selection. | | limit_usd | number | — | Max proxy spend in USD (up to 6 decimals). When reached, proxying stands down to direct fetch. | | on_limit_reached | (budget) => void | — | Callback fired once when stand-down is activated. |

Proxy spend tracking uses the fixed server price of $0.0001 per paid /proxy request (cached hits are not charged).

Auto Strategy (Default)

In auto mode, ProxyClient intercepts the global fetch() within the request context so your route handlers require no changes.

import express from 'express';
import { ProxyClient } from '@canister-software/consensus-cli';

const app = express();

// Proxy only /price — all other routes use direct fetch
app.use(
  ProxyClient(fetchWithPayment, {
    mode: 'exclusive',
    routes: ['/price'],
    matchSubroutes: false,
    strategy: 'auto',
    cache_ttl: 60,
    verbose: true,
  })
);

// No changes needed — fetch() is automatically proxied for /price
app.get('/price', async (_req, res) => {
  const response = await fetch('https://api.example.com/price');
  res.json(await response.json());
});

Manual Strategy

In manual mode, the proxy is not applied automatically. Use req.consensus.fetch() to explicitly proxy individual requests, or req.consensus.request() for a lower-level structured payload:

app.use(ProxyClient(fetchWithPayment, { strategy: 'manual' }));

app.get('/data', async (req, res) => {
  // Proxied fetch — returns a standard Response
  const response = await req.consensus.fetch('https://api.example.com/data');
  res.json(await response.json());

  // Or use the structured request helper — returns a ProxyResponseShape
  const result = await req.consensus.request({
    target_url: 'https://api.example.com/data',
    method: 'GET',
  });
  res.json(result.data);
});

Per-Request Node Selection

Both req.consensus.fetch() and req.consensus.request() accept per-request options as a second argument to override node routing at the call level:

const response = await req.consensus.fetch(
  'https://api.example.com/data',
  { method: 'GET' },
  { node_region: 'us-east', cache_ttl: 30 }
);

Framework-Agnostic Usage

Use runWithPath() to scope interception in any server framework and createFetch() for explicit route-scoped fetch:

const proxy = ProxyClient(fetchWithPayment, {
  mode: 'exclusive',
  routes: ['/api'],
  limit_usd: 1.25,
});

await proxy.runWithPath('/api', async () => {
  const response = await fetch('https://api.example.com/data');
  console.log(await response.json());
});

const apiFetch = proxy.createFetch('/api');
const directFetch = proxy.createFetch('/health');

SocketClient

SocketClient(fetchWithPayment, options) returns a client for opening paid WebSocket sessions through the Consensus Network. Token acquisition and reconnection are handled automatically.

SocketClient Options

| Option | Type | Default | Description | | --------------------- | ---------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------- | | openTimeoutMs | number | 12000 | Milliseconds to wait for the WebSocket connection to open before timing out. | | reconnectIntervalMs | number | 2000 | Milliseconds between automatic reconnection attempts. | | defaults | ConsensusSocketTokenParams | — | Default token parameters applied to every requestToken() call unless overridden. | | limit_usd | number | — | Max WebSocket spend in USD (up to 6 decimals). If next token quote exceeds remaining budget, token request is blocked. | | on_limit_reached | (budget) => void | — | Callback fired once when the WebSocket spend limit is reached. | | webSocketFactory | constructor | auto-detected | Custom WebSocket constructor (browser WebSocket or ws for Node.js). Auto-detected if not provided. |

WebSocket spend checks use a local quote from the known pricing model (model, minutes, megabytes) before token purchase, so there is no additional price-check round trip.

Billing Models

Token requests accept a model parameter to control how your session is billed:

| Model | Description | | ---------- | ------------------------------------------- | | "hybrid" | Billed by both time and data (default). | | "time" | Billed by duration only (minutes). | | "data" | Billed by data transfer only (megabytes). |

Basic Usage

import { SocketClient } from '@canister-software/consensus-cli';

const client = SocketClient(fetchWithPayment, {
  reconnectIntervalMs: 2000,
});

// Request a session token — pays for a time-based session
const auth = await client.requestToken({
  model: 'time',
  minutes: 5,
  megabytes: 0,
});

// Connect using the token — returns a managed session
const session = await client.connect(auth);

session.on('open', () => console.log('Connected'));
session.on('message', (msg) => console.log('Received:', msg));
session.on('error', (err) => console.error('Error:', err));
session.on('close', () => console.log('Disconnected'));

session.send('hello');

// Close when done
session.close();

Node Filtering

Target specific proxy nodes for WebSocket sessions:

const auth = await client.requestToken({
  model: 'hybrid',
  minutes: 10,
  megabytes: 100,
  nodeRegion: 'eu-west',
  nodeExclude: 'node.example.com',
});

Reconnection

Sessions reconnect automatically on unexpected disconnects. When reconnecting, SocketClient re-requests a fresh token using the same parameters from the last requestToken() call and re-establishes the WebSocket connection. Set reconnectIntervalMs to control retry pacing.

To stop reconnection, call session.close() — this sets an internal flag that suppresses all automatic retries.

Safe Mode

Both requestToken() and connect() support a { safe: true } option that catches errors and returns a result object instead of throwing:

const result = await client.requestToken({ model: 'time', minutes: 1 }, { safe: true });

if (!result.ok) {
  console.error('Token request failed:', result.error);
} else {
  const session = await client.connect(result.data);
}

Session State

const state = session.getState();
// { connected: boolean, reconnecting: boolean, closedByCaller: boolean }

CLI Commands

| Command | Description | | ------------------------- | ------------------------------------------------------------------ | | consensus setup | Create a wallet and register it with the x402 proxy. | | consensus setup --force | Force re-create the account, resetting any existing configuration. | | consensus help | Show help message. |


Setup Process

Run consensus setup to initialize your environment. The CLI will:

  1. Create a wallet using your CDP credentials.
  2. Generate .consensus-config.json containing your wallet and delegation credentials.
  3. Export wallet authorization to the x402 proxy for payment delegation.
  4. Add .consensus-config.json to .gitignore automatically to prevent accidental commits.

Security

⚠️ Never commit .consensus-config.json. It contains sensitive wallet credentials. The setup command adds it to .gitignore automatically, but verify this if you use a custom .gitignore setup.

DO NOT keep large amounts in the proxy-delegated wallet - if the proxy is compromised, your delegation could be at risk. Only fund the wallet with amounts you're comfortable delegating for API payments.