@naulon/enforce 0.1.0
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 +89 -0
- package/dist/agentDetect.d.ts +94 -0
- package/dist/agentDetect.d.ts.map +1 -0
- package/dist/agentDetect.js +149 -0
- package/dist/agentDetect.js.map +1 -0
- package/dist/botAuth.d.ts +159 -0
- package/dist/botAuth.d.ts.map +1 -0
- package/dist/botAuth.js +659 -0
- package/dist/botAuth.js.map +1 -0
- package/dist/build402.d.ts +46 -0
- package/dist/build402.d.ts.map +1 -0
- package/dist/build402.js +137 -0
- package/dist/build402.js.map +1 -0
- package/dist/decide.d.ts +125 -0
- package/dist/decide.d.ts.map +1 -0
- package/dist/decide.js +221 -0
- package/dist/decide.js.map +1 -0
- package/dist/discoverability.d.ts +68 -0
- package/dist/discoverability.d.ts.map +1 -0
- package/dist/discoverability.js +62 -0
- package/dist/discoverability.js.map +1 -0
- package/dist/enforce/fetch-handler.d.ts +12 -0
- package/dist/enforce/fetch-handler.d.ts.map +1 -0
- package/dist/enforce/fetch-handler.js +25 -0
- package/dist/enforce/fetch-handler.js.map +1 -0
- package/dist/enforce/index.d.ts +11 -0
- package/dist/enforce/index.d.ts.map +1 -0
- package/dist/enforce/index.js +11 -0
- package/dist/enforce/index.js.map +1 -0
- package/dist/enforce/middleware.d.ts +27 -0
- package/dist/enforce/middleware.d.ts.map +1 -0
- package/dist/enforce/middleware.js +88 -0
- package/dist/enforce/middleware.js.map +1 -0
- package/dist/enforce/next.d.ts +24 -0
- package/dist/enforce/next.d.ts.map +1 -0
- package/dist/enforce/next.js +32 -0
- package/dist/enforce/next.js.map +1 -0
- package/dist/enforce/quote-source.d.ts +31 -0
- package/dist/enforce/quote-source.d.ts.map +1 -0
- package/dist/enforce/quote-source.js +29 -0
- package/dist/enforce/quote-source.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/license.d.ts +16 -0
- package/dist/license.d.ts.map +1 -0
- package/dist/license.js +18 -0
- package/dist/license.js.map +1 -0
- package/dist/nonce.d.ts +45 -0
- package/dist/nonce.d.ts.map +1 -0
- package/dist/nonce.js +122 -0
- package/dist/nonce.js.map +1 -0
- package/dist/pop.d.ts +15 -0
- package/dist/pop.d.ts.map +1 -0
- package/dist/pop.js +77 -0
- package/dist/pop.js.map +1 -0
- package/dist/pricing.d.ts +53 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +44 -0
- package/dist/pricing.js.map +1 -0
- package/dist/revocation.d.ts +6 -0
- package/dist/revocation.d.ts.map +1 -0
- package/dist/revocation.js +45 -0
- package/dist/revocation.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toll discoverability — let an agent learn the gate exists, and its terms,
|
|
3
|
+
* without being told out of band.
|
|
4
|
+
*
|
|
5
|
+
* - `/.well-known/x402` serves a machine-readable manifest for the one
|
|
6
|
+
* publisher this gate fronts: the article path prefixes, the price for a read
|
|
7
|
+
* and a citation, the Arc/USDC network, and where to verify a license.
|
|
8
|
+
* - A `Link: rel="payment"` header on every 402 points an agent at that manifest.
|
|
9
|
+
*
|
|
10
|
+
* The manifest is article-agnostic, so it never names author wallets — payTo is
|
|
11
|
+
* resolved per article from the credits graph at payment time (custody-free,
|
|
12
|
+
* buyer → author). It's a discovery hint, not concrete x402 PaymentRequirements:
|
|
13
|
+
* to pay, an agent GETs an article URL and reads the 402's PAYMENT-REQUIRED header.
|
|
14
|
+
*
|
|
15
|
+
* Everything here derives from the resolved `PublisherConfig` + the Arc network
|
|
16
|
+
* constants — no new per-publisher seam.
|
|
17
|
+
*/
|
|
18
|
+
import { type PublisherConfig, type SettlementNetwork } from "@naulon/shared";
|
|
19
|
+
/** Well-known path for the toll manifest. */
|
|
20
|
+
export declare const X402_MANIFEST_PATH = "/.well-known/x402";
|
|
21
|
+
/** `Link` header value pointing an agent at the manifest (RFC 8288). */
|
|
22
|
+
export declare const PAYMENT_LINK_HEADER = "</.well-known/x402>; rel=\"payment\"; type=\"application/json\"";
|
|
23
|
+
interface PriceLeg {
|
|
24
|
+
/** Atomic USDC (6 decimals) — what the on-chain leg moves. */
|
|
25
|
+
atomic: string;
|
|
26
|
+
/** Human USDC, for display. */
|
|
27
|
+
usdc: number;
|
|
28
|
+
}
|
|
29
|
+
export interface X402Manifest {
|
|
30
|
+
x402Version: number;
|
|
31
|
+
/** The product's contract, machine-readable: humans read free, machines pay. */
|
|
32
|
+
humansReadFree: true;
|
|
33
|
+
resources: {
|
|
34
|
+
/** Path prefixes (no leading slash) whose articles are tolled. */
|
|
35
|
+
pathPrefixes: string[];
|
|
36
|
+
/** Toll kinds; a citation is priced up from a read. */
|
|
37
|
+
kinds: ["read", "citation"];
|
|
38
|
+
/** Header an agent sets to request the citation toll instead of a read. */
|
|
39
|
+
selectKindHeader: string;
|
|
40
|
+
note: string;
|
|
41
|
+
};
|
|
42
|
+
payment: {
|
|
43
|
+
scheme: "exact";
|
|
44
|
+
network: string;
|
|
45
|
+
chainId: number;
|
|
46
|
+
asset: string;
|
|
47
|
+
currency: "USDC";
|
|
48
|
+
maxTimeoutSeconds: number;
|
|
49
|
+
price: {
|
|
50
|
+
read: PriceLeg;
|
|
51
|
+
citation: PriceLeg & {
|
|
52
|
+
multiplier: number;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
/** How the single on-chain recipient is chosen (wallets are never listed here). */
|
|
56
|
+
payTo: string;
|
|
57
|
+
};
|
|
58
|
+
license: {
|
|
59
|
+
jwks: string;
|
|
60
|
+
verify: string;
|
|
61
|
+
/** issuer === audience for this publisher's Citation License Tokens. */
|
|
62
|
+
identity: string;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** Build the discovery manifest for the publisher this gate fronts. */
|
|
66
|
+
export declare function buildX402Manifest(publisher: PublisherConfig, net?: SettlementNetwork): X402Manifest;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=discoverability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discoverability.d.ts","sourceRoot":"","sources":["../src/discoverability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAA+B,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAE3G,6CAA6C;AAC7C,eAAO,MAAM,kBAAkB,sBAAsB,CAAC;AAMtD,wEAAwE;AACxE,eAAO,MAAM,mBAAmB,oEAAoE,CAAC;AAErG,UAAU,QAAQ;IAChB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,gFAAgF;IAChF,cAAc,EAAE,IAAI,CAAC;IACrB,SAAS,EAAE;QACT,kEAAkE;QAClE,YAAY,EAAE,MAAM,EAAE,CAAC;QACvB,uDAAuD;QACvD,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC5B,2EAA2E;QAC3E,gBAAgB,EAAE,MAAM,CAAC;QACzB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,OAAO,EAAE;QACP,MAAM,EAAE,OAAO,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,KAAK,EAAE;YAAE,IAAI,EAAE,QAAQ,CAAC;YAAC,QAAQ,EAAE,QAAQ,GAAG;gBAAE,UAAU,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,CAAC;QACvE,mFAAmF;QACnF,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,wEAAwE;QACxE,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAKD,uEAAuE;AACvE,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,eAAe,EAC1B,GAAG,GAAE,iBAAmC,GACvC,YAAY,CAgCd"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Toll discoverability — let an agent learn the gate exists, and its terms,
|
|
3
|
+
* without being told out of band.
|
|
4
|
+
*
|
|
5
|
+
* - `/.well-known/x402` serves a machine-readable manifest for the one
|
|
6
|
+
* publisher this gate fronts: the article path prefixes, the price for a read
|
|
7
|
+
* and a citation, the Arc/USDC network, and where to verify a license.
|
|
8
|
+
* - A `Link: rel="payment"` header on every 402 points an agent at that manifest.
|
|
9
|
+
*
|
|
10
|
+
* The manifest is article-agnostic, so it never names author wallets — payTo is
|
|
11
|
+
* resolved per article from the credits graph at payment time (custody-free,
|
|
12
|
+
* buyer → author). It's a discovery hint, not concrete x402 PaymentRequirements:
|
|
13
|
+
* to pay, an agent GETs an article URL and reads the 402's PAYMENT-REQUIRED header.
|
|
14
|
+
*
|
|
15
|
+
* Everything here derives from the resolved `PublisherConfig` + the Arc network
|
|
16
|
+
* constants — no new per-publisher seam.
|
|
17
|
+
*/
|
|
18
|
+
import { activeNetwork, toAtomicUsdc } from "@naulon/shared";
|
|
19
|
+
/** Well-known path for the toll manifest. */
|
|
20
|
+
export const X402_MANIFEST_PATH = "/.well-known/x402";
|
|
21
|
+
/** JWKS path (kept in sync with the route registered in app.ts). */
|
|
22
|
+
const JWKS_PATH = "/.well-known/naulon-jwks.json";
|
|
23
|
+
/** Online license verification path template. */
|
|
24
|
+
const LICENSE_VERIFY_PATH = "/licenses/{jti}";
|
|
25
|
+
/** `Link` header value pointing an agent at the manifest (RFC 8288). */
|
|
26
|
+
export const PAYMENT_LINK_HEADER = `<${X402_MANIFEST_PATH}>; rel="payment"; type="application/json"`;
|
|
27
|
+
/** Same validity window the 402 advertises (x402.ts MAX_TIMEOUT_SECONDS). */
|
|
28
|
+
const MAX_TIMEOUT_SECONDS = 345_600;
|
|
29
|
+
/** Build the discovery manifest for the publisher this gate fronts. */
|
|
30
|
+
export function buildX402Manifest(publisher, net = activeNetwork()) {
|
|
31
|
+
const readUsdc = publisher.price;
|
|
32
|
+
const citationUsdc = readUsdc * publisher.citationMultiplier;
|
|
33
|
+
return {
|
|
34
|
+
x402Version: 2,
|
|
35
|
+
humansReadFree: true,
|
|
36
|
+
resources: {
|
|
37
|
+
pathPrefixes: publisher.articlePrefixes,
|
|
38
|
+
kinds: ["read", "citation"],
|
|
39
|
+
selectKindHeader: "X-Naulon-Kind",
|
|
40
|
+
note: "GET any article URL under a prefix to receive a 402 with concrete PaymentRequirements.",
|
|
41
|
+
},
|
|
42
|
+
payment: {
|
|
43
|
+
scheme: "exact",
|
|
44
|
+
network: net.network,
|
|
45
|
+
chainId: net.chainId,
|
|
46
|
+
asset: net.usdc,
|
|
47
|
+
currency: "USDC",
|
|
48
|
+
maxTimeoutSeconds: MAX_TIMEOUT_SECONDS,
|
|
49
|
+
price: {
|
|
50
|
+
read: { atomic: toAtomicUsdc(readUsdc), usdc: readUsdc },
|
|
51
|
+
citation: {
|
|
52
|
+
atomic: toAtomicUsdc(citationUsdc),
|
|
53
|
+
usdc: citationUsdc,
|
|
54
|
+
multiplier: publisher.citationMultiplier,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
payTo: "Resolved per article to the primary author from the publisher's credits graph; the recursive co-author split is recorded on each settled event. Custody-free: settlement is buyer → author.",
|
|
58
|
+
},
|
|
59
|
+
license: { jwks: JWKS_PATH, verify: LICENSE_VERIFY_PATH, identity: publisher.licenseIdentity },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=discoverability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discoverability.js","sourceRoot":"","sources":["../src/discoverability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,aAAa,EAAE,YAAY,EAAgD,MAAM,gBAAgB,CAAC;AAE3G,6CAA6C;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AACtD,oEAAoE;AACpE,MAAM,SAAS,GAAG,+BAA+B,CAAC;AAClD,iDAAiD;AACjD,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AAE9C,wEAAwE;AACxE,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,kBAAkB,2CAA2C,CAAC;AAyCrG,6EAA6E;AAC7E,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAEpC,uEAAuE;AACvE,MAAM,UAAU,iBAAiB,CAC/B,SAA0B,EAC1B,MAAyB,aAAa,EAAE;IAExC,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAe,CAAC;IAC3C,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAC,kBAAkB,CAAC;IAC7D,OAAO;QACL,WAAW,EAAE,CAAC;QACd,cAAc,EAAE,IAAI;QACpB,SAAS,EAAE;YACT,YAAY,EAAE,SAAS,CAAC,eAAe;YACvC,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC;YAC3B,gBAAgB,EAAE,eAAe;YACjC,IAAI,EAAE,wFAAwF;SAC/F;QACD,OAAO,EAAE;YACP,MAAM,EAAE,OAAO;YACf,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,KAAK,EAAE,GAAG,CAAC,IAAI;YACf,QAAQ,EAAE,MAAM;YAChB,iBAAiB,EAAE,mBAAmB;YACtC,KAAK,EAAE;gBACL,IAAI,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACxD,QAAQ,EAAE;oBACR,MAAM,EAAE,YAAY,CAAC,YAAY,CAAC;oBAClC,IAAI,EAAE,YAAY;oBAClB,UAAU,EAAE,SAAS,CAAC,kBAAkB;iBACzC;aACF;YACD,KAAK,EACH,6LAA6L;SAChM;QACD,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,mBAAmB,EAAE,QAAQ,EAAE,SAAS,CAAC,eAAe,EAAE;KAC/F,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `withNaulon` — wrap any web fetch handler (Cloudflare Workers, Netlify/Deno
|
|
3
|
+
* Edge, a Hono/`itty` route, a bare `export default { fetch }`) with the toll.
|
|
4
|
+
*
|
|
5
|
+
* Unlike the Next adapter (a pre-filter that returns `next()`), a fetch handler
|
|
6
|
+
* IS the thing that produces the page — so on a pass we CALL the wrapped handler
|
|
7
|
+
* and stamp the paid-receipt headers onto its response. On a refusal (402/403) we
|
|
8
|
+
* short-circuit and never invoke the handler.
|
|
9
|
+
*/
|
|
10
|
+
import { type NaulonMiddlewareOptions } from "./middleware.ts";
|
|
11
|
+
export declare function withNaulon(handler: (req: Request) => Promise<Response> | Response, opts: NaulonMiddlewareOptions): (req: Request) => Promise<Response>;
|
|
12
|
+
//# sourceMappingURL=fetch-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-handler.d.ts","sourceRoot":"","sources":["../../src/enforce/fetch-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAoB,KAAK,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAEjF,wBAAgB,UAAU,CACxB,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,EACvD,IAAI,EAAE,uBAAuB,GAC5B,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAWrC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `withNaulon` — wrap any web fetch handler (Cloudflare Workers, Netlify/Deno
|
|
3
|
+
* Edge, a Hono/`itty` route, a bare `export default { fetch }`) with the toll.
|
|
4
|
+
*
|
|
5
|
+
* Unlike the Next adapter (a pre-filter that returns `next()`), a fetch handler
|
|
6
|
+
* IS the thing that produces the page — so on a pass we CALL the wrapped handler
|
|
7
|
+
* and stamp the paid-receipt headers onto its response. On a refusal (402/403) we
|
|
8
|
+
* short-circuit and never invoke the handler.
|
|
9
|
+
*/
|
|
10
|
+
import { naulonMiddleware } from "./middleware.js";
|
|
11
|
+
export function withNaulon(handler, opts) {
|
|
12
|
+
const core = naulonMiddleware(opts);
|
|
13
|
+
return async (req) => {
|
|
14
|
+
const { response, setHeaders } = await core(req);
|
|
15
|
+
if (response)
|
|
16
|
+
return response; // 402 / 403 — never reach the handler
|
|
17
|
+
const res = await handler(req);
|
|
18
|
+
if (setHeaders) {
|
|
19
|
+
for (const [k, v] of Object.entries(setHeaders))
|
|
20
|
+
res.headers.set(k, v);
|
|
21
|
+
}
|
|
22
|
+
return res;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=fetch-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-handler.js","sourceRoot":"","sources":["../../src/enforce/fetch-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,gBAAgB,EAAgC,MAAM,iBAAiB,CAAC;AAEjF,MAAM,UAAU,UAAU,CACxB,OAAuD,EACvD,IAA6B;IAE7B,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACpC,OAAO,KAAK,EAAE,GAAY,EAAqB,EAAE;QAC/C,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;QACjD,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC,CAAC,sCAAsC;QACrE,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,UAAU,EAAE,CAAC;YACf,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC;gBAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzE,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@naulon/sdk/enforce` — in-app toll enforcement.
|
|
3
|
+
*
|
|
4
|
+
* The framework-agnostic core (`naulonMiddleware`) + the pluggable `QuoteSource`.
|
|
5
|
+
* Framework adapters live alongside: the generic fetch-handler wrapper here, the
|
|
6
|
+
* Next.js adapter under `@naulon/sdk/next/middleware`.
|
|
7
|
+
*/
|
|
8
|
+
export * from "./quote-source.ts";
|
|
9
|
+
export * from "./middleware.ts";
|
|
10
|
+
export * from "./fetch-handler.ts";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/enforce/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@naulon/sdk/enforce` — in-app toll enforcement.
|
|
3
|
+
*
|
|
4
|
+
* The framework-agnostic core (`naulonMiddleware`) + the pluggable `QuoteSource`.
|
|
5
|
+
* Framework adapters live alongside: the generic fetch-handler wrapper here, the
|
|
6
|
+
* Next.js adapter under `@naulon/sdk/next/middleware`.
|
|
7
|
+
*/
|
|
8
|
+
export * from "./quote-source.js";
|
|
9
|
+
export * from "./middleware.js";
|
|
10
|
+
export * from "./fetch-handler.js";
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/enforce/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { QuoteSource } from "./quote-source.ts";
|
|
2
|
+
export interface NaulonMiddlewareOptions {
|
|
3
|
+
/**
|
|
4
|
+
* The site's toll config in `PublisherConfig` shape — `decide()` reads
|
|
5
|
+
* `id`, `articlePrefixes` (or `gateScope`), `licenseIdentity`, `seoAllowlist`,
|
|
6
|
+
* and `crawlerPolicy` from it.
|
|
7
|
+
*/
|
|
8
|
+
publisher: unknown;
|
|
9
|
+
/** Price + payees source: `localQuoteSource` (own data) or `httpQuoteSource` (cloud). */
|
|
10
|
+
quote: QuoteSource;
|
|
11
|
+
/** The hosted `POST /verify` URL (settles the presented payment, custody-free). */
|
|
12
|
+
verifyUrl: string;
|
|
13
|
+
/** The publisher's `nln_live_` key — bearer-auth to `/verify` (and the cloud quote). */
|
|
14
|
+
apiKey: string;
|
|
15
|
+
/** Injectable fetch (tests); defaults to the global `fetch`. */
|
|
16
|
+
fetchImpl?: typeof fetch;
|
|
17
|
+
/** Injectable clock (tests); defaults to `Date.now`. */
|
|
18
|
+
now?: () => number;
|
|
19
|
+
}
|
|
20
|
+
export interface MiddlewareResult {
|
|
21
|
+
/** A Response to send (short-circuit), or `null` to pass to the app. */
|
|
22
|
+
response: Response | null;
|
|
23
|
+
/** Headers to attach to the app's outgoing response on a pass (paid-OK receipt). */
|
|
24
|
+
setHeaders?: Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
export declare function naulonMiddleware(opts: NaulonMiddlewareOptions): (req: Request) => Promise<MiddlewareResult>;
|
|
27
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/enforce/middleware.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB,yFAAyF;IACzF,KAAK,EAAE,WAAW,CAAC;IACnB,mFAAmF;IACnF,SAAS,EAAE,MAAM,CAAC;IAClB,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,wEAAwE;IACxE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC;AAYD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,uBAAuB,GAC5B,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAsE7C"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `naulonMiddleware` — the framework-agnostic core of in-app toll enforcement.
|
|
3
|
+
*
|
|
4
|
+
* A tolled site on a per-IP rate-limiting edge (Vercel Free's 429) can't route
|
|
5
|
+
* through the fleet's single egress IP without tripping the limit. This runs the
|
|
6
|
+
* SAME `decide()` the gate runs, IN the publisher's own runtime, so the agent's
|
|
7
|
+
* own IP reaches the origin — no fleet hop, no shared-IP 429. The control plane
|
|
8
|
+
* still owns the money (hosted `/verify`) and the catalog (hosted `/quote`).
|
|
9
|
+
*
|
|
10
|
+
* The core takes a web `Request` and returns `{ response, setHeaders }`:
|
|
11
|
+
* - `response: Response` → SEND it, short-circuit the app (402 / 403).
|
|
12
|
+
* - `response: null` → PASS: let the app render locally. `setHeaders` (if any)
|
|
13
|
+
* must be applied to the app's OUTGOING response — a paid-OK request renders
|
|
14
|
+
* normally but still carries its `PAYMENT-RESPONSE` + license headers.
|
|
15
|
+
*
|
|
16
|
+
* Custody-free: the payment leg POSTs the buyer's signature to the hosted
|
|
17
|
+
* `/verify`, which settles buyer→author directly. This middleware never holds USDC.
|
|
18
|
+
*/
|
|
19
|
+
import { decide, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER, PAYMENT_LINK_HEADER, LICENSE_HEADER, } from "../decide.js";
|
|
20
|
+
export function naulonMiddleware(opts) {
|
|
21
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
22
|
+
const clock = opts.now ?? Date.now;
|
|
23
|
+
return async (req) => {
|
|
24
|
+
const url = new URL(req.url);
|
|
25
|
+
const d = await decide({
|
|
26
|
+
raw: req,
|
|
27
|
+
host: url.host,
|
|
28
|
+
path: url.pathname + url.search,
|
|
29
|
+
publisher: opts.publisher,
|
|
30
|
+
now: clock(),
|
|
31
|
+
quote: (publisher, slug, kind) => opts.quote.quote(publisher, slug, kind, { resource: req.url }),
|
|
32
|
+
});
|
|
33
|
+
switch (d.kind) {
|
|
34
|
+
// Human, free re-read, unknown/non-article: let the app render locally.
|
|
35
|
+
case "passthrough":
|
|
36
|
+
case "free":
|
|
37
|
+
case "reread":
|
|
38
|
+
return { response: null };
|
|
39
|
+
case "blocked":
|
|
40
|
+
return { response: new Response("This crawler is refused by the publisher.", { status: 403 }) };
|
|
41
|
+
case "payment-required":
|
|
42
|
+
return {
|
|
43
|
+
response: new Response(null, {
|
|
44
|
+
status: 402,
|
|
45
|
+
headers: { [PAYMENT_REQUIRED_HEADER]: d.header, Link: PAYMENT_LINK_HEADER },
|
|
46
|
+
}),
|
|
47
|
+
};
|
|
48
|
+
case "payment-presented": {
|
|
49
|
+
// Custody-free settlement: the buyer's signature goes to the hosted
|
|
50
|
+
// /verify, which settles buyer→author and mints the receipt.
|
|
51
|
+
let body;
|
|
52
|
+
let status;
|
|
53
|
+
try {
|
|
54
|
+
const res = await doFetch(opts.verifyUrl, {
|
|
55
|
+
method: "POST",
|
|
56
|
+
headers: {
|
|
57
|
+
authorization: `Bearer ${opts.apiKey}`,
|
|
58
|
+
"content-type": "application/json",
|
|
59
|
+
},
|
|
60
|
+
body: JSON.stringify({ payment: d.payment, legs: d.legs, quote: d.quote, resource: req.url }),
|
|
61
|
+
});
|
|
62
|
+
status = res.status;
|
|
63
|
+
body = (await res.json().catch(() => ({})));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// /verify unreachable → refuse this paid attempt (fail-closed on the
|
|
67
|
+
// payment leg: we can't confirm settlement, so we can't serve paid).
|
|
68
|
+
return { response: new Response(JSON.stringify({ error: "verify unreachable" }), { status: 402, headers: { "content-type": "application/json" } }) };
|
|
69
|
+
}
|
|
70
|
+
if (status === 200 && body.ok) {
|
|
71
|
+
const setHeaders = {};
|
|
72
|
+
if (body.responseHeader)
|
|
73
|
+
setHeaders[PAYMENT_RESPONSE_HEADER] = body.responseHeader;
|
|
74
|
+
if (body.licenseJws)
|
|
75
|
+
setHeaders[LICENSE_HEADER] = body.licenseJws;
|
|
76
|
+
return { response: null, setHeaders };
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
response: new Response(JSON.stringify({ error: body.error ?? "payment rejected" }), {
|
|
80
|
+
status: 402,
|
|
81
|
+
headers: { "content-type": "application/json" },
|
|
82
|
+
}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/enforce/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@naulon/enforce/next` (re-exported as `@naulon/sdk/enforce/next`) — the
|
|
3
|
+
* Next.js App Router middleware adapter.
|
|
4
|
+
*
|
|
5
|
+
* Next middleware is a pre-filter: return a `Response` to short-circuit (402/403),
|
|
6
|
+
* or `NextResponse.next()` to let the route render. The one thing that needs the
|
|
7
|
+
* `next` package is `NextResponse.next()` — so we INJECT `NextResponse` rather
|
|
8
|
+
* than importing `next/server`, keeping the gate free of a hard `next` dependency
|
|
9
|
+
* (the publisher's app already has it). Everything else is the framework-agnostic
|
|
10
|
+
* core.
|
|
11
|
+
*
|
|
12
|
+
* // middleware.ts (in the publisher's Next app)
|
|
13
|
+
* import { NextResponse } from "next/server";
|
|
14
|
+
* import { createNaulonMiddleware } from "@naulon/sdk/enforce/next";
|
|
15
|
+
* export const middleware = createNaulonMiddleware({ …opts }, NextResponse);
|
|
16
|
+
* export const config = { matcher: ["/essays/:path*"] };
|
|
17
|
+
*/
|
|
18
|
+
import { type NaulonMiddlewareOptions } from "./middleware.ts";
|
|
19
|
+
/** The slice of `NextResponse` this adapter uses (the real class satisfies it). */
|
|
20
|
+
export interface NextResponseLike {
|
|
21
|
+
next(): Response;
|
|
22
|
+
}
|
|
23
|
+
export declare function createNaulonMiddleware(opts: NaulonMiddlewareOptions, NextResponse: NextResponseLike): (req: Request) => Promise<Response>;
|
|
24
|
+
//# sourceMappingURL=next.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../../src/enforce/next.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAoB,KAAK,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAEjF,mFAAmF;AACnF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,IAAI,QAAQ,CAAC;CAClB;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,uBAAuB,EAC7B,YAAY,EAAE,gBAAgB,GAC7B,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAUrC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@naulon/enforce/next` (re-exported as `@naulon/sdk/enforce/next`) — the
|
|
3
|
+
* Next.js App Router middleware adapter.
|
|
4
|
+
*
|
|
5
|
+
* Next middleware is a pre-filter: return a `Response` to short-circuit (402/403),
|
|
6
|
+
* or `NextResponse.next()` to let the route render. The one thing that needs the
|
|
7
|
+
* `next` package is `NextResponse.next()` — so we INJECT `NextResponse` rather
|
|
8
|
+
* than importing `next/server`, keeping the gate free of a hard `next` dependency
|
|
9
|
+
* (the publisher's app already has it). Everything else is the framework-agnostic
|
|
10
|
+
* core.
|
|
11
|
+
*
|
|
12
|
+
* // middleware.ts (in the publisher's Next app)
|
|
13
|
+
* import { NextResponse } from "next/server";
|
|
14
|
+
* import { createNaulonMiddleware } from "@naulon/sdk/enforce/next";
|
|
15
|
+
* export const middleware = createNaulonMiddleware({ …opts }, NextResponse);
|
|
16
|
+
* export const config = { matcher: ["/essays/:path*"] };
|
|
17
|
+
*/
|
|
18
|
+
import { naulonMiddleware } from "./middleware.js";
|
|
19
|
+
export function createNaulonMiddleware(opts, NextResponse) {
|
|
20
|
+
const core = naulonMiddleware(opts);
|
|
21
|
+
return async (req) => {
|
|
22
|
+
const { response, setHeaders } = await core(req);
|
|
23
|
+
if (response)
|
|
24
|
+
return response; // short-circuit (402 / 403)
|
|
25
|
+
// Pass to the route, carrying the paid receipt headers onto the continuation.
|
|
26
|
+
const res = NextResponse.next();
|
|
27
|
+
for (const [k, v] of Object.entries(setHeaders ?? {}))
|
|
28
|
+
res.headers.set(k, v);
|
|
29
|
+
return res;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=next.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"next.js","sourceRoot":"","sources":["../../src/enforce/next.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,gBAAgB,EAAgC,MAAM,iBAAiB,CAAC;AAOjF,MAAM,UAAU,sBAAsB,CACpC,IAA6B,EAC7B,YAA8B;IAE9B,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACpC,OAAO,KAAK,EAAE,GAAY,EAAqB,EAAE;QAC/C,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;QACjD,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC,CAAC,4BAA4B;QAC3D,8EAA8E;QAC9E,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC;QAChC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;YAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7E,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A pluggable price+payees source for the in-app middleware. `decide()` asks it
|
|
3
|
+
* for a `Quote` per gated slug; returning `null` means "no toll — serve free"
|
|
4
|
+
* (the same don't-gate signal the gate's own pricing uses).
|
|
5
|
+
*
|
|
6
|
+
* Two implementations ship:
|
|
7
|
+
* - `localQuoteSource` — the publisher's OWN data (Phase 1a). A site that
|
|
8
|
+
* already knows its authors/prices (e.g. a credits API) wraps that lookup.
|
|
9
|
+
* - `httpQuoteSource` — the hosted cloud `/quote` (Phase 1b). A site with no
|
|
10
|
+
* local catalog defers to the scraper catalog the control plane maintains.
|
|
11
|
+
*
|
|
12
|
+
* Custody-free either way: a quote carries `payTo` addresses, never a key.
|
|
13
|
+
*/
|
|
14
|
+
import type { Quote, TollKind } from "../decide.ts";
|
|
15
|
+
/** Extra request context a source may need (the hosted source keys off the URL). */
|
|
16
|
+
export interface QuoteContext {
|
|
17
|
+
/** The full resource URL being decided (what the hosted `/quote` looks up). */
|
|
18
|
+
resource: string;
|
|
19
|
+
}
|
|
20
|
+
export interface QuoteSource {
|
|
21
|
+
quote(publisher: unknown, slug: string, kind: TollKind, ctx: QuoteContext): Promise<Quote | null>;
|
|
22
|
+
}
|
|
23
|
+
/** Wrap a publisher's own price+payees lookup. `undefined`/`null` → free read. */
|
|
24
|
+
export declare function localQuoteSource(fn: (publisher: unknown, slug: string, kind: TollKind) => Promise<Quote | null | undefined>): QuoteSource;
|
|
25
|
+
/**
|
|
26
|
+
* Defer pricing to the hosted `GET /quote?resource=…` (nln_live_ authed). A 204
|
|
27
|
+
* means "no toll" → `null` (free). A 200 returns the `Quote`. Never a wallet key.
|
|
28
|
+
* `fetchImpl` is injectable for tests; defaults to the global `fetch`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function httpQuoteSource(quoteUrl: string, apiKey: string, fetchImpl?: typeof fetch): QuoteSource;
|
|
31
|
+
//# sourceMappingURL=quote-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quote-source.d.ts","sourceRoot":"","sources":["../../src/enforce/quote-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAEpD,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;CACnG;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC,GAC1F,WAAW,CAMb;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,OAAO,KAAa,GAC9B,WAAW,CAYb"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Wrap a publisher's own price+payees lookup. `undefined`/`null` → free read. */
|
|
2
|
+
export function localQuoteSource(fn) {
|
|
3
|
+
return {
|
|
4
|
+
async quote(publisher, slug, kind) {
|
|
5
|
+
return (await fn(publisher, slug, kind)) ?? null;
|
|
6
|
+
},
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Defer pricing to the hosted `GET /quote?resource=…` (nln_live_ authed). A 204
|
|
11
|
+
* means "no toll" → `null` (free). A 200 returns the `Quote`. Never a wallet key.
|
|
12
|
+
* `fetchImpl` is injectable for tests; defaults to the global `fetch`.
|
|
13
|
+
*/
|
|
14
|
+
export function httpQuoteSource(quoteUrl, apiKey, fetchImpl = fetch) {
|
|
15
|
+
return {
|
|
16
|
+
async quote(_publisher, slug, kind, ctx) {
|
|
17
|
+
// slug+kind let the cloud price directly (decide() already derived the slug);
|
|
18
|
+
// resource is carried for catalog lookups keyed on the full URL.
|
|
19
|
+
const q = new URLSearchParams({ resource: ctx.resource, slug, kind });
|
|
20
|
+
const res = await fetchImpl(`${quoteUrl}?${q}`, { headers: { authorization: `Bearer ${apiKey}` } });
|
|
21
|
+
if (res.status === 204)
|
|
22
|
+
return null; // no toll → free read
|
|
23
|
+
if (!res.ok)
|
|
24
|
+
return null; // fail-open: a quote lookup miss must never gate a reader
|
|
25
|
+
return (await res.json());
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=quote-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quote-source.js","sourceRoot":"","sources":["../../src/enforce/quote-source.ts"],"names":[],"mappings":"AAyBA,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAC9B,EAA2F;IAE3F,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI;YAC/B,OAAO,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC;QACnD,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAgB,EAChB,MAAc,EACd,YAA0B,KAAK;IAE/B,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG;YACrC,8EAA8E;YAC9E,iEAAiE;YACjE,MAAM,CAAC,GAAG,IAAI,eAAe,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACtE,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,QAAQ,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;YACpG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;gBAAE,OAAO,IAAI,CAAC,CAAC,sBAAsB;YAC3D,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,OAAO,IAAI,CAAC,CAAC,0DAA0D;YACpF,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAU,CAAC;QACrC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@naulon/enforce` — the runtime-agnostic toll-decision kernel + the in-app
|
|
3
|
+
* enforcement middleware. The neutral low-level core that both `@naulon/tollgate`
|
|
4
|
+
* (the gate shell) and `@naulon/sdk` (the publisher SDK) sit ABOVE, with no
|
|
5
|
+
* dependency cycle. Depends only on `@naulon/shared` (+ `viem` for PoP).
|
|
6
|
+
*
|
|
7
|
+
* The Next.js middleware adapter is the separate `@naulon/enforce/next` subpath
|
|
8
|
+
* (it imports `next/server`, so it must not be pulled into the core barrel).
|
|
9
|
+
*/
|
|
10
|
+
export * from "./decide.ts";
|
|
11
|
+
export * from "./agentDetect.ts";
|
|
12
|
+
export * from "./botAuth.ts";
|
|
13
|
+
export * from "./nonce.ts";
|
|
14
|
+
export * from "./pop.ts";
|
|
15
|
+
export { build402, buildRequirements, bindingOf, MAX_TIMEOUT_SECONDS } from "./build402.ts";
|
|
16
|
+
export { quote } from "./pricing.ts";
|
|
17
|
+
export { licensing, type Licensing } from "./license.ts";
|
|
18
|
+
export { revocations, type RevocationStore } from "./revocation.ts";
|
|
19
|
+
export { X402_MANIFEST_PATH, buildX402Manifest, type X402Manifest } from "./discoverability.ts";
|
|
20
|
+
export * from "./enforce/quote-source.ts";
|
|
21
|
+
export * from "./enforce/middleware.ts";
|
|
22
|
+
export * from "./enforce/fetch-handler.ts";
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,cAAc,aAAa,CAAC;AAG5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AAIzB,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAG5F,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAGrC,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGhG,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,4BAA4B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@naulon/enforce` — the runtime-agnostic toll-decision kernel + the in-app
|
|
3
|
+
* enforcement middleware. The neutral low-level core that both `@naulon/tollgate`
|
|
4
|
+
* (the gate shell) and `@naulon/sdk` (the publisher SDK) sit ABOVE, with no
|
|
5
|
+
* dependency cycle. Depends only on `@naulon/shared` (+ `viem` for PoP).
|
|
6
|
+
*
|
|
7
|
+
* The Next.js middleware adapter is the separate `@naulon/enforce/next` subpath
|
|
8
|
+
* (it imports `next/server`, so it must not be pulled into the core barrel).
|
|
9
|
+
*/
|
|
10
|
+
// The decision surface + its wire re-exports (PAYMENT_* headers, PaymentRequirements,
|
|
11
|
+
// SettlementLegReq, PAYMENT_LINK_HEADER, Quote, TollKind).
|
|
12
|
+
export * from "./decide.js";
|
|
13
|
+
// Classification, Web Bot Auth, nonce replay guard, holder-of-key proof.
|
|
14
|
+
export * from "./agentDetect.js";
|
|
15
|
+
export * from "./botAuth.js";
|
|
16
|
+
export * from "./nonce.js";
|
|
17
|
+
export * from "./pop.js";
|
|
18
|
+
// x402 BUILD side — the symbols `decide.ts` does NOT already re-export (avoids the
|
|
19
|
+
// wildcard-merge ambiguity that would otherwise drop the shared PAYMENT_*/type names).
|
|
20
|
+
export { build402, buildRequirements, bindingOf, MAX_TIMEOUT_SECONDS } from "./build402.js";
|
|
21
|
+
// Pricing (the `quote` value; the `Quote` type comes through `./decide.ts`).
|
|
22
|
+
export { quote } from "./pricing.js";
|
|
23
|
+
// Licensing, revocation, discoverability manifest.
|
|
24
|
+
export { licensing } from "./license.js";
|
|
25
|
+
export { revocations } from "./revocation.js";
|
|
26
|
+
export { X402_MANIFEST_PATH, buildX402Manifest } from "./discoverability.js";
|
|
27
|
+
// In-app enforcement middleware (framework-agnostic). The Next adapter is `./next`.
|
|
28
|
+
export * from "./enforce/quote-source.js";
|
|
29
|
+
export * from "./enforce/middleware.js";
|
|
30
|
+
export * from "./enforce/fetch-handler.js";
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,sFAAsF;AACtF,2DAA2D;AAC3D,cAAc,aAAa,CAAC;AAE5B,yEAAyE;AACzE,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AAEzB,mFAAmF;AACnF,uFAAuF;AACvF,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE5F,6EAA6E;AAC7E,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAErC,mDAAmD;AACnD,OAAO,EAAE,SAAS,EAAkB,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,WAAW,EAAwB,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAqB,MAAM,sBAAsB,CAAC;AAEhG,oFAAoF;AACpF,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tollgate licensing boot. Resolves the Citation License signing key once at
|
|
3
|
+
* startup (a stable key from config, or — only on the single-instance mock path —
|
|
4
|
+
* an ephemeral one with a warning) and exposes the JWK Set the gate publishes at
|
|
5
|
+
* /.well-known/naulon-jwks.json. `null` when LICENSES_ENABLED=false.
|
|
6
|
+
*
|
|
7
|
+
* Minting + the re-read entitlement (which use `licensing.key`) land in P2; P1
|
|
8
|
+
* publishes the public key so verifiers can be wired up first.
|
|
9
|
+
*/
|
|
10
|
+
import { type JwkSet, type SigningKey } from "@naulon/shared";
|
|
11
|
+
export interface Licensing {
|
|
12
|
+
key: SigningKey;
|
|
13
|
+
jwks: JwkSet;
|
|
14
|
+
}
|
|
15
|
+
export declare const licensing: Licensing | null;
|
|
16
|
+
//# sourceMappingURL=license.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAqC,KAAK,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIjG,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,UAAU,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,SAAS,EAAE,SAAS,GAAG,IAK5B,CAAC"}
|
package/dist/license.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tollgate licensing boot. Resolves the Citation License signing key once at
|
|
3
|
+
* startup (a stable key from config, or — only on the single-instance mock path —
|
|
4
|
+
* an ephemeral one with a warning) and exposes the JWK Set the gate publishes at
|
|
5
|
+
* /.well-known/naulon-jwks.json. `null` when LICENSES_ENABLED=false.
|
|
6
|
+
*
|
|
7
|
+
* Minting + the re-read entitlement (which use `licensing.key`) land in P2; P1
|
|
8
|
+
* publishes the public key so verifiers can be wired up first.
|
|
9
|
+
*/
|
|
10
|
+
import { getConfig, jwksOf, loadSigningKey } from "@naulon/shared";
|
|
11
|
+
const cfg = getConfig();
|
|
12
|
+
export const licensing = cfg.LICENSES_ENABLED
|
|
13
|
+
? (() => {
|
|
14
|
+
const key = loadSigningKey(cfg.LICENSE_SIGNING_KEY);
|
|
15
|
+
return { key, jwks: jwksOf([key]) };
|
|
16
|
+
})()
|
|
17
|
+
: null;
|
|
18
|
+
//# sourceMappingURL=license.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"license.js","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAgC,MAAM,gBAAgB,CAAC;AAEjG,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;AAOxB,MAAM,CAAC,MAAM,SAAS,GAAqB,GAAG,CAAC,gBAAgB;IAC7D,CAAC,CAAC,CAAC,GAAG,EAAE;QACJ,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QACpD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;IACtC,CAAC,CAAC,EAAE;IACN,CAAC,CAAC,IAAI,CAAC"}
|