wayleave 0.1.2 → 0.1.3
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 +5 -1
- package/index.d.ts +179 -0
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# wayleave
|
|
2
2
|
|
|
3
|
+
[](https://github.com/gibrancorbin11-hub/wayleave/actions/workflows/test.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/wayleave)
|
|
5
|
+
[](https://www.npmjs.com/package/wayleave)
|
|
6
|
+
|
|
3
7
|
**Charge AI agents for passage across your app.**
|
|
4
8
|
|
|
5
9
|
A *wayleave* is the fee paid for the right of passage across private land — utility companies have paid them for 150 years to run cables through property. AI agents are about to cross your app millions of times for free. Charge them a wayleave.
|
|
6
10
|
|
|
7
|
-
Zero dependencies. Node's native crypto only.
|
|
11
|
+
Zero dependencies. Node's native crypto only. TypeScript declarations included.
|
|
8
12
|
|
|
9
13
|
## What it does
|
|
10
14
|
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wayleave — charge AI agents for passage across your app.
|
|
3
|
+
*
|
|
4
|
+
* Verifies Web Bot Auth signatures (RFC 9421 profile, Ed25519), classifies
|
|
5
|
+
* every request into a lane, applies per-lane policy, and returns 402 with an
|
|
6
|
+
* x402-shaped challenge on routes you price. Humans browse free.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A Node `KeyObject` from `node:crypto`. Typed structurally rather than
|
|
11
|
+
* imported so this declaration file needs no @types/node — the package has
|
|
12
|
+
* zero dependencies and the types shouldn't reintroduce one. A real
|
|
13
|
+
* KeyObject satisfies this.
|
|
14
|
+
*/
|
|
15
|
+
export interface KeyObjectLike {
|
|
16
|
+
readonly type: string;
|
|
17
|
+
readonly asymmetricKeyType?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The four lanes every request is sorted into. */
|
|
21
|
+
export type Lane =
|
|
22
|
+
| 'verified_agent'
|
|
23
|
+
| 'declared_agent'
|
|
24
|
+
| 'suspected_bot'
|
|
25
|
+
| 'human';
|
|
26
|
+
|
|
27
|
+
export const LANES: Readonly<{
|
|
28
|
+
VERIFIED: 'verified_agent';
|
|
29
|
+
DECLARED: 'declared_agent';
|
|
30
|
+
SUSPECT: 'suspected_bot';
|
|
31
|
+
HUMAN: 'human';
|
|
32
|
+
}>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A public key for a signature agent. Accepts a KeyObject, a PEM string, or a
|
|
36
|
+
* bare base64 raw Ed25519 public key (32 bytes, SPKI prefix added for you).
|
|
37
|
+
* Strings are converted to a KeyObject and cached on first use.
|
|
38
|
+
*/
|
|
39
|
+
export type PublicKeyLike = KeyObjectLike | string;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Key directories you trust, keyed by the URL an agent presents in its
|
|
43
|
+
* `signature-agent` header, then by keyid.
|
|
44
|
+
*
|
|
45
|
+
* Note: these are read from config, not fetched. Rotation is not handled.
|
|
46
|
+
*/
|
|
47
|
+
export type KeyDirectories = Record<string, Record<string, PublicKeyLike>>;
|
|
48
|
+
|
|
49
|
+
/** Incoming request, normalised. Framework-agnostic on purpose. */
|
|
50
|
+
export interface WayleaveRequest {
|
|
51
|
+
method: string;
|
|
52
|
+
path: string;
|
|
53
|
+
/** Host authority the signature was computed over, e.g. "example.com". */
|
|
54
|
+
authority: string;
|
|
55
|
+
headers: Record<string, string | string[] | undefined>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface VerifyResult {
|
|
59
|
+
ok: boolean;
|
|
60
|
+
/** `${directoryUrl}#${keyid}` when ok, otherwise null. */
|
|
61
|
+
agentId: string | null;
|
|
62
|
+
/** Human-readable reason, safe to log. */
|
|
63
|
+
reason: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface Classification {
|
|
67
|
+
lane: Lane;
|
|
68
|
+
agentId: string | null;
|
|
69
|
+
/** Why this lane was chosen. Loggable. */
|
|
70
|
+
evidence: string[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** x402-shaped payment challenge returned with a 402. */
|
|
74
|
+
export interface PaymentChallenge {
|
|
75
|
+
scheme: 'x402';
|
|
76
|
+
price_usd: number;
|
|
77
|
+
/** The priced path prefix that matched. */
|
|
78
|
+
resource: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface Decision {
|
|
82
|
+
lane: Lane;
|
|
83
|
+
/** agentId when verified, else `${lane}:${xff ?? 'unknown'}`. */
|
|
84
|
+
identity: string;
|
|
85
|
+
status: 200 | 402 | 403 | 429;
|
|
86
|
+
/** Why this status was returned. */
|
|
87
|
+
why: string;
|
|
88
|
+
evidence: string[];
|
|
89
|
+
/** Present only on 402. */
|
|
90
|
+
challenge?: PaymentChallenge;
|
|
91
|
+
/** Present only when a priced request was paid. */
|
|
92
|
+
billed?: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** What the metering hook receives. It may throw; serving continues. */
|
|
96
|
+
export interface MeterEvent {
|
|
97
|
+
/** Unix seconds. */
|
|
98
|
+
t: number;
|
|
99
|
+
path: string;
|
|
100
|
+
lane: Lane;
|
|
101
|
+
identity: string;
|
|
102
|
+
status: number;
|
|
103
|
+
why: string;
|
|
104
|
+
evidence: string[];
|
|
105
|
+
billedUsd: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** `[pathPrefix, allow]` — first matching prefix wins. */
|
|
109
|
+
export type PolicyRule = [prefix: string, allow: boolean];
|
|
110
|
+
|
|
111
|
+
export interface WayleaveOptions {
|
|
112
|
+
directories?: KeyDirectories;
|
|
113
|
+
/** Per-lane allow/deny rules, evaluated in order. */
|
|
114
|
+
rules?: Partial<Record<Lane, PolicyRule[]>>;
|
|
115
|
+
/** Max requests per identity per window, per lane. */
|
|
116
|
+
rateLimits?: Partial<Record<Lane, number>>;
|
|
117
|
+
/** Window length in seconds. Default 60. */
|
|
118
|
+
rateWindow?: number;
|
|
119
|
+
/** Path prefix -> price in USD. Non-human lanes get 402 here. */
|
|
120
|
+
pricedPaths?: Record<string, number>;
|
|
121
|
+
/**
|
|
122
|
+
* Metering hook, called once per request. Exceptions are swallowed —
|
|
123
|
+
* billing must never break serving.
|
|
124
|
+
*/
|
|
125
|
+
onEvent?: (event: MeterEvent) => void;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Build the `@signature-params` value for the Web Bot Auth profile. */
|
|
129
|
+
export function buildParams(
|
|
130
|
+
keyid: string,
|
|
131
|
+
created: number,
|
|
132
|
+
expires: number
|
|
133
|
+
): string;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Sign a request the way a legitimate agent operator's SDK would.
|
|
137
|
+
* Mutates and returns `headers`.
|
|
138
|
+
*/
|
|
139
|
+
export function signRequest(
|
|
140
|
+
headers: Record<string, string>,
|
|
141
|
+
authority: string,
|
|
142
|
+
privateKey: KeyObjectLike,
|
|
143
|
+
keyid: string,
|
|
144
|
+
directoryUrl: string,
|
|
145
|
+
created?: number,
|
|
146
|
+
expires?: number
|
|
147
|
+
): Record<string, string>;
|
|
148
|
+
|
|
149
|
+
/** Verify a presented signature against your configured directories. */
|
|
150
|
+
export function verifySignature(
|
|
151
|
+
headers: Record<string, string | string[] | undefined>,
|
|
152
|
+
authority: string,
|
|
153
|
+
directories: KeyDirectories,
|
|
154
|
+
now?: number
|
|
155
|
+
): VerifyResult;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Sort a request into a lane. A signature that FAILS verification lands in
|
|
159
|
+
* `suspected_bot`, not `human` — forging identity is the loudest signal there is.
|
|
160
|
+
*/
|
|
161
|
+
export function classify(
|
|
162
|
+
req: WayleaveRequest,
|
|
163
|
+
directories: KeyDirectories,
|
|
164
|
+
now?: number
|
|
165
|
+
): Classification;
|
|
166
|
+
|
|
167
|
+
export class Wayleave {
|
|
168
|
+
constructor(opts?: WayleaveOptions);
|
|
169
|
+
/** Classify, apply policy, price, and emit the metering event. */
|
|
170
|
+
handle(
|
|
171
|
+
req: WayleaveRequest,
|
|
172
|
+
now?: number,
|
|
173
|
+
paymentProof?: string
|
|
174
|
+
): Decision;
|
|
175
|
+
/** Express/Connect adapter. Sets `req.wayleave` to the Decision. */
|
|
176
|
+
express(): (req: any, res: any, next: (err?: any) => void) => void;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export default Wayleave;
|
package/package.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wayleave",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Charge AI agents for passage across your app. Verify agent signatures (Web Bot Auth / Ed25519), classify every request, and return 402 with a price on routes you choose. Humans browse free.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
|
+
"types": "./index.d.ts",
|
|
7
8
|
"exports": {
|
|
8
|
-
".":
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./index.d.ts",
|
|
11
|
+
"default": "./index.js"
|
|
12
|
+
}
|
|
9
13
|
},
|
|
10
14
|
"files": [
|
|
11
15
|
"index.js",
|
|
16
|
+
"index.d.ts",
|
|
12
17
|
"README.md",
|
|
13
18
|
"LICENSE"
|
|
14
19
|
],
|
|
@@ -40,4 +45,4 @@
|
|
|
40
45
|
"url": "https://github.com/gibrancorbin11-hub/wayleave/issues"
|
|
41
46
|
},
|
|
42
47
|
"homepage": "https://github.com/gibrancorbin11-hub/wayleave#readme"
|
|
43
|
-
}
|
|
48
|
+
}
|