@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.
- package/dist/agent/agent-mesh-join.js +80 -0
- package/dist/agent/main.js +61 -2
- package/dist/api/auth-whoami.js +1 -1
- package/dist/api/core/auth-admin-tokens.procedures.js +138 -0
- package/dist/api/core/auth-limits.js +77 -0
- package/dist/api/core/auth-reauth.procedures.js +108 -0
- package/dist/api/core/auth-sessions.procedures.js +166 -0
- package/dist/api/core/auth.router.js +281 -80
- package/dist/api/health/adaptive-probe.routes.js +1 -1
- package/dist/api/trpc/core-cap-bridge.js +47 -5
- package/dist/api/trpc/generated-cap-mounts.js +19 -1
- package/dist/api/trpc/generated-cap-routers.js +512 -205
- package/dist/api/trpc/trpc.context.js +86 -34
- package/dist/api/trpc/trpc.middleware.js +8 -11
- package/dist/api/trpc/trpc.router.js +6 -1
- package/dist/auth/auth-network-policy.js +75 -0
- package/dist/auth/auth-rate-limit.js +25 -20
- package/dist/auth/client-address.js +68 -0
- package/dist/auth/external-link-intents.js +12 -3
- package/dist/auth/hub-sessions.js +74 -0
- package/dist/auth/native-link-completions.js +68 -0
- package/dist/auth/native-return.js +33 -0
- package/dist/auth/session-lifetimes.js +46 -0
- package/dist/auth/session-mint.js +110 -13
- package/dist/auth/sso-finish-deps.js +56 -1
- package/dist/auth/sso-finish.js +132 -17
- package/dist/auth/sso-routes.js +95 -0
- package/dist/auth/used-bridge-ids.js +52 -0
- package/dist/core/addon/addon-registry.service.js +5 -0
- package/dist/core/agent/agent-registry.service.js +10 -0
- package/dist/core/auth/auth.service.js +50 -1
- package/dist/core/auth/handoff-code.service.js +9 -3
- package/dist/core/moleculer/hub-mesh-guard.js +132 -0
- package/dist/core/moleculer/moleculer.service.js +33 -0
- package/dist/main.js +92 -28
- 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
|
|
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
|
-
|
|
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:
|
|
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;
|