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

@px402/core

v0.1.1

Published

px402 core: HMAC payment tokens, clientRefId-based verification, rate limiting, base-chain polling subscriber

Readme

@px402/core

Framework-agnostic primitives for px402: private agentic-payment HTTP gating on Solana via MagicBlock PER.

You usually don't install this directly. Pick a framework adapter:

Use @px402/core directly only when you're writing a custom framework adapter or running the subscriber outside an HTTP server.

Install

pnpm add @px402/core

What's in here

| Export | Purpose | |---|---| | createHandler(config) | Framework-agnostic decision engine. Returns decide(ctx){ kind: "next" } or { kind: "respond", status, headers, body }. | | PrivateTransferSubscriber | Polls the MagicBlock queue PDA on the base chain, parses ExecuteReadyQueuedTransfer logs + token-balance deltas, and indexes verified transfers by clientRefId. | | createPaymentToken / verifyPaymentToken | HMAC-signed v1.<payload>.<hmac> tokens so the server stays stateless across the pay-then-retry window. | | verifyPayment | Match an incoming retry against the subscriber's verified-transfer index. | | RateLimiter | LRU-backed per-IP and per-wallet limiter. | | deriveQueuePda, deriveEphemeralAta, SPL_PP_PROGRAM_ID | PDA helpers for MagicBlock's private payments program. | | normalizePath | Strip query string and trailing slash before pricing lookup. |

Custom adapter sketch

import { createHandler, normalizePath } from "@px402/core";

const handler = createHandler({
  serverSecret: process.env.PX402_SERVER_SECRET!,
  paymentAddress: SERVER_WALLET_PUBKEY,
  pricing: { "/api/sentiment": "10000" },
  subscriber, // a PrivateTransferSubscriber instance
});

// In your framework's request hook:
const decision = handler.decide({
  path: normalizePath(req.path),
  ip: clientIp(req),
  paymentId: req.headers["x-payment-id"],
  paymentToken: req.headers["x-payment-token"],
});

if (decision.kind === "next") {
  // payment verified or no payment required
  return next();
}
// otherwise: write decision.headers, status, body

Crash-safe watermark persistence

The subscriber tracks a "watermark" — the signature of the most recent tx it indexed on the queue PDA. On a clean boot it seeds the watermark with the current chain tip, which means payments that landed during a server crash or redeploy are dropped.

To survive restarts, persist the watermark and pass it back on the next boot:

import { PrivateTransferSubscriber, deriveQueuePda } from "@px402/core";
import { promises as fs } from "node:fs";

const WATERMARK_PATH = "/var/lib/px402/watermark";

async function loadWatermark(): Promise<string | undefined> {
  try {
    return (await fs.readFile(WATERMARK_PATH, "utf8")).trim();
  } catch {
    return undefined;
  }
}

const subscriber = new PrivateTransferSubscriber({
  // Base-chain RPC. The crank executes ExecuteReadyQueuedTransfer here.
  rpcUrl: "https://rpc.magicblock.app/devnet",
  queuePda: deriveQueuePda(MINT, VALIDATOR).toBase58(),
  mint: MINT,
  receiverWallet: SERVER_WALLET,
  initialWatermark: await loadWatermark(),
  onWatermarkAdvance: async (sig) => {
    await fs.writeFile(WATERMARK_PATH, sig);
  },
});
await subscriber.start();

On restart, the subscriber backfills from the persisted watermark instead of the current tip, so a payment that landed in the crash window will be picked up on the next poll.

The callback errors are caught and re-emitted on error — the subscriber keeps polling even if your persistence layer is broken, so payment verification doesn't stop with disk full.

getWatermark() is also exposed for health endpoints and external snapshotting.

Graceful shutdown

subscriber.stop() is async. It signals the poll loop to halt, aborts any in-flight RPC fetch via AbortController, and resolves once the current poll has settled (or after timeoutMs, default 5s). Pair it with your HTTP server's drain step:

process.on("SIGTERM", async () => {
  await subscriber.stop();
  await server.close();
  process.exit(0);
});

After stop() resolves, no further tick / error / stalled events will fire.

RPC failure handling

The subscriber polls a single rpcUrl. Transient errors (HTTP 5xx, network flap, JSON-RPC error) are caught and the poll re-scheduled — payments aren't dropped, just delayed by one poll interval (~500 ms).

If polls keep failing for STALLED_THRESHOLD_MS (30s, not configurable in v0.1), the stalled event fires. Wire it to your alerting:

subscriber.on("stalled", ({ lastSuccessfulPollAt, error }) => {
  metrics.gauge("px402.subscriber.stall_age_ms", Date.now() - lastSuccessfulPollAt);
  pager.page("px402 subscriber stalled", { error: error.message });
});

Single-RPC failure model is intentional for v0.1 — most production deploys front their MagicBlock endpoint with Helius or Triton anyway and rely on the provider's own failover. Multi-RPC pool with health-checking is on the v0.2 roadmap (see the root README's "Limitations & roadmap" section).

If your RPC has a persistent outage, redeploy with a new rpcUrl env var — the persisted watermark (above) ensures no payments are dropped during the swap.

Observability hooks

The subscriber extends EventEmitter with four events:

| Event | Fires on | Use | |---|---|---| | ready | initial watermark seed complete | wire to readiness probe | | tick | a verified transfer indexed | normally consumed inside the middleware | | error | chunk-apply failure or watermark-persist failure | Sentry / Datadog / structured log | | stalled | 30s of consecutive failed polls | alerting; rotate rpcUrl if persistent |

subscriber.on("error", (err) => Sentry.captureException(err));
subscriber.on("stalled", (e) => log.error("subscriber stalled", e));

Operations

Errors

InvalidTokenError, ExpiredTokenError, ReplayError — all extend Px402Error with a code string.

License

MIT