agent402-tollbooth 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/demo.js +58 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -10,6 +10,31 @@ Stack Overflow) shipped pay-per-crawl as a closed, fiat, you-must-be-on-our-CDN
|
|
|
10
10
|
feature. This is the open, crypto-native, run-it-yourself version — built on the
|
|
11
11
|
same hardened 402 + proof-of-work machinery as [Agent402](https://github.com/MikeyPetrillo/Agent402).
|
|
12
12
|
|
|
13
|
+
## See it work (one command)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx agent402-tollbooth # then, in the repo: npm run --prefix tollbooth demo
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
agent402-tollbooth — live pay-per-crawl demo
|
|
21
|
+
|
|
22
|
+
① A human opens the page (normal browser)
|
|
23
|
+
→ HTTP 200 FREE "📄 The Future of Machine Payments — full article text…"
|
|
24
|
+
Humans are never charged.
|
|
25
|
+
|
|
26
|
+
② An AI crawler hits the same page (ClaudeBot)
|
|
27
|
+
→ HTTP 402 Payment Required
|
|
28
|
+
pay with USDC: $0.002 USDC on base → 0x…
|
|
29
|
+
…or free with proof-of-work: a 18-bit sha256 puzzle
|
|
30
|
+
|
|
31
|
+
③ The crawler has no wallet, so it spends CPU instead
|
|
32
|
+
solved in 0.32s (nonce=100208)
|
|
33
|
+
→ HTTP 200 OK (paid via pow) "📄 The Future of Machine Payments — full article text…"
|
|
34
|
+
|
|
35
|
+
✓ Pay-per-crawl, end to end — humans free, bots pay (USDC or compute).
|
|
36
|
+
```
|
|
37
|
+
|
|
13
38
|
## Install
|
|
14
39
|
|
|
15
40
|
```bash
|
package/demo.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Live, narrated pay-per-crawl demo. Spins up a "premium content" site behind
|
|
2
|
+
// the tollbooth, then shows a human getting it free and an AI crawler paying to
|
|
3
|
+
// get through (here: by solving a proof-of-work, since that needs no wallet).
|
|
4
|
+
//
|
|
5
|
+
// node demo.js (or: npm run demo)
|
|
6
|
+
import express from "express";
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
import { createTollbooth } from "./index.js";
|
|
9
|
+
|
|
10
|
+
const c = { dim: "\x1b[2m", reset: "\x1b[0m", green: "\x1b[32m", yellow: "\x1b[33m", cyan: "\x1b[36m", bold: "\x1b[1m" };
|
|
11
|
+
const log = (s = "") => console.log(s);
|
|
12
|
+
const lz = (buf) => { let b = 0; for (const x of buf) { if (x === 0) { b += 8; continue; } b += Math.clz32(x) - 24; break; } return b; };
|
|
13
|
+
|
|
14
|
+
// A "premium content" site, fronted by the tollbooth.
|
|
15
|
+
const app = express();
|
|
16
|
+
app.use(createTollbooth({ price: "$0.002", payTo: "0xYourWalletHere000000000000000000000000", powDifficulty: 18 }));
|
|
17
|
+
app.use((_req, res) => res.send("📄 The Future of Machine Payments — full article text…"));
|
|
18
|
+
const server = app.listen(0);
|
|
19
|
+
const base = `http://localhost:${server.address().port}`;
|
|
20
|
+
const visit = (ua, extra = {}) => fetch(`${base}/article`, { headers: { "user-agent": ua, ...extra } });
|
|
21
|
+
|
|
22
|
+
const HUMAN = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36";
|
|
23
|
+
const CRAWLER = "Mozilla/5.0 (compatible; ClaudeBot/1.0; +claudebot@anthropic.com)";
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
log(`${c.bold}agent402-tollbooth — live pay-per-crawl demo${c.reset}`);
|
|
27
|
+
log(`${c.dim}A premium-content site is now running behind the tollbooth.${c.reset}\n`);
|
|
28
|
+
|
|
29
|
+
// 1) A human browses — free.
|
|
30
|
+
log(`${c.cyan}① A human opens the page (normal browser)${c.reset}`);
|
|
31
|
+
let r = await visit(HUMAN);
|
|
32
|
+
log(` → HTTP ${r.status} ${c.green}FREE${c.reset} "${await r.text()}"`);
|
|
33
|
+
log(` ${c.dim}Humans are never charged.${c.reset}\n`);
|
|
34
|
+
|
|
35
|
+
// 2) An AI crawler hits the same page — 402.
|
|
36
|
+
log(`${c.cyan}② An AI crawler hits the same page (ClaudeBot)${c.reset}`);
|
|
37
|
+
r = await visit(CRAWLER);
|
|
38
|
+
const q = await r.json();
|
|
39
|
+
const a = q.accepts?.[0];
|
|
40
|
+
log(` → HTTP ${r.status} ${c.yellow}Payment Required${c.reset}`);
|
|
41
|
+
if (a) log(` ${c.dim}pay with USDC:${c.reset} ${a.maxAmountRequired} ${a.asset} on ${a.network} → ${a.payTo}`);
|
|
42
|
+
log(` ${c.dim}…or free with proof-of-work:${c.reset} a ${q.proofOfWork.difficulty}-bit sha256 puzzle\n`);
|
|
43
|
+
|
|
44
|
+
// 3) The crawler has no wallet, so it solves the proof-of-work.
|
|
45
|
+
log(`${c.cyan}③ The crawler has no wallet, so it spends CPU instead${c.reset}`);
|
|
46
|
+
const t0 = Date.now();
|
|
47
|
+
let nonce = 0;
|
|
48
|
+
while (lz(createHash("sha256").update(`${q.proofOfWork.challenge}:${nonce}`).digest()) < q.proofOfWork.difficulty) nonce++;
|
|
49
|
+
log(` ${c.dim}solved in ${((Date.now() - t0) / 1000).toFixed(2)}s (nonce=${nonce})${c.reset}`);
|
|
50
|
+
|
|
51
|
+
// 4) Retry with the solution — unlocked.
|
|
52
|
+
r = await visit(CRAWLER, { "x-pow-solution": `${q.proofOfWork.token}:${nonce}` });
|
|
53
|
+
log(` → HTTP ${r.status} ${c.green}OK${c.reset} (paid via ${r.headers.get("x-tollbooth-paid")}) "${await r.text()}"\n`);
|
|
54
|
+
|
|
55
|
+
log(`${c.bold}${c.green}✓ Pay-per-crawl, end to end${c.reset} — humans free, bots pay (USDC or compute). No Cloudflare, no Stripe, no signup.`);
|
|
56
|
+
} finally {
|
|
57
|
+
server.close();
|
|
58
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent402-tollbooth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Open-source, self-hostable x402 pay-per-crawl tollbooth: charge AI bots per request (USDC via x402, or free proof-of-work) while humans browse free. Drop-in Express middleware or reverse proxy — no Cloudflare, no Stripe, no signup.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"bots.js",
|
|
16
16
|
"pow.js",
|
|
17
17
|
"example.js",
|
|
18
|
+
"demo.js",
|
|
18
19
|
"README.md"
|
|
19
20
|
],
|
|
20
21
|
"engines": {
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
},
|
|
23
24
|
"scripts": {
|
|
24
25
|
"test": "node test.js",
|
|
26
|
+
"demo": "node demo.js",
|
|
25
27
|
"example": "node example.js"
|
|
26
28
|
},
|
|
27
29
|
"dependencies": {
|