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

@proofofbot/client-kasada

v3.1.1

Published

Runs a Kasada interrogation headlessly and carries the token the edge issues

Readme

@proofofbot/client-kasada

Generated client for the kasada target. It talks to a wred sidecar over a pipe.

Runs a Kasada interrogation headlessly and carries the token the edge issues

Install

npm install @proofofbot/client-kasada

The binary for your platform arrives as an optional dependency. Set WRE_BINARY to an absolute path to override it with a local build.

Use

import { KasadaClient } from "@proofofbot/client-kasada";

const client = await KasadaClient.open({});
const result = await client.solve({ url: "https://example.com", wait_ms: 1 });
console.log(result);
await client.close();

One client owns one session, which owns the mounted realm. Open it once and reuse it. Opening one per call pays the warmup cost every time.

How a run works

solve is the whole flow. It fetches the url, and if the edge answers with the interrogation page instead of the page you asked for, it reads the ips.js the page names, runs that script unmodified in the sandbox, and lets the script post its own payload to /tl. The token the edge answers with comes back as token.

The token is bound to the KP_UIDz cookie the interstitial set, so solve against the url you actually want. request then sends your request through the same jar, transport fingerprint and user agent, which is the point.

discover is the cheap look: it fetches the page and reports the tenant path, the build version and whether an interrogation is being served, without running anything.

The proof of work

Sites that turn it on want an x-kpsdk-cd header on every stamped request. Two ways to get one. pow computes it in Rust from a token and one of the loader's salts. loader mounts the site's own p.js in the same realm and request with stamped set sends through it, so the loader builds the header itself and a rebuild cannot drift from it.

Sessions

A session is a client: one jar, one realm, one transport fingerprint. Keep it open across calls rather than opening one per call. reset drops the realm.

Profiles

The interrogation enumerates the whole global surface, so this client mounts a graph profile: a captured object graph rather than a table of readings. Capture one with wre sandbox capture --graph, list them with wre sandbox list, then name one in profile. Without one, solve fails and says so; discover, request and pow still work, because none of them mount anything.

misses reports what the run asked for and the graph could not answer, separately from the receiver checks that fired on purpose.

Configuration

These fields go in the object you pass to open. The session keeps them for its whole life.

| Field | Type | Default | Description | | --- | --- | --- | --- | | page_url | string \| null | optional | Page the session solves for when an op does not name one | | profile | string \| null | optional | Graph profile id, from wre sandbox list | | proxy | string \| null | optional | Proxy url the session and the sandbox both go through, http or socks5 | | fingerprint | string \| null | optional | Transport fingerprint as profile[:platform], defaults to the sandbox profile's user agent | | user_agent | string \| null | optional | Overrides the user agent the sandbox profile carries | | wait_ms | number | 20000 | How long to let the agent run before giving up on a token | | step_ms | number | 100 | How often the timer queue is drained while the agent runs | | paced | boolean | true | Spend the wait in real time so the payload's clock matches the edge's | | friction_ms | number | 0.12 | Virtual cost of one DOM operation | | report | boolean | false | Let the agent's self report reach reporting.cdndex.io. Off by default: it is held back and decoded locally, which is what the report op reads | | version | string | "j-1.2.661" | Build version to claim when the page names none | | timeout_ms | number | 90000 | Cap on one http request the session makes | | frames | number | 4 | Child realms opened up front for the iframes the agent creates | | capture_vector | boolean | false | Keep the signal vector the agent built before it sealed it, for the vector op | | seed | number \| null | optional | Seed the random source |

Operations

client.info(options)

What this build is carrying

Takes no arguments.

Returns:

| Field | Type | Description | | --- | --- | --- | | target | string | - | | version | string | - | | profile | string | Graph profile this session mounts | | profiles | Array<string> | Every graph profile id the workspace holds | | user_agent | string | - | | fingerprint | string | Transport fingerprint, or where it is being derived from | | open | boolean | Whether a session is open |

client.discover(params, options)

Report the Kasada wiring of a page without running anything

Server side deadline: 45 s. Pass a shorter one per call to cap it further.

Takes:

| Field | Type | Default | Description | | --- | --- | --- | --- | | url | string \| null | optional | Page to fetch, defaults to page_url from the config |

Returns:

| Field | Type | Description | | --- | --- | --- | | url | string | Url after redirects | | status | number | - | | protected | boolean | Whether the edge answered with an interrogation | | surface | unknown | Tenant path, build version, the agent script and whether the page configures endpoints | | cookies | unknown | Jar after the fetch |

client.solve(params, options)

Run the interrogation and carry the token the edge issues

Server side deadline: 2 min. Pass a shorter one per call to cap it further.

Streams: progress.

Takes:

| Field | Type | Default | Description | | --- | --- | --- | --- | | url | string \| null | optional | Page to solve for, defaults to page_url from the config | | wait_ms | number \| null | optional | Overrides how long the agent is left running |

Returns:

| Field | Type | Description | | --- | --- | --- | | verdict | string | solved, unsolved or none | | token | string \| null | The x-kpsdk-ct the edge issued | | clearance | string \| null | The x-kpsdk-r the edge answered with | | url | string | - | | agent | unknown | The script that ran, and its size | | payload_bytes | number | Size of the body the agent posted to /tl | | sent | unknown | Every request the sandbox made | | misses | Array<string> | Surfaces the run asked for that the graph could not answer | | cookies | unknown | - | | ms | number | - |

