@naylence/runtime 0.3.12 → 0.3.13

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 (94) hide show
  1. package/dist/browser/index.cjs +1479 -926
  2. package/dist/browser/index.mjs +1472 -927
  3. package/dist/cjs/naylence/fame/connector/broadcast-channel-connector.browser.js +1 -1
  4. package/dist/cjs/naylence/fame/factory-manifest.js +6 -0
  5. package/dist/cjs/naylence/fame/grants/grant-materializer.js +59 -0
  6. package/dist/cjs/naylence/fame/node/admission/admission-profile-factory.js +4 -2
  7. package/dist/cjs/naylence/fame/node/admission/direct-admission-client-factory.js +3 -1
  8. package/dist/cjs/naylence/fame/node/admission/direct-admission-client.js +12 -9
  9. package/dist/cjs/naylence/fame/node/default-node-identity-policy-factory.js +21 -0
  10. package/dist/cjs/naylence/fame/node/default-node-identity-policy.js +60 -0
  11. package/dist/cjs/naylence/fame/node/factory-commons.js +31 -7
  12. package/dist/cjs/naylence/fame/node/index.js +11 -1
  13. package/dist/cjs/naylence/fame/node/node-config.js +4 -0
  14. package/dist/cjs/naylence/fame/node/node-identity-policy-factory.js +22 -0
  15. package/dist/cjs/naylence/fame/node/node-identity-policy-profile-factory.js +67 -0
  16. package/dist/cjs/naylence/fame/node/node-identity-policy.js +2 -0
  17. package/dist/cjs/naylence/fame/node/node.js +45 -9
  18. package/dist/cjs/naylence/fame/node/root-session-manager.js +1 -11
  19. package/dist/cjs/naylence/fame/node/rpc-client-manager.js +10 -3
  20. package/dist/cjs/naylence/fame/node/token-subject-node-identity-policy-factory.js +55 -0
  21. package/dist/cjs/naylence/fame/node/token-subject-node-identity-policy.js +84 -0
  22. package/dist/cjs/naylence/fame/node/upstream-session-manager.js +87 -9
  23. package/dist/cjs/naylence/fame/security/auth/auth-identity.js +2 -0
  24. package/dist/cjs/naylence/fame/security/auth/materializable-token-provider.js +9 -0
  25. package/dist/cjs/naylence/fame/security/auth/oauth2-pkce-token-provider.js +9 -0
  26. package/dist/cjs/naylence/fame/security/auth/static-token-provider.js +44 -0
  27. package/dist/cjs/naylence/fame/security/auth/token-provider.js +6 -0
  28. package/dist/cjs/naylence/fame/security/default-security-manager.js +4 -2
  29. package/dist/cjs/naylence/fame/security/index.js +1 -0
  30. package/dist/cjs/naylence/fame/security/keys/default-key-manager.js +1 -1
  31. package/dist/cjs/naylence/fame/util/task-spawner.js +8 -0
  32. package/dist/cjs/version.js +2 -2
  33. package/dist/esm/naylence/fame/connector/broadcast-channel-connector.browser.js +1 -1
  34. package/dist/esm/naylence/fame/factory-manifest.js +6 -0
  35. package/dist/esm/naylence/fame/grants/grant-materializer.js +55 -0
  36. package/dist/esm/naylence/fame/node/admission/admission-profile-factory.js +4 -2
  37. package/dist/esm/naylence/fame/node/admission/direct-admission-client-factory.js +3 -1
  38. package/dist/esm/naylence/fame/node/admission/direct-admission-client.js +13 -10
  39. package/dist/esm/naylence/fame/node/default-node-identity-policy-factory.js +17 -0
  40. package/dist/esm/naylence/fame/node/default-node-identity-policy.js +56 -0
  41. package/dist/esm/naylence/fame/node/factory-commons.js +31 -7
  42. package/dist/esm/naylence/fame/node/index.js +7 -0
  43. package/dist/esm/naylence/fame/node/node-config.js +4 -0
  44. package/dist/esm/naylence/fame/node/node-identity-policy-factory.js +18 -0
  45. package/dist/esm/naylence/fame/node/node-identity-policy-profile-factory.js +63 -0
  46. package/dist/esm/naylence/fame/node/node-identity-policy.js +1 -0
  47. package/dist/esm/naylence/fame/node/node.js +45 -9
  48. package/dist/esm/naylence/fame/node/root-session-manager.js +1 -11
  49. package/dist/esm/naylence/fame/node/rpc-client-manager.js +10 -3
  50. package/dist/esm/naylence/fame/node/token-subject-node-identity-policy-factory.js +18 -0
  51. package/dist/esm/naylence/fame/node/token-subject-node-identity-policy.js +80 -0
  52. package/dist/esm/naylence/fame/node/upstream-session-manager.js +87 -9
  53. package/dist/esm/naylence/fame/security/auth/auth-identity.js +1 -0
  54. package/dist/esm/naylence/fame/security/auth/materializable-token-provider.js +6 -0
  55. package/dist/esm/naylence/fame/security/auth/oauth2-pkce-token-provider.js +9 -0
  56. package/dist/esm/naylence/fame/security/auth/static-token-provider.js +44 -0
  57. package/dist/esm/naylence/fame/security/auth/token-provider.js +5 -0
  58. package/dist/esm/naylence/fame/security/default-security-manager.js +4 -2
  59. package/dist/esm/naylence/fame/security/index.js +1 -0
  60. package/dist/esm/naylence/fame/security/keys/default-key-manager.js +1 -1
  61. package/dist/esm/naylence/fame/util/task-spawner.js +8 -0
  62. package/dist/esm/version.js +2 -2
  63. package/dist/node/index.cjs +1432 -879
  64. package/dist/node/index.mjs +1425 -880
  65. package/dist/node/node.cjs +1560 -1007
  66. package/dist/node/node.mjs +1553 -1008
  67. package/dist/types/naylence/fame/factory-manifest.d.ts +1 -1
  68. package/dist/types/naylence/fame/grants/grant-materializer.d.ts +4 -0
  69. package/dist/types/naylence/fame/node/admission/admission-profile-factory.d.ts +1 -1
  70. package/dist/types/naylence/fame/node/admission/direct-admission-client-factory.d.ts +1 -1
  71. package/dist/types/naylence/fame/node/admission/direct-admission-client.d.ts +3 -0
  72. package/dist/types/naylence/fame/node/default-node-identity-policy-factory.d.ts +15 -0
  73. package/dist/types/naylence/fame/node/default-node-identity-policy.d.ts +5 -0
  74. package/dist/types/naylence/fame/node/factory-commons.d.ts +2 -0
  75. package/dist/types/naylence/fame/node/index.d.ts +7 -0
  76. package/dist/types/naylence/fame/node/node-config.d.ts +2 -0
  77. package/dist/types/naylence/fame/node/node-identity-policy-factory.d.ts +12 -0
  78. package/dist/types/naylence/fame/node/node-identity-policy-profile-factory.d.ts +15 -0
  79. package/dist/types/naylence/fame/node/node-identity-policy.d.ts +26 -0
  80. package/dist/types/naylence/fame/node/node-like.d.ts +3 -1
  81. package/dist/types/naylence/fame/node/node.d.ts +4 -1
  82. package/dist/types/naylence/fame/node/root-session-manager.d.ts +0 -1
  83. package/dist/types/naylence/fame/node/rpc-client-manager.d.ts +2 -0
  84. package/dist/types/naylence/fame/node/token-subject-node-identity-policy-factory.d.ts +14 -0
  85. package/dist/types/naylence/fame/node/token-subject-node-identity-policy.d.ts +5 -0
  86. package/dist/types/naylence/fame/node/upstream-session-manager.d.ts +4 -0
  87. package/dist/types/naylence/fame/security/auth/auth-identity.d.ts +6 -0
  88. package/dist/types/naylence/fame/security/auth/materializable-token-provider.d.ts +12 -0
  89. package/dist/types/naylence/fame/security/auth/oauth2-pkce-token-provider.d.ts +4 -2
  90. package/dist/types/naylence/fame/security/auth/static-token-provider.d.ts +4 -2
  91. package/dist/types/naylence/fame/security/auth/token-provider.d.ts +5 -0
  92. package/dist/types/naylence/fame/security/index.d.ts +1 -0
  93. package/dist/types/version.d.ts +1 -1
  94. package/package.json +1 -1
