wayleave 0.5.4 → 0.5.5

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.
Files changed (3) hide show
  1. package/README.md +20 -1
  2. package/index.js +14 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -25,7 +25,7 @@ Then policy runs per lane: allow, deny, rate-limit — and on routes you price,
25
25
 
26
26
  Only the first lane is cryptographic. The other three are read off what the client says about itself, so a bot willing to lie reaches the human lane — see [Guarantees](#guarantees-honestly-stated), and set `strictPricedPaths` on anything you charge for.
27
27
 
28
- ## Two ways to use it
28
+ ## Three ways to use it
29
29
 
30
30
  **1. The hosted Identity API — no install, any language.** Post a request's
31
31
  signature headers and get back who signed it and which lane it belongs in.
@@ -54,6 +54,25 @@ way to know which to believe.
54
54
  you do not send. This is the path if you want to price routes, enforce access
55
55
  rules, or stay in-process. Start below.
56
56
 
57
+ **3. A platform adapter — if your code does not run on Node.** This package is
58
+ Express middleware and Node's crypto; it cannot run on a Worker or a Deno
59
+ edge function. These can, share one policy engine with it, and are configured
60
+ the same way:
61
+
62
+ | Where you run | Install |
63
+ |---|---|
64
+ | Cloudflare Workers | [`@wayleave/cloudflare`](https://www.npmjs.com/package/@wayleave/cloudflare) |
65
+ | Vercel middleware | [`@wayleave/vercel`](https://www.npmjs.com/package/@wayleave/vercel) |
66
+ | Supabase Edge Functions | [`@wayleave/supabase`](https://www.npmjs.com/package/@wayleave/supabase) |
67
+ | Netlify Edge Functions | [`@wayleave/netlify`](https://www.npmjs.com/package/@wayleave/netlify) |
68
+ | An MCP server | [`@wayleave/mcp`](https://www.npmjs.com/package/@wayleave/mcp) |
69
+
70
+ All four HTTP adapters default to observe mode and fail open, exactly as this
71
+ one does. The MCP guard is the odd one: an MCP server has no HTTP request to
72
+ inspect and MCP clients carry no signatures, so it does not classify lanes at
73
+ all — it bounds tool calls by rate, budget and repetition instead, and says
74
+ `classification: "unknown"` rather than guessing.
75
+
57
76
  ## Quickstart: observe first
58
77
 
59
78
  ```sh
package/index.js CHANGED
@@ -344,7 +344,18 @@ export function classify(req, directories, now, opts = {}) {
344
344
  if (h['signature'] || h['signature-input'])
345
345
  return { lane: LANES.SUSPECT, agentId: null,
346
346
  evidence: [`crypto: ${v.reason}`, 'presented invalid signature'] };
347
- if (AGENT_UA.some(p => p.test(ua))) {
347
+ /* WHICH pattern matched, not merely that one did.
348
+
349
+ This used to be `.some(...)`, and the evidence then quoted the first 40
350
+ characters of the user-agent. Almost every bot UA opens with a Mozilla
351
+ compatibility preamble and names itself at the END, so the quote cut off
352
+ exactly where the identifying part began: 486 crossings on one site all
353
+ recorded as `mozilla/5.0 applewebkit/537.36 (khtml, l`, indistinguishable
354
+ from each other and from a browser.
355
+
356
+ The matched pattern is the name. It was in hand and discarded. */
357
+ const agentMatch = AGENT_UA.map(p => (p.exec(ua) || [])[0]).find(Boolean);
358
+ if (agentMatch) {
348
359
  // A UA naming a known operator is a claim, and claims are checkable when
349
360
  // the operator publishes its addresses. Claiming to be GPTBot from an
350
361
  // address OpenAI does not own is a stronger fraud signal than saying
@@ -356,10 +367,10 @@ export function classify(req, directories, now, opts = {}) {
356
367
  : null;
357
368
  if (ipOk === false)
358
369
  return { lane: LANES.SUSPECT, agentId: null,
359
- evidence: [`UA claims "${ua.slice(0, 40)}"`,
370
+ evidence: [`UA claims "${agentMatch}"`,
360
371
  `but ${opts.ip || 'the client'} is not a published address for it`] };
361
372
  return { lane: LANES.DECLARED, agentId: null,
362
- evidence: [`self-identified in UA: "${ua.slice(0, 40)}"`,
373
+ evidence: [`self-identified as "${agentMatch}"`,
363
374
  ipOk === true ? 'source address matches published range'
364
375
  : 'no signature presented'] };
365
376
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wayleave",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "Know which AI agents cross your API \u2014 verified by signature, not guessed from an IP. Sorts every request into four lanes, prices the non-human ones with HTTP 402, and never charges a human. Zero dependencies.",
5
5
  "type": "module",
6
6
  "main": "index.js",