@camstack/server 1.2.486 → 1.2.487

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 (36) hide show
  1. package/dist/agent/agent-mesh-join.js +80 -0
  2. package/dist/agent/main.js +61 -2
  3. package/dist/api/auth-whoami.js +1 -1
  4. package/dist/api/core/auth-admin-tokens.procedures.js +138 -0
  5. package/dist/api/core/auth-limits.js +77 -0
  6. package/dist/api/core/auth-reauth.procedures.js +108 -0
  7. package/dist/api/core/auth-sessions.procedures.js +166 -0
  8. package/dist/api/core/auth.router.js +281 -80
  9. package/dist/api/health/adaptive-probe.routes.js +1 -1
  10. package/dist/api/trpc/core-cap-bridge.js +47 -5
  11. package/dist/api/trpc/generated-cap-mounts.js +19 -1
  12. package/dist/api/trpc/generated-cap-routers.js +512 -205
  13. package/dist/api/trpc/trpc.context.js +86 -34
  14. package/dist/api/trpc/trpc.middleware.js +8 -11
  15. package/dist/api/trpc/trpc.router.js +6 -1
  16. package/dist/auth/auth-network-policy.js +75 -0
  17. package/dist/auth/auth-rate-limit.js +25 -20
  18. package/dist/auth/client-address.js +68 -0
  19. package/dist/auth/external-link-intents.js +12 -3
  20. package/dist/auth/hub-sessions.js +74 -0
  21. package/dist/auth/native-link-completions.js +68 -0
  22. package/dist/auth/native-return.js +33 -0
  23. package/dist/auth/session-lifetimes.js +46 -0
  24. package/dist/auth/session-mint.js +110 -13
  25. package/dist/auth/sso-finish-deps.js +56 -1
  26. package/dist/auth/sso-finish.js +132 -17
  27. package/dist/auth/sso-routes.js +95 -0
  28. package/dist/auth/used-bridge-ids.js +52 -0
  29. package/dist/core/addon/addon-registry.service.js +5 -0
  30. package/dist/core/agent/agent-registry.service.js +10 -0
  31. package/dist/core/auth/auth.service.js +50 -1
  32. package/dist/core/auth/handoff-code.service.js +9 -3
  33. package/dist/core/moleculer/hub-mesh-guard.js +132 -0
  34. package/dist/core/moleculer/moleculer.service.js +33 -0
  35. package/dist/main.js +92 -28
  36. package/package.json +13 -13
@@ -53,7 +53,7 @@ function registerAdaptiveProbeRoute(fastify, deps) {
53
53
  }, deps.verifyToken);
54
54
  if (!authorized)
55
55
  return reply.status(401).send({ ok: false, error: 'unauthorized' });