@@ -242,7 +242,7 @@ class BroadcastChannelConnector extends base_async_connector_js_1.BaseAsyncConne
242
242
  // Track page lifecycle events to detect browser unload/discard
243
243
  if (typeof window !== 'undefined') {
244
244
  const lifecycleLogger = (event) => {
245
- logger.info('broadcast_channel_page_lifecycle', {
245
+ logger.debug('broadcast_channel_page_lifecycle', {
246
246
  channel: this.channelName,
247
247
  connector_id: this.connectorId,
248
248
  event_type: event.type,
@@ -57,7 +57,10 @@ exports.MODULES = [
57
57
  "./node/admission/direct-admission-client-factory.js",
58
58
  "./node/admission/noop-admission-client-factory.js",
59
59
  "./node/admission/welcome-service-client-factory.js",
60
+ "./node/default-node-identity-policy-factory.js",
60
61
  "./node/node-factory.js",
62
+ "./node/node-identity-policy-profile-factory.js",
63
+ "./node/token-subject-node-identity-policy-factory.js",
61
64
  "./placement/static-node-placement-strategy-factory.js",
62
65
  "./security/auth/bearer-token-header-auth-injection-strategy-factory.js",
63
66
  "./security/auth/default-authorizer-factory.js",
@@ -132,7 +135,10 @@ exports.MODULE_LOADERS = {
132
135
  "./node/admission/direct-admission-client-factory.js": () => Promise.resolve().then(() => __importStar(require("./node/admission/direct-admission-client-factory.js"))),
133
136
  "./node/admission/noop-admission-client-factory.js": () => Promise.resolve().then(() => __importStar(require("./node/admission/noop-admission-client-factory.js"))),
134
137
  "./node/admission/welcome-service-client-factory.js": () => Promise.resolve().then(() => __importStar(require("./node/admission/welcome-service-client-factory.js"))),
138
+ "./node/default-node-identity-policy-factory.js": () => Promise.resolve().then(() => __importStar(require("./node/default-node-identity-policy-factory.js"))),
135
139
  "./node/node-factory.js": () => Promise.resolve().then(() => __importStar(require("./node/node-factory.js"))),
140
+ "./node/node-identity-policy-profile-factory.js": () => Promise.resolve().then(() => __importStar(require("./node/node-identity-policy-profile-factory.js"))),
141
+ "./node/token-subject-node-identity-policy-factory.js": () => Promise.resolve().then(() => __importStar(require("./node/token-subject-node-identity-policy-factory.js"))),
136
142
  "./placement/static-node-placement-strategy-factory.js": () => Promise.resolve().then(() => __importStar(require("./placement/static-node-placement-strategy-factory.js"))),
137
143
  "./security/auth/bearer-token-header-auth-injection-strategy-factory.js": () => Promise.resolve().then(() => __importStar(require("./security/auth/bearer-token-header-auth-injection-strategy-factory.js"))),
138
144
  "./security/auth/default-authorizer-factory.js": () => Promise.resolve().then(() => __importStar(require("./security/auth/default-authorizer-factory.js"))),
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GrantMaterializer = void 0;
4
+ const token_provider_factory_js_1 = require("../security/auth/token-provider-factory.js");
5
+ const materializable_token_provider_js_1 = require("../security/auth/materializable-token-provider.js");
6
+ const logging_js_1 = require("../util/logging.js");
7
+ const logger = (0, logging_js_1.getLogger)('naylence.fame.grants.grant_materializer');
8
+ class GrantMaterializer {
9
+ static async materialize(grant) {
10
+ const candidate = grant;
11
+ const auth = candidate.auth;
12
+ if (!auth) {
13
+ return grant;
14
+ }
15
+ const tokenProviderConfig = (auth.tokenProvider ??
16
+ auth.token_provider);
17
+ if (!tokenProviderConfig || typeof tokenProviderConfig.type !== 'string') {
18
+ return grant;
19
+ }
20
+ try {
21
+ const provider = await token_provider_factory_js_1.TokenProviderFactory.createTokenProvider(tokenProviderConfig);
22
+ if ((0, materializable_token_provider_js_1.isMaterializableTokenProvider)(provider)) {
23
+ const materializedConfig = await provider.materialize();
24
+ if (materializedConfig) {
25
+ logger.debug('grant_materialized', {
26
+ grantType: candidate.type,
27
+ providerType: tokenProviderConfig.type,
28
+ });
29
+ const newAuth = { ...auth };
30
+ if ('tokenProvider' in newAuth) {
31
+ newAuth.tokenProvider = materializedConfig;
32
+ }
33
+ if ('token_provider' in newAuth) {
34
+ newAuth.token_provider = materializedConfig;
35
+ }
36
+ return {
37
+ ...grant,
38
+ auth: newAuth,
39
+ };
40
+ }
41
+ }
42
+ }
43
+ catch (error) {
44
+ if (error &&
45
+ error.name === 'OAuth2PkceRedirectInitiatedError') {
46
+ logger.info('grant_materialization_redirecting', {
47
+ grantType: candidate.type,
48
+ });
49
+ throw error;
50
+ }
51
+ logger.warning('grant_materialization_failed', {
52
+ error: error instanceof Error ? error.message : String(error),
53
+ grantType: candidate.type,
54
+ });
55
+ }
56
+ return grant;
57
+ }
58
+ }
59
+ exports.GrantMaterializer = GrantMaterializer;
@@ -237,11 +237,13 @@ class AdmissionProfileFactory extends admission_client_factory_js_1.AdmissionCli
237
237
  super(...arguments);
238
238
  this.type = 'AdmissionProfile';
239
239
  }
240
- async create(config) {
240
+ async create(config, ...factoryArgs) {
241
241
  const normalized = normalizeConfig(config);
242
242
  const profileConfig = resolveProfileConfig(normalized.profile);
243
243
  logger.debug('enabling_admission_profile', { profile: normalized.profile });
244
- return admission_client_factory_js_1.AdmissionClientFactory.createAdmissionClient(profileConfig);
244
+ return admission_client_factory_js_1.AdmissionClientFactory.createAdmissionClient(profileConfig, {
245
+ factoryArgs,
246
+ });
245
247
  }
246
248
  }
247
249
  exports.AdmissionProfileFactory = AdmissionProfileFactory;
@@ -13,7 +13,7 @@ class DirectAdmissionClientFactory extends admission_client_factory_js_1.Admissi
13
13
  super(...arguments);
14
14
  this.type = 'DirectAdmissionClient';
15
15
  }
16
- async create(config) {
16
+ async create(config, ...factoryArgs) {
17
17
  if (!config) {
18
18
  throw new Error('DirectAdmissionClient configuration is required');
19
19
  }
@@ -24,9 +24,11 @@ class DirectAdmissionClientFactory extends admission_client_factory_js_1.Admissi
24
24
  });
25
25
  return JSON.parse(JSON.stringify(evaluated));
26
26
  });
27
+ const identityPolicy = factoryArgs.find((arg) => Boolean(arg && typeof arg === 'object' && 'identityPolicy' in arg))?.identityPolicy;
27
28
  return new direct_admission_client_js_1.DirectAdmissionClient({
28
29
  connectionGrants: evaluatedGrants,
29
30
  ttlSec: normalized.ttlSec ?? null,
31
+ nodeIdentityPolicy: identityPolicy,
30
32
  });
31
33
  }
32
34
  }
@@ -5,6 +5,7 @@ const core_1 = require("@naylence/core");
5
5
  const ttl_constants_js_1 = require("../../constants/ttl-constants.js");
6
6
  const logging_js_1 = require("../../util/logging.js");
7
7
  const ttl_validation_js_1 = require("../../util/ttl-validation.js");
8
+ const grant_materializer_js_1 = require("../../grants/grant-materializer.js");
8
9
  const logger = (0, logging_js_1.getLogger)('naylence.fame.node.admission.direct_admission_client');
9
10
  class DirectAdmissionClient {
10
11
  constructor(options) {
@@ -28,6 +29,7 @@ class DirectAdmissionClient {
28
29
  else {
29
30
  this.ttlSec = ttlCandidate;
30
31
  }
32
+ this.nodeIdentityPolicy = options.nodeIdentityPolicy;
31
33
  }
32
34
  async hello(systemId, instanceId, requestedLogicals) {
33
35
  logger.debug('direct_admission_hello_start', {
@@ -35,26 +37,27 @@ class DirectAdmissionClient {
35
37
  instanceId,
36
38
  requestedLogicals,
37
39
  });
38
- const effectiveSystemId = systemId && systemId.trim().length > 0
39
- ? systemId
40
- : await (0, core_1.generateIdAsync)({ mode: 'fingerprint' }).catch(async () => {
41
- logger.debug('direct_admission_fingerprint_generation_failed', {
42
- reason: 'falling back to random id',
43
- });
44
- return (0, core_1.generateIdAsync)({ mode: 'random' });
45
- });
40
+ const initialSystemId = systemId;
46
41
  const acceptedLogicals = requestedLogicals && requestedLogicals.length > 0
47
42
  ? [...requestedLogicals]
48
43
  : ['*'];
49
44
  const now = Date.now();
50
45
  const ttlSeconds = this.resolveTtlSeconds();
51
46
  const expiresAt = new Date(now + ttlSeconds * 1000);
47
+ const materializedGrants = await Promise.all(this.connectionGrants.map((grant) => grant_materializer_js_1.GrantMaterializer.materialize(grant)));
48
+ const effectiveSystemId = this.nodeIdentityPolicy
49
+ ? await this.nodeIdentityPolicy.resolveAdmissionNodeId({
50
+ currentNodeId: initialSystemId,
51
+ identities: [],
52
+ grants: materializedGrants,
53
+ })
54
+ : initialSystemId;
52
55
  const welcomeFrame = {
53
56
  type: 'NodeWelcome',
54
57
  systemId: effectiveSystemId,
55
58
  instanceId,
56
59
  acceptedLogicals,
57
- connectionGrants: this.connectionGrants.map((grant) => cloneGrant(grant)),
60
+ connectionGrants: materializedGrants.map((grant) => cloneGrant(grant)),
58
61
  expiresAt: expiresAt.toISOString(),
59
62
  };
60
63
  const envelope = (0, core_1.createFameEnvelope)({
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultNodeIdentityPolicyFactory = exports.FACTORY_META = void 0;
4
+ const default_node_identity_policy_js_1 = require("./default-node-identity-policy.js");
5
+ const node_identity_policy_factory_js_1 = require("./node-identity-policy-factory.js");
6
+ exports.FACTORY_META = {
7
+ base: node_identity_policy_factory_js_1.NODE_IDENTITY_POLICY_FACTORY_BASE_TYPE,
8
+ key: 'DefaultNodeIdentityPolicy',
9
+ };
10
+ class DefaultNodeIdentityPolicyFactory extends node_identity_policy_factory_js_1.NodeIdentityPolicyFactory {
11
+ constructor() {
12
+ super(...arguments);
13
+ this.type = 'DefaultNodeIdentityPolicy';
14
+ this.isDefault = true;
15
+ }
16
+ async create(_config) {
17
+ return new default_node_identity_policy_js_1.DefaultNodeIdentityPolicy();
18
+ }
19
+ }
20
+ exports.DefaultNodeIdentityPolicyFactory = DefaultNodeIdentityPolicyFactory;
21
+ exports.default = DefaultNodeIdentityPolicyFactory;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultNodeIdentityPolicy = void 0;
4
+ const core_1 = require("@naylence/core");
5
+ const token_provider_factory_js_1 = require("../security/auth/token-provider-factory.js");
6
+ const token_provider_js_1 = require("../security/auth/token-provider.js");
7
+ const logging_js_1 = require("../util/logging.js");
8
+ const logger = (0, logging_js_1.getLogger)('naylence.fame.node.default_node_identity_policy');
9
+ class DefaultNodeIdentityPolicy {
10
+ async resolveInitialNodeId(context) {
11
+ if (context.configuredId) {
12
+ return context.configuredId;
13
+ }
14
+ if (context.persistedId) {
15
+ return context.persistedId;
16
+ }
17
+ return await (0, core_1.generateIdAsync)({ mode: 'fingerprint' });
18
+ }
19
+ async resolveAdmissionNodeId(context) {
20
+ // Try to extract identity from grants first
21
+ if (context.grants && context.grants.length > 0) {
22
+ for (const grant of context.grants) {
23
+ try {
24
+ const auth = grant.auth;
25
+ if (!auth) {
26
+ continue;
27
+ }
28
+ const tokenProviderConfig = (auth.tokenProvider ??
29
+ auth.token_provider);
30
+ if (!tokenProviderConfig ||
31
+ typeof tokenProviderConfig.type !== 'string') {
32
+ continue;
33
+ }
34
+ const provider = await token_provider_factory_js_1.TokenProviderFactory.createTokenProvider(tokenProviderConfig);
35
+ if ((0, token_provider_js_1.isIdentityExposingTokenProvider)(provider)) {
36
+ const identity = await provider.getIdentity();
37
+ if (identity && identity.subject) {
38
+ logger.debug('identity_extracted_from_grant', {
39
+ identity_id: identity.subject,
40
+ grant_type: grant.type,
41
+ });
42
+ return identity.subject;
43
+ }
44
+ }
45
+ }
46
+ catch (error) {
47
+ logger.warning('identity_extraction_failed', {
48
+ error: error instanceof Error ? error.message : String(error),
49
+ grant_type: grant.type,
50
+ });
51
+ }
52
+ }
53
+ }
54
+ if (!context.currentNodeId) {
55
+ return await (0, core_1.generateIdAsync)({ mode: 'fingerprint' });
56
+ }
57
+ return context.currentNodeId;
58
+ }
59
+ }
60
+ exports.DefaultNodeIdentityPolicy = DefaultNodeIdentityPolicy;
@@ -34,9 +34,10 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.makeCommonOptions = makeCommonOptions;
37
- const core_1 = require("@naylence/core");
38
37
  const factory_1 = require("@naylence/factory");
39
38
  const admission_client_factory_js_1 = require("./admission/admission-client-factory.js");
39
+ const default_node_identity_policy_js_1 = require("./default-node-identity-policy.js");
40
+ const node_identity_policy_factory_js_1 = require("./node-identity-policy-factory.js");
40
41
  const default_node_attach_client_js_1 = require("./admission/default-node-attach-client.js");
41
42
  const transport_listener_factory_js_1 = require("../connector/transport-listener-factory.js");
42
43
  const storage_provider_factory_js_1 = require("../storage/storage-provider-factory.js");
@@ -158,6 +159,7 @@ async function makeCommonOptions(config, rawConfig) {
158
159
  const deliveryConfig = pickOption(config.delivery ?? null, aliasRecord, 'delivery_policy');
159
160
  const telemetryConfig = pickOption(config.telemetry ?? null, aliasRecord, 'trace_emitter', 'telemetry_config');
160
161
  const securityConfig = pickOption(config.security ?? null, aliasRecord, 'security_manager', 'security_profile');
162
+ const identityPolicyConfig = pickOption(config.identityPolicy ?? null, aliasRecord, 'identity_policy', 'node_identity_policy');
161
163
  const publicUrl = pickString(config.publicUrl ?? null, aliasRecord, 'public_url') ?? null;
162
164
  const directParentUrl = pickString(config.directParentUrl ?? null, aliasRecord, 'direct_parent_url') ?? null;
163
165
  const hasParentFlag = config.hasParent || Boolean(aliasRecord.has_parent ?? false);
@@ -166,7 +168,8 @@ async function makeCommonOptions(config, rawConfig) {
166
168
  const storageProvider = await resolveStorageProvider(storageConfig ?? null, expressionOptions);
167
169
  const nodeMetaStore = await storageProvider.getKeyValueStore(node_meta_js_1.NodeMetaRecord, node_meta_js_1.NODE_META_NAMESPACE);
168
170
  const nodeMeta = await nodeMetaStore.get('self');
169
- const admissionClient = await resolveAdmissionClient(admissionConfig ?? null, expressionOptions);
171
+ const identityPolicy = await resolveNodeIdentityPolicy(identityPolicyConfig ?? null, expressionOptions);
172
+ const admissionClient = await resolveAdmissionClient(admissionConfig ?? null, expressionOptions, identityPolicy ?? undefined);
170
173
  const hasParent = determineHasParent(hasParentFlag, directParentUrl, admissionClient);
171
174
  const replicaStickinessManager = await resolveReplicaStickinessManager(hasParent, requestedLogicals, expressionOptions);
172
175
  const attachmentKeyValidator = await resolveAttachmentKeyValidator(attachmentKeyValidatorConfig ?? null, expressionOptions);
@@ -198,9 +201,11 @@ async function makeCommonOptions(config, rawConfig) {
198
201
  addEventListener(listener, eventListeners);
199
202
  }
200
203
  const bindingStore = await storageProvider.getKeyValueStore(binding_manager_js_1.BindingStoreEntryRecord, BINDING_STORE_NAMESPACE);
201
- const systemId = systemIdOverride ??
202
- nodeMeta?.id ??
203
- (await (0, core_1.generateIdAsync)({ mode: 'fingerprint' }));
204
+ const effectiveIdentityPolicy = identityPolicy ?? new default_node_identity_policy_js_1.DefaultNodeIdentityPolicy();
205
+ const systemId = await effectiveIdentityPolicy.resolveInitialNodeId({
206
+ configuredId: systemIdOverride,
207
+ persistedId: nodeMeta?.id,
208
+ });
204
209
  const attachClientOptions = {
205
210
  ...(attachmentKeyValidator ? { attachmentKeyValidator } : {}),
206
211
  ...(replicaStickinessManager ? { replicaStickinessManager } : {}),
@@ -231,8 +236,20 @@ async function makeCommonOptions(config, rawConfig) {
231
236
  eventListeners,
232
237
  transportListeners,
233
238
  traceEmitter,
239
+ identityPolicy: identityPolicy ?? undefined,
234
240
  };
235
241
  }
242
+ async function resolveNodeIdentityPolicy(config, options) {
243
+ try {
244
+ return await node_identity_policy_factory_js_1.NodeIdentityPolicyFactory.createNodeIdentityPolicy(config ?? undefined, cloneCreateOptions(options));
245
+ }
246
+ catch (error) {
247
+ logger.warning('node_identity_policy_creation_failed', {
248
+ error: error instanceof Error ? error.message : String(error),
249
+ });
250
+ return null;
251
+ }
252
+ }
236
253
  async function resolveStorageProvider(config, options) {
237
254
  if (config) {
238
255
  try {
@@ -246,12 +263,19 @@ async function resolveStorageProvider(config, options) {
246
263
  }
247
264
  return new in_memory_storage_js_1.InMemoryStorageProvider();
248
265
  }
249
- async function resolveAdmissionClient(config, options) {
266
+ async function resolveAdmissionClient(config, options, identityPolicy) {
250
267
  if (config && typeof config.hello === 'function') {
251
268
  return config;
252
269
  }
253
270
  try {
254
- return await admission_client_factory_js_1.AdmissionClientFactory.createAdmissionClient((config ?? null), cloneCreateOptions(options));
271
+ const createOptions = cloneCreateOptions(options);
272
+ if (identityPolicy) {
273
+ createOptions.factoryArgs = [
274
+ ...(createOptions.factoryArgs ?? []),
275
+ { identityPolicy },
276
+ ];
277
+ }
278
+ return await admission_client_factory_js_1.AdmissionClientFactory.createAdmissionClient((config ?? null), createOptions);
255
279
  }
256
280
  catch (error) {
257
281
  logger.warning('admission_client_creation_failed', {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NodeFactory = exports.NODE_LIKE_FACTORY_BASE_TYPE = void 0;
3
+ exports.NodeIdentityPolicyProfileFactory = exports.TokenSubjectNodeIdentityPolicyFactory = exports.DefaultNodeIdentityPolicyFactory = exports.NodeFactory = exports.NODE_LIKE_FACTORY_BASE_TYPE = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  require("./node-factory.js");
6
6
  /**
@@ -24,3 +24,13 @@ tslib_1.__exportStar(require("./fame-environment-context.js"), exports);
24
24
  tslib_1.__exportStar(require("./session-manager.js"), exports);
25
25
  tslib_1.__exportStar(require("./upstream-session-manager.js"), exports);
26
26
  tslib_1.__exportStar(require("./root-session-manager.js"), exports);
27
+ tslib_1.__exportStar(require("./node-identity-policy.js"), exports);
28
+ tslib_1.__exportStar(require("./node-identity-policy-factory.js"), exports);
29
+ tslib_1.__exportStar(require("./default-node-identity-policy.js"), exports);
30
+ var default_node_identity_policy_factory_js_1 = require("./default-node-identity-policy-factory.js");
31
+ Object.defineProperty(exports, "DefaultNodeIdentityPolicyFactory", { enumerable: true, get: function () { return default_node_identity_policy_factory_js_1.DefaultNodeIdentityPolicyFactory; } });
32
+ tslib_1.__exportStar(require("./token-subject-node-identity-policy.js"), exports);
33
+ var token_subject_node_identity_policy_factory_js_1 = require("./token-subject-node-identity-policy-factory.js");
34
+ Object.defineProperty(exports, "TokenSubjectNodeIdentityPolicyFactory", { enumerable: true, get: function () { return token_subject_node_identity_policy_factory_js_1.TokenSubjectNodeIdentityPolicyFactory; } });
35
+ var node_identity_policy_profile_factory_js_1 = require("./node-identity-policy-profile-factory.js");
36
+ Object.defineProperty(exports, "NodeIdentityPolicyProfileFactory", { enumerable: true, get: function () { return node_identity_policy_profile_factory_js_1.NodeIdentityPolicyProfileFactory; } });
@@ -25,6 +25,7 @@ const FameNodeConfigSchemaInternal = zod_1.z
25
25
  attachmentKeyValidator: zod_1.z.unknown().optional().nullable(),
26
26
  telemetry: zod_1.z.unknown().optional().nullable(),
27
27
  requestedCapabilities: zod_1.z.array(zod_1.z.string()).optional(),
28
+ identityPolicy: zod_1.z.unknown().optional().nullable(),
28
29
  })
29
30
  .passthrough();
30
31
  function normalizeFameNodeConfig(input) {
@@ -63,6 +64,9 @@ function normalizeFameNodeConfig(input) {
63
64
  telemetry: parsed.telemetry === undefined
64
65
  ? null
65
66
  : parsed.telemetry,
67
+ identityPolicy: parsed.identityPolicy === undefined
68
+ ? null
69
+ : parsed.identityPolicy,
66
70
  };
67
71
  if (parsed.requestedCapabilities) {
68
72
  normalized.requestedCapabilities = coerceStringArray(parsed.requestedCapabilities);
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NodeIdentityPolicyFactory = exports.NODE_IDENTITY_POLICY_FACTORY_BASE_TYPE = void 0;
4
+ const factory_1 = require("@naylence/factory");
5
+ exports.NODE_IDENTITY_POLICY_FACTORY_BASE_TYPE = 'NodeIdentityPolicyFactory';
6
+ class NodeIdentityPolicyFactory extends factory_1.AbstractResourceFactory {
7
+ static async createNodeIdentityPolicy(config, options = {}) {
8
+ if (config) {
9
+ const policy = await (0, factory_1.createResource)(exports.NODE_IDENTITY_POLICY_FACTORY_BASE_TYPE, config, options);
10
+ if (!policy) {
11
+ throw new Error('Failed to create node identity policy from configuration');
12
+ }
13
+ return policy;
14
+ }
15
+ const policy = await (0, factory_1.createDefaultResource)(exports.NODE_IDENTITY_POLICY_FACTORY_BASE_TYPE, null, options);
16
+ if (!policy) {
17
+ throw new Error('Failed to create default node identity policy');
18
+ }
19
+ return policy;
20
+ }
21
+ }
22
+ exports.NodeIdentityPolicyFactory = NodeIdentityPolicyFactory;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NodeIdentityPolicyProfileFactory = exports.FACTORY_META = void 0;
4
+ const node_identity_policy_factory_js_1 = require("./node-identity-policy-factory.js");
5
+ const logging_js_1 = require("../util/logging.js");
6
+ const logger = (0, logging_js_1.getLogger)('naylence.fame.node.node_identity_policy_profile_factory');
7
+ const PROFILE_NAME_DEFAULT = 'default';
8
+ const PROFILE_NAME_TOKEN_SUBJECT = 'token-subject';
9
+ const PROFILE_NAME_TOKEN_SUBJECT_ALIAS = 'token_subject';
10
+ const DEFAULT_PROFILE = {
11
+ type: 'DefaultNodeIdentityPolicy',
12
+ };
13
+ const TOKEN_SUBJECT_PROFILE = {
14
+ type: 'TokenSubjectNodeIdentityPolicy',
15
+ };
16
+ const PROFILE_MAP = {
17
+ [PROFILE_NAME_DEFAULT]: DEFAULT_PROFILE,
18
+ [PROFILE_NAME_TOKEN_SUBJECT]: TOKEN_SUBJECT_PROFILE,
19
+ [PROFILE_NAME_TOKEN_SUBJECT_ALIAS]: TOKEN_SUBJECT_PROFILE,
20
+ };
21
+ exports.FACTORY_META = {
22
+ base: node_identity_policy_factory_js_1.NODE_IDENTITY_POLICY_FACTORY_BASE_TYPE,
23
+ key: 'NodeIdentityPolicyProfile',
24
+ };
25
+ class NodeIdentityPolicyProfileFactory extends node_identity_policy_factory_js_1.NodeIdentityPolicyFactory {
26
+ constructor() {
27
+ super(...arguments);
28
+ this.type = 'NodeIdentityPolicyProfile';
29
+ }
30
+ async create(config) {
31
+ const normalized = normalizeConfig(config);
32
+ const profileConfig = resolveProfileConfig(normalized.profile);
33
+ logger.debug('enabling_node_identity_policy_profile', {
34
+ profile: normalized.profile,
35
+ });
36
+ return node_identity_policy_factory_js_1.NodeIdentityPolicyFactory.createNodeIdentityPolicy(profileConfig);
37
+ }
38
+ }
39
+ exports.NodeIdentityPolicyProfileFactory = NodeIdentityPolicyProfileFactory;
40
+ function normalizeConfig(config) {
41
+ if (!config) {
42
+ return { profile: PROFILE_NAME_DEFAULT };
43
+ }
44
+ const candidate = config;
45
+ const profileValue = typeof candidate.profile === 'string' && candidate.profile.trim().length > 0
46
+ ? candidate.profile
47
+ : typeof candidate.profile_name === 'string' &&
48
+ candidate.profile_name.trim().length > 0
49
+ ? candidate.profile_name
50
+ : typeof candidate.profileName === 'string' &&
51
+ candidate.profileName.trim().length > 0
52
+ ? candidate.profileName
53
+ : PROFILE_NAME_DEFAULT;
54
+ const normalizedProfile = profileValue.trim().toLowerCase();
55
+ return { profile: normalizedProfile };
56
+ }
57
+ function resolveProfileConfig(profileName) {
58
+ const profile = PROFILE_MAP[profileName];
59
+ if (!profile) {
60
+ throw new Error(`Unknown node identity policy profile: ${profileName}`);
61
+ }
62
+ return deepClone(profile);
63
+ }
64
+ function deepClone(value) {
65
+ return JSON.parse(JSON.stringify(value));
66
+ }
67
+ exports.default = NodeIdentityPolicyProfileFactory;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -126,14 +126,15 @@ function sortListeners(listeners) {
126
126
  class FameNode extends task_spawner_js_1.TaskSpawner {
127
127
  constructor(options = {}) {
128
128
  super();
129
+ this._confirmedId = null;
129
130
  this._sessionManager = null;
130
131
  this._upstreamConnector = null;
131
132
  this._isStarted = false;
132
133
  this._lastHeartbeatAt = null;
133
134
  const systemIdOption = resolveStringOption(options, 'systemId', 'system_id');
134
- this._id = systemIdOption ?? (0, core_2.generateId)();
135
+ this._provisionalId = systemIdOption ?? (0, core_2.generateId)();
135
136
  const physicalPathOption = resolveStringOption(options, 'physicalPath', 'physical_path');
136
- this._physicalPath = physicalPathOption ?? `/${this._id}`;
137
+ this._physicalPath = physicalPathOption ?? `/${this._provisionalId}`;
137
138
  const hasParentOption = resolveBooleanOption(options, 'hasParent', 'has_parent');
138
139
  this._hasParent = hasParentOption ?? false;
139
140
  const storageProviderOption = resolveOption(options, 'storageProvider', 'storage_provider');
@@ -187,7 +188,7 @@ class FameNode extends task_spawner_js_1.TaskSpawner {
187
188
  const bindingStoreOption = resolveOption(options, 'bindingStore', 'binding_store');
188
189
  const bindingManagerOptions = {
189
190
  hasUpstream: this._hasParent,
190
- getId: () => this._id,
191
+ getId: () => this.id,
191
192
  getPhysicalPath: () => this._physicalPath,
192
193
  getAcceptedLogicals: () => this._acceptedLogicals,
193
194
  forwardUpstream: (envelope, context) => this.forwardUpstream(envelope, context),
@@ -242,7 +243,8 @@ class FameNode extends task_spawner_js_1.TaskSpawner {
242
243
  }
243
244
  }
244
245
  async initializeRootSessionManager() {
245
- const admissionClient = this._admissionClient ?? new noop_admission_client_js_1.NoopAdmissionClient({ systemId: this._id });
246
+ const admissionClient = this._admissionClient ??
247
+ new noop_admission_client_js_1.NoopAdmissionClient({ systemId: this._provisionalId });
246
248
  const manager = new root_session_manager_js_1.RootSessionManager({
247
249
  node: this,
248
250
  admissionClient,
@@ -279,9 +281,37 @@ class FameNode extends task_spawner_js_1.TaskSpawner {
279
281
  await this.deliver(envelope, context);
280
282
  return null;
281
283
  }
284
+ confirmIdentity(systemId, source) {
285
+ if (this._confirmedId) {
286
+ if (this._confirmedId !== systemId) {
287
+ logger.error('node_identity_mismatch', {
288
+ current_id: this._confirmedId,
289
+ new_id: systemId,
290
+ source,
291
+ });
292
+ throw new Error(`Node identity mismatch in ${source}: expected ${this._confirmedId}, got ${systemId}`);
293
+ }
294
+ return;
295
+ }
296
+ const isReassignment = this._provisionalId !== systemId;
297
+ this._confirmedId = systemId;
298
+ if (isReassignment) {
299
+ logger.debug('node_identity_reassigned', {
300
+ system_id: systemId,
301
+ previous_id: this._provisionalId,
302
+ source,
303
+ });
304
+ }
305
+ else {
306
+ logger.debug('node_identity_confirmed', {
307
+ system_id: systemId,
308
+ source,
309
+ });
310
+ }
311
+ }
282
312
  async handleWelcome(welcome) {
283
313
  if (welcome.systemId) {
284
- this._id = welcome.systemId;
314
+ this.confirmIdentity(welcome.systemId, 'handleWelcome');
285
315
  }
286
316
  if (welcome.acceptedLogicals) {
287
317
  this._acceptedLogicals = new Set(welcome.acceptedLogicals);
@@ -302,7 +332,7 @@ class FameNode extends task_spawner_js_1.TaskSpawner {
302
332
  await this.dispatchEvent('onWelcome', welcome);
303
333
  }
304
334
  async handleAttach(info, connector) {
305
- this._id = info.systemId;
335
+ this.confirmIdentity(info.systemId, 'handleAttach');
306
336
  this._physicalPath =
307
337
  info.assignedPath ?? info.targetPhysicalPath ?? this._physicalPath;
308
338
  this._upstreamConnector = connector;
@@ -388,7 +418,13 @@ class FameNode extends task_spawner_js_1.TaskSpawner {
388
418
  });
389
419
  }
390
420
  get id() {
391
- return this._id;
421
+ if (!this._confirmedId) {
422
+ throw new Error('Node ID has not been confirmed yet. Use provisionalId for bootstrapping.');
423
+ }
424
+ return this._confirmedId;
425
+ }
426
+ get provisionalId() {
427
+ return this._provisionalId;
392
428
  }
393
429
  get sid() {
394
430
  return this._sid;
@@ -907,8 +943,8 @@ class FameNode extends task_spawner_js_1.TaskSpawner {
907
943
  const store = await this._nodeMetaStorePromise;
908
944
  const existing = await store.get('self');
909
945
  const record = existing
910
- ? Object.assign(existing, { id: this._id })
911
- : new node_meta_js_1.NodeMetaRecord(this._id);
946
+ ? Object.assign(existing, { id: this.id })
947
+ : new node_meta_js_1.NodeMetaRecord(this.id);
912
948
  await store.set('self', record);
913
949
  }
914
950
  catch (error) {
@@ -173,8 +173,7 @@ class RootSessionManager extends task_spawner_js_1.TaskSpawner {
173
173
  }
174
174
  async performAdmission() {
175
175
  this.admissionEpoch += 1;
176
- this.initializeRootIdentityIfNeeded();
177
- const welcome = await this.admissionClient.hello(this.node.id, (0, core_1.generateId)(), this.requestedLogicals);
176
+ const welcome = await this.admissionClient.hello(this.node.provisionalId, (0, core_1.generateId)(), this.requestedLogicals);
178
177
  this.currentWelcome = welcome.frame;
179
178
  const cryptoProvider = this.node.cryptoProvider; //getCryptoProvider();
180
179
  if (welcome.frame.assignedPath && cryptoProvider?.prepareForAttach) {
@@ -383,15 +382,6 @@ class RootSessionManager extends task_spawner_js_1.TaskSpawner {
383
382
  seconds_before_expiry: RootSessionManager.JWT_REFRESH_SAFETY,
384
383
  });
385
384
  }
386
- initializeRootIdentityIfNeeded() {
387
- const nodeAny = this.node;
388
- if (!this.node.id) {
389
- nodeAny._id = (0, core_1.generateId)();
390
- logger.debug('root_identity_generated_id_for_admission', {
391
- system_id: this.node.id,
392
- });
393
- }
394
- }
395
385
  async consumeTask(task) {
396
386
  try {
397
387
  await task.promise;