@camstack/system 1.2.378 → 1.2.379
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-runner.js +1 -1
- package/dist/addon-runner.mjs +1 -1
- package/dist/addon-utils.js +1 -1
- package/dist/addon-utils.mjs +1 -1
- package/dist/auth/auth-manager.d.ts +10 -2
- package/dist/auth/jwt-signing-key.d.ts +42 -0
- package/dist/auth/user-manager.d.ts +19 -19
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/calendars/calendars.addon.js +1 -1
- package/dist/builtins/calendars/calendars.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composer.addon.js +2 -2
- package/dist/builtins/composer/composer.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
- package/dist/builtins/device-manager/device-manager.addon.js +2 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/keychain/index.js +34 -4
- package/dist/builtins/keychain/index.mjs +34 -4
- package/dist/builtins/keychain/keychain-service.d.ts +14 -2
- package/dist/builtins/keychain/system-held-secrets.d.ts +11 -0
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/admin-account-boot.d.ts +1 -1
- package/dist/builtins/local-auth/auth-config-reader.d.ts +9 -7
- package/dist/builtins/local-auth/jwt-signing-key-keeper.d.ts +30 -0
- package/dist/builtins/local-auth/local-auth.addon.d.ts +2 -6
- package/dist/builtins/local-auth/local-auth.addon.js +127 -221
- package/dist/builtins/local-auth/local-auth.addon.mjs +126 -220
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/cluster-secret-keeper.d.ts +36 -0
- package/dist/builtins/system-config/system-config.addon.d.ts +13 -0
- package/dist/builtins/system-config/system-config.addon.js +244 -53
- package/dist/builtins/system-config/system-config.addon.mjs +244 -54
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{child-cap-dispatch-C9lmrWFT.js → child-cap-dispatch-CZeRUgYF.js} +1 -1
- package/dist/{child-cap-dispatch-DpHovtQd.mjs → child-cap-dispatch-CpjohToR.mjs} +1 -1
- package/dist/cluster-secret-BDue-uJj.mjs +83 -0
- package/dist/cluster-secret-Dg0OOOSr.js +148 -0
- package/dist/{composition-sources-CxhGdtdh.js → composition-sources-CHvLMrFI.js} +1 -1
- package/dist/{composition-sources-BLBd338N.mjs → composition-sources-DT_uJwI3.mjs} +1 -1
- package/dist/{dist-Bvl5JgNt.mjs → dist-B9oFw2jh.mjs} +58 -2
- package/dist/{dist-KxsOMQik.js → dist-Cc6KOdY-.js} +69 -1
- package/dist/{event-loop-stall-monitor-D8rqSWfW.mjs → event-loop-stall-monitor-anUH2IE1.mjs} +14 -14
- package/dist/{event-loop-stall-monitor-CbSmCWkp.js → event-loop-stall-monitor-mk6WGxir.js} +14 -14
- package/dist/index.d.ts +4 -2
- package/dist/index.js +192 -62
- package/dist/index.mjs +171 -55
- package/dist/jwt-signing-key-C2rE3wH6.js +174 -0
- package/dist/jwt-signing-key-Qhde8r82.mjs +127 -0
- package/dist/kernel/config-manager.d.ts +1 -1
- package/dist/kernel/config-schema.d.ts +4 -11
- package/dist/kernel/index.d.ts +2 -1
- package/dist/kernel/moleculer/broker-factory.d.ts +0 -2
- package/dist/kernel/moleculer/cluster-secret.d.ts +31 -0
- package/dist/kernel/moleculer/hub-service.d.ts +7 -4
- package/dist/kernel/moleculer/node-registry.d.ts +1 -1
- package/dist/kernel/system-settings-policy.d.ts +7 -9
- package/dist/{retired-settings-keys-BAcfvFtm.mjs → retired-settings-keys-DlZ3VV6x.mjs} +1 -1
- package/dist/{retired-settings-keys-gTsZfpmP.js → retired-settings-keys-YOOKLA3v.js} +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { br as buildSecretRef } from "./dist-B9oFw2jh.mjs";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
//#region src/auth/jwt-signing-key.ts
|
|
4
|
+
/**
|
|
5
|
+
* The hub's session signing key — the HMAC key every JWT this hub mints
|
|
6
|
+
* (sessions, SSO bridge, TOTP challenges, service tokens) is signed with.
|
|
7
|
+
*
|
|
8
|
+
* It lives in the keychain, owner `system` ({@link JWT_SIGNING_KEY_REF}), and
|
|
9
|
+
* nowhere else: no environment variable, no `config.yaml` key (D797). The
|
|
10
|
+
* hub's `local-auth` builtin loads it after the keychain booted and registers
|
|
11
|
+
* it here; every `AuthManager` in the process asks this slot on every mint and
|
|
12
|
+
* every verify, so a key that arrives (or changes) after construction is used
|
|
13
|
+
* from the next call on.
|
|
14
|
+
*
|
|
15
|
+
* ## A locked keychain fails closed, visibly
|
|
16
|
+
*
|
|
17
|
+
* While the key cannot be read, sessions can be neither minted nor verified:
|
|
18
|
+
* {@link JwtSigningKeySource.current} throws {@link JwtSigningKeyUnavailableError}
|
|
19
|
+
* (a login answers that error, a bearer check reads it as "not authenticated").
|
|
20
|
+
* There is deliberately NO fallback — not an env var, and not a fresh random
|
|
21
|
+
* key, which would invalidate every session and every app token without
|
|
22
|
+
* anyone having asked for it. A key is generated ONLY when the keychain
|
|
23
|
+
* answers that none is stored (`missing`), i.e. on a fresh hub.
|
|
24
|
+
*
|
|
25
|
+
* Process-global through a registered symbol, like the desktop platform key,
|
|
26
|
+
* so a second copy of this module (an addon bundle) sees the same key.
|
|
27
|
+
*/
|
|
28
|
+
/** The keychain owner of every system-held secret. */
|
|
29
|
+
var SYSTEM_SECRET_OWNER = "system";
|
|
30
|
+
/** `secret://system/global/jwtSigningKey`. */
|
|
31
|
+
var JWT_SIGNING_KEY_REF = buildSecretRef({
|
|
32
|
+
owner: SYSTEM_SECRET_OWNER,
|
|
33
|
+
scope: { kind: "global" },
|
|
34
|
+
fieldPath: "jwtSigningKey"
|
|
35
|
+
});
|
|
36
|
+
/** Sessions cannot be minted or verified: the signing key is not loaded. */
|
|
37
|
+
var JwtSigningKeyUnavailableError = class extends Error {
|
|
38
|
+
reason;
|
|
39
|
+
constructor(reason) {
|
|
40
|
+
super(`sessions are unavailable: the session signing key could not be read from the keychain (${reason}). Unlock the keychain (System → Secrets, or fix keychain.keyFile and restart).`);
|
|
41
|
+
this.reason = reason;
|
|
42
|
+
this.name = "JwtSigningKeyUnavailableError";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var SLOT = Symbol.for("camstack.auth.jwt-signing-key");
|
|
46
|
+
var NOT_LOADED = {
|
|
47
|
+
state: "unavailable",
|
|
48
|
+
reason: "not-loaded"
|
|
49
|
+
};
|
|
50
|
+
function isState(value) {
|
|
51
|
+
if (value === null || typeof value !== "object") return false;
|
|
52
|
+
const state = Reflect.get(value, "state");
|
|
53
|
+
if (state === "ready") return typeof Reflect.get(value, "key") === "string";
|
|
54
|
+
return state === "unavailable" && typeof Reflect.get(value, "reason") === "string";
|
|
55
|
+
}
|
|
56
|
+
/** Register the loaded (or unloadable) key. Called by the hub's `local-auth`. */
|
|
57
|
+
function setProcessJwtSigningKey(state) {
|
|
58
|
+
Reflect.set(globalThis, SLOT, state);
|
|
59
|
+
}
|
|
60
|
+
function getProcessJwtSigningKeyState() {
|
|
61
|
+
const value = Reflect.get(globalThis, SLOT);
|
|
62
|
+
return isState(value) ? value : NOT_LOADED;
|
|
63
|
+
}
|
|
64
|
+
/** The process slot — the default source of every `AuthManager`. */
|
|
65
|
+
var processJwtSigningKey = { current() {
|
|
66
|
+
const state = getProcessJwtSigningKeyState();
|
|
67
|
+
if (state.state === "ready") return state.key;
|
|
68
|
+
throw new JwtSigningKeyUnavailableError(state.reason);
|
|
69
|
+
} };
|
|
70
|
+
/** A fixed key — tests, and nothing else. */
|
|
71
|
+
function staticJwtSigningKey(key) {
|
|
72
|
+
return { current: () => key };
|
|
73
|
+
}
|
|
74
|
+
/** 32 random bytes, hex. */
|
|
75
|
+
function generateJwtSigningKey() {
|
|
76
|
+
return randomBytes(32).toString("hex");
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Read the key from the keychain; generate and store one ONLY when the
|
|
80
|
+
* keychain answers that none exists. Never throws.
|
|
81
|
+
*/
|
|
82
|
+
async function loadJwtSigningKey(deps) {
|
|
83
|
+
let entry;
|
|
84
|
+
try {
|
|
85
|
+
entry = (await deps.secrets.resolve([JWT_SIGNING_KEY_REF]))[JWT_SIGNING_KEY_REF];
|
|
86
|
+
} catch (err) {
|
|
87
|
+
return {
|
|
88
|
+
state: "unavailable",
|
|
89
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (entry === void 0) return {
|
|
93
|
+
state: "unavailable",
|
|
94
|
+
reason: "no-answer"
|
|
95
|
+
};
|
|
96
|
+
switch (entry.kind) {
|
|
97
|
+
case "value": return {
|
|
98
|
+
state: "ready",
|
|
99
|
+
key: entry.value
|
|
100
|
+
};
|
|
101
|
+
case "unavailable": return {
|
|
102
|
+
state: "unavailable",
|
|
103
|
+
reason: entry.reason
|
|
104
|
+
};
|
|
105
|
+
case "forbidden": return {
|
|
106
|
+
state: "unavailable",
|
|
107
|
+
reason: "forbidden"
|
|
108
|
+
};
|
|
109
|
+
case "missing": {
|
|
110
|
+
const key = (deps.generate ?? generateJwtSigningKey)();
|
|
111
|
+
try {
|
|
112
|
+
await deps.secrets.put(JWT_SIGNING_KEY_REF, key);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
return {
|
|
115
|
+
state: "unavailable",
|
|
116
|
+
reason: `could not store a new key: ${err instanceof Error ? err.message : String(err)}`
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
state: "ready",
|
|
121
|
+
key
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
export { loadJwtSigningKey as a, staticJwtSigningKey as c, getProcessJwtSigningKeyState as i, JwtSigningKeyUnavailableError as n, processJwtSigningKey as o, SYSTEM_SECRET_OWNER as r, setProcessJwtSigningKey as s, JWT_SIGNING_KEY_REF as t };
|
|
@@ -82,7 +82,7 @@ export declare class ConfigManager {
|
|
|
82
82
|
* implementation (sqlite, redis, in-memory, …) behaves identically here.
|
|
83
83
|
*
|
|
84
84
|
* D675: a bootstrap-owned section takes ONLY allow-listed keys from the
|
|
85
|
-
* store (a stored `auth.jwtSecret` never
|
|
85
|
+
* store (a stale stored `auth.jwtSecret` never answers), and with only the
|
|
86
86
|
* door wired this sync read THROWS — the settings view's `getSection` reads
|
|
87
87
|
* the door.
|
|
88
88
|
*/
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
/** Default data directory — used when CAMSTACK_DATA env var is not set */
|
|
3
3
|
export declare const DEFAULT_DATA_PATH = "camstack-data";
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
* only: it never overwrites an existing account's password (`ensureAdminExists`).
|
|
7
|
-
*/
|
|
8
|
-
export declare const DEFAULT_ADMIN_PASSWORD = "changeme";
|
|
4
|
+
/** `auth.*` keys a pre-D797 config.yaml may still carry; ignored, and warned about. */
|
|
5
|
+
export declare const RETIRED_AUTH_CONFIG_KEYS: readonly ["jwtSecret", "adminUsername", "adminPassword"];
|
|
9
6
|
/** Bootstrap config -- loaded from config.yaml ONLY at startup.
|
|
10
7
|
* All other settings live in SQL (system_settings table). */
|
|
11
8
|
export declare const bootstrapSchema: z.ZodObject<{
|
|
@@ -19,11 +16,7 @@ export declare const bootstrapSchema: z.ZodObject<{
|
|
|
19
16
|
dataPath: z.ZodDefault<z.ZodString>;
|
|
20
17
|
corsOrigins: z.ZodDefault<z.ZodPreprocess<z.ZodArray<z.ZodString>>>;
|
|
21
18
|
}, z.core.$strip>>;
|
|
22
|
-
auth: z.ZodDefault<z.ZodObject<{
|
|
23
|
-
jwtSecret: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
24
|
-
adminUsername: z.ZodDefault<z.ZodString>;
|
|
25
|
-
adminPassword: z.ZodDefault<z.ZodString>;
|
|
26
|
-
}, z.core.$strip>>;
|
|
19
|
+
auth: z.ZodDefault<z.ZodObject<{}, z.core.$strip>>;
|
|
27
20
|
hub: z.ZodDefault<z.ZodObject<{
|
|
28
21
|
url: z.ZodDefault<z.ZodString>;
|
|
29
22
|
token: z.ZodDefault<z.ZodString>;
|
|
@@ -38,8 +31,8 @@ export declare const bootstrapSchema: z.ZodObject<{
|
|
|
38
31
|
bootstrap: z.ZodDefault<z.ZodObject<{
|
|
39
32
|
settingsStoreAddon: z.ZodDefault<z.ZodString>;
|
|
40
33
|
installSource: z.ZodOptional<z.ZodEnum<{
|
|
41
|
-
npm: "npm";
|
|
42
34
|
local: "local";
|
|
35
|
+
npm: "npm";
|
|
43
36
|
symlink: "symlink";
|
|
44
37
|
}>>;
|
|
45
38
|
requiredAddons: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
package/dist/kernel/index.d.ts
CHANGED
|
@@ -52,7 +52,8 @@ export { HubNodeRegistry } from './moleculer/node-registry.js';
|
|
|
52
52
|
export type { CallFn, NodeRootPackageInfo, RegisteredAddonManifest, RegisteredNativeCap, RegisterNodeParams, RegisterNodeResult, NodeCapEntry, NodeNativeCapEntry, } from './moleculer/node-registry.js';
|
|
53
53
|
export { callRegisterNodeWithRetry, buildNodeManifest } from './moleculer/register-node-client.js';
|
|
54
54
|
export type { RegisterNodeBroker, CallRegisterNodeOptions, } from './moleculer/register-node-client.js';
|
|
55
|
-
export { hashClusterSecret, clusterSecretMatches, isClusterSecretMismatchError, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REJECTED_EXIT_CODE, } from './moleculer/cluster-secret.js';
|
|
55
|
+
export { hashClusterSecret, clusterSecretMatches, isClusterSecretMismatchError, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REJECTED_EXIT_CODE, CLUSTER_SECRET_REF, CLUSTER_SECRET_SETTINGS_KEY, CLUSTER_SECRET_UNAVAILABLE_TYPE, checkClusterSecret, getProcessClusterSecretGate, setProcessClusterSecretGate, } from './moleculer/cluster-secret.js';
|
|
56
|
+
export type { ClusterSecretGate, ClusterSecretVerdict } from './moleculer/cluster-secret.js';
|
|
56
57
|
export { serializeTypedArrays } from './moleculer/typed-array-serde.js';
|
|
57
58
|
export { createReadinessService, createReadinessServiceForRegistry, AGENT_READINESS_SERVICE_NAME, } from './moleculer/readiness-service.js';
|
|
58
59
|
export type { ReadinessServiceDeps } from './moleculer/readiness-service.js';
|
|
@@ -4,8 +4,6 @@ interface BrokerConfig {
|
|
|
4
4
|
readonly mode: 'hub' | 'agent' | 'test';
|
|
5
5
|
readonly hubAddress?: string;
|
|
6
6
|
readonly logLevel?: string;
|
|
7
|
-
/** Shared secret — used as Moleculer namespace to isolate the cluster on the network */
|
|
8
|
-
readonly secret?: string;
|
|
9
7
|
/**
|
|
10
8
|
* TCP port the hub listens on for cluster traffic.
|
|
11
9
|
* Agents need a deterministic port to connect via `urls: 'hub@host:port'`
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** The keychain ref of the hub's cluster secret: `secret://system/global/clusterSecret`. */
|
|
2
|
+
export declare const CLUSTER_SECRET_REF: string;
|
|
3
|
+
/** The `system-settings` key whose value is the cluster secret's keychain marker. */
|
|
4
|
+
export declare const CLUSTER_SECRET_SETTINGS_KEY = "cluster.clusterSecret";
|
|
1
5
|
/** Moleculer error `type` marking a registration rejected for a bad cluster secret. */
|
|
2
6
|
export declare const CLUSTER_SECRET_MISMATCH_TYPE = "CLUSTER_SECRET_MISMATCH";
|
|
3
7
|
/** Process exit code a runner uses when its registration is rejected for a bad secret. */
|
|
@@ -13,3 +17,30 @@ export declare function hashClusterSecret(secret: string): string;
|
|
|
13
17
|
export declare function clusterSecretMatches(expectedHash: string | undefined, presentedHash: string | undefined): boolean;
|
|
14
18
|
/** Recognises a cluster-secret rejection, including after a Moleculer RPC round-trip. */
|
|
15
19
|
export declare function isClusterSecretMismatchError(err: unknown): boolean;
|
|
20
|
+
/** Moleculer error `type` of a registration refused because the hub cannot read its own secret yet. */
|
|
21
|
+
export declare const CLUSTER_SECRET_UNAVAILABLE_TYPE = "CLUSTER_SECRET_UNAVAILABLE";
|
|
22
|
+
/**
|
|
23
|
+
* What the hub knows about its own cluster secret:
|
|
24
|
+
* - `off`: none is set — every node may register (the behaviour of a hub
|
|
25
|
+
* that never had a secret);
|
|
26
|
+
* - `on`: set — a node must present the matching hash;
|
|
27
|
+
* - `unknown`: one is set (the marker exists) but the keychain cannot answer
|
|
28
|
+
* yet — registrations are refused with a RETRYABLE error until it does.
|
|
29
|
+
* Fail closed: a locked keychain never opens the gate.
|
|
30
|
+
*/
|
|
31
|
+
export type ClusterSecretGate = {
|
|
32
|
+
readonly kind: 'off';
|
|
33
|
+
} | {
|
|
34
|
+
readonly kind: 'on';
|
|
35
|
+
readonly hash: string;
|
|
36
|
+
} | {
|
|
37
|
+
readonly kind: 'unknown';
|
|
38
|
+
readonly reason: string;
|
|
39
|
+
};
|
|
40
|
+
export type ClusterSecretVerdict = 'allow' | 'mismatch' | 'unavailable';
|
|
41
|
+
export declare function checkClusterSecret(gate: ClusterSecretGate, presentedHash: string | undefined): ClusterSecretVerdict;
|
|
42
|
+
/** Register the hub's gate. Called by `system-config`, which owns the field. */
|
|
43
|
+
export declare function setProcessClusterSecretGate(gate: ClusterSecretGate): void;
|
|
44
|
+
export declare function clearProcessClusterSecretGate(): void;
|
|
45
|
+
/** The hub's gate; `unknown` until `system-config` loaded it. */
|
|
46
|
+
export declare function getProcessClusterSecretGate(): ClusterSecretGate;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ServiceSchema } from 'moleculer';
|
|
2
2
|
import { LogTags } from '@camstack/types';
|
|
3
3
|
import { RegisterNodeParams } from './node-registry.js';
|
|
4
|
+
import { ClusterSecretGate } from './cluster-secret.js';
|
|
4
5
|
interface HubServiceDeps {
|
|
5
6
|
readonly getAddonConfig: (addonId: string) => unknown;
|
|
6
7
|
readonly getSettings: (scope: string, key?: string) => unknown;
|
|
@@ -24,11 +25,13 @@ interface HubServiceDeps {
|
|
|
24
25
|
/** Explicit deregistration (graceful shutdown). `$node.disconnected` covers crashes. */
|
|
25
26
|
readonly onUnregisterNode?: (nodeId: string) => void;
|
|
26
27
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* node
|
|
28
|
+
* The hub's cluster-secret gate, read on EVERY registration (the secret
|
|
29
|
+
* lives in the keychain and may be set or changed at runtime — D797):
|
|
30
|
+
* `off` lets every node in, `on` requires the matching `clusterSecretHash`,
|
|
31
|
+
* `unknown` (the keychain cannot answer yet) refuses with a retryable error.
|
|
32
|
+
* Absent → `off` (test brokers); the hub always passes the keychain's gate.
|
|
30
33
|
*/
|
|
31
|
-
readonly
|
|
34
|
+
readonly clusterSecretGate?: () => ClusterSecretGate;
|
|
32
35
|
}
|
|
33
36
|
declare function createHubService(deps: HubServiceDeps): ServiceSchema;
|
|
34
37
|
export { createHubService };
|
|
@@ -56,7 +56,7 @@ interface RegisterNodeParams {
|
|
|
56
56
|
*/
|
|
57
57
|
readonly nativeCaps?: readonly RegisteredNativeCap[];
|
|
58
58
|
/**
|
|
59
|
-
* SHA-256 hash of the node's
|
|
59
|
+
* SHA-256 hash of the node's cluster secret. Optional — absent
|
|
60
60
|
* when the node has no secret configured. Verified by the `registerNode`
|
|
61
61
|
* receiver; never carries the raw secret. (Cluster-secret registration gate.)
|
|
62
62
|
*/
|
|
@@ -2,16 +2,14 @@
|
|
|
2
2
|
* Which `system-settings` keys the STORE may answer, and which the kernel
|
|
3
3
|
* loads from the door into memory at boot — D675.
|
|
4
4
|
*
|
|
5
|
-
* ## The store never
|
|
5
|
+
* ## The store never answers for a bootstrap section
|
|
6
6
|
*
|
|
7
|
-
* `system-settings`
|
|
8
|
-
* `auth.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* in "highest priority" would let a stale DB copy replace the JWT secret —
|
|
14
|
-
* invalidating every token — and change the login password.
|
|
7
|
+
* `system-settings` may still hold `auth.jwtSecret`, `auth.adminUsername` and
|
|
8
|
+
* `auth.adminPassword` — written there by `local-auth` (and by older builds)
|
|
9
|
+
* next to `auth.tokenExpiry`. Since D797 none of the three is a setting at
|
|
10
|
+
* all (the signing key is the keychain's, the first-boot admin is fixed), and
|
|
11
|
+
* the stale rows must still never answer: merging them back in would let a
|
|
12
|
+
* stale DB copy stand in for a credential. The one-off ops script deletes them.
|
|
15
13
|
*
|
|
16
14
|
* So a bootstrap-owned section is answered from env + config.yaml ONLY, and
|
|
17
15
|
* the store may add exactly the keys in {@link STORE_READABLE_BOOTSTRAP_KEYS}:
|