@nobulex/sdk 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.
Files changed (81) hide show
  1. package/dist/adapters/express.d.ts +322 -0
  2. package/dist/adapters/express.d.ts.map +1 -0
  3. package/dist/adapters/express.js +356 -0
  4. package/dist/adapters/express.js.map +1 -0
  5. package/dist/adapters/index.d.ts +15 -0
  6. package/dist/adapters/index.d.ts.map +1 -0
  7. package/dist/adapters/index.js +15 -0
  8. package/dist/adapters/index.js.map +1 -0
  9. package/dist/adapters/langchain.d.ts +183 -0
  10. package/dist/adapters/langchain.d.ts.map +1 -0
  11. package/dist/adapters/langchain.js +203 -0
  12. package/dist/adapters/langchain.js.map +1 -0
  13. package/dist/adapters/vercel-ai.d.ts +122 -0
  14. package/dist/adapters/vercel-ai.d.ts.map +1 -0
  15. package/dist/adapters/vercel-ai.js +128 -0
  16. package/dist/adapters/vercel-ai.js.map +1 -0
  17. package/dist/benchmarks.d.ts +164 -0
  18. package/dist/benchmarks.d.ts.map +1 -0
  19. package/dist/benchmarks.js +327 -0
  20. package/dist/benchmarks.js.map +1 -0
  21. package/dist/benchmarks.test.d.ts +2 -0
  22. package/dist/benchmarks.test.d.ts.map +1 -0
  23. package/dist/benchmarks.test.js +71 -0
  24. package/dist/benchmarks.test.js.map +1 -0
  25. package/dist/conformance.d.ts +160 -0
  26. package/dist/conformance.d.ts.map +1 -0
  27. package/dist/conformance.js +1242 -0
  28. package/dist/conformance.js.map +1 -0
  29. package/dist/events.d.ts +176 -0
  30. package/dist/events.d.ts.map +1 -0
  31. package/dist/events.js +208 -0
  32. package/dist/events.js.map +1 -0
  33. package/dist/index.d.ts +384 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +695 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/index.test.d.ts +2 -0
  38. package/dist/index.test.d.ts.map +1 -0
  39. package/dist/index.test.js +986 -0
  40. package/dist/index.test.js.map +1 -0
  41. package/dist/middleware.d.ts +104 -0
  42. package/dist/middleware.d.ts.map +1 -0
  43. package/dist/middleware.js +222 -0
  44. package/dist/middleware.js.map +1 -0
  45. package/dist/middleware.test.d.ts +5 -0
  46. package/dist/middleware.test.d.ts.map +1 -0
  47. package/dist/middleware.test.js +735 -0
  48. package/dist/middleware.test.js.map +1 -0
  49. package/dist/plugins/auth.d.ts +49 -0
  50. package/dist/plugins/auth.d.ts.map +1 -0
  51. package/dist/plugins/auth.js +82 -0
  52. package/dist/plugins/auth.js.map +1 -0
  53. package/dist/plugins/cache.d.ts +40 -0
  54. package/dist/plugins/cache.d.ts.map +1 -0
  55. package/dist/plugins/cache.js +191 -0
  56. package/dist/plugins/cache.js.map +1 -0
  57. package/dist/plugins/index.d.ts +16 -0
  58. package/dist/plugins/index.d.ts.map +1 -0
  59. package/dist/plugins/index.js +12 -0
  60. package/dist/plugins/index.js.map +1 -0
  61. package/dist/plugins/metrics-plugin.d.ts +32 -0
  62. package/dist/plugins/metrics-plugin.d.ts.map +1 -0
  63. package/dist/plugins/metrics-plugin.js +61 -0
  64. package/dist/plugins/metrics-plugin.js.map +1 -0
  65. package/dist/plugins/plugins.test.d.ts +8 -0
  66. package/dist/plugins/plugins.test.d.ts.map +1 -0
  67. package/dist/plugins/plugins.test.js +640 -0
  68. package/dist/plugins/plugins.test.js.map +1 -0
  69. package/dist/plugins/retry-plugin.d.ts +55 -0
  70. package/dist/plugins/retry-plugin.d.ts.map +1 -0
  71. package/dist/plugins/retry-plugin.js +133 -0
  72. package/dist/plugins/retry-plugin.js.map +1 -0
  73. package/dist/telemetry.d.ts +183 -0
  74. package/dist/telemetry.d.ts.map +1 -0
  75. package/dist/telemetry.js +241 -0
  76. package/dist/telemetry.js.map +1 -0
  77. package/dist/types.d.ts +200 -0
  78. package/dist/types.d.ts.map +1 -0
  79. package/dist/types.js +8 -0
  80. package/dist/types.js.map +1 -0
  81. package/package.json +52 -0
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Express/HTTP middleware adapter for the Stele SDK.
3
+ *
4
+ * Provides zero-config HTTP middleware that wraps any Express/Connect-compatible
5
+ * handler with Stele covenant enforcement. Uses generic request/response types
6
+ * so it works with Express, Koa, Hono, Fastify, and any Connect-compatible server.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ import type { SteleClient } from '../index.js';
11
+ import type { CovenantDocument } from '@nobulex/core';
12
+ import type { EvaluationResult } from '../types.js';
13
+ /**
14
+ * Generic incoming request interface.
15
+ *
16
+ * Compatible with Express, Koa, Hono, Fastify, and Node.js http.IncomingMessage.
17
+ */
18
+ export interface IncomingRequest {
19
+ /** HTTP method (GET, POST, PUT, DELETE, etc.). */
20
+ method?: string;
21
+ /** Full URL string. */
22
+ url?: string;
23
+ /** Parsed path component of the URL (Express-style). */
24
+ path?: string;
25
+ /** Request headers. */
26
+ headers?: Record<string, string | string[] | undefined>;
27
+ }
28
+ /**
29
+ * Generic outgoing response interface.
30
+ *
31
+ * Compatible with Express, Koa, Hono, Fastify, and Node.js http.ServerResponse.
32
+ */
33
+ export interface OutgoingResponse {
34
+ /** HTTP status code. */
35
+ statusCode?: number;
36
+ /** Set a response header. */
37
+ setHeader?: (name: string, value: string) => void;
38
+ /** End the response with an optional body. */
39
+ end?: (body?: string) => void;
40
+ }
41
+ /**
42
+ * Connect/Express-style next function.
43
+ */
44
+ export type NextFunction = (err?: unknown) => void;
45
+ /**
46
+ * Options for the steleMiddleware factory.
47
+ */
48
+ export interface SteleMiddlewareOptions {
49
+ /** The SteleClient instance to use for covenant evaluation. */
50
+ client: SteleClient;
51
+ /** The covenant document to enforce. */
52
+ covenant: CovenantDocument;
53
+ /**
54
+ * Extract the action string from the incoming request.
55
+ * Defaults to `req.method?.toLowerCase() ?? 'read'`.
56
+ */
57
+ actionExtractor?: (req: IncomingRequest) => string;
58
+ /**
59
+ * Extract the resource string from the incoming request.
60
+ * Defaults to `req.path ?? req.url ?? '/'`.
61
+ */
62
+ resourceExtractor?: (req: IncomingRequest) => string;
63
+ /**
64
+ * Custom handler for denied requests. If not provided, responds
65
+ * with a 403 JSON body.
66
+ */
67
+ onDenied?: (req: IncomingRequest, res: OutgoingResponse, result: EvaluationResult) => void;
68
+ /**
69
+ * Custom handler for errors during evaluation. If not provided,
70
+ * responds with a 500 JSON body.
71
+ */
72
+ onError?: (req: IncomingRequest, res: OutgoingResponse, error: unknown) => void;
73
+ }
74
+ /**
75
+ * Options for the steleGuardHandler factory.
76
+ */
77
+ export interface SteleGuardHandlerOptions {
78
+ /** The SteleClient instance to use for covenant evaluation. */
79
+ client: SteleClient;
80
+ /** The covenant document to enforce. */
81
+ covenant: CovenantDocument;
82
+ /**
83
+ * Extract the action string from the incoming request.
84
+ * Defaults to `req.method?.toLowerCase() ?? 'read'`.
85
+ */
86
+ actionExtractor?: (req: IncomingRequest) => string;
87
+ /**
88
+ * Extract the resource string from the incoming request.
89
+ * Defaults to `req.path ?? req.url ?? '/'`.
90
+ */
91
+ resourceExtractor?: (req: IncomingRequest) => string;
92
+ /**
93
+ * Custom handler for denied requests. If not provided, responds
94
+ * with a 403 JSON body.
95
+ */
96
+ onDenied?: (req: IncomingRequest, res: OutgoingResponse, result: EvaluationResult) => void;
97
+ /**
98
+ * Custom handler for errors during evaluation. If not provided,
99
+ * responds with a 500 JSON body.
100
+ */
101
+ onError?: (req: IncomingRequest, res: OutgoingResponse, error: unknown) => void;
102
+ }
103
+ /**
104
+ * Options for the createCovenantRouter factory.
105
+ */
106
+ export interface CovenantRouterOptions {
107
+ /** The SteleClient instance to use for covenant evaluation. */
108
+ client: SteleClient;
109
+ /** The covenant document to enforce. */
110
+ covenant: CovenantDocument;
111
+ }
112
+ /**
113
+ * Connect-compatible middleware factory that enforces a Stele covenant
114
+ * on every incoming HTTP request.
115
+ *
116
+ * For each request:
117
+ * - Extracts the action and resource using configurable extractors
118
+ * - Evaluates them against the covenant's CCL constraints
119
+ * - If permitted: sets `x-stele-permitted: true` header and calls `next()`
120
+ * - If denied: calls `onDenied` handler (default: 403 JSON response)
121
+ * - On error: calls `onError` handler (default: 500 JSON response)
122
+ *
123
+ * @param options - Middleware configuration options.
124
+ * @returns A Connect-compatible middleware function `(req, res, next) => void`.
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * import express from 'express';
129
+ * import { SteleClient, steleMiddleware } from '@nobulex/sdk';
130
+ *
131
+ * const client = new SteleClient();
132
+ * const app = express();
133
+ *
134
+ * app.use(steleMiddleware({
135
+ * client,
136
+ * covenant: myCovenantDoc,
137
+ * }));
138
+ *
139
+ * app.get('/data', (req, res) => {
140
+ * res.json({ data: 'allowed!' });
141
+ * });
142
+ * ```
143
+ */
144
+ export declare function steleMiddleware(options: SteleMiddlewareOptions): (req: IncomingRequest, res: OutgoingResponse, next: NextFunction) => void;
145
+ /**
146
+ * Async handler type compatible with any HTTP framework.
147
+ */
148
+ export type AsyncHandler = (req: IncomingRequest, res: OutgoingResponse) => Promise<void>;
149
+ /**
150
+ * Wraps an async handler with Stele covenant enforcement for standalone use
151
+ * (no next function required).
152
+ *
153
+ * Evaluates the request against the covenant before invoking the handler.
154
+ * If denied, the handler is never called and a 403 response is sent.
155
+ *
156
+ * @param options - Guard handler configuration options.
157
+ * @param handler - The async handler to wrap.
158
+ * @returns A new handler function that enforces the covenant before delegation.
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * const guardedHandler = steleGuardHandler(
163
+ * { client, covenant: myDoc },
164
+ * async (req, res) => {
165
+ * res.end(JSON.stringify({ data: 'success' }));
166
+ * },
167
+ * );
168
+ *
169
+ * // Use with any framework
170
+ * http.createServer(guardedHandler);
171
+ * ```
172
+ */
173
+ export declare function steleGuardHandler(options: SteleGuardHandlerOptions, handler: AsyncHandler): (req: IncomingRequest, res: OutgoingResponse) => Promise<void>;
174
+ /**
175
+ * Options for the well-known Stele discovery endpoint.
176
+ * See docs/DISCOVERY.md for the convention.
177
+ */
178
+ export interface WellKnownOptions {
179
+ /** Agent identity or content-address. */
180
+ agentId: string;
181
+ /** Covenant entries for discovery. */
182
+ covenants: Array<{
183
+ id: string;
184
+ url: string;
185
+ status: 'active' | 'expired' | 'revoked';
186
+ }>;
187
+ /** Optional base URL for resolver (e.g. https://example.com/stele/resolve). */
188
+ resolver?: string;
189
+ /** Protocol version. Defaults to "0.1.0". */
190
+ stele?: string;
191
+ }
192
+ /**
193
+ * Creates a handler for GET /.well-known/stele (federated covenant discovery).
194
+ * Trust the Ed25519 signature on the covenant, not the resolver.
195
+ *
196
+ * @param options - Discovery metadata.
197
+ * @returns AsyncHandler for the well-known endpoint.
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * const handler = createWellKnownHandler({
202
+ * agentId: 'agent-1',
203
+ * covenants: [{ id: doc.id, url: 'https://example.com/covenants/abc.json', status: 'active' }],
204
+ * });
205
+ * app.get('/.well-known/stele', handler);
206
+ * ```
207
+ */
208
+ export declare function createWellKnownHandler(options: WellKnownOptions): AsyncHandler;
209
+ /**
210
+ * A covenant router that provides fine-grained enforcement helpers.
211
+ */
212
+ export interface CovenantRouter {
213
+ /**
214
+ * Returns middleware that enforces a specific action/resource pair.
215
+ *
216
+ * Unlike `steleMiddleware` which extracts action/resource from the request,
217
+ * this allows you to specify exact values for route-level enforcement.
218
+ *
219
+ * @param action - The action to enforce (e.g., `"read"`, `"write"`).
220
+ * @param resource - The resource to enforce (e.g., `"/data/users"`).
221
+ * @returns A Connect-compatible middleware function.
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * const router = createCovenantRouter({ client, covenant });
226
+ *
227
+ * app.get('/users', router.protect('read', '/users'), getUsers);
228
+ * app.post('/users', router.protect('write', '/users'), createUser);
229
+ * ```
230
+ */
231
+ protect: (action: string, resource: string) => (req: IncomingRequest, res: OutgoingResponse, next: NextFunction) => void;
232
+ /**
233
+ * Evaluates a request against the covenant and returns the result
234
+ * without sending a response.
235
+ *
236
+ * Useful for custom enforcement logic or logging.
237
+ *
238
+ * @param req - The incoming request to evaluate.
239
+ * @returns A promise resolving to the EvaluationResult.
240
+ *
241
+ * @example
242
+ * ```typescript
243
+ * const router = createCovenantRouter({ client, covenant });
244
+ * const result = await router.evaluateRequest(req);
245
+ * if (result.permitted) {
246
+ * // custom handling
247
+ * }
248
+ * ```
249
+ */
250
+ evaluateRequest: (req: IncomingRequest) => Promise<EvaluationResult>;
251
+ }
252
+ /**
253
+ * Creates a covenant router with fine-grained enforcement helpers.
254
+ *
255
+ * Provides `.protect(action, resource)` for route-level enforcement and
256
+ * `.evaluateRequest(req)` for programmatic access to evaluation results.
257
+ *
258
+ * @param options - Router configuration options.
259
+ * @returns A CovenantRouter instance.
260
+ *
261
+ * @example
262
+ * ```typescript
263
+ * const router = createCovenantRouter({ client, covenant: myDoc });
264
+ *
265
+ * // Route-level protection
266
+ * app.get('/data', router.protect('read', '/data'), handler);
267
+ *
268
+ * // Programmatic evaluation
269
+ * const result = await router.evaluateRequest(req);
270
+ * ```
271
+ */
272
+ export declare function createCovenantRouter(options: CovenantRouterOptions): CovenantRouter;
273
+ /**
274
+ * Options for the Kova API Gateway middleware.
275
+ *
276
+ * Drop-in middleware that sits in front of any API and requires agents
277
+ * to present a valid covenant before granting access. Protects API providers
278
+ * from liability when agents misuse their API.
279
+ *
280
+ * @see docs/ADOPTION-STRATEGY.md — "Trust-Gated Access: The Liability Play"
281
+ */
282
+ export interface KovaGatewayOptions {
283
+ /** The SteleClient instance for covenant verification and evaluation. */
284
+ client: SteleClient;
285
+ /**
286
+ * Extract the covenant from the incoming request.
287
+ * Default: reads `x-kova-covenant` header as JSON string, or `authorization: Bearer <base64>`.
288
+ */
289
+ covenantExtractor?: (req: IncomingRequest) => CovenantDocument | string | null;
290
+ /**
291
+ * Optional: required constraints the client's covenant must satisfy.
292
+ * If provided, the gateway checks that the covenant's CCL includes these.
293
+ */
294
+ requiredConstraints?: string[];
295
+ actionExtractor?: (req: IncomingRequest) => string;
296
+ resourceExtractor?: (req: IncomingRequest) => string;
297
+ onDenied?: (req: IncomingRequest, res: OutgoingResponse, result: EvaluationResult) => void;
298
+ onError?: (req: IncomingRequest, res: OutgoingResponse, error: unknown) => void;
299
+ }
300
+ /**
301
+ * Kova API Gateway middleware — requires agents to present a valid covenant before API access.
302
+ *
303
+ * Drop-in middleware for Express/Connect. Every agent calling the API must present
304
+ * a covenant (via `x-kova-covenant` header or `Authorization: Bearer <base64>`).
305
+ * The gateway verifies the covenant and evaluates the request. Protects API
306
+ * providers from liability when agents misuse their API.
307
+ *
308
+ * @example
309
+ * ```typescript
310
+ * import express from 'express';
311
+ * import { SteleClient, kovaGatewayMiddleware } from '@nobulex/sdk';
312
+ *
313
+ * const client = new SteleClient();
314
+ * const app = express();
315
+ *
316
+ * app.use(kovaGatewayMiddleware({ client }));
317
+ *
318
+ * app.get('/data', (req, res) => res.json({ data: 'allowed' }));
319
+ * ```
320
+ */
321
+ export declare function kovaGatewayMiddleware(options: KovaGatewayOptions): (req: IncomingRequest, res: OutgoingResponse, next: NextFunction) => void;
322
+ //# sourceMappingURL=express.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;CACzD;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,wBAAwB;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,8CAA8C;IAC9C,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAInD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,+DAA+D;IAC/D,MAAM,EAAE,WAAW,CAAC;IACpB,wCAAwC;IACxC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,CAAC;IACnD;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,CAAC;IACrD;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC3F;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACjF;AAID;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,+DAA+D;IAC/D,MAAM,EAAE,WAAW,CAAC;IACpB,wCAAwC;IACxC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,CAAC;IACnD;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,CAAC;IACrD;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC3F;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACjF;AAID;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,+DAA+D;IAC/D,MAAM,EAAE,WAAW,CAAC;IACpB,wCAAwC;IACxC,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAkED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CA8B3E;AAID;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1F;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,wBAAwB,EACjC,OAAO,EAAE,YAAY,GACpB,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CA6BhE;AAID;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,SAAS,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAA;KAAE,CAAC,CAAC;IACxF,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAsB9E;AAID;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,EAAE,CACP,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,KACb,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IAE/E;;;;;;;;;;;;;;;;;OAiBG;IACH,eAAe,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACtE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CAiCnF;AAID;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,MAAM,EAAE,WAAW,CAAC;IACpB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAAC;IAC/E;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,CAAC;IACnD,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,CAAC;IACrD,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC3F,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACjF;AAmBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,kBAAkB,GAC1B,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAmE3E"}
@@ -0,0 +1,356 @@
1
+ /**
2
+ * Express/HTTP middleware adapter for the Stele SDK.
3
+ *
4
+ * Provides zero-config HTTP middleware that wraps any Express/Connect-compatible
5
+ * handler with Stele covenant enforcement. Uses generic request/response types
6
+ * so it works with Express, Koa, Hono, Fastify, and any Connect-compatible server.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ import { deserializeCovenant, verifyCovenant } from '@nobulex/core';
11
+ // ─── Default extractors ──────────────────────────────────────────────────────
12
+ /**
13
+ * Default action extractor: maps HTTP method to a lowercase action string.
14
+ */
15
+ function defaultActionExtractor(req) {
16
+ return req.method?.toLowerCase() ?? 'read';
17
+ }
18
+ /**
19
+ * Default resource extractor: uses the request path or URL.
20
+ */
21
+ function defaultResourceExtractor(req) {
22
+ return req.path ?? req.url ?? '/';
23
+ }
24
+ /**
25
+ * Default denied handler: sends a 403 JSON response.
26
+ */
27
+ function defaultOnDenied(_req, res, result) {
28
+ if (res.statusCode !== undefined) {
29
+ res.statusCode = 403;
30
+ }
31
+ if (res.setHeader) {
32
+ res.setHeader('content-type', 'application/json');
33
+ res.setHeader('x-stele-permitted', 'false');
34
+ }
35
+ if (res.end) {
36
+ res.end(JSON.stringify({
37
+ error: 'Forbidden',
38
+ permitted: false,
39
+ reason: result.reason ?? 'Request denied by covenant',
40
+ }));
41
+ }
42
+ }
43
+ /**
44
+ * Default error handler: sends a 500 JSON response.
45
+ */
46
+ function defaultOnError(_req, res, error) {
47
+ if (res.statusCode !== undefined) {
48
+ res.statusCode = 500;
49
+ }
50
+ if (res.setHeader) {
51
+ res.setHeader('content-type', 'application/json');
52
+ }
53
+ if (res.end) {
54
+ res.end(JSON.stringify({
55
+ error: 'Internal Server Error',
56
+ message: error instanceof Error ? error.message : 'Covenant evaluation failed',
57
+ }));
58
+ }
59
+ }
60
+ // ─── steleMiddleware ─────────────────────────────────────────────────────────
61
+ /**
62
+ * Connect-compatible middleware factory that enforces a Stele covenant
63
+ * on every incoming HTTP request.
64
+ *
65
+ * For each request:
66
+ * - Extracts the action and resource using configurable extractors
67
+ * - Evaluates them against the covenant's CCL constraints
68
+ * - If permitted: sets `x-stele-permitted: true` header and calls `next()`
69
+ * - If denied: calls `onDenied` handler (default: 403 JSON response)
70
+ * - On error: calls `onError` handler (default: 500 JSON response)
71
+ *
72
+ * @param options - Middleware configuration options.
73
+ * @returns A Connect-compatible middleware function `(req, res, next) => void`.
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * import express from 'express';
78
+ * import { SteleClient, steleMiddleware } from '@nobulex/sdk';
79
+ *
80
+ * const client = new SteleClient();
81
+ * const app = express();
82
+ *
83
+ * app.use(steleMiddleware({
84
+ * client,
85
+ * covenant: myCovenantDoc,
86
+ * }));
87
+ *
88
+ * app.get('/data', (req, res) => {
89
+ * res.json({ data: 'allowed!' });
90
+ * });
91
+ * ```
92
+ */
93
+ export function steleMiddleware(options) {
94
+ const { client, covenant, actionExtractor = defaultActionExtractor, resourceExtractor = defaultResourceExtractor, onDenied = defaultOnDenied, onError = defaultOnError, } = options;
95
+ return (req, res, next) => {
96
+ const action = actionExtractor(req);
97
+ const resource = resourceExtractor(req);
98
+ client
99
+ .evaluateAction(covenant, action, resource)
100
+ .then((result) => {
101
+ if (result.permitted) {
102
+ if (res.setHeader) {
103
+ res.setHeader('x-stele-permitted', 'true');
104
+ }
105
+ next();
106
+ }
107
+ else {
108
+ onDenied(req, res, result);
109
+ }
110
+ })
111
+ .catch((error) => {
112
+ onError(req, res, error);
113
+ });
114
+ };
115
+ }
116
+ /**
117
+ * Wraps an async handler with Stele covenant enforcement for standalone use
118
+ * (no next function required).
119
+ *
120
+ * Evaluates the request against the covenant before invoking the handler.
121
+ * If denied, the handler is never called and a 403 response is sent.
122
+ *
123
+ * @param options - Guard handler configuration options.
124
+ * @param handler - The async handler to wrap.
125
+ * @returns A new handler function that enforces the covenant before delegation.
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * const guardedHandler = steleGuardHandler(
130
+ * { client, covenant: myDoc },
131
+ * async (req, res) => {
132
+ * res.end(JSON.stringify({ data: 'success' }));
133
+ * },
134
+ * );
135
+ *
136
+ * // Use with any framework
137
+ * http.createServer(guardedHandler);
138
+ * ```
139
+ */
140
+ export function steleGuardHandler(options, handler) {
141
+ const { client, covenant, actionExtractor = defaultActionExtractor, resourceExtractor = defaultResourceExtractor, onDenied = defaultOnDenied, onError = defaultOnError, } = options;
142
+ return async (req, res) => {
143
+ const action = actionExtractor(req);
144
+ const resource = resourceExtractor(req);
145
+ try {
146
+ const result = await client.evaluateAction(covenant, action, resource);
147
+ if (result.permitted) {
148
+ if (res.setHeader) {
149
+ res.setHeader('x-stele-permitted', 'true');
150
+ }
151
+ await handler(req, res);
152
+ }
153
+ else {
154
+ onDenied(req, res, result);
155
+ }
156
+ }
157
+ catch (error) {
158
+ onError(req, res, error);
159
+ }
160
+ };
161
+ }
162
+ /**
163
+ * Creates a handler for GET /.well-known/stele (federated covenant discovery).
164
+ * Trust the Ed25519 signature on the covenant, not the resolver.
165
+ *
166
+ * @param options - Discovery metadata.
167
+ * @returns AsyncHandler for the well-known endpoint.
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * const handler = createWellKnownHandler({
172
+ * agentId: 'agent-1',
173
+ * covenants: [{ id: doc.id, url: 'https://example.com/covenants/abc.json', status: 'active' }],
174
+ * });
175
+ * app.get('/.well-known/stele', handler);
176
+ * ```
177
+ */
178
+ export function createWellKnownHandler(options) {
179
+ const { agentId, covenants, resolver, stele = '0.1.0' } = options;
180
+ return async (_req, res) => {
181
+ const body = JSON.stringify({
182
+ stele,
183
+ agentId,
184
+ covenants,
185
+ ...(resolver && { resolver }),
186
+ });
187
+ if (res.setHeader) {
188
+ res.setHeader('content-type', 'application/json');
189
+ res.setHeader('cache-control', 'public, max-age=60');
190
+ }
191
+ if (res.statusCode !== undefined) {
192
+ res.statusCode = 200;
193
+ }
194
+ if (res.end) {
195
+ res.end(body);
196
+ }
197
+ };
198
+ }
199
+ /**
200
+ * Creates a covenant router with fine-grained enforcement helpers.
201
+ *
202
+ * Provides `.protect(action, resource)` for route-level enforcement and
203
+ * `.evaluateRequest(req)` for programmatic access to evaluation results.
204
+ *
205
+ * @param options - Router configuration options.
206
+ * @returns A CovenantRouter instance.
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * const router = createCovenantRouter({ client, covenant: myDoc });
211
+ *
212
+ * // Route-level protection
213
+ * app.get('/data', router.protect('read', '/data'), handler);
214
+ *
215
+ * // Programmatic evaluation
216
+ * const result = await router.evaluateRequest(req);
217
+ * ```
218
+ */
219
+ export function createCovenantRouter(options) {
220
+ const { client, covenant } = options;
221
+ return {
222
+ protect(action, resource) {
223
+ return (_req, res, next) => {
224
+ client
225
+ .evaluateAction(covenant, action, resource)
226
+ .then((result) => {
227
+ if (result.permitted) {
228
+ if (res.setHeader) {
229
+ res.setHeader('x-stele-permitted', 'true');
230
+ }
231
+ next();
232
+ }
233
+ else {
234
+ defaultOnDenied(_req, res, result);
235
+ }
236
+ })
237
+ .catch((error) => {
238
+ defaultOnError(_req, res, error);
239
+ });
240
+ };
241
+ },
242
+ async evaluateRequest(req) {
243
+ const action = defaultActionExtractor(req);
244
+ const resource = defaultResourceExtractor(req);
245
+ return client.evaluateAction(covenant, action, resource);
246
+ },
247
+ };
248
+ }
249
+ function defaultCovenantExtractor(req) {
250
+ const h = req.headers ?? {};
251
+ const raw = (typeof h['x-kova-covenant'] === 'string' ? h['x-kova-covenant'] : h['x-kova-covenant']?.[0]) ?? null;
252
+ if (raw)
253
+ return raw;
254
+ const auth = (typeof h['authorization'] === 'string' ? h['authorization'] : h['authorization']?.[0]) ?? '';
255
+ const bearer = auth.startsWith('Bearer ') ? auth.slice(7).trim() : null;
256
+ if (bearer) {
257
+ try {
258
+ return JSON.parse(Buffer.from(bearer, 'base64').toString('utf-8'));
259
+ }
260
+ catch {
261
+ return null;
262
+ }
263
+ }
264
+ return null;
265
+ }
266
+ /**
267
+ * Kova API Gateway middleware — requires agents to present a valid covenant before API access.
268
+ *
269
+ * Drop-in middleware for Express/Connect. Every agent calling the API must present
270
+ * a covenant (via `x-kova-covenant` header or `Authorization: Bearer <base64>`).
271
+ * The gateway verifies the covenant and evaluates the request. Protects API
272
+ * providers from liability when agents misuse their API.
273
+ *
274
+ * @example
275
+ * ```typescript
276
+ * import express from 'express';
277
+ * import { SteleClient, kovaGatewayMiddleware } from '@nobulex/sdk';
278
+ *
279
+ * const client = new SteleClient();
280
+ * const app = express();
281
+ *
282
+ * app.use(kovaGatewayMiddleware({ client }));
283
+ *
284
+ * app.get('/data', (req, res) => res.json({ data: 'allowed' }));
285
+ * ```
286
+ */
287
+ export function kovaGatewayMiddleware(options) {
288
+ const { client, covenantExtractor = defaultCovenantExtractor, requiredConstraints, actionExtractor = defaultActionExtractor, resourceExtractor = defaultResourceExtractor, onDenied = defaultOnDenied, onError = defaultOnError, } = options;
289
+ return (req, res, next) => {
290
+ const raw = covenantExtractor(req);
291
+ if (!raw) {
292
+ if (res.setHeader)
293
+ res.setHeader('content-type', 'application/json');
294
+ if (res.statusCode !== undefined)
295
+ res.statusCode = 401;
296
+ if (res.end)
297
+ res.end(JSON.stringify({ error: 'Missing covenant. Send x-kova-covenant header or Authorization: Bearer <covenant-base64>' }));
298
+ return;
299
+ }
300
+ let covenant;
301
+ try {
302
+ covenant = typeof raw === 'string' ? deserializeCovenant(raw) : raw;
303
+ }
304
+ catch {
305
+ if (res.setHeader)
306
+ res.setHeader('content-type', 'application/json');
307
+ if (res.statusCode !== undefined)
308
+ res.statusCode = 401;
309
+ if (res.end)
310
+ res.end(JSON.stringify({ error: 'Invalid covenant format' }));
311
+ return;
312
+ }
313
+ void verifyCovenant(covenant).then((verifyResult) => {
314
+ if (!verifyResult.valid) {
315
+ if (res.setHeader)
316
+ res.setHeader('content-type', 'application/json');
317
+ if (res.statusCode !== undefined)
318
+ res.statusCode = 401;
319
+ if (res.end)
320
+ res.end(JSON.stringify({ error: 'Covenant verification failed', details: verifyResult.checks.filter((c) => !c.passed) }));
321
+ return;
322
+ }
323
+ if (requiredConstraints && requiredConstraints.length > 0) {
324
+ const constraints = covenant.constraints ?? '';
325
+ const missing = requiredConstraints.filter((rc) => !constraints.includes(rc));
326
+ if (missing.length > 0) {
327
+ if (res.setHeader)
328
+ res.setHeader('content-type', 'application/json');
329
+ if (res.statusCode !== undefined)
330
+ res.statusCode = 401;
331
+ if (res.end)
332
+ res.end(JSON.stringify({ error: 'Covenant missing required constraints', missing }));
333
+ return;
334
+ }
335
+ }
336
+ const action = actionExtractor(req);
337
+ const resource = resourceExtractor(req);
338
+ return client
339
+ .evaluateAction(covenant, action, resource)
340
+ .then((result) => {
341
+ if (result.permitted) {
342
+ if (res.setHeader)
343
+ res.setHeader('x-kova-permitted', 'true');
344
+ next();
345
+ }
346
+ else {
347
+ onDenied(req, res, result);
348
+ }
349
+ })
350
+ .catch((error) => {
351
+ onError(req, res, error);
352
+ });
353
+ });
354
+ };
355
+ }
356
+ //# sourceMappingURL=express.js.map