56
- const key = (0, auth_rate_limit_js_1.clientKeyFromRequest)(req);
56
+ const key = (0, auth_rate_limit_js_1.clientKeyFromRequest)(req, deps.network);
57
57
  if (key !== null) {
58
58
  const verdict = deps.limiter.check(`adaptive-probe:${key}`);
59
59
  if (!verdict.allowed) {
@@ -18,10 +18,12 @@
18
18
  * routes the call exactly like any addon-provided cap.
19
19
  */
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.CORE_CAP_REMOTE_FORBIDDEN_TYPE = exports.REMOTE_DENIED_NAMESPACES = void 0;
21
22
  exports.isCoreServiceCapName = isCoreServiceCapName;
22
23
  exports.describeMeshOrigin = describeMeshOrigin;
23
24
  exports.buildCoreCapService = buildCoreCapService;
24
25
  const system_1 = require("@camstack/system");
26
+ const moleculer_1 = require("moleculer");
25
27
  const http_request_census_singleton_js_1 = require("./http-request-census.singleton.js");
26
28
  const server_provided_caps_js_1 = require("./server-provided-caps.js");
27
29
  const trpc_context_js_1 = require("./trpc.context.js");
@@ -37,6 +39,10 @@ const trpc_middleware_js_1 = require("./trpc.middleware.js");
37
39
  * - `auth` — issuing service / scoped tokens over the trusted mesh
38
40
  * would let any forked addon mint admin credentials. Addons must
39
41
  * not perform authentication operations; this stays hub-local.
42
+ * - `repl` — `repl.execute` runs code in the hub process (`vm` is no
43
+ * boundary). The admin UI's REPL reaches it over HTTP tRPC as an admin
44
+ * session; nothing calls it over the mesh, and a mesh caller must never
45
+ * be able to (D802, security plan §1).
40
46
  * - `live`, `systemEvents` — listed in `NEVER_BRIDGED_CAPS`
41
47
  * (`trpc-links.ts`). They are hub-only push streams, not
42
48
  * request/reply, and the worker side fast-fails on them by design.
@@ -51,9 +57,21 @@ const CORE_NAMESPACES = new Set([
51
57
  'hwaccel',
52
58
  'streamProbe',
53
59
  'eventBusProxy',
54
- 'repl',
55
60
  'addonSettingsRaw',
56
61
  ]);
62
+ /**
63
+ * Bridged namespaces another NODE may not call (D802) — only the hub's own
64
+ * runners may. `logs` reads the hub's whole log store, `addonSettingsRaw` every
65
+ * addon's raw config (tokens included). No addon calls either over the mesh
66
+ * (they read their own settings through the settings door), so refusing them
67
+ * to an agent costs nothing and takes them off the LAN.
68
+ */
69
+ exports.REMOTE_DENIED_NAMESPACES = new Set([
70
+ 'logs',
71
+ 'addonSettingsRaw',
72
+ ]);
73
+ /** Moleculer error `type` of a bridged call another node may not make. */
74
+ exports.CORE_CAP_REMOTE_FORBIDDEN_TYPE = 'CORE_CAP_REMOTE_FORBIDDEN';
57
75
  /**
58
76
  * camelCase → kebab-case. Mirrors `toKebab` in `trpc-links.ts` so the
59
77
  * action name matches what `brokerTransportLink` derives from `op.path`.
@@ -164,9 +182,21 @@ function resolveInvoker(caller, path) {
164
182
  * appRouter. Query + mutation procedures in {@link CORE_NAMESPACES} are
165
183
  * exposed; subscriptions are skipped (the broker transport is
166
184
  * request/reply only).
185
+ *
186
+ * Each call runs under a principal naming its origin (D802): `mesh` for the
187
+ * hub's own runners, `node:<nodeId>` for another node.
167
188
  */
168
- function buildCoreCapService(appRouter, census = (0, http_request_census_singleton_js_1.getHttpRequestCensus)()) {
169
- const meshCaller = (0, trpc_middleware_js_1.createCallerFactory)(appRouter)((0, trpc_context_js_1.createMeshTrpcContext)());
189
+ function buildCoreCapService(appRouter, census = (0, http_request_census_singleton_js_1.getHttpRequestCensus)(), options = {}) {
190
+ const callerFactory = (0, trpc_middleware_js_1.createCallerFactory)(appRouter);
191
+ const callers = new Map();
192
+ const callerFor = (principal) => {
193
+ let caller = callers.get(principal);
194
+ if (caller === undefined) {
195
+ caller = callerFactory((0, trpc_context_js_1.createMeshTrpcContext)(principal));
196
+ callers.set(principal, caller);
197
+ }
198
+ return caller;
199
+ };
170
200
  const discovered = [];
171
201
  collectProcedures(appRouter._def.procedures, '', discovered);
172
202
  const actions = [];
@@ -179,17 +209,29 @@ function buildCoreCapService(appRouter, census = (0, http_request_census_singlet
179
209
  continue;
180
210
  if (type === 'subscription')
181
211
  continue;
182
- const invoke = resolveInvoker(meshCaller, path);
183
- if (invoke === null)
212
+ if (resolveInvoker(callerFor('mesh'), path) === null)
184
213
  continue;
185
214
  const method = path.slice(dot + 1);
186
215
  actions.push({
187
216
  actionName: `${toKebab(namespace)}.${method}`,
188
217
  invoke: (input, origin) => {
189
218
  countMeshCall(census, path, origin);
219
+ const remoteNode = remoteOrigin(origin, options.ownNodeId);
220
+ if (remoteNode !== null && exports.REMOTE_DENIED_NAMESPACES.has(namespace)) {
221
+ throw new moleculer_1.Errors.MoleculerClientError(`"${path}" is not callable from another node (caller "${remoteNode}")`, 403, exports.CORE_CAP_REMOTE_FORBIDDEN_TYPE);
222
+ }
223
+ const invoke = resolveInvoker(callerFor(remoteNode === null ? 'mesh' : `node:${remoteNode}`), path);
224
+ if (invoke === null)
225
+ throw new Error(`$core-caps: "${path}" is not invocable`);
190
226
  return invoke(input);
191
227
  },
192
228
  });
193
229
  }
194
230
  return (0, system_1.createCoreCapService)({ actions });
195
231
  }
232
+ /** The calling node when it is not the hub itself; `null` for the hub's own calls. */
233
+ function remoteOrigin(origin, ownNodeId) {
234
+ if (ownNodeId === undefined || origin.nodeId === null)
235
+ return null;
236
+ return origin.nodeId === ownNodeId ? null : origin.nodeId;
237
+ }
@@ -2,7 +2,7 @@
2
2
  // AUTO-GENERATED by scripts/generate-cap-mounts.ts — DO NOT EDIT
3
3
  // Re-run: npx tsx scripts/generate-cap-mounts.ts
4
4
  //
5
- // Mounted: 164 Skipped (legacy): 3
5
+ // Mounted: 166 Skipped (legacy): 3
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.LEGACY_SHAPE_SKIP = void 0;
8
8
  exports.mountAllCaps = mountAllCaps;
@@ -530,6 +530,24 @@ function mountAllCaps(services) {
530
530
  terminalSession: (0, generated_cap_routers_js_1.createCapRouter_terminalSession)((_ctx) => reg?.getSingleton('terminal-session') ??
531
531
  null, remoteCapProxy),
532
532
  toast: (0, generated_cap_routers_js_1.createCapRouter_toast)((_ctx) => reg?.getSingleton('toast') ?? null, remoteCapProxy),
533
+ training: (0, generated_cap_routers_js_1.createCapRouter_training)((_ctx) => reg?.getSingleton('training') ?? null, remoteCapProxy),
534
+ trainingProvider: (0, generated_cap_routers_js_1.createCapRouter_trainingProvider)((_ctx, addonId) => {
535
+ if (!reg)
536
+ return null;
537
+ if (addonId !== undefined) {
538
+ return reg.getProviderByAddonId('training-provider', addonId);
539
+ }
540
+ const entries = reg.getCollectionEntries('training-provider');
541
+ if (entries.length === 0)
542
+ return null;
543
+ const providers = entries.map(([, p]) => p);
544
+ const first = providers[0];
545
+ return {
546
+ ...first,
547
+ describe: (0, cap_mount_helpers_js_1.concatCollection)(providers, 'describe'),
548
+ listJobs: (0, cap_mount_helpers_js_1.concatCollection)(providers, 'listJobs'),
549
+ };
550
+ }, remoteCapProxy),
533
551
  turnProvider: (0, generated_cap_routers_js_1.createCapRouter_turnProvider)((_ctx, addonId) => {
534
552
  if (!reg)
535
553
  return null;