@camstack/server 1.2.592 → 1.2.594
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/addon-root-inventory.js +11 -50
- package/dist/agent/addon-scan.js +6 -44
- package/dist/agent/agent-addon-runtime.js +3 -8
- package/dist/agent/agent-addon-updates.js +12 -52
- package/dist/agent/agent-cap-dispatch-service.js +3 -5
- package/dist/agent/agent-config.js +7 -42
- package/dist/agent/agent-deploy-swap.js +5 -41
- package/dist/agent/agent-group-runner.js +8 -47
- package/dist/agent/agent-http-auth.js +10 -22
- package/dist/agent/agent-http.js +69 -115
- package/dist/agent/agent-hub-addon-update.js +10 -16
- package/dist/agent/agent-hub-registration.js +4 -7
- package/dist/agent/agent-infra-capabilities.js +5 -9
- package/dist/agent/agent-json-file.js +5 -42
- package/dist/agent/agent-mesh-gate.js +5 -8
- package/dist/agent/agent-node-certificates.js +25 -28
- package/dist/agent/agent-pairing-local.js +10 -47
- package/dist/agent/agent-pairing-routes.js +16 -20
- package/dist/agent/agent-pairing-store.js +18 -58
- package/dist/agent/agent-reconnect.js +1 -4
- package/dist/agent/agent-service.js +42 -78
- package/dist/agent/agent-sniffing-listener.js +11 -48
- package/dist/agent/agent-undeploy.js +5 -41
- package/dist/agent/agent-update-routes.js +14 -19
- package/dist/agent/agent-update-service.js +38 -80
- package/dist/agent/apply-model-distribution.js +1 -4
- package/dist/agent/apply-stage-dev-uploads.js +6 -9
- package/dist/agent/derive-hub-url.js +3 -8
- package/dist/agent/fetch-bundle-from-hub.js +6 -9
- package/dist/agent/hubs-found.js +6 -43
- package/dist/agent/infra-boot-guard.js +1 -4
- package/dist/agent/infra-boot-plan.js +2 -6
- package/dist/agent/main.js +158 -186
- package/dist/agent/register-agent-cap-dispatch.js +3 -6
- package/dist/agent/serial-queue.js +2 -7
- package/dist/agent-status-page.js +1 -4
- package/dist/api/addon-upload.js +22 -61
- package/dist/api/agent-addon-delivery.js +6 -11
- package/dist/api/auth-whoami.js +1 -4
- package/dist/api/backup-download-pump.js +5 -9
- package/dist/api/backup-download.js +7 -10
- package/dist/api/core/addon-settings.router.js +16 -19
- package/dist/api/core/agent-pairing.router.js +76 -79
- package/dist/api/core/agents.router.js +21 -24
- package/dist/api/core/auth-admin-tokens.procedures.js +48 -52
- package/dist/api/core/auth-device-cert.procedures.js +62 -67
- package/dist/api/core/auth-limits.js +16 -25
- package/dist/api/core/auth-reauth.procedures.js +32 -37
- package/dist/api/core/auth-revoke-credentials.procedures.js +22 -26
- package/dist/api/core/auth-sessions.procedures.js +52 -56
- package/dist/api/core/auth.router.js +300 -304
- package/dist/api/core/cap-providers.js +74 -121
- package/dist/api/core/capabilities.router.js +34 -37
- package/dist/api/core/capability-preference-writes.js +10 -17
- package/dist/api/core/certificates.router.js +106 -109
- package/dist/api/core/cluster-nodes.router.js +15 -18
- package/dist/api/core/collection-preference.js +2 -6
- package/dist/api/core/event-bus-proxy.router.js +34 -37
- package/dist/api/core/failure-contributions.js +4 -8
- package/dist/api/core/hwaccel.router.js +17 -20
- package/dist/api/core/integration-secrets.js +8 -12
- package/dist/api/core/lifecycle-job-runner.js +13 -49
- package/dist/api/core/live-events.router.js +29 -32
- package/dist/api/core/load-contributions.js +4 -8
- package/dist/api/core/logging-settings.js +30 -43
- package/dist/api/core/logs.router.js +40 -43
- package/dist/api/core/notifications.router.js +15 -18
- package/dist/api/core/public-event-filter.js +4 -8
- package/dist/api/core/repl.router.js +18 -21
- package/dist/api/core/request-census-settings.js +11 -16
- package/dist/api/core/server-management.provider.js +1 -4
- package/dist/api/core/server-name.js +9 -17
- package/dist/api/core/site-location.js +16 -24
- package/dist/api/core/stream-probe.router.js +21 -24
- package/dist/api/core/system-events.router.js +23 -26
- package/dist/api/cors/cors-policy.js +20 -29
- package/dist/api/cors/register-cors-policy.js +9 -14
- package/dist/api/deploy-stage-registry.js +6 -10
- package/dist/api/dev-uploads-bundle.js +12 -50
- package/dist/api/health/adaptive-probe.routes.js +9 -13
- package/dist/api/health/health.routes.js +8 -13
- package/dist/api/hub-cluster-url.js +3 -9
- package/dist/api/model-distributor.js +12 -50
- package/dist/api/oauth2/consent-page.js +10 -13
- package/dist/api/oauth2/oauth2-routes.js +17 -25
- package/dist/api/oauth2/private-host.js +1 -4
- package/dist/api/server-upload-propagate.js +15 -51
- package/dist/api/server-upload.js +13 -50
- package/dist/api/sse-no-compression.js +1 -4
- package/dist/api/static/frame-policy.js +4 -9
- package/dist/api/static/precompressed-asset.js +15 -57
- package/dist/api/static/spa-mount.js +8 -47
- package/dist/api/static/spa-static.js +5 -12
- package/dist/api/tarball-manifest.js +7 -44
- package/dist/api/trpc/cap-mount-helpers.js +7 -12
- package/dist/api/trpc/cap-route-error-formatter.js +10 -13
- package/dist/api/trpc/cap-router-runtime.js +40 -51
- package/dist/api/trpc/client-ip.js +9 -20
- package/dist/api/trpc/core-cap-bridge.js +32 -42
- package/dist/api/trpc/device-collection-dispatch.js +23 -27
- package/dist/api/trpc/device-config-secret-redaction.js +24 -40
- package/dist/api/trpc/generated-cap-mounts.js +219 -221
- package/dist/api/trpc/generated-cap-routers.js +4878 -4726
- package/dist/api/trpc/http-request-census-hook.js +8 -11
- package/dist/api/trpc/http-request-census.js +34 -41
- package/dist/api/trpc/http-request-census.singleton.js +4 -8
- package/dist/api/trpc/localized-schema-paths.js +1 -4
- package/dist/api/trpc/principal-visibility.js +14 -22
- package/dist/api/trpc/remote-addon-update-grant.js +1 -4
- package/dist/api/trpc/scope-access.js +10 -15
- package/dist/api/trpc/server-provided-caps.js +3 -7
- package/dist/api/trpc/share-view-access.js +6 -14
- package/dist/api/trpc/trpc-error-device-tags.js +3 -6
- package/dist/api/trpc/trpc-error-principal.js +2 -6
- package/dist/api/trpc/trpc.context.js +20 -25
- package/dist/api/trpc/trpc.middleware.js +38 -46
- package/dist/api/trpc/trpc.router.js +58 -99
- package/dist/api/trpc/user-error.js +5 -8
- package/dist/api/trpc/ws-request-census.js +17 -24
- package/dist/api/trpc/ws-slow-consumer-guard.js +11 -16
- package/dist/api/upload-auth.js +5 -9
- package/dist/auth/addon-route-jwt-gate.js +4 -7
- package/dist/auth/addon-route-share-gate.js +7 -12
- package/dist/auth/app-confirm-page.js +17 -22
- package/dist/auth/app-confirm-routes.js +26 -31
- package/dist/auth/app-confirmations.js +7 -44
- package/dist/auth/app-page-shell.js +17 -30
- package/dist/auth/app-page-strings.js +11 -17
- package/dist/auth/app-passkey-page.js +16 -21
- package/dist/auth/app-passkey-routes.js +37 -41
- package/dist/auth/app-refused-route.js +13 -18
- package/dist/auth/auth-network-policy.js +11 -15
- package/dist/auth/auth-provider-gate.js +4 -9
- package/dist/auth/auth-rate-limit.js +3 -7
- package/dist/auth/client-address.js +6 -9
- package/dist/auth/device-enroll-proofs.js +6 -43
- package/dist/auth/external-link-intents.js +3 -7
- package/dist/auth/hub-sessions.js +6 -12
- package/dist/auth/native-link-completions.js +7 -11
- package/dist/auth/native-return.js +9 -13
- package/dist/auth/passkey-provider.js +26 -32
- package/dist/auth/second-factors.js +6 -10
- package/dist/auth/session-connections.js +6 -12
- package/dist/auth/session-cookie.js +12 -23
- package/dist/auth/session-lifetimes.js +8 -12
- package/dist/auth/session-mint.js +4 -11
- package/dist/auth/sso-finish-app.js +13 -16
- package/dist/auth/sso-finish-deps.js +21 -27
- package/dist/auth/sso-finish.js +28 -38
- package/dist/auth/sso-routes.js +22 -25
- package/dist/auth/used-bridge-ids.js +1 -5
- package/dist/boot/boot-config.js +16 -55
- package/dist/boot/init-sequence.js +1 -4
- package/dist/boot/integration-id-backfill.js +3 -8
- package/dist/boot/platform-keychain-key.js +5 -42
- package/dist/boot/post-boot.service.js +14 -18
- package/dist/boot/reconcile-lifecycle-jobs.js +3 -6
- package/dist/boot/retired-public-url-env.js +4 -8
- package/dist/bootstrap-packages.js +1 -4
- package/dist/closure-node-modules.js +2 -38
- package/dist/closure-patches/patch-reach.json +6 -2
- package/dist/core/addon/addon-call-gateway.js +1 -5
- package/dist/core/addon/addon-package.service.js +83 -123
- package/dist/core/addon/addon-process-view.js +2 -6
- package/dist/core/addon/addon-realign-plan.js +1 -4
- package/dist/core/addon/addon-registry.service.js +254 -197
- package/dist/core/addon/addon-row-manifest.js +1 -4
- package/dist/core/addon/addon-search.service.js +3 -7
- package/dist/core/addon/addon-settings-provider.js +1 -3
- package/dist/core/addon/addon-uninstall.js +4 -11
- package/dist/core/addon/addon.tokens.js +2 -5
- package/dist/core/addon/device-meta-mirror.js +8 -13
- package/dist/core/addon/forked-custom-actions.js +6 -11
- package/dist/core/addon/integration-visibility.js +3 -6
- package/dist/core/addon/materialize-route-path.js +1 -4
- package/dist/core/addon/package-dir-utils.js +8 -45
- package/dist/core/addon/prune-misplaced-addons.js +5 -43
- package/dist/core/addon/runner-convergence.js +1 -4
- package/dist/core/addon/runner-serialization.js +1 -4
- package/dist/core/addon/runner-spawn-fanout.js +3 -8
- package/dist/core/addon/settings-door-wiring.js +5 -8
- package/dist/core/addon/single-flight-refresh.js +1 -4
- package/dist/core/addon-bridge/addon-bridge.service.js +7 -44
- package/dist/core/addon-pages/addon-pages.service.js +3 -40
- package/dist/core/addon-widgets/addon-widgets.service.js +4 -42
- package/dist/core/agent/agent-addon-backfill.js +9 -17
- package/dist/core/agent/agent-delivery-ledger.js +3 -7
- package/dist/core/agent/agent-liveness-event.js +2 -6
- package/dist/core/agent/agent-pairing-http.js +41 -45
- package/dist/core/agent/agent-registry.service.js +53 -92
- package/dist/core/agent/cluster-node-history-mirror.js +3 -7
- package/dist/core/agent/cluster-node-history-store.js +113 -58
- package/dist/core/agent/hub-agent-pairing.js +1 -5
- package/dist/core/agent/node-certificates-client.js +3 -6
- package/dist/core/agent/node-directory.js +1 -4
- package/dist/core/app-links/well-known-app-links.js +2 -6
- package/dist/core/auth/auth.service.js +3 -8
- package/dist/core/auth/handoff-code.service.js +11 -49
- package/dist/core/auth/record-kinds.js +35 -0
- package/dist/core/auth/share-token.service.js +174 -122
- package/dist/core/capability/capability.service.js +1 -5
- package/dist/core/config/config.schema.js +1 -6
- package/dist/core/config/config.service.js +2 -6
- package/dist/core/events/event-bus.service.js +3 -7
- package/dist/core/feature/feature.service.js +2 -6
- package/dist/core/lifecycle/lifecycle-runner.singleton.js +5 -10
- package/dist/core/lifecycle/lifecycle-state-machine.js +1 -5
- package/dist/core/logging/log-ring-buffer.js +1 -5
- package/dist/core/logging/logging.service.js +5 -9
- package/dist/core/logging/scoped-logger.js +1 -5
- package/dist/core/moleculer/agent-readiness-pull.js +1 -4
- package/dist/core/moleculer/agent-registration-request.js +3 -7
- package/dist/core/moleculer/cap-call-fn.js +39 -5
- package/dist/core/moleculer/cap-route-authority.js +3 -8
- package/dist/core/moleculer/hub-mesh-guard.js +5 -9
- package/dist/core/moleculer/moleculer.service.js +108 -119
- package/dist/core/network/network-quality.service.js +2 -6
- package/dist/core/notification/notification-wrapper.service.js +3 -7
- package/dist/core/notification/toast-wrapper.service.js +3 -7
- package/dist/core/provider/provider.tokens.js +1 -4
- package/dist/core/repl/repl-engine.service.js +4 -8
- package/dist/core/runner-health/runner-boot-check.js +7 -15
- package/dist/core/runner-health/update-preflight.js +9 -14
- package/dist/core/server-update/server-update.service.js +12 -49
- package/dist/core/server-update/system-apply-closure-patches.js +6 -42
- package/dist/core/server-update/system-ensure-prebuilds.js +3 -6
- package/dist/core/server-update/system-exec-npm.js +2 -5
- package/dist/core/storage/fs-storage-backend.js +1 -5
- package/dist/core/storage/storage-location-manager.js +1 -5
- package/dist/core/storage/storage.service.js +2 -6
- package/dist/core/streaming/ffmpeg-stream-info.js +3 -8
- package/dist/core/streaming/stream-probe.service.js +12 -16
- package/dist/core/streaming/ttl-cache.js +1 -5
- package/dist/core/topology/topology-emitter.service.js +19 -23
- package/dist/core/update-availability-emitter.js +3 -7
- package/dist/core/update-availability-store.js +8 -45
- package/dist/core/updates/agent-installed-packages.js +5 -9
- package/dist/core/updates/restart-marker.js +3 -8
- package/dist/core/updates/update-check-scheduler.js +13 -18
- package/dist/core/updates/wrapper-update-checker.js +3 -7
- package/dist/first-boot-addon-plan.js +2 -6
- package/dist/framework-nodepath.js +2 -38
- package/dist/i18n/request-lang.js +7 -11
- package/dist/launcher.js +30 -65
- package/dist/lifecycle-journal-path.js +2 -38
- package/dist/main.js +287 -324
- package/dist/manual-boot.js +120 -157
- package/dist/node-role.js +1 -4
- package/dist/package-inventory.js +7 -48
- package/dist/pending-restore.js +7 -43
- package/dist/single-copy-cleanup-runner.js +9 -45
- package/dist/single-copy-cleanup.js +7 -15
- package/dist/sqlite-restore.js +9 -48
- package/dist/starter.js +4 -39
- package/dist/ui-serving/lazy-ui-serving.js +3 -39
- package/package.json +13 -13
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.singletonPreferenceKey = singletonPreferenceKey;
|
|
4
|
-
exports.singletonSwitchRefusal = singletonSwitchRefusal;
|
|
5
|
-
exports.writeSingletonPreference = writeSingletonPreference;
|
|
6
|
-
exports.writeCollectionToggle = writeCollectionToggle;
|
|
7
|
-
exports.writeCollectionEnabledSet = writeCollectionEnabledSet;
|
|
8
1
|
/**
|
|
9
2
|
* The ONE write order for an operator's capability preference: PERSIST, then
|
|
10
3
|
* switch the live registry (D675 amendment, 2026-09-29).
|
|
@@ -28,10 +21,10 @@ exports.writeCollectionEnabledSet = writeCollectionEnabledSet;
|
|
|
28
21
|
* `setCollectionEnabledProviders`) and the `addons` cap's
|
|
29
22
|
* `setCapabilityProviderEnabled`.
|
|
30
23
|
*/
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
import { TRPCError } from '@trpc/server';
|
|
25
|
+
import { persistCollectionDisabled } from './collection-preference';
|
|
33
26
|
/** ConfigService key holding a singleton preference (global or per-node). */
|
|
34
|
-
function singletonPreferenceKey(capability, nodeId) {
|
|
27
|
+
export function singletonPreferenceKey(capability, nodeId) {
|
|
35
28
|
return nodeId === undefined
|
|
36
29
|
? `capabilities.singleton.${capability}`
|
|
37
30
|
: `capabilities.singletonNode.${capability}.${nodeId}`;
|
|
@@ -67,7 +60,7 @@ function findCapability(registry, capability) {
|
|
|
67
60
|
* Reads the registry's own projection (`providers`, `perNode`), the same sets
|
|
68
61
|
* `setActiveSingleton` checks.
|
|
69
62
|
*/
|
|
70
|
-
function singletonSwitchRefusal(registry, write) {
|
|
63
|
+
export function singletonSwitchRefusal(registry, write) {
|
|
71
64
|
const cap = findCapability(registry, write.capability);
|
|
72
65
|
if (cap === null) {
|
|
73
66
|
return { code: 'NOT_FOUND', message: `Unknown capability: ${write.capability}` };
|
|
@@ -94,10 +87,10 @@ function singletonSwitchRefusal(registry, write) {
|
|
|
94
87
|
return null;
|
|
95
88
|
}
|
|
96
89
|
function refuse(refusal) {
|
|
97
|
-
throw new
|
|
90
|
+
throw new TRPCError({ code: refusal.code, message: refusal.message });
|
|
98
91
|
}
|
|
99
92
|
/** Persist a singleton preference, then switch the registry. */
|
|
100
|
-
async function writeSingletonPreference(store, registry, write) {
|
|
93
|
+
export async function writeSingletonPreference(store, registry, write) {
|
|
101
94
|
await serialised(write.capability, async () => {
|
|
102
95
|
const refusal = singletonSwitchRefusal(registry, write);
|
|
103
96
|
if (refusal !== null)
|
|
@@ -127,7 +120,7 @@ function applyDisabledSet(registry, capability, current, next) {
|
|
|
127
120
|
}
|
|
128
121
|
}
|
|
129
122
|
/** Persist one collection provider's enable/disable, then apply it live. */
|
|
130
|
-
async function writeCollectionToggle(store, registry, toggle) {
|
|
123
|
+
export async function writeCollectionToggle(store, registry, toggle) {
|
|
131
124
|
await serialised(toggle.capability, async () => {
|
|
132
125
|
const cap = requireCollection(registry, toggle.capability);
|
|
133
126
|
if (!cap.providers.includes(toggle.addonId)) {
|
|
@@ -142,7 +135,7 @@ async function writeCollectionToggle(store, registry, toggle) {
|
|
|
142
135
|
next.delete(toggle.addonId);
|
|
143
136
|
else
|
|
144
137
|
next.add(toggle.addonId);
|
|
145
|
-
await
|
|
138
|
+
await persistCollectionDisabled(store, toggle.capability, [...next]);
|
|
146
139
|
applyDisabledSet(registry, toggle.capability, current, next);
|
|
147
140
|
});
|
|
148
141
|
}
|
|
@@ -151,7 +144,7 @@ async function writeCollectionToggle(store, registry, toggle) {
|
|
|
151
144
|
* Registered providers outside `enabledAddonIds` are disabled; a disabled id
|
|
152
145
|
* whose provider is not registered right now keeps its disabled mark.
|
|
153
146
|
*/
|
|
154
|
-
async function writeCollectionEnabledSet(store, registry, capability, enabledAddonIds) {
|
|
147
|
+
export async function writeCollectionEnabledSet(store, registry, capability, enabledAddonIds) {
|
|
155
148
|
await serialised(capability, async () => {
|
|
156
149
|
const cap = requireCollection(registry, capability);
|
|
157
150
|
const wanted = new Set(enabledAddonIds);
|
|
@@ -163,7 +156,7 @@ async function writeCollectionEnabledSet(store, registry, capability, enabledAdd
|
|
|
163
156
|
else
|
|
164
157
|
next.add(providerId);
|
|
165
158
|
}
|
|
166
|
-
await
|
|
159
|
+
await persistCollectionDisabled(store, capability, [...next]);
|
|
167
160
|
applyDisabledSet(registry, capability, current, next);
|
|
168
161
|
});
|
|
169
162
|
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createCertificatesRouter = createCertificatesRouter;
|
|
4
1
|
/**
|
|
5
2
|
* Certificates inventory and management - fixed core API for Identity ->
|
|
6
3
|
* Certificates (D855, D856).
|
|
@@ -22,100 +19,100 @@ exports.createCertificatesRouter = createCertificatesRouter;
|
|
|
22
19
|
* the node, which must be paired again. Neither forgets a node:
|
|
23
20
|
* that stays `clusterNodes.forgetNode`.
|
|
24
21
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
import { X509Certificate } from 'node:crypto';
|
|
23
|
+
import { caFingerprint, dropProcessPeer, getProcessMeshCaState, getProcessAgentHttpsNames, getProcessMeshCertificateRegistry, getProcessMeshTlsListenerState, getProcessPeerLeaf, onProcessNodeRegistered, issueAgentHttpsLeaf, processMeshCrl, renewMeshCertificate, revokeMeshCertificate, } from '@camstack/system';
|
|
24
|
+
import { TRPCError } from '@trpc/server';
|
|
25
|
+
import { z } from 'zod';
|
|
26
|
+
import { adminProcedure, trpcRouter } from '../trpc/trpc.middleware.js';
|
|
30
27
|
const HUB_NODE_ID = 'hub';
|
|
31
28
|
const EXPIRING_SOON_MS = 7 * 86_400_000;
|
|
32
|
-
const TlsStatusRowSchema =
|
|
33
|
-
mode:
|
|
34
|
-
leafFingerprintSha256:
|
|
35
|
-
caFingerprintSha256:
|
|
36
|
-
validTo:
|
|
37
|
-
sans:
|
|
38
|
-
reissueError:
|
|
39
|
-
restartRequired:
|
|
29
|
+
const TlsStatusRowSchema = z.object({
|
|
30
|
+
mode: z.enum(['generated', 'uploaded', 'disabled']),
|
|
31
|
+
leafFingerprintSha256: z.string().nullable(),
|
|
32
|
+
caFingerprintSha256: z.string().nullable(),
|
|
33
|
+
validTo: z.string().nullable(),
|
|
34
|
+
sans: z.array(z.string()),
|
|
35
|
+
reissueError: z.string().nullable(),
|
|
36
|
+
restartRequired: z.boolean(),
|
|
40
37
|
});
|
|
41
|
-
const NodeHttpsSchema =
|
|
42
|
-
nodeId:
|
|
43
|
-
isHub:
|
|
44
|
-
online:
|
|
45
|
-
https:
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
const NodeHttpsSchema = z.object({
|
|
39
|
+
nodeId: z.string(),
|
|
40
|
+
isHub: z.boolean(),
|
|
41
|
+
online: z.boolean(),
|
|
42
|
+
https: z.discriminatedUnion('kind', [
|
|
43
|
+
z.object({ kind: z.literal('served'), status: TlsStatusRowSchema }),
|
|
44
|
+
z.object({ kind: z.literal('not-served'), reason: z.string() }),
|
|
48
45
|
/** The hub could not read it right now: say so, never present it as "none". */
|
|
49
|
-
|
|
46
|
+
z.object({ kind: z.literal('unreadable'), reason: z.string() }),
|
|
50
47
|
]),
|
|
51
48
|
});
|
|
52
|
-
const MeshLeafRowSchema =
|
|
53
|
-
serial:
|
|
54
|
-
fingerprintSha256:
|
|
55
|
-
notBefore:
|
|
56
|
-
notAfter:
|
|
57
|
-
issuedAt:
|
|
49
|
+
const MeshLeafRowSchema = z.object({
|
|
50
|
+
serial: z.string(),
|
|
51
|
+
fingerprintSha256: z.string(),
|
|
52
|
+
notBefore: z.number(),
|
|
53
|
+
notAfter: z.number(),
|
|
54
|
+
issuedAt: z.number(),
|
|
58
55
|
/** `enroll` | `pairing` | `renewal` | `boot` were issued by the hub; `observed` was read off a live connection. */
|
|
59
|
-
issuedVia:
|
|
60
|
-
status:
|
|
61
|
-
revokedAt:
|
|
62
|
-
revokedReason:
|
|
56
|
+
issuedVia: z.enum(['enroll', 'pairing', 'renewal', 'boot', 'observed']),
|
|
57
|
+
status: z.enum(['valid', 'expiring', 'expired', 'revoked']),
|
|
58
|
+
revokedAt: z.number().nullable(),
|
|
59
|
+
revokedReason: z.string().nullable(),
|
|
63
60
|
});
|
|
64
|
-
const MeshNodeSchema =
|
|
65
|
-
nodeId:
|
|
66
|
-
online:
|
|
61
|
+
const MeshNodeSchema = z.object({
|
|
62
|
+
nodeId: z.string(),
|
|
63
|
+
online: z.boolean(),
|
|
67
64
|
/** The leaf the node holds now: the newest non-revoked one. `null` when none is known. */
|
|
68
65
|
current: MeshLeafRowSchema.nullable(),
|
|
69
66
|
/** The node is connected and presents exactly `current`. `null` when it is offline. */
|
|
70
|
-
presentingCurrent:
|
|
67
|
+
presentingCurrent: z.boolean().nullable(),
|
|
71
68
|
/** Older and revoked leaves, newest first. */
|
|
72
|
-
history:
|
|
69
|
+
history: z.array(MeshLeafRowSchema),
|
|
73
70
|
});
|
|
74
|
-
const MeshInventorySchema =
|
|
75
|
-
ca:
|
|
76
|
-
|
|
77
|
-
state:
|
|
78
|
-
subject:
|
|
79
|
-
fingerprintSha256:
|
|
80
|
-
validTo:
|
|
71
|
+
const MeshInventorySchema = z.object({
|
|
72
|
+
ca: z.discriminatedUnion('state', [
|
|
73
|
+
z.object({
|
|
74
|
+
state: z.literal('ready'),
|
|
75
|
+
subject: z.string(),
|
|
76
|
+
fingerprintSha256: z.string(),
|
|
77
|
+
validTo: z.string(),
|
|
81
78
|
}),
|
|
82
|
-
|
|
79
|
+
z.object({ state: z.literal('unavailable'), reason: z.string() }),
|
|
83
80
|
]),
|
|
84
81
|
/** Serials the hub refuses at the mesh handshake. Persisted with the registry and replayed at boot. */
|
|
85
|
-
revokedSerials:
|
|
82
|
+
revokedSerials: z.array(z.string()),
|
|
86
83
|
/** `false` while the registry is not up (no settings store): the node list is then incomplete. */
|
|
87
|
-
registryAvailable:
|
|
84
|
+
registryAvailable: z.boolean(),
|
|
88
85
|
/** The hub's mesh TLS listener: `closed` means every agent is refused (e.g. revocations not loaded). */
|
|
89
|
-
listener:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
listener: z.discriminatedUnion('state', [
|
|
87
|
+
z.object({ state: z.literal('open') }),
|
|
88
|
+
z.object({ state: z.literal('closed'), reason: z.string(), since: z.number() }),
|
|
89
|
+
z.object({ state: z.literal('not-started') }),
|
|
93
90
|
]),
|
|
94
|
-
nodes:
|
|
91
|
+
nodes: z.array(MeshNodeSchema),
|
|
95
92
|
});
|
|
96
|
-
const MeshRenewResultSchema =
|
|
97
|
-
|
|
98
|
-
ok:
|
|
99
|
-
nodeId:
|
|
100
|
-
oldSerial:
|
|
101
|
-
newSerial:
|
|
102
|
-
newNotAfter:
|
|
93
|
+
const MeshRenewResultSchema = z.discriminatedUnion('ok', [
|
|
94
|
+
z.object({
|
|
95
|
+
ok: z.literal(true),
|
|
96
|
+
nodeId: z.string(),
|
|
97
|
+
oldSerial: z.string().nullable(),
|
|
98
|
+
newSerial: z.string(),
|
|
99
|
+
newNotAfter: z.number(),
|
|
103
100
|
}),
|
|
104
|
-
|
|
105
|
-
ok:
|
|
106
|
-
nodeId:
|
|
107
|
-
stage:
|
|
108
|
-
reason:
|
|
101
|
+
z.object({
|
|
102
|
+
ok: z.literal(false),
|
|
103
|
+
nodeId: z.string(),
|
|
104
|
+
stage: z.enum(['prepare', 'issue', 'install', 'reconnect', 'confirm']),
|
|
105
|
+
reason: z.string(),
|
|
109
106
|
/** The node is on its previous leaf (or never left it): nothing was broken. */
|
|
110
|
-
keptPreviousLeaf:
|
|
107
|
+
keptPreviousLeaf: z.boolean(),
|
|
111
108
|
}),
|
|
112
109
|
]);
|
|
113
|
-
const MeshRevokeResultSchema =
|
|
114
|
-
nodeId:
|
|
115
|
-
revokedSerials:
|
|
116
|
-
disconnected:
|
|
110
|
+
const MeshRevokeResultSchema = z.object({
|
|
111
|
+
nodeId: z.string(),
|
|
112
|
+
revokedSerials: z.array(z.string()),
|
|
113
|
+
disconnected: z.number().int(),
|
|
117
114
|
/** What the operator does next. Revoking does not forget the node. */
|
|
118
|
-
next:
|
|
115
|
+
next: z.string(),
|
|
119
116
|
});
|
|
120
117
|
function leafStatus(record, at) {
|
|
121
118
|
if (record.revokedAt !== null)
|
|
@@ -124,11 +121,11 @@ function leafStatus(record, at) {
|
|
|
124
121
|
return 'expired';
|
|
125
122
|
return record.notAfter - at < EXPIRING_SOON_MS ? 'expiring' : 'valid';
|
|
126
123
|
}
|
|
127
|
-
function createCertificatesRouter(deps) {
|
|
124
|
+
export function createCertificatesRouter(deps) {
|
|
128
125
|
const localNetwork = () => {
|
|
129
126
|
const provider = deps.registry?.getSingleton('local-network');
|
|
130
127
|
if (!provider) {
|
|
131
|
-
throw new
|
|
128
|
+
throw new TRPCError({
|
|
132
129
|
code: 'PRECONDITION_FAILED',
|
|
133
130
|
message: 'The local-network capability is not available yet',
|
|
134
131
|
});
|
|
@@ -136,22 +133,22 @@ function createCertificatesRouter(deps) {
|
|
|
136
133
|
return provider;
|
|
137
134
|
};
|
|
138
135
|
const peers = deps.peers ?? {
|
|
139
|
-
peerLeaf: (nodeId) =>
|
|
140
|
-
dropPeer: (nodeId) =>
|
|
136
|
+
peerLeaf: (nodeId) => getProcessPeerLeaf(nodeId),
|
|
137
|
+
dropPeer: (nodeId) => dropProcessPeer(nodeId),
|
|
141
138
|
};
|
|
142
|
-
const meshRegistry = deps.meshRegistry ??
|
|
143
|
-
const crl = deps.crl ??
|
|
139
|
+
const meshRegistry = deps.meshRegistry ?? getProcessMeshCertificateRegistry;
|
|
140
|
+
const crl = deps.crl ?? processMeshCrl;
|
|
144
141
|
const meshCa = deps.meshCa ??
|
|
145
142
|
(() => {
|
|
146
|
-
const state =
|
|
143
|
+
const state = getProcessMeshCaState();
|
|
147
144
|
return state.state === 'ready' ? state.ca : null;
|
|
148
145
|
});
|
|
149
|
-
const issueHttps = deps.issueHttps ??
|
|
146
|
+
const issueHttps = deps.issueHttps ?? issueAgentHttpsLeaf;
|
|
150
147
|
const listenerState = deps.listenerState ??
|
|
151
|
-
(() =>
|
|
152
|
-
const httpsNames = deps.httpsNames ??
|
|
148
|
+
(() => getProcessMeshTlsListenerState() ?? { state: 'not-started' });
|
|
149
|
+
const httpsNames = deps.httpsNames ?? getProcessAgentHttpsNames;
|
|
153
150
|
const registrations = deps.registrations ?? {
|
|
154
|
-
subscribe: (listener) =>
|
|
151
|
+
subscribe: (listener) => onProcessNodeRegistered(listener),
|
|
155
152
|
};
|
|
156
153
|
/** One certificate operation per node at a time: a second one is refused, not interleaved. */
|
|
157
154
|
const busy = new Set();
|
|
@@ -161,7 +158,7 @@ function createCertificatesRouter(deps) {
|
|
|
161
158
|
tags: { nodeId: key },
|
|
162
159
|
meta: { what },
|
|
163
160
|
});
|
|
164
|
-
throw new
|
|
161
|
+
throw new TRPCError({
|
|
165
162
|
code: 'CONFLICT',
|
|
166
163
|
message: `Another certificate operation is already running on ${key}. Wait for it to finish.`,
|
|
167
164
|
});
|
|
@@ -176,14 +173,14 @@ function createCertificatesRouter(deps) {
|
|
|
176
173
|
};
|
|
177
174
|
const requireAgent = (nodeId) => {
|
|
178
175
|
if (nodeId === HUB_NODE_ID) {
|
|
179
|
-
throw new
|
|
176
|
+
throw new TRPCError({
|
|
180
177
|
code: 'PRECONDITION_FAILED',
|
|
181
178
|
message: 'The hub is not an agent: its HTTPS leaf is renewed with renewHttpsOnNode on "hub", and its mesh leaf is re-issued at every boot.',
|
|
182
179
|
});
|
|
183
180
|
}
|
|
184
181
|
const client = deps.nodeClient(nodeId);
|
|
185
182
|
if (client === null) {
|
|
186
|
-
throw new
|
|
183
|
+
throw new TRPCError({
|
|
187
184
|
code: 'PRECONDITION_FAILED',
|
|
188
185
|
message: `Node ${nodeId} is not reachable, or runs an agent too old to manage its certificates (update it).`,
|
|
189
186
|
});
|
|
@@ -216,11 +213,11 @@ function createCertificatesRouter(deps) {
|
|
|
216
213
|
reissueError: null,
|
|
217
214
|
restartRequired: false,
|
|
218
215
|
});
|
|
219
|
-
return
|
|
216
|
+
return trpcRouter({
|
|
220
217
|
/** HTTPS certificate state per node. The hub serves its own; an agent serves a hub-issued leaf. */
|
|
221
|
-
listHttpsCertificates:
|
|
222
|
-
.input(
|
|
223
|
-
.output(
|
|
218
|
+
listHttpsCertificates: adminProcedure
|
|
219
|
+
.input(z.void())
|
|
220
|
+
.output(z.array(NodeHttpsSchema))
|
|
224
221
|
.query(async () => {
|
|
225
222
|
const nodes = deps.agentRegistry.listNodeLiveness();
|
|
226
223
|
const entries = nodes.some((n) => n.id === HUB_NODE_ID)
|
|
@@ -310,8 +307,8 @@ function createCertificatesRouter(deps) {
|
|
|
310
307
|
* local CA for the names the agent reports (private addresses and local-only host
|
|
311
308
|
* names), and the agent hot-swaps it - no restart, no private key on the mesh.
|
|
312
309
|
*/
|
|
313
|
-
renewHttpsOnNode:
|
|
314
|
-
.input(
|
|
310
|
+
renewHttpsOnNode: adminProcedure
|
|
311
|
+
.input(z.object({ nodeId: z.string().min(1), reason: z.string().max(200).optional() }))
|
|
315
312
|
.output(TlsStatusRowSchema)
|
|
316
313
|
.mutation(async ({ input }) => {
|
|
317
314
|
if (input.nodeId === HUB_NODE_ID) {
|
|
@@ -353,7 +350,7 @@ function createCertificatesRouter(deps) {
|
|
|
353
350
|
// the authenticated install call, so the node re-pins instead of refusing for ever.
|
|
354
351
|
const caRotation = prepared.pinnedCaFingerprintSha256 !== null &&
|
|
355
352
|
prepared.pinnedCaFingerprintSha256 !==
|
|
356
|
-
new
|
|
353
|
+
new X509Certificate(issued.caCertPem).fingerprint256;
|
|
357
354
|
deps.log.info('Agent HTTPS leaf issued', {
|
|
358
355
|
tags,
|
|
359
356
|
meta: { accepted: issued.accepted, rejected: issued.rejected },
|
|
@@ -372,7 +369,7 @@ function createCertificatesRouter(deps) {
|
|
|
372
369
|
catch (error) {
|
|
373
370
|
const message = error instanceof Error ? error.message : String(error);
|
|
374
371
|
deps.log.error('Agent HTTPS renewal failed', { tags, meta: { error: message } });
|
|
375
|
-
throw new
|
|
372
|
+
throw new TRPCError({
|
|
376
373
|
code: 'INTERNAL_SERVER_ERROR',
|
|
377
374
|
message: `HTTPS renewal on ${input.nodeId} failed: ${message}. The node keeps serving what it had.`,
|
|
378
375
|
});
|
|
@@ -380,17 +377,17 @@ function createCertificatesRouter(deps) {
|
|
|
380
377
|
});
|
|
381
378
|
}),
|
|
382
379
|
/** The mesh CA, the revocation list, and every node's recorded leaves. */
|
|
383
|
-
getMeshCertificates:
|
|
384
|
-
.input(
|
|
380
|
+
getMeshCertificates: adminProcedure
|
|
381
|
+
.input(z.void())
|
|
385
382
|
.output(MeshInventorySchema)
|
|
386
383
|
.query(async () => {
|
|
387
|
-
const state =
|
|
384
|
+
const state = getProcessMeshCaState();
|
|
388
385
|
const ca = state.state === 'ready'
|
|
389
386
|
? {
|
|
390
387
|
state: 'ready',
|
|
391
|
-
subject: new
|
|
392
|
-
fingerprintSha256:
|
|
393
|
-
validTo: new
|
|
388
|
+
subject: new X509Certificate(state.ca.certPem).subject,
|
|
389
|
+
fingerprintSha256: caFingerprint(state.ca.certPem),
|
|
390
|
+
validTo: new X509Certificate(state.ca.certPem).validTo,
|
|
394
391
|
}
|
|
395
392
|
: { state: 'unavailable', reason: state.reason };
|
|
396
393
|
const registry = meshRegistry();
|
|
@@ -440,20 +437,20 @@ function createCertificatesRouter(deps) {
|
|
|
440
437
|
* after the hub SEES it connected with the new leaf is the old serial revoked. A failed
|
|
441
438
|
* swap keeps the old leaf valid and says why.
|
|
442
439
|
*/
|
|
443
|
-
renewMeshOnNode:
|
|
444
|
-
.input(
|
|
440
|
+
renewMeshOnNode: adminProcedure
|
|
441
|
+
.input(z.object({ nodeId: z.string().min(1) }))
|
|
445
442
|
.output(MeshRenewResultSchema)
|
|
446
443
|
.mutation(async ({ input }) => {
|
|
447
444
|
const client = requireAgent(input.nodeId);
|
|
448
445
|
const ca = meshCa();
|
|
449
446
|
const registry = meshRegistry();
|
|
450
447
|
if (ca === null || registry === null) {
|
|
451
|
-
throw new
|
|
448
|
+
throw new TRPCError({
|
|
452
449
|
code: 'PRECONDITION_FAILED',
|
|
453
450
|
message: 'The mesh CA or the certificate registry is not available yet.',
|
|
454
451
|
});
|
|
455
452
|
}
|
|
456
|
-
const result = await exclusive(input.nodeId, 'mesh', () =>
|
|
453
|
+
const result = await exclusive(input.nodeId, 'mesh', () => renewMeshCertificate({
|
|
457
454
|
nodeId: input.nodeId,
|
|
458
455
|
client,
|
|
459
456
|
ca,
|
|
@@ -476,24 +473,24 @@ function createCertificatesRouter(deps) {
|
|
|
476
473
|
* connect: it must be paired again. It is NOT forgotten - that stays
|
|
477
474
|
* `clusterNodes.forgetNode`.
|
|
478
475
|
*/
|
|
479
|
-
revokeMeshOnNode:
|
|
480
|
-
.input(
|
|
476
|
+
revokeMeshOnNode: adminProcedure
|
|
477
|
+
.input(z.object({ nodeId: z.string().min(1) }))
|
|
481
478
|
.output(MeshRevokeResultSchema)
|
|
482
479
|
.mutation(async ({ input }) => {
|
|
483
480
|
if (input.nodeId === HUB_NODE_ID) {
|
|
484
|
-
throw new
|
|
481
|
+
throw new TRPCError({
|
|
485
482
|
code: 'PRECONDITION_FAILED',
|
|
486
483
|
message: "The hub's own mesh leaf is re-issued at every boot; there is nothing to revoke.",
|
|
487
484
|
});
|
|
488
485
|
}
|
|
489
486
|
const registry = meshRegistry();
|
|
490
487
|
if (registry === null) {
|
|
491
|
-
throw new
|
|
488
|
+
throw new TRPCError({
|
|
492
489
|
code: 'PRECONDITION_FAILED',
|
|
493
490
|
message: 'The certificate registry is not available yet.',
|
|
494
491
|
});
|
|
495
492
|
}
|
|
496
|
-
const result = await
|
|
493
|
+
const result = await revokeMeshCertificate({
|
|
497
494
|
nodeId: input.nodeId,
|
|
498
495
|
registry,
|
|
499
496
|
crl: crl(),
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createClusterNodesRouter = createClusterNodesRouter;
|
|
4
1
|
/**
|
|
5
2
|
* Cluster-nodes router — fixed core API (not a capability).
|
|
6
3
|
*
|
|
@@ -13,9 +10,9 @@ exports.createClusterNodesRouter = createClusterNodesRouter;
|
|
|
13
10
|
* `computeTopology` and is intentionally NOT exposed here — the admin UI reads
|
|
14
11
|
* offline rows off the pushed `cluster.topology-snapshot`, never by polling.
|
|
15
12
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const ForgetNodeInputSchema =
|
|
13
|
+
import { z } from 'zod';
|
|
14
|
+
import { trpcRouter, adminProcedure } from '../trpc/trpc.middleware.js';
|
|
15
|
+
const ForgetNodeInputSchema = z.object({ nodeId: z.string().min(1) });
|
|
19
16
|
/**
|
|
20
17
|
* What forgetting a node ACTUALLY removed.
|
|
21
18
|
*
|
|
@@ -26,21 +23,21 @@ const ForgetNodeInputSchema = zod_1.z.object({ nodeId: zod_1.z.string().min(1) }
|
|
|
26
23
|
* sources of its topology row was being purged. An action that changed nothing
|
|
27
24
|
* must not answer `success`.
|
|
28
25
|
*/
|
|
29
|
-
const ForgetNodeResultSchema =
|
|
26
|
+
const ForgetNodeResultSchema = z.object({
|
|
30
27
|
/** At least one source of the node's topology row was purged. */
|
|
31
|
-
forgotten:
|
|
28
|
+
forgotten: z.boolean(),
|
|
32
29
|
/** The durable history row existed and is gone. */
|
|
33
|
-
historyRemoved:
|
|
30
|
+
historyRemoved: z.boolean(),
|
|
34
31
|
/** The Moleculer node-catalog entry existed and is gone. */
|
|
35
|
-
registryRemoved:
|
|
32
|
+
registryRemoved: z.boolean(),
|
|
36
33
|
/** Read back AFTER the attempt: the node is still in the cluster registry. */
|
|
37
|
-
stillListed:
|
|
34
|
+
stillListed: z.boolean(),
|
|
38
35
|
/** One operator-grade sentence naming what happened. */
|
|
39
|
-
message:
|
|
36
|
+
message: z.string(),
|
|
40
37
|
});
|
|
41
|
-
const RepairNodeAddonsInputSchema =
|
|
42
|
-
function createClusterNodesRouter(agentRegistry) {
|
|
43
|
-
return
|
|
38
|
+
const RepairNodeAddonsInputSchema = z.object({ nodeId: z.string().min(1) });
|
|
39
|
+
export function createClusterNodesRouter(agentRegistry) {
|
|
40
|
+
return trpcRouter({
|
|
44
41
|
/**
|
|
45
42
|
* The explicit half of the addon back-fill's trigger set.
|
|
46
43
|
*
|
|
@@ -55,9 +52,9 @@ function createClusterNodesRouter(agentRegistry) {
|
|
|
55
52
|
* roster the node itself declared, with the same circuit breaker. Nothing
|
|
56
53
|
* can be repaired by hand that the node could not have converged to.
|
|
57
54
|
*/
|
|
58
|
-
repairNodeAddons:
|
|
55
|
+
repairNodeAddons: adminProcedure
|
|
59
56
|
.input(RepairNodeAddonsInputSchema)
|
|
60
|
-
.output(
|
|
57
|
+
.output(z.object({ success: z.boolean() }))
|
|
61
58
|
.mutation(async ({ input }) => {
|
|
62
59
|
await agentRegistry.repairNodeAddons(input.nodeId);
|
|
63
60
|
return { success: true };
|
|
@@ -65,7 +62,7 @@ function createClusterNodesRouter(agentRegistry) {
|
|
|
65
62
|
// Purge one node's persisted offline history. Pairs with
|
|
66
63
|
// `pipelineOrchestrator.removeAgentSettings` on the frontend to fully
|
|
67
64
|
// forget an offline node (history row + per-node pipeline assignments).
|
|
68
|
-
forgetNode:
|
|
65
|
+
forgetNode: adminProcedure
|
|
69
66
|
.input(ForgetNodeInputSchema)
|
|
70
67
|
.output(ForgetNodeResultSchema)
|
|
71
68
|
// The service's own result IS the answer — this procedure adds nothing
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.collectionPreferenceKey = collectionPreferenceKey;
|
|
4
|
-
exports.persistCollectionDisabled = persistCollectionDisabled;
|
|
5
1
|
/** ConfigService key holding the persisted disabled-set for a collection cap. */
|
|
6
|
-
function collectionPreferenceKey(capability) {
|
|
2
|
+
export function collectionPreferenceKey(capability) {
|
|
7
3
|
return `capabilities.collection.${capability}`;
|
|
8
4
|
}
|
|
9
5
|
/**
|
|
10
6
|
* Persist the disabled-addonId set for a collection capability: the set the
|
|
11
7
|
* registry WILL hold once the write has landed and is applied.
|
|
12
8
|
*/
|
|
13
|
-
async function persistCollectionDisabled(configService, capability, disabled) {
|
|
9
|
+
export async function persistCollectionDisabled(configService, capability, disabled) {
|
|
14
10
|
await configService.set(collectionPreferenceKey(capability), JSON.stringify({ disabled: [...disabled] }));
|
|
15
11
|
}
|