client.request(params, options)

Send a request carrying the session's token, cookies and transport

Server side deadline: 1 min. Pass a shorter one per call to cap it further.

Takes:

| Field | Type | Default | Description | | --- | --- | --- | --- | | url | string | required | - | | method | string | "GET" | - | | headers | Record<string, string> \| null | optional | - | | body | string \| null | optional | - | | token | boolean | true | Carry the solved token as headers and as the KP_UIDz cookie | | stamped | boolean | false | Send from inside the realm so the mounted loader stamps it, proof of work included |

Returns:

| Field | Type | Description | | --- | --- | --- | | status | number | - | | url | string | - | | headers | Record<string, string> | - | | bytes | number | - | | body | string | - |

client.loader(params, options)

Mount the site's own p.js so it stamps requests the way a page does

Server side deadline: 45 s. Pass a shorter one per call to cap it further.

Takes:

| Field | Type | Default | Description | | --- | --- | --- | --- | | endpoints | Array<unknown> \| null | optional | KPSDK.configure entries, defaults to every path on the page's host |

Returns:

| Field | Type | Description | | --- | --- | --- | | url | string | - | | bytes | number | - | | threw | string \| null | - |

client.pow(params, options)

Build an x-kpsdk-cd header for a token

Server side deadline: 30 s. Pass a shorter one per call to cap it further.

Takes:

| Field | Type | Default | Description | | --- | --- | --- | --- | | salt | string | required | One of the 64 character hex constants in the loader | | token | string \| null | optional | Token to bind to, defaults to the solved one | | difficulty | number | 10.0 | - | | count | number | 2 | - | | st | number \| null | optional | Server clock sample, defaults to the one /tl answered with |

Returns:

| Field | Type | Description | | --- | --- | --- | | header | string | Ready for x-kpsdk-cd | | answers | Array<number> | - | | work_time | number | - | | id | string | - | | duration | number | - |

client.payload(options)

The encrypted body the agent posted

Takes no arguments.

Returns:

| Field | Type | Description | | --- | --- | --- | | bytes | number | - | | body | string \| null | The /tl body the agent built, base64 |

client.vector(options)

The signal vector behind the payload, when capture_vector is on

Takes no arguments.

Returns:

| Field | Type | Description | | --- | --- | --- | | slots | number | - | | vector | unknown | The signal array, as the agent built it | | agent | string | The build that produced it, so a replay lines up slot for slot |

client.report(options)

Decode the report the agent writes about itself

Takes no arguments.

Returns:

| Field | Type | Description | | --- | --- | --- | | posted | number | How many self reports the agent tried to send | | about | unknown | Build tag, version and origin | | flagged | unknown | Checks the agent flagged, with what each said | | raw | unknown | The decoded report in full |

client.cookies(options)

Read the session jar

Takes no arguments.

Returns:

| Field | Type | Description | | --- | --- | --- | | pairs | Record<string, string> | - | | header | string | Ready for a Cookie header |

client.misses(options)

What the sandbox could not answer, and what it refused on purpose

Takes no arguments.

Returns:

| Field | Type | Description | | --- | --- | --- | | misses | Array<string> | Asked for and not answered | | guards | Array<string> | Receiver checks that fired, which is a browser's answer too |

client.reset(params, options)

Drop the realm and start a new session

Takes:

| Field | Type | Default | Description | | --- | --- | --- | --- | | cookies | boolean | false | Empty the jar as well |

Returns:

| Field | Type | Description | | --- | --- | --- | | ok | boolean | - |

Events

solve streams progress while it runs. A session is not an emitter, so pass onEvent when you open the client to see every event, or per call to scope it to one:

const client = await KasadaClient.open({}, {
  onEvent: (id, event, data) => console.log(event, data),
});

await client.solve({ url: "https://example.com", wait_ms: 1 }, {
  onEvent: (id, event, data) => console.log(event, data),
});

| Event | Data | Description | | --- | --- | --- | | progress | Progress | Where a solve has got to |

Deadlines and cancellation

Every op takes a second options argument: deadlineMs caps the call and fails it with kind === "timeout", signal takes an AbortSignal and fails it with kind === "cancelled". Both stop the work inside the sidecar, they do not only abandon the promise.

const abort = new AbortController();
setTimeout(() => abort.abort(), 5000);
await client.solve({ url: "https://example.com", wait_ms: 1 }, { signal: abort.signal, deadlineMs: 120000 });

Errors

Every rejection is a WreError with a stable kind: bad_input, unsupported, target_drift, blocked, timeout, cancelled, resource, protocol, internal. Branch on kind, never on the message. error.retryable says whether the same call is worth repeating.

Sidecar output and diagnostics

The sidecar logs to its own stderr, which is discarded by default. Pass { stderr: "inherit" } to open, or set WRE_STDERR=inherit, to see it.

A failing call writes a diagnostic report and puts its path in error.detail.diagnostics. WRE_DIAG=always records every call, WRE_DIAG=off records none, and await client.diagnose(true) writes one on demand. Send that file with a bug report.

Pinned build

  • bundle default
  • binary version 0.1.0
  • schema hash 26bd761bdc493953

The schema hash is checked at connect time. A mismatch means this package and the installed binary disagree about the callable surface, and the connect call fails.