@horizonmedia/blu-sdk 0.0.0-stage → 1.8.4

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.
@@ -0,0 +1,448 @@
1
+ /** True when `policy` admits this principal's entry point with all required permissions. */
2
+ declare function policyAllows(policy: any, principal: any): any;
3
+ /**
4
+ * @param {{ name: string, version: string, authenticate: Function | Function[], tools: ModuleTool[], manifest?: object }} opts
5
+ * manifest — the module's blu.module.json. When given, the tools in code must
6
+ * match its `mcp.tools` exactly (name, read-only flag, requirements) or this
7
+ * throws at start-up: the API won't run with an undeclared or mis-declared
8
+ * tool, so what the Admin approved is what actually runs.
9
+ * @returns {import("express").Router-like handlers} mount with `app.post("/mcp", ...createMcpEndpoint(...))`
10
+ */
11
+ declare function createMcpEndpoint({ name, version, authenticate, tools, manifest }: {
12
+ name: string;
13
+ version: string;
14
+ authenticate: Function | Function[];
15
+ tools: ModuleTool[];
16
+ manifest?: object;
17
+ }): any;
18
+ /**
19
+ * The same MCP endpoint for Web-standard handlers — e.g. a Next.js route
20
+ * handler: `export const POST = createMcpWebHandler({ … })`. Same tools shape,
21
+ * same per-caller tool filtering, same manifest drift check and link checks.
22
+ *
23
+ * @param {{ name: string, version: string, verify: (request: Request) => Promise<object>, tools: ModuleTool[], manifest?: object }} opts
24
+ * verify — e.g. createModuleAuth(…).verifyRequest; throws an error with `status`/`code` to refuse
25
+ * @returns {(request: Request) => Promise<Response>}
26
+ */
27
+ declare function createMcpWebHandler({ name, version, verify, tools, manifest }: {
28
+ name: string;
29
+ version: string;
30
+ verify: (request: Request) => Promise<object>;
31
+ tools: ModuleTool[];
32
+ manifest?: object;
33
+ }): (request: Request) => Promise<Response>;
34
+ declare function link(route: any, params: any, label: any): {
35
+ route: any;
36
+ params: any;
37
+ label: any;
38
+ };
39
+ /**
40
+ * A Zod schema, from Zod 3.25+ or Zod 4: whichever your API uses (the MCP SDK accepts both). Use Zod
41
+ * 4.6.6 or later: earlier versions can exhaust memory validating oversized input (GHSA-mrhx-c7xc-m3w5).
42
+ */
43
+ type ZodSchema = {
44
+ parse(data: unknown): unknown;
45
+ safeParse(data: unknown): unknown;
46
+ };
47
+ type ModuleTool = {
48
+ name: string;
49
+ title: string;
50
+ /**
51
+ * Written for the model: when to use it, what it returns.
52
+ */
53
+ description: string;
54
+ /**
55
+ * zod raw shape for arguments (Zod 4.6.6+ recommended; 3.25+ works)
56
+ */
57
+ input?: Record<string, ZodSchema>;
58
+ policy: Partial<Record<"blu" | "service" | "team" | "api-key", string[]>>;
59
+ annotations?: {
60
+ readOnlyHint?: boolean;
61
+ destructiveHint?: boolean;
62
+ };
63
+ /**
64
+ * principal: the normalized caller (req.principal)
65
+ */
66
+ run: (principal: any, args: any) => Promise<{
67
+ summary: string;
68
+ data: unknown;
69
+ links?: ReturnType<typeof link>[];
70
+ }>;
71
+ };
72
+
73
+ /**
74
+ * A user signed in with Blu — in a Blu shell, through the Concierge, or in this
75
+ * module's own standalone app ("Sign in with Blu"). Blu mints these tokens for
76
+ * ONE module (audience `blu-module:<moduleId>`), only while the module is
77
+ * enabled for the token's client, so a token issued for another module — or
78
+ * the shell's own session token — is refused here. The token carries the
79
+ * user's global Blu role on that client; `roleMap` turns it into permissions.
80
+ *
81
+ * @param {{ issuer: string, jwksUrl: string, moduleId: string,
82
+ * roleMap: Record<string, string[]> }} opts
83
+ * roleMap — TEAM-OWNED: Blu role ("viewer" | "analyst" | "owner") → this API's permissions.
84
+ */
85
+ declare function bluPlatformAuth({ issuer, jwksUrl, moduleId, roleMap }: {
86
+ issuer: string;
87
+ jwksUrl: string;
88
+ moduleId: string;
89
+ roleMap: Record<string, string[]>;
90
+ }): {
91
+ mode: string;
92
+ kind: string;
93
+ issuer: string;
94
+ accepts: (claims: any) => boolean;
95
+ authenticate(token: any): Promise<{
96
+ authMode: string;
97
+ kind: string;
98
+ issuer: string;
99
+ subject: any;
100
+ key: string;
101
+ devIdentity: any;
102
+ displayName: any;
103
+ email: any;
104
+ provider: any;
105
+ via: any;
106
+ actor: any;
107
+ clientId: any;
108
+ allowedClients: any;
109
+ roles: any;
110
+ permissions: any[];
111
+ }>;
112
+ };
113
+ /**
114
+ * Another backend calling with a Blu-issued client-credentials token. Its scopes
115
+ * are exactly what the Blu Admin granted it on THIS module (audience
116
+ * `blu-module:<moduleId>`). It names the client per call with X-Client-Id, from
117
+ * the clients this module is enabled for.
118
+ *
119
+ * @param {{ issuer: string, jwksUrl: string, moduleId: string }} opts
120
+ */
121
+ declare function bluServiceAuth({ issuer, jwksUrl, moduleId }: {
122
+ issuer: string;
123
+ jwksUrl: string;
124
+ moduleId: string;
125
+ }): {
126
+ mode: string;
127
+ kind: string;
128
+ issuer: string;
129
+ accepts: (claims: any) => boolean;
130
+ authenticate(token: any, req: any): Promise<{
131
+ authMode: string;
132
+ kind: string;
133
+ issuer: string;
134
+ subject: any;
135
+ key: string;
136
+ displayName: any;
137
+ clientId: any;
138
+ allowedClients: any;
139
+ roles: any[];
140
+ permissions: string[];
141
+ clientFromRequest: boolean;
142
+ }>;
143
+ };
144
+ /**
145
+ * The team owns this IdP (Cognito, Auth0, homegrown — anything that issues JWTs
146
+ * with a JWKS). `toPrincipal` maps the team's claim shape onto the common one.
147
+ *
148
+ * @param {{ issuer: string, jwksUrl: string, audience: string,
149
+ * toPrincipal?: (payload: object) => Partial<Principal> }} opts
150
+ */
151
+ declare function teamIdpAuth({ issuer, jwksUrl, audience, toPrincipal }: {
152
+ issuer: string;
153
+ jwksUrl: string;
154
+ audience: string;
155
+ toPrincipal?: (payload: object) => Partial<Principal>;
156
+ }): {
157
+ mode: string;
158
+ kind: string;
159
+ issuer: string;
160
+ accepts: () => boolean;
161
+ authenticate(token: any): Promise<{
162
+ authMode: string;
163
+ kind: string;
164
+ issuer: string;
165
+ /**
166
+ * user id, or api-key id
167
+ */
168
+ subject?: string;
169
+ /**
170
+ * stable identity across requests: `<authMode>|<issuer>|<subject>`.
171
+ * Key your own records (audit, ownership, preferences) on this —
172
+ * a Blu user needs no account in your system first.
173
+ */
174
+ key: string;
175
+ displayName?: string;
176
+ email?: string;
177
+ /**
178
+ * Blu only: the app that obtained the token ("blu-host",
179
+ * "blu-concierge", or your own standalone app)
180
+ */
181
+ via?: string;
182
+ /**
183
+ * Blu only: software acting for the user (e.g. "blu-concierge")
184
+ */
185
+ actor?: string;
186
+ /**
187
+ * active client (from the credential, never the request — except see api keys)
188
+ */
189
+ clientId?: string | null;
190
+ allowedClients?: string[];
191
+ roles?: string[];
192
+ /**
193
+ * resolved; may contain "*"
194
+ */
195
+ permissions?: string[];
196
+ devIdentity: any;
197
+ } | {
198
+ subject: any;
199
+ displayName: any;
200
+ email: any;
201
+ clientId: any;
202
+ allowedClients: any;
203
+ roles: any[];
204
+ permissions: any;
205
+ authMode: string;
206
+ kind: string;
207
+ issuer: string;
208
+ key: string;
209
+ devIdentity: any;
210
+ }>;
211
+ };
212
+ /**
213
+ * Keys are stored only as SHA-256 hashes, each scoped to clients + permissions
214
+ * and rate-limited. Browsers are refused outright: a key in a web page is a key
215
+ * anyone can copy.
216
+ *
217
+ * @param {{ keys: { id: string, name: string, hash: string, clients: string[],
218
+ * permissions: string[], ratePerMinute?: number }[] }} opts
219
+ */
220
+ declare function apiKeyAuth({ keys }: {
221
+ keys: {
222
+ id: string;
223
+ name: string;
224
+ hash: string;
225
+ clients: string[];
226
+ permissions: string[];
227
+ ratePerMinute?: number;
228
+ }[];
229
+ }): {
230
+ mode: string;
231
+ kind: string;
232
+ authenticate(presented: any, req: any): Promise<{
233
+ authMode: string;
234
+ kind: string;
235
+ issuer: string;
236
+ subject: string;
237
+ key: string;
238
+ displayName: string;
239
+ clientId: any;
240
+ allowedClients: string[];
241
+ roles: any[];
242
+ permissions: string[];
243
+ clientFromRequest: boolean;
244
+ }>;
245
+ };
246
+ /**
247
+ * @param {{ strategies: ReturnType<typeof bluPlatformAuth|typeof teamIdpAuth|typeof apiKeyAuth>[], apiUrl?: string }} opts
248
+ * apiUrl — this API's own public URL, as configured. Tokens from local-only
249
+ * (passwordless) sign-ins are accepted only when it's on this machine; leave
250
+ * it out and they're always refused.
251
+ */
252
+ declare function createModuleAuth({ strategies, apiUrl }: {
253
+ strategies: ReturnType<typeof bluPlatformAuth | typeof teamIdpAuth | typeof apiKeyAuth>[];
254
+ apiUrl?: string;
255
+ }): {
256
+ authenticate: (req: any, res: any, next: any) => Promise<any>;
257
+ verifyRequest: (request: any) => Promise<{
258
+ authMode: string;
259
+ kind: string;
260
+ issuer: string;
261
+ subject: any;
262
+ key: string;
263
+ devIdentity: any;
264
+ displayName: any;
265
+ email: any;
266
+ provider: any;
267
+ via: any;
268
+ actor: any;
269
+ clientId: any;
270
+ allowedClients: any;
271
+ roles: any;
272
+ permissions: any[];
273
+ } | {
274
+ authMode: string;
275
+ kind: string;
276
+ issuer: string;
277
+ /**
278
+ * user id, or api-key id
279
+ */
280
+ subject?: string;
281
+ /**
282
+ * stable identity across requests: `<authMode>|<issuer>|<subject>`.
283
+ * Key your own records (audit, ownership, preferences) on this —
284
+ * a Blu user needs no account in your system first.
285
+ */
286
+ key: string;
287
+ displayName?: string;
288
+ email?: string;
289
+ /**
290
+ * Blu only: the app that obtained the token ("blu-host",
291
+ * "blu-concierge", or your own standalone app)
292
+ */
293
+ via?: string;
294
+ /**
295
+ * Blu only: software acting for the user (e.g. "blu-concierge")
296
+ */
297
+ actor?: string;
298
+ /**
299
+ * active client (from the credential, never the request — except see api keys)
300
+ */
301
+ clientId?: string | null;
302
+ allowedClients?: string[];
303
+ roles?: string[];
304
+ /**
305
+ * resolved; may contain "*"
306
+ */
307
+ permissions?: string[];
308
+ devIdentity: any;
309
+ } | {
310
+ subject: any;
311
+ displayName: any;
312
+ email: any;
313
+ clientId: any;
314
+ allowedClients: any;
315
+ roles: any[];
316
+ permissions: any;
317
+ authMode: string;
318
+ kind: string;
319
+ issuer: string;
320
+ key: string;
321
+ devIdentity: any;
322
+ } | {
323
+ authMode: string;
324
+ kind: string;
325
+ issuer: string;
326
+ subject: string;
327
+ key: string;
328
+ displayName: string;
329
+ clientId: any;
330
+ allowedClients: string[];
331
+ roles: any[];
332
+ permissions: string[];
333
+ clientFromRequest: boolean;
334
+ }>;
335
+ allows: (policy: any, principal: any, askedClientId: any) => any;
336
+ authorize: (policy: Partial<Record<"blu" | "service" | "team" | "api-key", string[]>>) => (req: any, res: any, next: any) => any;
337
+ requirePermission: (permission: any) => (req: any, res: any, next: any) => any;
338
+ modes: string[];
339
+ };
340
+ /**
341
+ * Who this API has seen, keyed by `principal.key` — so a person signed in with
342
+ * Blu needs no account in the team's own user store first. The team records
343
+ * them on first sight (just-in-time) and can attach its own data to that key:
344
+ * "flagged by", audit trails, preferences. Nothing is linked to the team's own
345
+ * users; a Blu user and a team-IdP user are different principals.
346
+ *
347
+ * In-memory here; a real team keeps this in its own database.
348
+ *
349
+ * const people = createPrincipalDirectory();
350
+ * app.get("/x", [authenticate, people.remember], authorize({...}), handler)
351
+ */
352
+ declare function createPrincipalDirectory(): {
353
+ remember: (req: any, _res: any, next: any) => void;
354
+ get: (key: any) => any;
355
+ list: () => any[];
356
+ };
357
+ /** Parse AUTH_MODES="blu,service,team,api-key" into a Set (defaults to all). */
358
+ declare function enabledModes(value: any, all?: string[]): Set<any>;
359
+ /**
360
+ * Explicit-allowlist CORS. Written out (rather than `cors()`) so the mechanics
361
+ * are visible: echo only allowlisted origins, `Vary: Origin`, answer preflights
362
+ * for the Authorization header. Unlisted origins get no CORS headers at all, so
363
+ * the *browser* refuses to hand the response to the calling page.
364
+ *
365
+ * `origins` may be an array or a (possibly async) function, for allowlists that
366
+ * come from live config such as the registry.
367
+ *
368
+ * @param {{ origins: string[] | (() => string[] | Promise<string[]>), credentials?: boolean }} opts
369
+ */
370
+ declare function corsAllowlist({ origins, credentials }: {
371
+ origins: string[] | (() => string[] | Promise<string[]>);
372
+ credentials?: boolean;
373
+ }): (req: any, res: any, next: any) => Promise<any>;
374
+ /** Tiny request logger that shows who is calling and how — handy in the demo. */
375
+ declare function originLogger(name: any): (req: any, _res: any, next: any) => any;
376
+ /**
377
+ * @typedef {object} Principal
378
+ * @property {"blu"|"service"|"team"|"api-key"} authMode
379
+ * @property {"user"|"service"} kind
380
+ * @property {string} issuer
381
+ * @property {string} subject user id, or api-key id
382
+ * @property {string} key stable identity across requests: `<authMode>|<issuer>|<subject>`.
383
+ * Key your own records (audit, ownership, preferences) on this —
384
+ * a Blu user needs no account in your system first.
385
+ * @property {string} displayName
386
+ * @property {string} [email]
387
+ * @property {string} [via] Blu only: the app that obtained the token ("blu-host",
388
+ * "blu-concierge", or your own standalone app)
389
+ * @property {string} [actor] Blu only: software acting for the user (e.g. "blu-concierge")
390
+ * @property {string|null} clientId active client (from the credential, never the request — except see api keys)
391
+ * @property {string[]} allowedClients
392
+ * @property {string[]} roles
393
+ * @property {string[]} permissions resolved; may contain "*"
394
+ */
395
+ declare class AuthError extends Error {
396
+ constructor(status: any, code: any, detail: any);
397
+ status: any;
398
+ code: any;
399
+ }
400
+ declare function hashApiKey(key: any): any;
401
+ type Principal = {
402
+ authMode: "blu" | "service" | "team" | "api-key";
403
+ kind: "user" | "service";
404
+ issuer: string;
405
+ /**
406
+ * user id, or api-key id
407
+ */
408
+ subject: string;
409
+ /**
410
+ * stable identity across requests: `<authMode>|<issuer>|<subject>`.
411
+ * Key your own records (audit, ownership, preferences) on this —
412
+ * a Blu user needs no account in your system first.
413
+ */
414
+ key: string;
415
+ displayName: string;
416
+ email?: string;
417
+ /**
418
+ * Blu only: the app that obtained the token ("blu-host",
419
+ * "blu-concierge", or your own standalone app)
420
+ */
421
+ via?: string;
422
+ /**
423
+ * Blu only: software acting for the user (e.g. "blu-concierge")
424
+ */
425
+ actor?: string;
426
+ /**
427
+ * active client (from the credential, never the request — except see api keys)
428
+ */
429
+ clientId: string | null;
430
+ allowedClients: string[];
431
+ roles: string[];
432
+ /**
433
+ * resolved; may contain "*"
434
+ */
435
+ permissions: string[];
436
+ };
437
+
438
+ /**
439
+ * Blu's public addresses, per environment ("dev", "staging" or "prod"). Configure your API from these
440
+ * instead of copying URLs: what to trust (the issuer), who may call you (the shell origins), and where your
441
+ * pipeline registers releases (the registry).
442
+ */
443
+ /** Where every module's pipeline registers its releases, for every environment. Permanent. */
444
+ declare const REGISTRY_URL: "https://registry.blu-ce.horizonmedia.com";
445
+ declare function bluShellOrigins(stage: "dev" | "staging" | "prod"): string[];
446
+ declare function bluIssuer(stage: "dev" | "staging" | "prod"): string;
447
+
448
+ export { AuthError, REGISTRY_URL, apiKeyAuth, bluIssuer, bluPlatformAuth, bluServiceAuth, bluShellOrigins, corsAllowlist, createMcpEndpoint, createMcpWebHandler, createModuleAuth, createPrincipalDirectory, enabledModes, hashApiKey, link, originLogger, policyAllows, teamIdpAuth };