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

agent-challenge

v1.3.0

Published

Drop-in LLM authentication for any API endpoint. Reasoning puzzles that agents solve once, then pass through forever. Stateless HMAC tokens, no database.

Readme

📖 Full documentation, live demo, and interactive examples: challenge.llm.kaveenk.com


Why?

You built an API. Now bots are hitting it — not the smart kind, the dumb kind. Automated scripts cycling through endpoints, low-effort crawlers scraping your data, spammy throwaway clients burning through your resources.

Traditional CAPTCHAs block everyone who isn't a human in a browser. API keys require manual signup and approval flows. agent-challenge sits in the middle: it blocks automated scripts and low-capability bots while letting any competent LLM walk right through.

The challenge requires actual reasoning — reversing strings, solving arithmetic, decoding ciphers. A real language model handles it instantly. A curl loop can't.

  • ✅ GPT-4, Claude, Gemini, Llama — pass instantly
  • ❌ Automated scripts, spammy bots, dumb wrappers — blocked

It's the ultimate automated-script buster. If the other end can't think, it doesn't get in.

Install

npm install agent-challenge

Quick Start

import { AgentChallenge } from 'agent-challenge';

const ac = new AgentChallenge({ secret: 'your-secret-key-min-8-chars' });

// Drop this into any existing route — one line handles everything
app.post('/api/your-endpoint', (req, res) => {
  const result = ac.gateHttp(req.headers, req.body);
  if (result.status !== 'authenticated')
    return res.status(401).json(result);

  // Your existing logic — unchanged
  res.json({ data: 'protected stuff' });
});

That's it. Agents solve a reasoning puzzle once, get a permanent token, and pass through instantly on every future request.

How It Works

Agent                          Your API
  │                               │
  ├──POST /api/your-endpoint────►│
  │                               ├── gateSync() → no token
  │◄──401 { challenge_required }──┤
  │                               │
  │  LLM reads prompt, answers    │
  │                               │
  ├──POST { answer, token }─────►│
  │                               ├── gateSync() → correct!
  │◄──200 { token: "eyJpZ..." }───┤
  │                               │
  │  Saves token permanently      │
  │                               │
  ├──POST + Bearer eyJpZ...─────►│
  │                               ├── gateSync() → valid token
  │◄──200 { authenticated }───────┤   (instant, no puzzle)

The gateSync() API

One function, three modes:

| Arguments | What happens | Response | |-----------|-------------|----------| | (none) | New challenge | { status: "challenge_required", prompt, challenge_token } | | challengeToken + answer | Verify → issue token | { status: "authenticated", token: "eyJpZ..." } | | token | Validate saved token | { status: "authenticated" } |

Challenge Every Time (No Persistent Tokens)

Want agents to solve a challenge on every request? Disable persistent tokens:

const ac = new AgentChallenge({
  secret: 'your-secret',
  persistent: false,  // No tokens issued — challenge every time
});

When persistent: false:

  • Solving a challenge returns { status: "authenticated" } with no token
  • Passing a saved token returns an error
  • Every request requires solving a new puzzle

Agent-Only Mode (Block Humans)

Tight time limit + hard difficulty = only AI agents can pass:

const ac = new AgentChallenge({
  secret: 'your-secret',
  difficulty: 'agentic',  // multi-step chains — only top-tier LLMs pass
  ttl: 10,                // 10 seconds — humans can't
  persistent: false,      // challenge every request
});

A human can't decode a caesar cipher in 10 seconds. An LLM does it in under 2.

Configuration

const ac = new AgentChallenge({
  secret: 'your-secret',       // Required — HMAC signing key (min 8 chars)
  difficulty: 'medium',        // 'easy' | 'medium' | 'hard' | 'agentic' (default: 'easy')
  ttl: 300,                    // Challenge expiry in seconds (default: 300)
  types: ['rot13', 'caesar'],  // Restrict to specific types (optional)
  persistent: true,            // Issue permanent tokens (default: true)
});

12 Challenge Types

All use randomized inputs — no fixed word lists.

| Difficulty | Types | |-----------|-------| | Easy | reverse_string, simple_math, pattern, counting | | Medium | rot13, letter_position, extract_letters, sorting, binary | | Hard | caesar, word_math, transform |

Each type has 3–8 prompt templates with randomized phrasing.

Lower-Level API

const ac = new AgentChallenge({ secret: 'your-secret-key' });

// Create a challenge manually
const challenge = ac.createSync();
// challenge.prompt  → "Reverse the following string: NOHTYP"
// challenge.token   → "eyJ..."

// Verify an answer
const result = ac.verify(challenge.token, 'PYTHON');
// result.valid → true

// Create/verify persistent tokens directly
const token = ac.createToken('agent-name');
ac.verifyToken(token); // → true

Stateless Architecture

No database. Tokens are HMAC-SHA256 signed JSON:

base64url(payload).HMAC-SHA256(payload, secret)
  • Challenge tokens (ch_): 5-minute TTL, contain answer hash
  • Agent tokens (at_): Permanent, contain agent ID + timestamp
  • Can't be forged — HMAC catches tampering
  • No server-side storage needed

Also Available for Python

pip install agent-challenge
from agentchallenge import AgentChallenge

ac = AgentChallenge(secret="your-secret-key")
result = ac.gate(
    token=request.headers.get("Authorization", "").removeprefix("Bearer ") or None,
    challenge_token=request.json.get("challenge_token"),
    answer=request.json.get("answer"),
)

Docs & Demo

challenge.llm.kaveenk.com — Interactive docs with live demo

License

MIT