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

@btx-tools/middleware-hono

v1.1.0

Published

Hono middleware for @btx-tools/challenges-sdk — drop-in BTX service-challenge admission gate (Node + edge)

Downloads

621

Readme

@btx-tools/middleware-hono

Drop-in Hono admission gate backed by BTX service challenges. Works on Node, Deno, Bun, Cloudflare Workers, and other edge runtimes Hono targets. Same flow + ergonomics as @btx-tools/middleware-express and @btx-tools/middleware-fastify, tailored to Hono's middleware model + c.set('btx', ...) variables.

📖 API Reference — TypeDoc for all @btx-tools/* SDK packages.

New to BTX service challenges? This puts a chain-anchored proof-of-work checkpoint in front of a route — the caller spends a few seconds of verifiable compute instead of a CAPTCHA or a signup. Concept + issue → solve → redeem flow: see the core SDK README.

Prerequisites: you need a reachable BTX node (btxd) — non-mining for fast (~1–4 s) solves; there's no hosted endpoint, so you can't use this with zero BTX infrastructure. (Edge note: a Worker/edge runtime can't reach 127.0.0.1 — point at a public/tunnelled btxd.) See core → Prerequisites.

End-to-end example: a runnable adopter example is in examples/02-express-gate (Express-based; the wiring shape is structurally identical for Hono — swap the route + middleware call). A Hono-native parity example covering both Node and edge deploy is queued for the SDK Phase 3.5 roadmap.

pnpm add @btx-tools/middleware-hono @btx-tools/challenges-sdk hono

Quickstart

import { Hono } from 'hono';
import { BtxChallengeClient } from '@btx-tools/challenges-sdk';
import { btxAdmission, type BtxAdmissionVariables } from '@btx-tools/middleware-hono';

const client = new BtxChallengeClient({
  rpcUrl: 'http://127.0.0.1:19334',
  rpcAuth: { user: 'rpcuser', pass: 'rpcpass' },
});

const app = new Hono<{ Variables: BtxAdmissionVariables }>();

app.post(
  '/v1/generate',
  btxAdmission({
    client,
    purpose: 'ai_inference_gate',
    resource: (c) => `route:${c.req.path}`,
    subject: async (c) => `tenant:${(await c.req.json()).tenant_id}`,
    issueParams: { target_solve_time_s: 1.0, expires_in_s: 60 },
    onError: (err, c) => c.var.logger?.error({ err }, 'btx admission error'),
  }),
  async (c) => {
    const admit = c.get('btx').result;
    return c.json({ ok: true, reason: admit.reason });
  },
);

export default app;

⚠️ Body consumption (read before async resolvers)

Hono's c.req.json() is one-shot — once consumed, the body stream is gone. If your resource / subject resolver does await c.req.json(), the route handler downstream cannot read the body again and will throw BodyAlreadyUsedError.

This breaks:

(btxAdmission({
  // ...
  resource: async (c) => `model:${(await c.req.json()).model}`,
}),
  async (c) => {
    const body = await c.req.json(); // ← throws — body already consumed!
    return c.json({ ok: true });
  });

Two safe patterns:

// Pattern 1: cache the body once at the top, pass through context
app.post('/v1/generate', async (c, next) => {
  c.set('body', await c.req.json());
  return next();
});
app.post('/v1/generate',
  btxAdmission({
    // ...
    resource: (c) => `model:${(c.get('body') as { model: string }).model}`,
  }),
  async (c) => {
    const body = c.get('body');
    return c.json({ ok: true, body });
  },
);

// Pattern 2: derive resolver inputs from headers, not body
btxAdmission({
  // ...
  resource: (c) => `model:${c.req.header('x-model') ?? 'default'}`,
}),

How it works

Stateless echo-the-challenge flow:

  1. First request has no proof headers → middleware calls client.issue() → replies 402 Payment Required with X-BTX-Challenge header containing the challenge JSON + a body listing the headers the client should add on retry.
  2. Client solves the challenge (locally or via RPC) and retries with X-BTX-Challenge (echoed), X-BTX-Proof-Nonce, X-BTX-Proof-Digest.
  3. Middleware calls client.redeem() → if result.valid === true, sets c.set('btx', { result }) and yields to await next() (route handler runs). Else replies 403.

No server-side challenge store. Scales horizontally; the challenge JSON rides in the X-BTX-Challenge header on retry (~3-5 KB). Check edge-runtime header-size limits — Cloudflare Workers and Fastly accept large headers, but Vercel Edge caps at smaller sizes.

API

btxAdmission(opts): MiddlewareHandler

Returns a Hono middleware function to attach per-route.

Options

| Field | Type | Notes | | ---------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | client | BtxChallengeClient | required. Construct once at boot. | | purpose | string \| (c) => string \| (c) => Promise<string> | required. Logical purpose label. Async resolver supported so you can await c.req.json(). | | resource | string \| (c) => string \| (c) => Promise<string> | required. | | subject | string \| (c) => string \| (c) => Promise<string> | required. | | issueParams | Partial<IssueParams> | optional. | | onAdmit | (c, result) => void | optional. Fires on successful admission. | | onError | (err, c) => void | optional. Fires when client.issue() or client.redeem() throws. Re-thrown to Hono's onError. Audit ref: D-1. | | isProofPresent | (c) => boolean | optional. Predicate override. |

BtxAdmissionVariables

Type the Hono instance with this for c.get('btx') type narrowing:

const app = new Hono<{ Variables: BtxAdmissionVariables }>();

After admission, c.get('btx') is { result: VerifyResult } | undefined.

Header constants

| Constant | Value | | --------------------- | ---------------------- | | HEADER_CHALLENGE | 'x-btx-challenge' | | HEADER_CHALLENGE_ID | 'x-btx-challenge-id' | | HEADER_PROOF_NONCE | 'x-btx-proof-nonce' | | HEADER_PROOF_DIGEST | 'x-btx-proof-digest' |

Error handling

When client.issue() or client.redeem() throws (e.g., btxd RPC down, network error), the middleware:

  1. Calls opts.onError(err, c) if provided
  2. Re-throws — Hono's app.onError() handler kicks in

Use app.onError() to map BTX errors to your preferred response shape:

app.onError((err, c) => {
  if (err instanceof BtxNetworkError) return c.json({ error: 'btxd unreachable' }, 503);
  return c.json({ error: 'internal' }, 500);
});

Edge-runtime notes

Network reachability

BtxChallengeClient uses fetch() to reach btxd's JSON-RPC endpoint. Edge runtimes cannot reach 127.0.0.1 — they're sandboxed away from the host loopback. You need a publicly reachable btxd RPC URL:

  • Cloudflare Tunnel (Argo Tunnel) — runs in front of your btxd, gives you a stable HTTPS URL the Worker can call
  • Public RPC proxy — terminate TLS at Caddy/nginx in front of btxd, expose on a real DNS name
  • Self-hosted relay with a public IP + Basic auth (verify rpcallowip in btx.conf permits the egress IP)

Do not put btxd's RPC port directly on the public internet without auth + TLS termination.

Runtime-specific

  • Cloudflare Workers / Pages: works once reachability is solved. fetch() is native; no Node polyfills needed.
  • Deno Deploy: same — Web fetch() is standard.
  • Bun: works natively (also accepts a Node btxd via localhost when self-hosting Bun on the same box).
  • Vercel Edge: works for typical challenge sizes. Header-size limits vary across edge platforms — Vercel, Cloudflare, and Fastly all have different caps for incoming headers. The X-BTX-Challenge header is ~3-5 KB for default difficulty; check your platform's documentation if you set high target_solve_time_s or run into preflight errors. For very large challenges, consider a stateful challenge-store middleware variant.

CORS

The X-BTX-Challenge, X-BTX-Proof-Nonce, and X-BTX-Proof-Digest headers are custom, which triggers a CORS preflight for any browser-originated fetch. Configure Hono's built-in cors middleware:

import { cors } from 'hono/cors';
app.use(
  '/v1/*',
  cors({
    origin: 'https://your-frontend.example',
    allowHeaders: [
      'content-type',
      'x-btx-challenge',
      'x-btx-challenge-id',
      'x-btx-proof-nonce',
      'x-btx-proof-digest',
    ],
    exposeHeaders: [
      'x-btx-challenge', // so the browser can READ the 402's challenge header
    ],
  }),
);

Without exposeHeaders including x-btx-challenge, the browser sees the 402 status but cannot read the challenge JSON from the response header (Web Fetch hides non-CORS-safelisted response headers by default).

Requirements

  • Node.js ≥ 18.17 (when running on Node)
  • Hono ^4.0.0 (peer dep)
  • @btx-tools/challenges-sdk ^0.0.4 (peer dep)

License

MIT. See LICENSE.